Quest identifiers¶
QuestLevel is the canonical quest identifier across live runtime state and
msgspec wire types.
Canonical shape¶
Use QuestLevel
for:
- gameplay/runtime state
- replay headers
- network session settings and protocol structs
- quest definitions and quest registry lookups
- UI/view state after text input has been parsed
The value object intentionally stays small:
majorminortexttitleglobal_indexQuestLevel.from_global_index(...)QuestLevel.parse(...)QuestLevel.try_parse(...)
Boundary shapes¶
We still keep split quest stage integers only at external or native-shaped boundaries:
crimson.cfgpersistence- high score record bytes
- raw/debug trace rows
- filenames and human-facing labels
Those boundaries should convert to or from QuestLevel exactly once.
Rules¶
- Live runtime logic should accept
QuestLevel | None, not"1.1"and not(major, minor). - If a caller already has a
QuestLevel, do not reparse or reformat it just to pass it deeper. - Use
level.textonly for text I/O. - Use
level.global_indexonly for ordered quest-table traversal. - Keep quest counter math out of
QuestLevel; it lives inquests/status.py.
Boundary map¶
flowchart LR
A["CLI / text input ('1.1')"] --> B["QuestLevel.parse(...)"]
B --> C["Runtime state / replay / net / quest defs"]
C --> D["level.text for labels and filenames"]
C --> E["level.global_index for menu ordering"]
C --> F["split major/minor only at native-shaped persistence edges"]