From d2464bd3c243e441a57ddeb7cd81500dc36e1fc6 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 19 May 2026 14:31:36 -0700 Subject: [PATCH] Improve technician AI: actively seek and move toward broken traps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Technicians now pathfind to nearest unpowered trap instead of only repairing adjacent ones - One repair per turn, then moves toward next broken trap - Updated process_technician_repairs to take &mut [Minion] - 116 tests passing, both binaries compile 👾 Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta Code --- src/bin/terminal/mod.rs | 2 +- src/combat.rs | 63 ++++++++++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/bin/terminal/mod.rs b/src/bin/terminal/mod.rs index d5fe8f0..5cf2627 100644 --- a/src/bin/terminal/mod.rs +++ b/src/bin/terminal/mod.rs @@ -667,7 +667,7 @@ impl Game { } // Technician repairs - self.combat.process_technician_repairs(&mut self.game_map, &self.minions); + self.combat.process_technician_repairs(&mut self.game_map, &mut self.minions); // Medbay healing self.combat.process_medbay_healing(&self.game_map, &mut self.minions); diff --git a/src/combat.rs b/src/combat.rs index 5d10c64..adf818b 100644 --- a/src/combat.rs +++ b/src/combat.rs @@ -500,17 +500,58 @@ impl CombatSystem { } } - pub fn process_technician_repairs(&mut self, game_map: &mut GameMap, minions: &[Minion]) { - for minion in minions { - if !minion.can_repair() { continue; } + pub fn process_technician_repairs(&mut self, game_map: &mut GameMap, minions: &mut [Minion]) { + // Find all unpowered traps + let mut broken_traps: Vec<(i32, i32)> = Vec::new(); + for y in 0..game_map.height { + for x in 0..game_map.width { + let tile = game_map.get_tile(x, y); + if tile.is_trap() && !game_map.powered.contains(&(x, y)) { + broken_traps.push((x, y)); + } + } + } + + if broken_traps.is_empty() { return; } + + for i in 0..minions.len() { + if !minions[i].entity.alive || !minions[i].can_repair() { continue; } + + // First check adjacent tiles for unpowered traps + let mut repaired = false; for (dx, dy) in [(0, -1), (0, 1), (-1, 0), (1, 0)] { - let nx = minion.entity.x + dx; - let ny = minion.entity.y + dy; + let nx = minions[i].entity.x + dx; + let ny = minions[i].entity.y + dy; let tile = game_map.get_tile(nx, ny); if tile.is_trap() && !game_map.powered.contains(&(nx, ny)) { - // Re-power the trap (simplified: just add to powered set) game_map.powered.insert((nx, ny)); - self.log_msg("Technician repairs a trap!"); + self.log_msg(&format!("{} repairs a {}!", minions[i].entity.name, tile.name())); + repaired = true; + break; // One repair per turn + } + } + + if repaired { continue; } + + // If no adjacent broken trap, move toward nearest one + let mut nearest_dist = i32::MAX; + let mut nearest_trap = None; + for &(tx, ty) in &broken_traps { + let dist = minions[i].entity.distance_to(tx, ty); + if dist < nearest_dist { + nearest_dist = dist; + nearest_trap = Some((tx, ty)); + } + } + + if let Some((tx, ty)) = nearest_trap { + if let Some(path) = game_map.find_path( + (minions[i].entity.x, minions[i].entity.y), + (tx, ty), + &std::collections::HashSet::new(), + ) && path.len() >= 2 { + minions[i].entity.x = path[1].0; + minions[i].entity.y = path[1].1; } } } @@ -886,9 +927,9 @@ mod tests { // Trap is NOT in powered set let tech = crate::entities::Minion::new(10, 10, crate::entities::MinionClass::Technician, 0, 0); - let minions = vec![tech]; + let mut minions = vec![tech]; - cs.process_technician_repairs(&mut map, &minions); + cs.process_technician_repairs(&mut map, &mut minions); // Trap should now be powered assert!(map.powered.contains(&(11, 10))); @@ -901,9 +942,9 @@ mod tests { map.set_tile(11, 10, crate::tiles::TileType::TrapSpike); let worker = crate::entities::Minion::new(10, 10, crate::entities::MinionClass::Worker, 0, 0); - let minions = vec![worker]; + let mut minions = vec![worker]; - cs.process_technician_repairs(&mut map, &minions); + cs.process_technician_repairs(&mut map, &mut minions); // Worker should NOT repair the trap assert!(!map.powered.contains(&(11, 10))); -- 2.51.2