Entity model - damn/moon GitHub Wiki
Entity model (runtime)
Living notes on what a piece is in Cyber Dungeon — the MTG/chess layer inside
:world/entity-ids, not the engine hooks around it.
Runtime shape
An entity is not a typed record. It is:
;; Stored in world:
:world/entity-ids → {numeric-id → (atom entity-map)}
;; entity-map: open map of :entity/* and occasional :property/* keys
@eid ;; => {:entity/id 7, :entity/position [x y], ...}
- Identity: atom reference
eidis used in grid/content-grid during life;:entity/idis assigned inentity.after-create/idand used in txs/render. - Validation today: per-key Malli types in
resources/schemas.edn; no closed:entity/map.records/entityqrecord is unused (only declaresentity/body).
Lifecycle (spawn → tick → destroy)
Recipe map (properties + spawn components)
→ entity.create/v (per-key normalizers)
→ merge entity->record (empty Entity record stub)
→ (atom ...)
→ entity.after-create/v (grid, id, inventory fill, content-grid)
→ registered in :world/entity-ids when :entity/id runs
Each tick (active entities only):
→ entity.tick/v (per present key → txs)
→ :entity/state → entity-state.tick-txs/v (FSM behaviour)
Move:
→ world/op :fn/entity position update
→ entity.on-position/v (grid + content-grid refresh)
Destroy:
→ :entity/destroyed? true
→ remove-destroyed-entities! → entity.destroy/v → dissoc from entity-ids
Entry point for all spawns: world/spawn-entity!.
Entity kinds (spawn paths)
| Kind | Spawn fn | Recipe source |
|---|---|---|
| Creature | spawn-creature-id! |
properties.edn :properties/creatures + game.edn :components |
| Player | spawn-player! |
creature :creatures/vampire + player components in game.edn |
| Enemy | spawn-enemies! |
level tile "creatures" / "id" + enemy components |
| Ground item | spawn-item! |
properties/items entry |
| Teleporter | spawn-teleporter! |
hardcoded in world.clj |
| Effect (FX) | spawn-effect! |
ad-hoc components (animation, small body, :z-order/effect) |
| Projectile | world/op :fn/spawn-projectile |
properties/projectiles + movement/collision |
| Line FX | world/op :fn/spawn-line-render |
:entity/line-render + :entity/delete-after-duration |
| Skill spawn | effect :spawn |
new creature via tx chain |
Creatures go through make-creature-props, which merges property template with
spawn :components, sets position/body/z-order/solid, and expands :entity/fsm
into runtime :entity/fsm + :entity/state [state-k state-obj].
Component keys (full vocabulary)
32 :entity/* keys appear in src, resources, test:
Core spatial (almost every piece)
| Key | Role | Spawn | Schema |
|---|---|---|---|
:entity/id |
numeric id | after-create | nat-int |
:entity/position |
[x y] float world coords |
recipe | entity-position |
:entity/body |
{:body/width :body/height [:body/flying?]} |
recipe / spawn | entity-body |
:entity/z-order |
draw + collision layer | recipe | enum |
:entity/solid? |
blocks tiles when true | creatures default true | boolean |
:entity/rotation |
draw rotation radians | projectiles | number |
:entity/occupied-cells |
grid index (solid) | after-create | some |
:entity/touched-cells |
grid index (body AABB) | after-create | some |
Visual
| Key | Role |
|---|---|
:entity/image |
static sprite |
:entity/animation |
frames + runtime :cnt :maxcnt |
:entity/line-render |
debug/FX line |
:entity/string-effect |
floating combat text |
Creature / gameplay
| Key | Role |
|---|---|
:entity/species |
:species/... keyword |
:entity/stats |
hp/mana/speed/aggro/… + :stats/modifiers |
:entity/skills |
map skill-id → skill property |
:entity/inventory |
slot → item property |
:entity/faction |
:good / :evil |
:entity/controller |
:controller/player / :controller/npc |
:entity/fsm |
reduce-fsm instance + :state keyword |
:entity/state |
[fsm-state-k state-obj] — see entity-state/* |
:entity/free-skill-points |
player build |
:entity/click-distance-tiles |
interaction radius |
:entity/clickable |
{:type :clickable/... :text ...} |
:entity/movement |
{:direction :speed :delta-time ...} |
:entity/item-on-cursor |
player carrying item |
:entity/item |
item property on ground entity |
:entity/projectile-collision |
hit tracking + effects |
:entity/alert-friendlies-after-duration |
alert timer + faction |
:entity/temp-modifier |
timed stat mods |
:entity/delete-after-duration |
timer → destroy |
:entity/destroyed? |
tombstone until removed |
Property keys sometimes on instances
:property/pretty-name, :property/id, :creature/level — copied from templates.
FSM / entity-state (behaviour on top of data)
:entity/state is a pair [state-keyword state-object]:
- state-keyword: e.g.
:player/idle,:npc/moving,:state/active-skill - state-object: built by
entity-state.create/v(timers, movement vector, skill ctx)
FSM transition tables live in game.edn :fsms. Behaviour lives in
entity-state/tick-txs, enter, exit, handle-input, etc. — not in the
entity map schema today.
Property templates (properties.edn)
Catalog only — not runtime board state. Defines defaults for:
| Catalog key | Typical :entity/* on template |
|---|---|
:properties/creatures |
animation, body, species, stats, skills, inventory? |
:properties/items |
image, stats/modifiers, item/slot |
:properties/skills |
image, skill/*, effects |
:properties/projectiles |
image, speed, effects |
:properties/audiovisuals |
animation, tx/sound |
Spawn copies subsets into instances via entity.create/v (e.g. skills resolved
through world/db).
Spawn components (game.edn)
Player/enemy only add controller/fsm/faction/clickable — not the full creature:
:player-spawn {:creature-id :creatures/vampire
:components {:entity/fsm {:fsm :fsms/player :initial-state :player/idle}
:entity/faction :good
:entity/controller :controller/player
...}}
:enemy-spawn {:components {:entity/fsm {:fsm :fsms/npc :initial-state :npc/sleeping}
:entity/faction :evil
:entity/controller :controller/npc}}
Render layers (game.edn :ctx/render-layers)
Draw order groups by component key on entities:
- clickable, line-render, animation, image
- string-effect, temp-modifier
- state, stats (bars)
Implemented in entity.render/v + entity-state.draw-world/v.
Namespaces that touch entity data
| Area | Namespaces | Role |
|---|---|---|
| Spawn / life | world, entity/create, after_create, on_position, destroy |
recipe → instance |
| Per-frame | entity/tick, entity-state/* |
txs from components + FSM |
| Draw | entity/render, entity-state/draw-world, effect/render, graphics |
read entity maps |
| Space | grid, content_grid |
occupied/touched cells, movement |
| Mutators | creature, item, inventory, stats, entity |
assoc/update on @eid |
| Effects | effect/*, effect-ctx |
read skills/stats/position |
| UI | ui, imgui-hud, game |
player inventory, mouseover, dev labels |
| Catalog | db, properties.edn, editor |
templates only |
| Types | schemas, schema |
Malli per attribute |
Not entity-definition (engine): raycaster, timer, faction, val-max, level
gens (uf-caves, vampire, modules) — they read entities or spawn via world.
How to build and maintain this doc
- Vocabulary:
rg -o ':entity/[a-z0-9-]+' src resources test | sort -u - Spawn keys: keys of
entity.create/vinentity/create.clj - Runtime-only keys: in grep but not in create, or set only in
world/op/grid/after_create - Kinds: grep
spawn-entity!call sites inworld.clj+ effect spawn txs - Templates: sample one creature/item/skill in
properties.edn - Validate: add
:entity/maptoschemas.edn(optional keys by kind) +validate-board!in dev — schema becomes living documentation
Suggested closed map (future Malli):
;; Minimal piece — every entity
[:entity/id :entity/position]
;; Creature profile (optional bundle)
[:entity/species :entity/body :entity/stats :entity/faction
:entity/fsm :entity/state :entity/controller ...]
Open questions
- Single
:entity/mapvs per-kind schemas (:entity/creature,:entity/item, …)? - Should
:entity/stateFSM objects be part of public board state or engine-internal? - Move
:world/entity-idsunder an explicit:board/entitieswhen board is split?