Statistics with a chart at the end of a game, 64 achievements kept locally and on the server
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
193b199875
commit
9931ca9b54
37 files changed
+1833
-4
No files matched your search
+49
@@ -1,5 +1,6 @@
|
||||
// DungeonCraft – dungeon building & defence in the browser (Rust + macroquad, WASM).
|
||||
|
||||
mod achievements;
|
||||
mod audio;
|
||||
mod campaign;
|
||||
mod campaign_doc;
|
||||
@@ -41,6 +42,7 @@ mod traps;
|
||||
mod sound;
|
||||
mod surface;
|
||||
mod spells;
|
||||
mod stats;
|
||||
mod touch;
|
||||
mod ui;
|
||||
mod voice;
|
||||
@@ -847,6 +849,12 @@ async fn main() {
|
||||
let mut r3d = R3d::new();
|
||||
let mut sounds = SoundBank::new();
|
||||
let mut music = MusicDirector::new();
|
||||
// lifetime statistics and achievements (browser storage, merged with the server after a login)
|
||||
let stored_profile: achievements::Profile = platform::storage_get(achievements::PROFILE_KEY).and_then(|s| serde_json::from_str(&s).ok()).unwrap_or_default();
|
||||
let mut tracker = achievements::Tracker::new(stored_profile);
|
||||
let mut want_profile = false; // fetch the server's profile when the client is free
|
||||
let mut profile_store_t = 0.0f32;
|
||||
let mut profile_upload_t = 0.0f32;
|
||||
let mut ui = Ui::new();
|
||||
let mut menu = Menu::new();
|
||||
let mut cloud = Cloud::new(); // savegame service; files + browser autosave when unreachable
|
||||
@@ -992,6 +1000,7 @@ async fn main() {
|
||||
}
|
||||
}
|
||||
CloudEvent::LoggedIn => {
|
||||
want_profile = true;
|
||||
if menu.page == Some(Page::Login) {
|
||||
menu.page = Some(Page::Title);
|
||||
}
|
||||
@@ -1939,8 +1948,47 @@ app.pending_briefing = briefing_of(&campaign, i);
|
||||
| Some(Page::Account)
|
||||
| Some(Page::Bestiary { from_title: true, .. })
|
||||
| Some(Page::Match { from_title: true })
|
||||
| Some(Page::Achievements { from_title: true })
|
||||
);
|
||||
music.update(&app.game, on_title, (app.cam.rx, app.cam.rz));
|
||||
// ---------------- achievements: count the game, unlock, store and share the profile
|
||||
{
|
||||
let ctx = achievements::Context { coop: app.coop, editor_test: app.editor_testing };
|
||||
let unix = miniquad::date::now() as u64;
|
||||
let counting = !on_title && (playing || app.game.state != 0);
|
||||
for id in if counting { tracker.update(&app.game, &ctx, dt, unix) } else { Vec::new() } {
|
||||
let (name, _) = achievements::texts(&id);
|
||||
app.game.msgs.push(game::Msg { key: "msg.achievement", args: vec![name] });
|
||||
audio::play_sfx(game::Sfx::Victory.index(), 0.8, 0.0);
|
||||
}
|
||||
if let Some(json) = cloud.profile_loaded.take() {
|
||||
if let Ok(p) = serde_json::from_str::<achievements::Profile>(&json) {
|
||||
tracker.profile.merge(&p);
|
||||
tracker.profile.check_tiers(unix);
|
||||
tracker.dirty = true;
|
||||
tracker.upload = tracker.profile != p;
|
||||
}
|
||||
}
|
||||
if want_profile && cloud.session.is_some() && cloud.load_profile() {
|
||||
want_profile = false;
|
||||
}
|
||||
profile_store_t += dt;
|
||||
profile_upload_t += dt;
|
||||
if tracker.dirty && (profile_store_t > 10.0 || tracker.upload) {
|
||||
profile_store_t = 0.0;
|
||||
tracker.dirty = false;
|
||||
platform::storage_set(achievements::PROFILE_KEY, &serde_json::to_string(&tracker.profile).unwrap_or_default());
|
||||
}
|
||||
if tracker.upload && cloud.session.is_some() && profile_upload_t > 30.0 && !want_profile {
|
||||
if cloud.save_profile(&serde_json::to_string(&tracker.profile).unwrap_or_default()) {
|
||||
tracker.upload = false;
|
||||
profile_upload_t = 0.0;
|
||||
}
|
||||
}
|
||||
if matches!(menu.page, Some(Page::Achievements { .. })) {
|
||||
menu.profile = tracker.profile.clone();
|
||||
}
|
||||
}
|
||||
for m in app.game.msgs.drain(..) {
|
||||
if app.settings.voice && voice::speaks(m.key) {
|
||||
voice.say(&i18n::trf(m.key, &m.args), now, 12);
|
||||
@@ -2039,6 +2087,7 @@ fn draw_frame(app: &mut App, gfx: &Gfx, r3d: &mut R3d, ui: &mut Ui, menu: &mut M
|
||||
| Some(Page::Account)
|
||||
| Some(Page::Bestiary { from_title: true, .. })
|
||||
| Some(Page::Match { from_title: true })
|
||||
| Some(Page::Achievements { from_title: true })
|
||||
);
|
||||
if !title_screen {
|
||||
let info = if over_ui || (touch && inp.inspect.is_none()) {
|
||||
|
||||
Reference in new issue
Block a user