Streamline dead Pi-Rat rank voting
Dead and awaiting-recruit Pi-Rats can no longer be nominated to rank up (they'd reset to Rank 1 anyway). submit_rank_vote rejects them, and the upkeep dropdown only lists living, in-play crewmates via the shared eligible_vote_nominees helper. When a voter has no eligible nominees (e.g. a lone survivor whose only crewmates are the previous Deep and a dead rat) they now skip voting entirely and can ready up directly, instead of deadlocking — which is why dead players were previously left nominable. The roster shows such players as "No vote". Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,19 @@
|
||||
// Helpers
|
||||
$: hand = state.player.hand_cards ? JSON.parse(state.player.hand_cards) : [];
|
||||
|
||||
// A nominee must be another living, in-play Pi-Rat: not the voter, not a
|
||||
// previous-scene Deep, and not dead / awaiting-recruit (they'd reset to
|
||||
// Rank 1 anyway). Mirrors crud_upkeep.is_eligible_nominee.
|
||||
function eligibleNomineesFor(voter) {
|
||||
return state.players.filter(p =>
|
||||
p.id !== voter.id && p.role !== 'deep' && !p.is_dead && !p.needs_reroll
|
||||
);
|
||||
}
|
||||
$: myNominees = eligibleNomineesFor(state.player);
|
||||
$: hasVoted = !!state.votes.find(v => v.voter_player_id === state.player.id);
|
||||
// With no eligible crewmates, the voter skips voting entirely (no deadlock).
|
||||
$: mustVote = myNominees.length > 0;
|
||||
|
||||
// Drag & Drop State
|
||||
let keepCards = [];
|
||||
let discardCards = [];
|
||||
@@ -191,19 +204,21 @@
|
||||
<div class="voted-confirmation-box glass-panel">
|
||||
<p class="success-text">✔️ Voting complete! Results tallied.</p>
|
||||
</div>
|
||||
{:else if state.votes.find(v => v.voter_player_id === state.player.id)}
|
||||
{:else if hasVoted}
|
||||
<div class="voted-confirmation-box glass-panel">
|
||||
<p class="success-text">✔️ Vote submitted! Waiting for other players to submit their nominations.</p>
|
||||
</div>
|
||||
{:else if !mustVote}
|
||||
<div class="voted-confirmation-box glass-panel">
|
||||
<p class="info-text">No living crewmates left to nominate — you skip voting this round. Go ahead and ready up.</p>
|
||||
</div>
|
||||
{:else}
|
||||
<form on:submit|preventDefault={submitVote} class="vote-form inline-form">
|
||||
<div class="form-group inline-group">
|
||||
<select class="select-field" bind:value={selectedVoteId} required>
|
||||
<option value="">Nominate crewmate...</option>
|
||||
{#each state.players as p}
|
||||
{#if p.id !== state.player.id && p.role !== 'deep'}
|
||||
<option value={p.id}>{displayName(p)} (Rank {p.rank})</option>
|
||||
{/if}
|
||||
{#each myNominees as p}
|
||||
<option value={p.id}>{displayName(p)} (Rank {p.rank})</option>
|
||||
{/each}
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary" disabled={voting || !selectedVoteId}>Cast Nomination</button>
|
||||
@@ -218,9 +233,10 @@
|
||||
<div class="player-chips list-chips" id="voting-roster">
|
||||
{#each state.players as p}
|
||||
{@const voted = state.votes.some(v => v.voter_player_id === p.id)}
|
||||
<div class="player-chip {voted ? 'voted-chip' : 'not-voted-chip'}">
|
||||
{@const skips = eligibleNomineesFor(p).length === 0}
|
||||
<div class="player-chip {voted || skips ? 'voted-chip' : 'not-voted-chip'}">
|
||||
<span class="name">{displayName(p)}</span>
|
||||
<span class="status-badge">{voted ? 'Done' : 'Voting...'}</span>
|
||||
<span class="status-badge">{voted ? 'Done' : skips ? 'No vote' : 'Voting...'}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -325,7 +341,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
{#if !state.votes.find(v => v.voter_player_id === state.player.id)}
|
||||
{#if mustVote && !hasVoted}
|
||||
<p class="info-text gold-text" style="margin-bottom:0.5rem;">⚠️ You must nominate a crewmate above before you can ready up.</p>
|
||||
<button class="btn btn-primary btn-large" disabled>Ready for Next Scene</button>
|
||||
{:else}
|
||||
|
||||
Reference in New Issue
Block a user