Raise walls: plan them on own floor, imps build reinforced earth
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
398bb95681
commit
ed3bc437e5
26 files changed
+347
-11
No files matched your search
+162
@@ -0,0 +1,162 @@
|
||||
// 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<usize> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user