The dead leave remains for good: a pool of blood and bones, armour bits from heroes, splinters from machines; dust where they fall
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8d2362fc11
commit
af330c09b7
2 files changed
+77
-5
No files matched your search
+76
-4
@@ -1267,6 +1267,7 @@ pub struct R3d {
|
||||
dying: Vec<Dying>, // units shown dying (see update_dying)
|
||||
seen: Vec<Dying>, // the units of the last frame (their last looks)
|
||||
seen_time: f32, // the game time of the last frame
|
||||
remains: Vec<(f32, f32, f32, EK, u32)>, // what the dead leave (for good): x, z, since, kind, a seed
|
||||
}
|
||||
|
||||
// a unit that has just died, shown falling for DEATH_T seconds of game time (only the renderer
|
||||
@@ -1282,6 +1283,7 @@ struct Dying {
|
||||
t0: f32,
|
||||
}
|
||||
const DEATH_T: f32 = 1.4;
|
||||
const MAX_REMAINS: usize = 400; // the remains of the dead lying on the floor (the oldest give way)
|
||||
|
||||
// The 3D resolution setting refers to a window 1080 device pixels high: smaller windows use a
|
||||
// proportionally smaller divisor, so the 3D pixels keep their size relative to the window.
|
||||
@@ -1331,7 +1333,7 @@ fn chunk_sig(map: &Map, cx: i32, cz: i32) -> u64 {
|
||||
|
||||
impl R3d {
|
||||
pub fn new() -> R3d {
|
||||
R3d { lit: lit_material(), glow: glow_material(), opaque: opaque_material(), chunks: Vec::new(), ncx: 0, ncz: 0, outlines: true, wall_sub: 3, max_lights: MAX_LIGHTS, pixel_rows: crate::settings::PIXEL_ART_ROWS as u32, target: None, lava: Vec::new(), chunks_hd: false, chunks_holiday: 0, dying: Vec::new(), seen: Vec::new(), seen_time: 0.0 }
|
||||
R3d { lit: lit_material(), glow: glow_material(), opaque: opaque_material(), chunks: Vec::new(), ncx: 0, ncz: 0, outlines: true, wall_sub: 3, max_lights: MAX_LIGHTS, pixel_rows: crate::settings::PIXEL_ART_ROWS as u32, target: None, lava: Vec::new(), chunks_hd: false, chunks_holiday: 0, dying: Vec::new(), seen: Vec::new(), seen_time: 0.0, remains: Vec::new() }
|
||||
}
|
||||
|
||||
// Changing the wall detail rebuilds all chunks
|
||||
@@ -1693,11 +1695,12 @@ impl R3d {
|
||||
draw_cube(vec3(x, 0.55, z), vec3(0.07, 0.3, 0.07), None, Color::from_rgba(70, 45, 28, 255));
|
||||
}
|
||||
}
|
||||
// the materials of the furniture and the buildings in the graphics style's resolution
|
||||
crate::props::set_materials(if self.chunks_hd { &gfx.materials } else { &gfx.materials_px });
|
||||
draw_landmarks_solid(g, t);
|
||||
draw_towers_solid(g);
|
||||
// the furniture's materials in the graphics style's resolution
|
||||
crate::props::set_materials(if self.chunks_hd { &gfx.materials } else { &gfx.materials_px });
|
||||
crate::props::draw_props_solid(g, cam, t);
|
||||
self.draw_remains(g);
|
||||
draw_parts_solid(g);
|
||||
draw_traps_solid(g, t);
|
||||
|
||||
@@ -1757,6 +1760,66 @@ impl R3d {
|
||||
self.draw_2d(g, gfx, cam, &m, t);
|
||||
}
|
||||
|
||||
// what the dead leave, for good: a pool of blood and a few bones (heroes: dented armour and
|
||||
// a broken blade; machines, stone and clockwork: splinters and rubble instead of blood);
|
||||
// dust puffs where a body hits the ground
|
||||
fn draw_remains(&self, g: &Game) {
|
||||
for &(x, z, since, kind, seed) in &self.remains {
|
||||
let age = g.time - since;
|
||||
if age < 0.0 || !g.map.shown(idx(x as i32, z as i32)) {
|
||||
continue;
|
||||
}
|
||||
let hero = crate::gfx::is_hero_kind(kind);
|
||||
let sink = 0.0;
|
||||
let lifeless = matches!(kind, EK::Ram | EK::Catapult | EK::Gargoyle | EK::Cogling);
|
||||
let off0 = |k: u32| ((((seed.wrapping_mul(2654435761) ^ k.wrapping_mul(40503)) >> 7) % 1000) as f32 / 1000.0 - 0.5) * 0.4;
|
||||
if lifeless {
|
||||
let wood = if matches!(kind, EK::Ram | EK::Catapult) { Color::from_rgba(110, 76, 44, 255) } else { Color::from_rgba(96, 94, 100, 255) };
|
||||
for k in 0..5u32 {
|
||||
crate::props::auto_cube(vec3(x + off0(k + 20) * 1.2, 0.02, z + off0(k + 30) * 1.2), vec3(0.09, 0.04, 0.05 + (k % 2) as f32 * 0.08), wood);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// the pool of blood, spread a little in the first seconds
|
||||
let spread = (age / 2.0).min(1.0);
|
||||
for k in 0..4u32 {
|
||||
let r = (0.12 + off0(k + 40).abs() * 0.4) * (0.5 + 0.5 * spread);
|
||||
draw_cube(vec3(x + off0(k + 50) * 0.6, 0.006 + k as f32 * 0.001, z + off0(k + 60) * 0.6), vec3(r, 0.004, r * 0.8), None, Color::from_rgba(92, 10, 14, 255));
|
||||
}
|
||||
let off = off0;
|
||||
let bone = Color::from_rgba(214, 204, 182, 255);
|
||||
let iron = Color::from_rgba(120, 122, 132, 255);
|
||||
for k in 0..3u32 {
|
||||
let p = vec3(x + off(k * 2), 0.02 - sink, z + off(k * 2 + 1));
|
||||
if k % 2 == 0 {
|
||||
crate::props::auto_cube(p, vec3(0.16, 0.03, 0.03), bone);
|
||||
} else {
|
||||
crate::props::auto_cube(p, vec3(0.03, 0.03, 0.14), bone);
|
||||
}
|
||||
}
|
||||
if hero {
|
||||
crate::props::auto_cube(vec3(x + off(7), 0.04 - sink, z + off(8)), vec3(0.1, 0.07, 0.1), iron);
|
||||
crate::props::auto_cube(vec3(x + off(9), 0.015 - sink, z + off(10)), vec3(0.2, 0.02, 0.03), iron);
|
||||
} else {
|
||||
crate::props::auto_cube(vec3(x + off(7), 0.04 - sink, z + off(8)), vec3(0.08, 0.07, 0.08), bone);
|
||||
}
|
||||
}
|
||||
// dust where the dying hit the ground
|
||||
for d in &self.dying {
|
||||
let el = g.time - d.t0;
|
||||
if !(0.3..1.2).contains(&el) {
|
||||
continue;
|
||||
}
|
||||
let f = (el - 0.3) / 0.9;
|
||||
for k in 0..6 {
|
||||
let a = k as f32 * 1.05 + d.id as f32;
|
||||
let r = 0.15 + f * 0.35;
|
||||
let p = vec3(d.x + a.cos() * r, 0.05 + f * 0.15, d.z + a.sin() * r);
|
||||
draw_cube(p, Vec3::splat(0.06 + f * 0.08), None, Color::new(0.55, 0.5, 0.45, 0.5 * (1.0 - f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// units that vanished since the last frame died (or left): they are shown falling for a
|
||||
// moment; a new game, a loaded one or a jump in time starts afresh
|
||||
fn update_dying(&mut self, g: &Game) {
|
||||
@@ -1768,14 +1831,23 @@ impl R3d {
|
||||
.collect();
|
||||
if g.time < self.seen_time || g.time > self.seen_time + 2.0 {
|
||||
self.dying.clear();
|
||||
self.remains.clear();
|
||||
} else {
|
||||
let gone: Vec<Dying> = self.seen.iter().filter(|d| !now.iter().any(|e| e.id == d.id)).copied().collect();
|
||||
// many at once: not deaths but a change of the map
|
||||
if gone.len() <= 24 {
|
||||
self.dying.extend(gone.into_iter().map(|d| Dying { t0: g.time, ..d }));
|
||||
for d in gone {
|
||||
self.dying.push(Dying { t0: g.time, ..d });
|
||||
// the remains appear when the body has sunk
|
||||
self.remains.push((d.x, d.z, g.time + DEATH_T, d.kind, d.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
self.dying.retain(|d| g.time - d.t0 < DEATH_T);
|
||||
// they stay; only the oldest give way when there are very many
|
||||
if self.remains.len() > MAX_REMAINS {
|
||||
self.remains.drain(..self.remains.len() - MAX_REMAINS);
|
||||
}
|
||||
self.seen = now;
|
||||
self.seen_time = g.time;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user