fix(engine): auto-complete delay + right-click shake on no legal move
Closes #80: add AUTO_COMPLETE_INITIAL_DELAY (0.75 s) before the first auto-complete move fires. Previously cooldown was 0.0, causing the sequence to hijack the board the same frame the condition was met. Closes #81: fire MoveRejectedEvent in radial_open_on_right_click when the right-clicked card has no legal destinations, so the shake animation and invalid-move sound play consistently on desktop/web. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -52,7 +52,7 @@ use solitaire_core::game_state::GameState;
|
||||
use solitaire_core::pile::PileType;
|
||||
|
||||
use crate::card_plugin::TABLEAU_FACEDOWN_FAN_FRAC;
|
||||
use crate::events::MoveRequestEvent;
|
||||
use crate::events::{MoveRejectedEvent, MoveRequestEvent};
|
||||
use crate::layout::{Layout, LayoutResource, TABLEAU_FAN_FRAC};
|
||||
use crate::pause_plugin::PausedResource;
|
||||
use crate::resources::{DragState, GameStateResource};
|
||||
@@ -396,9 +396,10 @@ fn cursor_world(
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// On `MouseButton::Right` `just_pressed`, attempts to open the radial
|
||||
/// menu over the card the cursor is on. Skips when a left-mouse drag is
|
||||
/// in progress, when the game is paused, or when the clicked card has no
|
||||
/// legal destinations.
|
||||
/// menu over the card the cursor is on. When the cursor is on a face-up
|
||||
/// card but no legal destinations exist, fires `MoveRejectedEvent` so the
|
||||
/// shake animation and invalid-move sound play. Skips silently when no
|
||||
/// card is under the cursor, when a drag is in progress, or when paused.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn radial_open_on_right_click(
|
||||
buttons: Option<Res<ButtonInput<MouseButton>>>,
|
||||
@@ -410,6 +411,7 @@ fn radial_open_on_right_click(
|
||||
layout: Option<Res<LayoutResource>>,
|
||||
game: Option<Res<GameStateResource>>,
|
||||
mut state: ResMut<RightClickRadialState>,
|
||||
mut rejected: MessageWriter<MoveRejectedEvent>,
|
||||
) {
|
||||
if paused.is_some_and(|p| p.0) {
|
||||
return;
|
||||
@@ -438,6 +440,12 @@ fn radial_open_on_right_click(
|
||||
// cards and the highlight tint shows the same set the radial offers.
|
||||
let dests = legal_destinations_for_card(&card, &source_pile, &game.0);
|
||||
if dests.is_empty() {
|
||||
// No legal destinations — shake the source pile as feedback.
|
||||
rejected.write(MoveRejectedEvent {
|
||||
from: source_pile.clone(),
|
||||
to: source_pile,
|
||||
count: 1,
|
||||
});
|
||||
return;
|
||||
}
|
||||
let legal_destinations = build_radial_destinations(world, dests);
|
||||
@@ -745,6 +753,7 @@ mod tests {
|
||||
let mut app = App::new();
|
||||
app.add_plugins(MinimalPlugins);
|
||||
app.add_message::<MoveRequestEvent>();
|
||||
app.add_message::<MoveRejectedEvent>();
|
||||
app.init_resource::<DragState>();
|
||||
app.init_resource::<ButtonInput<MouseButton>>();
|
||||
app.init_resource::<ButtonInput<KeyCode>>();
|
||||
|
||||
Reference in New Issue
Block a user