Title screen, pause menu, settings dialog and savegames
- title screen (continue/new/load/settings/help) and Esc pause menu - settings: language & sound, camera, graphics, game, rebindable keys; stored in localStorage and in every savegame (always applied on load) - savegames as JSON (.dcsave): download, load via file picker or drag & drop - autosave to localStorage every 2 minutes and when the tab is hidden/closed - configurable wall detail, light count, interface size, game speed, hints - audio behind a cargo feature so native tests run (cargo test --no-default-features) - savegame round-trip test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fbf6f223fc
commit
fb7dab3683
17 files changed
+1742
-345
No files matched your search
+13
-5
@@ -1,6 +1,7 @@
|
||||
// Game logic: units, AI, economy, hero waves.
|
||||
|
||||
use crate::map::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const CORE_HP: f32 = 2000.0;
|
||||
pub const GATE_HP: f32 = 3000.0;
|
||||
@@ -71,7 +72,7 @@ pub struct SfxEvent {
|
||||
pub const MANA_MAX: f32 = 400.0;
|
||||
pub const CARRY_MAX: i32 = 300;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
pub enum EK {
|
||||
Worker,
|
||||
Kobold,
|
||||
@@ -112,7 +113,7 @@ pub fn st(k: EK) -> St {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
pub enum Job {
|
||||
None,
|
||||
Dig(usize),
|
||||
@@ -129,7 +130,7 @@ pub enum Job {
|
||||
Rally,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Ent {
|
||||
pub id: u32,
|
||||
pub kind: EK,
|
||||
@@ -217,6 +218,7 @@ pub struct Fx {
|
||||
pub col: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Game {
|
||||
pub map: Map,
|
||||
pub ents: Vec<Ent>,
|
||||
@@ -231,7 +233,9 @@ pub struct Game {
|
||||
pub warned: bool,
|
||||
pub portal_timer: f32,
|
||||
pub payday: f32,
|
||||
#[serde(skip)]
|
||||
pub fx: Vec<Fx>,
|
||||
#[serde(skip)]
|
||||
pub msgs: Vec<Msg>,
|
||||
pub state: u8, // 0 running, 1 victory, 2 defeat
|
||||
pub paused: bool,
|
||||
@@ -241,14 +245,17 @@ pub struct Game {
|
||||
pub gate_hit_t: f32,
|
||||
pub gate_stage: u32, // defender thresholds triggered (0..3)
|
||||
pub flag: Option<usize>,
|
||||
#[serde(skip)]
|
||||
pub events: Vec<SfxEvent>,
|
||||
pub last_wave_value: f32,
|
||||
pub torches: Vec<Torch>,
|
||||
#[serde(skip)]
|
||||
pub hints: bool, // show hint messages (setting)
|
||||
hint_t: [f32; 16],
|
||||
}
|
||||
|
||||
// torch on a wall face: wall tile + direction (index into DIR4) towards the open side
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Torch {
|
||||
pub wall: usize,
|
||||
pub dir: usize,
|
||||
@@ -315,6 +322,7 @@ impl Game {
|
||||
events: Vec::new(),
|
||||
last_wave_value: 0.0,
|
||||
torches: Vec::new(),
|
||||
hints: true,
|
||||
hint_t: [-1000.0; 16],
|
||||
};
|
||||
let (cx, cy) = g.map.core;
|
||||
@@ -391,7 +399,7 @@ impl Game {
|
||||
}
|
||||
|
||||
fn hint(&mut self, slot: usize, key: &'static str) {
|
||||
if self.time - self.hint_t[slot] > 30.0 {
|
||||
if self.hints && self.time - self.hint_t[slot] > 30.0 {
|
||||
self.hint_t[slot] = self.time;
|
||||
self.msg(key, vec![]);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user