- 417
- Posts
- 9
- Years
- Seen Nov 20, 2016
I've been dragging my feet on writing this up, but today I bring you hidden abilities for FireRed.
Prerequisites
- Basic experience inserting ASM
- A hex editor
- The ability to read and write simple routines is a plus, though not required
I'll try to be detailed with what specifically each step is doing in order to make modifications easier. If you don't care about that, simply look for the walls of code.
Step 1: New Method for Determining Abilities
Step 2: Everywhere the Game Calls the Function
Step 3: Base Stats Table
Step 4: Pokemon Generation
Step 5: Wild Pokemon
Step 6: NPC Wild Encounters
Step 7: Daycare
Step 8: Egg Hatching
Step 9: Trainers
And we're done! But a few more comments.
All of the above steps were done with a vanilla FireRed ROM. I haven't tested much with them, so I could imagine contributions to the Ability Resource/Move Resource Threads having conflicting hooks or more hooks being necessary for new and updated abilities/attacks. If necessary, I'll attempt to make updates to remedy any incompatibilities. I'll also be adding a method for triggering hidden abilities with rods sometime in the future. I haven't even looked into it yet because, to be frank, it isn't something I really cared about. But rest assured I'll find another hook for completeness sake.
Credits:
A big thanks to Touched for providing the necessary hooks for many of these routines as well as walking me through a good amount of ASM reading and logic in general.
Also instrumental was the valuable research in knizz's idb, IMO the greatest tool in a FireRed hacker's arsenal.
DoesntKnowHowToPlay's Trainer EV Hack saved me a good amount of time with trainer research.
Any questions/comments welcome.
Extras
Prerequisites
- Basic experience inserting ASM
- A hex editor
- The ability to read and write simple routines is a plus, though not required
I'll try to be detailed with what specifically each step is doing in order to make modifications easier. If you don't care about that, simply look for the walls of code.
Step 1: New Method for Determining Abilities
Spoiler:
In a vanilla ROM, a commonly used subroutine for deciding which ability a Pokemon gets is found at 0x08040D38. The function takes species and "ability bit" as arguments. This "ability bit" is a single bit in the Misc data substructure and is actually redundant, as it is merely a copy of the least significant bit of personality for any Pokemon with two possible abilities. My goal is to instead use "ability bit" as a marker for a Pokemon with hidden abilities and use personality to read the slotted abilities of Pokemon that do not have the hidden ability marker. All future mentions of "ability bit" (mostly in various routines I wrote) are now a reference to a hidden ability marker.
Assemble and paste binary at 0x08040D38:
Change 0xYYYYYYYY in the literal pool to wherever your base stats table happens to be. If the format looks a little odd to you, I've written it this way to allow future routines with slightly different arguments and required output to easily bl to different parts of the function.
Assemble and paste binary at 0x08040D38:
Code:
.text
.align 2
.thumb
.thumb_func
@.org 0x040D38
main:
push {lr}
bl offset_to_ability
ldr r2, b_last_copied_ability
strb r0, [r2]
pop {r1}
bx r1
offset_to_ability:
push {r4-r5, lr}
mov r4, r0
mov r1, #0xB
bl main - 0x1150 @get_attr
lsl r5, r0, #0x10
lsr r5, r5, #0x10
mov r0, r4
mov r1, #0x2E
bl main - 0x1150 @get_attr
lsl r1, r0, #0x1F
ldr r0, [r4, #0x0]
mov r2, r5 @species
bl determine_ability
pop {r4-r5}
pop {r1}
bx r1
determine_and_copy:
push {lr}
bl determine_ability
ldr r2, b_last_copied_ability
strb r0, [r2]
pop {r1}
bx r1
determine_ability:
lsl r3, r2, #0x3
sub r3, r3, r2
lsl r3, r3, #0x2
ldr r2, base_stats_table
add r3, r2, r3
cmp r1, #0x0
beq no_hidden
ldrb r2, [r3, #0x1A]
cmp r2, #0x0
bne copy_hidden
no_hidden:
lsl r0, r0, #0x1F
cmp r0, #0x0
beq first_slotted
ldrb r0, [r3, #0x17]
cmp r0, #0x0
beq first_slotted
bx lr
first_slotted:
ldrb r0, [r3, #0x16]
bx lr
copy_hidden:
mov r0, r2
bx lr
.align
[b]base_stats_table: .word 0xYYYYYYYY[/b] @vanilla: 0x08254784
b_last_copied_ability: .word 0x02023D6A
Step 2: Everywhere the Game Calls the Function
Spoiler:
We'll be making slight modifications to eleven routines. For most of them, it is most efficient to add an additional argument (personality) before the bl.
Paste the binaries of each of the following where the .org indicates. DO NOT PASTE THE WALLS OF ZEROES. If you don't like the .org format or are not comfortable with where to begin copy/paste, you can comment them out and assemble all twelve of these one at a time as separate files.
Paste the binaries of each of the following where the .org indicates. DO NOT PASTE THE WALLS OF ZEROES. If you don't like the .org format or are not comfortable with where to begin copy/paste, you can comment them out and assemble all twelve of these one at a time as separate files.
Code:
.text
.align 2
.thumb
.thumb_func
.org 0x013144
first_routine:
mov r1, r9
ldrb r1, [r1]
mul r1, r7
add r1, r8
ldr r0, [r1, #0x48] @personality
ldrh r2, [r1] @species
ldrb r1, [r1, #0x17]
lsr r1, r1, #0x7 @ability bit
bl first_routine + 0x2DC28 @0x08040D6C
.org 0x023FD0
second_routine:
ldrb r0, [r7]
mul r0, r6
add r0, r0, r4
ldrh r2, [r0, #0x0] @species
ldrb r1, [r0, #0x17]
lsr r1, r1, #0x7 @ability bit
ldr r0, [r1, #0x48] @personality
bl second_routine + 0x1CD9C @0x08040D6C
ldrb r1, [r7]
@switches
.org 0x026E34
third_routine:
ldrb r0, [r4, #0x0] @personality
mov r1, r6 @ability bit
mov r2, r5 @species
bl third_routine + 0x19F38 @0x08040D6C
lsl r0, r0, #0x0
@player ability
.org 0x026ECC
fourth_routine:
and r0, r1
cmp r0, #0x0
beq fourth_routine + 0x20
ldrb r0, [r4, #0x0] @personality
mov r1, r6 @ability bit
mov r2, r5 @species
bl fourth_routine + 0x19EA0 @0x08040D6C
lsl r0, r0, #0x0
cmp r0, #0x2B
beq fourth_routine + 0x20
mov r0, #0x1
@opponent ability
.org 0x02A800
fifth_routine_part_a:
mov r1, #0x41
bl fifth_routine_part_a + 0x153E8 @get_attr
mov r5, r0
mov r0, r4
mov r1, #0x2E
bl fifth_routine_part_a + 0x153E8 @get_attr
ldrb r4, [r4, #0x0] @lowest personality byte
lsl r0, r0, #0x18
orr r4, r0 @000000b 00000000 00000000 pppppppp
@player ability second
.org 0x02A890
fifth_routine_part_b:
lsl r0, r4, #0x18
lsr r0, r0, #0x18 @personality lowest byte
lsr r1, r4, #0x18 @ability bit
mov r2, r5 @species
bl fifth_routine_part_b + 0x164DC @0x08040D6C
@opponent ability second
.org 0x02CE60
sixth_routine:
mov r1, #0x41
bl sixth_routine + 0x12D88 @get_attr
lsl r0, r0, #0x10
lsr r5, r0, #0x10
mov r0, r4
mov r1, #0xC @held item
bl sixth_routine + 0x12D88 @get_attr
lsl r0, r0, #0x10
lsr r6, r0, #0x10
mov r0, r4
mov r1, #0x2E
bl sixth_routine + 0x12D88 @get_attr
lsl r1, r0, #0x18
lsr r1, r1, #0x18 @ability bit
ldrb r0, [r4, #0x0] @personality
mov r2, r5 @species
bl sixth_routine + 0x13F1A @0x08040D7A
b sixth_routine + 0x42 @0x0802CEA2
@pickup
.org 0x039548
seventh_routine:
lsl r0, r0, #0x18
lsr r1, r0, #0x18 @ability bit
ldrb r0, [r5, #0x0] @personality
mov r2, r4 @species
bl seventh_routine + 0x7832 @ 0x08040D7A
b seventh_routine + 0x2A @ 0x08039572
@water/volt absorb, flash fire
.org 0x03999E
eighth_routine:
lsl r0, r0, #0x18
lsr r1, r0, #0x18 @ability bit
ldrb r0, [r5, #0x0] @personality
mov r2, r4 @species
bl eighth_routine + 0x73DC @ 0x08040D7A
b eighth_routine + 0x2E @ 0x080399CC
@ai calculations
.org 0x041318
ninth_routine:
ldrh r2, [r7] @species
ldrb r1, [r7, #0x17]
lsr r1, r1, #0x7 @ability bit
ldrb r0, [r4, #0x0] @personality
bl ninth_routine - 0x5AC @ 0x08040D6C
add r1, r7, #0x4
strb r0, [r1, #0x1C]
@poke summary info
.org 0x082C70
tenth_routine:
bl tenth_routine - 0x41F38 @0x08040D38
@stench, illuminate
.org 0x1366EC
eleventh_routine:
lsl r4, r0, #0x10
ldr r0, [r6]
add r0, r8
ldrb r1, [r0, #0x0] @personality
orr r4, r1 @ssssssss ssssssss 00000000 pppppppp
mov r1, #0x2E
bl eleventh_routine - 0xF6B04 @get_attr
lsl r0, r0, #0x18
lsr r1, r0, #0x18 @ability bit
lsl r0, r4, #0x18
lsr r0, r0, #0x18 @personality
lsr r2, r4, #0x10 @species
bl eleventh_routine - 0xF5980 @ 0x08040D6C
lsl r0, r0, #0x18
lsr r4, r0, #0x18
ldr r0, [r6]
@ability names
Spoiler:
The base stats table contains a 0x1C bytes long entry for each Pokemon in the game ordered by index number. Fortunately, there is a halfword of padding at the end. If you read the first routine, you'll know that I used the first of them, byte 0x1A (zero indexed), to hold the index of a hidden ability.
Crack open a hex editor, change width to 0x1C bytes, and add hidden abilities to the base stats table. Leaving a hidden ability/second slotted ability as 0x00 won't cause problems even if an in game Pokemon has a "hidden" marker; the routine from step 1 simply moves on until it finds a non-zero ability index. If you have not expanded Pokemon, your base stats table is most likely at 0x08254784. For convenience sake, here are the indices for each ability in a vanilla FireRed ROM:
Crack open a hex editor, change width to 0x1C bytes, and add hidden abilities to the base stats table. Leaving a hidden ability/second slotted ability as 0x00 won't cause problems even if an in game Pokemon has a "hidden" marker; the routine from step 1 simply moves on until it finds a non-zero ability index. If you have not expanded Pokemon, your base stats table is most likely at 0x08254784. For convenience sake, here are the indices for each ability in a vanilla FireRed ROM:
Code:
stench: .equ 1
drizzle: .equ 2
speed_boost: .equ 3
battle_armor: .equ 4
sturdy: .equ 5
damp: .equ 6
limber: .equ 7
sand_veil: .equ 8
static_: .equ 9
volt_absorb: .equ 0xA
water_absorb: .equ 0xB
oblivious: .equ 0xC
cloud_nine: .equ 0xD
compound_eyes: .equ 0xE
insomnia: .equ 0xF
color_change: .equ 0x10
immunity: .equ 0x11
flash_fire: .equ 0x12
shield_dust: .equ 0x13
own_tempo: .equ 0x14
suction_cups: .equ 0x15
intimidate: .equ 0x16
shadow_tag: .equ 0x17
rough_skin: .equ 0x18
wonder_guard: .equ 0x19
levitate: .equ 0x1A
effect_spore: .equ 0x1B
synchronize: .equ 0x1C
clear_body: .equ 0x1D
natural_cure: .equ 0x1E
lightningrod: .equ 0x1F
serene_grace: .equ 0x20
swift_swim: .equ 0x21
chlorophyll: .equ 0x22
illuminate: .equ 0x23
trace: .equ 0x24
huge_power: .equ 0x25
poison_point: .equ 0x26
inner_focus: .equ 0x27
magma_armor: .equ 0x28
water_veil: .equ 0x29
magnet_pull: .equ 0x2A
soundproof: .equ 0x2B
rain_dish: .equ 0x2C
sand_stream: .equ 0x2D
pressure: .equ 0x2E
thick_fat: .equ 0x2F
early_bird: .equ 0x30
flame_body: .equ 0x31
run_away: .equ 0x32
keen_eye: .equ 0x33
hyper_cutter: .equ 0x34
pickup: .equ 0x35
truant: .equ 0x36
hustle: .equ 0x37
cute_charm: .equ 0x38
plus: .equ 0x39
minus: .equ 0x3A
forecast: .equ 0x3B
sticky_hold: .equ 0x3C
shed_skin: .equ 0x3D
guts: .equ 0x3E
marvel_scale: .equ 0x3F
liquid_ooze: .equ 0x40
overgrow: .equ 0x41
blaze: .equ 0x42
torrent: .equ 0x43
swarm: .equ 0x44
rock_head: .equ 0x45
drought: .equ 0x46
arena_trap: .equ 0x47
vital_spirit: .equ 0x48
white_smoke: .equ 0x49
pure_power: .equ 0x4A
shell_armor: .equ 0x4B
cacophony: .equ 0x4C
air_lock: .equ 0x4D
Spoiler:
Great! Now the game knows how to read hidden abilities. But in the current state, the only in game effect would be a Pokemon having a hidden ability half the time and never having a second slotted ability. Clearly this isn't ideal.
We begin with a commonly used Pokemon generation routine, specifically a part of it at 0x0803DD58. This is responsible for setting the ability bit of a Pokemon dependent on the least significant bit of personality and whether or not the Pokemon can have a second ability. There's a little space here for some manipulation, but because the function is used by numerous other routines, I've decided to make it automatically set the "ability bit" of every Pokemon generated to zero. In other words, by default, generated Pokemon will not have a hidden ability. Any setting of the hidden ability bit will occur after this function is called to allow for more flexibility between different methods of Pokemon creation.
Assemble and paste binary at 0x0803DD58:
We begin with a commonly used Pokemon generation routine, specifically a part of it at 0x0803DD58. This is responsible for setting the ability bit of a Pokemon dependent on the least significant bit of personality and whether or not the Pokemon can have a second ability. There's a little space here for some manipulation, but because the function is used by numerous other routines, I've decided to make it automatically set the "ability bit" of every Pokemon generated to zero. In other words, by default, generated Pokemon will not have a hidden ability. Any setting of the hidden ability bit will occur after this function is called to allow for more flexibility between different methods of Pokemon creation.
Assemble and paste binary at 0x0803DD58:
Code:
.text
.align 2
.thumb
.thumb_func
@.org 0x03DD58
main:
mov r0, #0x0
str r0, [sp, #0x18]
mov r0, r7
mov r1, #0x2E
mov r2, r9
bl main + 0x2778 @ set_pokemon_data_2
b main + 0x26 @ 0x0803DD7E
Spoiler:
As indicated by the header, we now work our way through being able to set hidden ability bits for wild Pokemon.
Assemble at 0x080829FC:
Note the bolded instructions. These are merely examples; you can do whatever you want so long as the ultimate result is r0 containing byte 0x0 for no hidden ability or 0x1 for having a hidden ability in that first bolded section. Remember, however, that what is assembled cannot exceed the next routine which begins at 0x08082AB8. In total, (and assuming you removed all three bolded sections), you have 0x24 bytes to work with. If you need more than that, you'll need to either give yourself extra bytes by finding inefficient instructions in the existing routine or add a hook.
I chose the format above to make it easy to work with for people without asm knowledge. In the current form, it will check a specified flag (0xYYY in the literal pool) and if the flag is set, gives the wild Pokemon a hidden ability bit. I also made the routine automatically clear the flag. This isn't necessary, but I would guess that it's easier to use from a scripting standpoint.
It is also rather simple to make a hidden ability luck dependent by bl'ing to the prng at 0x08044EC8 and doing a comparison after, or perhaps in lieu of, the flag check.
Don't worry about the Unown stuff; I tacked it together because the only calls to it originated from this routine and I wanted to rewrite it to give more space for people to write hidden ability triggers.
Assemble at 0x080829FC:
Code:
.text
.align 2
.thumb
.thumb_func
@.org 0x0829FC
main:
push {r4-r6, lr}
sub sp, sp, #0x10
lsl r4, r0, #0x10
lsr r4, r4, #0x10
lsl r6, r1, #0x18
lsl r5, r2, #0x18
lsr r5, r5, #0x18
bl main - 0x44FC8 @purge_opponent 0x0803DA34
cmp r4, #0xC9
beq generate_unown
mov r1, r4 @species
mov r0, #0x0
str r0, [sp, #0x0] @random personality
b call_make_mon
generate_unown:
ldr r0, block_eight
ldr r0, [r0]
ldrb r1, [r0, #0x5]
sub r1, r1, #0x1B
ldr r2, tanoby_table_probably
lsl r1, r1, #0x18
asr r1, r1, #0x18
lsl r0, r1, #0x1
add r0, r0, r1
lsl r0, r0, #0x2
add r0, r5, r0
add r0, r0, r2
ldrb r0, [r0]
bl unown_something
mov r1, #0x1
str r1, [sp, #0x0]
str r0, [sp, #0x4]
mov r1, #0xC9
mov r0, #0x0
call_make_mon:
str r0, [sp, #0x8] @no auto shineaz
str r0, [sp, #0xC]
ldr r0, party_opponent
lsr r2, r6, #0x18
mov r3, #0x20
bl main - 0x44FA8 @pokemon_make_2 0x0803DA54
[b]checks_hidden_flag:
ldr r0, flag_hex
bl main - 0x1432C @flag check 0x0806E6D0[/b]
store_hidden_ability_result:
str r0, [sp, #0x0]
ldr r0, party_opponent
mov r1, #0x2E
mov r2, sp
bl main - 0x4252C @set_pokemon_data_2 0x080404D0
[b]clear_hidden_flag:
ldr r0, flag_hex
bl main - 0x14354 @flag clear 0x0806E6A8[/b]
end:
add sp, sp, #0x10
pop {r4-r6}
pop {r0}
bx r0
unown_something:
push {r4-r5, lr}
lsl r5, r0, #0x18
unown_loop:
bl main - 0x3DB34 @ random 0x044EC8
lsl r4, r0, #0x10
bl main - 0x3DB34 @ random 0x044EC8
orr r4, r0
bl main + 0xBC @ 0x08082AB8
lsl r0, r0, #0x18
cmp r0, r5
bne unown_loop
mov r0, r4
pop {r4, r5}
pop {r1}
bx r1
.align
block_eight: .word 0x03005008
tanoby_table_probably: .word 0x083CA71C
party_opponent: .word 0x0202402C
[b]flag_hex: .word 0xYYY[/b]
I chose the format above to make it easy to work with for people without asm knowledge. In the current form, it will check a specified flag (0xYYY in the literal pool) and if the flag is set, gives the wild Pokemon a hidden ability bit. I also made the routine automatically clear the flag. This isn't necessary, but I would guess that it's easier to use from a scripting standpoint.
It is also rather simple to make a hidden ability luck dependent by bl'ing to the prng at 0x08044EC8 and doing a comparison after, or perhaps in lieu of, the flag check.
Don't worry about the Unown stuff; I tacked it together because the only calls to it originated from this routine and I wanted to rewrite it to give more space for people to write hidden ability triggers.
Step 6: NPC Wild Encounters
Spoiler:
It would probably be useful to generate hidden ability Pokemon through script, such as legendaries or special Pokemon in general. Assemble and paste the following in any halfword aligned free space:
It should look very familiar. Just like standard wild Pokemon, I'm using a simple flag to toggle hidden abilities. You can rewrite the conditions to be as arbitrary as you would like. It is worth mentioning that although it can be, the flag in this step does not necessarily need to be the same as the flag used in the fifth step.
Take note of where you pasted the above routine. You'll need that offset to place the following at 0x080A02EC:
Code:
.text
.align 2
.thumb
.thumb_func
check_for_hidden:
ldr r0, hex_flag
ldr r3, func_flag_check
bl bx_r3
cmp r0, #0x1
bne end
give_hidden_ability:
push {r0}
ldr r3, func_set_attr
mov r2, sp
mov r1, #0x2E
mov r0, r8
bl bx_r3
add sp, #0x4
clear_flag:
ldr r0, hex_flag
ldr r3, func_flag_clear
bl bx_r3
end:
pop {r3}
mov r8, r3
pop {r4-r7}
pop {r3}
bx_r3:
bx r3
.align
func_flag_check: .word 0x0806E6D0 + 1
func_set_attr: .word 0x0804037C + 1
func_flag_clear: .word 0x0806E6A8 + 1
[b]hex_flag: .word 0xYYY[/b]
Take note of where you pasted the above routine. You'll need that offset to place the following at 0x080A02EC:
Code:
.text
.align 2
.thumb
.thumb_func
@.org 0x0A02EC
main:
ldr r0, check_for_hidden
bx r0
.align
[b]check_for_hidden: .word 0xYYYYYYYY + 1[/b]
Spoiler:
We'll be doing another hook here. Again, assemble and paste binary in halfword aligned free space:
Add the following hook at 0x08046116:
Code:
.text
.align 2
.thumb
.thumb_func
inherit_hidden:
add r0, r5, #0x0
ldr r3 , func_roll_gender
bl bx_r3
cmp r0, #0xFE
beq mother_in_slot_one
mov r0, r5
add r0, r0, #0x8C
mov r1, #0xB
bl get_zero_attr
cmp r0, #0x84
beq mother_in_slot_one
mov r0, r5
add r0, r0, #0x8C
b check_for_hidden
mother_in_slot_one:
add r0, r5, #0x0
check_for_hidden:
mov r1, #0x2E
bl get_zero_attr
cmp r0, #0x0
beq end
ldr r3, func_rand
bl bx_r3
lsr r0, r0, #0x8
cmp r0, #0x99
bhi end
ldr r3, func_set_attr
mov r2, #0x1
push {r2}
mov r2, sp
mov r1, #0x2E
mov r0, r4
bl bx_r3
add sp, #0x4
end:
mov r2, r4
add r2, r2, #0x6A
mov r0, #0x1
strb r0, [r2]
mov r0, r4
ldr r1, func_return
bx r1
get_zero_attr:
mov r2, #0x0
ldr r3, func_get_attr
bx_r3:
bx r3
.align
func_roll_gender: .word 0x0803F730 + 1
func_get_attr: .word 0x0803FBE8 + 1
func_rand: .word 0x08044EC8 + 1
func_set_attr: .word 0x0804037C + 1
func_return: .word 0x08046120 + 1
Code:
.text
.align 2
.thumb
.thumb_func
@.org 0x046116
main:
mov r4, sp
ldr r0, inherit_hidden
bx r0
[b]inherit_hidden: .word 0xYYYYYYYY + 1 [/b]@your routine
Spoiler:
Apparently the egg hatching routine saves attributes that are already in the egg, calls a Pokemon making routine, then sets those attributes again. So two little fixes:
First, at 0x08046CA0:
Second, at 0x08046D3C:
Feel free to look at what's going on here beginning at 0x08046BFC. All I've done was change the save and set obedience to instead save and set hidden ability bit. If you're like, super attached to creating obedient/disobedient Mew/Deoxys eggs, I guess you can tighten up the main routine, but it honestly isn't something I am particularly passionate about.
First, at 0x08046CA0:
Code:
.text
.align 2
.thumb
.thumb_func
@.org 0x046CA0
main:
mov r0, r6
mov r1, #0x2E
Code:
.text
.align 2
.thumb
.thumb_func
@.org 0x046D3C
main:
mov r0, r5
mov r1, #0x2E
Step 9: Trainers
Spoiler:
A great way to selectively give hidden abilities to trainers is a modified version of DoesntKnowHowToPlay's Trainer EV Method. It can be found here: https://www.pokecommunity.com/threads/307117
Use any of the filler bytes and add a hidden ability insertion somewhere in the Method Address that mimics the load style. For example, the following could be placed anywhere around the loads:
Use any of the filler bytes and add a hidden ability insertion somewhere in the Method Address that mimics the load style. For example, the following could be placed anywhere around the loads:
Code:
lsl r0, r6, #0x4
ldr r2, .EV_Table
add r2, r0
add r2, #0xF @1 = hidden ability
mov r1, #0x2E @needed to set hidden ability bit
mov r0, r4
bl Insert_Element
And we're done! But a few more comments.
All of the above steps were done with a vanilla FireRed ROM. I haven't tested much with them, so I could imagine contributions to the Ability Resource/Move Resource Threads having conflicting hooks or more hooks being necessary for new and updated abilities/attacks. If necessary, I'll attempt to make updates to remedy any incompatibilities. I'll also be adding a method for triggering hidden abilities with rods sometime in the future. I haven't even looked into it yet because, to be frank, it isn't something I really cared about. But rest assured I'll find another hook for completeness sake.
Credits:
A big thanks to Touched for providing the necessary hooks for many of these routines as well as walking me through a good amount of ASM reading and logic in general.
Also instrumental was the valuable research in knizz's idb, IMO the greatest tool in a FireRed hacker's arsenal.
DoesntKnowHowToPlay's Trainer EV Hack saved me a good amount of time with trainer research.
Any questions/comments welcome.
Extras
Spoiler:
Flame Body and Magma Armor
Spoiler:
Halfword aligned free space:
Assemble and paste at 0x08046340:
Where "check_flame" is the routine placed in free space above.
At 0x0804637C:
Code:
.text
.align 2
.thumb
.thumb_func
check_flame:
mov r5, #0x0 @counter
check_ability_loop:
mov r0, #0x64
mul r0, r5
ldr r1, party_offset
add r4, r1, r0
mov r0, r4
mov r1, #0x6
ldr r3, func_get_attr
bl bx_r3
cmp r0, #0x0
bne increment_loop
mov r0, r4
ldr r3, func_get_ability
bl bx_r3
cmp r0, #0x28 @magma armor
beq set_doubled
cmp r0, #0x31 @flame body
beq set_doubled
increment_loop:
add r5, #0x1
ldr r0, party_quantity
ldrb r0, [r0]
cmp r5, r0
ble check_ability_loop
mov r7, #0x1
b end
set_doubled:
mov r7, #0x2
end:
mov r5, #0x0
ldr r3, func_return
bx_r3:
bx r3
.align
party_offset: .word 0x02024284
party_quantity: .word 0x02024029
func_get_attr: .word 0x0803FBE8 + 1
func_get_ability: .word 0x08040D46 + 1
func_return: .word 0x080463A0 + 1
Code:
.text
.align 2
.thumb
.thumb_func
@.org 0x046340
hook_flame_check:
cmp r0, #0xFF
bne hook_flame_check + 0x68 @0x080463A8
ldr r0, check_flame
bx r0
[b]check_flame: .word 0xYYYYYYYY + 1[/b]
At 0x0804637C:
Code:
.text
.align 2
.thumb
.thumb_func
@.org 0x04637C
compare_to_decrement:
cmp r0, r7
bge reduce_cycles
ldr r0, var8004
strh r5, [r0]
mov r0, #0x1
b compare_to_decrement + 0x2E
party_player: .word 0x02024284
var8004: .word 0x020370C0
reduce_cycles:
sub r0, r0, r7
str r0, [sp, #0x0]
Last edited: