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:
1 parent
a13abf63f3
commit
6c16bc7b18
2 files changed
+69
-7
No files matched your search
+35
-4
@@ -1142,11 +1142,42 @@ impl R3d {
|
|||||||
for &(ex, ey) in &g.planned_entries {
|
for &(ex, ey) in &g.planned_entries {
|
||||||
push(vec3(ex as f32 + 0.5, 1.2, ey as f32 + 0.5), 5.0, vec3(1.0, 0.12, 0.08), 0.6 + 1.0 * pulse, 30.0);
|
push(vec3(ex as f32 + 0.5, 1.2, ey as f32 + 0.5), 5.0, vec3(1.0, 0.12, 0.08), 0.6 + 1.0 * pulse, 30.0);
|
||||||
}
|
}
|
||||||
for tw in &g.towers {
|
// towers: the dungeon's glow red from their joints, the knights' carry a cold lantern;
|
||||||
if tw.kind == TowerKind::Magic {
|
// magic and rune towers shine in their colour; every shot flares up
|
||||||
|
for tw in g.towers.iter().filter(|tw| g.map.shown(tw.tile)) {
|
||||||
let (x, z) = tw.pos();
|
let (x, z) = tw.pos();
|
||||||
push(vec3(x, tower_height(tw) + 0.6, z), 4.5, vec3(0.75, 0.4, 1.0), 0.8 + tw.flash * 3.0, 10.0);
|
let top = tower_height(tw);
|
||||||
|
let (c, i, r) = match tw.kind {
|
||||||
|
TowerKind::Magic => (vec3(0.75, 0.4, 1.0), 0.9, 4.5),
|
||||||
|
TowerKind::Rune => (vec3(0.4, 0.8, 1.0), 1.0, 5.0),
|
||||||
|
_ if tw.hero => (vec3(0.85, 0.9, 1.0), 0.8, 4.0),
|
||||||
|
_ => (vec3(1.0, 0.3, 0.12), 0.85, 4.0),
|
||||||
|
};
|
||||||
|
let pulse = 0.9 + 0.1 * (t * 2.0 + tw.id as f32).sin();
|
||||||
|
push(vec3(x, top + 0.4, z), r, c, i * pulse + tw.flash * 3.0, 10.0);
|
||||||
}
|
}
|
||||||
|
// traps: each kind in its colour, brighter while it goes off. Another master's traps
|
||||||
|
// stay dark until they have gone off once (so the light does not betray them)
|
||||||
|
for tr in g.traps.iter().filter(|tr| tr.stage == crate::traps::ARMED && g.map.shown(tr.tile)) {
|
||||||
|
use crate::traps::TrapKind as K;
|
||||||
|
if !g.trap_glows(tr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let fresh = (1.0 - tr.fired / 0.8).clamp(0.0, 1.0); // just went off
|
||||||
|
let (c, i, r) = match tr.kind {
|
||||||
|
K::Poison => (vec3(0.35, 1.0, 0.3), 0.55, 2.5),
|
||||||
|
K::Fire => (vec3(1.0, 0.5, 0.12), 0.6, 3.0),
|
||||||
|
K::Alarm => (vec3(1.0, 0.15, 0.1), 0.35 + 0.35 * (0.5 + 0.5 * (t * 4.0).sin()), 3.0),
|
||||||
|
K::Mine => (vec3(0.7, 0.4, 1.0), 0.5, 2.5),
|
||||||
|
K::Frost => (vec3(0.5, 0.85, 1.0), 0.65, 3.0),
|
||||||
|
K::Spikes | K::Roller => (vec3(1.0, 0.85, 0.6), 0.0, 2.5),
|
||||||
|
_ => continue, // doors
|
||||||
|
};
|
||||||
|
if i + fresh <= 0.0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let (x, z) = xy(tr.tile);
|
||||||
|
push(vec3(x as f32 + 0.5, 0.35, z as f32 + 0.5), r + fresh, c, i + 2.5 * fresh, 5.0);
|
||||||
}
|
}
|
||||||
// effects light up their surroundings: lightning, explosions, glowing projectiles, spells
|
// effects light up their surroundings: lightning, explosions, glowing projectiles, spells
|
||||||
let tint = |c: u32| {
|
let tint = |c: u32| {
|
||||||
@@ -2602,7 +2633,7 @@ fn draw_traps_glow(g: &Game, t: f32) {
|
|||||||
line3(c + vec3(-0.35, y, 0.0), c + vec3(-0.35 + 0.7 * f, y, 0.0), 0.08, Color::new(1.0, 0.7, 0.3, 0.8));
|
line3(c + vec3(-0.35, y, 0.0), c + vec3(-0.35 + 0.7 * f, y, 0.0), 0.08, Color::new(1.0, 0.7, 0.3, 0.8));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if tr.stage != ARMED {
|
if tr.stage != ARMED || !g.trap_glows(tr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if tr.revealed && !tr.kind.door() {
|
if tr.revealed && !tr.kind.door() {
|
||||||
|
|||||||
+34
-3
@@ -100,6 +100,8 @@ pub struct Trap {
|
|||||||
pub aim: f32, // direction of the last shot (fire jet, roller)
|
pub aim: f32, // direction of the last shot (fire jet, roller)
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub revealed: bool, // a fairy has found it: heroes wreck it
|
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
|
// a boulder rolling down a corridor
|
||||||
@@ -178,7 +180,7 @@ impl Game {
|
|||||||
}
|
}
|
||||||
let id = self.next_id;
|
let id = self.next_id;
|
||||||
self.next_id += 1;
|
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.fx(8, x as f32 + 0.5, y as f32 + 0.5, 0.0, 0.0, 0.4, 0xff40d0ff);
|
||||||
self.sfx_ui(Sfx::FlagSet);
|
self.sfx_ui(Sfx::FlagSet);
|
||||||
}
|
}
|
||||||
@@ -227,6 +229,12 @@ impl Game {
|
|||||||
self.update_traps(dt);
|
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)?
|
// is there forge work to do (an order waiting)?
|
||||||
pub fn forge_orders(&self) -> bool {
|
pub fn forge_orders(&self) -> bool {
|
||||||
self.traps.iter().any(|t| t.stage == ORDERED)
|
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);
|
let (cx, cy) = (tx as f32 + 0.5, ty as f32 + 0.5);
|
||||||
self.traps[k].hp -= dmg;
|
self.traps[k].hp -= dmg;
|
||||||
self.traps[k].fired = 0.0;
|
self.traps[k].fired = 0.0;
|
||||||
|
self.traps[k].sprung = true;
|
||||||
self.fx(2, cx, cy, 0.0, 0.0, 0.25, 0xff4080c0);
|
self.fx(2, cx, cy, 0.0, 0.0, 0.25, 0xff4080c0);
|
||||||
self.sfx(Sfx::Dig, cx, cy);
|
self.sfx(Sfx::Dig, cx, cy);
|
||||||
if self.traps[k].kind == TrapKind::MagicDoor && self.traps[k].cd <= 0.0 {
|
if self.traps[k].kind == TrapKind::MagicDoor && self.traps[k].cd <= 0.0 {
|
||||||
@@ -441,6 +450,7 @@ impl Game {
|
|||||||
if damage > 0.0 {
|
if damage > 0.0 {
|
||||||
t.hp -= damage * dt;
|
t.hp -= damage * dt;
|
||||||
t.fired = 0.0;
|
t.fired = 0.0;
|
||||||
|
t.sprung = true;
|
||||||
if t.hp <= 0.0 {
|
if t.hp <= 0.0 {
|
||||||
wrecked.push(k);
|
wrecked.push(k);
|
||||||
}
|
}
|
||||||
@@ -482,6 +492,7 @@ impl Game {
|
|||||||
if !hit.is_empty() {
|
if !hit.is_empty() {
|
||||||
self.traps[k].cd = 2.5;
|
self.traps[k].cd = 2.5;
|
||||||
self.traps[k].fired = 0.0;
|
self.traps[k].fired = 0.0;
|
||||||
|
self.traps[k].sprung = true;
|
||||||
for j in hit {
|
for j in hit {
|
||||||
self.damage_unit(j, 45.0);
|
self.damage_unit(j, 45.0);
|
||||||
}
|
}
|
||||||
@@ -493,6 +504,7 @@ impl Game {
|
|||||||
if !near(0.9, &self.ents).is_empty() {
|
if !near(0.9, &self.ents).is_empty() {
|
||||||
self.traps[k].cd = 12.0;
|
self.traps[k].cd = 12.0;
|
||||||
self.traps[k].fired = 0.0;
|
self.traps[k].fired = 0.0;
|
||||||
|
self.traps[k].sprung = true;
|
||||||
self.clouds.push(Cloud { x: cx, y: cy, t: 0.0 });
|
self.clouds.push(Cloud { x: cx, y: cy, t: 0.0 });
|
||||||
self.sfx(Sfx::Magic, cx, cy);
|
self.sfx(Sfx::Magic, cx, cy);
|
||||||
}
|
}
|
||||||
@@ -505,6 +517,7 @@ impl Game {
|
|||||||
let a = (ey - cy).atan2(ex - cx);
|
let a = (ey - cy).atan2(ex - cx);
|
||||||
self.traps[k].cd = 4.0;
|
self.traps[k].cd = 4.0;
|
||||||
self.traps[k].fired = 0.0;
|
self.traps[k].fired = 0.0;
|
||||||
|
self.traps[k].sprung = true;
|
||||||
self.traps[k].aim = a;
|
self.traps[k].aim = a;
|
||||||
let (dx, dy) = (a.cos(), a.sin());
|
let (dx, dy) = (a.cos(), a.sin());
|
||||||
for o in near(3.4, &self.ents) {
|
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()) };
|
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].cd = 18.0;
|
||||||
self.traps[k].fired = 0.0;
|
self.traps[k].fired = 0.0;
|
||||||
|
self.traps[k].sprung = true;
|
||||||
self.traps[k].aim = dy.atan2(dx);
|
self.traps[k].aim = dy.atan2(dx);
|
||||||
self.rollers.push(Roller { x: cx, y: cy, dx, dy, t: 0.0, hit: Vec::new() });
|
self.rollers.push(Roller { x: cx, y: cy, dx, dy, t: 0.0, hit: Vec::new() });
|
||||||
self.sfx(Sfx::DigDone, cx, cy);
|
self.sfx(Sfx::DigDone, cx, cy);
|
||||||
@@ -541,6 +555,7 @@ impl Game {
|
|||||||
if let Some(&j) = seen.first() {
|
if let Some(&j) = seen.first() {
|
||||||
self.traps[k].cd = 20.0;
|
self.traps[k].cd = 20.0;
|
||||||
self.traps[k].fired = 0.0;
|
self.traps[k].fired = 0.0;
|
||||||
|
self.traps[k].sprung = true;
|
||||||
let target = self.ents[j].id;
|
let target = self.ents[j].id;
|
||||||
for o in self.ents.iter_mut() {
|
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 {
|
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() {
|
fn doors_stop_enemies_until_smashed_and_lock() {
|
||||||
let (mut g, cx, cy) = forge_game();
|
let (mut g, cx, cy) = forge_game();
|
||||||
let i = idx(cx + 10, cy);
|
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;
|
g.map.t[i].kind = Kind::Door;
|
||||||
assert!(g.map.walk_cost()(i).is_some());
|
assert!(g.map.walk_cost()(i).is_some());
|
||||||
g.place_trap(TrapKind::WoodDoor, cx + 10, cy);
|
g.place_trap(TrapKind::WoodDoor, cx + 10, cy);
|
||||||
@@ -653,7 +668,7 @@ mod tests {
|
|||||||
let a = idx(cx + 10, cy);
|
let a = idx(cx + 10, cy);
|
||||||
let b = idx(cx + 10, cy + 4);
|
let b = idx(cx + 10, cy + 4);
|
||||||
for (id, i, kind) in [(1001, a, TrapKind::Frost), (1002, b, TrapKind::Alarm)] {
|
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::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);
|
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().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");
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user