diff --git a/src/particles.rs b/src/particles.rs index b8fe47d..0b8eff4 100644 --- a/src/particles.rs +++ b/src/particles.rs @@ -191,6 +191,7 @@ impl Game { self.impact(&f); } self.spawn_lava_particles(dt); + self.spawn_torch_smoke(dt); // particles for p in self.parts.iter_mut() { p.t += dt; @@ -395,6 +396,25 @@ impl Game { } } + // a thin wisp of smoke above every known torch, now and then a spark + fn spawn_torch_smoke(&mut self, dt: f32) { + let spots: Vec<(f32, f32)> = self.torches.iter().filter(|t| self.map.shown(t.wall)).map(|t| t.pos()).collect(); + for (x, y) in spots { + if self.parts.len() >= PARTS_MAX - 100 { + 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, 1.0), v, life, abgr(70, 62, 58), 0.05); + } + 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); + self.part(EMBER, (x, y, 0.95), v, 0.9, abgr(255, 170, 60), 0.025); + } + } + } + fn spawn_lava_particles(&mut self, dt: f32) { if self.parts.len() >= PARTS_MAX { return; @@ -466,6 +486,7 @@ mod tests { #[test] fn projectiles_trail_and_explode_and_arrows_stick() { let mut g = Game::new(3, 64, 64); + g.torches.clear(); // no torch smoke in the count below let (cx, cy) = g.map.core; let (x, y) = (cx as f32 + 1.5, cy as f32 - 1.5); g.shot(x, y, 0.5, x + 3.0, y, 0.45, 0.3, 0xffffffff); diff --git a/src/render3d.rs b/src/render3d.rs index 487b39c..a8e1dd0 100644 --- a/src/render3d.rs +++ b/src/render3d.rs @@ -1106,7 +1106,7 @@ impl R3d { for (k, tr) in g.torches.iter().enumerate() { let (x, z) = tr.pos(); let fl = 0.85 + 0.1 * (t * 9.0 + k as f32 * 1.7).sin() + 0.05 * (t * 23.0 + k as f32).sin(); - push(vec3(x, 0.75, z), 4.5, vec3(1.0, 0.62, 0.28), 0.9 * fl, 0.0); + push(vec3(x, 0.85, z), 5.5, vec3(1.0, 0.62, 0.28), 1.5 * fl, 0.0); } let (cx, cy) = g.map.core; let hp = (g.core_hp / CORE_HP).clamp(0.0, 1.0); @@ -1297,10 +1297,7 @@ impl R3d { } } for (k, tr) in g.torches.iter().enumerate().filter(|(_, tr)| g.map.shown(tr.wall)) { - let (x, z) = tr.pos(); - let fl = 1.0 + 0.15 * (t * 11.0 + k as f32 * 2.3).sin(); - draw_sphere(vec3(x, 0.78, z), 0.09 * fl, None, Color::new(1.0, 0.55, 0.15, 0.9)); - draw_sphere(vec3(x, 0.8, z), 0.05 * fl, None, Color::new(1.0, 0.95, 0.6, 1.0)); + draw_torch_flame(tr.pos(), k as f32, t); } draw_shadows(g); if let Some(f) = g.flag { @@ -2648,6 +2645,28 @@ fn draw_traps_glow(g: &Game, t: f32) { } } +// a torch flame: stacked glowing blocks, hot and white at the bottom, red at the tip, that +// sway and flicker; a soft halo around it (the smoke comes from particles.rs) +fn draw_torch_flame((x, z): (f32, f32), k: f32, t: f32) { + let base = 0.72; + 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; + let sway = (t * 7.0 + k * 1.3 + f * 2.5).sin() * 0.035 * f; + let sway2 = (t * 5.3 + k * 2.7 + f * 1.9).cos() * 0.025 * f; + let h = base + f * 0.24 * flick; + let size = (0.11 - f * 0.08) * (1.0 + 0.18 * (t * 17.0 + k * 3.1 + f * 4.0).sin()); + let c = if f < 0.4 { + vec3(1.0, 0.95, 0.65).lerp(vec3(1.0, 0.68, 0.2), f / 0.4) + } else { + vec3(1.0, 0.68, 0.2).lerp(vec3(0.95, 0.25, 0.06), (f - 0.4) / 0.6) + }; + 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)); + } +} + // particles that glow (embers, sparks, smoke, flashes, shock waves) fn draw_scorch_glow(g: &Game) { use crate::particles::*;