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
+108
@@ -0,0 +1,108 @@
|
||||
// Statistics of a game for the master at this computer: counters (heroes slain, tiles dug, wood
|
||||
// felled, ...) and a sample of gold, creatures and army strength every few seconds for the charts
|
||||
// at the end. They are local (not part of the shared match state) and feed the achievements.
|
||||
|
||||
use crate::game::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// counters (index into GameStats::c)
|
||||
pub const HEROES: usize = 0; // heroes slain or captured
|
||||
pub const BOSSES: usize = 1; // champions of the Order brought down
|
||||
pub const LOST: usize = 2; // own creatures lost
|
||||
pub const DUG: usize = 3; // tiles dug out
|
||||
pub const WOOD: usize = 4; // wood felled
|
||||
pub const WALLS: usize = 5; // walls raised
|
||||
pub const ROOMS: usize = 6; // room tiles built
|
||||
pub const SPELLS: usize = 7; // spells cast (incl. lightning and healing)
|
||||
pub const SLAPS: usize = 8; // creatures slapped
|
||||
pub const TRAPS: usize = 9; // traps and doors ordered
|
||||
pub const TOWERS: usize = 10; // towers built
|
||||
pub const DROWNED: usize = 11; // heroes who fell into water or lava
|
||||
pub const BURNT: usize = 12; // bridge tiles that burnt down
|
||||
pub const SIEGE: usize = 13; // siege engines built
|
||||
pub const CAPTURED: usize = 14; // heroes knocked out and carried off
|
||||
pub const GOLD: usize = 15; // gold earned (treasure delivered, bounties, loot)
|
||||
pub const DRAWBRIDGE: usize = 16; // drawbridges raised
|
||||
pub const COUNTERS: usize = 17;
|
||||
|
||||
// one sample every SAMPLE_EVERY seconds: time, gold, creatures, army
|
||||
pub const SAMPLE_EVERY: f32 = 10.0;
|
||||
const MAX_SAMPLES: usize = 720;
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct GameStats {
|
||||
#[serde(default)]
|
||||
pub c: Vec<f32>,
|
||||
#[serde(default)]
|
||||
pub samples: Vec<(f32, f32, u16, f32)>,
|
||||
#[serde(default)]
|
||||
pub sample_t: f32,
|
||||
}
|
||||
|
||||
impl GameStats {
|
||||
pub fn get(&self, k: usize) -> f32 {
|
||||
self.c.get(k).copied().unwrap_or(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Game {
|
||||
// counts for the master at this computer (another master's deeds count on his computer)
|
||||
pub(crate) fn count(&mut self, k: usize, n: f32) {
|
||||
if self.me_side != self.local_side || self.demo.is_some() {
|
||||
return;
|
||||
}
|
||||
if self.stats.c.len() < COUNTERS {
|
||||
self.stats.c.resize(COUNTERS, 0.0);
|
||||
}
|
||||
self.stats.c[k] += n;
|
||||
}
|
||||
|
||||
// a hero fell or was captured: the master at this computer counts it in single player and
|
||||
// co-op (the heroes are everybody's enemy there)
|
||||
pub(crate) fn count_local(&mut self, k: usize) {
|
||||
let me = self.me_side;
|
||||
let local = self.local_side;
|
||||
self.switch_to(local);
|
||||
self.count(k, 1.0);
|
||||
self.switch_to(me);
|
||||
}
|
||||
|
||||
// every few seconds: how the local master stands
|
||||
pub(crate) fn sample_stats(&mut self, dt: f32) {
|
||||
if self.demo.is_some() || self.state != 0 {
|
||||
return;
|
||||
}
|
||||
self.stats.sample_t -= dt;
|
||||
if self.stats.sample_t > 0.0 {
|
||||
return;
|
||||
}
|
||||
self.stats.sample_t = SAMPLE_EVERY;
|
||||
let s = (self.time, self.gold, self.creature_count() as u16, self.army_value());
|
||||
self.stats.samples.push(s);
|
||||
if self.stats.samples.len() > MAX_SAMPLES {
|
||||
// keep the whole game in view: every second sample goes
|
||||
let v: Vec<_> = self.stats.samples.iter().copied().step_by(2).collect();
|
||||
self.stats.samples = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn a_game_keeps_its_statistics() {
|
||||
let mut g = Game::new(3, 64, 64);
|
||||
for _ in 0..(35 * 20) {
|
||||
g.update(0.05);
|
||||
}
|
||||
assert!(g.stats.samples.len() >= 3, "{} samples", g.stats.samples.len());
|
||||
g.count(DUG, 2.0);
|
||||
assert_eq!(g.stats.get(DUG), 2.0);
|
||||
// saved with the game
|
||||
let json = serde_json::to_string(&g.stats).unwrap();
|
||||
let back: GameStats = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(back.get(DUG), 2.0);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user