Sprite Dialogue Data

Sprite Dialogue Data

Background

The Sprite Dialogue Editor handles the events found in the dialogue event routines (found in Bank E of a clean FF1 ROM image). These routines specify a series of actions, and typically refer to either a hardcoded value or a collection of parameters to identfy in-game entities.

Each sprite in the game has four parameters (each being an 8-bit value) to use with its routine.
A table in the ROM assigns a routine to each sprite.

Paulygon's FFHPlus Sprite Dialogue editor allowed for editing of both parameter data and hardcoded values.
I wanted the same thing in FFHEx, but I had two problems:

  1. I didn't have the source for FFHPlus, so I'd have to write it from scratch;
  2. It worked for ROM, but how could I make it work for assembly?

I didn't want to hardcode the data used to populate the editor, so I created a small Ini file (yeah, I know) to store the information for each handler. The editor won't function without this file. In the project folder, you should find a file with the extension ".dialogue". That file holds the information used to populate the editor.
The assembly project can simply refer to the elements in the same file, which means the file should work for both an assembly project and the ROM image it produces when compiled.


sprite-data]=[

Sprite Data

Sprites, Events, and Flags

A sprite is a visible NPC placed on a standard map.
Sprites occupy a single tile on the map and move around in a random pattern one step at a time.
The player can interact with a sprite using the A button. Sprites are game objects.

An event is a routine in the game that is intended to execute instructions as a result of interaction with a game object.
These routines make certain assumptions regarding values in memory, and about other setup routines having already been called to prime values in memory.

A game object flags is a byte that is set to indicate that a specific event has happened (e.g. has Nerrick received the TNT?).
Since each sprite is a game object, each sprite has an entry in the table of game object event flags.

Sprite Table

The sprite table is a table containing the details of each sprite in the game - details such as X position, Y position, room flag, graphic ID, etc..
In FFHEx, the names of the sprites are stored as editor settings; the game itself does not store labels for the sprites.

Sprite Routine Params Table

Each sprite has 4 parameters, which vary according to the routine assigned to the sprite.
A table (in Bank E in the original game) holds param entries for each sprite in the game; each row in the table holds 4 bytes representing the parameters for the corresponding sprite's event routine.
By default, the params occur in the same order as the sprite table.

Sprite Routine Jump Table

In bank E (original game) there's a table holding the bank address of the event routine assigned to the corresponding sprite. Sans code changes, the game only supports a single routine per sprite.
By default, the routines occur in the same order as the sprite table.

Sprite Routines

Routines implement various kinds of interaction, ranging from setting game object flags to adding weapons to inventory.
The routines can also use hard coded IDs to refer to any of those same entites (sprites, game objects, flags, weapons, items, etc.);

For example, the King of Coneria hard codes the ID of the Rescued Princess to determine whether or not the bridge needs to be built.
Similarly, the Rescued Princess hard codes the RAM address of the LUTE when she checks to see if she needs to give it to you or you already have it.
Because of the differing hard coded values, they both need a dedicated routine.

On the other hand, each of the four ORBs uses its first three sprite params to indicate the ORB sprite to hide, the boss battle to launch, and the text to display before battle.
By using params instead of hard coded IDs, all four sprites can use the same routine.


the-dialogue-file]=[The '.dialogue' File

The .dialogue File

The ".dialogue " file contains Ini sections. Some are for general-purpose information, a couple are specific to assembly projects, and the rest are mappings for each routine used in the game.
We'll cover each section in the file, giving an explantion of its contents and usage.

[VALUETYPES]

This section provides bit widths and descriptive names for the supported types. Bit widths are used to evaluate and process dialogue elements.
See Value Types below for more information about the value types that are used in the editor.

[Label_TalkJumpTable]

[Label_TalkJumpTable] value=lut_MapObjTalkJumpTbl desc=Label denoting the start of the Talk handler jump table

Used by Assembly projects only.
The value contains the label marking the start of the sprite routine mapping list. The list contains a label (i.e. bank address) for each sprite in the game. Assembly projects read this label to know where to start reading the list.
The list length is known, so no marker is explicitly required needed to indicate its end.

[Label_EndParsingMarker]

[Label_EndParsingMarker]
value=|:endtalkparsing
desc=Marker that signals the end of talk handler parsing

Used by Assembly projects only.
The value contains the marker used to unambiguously indicate the end of the routines. The editor will stop parsing the file when it reaches this marker.
This isn't strictly necessary since the list length is known, but can useful to prevent the sprite editor from parsing extra whitespace lines occurring after the list.

Dialogue routines

[RoutineLabel]
desc={Description of the routine}
bankaddr=0x{hex address}
elem0={type}|{funcoffset}|{paramindex}|{comment}
...
elemN={type}|{funcoffset}|{paramindex}|{comment}
Field nameDetail
Routine LabelA label identifying the routine.
In ROM Projects
Only used to identify the routine by name.
In Assembly Projects
The name of the routine in the assembly source file.
The label here must match the name of the routine label in the assembly source file verbatim.
descA description of the field.
This is not used by FFHEx; it was used by previous external editing tools.
bankaddrThe in-bank address of the start of the routine.
Pay close attention to the fact that this is **NOT** a ROM offset. Using a ROM offset here will likely crash your game or corrupt memory when the event fires.
elem[0 - N]A trio of pipe-separated values defining a dialog routine element.
type
the element value type, as specified in Values Types below.
funcoffset
offset in bytes from the start of the routine
paramindex
index into the sprite's 4-element parameter array, -1 if not used (i.e. hardcoded).
comment
shown in the editor to help identify the element's purpose.

value-types]=[

Value Types

Param types

These type will expect a value from 0-3, which is an index into the sprite's 4-byte parameter array. Each sprite in the game has its own 4-byte parameter list. There's not a fixed format for them, but most routines use index 0 to represent the sprite taking the action.
Consult the assembly source for more details on each routine uss its parameters.

Type nameDetail
text Index of dialog text (pointer table)
spr Index of a sprite, this is a reference to another sprite.
This can be any sprite in the game, not just the ones on the current map.
obj Same as spr (visibility change - semantic difference)
event Same as spr (event flag check - semantic difference)
battle Index of a battle
item
Index of item, added to(unsram + $20).
Index is added to the RAM address specified by the unsram RAM value.
weapon Index of a weapon
gold Index of a gold item

Hardcoded types

These expect to read one or more bytes which directly represent either a value or an address. The routines dictate how the values are actually used, but the types instruct the editor toload them in a way that the routine would expect.
Consult the assembly source for more details on how each routine ues its hardcoded values.

Type nameDetail
hctext 1-byte index of dialog text (index into the text pointer table)
hcspr 1-byte sprite index
hcobj 1-byte sprite index - semantically, a visibility change
hcevent 1-byte sprite index - semantically, an event flag check
(has this event happened yet?)
hcbattle 1-byte battle index
hcitem 2-byte address of item (includes unsram + $20).
This is the actual RAM address of the item.
hcmap 2-byte overworld map object (canal, bridge, etc.).
This is the RAM address of the overworld object in question.
hcweapon 1-byte weapon index
hcgold 1-byte gold item index
hcitemorcanoe 2-byte item or canoe.
This is the RAM address of the object in question.
hcitemormap 2-byte item or Overworld map object.
This is the RAM address of the object in question.
hcfanfare
2-byte value to increment the sound register or ignore it:
E6 7D = play the sound
EA EA = on't play the sound
hcnntele 1-byte, index of NN teleport
hcnotele 1-byte, index of NO teleport
hcuint8 1-byte decimal value
hcbyte 1-byte hex value
fanfare-note]=[About the Hardcoded Fanfare type (hcfanfare)
* About hcfanfare usage

Putting two hcfanfare elements back-to-back with the value E67D will play the treasure chest sound. The only place where this is done in the game is the Black Orb's routine. To make this happen with any other routine requires an assembly hack.
In the code, this increments a byte in RAM twice (effectively setting it to 2), which tells the sound subsystem to play the treasure chest sound instead of the fanfare (which plays if set to 1).


an-example]=[

An example

FFHPlus

I took inspiration from FFHPlus, which shows the elements of the event and editing on many of them. The three Text fields are parameters. The Weapon received and the "Weapon slots full" text are hardcoded.

FFHEx

FFHEx expands on the idea and exposes more of the elements composing the routine.
The object that will be affected by event is now configurable (in this case, the Blacksmith).
There's an option to prevent the fanfare from playing, if desired.
The conditional object is also configurable now (here, ADAMANT).
The comments tell what each element will do, and they are editable.

Notice the Type column; it shows the type names (not descriptions for them).
The blacksmith has a mix of hardcoded and parameterized values. Each line is represented
in the ".dialogue" file with an element.
In a ROM, the bank addresses in the elem tags are are used to reference the ROM data.
In assembly projects, assembly source refers to the elem tags directly.

Again, this is why the Annotated Disch disassembly is required for this to work.
Without the tags, there's currrently no other way the assembly source could be matched to the elements.

In the .dialogue file

The Blacksmith's Routine

The elements in the editor reflect what is in the ".dialogue" file. This is how an entry in the file looks as plain text:

[Talk_Smith]
desc=Dwareven Blacksmith
bankaddr=0x936C
elem0=hcobj|0x1|-1|sprite to check (already have XCalber)
elem1=text|0x8|3|final (already made XCalber)
elem2=hcitem|0xB|-1|item to check (do we have adamant)
elem3=text|0x10|1|Initial (no adamant yet)
elem4=hcweapon|0x18|-1|weapon to give to party (XCalber)
elem5=hcobj|0x1D|-1|map obj to set (Smith just made XCalber)
elem6=hcitem|0x22|-1|item to remove (take Adamant from party)
elem7=hcfanfare|0x24|-1|fanfare
elem8=text|0x27|2|condition met (Smith now makes XCalber)
elem9=hctext|0x2A|-1|no room for weapon ('don't be greedy')

Same routine with elements highlighted

[Talk_Smith]
desc = Dwareven Blacksmith
bankaddr = 0x936C
elem0 = hcobj      |  0x1   | -1  |  sprite to check (already have XCalber)
elem1 = text       |  0x8   |  3  |  final (already made XCalber)
elem2 = hcitem     |  0xB   | -1  |  item to check (do we have adamant)
elem3 = text       |  0x10  |  1  |  Initial (no adamant yet)
elem4 = hcweapon   |  0x18  | -1  |  weapon to give to party (XCalber)
elem5 = hcobj      |  0x1D  | -1  |  map obj to set (Smith just made XCalber)
elem6 = hcitem     |  0x22  | -1  |  item to remove (take Adamant from party)
elem7 = hcfanfare  |  0x24  | -1  |  fanfare
elem8 = text       |  0x27  |  2  |  condition met (Smith now makes XCalber)
elem9 = hctext     |  0x2A  | -1  |  no room for weapon ('don't be greedy')

Source with element annotations

The elements specified in the ".dialogue" file follow events in the ROM or assembly source.
The bank address refers to the in-bank location of the start of the routine.
For Assembly projects, the elem tags refer to those same tags that are present in the source.
Look at the source for the Talk_Smith routine below, and note how the elements follow the
flow of the routine:

Talk_Smith:
    LDY #OBJID_SMITH         ; check Smith's event flag to see if we already made      ;|:elem0
    JSR CheckGameEventFlag   ;  the Xcalbur for the party
    BCC @WantSword           ; if he already made it....
      LDA tmp+3              ; ... then simply print [3]                               ;|:elem1
      RTS
 
  @WantSword:
    LDA item_adamant         ; otherwise check to see if party has the Adamant         ;|:elem2
    BNE @HaveAdamant         ; if not...
    LDA tmp+1                ; ... simply print [1]                                    ;|:elem3
    RTS
 
  @HaveAdamant:              ; otherwise, make the sword!
    JSR FindEmptyWeaponSlot  ; find an empty slot
    BCS @WontFit             ; if no empty slot, sword won't fit
      LDA #WPNID_XCALBUR     ; put the XCalbur in the previously found slot            ;|:elem4
      STA ch_stats, X
      LDY #OBJID_SMITH       ; set Smith's event flag to mark that we made the sword   ;|:elem5
      JSR SetGameEventFlag
      DEC item_adamant       ; take the Adamant away from the party                    ;|:elem6
      INC dlgsfx             ; play fanfare                                            ;|:elem7 ? "INC dlgsfx" : "NILS 2"
      LDA tmp+2              ; and print [2]                                           ;|:elem8
      RTS
 
  @WontFit:                  ; if XCalbur won't fit in the inventory...
    LDA #DLGID_DONTBEGREEDY  ; print "Don't be Greedy" text (note:  hardcoded)         ;|:elem9
    RTS

Standard elements

Typical elements will replace either a sprite parameter or a hardcoded value with the value selected in the editor.

Conditional Elements

Conditional clauses (e.g. elem7) controls how the source is loaded and saved.
The conditional clause has this form:

;|:elem7 ? "INC dlgsfx" : "NILS 2"

The clause consists of three parts:

  • The element marker
    ;|:elem7

    This links the line to the corresponding entry in the dialogue file. It tells the dialogue editor to associate the entry in the editor with the element defined by key "elem7" in the file.

  • The first ("true") code snippet
    "INC dlgsfx"

    The code used in the loading and saving scenarios described below.

  • The second ("false") code snippet
    "NILS 2"

    The code used in the loading and saving scenarios described below.

Loading: the assembly source line's code (non-comment) portion is compared to the quoted strings.

  • If the code equals what's in the first (true) quoted string, then pass the value 1 to the editor;
  • If the code equals what's in the second (false) quoted string, then pass the value 0 to the editor;
  • otherwise, pass a value telling the editor to ignore this and do not edit it.

Saving: the editor's value is sent to the serializer.

  • If the value equals 1, write the source in the first (true) quoted string as the assembly's code portion;
  • If the value equals 0, write the source in the second (false) quoted string as the assembly's code portion;
  • otherwise, don't change the line at all, keep it intact.

In this example, if the selection for this line in the editor (elem7) evaluates to 1, then "INC dlgsfx" is written as the code on the matching line in the assembly source file (which plays the fanfare sound when opening a chest).
If the value evaluates to zero, then "NILS 2" is wtitten instead; this translates to two NOPs, meaning that the instruction to queue the fanfare is never written (and therefore the fanfare won't be played.
If the editor returns any other value, no action is taken and the assembly source code line is written in its original, unaltered form.


adding-sprite-routines]=[

Adding Sprite Routines

Suppose we want to add a routine that unconditionally gives money to the party on first contact, then urges them to revive the orbs thereafter. To do so we:

Assembly Project
  • Add assembly code to define the routine.
  • Update the dialog file to tell the editor about the changes to the ROM layout for the routine.

ROM Project
  • Add bytes via a hex editor to define the routine;
  • Update the dialog file to tell the editor about the changes to the ROM layout for the routine.

Adding the routine as assembly source

Once you've made space in the bank (E in the original game), you can add the routine.
Suppose we add this customized dialogue routine to the assembly source file that unconditionally gives money to the party on first contact, then urges them to revive the orbs thereafter:

;; An NPC will unconditionally award you some money.
;;  [1] The golditem index to award
;;  [2] displays the message that prints how much money you received
;;  [3] a message of encouragement after awarding the money
 
Talk_NPCAwardMoney:
  JSR CheckMyEventFlag         ; checks [0] (this sprite's event flag)
  BCS :+                       ; if not tripped
    JSR NPCAwardMoney          ; sets 0, awards gold in [1], and sets dlg_itemid to [1]
    DLGPARAM tmp+1             ; tell the editor that [1] = golditem ID to give            ;|:elem0
    LDA tmp+2                  ; print [2], which should display dlg_itemid using {02}     ;|:elem1
  RTS
  :
  LDA tmp+3                    ; already gave, print [3]                                   ;|:elem2
  RTS

The DLGPARAM macro

Assembly-Specific

This macro tells the assembly version of the dialogue editor that a sprite dialogue param should be associated to the dialogue element specified on the same line.

The sprite dialogue param is 1 (encoded as tmp+1) and the element is 0 (elem0).

The DLGPARAM macro doesn't generate code; it's a hack (indeed) that allows the assembly editor to associate the element on this line with the sprite dialog param indentified by the DLGPARAM parameter. In this case, tmp+1 specifies sprite param index 1, so the the editor will refer to that param when editing this element.

This is needed because the param is never referenced in this routine; it's referenced by another routine named NPCAwardMoney, but not in this routine. Remember that the assembly editor reads the line with the marker on it and extracts the param index from it. Without some way to tell the assembly editor that param index 1 is used here, there's no convenent way to edit the param (and change the amount of money awarded).
DLGPARAM allows that to happen without actually emitting in code (and therefore having no side effect on the game).

While it is a hack, it helps to keep all relevant information for the operation on the same line. Short of an assembly parser embedded in the app, it was the quickest thing I came up with.

Adding the routine as hex bytes

In a clean ROM, at hex offset 0x39596, replace these bytes:

ad 2e 60 f0 03 a5 12 60 ee 2e 60 a5 11 60

with these:

20 a7 b4 b0 06 20 3d bf a5 12 60 a5 13 60

The new routine (in hex) becomes:

20 a7 b4    -> JSR CheckMyEventFlag         ; checks [0] (this sprite's event flag)
b0 06       -> BCS :+                       ; if not tripped
20 3d bf    ->   JSR NPCAwardMoney          ; sets 0, awards gold in [1], and sets dlg_itemid to [1]
            ->                              ; DLGPARAM excluded since it's **NOT** an assembly instruction
a5 12       ->   LDA tmp+2                  ; print [2], which should display dlg_itemid using {02}     ;|:elem1
60          ->   RTS
            -> :                            ; labels (of all types) are also not assembly instructions
a5 13       -> LDA tmp+3                    ; already gave, print [3]                                   ;|:elem2
60          -> RTS

Updating the .dialogue file to add the routine entry

;;; An NPC will unconditionally award you some money.
;;;  [1] The golditem index to award
;;;  [2] displays the message that prints how much money you received 
;;;  [3] a message of encouragement after awarding the money
 
[Talk_NPCAwardMoney]
desc=Routine for NPCs who award money
bankaddr=0x9586
elem0=gold|1|golditem index to award
elem1=text|2|text when awarding the money
elem2=text|3|text if money was previously given

At this point, the routine should be available when the Dialogue Editor loads.