Scenario API reference
Scenarios are Lua scripts that exercise end-to-end gameplay through the real gRPC service stack. They live under internal/test/game/scenarios/ and run as part of make smoke.
Running scenarios
# All scenarios (integration + scenario lanes)
make smoke
# Single scenario file (via go test, matching the runner)
go test ./internal/test/game/... -run TestScenario/basic_flow -count=1
Core DSL
Every scenario starts with a constructor and returns the scenario object:
local scn = Scenario.new("my_scenario")
local dh = scn:system("DAGGERHEART")
-- ...steps...
return scn
Setup phase
| Function | Purpose |
|---|---|
Scenario.new(name) | Create a named scenario. |
scn:system(id) | Obtain a system handle for system-specific helpers (e.g. "DAGGERHEART"). |
scn:campaign{name, system, gm_mode, theme} | Create the campaign. |
scn:participant(name, opts) | Join a participant (GM or player). |
scn:pc(name, opts) | Create a player character (auto-joins a participant if needed). |
scn:npc(name, opts) | Create a non-player character. |
scn:prefab(name, opts) | Create a character from a prefab template. |
Session lifecycle
| Function | Purpose |
|---|---|
scn:start_session(name) | Start a session (auto-creates if needed). |
scn:end_session() | End the current session. |
Scene management
| Function | Purpose |
|---|---|
scn:create_scene{name, ...} | Open a new scene. |
scn:end_scene() | Close the current scene. |
scn:scene_add_character(name) | Add a character to the active scene. |
scn:scene_remove_character(name) | Remove a character from the active scene. |
scn:scene_transfer_character(name, opts) | Transfer a character between scenes. |
scn:scene_transition{...} | Transition the scene (change phase, setting, etc.). |
scn:set_spotlight(target) | Set the campaign spotlight. |
scn:clear_spotlight() | Clear the campaign spotlight. |
Interaction (player/GM turns)
| Function | Purpose |
|---|---|
scn:interaction_activate_scene(...) | Activate a scene for interaction. |
scn:interaction_open_scene_player_phase(...) | Open a player phase. |
scn:interaction_submit_scene_player_action(...) | Submit a player action. |
scn:interaction_yield_scene_player_phase(...) | Player yields their phase. |
scn:interaction_interrupt_scene_player_phase(...) | GM interrupts the player phase. |
scn:interaction_resolve_scene_player_review(...) | Resolve a review step. |
scn:interaction_record_scene_gm_interaction(...) | Record a GM interaction. |
scn:interaction_open_session_ooc(...) | Pause for out-of-character discussion. |
scn:interaction_post_session_ooc(...) | Post an OOC message. |
scn:interaction_resolve_session_ooc(...) | Resolve the OOC pause. |
scn:interaction_expect{...} | Assert interaction state. |
System-specific (Daggerheart)
Obtained via dh = scn:system("DAGGERHEART"):
| Function | Purpose |
|---|---|
dh:adversary(name, opts) | Create an adversary. |
dh:attack{actor, target, trait, ...} | Execute an attack roll with outcome assertions. |
dh:expect_gm_fear{...} | Assert GM fear value. |
Modifiers.mod(source, value) | Build a roll modifier. |
Modifiers.hope(source, amount) | Build a hope spend descriptor. |
Example
A minimal scenario that creates a campaign, adds a PC, and runs a quiet session:
local scn = Scenario.new("basic_flow")
scn:system("DAGGERHEART")
scn:campaign{
name = "Basic Flow Campaign",
system = "DAGGERHEART",
gm_mode = "HUMAN",
theme = "basics"
}
scn:pc("Frodo")
scn:start_session("First Session")
scn:end_session()
return scn
A combat scenario with outcome assertions:
local scn = Scenario.new("action_roll_outcomes")
local dh = scn:system("DAGGERHEART")
scn:campaign{ name = "Outcomes", system = "DAGGERHEART", gm_mode = "HUMAN" }
scn:pc("Frodo", { stress = 1 })
dh:adversary("Nazgul")
scn:start_session("Combat")
dh:attack{
actor = "Frodo", target = "Nazgul", trait = "instinct",
difficulty = 0, outcome = "hope",
expect_outcome = "hope", expect_hope_delta = 1,
expect_damage_total = 5, damage_type = "physical"
}
scn:end_session()
return scn