Real AI masters: rivals, AI seats and the co-op partner play by the player's rules (imps dig, mine and claim, rooms, towers, tunnels and the attack flag); other masters are saved; portals belong to whoever claims them

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-06 08:18:09 +01:00
1 parent b940a959f1
commit a00ae6a06b
11 files changed
+456 -223

No files matched your search

+9 -9
View File
@@ -480,7 +480,8 @@ fn player_owner() -> Owner {
// The state of a dungeon master that is not being processed right now. The Game's own fields
// (gold, mana, hand, ...) always belong to the master whose side is `me_side`; switching swaps
// them with the stored ones. A single player game has no stored masters at all.
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct MasterState {
pub side: u8,
pub owner: Option<Owner>,
@@ -656,8 +657,8 @@ pub struct Game {
pub me_side: u8, // that master's side (0 = the first player)
#[serde(skip)]
pub local_side: u8, // the master who plays at this computer
#[serde(skip)]
pub masters: Vec<MasterState>, // the other masters (multiplayer)
#[serde(default)]
pub masters: Vec<MasterState>, // the other masters (multiplayer, AI masters), saved with the game
#[serde(skip)]
pub pending: Vec<Command>, // orders of this master, applied at the next step boundary
#[serde(skip)]
@@ -1291,7 +1292,7 @@ impl Game {
}
pub fn portals_owned(&self) -> usize {
(0..self.map.portals.len()).filter(|&k| self.map.portal_owned(k)).count()
(0..self.map.portals.len()).filter(|&k| self.map.portal_owner(k) == self.me).count()
}
// gate defenders alive (not the treasure guards)
@@ -1610,8 +1611,7 @@ impl Game {
}
// the starting area of a master: a 9x9 room of his own floor and his 3x3 soul core
#[allow(dead_code)] // called by start_masters only
fn carve_master_area(&mut self, side: u8, core: (i32, i32)) {
pub(crate) fn carve_master_area(&mut self, side: u8, core: (i32, i32)) {
let owner = Owner::of_side(side);
for y in core.1 - 3..core.1 + 6 {
for x in core.0 - 3..core.0 + 6 {
@@ -2007,7 +2007,7 @@ impl Game {
}
r -= w;
}
let owned: Vec<usize> = (0..self.map.portals.len()).filter(|&k| self.map.portal_owned(k)).collect();
let owned: Vec<usize> = (0..self.map.portals.len()).filter(|&k| self.map.portal_owner(k) == self.me).collect();
let pk = owned[(self.rng.next() as usize) % owned.len()];
let (px, py) = self.map.portals[pk];
let id = self.spawn(kind, false, px as f32 + 1.5, py as f32 + 1.5, 1);
@@ -2436,7 +2436,7 @@ impl Game {
self.demo_ai(e, dt);
} else if e.hero && e.captive != 0 {
self.captive_update(e, dt);
} else if self.is_rival_side(e.side) || (e.rival() && !self.is_master(e.side)) {
} else if !self.is_master(e.side) && (self.is_rival_side(e.side) || e.rival()) {
self.rival_ai(e, dt);
} else if e.kind == EK::Trader {
self.trader_ai(e, dt);
@@ -2499,7 +2499,7 @@ impl Game {
}
}
if !tile.walkable() && !(flies(e.kind) && tile.liquid()) {
if (e.hero || self.is_rival_side(e.side) || (e.rival() && !self.is_master(e.side))) && tile.diggable() {
if (e.hero || (!self.is_master(e.side) && (self.is_rival_side(e.side) || e.rival()))) && tile.diggable() {
// heroes dig their way through
e.working = true;
let (tx, ty) = xy(next);