Three new heroes: giant, fairy spy, battle mage
- giant (from wave 6): slow and enormous, smashes doors three times as hard and wrecks traps by standing next to them - fairy spy (from wave 3): flies, finds the dungeon's traps (marked with a glint) so the heroes wreck them, and heals heroes around it - battle mage (from wave 4): throws fireballs with splash and blasts reinforced walls (steel resists him better) - sprites, bestiary texts EN/DE, test: fairies find traps, giants wreck them Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a58497e3a9
commit
4ce2c2919e
7 files changed
+186
-15
No files matched your search
+72
-4
@@ -98,6 +98,8 @@ pub struct Trap {
|
||||
pub carrier: u32, // imp carrying or installing it (entity id + 1)
|
||||
pub fired: f32, // time since it last went off (for drawing)
|
||||
pub aim: f32, // direction of the last shot (fire jet, roller)
|
||||
#[serde(default)]
|
||||
pub revealed: bool, // a fairy has found it: heroes wreck it
|
||||
}
|
||||
|
||||
// a boulder rolling down a corridor
|
||||
@@ -122,8 +124,12 @@ pub struct Cloud {
|
||||
const CLOUD_TIME: f32 = 6.0;
|
||||
|
||||
// how hard a unit hits doors
|
||||
pub fn door_breaker(_k: EK) -> f32 {
|
||||
1.0
|
||||
pub fn door_breaker(k: EK) -> f32 {
|
||||
if k == EK::Giant {
|
||||
3.0
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
|
||||
fn enemy(e: &Ent) -> bool {
|
||||
@@ -172,7 +178,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 });
|
||||
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.fx(8, x as f32 + 0.5, y as f32 + 0.5, 0.0, 0.0, 0.4, 0xff40d0ff);
|
||||
self.sfx_ui(Sfx::FlagSet);
|
||||
}
|
||||
@@ -408,6 +414,45 @@ impl Game {
|
||||
}
|
||||
rollers.retain(|r| r.t < 50.0);
|
||||
self.rollers = rollers;
|
||||
// fairies find traps, giants and heroes wreck the traps they know about
|
||||
let mut wrecked = Vec::new();
|
||||
for k in 0..self.traps.len() {
|
||||
let t = &self.traps[k];
|
||||
if t.stage != ARMED || t.kind.door() {
|
||||
continue;
|
||||
}
|
||||
let (tx, ty) = xy(t.tile);
|
||||
let (cx, cy) = (tx as f32 + 0.5, ty as f32 + 0.5);
|
||||
let mut damage = 0.0;
|
||||
let mut found = t.revealed;
|
||||
for e in self.ents.iter().filter(|e| e.alive && !e.held && e.active_hero()) {
|
||||
let d2 = (e.x - cx).powi(2) + (e.y - cy).powi(2);
|
||||
if e.kind == EK::Fairy && d2 < 9.0 {
|
||||
found = true;
|
||||
}
|
||||
if d2 < 1.6 && (found || e.kind == EK::Giant) {
|
||||
damage += if e.kind == EK::Giant { 40.0 } else { 15.0 };
|
||||
}
|
||||
}
|
||||
let t = &mut self.traps[k];
|
||||
if found && !t.revealed {
|
||||
t.revealed = true;
|
||||
}
|
||||
if damage > 0.0 {
|
||||
t.hp -= damage * dt;
|
||||
t.fired = 0.0;
|
||||
if t.hp <= 0.0 {
|
||||
wrecked.push(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
for &k in wrecked.iter().rev() {
|
||||
let t = self.traps.remove(k);
|
||||
let (tx, ty) = xy(t.tile);
|
||||
self.fx(3, tx as f32 + 0.5, ty as f32 + 0.5, 0.0, 0.0, 0.8, 0xff505070);
|
||||
self.fx(6, tx as f32 + 0.5, ty as f32 + 0.5, 0.0, 0.0, 0.5, 0xff3a5a7a);
|
||||
self.msg("msg.trap_wrecked", vec![format!("@trap.{}", t.kind.id())]);
|
||||
}
|
||||
for k in 0..self.traps.len() {
|
||||
if k >= self.traps.len() {
|
||||
break;
|
||||
@@ -583,7 +628,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 });
|
||||
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.map.t[i].kind = Kind::Door;
|
||||
assert!(g.map.walk_cost()(i).is_some());
|
||||
g.place_trap(TrapKind::WoodDoor, cx + 10, cy);
|
||||
@@ -601,4 +646,27 @@ mod tests {
|
||||
assert!(g.trap_at(i).is_none(), "door smashed");
|
||||
assert_eq!(g.map.t[i].kind, Kind::Floor);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fairies_find_traps_and_giants_wreck_them() {
|
||||
let (mut g, cx, cy) = forge_game();
|
||||
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.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);
|
||||
for j in 0..g.ents.len() {
|
||||
g.ents[j].stun = 999.0; // stand still
|
||||
}
|
||||
for _ in 0..(20 * 15) {
|
||||
g.update(0.05);
|
||||
for j in 0..g.ents.len() {
|
||||
g.ents[j].stun = 999.0;
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user