[FR] Ability Capsule
Find 0x1FC bytes of free space at a word-aligned address, and take note of it.
Create an item of Type 1 ("Out of battle").
Set the item's Field pointer to the address you noted, plus 1.
Assemble the following routine, changing 0xXXXXXX to the address you noted (not plus 1).
Open the generated .bin file, navigate to the location address, and select 0x1FC bytes.
Copy the bytes and paste them in your ROM, at the same address.
Code:
.thumb
.equ location, 0xXXXXXX
.equ rom, (0x08000000 + 1)
.equ ability_name_length, 0x0D
.equ base_stats_length, 0x1C
.equ pokemon_length, 0x64
.equ req_species, 0x0B
.equ req_species2, 0x41
.equ req_ability, 0x2E
.equ ability1, 0x16
.equ ability2, 0x17
.equ egg_species, 0x19C
.org location, 0xFF
ability_capsule:
push {lr}
lsl r0, r0, #0x18
lsr r0, r0, #0x18
ldr r2, item_function_ptr
ldr r1, =(rom + dp05_abilitycapsule)
str r1, [r2]
ldr r3, item_consume_maybe
bl call_via_r3
pop {r3}
bx r3
dp05_abilitycapsule:
push {r4-r7,lr}
mov r6, r1
lsl r0, r0, #0x18
lsr r5, r0, #0x18
get_selected_pokemon:
ldr r0, brm
ldrb r0, [r0, #9]
mov r1, #pokemon_length
mul r0, r1
ldr r1, party_player
add r4, r0, r1
get_species:
mov r0, r4
mov r1, #req_species2
ldr r3, get_attr
bl call_via_r3
egg_check:
ldrh r1, =egg_species
cmp r0, r1
beq fail
compare_abilities:
ldr r1, base_stats_ptr
ldr r1, [r1]
mov r2, #base_stats_length
mul r0, r2
add r1, r0
ldrb r0, [r1, #ability1]
ldrb r1, [r1, #ability2]
cmp r0, r1
beq fail
cmp r0, #0
beq fail
cmp r1, #0
beq fail
mov r4, #0
b continue
fail:
mov r4, #1
continue:
mov r0, #5
ldr r3, audio_play
bl call_via_r3
cmp r4, #0
beq effect
no_effect:
ldr r0, no_effect_str
mov r1, #1
ldr r3, item_menu_string
bl call_via_r3
mov r0, #2
ldr r3, bgid_mark_for_sync
bl call_via_r3
ldr r1, tasks
lsl r0, r5, #2
add r0, r0, r5
lsl r0, r0, #3
add r0, r0, r1
mov r1, r6
str r1, [r0]
b end
effect:
mov r0, r5
ldr r3, item_use_animation
bl call_via_r3
ldr r1, item_function_ptr
ldr r0, =(rom + abilitycapsule_use)
str r0, [r1]
b end
abilitycapsule_use:
push {r4-r7,lr}
lsl r0, r0, #0x18
lsr r5, r0, #0x18
get_selected_pokemon_again:
ldr r0, brm
ldrb r0, [r0, #9]
mov r1, #pokemon_length
mul r0, r1
ldr r1, party_player
add r4, r0, r1
get_ability_id_again:
mov r0, r4
mov r1, #req_ability
ldr r3, get_attr
bl call_via_r3
lsl r0, r0, #0x18
lsr r0, r0, #0x18
invert_ability_id:
mov r1, #1
eor r0, r1
ldr r2, var_800D
strb r0, [r2]
set_new_ability_id:
mov r0, r4
mov r1, #req_ability
ldr r3, set_attr
bl call_via_r3
set_item_effectiveness:
mov r0, #1
ldr r1, item_effectiveness
strb r0, [r1]
remove_item:
ldr r0, var_800E
ldrh r0, [r0]
mov r1, #1
ldr r3, bag_remove_item
bl call_via_r3
buffer_nickname:
mov r0, r4
ldr r1, fcode_buffer2
ldr r3, buffer_pkmn_nick
bl call_via_r3
buffer_ability_name:
mov r0, r4
ldr r1, fcode_buffer3
ldr r3, =(rom + buffer_ability)
bl call_via_r3
construct_string:
ldr r4, displayed_string
mov r0, r4
ldr r1, =(rom + abilitycapsule_str)
ldr r3, fdecoder
bl call_via_r3
print_string:
mov r0, r4
mov r1, #1
ldr r3, item_menu_string
bl call_via_r3
display_box:
mov r0, #2
ldr r3, bgid_mark_for_sync
bl call_via_r3
add_callback_task:
ldr r1, tasks
mov r2, r5
lsl r0, r2, #2
add r0, r0, r5
lsl r0, r0, #3
add r0, r0, r1
ldr r1, item_menu_callback
str r1, [r0]
b end
buffer_ability:
push {r4-r7,lr}
mov r4, r0
mov r5, r1
get_species_again:
mov r1, #req_species2
ldr r3, get_attr
bl call_via_r3
lsl r0, r0, #0x10
lsr r6, r0, #0x10
get_ability_bit:
mov r0, r4
mov r1, #req_ability
ldr r3, get_attr
bl call_via_r3
lsl r0, r0, #0x18
lsr r1, r0, #0x18
get_ability_id:
mov r0, r6
mov r2, #base_stats_length
mul r0, r2
ldr r2, base_stats_ptr
ldr r2, [r2]
add r2, r0
add r2, #ability1
ldrb r0, [r2, r1]
get_ability_name_string:
mov r1, #ability_name_length
mul r0, r1
ldr r1, ability_names_ptr
ldr r1, [r1]
add r1, r0
mov r0, r5
ldr r3, strcpy_xFF_terminated
bl call_via_r3
end:
pop {r4-r7}
pop {r3}
call_via_r3:
bx r3
abilitycapsule_str:
.byte 0x00
.byte 0xFD, 0x02, 0xB4, 0xE7, 0x00, 0xBB, 0xD6, 0xDD, 0xE0, 0xDD, 0xE8, 0xED, 0xFE, 0xD7, 0xDC, 0xD5, 0xE2, 0xDB, 0xD9, 0xD8, 0x00, 0xE8, 0xE3, 0x00, 0xFD, 0x03, 0xAB, 0xFC, 0x09, 0xFF
@ "[PKMN]'s Ability[NEWLINE]changed to [ABILITY]![WAITKEYPRESS]"
.align 2
fcode_buffer2: .word 0x02021CD0
fcode_buffer3: .word 0x02021CF0
displayed_string: .word 0x02021D18
party_player: .word 0x02024284
var_800D: .word 0x020370D0
var_800E: .word 0x0203AD30
brm: .word 0x0203B0A0
item_effectiveness: .word 0x0203B0C0
tasks: .word 0x03005090
item_function_ptr: .word 0x03005E98
base_stats_ptr: .word 0x080001BC
ability_names_ptr: .word 0x080001C0
strcpy_xFF_terminated: .word 0x08008D84|1
fdecoder: .word 0x08008FCC|1
get_attr: .word 0x0803FBE8|1
set_attr: .word 0x0804037C|1
audio_play: .word 0x080722CC|1
bag_remove_item: .word 0x0809A1D8|1
item_consume_maybe: .word 0x080A16D0|1
bgid_mark_for_sync: .word 0x080F67A4|1
buffer_pkmn_nick: .word 0x081202E0|1
item_menu_string: .word 0x081202F8|1
item_use_animation: .word 0x08124DC0|1
item_menu_callback: .word 0x081255BC|1
no_effect_str: .word 0x084169DC @ "It won't have any effect."
This implementation is not identical to how it is in the official games, as it doesn't have a further prompt asking the player to confirm when a Pokémon is selected.
It will fail ("It won't have any effect") if used on a Pokémon whose base stats has two identical abilities, or if one of the abilities are ID 0x0.
EDIT 2019/05/29: There was a bug at line 72 where 0 was being stored at a garbage address. It also now checks the species using req_species2 to prevent the Ability Capsule (potentially) working on Eggs, and adds a relevant check (as it turns out, this is technically redundant as the Use menu will not come up for Eggs to begin with, but it's better to redundant than sorry). If you used this routine before this edit's date, I'd recommend updating the ASM to make sure! Thanks to BreadCrumbs/Squeetz for pointing these issues out!
|