use bevy::{prelude::*,math::UVec3}; use crate::{MAX_VERTICAL_FLOW,MAX_HORIZONTAL_FLOW,grid::{Grid,GridElement}}; pub struct SimulationPlugin; impl Plugin for SimulationPlugin { fn build(&self, app: &mut App) { app.init_state::() .insert_resource(Time::::from_hz(32.)) .add_systems(FixedUpdate, tick.run_if(in_state(SimulationStatus::Continued))); } } enum GridAction{ MoveLiquid{ from: UVec3, to: UVec3, amount: u32, }, SpawnLiquid { at: UVec3, amount: u32, } } #[derive(States,Default,Hash,Eq,PartialEq,Clone,Debug)] pub enum SimulationStatus { #[default] Paused, Continued, } pub fn tick(mut grid: ResMut) { let mut stack: Vec = Vec::new(); for x in 0..grid.size.x { for y in 0..grid.size.y { for z in 0..grid.size.z { let vector = UVec3::new(x,y,z); let Some(index) = grid.indexify(vector) else { continue; }; match grid.data[index as usize] { GridElement::Water { amount } => { let mut transfer_amount = amount.clone(); if let Some(gravity_vector) = vector.checked_sub(UVec3::new(0,1,0)) { if let Some(gravity_amount) = grid.data [grid.indexify(gravity_vector).unwrap()] .try_fill(transfer_amount.min(MAX_VERTICAL_FLOW)) { stack.push(GridAction::MoveLiquid { from: vector, to: gravity_vector, amount: gravity_amount }); transfer_amount -= gravity_amount; } } transfer_amount = transfer_amount.min(MAX_HORIZONTAL_FLOW) / 4; if transfer_amount > 0 { for lookup_vector in [IVec3::new(1,0,0),IVec3::new(0,0,1),IVec3::new(-1,0,0),IVec3::new(0,0,-1)] { let push_vector = (vector.as_ivec3() + lookup_vector).max(IVec3::ZERO).as_uvec3(); if push_vector == vector { continue; } let Some(push_index) = grid.indexify(push_vector) else { continue; }; let Some(push_amount) = grid.data[push_index].try_fill(transfer_amount) else { continue; }; stack.push(GridAction::MoveLiquid { from: vector, to: push_vector, amount: push_amount }); } } }, GridElement::Source { source_amount } => { if let Some(gravity_vector) = vector.checked_sub(UVec3::new(0,1,0)) { if grid.data[grid.indexify(gravity_vector).unwrap()].can_fill() { stack.push(GridAction::SpawnLiquid { at: gravity_vector, amount: source_amount }); } } for lookup_vector in [IVec3::new(1,0,0),IVec3::new(0,0,1),IVec3::new(-1,0,0),IVec3::new(0,0,-1)] { let push_vector = (vector.as_ivec3() + lookup_vector).max(IVec3::ZERO).as_uvec3(); let Some(push_index) = grid.indexify(push_vector) else { continue; }; if grid.data[push_index].can_fill() == false { continue; } stack.push(GridAction::SpawnLiquid { at: push_vector, amount: source_amount }); } }, _ => {}, } } } } for action in stack { match action { GridAction::MoveLiquid { from, to, amount } => { let (from_index, to_index) = (grid.indexify(from).unwrap(), grid.indexify(to).unwrap()); grid.tranfer_fluid(from_index, to_index, amount); }, GridAction::SpawnLiquid { at, amount } => { let at_index = grid.indexify(at).unwrap(); grid.fill(at_index, amount); } } } }