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 eid is used in grid/content-grid during life; :entity/id is assigned in entity.after-create/id and used in txs/render.
  • Validation today: per-key Malli types in resources/schemas.edn; no closed :entity/map. records/entity qrecord is unused (only declares entity/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:

  1. clickable, line-render, animation, image
  2. string-effect, temp-modifier
  3. 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

  1. Vocabulary: rg -o ':entity/[a-z0-9-]+' src resources test | sort -u
  2. Spawn keys: keys of entity.create/v in entity/create.clj
  3. Runtime-only keys: in grep but not in create, or set only in world/op / grid / after_create
  4. Kinds: grep spawn-entity! call sites in world.clj + effect spawn txs
  5. Templates: sample one creature/item/skill in properties.edn
  6. Validate: add :entity/map to schemas.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/map vs per-kind schemas (:entity/creature, :entity/item, …)?
  • Should :entity/state FSM objects be part of public board state or engine-internal?
  • Move :world/entity-ids under an explicit :board/entities when board is split?