need to commit more
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
let playerId = localStorage.getItem('playerId') || '';
|
||||
let currentGameId = localStorage.getItem('gameId') || '';
|
||||
/** Avoid duplicate tabs when toggleReady and lobby poll both notice the new game */
|
||||
let openedVisualGameTabForGameId = '';
|
||||
let lastGameState = null;
|
||||
let lastRenderedGameLogKey = null;
|
||||
let finalizeRollInFlight = false;
|
||||
@@ -115,6 +117,14 @@ let playerId = localStorage.getItem('playerId') || '';
|
||||
wireDiceRigUi();
|
||||
wireDebugStartingResourcesUi();
|
||||
wireAutoHarvestUi();
|
||||
|
||||
function openVisualGameClientTab(gameId) {
|
||||
if (!gameId || !playerId) return;
|
||||
if (openedVisualGameTabForGameId === gameId) return;
|
||||
openedVisualGameTabForGameId = gameId;
|
||||
const q = new URLSearchParams({ game_id: gameId, player_id: playerId });
|
||||
window.open(`${location.origin}/?${q}`, '_blank', 'noopener,noreferrer');
|
||||
}
|
||||
|
||||
async function joinLobby() {
|
||||
const name = document.getElementById('playerName').value;
|
||||
@@ -156,6 +166,7 @@ let playerId = localStorage.getItem('playerId') || '';
|
||||
if (data.in_game) {
|
||||
html += `<p><strong>You are in game: ${data.game_id}</strong></p>`;
|
||||
if (data.game_id && data.game_id !== currentGameId) {
|
||||
openVisualGameClientTab(data.game_id);
|
||||
currentGameId = data.game_id;
|
||||
localStorage.setItem('gameId', currentGameId);
|
||||
// Fetch immediately when we first learn the game id
|
||||
@@ -190,7 +201,7 @@ let playerId = localStorage.getItem('playerId') || '';
|
||||
if (data.game_id) {
|
||||
currentGameId = data.game_id;
|
||||
localStorage.setItem('gameId', currentGameId);
|
||||
alert('Game started! Game ID: ' + data.game_id);
|
||||
openVisualGameClientTab(data.game_id);
|
||||
// Immediately fetch state so the Game section fills in
|
||||
getGameState(false);
|
||||
}
|
||||
@@ -428,7 +439,9 @@ let playerId = localStorage.getItem('playerId') || '';
|
||||
boardBtn.textContent = 'Board';
|
||||
boardBtn.style.left = '50%';
|
||||
boardBtn.style.top = '50%';
|
||||
boardBtn.onclick = () => { openBoardTableau(); };
|
||||
boardBtn.onclick = () => {
|
||||
window.open(`/?game_id=${currentGameId}&player_id=${playerId}`, '_blank');
|
||||
};
|
||||
wrap.appendChild(boardBtn);
|
||||
|
||||
const cleanPlayers = players.filter(p => p && p.player_id);
|
||||
@@ -555,7 +568,7 @@ let playerId = localStorage.getItem('playerId') || '';
|
||||
if (reqAction === 'bonus_resource_choice') return true;
|
||||
const trimmed = reqAction.trim();
|
||||
if (trimmed.startsWith('choose ')) return true;
|
||||
if (trimmed === 'choose_player' || trimmed === 'choose_monster_strength' || trimmed === 'domain_self_convert') return true;
|
||||
if (trimmed === 'choose_player' || trimmed === 'choose_monster_strength' || trimmed === 'domain_self_convert' || trimmed === 'harvest_optional_exchange') return true;
|
||||
if (reqAction === 'standard_action' && (gameState.phase || '') === 'action') return true;
|
||||
return false;
|
||||
}
|
||||
@@ -1226,6 +1239,10 @@ let playerId = localStorage.getItem('playerId') || '';
|
||||
return renderDomainSelfConvertPrompt(gameState);
|
||||
}
|
||||
|
||||
if (reqAction === 'harvest_optional_exchange') {
|
||||
return renderHarvestOptionalExchangePrompt(gameState);
|
||||
}
|
||||
|
||||
if (reqAction === 'choose_player') {
|
||||
return renderDomainChoosePlayer(gameState);
|
||||
}
|
||||
@@ -1655,6 +1672,79 @@ let playerId = localStorage.getItem('playerId') || '';
|
||||
}
|
||||
}
|
||||
|
||||
function harvestExchangeExplain(command) {
|
||||
const parts = (command || '').trim().split(/\s+/);
|
||||
if (parts.length < 5 || parts[0].toLowerCase() !== 'exchange') return (command || '').trim() || 'Optional harvest exchange.';
|
||||
const pay = parts[1].toLowerCase();
|
||||
const payN = parts[2];
|
||||
const gain = parts[3].toLowerCase();
|
||||
const gainN = parts[4];
|
||||
const labels = { g: 'gold', s: 'strength', m: 'magic', v: 'victory points' };
|
||||
return `Pay ${payN} ${labels[pay] || pay}, gain ${gainN} ${labels[gain] || gain}.`;
|
||||
}
|
||||
|
||||
function renderHarvestOptionalExchangePrompt(gameState) {
|
||||
const panel = document.getElementById('choicePanel');
|
||||
if (!panel) return;
|
||||
const req = gameState?.action_required || {};
|
||||
const reqId = (req?.id || '').toString();
|
||||
const isYou = (playerId && reqId === playerId);
|
||||
const prc = gameState?.pending_required_choice || null;
|
||||
const cmd = (prc?.command || '').toString();
|
||||
const explain = harvestExchangeExplain(cmd);
|
||||
if (!isYou) {
|
||||
panel.innerHTML = `<div style="padding:8px;border:1px solid #ddd;border-radius:8px;background:#fff6d8;">
|
||||
Waiting on <code>${escapeHtml(reqId)}</code> — optional citizen harvest exchange.
|
||||
</div>`;
|
||||
return;
|
||||
}
|
||||
panel.innerHTML = `
|
||||
<div style="padding:10px;border:1px solid #ddd;border-radius:8px;background:#eef7ff;">
|
||||
<div style="font-weight:700;margin-bottom:8px;">Harvest: optional exchange</div>
|
||||
<div class="mini" style="margin-bottom:8px;color:#333;">${escapeHtml(explain)}</div>
|
||||
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
||||
<button type="button" onclick="sendHarvestExchangeConfirm()">Take exchange</button>
|
||||
<button type="button" onclick="sendHarvestExchangeSkip()">Skip (keep resources)</button>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
async function sendHarvestExchangeConfirm() {
|
||||
if (!playerId || !currentGameId) return;
|
||||
try {
|
||||
await fetch(`/api/game/${currentGameId}/action`, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
player_id: playerId,
|
||||
action_type: 'act_on_required_action',
|
||||
action: 'confirm_harvest_exchange'
|
||||
})
|
||||
});
|
||||
getGameState(false);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendHarvestExchangeSkip() {
|
||||
if (!playerId || !currentGameId) return;
|
||||
try {
|
||||
await fetch(`/api/game/${currentGameId}/action`, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
player_id: playerId,
|
||||
action_type: 'act_on_required_action',
|
||||
action: 'skip_harvest_exchange'
|
||||
})
|
||||
});
|
||||
getGameState(false);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function renderDomainChoosePlayer(gameState) {
|
||||
const panel = document.getElementById('choicePanel');
|
||||
if (!panel) return;
|
||||
|
||||
3095
static/game/game.js
Normal file
3095
static/game/game.js
Normal file
File diff suppressed because it is too large
Load Diff
51
static/game/index.html
Normal file
51
static/game/index.html
Normal file
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Valeria Card Kingdoms</title>
|
||||
<link rel="stylesheet" href="/static/game/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<a href="/" class="game-lobby-btn">Lobby</a>
|
||||
<div class="board" id="board">
|
||||
<div class="seat seat-0" id="seat-0"></div>
|
||||
<div class="seat seat-1" id="seat-1"></div>
|
||||
<div class="seat seat-2" id="seat-2"></div>
|
||||
<div class="seat seat-3" id="seat-3"></div>
|
||||
<div class="seat seat-4" id="seat-4"></div>
|
||||
<div class="center-board" id="zone-center"></div>
|
||||
</div>
|
||||
<div id="conn-status" class="conn-status"></div>
|
||||
<div id="lobby-overlay" class="lobby-overlay" aria-hidden="true">
|
||||
<div class="lobby-sheet" role="dialog" aria-modal="true" aria-labelledby="lobby-title">
|
||||
<div class="lobby-brand">
|
||||
<p class="lobby-kicker">Table</p>
|
||||
<h1 id="lobby-title" class="lobby-title">Valeria Card Kingdoms</h1>
|
||||
<p class="lobby-tagline">Join the lobby, ready up when your group is here.</p>
|
||||
</div>
|
||||
<div id="lobby-error" class="lobby-error lobby-hidden" role="alert"></div>
|
||||
<div id="lobby-step-join" class="lobby-step">
|
||||
<label class="lobby-label" for="lobby-display-name">Display name</label>
|
||||
<div class="lobby-join-row">
|
||||
<input id="lobby-display-name" class="lobby-input" type="text" maxlength="40" placeholder="Your name" autocomplete="nickname" />
|
||||
<button type="button" id="lobby-join-btn" class="lobby-btn lobby-btn-primary">Join lobby</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="lobby-step-wait" class="lobby-step lobby-hidden">
|
||||
<p class="lobby-hint">Need at least two players. Everyone taps ready to start.</p>
|
||||
<ul id="lobby-player-list" class="lobby-player-list"></ul>
|
||||
<div class="lobby-actions">
|
||||
<button type="button" id="lobby-ready-btn" class="lobby-btn lobby-btn-ready">Ready</button>
|
||||
<button type="button" id="lobby-leave-btn" class="lobby-btn lobby-btn-ghost">Leave</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lobby-footer">
|
||||
<span id="lobby-live" class="lobby-live lobby-live--warn" aria-live="polite">Connecting…</span>
|
||||
<p id="lobby-meta" class="lobby-meta"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/static/game/game.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
1574
static/game/style.css
Normal file
1574
static/game/style.css
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user