fix: rabbit dig occlusion refresh, bounds guard panic, world floor clamp
- Fire TileOcclusionEvent for 27-tile neighbourhood (3x3x3) so tiles above the dug tile recalculate visibility and stop rendering black - Clamp gravity in movement to world minimum z — rabbits digging the floor below them now stop at world floor instead of falling past it - Assert bounds in remove_floor/remove_fixture before pos_to_index to panic with z/coords info instead of silently overflowing - Hoist ITILE_SIZE, CHUNK_SIZE, Z_BELOW, Z_ABOVE to module-level imports in tilemap.rs
This commit is contained in:
@@ -128,9 +128,19 @@ pub fn rabbit_dig_system(
|
||||
|
||||
if tilemap.remove_floor(&below_pos).is_some() {
|
||||
tile_changed.write(TileChangedEvent { pos: below_pos });
|
||||
occlusion.write(TileOcclusionEvent {
|
||||
tile_position: below_pos,
|
||||
});
|
||||
|
||||
// Refresh visibility for the dug tile and all 26 neighbours.
|
||||
// Removing a tile changes what is visible from every adjacent position.
|
||||
for dz in -1..=1i32 {
|
||||
for dy in -1..=1i32 {
|
||||
for dx in -1..=1i32 {
|
||||
occlusion.write(TileOcclusionEvent {
|
||||
tile_position: below_pos
|
||||
+ IVec3::new(dx * ITILE_SIZE, dy * ITILE_SIZE, dz * ITILE_SIZE),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,7 +609,12 @@ pub fn movement(
|
||||
.for_each(|(mut ambulatory, mut transform)| {
|
||||
let current_pos = transform.translation;
|
||||
if !is_standable_tile(&tilemap, current_pos.as_ivec3()) {
|
||||
transform.translation.z -= TILE_SIZE;
|
||||
// Don't fall below world minimum z — rabbits digging can leave no floor
|
||||
// to stand on, but they should stop at the world floor rather than
|
||||
// falling into oblivion and panicking pos_to_index overflow.
|
||||
if transform.translation.z > -(crate::world::chunks::Z_BELOW as f32) * TILE_SIZE {
|
||||
transform.translation.z -= TILE_SIZE;
|
||||
}
|
||||
ambulatory.current_path = None;
|
||||
ambulatory.target = None;
|
||||
return;
|
||||
|
||||
@@ -32,7 +32,8 @@ use bevy::prelude::*;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use super::chunk_data::ChunkData;
|
||||
use crate::world::chunks::world_to_chunk;
|
||||
use crate::constants::ITILE_SIZE;
|
||||
use crate::world::chunks::{world_to_chunk, CHUNK_SIZE, Z_ABOVE, Z_BELOW};
|
||||
|
||||
/// Packed floor tile data for efficient storage. ~35 bytes vs 76 bytes tuple.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@@ -255,12 +256,27 @@ impl TileMap {
|
||||
/// Remove a fixture tile, clearing both the HashMap entry and ChunkData bitsets.
|
||||
/// BOTH must be cleared — leaving ChunkData stale causes is_standable bugs.
|
||||
pub fn remove_fixture(&mut self, pos: &IVec3) -> Option<FixtureTileData> {
|
||||
use super::chunk_data::ChunkData;
|
||||
|
||||
let removed = self.fixture_tiles.remove(pos);
|
||||
let chunk_pos = world_to_chunk(*pos);
|
||||
if let Some(chunk) = self.chunks.get_mut(&chunk_pos) {
|
||||
let (lx, ly, z) = ChunkData::world_to_local(*pos);
|
||||
// Guard bounds before pos_to_index — out-of-range z panics with overflow.
|
||||
assert!(
|
||||
z >= -(Z_BELOW as i32) && z <= Z_ABOVE as i32,
|
||||
"remove_fixture: z={} out of bounds [{}, {}] at pos={:?}",
|
||||
z,
|
||||
-Z_BELOW,
|
||||
Z_ABOVE,
|
||||
pos
|
||||
);
|
||||
assert!(
|
||||
(0..CHUNK_SIZE).contains(&lx) && (0..CHUNK_SIZE).contains(&ly),
|
||||
"remove_fixture: local coords ({}, {}) out of bounds [0, {}) at pos={:?}",
|
||||
lx,
|
||||
ly,
|
||||
CHUNK_SIZE,
|
||||
pos
|
||||
);
|
||||
let idx = ChunkData::pos_to_index(lx, ly, z);
|
||||
let word = idx / 32;
|
||||
let clear_mask = !(1u32 << (idx % 32));
|
||||
@@ -278,6 +294,25 @@ impl TileMap {
|
||||
let chunk_pos = world_to_chunk(*pos);
|
||||
if let Some(chunk) = self.chunks.get_mut(&chunk_pos) {
|
||||
let (lx, ly, z) = ChunkData::world_to_local(*pos);
|
||||
// Guard bounds before pos_to_index — out-of-range z panics with overflow.
|
||||
// This fires when a rabbit digs the floor below the world minimum z,
|
||||
// leaving no standable tile and causing the entity to fall past the floor.
|
||||
assert!(
|
||||
z >= -(Z_BELOW as i32) && z <= Z_ABOVE as i32,
|
||||
"remove_floor: z={} out of bounds [{}, {}] at pos={:?}",
|
||||
z,
|
||||
-Z_BELOW,
|
||||
Z_ABOVE,
|
||||
pos
|
||||
);
|
||||
assert!(
|
||||
(0..CHUNK_SIZE).contains(&lx) && (0..CHUNK_SIZE).contains(&ly),
|
||||
"remove_floor: local coords ({}, {}) out of bounds [0, {}) at pos={:?}",
|
||||
lx,
|
||||
ly,
|
||||
CHUNK_SIZE,
|
||||
pos
|
||||
);
|
||||
let idx = ChunkData::pos_to_index(lx, ly, z);
|
||||
let word = idx / 32;
|
||||
let clear_mask = !(1u32 << (idx % 32));
|
||||
@@ -292,9 +327,6 @@ impl TileMap {
|
||||
/// Iterates all positions in the chunk volume and removes from HashMaps.
|
||||
/// Used during chunk unloading to clean up tile data.
|
||||
pub fn remove_chunk_data(&mut self, chunk_pos: IVec2) {
|
||||
use crate::constants::ITILE_SIZE;
|
||||
use crate::world::chunks::{CHUNK_SIZE, Z_ABOVE, Z_BELOW};
|
||||
|
||||
for local_x in 0..CHUNK_SIZE {
|
||||
for local_y in 0..CHUNK_SIZE {
|
||||
for z in -Z_BELOW as i32..=Z_ABOVE as i32 {
|
||||
|
||||
Reference in New Issue
Block a user