Display rank-up vote winner on the post-voting upkeep screen

The post-voting (deep_upkeep) screen now names who won the rank-up vote
and their new rank, instead of just "Voting complete!". Adds
Game.last_rank_up_player_id (+ migration), set when votes are tallied
and cleared when the next scene begins; falls back to a "no one ranked
up" message when there were no votes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 09:23:18 -07:00
parent b37b655118
commit 5a8919d967
5 changed files with 53 additions and 5 deletions

View File

@@ -10,7 +10,7 @@
- [x] Visual feedback confirming hand refresh in Deep Upkeep - [x] Visual feedback confirming hand refresh in Deep Upkeep
- [ ] On the screen after pirat rank up voting, it should display the winner - [x] On the screen after pirat rank up voting, it should display the winner
- [ ] When Deep issues a challenge, stakes should be two fields, success and failure - [ ] When Deep issues a challenge, stakes should be two fields, success and failure

View File

@@ -13,6 +13,10 @@
// Helpers // Helpers
$: hand = state.player.hand_cards ? JSON.parse(state.player.hand_cards) : []; $: hand = state.player.hand_cards ? JSON.parse(state.player.hand_cards) : [];
// Winner of the rank-up vote, surfaced on the post-voting (deep_upkeep) screen.
$: rankUpWinner = state.game.last_rank_up_player_id
? state.players.find(p => p.id === state.game.last_rank_up_player_id)
: null;
// A nominee must be another living, in-play Pi-Rat: not the voter, not a // 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 // previous-scene Deep, and not dead / awaiting-recruit (they'd reset to
@@ -202,7 +206,11 @@
{#if state.game.phase === 'deep_upkeep'} {#if state.game.phase === 'deep_upkeep'}
<div class="voted-confirmation-box glass-panel"> <div class="voted-confirmation-box glass-panel">
<p class="success-text">✔️ Voting complete! Results tallied.</p> {#if rankUpWinner}
<p class="success-text">🏆 {displayName(rankUpWinner)} won the vote and ranked up to Rank {rankUpWinner.rank}!</p>
{:else}
<p class="success-text">✔️ Voting complete! No one ranked up this time.</p>
{/if}
</div> </div>
{:else if hasVoted} {:else if hasVoted}
<div class="voted-confirmation-box glass-panel"> <div class="voted-confirmation-box glass-panel">

View File

@@ -83,7 +83,11 @@ def process_between_scenes_votes(db: Session, game: Game):
Then sets players who were Deep to discard and redraw. Then sets players who were Deep to discard and redraw.
""" """
votes = game.votes votes = game.votes
# Reset the previous scene's winner before tallying this one.
game.last_rank_up_player_id = None
db.add(game)
if not votes: if not votes:
db.commit()
return return
# Count votes # Count votes
@@ -101,6 +105,8 @@ def process_between_scenes_votes(db: Session, game: Game):
winner = get_player(db, winner_id) winner = get_player(db, winner_id)
if winner: if winner:
change_player_rank(db, game, winner, 1) change_player_rank(db, game, winner, 1)
game.last_rank_up_player_id = winner.id
db.add(game)
add_game_event(db, game.id, f"{winner.name} was voted to rank up! They are now Rank {winner.rank}.", kind="rank") add_game_event(db, game.id, f"{winner.name} was voted to rank up! They are now Rank {winner.rank}.", kind="rank")
# Clear all votes # Clear all votes
@@ -112,6 +118,7 @@ def begin_next_scene_setup(db: Session, game: Game):
"""Advances to the next scene's setup: bump the scene counter and clear roles.""" """Advances to the next scene's setup: bump the scene counter and clear roles."""
game.current_scene_number += 1 game.current_scene_number += 1
game.phase = "scene_setup" game.phase = "scene_setup"
game.last_rank_up_player_id = None # Stale once we leave the post-voting screen
for p in game.players: for p in game.players:
p.role = None p.role = None
p.is_ready = False p.is_ready = False

View File

@@ -0,0 +1,32 @@
"""add last_rank_up_player_id to game
Revision ID: 3bbf6812c261
Revises: 19ee9ae5ca0e
Create Date: 2026-06-14 09:21:11.037751
"""
from alembic import op
import sqlalchemy as sa
import sqlmodel
revision = '3bbf6812c261'
down_revision = '19ee9ae5ca0e'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('game', schema=None) as batch_op:
batch_op.add_column(sa.Column('last_rank_up_player_id', sqlmodel.sql.sqltypes.AutoString(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('game', schema=None) as batch_op:
batch_op.drop_column('last_rank_up_player_id')
# ### end Alembic commands ###

View File

@@ -19,6 +19,7 @@ class Game(SQLModel, table=True):
completed_crew_3: bool = Field(default=False) # Commit Piracy completed_crew_3: bool = Field(default=False) # Commit Piracy
captain_player_id: Optional[str] = Field(default=None) # Persistent captaincy; set once the crew controls a ship captain_player_id: Optional[str] = Field(default=None) # Persistent captaincy; set once the crew controls a ship
teaching_deep_player_id: Optional[str] = Field(default=None) # Teaching mode: an admin who seeds themselves as the forced Rank-3 Deep for the first scene (set in the lobby, honored when starting ranks are rolled) teaching_deep_player_id: Optional[str] = Field(default=None) # Teaching mode: an admin who seeds themselves as the forced Rank-3 Deep for the first scene (set in the lobby, honored when starting ranks are rolled)
last_rank_up_player_id: Optional[str] = Field(default=None) # Winner of the most recent rank-up vote, shown on the post-voting upkeep screen; cleared when the next scene begins
players: List["Player"] = Relationship(back_populates="game", cascade_delete=True) players: List["Player"] = Relationship(back_populates="game", cascade_delete=True)
obstacles: List["Obstacle"] = Relationship(back_populates="game", cascade_delete=True) obstacles: List["Obstacle"] = Relationship(back_populates="game", cascade_delete=True)