# Totk Eventflow Editor

> Edit, inspect, patch, and validate Tears of the Kingdom EventFlow files (.bfevfl.zs), EventSetting/GameAnalyzedEventInfo wiring, AISchedule talk/near hooks, and Mals/MSBT dialogue entries. Use when Codex needs to add or modify NPC dialogue, choices, quest progress, GameData checks, subflows, actor actions/queries, or localized EventFlowMsg text in TOTK mods.

- Skill: `cargocult-mods/totk-eventflow-editor` (Agent Skill, multi-file: 10 files)
- Install (CLI): `npx skillmds@latest add cargocult-mods/totk-eventflow-editor`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cargocult-mods/totk-eventflow-editor/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: cargocult-mods (https://skillmd.com/u/cargocult-mods)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/cargocult-mods/totk-eventflow-editor

---


# TOTK EventFlow Editor

## Vanilla RomFS Rule

Use a vanilla TOTK `romfs` path already supplied in the current turn or registered project guidance. Otherwise ask the user for it before inspecting game files. Treat the path as read-only; do not search unrelated folders or guess a location.

## Core Workflow

Use this skill for EVFL work, but keep `totk-local-operator` loaded for compact file inspection and `totk-toolkit` loaded for shared file plumbing. Use `totk-msbt-mals-editor` for Mals/MSBT text wrapping, control tags, and EventTalk blank-slot padding.

1. Identify the gameplay trigger:
   - `AIScheduleParam` `EventEntry` maps interaction hooks such as `OnPlayerTalk` and `OnNearPlayer` to an `EventName`.
   - `RSDB/EventSetting.Product.*.rstbl.byml.zs` maps `EventName` to `FlowchartFileName` and `EntryPoint`.
   - `RSDB/GameAnalyzedEventInfo.Product.*.rstbl.byml.zs` must include the event and every subflow the event needs loaded. This is not recursive: if a custom flow calls a custom common flow that calls a vanilla flow, also include that vanilla flow and the vanilla flow's own listed dependencies.
2. Inspect the target flow before editing:
   - List actors, entry points, actions, queries, and reachable events from the edited entry point.
   - Trace from the exact entry point (`Talk`, `Near`, custom quest entry, etc.), not merely from the file root.
   - Search vanilla for the same actor action/query before inventing parameter shapes.
3. Patch the smallest surface:
   - Add action/query names to the actor's declared action/query list before using them in events.
   - When creating `evfl.Actor()` declarations, clone a vanilla declaration for the same performer when possible. For every actor record synthesized from scratch, populate all six safe-default event-member params: integer `EventMemberCreateMethod = 0`; booleans `IsGrounding = false`, `IsWorld = false`, and `IsBindNearestActor = false`; and strings `UseEventModelAnime = ""` and `UseEventAS = ""`. Do not emit `params = None` or an empty params container (`{}`). Newly added actor rows without a minimally populated parameter table can crash the game even when the file parses and reopens successfully. The mandatory minimal subset has not been isolated; no penalty is currently known for including all six, so builders must include the full set. Preserve or add actor-specific fields where vanilla uses them, such as `ExceptionalBindType = 1` for common `Npc_EventStarter` speaker patterns. Also set the correct `concurrent_clips`; `Npc_EventStarter` and `GameDataActor` talk-flow examples commonly use `1`.
   - Use `scripts/eventflow_actor_params.py` from builders and validators instead of retyping the six-field table. Call `ensure_safe_actor_params()` when synthesizing an actor and `assert_safe_actor_params()` on the written/reopened flow, scoped to the actor records the builder added.
   - For actor event-root AINB action/query work, trace the runtime actor's `EventPerformerParam.EventUniqueAin`; an EVFL actor declaration such as `Npc_EventStarter` may be a relay/facade and not the AINB owner that must be patched.
   - Preserve existing parameter keys and scalar types by cloning a nearby vanilla or local event when possible.
   - For `EventTalk`, update the referenced Mals/MSBT label in the same task as the EVFL edit.
4. Validate from written output:
   - Decompress and reparse the patched `.bfevfl.zs`.
   - Trace the edited entry point and confirm branch targets, params, and terminal events.
   - Read back `EventSetting` and `GameAnalyzedEventInfo`; confirm the event row names the intended entry point and includes the complete non-recursive subflow preload set.
   - For minimal/generated one-entry flows, also confirm there are no stale entry points or unreachable template events left in the written flow.
   - For vanilla-preserved flows, confirm retained vanilla events are reachable from explicit entry points. If the original `Talk` branch is kept while `Talk` is redirected, preserve the original branch as `vanillaTalk` instead of leaving it as a disconnected root.
   - Decompress and search the patched Mals archive for each new `MessageId`.
   - Validate every newly synthesized actor declaration after reopening the written output. Require all six safe-default keys with the expected scalar types; for blank-authored defaults, also require the expected values. Additional vanilla-derived keys are allowed.
   - Confirm the overlay contains only intentional files.

## Runtime Debugging Procedure

Use this procedure whenever an EventFlow parses but does not run, stops before an expected branch, or behaves differently in game than static inspection suggests.

1. Separate launch failures from logic failures:
   - If the actor exists but the event never starts, first verify `AIScheduleParam` hook, `EventSetting`, `GameAnalyzedEventInfo`, target flow name, entry point, actor declarations, and Mals availability.
   - If any debug `EventTalk` appears, the event launched; debug from the last visible message forward instead of reworking actor/schedule wiring.
2. Build from a proven baseline:
   - Preserve the last mod folder that ran in game.
   - Create fresh versioned test overlays instead of mutating failed builds in place.
   - Keep source overlays read-only and write only the new test folder.
3. Use cropped breakpoint variants:
   - Physically remove every event after the chosen breakpoint and remove unrelated/unreachable late events from the test file.
   - Do not merely disconnect, bypass, or rewire around suspected nodes. A malformed unreachable node can still make the EventFlow fail at load time, so disconnected content can invalidate the test.
   - Keep only the entry path, required actors, required subflow entries, and the next small EventFlow surface under test.
4. Add diagnostic `EventTalk` nodes:
   - Put a short debug talk immediately before and after the newly tested section, plus branch-case talks where case meaning is uncertain.
   - Add matching Mals/MSBT labels in the same build, enforce the dialogue line-length rule, and verify labels after recompressing.
   - Once earlier sections are proven, put debug talks only in the newly added section so playtest output stays readable.
5. Advance in a ladder:
   - Add one class of surface per variant: direct `EventTalk`, a subflow call, a self-subflow with literal args, a GameData int/bool branch, a player equipment query, an attachment query, an item remove/award action, etc.
   - If a later variant reaches its after-debug message, earlier variants in the same ladder are normally proven and do not need more testing.
   - If a variant never starts, suspect load metadata or a malformed node still present in the cropped file. If it starts and stops, the failing surface is between the last visible debug talk and the next expected one.
6. Control stateful confounders:
   - Reset or explicitly read back GameData work variables before relying on them.
   - Add debug readouts before and after reset/classification branches when stale values could mimic a valid result.
   - Do not infer actor/query semantics from names alone; use debug branches to prove case meanings in the exact player state being tested.

## Editing Rules

- Prefer a task-local Python builder script for binary edits. Do not hand-edit hex or rely on one-off GUI state for repeatable mod builds.
- Use the vanilla `Pack/ZsDic.pack.zs` `zs.zsdic` dictionary for `.bfevfl.zs`, Mals `.sarc.zs`, RSDB, and most compressed BYML files unless a known file type requires a different dictionary.
- Keep source mod files read-only when building tests; write outputs to a separate overlay folder.
- Use event numbers that do not collide with existing names. Reparse after writing because EVFL index tables are easy to corrupt silently.
- Be suspicious of event parameter types. In event-editor, write vector params as arrays like `[0,0,0]` when floats are intended; a string that looks like a vector is wrong.
- For new or changed text, patch the correct locale archive, usually `Mals/USen.Product.*.sarc.zs`, under `EventFlowMsg/<FlowchartName>.msbt`.
- Keep generated EventFlow dialogue lines short and validate every changed MSBT label before packaging. Use the established 48 UI-rendered character line limit for each line, excluding MSBT tags/markup from the count; insert explicit line breaks instead of leaving one long unwrapped line, preserve balanced tags, and pad intentional one-line EventTalk bubbles as `line\n\n` and two-line bubbles as `line 1\nline 2\n`.
- When cloning an EventFlow to a new file, set both the EventFlow root name and `flowchart.name` to the new flow name if the editor library exposes both. EventSetting still points at `Work/Event/EventFlow/<FlowchartName>.evfc`, while the overlay file is normally `Event/EventFlow/<FlowchartName>.bfevfl.zs`.
- Add the full transitive subflow set to `EventSetting` and `GameAnalyzedEventInfo`. Subflow preload does not cascade; include child subflows even from branches you do not expect to reach. Missing subflow load data can prevent the event from running even when the EVFL file itself parses.
   - Do not guess the closure from direct EVFL calls alone. Trace every custom subflow until it reaches vanilla flowcharts; for each vanilla flowchart you call, inspect its vanilla `GameAnalyzedEventInfo` row and copy/merge its `SubFlowList` into the custom event's row. For example, a custom flow that calls a custom common flow, which then calls vanilla `GetPouchContent`, must also include `GetPouchContent` and the vanilla `GetPouchContent` dependencies such as `Common` and `OpenActionGuideScreen`.
   - If the user is testing runtime behavior, follow the Runtime Debugging Procedure above: use cropped breakpoint variants, not rewired full graphs, and do not leave disconnected later events in the file.
   - Build breakpoint ladders from a proven minimal baseline. Each new variant should add one class of EventFlow surface at a time, such as a subflow call, a literal self-subflow, an int/bool branch, a player equipment query, an attachment query, or an award/remove action. If a later variant reaches its after-debug talk, earlier variants in the same ladder are normally redundant.
- When an EventFlow task reaches into actor event-root AINB files to add or expose actions/queries, validate the AINB with more than the same writer that produced it. AINB round-trips can preserve parseability while corrupting unmodelled selector-default string fields, including Japanese "other" default labels on F32/String selectors. Cross-check untouched default selector labels and string-pool fields with a second parser/export or raw scan before handing back a test mod.

## Minimal Talk Flow Pattern

For a simple placed NPC that only needs one custom `Talk` line:

- Reuse a vanilla talk EventFlow as a template and keep the `Npc_EventStarter` actor plus its `EventTalk` action.
- Start with the vanilla `InitTalk` subflow when the template uses it; wire its `nxt` to a new single `EventTalk` action whose `MessageId` is `EventFlowMsg/<FlowchartName>:<Label>`.
- Clone an existing `EventTalk` parameter container from the same flow, then change only choice fields and `MessageId` unless the requested behavior needs more.
- When the output only needs a single `Talk` line, do not leave the rest of the vanilla template graph in `flowchart.events`. After cloning the actor/action/parameter shape, prune the flow to the intended entry point(s) and reachable events only, usually `Talk -> InitTalk -> EventTalk`.
- If the user asks to preserve the vanilla template graph, keep the old `Talk` branch reachable by renaming its entry point to `vanillaTalk` before repointing `Talk` to the custom `InitTalk -> EventTalk` chain.
- For a one-line `Talk` flow, validate a compact graph shape after writing: one `Talk` entry point, an `InitTalk` subflow as the entry event, one `EventTalk` speech event, and zero events unreachable from `Talk`. This avoids editor tools showing disconnected leftover template nodes even when the game can run the flow.
- For blank-authored talk flows, also re-open the written flow and confirm generated actor declarations have valid flowchart performer metadata: used actor rows should have non-empty event-member params with the standard keys, and `Npc_EventStarter`/`GameDataActor` should use the same `concurrent_clips` and params shape as a comparable vanilla flow. Do not treat successful EVFL parse/readback alone as runtime-safe.
- In Python `evfl`, look actors up with `ActorIdentifier("Npc_EventStarter")`, not a generic string holder.
- If the source flow contains an actor identifier for the base NPC and the clone has a new actor name, rename that identifier in the cloned flow even when no event currently references it. This keeps future edits and diagnostics coherent.
- For the matching Mals archive, add `EventFlowMsg/<FlowchartName>.msbt` and verify the label bytes and UTF-16LE message text after recompressing.

## Common Patterns

- Start NPC talk with `InitTalk` subflows when vanilla examples do so. Use `Arg_Greeting` and `Arg_Turn`; pair talk uses `InitTalk_Pair` with actor params.
- Use `GameDataActor` bool/int/enum queries for quest state, remembered dialogue, and branch gating.
- For `GameDataActor.EventQueryIsGameDataBool`, case `0` means the bool is true and case `1` means the bool is false. Do not treat this like ordinary false/true integer ordering.
- Use `ChallengeSystemActor` actions for quest step progress; persistent quest progress also needs the `Step_<ChallengeName>` enum in `GameDataList`.
- Use `PouchSystemActor` for adding/removing/checking inventory contents; use `GetPouchContent` subflows for item get windows.
- Use `EventRequestQuit` only when you want to terminate the playable event deliberately. Vanilla examples vary in `SpecialEventActionType`; verify with a minimal test.

## References

- Read `references/eventflow-working-model.md` first when authoring or substantially changing EventFlows. It gives the mental map for EVFL graph structure, external wiring, common recipes, validation, and failure modes.
- Read `references/player-state-query.md` when using `Player.EventQueryCheckPlayerState`, interpreting `game::player::EventQueryPlayerState` values, or debugging player-state branch behavior.
- Read `references/action-query-catalog.md` when choosing actor actions, queries, or parameters from observed vanilla examples.
- Read `references/quest-wiring.md` when creating or debugging the full chain from Banc actor placement to AISchedule, EventSetting, GameData, Challenge, Mals, and EventFlow.
- Read `references/vanilla-eventflow-patterns.md` when designing new dialogue/content-selection behavior or interpreting vanilla flow logic such as ordered branches, missing cases, recent-talk variants, and reusable story barks.
- Read `references/research-findings.md` when answering open-ended questions about what vanilla evidence supports, especially NPC behind-talk, actor event-root AINB command injection, relative orientation, AISchedule versus EventFlow responsibilities, and promising query/action families.

