Commit Graph
100 Commits
Author SHA1 Message Date
popertots 1f5dfe5182 Task fix attempt 2026-03-23 19:31:30 +00:00
popertots 3632836016 fix 2026-03-22 22:30:15 +00:00
popertots bf81f42ced fix 2026-03-22 22:10:00 +00:00
popertots 57764fbcbb fix 2026-03-22 22:05:08 +00:00
popertots c448ef4bff fix 2026-03-22 22:01:11 +00:00
popertots 760f6602cf fix 2026-03-22 16:51:20 +00:00
popertots ffb0e79cf3 fix 2026-03-22 16:45:46 +00:00
popertots 738f595f67 debug 2026-03-22 16:38:54 +00:00
popertots e7a43457a3 fix 2026-03-22 16:05:38 +00:00
popertots bceec18a7a log sprite 2026-03-22 16:05:33 +00:00
popertots c46e23af06 fix 2026-03-22 15:23:44 +00:00
popertots 3b9b2d9c88 fix 2026-03-22 15:07:05 +00:00
popertots 5a830f46f9 demo part 2 2026-03-22 14:57:59 +00:00
popertots 54eabe85e2 fix 2026-03-22 14:34:59 +00:00
popertots 57e377c093 fix 2026-03-22 13:01:10 +00:00
popertots 2eaf0196a5 demo 1 2026-03-22 12:56:52 +00:00
popertots 360ffc7526 value tweaks 2026-03-22 12:06:33 +00:00
popertots 26a861e1a5 value tweaks 2026-03-22 12:00:17 +00:00
popertots cb5b18008d fix 2026-03-22 11:50:53 +00:00
popertots 2f456f80b8 Gaussian idle 2026-03-22 11:29:50 +00:00
popertots b2dc0cb864 fix 2026-03-22 11:02:36 +00:00
popertots 749913a028 fix? 2026-03-22 01:58:34 +00:00
popertots adc236dc3d fix 2026-03-22 01:52:20 +00:00
popertots 9afcb0f612 more debug 2026-03-22 01:32:22 +00:00
popertots 64a69d6a40 Debug 2026-03-22 01:24:08 +00:00
popertots deb3f741b0 rng fix 2026-03-22 01:00:42 +00:00
popertots 71653350d8 fix 2026-03-22 00:52:06 +00:00
popertots cafe376841 Plugin cleanup 2026-03-22 00:43:03 +00:00
popertots d61baaf1ba Task Infrastructure — Task Enum, Queue, Events, Executor 2026-03-22 00:35:22 +00:00
popertots 458068fb87 run_if 2026-03-22 00:03:57 +00:00
popertots ff149117ca remove Digger from dorf 2026-03-21 23:56:03 +00:00
popertots 029e4162d8 fixes 2026-03-21 23:48:49 +00:00
popertots c776b91a44 haulage system WIP 2026-03-21 23:39:58 +00:00
popertots dcdfb375f0 Fix 2026-03-21 21:51:29 +00:00
popertots f52c8ab18c inventory: early return before HashSet alloc, restore doc comments 2026-03-21 21:44:06 +00:00
popertots b7b7af5560 inventory: linter fixes - saturating_add, SmallVec<4>, new_default, drop PIG_SLOTS 2026-03-21 21:39:52 +00:00
popertots 2f9360c8bc inv update 2026-03-21 21:27:26 +00:00
popertots 40b5d8ce95 Fix forrestry 2026-03-21 19:20:57 +00:00
popertots 1ddcb538df tree felling optimisations 2026-03-21 19:14:14 +00:00
popertots 4fc4843ed7 fix 2026-03-21 19:10:04 +00:00
popertots 03c3e5d6d7 first pass at tree interaction 2026-03-21 18:57:15 +00:00
popertots cabe944f1b use constants better 2026-03-21 17:51:05 +00:00
popertots f96c71b61b docs: update README architecture diagram with magic_numbers locations 2026-03-21 17:31:57 +00:00
popertots 7fe33d4b2d refactor: split magic_numbers into per-module files
Extracted all doc-commented magic numbers into module-local magic_numbers.rs
files, co-located with the module that owns them. No logic changes.

- entities/shared_systems/magic_numbers: pathfinding + dig constants
- world/generation/terrain/magic_numbers: terrain + cave constants
- world/generation/forestry/magic_numbers: tree constants
- world/tiles/magic_numbers: A*, benchmark, memory constants
- entities/item/magic_numbers: item sprite z-offset
- Removed src/magic_numbers.rs (central file deleted)

Constants imported as `crate::{module}::magic_numbers::*` from each
consumer. src/constants.rs retains only structural constants (PIXEL_RATIO,
TILE_SIZE, SEED, pathfinding tier thresholds).
2026-03-21 17:31:39 +00:00
popertots 776d3caefc fix: dig_rng sequential multiply-add mixing
Switch from add-then-XOR pattern to proper sequential multiply-add
avalanche. Each coordinate feeds into the next multiply, preventing
correlation between adjacent tiles when rare drops are introduced.
2026-03-21 17:10:52 +00:00
popertots 2622144cdf fixed typo 2026-03-21 17:09:32 +00:00
popertots 5b563bad67 perf: SmallVec inline storage, TOML drop tables, seeded deterministic RNG
- SmallVec<[DropEntry; 2]> replaces Vec in DropTable: zero heap allocation for
  all current tiles (0 or 1 entries), spills to heap only at 3+
- DropEntry fields packed to u8 (chance_pct, min_count, max_count): ~12 bytes ->
  4 bytes; derives Copy so no extra clones
- Replaced SystemTime pseudo-RNG with dig_rng(pos): PCG-style hash of world SEED
  + tile position. Deterministic per world, same dig = same roll every time
- TOML-driven drop tables: assets/drop_tables.toml (grass=5%/1-2, rock=10%/1,
  dirt/air=none). TOML parsed at startup into DropTableRegistry resource
- OnceLock global map for async terrain generation tasks to access drop tables
  without Bevy resource borrowing
- terrain.rs: DropTableRegistry::global_get("tile") replaces hardcoded drop_table_for
2026-03-21 16:06:41 +00:00
popertots f03651e0cf docs: update README for generic digging, drop tables, revised architecture
- Add generic digging + drop tables to implemented list
- Rabbit digging: now using Digger component, not debug code
- Building and construction: updated to reflect generic system in place
- Architecture: added digging to shared_systems, drop_table to tiles
2026-03-21 15:41:20 +00:00
popertots ce3746366c refactor: extract generic digging system, drop tables, remove debug code
- New src/entities/item/drop_table.rs: DropEntry (chance, min/max count,
  pseudo-RNG roll) + DropTable wrapper. grass=5% coin 1-2, rock=10% coin 1,
  dirt/air=none.
- New src/entities/shared_systems/digging.rs: Digger component + dig_system.
  Any entity with Digger digs the tile below on its interval. Replaces
  rabbit_dig_system/rabbit_fall_debug_system/RabbitDigTimer/RabbitFallDebug.
- New TileMap::dig_floor: remove_floor + insert air. Used by dig_system.
- FloorTileData: added drop_table field. Lost Copy derive (Vec field).
  Updated all 6 terrain.rs call sites with per-tile drop tables.
- Pig: removed PigDropTimer + pig_drop_system. Drops were debug placeholder.
  TODO added for future loot-on-death/butcher system.
- Rabbit: removed all debug components/systems. Now uses Digger::new(5.0).
- main.rs: removed rabbit_dig_system/rabbit_fall_debug_system/pig_drop_system,
  added shared_systems::digging::dig_system.
2026-03-21 15:40:34 +00:00
popertots 1a170ea9a9 docs: update README with collision, tile events, rabbit dig, visibility notes
- Add Stage 2 collision section documenting convoy / head-on / E/S yield / W/N right-of-way
- Add TileChangedEvent + path invalidation section
- Add Rabbit digging to implemented list
- Add visibility raycast note (absent tiles in loaded chunks = air)
- Remove stale warning count from build instructions
2026-03-21 15:23:11 +00:00
popertots 9d2f41faed fix: insert_floor syncs ChunkData, rabbit replaces dug tile with air
- TileMap::insert_floor now syncs ChunkData bits (set_floor_tile) so is_standable
  returns correct values for tiles inserted via insert_floor. Previously the
  HashMap was updated but ChunkData remained stale, causing is_standable to
  return false for valid tiles.
- rabbit_dig_system inserts air tile at the dug position after remove_floor.
  Without this, the HashMap entry is absent and ChunkData bits stay 0 (both
  false), making is_standable return false at that position. The entity then
  falls through multiple levels. Air's can_stand_in=true means the space is
  passable — the entity falls through it and lands on solid ground below.
2026-03-21 15:09:40 +00:00
popertots 2c92bdfe28 fix: remove_floor clears only stand_on_floor, not stand_in_floor
Clearing stand_in_floor when removing a floor tile made the dug position
impassable. An entity falling into the dug slot found (false || false) && true
= false and kept falling. Now only stand_on_floor is cleared — the space
reverts to air (passable), and only the tile above loses its platform.
2026-03-21 14:58:28 +00:00
popertots 89693f968c add debug 2026-03-21 14:50:31 +00:00
popertots ec5287ca86 fix: loaded-chunk guard in calculate_visibility, reset dig timer while airborne
- calculate_visibility: None from floor_tiles.get() now checks tilemap.chunks
  to confirm the neighbour's chunk is loaded before treating it as air.
  Without this, unloaded world borders and pending chunks were incorrectly
  seen as open space, causing underground tiles to render as black.
- rabbit_dig_system: skip and reset timer when entity is not on standable
  ground, preventing rapid-dig cascade as rabbit falls through multiple levels.
2026-03-21 14:25:28 +00:00
popertots 36f068bf0c fix: treat absent tiles as air in visibility, full-column occlusion refresh
- calculate_visibility: None from floor_tiles.get() now breaks touches_air=true.
  Absent tile = open space = air for visibility purposes. Previously, removed
  tiles left their below-neighbours with all-zero visible_range (black).
- rabbit_dig_system: fire TileOcclusionEvent for full Z_BELOW+1 level column
  below the dug tile + 8 XY neighbours each, not just 3x3x3. calculate_visibility
  traces up to Z_TOTAL+Z_BELOW+1 tiles above each tile, so removing one tile
  can expose visibility arbitrarily deep below.
2026-03-21 12:30:56 +00:00
popertots 0ebc48ff40 fix: snap-to-top for above-world spawn, guard rabbit dig at world floor
- Entities spawn at z=35*TILE=560 but Z_ABOVE max is 15*TILE=240. Snap to
  Z_ABOVE*TILE instead of falling one tile per tick through unloaded space.
- Skip rabbit dig when below_pos would be outside ChunkData z-bounds,
  preventing remove_floor assert panic.
2026-03-21 12:16:58 +00:00
popertots 4d702bc8f5 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
2026-03-21 12:09:49 +00:00
popertots 5357a69930 feat: stage 2 collision, TileChangedEvent, path invalidation, rabbit digging
- Stage 2 collision: convoy skip when entities move same direction (dot>0.7),
  E/S yield rule for crossings, W/N right-of-way, head-on unchanged
- TileChangedEvent (Message) + PathfindingDirtyChunks (Resource) in new tile_changed module
- collect_pathfinding_dirty_chunks / invalidate_paths_on_tile_change /
  clear_pathfinding_dirty_chunks in PathfindingPlugin FixedUpdate chain
- TileMap::remove_floor clears HashMap + ChunkData bitsets + tile_ids
- RabbitDigTimer component: rabbits dig floor below every 5s, fires TileChangedEvent
  and TileOcclusionEvent for path invalidation + rendering
2026-03-21 11:56:51 +00:00
popertots d325aa6488 feat: fix log fixture passthrough, improve collision, add move_direction
Fix 1 — insert_fixture now updates ChunkData bitsets:
  Log trunks (can_stand_in=false) now block movement in is_standable.
  Leaf canopy (can_stand_in=true) remains walkable.

Fix 2 — Diagonal-aware collision avoidance:
  Sidestep priority: left+forward, left, right+forward, right.
  If all blocked: push through (excuse me), advance path, apply one-step
  delay (speed-modulated recovery). Does not stack delays.

Fix 3 — Movement direction tracking:
  Ambulatory now has move_direction (Vec2) and step_history ([i16; 4]).
  TileOccupancy tracks per-tile direction hints for Stage 2 collision
  (convoy skip, E/S yield) via direction_at().
  move_direction is smoothed from: 2 historical steps, current confirmed
  step, and 2-step path lookahead.

Ignore: add *.patch and *.diff to .gitignore
2026-03-21 11:07:35 +00:00
popertots f0cb86e73c fix: compare next vs current occupancy counts instead of absolute threshold
Two entities on the same tile (16,-0.0) each saw count=2 and blocked each
other with occupied=true since 2>1. The collision check was treating the
entity's own presence as a blocker.

Now: only trigger avoidance when next_tile has strictly more entities than
current_tile. Handles path[0]=current_pos (count equal → move), two entities
passing through same tile (counts stay equal → move), and genuinely crowded
destinations (next has more → dodge). Also raise crowded threshold to 4+.
2026-03-21 10:35:35 +00:00
popertots 31516f5c58 0 -> 1 2026-03-21 10:31:08 +00:00
popertots dd703f05f3 fix: remove collision_delay to eliminate movement deadlock
collision_delay was causing entities to skip step_recovery ticks, freezing
movement whenever the next tile was occupied. Combined with the ordering fix
(collision_delay placed before step_recovery), entities were stuck at
path_index=0 indefinitely in spawn clusters.

Remove collision_delay entirely — step_recovery already provides the
per-tick pacing, and the natural next-tick retry (via left-step or
normal movement) handles occupied tiles without an artificial delay.

Actual thresholds (grass weight=50): dorfs move after 6 ticks, pigs after
26 ticks, rabbits after 2 ticks. Without collision_delay interference,
movement is now governed purely by step_recovery.
2026-03-21 10:21:44 +00:00
popertots e60d057276 fix: move collision_delay after step_recovery to prevent deadlock
collision_delay was placed before the walk_speed/step_recovery block,
causing step_recovery to never tick when collision_delay was active —
entities stayed permanently pre-empted at path_index=0.

Correct order: step_recovery (must tick every frame) before
collision_delay (only checked when entity is ready to move).
2026-03-21 01:45:36 +00:00
popertots b4804bee5a fix gitignore 2026-03-21 01:37:44 +00:00
popertots 90a6196443 feat: soft entity collision, dead code removal, leaf standability fix
- Add TileOccupancy resource and rebuild_tile_occupancy system to track
  per-tile entity counts for soft collision avoidance
- Add collision_delay to Ambulatory; entities try relative-left step on
  occupied tiles, wait 1 tick, then push through
- Fix leaf canopy standability: leaves are now can_stand_in=true,
  can_stand_on=false (walkable, not standable-on)
- Delete FloorTilePrefab / FixtureTilePrefab / FloorTile / FixtureTile /
  TileState (all fully dead — TileMap + ChunkData are sole truth)
- Delete tile_spawns from TerrainBlob (populated but never consumed)
- Delete leaf ghost entity spawn in forestry (orphaned invisible ECS entity)
- Replace log prefab spawn with inline commands.spawn(Transform, Visibility)
- Add TileMap::remove_fixture for future digging/explosion use
- Skip collision avoidance when current tile has >2 entities (handles spawn
  cluster deadlock)
2026-03-21 01:36:38 +00:00
popertots 1fe4781bab fix: eliminate chunk-boundary doglegs in hierarchical pathfinding
Replace hard-coded chunk-centre waypoints with directional edge waypoints.
When a path segment crosses into the next chunk, directional_chunk_waypoint()
samples standable tiles along the entry edge and picks the one closest to the
straight-line projection from the entity's current position toward the goal.
Falls back to chunk centre if no standable edge tile is found.

This preserves tile-locked DF movement feel while removing the forced dogleg
at every chunk boundary that the chunk-centre approach introduced.
2026-03-20 17:47:35 +00:00
popertots e61a27fdb0 cargo 2026-03-20 17:29:47 +00:00
popertots c9802e6e0f gitignore 2026-03-20 17:28:41 +00:00
popertots 017ddb8047 feat: add distro, kernel version, and display resolution+Hz to system info
Distro parsed from /etc/os-release PRETTY_NAME.
Kernel from uname -r.
Display resolution and refresh rate from xrandr (linux only, N/A elsewhere).
2026-03-20 17:20:29 +00:00
popertots 06bbc9caa8 feat: neofetch-style system info dump at startup
Prints DORF SYSTEM box to stdout before game loop begins:
OS, CPU (model + cores), GPU (WEBGPU_ADAPTER_NAME env var),
RAM total + free (from /proc/meminfo), display vsync mode,
initial chunk radius, world seed.
2026-03-20 17:16:54 +00:00
popertots eac787be3f feat: runtime-configurable VSync via config.toml
[display]
vsync = "mailbox"   # "vsync" | "mailbox" | "uncapped"

VsyncMode enum with custom Deserialize — accepts string values in toml.
apply_vsync_setting system reads GameConfig on every frame, updates
Window.present_mode immediately on change. No restart needed.
2026-03-20 17:10:54 +00:00
popertots 11d4b6a8a0 fix 2026-03-20 15:57:24 +00:00
popertots 21e9dc1f34 perf: double populate budget in release builds
512 chunks/frame in debug, 1024 in release. In release, recolor runs
~2x faster so 1024 still costs ~1ms but clears the dirty queue twice
as fast, closing the p99 gap during rapid z-scrolling.
2026-03-20 15:44:47 +00:00
popertots 2b8e1c32b9 fix: remove .max(0) clamp from z_min — was inflating dirty set to all 20181 keys
Negative world z (below ground) was excluded by the clamp, making the
visible window asymmetrically large. Dirty count now proportional to
actual scroll distance. Frame time dropped from ~13ms to ~8.3ms avg.
2026-03-20 15:31:53 +00:00
popertots 0d73311c68 fix: correct boundary z-marking and tileset_index swap in recolor
Boundary: mark all z-levels where z_diff changed, not just 4 boundary
indices. For multi-step scrolls, every z-level between
min(old,new)-8 and max(old,new) inclusive needs recoloring.
Also fixes last_z_change_dirty_count to use actual new_keys.len().

Recolor: update tileset_index alongside color so sky tile switch at
t>=0.98 actually renders the sky texture, not sky-coloured grass.

Cleanup: removed solid_bits (was unused after recolor fix).
2026-03-20 15:28:17 +00:00
popertots 4c14f79cd2 perf: eliminate HashMap lookups on z-scroll with fast recolor + targeted boundary dirty
Three optimizations for z-scroll performance:

Option 1 — Fast in-place recolor (no HashMap lookups):
- TilemapChunkStates stores solid_bits + underground_bits per chunk
- recolor_chunk_for_depth iterates flat Vec<Option<TileData>> — pure cache-friendly sequential reads
- tile_fade_color computes color from tileset_idx + z_diff without any HashMap access
- vs repopulate_chunk_tiles: 32K Vec reads vs 32K HashMap lookups per frame

Option 2 — Targeted boundary dirty (from 9×chunks to 2×chunks):
- on_camera_z_changed now only marks the 4 boundary z-levels:
  old_entered, old_exited, new_entered, new_exited
- Registry.z_change_keys tracks z-changed keys separately from occlusion dirty_keys
- populate_tilemap_chunk_data uses fast recolor for z_change_keys,
  full repopulate for dirty_keys (occlusion — rare)

Option 3 — Separate budgets:
- z_change_keys processed with MAX_POPULATE_PER_FRAME budget (fast recolor)
- dirty_keys processed with MAX_POPULATE_PER_FRAME budget (full repopulate)
- Both pipelines tracked separately in benchmark

Also:
- populate_chunk_tiles now returns underground_bits for state storage
- compute_solid_bits helper extracts solid tiles from TileData Vec
- spawn_tilemap_chunks computes and stores both bitmasks on spawn
- despawn_tilemap_chunks clears all state for despawned chunks
2026-03-20 15:21:19 +00:00
popertots ac0e8bc1c7 perf: targeted z-scroll dirty marking, per-frame populate budget
on_camera_z_changed: only dirty z-levels whose z_diff changed
(both old and new camera positions, within 0..=8 window).
Previously dirtied all 20k entities on every z-change.

populate_tilemap_chunk_data: process up to MAX_POPULATE_PER_FRAME
(512) keys per frame, sorted by z-distance from current camera.
Spreads z-scroll cost across multiple frames instead of one spike.

TilemapBenchmark: added z-change spike instrumentation
(last_z_change_dirty_ms, last_z_change_dirty_count,
last_z_change_populate_ms, z_change_history_ms).
F9 report now shows z-scroll metrics.

PreviousZIndex resource tracks prior camera z for targeted dirty calc.
2026-03-20 14:50:13 +00:00
popertots b48459fceb fix: distinguish air vs solid non-visible tiles
Air (id=0) not in line of sight participates in depth fade (open space).
Solid terrain not visible is genuinely occluded — render black.
2026-03-20 14:37:48 +00:00
popertots 5c4725b4a0 fix: lerp RGB toward sky colour with full opacity — no alpha fade
- tile_for_depth: alpha always 1.0, lerp RGB from white toward sky RGB
  instead of fading tile out via alpha. Tiles stay opaque.
- Expose LAYER_ALPHA as pub const so it can be tuned at runtime
- LAYER_ALPHA changed from 41/255 to 50/255 for steeper visible fade
- Invisible tiles: explicit solid sky colour, not tile_for_depth(0, 999)
2026-03-20 14:34:07 +00:00
popertots fc47e719d7 fix: cross-fade tiles to sky tile using alpha instead of color tint
Replace df_fade_color (multiplicative tint that preserves texture hue)
with tile_for_depth which cross-fades between real tile and sky tile:

- z_diff <= 0: real tile at full opacity (no fade)
- z_diff in (0, threshold): real tile fades out via alpha
- z_diff past threshold: sky tile (index 0) fades in
- invisible tiles: solid sky colour

Also fix tile_id_to_tileset_index to include sky (id=0) as index 0,
so sky tiles render correctly as the fade target.
2026-03-20 14:27:24 +00:00
popertots 6fbfb10e48 fix: replace HSV df_fade_color with exact stacked blue compositing 2026-03-20 14:17:19 +00:00
popertots f9a74ed432 feat: migrate quilter rendering to Bevy TilemapChunk
Replaces the CPU pixel-baking quilter system (QuiltCache, TerrainSprite,
pixel_buffers) with Bevy's native TilemapChunk GPU-index-lookup API.

Architecture:
- TilemapChunk entities replace TerrainSprite entities per (chunk, z, layer)
- Tileset PNG stacked vertically as texture_2d_array (11 rows: 6 floor + 5 fixture)
- populate_chunk_tiles reads TileMap HashMap directly; no ECS FloorTile entities
- Chunk spawn deferred to apply_terrain_blobs (after TileMap data exists)
- Dirty key system triggers repopulate on occlusion/camera-z changes

Bug fixes:
- populate_chunk_tiles: add chunk_pos offset to world tile lookups (was always (0,0))
- spawn_tilemap_chunks: offset Transform by -TILE_SIZE/2 (tile-centre vs bottom-left)
- spawn_tilemap_chunks: AlphaMode2d::Blend (was Opaque, blocking DF fade)
- update_tilemap_chunk_visibility: actually mutate Visibility (was read-only)
- handle_tile_occlusion_updates: mark dirty keys inline (was double-reading events)
- TilemapChunkSpawner: deduplicate pending queue and guard stale registry

Performance:
- ~115K FloorTile ECS entities eliminated
- GPU memory: O(tile_data) instead of O(CHUNK_TILES² × z_levels × pixel_bytes)
- Z-scroll: only TilemapChunkTileData repopulates (no entity re-spawn)
- Benchmarks via TilemapBenchmark resource (F9 to report)
2026-03-20 14:02:04 +00:00
popertots 0eb2fcfbb3 plan 2026-03-20 11:19:54 +00:00
popertots 9018dabd6b remove tests 2026-03-20 10:52:46 +00:00
popertots 792a8981c1 docs: rewrite README to reflect actual implementation state 2026-03-20 10:33:20 +00:00
popertots d7cbbaa04b feat: implement chunk unloading infrastructure
Chunk unloading system with cleanly abstracted decision layer.

- ChunkMap: replace (bool, i32) timer tuple with () presence marker;
  remove FIFO cycle fields pending_unload/unload_cycle/unload_cursor/
  unload_countdown. loaded_chunks is now HashMap<IVec2, ()>.
- handle_chunk_events: simplify re-entrancy check to contains_key;
  remove parallel Mutex dedup machinery — load detection is now trivial.
- is_standable: single HashMap get; return false for unloaded chunks
  instead of falling back to is_standable_slow (pathfinding correctness).
- remove_chunk_data: bounds-iteration tile cleanup for chunk unload.
- ChunkOwner component + chunk_entity_index: static terrain entities
  (floor tiles, fixtures, trees) tagged at spawn and despawned by
  chunk. Mobile entities (dorfs, pigs, rabbits) not tracked — safe
  by design since they were never indexed.
- unload_chunk: canonical single-chunk unload function; cleans
  loaded_chunks, despawns entities, removes tilemap data, marks render
  chunk dirty via ChunkZKey::from_world, updates connectivity, fires
  rebake. Full docstring with step-by-step state map, working example
  for dynamic unload, and "what it does NOT handle" section.
- dynamic_chunk_unloading_system: no-op stub. Re-enable by writing
  a system that diffs wanted vs loaded chunks and calls unload_chunk.
  Wired out of FixedUpdate during foundation work.
- render chunk fix: ChunkZKey::from_world now pub(crate) so unload
  can derive the correct render chunk key (spans 4x4 world chunks)
  matching what build_quilted_terrain_sprites uses at spawn.
- FloorTilePrefab::spawn: pre-existing bug — return Entity not ().
2026-03-20 10:30:33 +00:00
popertots a0eb608d73 fix pathfinding 2026-03-19 21:35:26 +00:00
popertots a97e2908ed fix(pathfinding): correct BinaryHeap ordering and movement threshold
- Fix PathNode and ChunkPathNode Ord impl to use correct min-heap ordering
  (self.f_score.cmp vs other.f_score.cmp) - was causing zig-zag paths
- Fix movement threshold formula to use tile_weight directly
  (walk_speed * tile_weight / 50) instead of inverted multiplier
- Update comments to reflect 'higher = slower to traverse' semantics
2026-03-19 20:27:36 +00:00
popertots c4890fa23e config cleanup 2026-03-19 20:17:15 +00:00
popertots d29a3e793f docs: add session summary and 48h diff for handoff 2026-03-19 19:16:27 +00:00
popertots 9535d65bd7 feat: data-oriented chunks optimization with tile registry
- Phase 1: Bit-packed standability (ChunkData with bitsets)
- Phase 2: Reactive connectivity (dirty chunks)
- Phase 3: Async terrain baking (AsyncComputeTaskPool)
- Pathfinding weight system (rock=50 preferred, bedrock=150 avoided)
- Movement speed affected by tile weight
- External tiles.toml for hot-reloadable tile definitions
- TileRegistry singleton for async-safe tile lookups
- Fixed world_to_chunk to use CHUNK_SIZE_TILE (128) not CHUNK_SIZE (8)
- Fixed infinite spawner with Local<bool> state guards
- Fixed spawn coordinate grid alignment

Note: Zigzag pathfinding bug introduced - needs investigation
2026-03-19 19:15:36 +00:00
popertots 50788af3c7 feat(optimization): implement data-oriented chunk architecture
Phase 1: Bit-packed standability
- Add ChunkData struct with 4 bitsets per chunk (stand_in/on for floor/fixture)
- Replace 4 HashMap lookups per standability check with O(1) bit operations
- Memory: ~2KB bitsets per chunk vs ~50KB HashMap overhead

Phase 2: Reactive connectivity
- Add dirty_chunks HashSet to ChunkMap for incremental updates
- update_chunk_connectivity now O(d) where d = dirty chunks
- Early exit when no changes, preventing O(N) full rebuilds

Phase 3: Async terrain baking
- Move terrain generation to AsyncComputeTaskPool
- spawn_terrain_tasks: non-blocking task spawn (~34µs)
- apply_terrain_blobs: batched entity spawn on main thread
- Eliminates main-thread stutters during world generation
2026-03-19 17:01:51 +00:00
popertots 15c0d1c7a2 external config 2026-03-18 23:19:58 +00:00
popertots a5154f73dd docs(pathfinding): add documentation and remove unused async infrastructure
- Add comprehensive module documentation explaining tier architecture
- Add TileMap documentation explaining design choices (FxHashMap, packed data)
- Remove unused async pathfinding code (StandableTileSnapshot, spawn_async_path_task)
- Remove unused imports (check_ready, rayon, StdHashMap)
- Fix unused variable warnings with underscore prefixes
- Add #[allow(dead_code)] for intentionally unused fields
2026-03-18 22:08:27 +00:00
popertots 4d6692c432 cleanup 2026-03-18 21:56:15 +00:00
popertots ea448a03b9 bench: final benchmark after Phase 3 reactive pathing
Results (n=37,707):
- avg=256µs, median=142µs, p95=357µs
- 96% of paths complete in <500µs
- 99.6% complete in <1ms
- 100% success rate (6 failures)

Performance improvements from baseline:
- P99: -99.6% (80ms → 357µs)
- Max: -57% (80ms → 34ms)
- Avg: -39% (418µs → 256µs)

Implementation complete:
- Phase 1: Chunk-Graph Layer
- Phase 2: Async Infrastructure (ready)
- Phase 3: Reactive Pathing
2026-03-18 21:53:41 +00:00
popertots 49f91de0c9 feat(pathfinding): implement Phase 3 reactive pathing
- Add validation_cooldown field to Ambulatory component
- Implement validate_next_steps to check walkability of next 3 path nodes
- Integrate validation into movement system (every 10 frames)
- Trigger re-path when validation fails (path blocked or invalid)
- Entities now detect and recover from invalid paths automatically
2026-03-18 21:33:38 +00:00
popertots a6f6e7cb9d feat(pathfinding): implement hierarchical task-based pathfinding
Phase 1 - Chunk-Graph Layer:
- Add world_to_chunk, chunk_to_world, get_chunk_neighbors helpers
- Add update_chunk_connectivity system to build chunk adjacency graph
- Implement calculate_chunk_path for macro A* on chunk coordinates
- Wire hierarchical tier dispatch into prepare_paths

Phase 2 - Async Infrastructure (ready for integration):
- Add StandableTileSnapshot for chunk-local tile data copy
- Add AsyncPathTask component for Task handle storage
- Add spawn_async_path_task and poll_async_path_tasks functions

Performance improvements:
- Before: avg=418µs, median=175µs, max=80ms, p95=791µs
- After: avg=244µs, median=140µs, max=16ms, p95=355µs
- 87% reduction in tail latency, 41% faster average
2026-03-18 21:26:47 +00:00
popertots 5704942950 chore: add benchmark analysis scripts 2026-03-18 18:12:33 +00:00
popertots e9ef5ebbcd fix: four interlocking pathfinding bugs causing panic, failure, and yo-yo teleportation
1. CSV Overflow Panic: Use saturating_sub to prevent underflow when total_failed_paths > n
2. Heuristic Scale Mismatch: octile_distance_3d now divides by ITILE_SIZE to match g-score units
3. Goal Z Offset: Correct target.as_ivec3() - ivec3(0,0,1) not ITILE_SIZE (target already has +1.0)
4. Gravity Yo-Yo: Clear current_path and target when entity falls to prevent teleportation loop
2026-03-18 17:49:07 +00:00