[FE7 and FE8] Shuusuke's Simple Sorcery

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


1 Like