fix(client_web): show opponent's dice animation
This commit is contained in:
parent
72c5e16ea3
commit
68ecafd0dc
3 changed files with 39 additions and 25 deletions
|
|
@ -450,8 +450,8 @@ body {
|
||||||
strip after a few seconds, and reveal fully on hover. */
|
strip after a few seconds, and reveal fully on hover. */
|
||||||
.side-panel {
|
.side-panel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: -8px;
|
||||||
top: 0;
|
top: 10px;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -645,8 +645,7 @@ body {
|
||||||
/* ── Wrapper: handles slide-in → peek → reveal lifecycle ──────────────
|
/* ── Wrapper: handles slide-in → peek → reveal lifecycle ──────────────
|
||||||
The wrapper starts off-screen right (translateX(100%)), slides in on
|
The wrapper starts off-screen right (translateX(100%)), slides in on
|
||||||
mount via animation, then Leptos adds .peeked after 3.4s to slide it
|
mount via animation, then Leptos adds .peeked after 3.4s to slide it
|
||||||
back to a 28px peek strip. First hover adds .revealed for permanent
|
back to a 28px peek strip. */
|
||||||
visibility. pointer-events: auto overrides the parent's none. */
|
|
||||||
@keyframes scoring-panel-enter {
|
@keyframes scoring-panel-enter {
|
||||||
from { transform: translateX(100%); }
|
from { transform: translateX(100%); }
|
||||||
to { transform: translateX(0); }
|
to { transform: translateX(0); }
|
||||||
|
|
@ -662,7 +661,7 @@ body {
|
||||||
|
|
||||||
/* Peeked: slide right by the full panel width so the board is 100% clear.
|
/* Peeked: slide right by the full panel width so the board is 100% clear.
|
||||||
The panel's left portion stays visible in whatever free space exists to
|
The panel's left portion stays visible in whatever free space exists to
|
||||||
the right of the board (depends on viewport width). */
|
the right of the board. */
|
||||||
.scoring-panel-wrapper.peeked {
|
.scoring-panel-wrapper.peeked {
|
||||||
transform: translateX(100%);
|
transform: translateX(100%);
|
||||||
}
|
}
|
||||||
|
|
@ -868,8 +867,8 @@ body {
|
||||||
.board-quarter .field.zone-retour:nth-child(odd) { --fc: #6a2810; }
|
.board-quarter .field.zone-retour:nth-child(odd) { --fc: #6a2810; }
|
||||||
.board-quarter .field.zone-retour:nth-child(even) { --fc: #f2dfa0; }
|
.board-quarter .field.zone-retour:nth-child(even) { --fc: #f2dfa0; }
|
||||||
|
|
||||||
/* ── Rest corner (§3) — before .clickable so green wins when interactive ── */
|
/* ── Rest corner — before .clickable so green wins when interactive ── */
|
||||||
.field.corner { --fc: var(--field-corner) !important; }
|
/* .field.corner { --fc: var(--field-corner) !important; } */
|
||||||
|
|
||||||
/* Crown glyph sits behind checkers (z-index:-1) so it shows only on empty corners */
|
/* Crown glyph sits behind checkers (z-index:-1) so it shows only on empty corners */
|
||||||
.field.corner::after {
|
.field.corner::after {
|
||||||
|
|
|
||||||
|
|
@ -256,6 +256,7 @@ pub fn Board(
|
||||||
/// Whether we're in the move stage (determines used/unused die appearance).
|
/// Whether we're in the move stage (determines used/unused die appearance).
|
||||||
#[prop(default = false)]
|
#[prop(default = false)]
|
||||||
bar_is_move: bool,
|
bar_is_move: bool,
|
||||||
|
#[prop(default = false)] is_my_turn: bool,
|
||||||
/// Whether the dice are a double (golden glow).
|
/// Whether the dice are a double (golden glow).
|
||||||
#[prop(default = false)]
|
#[prop(default = false)]
|
||||||
bar_is_double: bool,
|
bar_is_double: bool,
|
||||||
|
|
@ -344,6 +345,9 @@ pub fn Board(
|
||||||
cls.push_str(" corner-available");
|
cls.push_str(" corner-available");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if is_rest_corner(field_num, !is_white) {
|
||||||
|
cls.push_str(" corner");
|
||||||
|
}
|
||||||
if all_in_exit && exit_field_test(field_num) {
|
if all_in_exit && exit_field_test(field_num) {
|
||||||
cls.push_str(" exit-eligible");
|
cls.push_str(" exit-eligible");
|
||||||
}
|
}
|
||||||
|
|
@ -501,8 +505,10 @@ pub fn Board(
|
||||||
let staged = staged_moves.get();
|
let staged = staged_moves.get();
|
||||||
let (u0, u1) = if bar_is_move {
|
let (u0, u1) = if bar_is_move {
|
||||||
bar_matched_dice_used(&staged, dice_vals)
|
bar_matched_dice_used(&staged, dice_vals)
|
||||||
} else {
|
} else if is_my_turn {
|
||||||
(true, true)
|
(true, true)
|
||||||
|
} else {
|
||||||
|
(false, false)
|
||||||
};
|
};
|
||||||
let used = if die_idx == 0 { u0 } else { u1 };
|
let used = if die_idx == 0 { u0 } else { u1 };
|
||||||
view! { <Die value=die_val used=used is_double=bar_is_double /> }
|
view! { <Die value=die_val used=used is_double=bar_is_double /> }
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ pub fn GameScreen(state: GameUiState) -> impl IntoView {
|
||||||
|
|
||||||
let cmd_tx = use_context::<UnboundedSender<NetCommand>>()
|
let cmd_tx = use_context::<UnboundedSender<NetCommand>>()
|
||||||
.expect("UnboundedSender<NetCommand> not found in context");
|
.expect("UnboundedSender<NetCommand> not found in context");
|
||||||
let pending = use_context::<RwSignal<VecDeque<GameUiState>>>()
|
let pending =
|
||||||
.expect("pending not found in context");
|
use_context::<RwSignal<VecDeque<GameUiState>>>().expect("pending not found in context");
|
||||||
let cmd_tx_effect = cmd_tx.clone();
|
let cmd_tx_effect = cmd_tx.clone();
|
||||||
Effect::new(move |_| {
|
Effect::new(move |_| {
|
||||||
let moves = staged_moves.get();
|
let moves = staged_moves.get();
|
||||||
|
|
@ -68,7 +68,9 @@ pub fn GameScreen(state: GameUiState) -> impl IntoView {
|
||||||
if show_roll && !waiting_for_confirm {
|
if show_roll && !waiting_for_confirm {
|
||||||
let cmd_tx_auto = cmd_tx.clone();
|
let cmd_tx_auto = cmd_tx.clone();
|
||||||
Effect::new(move |_| {
|
Effect::new(move |_| {
|
||||||
cmd_tx_auto.unbounded_send(NetCommand::Action(PlayerAction::Roll)).ok();
|
cmd_tx_auto
|
||||||
|
.unbounded_send(NetCommand::Action(PlayerAction::Roll))
|
||||||
|
.ok();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,13 +94,19 @@ pub fn GameScreen(state: GameUiState) -> impl IntoView {
|
||||||
let mut store_board = StoreBoard::new();
|
let mut store_board = StoreBoard::new();
|
||||||
store_board.set_positions(&Color::White, vs.board);
|
store_board.set_positions(&Color::White, vs.board);
|
||||||
let store_dice = StoreDice { values: dice };
|
let store_dice = StoreDice { values: dice };
|
||||||
let color = if player_id == 0 { Color::White } else { Color::Black };
|
let color = if player_id == 0 {
|
||||||
|
Color::White
|
||||||
|
} else {
|
||||||
|
Color::Black
|
||||||
|
};
|
||||||
let rules = MoveRules::new(&color, &store_board, store_dice);
|
let rules = MoveRules::new(&color, &store_board, store_dice);
|
||||||
let raw = rules.get_possible_moves_sequences(true, vec![]);
|
let raw = rules.get_possible_moves_sequences(true, vec![]);
|
||||||
if player_id == 0 {
|
if player_id == 0 {
|
||||||
raw
|
raw
|
||||||
} else {
|
} else {
|
||||||
raw.into_iter().map(|(m1, m2)| (m1.mirror(), m2.mirror())).collect()
|
raw.into_iter()
|
||||||
|
.map(|(m1, m2)| (m1.mirror(), m2.mirror()))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
|
|
@ -113,7 +121,8 @@ pub fn GameScreen(state: GameUiState) -> impl IntoView {
|
||||||
// ── Scoring notifications ──────────────────────────────────────────────────
|
// ── Scoring notifications ──────────────────────────────────────────────────
|
||||||
let my_scored_event = state.my_scored_event.clone();
|
let my_scored_event = state.my_scored_event.clone();
|
||||||
let opp_scored_event = state.opp_scored_event.clone();
|
let opp_scored_event = state.opp_scored_event.clone();
|
||||||
let hole_toast_info = my_scored_event.as_ref()
|
let hole_toast_info = my_scored_event
|
||||||
|
.as_ref()
|
||||||
.filter(|e| e.holes_gained > 0)
|
.filter(|e| e.holes_gained > 0)
|
||||||
.map(|e| (e.holes_total, e.bredouille));
|
.map(|e| (e.holes_total, e.bredouille));
|
||||||
|
|
||||||
|
|
@ -123,14 +132,16 @@ pub fn GameScreen(state: GameUiState) -> impl IntoView {
|
||||||
|
|
||||||
// §6e — fields where a battue (hit) was scored; ripple animation shown there.
|
// §6e — fields where a battue (hit) was scored; ripple animation shown there.
|
||||||
let hit_fields: Vec<u8> = {
|
let hit_fields: Vec<u8> = {
|
||||||
let is_hit_jan = |jan: &Jan| matches!(
|
let is_hit_jan = |jan: &Jan| {
|
||||||
jan,
|
matches!(
|
||||||
Jan::TrueHitSmallJan
|
jan,
|
||||||
| Jan::TrueHitBigJan
|
Jan::TrueHitSmallJan
|
||||||
| Jan::TrueHitOpponentCorner
|
| Jan::TrueHitBigJan
|
||||||
| Jan::FalseHitSmallJan
|
| Jan::TrueHitOpponentCorner
|
||||||
| Jan::FalseHitBigJan
|
| Jan::FalseHitSmallJan
|
||||||
);
|
| Jan::FalseHitBigJan
|
||||||
|
)
|
||||||
|
};
|
||||||
let mut fields: Vec<u8> = vec![];
|
let mut fields: Vec<u8> = vec![];
|
||||||
for event_opt in [&my_scored_event, &opp_scored_event] {
|
for event_opt in [&my_scored_event, &opp_scored_event] {
|
||||||
if let Some(event) = event_opt {
|
if let Some(event) = event_opt {
|
||||||
|
|
@ -148,9 +159,6 @@ pub fn GameScreen(state: GameUiState) -> impl IntoView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !fields.is_empty() {
|
|
||||||
leptos::logging::log!("[6e] hit_fields = {:?}", fields);
|
|
||||||
}
|
|
||||||
fields
|
fields
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -248,6 +256,7 @@ pub fn GameScreen(state: GameUiState) -> impl IntoView {
|
||||||
valid_sequences=valid_sequences
|
valid_sequences=valid_sequences
|
||||||
bar_dice=show_dice.then_some(dice)
|
bar_dice=show_dice.then_some(dice)
|
||||||
bar_is_move=is_move_stage
|
bar_is_move=is_move_stage
|
||||||
|
is_my_turn=is_my_turn
|
||||||
bar_is_double=is_double_dice
|
bar_is_double=is_double_dice
|
||||||
last_moves=last_moves
|
last_moves=last_moves
|
||||||
hit_fields=hit_fields
|
hit_fields=hit_fields
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue