Entity.gridstate as DiscreteMap reference #294
Labels
No labels
Alpha Release Requirement
Bugfix
Demo Target
Documentation
Major Feature
Minor Feature
priority:tier1-active
priority:tier2-foundation
priority:tier3-future
priority:tier4-deferred
Refactoring & Cleanup
system:animation
system:documentation
system:grid
system:input
system:performance
system:procgen
system:python-binding
system:rendering
system:ui-hierarchy
Tiny Feature
workflow:blocked
workflow:needs-benchmark
workflow:needs-documentation
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Depends on
#293 DiscreteMap serialization via bytes
john/McRogueFace
Reference
john/McRogueFace#294
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Replace the current entity gridstate implementation (flat arrays with
ensureGridstate()resize logic) with aDiscreteMapreference. Entity perspective knowledge becomes a first-class, swappable, serializable object rather than an implicit internal array.Current problems
ensureGridstate()silently destroys exploration data — moving between grids of different sizes wipes all discovery with no callback or save mechanismentity.gridstatereturns detached copies (SimpleNamespace snapshots), forcing O(w×h) Python iteration for save/restore — violates the principle that grid-shaped iteration stays on the C++ sideProposed API
Engine behavior on grid transitions
When an entity is removed from a grid (or the grid's perspective entity changes):
Game-side multi-grid memory pattern
The engine provides correct primitives; game code composes them for its specific needs.
Dependencies
Scope
Could be implemented incrementally on the current Entity/Grid, or as part of #252. The DiscreteMap serialization issue is a prerequisite.
Roadmap context
This issue is part of the Grid & Entity Overhaul Roadmap (
docs/GRID_ENTITY_OVERHAUL_ROADMAP.md), placed in Phase 4 (after #252 Grid/GridView split).The behavior system's seek/flee behaviors (#300) will initially use the current
DijkstraMaptype, then migrate toDiscreteMapwhen this issue lands. TheDiscreteMapwill also serve as the entity's perspective map for FOV-based TARGET triggers in the behavior system.Implementation plan — design decisions settled 2026-04-17
Following a design review, this issue is narrowed to perspective storage only. Pathfinding/behavior integration (SEEK/FLEE referencing DiscreteMaps) has been moved to #315 and is explicitly out of scope here.
Settled design decisions
1. Data model: one DiscreteMap, three states
Invariant:
visible ⊆ discovered— every cell that is currently visible must also be discovered.updateVisibility()enforces this by:2 → 1at the start of each FOV pass (discovered stays, visible is transient)1 → 2for cells the entity currently sees (and bumping0 → 2if freshly seen)No separate
visibleanddiscoveredfields. Avoids sync bugs and halves memory vs. two maps.2. Python API:
entity.perspective_mapPairs with the existing
GridView.perspective_entity/GridView.perspective_enabledterminology (seesrc/UIGridView.h:56-57). The symmetry reads as "GridView renders from this entity's perspective; the entity stores its perspective_map."The property is lazy: first access on an entity that has a grid allocates a DiscreteMap sized to the grid (all zeros). Entities without a grid have
perspective_map is None.3. Removals (clean break — no deprecation shims)
UIEntity::gridstatemember —src/UIEntity.h:69UIEntity::ensureGridstate()—src/UIEntity.cpp:39-entity.gridstatePython property and itsget_gridstategetter returning detached snapshotsUIGridPointState::get_point()—src/UIGridPoint.h:76(the visibility-gated grid point accessor)UIGridPointStatestruct itself —src/UIGridPoint.h:65-80— becomes vestigial. Check for remaining readers before deletion; if any, migrate toentity.perspective_map[x, y]+grid.at(x, y).No backward-compat alias. A future ticket may expose a layer-dict accessor on
grid.at(x, y)returning{layer_name: value}, but that is out of scope here.4. Perspective API: already correct
GridView::perspective_entityis alreadystd::weak_ptr<UIEntity>(no tuple). The tuple-stylegrid.perspective = (entity, dmap)mentioned in the original issue description is rejected — the entity owns its own DiscreteMap, and the GridView just references the entity. No newGrid.perspectiveAPI is needed.Existing code path:
GridView::perspective_enabled+perspective_entitystay as-isUIGridView.cpp:223(if (perspective_enabled) { auto entity = perspective_entity.lock(); ... }) switches from readingentity->gridstate[...]to readingentity->perspective_map->values[...]with a comparison against>= 1(discovered — render dimmed) or== 2(visible — render normal)5. C++ storage choice — implementer's call
PyDiscreteMapcurrently stores rawuint8_t*directly on the Python object (PyDiscreteMap.h:10-15) without a C++ backing class, unlikeDijkstraMapwhich has a C++ class wrapped byPyDijkstraMapObject. Two viable options:class DiscreteMapwith shared ownership, makePyDiscreteMapObjecthold ashared_ptr<DiscreteMap>, andUIEntityholds the sameshared_ptr<DiscreteMap>.updateVisibility()can write in C++ without touching the Python type.PyObject*(refcounted) pointing to a PyDiscreteMapObject: no refactor needed, butupdateVisibility()must either reach through the Python object to theuint8_t*(type-check + unwrap each time) or route through the Python API.Recommend (a) for consistency with DijkstraMap and to keep
updateVisibility()clean. Defer the final call to whoever picks this up.Acceptance criteria
UIEntitystores perspective as one DiscreteMap (3 states)updateVisibility()demotes2 → 1then promotes visible cells to2(and0 → 2on first sight)entity.perspective_mapreturns the DiscreteMap reference (NOT a snapshot copy)entity.perspective_map = other_dmap) validates size against grid;ValueErroron mismatchto_bytes()→from_bytes()→ assign →updateVisibility()still worksGridViewfog-of-war rendering reads fromentity.perspective_mapinstead ofgridstateentity.gridstate,UIGridPointState::get_point(), andensureGridstate()removedUIGridPointStatestruct removed if no remaining readersValueErrorvisible ⊆ discoveredinvariant holds after everyupdateVisibility()callKey files
src/UIEntity.h/src/UIEntity.cpp— the member and its methodssrc/UIGridPoint.h—UIGridPointStatestruct removalsrc/PyDiscreteMap.h/src/PyDiscreteMap.cpp— possible C++-class refactor (option a)src/UIGridView.cpp:223— fog-of-war rendering siteNot in scope (moved to #315)
EntityBehavior::dijkstra_mapmigration