|
8 | 8 | import { page } from '$app/state'; |
9 | 9 | import ResourcesBar from '$lib/components/ResourcesBar.svelte'; |
10 | 10 | import type { Player } from '../../../../../islanders-shared/lib/Shared'; |
11 | | - import { notifications } from '$lib/stores/notifications.svelte'; |
| 11 | + import { EndTurnAction, StealFromPlayerAction } from '../../../../../islanders-shared/lib/Action'; |
| 12 | + import { ui } from '$lib/stores/ui.svelte'; |
| 13 | + import { getStealablePlayers } from '$lib/components/mapUtils'; |
12 | 14 |
|
13 | 15 | const props = $props(); |
14 | 16 | const { children, data } = props; |
|
18 | 20 |
|
19 | 21 | const tabs = [ |
20 | 22 | { id: 'players' as const, href: `${base}`, label: 'Players' }, |
21 | | - { id: 'build' as const, href: `${base}/build`, label: 'Build' }, |
| 23 | + { id: 'actions' as const, href: `${base}/actions`, label: 'Actions' }, |
22 | 24 | { id: 'trade' as const, href: `${base}/trade`, label: 'Trade' }, |
23 | 25 | { id: 'chat' as const, href: `${base}/chat`, label: 'Chat' }, |
24 | 26 | { id: 'setup' as const, href: `${base}/setup`, label: 'Setup' } |
|
103 | 105 | const playerResources = $derived( |
104 | 106 | currentPlayer?.resources ?? { wood: 0, clay: 0, stone: 0, grain: 0, wool: 0 } |
105 | 107 | ); |
| 108 | +
|
| 109 | + let stealModalElement: HTMLDialogElement; |
| 110 | + const isStealingFromPlayers = $derived(ui.isStealingFromPlayers); |
| 111 | +
|
| 112 | + const stealablePlayers = $derived.by(() => { |
| 113 | + if (!game.world) return []; |
| 114 | + return getStealablePlayers(game.world, game.playerName); |
| 115 | + }); |
| 116 | +
|
| 117 | + $effect(() => { |
| 118 | + console.log('Steal effect running:', { |
| 119 | + isStealingFromPlayers, |
| 120 | + stealableCount: stealablePlayers.length, |
| 121 | + modalElement: !!stealModalElement |
| 122 | + }); |
| 123 | + |
| 124 | + if (isStealingFromPlayers && stealablePlayers.length > 0) { |
| 125 | + console.log('Opening steal modal with players:', stealablePlayers.map(p => p.name)); |
| 126 | + stealModalElement?.showModal(); |
| 127 | + } else if (isStealingFromPlayers && stealablePlayers.length === 0) { |
| 128 | + console.log('No stealable players, closing steal UI'); |
| 129 | + ui.setStealingFromPlayers(false); |
| 130 | + } |
| 131 | + }); |
| 132 | +
|
| 133 | + const handleStealFrom = async (playerToStealFrom: string) => { |
| 134 | + if (!game.playerName) return; |
| 135 | + |
| 136 | + const action = new StealFromPlayerAction(game.playerName, playerToStealFrom); |
| 137 | + await game.sendAction(action); |
| 138 | + |
| 139 | + stealModalElement?.close(); |
| 140 | + ui.setStealingFromPlayers(false); |
| 141 | + }; |
| 142 | +
|
| 143 | + const handleSkipStealing = () => { |
| 144 | + stealModalElement?.close(); |
| 145 | + ui.setStealingFromPlayers(false); |
| 146 | + }; |
106 | 147 | </script> |
107 | 148 |
|
108 | 149 | <div class="flex h-screen overflow-hidden"> |
|
111 | 152 | <Map /> |
112 | 153 | {#if game.isGameStarted && currentPlayer && game.world?.players[game.world?.currentPlayer].name === game.playerName} |
113 | 154 | <div |
114 | | - class="absolute right-4 bottom-4 z-10 flex h-14 gap-1 rounded-md bg-base-200/40 p-1 backdrop-blur-md" |
| 155 | + class="absolute right-4 bottom-20 z-10 flex h-28 w-28 flex-col items-center justify-center rounded-md bg-base-200/40 p-3 backdrop-blur-md" |
| 156 | + > |
| 157 | + <div class="text-xs font-medium opacity-70">Dice</div> |
| 158 | + <div class="text-4xl font-bold"> |
| 159 | + {game.world?.currentDie !== 'None' ? game.world?.currentDie : '-'} |
| 160 | + </div> |
| 161 | + </div> |
| 162 | + <div |
| 163 | + class="absolute right-4 bottom-4 z-10 flex h-14 w-28 gap-1 rounded-md bg-base-200/40 p-1 backdrop-blur-md" |
115 | 164 | > |
116 | 165 | <button |
117 | | - class="btn h-full btn-primary" |
118 | | - onclick={() => { |
119 | | - console.log('End turn clicked'); |
| 166 | + class="btn h-full w-full btn-primary" |
| 167 | + onclick={async () => { |
| 168 | + if (game.playerName) { |
| 169 | + const action = new EndTurnAction(game.playerName); |
| 170 | + await game.sendAction(action); |
| 171 | + } |
120 | 172 | }} |
121 | 173 | > |
122 | 174 | End Turn |
|
209 | 261 | </form> |
210 | 262 | </div> |
211 | 263 | </dialog> |
| 264 | + |
| 265 | + <dialog class="modal" bind:this={stealModalElement}> |
| 266 | + <div class="modal-box"> |
| 267 | + <h2 class="mb-4 text-lg font-semibold">Steal from a Player</h2> |
| 268 | + <p class="mb-4 text-sm opacity-70"> |
| 269 | + Choose a player to steal a random resource from. These players have settlements or cities |
| 270 | + adjacent to the robber. |
| 271 | + </p> |
| 272 | + |
| 273 | + <div class="flex flex-col gap-2"> |
| 274 | + {#each stealablePlayers as player} |
| 275 | + <button |
| 276 | + class="btn btn-block justify-start" |
| 277 | + onclick={() => handleStealFrom(player.name)} |
| 278 | + > |
| 279 | + <div |
| 280 | + class="h-4 w-4 flex-shrink-0 rounded-full" |
| 281 | + style="background-color: #{player.color.toString(16).padStart(6, '0')}" |
| 282 | + ></div> |
| 283 | + <span class="flex-1 text-left">{player.name}</span> |
| 284 | + <span class="text-xs opacity-60"> |
| 285 | + {player.resources.wood + |
| 286 | + player.resources.clay + |
| 287 | + player.resources.stone + |
| 288 | + player.resources.grain + |
| 289 | + player.resources.wool} |
| 290 | + resources |
| 291 | + </span> |
| 292 | + </button> |
| 293 | + {/each} |
| 294 | + </div> |
| 295 | + |
| 296 | + <div class="modal-action"> |
| 297 | + <button class="btn" onclick={handleSkipStealing}>Skip Stealing</button> |
| 298 | + </div> |
| 299 | + </div> |
| 300 | + </dialog> |
212 | 301 | </div> |
0 commit comments