Title menu background: an endless battle at a bridge over an underground river

- dungeon creatures against knights, balanced reinforcements for the weaker side
- towers on both banks; heavy units go for them, destroyed towers are rebuilt after 25 s
- the camera slowly circles the bridge; battle sounds stay muted; the demo is never saved

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-03 15:51:35 +01:00
1 parent d06ce2edb5
commit f359671dac
3 files changed
+351 -16

No files matched your search

+27 -14
View File
@@ -445,6 +445,8 @@ pub struct Game {
pub gate_on: bool, // free game: the humans' gate exists
#[serde(skip)]
pub springs: usize, // claimed mana springs (recounted)
#[serde(skip)]
pub demo: Option<crate::demo::Demo>, // the endless bridge battle behind the title menu
#[serde(default)]
pub dwarf_caves: Vec<crate::dwarves::DwarfCave>,
#[serde(default)]
@@ -603,6 +605,7 @@ impl Game {
gate_on: true,
springs: 0,
freed: 0,
demo: None,
dwarf_caves: Vec::new(),
dwarf_t: 30.0,
rune_free: 0,
@@ -964,7 +967,25 @@ impl Game {
self.fx.retain(|f| f.t < f.max);
}
pub(crate) fn update_units(&mut self, dt: f32) {
for i in 0..self.ents.len() {
if !self.ents[i].alive || self.ents[i].held {
continue;
}
let mut e = std::mem::replace(&mut self.ents[i], Ent::dummy());
let (x0, y0) = (e.x, e.y);
self.update_ent(&mut e, dt);
self.turn(&mut e, x0, y0);
self.ents[i] = e;
}
self.ents.retain(|e| e.alive);
}
fn step(&mut self, dt: f32) {
if self.demo.is_some() {
self.demo_step(dt);
return;
}
self.time += dt;
self.recount();
self.mana = (self.mana + (1.0 + SPRING_MANA * self.springs as f32) * dt).min(self.mana_max());
@@ -1014,17 +1035,7 @@ impl Game {
self.reinforce_corners();
}
self.update_contested();
for i in 0..self.ents.len() {
if !self.ents[i].alive || self.ents[i].held {
continue;
}
let mut e = std::mem::replace(&mut self.ents[i], Ent::dummy());
let (x0, y0) = (e.x, e.y);
self.update_ent(&mut e, dt);
self.turn(&mut e, x0, y0);
self.ents[i] = e;
}
self.ents.retain(|e| e.alive);
self.update_units(dt);
self.update_towers(dt);
if !self.rivals.is_empty() {
self.update_rivals(dt);
@@ -1611,6 +1622,8 @@ impl Game {
self.sfx(Sfx::Convert, e.x, e.y);
self.msg("msg.creature_freed", vec![format!("@{}", st(e.kind).name)]);
}
} else if self.demo.is_some() {
self.demo_ai(e, dt);
} else if e.hero && e.captive != 0 {
self.captive_update(e, dt);
} else if e.rival() {
@@ -1934,7 +1947,7 @@ impl Game {
cap.saturating_sub(used)
}
fn kill(&mut self, j: usize) {
pub(crate) fn kill(&mut self, j: usize) {
let e = &self.ents[j];
if e.hero && e.captive == 0 && self.prison_space() > 0 {
// knocked out instead of killed: imps carry the hero to the torture chamber
@@ -4064,7 +4077,7 @@ impl Game {
}
}
fn update_towers(&mut self, dt: f32) {
pub(crate) fn update_towers(&mut self, dt: f32) {
for k in 0..self.towers.len() {
if k >= self.towers.len() {
break;
@@ -4227,7 +4240,7 @@ impl Game {
}
// a hero attacks a tower; false = tower gone or unreachable
fn attack_tower(&mut self, e: &mut Ent, id: u32, dt: f32) -> bool {
pub(crate) fn attack_tower(&mut self, e: &mut Ent, id: u32, dt: f32) -> bool {
let Some(k) = self.towers.iter().position(|t| t.id == id) else { return false };
let (tx, ty) = self.towers[k].pos();
let hit_h = self.towers[k].top() * 0.6;