#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.