The knowledge model¶
Everything served is an entity, a typed link between two entities, or a price series. No
second shape, which is what lets one front end draw a page for a type it has never heard
of. The model is wiki_api.domain and imports nothing but pydantic.
Entities¶
Nine types: item, npc, shop, quest, location, scenery, task,
room, music.
Identified by type and an id unique within it as a slug item:4587.
|
Readable, unique per type, derived so links do not rot. On a collision the lowest id keeps the bare slug. |
|
A model decided by the entity type. An item cannot carry npc attributes. |
|
Source kind, game commit, optionally file and record. Kinds:
|
|
Set together on a duplicate: noted, bound, placeholder or plain. |
|
A variant is never searchable and points at what it duplicates. |
|
A hidden entity stays in the build, is not served, and says why: |
|
unnamed, suppressed, duplicate or placeholder. |
|
For things the game names but never numbers, so an allocated id survives a rebuild. |
The attribute registry¶
Every attribute field is annotated with what it means and how to show it. A field that forgets raises at import.
ge_buy_limit: Annotated[
int | None,
AttributeMeta("Buy limit", AttributeGroup.TRADE, 30, AttributeFormat.INT),
] = None
The registry records label, group, order, format and unit, plus
flags: display, derived (worked out rather than read), prominent (in the
overview box), technical (visible, not for a model to repeat), totalled,
choices (off the enum behind the field) and fields (the parts of a packed value,
each addressable).
Two things follow: a front end never hard-codes a field name, because /v1/types
hands it the whole registry; and a value pointing at another entity comes back as a
whole link rather than a bare id.
Relationships¶
Thirteen are declared, each with a label both ways round and the types it may join.
relationship |
forward |
inverse |
from |
to |
|---|---|---|---|---|
|
Drops |
Dropped by |
npc |
item |
|
Sells |
Sold in |
shop |
item |
|
Staffed by |
Runs shop |
shop |
npc |
|
Rewards |
Reward from |
quest |
item |
|
Uses ammunition |
Used by |
item |
item |
|
Found in |
Found here |
npc, shop, item, quest, scenery, music |
location |
|
Part of |
Contains |
location |
location |
|
Yields |
Gathered from |
scenery, npc |
item |
|
Makes |
Made from |
item |
item |
|
Requires |
Needed for |
quest, task |
item, quest |
|
Assigns |
Assigned by |
npc |
task |
|
Satisfied by |
Counts towards |
task |
npc |
|
Heard during |
Music heard |
music |
quest |
Edges are validated against that table: a drops edge starting at an item is refused,
as is one carrying the wrong attribute model.
Some links are the whole fact; staffed_by records nothing beyond somebody running
the shop. Others carry data. A drop keeps weight and denominator rather than a
rate, so a reader is shown an exact 1/512.
Two edges can join the same pair and still be different facts. Rather than a counter, an edge is keyed by its own attributes: a placement by its tile, a drop by which table it rolls on, a requirement by its kind. Reordering a source cannot move an edge onto a different key.
Variants and totals¶
The game numbers a noted item separately from the item it notes. Two pages would split every answer, so a variant points at its canonical entity and stays out of search. Walks travel over an entity and all its variants at once, which is why a walk total can be trusted.
Prices¶
A weekly series per item over roughly two years, carrying a confidence judged by how the
record itself moved: traded (genuine activity), static (never moved) or
untraded (nothing usable).
The manifest¶
Every artifact says what it is. data_version names the build, and is what
X-Data-Version reports and ?v= pins to. schema_version is the layout; a
surface refuses one it does not know. content_hash covers the whole snapshot, which
is how determinism is tested. built_at and game_version say when it was written
and from which commit.