Made 3 ASMCs a few days ago, they’re all added to FEBuilder as patches but I forgot to make a post on them. Thanks to Contro, Huichelaar, Stan, Sme, Vesly and 7743 for the help in making them.
ClearZombies
This ASMC goes through all units and erases those that have 0 HP or are set to Dead. You can use it to free slots in the player roster by erasing dead units (instead of having to use CHECK_ALIVE and erasing them one-by-one), to ensure that an event battle erased the unit(s) that died in it (particularly convenient if the units involved in it can vary), or if you want something that affects multiple units (say, an event that damages units within an area, or an AoE skill like Savage Blow) to also be able to kill them (since this won’t happen even if said something does get the units HP to 0). Truth be told, I made this as a safeguard to ensure you don’t have any lingering units that shouldn’t be around anymore (which is why I refer to them as zombies, also because I think zombie process sounds funny).
@
@
@Author 7743
@"Zombie" edits by Shuusuke
@
.align 4
.macro blh to, reg=r3
ldr \reg, =\to
mov lr, \reg
.short 0xf800
.endm
.thumb
push {r4,r5,lr}
@MemorySlot1 (UnitID) 00=ANY
@MemorySlot2 (ClassID) 00=ANY
@MemorySlot3 (ItemID) 00=ANY
@MemorySlot4 (affiliation) FF=ANY 00=Player 01=Enemy 02=NPC 03=Purple
@ldr r4,=0x030004B0 @MemorySlot FE8J
ldr r4,=0x030004B8 @MemorySlot FE8U
@ldr r0, =0x0202BE48 @UnitRAM FE8J
ldr r0, =0x0202BE4C @UnitRAM FE8U
@ldr r5,=0x85 * 0x48 @Player+Enemy+NPC
ldr r5,=0x99 * 0x48 @Player+Enemy+NPC+Purple
add r5,r0
sub r0,#0x48 @Because it is troublesome, first subtract
next_loop:
cmp r0,r5
bgt break_loop
add r0,#0x48
ldr r2, [r0] @unitram->unit
cmp r2, #0x00
beq next_loop @Check Empty
ldrb r2, [r0,#0xC] @unitram->status
mov r3,#0x4 @dead
and r2,r3
cmp r2,#0x0 @proceed if unit has the dead state set, else check if current HP == 0
bne IsZombie
ldrb r2, [r0,#0x13] @unitram->Current HP
cmp r2,#0x00 @proceed if unit has 0 HP, else check next unit
beq IsZombie
bne next_loop
IsZombie:
check_unit_id:
ldr r3,[r4,#0x01 * 4] @MemorySlot1 (UnitID)
cmp r3,#0x00
beq check_class_id
ldr r2, [r0] @unitram->unit
ldrb r2, [r2, #0x4] @unitram->unit->id
cmp r2, r3
bne next_loop
check_class_id:
ldr r3,[r4,#0x02 * 4] @MemorySlot2 (ClassID)
cmp r3,#0x00
beq check_item_id
ldr r2, [r0, #0x4] @unitram->class
cmp r2, #0x00
beq next_loop
ldrb r2, [r2, #0x4] @unitram->class->id
cmp r2, r3
bne next_loop
check_item_id:
ldr r3,[r4,#0x03 * 4] @MemorySlot3 (ItemID)
cmp r3,#0x00
beq check_affiliation
ldrb r2, [r0, #0x1e] @unitram->item1
cmp r2, r3
beq item_match
mov r2, #0x20
ldrb r2, [r0, r2] @unitram->item2
cmp r2, r3
beq item_match
mov r2, #0x22
ldrb r2, [r0, r2] @unitram->item3
cmp r2, r3
beq item_match
mov r2, #0x24
ldrb r2, [r0, r2] @unitram->item4
cmp r2, r3
beq item_match
mov r2, #0x26
ldrb r2, [r0, r2] @unitram->item5
cmp r2, r3
bne next_loop
item_match:
check_affiliation:
ldr r3,[r4,#0x04 * 4] @MemorySlot4 (affiliation)
cmp r3,#0xFF @FF=ANY
beq affiliation_match
ldrb r2, [r0, #0xb] @unitram->affiliation
cmp r3,#0x01 @01=Enemy
beq check_affiliation_enemy
cmp r3,#0x02 @02=NPC
beq check_affiliation_npc
cmp r3,#0x03 @03=Purple
beq check_affiliation_purple
check_affiliation_player: @00=Player
@Player that did misconfiguration is treated as Player.
cmp r2,#0x40 @if (unit->affiliation >= 0x40){ cotinue; }
bge next_loop
b affiliation_match
check_affiliation_npc:
cmp r2,#0x40 @if (unit->affiliation < 0x40 || unit->affiliation >= 0x80){ cotinue; }
blt next_loop
cmp r2,#0x80
bge next_loop
b affiliation_match
check_affiliation_enemy:
cmp r2,#0x80 @if (unit->affiliation < 0x80 || unit->affiliation >= 0xC0){ cotinue; }
blt next_loop
cmp r2,#0xC0
bge next_loop
check_affiliation_purple:
cmp r2,#0xC0 @if (unit->affiliation < 0xC0){ cotinue; }
blt next_loop
@b affiliation_match
affiliation_match:
found:
bl Update
b next_loop
break_loop:
@ blh 0x08019ecc @RefreshFogAndUnitMaps @FE8J
@ blh 0x08027144 @SMS_UpdateFromGameData @FE8J
@ blh 0x08019914 @UpdateGameTilesGraphics @FE8J
blh 0x0801a1f4 @RefreshFogAndUnitMaps @FE8U
blh 0x080271a0 @SMS_UpdateFromGameData @FE8U
blh 0x08019c3c @UpdateGameTilesGraphics @FE8U
pop {r4,r5}
pop {r1}
bx r1
@r0 current unit ram
@r1 temp
@r2 temp
@r3 temp
@r4 MemorySlot
Update:
push {r5,lr}
mov r3, #0x0 @unit ID
cmp r3, #0x00 @unit pointer
beq set_str
b Update_Exit
set_str:
mov r2, #0x00
str r2, [r0,r3]
b Update_Exit
Update_Exit:
pop {r5}
pop {r1}
bx r1
GetSucessor
This one’s a little weird to explain but the short version is that it determines who should succeed as commander of an army/clan should the current leader die.
There aren’t examples like this in vanilla FE8 so I’ll give two from FE7 (even though this ASMC isn’t for FE7), in Dragon’s Gate, Darin’s successor would be Cameron, and in Battle Before Dawn, Ursula’s successor would be Maxime; this is because both Cameron and Maxime are bosses who have Darin and Ursula respectively set as their commanders. You could use this ASMC when Darin/Ursula die and, should Cameron/Maxime still be alive, make them the commander of the remaining generics.
There are two additional options, which is to enable non-bosses to be the successors (keep in mind that if there are multiple units of the same ID in a map and they get set as commander, there may be problems), as well as enabling female units from being successors (since traditionally, sons had priority over daughters when it came to succession, so I thought it made sense to make this an option should someone want to simulate it in their hack).
I had more plans for this ASMC but got fed up enough with ASM for the time being, so the base functionality will have to do
@Original author: 7743
@Sucessor check added by Shuusuke
.thumb
.align 4
.macro blh to, reg=r3
ldr \reg, =\to
mov lr, \reg
.short 0xf800
.endm
.thumb
push {r4,r5,r6,r7,lr}
@ldr r4,=0x030004B0 @MemorySlot FE8J
ldr r4,=0x030004B8 @MemorySlot FE8U
mov r5,#0x00 @r5 = commander ID
mov r6,#0xFF @r6 = commander affiliation 0=P 1=N 2=E 3=F
ldrb r0,[r4,#0x01 * 4] @MemorySlot1 (UnitID) @r0 = commander ID
cmp r0,#0x00
beq exit_commander @no commander?
mov r5,r0
bl get_commander_affiliation
cmp r0,#0x03
ble valid_commander @valid commander
mov r0,#0x00 @invalid commander
b exit_commander
valid_commander:
mov r6,r0
mov r1,r5
mov r2,r6
bl get_sucessor
exit_commander:
mov r7,#0x30
str r0,[r4,r7] @MemorySlotC (Result Value)
pop {r4,r5,r6,r7}
pop {r1}
bx r1
get_commander_affiliation:
push {r4,r5,r6,r7,lr}
@r0 = commander ID
@ldr r4, =0x0202BE48 @UnitRAM FE8J
ldr r4, =0x0202BE4C @UnitRAM FE8U
@ldr r5,=0x85 * 0x48 @Player+Enemy+NPC
ldr r5,=0x99 * 0x48 @Player+Enemy+NPC+Purple
add r5,r4
mov r3,r0
sub r4,#0x48 @Because it is troublesome, first subtract
next_loop_commander:
@cmp r0,#0xFF
@bne found_affiliation
cmp r4,r5
bgt break_loop_commander
add r4,#0x48
ldr r2, [r4] @unitram->unit
ldrb r2, [r2, #0x4] @unitram->unit->id
cmp r2, r3
bne next_loop_commander
ldrb r0, [r4, #0xb] @r0 = commander->affiliation
cmp r0,#0x40
blt player_commander
cmp r0,#0x80
blt npc_commander
cmp r0,#0xC0
blt enemy_commander
b purple_commander
player_commander:
mov r0,#0x00
b break_loop_commander
npc_commander:
mov r0,#0x01
b break_loop_commander
enemy_commander:
mov r0,#0x02
b break_loop_commander
purple_commander:
mov r0,#0x03
@b break_loop_commander
@r0=commander affiliation 0=P 1=N 2=E 3=F
break_loop_commander:
pop {r4,r5,r6,r7}
pop {r1}
bx r1
get_sucessor:
push {r4,r5,r6,r7,lr}
@r0 -> sucessor ID
@r1 = commander ID
@r2 = commander affiliation 0=P 1=N 2=E 3=F
@ldr r4, =0x0202BE48 @UnitRAM FE8J
ldr r4, =0x0202BE4C @UnitRAM FE8U
ldr r3,=0x85 * 0x48 @Player+Enemy+NPC
mov r7,r3
add r7,r4
mov r5, r1 @r5 = commander ID
mov r6, r2 @r6 = commander affiliation 0=P 1=N 2=E 3=F
sub r4,#0x48 @Because it is troublesome, first subtract
next_loop_unit:
cmp r4,r7
ble continue_loop_sucessor
mov r0,#0x00
b get_sucessor_break_loop
continue_loop_sucessor:
add r4,#0x48
mov r3,#0x38
ldrb r2, [r4,r3] @r2 = unitram->commander
cmp r2,r5 @commander mismatch
bne next_loop_unit
ldrb r1, [r4, #0xb] @r2 = unitram->affiliation
cmp r1,#0x40
blt player_unit
cmp r1,#0x80
blt npc_unit
cmp r1,#0xC0
blt enemy_unit
b purple_unit
player_unit:
mov r1,#0x00
b unit_allegiance_found
npc_unit:
mov r1,#0x01
b unit_allegiance_found
enemy_unit:
mov r1,#0x02
b unit_allegiance_found
purple_unit:
mov r1,#0x03
b unit_allegiance_found
unit_allegiance_found:
cmp r1,r6
bne next_loop_unit @affiliation mismatch?
ldr r0, [r4] @unitram->unit
ldrb r0, [r0, #0x4] @r0 = unitram->ID
bl validate_restriction
cmp r0,#0x00
beq next_loop_unit @candidate was found but invalidated due to restrictions
get_sucessor_break_loop:
pop {r4,r5,r6,r7}
pop {r1}
bx r1
validate_restriction:
push {r4,r5,r6,lr}
@r0 = unit ID
@MemorySlot 2 = EnableNonBoss
@MemorySlot 3 = EnableFemale
@ldr r4, =0x030004B0 @Slot0 {J}
ldr r4, =0x030004B8 @Slot0 {U}
ldr r1, [r4 , #0x2 * 4] @Slot2
cmp r1,#0x00
bne test_female
mov r6, r0 @r6 = unit ID
@blh 0x0800bf3c @UNITIDの解決 GetUnitStructFromEventParameter {J}
blh 0x0800bc50 @UNITIDの解決 GetUnitStructFromEventParameter {U}
@cmp r0,#0x00
@beq end_test
mov r5, r0
mov r0,r6
ldr r1, [r5, #0x0] @RAMUnit->Unit
ldr r3, [r5, #0x4] @RAMUnit->Class
ldr r2, [r1, #0x28]
ldr r3, [r3, #0x28]
orr r3 ,r2
mov r2,#0x80
lsl r2,#0x08 @boss ability
and r2,r3
cmp r2,#0x00
bne test_female
mov r0,#0x00 @unit disqualified for not being a boss
b end_test
test_female:
ldr r1, [r4 , #0x3 * 4] @Slot3
cmp r1,#0x00
bne end_test
@b test_lord
mov r6, r0 @r6 = unit ID
@blh 0x0800bf3c @UNITIDの解決 GetUnitStructFromEventParameter {J}
blh 0x0800bc50 @UNITIDの解決 GetUnitStructFromEventParameter {U}
@cmp r0,#0x00
@beq end_test
mov r5, r0
mov r0,r6
ldr r1, [r5, #0x0] @RAMUnit->Unit
ldr r3, [r5, #0x4] @RAMUnit->Class
ldr r2, [r1, #0x28]
ldr r3, [r3, #0x28]
orr r3 ,r2
mov r2,#0x40
lsl r2,#0x08 @female ability
and r2,r3
cmp r2,#0x00
beq end_test
mov r0,#0x00 @unit disqualified for being female
b end_test
end_test:
pop {r4,r5,r6}
pop {r1}
bx r1
SetUnitStatusCommander
A variation of the Set All Unit Status patch, which takes the commander as an additional parameter. You can use it to change the AI of all units who follow the same commander to simulate them giving orders, or lower/increase stats of all units when their commander dies, etc.
@Original author: 7743
@Commander check added by Shuusuke
.thumb
.align 4
.macro blh to, reg=r3
ldr \reg, =\to
mov lr, \reg
.short 0xf800
.endm
.thumb
.equ GetUnitByEventParameter, 0x800bc50
push {r4,r5,r6,lr}
@MemorySlot1 (UnitID) 00=ANY
@MemorySlot2 (ClassID) 00=ANY
@MemorySlot3 (ItemID) 00=ANY
@MemorySlot4 (affiliation) FF=ANY 00=Player 01=Enemy 02=NPC 03=Purple
@MemorySlot5 (Commander)
@MemorySlotA Type
@MemorySlotB Value
@ldr r4,=0x030004B0 @MemorySlot FE8J
ldr r4,=0x030004B8 @MemorySlot FE8U
@ldr r0, =0x0202BE48 @UnitRAM FE8J
ldr r0, =0x0202BE4C @UnitRAM FE8U
@ldr r5,=0x85 * 0x48 @Player+Enemy+NPC
ldr r5,=0x99 * 0x48 @Player+Enemy+NPC+Purple
add r5,r0
sub r0,#0x48 @Because it is troublesome, first subtract
next_loop:
cmp r0,r5
bgt break_loop
add r0,#0x48
ldr r2, [r0] @unitram->unit
cmp r2, #0x00
beq next_loop @Check Empty
ldrb r2, [r0,#0xC] @unitram->status
mov r3,#0xC @dead or not deploy
and r2,r3
cmp r2,#0x0 @maybe he is dead
bne next_loop
check_unit_id:
ldr r3,[r4,#0x01 * 4] @MemorySlot1 (UnitID)
cmp r3,#0x00
beq check_class_id
ldr r2, [r0] @unitram->unit
ldrb r2, [r2, #0x4] @unitram->unit->id
cmp r2, r3
bne next_loop
check_class_id:
ldr r3,[r4,#0x02 * 4] @MemorySlot2 (ClassID)
cmp r3,#0x00
beq check_item_id
ldr r2, [r0, #0x4] @unitram->class
cmp r2, #0x00
beq next_loop
ldrb r2, [r2, #0x4] @unitram->class->id
cmp r2, r3
bne next_loop
check_item_id:
ldr r3,[r4,#0x03 * 4] @MemorySlot3 (ItemID)
cmp r3,#0x00
beq check_affiliation
ldrb r2, [r0, #0x1e] @unitram->item1
cmp r2, r3
beq item_match
mov r2, #0x20
ldrb r2, [r0, r2] @unitram->item2
cmp r2, r3
beq item_match
mov r2, #0x22
ldrb r2, [r0, r2] @unitram->item3
cmp r2, r3
beq item_match
mov r2, #0x24
ldrb r2, [r0, r2] @unitram->item4
cmp r2, r3
beq item_match
mov r2, #0x26
ldrb r2, [r0, r2] @unitram->item5
cmp r2, r3
bne next_loop
item_match:
check_affiliation:
ldr r3,[r4,#0x04 * 4] @MemorySlot4 (affiliation)
cmp r3,#0xFF @FF=ANY
beq affiliation_match
ldrb r2, [r0, #0xb] @unitram->affiliation
cmp r3,#0x01 @01=Enemy
beq check_affiliation_enemy
cmp r3,#0x02 @02=NPC
beq check_affiliation_npc
cmp r3,#0x03 @03=Purple
beq check_affiliation_purple
check_affiliation_player: @00=Player
@Player that did misconfiguration is treated as Player.
cmp r2,#0x40 @if (unit->affiliation >= 0x40){ cotinue; }
bge next_loop
b affiliation_match
check_affiliation_npc:
cmp r2,#0x40 @if (unit->affiliation < 0x40 || unit->affiliation >= 0x80){ cotinue; }
blt next_loop
cmp r2,#0x80
bge next_loop
b affiliation_match
check_affiliation_enemy:
cmp r2,#0x80 @if (unit->affiliation < 0x80 || unit->affiliation >=0xC0){ cotinue; }
blt next_loop
cmp r2,#0xC0
bge next_loop
b affiliation_match
check_affiliation_purple:
cmp r2,#0xC0 @if (unit->affiliation < 0xC0){ cotinue; }
blt next_loop
@b affiliation_match
affiliation_match:
check_commander:
@ldrb r3,[r4,#0x05 * 4] @MemorySlot5 (Commander)
ldr r3,[r4,#0x05 * 4] @MemorySlot5 (Commander)
cmp r3,#0xFF
ble check_commander_by_ID
mov r6, r0
ldr r0,[r4,#0x05 * 4] @MemorySlot5
blh GetUnitByEventParameter
mov r3, r0
mov r0, r6
check_commander_by_ID:
mov r1, #0x38
ldrb r2, [r0, r1]
cmp r2, r3
beq commander_match
b next_loop
commander_match:
found:
bl Update
b next_loop
break_loop:
@ blh 0x08019ecc @RefreshFogAndUnitMaps @FE8J
@ blh 0x08027144 @SMS_UpdateFromGameData @FE8J
@ blh 0x08019914 @UpdateGameTilesGraphics @FE8J
blh 0x0801a1f4 @RefreshFogAndUnitMaps @FE8U
blh 0x080271a0 @SMS_UpdateFromGameData @FE8U
blh 0x08019c3c @UpdateGameTilesGraphics @FE8U
pop {r4,r5,r6}
pop {r1}
bx r1
@r0 current unit ram
@r1 temp
@r2 temp
@r3 temp
@r4 MemorySlot
Update:
push {r5,lr}
ldr r3,[r4,#0x0A * 4] @MemorySlotA (Type)
cmp r3, #0x00 @unit pointer
beq set_str
cmp r3, #0x04 @class pointer
beq set_str
cmp r3, #0x0C @bad status
beq set_str
cmp r3, #0x01 @unit id
beq set_unit_id
cmp r3, #0x05 @class id
beq set_class_id
cmp r3, #0x47
ble set_strb
cmp r3, #0x50
beq set_1A_lower
cmp r3, #0x51
beq set_1A_upper
cmp r3, #0x52
beq set_1D_lower
cmp r3, #0x53
beq set_1D_upper
cmp r3, #0x54
beq set_30_lower
cmp r3, #0x55
beq set_30_upper
cmp r3, #0x56
beq set_31_lower
cmp r3, #0x57
beq set_31_upper
b Update_Exit
set_str:
ldr r2,[r4,#0x0B * 4] @MemorySlotB (Value)
str r2, [r0,r3]
b Update_Exit
set_strb:
ldr r2,[r4,#0x0B * 4] @MemorySlotB (Value)
strb r2, [r0,r3]
b Update_Exit
set_1A_lower:
mov r3, #0x1A
b set_lower
set_1A_upper:
mov r3, #0x1A
b set_lower
set_1D_lower:
mov r3, #0x1D
b set_lower
set_1D_upper:
mov r3, #0x1D
b set_lower
set_30_lower:
mov r3, #0x30
b set_lower
set_30_upper:
mov r3, #0x30
b set_lower
set_31_lower:
mov r3, #0x31
b set_lower
set_31_upper:
mov r3, #0x31
b set_lower
set_unit_id:
mov r5, r0 @unit ram\‘¢‘̂̃|ƒCƒ“ƒ^‚ð•Û‘¶
ldr r0,[r4,#0x0B * 4] @MemorySlotB (Value) @unit id
@ blh 0x08019108 @GetUnitStruct @FE8J
blh 0x08019430 @GetUnitStruct @FE8U
cmp r0, #0x00
beq Update_Exit
str r0, [r5, #0x00]
mov r0, r5
b Update_Exit
set_class_id:
mov r5, r0 @unit ram\‘¢‘̂̃|ƒCƒ“ƒ^‚ð•Û‘¶
ldr r0,[r4,#0x0B * 4] @MemorySlotB (Value) @class id
@ blh 0x0801911C @GetROMClassStruct @FE8J
blh 0x08019444 @GetROMClassStruct @FE8U
cmp r0, #0x00
beq Update_Exit
str r0, [r5, #0x04]
mov r0, r5
b Update_Exit
set_upper:
ldrb r2, [r0,r3] @ ([r0,r3] & 0xf) | r2 << 4
mov r1,#0xf
and r1,r2
ldr r2,[r4,#0x0B * 4] @MemorySlotB (Value)
lsl r2,#0x4
orr r2, r1
strb r2, [r0,r3]
b Update_Exit
set_lower:
ldrb r2, [r0,r3] @ ([r0,r3] & 0xf0) | r2
mov r1,#0xf0
and r1,r2
ldr r2,[r4,#0x0B * 4] @MemorySlotB (Value)
orr r2,r1
strb r2, [r0,r3]
@b Update_Exit
Update_Exit:
pop {r5}
pop {r1}
bx r1