diff --git a/src/particles.rs b/src/particles.rs index ecf4bd2..adf4c7c 100644 --- a/src/particles.rs +++ b/src/particles.rs @@ -25,6 +25,7 @@ pub const FLASH: u8 = 8; // expanding fireball pub const RING: u8 = 9; // shock wave pub const SPLASH: u8 = 10; // liquid droplets, bounce on the river surface pub const BURNING_ARROW: u8 = 11; // arrow drifting on lava until it burns up (vx, vy = its direction) +pub const WISP: u8 = 12; // torch smoke: a small pixel that rises in a gentle wave and fades // projectile styles (Fx::style) pub const ST_PLAIN: u8 = 0; @@ -210,6 +211,13 @@ impl Game { p.h += p.vh * dt; } FLASH | RING => {} + WISP => { + // rises and sways a little from side to side + p.h += p.vh * dt; + let sway = (p.t * 3.0 + p.vx * 50.0).sin() * 0.06 * dt; + p.x += sway; + p.y += sway * 0.6; + } BURNING_ARROW => { // drifts slowly with the lava p.x += p.vx * 0.05 * dt; @@ -400,13 +408,16 @@ impl Game { fn spawn_torch_smoke(&mut self, dt: f32) { let spots: Vec<(f32, f32, f32)> = self.torches.iter().filter(|t| self.map.shown(t.wall)).map(|t| (t.pos().0, t.pos().1, t.flame_h())).collect(); for (x, y, fh) in spots { - if self.parts.len() >= PARTS_MAX - 100 { + // torch smoke takes at most half of the particles: explosions and splashes come first + if self.parts.len() >= PARTS_MAX / 2 { break; } - if self.vrng.f() < 1.1 * dt { - let v = ((self.vrng.f() - 0.5) * 0.08, (self.vrng.f() - 0.5) * 0.08, 0.32 + self.vrng.f() * 0.12); - let life = 1.8 + self.vrng.f() * 0.6; - self.part(SMOKE, (x, y, fh + 0.28), v, life, abgr(70, 62, 58), 0.05); + if self.vrng.f() < 3.0 * dt { + // vx only seeds the sway (see WISP in the update) + let v = (self.vrng.f(), 0.0, 0.3 + self.vrng.f() * 0.1); + let life = 1.2 + self.vrng.f() * 0.5; + let size = 0.025 + self.vrng.f() * 0.012; + self.part(WISP, (x, y, fh + 0.3), v, life, abgr(150, 140, 132), size); } if self.vrng.f() < 0.25 * dt { let v = ((self.vrng.f() - 0.5) * 0.3, (self.vrng.f() - 0.5) * 0.3, 0.9); diff --git a/src/render3d.rs b/src/render3d.rs index 92537f7..f7cd8ed 100644 --- a/src/render3d.rs +++ b/src/render3d.rs @@ -2698,7 +2698,6 @@ fn draw_torch_stand(x: f32, z: f32) { // sway and flicker; a soft halo around it (the smoke comes from particles.rs) fn draw_torch_flame((x, z): (f32, f32), base: f32, k: f32, t: f32) { let flick = 0.8 + 0.2 * (t * 13.0 + k * 2.1).sin() + 0.1 * (t * 29.0 + k * 0.7).sin(); - draw_sphere(vec3(x, base + 0.1, z), 0.24 * flick, None, Color::new(1.0, 0.55, 0.15, 0.16)); let n = 6; for i in 0..n { let f = i as f32 / (n - 1) as f32; @@ -2713,6 +2712,9 @@ fn draw_torch_flame((x, z): (f32, f32), base: f32, k: f32, t: f32) { }; draw_cube(vec3(x + sway, h, z + sway2), Vec3::splat(size), None, Color::new(c.x, c.y, c.z, 0.95 - f * 0.4)); } + // the halo last: the glow material writes depth, so anything drawn after it inside the + // sphere would be hidden + draw_sphere(vec3(x, base + 0.1, z), 0.24 * flick, None, Color::new(1.0, 0.55, 0.15, 0.16)); } // particles that glow (embers, sparks, smoke, flashes, shock waves) @@ -2774,6 +2776,10 @@ fn draw_parts_glow(g: &Game) { let life = (p.t / p.max).clamp(0.0, 1.0); let at = vec3(p.x, p.h, p.y); match p.kind { + WISP => { + // a thin thread of smoke: small pixels that fade, they do not grow + draw_cube(at, Vec3::splat(p.size), None, Color::new(c.r, c.g, c.b, 0.55 * (1.0 - life))); + } EMBER | SPARK | MOTE | SPLASH => { let s = p.size * (1.0 - life * 0.6); draw_cube(at, Vec3::splat(s), None, Color::new(c.r, c.g, c.b, 1.0 - life * life));