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.
198 lines
4.4 KiB
Rust
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)
|
|
}
|
|
}
|