feat(engine): fire XpAwardedEvent for daily and weekly bonus XP

Daily challenge completions (+100 XP) and weekly goal bonuses (+75 XP)
now fire XpAwardedEvent so the player sees a "+N XP" toast — consistent
with the post-win XP toast already shown by ProgressPlugin.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-04-27 03:23:55 +00:00
parent 91b675f2f1
commit 3eb7901023
2 changed files with 8 additions and 2 deletions
@@ -18,7 +18,7 @@ use chrono::{Local, NaiveDate};
use solitaire_data::{daily_seed_for, save_progress_to}; use solitaire_data::{daily_seed_for, save_progress_to};
use solitaire_sync::ChallengeGoal; use solitaire_sync::ChallengeGoal;
use crate::events::{GameWonEvent, NewGameRequestEvent}; use crate::events::{GameWonEvent, NewGameRequestEvent, XpAwardedEvent};
use crate::game_plugin::GameMutation; use crate::game_plugin::GameMutation;
use crate::progress_plugin::{ProgressResource, ProgressStoragePath, ProgressUpdate}; use crate::progress_plugin::{ProgressResource, ProgressStoragePath, ProgressUpdate};
use crate::resources::GameStateResource; use crate::resources::GameStateResource;
@@ -81,6 +81,7 @@ impl Plugin for DailyChallengePlugin {
.add_event::<DailyGoalAnnouncementEvent>() .add_event::<DailyGoalAnnouncementEvent>()
.add_event::<GameWonEvent>() .add_event::<GameWonEvent>()
.add_event::<NewGameRequestEvent>() .add_event::<NewGameRequestEvent>()
.add_event::<XpAwardedEvent>()
.add_systems(Startup, fetch_server_challenge) .add_systems(Startup, fetch_server_challenge)
.add_systems(Update, poll_server_challenge) .add_systems(Update, poll_server_challenge)
// record/award after the base ProgressUpdate so we don't fight // record/award after the base ProgressUpdate so we don't fight
@@ -149,6 +150,7 @@ fn handle_daily_completion(
mut progress: ResMut<ProgressResource>, mut progress: ResMut<ProgressResource>,
path: Res<ProgressStoragePath>, path: Res<ProgressStoragePath>,
mut completed: EventWriter<DailyChallengeCompletedEvent>, mut completed: EventWriter<DailyChallengeCompletedEvent>,
mut xp_awarded: EventWriter<XpAwardedEvent>,
) { ) {
for ev in wins.read() { for ev in wins.read() {
if game.0.seed != daily.seed { if game.0.seed != daily.seed {
@@ -170,6 +172,7 @@ fn handle_daily_completion(
continue; continue;
} }
progress.0.add_xp(DAILY_BONUS_XP); progress.0.add_xp(DAILY_BONUS_XP);
xp_awarded.send(XpAwardedEvent { amount: DAILY_BONUS_XP });
if let Some(target) = &path.0 { if let Some(target) = &path.0 {
if let Err(e) = save_progress_to(target, &progress.0) { if let Err(e) = save_progress_to(target, &progress.0) {
warn!("failed to save progress after daily completion: {e}"); warn!("failed to save progress after daily completion: {e}");
+4 -1
View File
@@ -9,7 +9,7 @@ use solitaire_data::{
WEEKLY_GOAL_XP, WEEKLY_GOAL_XP,
}; };
use crate::events::GameWonEvent; use crate::events::{GameWonEvent, XpAwardedEvent};
use crate::game_plugin::GameMutation; use crate::game_plugin::GameMutation;
use crate::progress_plugin::{LevelUpEvent, ProgressResource, ProgressStoragePath, ProgressUpdate}; use crate::progress_plugin::{LevelUpEvent, ProgressResource, ProgressStoragePath, ProgressUpdate};
use crate::resources::GameStateResource; use crate::resources::GameStateResource;
@@ -27,6 +27,7 @@ impl Plugin for WeeklyGoalsPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_event::<WeeklyGoalCompletedEvent>() app.add_event::<WeeklyGoalCompletedEvent>()
.add_event::<GameWonEvent>() .add_event::<GameWonEvent>()
.add_event::<XpAwardedEvent>()
.add_systems(Startup, roll_weekly_goals_on_startup) .add_systems(Startup, roll_weekly_goals_on_startup)
// Run after GameMutation (so GameWonEvent is available) and // Run after GameMutation (so GameWonEvent is available) and
// ProgressUpdate (so we don't fight ProgressPlugin's add_xp). // ProgressUpdate (so we don't fight ProgressPlugin's add_xp).
@@ -62,6 +63,7 @@ fn evaluate_weekly_goals(
path: Res<ProgressStoragePath>, path: Res<ProgressStoragePath>,
mut completions: EventWriter<WeeklyGoalCompletedEvent>, mut completions: EventWriter<WeeklyGoalCompletedEvent>,
mut levelups: EventWriter<LevelUpEvent>, mut levelups: EventWriter<LevelUpEvent>,
mut xp_awarded: EventWriter<XpAwardedEvent>,
) { ) {
let mut events: Vec<&GameWonEvent> = wins.read().collect(); let mut events: Vec<&GameWonEvent> = wins.read().collect();
if events.is_empty() { if events.is_empty() {
@@ -99,6 +101,7 @@ fn evaluate_weekly_goals(
} }
if bonus_xp > 0 { if bonus_xp > 0 {
xp_awarded.send(XpAwardedEvent { amount: bonus_xp });
let prev_level = progress.0.add_xp(bonus_xp); let prev_level = progress.0.add_xp(bonus_xp);
if progress.0.leveled_up_from(prev_level) { if progress.0.leveled_up_from(prev_level) {
levelups.send(LevelUpEvent { levelups.send(LevelUpEvent {