summaryrefslogtreecommitdiff
path: root/src/sim/sim.rs
diff options
context:
space:
mode:
authorKai Stevenson <kai@kaistevenson.com>2026-08-11 20:41:41 -0700
committerKai Stevenson <kai@kaistevenson.com>2026-08-11 20:41:41 -0700
commit7ce9781f86c56f932dabc3bc84b8d21d7be8fecc (patch)
tree25bd371970910934a1957a09e2e84936021d73af /src/sim/sim.rs
parent6f67586b8fc6efdb86d0c9a9744c546da97c4dab (diff)
refactor sim loop
Diffstat (limited to 'src/sim/sim.rs')
-rw-r--r--src/sim/sim.rs149
1 files changed, 39 insertions, 110 deletions
diff --git a/src/sim/sim.rs b/src/sim/sim.rs
index 186483d..350b823 100644
--- a/src/sim/sim.rs
+++ b/src/sim/sim.rs
@@ -1,9 +1,32 @@
-use core::range::Range;
+use crate::{Board, sim::board::Cell};
-use crate::{
- Board,
- sim::materials::{MATERIALS, MaterialId},
-};
+pub struct UpdateCtx<'a> {
+ pub self_x: i32,
+ pub self_y: i32,
+ pub self_cell: Cell,
+ pub delta_time: f32,
+ pub seqno_parity: u8,
+ pub board: &'a mut Board,
+}
+
+impl UpdateCtx<'_> {
+ pub fn candidates_swap(&mut self, candidates: &[(i32, i32)]) -> bool {
+ for candidate in candidates {
+ let target = self.board.cell_at_position(candidate.0, candidate.1);
+ if let Some(target) = target
+ && target.material.def().density < self.self_cell.material.def().density
+ {
+ // swap the cells
+ self.board
+ .set_cell_at_position(self.self_x, self.self_y, target);
+ self.board
+ .set_cell_at_position(candidate.0, candidate.1, self.self_cell);
+ return true;
+ }
+ }
+ false
+ }
+}
// TODO: chunks
pub fn sim_tick(board: &mut Board, seqno: u64, delta_time: f32) {
@@ -18,115 +41,21 @@ pub fn sim_tick(board: &mut Board, seqno: u64, delta_time: f32) {
let x = if seqno_parity == 0 { col } else { -col };
let cell = board.cell_at_position(x, y);
- if let Some(cell) = cell
- && cell.flags & 0b1 == seqno_parity
+ if let Some(mut cur) = cell
+ && cur.flags & 0b1 == seqno_parity
{
- let mut cur = cell.clone();
// flip the parity bit
cur.flags = cur.flags ^ 0b1;
- let material = &MATERIALS[cur.material as usize];
-
- match cur.material {
- MaterialId::Void => {}
- // TODO abstract density based movement
- MaterialId::Sand => {
- for candidate in [
- (x, y + 1),
- (x - 1 + 2 * seqno_parity as i32, y + 1),
- (x + 1 - 2 * seqno_parity as i32, y + 1),
- ] {
- let target = board.cell_at_position(candidate.0, candidate.1);
- if let Some(target) = target
- && MATERIALS[target.material as usize].density < material.density
- {
- // swap the cells
- board.set_cell_at_position(x, y, target);
- board.set_cell_at_position(candidate.0, candidate.1, cur);
- break;
- }
- }
- }
- MaterialId::Water => 'water: {
- // if the water can fall, do so
- for candidate in [
- (x, y + 1),
- (x - 1 + 2 * seqno_parity as i32, y + 1),
- (x + 1 - 2 * seqno_parity as i32, y + 1),
- ] {
- let target = board.cell_at_position(candidate.0, candidate.1);
- if let Some(target) = target
- && MATERIALS[target.material as usize].density < material.density
- {
- // swap the cells
- board.set_cell_at_position(x, y, target);
- board.set_cell_at_position(candidate.0, candidate.1, cur);
- break 'water;
- }
- }
- // if the water can't fall, check if we can move left or right
- // these are inverted on parity so that we don't preference a direction
- let left_target = board.cell_at_position(x - 1, y);
- let can_move_left = left_target.is_some_and(|c| {
- MATERIALS[c.material as usize].density < material.density
- });
- let right_target = board.cell_at_position(x + 1, y);
- let can_move_right = right_target.is_some_and(|c| {
- MATERIALS[c.material as usize].density < material.density
- });
-
- // we can't move down or to other side, so we're stuck
- if !can_move_left && !can_move_right {
- break 'water;
- }
-
- // find the closest hole within 20 pixels (TODO optimize)
- // a hole is any space below us with a lesser density
- // prevents equidistance stuck state
- let starting_side = if seqno_parity == 0 { 1 } else { -1 };
- for i in 0..20 {
- let side = if i % 2 == 0 {
- starting_side
- } else {
- -starting_side
- };
- if (side == 1 && !can_move_right) || (side == -1 && !can_move_left) {
- continue;
- }
-
- let offset = side * (1 + i / 2);
-
- let target = board.cell_at_position(x + offset, y + 1);
- if let Some(target) = target
- && MATERIALS[target.material as usize].density < material.density
- {
- // we identified a hole and we know that the space on this side is open
- // move toward the hole
- let mut new_target =
- if side == 1 { right_target } else { left_target }.clone();
- // new_target.flags = new_target.flags ^ 0b1;
- // safe to unwrap
- board.set_cell_at_position(x, y, new_target.unwrap());
- board.set_cell_at_position(x + side, y, cur);
- break 'water;
- }
- }
-
- // we didn't find a hole, so just move "randomly" on the same surface
- // TODO when to settle?
- let (target, target_x) = if !can_move_left {
- (right_target, 1)
- } else if !can_move_right {
- (left_target, -1)
- } else if seqno_parity % 2 == 1 {
- (right_target, 1)
- } else {
- (left_target, -1)
- };
-
- board.set_cell_at_position(x, y, target.unwrap());
- board.set_cell_at_position(x + target_x, y, cur);
- }
+ if let Some(update) = cur.material.def().sim_update {
+ update(&mut UpdateCtx {
+ self_x: x,
+ self_y: y,
+ self_cell: cur,
+ board,
+ delta_time,
+ seqno_parity,
+ });
}
}
}