// Raising walls: the master plans walls on his own empty floor (the wall tool, paid at once); // pit imps raise them as reinforced earth. Tunnels can be closed again and doors put where they // matter. Dragging over a planned tile again cancels the plan (the gold comes back). use crate::game::*; use crate::map::*; // gold a raised wall costs pub const WALL_COST: f32 = 50.0; // seconds of imp work for one wall pub const WALL_WORK: f32 = 3.0; impl Game { // a tile where a wall may be planned: own plain floor without a trap, door or torch stand fn wall_spot(&self, i: usize) -> bool { let t = &self.map.t[i]; t.kind == Kind::Floor && t.owner == self.me && !self.traps.iter().any(|tr| tr.tile == i) } // the wall tool over a rectangle: plans walls (or cancels planned ones) pub fn plan_walls(&mut self, x0: i32, y0: i32, x1: i32, y1: i32) { let (mut planned, mut cancelled, mut broke) = (0, 0, false); for y in y0.min(y1)..=y0.max(y1) { for x in x0.min(x1)..=x0.max(x1) { if !inb(x, y) { continue; } let i = idx(x, y); if let Some(k) = self.wall_plans.iter().position(|p| p.0 == i && p.1 == self.me_side) { self.wall_plans.swap_remove(k); self.gold += WALL_COST; cancelled += 1; continue; } if !self.wall_spot(i) { continue; } if self.gold < WALL_COST { broke = true; continue; } self.gold -= WALL_COST; self.wall_plans.push((i, self.me_side)); planned += 1; } } if broke { self.msg("msg.no_gold", vec![]); self.sfx_ui(Sfx::Error); } else if planned + cancelled == 0 { self.hint(33, "hint.wall_where"); } else { self.sfx_ui(Sfx::FlagSet); } } // a planned wall of the master being processed next to tile i (for his imps) pub(crate) fn planned_wall_near(&self, i: usize, me: u32) -> Option { if self.wall_plans.is_empty() { return None; } let (x, y) = xy(i); DIR4.iter().find_map(|&(dx, dy)| { if !inb(x + dx, y + dy) { return None; } let n = idx(x + dx, y + dy); let t = &self.map.t[n]; let mine = self.wall_plans.iter().any(|p| p.0 == n && p.1 == self.me_side); (mine && (t.reserved == 0 || t.reserved == me)).then_some(n) }) } // an imp works on a planned wall: true while there is more to do pub(crate) fn raise_wall_job(&mut self, e: &mut Ent, w: usize, dt: f32) -> bool { let me = e.id + 1; let planned = self.wall_plans.iter().any(|p| p.0 == w && p.1 == self.me_side); if !planned || !adj4(e.tile(), w) { return false; } // the ground was lost or built over: the plan is dropped, the gold comes back if !self.wall_spot(w) { self.wall_plans.retain(|p| p.0 != w); self.gold += WALL_COST; return false; } self.map.t[w].reserved = me; let (wx, wy) = xy(w); e.face = sgn(wx as f32 + 0.5 - e.x); e.working = true; e.timer += dt * self.help_speed(w); if e.timer < WALL_WORK { return true; } e.timer = 0.0; // whoever stands there steps aside let bank = DIR8.iter().map(|&(dx, dy)| (wx + dx, wy + dy)).find(|&(bx, by)| self.map.walk(bx, by) && idx(bx, by) != w); for o in self.ents.iter_mut() { if o.alive && !o.held && o.tile() == w { if let Some((bx, by)) = bank { o.x = bx as f32 + 0.5; o.y = by as f32 + 0.5; o.path.clear(); } } } // torch stands on the tile and wall torches facing it go self.torches.retain(|t| !(t.stand && t.wall == w) && !(!t.stand && { let (tx, ty) = xy(t.wall); let (dx, dy) = DIR4[t.dir % 4]; inb(tx + dx, ty + dy) && idx(tx + dx, ty + dy) == w })); let tl = &mut self.map.t[w]; tl.kind = Kind::Earth; tl.reinforced = true; tl.hp = 12.0; tl.marked = false; tl.reserved = 0; tl.bed = 0; tl.food = 0.0; self.wall_plans.retain(|p| p.0 != w); 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); false } } #[cfg(test)] mod tests { use super::*; #[test] fn imps_raise_planned_walls() { let mut g = Game::new(4, 64, 64); let (cx, cy) = g.map.core; // own floor next to the core let (x, y) = (cx + 4, cy + 1); for (xx, yy) in [(x, y), (x - 1, y), (x + 1, y)] { let t = &mut g.map.t[idx(xx, yy)]; t.kind = Kind::Floor; t.owner = Owner::Player; } g.gold = 1000.0; g.plan_walls(x, y, x, y); assert_eq!(g.wall_plans.len(), 1); assert_eq!(g.gold, 1000.0 - WALL_COST); // planning it again cancels (and pays back) g.plan_walls(x, y, x, y); assert!(g.wall_plans.is_empty() && g.gold == 1000.0); g.plan_walls(x, y, x, y); let mut raised = false; for _ in 0..(60 * 20) { g.update(0.05); if g.map.t[idx(x, y)].kind == Kind::Earth { raised = true; break; } } assert!(raised, "an imp raised the wall"); assert!(g.map.t[idx(x, y)].reinforced && g.wall_plans.is_empty()); } }