Light for towers and traps; other masters' traps stay dark until sprung

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-05 16:08:04 +01:00
1 parent a13abf63f3
commit 6c16bc7b18
2 files changed
+70 -8

No files matched your search

+34 -3
View File
@@ -100,6 +100,8 @@ pub struct Trap {
pub aim: f32, // direction of the last shot (fire jet, roller)
#[serde(default)]
pub revealed: bool, // a fairy has found it: heroes wreck it
#[serde(default)]
pub sprung: bool, // has gone off at least once (its light shows to the other masters from then on)
}
// a boulder rolling down a corridor
@@ -178,7 +180,7 @@ impl Game {
}
let id = self.next_id;
self.next_id += 1;
self.traps.push(Trap { id, kind, tile: i, stage: ORDERED, crate_tile: 0, progress: 0.0, cd: 0.0, hp: kind.hp(), locked: false, carrier: 0, fired: 99.0, aim: 0.0, revealed: false });
self.traps.push(Trap { id, kind, tile: i, stage: ORDERED, crate_tile: 0, progress: 0.0, cd: 0.0, hp: kind.hp(), locked: false, carrier: 0, fired: 99.0, aim: 0.0, revealed: false, sprung: false });
self.fx(8, x as f32 + 0.5, y as f32 + 0.5, 0.0, 0.0, 0.4, 0xff40d0ff);
self.sfx_ui(Sfx::FlagSet);
}
@@ -227,6 +229,12 @@ impl Game {
self.update_traps(dt);
}
// may the local master see this trap's light and glow? His own always; another master's
// only once it has gone off (in a match the light must not betray a trap)
pub fn trap_glows(&self, t: &Trap) -> bool {
t.kind.door() || self.map.t[t.tile].owner == self.me || t.sprung
}
// is there forge work to do (an order waiting)?
pub fn forge_orders(&self) -> bool {
self.traps.iter().any(|t| t.stage == ORDERED)
@@ -334,6 +342,7 @@ impl Game {
let (cx, cy) = (tx as f32 + 0.5, ty as f32 + 0.5);
self.traps[k].hp -= dmg;
self.traps[k].fired = 0.0;
self.traps[k].sprung = true;
self.fx(2, cx, cy, 0.0, 0.0, 0.25, 0xff4080c0);
self.sfx(Sfx::Dig, cx, cy);
if self.traps[k].kind == TrapKind::MagicDoor && self.traps[k].cd <= 0.0 {
@@ -441,6 +450,7 @@ impl Game {
if damage > 0.0 {
t.hp -= damage * dt;
t.fired = 0.0;
t.sprung = true;
if t.hp <= 0.0 {
wrecked.push(k);
}
@@ -482,6 +492,7 @@ impl Game {
if !hit.is_empty() {
self.traps[k].cd = 2.5;
self.traps[k].fired = 0.0;
self.traps[k].sprung = true;
for j in hit {
self.damage_unit(j, 45.0);
}
@@ -493,6 +504,7 @@ impl Game {
if !near(0.9, &self.ents).is_empty() {
self.traps[k].cd = 12.0;
self.traps[k].fired = 0.0;
self.traps[k].sprung = true;
self.clouds.push(Cloud { x: cx, y: cy, t: 0.0 });
self.sfx(Sfx::Magic, cx, cy);
}
@@ -505,6 +517,7 @@ impl Game {
let a = (ey - cy).atan2(ex - cx);
self.traps[k].cd = 4.0;
self.traps[k].fired = 0.0;
self.traps[k].sprung = true;
self.traps[k].aim = a;
let (dx, dy) = (a.cos(), a.sin());
for o in near(3.4, &self.ents) {
@@ -531,6 +544,7 @@ impl Game {
let (dx, dy) = if ex.abs() > ey.abs() { (ex.signum(), 0.0) } else { (0.0, ey.signum()) };
self.traps[k].cd = 18.0;
self.traps[k].fired = 0.0;
self.traps[k].sprung = true;
self.traps[k].aim = dy.atan2(dx);
self.rollers.push(Roller { x: cx, y: cy, dx, dy, t: 0.0, hit: Vec::new() });
self.sfx(Sfx::DigDone, cx, cy);
@@ -541,6 +555,7 @@ impl Game {
if let Some(&j) = seen.first() {
self.traps[k].cd = 20.0;
self.traps[k].fired = 0.0;
self.traps[k].sprung = true;
let target = self.ents[j].id;
for o in self.ents.iter_mut() {
if o.alive && o.creature() && !matches!(o.job, Job::Attack(_)) && (o.x - cx).powi(2) + (o.y - cy).powi(2) < 18.0 * 18.0 {
@@ -628,7 +643,7 @@ mod tests {
fn doors_stop_enemies_until_smashed_and_lock() {
let (mut g, cx, cy) = forge_game();
let i = idx(cx + 10, cy);
g.traps.push(Trap { id: 999, kind: TrapKind::WoodDoor, tile: i, stage: ARMED, crate_tile: 0, progress: 0.0, cd: 0.0, hp: 400.0, locked: false, carrier: 0, fired: 99.0, aim: 0.0, revealed: false });
g.traps.push(Trap { id: 999, kind: TrapKind::WoodDoor, tile: i, stage: ARMED, crate_tile: 0, progress: 0.0, cd: 0.0, hp: 400.0, locked: false, carrier: 0, fired: 99.0, aim: 0.0, revealed: false, sprung: false });
g.map.t[i].kind = Kind::Door;
assert!(g.map.walk_cost()(i).is_some());
g.place_trap(TrapKind::WoodDoor, cx + 10, cy);
@@ -653,7 +668,7 @@ mod tests {
let a = idx(cx + 10, cy);
let b = idx(cx + 10, cy + 4);
for (id, i, kind) in [(1001, a, TrapKind::Frost), (1002, b, TrapKind::Alarm)] {
g.traps.push(Trap { id, kind, tile: i, stage: ARMED, crate_tile: 0, progress: 0.0, cd: 99.0, hp: kind.hp(), locked: false, carrier: 0, fired: 99.0, aim: 0.0, revealed: false });
g.traps.push(Trap { id, kind, tile: i, stage: ARMED, crate_tile: 0, progress: 0.0, cd: 99.0, hp: kind.hp(), locked: false, carrier: 0, fired: 99.0, aim: 0.0, revealed: false, sprung: false });
}
g.spawn(EK::Fairy, true, cx as f32 + 11.5, cy as f32 + 0.5, 1);
g.spawn(EK::Giant, true, cx as f32 + 10.5, cy as f32 + 5.5, 1);
@@ -669,4 +684,20 @@ mod tests {
assert!(g.traps.iter().all(|t| t.id != 1002), "the giant wrecked the alarm");
assert!(g.traps.iter().find(|t| t.id == 1001).map_or(true, |t| t.revealed), "the fairy found the frost glow");
}
#[test]
fn foreign_traps_glow_only_after_going_off() {
let mut g = Game::new(3, 64, 64);
let (cx, cy) = g.map.core;
let mine = idx(cx + 2, cy - 2);
let theirs = idx(cx + 3, cy - 2);
g.map.t[mine].owner = g.me;
g.map.t[theirs].owner = Owner::Rival1;
let trap = |tile| Trap { id: 1, kind: TrapKind::Fire, tile, stage: ARMED, crate_tile: 0, progress: 0.0, cd: 0.0, hp: 10.0, locked: false, carrier: 0, fired: 99.0, aim: 0.0, revealed: false, sprung: false };
assert!(g.trap_glows(&trap(mine)), "my own trap");
let mut t = trap(theirs);
assert!(!g.trap_glows(&t), "a rival's trap stays dark");
t.sprung = true;
assert!(g.trap_glows(&t), "until it has gone off");
}
}