[FE7] The Official AI Documentation Thread

Quick update on AI Jump tables.

I’m just analyzing the AI1 = 0x0 default AI first… Its AIData in rom is
05 64 FF 00 00 00 00 00 00 00 00 00 00 00 00 00
03 00 FF 00 00 00 00 00 00 00 00 00 00 00 00 00
So I’m looking at Jump Table AI 05 and 03. 03 seems to be the “do nothing” AI.
05 has a ton of branch calls and is going to be a pain to analyze.

Breakthrough on staff AI. For the jump table 0x5, this is what it calls to check for using staves, but I think it’s fair to assume the other movement AIs use this
0803AAD8:

0803AB0C D01E     beq     #0x803AB4C		@if not staff, branch.
0803AB0E 1C20     mov     r0,r4
0803AB10 F7DCFC52 bl      #0x80173B8
0803AB14 42B0     cmp     r0,r6
0803AB16 DB19     blt     #0x803AB4C
0803AB18 1C20     mov     r0,r4
0803AB1A F7FFFFA9 bl      #0x803AA70
0803AB1E 1C01     mov     r1,r0
0803AB20 2001     mov     r0,#0x1
0803AB22 4240     neg     r0,r0
0803AB24 4281     cmp     r1,r0
0803AB26 D011     beq     #0x803AB4C
0803AB28 00C8     lsl     r0,r1,#0x3
0803AB2A 4440     add     r0,r8
0803AB2C 6802     ldr     r2,[r0]
0803AB2E 1C28     mov     r0,r5
0803AB30 1C39     mov     r1,r7
0803AB32 F085F88F bl      #0x80BFC54

I’m going to investigate those bl calls and get back to you on them.

I did some testing with the third and fourth AI bytes.

AI Byte 3 - Healing

When a unit’s HP falls below a certain percentage it enters a state I’ll call “recovery mode”
Being in recovery mode has these effects:

  • If a vulnerary or elixir is in inventory, retreat from enemies and use item
  • Unit is healed by allied staff-users (Fortify requires at least 3 units in recovery mode?)
  • Retreat towards an allied healer (only if unit can stay out of enemy range)
  • Retreat to nearest fort (only if unit can stay out of enemy range)

Recovery Mode thresholds:
0x00 = Enter recovery mode when HP is less than 50%. Exit when HP is 100%
0x01 = Enter recovery mode when HP is less than 30%. Exit when HP is 80% or greater
0x02 = Enter recovery mode when HP is less than 10%. Exit when HP is 50% or greater
0x03 = Enter recovery mode when HP is less than 80%. Exit when HP is 100%
0x04 = Unable to enter recovery mode

AI Byte 4

0x20 = Unit in recovery mode will not retreat before using a vulnerary/elixir or retreat to a healer/fort. Also seems to apply the “attack without moving” effect.

2 Likes

Disregard the AI3 0x20 = “retreat to fort” thing. Enemies can apparently still retreat to forts without it.

Completely baseless hypothesis that I;m taking note of here so I can investigate it later:
=#0x30013B4 - Which AI Byte we’re investigating.

=#0x8B989E4 is in this code when dealing with AI2. Dereferencing it reveals 08 89 B9 08.

So in other words, I confirm this.

1 Like

The third byte is read by jump table 0x3. I think it’s for innate AI changes/cycles. If it’s nonzero and jump table 03 gets called, then it searches for the entry after (a 0x1B entry that has a matching 0x3 entry).

Unknown stuff for the third AI byte:

0x08 = The 0x08 bit is on for… the vast majority of enemies in the game. Most enemies have 0x09 or 0x0A for their third byte, then 0x01 is the next most common.

0x10 = Used by the lv 6 reinforcements that come out of the southwest fort in chapter 13 and NPC Jaffar.

0x20 = Used by ballista archer in chapter 8. Various archers in chapter 11E, 12, 13. Some pegasus knights in chapter 12. A single soldier in chapter 12.

I’m fairly confident AI3 is used as a flag since recovery mode thresholds will carry over to bytes like 0x0A and 0x29. The unknown bits are possibly used for something other than healing? I really can’t think of anything else related to HP recovery.

Adding to this, there’s a Sage in Final with a Physic that has 0x0A as its AI. Have you located a similar pointer table to the others for AI byte 3? At least if we have that we can take a peak into each AI setting and see if there are any hints there.

I’ll take a look at it right now. Literally. Gimme like, 5 minutes. I don’t think I’ll find a table, but there’ll at least be AI routines for other people to look at if they’re interested.

Edit: It’s read at 080A167C. Then it’s stored into 02020BD0+0x32. So we might have to break from there… It’s a bit more complex…

Hypothesis: AI3 0x20 might be “do not consider target’s ability to counter” since this AI is assigned to several archers.

I created 2 identical knights and gave one an iron lance and a javelin to the other. Then I created an enemy archer.

0 dmg at 100 hit vs iron lance knight with 30 HP
0 dmg at 100 hit vs javelin knight with 30 HP
0x00 = targets iron lance
0x08 = targets iron lance
0x10 = targets iron lance
0x20 = targets iron lance

But what if I lower the javelin knight’s defense? At 9 damage the behavior was the same, but then…

0 dmg at 100 hit vs iron lance knight with 30 HP
10 dmg at 100 hit vs javelin knight with 30 HP
0x00 = targets iron lance
0x08 = targets javelin!
0x10 = targets iron lance
0x20 = targets javelin!

Keep lowering defense until…
0 dmg at 100 hit vs iron lance knight with 30 HP
13 dmg at 100 hit vs javelin knight with 30 HP
0x00 = targets javelin!
0x08 = targets javelin!
0x10 = targets iron lance
0x20 = targets javelin!

Lower! Lower!
0 dmg at 100 hit vs iron lance knight with 30 HP
22 dmg at 100 hit vs javelin knight with 30 HP
0x00 = targets javelin!
0x08 = targets javelin!
0x10 = targets javelin!
0x20 = targets javelin!

So it looks like the 0x08, 0x10, and 0x20 bits influence the enemy’s targeting decision.

1 Like

That may well be the case; several enemy archers in Chapter 13 Hector Mode use 0x29 for their third AI byte, and the rest of the enemy units have 0x09.

We should test what happens as we combine these bits.

0x09, 0x0A: switch from iron lance to javelin at 10 damage (same results as 0x08)
0x18, 0x30, 0x38: switch at 12 damage
0x28, 0x29: switch at 5 damage (the lowest result seen so far)

It’s possible there could be more factors like hit rate.

Hax in stuff so everything has 100 hit and 0 crit, then. I bet the FE AI just does expected value sthough

I’m pretty sure now that the first three bits of AI byte 3 control healing behavior; at $080397AE, the game loads bytes 3 and 4 together as a halfword, and isolates the first three bits (AND 0x7).

Then this result is multiplied by 4 and added to $08B9727C; it then loads a byte from that+0x1. The values of these bytes correspond to the %HP below which a unit will seek healing; the byte immediately before corresponds to the %HP that the unit will stop healing at.

So probably at least the next three bits - 0x8,0x10,0x20 - are to do with how strongly the AI seeks to avoid counters.

3 Likes

New test with a 30 HP evasive/low defense unit and a 30 HP slow/tanky unit. Both equipped with an iron lance so neither can counter the archer. 0 crit in all cases.

0 dmg 100 hit vs 29 dmg 0 hit
0x00, 0x08, 0x10, 0x20, 0x28 = all target the evasive unit (29 dmg 0 hit)

Increase the damage done to the tank until the enemy switches targets
0x00 = 10 dmg 100 hit > 29 dmg 0 hit
0x08 = 20 dmg 100 hit
0x10 = 20 dmg 100 hit
0x20 =  1 dmg 100 hit
0x28 = 10 dmg 100 hit

With 75 hit…
0x00 = 12 dmg 75 hit > 29 dmg 0 hit
0x08 = 22 dmg 75 hit
0x10 = 22 dmg 75 hit
0x20 =  2 dmg 75 hit
0x28 = 12 dmg 75 hit

With 50 hit…
0x00 = 16 dmg 50 hit > 29 dmg 0 hit
0x08 = 24 dmg 50 hit
0x10 = 24 dmg 50 hit
0x20 =  2 dmg 50 hit
0x28 = 16 dmg 50 hit

With 25 hit…
0x00 = 20 dmg 25 hit > 29 dmg 0 hit
0x08 = 27 dmg 25 hit
0x10 = 27 dmg 25 hit
0x20 =  4 dmg 25 hit
0x28 = 20 dmg 25 hit

As hit decreased, the amount of damage needed to cause a switch increased. It seems the AI was fairly reluctant to move away from the highest damage option except with 0x20.

29 dmg 100 hit vs 30 dmg 0 hit
All the above went for the 30 damage lethal option.

If I had to make a guess about what’s going on…

  1. Attacks that can deal lethal damage (even if hit chance is zero) get the highest priority.
  2. If no lethal attacks, identify the attack option with highest damage (regardless of hit chance)
  3. A lower damage attack can override the highest damage attack if it meets some requirement. It might be based on the attack’s expected value (damage x hit)
  4. The ability of the target to counterattack or not counterattack plays a role somehow. Maybe it modifies the override requirement?

I’m too tired at the moment to investigate these thoroughly, but here are some addresses of code about interpreting AI byte 3:

$08034D96:
-Loads AI bytes 3,4 to r1; sets r0 to 0xF8
-Clears bits 1-3 from r1, then shifts bits down (AND r0, lsr 0x3)
    -Stores result to $0203A969 (bits 4-8 of AI byte 3)

$0803924C - loads byte from $0202A969 (bits 4-8 of AI byte 3!)
    -Multiplies by 20; adds to $081D36F4; stores this pointer to $030013C0

$08039010, $08039070, $08039094, $08039138 - loads pointer from $030013C0
$08039240 - routine that calls the above routines (and more)

1 Like

*bits :wink:

Just giving this a little revival, I’ll be looking at some of that Jump Table 0x5 code this week and trying to figure out where it decides if it should use an item. (Enemies that know how to use promotion items, anyone ;)?)

edit: Also, we were tied in likes with the “Under” thread at one point – what happened D:?

This routine seems to do the bulk of the work for the AI.
On a side note, the jump table 03 just seems to be the “finalize these commands and execute”, and if executed before any commands are done, then it does nothing.
Anyway, this is the branch call called by AI table 5; I think it might be called by other routines with outher r0 parameters passed in, which seem to lead to a routine.

Anyway:

08038558 B5F0     push    {r4-r7,r14}		@Uses 10 local variables.
0803855A 4657     mov     r7,r10
0803855C 464E     mov     r6,r9
0803855E 4645     mov     r5,r8
08038560 B4E0     push    {r5-r7}
08038562 B08A     add     sp,-#0x28			@Allocate 0xA words of space
08038564 9009     str     r0,[sp,#0x24]		@The was the location of a routine that was passed in.
08038566 A806     add     r0,sp,#0x18		@r0 is 4 deep into the stack
08038568 2500     mov     r5,#0x0
0803856A 7085     strb    r5,[r0,#0x2]
0803856C 6085     str     r5,[r0,#0x8]		@clear -0x8 to -0x4
0803856E 4E14     ldr     r6,=#0x3004690	@Unit being processed
08038570 6833     ldr     r3,[r6]
08038572 68D9     ldr     r1,[r3,#0xC]		@turn status, hidden status
08038574 2280     mov     r2,#0x80
08038576 0112     lsl     r2,r2,#0x4		@r2 = 0000 1000  0000 0000
08038578 4011     and     r1,r2				@hidden status 0x08
0803857A 2900     cmp     r1,#0x0			
0803857C D024     beq     #0x80385C8		@
0803857E 4C11     ldr     r4,=#0x202E3E4	
08038580 6820     ldr     r0,[r4]
08038582 2101     mov     r1,#0x1
08038584 4249     neg     r1,r1
08038586 F7E0FD91 bl      #0x80190AC
0803858A 6832     ldr     r2,[r6]
0803858C 2011     mov     r0,#0x11
0803858E 5610     ldsb    r0,[r2,r0]
08038590 6821     ldr     r1,[r4]
08038592 0080     lsl     r0,r0,#0x2
08038594 1840     add     r0,r0,r1
08038596 2110     mov     r1,#0x10
08038598 5651     ldsb    r1,[r2,r1]
0803859A 6800     ldr     r0,[r0]
0803859C 1840     add     r0,r0,r1
0803859E 7005     strb    r5,[r0]
080385A0 6831     ldr     r1,[r6]
080385A2 2010     mov     r0,#0x10
080385A4 5608     ldsb    r0,[r1,r0]
080385A6 7C49     ldrb    r1,[r1,#0x11]
080385A8 0609     lsl     r1,r1,#0x18
080385AA 1609     asr     r1,r1,#0x18
080385AC F7FCF874 bl      #0x8034698
080385B0 2800     cmp     r0,#0x0
080385B2 D000     beq     #0x80385B6
080385B4 E0B3     b       #0x803871E
080385B6 6830     ldr     r0,[r6]
080385B8 F7FCF8F6 bl      #0x80347A8
080385BC E04F     b       #0x803865E
080385BE 0000     lsl     r0,r0,#0x0
080385C0 4690     mov     r8,r2
080385C2 0300     lsl     r0,r0,#0xC
080385C4 E3E4     b       #0x8038D90
080385C6 0202     lsl     r2,r0,#0x8
080385C8 6818     ldr     r0,[r3]
080385CA 6859     ldr     r1,[r3,#0x4]
080385CC 6A80     ldr     r0,[r0,#0x28]
080385CE 6A89     ldr     r1,[r1,#0x28]
080385D0 4308     orr     r0,r1
080385D2 2104     mov     r1,#0x4
080385D4 4008     and     r0,r1
080385D6 2800     cmp     r0,#0x0
080385D8 D011     beq     #0x80385FE
080385DA 1C18     mov     r0,r3
080385DC F7DFF87E bl      #0x80176DC
080385E0 2804     cmp     r0,#0x4
080385E2 DC0C     bgt     #0x80385FE
080385E4 6830     ldr     r0,[r6]
080385E6 F7E1FADB bl      #0x8019BA0
080385EA F7E1FD87 bl      #0x801A0FC
080385EE F000FB29 bl      #0x8038C44
080385F2 0600     lsl     r0,r0,#0x18
080385F4 1600     asr     r0,r0,#0x18
080385F6 2801     cmp     r0,#0x1
080385F8 D101     bne     #0x80385FE
080385FA 2000     mov     r0,#0x0
080385FC E0C9     b       #0x8038792
080385FE 490D     ldr     r1,=#0x203A8EC
08038600 317B     add     r1,#0x7B
08038602 2002     mov     r0,#0x2
08038604 7809     ldrb    r1,[r1]
08038606 4008     and     r0,r1
08038608 2800     cmp     r0,#0x0
0803860A D019     beq     #0x8038640
0803860C 4C0A     ldr     r4,=#0x202E3E4
0803860E 6820     ldr     r0,[r4]
08038610 2101     mov     r1,#0x1
08038612 4249     neg     r1,r1
08038614 F7E0FD4A bl      #0x80190AC
08038618 4808     ldr     r0,=#0x3004690
0803861A 6802     ldr     r2,[r0]
0803861C 2011     mov     r0,#0x11
0803861E 5610     ldsb    r0,[r2,r0]
08038620 6821     ldr     r1,[r4]
08038622 0080     lsl     r0,r0,#0x2
08038624 1840     add     r0,r0,r1
08038626 2110     mov     r1,#0x10
08038628 5651     ldsb    r1,[r2,r1]
0803862A 6800     ldr     r0,[r0]
0803862C 1840     add     r0,r0,r1
0803862E 2100     mov     r1,#0x0
08038630 7001     strb    r1,[r0]
08038632 E009     b       #0x8038648
08038634 A8EC     add     r0,sp,#0x3B0
08038636 0203     lsl     r3,r0,#0x8
08038638 E3E4     b       #0x8038E04
0803863A 0202     lsl     r2,r0,#0x8
0803863C 4690     mov     r8,r2
0803863E 0300     lsl     r0,r0,#0xC
08038640 4858     ldr     r0,=#0x3004690
08038642 6800     ldr     r0,[r0]
08038644 F7E1FAAC bl      #0x8019BA0
08038648 4856     ldr     r0,=#0x3004690
0803864A 6800     ldr     r0,[r0]
0803864C F7DFFF46 bl      #0x80184DC
08038650 0600     lsl     r0,r0,#0x18
08038652 2800     cmp     r0,#0x0
08038654 D003     beq     #0x803865E
08038656 2001     mov     r0,#0x1
08038658 4240     neg     r0,r0
0803865A F7E2FD75 bl      #0x801B148
0803865E 4852     ldr     r0,=#0x202E3E8
08038660 6800     ldr     r0,[r0]
08038662 F7E2FD95 bl      #0x801B190
08038666 2000     mov     r0,#0x0
08038668 4680     mov     r8,r0
0803866A 494E     ldr     r1,=#0x3004690
0803866C 6808     ldr     r0,[r1]
0803866E 8BC5     ldrh    r5,[r0,#0x1E]
08038670 2D00     cmp     r5,#0x0
08038672 D054     beq     #0x803871E
08038674 468A     mov     r10,r1
08038676 A903     add     r1,sp,#0xC
08038678 4689     mov     r9,r1
0803867A 4652     mov     r2,r10
0803867C 6810     ldr     r0,[r2]
0803867E 1C29     mov     r1,r5
08038680 F7DDFD90 bl      #0x80161A4
08038684 0600     lsl     r0,r0,#0x18
08038686 4647     mov     r7,r8
08038688 3701     add     r7,#0x1
0803868A 2800     cmp     r0,#0x0
0803868C D03C     beq     #0x8038708
0803868E 4644     mov     r4,r8
08038690 464B     mov     r3,r9
08038692 809C     strh    r4,[r3,#0x4]
08038694 2601     mov     r6,#0x1
08038696 1C30     mov     r0,r6
08038698 F7E0FB38 bl      #0x8018D0C
0803869C 1C04     mov     r4,r0
0803869E 2C00     cmp     r4,#0x0
080386A0 D02F     beq     #0x8038702
080386A2 6820     ldr     r0,[r4]
080386A4 2800     cmp     r0,#0x0
080386A6 D02C     beq     #0x8038702
080386A8 68E0     ldr     r0,[r4,#0xC]
080386AA 4940     ldr     r1,=#0x10025
080386AC 4008     and     r0,r1
080386AE 2800     cmp     r0,#0x0
080386B0 D127     bne     #0x8038702
080386B2 1C20     mov     r0,r4
080386B4 9909     ldr     r1,[sp,#0x24]
080386B6 F087FACB bl      #0x80BFC50
080386BA 0600     lsl     r0,r0,#0x18
080386BC 2800     cmp     r0,#0x0
080386BE D020     beq     #0x8038702
080386C0 4652     mov     r2,r10
080386C2 6810     ldr     r0,[r2]
080386C4 1C21     mov     r1,r4
080386C6 1C2A     mov     r2,r5
080386C8 F7FDFB42 bl      #0x8035D50
080386CC 0600     lsl     r0,r0,#0x18
080386CE 2800     cmp     r0,#0x0
080386D0 D017     beq     #0x8038702
080386D2 1C20     mov     r0,r4
080386D4 1C29     mov     r1,r5
080386D6 F000F945 bl      #0x8038964
080386DA 7AE0     ldrb    r0,[r4,#0xB]
080386DC 464B     mov     r3,r9
080386DE 7098     strb    r0,[r3,#0x2]
080386E0 A803     add     r0,sp,#0xC
080386E2 F000FB5F bl      #0x8038DA4
080386E6 0600     lsl     r0,r0,#0x18
080386E8 2800     cmp     r0,#0x0
080386EA D00A     beq     #0x8038702
080386EC 9905     ldr     r1,[sp,#0x14]
080386EE 9808     ldr     r0,[sp,#0x20]
080386F0 4281     cmp     r1,r0
080386F2 D306     bcc     #0x8038702
080386F4 A906     add     r1,sp,#0x18
080386F6 A803     add     r0,sp,#0xC
080386F8 C81C     ldmia   r0!,{r2-r4}
080386FA C11C     stmia   r1!,{r2-r4}
080386FC 4640     mov     r0,r8
080386FE 466C     mov     r4,r13
08038700 83A0     strh    r0,[r4,#0x1C]
08038702 3601     add     r6,#0x1
08038704 2EBF     cmp     r6,#0xBF
08038706 DDC6     ble     #0x8038696
08038708 46B8     mov     r8,r7
0803870A 2F04     cmp     r7,#0x4
0803870C DC07     bgt     #0x803871E
0803870E 4651     mov     r1,r10
08038710 6808     ldr     r0,[r1]
08038712 0079     lsl     r1,r7,#0x1
08038714 301E     add     r0,#0x1E
08038716 1840     add     r0,r0,r1
08038718 8805     ldrh    r5,[r0]
0803871A 2D00     cmp     r5,#0x0
0803871C D1AD     bne     #0x803867A
0803871E 4821     ldr     r0,=#0x3004690
08038720 6800     ldr     r0,[r0]
08038722 6801     ldr     r1,[r0]
08038724 6842     ldr     r2,[r0,#0x4]
08038726 6A88     ldr     r0,[r1,#0x28]
08038728 6A91     ldr     r1,[r2,#0x28]
0803872A 4308     orr     r0,r1
0803872C 2180     mov     r1,#0x80
0803872E 4008     and     r0,r1
08038730 2800     cmp     r0,#0x0
08038732 D00F     beq     #0x8038754
08038734 9809     ldr     r0,[sp,#0x24]
08038736 A903     add     r1,sp,#0xC
08038738 F000F9A0 bl      #0x8038A7C
0803873C 0600     lsl     r0,r0,#0x18
0803873E 1600     asr     r0,r0,#0x18
08038740 2801     cmp     r0,#0x1
08038742 D107     bne     #0x8038754
08038744 9905     ldr     r1,[sp,#0x14]
08038746 9808     ldr     r0,[sp,#0x20]
08038748 4281     cmp     r1,r0
0803874A D303     bcc     #0x8038754
0803874C A906     add     r1,sp,#0x18
0803874E A803     add     r0,sp,#0xC
08038750 C81C     ldmia   r0!,{r2-r4}
08038752 C11C     stmia   r1!,{r2-r4}
08038754 A906     add     r1,sp,#0x18
08038756 6888     ldr     r0,[r1,#0x8]
08038758 2800     cmp     r0,#0x0
0803875A D102     bne     #0x8038762
0803875C 7888     ldrb    r0,[r1,#0x2]
0803875E 2800     cmp     r0,#0x0
08038760 D017     beq     #0x8038792
08038762 4669     mov     r1,r13
08038764 7E08     ldrb    r0,[r1,#0x18]
08038766 7E49     ldrb    r1,[r1,#0x19]
08038768 466A     mov     r2,r13
0803876A 7E93     ldrb    r3,[r2,#0x1A]
0803876C 7F12     ldrb    r2,[r2,#0x1C]
0803876E 9200     str     r2,[sp]
08038770 2200     mov     r2,#0x0
08038772 9201     str     r2,[sp,#0x4]
08038774 9202     str     r2,[sp,#0x8]
08038776 2201     mov     r2,#0x1
08038778 F7FCFB6A bl      #0x8034E50
0803877C 466B     mov     r3,r13
0803877E 211C     mov     r1,#0x1C
08038780 5659     ldsb    r1,[r3,r1]
08038782 2001     mov     r0,#0x1
08038784 4240     neg     r0,r0
08038786 4281     cmp     r1,r0
08038788 D003     beq     #0x8038792
0803878A 4806     ldr     r0,=#0x3004690
0803878C 6800     ldr     r0,[r0]
0803878E F7FCF80B bl      #0x80347A8
08038792 B00A     add     sp,#0x28
08038794 BC38     pop     {r3-r5}
08038796 4698     mov     r8,r3
08038798 46A1     mov     r9,r4
0803879A 46AA     mov     r10,r5
0803879C BCF0     pop     {r4-r7}
0803879E BC02     pop     {r1}				@indicates a return value; disregarded by table 0x5
080387A0 4708     bx      r1
080387A2 0000     lsl     r0,r0,#0x0
080387A4 4690     mov     r8,r2				@literals
080387A6 0300     lsl     r0,r0,#0xC
080387A8 E3E8     b       #0x8038F7C
080387AA 0202     lsl     r2,r0,#0x8
080387AC 0025     lsl     r5,r4,#0x0
080387AE 0001     lsl     r1,r0,#0x0

Yeah, it’s a lot of code. I’ll be analyzing this, though.