[FE7] The Official AI Documentation Thread

I followed this to a pointer table, and went to the 0x12th pointer. I followed that pointer to 0x8B97A14, which has 8 bytes of something and then a pointer to 0x8B97A0C, which has the byte 0x10 (Raven) there. I did the same for the 0x13th pointer which ends in the byte 0x04 (Lucius) there, and the same again for the 0x07th pointer which ends up pointing to a byte that has 0x14 (Nino). I suspect that expanding & repointing the table and copying and pasting the relevant bits in the AI might allow us to create our own custom versions of these or, in the very least, have more of them. :smiley:

Update: 0x8B98908 is the corresponding table for the Movement AI bytes :D. Following this, if you head to the 0x0Bth (Farina talks to Hector) pointer it brings you to 0x8B97FDC. If you go along a little further thereā€™s a 0x02 at 0x8B97FE0. Oh, where have we seen this offset before? Rightā€¦

3 Likes

Found where the locations are stored for the second AI bytes that make units move to specific locations.
So 0x13 as the second byte of AI should make units move towards 3,13 on the map.
Using the table at 0x8B98908 that @Agro posted for the second AI Bytes, the 13th pointer points to B98724, these are the first 16 bytes at B98724.
01 00 FF 00 00 00 00 00 49 A5 03 08 (1C 87 B9 08)

In the data above in brackets is a pointer leading to B9871C and at that location, if you look at the four bytes prior so at B98718, you see 03 0D 00 00. 03 0D translated from hex = 3,13; which is the location that units with 0x13 as their second AI byte move to. Iā€™ve tested it and changed the 03 0D and the enemy then moves to the new location specified.

So to change the location units with 0x13 as their second AI byte move to, just edit the bytes at B98718.
And so on for the rest, so for 0x14(move to 18,13 on map), the 14th pointer in the table points to B987A0, and the data at that location contains a pointer to B98798 and at that location, four bytes prior so B98794, is 12 0D 00 00 which is the location 18,13. Hopefully that all makes sense. Iā€™m going through now and noting the offsets of the locations so Iā€™ll post those in a bit.

EDIT
actually it seems the locations arenā€™t all done in the same way. so I havenā€™t found them all yet. so far though
0x13 - B98718 (3,13)
0x14 - B98794 (18,13)
0x15 - B98810 (10,24)
0x16 - B9888C (8,2)

1 Like

Where/how did you find this?

1 Like

probably just by following pointers and changing random bytes until stuff happened

So theres these ones and I think Iā€™ve got how the others ones work
0x13 - B98718 (3,13) Change 03 0D
0x14 - B98794 (18,13) Change 12 0D
0x15 - B98810 (10,24) Change 0A 18
0x16 - B9888C (8,2) Change 08 02
0x1D - B9869C (15,17) Change 0F 11

For the others. The bytes to edit are just at the offset the entrys in pointer table point to I think. I havenā€™t tested this yet though, but i think it should be right

Just follow the pointer and edit the bytes at the location
0x17 - B97578 (6,2)
0C 06 FF 02 - Change 06 02
0x19 - B974B8 (6,9)
0C 06 FF 09 - Change 06 09
0x1A - B974F8 (6,5)
0C 06 FF 05 - Change 06 05
0x1E - B97538 (5,2)
0C 05 FF 02 - Change 05 02

1 Like

Oh ā€“ I see. There are 0x13 Aggresion AI pointers. then after that a pointer to 08B98908 is written 3 timesā€¦ then 3 pointers to 08B98994 O.oā€¦ what?

ā€¦ Iā€™m gonna take a closer look at the asm that uses thisā€¦

Okay, I want to clear up what Iā€™ve been looking at(mostly because this is still kinda complicated even with annotations) so here goes.

  1. We load 0x8B989F0, which is a pointer toā€¦
  2. We dereference it, getting 0x08B98994
  3. We find the 1stAI-th entry in that pointer table and dereference if.
  4. We take the 1stAIData byte times 0x10 and add that to what we just got.
  5. (some processing, but usually) The first byte there is taken (e.g. for AI = 0x00, 0x00, we get 05)
  6. At the table at 0x81D3678, we use that index and jump to it. (More complicated stuff if the number is >=1B)

Okay, so the tables we get (that are 0x10 bytes long) by following the 1st and 2nd AI bytes look something like this (Annotating as I go)

05 64 FF 00 00 00 00 00 00 00 00 00 00 00 00 00
AI = 0x00, 0x00

The 0th byte is the jump table index to use.
The 1st byte is something chance based, I think ā€“ [some routine generates a random multiple of 0x64 and subtracts one.][1] If the chance is unfulfilled then it ā€¦ branches and does something.
The 2nd byte is written to RAM(at 0x0203A96A), so it might be important?
The 3rd byte is read by 08037890
[1]:https://dl.dropboxusercontent.com/u/92273434/FE%20Hacking%2C%20Public%20Files/AI%20Analysis/08000E30

1 Like

I just went to this address and saw that there was a pointer to 0x8B989F0. I assumed that the pointer to the movement AI table was somewhere nearby and just searched for other pointers (there arenā€™t many in that area) and followed the pointers through

0x30013B4 - Whether to use 1st AI Byte or 3rd AI Byte(at least, in these calculations). 0x0 = 1st. Any other value = 3rd.
Read and branched upon at 08037894

0x30013B0 - Branched upon at 080378CCā€¦

Aggression AI 0x08 = Do not attack character 0x0A (citizen)

I did some more testing and found there is a difference between the two groups.

After reaching destination, if the unit is lured away from point and later has no enemies in range:
0x13, 0x14, 0x15, 0x16, 0x1D = will move back to target coordinate
0x17, 0x19, 0x1A, 0x1E = will not move back

Yeah, there does seem to be a random factor. An enemy with 0x02 will frequently not move or attack on the enemy phase.

0x00 = 05 64 FF
0x01 = 05 50 FF
0x02 = 05 32 FF
0x03 = 07 64 FF
0x04 = 07 50 FF
0x05 = 07 32 FF

So I guess 0x00 - 0x05 can be defined as:

0x00 = Attack units 100%
0x01 = Attack units 80%, do nothing 20%
0x02 = Attack units 50%, do nothing 50%
0x03 = Attack without moving 100%
0x04 = Attack without moving 80%, do nothing 20%
0x05 = Attack without moving 50%, do nothing 50%

1 Like

Update: Aggression AI 0x12 = Do not attack characters 0x10 (Lucius) and 0x1B (Priscilla)

You can have multiple ā€œdo not attackā€ characters by pointing to a character list like:
1A 00 1B 00 1C 00 00 00 (end list with 00 00)


Aggression AI 0x0A - Only attack character 0x03 (tutorial Lyn)

04 64 FF 00 03 00 00 00 00 00 00 00 00 00 00 00

Change 03 to target character of choice. It seems this AI only allows for 1 character.


Units with ā€œdo not attack certain characterā€ AI or ā€œonly attack certain characterā€ AI and have 0x00 for second AI byte can still move towards prohibited characters. Units with 0x03 for the second byte will not be triggered by prohibited characters.

2 Likes

Offset please.

Entry 0x0A in the aggression AI table = 0B97ABC. Target character at 0B97AC0


Iā€™m confused by the table[AI1][AI2] thing. If movement AI has its own table, why is AI2 being used as an index in the aggression table?

In the RAM, an enemy with AI [AA,BB,CC,DD] has AI stored like this:
CC DD AA 00 BB 00

The 00 after AI1 and the 00 after AI2 can change values.
For example, an enemy with AI 06,1E,09,00 (move to 05,02)

Turn 1

09 00 06 00 1E 00

Turn 2 (moved closer to 05,02)

09 00 06 01 1E 00

Turn 3 (reached 05,02)

09 00 06 01 1E 01

Turn 4

09 00 06 01 1E 03

Is it possible these values after AI1 and AI2 are being used as the secondary index for the AI tables? Possibly as a way to indicate a state change since several AI change behavior after certain conditions?

Okay, then I was probably confused by the Unit struct. Iā€™ll check bak over my decomentation and clear this up.

What you have is absolutely correct. I justā€¦ read off the battle data struct and assumed it was the same, eheh.

ā€“ Iā€™m calling the byte after AI1 1stAIData and the byte after AI2 2ndAIData

@CT075
Hereā€™s some stuff you can do to help!

08034EF2 4812     ldr     r0,=#0x203A97C		@Some data...
Find out the structure of the data stored here and(possibly) when it's stored. 

Help us decipher this structure
04 64 FF 00 03 00 00 00 00 00 00 00 00 00 00 00
pointed to by the table at 0x08B98994

08035010 4904     ldr     r1,=#0x203A8EC		
08035012 317B     add     r1,#0x7B				@Some kind of flag for the enemy team...? 0x01 for me.
	@-Modified at 0803495A(set to 0x4 as I end the turn)
	@-Modified at 080349B2(Set to 0x0)(In fact, that's all that subroutine does)
		@-Called by 08004902, r0 = 0203A967 = this offset

Pick as many or as few as you want.

Note: I think the flag on the last one is a flag storage for the currently processing AI and is clear in between each enemyā€™s AIā€¦ just a guess.
Note2: Seems that the 0x1 and 0x2 bits are preserved in-between enemy calls ā€“ probably clears each turn. Done at 0803C0F0.
Note3: 0x4 bit checked at 08034F4C, 0x8035018,ā€¦ maybe itā€™s pretty importantā€¦ (ā€œhas moved alreadyā€? Just a wild guess, take with grain of salt)

Edit: I renamed a lot of files for more easy browsing, so a lot of things probably had links broken. So I zipped up all my ducmentation(as of today) and stuck it here for you all
https://dl.dropboxusercontent.com/u/92273434/FE%20Hacking%2C%20Public%20Files/AI%20Analysis/AI%20Analysis%208-15-14%203.38%20AM.zip

If you have an update/clarification on some code, let me know which file/line and send me your copy.

Note4: Iā€™m now calling this byte the ā€œEnemy Flagsā€, which i might have to rename, since itā€™s used as indexing for the jump table at
08034EDE 4A15 ldr r2,=#0x8B96F14 @ā€œTable Aā€. Some kind of jump table?

Note5: Okay, what I think this does, is it starts at the enemy flagā€™s offset in the table, and keeps executing the jumps in order until it hits a 0 entry or
08034F20 4806 ldr r0,=#0x203A97C @Same offsetā€¦ ā€œsome dataā€
08034F22 7A80 ldrb r0,[r0,#0xA]
Becomes nonzero.

Looking just at the Enemy Phase code, hoping to see where it loops through each unit. Interestingly, I found another jump table

0x8B858A4; loaded at 08004B9E

WAHAHAHHAHAHAH
AHAHAHAHAHAHAH
MUAHAHAHAHAHAH

ā€¦ why the evil laughter, you ask?
Check out 0203A8EC. At the start of each enemy turn, this is initialized to a list of enemy units to calculated the AI for and move (0x00 terminated). The rub isā€¦ they donā€™t have to be enemy units. Yep, to be sure, I set the values to 01 02 03 04 05 through memory hacking shortly after the start of the enemy phaseā€¦ and the AI moved my player units.
Oh. My. God.

And to make multiple moves per unit per turn for the AI possible, we probably, just have to remove the check at 08034D5E. Testing that out now.

Edit: Yeah, it worked. Turn 08034D62 into 00 25 anbda unit on the queue will move regardless of being grayed out (may cause bugs if you rescue people, but let me play around here, will ya?). So now Eliwood is moving 10 times in a turn killing enemies on the enemy phase. Win. Btw, thereā€™s only room, for, like, 0x64(I think) units for the AI to process. So donā€™t go too overboard with giving eliwood 999 turns in one turn.

Edit2: There actually is merit to this though. It shows that the AI is built to recognize which units are on the unit being processedā€™s side and which arenā€™t.

Edit3:
[7:38:57 PM] Crazy Colorz: codebreaker code for enemies do nothing on their turn for FE7, made by yours truly
[7:38:59 PM] Crazy Colorz: 8203A8EC 0000
[7:39:12 PM] Crazy Colorz: What this does is it messes with the list of units that FE7 looks at to process.

1 Like

08004634 seems to be a recursive algorithm. Is this responsible for FE7ā€™s search algorithm?
Edit: Probably not. (Though it might be)

This is reeeeeeeeeeally complicated and Iā€™m ready to give up having a complete understanding on FE7ā€™s AIā€¦ maybe just analyze the existing AI. The AI1Data and such seem to be the key for innate AI changes, like ā€œHead to _ then do _ā€.