104 lines
6.2 KiB
Svelte
104 lines
6.2 KiB
Svelte
<script>
|
|
import Card from './Card.svelte';
|
|
import ChallengePanel from './scene/ChallengePanel.svelte';
|
|
import ObstacleBoard from './scene/ObstacleBoard.svelte';
|
|
import DeepControlPanel from './scene/DeepControlPanel.svelte';
|
|
import CharacterSheet from './scene/CharacterSheet.svelte';
|
|
import EventLog from './EventLog.svelte';
|
|
|
|
export let state;
|
|
|
|
$: hand = state.player.hand_cards ? JSON.parse(state.player.hand_cards) : [];
|
|
$: captain = state.players.find(p => p.id === state.game.captain_player_id) || null;
|
|
$: isCaptain = state.game.captain_player_id === state.player.id;
|
|
$: playerTechs = { J: state.player.tech_jack, Q: state.player.tech_queen, K: state.player.tech_king };
|
|
</script>
|
|
|
|
<div class="scene-view-layout" id="scene-layout-container" data-game-id={state.game.id} data-player-id={state.player.id}>
|
|
<!-- LEFT COLUMN: The Shared Table -->
|
|
<div class="table-column">
|
|
|
|
<!-- Game Deck and Obstacles Roster -->
|
|
<div class="card glass-panel obstacle-list-card">
|
|
<div class="card-header">
|
|
<h3>🌊 The Obstacle List</h3>
|
|
<span class="deck-counter">🎴 Deck: {state.game.deck_cards ? JSON.parse(state.game.deck_cards).length : 0} cards left</span>
|
|
</div>
|
|
|
|
<div class="obstacles-container" id="scene-obstacles-container">
|
|
<!-- TOP STATUS BAR: Scene Info and Captain -->
|
|
<div class="scene-status-banner glass-panel" style="margin-bottom: 1.5rem; padding: 1rem; background: rgba(7, 11, 18, 0.6); border: 1px solid rgba(212, 175, 55, 0.2); border-radius: 8px;">
|
|
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 1rem;">
|
|
<!-- Captain Badge -->
|
|
<div>
|
|
{#if captain}
|
|
<span class="captain-badge" style="background: rgba(212, 175, 55, 0.15); border: 1px solid var(--gold); color: var(--gold); padding: 0.4rem 1rem; border-radius: 20px; font-size: 1rem; font-weight: 700; font-family: var(--font-heading); display: inline-flex; align-items: center; gap: 0.5rem;">
|
|
🏴☠️ Captain: {captain.name} (Rank {captain.rank}){isCaptain ? ' — You!' : ''}
|
|
</span>
|
|
{:else}
|
|
<span class="captain-badge" style="background: rgba(255, 255, 255, 0.05); border: 1px dashed rgba(255, 255, 255, 0.2); color: var(--text-muted); padding: 0.4rem 1rem; border-radius: 20px; font-size: 1rem; display: inline-flex; align-items: center; gap: 0.5rem;">
|
|
🏴☠️ Captain: None{state.game.completed_crew_1 ? '' : ' (steal a ship first!)'}
|
|
</span>
|
|
{/if}
|
|
</div>
|
|
|
|
<!-- Active Scene Roster Mini-list -->
|
|
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;">
|
|
<span style="font-size: 0.85rem; color: var(--text-muted); font-family: var(--font-heading); margin-right: 0.25rem;">Active Roster:</span>
|
|
{#each state.players as p}
|
|
{#if p.role === 'deep'}
|
|
<span class="player-chip deep-chip" style="font-size: 0.8rem; padding: 0.25rem 0.6rem; border-radius: 12px; margin: 0; display: inline-flex; align-items: center; gap: 0.25rem;">
|
|
🌊 {p.name} (Deep)
|
|
</span>
|
|
{:else if p.role === 'pirat'}
|
|
<span class="player-chip pirat-chip" style="font-size: 0.8rem; padding: 0.25rem 0.6rem; border-radius: 12px; margin: 0; display: inline-flex; align-items: center; gap: 0.25rem; {p.is_ghost ? 'border-color: #7890a8; color: #a0b0c0; background: rgba(120, 150, 180, 0.1);' : ''} {p.is_dead ? 'border-color: var(--red-suit); color: var(--red-suit); background: rgba(255, 42, 95, 0.1);' : ''} {p.id === state.game.captain_player_id ? 'border-color: var(--gold); color: var(--gold); background: rgba(212, 175, 55, 0.1);' : ''}">
|
|
{#if p.is_ghost}👻{:else if p.is_dead}💀{:else}🐀{/if} {p.name} (Rank {p.rank}){p.id === state.game.captain_player_id ? ' ⭐' : ''}
|
|
</span>
|
|
{/if}
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<ChallengePanel {state} />
|
|
<ObstacleBoard {state} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- RIGHT COLUMN: Private Hand & Character Sheet -->
|
|
<div class="private-column">
|
|
{#if state.player.role === "deep"}
|
|
<DeepControlPanel {state} />
|
|
{/if}
|
|
|
|
<!-- Player Hand Card -->
|
|
<div class="card glass-panel hand-card">
|
|
<h3>🃏 Your Hand</h3>
|
|
<p class="section-desc">Keep your cards secret! {#if state.player.role !== "deep"}When the Deep challenges you (or you assist a crewmate), drag a card onto an applied obstacle to play it. Jokers can hit any obstacle, any time.{/if}</p>
|
|
|
|
<div class="hand-flex">
|
|
{#each hand as card}
|
|
<Card {card} techs={playerTechs}
|
|
draggable={state.player.role !== "deep"}
|
|
on:dragstart={(e) => {
|
|
e.dataTransfer.setData('text/plain', card);
|
|
e.currentTarget.classList.add("dragging");
|
|
}}
|
|
on:dragend={(e) => {
|
|
e.currentTarget.classList.remove("dragging");
|
|
}} />
|
|
{:else}
|
|
<p class="empty-text text-center">Your hand is empty! (You draw cards when playing cards that match the suit color of the obstacle.)</p>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
|
|
{#if state.player.role !== "deep"}
|
|
<CharacterSheet {state} />
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
|
|
<EventLog {state} />
|