object model — containment, verbs, and the location tree
========================================================

a design doc for the object hierarchy that powers containment, inventory,
zones, and verb-driven interaction. captures the core rule, the class
structure, and how everything connects.

see also: ``things-and-building.rst`` (@thing/@verb decorator system),
``zones-and-building.rst`` (zone data format), ``architecture-plan.txt``
(engine/content boundary), ``combat.rst`` (combat on Entity subgraph).


the core rule
-------------

every object has a ``location`` attribute that points to another object, or
None. that's the entire containment system.

where something is = what its location points to. None means it's a top-level
thing — a zone, a template that hasn't spawned yet, or something removed from
the world.

here's what the tree looks like::

    overworld zone (location=None)
    ├── player "jared" (location=overworld, x=50, y=30)
    │   ├── sword (location=jared)          ← inventory
    │   └── backpack (location=jared)       ← inventory
    │       ├── gem (location=backpack)     ← nested
    │       └── potion (location=backpack)
    ├── oak tree (location=overworld, x=50, y=31)  ← fixture
    └── portal (location=overworld, x=52, y=30)    ← leads to tavern zone

    tavern zone (location=None)
    ├── bartender mob (location=tavern, x=3, y=2)
    ├── chair (location=tavern, x=1, y=1)    ← fixture
    └── mug (location=tavern, x=3, y=3)      ← item on the floor

``contents`` is the reverse query — "give me everything whose location is me."

spatial position (x, y) is optional. objects in zones have coordinates. items
in inventory don't. a sword in your backpack has location=backpack, x=None,
y=None.


the hierarchy
-------------

::

    Object (name, location, x, y)  ← @verb targets this
    ├── Zone (width, height, toroidal, terrain)
    ├── Entity (combat stats)
    │   ├── Player (session stuff)
    │   └── Mob (ai stuff)
    ├── Thing (description, portable)
    │   ├── Container (capacity, closed, locked)
    │   └── Portal (target_zone, target_x, target_y)
    └── (Exit — maybe, if portals don't cover it)

**Object**
    base class for everything in the world. has name, location (Object or
    None), optional x/y for spatial position within location. the @verb
    decorator targets this — anything can have verbs. defines ``can_accept(obj)``
    method (default: False) for controlling what can be placed inside.

**Zone**
    a spatial area with a grid. the overworld, a tavern interior, a pocket
    dimension. location=None (top-level). has terrain, dimensions, toroidal
    flag. ``can_accept`` returns True (zones accept everything). defines
    ``contents_at(x, y)`` for spatial queries.

**Entity**
    something that fights. adds combat stats (PL, stamina, defense timing).
    ``can_accept`` allows portable Things (inventory).

**Player**
    a connected human. adds writer, mode_stack, caps, editor, if_session.
    subclass of Entity.

**Mob**
    AI-controlled entity. adds moves list, behavior. subclass of Entity.

**Thing**
    an interactive object. adds description, portable flag. the @thing
    decorator targets this for spawnable templates.

**Container**
    a Thing that explicitly accepts contents. adds capacity, closed, locked
    state. game-facing behaviors (open/close, lock/unlock, put/get).

**Portal**
    a Thing that connects zones. adds target_zone, target_x, target_y.
    portable=False.

**Exit**
    not needed yet. portals + zone grids might cover all transitions. parked
    until we discover it's necessary.

note: Fixture was considered as a class but we just use
``Thing(portable=False)`` instead. no shared behavior that warrants a class.


containment mechanics
---------------------

the containment MECHANISM is on Object (any object can technically be a
location via the location pointer). but the GAME restricts it via
``can_accept()``:

- Zone: accepts everything
- Entity: accepts portable Things (inventory)
- Container: accepts items up to capacity, if not closed
- Thing: rejects by default (can't put a sword inside another sword)
- Object base: rejects by default

this means containment is a capability, not a class. follows the Evennia/LPC
pattern. the Container class just bundles the common container game behaviors
— it's not special from a tree perspective.


location=None
-------------

three things have location=None:

1. **zones** — they're top-level containers. the overworld, tavern zone, etc.

2. **templates/prototypes** — things that exist but aren't placed anywhere yet.
   a mob template, an item blueprint. they're in code or data files but not
   spawned into the world.

3. **despawned objects** — removed from the world but not deleted. a mob dies
   and goes back to location=None until respawn.

this is first-class. a mob template at location=None is the prototype. spawn
it = clone with location=zone. dies = back to location=None until respawn.
no special flags needed, just the natural state.


verbs on everything
-------------------

@verb targets Object, not Thing. everything in the world can have verbs:

- "pet" a mob (verb on Entity)
- "open" a chest (verb on Container)
- "drink from" a fountain (verb on Thing with portable=False)
- "dig" in a zone (verb on Zone)
- "high-five" a player (verb on Entity)

the verb system doesn't care about the class hierarchy. if it's an Object and
has a verb registered, players can use it.


how existing code maps in
--------------------------

Entity(name, x, y) → Entity(name, location=zone, x=5, y=10)
    entities gain a location field. x/y stay but become relative to location.

Player/Mob → same, just gains location field from Object parent
    existing code largely unchanged. location replaces implicit zone references.

overworld terrain grid → becomes a Zone object
    the procedural perlin overworld becomes a Zone instance. toroidal, with
    generator=perlin.

planned zones (tavern, tutorial) → each a Zone object
    hand-built zones like the treehouse. bounded or toroidal, tile data from
    TOML files.

planned portals → Portal things inside zones
    portals are objects with target_zone, target_x, target_y. live inside zones
    like any other Thing.

@thing PepsiCan from things-and-building doc → Thing subclass
    same as planned. @thing decorator creates Thing templates. spawn them into
    zones or player inventory.

combat encounters → still between Entities, unchanged
    combat operates on the Entity subgraph. location doesn't matter for combat
    resolution, only proximity (same zone, nearby x/y).

IF sessions → still on Player, unchanged
    if_session lives on Player. the IF subprocess doesn't care about location.

mode stack → still on Player, unchanged
    mode_stack is a Player concern. doesn't interact with the object tree.

the migration is gentle. Entity gains a location field and x/y become
optional (None when inside a non-spatial container like a backpack).


what this enables
-----------------

- **inventory** — put sword in backpack, backpack in player. nested
  containment via location pointers.

- **items on the ground** — sword at location=zone, x=5, y=10. visible to
  anyone at those coordinates.

- **furniture and fixtures** — Thing with portable=False. a fountain, a tree,
  a statue. stays put, but can have verbs.

- **containers** — chests, bags, locked doors. Container subclass handles
  capacity, closed, locked state.

- **zone transitions** — portals as objects. step on portal tile, location
  changes to target zone.

- **mob templates and spawning** — location=None is the template. spawn =
  clone with location=zone. dies = back to None.

- **verb-driven interaction on anything** — pet the cat, read the sign, dig
  the ground. @verb on Object means no restrictions.

- **the "phone switch" navigation zone** — a zone full of portals. walk to
  a portal, step through, appear in that world. the hub.


open questions (parked)
-----------------------

- **equipment slots** (wield, wear) — additive later when combat needs it. do
  we track worn/wielded separately from inventory, or is it just metadata on
  the Thing? defer until combat design demands it.

- **Exit class** — may not be needed. portals might cover it. if we discover
  a need for one-way passages with different behavior than portals, revisit.

- **persistence** — which objects persist to SQLite, which are runtime-only?
  player inventory persists. zone state (tile changes from paint mode)
  persists to TOML. mob instances probably don't persist (respawn from
  templates). decide per-class as we implement.

- **object registry implementation** — global dict? spatial index? contents
  cache? probably: zones maintain contents_at(x, y) lookups, global registry
  maps object IDs to instances for verb resolution.


related docs
------------

- ``things-and-building.rst`` — @thing/@verb decorator system, content layer
  on top of this model
- ``zones-and-building.rst`` — zone data format, building tools, paint mode
- ``architecture-plan.txt`` — overall engine/content boundary
- ``combat.rst`` — combat system (operates on Entity subgraph)
