[EA] [WIP] A Guide To Inserting Everything Through EA

#Chapter 2 - Basically Nightmare+

setCharacterName(Arch, Arch_Name)
setCharacterDescription(Arch, Arch_Description)
setCharacterGrowths(Arch, 80, 45, 35, 30, 25, 40, 20)
setCharacterBases(Arch, 2, 0, 0, 0, 3, 2, negateByte(2))
setCharacterWeaponBases(Arch, DRank, NoRank, NoRank, NoRank, NoRank, NoRank, NoRank, NoRank)
setCharacterSkills(Arch, Lord)

Uh… w-what?


###Section 2.1 - Turning a Nightmare Module into Macros

All nightmare does is calclate a certain offset based on table entry size and offset within the table, then write to that location. You can replicate these as long as you know the size of the data. The .nmm file handily gives you all of the information you need, so you can define macros to make editing data in the ROM very readable and easy. Let’s look at some examples with the FE8 Character Editor.nmm.

#FE8 Character Editor by SpyroDi
#

1
FE8 Character Editor by SpyroDi
0x803D30
256
52
FE8 Character Editor.txt
NULL

Name value
0
2
NEHU
NULL

Description value
2
2
NEHU
NULL

Character Number
4
1
NEHU
NULL

Class (support viewer only)
5
1
NDHU
Class List.txt

Portrait (Zero is Default)
6
1
NDHU
Portrait List.txt

***UNKNOWN*** (Zero)
7
1
NEHU
NULL

Mini Portrait (Zero is Default)
8
1
NEHU
NULL

Affinity
9
1
NDDU
Character Affinity.txt

***UNKNOWN*** (Zero)
10
1
NEHU
NULL

Level
11
1
NEDU
NULL

Base HP
12
1
NEDS
NULL

Base Strength/Magic
13
1
NEDS
NULL

Base Skill
14
1
NEDS
NULL

Base Speed
15
1
NEDS
NULL

Base Luck
18
1
NEDS
NULL

Base Defense
16
1
NEDS
NULL

Base Magic Defense
17
1
NEDS
NULL

Constitution Bonus
19
1
NEDS
NULL

Sword Level
20
1
NDDU
Weapon Rank.txt

Spear Level
21
1
NDDU
Weapon Rank.txt

Axe Level
22
1
NDDU
Weapon Rank.txt

Bow Level
23
1
NDDU
Weapon Rank.txt

Staff Level
24
1
NDDU
Weapon Rank.txt

Anima Level
25
1
NDDU
Weapon Rank.txt

Light Level
26
1
NDDU
Weapon Rank.txt

Dark Level
27
1
NDDU
Weapon Rank.txt

HP Growth
28
1
NEDU
NULL

Strength/Magic Growth
29
1
NEDU
NULL

Skill Growth
30
1
NEDU
NULL

Speed Growth
31
1
NEDU
NULL

Luck Growth
34
1
NEDU
NULL

Defense Growth
32
1
NEDU
NULL

Magic Defense Growth
33
1
NEDU
NULL

***UNKNOWN*** (Zero)
35
1
NEHU
NULL

***UNKNOWN*** (Zero)
36
1
NEHU
NULL

***UNKNOWN*** (Zero)
37
1
NEHU
NULL

***UNKNOWN*** (Zero)
38
1
NEHU
NULL

***UNKNOWN*** (Zero)
39
1
NEHU
NULL

Character Ability 1
40
1
NDHU
Ability 1.txt

Character Ability 2
41
1
NDHU
Ability 2.txt

Character Ability 3
42
1
NDHU
Ability 3.txt

Character Ability 4
43
1
NDHU
Ability 4.txt

Supports Data Pointer
44
4
NDHU
Support Pointers.txt

***UNKNOWN***
48
1
NEHU
NULL

***UNKNOWN*** (Zero)
49
1
NEHU
NULL

***UNKNOWN*** (Zero)
50
1
NEHU
NULL

***UNKNOWN*** (Zero)
51
1
NEHU
NULL

Example 1 - Setting a Single Data Field

For now, let’s look at the description value. The nightmare file tells uf the following:

  • The offset of the character table is 0x803D30.
  • Each character is 52 (=0x34) bytes long.
  • The description field has an offset of 2.
  • The description field is 2 bytes long (i.e. it’s a short).

From this data we can use the following formula to make a macro: `#define informativeName(entry, param) “PUSH; ORG tableStart + entry*entrySize + offsetInTable; BYTE/SHORT/LONG/POIN param; POP”

Note that it’s best to PUSH and POP in your definition like that, so that using a setter doesn’t change the current offset that EA is writing to.

It’s also a good idea to #define your table’s offset so if you repoint it, you only need to change it once.
So we can make a macro to set a character’s description’s textID like so:

#ifndef CharacterTable
    #define CharacterTable 0x803D30
#endif
#define setCharacterDescription(char, descID) "PUSH; ORG CharacterTable + char*52 + 2; SHORT descID; POP"

Example 2 - Setting Multiple Data Fields

We don’t need to set only one field per macro, though it gets unwieldy if you set many non-contiguous fields. However, one good example on when it’s easy to have one macro to set many fields is stats (bases and growths)

Note that in the nightmare module, we have

  • At offset 12: base HP
  • At offset 13: strength
  • At offset 14: skill
  • At offset 15: speed
  • At offset 18: luck
  • At offset 16: defense
  • At offset 17: resistance (magic defense)

Or in order in the ROM; hp, str, skl, spd, def, res, luk.

Since they’re continuous and all one byte, it’s very easy to make a macro for them. Assuming we still have the CharacterTable definition from before,

#define setCharacterBases(char, hp, str, skl, spd, luk, def, res) "PUSH; ORG CharacterTable + char*52 + 12; BYTE hp str skl spd def res; POP"

Note that the macro defines the stats in display order, and we shuffle around the parameters into ROM order in the macro. This is useful functionality when editing the stats of a character.

Example 3 - A Pointer to Data

For this example, I’ll use the effectiveness pointers in the Item Editor. For reference, here’s the relevant parts of the nightmare module:

1
FE8 Item Editor by SpyroDi
0x809B34
205
36
FE8 Item Editor.txt
NULL

. . .

Effectiveness Pointer
16
4
NEHU
NULL

Since the effectiveness pointer is, well, a pointer, we need to use the POIN command to make an entry. Note also we should be using ROM offsets and not hardware offsets (that is, it should NOT be in the 0x08000000 range). This is important in a moment.

#ifndef ItemTable
    #define ItemTable 0x809B34
#endif
#define setEffectiveness(item, ptr) "PUSH; ORG ItemTable + item*36 + 16; POIN ptr; POP"

It may also be handy to have something like such, for the built in effectivenesses (these are for FE8):

#define FlyingEffectiveness 0x8ADF2A
#define HorseEffectiveness 0x8ADEE0
#define ArmorEffectiveness 0x8ADEBB
#define DragonEffectiveness 0x8ADF13
#define HorseAndArmorEffectiveness 0x8ADEC2
#define SwordfighterEffectiveness 0x8ADF57

But it gets MORE interesting. Let’s say we have some free space (if, by convention, we can assume the file starts in freespace). Then we can actually, in EA, write our own list, and automatically point to that. Check it out!

#define LordSlayer 0x01 //Just used as an example
setEffectiveness(LordSlayer, LordEffectiveness)

ALIGN 4
LordEffectiveness:
BYTE EirikaLord EphraimLord EirikaMasterLord EphraimMasterLord 0x00

Note that this does require knowing the format of what the data that the pointer takes to takes. In the case of effectiveness lists, it’s a list of class IDs terminated by 00.

By declaring our pointer and data this way, we can move around the list, and have EA take care of the addressing. We can also easily expand or change the list. We will see this pattern and flexibility a lot more in the section about general ROM data, like text and portraits.


###Section 2.2 - Editing Properties with EA

NMM2CSV and CSV2EA are other great ways to edit Nightmare properties, and they output event files. These methods of editing can be combined, though I suggest that you #include the Table Installer from c2e before you do your own table editing through events.

In any case, the idea is, is that once we have all the macros we need, we can just serially (and readably) tell the changes we need to make to each table entry.

Here’s an example:

makeThrowableWeapon(HandAxe)
setWeaponStats(HandAxe, 7, 65, 0, 0xFF)
setWeaponType(HandAxe, Axes)
setWeaponRank(HandAxe, DRank)
setNoStatBoosts(HandAxe)
makeNotEffectiveWeapon(HandAxe)

And you’re able to have a small block like this for every item you need to change. To further make a change, you just need to search/find the item to change and add a macro or change an existing one.