Skip to content

7. Add Lysa and the Second Quest

Quest 1 ends with Oren reading the courier’s warning. This chapter turns that discovery into the next playable objective: recruit Lysa Fen before approaching the ruined belfry.

Start from Checkpoint 6 or download Checkpoint 7.

  • How companions differ from NPCs
  • How a class template supplies companion presentation and baseline behavior
  • How recruitmentDialogue connects a companion to a dialogue tree
  • How to start one quest while completing another
  • How a targeted event produces durable story state
  • How completeWhen consumes that state without depending on dialogue timing

Replace the empty companions object with:

"companions": {
"tutorial_codex:lysa_fen": {
"id": "tutorial_codex:lysa_fen",
"name": "Lysa Fen",
"class": "Seer",
"hp": 62,
"maxHp": 62,
"str": 8,
"dex": 11,
"con": 10,
"int": 16,
"wis": 18,
"cha": 13,
"maxAether": 32,
"weapon": "staff",
"armor": "cloth",
"knownSpells": [
"cantrip_light",
"cantrip_minorHeal"
],
"bark": "The sound is not traveling through the air. It is traveling through memory.",
"dialogueRoot": "tutorial_codex:lysa_fen_dialogue",
"recruitmentDialogue": "tutorial_codex:lysa_fen_dialogue",
"placement": {
"target": "map:112,96:tavern"
}
}
}

Companions do not use NPC appearance fallback

Section titled “Companions do not use NPC appearance fallback”

Do not add an NPC-style appearance object to Lysa.

A companion uses its class template for its fallback sprite and class behavior. Lysa deliberately names the supported Seer class. Her authored statistics, equipment, and known spells then describe this particular companion.

An unknown class can fall back to the built-in Wanderer template, but that is a safety net—not an authoring strategy. Use a supported class intentionally and validate every referenced spell and equipment ID.

dialogueRoot supplies Lysa’s ordinary conversation. recruitmentDialogue identifies the conversation used for joining the party. Both point to the same dialogue tree in this campaign.

Add this quest beside tutorial_codex:missing_courier:

"tutorial_codex:silence_drowned_bell": {
"id": "tutorial_codex:silence_drowned_bell",
"title": "Silence the Drowned Bell",
"summary": "Recruit Lysa Fen, then prepare to descend into the ruined belfry north of Greyhaven.",
"objectives": [
{
"id": "recruit_lysa",
"text": "Ask Lysa Fen to join you.",
"detail": "Lysa is waiting in Greyhaven's tavern. Her knowledge of the bell may help in the crypt.",
"marker": {
"targetId": "tutorial_codex:lysa_fen"
},
"completeWhen": {
"hasFlag": "tutorial_codex:lysa_recruited"
}
}
]
}

This checkpoint introduces only the objective whose target already exists. The dungeon, boss, and remaining objectives arrive together in Chapter 8, so Checkpoint 7 does not contain forward references to undeclared content.

In Oren’s letter-resolution actions, add:

{
"startQuest": "tutorial_codex:silence_drowned_bell"
}

Place it after:

{
"completeQuest": "tutorial_codex:missing_courier"
}

Update the progress message to:

{
"showMessage": "The Missing Courier is complete. Silence the Drowned Bell begins. Speak with Lysa in Greyhaven's tavern."
}

One conversation now performs a deliberate handoff:

complete Quest 1
→ start Quest 2
→ direct the player to Lysa

The second quest must already exist before this action references it.

Put these Oren branches before his Quest 1 branches:

{
"when": {
"all": [
{
"questActive": "tutorial_codex:silence_drowned_bell"
},
{
"lacksFlag": "tutorial_codex:lysa_recruited"
}
]
},
"text": "Speak with Lysa in the tavern. She has heard the bell in dreams and knows how the belfry's lower stones were laid.",
"actions": [
{
"endDialogue": true
}
]
},
{
"when": {
"questActive": "tutorial_codex:silence_drowned_bell"
},
"text": "Lysa has joined you. Prepare before leaving Greyhaven; the ruined belfry lies north of town.",
"actions": [
{
"endDialogue": true
}
]
}

Add this as the first branch of Mara’s quest topic:

{
"when": {
"questActive": "tutorial_codex:silence_drowned_bell"
},
"text": "Oren is right to send you to Lysa. She listens to things the rest of us would rather ignore.",
"actions": [
{
"endDialogue": true
}
]
}

These branches make the new state visible through more than one character. They do not advance the quest; they help the player understand it.

Add a new dialogue record:

"tutorial_codex:lysa_fen_dialogue": {
"id": "tutorial_codex:lysa_fen_dialogue",
"npcId": "tutorial_codex:lysa_fen",
"greeting": [
{
"when": {
"hasCompanion": "tutorial_codex:lysa_fen"
},
"text": "Lysa nods toward the door. “We should keep moving.”"
},
{
"when": {
"questActive": "tutorial_codex:silence_drowned_bell"
},
"text": "Lysa looks up before you speak. “Oren read the letter.”"
},
{
"text": "Lysa watches the surface of her cup as though it contains a distant sky."
}
],
"topics": {
"name": {
"text": "Lysa Fen. I study visions, echoes, and the lies people tell themselves about both."
},
"job": {
"text": "I listen for patterns. The bell is not traveling through air; it is traveling through memory."
},
"join": [
{
"when": {
"hasCompanion": "tutorial_codex:lysa_fen"
},
"text": "I am already with you.",
"actions": [
{
"endDialogue": true
}
]
},
{
"when": {
"all": [
{
"questActive": "tutorial_codex:silence_drowned_bell"
},
{
"lacksCompanion": "tutorial_codex:lysa_fen"
}
]
},
"text": "The belfry's lower chamber was built to guide the dead home. If the guardian has forgotten that purpose, you will need someone who can hear what it remembers.",
"actions": [
{
"recruitCompanion": "tutorial_codex:lysa_fen"
},
{
"endDialogue": true
}
]
},
{
"text": "Not yet. Oren must first read whatever warning the courier carried.",
"actions": [
{
"endDialogue": true
}
]
}
],
"quest": [
{
"when": {
"questActive": "tutorial_codex:silence_drowned_bell"
},
"text": "The deepest chamber will be shaped by memory more than stone. Light will help. So will refusing to listen when it speaks in a familiar voice.",
"actions": [
{
"endDialogue": true
}
]
},
{
"text": "The bell is awake, but the road to it is not yours yet.",
"actions": [
{
"endDialogue": true
}
]
}
]
},
"keywords": {}
}

The join branches are ordered from most specific to least specific:

  1. Already recruited
  2. Quest active and not recruited
  3. Unconditional fallback

That ordering prevents repeat recruitment and prevents Lysa from joining before Oren starts Quest 2.

Record recruitment through a targeted event

Section titled “Record recruitment through a targeted event”

Replace the empty triggers array with:

"triggers": [
{
"id": "tutorial_codex:record_lysa_recruited",
"event": "companionRecruited",
"targetId": "tutorial_codex:lysa_fen",
"once": true,
"actions": [
{
"setFlag": "tutorial_codex:lysa_recruited"
},
{
"showMessage": "Lysa Fen joins the expedition to the Drowned Belfry."
},
{
"playSFX": "quest_progress"
}
]
}
]

event: "companionRecruited" describes the kind of occurrence. targetId narrows it to Lysa.

Without the target, recruiting some unrelated companion could set the campaign’s Lysa flag. The event would be real, but the conclusion would be false.

The trigger produces durable state:

companionRecruited: Lysa
→ set tutorial_codex:lysa_recruited

The objective consumes that state:

hasFlag: tutorial_codex:lysa_recruited
→ complete recruit_lysa

This separation is safer than making the dialogue action directly complete the objective. Recruitment can occur through the supported recruitment system, while the quest checks the durable result.

The validator should resolve:

Oren startQuest → silence_drowned_bell
recruit_lysa marker → Lysa companion
Lysa dialogue npcId → Lysa companion
recruitCompanion → Lysa companion
companionRecruited targetId → Lysa companion
completeWhen hasFlag → supported condition shape

It cannot prove that the recruitment event fires during play or that the flag causes the objective to complete. Those are runtime checks.

Use a fresh game and complete Quest 1.

Verify:

  1. Oren completes The Missing Courier and starts Silence the Drowned Bell.
  2. The new quest initially points to Lysa.
  3. Lysa appears in Greyhaven’s tavern using her Seer class-template presentation.
  4. Lysa refuses recruitment before Quest 2 is active.
  5. The join topic recruits her after Quest 2 starts.
  6. tutorial_codex:lysa_recruited is recorded through the targeted event.
  7. recruit_lysa completes.
  8. Speaking with Lysa again reaches “I am already with you.”
  9. Dismissing and re-recruiting her does not erase completed quest progress.

The campaign now contains:

  • two linked quests,
  • Lysa’s class-template companion record,
  • recruitment-aware dialogue,
  • one targeted companionRecruited trigger,
  • a durable recruitment flag,
  • an objective completed from that flag.

Continue to Chapter 8: Add the Dungeon and Boss.