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(() => ({
|
||||
// tick to recompute when non-ref state fields change via forceUpdate()
|
||||
_tick: refreshTick.value,
|
||||
isFinished: (gameStatus.value || '').toLowerCase() === 'finished',
|
||||
currentVariant: currentVariant.value,
|
||||
currentRound: currentRound.value,
|
||||
p1Action: p1Action.value,
|
||||
p2Action: p2Action.value,
|
||||
forcedByP2: forcedByP2.value,
|
||||
g2ForcePending: roomOffer('g2ForcePending'),
|
||||
reported: reported.value,
|
||||
shameAssigned: shameAssigned.value,
|
||||
offer: {
|
||||
@@ -270,6 +274,7 @@ onMounted(() => {
|
||||
$(room.state).listen("p1Action", (value: string) => { p1Action.value = value; });
|
||||
$(room.state).listen("p2Action", (value: string) => { p2Action.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("shameAssigned", (value: boolean) => { shameAssigned.value = value; });
|
||||
// Offer fields
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<template>
|
||||
<div class="g">
|
||||
<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
|
||||
:state="state"
|
||||
:my-role="myRole"
|
||||
|
||||
@@ -4,16 +4,19 @@
|
||||
class="offer-actions-card"
|
||||
:style="({ '--p1': p1Color, '--p2': p2Color } as any)"
|
||||
>
|
||||
<!-- G2: Force option for P2 -->
|
||||
<div v-if="myRole === 'P2' && currentVariant === 'G2' && !state.offer?.active" class="force-section">
|
||||
<label class="force-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="state.forcedByP2"
|
||||
@change="$emit('p2Force', ($event.target as HTMLInputElement).checked)"
|
||||
/>
|
||||
<span class="checkbox-label">Forzar oferta</span>
|
||||
</label>
|
||||
<!-- G2: P2 must decide whether to force P1 before the offer UI appears -->
|
||||
<div v-if="currentVariant === 'G2' && !state.offer?.active && state.g2ForcePending" class="force-section">
|
||||
<div v-if="myRole === 'P2'" class="force-decision">
|
||||
<div class="force-title">Forzar a P1 a realizar una oferta?</div>
|
||||
<div class="action-buttons">
|
||||
<button class="btn accept" :disabled="isFinished" @click="$emit('p2Force', true)"><span class="btn-icon">✓</span> Forzar</button>
|
||||
<button class="btn no-action" :disabled="isFinished" @click="$emit('p2Force', false)">No forzar</button>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<!-- Offer display when active -->
|
||||
@@ -124,7 +127,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 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>
|
||||
<span class="waiting-text">Esperando oferta de P1...</span>
|
||||
</div>
|
||||
@@ -154,8 +160,8 @@ const showP1PostActions = computed(() => {
|
||||
});
|
||||
|
||||
const shouldShowComponent = computed(() => {
|
||||
// Show for P2 in G2 when forcing offer (even without active offer)
|
||||
if (props.myRole === 'P2' && props.currentVariant === 'G2' && !props.state.offer?.active) {
|
||||
// In G2 while decision pending, show component for both roles
|
||||
if (props.currentVariant === 'G2' && !props.state.offer?.active && props.state.g2ForcePending) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -169,8 +175,10 @@ const shouldShowComponent = computed(() => {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Show waiting state for P2 (except in G2 which is handled above)
|
||||
if (props.myRole === 'P2' && !props.state.offer?.active && props.currentVariant !== 'G2') {
|
||||
// Show waiting state for P2
|
||||
// - 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;
|
||||
}
|
||||
|
||||
@@ -178,6 +186,11 @@ const shouldShowComponent = computed(() => {
|
||||
return false;
|
||||
});
|
||||
|
||||
const isFinished = computed(() => {
|
||||
const v = (props.state?.isFinished ?? false) as boolean;
|
||||
return !!v;
|
||||
});
|
||||
|
||||
defineEmits(['p2Action', 'p2Force', 'assignShame', 'report']);
|
||||
</script>
|
||||
|
||||
@@ -197,25 +210,8 @@ defineEmits(['p2Action', 'p2Force', 'assignShame', 'report']);
|
||||
border-bottom: 1px solid #e5e9f0;
|
||||
}
|
||||
|
||||
.force-checkbox {
|
||||
display: flex;
|
||||
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;
|
||||
}
|
||||
.force-decision { display:flex; flex-direction:column; gap:10px; }
|
||||
.force-title { font-weight:700; color:#334155; font-size:14px; }
|
||||
|
||||
.offer-display {
|
||||
display: flex;
|
||||
|
||||
@@ -110,9 +110,10 @@ export class GameRoom extends Room<GameState> {
|
||||
currentVariant: this.state.currentVariant
|
||||
});
|
||||
|
||||
// G2: Force offer by default
|
||||
// G2: start next round awaiting P2 decision, not forced by default
|
||||
if (variant === 'G2') {
|
||||
this.state.forcedByP2 = true;
|
||||
this.state.g2ForcePending = true;
|
||||
this.state.forcedByP2 = false;
|
||||
}
|
||||
this.broadcast("variantChanged", { variant });
|
||||
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.role !== "P2") return;
|
||||
this.state.forcedByP2 = !!force;
|
||||
this.state.g2ForcePending = false;
|
||||
// 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
|
||||
@@ -481,9 +488,10 @@ export class GameRoom extends Room<GameState> {
|
||||
currentRound: this.state.currentRound,
|
||||
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') {
|
||||
this.state.forcedByP2 = true;
|
||||
this.state.g2ForcePending = true;
|
||||
this.state.forcedByP2 = false;
|
||||
}
|
||||
this.broadcast("gameStart");
|
||||
// System chat: start at round 1
|
||||
@@ -651,7 +659,8 @@ export class GameRoom extends Room<GameState> {
|
||||
});
|
||||
|
||||
if (variant === 'G2') {
|
||||
this.state.forcedByP2 = true;
|
||||
this.state.g2ForcePending = true;
|
||||
this.state.forcedByP2 = false;
|
||||
}
|
||||
|
||||
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") p2Action: string = ""; // accept|reject|snatch
|
||||
@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") shameAssigned: boolean = false; // G3
|
||||
|
||||
@@ -120,7 +121,9 @@ export class GameState extends Schema {
|
||||
resetRound(): void {
|
||||
this.p1Action = "";
|
||||
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.shameAssigned = false;
|
||||
this.offerPavo = this.offerElote = 0;
|
||||
|
||||
Reference in New Issue
Block a user