implementado funcionmiento turn-based g2 flujo de forzar o no forzar
This commit is contained in:
@@ -218,11 +218,15 @@ const myRole = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const roundState = computed(() => ({
|
const roundState = computed(() => ({
|
||||||
|
// tick to recompute when non-ref state fields change via forceUpdate()
|
||||||
|
_tick: refreshTick.value,
|
||||||
|
isFinished: (gameStatus.value || '').toLowerCase() === 'finished',
|
||||||
currentVariant: currentVariant.value,
|
currentVariant: currentVariant.value,
|
||||||
currentRound: currentRound.value,
|
currentRound: currentRound.value,
|
||||||
p1Action: p1Action.value,
|
p1Action: p1Action.value,
|
||||||
p2Action: p2Action.value,
|
p2Action: p2Action.value,
|
||||||
forcedByP2: forcedByP2.value,
|
forcedByP2: forcedByP2.value,
|
||||||
|
g2ForcePending: roomOffer('g2ForcePending'),
|
||||||
reported: reported.value,
|
reported: reported.value,
|
||||||
shameAssigned: shameAssigned.value,
|
shameAssigned: shameAssigned.value,
|
||||||
offer: {
|
offer: {
|
||||||
@@ -270,6 +274,7 @@ onMounted(() => {
|
|||||||
$(room.state).listen("p1Action", (value: string) => { p1Action.value = value; });
|
$(room.state).listen("p1Action", (value: string) => { p1Action.value = value; });
|
||||||
$(room.state).listen("p2Action", (value: string) => { p2Action.value = value; });
|
$(room.state).listen("p2Action", (value: string) => { p2Action.value = value; });
|
||||||
$(room.state).listen("forcedByP2", (value: boolean) => { forcedByP2.value = value; });
|
$(room.state).listen("forcedByP2", (value: boolean) => { forcedByP2.value = value; });
|
||||||
|
$(room.state).listen("g2ForcePending", () => forceUpdate());
|
||||||
$(room.state).listen("reported", (value: boolean) => { reported.value = value; });
|
$(room.state).listen("reported", (value: boolean) => { reported.value = value; });
|
||||||
$(room.state).listen("shameAssigned", (value: boolean) => { shameAssigned.value = value; });
|
$(room.state).listen("shameAssigned", (value: boolean) => { shameAssigned.value = value; });
|
||||||
// Offer fields
|
// Offer fields
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="g">
|
<div class="g">
|
||||||
<h3>G2 – Regla contraproductiva (P2 puede forzar)</h3>
|
<h3>G2 – Regla contraproductiva (P2 puede forzar)</h3>
|
||||||
<OfferControls v-if="myRole==='P1' && !state.offer?.active" :my-role="myRole" :disable-no-offer="state.forcedByP2" @propose="onPropose" @no-offer="onNoOffer"/>
|
<OfferControls
|
||||||
|
v-if="myRole==='P1' && !state.offer?.active && !state.g2ForcePending"
|
||||||
|
:my-role="myRole"
|
||||||
|
:disable-no-offer="state.forcedByP2"
|
||||||
|
@propose="onPropose"
|
||||||
|
@no-offer="onNoOffer"
|
||||||
|
/>
|
||||||
<OfferActions
|
<OfferActions
|
||||||
:state="state"
|
:state="state"
|
||||||
:my-role="myRole"
|
:my-role="myRole"
|
||||||
|
|||||||
@@ -4,16 +4,19 @@
|
|||||||
class="offer-actions-card"
|
class="offer-actions-card"
|
||||||
:style="({ '--p1': p1Color, '--p2': p2Color } as any)"
|
:style="({ '--p1': p1Color, '--p2': p2Color } as any)"
|
||||||
>
|
>
|
||||||
<!-- G2: Force option for P2 -->
|
<!-- G2: P2 must decide whether to force P1 before the offer UI appears -->
|
||||||
<div v-if="myRole === 'P2' && currentVariant === 'G2' && !state.offer?.active" class="force-section">
|
<div v-if="currentVariant === 'G2' && !state.offer?.active && state.g2ForcePending" class="force-section">
|
||||||
<label class="force-checkbox">
|
<div v-if="myRole === 'P2'" class="force-decision">
|
||||||
<input
|
<div class="force-title">Forzar a P1 a realizar una oferta?</div>
|
||||||
type="checkbox"
|
<div class="action-buttons">
|
||||||
:checked="state.forcedByP2"
|
<button class="btn accept" :disabled="isFinished" @click="$emit('p2Force', true)"><span class="btn-icon">✓</span> Forzar</button>
|
||||||
@change="$emit('p2Force', ($event.target as HTMLInputElement).checked)"
|
<button class="btn no-action" :disabled="isFinished" @click="$emit('p2Force', false)">No forzar</button>
|
||||||
/>
|
</div>
|
||||||
<span class="checkbox-label">Forzar oferta</span>
|
</div>
|
||||||
</label>
|
<div v-else-if="myRole === 'P1'" class="waiting-state">
|
||||||
|
<div class="spinner spinner--p2"></div>
|
||||||
|
<span class="waiting-text">Esperando decisión de P2...</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Offer display when active -->
|
<!-- Offer display when active -->
|
||||||
@@ -124,7 +127,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Waiting for offer from P1 (P2 perspective) -->
|
<!-- Waiting for offer from P1 (P2 perspective) -->
|
||||||
<div v-else-if="myRole === 'P2' && !state.offer?.active && currentVariant !== 'G2'" class="waiting-state">
|
<div
|
||||||
|
v-else-if="myRole === 'P2' && !state.offer?.active && !(currentVariant === 'G2' && state.g2ForcePending)"
|
||||||
|
class="waiting-state"
|
||||||
|
>
|
||||||
<div class="spinner spinner--p1"></div>
|
<div class="spinner spinner--p1"></div>
|
||||||
<span class="waiting-text">Esperando oferta de P1...</span>
|
<span class="waiting-text">Esperando oferta de P1...</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -154,8 +160,8 @@ const showP1PostActions = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const shouldShowComponent = computed(() => {
|
const shouldShowComponent = computed(() => {
|
||||||
// Show for P2 in G2 when forcing offer (even without active offer)
|
// In G2 while decision pending, show component for both roles
|
||||||
if (props.myRole === 'P2' && props.currentVariant === 'G2' && !props.state.offer?.active) {
|
if (props.currentVariant === 'G2' && !props.state.offer?.active && props.state.g2ForcePending) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,8 +175,10 @@ const shouldShowComponent = computed(() => {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show waiting state for P2 (except in G2 which is handled above)
|
// Show waiting state for P2
|
||||||
if (props.myRole === 'P2' && !props.state.offer?.active && props.currentVariant !== 'G2') {
|
// - In non-G2 when there's no active offer
|
||||||
|
// - In G2 after P2 has decided (g2ForcePending == false) and there's no active offer
|
||||||
|
if (props.myRole === 'P2' && !props.state.offer?.active && (props.currentVariant !== 'G2' || (props.currentVariant === 'G2' && !props.state.g2ForcePending))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +186,11 @@ const shouldShowComponent = computed(() => {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isFinished = computed(() => {
|
||||||
|
const v = (props.state?.isFinished ?? false) as boolean;
|
||||||
|
return !!v;
|
||||||
|
});
|
||||||
|
|
||||||
defineEmits(['p2Action', 'p2Force', 'assignShame', 'report']);
|
defineEmits(['p2Action', 'p2Force', 'assignShame', 'report']);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -197,25 +210,8 @@ defineEmits(['p2Action', 'p2Force', 'assignShame', 'report']);
|
|||||||
border-bottom: 1px solid #e5e9f0;
|
border-bottom: 1px solid #e5e9f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.force-checkbox {
|
.force-decision { display:flex; flex-direction:column; gap:10px; }
|
||||||
display: flex;
|
.force-title { font-weight:700; color:#334155; font-size:14px; }
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.force-checkbox input[type="checkbox"] {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
margin-right: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-label {
|
|
||||||
font-weight: 600;
|
|
||||||
color: #334155;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.offer-display {
|
.offer-display {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -110,9 +110,10 @@ export class GameRoom extends Room<GameState> {
|
|||||||
currentVariant: this.state.currentVariant
|
currentVariant: this.state.currentVariant
|
||||||
});
|
});
|
||||||
|
|
||||||
// G2: Force offer by default
|
// G2: start next round awaiting P2 decision, not forced by default
|
||||||
if (variant === 'G2') {
|
if (variant === 'G2') {
|
||||||
this.state.forcedByP2 = true;
|
this.state.g2ForcePending = true;
|
||||||
|
this.state.forcedByP2 = false;
|
||||||
}
|
}
|
||||||
this.broadcast("variantChanged", { variant });
|
this.broadcast("variantChanged", { variant });
|
||||||
this.sysChat(`🔄 Variante cambiada a ${variant} - Juego reiniciado`, 'variant_change');
|
this.sysChat(`🔄 Variante cambiada a ${variant} - Juego reiniciado`, 'variant_change');
|
||||||
@@ -171,7 +172,13 @@ export class GameRoom extends Room<GameState> {
|
|||||||
if (!player) return;
|
if (!player) return;
|
||||||
if (player.role !== "P2") return;
|
if (player.role !== "P2") return;
|
||||||
this.state.forcedByP2 = !!force;
|
this.state.forcedByP2 = !!force;
|
||||||
|
this.state.g2ForcePending = false;
|
||||||
// When forced, P1 must propose an offer; nothing automatic here.
|
// When forced, P1 must propose an offer; nothing automatic here.
|
||||||
|
// System chat feedback and dashboard update
|
||||||
|
if (this.state.currentVariant === 'G2') {
|
||||||
|
if (force) this.sysChat('📌 P2 decidió forzar a P1 a ofrecer', 'p2_force');
|
||||||
|
else this.sysChat('🕊️ P2 decidió no forzar a P1', 'p2_no_force');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// System chat helper moved to class method this.sysChat
|
// System chat helper moved to class method this.sysChat
|
||||||
@@ -481,9 +488,10 @@ export class GameRoom extends Room<GameState> {
|
|||||||
currentRound: this.state.currentRound,
|
currentRound: this.state.currentRound,
|
||||||
currentVariant: this.state.currentVariant
|
currentVariant: this.state.currentVariant
|
||||||
});
|
});
|
||||||
// G2: Force offer by default when starting game
|
// G2: awaiting P2 decision at round start (not forced by default)
|
||||||
if (this.state.currentVariant === 'G2') {
|
if (this.state.currentVariant === 'G2') {
|
||||||
this.state.forcedByP2 = true;
|
this.state.g2ForcePending = true;
|
||||||
|
this.state.forcedByP2 = false;
|
||||||
}
|
}
|
||||||
this.broadcast("gameStart");
|
this.broadcast("gameStart");
|
||||||
// System chat: start at round 1
|
// System chat: start at round 1
|
||||||
@@ -651,7 +659,8 @@ export class GameRoom extends Room<GameState> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (variant === 'G2') {
|
if (variant === 'G2') {
|
||||||
this.state.forcedByP2 = true;
|
this.state.g2ForcePending = true;
|
||||||
|
this.state.forcedByP2 = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.broadcast("variantChanged", { variant });
|
this.broadcast("variantChanged", { variant });
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export class GameState extends Schema {
|
|||||||
@type("string") p1Action: string = ""; // no_offer|"" (variable offers handled via fields below)
|
@type("string") p1Action: string = ""; // no_offer|"" (variable offers handled via fields below)
|
||||||
@type("string") p2Action: string = ""; // accept|reject|snatch
|
@type("string") p2Action: string = ""; // accept|reject|snatch
|
||||||
@type("boolean") forcedByP2: boolean = false; // G2
|
@type("boolean") forcedByP2: boolean = false; // G2
|
||||||
|
@type("boolean") g2ForcePending: boolean = false; // G2: awaiting P2 decision at round start
|
||||||
@type("boolean") reported: boolean = false; // G4
|
@type("boolean") reported: boolean = false; // G4
|
||||||
@type("boolean") shameAssigned: boolean = false; // G3
|
@type("boolean") shameAssigned: boolean = false; // G3
|
||||||
|
|
||||||
@@ -120,7 +121,9 @@ export class GameState extends Schema {
|
|||||||
resetRound(): void {
|
resetRound(): void {
|
||||||
this.p1Action = "";
|
this.p1Action = "";
|
||||||
this.p2Action = "";
|
this.p2Action = "";
|
||||||
this.forcedByP2 = (this.currentVariant === "G2");
|
// In G2, start each round awaiting P2's force decision; do not force by default
|
||||||
|
this.g2ForcePending = (this.currentVariant === "G2");
|
||||||
|
this.forcedByP2 = false;
|
||||||
this.reported = false;
|
this.reported = false;
|
||||||
this.shameAssigned = false;
|
this.shameAssigned = false;
|
||||||
this.offerPavo = this.offerElote = 0;
|
this.offerPavo = this.offerElote = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user