fix: die animation
This commit is contained in:
parent
cf289fa779
commit
7a760980ba
5 changed files with 26 additions and 100 deletions
|
|
@ -1,9 +1,9 @@
|
|||
pub mod platform;
|
||||
pub mod session;
|
||||
pub mod traits;
|
||||
|
||||
mod client;
|
||||
mod host;
|
||||
mod platform;
|
||||
mod protocol;
|
||||
|
||||
pub use session::{ConnectError, GameSession, RoomConfig, RoomRole, SessionEvent};
|
||||
|
|
|
|||
|
|
@ -839,8 +839,8 @@ a:hover { text-decoration: underline; }
|
|||
}
|
||||
|
||||
.peg-hole {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid rgba(138,106,40,0.45);
|
||||
background: rgba(0,0,0,0.06);
|
||||
|
|
@ -848,12 +848,6 @@ a:hover { text-decoration: underline; }
|
|||
transition: background 0.3s ease-out, border-color 0.3s, box-shadow 0.3s;
|
||||
}
|
||||
|
||||
.peg-hole.filled {
|
||||
background: var(--ui-gold);
|
||||
border-color: var(--ui-gold-dark);
|
||||
box-shadow: 0 0 4px rgba(200,164,72,0.6);
|
||||
}
|
||||
|
||||
.bredouille-badge {
|
||||
font-size: 0.62rem;
|
||||
font-weight: 500;
|
||||
|
|
@ -868,20 +862,7 @@ a:hover { text-decoration: underline; }
|
|||
margin: 0.4em;
|
||||
}
|
||||
|
||||
/* ── Merged scoreboard (both players, above board) ──────────────────── */
|
||||
.merged-score-panel {
|
||||
background: var(--ui-parchment);
|
||||
border-radius: 5px;
|
||||
padding: 0.5rem 1.25rem 0.45rem;
|
||||
font-size: 0.88rem;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.25);
|
||||
width: 100%;
|
||||
border-top: 2px solid var(--ui-gold-dark);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
}
|
||||
|
||||
/* ── scoreboard (both players, above board) ──────────────────── */
|
||||
.score-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -964,33 +945,6 @@ a:hover { text-decoration: underline; }
|
|||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
/* ── Hole pegs — larger and coloured (me = green, opp = red) ─────────── */
|
||||
.merged-score-panel .peg-track {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.merged-score-panel .peg-hole {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid rgba(138,106,40,0.3);
|
||||
background: rgba(0,0,0,0.06);
|
||||
flex-shrink: 0;
|
||||
transition: background 0.3s ease-out, border-color 0.3s, box-shadow 0.3s;
|
||||
}
|
||||
|
||||
.merged-score-panel .peg-hole.filled {
|
||||
background: #5aab38;
|
||||
border-color: #3a7828;
|
||||
box-shadow: 0 0 5px rgba(90,171,56,0.55);
|
||||
}
|
||||
|
||||
.merged-score-panel .peg-hole.peg-opp.filled {
|
||||
background: #c05030;
|
||||
border-color: #8a3018;
|
||||
box-shadow: 0 0 5px rgba(192,80,48,0.55);
|
||||
}
|
||||
|
||||
/* Peg pop-in animation when a new hole is scored */
|
||||
@keyframes peg-pop {
|
||||
0% { transform: scale(0.15); opacity: 0; }
|
||||
|
|
@ -999,10 +953,6 @@ a:hover { text-decoration: underline; }
|
|||
100% { transform: scale(1.0); opacity: 1; }
|
||||
}
|
||||
|
||||
.merged-score-panel .peg-hole.peg-new {
|
||||
animation: peg-pop 0.52s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
|
||||
}
|
||||
|
||||
/* Thin separator between the two player rows */
|
||||
.score-row-sep {
|
||||
height: 1px;
|
||||
|
|
@ -1010,31 +960,6 @@ a:hover { text-decoration: underline; }
|
|||
margin: 0.05rem 0;
|
||||
}
|
||||
|
||||
/* ── Non-blocking hole flash (replaces old toast) ───────────────────── */
|
||||
@keyframes hole-flash-in-out {
|
||||
0% { opacity: 0; transform: translateY(-3px); }
|
||||
14% { opacity: 1; transform: translateY(0); }
|
||||
65% { opacity: 1; }
|
||||
100% { opacity: 0; transform: translateY(2px); }
|
||||
}
|
||||
|
||||
.hole-flash {
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.88rem;
|
||||
font-weight: 600;
|
||||
color: var(--ui-green-accent);
|
||||
letter-spacing: 0.05em;
|
||||
animation: hole-flash-in-out 2.5s ease-out forwards;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hole-flash.hole-flash-bredouille {
|
||||
color: var(--ui-gold-dark);
|
||||
}
|
||||
|
||||
/* ── Game bottom strip — status, hints, buttons on cream ────────────── */
|
||||
.game-bottom-strip {
|
||||
background: var(--ui-parchment);
|
||||
|
|
@ -2365,7 +2290,6 @@ a:hover { text-decoration: underline; }
|
|||
|
||||
/* Strip peg overrides */
|
||||
.players-strip .peg-track { gap: 3px; direction: ltr; }
|
||||
.players-strip .peg-hole { width: 12px; height: 12px; }
|
||||
.players-strip .peg-hole.filled {
|
||||
background: #5aab38; border-color: #3a7828;
|
||||
box-shadow: 0 0 5px rgba(90,171,56,0.55);
|
||||
|
|
|
|||
|
|
@ -126,7 +126,13 @@ pub fn GameScreen(state: GameUiState) -> impl IntoView {
|
|||
}
|
||||
|
||||
let dice = vs.dice;
|
||||
let show_dice = dice != (0, 0);
|
||||
// Hide dice during RollDice/RollWaiting: the stored dice values are stale from the
|
||||
// previous turn and showing them would trigger the tumble animation incorrectly.
|
||||
let show_dice = dice != (0, 0)
|
||||
&& !matches!(
|
||||
vs.turn_stage,
|
||||
SerTurnStage::RollDice | SerTurnStage::RollWaiting
|
||||
);
|
||||
|
||||
// ── Button senders ─────────────────────────────────────────────────────────
|
||||
let cmd_tx_go = cmd_tx.clone();
|
||||
|
|
|
|||
|
|
@ -183,19 +183,6 @@ pub fn MergedScorePanel(
|
|||
})}
|
||||
</div>
|
||||
</div>
|
||||
{(my_holes_gained > 0).then(|| {
|
||||
let label = if my_bredouille {
|
||||
format!("Trou {} · ×2 bredouille", my_holes)
|
||||
} else {
|
||||
format!("Trou {}", my_holes)
|
||||
};
|
||||
view! {
|
||||
<div class="hole-flash"
|
||||
class:hole-flash-bredouille=my_bredouille>
|
||||
{label}
|
||||
</div>
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,8 @@ use backbone_lib::traits::{BackEndArchitecture, BackendCommand};
|
|||
use crate::app::{GameUiState, NetCommand, PauseReason, Screen};
|
||||
use crate::game::trictrac::backend::TrictracBackend;
|
||||
use crate::game::trictrac::bot_local::bot_decide;
|
||||
use crate::game::trictrac::types::{
|
||||
JanEntry, ScoredEvent, SerStage, SerTurnStage, ViewState,
|
||||
};
|
||||
use crate::game::trictrac::types::{JanEntry, ScoredEvent, SerStage, SerTurnStage, ViewState};
|
||||
use backbone_lib::platform::sleep_ms;
|
||||
use trictrac_store::CheckerMove;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
|
@ -124,6 +123,7 @@ async fn run_local_bot_game_loop(
|
|||
match bot_decide(backend.get_game(), pgr.as_ref()) {
|
||||
None => break,
|
||||
Some(action) => {
|
||||
sleep_ms(500).await;
|
||||
backend.inform_rpc(1, action);
|
||||
for cmd in backend.drain_commands() {
|
||||
if let BackendCommand::Delta(delta) = cmd {
|
||||
|
|
@ -189,7 +189,11 @@ pub fn compute_last_moves(
|
|||
}
|
||||
|
||||
/// Computes a scoring event for `player_id` by comparing the previous and next ViewState.
|
||||
pub fn compute_scored_event(prev: &ViewState, next: &ViewState, player_id: u16) -> Option<ScoredEvent> {
|
||||
pub fn compute_scored_event(
|
||||
prev: &ViewState,
|
||||
next: &ViewState,
|
||||
player_id: u16,
|
||||
) -> Option<ScoredEvent> {
|
||||
let prev_score = &prev.scores[player_id as usize];
|
||||
let next_score = &next.scores[player_id as usize];
|
||||
|
||||
|
|
@ -275,7 +279,11 @@ pub fn push_or_show(
|
|||
|
||||
/// Compares the previous and next ViewState to decide whether the transition
|
||||
/// warrants a confirmation pause.
|
||||
pub fn infer_pause_reason(prev: &ViewState, next: &ViewState, player_id: u16) -> Option<PauseReason> {
|
||||
pub fn infer_pause_reason(
|
||||
prev: &ViewState,
|
||||
next: &ViewState,
|
||||
player_id: u16,
|
||||
) -> Option<PauseReason> {
|
||||
let opponent_id = 1 - player_id;
|
||||
|
||||
if next.stage == SerStage::PreGameRoll {
|
||||
|
|
@ -297,7 +305,8 @@ pub fn infer_pause_reason(prev: &ViewState, next: &ViewState, player_id: u16) ->
|
|||
if next.dice != prev.dice {
|
||||
return Some(PauseReason::AfterOpponentRoll);
|
||||
}
|
||||
if prev.turn_stage == SerTurnStage::HoldOrGoChoice && next.turn_stage == SerTurnStage::Move {
|
||||
if prev.turn_stage == SerTurnStage::HoldOrGoChoice && next.turn_stage == SerTurnStage::Move
|
||||
{
|
||||
return Some(PauseReason::AfterOpponentGo);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue