Files
dorf/src/world/tiles/tilemap.rs
T
popertots 00cbd92491 refactor: simplify to time-sliced synchronous path queue
The async pathfinding with StandableBitGrid caused massive lag due to synchronous
bounding box calculation in the main thread (millions of hashmap lookups for long paths).
Additionally, the splicing logic had a catastrophic bug where it applied a single
finished path to ALL entities unconditionally.

Solution:
- Revert to thread-local synchronous A*
- Implement PathRequestQueue to process max 8 paths per frame
- Keep provisional paths for immediate movement
- Since entity walks provisional path, full path calculates from current position,
  eliminating the need for complex splicing logic.
2026-03-18 17:23:48 +00:00

198 lines
4.4 KiB
Rust

use bevy::prelude::*;
use rustc_hash::FxHashMap;
use crate::constants::ITILE_SIZE;
/// Packed floor tile data for efficient storage. ~35 bytes vs 76 bytes tuple.
#[derive(Clone, Copy, Debug)]
pub struct FloorTileData {
pub id: u8,
/// bit0=can_stand_in, bit1=can_stand_on, bit2=visibly_transparent
pub flags: u8,
pub astar_weight: u8,
pub visible_range: [u32; 8],
}
impl Default for FloorTileData {
fn default() -> Self {
Self {
id: 0,
flags: 0b001,
astar_weight: 0,
visible_range: [0; 8],
}
}
}
impl FloorTileData {
pub fn new(
id: u8,
can_stand_in: bool,
can_stand_on: bool,
visibly_transparent: bool,
astar_weight: u8,
visible_range: [u32; 8],
) -> Self {
let mut flags = 0u8;
if can_stand_in {
flags |= 0b001;
}
if can_stand_on {
flags |= 0b010;
}
if visibly_transparent {
flags |= 0b100;
}
Self {
id,
flags,
astar_weight,
visible_range,
}
}
#[inline]
pub fn can_stand_in(&self) -> bool {
self.flags & 0b001 != 0
}
#[inline]
pub fn can_stand_on(&self) -> bool {
self.flags & 0b010 != 0
}
#[inline]
pub fn visibly_transparent(&self) -> bool {
self.flags & 0b100 != 0
}
#[inline]
pub fn set_can_stand_in(&mut self, value: bool) {
if value {
self.flags |= 0b001;
} else {
self.flags &= !0b001;
}
}
#[inline]
pub fn set_can_stand_on(&mut self, value: bool) {
if value {
self.flags |= 0b010;
} else {
self.flags &= !0b010;
}
}
#[inline]
pub fn set_visibly_transparent(&mut self, value: bool) {
if value {
self.flags |= 0b100;
} else {
self.flags &= !0b100;
}
}
}
/// Packed fixture tile data. ~18 bytes vs 48 bytes tuple.
#[derive(Clone, Copy, Debug)]
pub struct FixtureTileData {
pub id: u8,
/// bit0=can_stand_in, bit1=can_stand_on
pub flags: u8,
pub visible_range: [u32; 8],
}
impl Default for FixtureTileData {
fn default() -> Self {
Self {
id: 0,
flags: 0,
visible_range: [0; 8],
}
}
}
impl FixtureTileData {
pub fn new(id: u8, can_stand_in: bool, can_stand_on: bool, visible_range: [u32; 8]) -> Self {
let mut flags = 0u8;
if can_stand_in {
flags |= 0b001;
}
if can_stand_on {
flags |= 0b010;
}
Self {
id,
flags,
visible_range,
}
}
#[inline]
pub fn can_stand_in(&self) -> bool {
self.flags & 0b001 != 0
}
#[inline]
pub fn can_stand_on(&self) -> bool {
self.flags & 0b010 != 0
}
}
/// Tile map using FxHashMap for fast lookups. No Arc wrapper - single-threaded access.
#[derive(Resource, Default)]
pub struct TileMap {
pub floor_tiles: FxHashMap<IVec3, FloorTileData>,
pub fixture_tiles: FxHashMap<IVec3, FixtureTileData>,
pub item_tiles: FxHashMap<IVec3, Vec<u32>>,
}
impl TileMap {
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn get_floor(&self, pos: &IVec3) -> Option<&FloorTileData> {
self.floor_tiles.get(pos)
}
#[inline]
pub fn get_fixture(&self, pos: &IVec3) -> Option<&FixtureTileData> {
self.fixture_tiles.get(pos)
}
#[inline]
pub fn has_floor(&self, pos: &IVec3) -> bool {
self.floor_tiles.contains_key(pos)
}
#[inline]
pub fn has_fixture(&self, pos: &IVec3) -> bool {
self.fixture_tiles.contains_key(pos)
}
#[inline]
pub fn insert_floor(&mut self, pos: IVec3, tile: FloorTileData) {
self.floor_tiles.insert(pos, tile);
}
#[inline]
pub fn insert_fixture(&mut self, pos: IVec3, tile: FixtureTileData) {
self.fixture_tiles.insert(pos, tile);
}
#[inline]
pub fn insert_item(&mut self, pos: IVec3, entity_id: u32) {
self.item_tiles.entry(pos).or_default().push(entity_id);
}
#[inline]
pub fn remove_item(&mut self, pos: &IVec3) -> Option<Vec<u32>> {
self.item_tiles.remove(pos)
}
#[inline]
pub fn get_floor_mut(&mut self, pos: &IVec3) -> Option<&mut FloorTileData> {
self.floor_tiles.get_mut(pos)
}
}