Dead code cleanups

This commit is contained in:
2026-06-15 07:46:32 -07:00
parent 045857a8fd
commit daeac91d07
21 changed files with 578 additions and 473 deletions
@@ -1,17 +1,27 @@
<script>
import { apiRequest } from '../../lib/api';
import { getCardDisplay, isJoker, displayName, playerName as lookupName, cardTooltip, obstacleTable } from '../../lib/cards';
import { getCardDisplay, isJoker, displayName, playerName as lookupName, cardTooltipHtml, obstacleTable } from '../../lib/cards';
import { tooltip } from '../../lib/tooltip';
import ObstacleItem from './ObstacleItem.svelte';
export let state;
let error = '';
let taxTargetId = '';
// Deep: call-a-challenge form (moved here from the old Deep Control Panel)
let showCreate = false;
let challengeTargetId = '';
let stakesSuccess = '';
let stakesFailure = '';
let selectedObstacles = {};
$: hand = state.player.hand_cards ? JSON.parse(state.player.hand_cards) : [];
$: openChallenges = (state.challenges || []).filter(c => c.status === 'open');
$: challengedObstacleIds = new Set(openChallenges.flatMap(c => JSON.parse(c.obstacle_ids || '[]')));
$: myTaxRequest = openChallenges.find(c => c.tax_state === 'requested' && c.tax_target_id === state.player.id) || null;
$: myPvpDefense = openChallenges.find(c => c.challenge_type === 'pvp' && c.acting_player_id === state.player.id) || null;
$: scenePirats = state.players.filter(p => p.role === 'pirat');
$: isDeep = state.player.role === 'deep';
function playerName(id) {
return lookupName(state.players, id);
@@ -53,6 +63,31 @@
taxTargetId = '';
}
}
async function createChallenge() {
const ids = Object.keys(selectedObstacles).filter(id => selectedObstacles[id]);
if (await post('challenge/create', {
target_player_id: challengeTargetId,
obstacle_ids: JSON.stringify(ids),
stakes_success: stakesSuccess,
stakes_failure: stakesFailure,
})) {
challengeTargetId = '';
stakesSuccess = '';
stakesFailure = '';
selectedObstacles = {};
showCreate = false;
}
}
async function endScene() {
error = '';
try {
await apiRequest(`/game/${state.game.id}/scene/end`, 'POST');
} catch(e) {
error = e.message;
}
}
</script>
{#if error}
@@ -89,17 +124,26 @@
{/if}
{#if ch.challenge_type === 'pvp'}
<p class="info-text" style="margin: 0.5rem 0 0 0;">
Temporary Obstacle: <strong title={cardTooltip(ch.temp_card, $obstacleTable)}>{getCardDisplay(ch.temp_card)}</strong>{playerName(ch.acting_player_id)} must answer it!
Temporary Obstacle: <strong use:tooltip={{ html: cardTooltipHtml(ch.temp_card, $obstacleTable) }}>{getCardDisplay(ch.temp_card)}</strong>{playerName(ch.acting_player_id)} must answer it!
</p>
{:else}
<p class="info-text" style="margin: 0.5rem 0 0 0;">
Applied Obstacles: {chObstacleIds.map(oid => state.obstacles.find(o => o.id === oid)?.title || '(discarded)').join(', ') || 'None left'}
— attempted by <strong>{playerName(ch.acting_player_id)}</strong>{ch.acting_player_id !== ch.target_player_id ? ` (took it over via ${ch.tax_type === 'gat' ? 'Gat' : 'Name'} Tax)` : ''}.
Other Pi-Rats may assist (assistants don't draw back).
Attempted by <strong>{playerName(ch.acting_player_id)}</strong>{ch.acting_player_id !== ch.target_player_id ? ` (took it over via ${ch.tax_type === 'gat' ? 'Gat' : 'Name'} Tax)` : ''}.
Drag a card onto an Obstacle below to play it. Other Pi-Rats may assist (assistants don't draw back).
</p>
<div class="challenge-obstacles">
{#each chObstacleIds as oid}
{@const obs = state.obstacles.find(o => o.id === oid)}
{#if obs}
<ObstacleItem {state} {obs} embedded />
{:else}
<p class="info-text" style="margin: 0;">An applied Obstacle was discarded.</p>
{/if}
{/each}
</div>
{/if}
{#if chPlays.length > 0}
<p class="info-text" style="margin: 0.25rem 0 0 0; font-size: 0.85rem;">
<p class="info-text" style="margin: 0.5rem 0 0 0; font-size: 0.85rem;">
Plays: {chPlays.map(p => `${p.player_name}: ${getCardDisplay(p.card)} ${p.success ? '✔' : '✘'}`).join(' · ')}
</p>
{/if}
@@ -140,10 +184,53 @@
<p style="margin: 0 0 0.5rem 0;"><strong>Defend yourself!</strong> Pick a card:</p>
<div class="challenge-actions">
{#each hand.filter(c => !isJoker(c)) as card}
<button class="btn btn-secondary" title={cardTooltip(card, $obstacleTable)} on:click={() => pvpDefend(ch.id, card)}>{getCardDisplay(card)}</button>
<button class="btn btn-secondary" use:tooltip={{ html: cardTooltipHtml(card, $obstacleTable) }} on:click={() => pvpDefend(ch.id, card)}>{getCardDisplay(card)}</button>
{/each}
</div>
</div>
{/if}
</div>
{/each}
<!-- Deep controls: call a Challenge, and end the scene when none is active -->
<!-- The Deep calls Challenges and ends the scene only when none is active. -->
{#if isDeep && openChallenges.length === 0}
<div class="deep-challenge-controls">
<button class="btn {showCreate ? 'btn-secondary' : 'btn-primary'} btn-full" on:click={() => showCreate = !showCreate}>
{showCreate ? '✕ Cancel' : '⚔️ Call a Challenge'}
</button>
{#if showCreate}
<div class="sheet-group margin-top">
<p class="info-text" style="font-size: 0.85rem;">Establish the stakes, pick a Pi-Rat, and apply one or more Obstacles. They play one card per Obstacle — beating one passes; each failure breeds a complication.</p>
<select class="select-field" bind:value={challengeTargetId} style="width: 100%; margin-bottom: 0.5rem;">
<option value="">Challenge which Pi-Rat?</option>
{#each scenePirats.filter(p => !p.is_dead) as p}
<option value={p.id}>{displayName(p)} (Rank {p.rank})</option>
{/each}
</select>
<div style="margin-bottom: 0.5rem;">
{#each state.obstacles as obs}
{@const taken = challengedObstacleIds.has(obs.id)}
<label class="checkbox-label" style="{taken ? 'opacity: 0.5;' : ''}">
<input type="checkbox" bind:checked={selectedObstacles[obs.id]} disabled={taken}>
<span>{obs.title}{taken ? ' (in a challenge)' : ''}</span>
</label>
{/each}
</div>
<input type="text" class="input-field" placeholder="On success (optional): what they win…" bind:value={stakesSuccess} style="width: 100%; margin-bottom: 0.5rem;">
<input type="text" class="input-field" placeholder="On failure (optional): what it costs them…" bind:value={stakesFailure} style="width: 100%; margin-bottom: 0.5rem;">
<button class="btn btn-primary btn-full" on:click={createChallenge}
disabled={!challengeTargetId || !Object.values(selectedObstacles).some(v => v)}>
Call Challenge
</button>
</div>
{/if}
<div class="scene-upkeep-controls text-center">
<p class="info-text">End the scene once every Pi-Rat has faced a Challenge and had a chance at an Objective, or the Obstacle List is empty.</p>
<button on:click={endScene} class="btn btn-danger btn-large btn-full glow-effect">
End Scene & Proceed to Ranking
</button>
</div>
</div>
{/if}