From a216e61a00bde792a49a92593012f9baecf86f3d Mon Sep 17 00:00:00 2001 From: Kai Stevenson Date: Fri, 21 Aug 2026 00:14:10 -0700 Subject: explosions --- src/main.rs | 59 +++++++++++++++++++++++++++++++++++++++--- src/sim/cell/materials/fire.rs | 9 ++++--- src/sim/cell/materials/mod.rs | 8 +++--- src/sim/cell_sim/sim.rs | 8 +++--- src/sim/particle_sim/mod.rs | 11 +++++--- 5 files changed, 77 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/main.rs b/src/main.rs index 08bc61c..c947f05 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,8 +4,8 @@ mod renderer; mod sim; use futures::executor; -use glam::Vec2; -use rand::random_range; +use glam::{IVec2, Vec2}; +use rand::{random_range, random_ratio}; use std::{collections::VecDeque, sync::Arc, time::Instant}; use winit::{ application::ApplicationHandler, @@ -23,7 +23,10 @@ use crate::{ config::{PHYSICS_DELTA_TIME, PHYSICS_FPS, SIM_FPS, WINDOW_TITLE}, renderer::RendererState, sim::{ - cell::{cell::Cell, materials::MaterialId}, + cell::{ + cell::Cell, + materials::{MaterialId, fire::FireCellView}, + }, cell_sim::{sim::sim_tick, world::World}, particle_sim::{ParticleManager, particle::Particle}, rb_sim::{DebugRenderMode, RbSimManager, debug_ops::DebugOperator}, @@ -54,6 +57,8 @@ struct Input { trigger_test_1: bool, trigger_test_2: bool, trigger_test_3: bool, + trigger_test_4: bool, + trigger_test_5: bool, // keybindings is_up_pressed: bool, @@ -144,6 +149,50 @@ impl App { } } + if self.input.trigger_test_4 + && let Some(lm) = self.input.last_mouse_world_pos + && let Some(world) = &mut self.world + && let Some(pm) = &mut self.particle_manager + && let Some(rbsm) = &mut self.rb_sim_manager + { + let mouse = Vec2::new(lm.0, lm.1); + self.input.trigger_test_4 = false; + let r: i32 = 30; + for x in -r..r { + for y in -r..r { + if x.pow(2) + y.pow(2) < r.pow(2) { + // if there's a cell, convert it to a particle + let pos = Vec2::new(mouse.x + x as f32, mouse.y + y as f32); + let ipos = pos.round().as_ivec2(); + if let Some(cell) = world.get_cell_from_game_position(ipos.x, ipos.y) { + let mut fire = Cell::from_material(MaterialId::Fire); + fire.set_ticks_lived(300); + fire.match_parity(self.sim_seqno + 1); + if cell.material == MaterialId::Void && random_range(0.0..1.0) > 0.5 { + world.set_cell_from_game_position(ipos.x, ipos.y, fire, false); + } else { + world.set_cell_from_game_position(ipos.x, ipos.y, fire, false); + if random_range(0.0..1.0) > 0.6 { + let d = pos.distance(mouse); + if d < 0.001 { + continue; + } // skip the center cell + let dir = (pos - mouse) / d; // normalize, reusing d + let falloff = 1.0 - (d / r as f32); // 1 at center, 0 at edge + let vel = (Vec2::new(0.0, -0.6) + dir) + * 300.0 + * random_range(0.5..1.5) + * falloff; + pm.particles + .push(Particle::new(pos, vel, cell.material, 5.0)); + } + } + } + } + } + } + } + // --TEST DRAWING-- if (self.input.is_lmb_pressed || self.input.is_rmb_pressed) && let Some(lm) = self.input.last_mouse_world_pos @@ -273,6 +322,8 @@ impl Default for App { trigger_test_1: false, trigger_test_2: false, trigger_test_3: false, + trigger_test_4: false, + trigger_test_5: false, is_lmb_pressed: false, is_rmb_pressed: false, is_up_pressed: false, @@ -406,6 +457,8 @@ impl ApplicationHandler for App { KeyCode::Digit1 if pressed && !repeat => self.input.trigger_test_1 = true, KeyCode::Digit2 if pressed && !repeat => self.input.trigger_test_2 = true, KeyCode::Digit3 if pressed && !repeat => self.input.trigger_test_3 = true, + KeyCode::Digit4 if pressed && !repeat => self.input.trigger_test_4 = true, + KeyCode::Digit5 if pressed && !repeat => self.input.trigger_test_5 = true, _ => {} } } diff --git a/src/sim/cell/materials/fire.rs b/src/sim/cell/materials/fire.rs index dd1ff8c..f582a0c 100644 --- a/src/sim/cell/materials/fire.rs +++ b/src/sim/cell/materials/fire.rs @@ -5,7 +5,7 @@ use crate::sim::{ cell_sim::sim::UpdateCtx, }; -trait FireCellView { +pub trait FireCellView { fn get_ticks_lived(self) -> u16; fn set_ticks_lived(&mut self, ticks: u16) -> (); fn is_flammable(self) -> bool; @@ -31,12 +31,13 @@ pub fn sim_update(ctx: &mut UpdateCtx) { let ticks_lived = ctx.cell.get_ticks_lived(); // 2 seconds if ticks_lived > 240 { - // we have a chance to live longer--roughly 50% chance of living an extra second - if ctx.rng.random_range(0.0..1.0) > 0.995 { + // we have a chance to live longer--roughly ?% chance of living an extra second + if ctx.rng.random_range(0.0..1.0) > 0.9 { // kill ourselves, with a chance to turn into ash + ctx.do_rewrite = false; if ctx.rng.random_range(0.0..1.0) > 0.8 { // TODO ash material - ctx.set_cell(0, 0, Cell::from_material(MaterialId::Sand)); + // ctx.set_cell(0, 0, Cell::from_material(MaterialId::Sand)); } else { ctx.set_cell(0, 0, Cell::void()); } diff --git a/src/sim/cell/materials/mod.rs b/src/sim/cell/materials/mod.rs index 9bcf5e4..e2b5fc6 100644 --- a/src/sim/cell/materials/mod.rs +++ b/src/sim/cell/materials/mod.rs @@ -1,9 +1,9 @@ use crate::sim::cell_sim::sim::UpdateCtx; -mod fire; -mod gas; -mod liquid; -mod powder; +pub mod fire; +pub mod gas; +pub mod liquid; +pub mod powder; #[repr(u8)] #[derive(Clone, Copy, PartialEq, Eq, Debug)] diff --git a/src/sim/cell_sim/sim.rs b/src/sim/cell_sim/sim.rs index 9b07b98..3fc3a0f 100644 --- a/src/sim/cell_sim/sim.rs +++ b/src/sim/cell_sim/sim.rs @@ -171,7 +171,7 @@ pub struct UpdateCtx<'a, 'b, 'c> { pub material: &'c MaterialDef, pub rng: &'c mut dyn Rng, - swapped: bool, + pub do_rewrite: bool, } impl UpdateCtx<'_, '_, '_> { @@ -202,7 +202,7 @@ impl UpdateCtx<'_, '_, '_> { self.cell.match_parity(self.seqno + 1); self.set_cell(0, 0, candidate_cell); self.set_cell(dx, dy, *self.cell); - self.swapped = true; + self.do_rewrite = false; return true; } } @@ -245,12 +245,12 @@ pub fn sim_tick_chunk(chunks: &mut [Option<&mut Chunk>; 9], seqno: u64) { // TODO this is platform-dependent, will break for multiplayer rng: &mut rng, - swapped: false, + do_rewrite: true, }; update(&mut update_ctx); - if !update_ctx.swapped { + if update_ctx.do_rewrite { // the cell didn't move, so it's more settled, and we also need to update its state for parity cell.increment_settled(); set_cell(chunks, x, y, cell); diff --git a/src/sim/particle_sim/mod.rs b/src/sim/particle_sim/mod.rs index 8bad61d..08febfb 100644 --- a/src/sim/particle_sim/mod.rs +++ b/src/sim/particle_sim/mod.rs @@ -3,7 +3,10 @@ use glam::{IVec2, Vec2}; use crate::{ config::PIXELS_TO_METRES, sim::{ - cell::{cell::Cell, materials::MaterialId}, + cell::{ + cell::Cell, + materials::{MaterialForm, MaterialId}, + }, cell_sim::world::World, particle_sim::particle::Particle, }, @@ -56,7 +59,8 @@ impl ParticleManager { let mut prev = cur; if let Some(cell) = world.get_cell_from_game_position(prev.x, prev.y) - && cell.material != MaterialId::Void + && [MaterialForm::Solid, MaterialForm::Powder] + .contains(&cell.material.def().form) { // already occupied, die self.particles.swap_remove(i); @@ -71,7 +75,8 @@ impl ParticleManager { t_max.y += delta.y; } if let Some(cell) = world.get_cell_from_game_position(cur.x, cur.y) - && cell.material != MaterialId::Void + && [MaterialForm::Solid, MaterialForm::Powder] + .contains(&cell.material.def().form) { // write ourselves to the board world.set_cell_from_game_position( -- cgit v1.3.1