Imps reinforce walls next to claimed ground (heroes avoid them, ~10x slower to break)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-03 07:39:39 +01:00
1 parent c508e232b0
commit a1fe02ffa6
8 files changed
+197 -13

No files matched your search

+75 -2
View File
@@ -42,13 +42,19 @@ pub enum Sfx {
Defeat,
Click,
Error,
Reinforce,
Cannon,
Magic,
Capture,
Convert,
}
pub const SFX_ALL: [Sfx; 29] = [
pub const SFX_ALL: [Sfx; 34] = [
Sfx::Dig, Sfx::GoldDig, Sfx::DigDone, Sfx::Claim, Sfx::Deposit, Sfx::Build, Sfx::Sell, Sfx::MeleeHit,
Sfx::Shoot, Sfx::Death, Sfx::HeroDeath, Sfx::Lightning, Sfx::Heal, Sfx::Summon, Sfx::PortalArrive,
Sfx::WaveAlarm, Sfx::WaveStart, Sfx::CoreHit, Sfx::GateHit, Sfx::GateDefenders, Sfx::LevelUp,
Sfx::Pickup, Sfx::Drop, Sfx::Payday, Sfx::FlagSet, Sfx::Victory, Sfx::Defeat, Sfx::Click, Sfx::Error,
Sfx::Reinforce, Sfx::Cannon, Sfx::Magic, Sfx::Capture, Sfx::Convert,
];
impl Sfx {
@@ -128,6 +134,7 @@ pub enum Job {
AttackCore,
Leave,
Rally,
Reinforce(usize), // wall tile
}
#[derive(Clone, Serialize, Deserialize)]
@@ -840,7 +847,7 @@ impl Game {
EK::Dwarf => 3.0,
EK::Paladin => 2.0,
_ => 1.0,
};
} * if tile.reinforced { 0.2 } else { 1.0 };
self.fx(6, tx as f32 + 0.5, ty as f32 + 0.5, 0.0, 0.0, 0.4, 0xff3a5a7a);
self.sfx(Sfx::Dig, tx as f32 + 0.5, ty as f32 + 0.5);
let t = &mut self.map.t[next];
@@ -884,6 +891,7 @@ impl Game {
t.kind = Kind::Floor;
t.owner = Owner::None;
t.marked = false;
t.reinforced = false;
t.reserved = 0;
t.hp = 0.0;
t.gold = 0;
@@ -1054,6 +1062,22 @@ impl Game {
found
}
// an earth wall next to own floor that can be reinforced from tile i
fn reinforce_neighbor(&self, i: usize, me: u32) -> Option<usize> {
if self.map.t[i].owner != Owner::Player {
return None;
}
let (x, y) = xy(i);
for &(dx, dy) in &DIR4 {
if let Some(t) = self.map.get(x + dx, y + dy) {
if t.kind == Kind::Earth && !t.reinforced && !t.marked && (t.reserved == 0 || t.reserved == me) {
return Some(idx(x + dx, y + dy));
}
}
}
None
}
fn claimable(&self, i: usize, me: u32) -> bool {
let t = &self.map.t[i];
if t.owner != Owner::None || !matches!(t.kind, Kind::Floor | Kind::Portal) {
@@ -1147,6 +1171,27 @@ impl Game {
}
}
}
Job::Reinforce(w) => {
let t = self.map.t[w];
if !adj4(cur, w) || t.kind != Kind::Earth || t.reinforced || t.marked {
self.unreserve(me);
e.job = Job::None;
return;
}
let (wx, wy) = xy(w);
e.face = sgn(wx as f32 + 0.5 - e.x);
e.working = true;
e.timer += dt;
if e.timer >= 2.5 {
self.unreserve(me);
e.job = Job::None;
let tl = &mut self.map.t[w];
tl.reinforced = true;
tl.hp = 12.0; // the player's imps need twice as long to dig it out again
self.fx(8, wx as f32 + 0.5, wy as f32 + 0.5, 0.0, 0.0, 0.5, 0xff4040e0);
self.sfx(Sfx::Reinforce, wx as f32 + 0.5, wy as f32 + 0.5);
}
}
Job::Claim(t) => {
if cur != t || !self.claimable(t, me) {
self.unreserve(me);
@@ -1229,6 +1274,16 @@ impl Game {
e.job = Job::Claim(end);
return;
}
if let Some(p) = self.map.search(cur, self.map.walk_cost(), |i| self.reinforce_neighbor(i, me).is_some(), true) {
let end = p.last().copied().unwrap_or(cur);
if let Some(w) = self.reinforce_neighbor(end, me) {
self.map.t[w].reserved = me;
e.path = p;
e.job = Job::Reinforce(w);
e.timer = 0.0;
return;
}
}
e.think = 1.0;
self.wander(e, 3);
}
@@ -1875,6 +1930,7 @@ pub fn job_name(e: &Ent) -> &'static str {
}
}
Job::Leave => "job.leave",
Job::Reinforce(_) => "job.reinforce",
Job::Rally => {
if e.working {
"job.gate_attack"
@@ -1962,4 +2018,21 @@ mod tests {
}
}
}
#[test]
fn imps_reinforce_walls() {
let mut g = Game::new(3, 64, 64);
for _ in 0..(120 * 20) {
g.update(0.05);
}
let n = g.map.t.iter().filter(|t| t.reinforced).count();
println!("reinforced walls after 2 minutes: {}", n);
assert!(n >= 10);
// heroes avoid them: the hero path cost through reinforced earth is 10x
let (cx, cy) = g.map.core;
let w = (cx - 4, cy);
if g.map.t[idx(w.0, w.1)].reinforced {
assert_eq!(g.map.hero_cost()(idx(w.0, w.1)), Some(70));
}
}
}