Free game against rival dungeon masters (green, blue) and/or the humans

- One opponent on maps up to 128x128, two on bigger maps; with a chance of one in two one of
  them is the humans' gate instead of a rival master
- Simplified rival masters (rival.rs): own soul core and dungeon (lair, treasury to loot) that
  grows ring by ring, creatures appear and get stronger over time, guard the dungeon and attack
  in groups - the player, the other master or the humans' gate; a fallen core puts the master
  out and frees its ground
- The humans attack everybody: waves now and then march on a rival master
- Sides for units: all other sides are enemies; towers and contested ground follow them; the
  hand cannot pick up rival creatures; the flag next to a rival core attacks it
- Difficulty (easy / medium / hard) in the New Game dialog
- Colours: green and blue floors, cores, unit rings, minimap, HUD bars of the rival cores
- Victory in free games: every rival core and the gate (if any) destroyed
- Tests for setups, growth and attacks, elimination and victory, humans against rivals

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-03 14:11:23 +01:00
1 parent 52c4d1caf8
commit dd16c684e5
11 files changed
+771 -38

No files matched your search

+23 -2
View File
@@ -125,9 +125,23 @@ pub enum Owner {
None,
Player,
Hero,
Rival1, // AI dungeon masters (free play, campaign)
Rival2,
}
const OWNERS: [Owner; 3] = [Owner::None, Owner::Player, Owner::Hero];
const OWNERS: [Owner; 5] = [Owner::None, Owner::Player, Owner::Hero, Owner::Rival1, Owner::Rival2];
impl Owner {
pub fn of_side(side: u8) -> Owner {
match side {
0 => Owner::Player,
1 => Owner::Hero,
2 => Owner::Rival1,
3 => Owner::Rival2,
_ => Owner::None,
}
}
}
// Short serde names (only used by version 1 savegames, newer ones use the packed codec)
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
@@ -648,15 +662,22 @@ impl Map {
}
}
// next to the player's soul core
pub fn adj_core(&self, i: usize) -> bool {
self.adj_core_of(i, Owner::Player)
}
// next to the soul core of a master (player or rival)
pub fn adj_core_of(&self, i: usize, owner: Owner) -> bool {
let (x, y) = xy(i);
DIR4.iter().any(|&(dx, dy)| self.get(x + dx, y + dy).map_or(false, |t| t.kind == Kind::Core))
DIR4.iter().any(|&(dx, dy)| self.get(x + dx, y + dy).map_or(false, |t| t.kind == Kind::Core && t.owner == owner))
}
pub fn hero_path_to_core(&self, start: usize) -> Option<Vec<usize>> {
self.search(start, self.hero_cost(), |i| self.adj_core(i), true)
}
pub fn path_to(&self, start: usize, target: usize) -> Option<Vec<usize>> {
self.search(start, self.walk_cost(), |i| i == target, true)
}