Axel Meyer b6a16df6f0 initial: composition v0.1.0 — template + tag-index + instantiation
Phase-A.1 implementation per
sporel-meta/docs/superpowers/specs/2026-06-09-phase-A-...
Template-Only-Subset; slots/container/quality/condition produce
loud-errors with explicit re-entry-phase hints. Position-Format
revised to two flat scalar properties (position.x, position.y) per
engine reality: no PROPERTY_TABLE exists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-09 13:28:52 +00:00

lib-core.composition

Template + Instantiation + Tag-Index over engine.entity. Templates define property defaults; create spawns an engine.entity with template defaults + per-instance overrides; entities are indexed by template-id and tag for cheap reverse-lookup.

Version: 0.1.0 Lib-ID: lib-core.composition Requires: (none — uses engine entity.*, domain.contribute only) Tags: composition, entity, template, tag-index

Topology

graph LR
  this["lib-core.composition"]
  engine["engine.*"]
  this --> engine

Scope (v0.1.0 — Spine)

Template-Only-Subset per Phase-A Spec.

Supported:

  • define_template{id, properties, tags}
  • create{template, properties}
  • list_by_template(id), list_by_tag(tag)
  • destroy(entity)

Deferred (Loud-Error if attempted):

  • slots block → Phase D trigger (Composite Items with Sub-Items)
  • container block → Phase B trigger (Items + Inventory)
  • quality, condition → Phase J trigger (Damage / Wear)
  • parent: template-inheritance → no consumer yet

API

composition.define_template(def)

Syntax: composition.define_template({id: string, properties?: table, tags?: array}) -> void

Example:

composition.define_template{
    id = "sign",
    properties = {
        sprite_path = "sprites/sign.png",
        text        = "default sign text",
        position    = {x = 0, y = 0},  -- auto-flattened to position.x + position.y
    },
    tags = {"renderable"},
}

Description: Registers a template under id. All properties are auto-declared as inert via domain.contribute with type inferred from the default-value. Nested tables in properties are flattened to dotted scalar keys (e.g. position = {x, y} → declares position.x and position.y as separate number properties). Tag-list registers the template in the tag-index used by list_by_tag.

Loud error(...) on: missing/non-string id, duplicate id, non-table properties, malformed tags, attempted slots / container / quality / condition / parent: block (each pointing to its re-entry phase).

composition.create(spec)

Syntax: composition.create({template: string, properties?: table}) -> entity

Example:

local sign = composition.create{
    template = "sign",
    properties = {
        position = {x = 144, y = 200},
        text     = "Hier steht: Willkommen im Spine.",
    },
}

Description: Spawns an engine.entity, applies template-defaults shallow-overridden by per-instance properties. Nested-table overrides (like position = {x, y}) are flattened the same way as in define_template. Per-instance properties that weren't declared by the template get declared on-the-fly as inert. Entity is registered in the template-index + tag-index of the template. Returns the engine.entity handle.

Loud error(...) on: non-table spec, missing/unknown template, non-table properties.

composition.list_by_template(id)

Syntax: composition.list_by_template(id: string) -> {entity, ...}

Example:

for _, sign in ipairs(composition.list_by_template("sign")) do
    engine.print(sign:get_property("text"))
end

Description: Returns a shallow-copied list of all currently-alive entities created with the given template-id. Empty list if template is unknown or has no instances.

composition.list_by_tag(tag)

Syntax: composition.list_by_tag(tag: string) -> {entity, ...}

Example:

for _, e in ipairs(composition.list_by_tag("renderable")) do
    local x = e:get_property("position.x")
    local y = e:get_property("position.y")
    -- ... render
end

Description: Returns all currently-alive entities whose template declared tag in its tags array. Used by lib-core.render draw_entities{tag="..."} (see A.2). Empty list if tag has no entities.

composition.destroy(entity)

Syntax: composition.destroy(entity) -> void

Description: Removes entity from template-index + tag-index + destroys via engine.entity.destroy. Idempotent. If entity wasn't composition-created, falls through to entity.destroy.

composition.list_templates()

Syntax: composition.list_templates() -> {id, ...}

Description: Debug/introspection — returns set of declared template-ids.

Conventions

  • Property-Value-Types: number, string, boolean (engine §PROPERTY_* catalog). Tables are flattened to dotted scalar keys; arrays inside properties → loud error.
  • Position-Format: stored as two scalar properties position.x and position.y (per Phase-A Spec §5 A-Q5 revision 2026-06-09 — engine has no PROPERTY_TABLE).
  • Tag-Index: maintained in lib, not in engine. Tag-lookup is O(1) by tag, list iteration is O(n) per tag.
  • composition.reg_id: internal book-keeping property on each created entity; used by destroy to find the meta-record. Do not set or read manually.

Future Phases

Phase Triggers in Template Spec
B (Items) container = true inventory-model.md
D (Composites) slots = {...} composition-model.md (full Slot-System)
J (Damage/Wear) quality, condition damage-model.md
Description
No description provided
Readme 70 KiB
Languages
Lua 100%