Refresh the scene dashboard around a crew bubble column
Replace the roster banner and the standalone character-sheet panel with a left-hand column of crew bubbles. Each bubble shows the player's role icon, name, rank, current hand size, and a 🛞 for the Captain; clicking one pops out that player's character sheet as a modal. - New CrewColumn.svelte: bubbles (Pi-Rats first, Deep last) + an Objectives button that opens a crew-objectives popup. - CharacterSheet.svelte is now a target-driven modal: description, likes/hates, and personal objectives for anyone; secret J/Q/K only on your own sheet; the Duel UI (no "Challenge whom?" — target is fixed) only when a living Pi-Rat views another living Pi-Rat. - ScenePhase.svelte → 3-column layout (crew | obstacles | hand/deep); crew column collapses to a horizontal strip on narrow screens. - Shared .modal-backdrop/.modal-box/.modal-close moved into components.css; removed the now-dead roster-banner and captain-badge styles. Backend unchanged — the state blob already carries hands, roles, ranks, objectives, and the captain. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
81
frontend/src/components/scene/CrewColumn.svelte
Normal file
81
frontend/src/components/scene/CrewColumn.svelte
Normal file
@@ -0,0 +1,81 @@
|
||||
<script>
|
||||
import CharacterSheet from './CharacterSheet.svelte';
|
||||
|
||||
export let state;
|
||||
|
||||
let openTargetId = null;
|
||||
let showObjectives = false;
|
||||
|
||||
// Pi-Rats first, the Deep last; otherwise keep join order.
|
||||
$: crew = [...state.players].sort((a, b) => (a.role === 'deep' ? 1 : 0) - (b.role === 'deep' ? 1 : 0));
|
||||
$: openTarget = openTargetId ? state.players.find(p => p.id === openTargetId) : null;
|
||||
|
||||
function handSize(p) {
|
||||
try { return JSON.parse(p.hand_cards || '[]').length; } catch { return 0; }
|
||||
}
|
||||
function roleIcon(p) {
|
||||
if (p.role === 'deep') return '🌊';
|
||||
if (p.is_ghost) return '👻';
|
||||
if (p.is_dead) return '💀';
|
||||
return '🐀';
|
||||
}
|
||||
|
||||
function onKeydown(e) {
|
||||
// CharacterSheet handles its own Escape; this closes the objectives popup.
|
||||
if (e.key === 'Escape') showObjectives = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeydown} />
|
||||
|
||||
<div class="crew-column">
|
||||
<button class="crew-objectives-btn" on:click={() => showObjectives = true}>🎯 Objectives</button>
|
||||
|
||||
<div class="crew-bubbles">
|
||||
{#each crew as p (p.id)}
|
||||
<button
|
||||
class="crew-bubble"
|
||||
class:is-you={p.id === state.player.id}
|
||||
class:is-deep={p.role === 'deep'}
|
||||
class:is-dead={p.is_dead}
|
||||
class:is-ghost={p.is_ghost}
|
||||
title="View {p.name}'s character sheet"
|
||||
on:click={() => openTargetId = p.id}
|
||||
>
|
||||
<span class="bubble-icon">{roleIcon(p)}</span>
|
||||
<span class="bubble-name">{p.name}{#if p.id === state.game.captain_player_id} 🛞{/if}</span>
|
||||
<span class="bubble-meta">
|
||||
{#if p.role === 'deep'}Deep{:else}Rank {p.rank}{/if} · 🃏 {handSize(p)}
|
||||
</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if openTarget}
|
||||
<CharacterSheet {state} target={openTarget} on:close={() => openTargetId = null} />
|
||||
{/if}
|
||||
|
||||
{#if showObjectives}
|
||||
<div class="modal-backdrop" on:click|self={() => showObjectives = false}>
|
||||
<div class="modal-box glass-panel" style="text-align: left;">
|
||||
<button class="modal-close" on:click={() => showObjectives = false} aria-label="Close">×</button>
|
||||
<h3>🎯 Crew Objectives</h3>
|
||||
<p class="info-text" style="font-size: 0.85rem;">The whole crew works toward these. The Deep marks them off as you accomplish them.</p>
|
||||
<div class="objectives-checklist">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" checked={state.game.completed_crew_1} disabled>
|
||||
<span>Steal a Ship</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" checked={state.game.completed_crew_2} disabled>
|
||||
<span>Choose a Captain</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" checked={state.game.completed_crew_3} disabled>
|
||||
<span>Commit Piracy</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
Reference in New Issue
Block a user