Planned hero entry points: walls glow and pulse red, with a pulsing red light

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-03 08:58:35 +01:00
1 parent f113e1085c
commit 05ba88359e
3 files changed
+53 -1

No files matched your search

+1 -1
View File
@@ -82,7 +82,7 @@ An original dungeon management game (genre: dungeon building & defence) that run
- Each wave picks an **entry point** on the border of the realm facing the hero gate: few towers/creatures, no reinforced walls and a short way are attractive, entry points of recent waves are avoided, plus some randomness - Each wave picks an **entry point** on the border of the realm facing the hero gate: few towers/creatures, no reinforced walls and a short way are attractive, entry points of recent waves are avoided, plus some randomness
- Waves of **6+ heroes split into two groups** with different entry points (the paladin goes with the bigger group) - Waves of **6+ heroes split into two groups** with different entry points (the paladin goes with the bigger group)
- Heroes first tunnel to their entry point, then go for the core - Heroes first tunnel to their entry point, then go for the core
- **Warning** 30 s before: scout message with the direction(s) and blinking markers on the minimap - **Warning** 30 s before: scout message with the direction(s), blinking markers on the minimap and the **walls at the entry points glowing and pulsing red** (plus a pulsing red light) until the wave has broken in
## Goal ## Goal
- **Victory**: destroy the Hero Gate - **Victory**: destroy the Hero Gate
+32
View File
@@ -807,6 +807,10 @@ impl R3d {
let (gx, gz) = g.gate_center(); let (gx, gz) = g.gate_center();
push(vec3(gx, 1.2, gz), 7.0, vec3(0.75, 0.85, 1.0), 1.0, 50.0); push(vec3(gx, 1.2, gz), 7.0, vec3(0.75, 0.85, 1.0), 1.0, 50.0);
} }
let pulse = 0.5 + 0.5 * (t * 4.0).sin();
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);
}
for tw in &g.towers { for tw in &g.towers {
if tw.kind == TowerKind::Magic { if tw.kind == TowerKind::Magic {
let (x, z) = tw.pos(); let (x, z) = tw.pos();
@@ -935,6 +939,7 @@ impl R3d {
draw_flag(f, t); draw_flag(f, t);
} }
draw_overlay(&g.map, ov, self.wall_sub); draw_overlay(&g.map, ov, self.wall_sub);
draw_entry_warnings(g, t, self.wall_sub);
// translucent glows and effects last (they write depth, see glow_material) // translucent glows and effects last (they write depth, see glow_material)
draw_landmarks_glow(g, t); draw_landmarks_glow(g, t);
draw_towers_glow(g, t); draw_towers_glow(g, t);
@@ -1359,6 +1364,33 @@ fn outline(x0: f32, z0: f32, x1: f32, z1: f32, y: f32, c: Color) {
} }
} }
// Expected hero entry points: the walls around them glow red and pulse
fn draw_entry_warnings(g: &Game, t: f32, n: usize) {
if g.planned_entries.is_empty() {
return;
}
let p = 0.5 + 0.5 * (t * 4.0).sin();
let fill = [255, 40, 30, (50.0 + 90.0 * p) as u8];
let line = [255, 90, 60, (120.0 + 120.0 * p) as u8];
let mut mb = MB::new();
for &(ex, ey) in &g.planned_entries {
for dy in -2..=2 {
for dx in -2..=2 {
let (x, y) = (ex + dx, ey + dy);
let Some(tile) = g.map.get(x, y) else { continue };
// walls of the realm's border near the entry point (not rock)
let border = DIR4.iter().any(|&(ax, ay)| g.map.get(x + ax, y + ay).map_or(false, |o| o.owner == Owner::Player && !o.solid()));
if tile.diggable() && border && dx.abs() + dy.abs() <= 3 && mb.v.len() < 30000 {
block_shell(&mut mb, &g.map, x, y, n, fill, line, 1.0, 0.04);
}
}
}
}
if let Some(m) = mb.into_plain_mesh() {
draw_mesh(&m);
}
}
// Hover feedback: wall blocks get a faint version of the dig-mark box (in the tool colour), // Hover feedback: wall blocks get a faint version of the dig-mark box (in the tool colour),
// floor tiles an outline. While dragging a dig rectangle, every block inside is tinted. // floor tiles an outline. While dragging a dig rectangle, every block inside is tinted.
fn draw_overlay(map: &Map, ov: &Overlay, n: usize) { fn draw_overlay(map: &Map, ov: &Overlay, n: usize) {
+20
View File
@@ -111,4 +111,24 @@ mod tests {
let l = from_bytes(json.as_bytes()).expect("load"); let l = from_bytes(json.as_bytes()).expect("load");
assert_eq!(l.game.map.t.len(), 256 * 256); assert_eq!(l.game.map.t.len(), 256 * 256);
} }
// writes a savegame shortly before a wave (with planned entry points) when DC_SAVE_OUT is set
#[test]
fn dump_entry_warning_save() {
let Ok(path) = std::env::var("DC_SAVE_OUT") else { return };
let mut g = Game::new(11, 64, 64);
for _ in 0..(60 * 20) {
g.update(0.05);
}
g.wave_timer = 31.0;
for _ in 0..30 {
g.update(0.05); // the scouts warn 30 s before the wave
}
assert!(!g.planned_entries.is_empty());
let (ex, ey) = g.planned_entries[0];
let mut cam = Cam::new(ex as f32 + 0.5, ey as f32 + 0.5);
cam.zoom = 12.0;
std::fs::write(path, to_json(&g, &cam, &Settings { language: "en".into(), ..Settings::default() })).unwrap();
}
} }