diff options
Diffstat (limited to 'src/sim/cell/materials/fire.rs')
| -rw-r--r-- | src/sim/cell/materials/fire.rs | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/src/sim/cell/materials/fire.rs b/src/sim/cell/materials/fire.rs new file mode 100644 index 0000000..e0416b0 --- /dev/null +++ b/src/sim/cell/materials/fire.rs @@ -0,0 +1,79 @@ +use rand::RngExt; + +use crate::sim::{ + cell::{cell::Cell, materials::MaterialId}, + cell_sim::sim::UpdateCtx, +}; + +trait FireCellView { + fn get_ticks_lived(self) -> u16; + fn set_ticks_lived(&mut self, ticks: u16) -> (); + fn is_flammable(self) -> bool; +} + +impl FireCellView for Cell { + fn get_ticks_lived(self) -> u16 { + self.data + } + fn set_ticks_lived(&mut self, ticks: u16) { + self.data = ticks; + } + // this could be a property of the material def, I think it's better here for now + fn is_flammable(self) -> bool { + [MaterialId::Wood].contains(&self.material) + } +} + +// TODO optimize number of rng calls? +#[inline] +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 { + // kill ourselves, with a chance to turn into ash + if ctx.rng.random_range(0.0..1.0) > 0.8 { + // TODO ash material + ctx.set_cell(0, 0, Cell::from_material(MaterialId::Sand)); + } else { + ctx.set_cell(0, 0, Cell::void()); + } + return; + } + } + + // at an average of 4 times per lifespan, try to spread + // 1/60 * 240 = 4 + if ctx.rng.random_range(0.0..1.0) > (59.0 / 60.0) { + let (dx, dy) = (ctx.rng.random_range(-1..=1), ctx.rng.random_range(-1..=1)); + if let Some(target) = ctx.get_cell(dx, dy) + && target.is_flammable() + { + ctx.set_cell(dx, dy, Cell::from_material(MaterialId::Fire)); + } + } + + // 8 times in our lifespan, emit smoke + if ticks_lived.is_multiple_of(30) + && let Some(target) = ctx.get_cell(0, -1) + && target.material == MaterialId::Void + { + for (dx, dy) in [ + (0, -1), + (1 - ctx.seqno_parity as i32 * 2, 0), + (-1 + ctx.seqno_parity as i32 * 2, 0), + (0, 1), + ] { + if let Some(target) = ctx.get_cell(dx, dy) + && target.material == MaterialId::Void + { + ctx.set_cell(dx, dy, Cell::from_material(MaterialId::Smoke)); + break; + } + } + } + + ctx.cell.set_ticks_lived(ticks_lived + 1); + ctx.set_cell(0, 0, *ctx.cell); +} |
