I got the Warp staff working in Fates:
GitHub Link
Some notes on the process below in case it helps anyone else.
Procs in FE14 (warning, essay below)
If you don’t know what Procs are go read Stan’s guide. Fates is a bit different from FE8 but not that much.
The main game loop is called nnMain and on startup it does a whole lot of initialising, including running a whole lot of routines with the name scheme: `global_constructor_keyed_to’_XX_Something_cpp (more on these later). Then every frame it will do things like check for Home button pressed, update key buffers, clean up Procs marked for deletion, execute Proc trees, and update graphics.
Unlike GBA, Fates has 3 Proc trees which are titled ‘Hi’, ‘Def’, and ‘Low’. I have not investigated what goes where within these trees. Stan’s guide refers to ProcStates which are 0x6c bytes in ram and contain a header plus a fixed amount of space for the Proc to store data. In Fates these are called ProcInsts and while they have a fixed header the size is variable and depends on the type of Proc. I unfortunately do not have much doc on the format of this struct, except that 0x0 and 0x8 seem to both contain Proc Commands? 0xC appears to be the name string pointer. Beyond that I am not sure.
The main purpose of the global_constructor routines which are run at game start is to write the Proc Codes into RAM. For some reason these are not stored as data in code.bin but rather as a series of instructions e.g. store ‘4’ at address XX, then store ‘0’ at address XX+1, etc etc. This might have been a problem but in fact you can ignore the RAM and just put your own Proc Codes into code.bin and repoint the references (more on that later). Proc Codes are 0x14 bytes long and their format depends on the opcode which is the first 4 bytes.
I also haven’t documented many of these but 0x4 is the ProcLabel opcode while 0x8 and 0xB seem to run routines (unsure what the difference is). 0xD sets the cycle routine.
For the purposes of Warp, the biggest problem was that by default, the game flow would be Select Unit->Show Action Menu->Select “Staff” Action->Select which staff to use->Select a target->Execute action and end turn. What I needed was to insert a step after selecting a target to allow the user to select a destination. I decided to base this second selection off the ballista routine because it is a freeselect with the map cursor, rather than scrolling through predefined target tiles.
I identified the relevant Proc was MapSequenceHuman and used the decompiled code in Ghidra to reconstruct the Proc Code. Below is an excerpt - the actual routine goes on for about 500 lines…
void 'global_constructor_keyed_to'_20_MapSequenceHuman_cpp(void)
{
DAT_0072b8c8 = 4;
_DAT_0072b8cc = 0;
_DAT_0072b8d4 = 0;
DAT_0072b8dc = 0xb;
_DAT_0072b8e8 = map__SequenceHuman__anonymous_namespace__ProcSequence__FreeCursorPrepare;
_DAT_0072b8ec = 0;
DAT_0072b8f0 = 0xd;
_DAT_0072b8fc = map__SequenceHuman__anonymous_namespace__ProcSequence__FreeCursorTick;
_DAT_0072b900 = 0;
DAT_0072b904 = 4;
_DAT_0072b908 = 1;
_DAT_0072b910 = 0;
DAT_0072b918 = 0xb;
_DAT_0072b924 = map__SequenceHuman__anonymous_namespace__ProcSequence__PickCursorPrepare;
From this I was able to do a bit of find and replace shenanigans to turn it into a usable .csv file and reconstruct it:
That was turned into a bit of C so that I could expand it and add my custom routines based off the ballista routines:
struct ProcCmd
{
int opcode;
int unk1;
int unk2;
int functionPtr;
int unk4;
};
extern void WarpCursorPrepare();
extern void WarpCursorTick();
#define PROC_LABEL(num) {0x4, num, 0, 0, 0}
#define PROC_END {0, 0, 0, 0, 0}
const struct ProcCmd gProcMapSequenceHuman[] = {
PROC_LABEL(0x0),
{0x0b, 0x0, 0x0, 0x034c9cc, 0x0},
{0x0d, 0x0, 0x0, 0x0351904, 0x0},
PROC_LABEL(0x01),
{0x0b, 0x0, 0x0, 0x03537dc, 0x0},
PROC_LABEL(0x02),
{0x0b, 0x0, 0x0, 0x0353bd4, 0x0},
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
PROC_LABEL(0x027),
{0x0b, 0x0, 0x0, 0x01ed9c4, 0x0},
{0x0b, 0x0, 0x0, 0x0353b0c, 0x0},
PROC_LABEL(0x2A), //use this one for warp staff!
{0x0b, 0x0, 0x0, (int) WarpCursorPrepare, 0x0},
{0x0d, 0x0, 0x0, (int) WarpCursorTick, 0x0},
PROC_LABEL(0x029),
PROC_END
};
As you can see I inserted a new label into the Proc Code. The default RAM address of the vanilla Proc Code was 0x72B8C8 which was referred to in the routine map__SequenceHuman__Create (which creates a ProcInst of SequenceHuman and sets the Proc Code pointer to this location). I repointed this to my custom Proc Code, but next I needed a way to reach the new label.
The relevant routine here was map__anonymous_namespace__ProcSequence__Decide, which contains a switch statement for each action performed at the moment the target is decided (i.e. right before I wanted to show the warp destination select). The case for Rescue Staff was 0x25 which says among other things to jump to Label 0x26 in the SequenceHuman Proc Code. I simply replaced 0x26 with 0x2A and boom now we are in business.
Based on trial and error I could see that if I simply jumped to label 0x6 (CannonCursorResumePrepare, followed by CannonCursorTick) it would draw the ballista freeselect and try to attack with a Fire Orb once a valid target was selected (i.e. enemy within damage radius). Therefore I created the WarpCursorPrepare routine based heavily on CannonCursorResumePrepare, and the WarpCursorTick routine based on CannonCursorTick.
To cut a very long story short, this was successful but meant that every staff now would draw the Warp cursor instead of behaving normally.
So I added a check to see if the item being used was the Warp Staff and if no, jump back to label 0x26 to behave like a normal staff. I had to check if the unit has an equipped item - if no, the staff is top of their inventory. If yes, the staff is 2nd. Incidentally, the current unit is checked by using map__Mind__Get followed by map__Mind__GetUnit to get the unit struct in RAM. The inventory begins at 0x108 of the unit struct and is 4 bytes per item. First halfword is item id, second is durability.
Once the destination tile was selected, I jumped back to label 0x26 to proceed as usual for a Rescue staff, right up until the moment it tries to decide on the tile to warp the unit to (function map__ItemHelper__Rod__GetRescuePosition). This routine is actually used for a lot of things to try and get the nearest valid tile to warp someone onto, including things like Pheonix mode resurrection, so I did not replace the whole routine. Instead I replaced the call within the parent routine map__SequenceBattle__anonymous_namespace__ProcSequence__Rescue to my own custom routine.
Here I reused my previous check to whether the item being used was a Warp staff - if no it would continue to the vanilla routine as usual for Rescue. If yes, it would instead warp the target unit to the cursor position, which of course is where you last had the cursor at the moment you confirmed the warp decision.
Also I updated the validity condition from “enemy within damage radius” to “no unit on tile and target is able to move on terrain type” - this part was also heavily referenced from vanilla code.
Now, the above makes me sound like I know what I’m doing. I want to be clear - I really, really do not have any idea what I’m doing at any given time.
Next time: how to debug when you don’t know what you’re doing.