The generated constants and stores
NodeTypes, GameBoards, EntityCodes, GameTags, MediaRegistry and the four stores — the typed names that replace every string literal in your game code.
All documentation — The generated code
Start here
Concepts
Authoring for a game
Playing a story
The generated code
The plugin and sync
On this page
Almost everything a sync generates exists so your game code never types a string literal. A renamed board should break your build, not your story.
The constants
NodeTypes and NodeTypeIds
The most useful of them, and the least obvious.
NodeTypes carries one nested static class per node type the project declares, holding that
type’s id, its code, and a constant for every field code it declares:
| Constant | |
|---|---|
TypeId | The stored node type id — what StepNode.TypeId carries |
TypeCode | The author’s own code for the type |
| one per field | The field’s code, ready for step.Field(…) |
<Field>Picture | For a position or area field, the code of the picture field it is placed on — so a handler finds the picture without spelling its code |
TrackLengthField | The code of the node-level number field that ends the type’s track; only on a type that names one |
| one nested class per repeater | Carrying RepeaterCode and that repeater’s own field codes |
<Repeater>.StartField · .DurationField | The codes of the block’s number fields that place a row in time; a duration never appears without a start |
<Repeater>.PauseField | The code of the number field a conversation waits after each row; only on a repeater drawn as a conversation (bubbles or screenplay) |
RepeaterCode is what PlayStep.Blocks(…) takes. The role constants (StartField, PauseField,
<Field>Picture, …) are how a handler reads a rule the editor decided without spelling the field’s
own code — the same field is read by the same constant in the editor’s own player.
Each constant carries the field’s label and type in its doc comment, and a field backed by an option
set says so and tells you to read it with field.Option().
NodeTypeIds is the same ids as a flat list, one constant per type. It is superseded by
NodeTypes.<Type>.TypeId — the two always agree — and is still emitted so an existing call site keeps
compiling. Use it where you need an id and nothing else about the type: a set of types to skip, a
player policy declaration, a key in a table of your own.
The rest
| Class | Holds |
|---|---|
GameBoards | One constant per board, plus GameStartBoard and GameStartNode |
BoardRegistry | Names — every board name |
EntityCodes | One constant per entity, grouped into a nested class per definition |
GameTags | One constant per tag, plus Label(string tagId) |
MediaRegistry | One constant per media item |
MediaIndex | The three lookup maps, and KeyOf(string idOrCode) |
OptionCodes | One class per option set, each carrying EnumCode and its option constants |
OptionLabels | Of(value), the author’s label for a typed option; Of(value, language, loadJson), its translation, falling back to the master label |
EntityFields | One constant per entity field, the key ResolvedEntity.Fields is read by, grouped into a nested class per definition |
LocalizationRegistry | DefaultLanguage, SupportedLanguages, and the three resource-path helpers |
MediaIndex is what the media catalog is installed from, and the reason
that bootstrap line names three maps.
LocalizationRegistry.GetUILocalizationPath returns the same address
UIStrings.ResourcePath does. There is one address, computed two
places, and they are kept equal deliberately.
The stores
Four objects, composed into the IGameState your generated code is written against. All four are
views over the one script runtime — none of them holds storage.
GameVariables
One typed member per variable, grouped into a nested class per context, plus the untyped surface the evaluator uses.
| Member | |
|---|---|
| one property per variable | Typed. Reading and writing goes through to the runtime. |
| one property per context | The nested class holding that context’s variables |
AllCodes | Every variable code |
GetVariable(string code) · SetVariable(string code, object value) | |
HasVariable(string code) | |
GetVariableEnumCode(string code) | What lets the evaluator resolve an enum comparison |
Reset() | Forwards to the runtime’s own reset |
EntityStore
| Member | |
|---|---|
FindEntity(string code) · FindEntityById(string id) | |
AllEntities() | |
WithTag(string tagId) | |
HasEntity(string entityCode) · HasTag(string tagId) | |
GetEntityField(string entityCode, string fieldName) | |
SetEntityField(string entityCode, string fieldName, object value) | Writes the playthrough overlay; the authored entity is untouched |
Run<Definition><Field>(…) | One per code field: runs the script the entity holds there. Returns a ScriptExecutionResult |
Check<Definition><Field>(…) | One per condition field: checks it. Returns a bool |
Spawn<Definition>(<Definition> template, string instanceId = null) | One per definition: spawns an instance and returns its id |
A script field’s call is typed by the field’s roles. For an Item whose onUse script receives
target and user, both Character:
ScriptExecutionResult RunItemOnUse(Item item, Character target, Character user)ScriptExecutionResult RunItemOnUse(string item, string target, string user)bool CheckItemUsable(Item item, Character target)The first overload takes the generated entities; the second takes codes or ids, which is how a
spawned instance is passed. Inside the script self is item, and each role is the argument of the
same name, so GameData.EntityStore.RunItemOnUse(dagger, target: goblin, user: hero) reads as the
script does — and a role renamed in the editor breaks the build at the named argument, rather
than the fight at run time. A field whose script receives no roles takes the entity alone. The name
joins the definition’s class and the field’s member name, so two definitions with a field of the
same name never collide; should two joins still produce one name, the second takes a numeric suffix
and the sync reports it. The same goes for two roles whose names differ only by case, such as
target and Target: both stay, the second as Target2, and each still reaches the script under
its own role name. A role whose definition the project no longer has is taken as a string
and reported as EDEF009.
For drawing an entity — name, colour, portrait — use
EntityPalette instead. This store is the data; the palette is the
presentation, and it resolves through the runtime so a renamed or translated character cannot be
current in one place and stale in another.
EnumRegistry
GetEnumValue(string enumCode, string optionCode) · GetEnumName(string enumCode, int value) ·
IsEnum(string code) · IsBitmask(string code).
CommandHandler
The bridge between a command an author declared and a method you write.
| Member | |
|---|---|
Register(IScriptRuntime runtime, EnumRegistry enums) | Called by the bootstrapper |
Dispatch(string commandCode, object[] arguments) | |
DispatchAwaited(string commandCode, object[] arguments, …) | |
public virtual void On<Command>(…), on CommandHandlerBase | One per declared command |
Diagnostics | Where a command you have not overridden is reported. Set by the bootstrapper |
ProjectDatabase and the data classes
ProjectDatabase carries one EntitySet<T> per entity definition, and a Seal() the
bootstrapper calls once. Every read throws until it has.
Around it:
EntityBase | The generated base — implements IProjectEntity and the tag interface, so a project with no tags still compiles |
| one class per definition | Its custom fields as typed members. Inherits its parent definition’s class when the project nests definitions. |
| one class per trait | Shared by every definition that carries it, and nested where a trait carries another |
EntityLoader.LoadAll(db, loadJson) | Fills the database |
TableData | One method per spreadsheet, each returning a list of that table’s typed row class |
A table’s row class has one typed member per column. No formula is evaluated and no reference is resolved at play time — the sync server materializes cells with entity and asset references already resolved, so what you read is a value.