Dialogue Injection
DialogueInjection lets you attach an extra choice to an existing vanilla or custom NPC dialogue flow without replacing the whole container.
This is useful when you want to extend an NPC's normal conversation with one additional branch, such as a service unlock, a follow-up action, or a context-specific option that should only appear for a specific character.
Overview
The injection flow has two parts:
- Create a
DialogueInjectionthat describes where the new choice should be inserted - Register it with
DialogueInjector.Register(...)
S1API then waits until the matching NPC is available in the world, finds the requested dialogue container and source node, appends the new choice, creates the node link, and wires up your confirmation callback.
using S1API.Dialogues;
DialogueInjector.Register(new DialogueInjection(
npc: "Philip",
container: "CasinoDialogue",
from: "ROOT_NODE_GUID",
to: "BANKING_NODE_GUID",
label: "ASK_ABOUT_PAYOUTS",
text: "Quick question about payouts.",
onConfirmed: () =>
{
// Handle the selected option
}));
When to Use It
Use DialogueInjection when you want to:
- Add one or more new choices to an existing NPC dialogue tree
- Reuse an existing container instead of building a full replacement
- Hook into a known dialogue node and branch into your own follow-up node
- Attach lightweight interaction logic to a dialogue choice callback
If you need to build a full custom dialogue flow from scratch, start with Dialogue System instead.
Constructor Options
DialogueInjection supports two ways to target an NPC.
By NPC ID
Use the string overload when you know the NPC's ID.
var injection = new DialogueInjection(
npc: "Philip",
container: "CasinoDialogue",
from: "ROOT_NODE_GUID",
to: "BANKING_NODE_GUID",
label: "ASK_ABOUT_PAYOUTS",
text: "Quick question about payouts.",
onConfirmed: OnAskAboutPayouts);
Internally this maps to x => x.ID.Equals(npc).
By Predicate
Use the predicate overload when you need more control.
using S1API.Entities;
var injection = new DialogueInjection(
appliesToNpc: npc => npc.ID == "Philip" && npc.FullName.Contains("Philip"),
container: "CasinoDialogue",
from: "ROOT_NODE_GUID",
to: "BANKING_NODE_GUID",
label: "ASK_ABOUT_PAYOUTS",
text: "Quick question about payouts.",
onConfirmed: OnAskAboutPayouts);
This is helpful when multiple NPC variants share related data or when your targeting rule depends on runtime state.
Field Reference
Each DialogueInjection instance needs the following values:
AppliesTo: Predicate used to decide whether a world NPC should receive the injectionContainerName: Name of the target dialogue containerFromNodeGuid: GUID of the node that should receive the new choiceToNodeGuid: GUID of the node the new choice should lead toChoiceLabel: Internal identifier used for callback registrationChoiceText: Text shown to the player in the dialogue UIOnConfirmed: Action invoked when the player selects the injected choice
How Registration Works
When you call DialogueInjector.Register(...):
- The injection is queued
- S1API starts a lightweight wait loop if one is not already running
- The loop checks loaded NPCs until one matches
AppliesTo - The injector resolves the requested container by name
- The injector finds the source node by
FromNodeGuid - A new choice and node link are added to that dialogue container
DialogueChoiceListenerbinds yourOnConfirmedcallback toChoiceLabel
This means you can usually register injections during your mod setup without waiting for the NPC to already be spawned.
Example
The example below adds a new question to an existing NPC dialogue and sends the player into a custom follow-up node when selected.
using S1API.Dialogues;
public static class PayoutDialogueFeature
{
public static void Register()
{
DialogueInjector.Register(new DialogueInjection(
npc: "Philip",
container: "CasinoDialogue",
from: "INTRO_NODE_GUID",
to: "PAYOUT_INFO_NODE_GUID",
label: "ASK_PAYOUT_QUESTION",
text: "Quick question about payouts.",
onConfirmed: OnPayoutQuestionSelected));
}
private static void OnPayoutQuestionSelected()
{
// Put your feature logic here
}
}
In practice, the target node identified by ToNodeGuid must already exist in the container you are linking into.
Requirements and Limitations
ContainerNamemust match a real container name exactlyFromNodeGuidmust point to an existing node in that containerToNodeGuidmust point to an existing destination nodeChoiceLabelshould be unique enough for your mod's callback usage- If the NPC, container, or node cannot be found, the injection is skipped silently
Because this API works against existing dialogue assets, you should verify container names and GUIDs carefully before shipping.
Best Practices
- Prefer stable NPC IDs over loose matching when possible
- Keep
ChoiceLabeldescriptive and mod-specific to avoid collisions - Use player-facing
ChoiceTextthat fits the NPC's existing conversation tone - Keep
OnConfirmedfast and push larger flows into your own dialogue nodes or systems - Test the target dialogue path in-game after save loads and scene changes
Troubleshooting
The choice never appears
Check these first:
- The NPC ID or predicate actually matches a loaded
S1API.Entities.NPC - The container name is correct
- The source node GUID exists in that container
- The target node GUID exists and is reachable
The callback does not run
Make sure:
ChoiceLabelis unique for the injected choiceOnConfirmeddoes not throw immediately- Another mod is not reusing the same choice label in the same dialogue flow
Next Steps
- Read Dialogue System for building full containers and custom nodes
- Use
DialogueInjectionfor small extensions andDialogue.BuildAndRegisterContainer(...)for larger custom flows