Full Dialogue Run, Revisited
![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd95c1fb2-fa10-4311-8466-946dfa89aa3c_819x1024.png)
I began developing a full dialogue run of Earthbound (1994) in March. This post represents a refinement and advancement of this idea (Post 1, Post 2, Post 3, Post 4). The current definition for this run is as follows:
A full-dialogue run of Earthbound is a playthrough of the game in which every normally-accessible line of overworld and inventory dialogue appears on-screen. “Normally-accessible” means lines of dialogue that are accessible without the use of glitches, cheats, or hacks. Battle-only text such as enemy attacks or in-battle item usage are excluded.
Complexity and Challenges
This run is complex because Earthbound’s dialogue is complex. While the volume of text is comparable to other JRPGs of the era (Table 1), Earthbound’s script is characterized by deep branching and frequent checking of dynamic gameplay conditions such as party composition, inventory contents, and/or the current state of the plot.
Such is the complexity, in fact, that the script of Earthbound is also a “script” in the programmatic sense. The dialogue data contains code for displaying all of the game’s menus and includes the numerous scripting commands that are readily apparent through gameplay, including changing the speed of text, changing the music or party location, adding or removing party members, and more.
This poses serious challenges for the routing of a full-dialogue run, much less the prospect of optimizing such a route. A prototype route in progress has about 400 travel and interaction steps between the start of the game and arrival to Twoson and takes about 5 hours to complete. As the game goes on, towns become more sparse and travel speed increases, but conversely an increasing amount of backtracking is required to register new dialogue in previously-visited locations. Given the existence of exclusive branches in dialogue, reloading the game or performing multiple runs of the game is also required.
An equally important challenge is validation of the selected route, that is, demonstrating that it has satisfied the requirement of “full dialogue”. “A stitch in time saves nine” is especially true here: a well-documented route with careful tracking of NPC interactions and thorough checking against the decompiled game script will support validation of the route.
Also unaccounted-for in the discussion so far are the game’s exclusive branches, where one path (usually a dialogue choice) permanently locks out another one. An early example: if the player has collected the Cracked Bat from Tracy’s room but not equipped it, Pokey asks if Ness/the player knows what “Equip” means. His dialogue is different depending on the player’s response and the selection can’t be repeated. There are two ways to solve this complexity: perform multiple runs through the game, or use saving and loading to make different choices.
As a matter of optimization, a combined approach is probably best, using saving and loading for short branches and additional runthroughs for long branches. (Note that similar basic questions arise around some of the game state-dependent dialogue: is it more efficient to kill Ness and get Paula-specific dialogue when it becomes available, or should this conditional dialogue be grouped and deferred to later in the run when transit speed is increased?)
The final challenge is executing the validated route. A runner must be sure not to skip any steps — a speedrun tracker may be useful for this purpose and, coupled with a recording, should suffice for manual validation against the published route.
A more robust and ultimately less time-consuming solution would be to redefine the parameters for a successful run in terms of a list of memory addresses which must be accessed in the course of the run. A LUA script could monitor those addresses and log any reads that happen in the course of a run. This log could then be compared to the list of required addresses and a run validated trivially.
Preparation
Decompile the game ROM
Place Earthbound ROM into “roms” folder
Install CoilSnake 4.1 into “utilities” folder
Decompile Earthbound ROM with CoilSnake to “decompilations/[your_name]”
ETL game script and NPC data
(This workflow collates NPC data and generates more readable versions of the game script. Surprisingly, I found no canonical or ad hoc list of names or descriptions for Earthbound’s NPCs. I have created simple names for every sprite ID using community-given names (e.g. “Mr. T”), distinctive traits, or the object they represent.)
Dump Earthbound script to “decompilations/[your_name]/_cleanscript” using “earthbound-script-dumper” (CataLatas)
Run “main.py”
Requires a decompilation and clean script as above.
Operation:
Dereference conditional and branching dialogue in the dumped Earthbound script
Join dereferenced dialogue to NPC configuration and map position tables from the decompiled ROM
Outputs:
“npc_values.csv”, table of NPC IDs, sprite IDs and labels, flag conditions, and tile location
“flat_dialog.json”, all NPC details, base dialogue, and dereferenced dialogue by NPC ID
ETL game map data
(Earthbound uses a monolithic world map that includes both overworld regions, dungeons, and rooms. NPC locations are described in terms of world map tiles, so it is useful to have a visualization and description of the referenced regions.)
Export Earthbound world map using EB Project Editor (integrated with CoilSnake 4.1)
Use Photoshop to create a named and nested layer for each overworld region and room
Use “Photoshop-Export-Layers-to-Files-Fast-2.7.1” (antipalindrome) to export each layer to a nested folder
Use Bulk Rename Utility or similar to rename and flatten images to a single directory
Run “process-images.py”
Operation:
Trim output from Photoshop script to the boundaries of the region/room
Log the size and bounding box for the region/room
Outputs:
“split-regions-and-rooms”, folder of nested images of map regions/rooms
“list_of_regions.csv”, table of map regions with hierarchy, size, and bounding box
Since I’ve already done this process, Step 2 can be expedited using the data from “list_of_regions.csv”. This process is documented mainly for historical and validation purposes.
Develop the Route
Organization
A significant amount of NPC dialogue is conditioned on game state. The game script checks the following conditions:
Flags. Flags are values tracked in RAM and the game save file for a variety of purposes:
Visibility of NPCs, e.g.:
107: ‘ONETT_SUNRISE’
109: ‘POLICE_AT_METEORITE’
Completion of narrative milestones, e.g.
272: ‘DIAMOND_TO_BE_DELIVERED’
273: ‘MU_TRAINING_COMPLETE’
275: ‘LEARNED_TELEPORT’
Pointers for case/switch-style dialogue, e.g.:
250: ‘SHOP_SATURN_CONSUMABLES’,
251: ‘SHOP_DESERT_DRUGSTORE_EQUIPMENT’
252: ‘SHOP_DESERT_DRUGSTORE_CONSUMABLES’
(Static/Dynamic) Despawning one-time enemies, e.g.:
417: ‘BRICK_ROAD_DUNGEON_DUCK_2_DEFEATED’
418: ‘BRICK_ROAD_DUNGEON_PROTOPLASM_1_DEFEATED’
Party composition, including:
Lead party member
Presence or absence of party member
Party state, including:
Specific status condition (Homesick, Mushroomized, etc.)
Incapacitated (Diamondized, Unconscious)
Inventory state, including:
Empty/full inventory
Presence/absence of item
Availability/unavailability of money
Knowing this, and using the resources generated in the “Preparation” stage, the target for route development is to prepare a sequence of actions and events that, if followed, result in the appearance of “every normally-accessible line of of overworld and inventory dialogue”. These actions and events include:
Routing steps
“Walk to Tracy’s Room”
Interaction steps
“Talk to Tracy > Yes > No”
“Use Key to Cabin on Tracy (123)”
Game state mutation steps
“Fill inventory”
“Kill Ness”
Flag updates
“Set LEARNED_TELEPORT”
Inaccessible Dialogue
Inaccessible 123 (geometry)
Exclusive Branch
Xbranch (Pokey’s “Equip” dialogue)
Save Game
Save Slot1
Load Game
Load Slot1
Note that “Inaccessible Dialogue” is more an observation than an action/event. For run validation purposes, it is helpful to include this. Where possible I’ve tried to annotate this at the point in the sequence when a flag update would make the dialogue logically inaccessible.
I’m still not sure how best to handle exclusive branches, especially in cases where it’s not clear that saving and loading is the fastest choice. For now, I am annotating these considerations in the sequence as shown above.
Implementation
So, what does the sequence actually look like? I have created a spreadsheet with these primary fields:
Playthrough Number. The current playthrough of the game
Segment Number. The order of the segment in the playthrough
Step Number. The order of the step in the sequence
Segment. The most recent flag change, or change of game state leading to a flag change
Step. One of the actions or events defined above
These information fields:
Subsegment. A well-defined or repeating set of steps
Read Newspapers
Traverse Shop Dialogue
NPC (ID)
NPC (Label)
Item (Label)
Map Location (X, Y)
Map Location (Label)
And supplemental tabs for quick access to “npc_values.csv” and “list_of_regions.csv”.
Assembly
Because plot flags are the primary source of segmentation for this run, the most intuitive way to assemble the route is to advance the game normally, stopping each time a flag is modified to review newly-accessible NPCs. To review a new NPC:
Locate the NPC record in “flat_dialog.json”
Review the dereferenced dialog and add routing steps for all conditions and branches
Logically, this should yield a (if unoptimized) sequence that fulfills the requirements for the full dialogue run.