Skip to content

Commit 4447270

Browse files
Tim Willebrandsclaude
andcommitted
feat(engine): edge-override permissions for doors and windows
Edges get an override map keyed by tile pair: present entries decide outright with EDGE_LIGHT/EDGE_MOVE bits (mask 0 seals even same-room), absent entries fall back to room membership. Exposed via set_edge / replace_edges plus edge_light()/edge_move() bit exports for JS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 5496332 commit 4447270

4 files changed

Lines changed: 272 additions & 131 deletions

File tree

examples/cold_start_bench.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
1919
use std::time::Instant;
2020

21-
use bresenham_lighting_engine::engine::LightingEngine;
21+
use bresenham_lighting_engine::engine::{LightingEngine, EDGE_LIGHT, EDGE_MOVE};
2222

2323
const LAYER_SIZE: usize = 30;
2424
const ENGINE_BUFFER_TILES: usize = 1;
@@ -95,15 +95,15 @@ fn main() {
9595

9696
let t = Instant::now();
9797
for (a, b) in &door_pairs {
98-
engine.set_door_edge(*a, *b, true);
98+
engine.set_edge(*a, *b, EDGE_LIGHT | EDGE_MOVE);
9999
}
100-
println!("[4a] set_door_edge × 50 (open) ...... {:?}", t.elapsed());
100+
println!("[4a] set_edge × 50 (open) ........... {:?}", t.elapsed());
101101

102102
let t = Instant::now();
103103
for (a, b) in &door_pairs {
104-
engine.set_door_edge(*a, *b, false);
104+
engine.set_edge(*a, *b, 0);
105105
}
106-
println!("[4b] set_door_edge × 50 (close) ..... {:?}", t.elapsed());
106+
println!("[4b] set_edge × 50 (close) .......... {:?}", t.elapsed());
107107

108108
// Phase 5: re-render the light after door churn (mirrors a frame where
109109
// the lighting system runs after a door toggle invalidated the engine).

src/collision.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use std::collections::HashSet;
1616
use std::sync::{Arc, RwLock};
1717

18-
use crate::engine::DEFAULT_ENGINE;
18+
use crate::engine::{canonical_edge, DEFAULT_ENGINE};
1919
use crate::map_grid::UnionFind;
2020

2121
/// Unified interface for collision detection backends. Kept as a trait so
@@ -153,10 +153,13 @@ pub struct HybridCollisionMap {
153153
map_size: usize,
154154
/// Canonical `(lo, hi)` cell-index pairs where the broad-phase walk is
155155
/// allowed to step between two cells that the union-find considers to be
156-
/// in different rooms. Populated by the engine from its `door_edges`
157-
/// overlay — a door dissolves the wall only along its own cell-edges, not
158-
/// across the entire room boundary (which is what a UF union would do).
159-
door_cell_edges: HashSet<(usize, usize)>,
156+
/// in different rooms. Populated by the engine from its edge overrides —
157+
/// a door dissolves the wall only along its own cell-edges, not across
158+
/// the entire room boundary (which is what a UF union would do).
159+
pass_cell_edges: HashSet<(usize, usize)>,
160+
/// Inverse of `pass_cell_edges`: cell-pairs the walk may NOT step across
161+
/// even within one room (a closed door sealing a same-room edge).
162+
block_cell_edges: HashSet<(usize, usize)>,
160163
}
161164

162165
impl HybridCollisionMap {
@@ -166,7 +169,8 @@ impl HybridCollisionMap {
166169
union_find: Arc::new(RwLock::new(uf)),
167170
pixel_map: PixelCollisionMap::new(map_size as u16, map_size as u16),
168171
map_size,
169-
door_cell_edges: HashSet::new(),
172+
pass_cell_edges: HashSet::new(),
173+
block_cell_edges: HashSet::new(),
170174
}
171175
}
172176

@@ -181,11 +185,16 @@ impl HybridCollisionMap {
181185
&mut self.pixel_map
182186
}
183187

184-
/// Replace the set of open door cell-edges. Each entry is a canonical
185-
/// `(lo, hi)` cell-index pair flagging "the broad-phase walk may step
186-
/// across these two cells even though they are in different rooms".
187-
pub fn set_door_cell_edges(&mut self, edges: HashSet<(usize, usize)>) {
188-
self.door_cell_edges = edges;
188+
/// Replace both cell-edge override sets (canonical `(lo, hi)` pairs).
189+
/// `pass`: the broad-phase walk may step across despite a room boundary.
190+
/// `block`: it may not, despite sharing a room.
191+
pub fn set_edge_cell_overrides(
192+
&mut self,
193+
pass: HashSet<(usize, usize)>,
194+
block: HashSet<(usize, usize)>,
195+
) {
196+
self.pass_cell_edges = pass;
197+
self.block_cell_edges = block;
189198
}
190199

191200
pub fn pixel_map(&self) -> &PixelCollisionMap {
@@ -230,14 +239,18 @@ impl CollisionDetector for HybridCollisionMap {
230239
let next_idx = (py * size + px) as usize;
231240
let next_room = uf.find(next_idx);
232241
if next_room != current_room {
233-
let pair = if prev_idx <= next_idx {
234-
(prev_idx, next_idx)
235-
} else {
236-
(next_idx, prev_idx)
237-
};
238-
if !self.door_cell_edges.contains(&pair) {
242+
if !self
243+
.pass_cell_edges
244+
.contains(&canonical_edge(prev_idx, next_idx))
245+
{
239246
return true;
240247
}
248+
} else if !self.block_cell_edges.is_empty()
249+
&& self
250+
.block_cell_edges
251+
.contains(&canonical_edge(prev_idx, next_idx))
252+
{
253+
return true;
241254
}
242255
current_idx = next_idx;
243256
current_room = next_room;
@@ -252,7 +265,8 @@ impl CollisionDetector for HybridCollisionMap {
252265
*uf = UnionFind::new(vec![0; self.map_size * self.map_size], self.map_size);
253266
}
254267
self.pixel_map.clear();
255-
self.door_cell_edges.clear();
268+
self.pass_cell_edges.clear();
269+
self.block_cell_edges.clear();
256270
}
257271

258272
fn as_any(&self) -> &dyn std::any::Any {

0 commit comments

Comments
 (0)