feat(engine,server): XP toast on win + display_name max-length validation
ProgressPlugin now fires XpAwardedEvent on every win. AnimationPlugin shows a "+N XP" toast so players see XP feedback immediately after winning. Server leaderboard opt-in endpoint also now validates that display_name is at most 32 characters. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ use crate::auto_complete_plugin::AutoCompleteState;
|
||||
use crate::card_plugin::CardEntity;
|
||||
use crate::challenge_plugin::ChallengeAdvancedEvent;
|
||||
use crate::daily_challenge_plugin::{DailyChallengeCompletedEvent, DailyGoalAnnouncementEvent};
|
||||
use crate::events::{InfoToastEvent, NewGameConfirmEvent};
|
||||
use crate::events::{InfoToastEvent, NewGameConfirmEvent, XpAwardedEvent};
|
||||
use crate::events::{AchievementUnlockedEvent, GameWonEvent};
|
||||
use crate::game_plugin::GameMutation;
|
||||
use crate::layout::LayoutResource;
|
||||
@@ -94,6 +94,7 @@ impl Plugin for AnimationPlugin {
|
||||
.add_event::<SettingsChangedEvent>()
|
||||
.add_event::<NewGameConfirmEvent>()
|
||||
.add_event::<InfoToastEvent>()
|
||||
.add_event::<XpAwardedEvent>()
|
||||
.init_resource::<EffectiveSlideDuration>()
|
||||
.add_systems(Startup, init_slide_duration)
|
||||
.add_systems(
|
||||
@@ -113,6 +114,7 @@ impl Plugin for AnimationPlugin {
|
||||
handle_auto_complete_toast,
|
||||
handle_new_game_confirm_toast,
|
||||
handle_info_toast,
|
||||
handle_xp_awarded_toast,
|
||||
tick_toasts,
|
||||
)
|
||||
.after(GameMutation),
|
||||
@@ -330,6 +332,12 @@ fn handle_info_toast(mut commands: Commands, mut events: EventReader<InfoToastEv
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_xp_awarded_toast(mut commands: Commands, mut events: EventReader<XpAwardedEvent>) {
|
||||
for ev in events.read() {
|
||||
spawn_toast(&mut commands, format!("+{} XP", ev.amount), 3.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn tick_toasts(
|
||||
mut commands: Commands,
|
||||
time: Res<Time>,
|
||||
|
||||
@@ -80,3 +80,10 @@ pub struct NewGameConfirmEvent;
|
||||
/// a short string to the player, e.g. "Locked — reach level 5".
|
||||
#[derive(Event, Debug, Clone)]
|
||||
pub struct InfoToastEvent(pub String);
|
||||
|
||||
/// Fired by `ProgressPlugin` immediately after awarding XP for a win so the
|
||||
/// animation layer can display a "+N XP" toast alongside the win cascade.
|
||||
#[derive(Event, Debug, Clone, Copy)]
|
||||
pub struct XpAwardedEvent {
|
||||
pub amount: u64,
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ pub use card_plugin::{CardEntity, CardLabel, CardPlugin};
|
||||
pub use events::{
|
||||
AchievementUnlockedEvent, CardFlippedEvent, DrawRequestEvent, GameWonEvent, InfoToastEvent,
|
||||
ManualSyncRequestEvent, MoveRejectedEvent, MoveRequestEvent, NewGameConfirmEvent,
|
||||
NewGameRequestEvent, StateChangedEvent, UndoRequestEvent,
|
||||
NewGameRequestEvent, StateChangedEvent, UndoRequestEvent, XpAwardedEvent,
|
||||
};
|
||||
pub use game_plugin::{GameMutation, GamePlugin, GameStatePath};
|
||||
pub use help_plugin::{HelpPlugin, HelpScreen};
|
||||
|
||||
@@ -12,7 +12,7 @@ use solitaire_data::{
|
||||
load_progress_from, progress_file_path, save_progress_to, xp_for_win, PlayerProgress,
|
||||
};
|
||||
|
||||
use crate::events::GameWonEvent;
|
||||
use crate::events::{GameWonEvent, XpAwardedEvent};
|
||||
use crate::game_plugin::GameMutation;
|
||||
use crate::resources::GameStateResource;
|
||||
|
||||
@@ -65,6 +65,7 @@ impl Plugin for ProgressPlugin {
|
||||
app.insert_resource(ProgressResource(loaded))
|
||||
.insert_resource(ProgressStoragePath(self.storage_path.clone()))
|
||||
.add_event::<LevelUpEvent>()
|
||||
.add_event::<XpAwardedEvent>()
|
||||
.add_event::<GameWonEvent>()
|
||||
.add_systems(
|
||||
Update,
|
||||
@@ -78,6 +79,7 @@ impl Plugin for ProgressPlugin {
|
||||
fn award_xp_on_win(
|
||||
mut wins: EventReader<GameWonEvent>,
|
||||
mut levelups: EventWriter<LevelUpEvent>,
|
||||
mut xp_awarded: EventWriter<XpAwardedEvent>,
|
||||
game: Res<GameStateResource>,
|
||||
path: Res<ProgressStoragePath>,
|
||||
mut progress: ResMut<ProgressResource>,
|
||||
@@ -86,6 +88,7 @@ fn award_xp_on_win(
|
||||
let used_undo = game.0.undo_count > 0;
|
||||
let amount = xp_for_win(ev.time_seconds, used_undo);
|
||||
let prev_level = progress.0.add_xp(amount);
|
||||
xp_awarded.send(XpAwardedEvent { amount });
|
||||
if progress.0.leveled_up_from(prev_level) {
|
||||
levelups.send(LevelUpEvent {
|
||||
previous_level: prev_level,
|
||||
|
||||
Reference in New Issue
Block a user