- 5
- Posts
- 6
- Years
- Seen Oct 11, 2022
I would like to ask if anyone can do a routine wich maximizes the IV's/EV's stats of my team's first pokemon
Last edited:
Getting IVs and EVs
The routines are very similar so I ended up just combining the two routines into one. Basically given a slot number of a Pokemon in 0x8004, the routines will return the EVs or IVs, respectively, into the vars 0x8005-0x800A with 0x8003 as the IV/EV switch.
How to insert:
Compile and insert the following routine into free space:
Spoiler:
Code:.text .align 2 .thumb .thumb_func main: push {r0-r5, lr} mov r4, #0x0 loop: cmp r4, #0x6 bhi end ldr r0, =(0x20370C0) @var 0x8004 = slot number ldrh r0, [r0] mov r1, #0x64 mul r1, r1, r0 ldr r0, =(0x2024284) add r0, r0, r1 ldr r3, =(0x20370BE) ldrh r3, [r3] cmp r3, #0x0 beq EVs add r1, r1, #0x27 @IV b continue EVs: add r1, r1, #0x1A continue: ldr r2, =(0x803FBE8 +1) bl linker mov r1, r0 @get var ldr r2, =(0x20370C2) @using vars 0x8005-0x800A lsl r0, r4, #0x1 add r2, r2, r0 strh r1, [r2] @store IV in var add r4, r4, #0x1 b loop linker: bx r2 end: pop {r0-r5, pc} .align 2
Usage:
setvar 0x8003 0x[anything except 0 = IV, 0 = EV]
setvar 0x8004 0x[slot number 0 to 5]
callasm 0x[this routine +1]
The variables will be:
0x8005: HP IV/EV
0x8006: Atk IV/EV
0x8007: Def IV/EV
0x8008: Spd IV/EV
0x8009: S.atk IV/EV
0x800A: S.def IV/EV
.text
.align 2
.thumb
.thumb_func
main:
push {r0-r5, lr} @loads registers r0-r5
mov r4, #0x0 @copies 0(aka 0x0) to register r4
loop:
cmp r4, #0x6 @finds the difference between the value in register r4 and the number 6(aka 0x6)
bhi end @I believe this is roughly equivalent to if(r4==6) goto end
ldr r0, =(0x20370C0) @var 0x8004 = slot number
ldrh r0, [r0] @loads register r0 at the offset stored in r0 (set right before this to 0x20370C0) ONLY A HALFWORD IS LOADED
mov r1, #0x64 @copies 100(aka 0x64) to register r1
mul r1, r1, r0 @multiplies r0(the value at 0x20370C0/var 0x8004) by r1(100 aka 0x64) and stores the result in r1
ldr r0, =(0x2024284) @loads register r0 at offset 0x2024284, upon inspection of this memory location, it is where the pokemon in your party are stored
add r0, r0, r1 @adds the value in r1 to the offset r0 is loaded at, getting the party member location
ldr r3, =(0x20370BE) @loads register r3 at 0x20370BE, the location of var 0x8003
ldrh r3, [r3] @reads the halfword at 0x20370BE and writes it to r3, aka sets r3 equal to var 8003
cmp r3, #0x0 @checks if r3(at this point effectively var 0x8003) is 0
beq EVs @goes to evs if var 0x8003 is 0
add r1, r1, #0x27 @IV r1 at this point is equal to the offset at whatever pokemon party number you want, adding 0x27 sets the offset to the ivs section of the pokemon's data
b continue @goes to continue
EVs:
add r1, r1, #0x1A
continue:
ldr r2, =(0x803FBE8 +1) @I think this sets register r2 equal to the offset 0x803FBE9
bl linker @branches to linker and stores the current offset in the link register(r14)
mov r1, r0 @copies r0 to r1
@get var
ldr r2, =(0x20370C2) @using vars 0x8005-0x800A
lsl r0, r4, #0x1 @I think shifts r0 to the left(subtracts 1 from r0)
add r2, r2, r0
strh r1, [r2] @store IV in var
add r4, r4, #0x1 @adds 1 to the value of r4, allowing the assembly to terminate once it runs 6 times
b loop @goes to loop
linker:
bx r2 @branches to the offset stored in r2
end:
pop {r0-r5, pc}
.align 2
//---------------
#org 0x2514D0
lock
faceplayer
msgbox 0x82515D0 MSG_YESNO //"Would you like to know the\npotent..."
compare LASTRESULT 0x0
if 0x1 goto 0x8251570
setvar 0x8003 0x1
setvar 0x8004 0x0
callasm 0x8251C51
buffernumber 0x0 0x8005
buffernumber 0x1 0x8006
msgbox 0x8251610 MSG_NORMAL //"HP: [buffer1] IVs\nAttack: [buffer..."
buffernumber 0x0 0x8007
buffernumber 0x1 0x8008
msgbox 0x8251630 MSG_NORMAL //"Defence: [buffer1] IVs\nSpeed: [bu..."
buffernumber 0x0 0x8009
buffernumber 0x1 0x800A
msgbox 0x8251650 MSG_NORMAL //"Special Attack: [buffer1] IVs\nSpe..."
end
//---------------
#org 0x251570
//---------
// Strings
//---------
#org 0x2515D0
= Would you like to know the\npotential of your Pokémon?
#org 0x251610
= HP: [buffer1] IVs\nAttack: [buffer2] IVs
#org 0x251630
= Defence: [buffer1] IVs\nSpeed: [buffer2] IVs
#org 0x251650
= Special Attack: [buffer1] IVs\nSpecial Defence: [buffer2] IVs.
0x08251C50 3F B5 00 24 05 2C 17 D8 0C 48 00 88 64 21 41 43
0x08251C60 0B 48 40 18 0B 4B 1B 88 00 2B 01 D0 27 31 00 E0
0x08251C70 1A 31 09 4A 00 F0 07 F8 01 1C 08 4A 60 00 12 18
0x08251C80 11 80 01 34 E6 E7 10 47 3F BD C0 46 C0 70 03 02
0x08251C90 86 42 02 02 BE 70 03 02 E9 FB 03 08 C4 70 03 02
0x020370BD 00 01 00 00 00 19 00 19 00 19 00 19 00 19 00 19
0x020370CD 00 19 00 01 00 02 00 04 00 00 00 00 00 FF 00 01
Getting IVs and EVs
The routines are very similar so I ended up just combining the two routines into one. Basically given a slot number of a Pokemon in 0x8004, the routines will return the EVs or IVs, respectively, into the vars 0x8005-0x800A with 0x8003 as the IV/EV switch.
How to insert:
Compile and insert the following routine into free space:
Spoiler:
Code:.text .align 2 .thumb .thumb_func main: push {r0-r5, lr} mov r4, #0x0 loop: cmp r4, #0x6 bhi end ldr r0, =(0x20370C0) @var 0x8004 = slot number ldrh r0, [r0] mov r1, #0x64 mul r1, r1, r0 ldr r0, =(0x2024284) add r0, r0, r1 ldr r3, =(0x20370BE) ldrh r3, [r3] cmp r3, #0x0 beq EVs add r1, r1, #0x27 @IV b continue EVs: add r1, r1, #0x1A continue: ldr r2, =(0x803FBE8 +1) bl linker mov r1, r0 @get var ldr r2, =(0x20370C2) @using vars 0x8005-0x800A lsl r0, r4, #0x1 add r2, r2, r0 strh r1, [r2] @store IV in var add r4, r4, #0x1 b loop linker: bx r2 end: pop {r0-r5, pc} .align 2
Usage:
setvar 0x8003 0x[anything except 0 = IV, 0 = EV]
setvar 0x8004 0x[slot number 0 to 5]
callasm 0x[this routine +1]
The variables will be:
0x8005: HP IV/EV
0x8006: Atk IV/EV
0x8007: Def IV/EV
0x8008: Spd IV/EV
0x8009: S.atk IV/EV
0x800A: S.def IV/EV
.text
.align 2
.thumb
.thumb_func
main:
push {r0-r5, lr} @loads registers r0-r5
mov r4, #0x0 @copies 0(aka 0x0) to register r4
loop:
cmp r4, #0x6 @finds the difference between the value in register r4 and the number 6(aka 0x6)
bhi end @I believe this is roughly equivalent to if(r4==6) goto end
ldr r0, =(0x20370C0) @var 0x8004 = slot number
ldrh r0, [r0] @loads register r0 at the offset stored in r0 (set right before this to 0x20370C0) ONLY A HALFWORD IS LOADED
mov r1, #0x64 @copies 100(aka 0x64) to register r1
mul r1, r1, r0 @multiplies r0(the value at 0x20370C0/var 0x8004) by r1(100 aka 0x64) and stores the result in r1
ldr r0, =(0x2024284) @loads register r0 at offset 0x2024284, upon inspection of this memory location, it is where the pokemon in your party are stored
add r0, r0, r1 @adds the value in r1 to the offset r0 is loaded at, getting the party member location
ldr r3, =(0x20370BE) @loads register r3 at 0x20370BE, the location of var 0x8003
ldrh r3, [r3] @reads the halfword at 0x20370BE and writes it to r3, aka sets r3 equal to var 8003
cmp r3, #0x0 @checks if r3(at this point effectively var 0x8003) is 0
beq EVs @goes to evs if var 0x8003 is 0
add r1, r1, #0x27 @IV r1 at this point is equal to the offset at whatever pokemon party number you want, adding 0x27 sets the offset to the ivs section of the pokemon's data
b continue @goes to continue
EVs:
add r1, r1, #0x1A
continue:
ldr r2, =(0x803FBE8 +1) @I think this sets register r2 equal to the offset 0x803FBE9
bl linker @branches to linker and stores the current offset in the link register(r14)
mov r1, r0 @copies r0 to r1
@get var
ldr r2, =(0x20370C2) @using vars 0x8005-0x800A
lsl r0, r4, #0x1 @I think shifts r0 to the left(subtracts 1 from r0)
add r2, r2, r0
strh r1, [r2] @store IV in var
add r4, r4, #0x1 @adds 1 to the value of r4, allowing the assembly to terminate once it runs 6 times
b loop @goes to loop
linker:
bx r2 @branches to the offset stored in r2
end:
pop {r0-r5, pc}
.align 2
0x08251C50 3F B5 00 24 05 2C 17 D8 0C 48 00 88 64 21 41 43
0x08251C60 0B 48 40 18 0B 4B 1B 88 00 2B 01 D0 27 31 00 E0
0x08251C70 1A 31 09 4A 00 F0 07 F8 01 1C 08 4A 60 00 12 18
0x08251C80 11 80 01 34 E6 E7 10 47 3F BD C0 46 C0 70 03 02
0x08251C90 86 42 02 02 BE 70 03 02 E9 FB 03 08 C4 70 03 02
//---------------
#org 0x2514D0
lock
faceplayer
msgbox 0x82515D0 MSG_YESNO //"Would you like to know the\npotent..."
compare LASTRESULT 0x0
if 0x1 goto 0x8251570
setvar 0x8003 0x1
setvar 0x8004 0x0
callasm 0x8251C51
buffernumber 0x0 0x8005
buffernumber 0x1 0x8006
msgbox 0x8251610 MSG_NORMAL //"HP: [buffer1] IVs\nAttack: [buffer..."
buffernumber 0x0 0x8007
buffernumber 0x1 0x8008
msgbox 0x8251630 MSG_NORMAL //"Defence: [buffer1] IVs\nSpeed: [bu..."
buffernumber 0x0 0x8009
buffernumber 0x1 0x800A
msgbox 0x8251650 MSG_NORMAL //"Special Attack: [buffer1] IVs\nSpe..."
end
//---------------
#org 0x251570
//---------
// Strings
//---------
#org 0x2515D0
= Would you like to know the\npotential of your Pokémon?
#org 0x251610
= HP: [buffer1] IVs\nAttack: [buffer2] IVs
#org 0x251630
= Defence: [buffer1] IVs\nSpeed: [buffer2] IVs
#org 0x251650
= Special Attack: [buffer1] IVs\nSpecial Defence: [buffer2] IVs.
0x020370BD 00 01 00 00 00 19 00 19 00 19 00 19 00 19 00 19
0x020370CD 00 19 00 01 00 02 00 04 00 00 00 00 00 FF 00 01
This line of code I assume gets the HP IV, however, it will always be 0x27, and therefore not get any subsequent IVs.add r1, r1, #0x27 @IV
If the assembler says the above line is invalid, try:add r1, r4, #0x27
Hopefully that does the trick.add r1, r1, r4
add r1, r1, #0x27
Hi, first of all I'd like to apologize for the mistake. I haven't tested that code, and it's in assembly so a semantic error here and there tend to be around for some of them. A quick skim through it, it does seem like there is a minor error.
Code:add r1, r1, #0x27 @IV
This line of code I assume gets the HP IV, however, it will always be 0x27, and therefore not get any subsequent IVs.
That line should be replaced by:
Code:add r1, r4, #0x27
If the assembler says the above line is invalid, try:
Code:add r1, r1, r4 add r1, r1, #0x27
Hopefully that does the trick.
add r1, r1, r4
add r1, r1, #0x27
.text
.align 2
.thumb
.thumb_func
.global LoadMapMusic
/*
load song regardless of map default
hook at 55D70 via r0
aka 00 48 00 47 xx+1 xx xx 08
*/
.equ MUSIC_VAR, 0x4XXX @some free variable in save block
Main:
push {r1-r3}
ldr r0, .var
bl GetVarVal
pop {r1-r3}
cmp r0, #0x0
beq LoadFromMapHeader
pop {pc}
LoadFromMapHeader:
ldrb r0, [r1]
ldr r2, =(0x08055D78 +1)
bx r2
GetVarVal:
ldr r2, =(0x0806e568 +1)
bx r2
.align 2
.var: .word MUSIC_VAR
Pokemon LoathingFriendship Evolution:
Code:.text .align 2 .thumb .thumb_func .global loathFriendship main: push {r0-r7} add r0, r6, r7 lsl r0, r0, #0x3 add r0, r2, r0 add r3, r0, r3 ldrb r2, [r3, #0x2] mov r0, r8 bl decrypt mov r11, r0 pop {r0-r7} mov r1, r11 cmp r1, #0x10 blo max_lowhappy b exit max_lowhappy: mov r10, r3 pop {r0-r7} mov r1, r10 ldr r0, evolution_loc bx r0 exit: pop {r0-r7} ldr r0, no_evo bx r0 decrypt: push {r0-r7} mov r1, #0x20 ldr r2, decryptpoke bx r2 .align 2 no_evo: .word 0x08043111 evolution_loc: .word 0x0804310D decryptpoke: .word 0x0803FBE9
The Pokemons happiness must be between 0-10 to trigger the evolution.
To insert follow instructions in this thread.
I hope some find this useful.
I thought about trade evolution basically being just this.
A Pokemon so depressed it evolves...
@ the whole headbutt tree system
@ it implements jpan's behavior byte hack as well as the headbutt system in the space that the old routine took up. when he said it was overly inefficient, he meant it!
@ compile this and then go to the offset 0x806D1F0 in the resulting .bin output. copy everything there until the next block of zeros. paste it to offset 0x6D1F0 in your rom.
@ there are various parameters you can play around with. most of these are primarily the tables halfway through this.
@ i tried to comment it throughout!
@ now, in amap, there's the one field that is primarily used in wild encounter spots. it's 2 when wild grass and 4 when surfing on water encounters.
@ because we won't be walking on the headbutt tree, we can use this field to our advantage.
@ cur_mapdata_block_get_field_at(s16 x, s16 y, u8 field) references a bunch of other routines, but eventually, it boils down to a bunch of preset fields or'd with the header info.
@ the first is 0x1FF, or the 9 least significant bits. this is the behavior byte itself.
@ the second is 0x3E00, or the next 5 bits in terms of significance. this allows for 2^5 (32) bits for use for our table.
@ note that the entry entered in this table should be multiplied by 2 because of the way that amap (incorrectly) displays the data. this allows the code below to reference it properly.
@ screens are given in the post.
.text
.align 2
.thumb
.thumb_func
.org 0x08044EC8
@ returns r0 = pseudorandom number
random:
.hword 0x4A04
.org 0x08058F48
@ returns r0 = (metatile behavior long or blockinfo_bit_masks[field]) >> blockinfo_bit_right_shift_num[field]
cur_mapdata_block_get_field_at: @ (s16 x, s16 y, u8 field)
push {r4, lr}
.org 0x0806CE38
@ writes the next pos and height to the pointer given in r0
player_get_next_pos_and_height: @ (*[s16 x, s16 y, u8 height])
push {r4, r5, lr}
.org 0x0806D1F0
@ this assembly routine is a modded version of jpan's behavior byte hack that decreases space required for the table while slightly increasing what's needed for the code
onpress_a_get_script_tile:
lsl r1, r1, #0x18
lsr r0, r1, #0x18
cmp r0, #0x80
blt _not_governed @ we don't care about entries less than h80
sub r0, r0, #0x80
lsl r0, r0, #0x2
ldr r1, behavior_table_ref @ load the table pointer here
add r0, r0, r1
ldr r0, [r0] @ load the script pointer here
b _return_loc
_not_governed:
mov r0, #0
_return_loc:
mov pc, lr
.align 2
behavior_table_ref:
.word behavior_table
behavior_table: @ for the playerfacing behaviors
.word 0
.word 0x081A7606 @ bookshelf
.word 0x081A760F @ market shelf
.word 0x081A6955 @ pc
.word 0
.word 0x081A6C32 @ town map
.word 0x081A764E @ tv
.word 0x081A76E7 @ pokemon center
.word 0x081A76DE @ pokemon mart
.word 0x081A7657 @ cabinet
.word 0x081A7660 @ cooking smells great!
.word 0x081A7669 @ dresser
.word 0x081A7672 @ snacks
.word 0x081BBFD8 @ wireless
.word 0x081BB8A7 @ not available
.word 0x081A7702 @ questionnaire
@ 0x90
.word 0x081A7618 @ fridge
.word 0x081A76F0 @ indigo plateau
.word 0x081A76F9 @ indigo plateau 2
.word 0x081A763C @ blueprint
.word 0x081A767B @ pokemon pics
.word 0x081A7684 @ complex machine. better not touch it!
.word 0x081A768D @ telephone
.word 0x081A762A @ complex numbers
.word 0x081A7696 @ ads
.word 0x081A769F @ tasty food
.word 0x081A76A8 @ trash can
.word 0x081A76B1 @ cup
.word 0
.word 0
.word 0x081A76CC @ lights
.word 0x081A76D5 @ tools
@ 0xA0
.word 0x081A7633 @ random machine
.word 0x081A7621 @ consoles
.word 0x081A7645 @ robbery
.word 0x081C549C @ timer
@ now, i personally added headbutt_script here, but it can go in any one of the zeros above. just make sure that you note the entry correctly!
.word headbutt_script @ headbutt tree behavior byte (0xA4)
.equ table_amount, 3
.equ terminate, 0xFE @ only rule here is that it isn't a pokemon you want to appear in a tree. if you wanted to use pokemon index 254, go for it i guess, but change this
.equ var_begin, 0x020270B8
headbutt_tree:
push {r4-r6, lr}
ldr r0, var_8000 @ position_ptr
@ the below branch will store the position in front of the player's x in var_8000, y in var_8001, and the height in the most significant byte of var_8002
@ this is because it writes to the pointer in r0
bl player_get_next_pos_and_height
ldr r4, var_8000
ldrh r0, [r4] @ x
ldrh r1, [r4, #2] @ y
mov r2, #1 @ field id
@ the field entry 1 is 5 bits of the second behavior byte thing in advancemap. this means we can have up to 32 headbutt tables denoted by even numbers from 0-64 in amap.
@ this is explained further above
bl cur_mapdata_block_get_field_at
lsl r0, r0, #0x18
lsr r4, r0, #0x18 @ r4 = table number
@ part below catches invalid tables and prevents them from working
cmp r4, #table_amount
bge _nothingfell
ldr r0, tableoftables_ref
lsl r1, r4, #2 @ r1 = table number * 4
ldr r4, [r0, r1] @ r4 = table pointer to the relevant one
@ below, generate a random number less than 100 to emulate chance
bl random @ r0 = random()
mov r1, #100
bl __umodsi3 @ r0 = r0 % 100
mov r5, #0 @ set up the loop counter
ldr r6, terminate_byte
@ at this point, r0 = random() % 100, r4 = table pointer, r5 = loop counter, r6 = terminator
_loop:
mov r1, #7 @ 7 being the size of each entry
mul r1, r5, r1
add r3, r4, r1 @ store the current entry's pointer in r3
ldrh r2, [r3] @ seems to be very finicky at times, loading random values from the surroundings?
lsl r2, r2, #0x10
lsr r2, r2, #0x10
cmp r2, r6
beq _nothingfell
ldrb r2, [r3, #6] @ load the chance of this entry into r2
cmp r0, r2
ble _somethingfell
add r5, r5, #1 @ increment the loop counter
b _loop
_somethingfell:
mov r4, r3
@ at this point, r4 is the table entry we care about. don't touch it
bl random @ generate a new random number
@ r0 = random()
ldrb r2, [r4, #2] @ load the lower level
ldrb r1, [r4, #3] @ load the higher level
sub r1, r1, r2 @ subtract the lower level from the higher level, store it as the parameter for modulo
bl __umodsi3
@ r0 = random() % (highlevel - lowlevel)
ldrb r2, [r4, #2] @ load the lower level again bc umodsi ****in w the value
add r1, r0, r2 @ level parameter
ldrh r0, [r4] @ species parameter
ldrh r2, [r4, #4] @ item parameter
bl create_scripted_mon
ldr r0, var_8000
mov r1, #0
strh r1, [r0, #0x18] @ store 0 into var_800D
b _end
_nothingfell:
ldr r0, var_8000
mov r1, #0xFF
strh r1, [r0, #0x18] @ store 0xFF into var_800D
_end:
pop {r4-r6}
pop {r1} @ this is the return method used by many of the script stuff so
bx r1
.align 2
@ used to store the position struct
var_8000:
.word (var_begin + (0x8000 * 2))
terminate_byte:
.word terminate
tableoftables_ref:
.word tableoftables
@ note that, in order to expand the tables, you need to add another entry here for each of the tables.
tableoftables:
.word lowencounter
.word medencounter
.word highencounter
@ bunch of defines to make the below better readable
.equ caterpie, 10
.equ metapod, 11
.equ butterfree, 12
.equ weedle, 13
.equ kakuna, 14
.equ beedrill, 15
.equ exeggcute, 102
.equ exeggutor, 103
.equ tangela, 114
.equ pinsir, 127
.equ snorlax, 143
.equ pineco, 204
.equ heracross, 214
@ a sample item
.equ brightpowder, 179
@.org 0x08XXXXXX @ optional repoint of the tables below in case you want a higher amount of pokemon.
@ here's the table format:
@ [2 bytes - species] [1 byte - lower level] [1 byte - higher level] [2 bytes] - item it can hold] [1 byte - chance it will appear]
@ the chance byte at the very end is subtracted from the previous entry in the tables in order to get that specific entry's chance of showing up.
@ sample entries are below:
lowencounter:
.hword weedle @ species
.byte 5 @ lower level
.byte 7 @ higher level
.hword brightpowder @ item
.byte 20 @ chance
.hword kakuna @ species
.byte 12 @ lower level
.byte 15 @ higher level
.hword 0 @ no item
.byte 40 @ chance: 40 - 20 (weedle's entry above) = 20
.hword beedrill
.byte 60
.byte 70
.hword brightpowder
.byte 100 @ chance: 100 - 40 (kakuna's entry above) = 60
.hword terminate
@ this table represents an instance where we don't have to get a pokemon 100% of the time. the code handles it!
medencounter:
.hword caterpie
.byte 5
.byte 7
.hword brightpowder
.byte 35
.hword metapod
.byte 12
.byte 15
.hword 0
.byte 45
.hword butterfree
.byte 60
.byte 70
.hword brightpowder
.byte 50
.hword terminate
@ 50% of the time, a pokemon won't fall.
highencounter:
.hword exeggcute
.byte 10
.byte 12
.hword 0
.byte 40
.hword tangela
.byte 20
.byte 25
.hword 0
.byte 60
.hword heracross
.byte 17
.byte 22
.hword brightpowder
.byte 90
.hword snorlax
.byte 50
.byte 60
.hword brightpowder
.byte 100
.hword terminate
@ optional insert of the script. there's still about hB0 bytes left over after the script is inserted, allowing for about 19 EXTRA mon entries (on top of the 10 there already)
@ repoint if so desired. the assembly will take care of it!
@.org 0x08XXXXXX
.equ headbutt, 0x1D
.equ lastresult, 0x800D
headbutt_script: @ this is included here for the dynamic compiling so that you don't have to care. feel free to edit it though
.byte 0x6A
.byte 0x7C
.hword headbutt
.byte 0x21
.hword lastresult
.hword 0x06
.byte 0x06
.byte 0x01
.word no_headbutt_in_party
.byte 0x9D
.byte 0x00
.hword lastresult
.byte 0x7F
.byte 0x00
.hword lastresult
.byte 0x82
.byte 0x01
.hword headbutt
.byte 0x0F
.byte 0x00
.word text_headbutt_in_party
.byte 0x09
.byte 0x05
.byte 0x21
.hword lastresult
.hword 0x00
.byte 0x06
.byte 0x01
.word said_no
.byte 0x0F
.byte 0x00
.word text_poke_used_atk
.byte 0x09
.byte 0x04
.byte 0x68
.byte 0x9C
.hword 0x2
.byte 0x27
@ the callasm is here
.byte 0x23
.word headbutt_tree + 1
@ this comparison here prevents the dowildbattle from happening if lastresult is 0xFF (the chance was higher than the highest chance defined in the table)
@ lastresult's modification is in the assembly above
.byte 0x21
.hword lastresult
.hword 0xFF
.byte 0x06
.byte 0x01
.word tree_was_undisturbed
.byte 0x0F
.byte 0x00
.word text_exclamation
.byte 0x09
.byte 0x06
.byte 0xB7
.byte 0x6C
.byte 0x02
.byte 0xFF
no_headbutt_in_party:
.byte 0x0F
.byte 0x00
.word text_cant_headbutt
.byte 0x09
.byte 0x06
.byte 0x6C
.byte 0x02
.byte 0xFF
said_no: @ closes the textbox
.byte 0x68
.byte 0x6C
.byte 0x02
.byte 0xFF
tree_was_undisturbed:
.byte 0x0F
.byte 0x00
.word text_tree_undisturbed
.byte 0x09
.byte 0x06
.byte 0x6C
.byte 0x02
.byte 0xFF
text_headbutt_in_party: @ This tree can be headbutted! Would you like to use headbutt?
.byte 0xCE
.byte 0xDC
.byte 0xDD
.byte 0xE7
.byte 0x00
.byte 0xE8
.byte 0xE6
.byte 0xD9
.byte 0xD9
.byte 0x00
.byte 0xD7
.byte 0xD5
.byte 0xE2
.byte 0x00
.byte 0xD6
.byte 0xD9
.byte 0x00
.byte 0xDC
.byte 0xD9
.byte 0xD5
.byte 0xD8
.byte 0xD6
.byte 0xE9
.byte 0xE8
.byte 0xE8
.byte 0xD9
.byte 0xD8
.byte 0xAB
.byte 0xFE
.byte 0xD1
.byte 0xE3
.byte 0xE9
.byte 0xE0
.byte 0xD8
.byte 0x00
.byte 0xED
.byte 0xE3
.byte 0xE9
.byte 0x00
.byte 0xE0
.byte 0xDD
.byte 0xDF
.byte 0xD9
.byte 0x00
.byte 0xE8
.byte 0xE3
.byte 0x00
.byte 0xE9
.byte 0xE7
.byte 0xD9
.byte 0x00
.byte 0xC2
.byte 0xD9
.byte 0xD5
.byte 0xD8
.byte 0xD6
.byte 0xE9
.byte 0xE8
.byte 0xE8
.byte 0xAC
.byte 0xFF
.byte 0x00
.byte 0xFF
text_poke_used_atk: @ [string1] used [string2]!
.byte 0xFD
.byte 0x02
.byte 0x00
.byte 0xE9
.byte 0xE7
.byte 0xD9
.byte 0xD8
.byte 0x00
.byte 0xFD
.byte 0x03
.byte 0xAB
.byte 0xFF
.byte 0x00
.byte 0xFF
text_exclamation: @ Whoa!
.byte 0xD1
.byte 0xDC
.byte 0xE3
.byte 0xD5
.byte 0xAB
.byte 0xFF
.byte 0x00
.byte 0xFF
text_cant_headbutt: @ This tree looks like Pokémon can live in it.
.byte 0xCE
.byte 0xDC
.byte 0xDD
.byte 0xE7
.byte 0x00
.byte 0xE8
.byte 0xE6
.byte 0xD9
.byte 0xD9
.byte 0x00
.byte 0xE0
.byte 0xE3
.byte 0xE3
.byte 0xDF
.byte 0xE7
.byte 0x00
.byte 0xE0
.byte 0xDD
.byte 0xDF
.byte 0xD9
.byte 0x00
.byte 0xCA
.byte 0xE3
.byte 0xDF
.byte 0x1B
.byte 0xE1
.byte 0xE3
.byte 0xE2
.byte 0x00
.byte 0xD7
.byte 0xD5
.byte 0xE2
.byte 0xFE
.byte 0xE0
.byte 0xDD
.byte 0xEA
.byte 0xD9
.byte 0x00
.byte 0xDD
.byte 0xE2
.byte 0x00
.byte 0xDD
.byte 0xE8
.byte 0xAD
.byte 0xFF
.byte 0x00
.byte 0xFF
text_tree_undisturbed: @ But the tree remained undisturbed...
.byte 0xBC
.byte 0xE9
.byte 0xE8
.byte 0x00
.byte 0xE8
.byte 0xDC
.byte 0xD9
.byte 0x00
.byte 0xE8
.byte 0xE6
.byte 0xD9
.byte 0xD9
.byte 0x00
.byte 0xE6
.byte 0xD9
.byte 0xE1
.byte 0xD5
.byte 0xDD
.byte 0xE2
.byte 0xD9
.byte 0xD8
.byte 0xFE
.byte 0xE9
.byte 0xE2
.byte 0xD8
.byte 0xDD
.byte 0xE7
.byte 0xE8
.byte 0xE9
.byte 0xE6
.byte 0xD6
.byte 0xD9
.byte 0xD8
.byte 0xAD
.byte 0xAD
.byte 0xAD
.byte 0xFF
.byte 0x00
.org 0x080A029C
create_scripted_mon: @ (u16 species, u8 level, u16 item)
push {r4-r7, lr}
.org 0x081E4684
@ returns r0 = dividend % divisor
__umodsi3: @ (u16 dividend, u16 divisor)
cmp r1, #0
.THUMB
.ALIGN 2
PUSH {R0-R7}
MOV R0, R5
PUSH {R1-R7}
BL GET_NATURE
POP {R1-R7}
LDR R1, = 0x081FD070
MOV R2, #5
MUL R0, R2
ADD R6, R0, R1
MOV R4, #1
LOOP:
MOV R0, R5
MOV R2, PC
ADD R2, R2, #0x74
LDRB R1, [R2, R4]
MOV R7, R1
GET_DATA:
PUSH {R1-R7}
BL GET_MON_DATA
POP {R1-R7}
MOV R1, R0
LDR R0, .BUFFER
PUSH {R0-R7}
BL INT_TO_DEC
POP {R0-R7}
MOV R0, R4
MOV R1, #3
PUSH {R1-R7}
BL MOD
POP {R1-R7}
LSL R3, R0, #1
ADD R3, R3, #7
LDR R0, .BUFFER
MOV R1, #0xF
MOV R2, #22
CMP R4, #3
BLT COLORED
ADD R2, R2, #9
COLORED:
SUB R7, R7, #0x3B
LDRB R7, [R6, R7]
CMP R7, #0
BEQ PRINT_STR
CMP R7, #0xFF
BEQ LOWER
UPPER:
MOV R1, #0xB
B PRINT_STR
LOWER:
MOV R1, #0xC
PRINT_STR:
PUSH {R0-R7}
MOV R4, #36
BL PRINT
POP {R0-R7}
ADD R4, R4, #1
CMP R4, #6
BNE LOOP
POP {R0-R7}
LDR R0, = 0x080A0048+1
BX R0
GET_NATURE:
LDR R1, = 0x0803F464+1
BX R1
PRINT:
LDR R4, = 0x080A1F48+1
BX R4
GET_MON_DATA:
LDR R2, = 0x0803CB60+1
BX R2
INT_TO_DEC:
LDR R2, = 0x08006DDC+1
BX R2
MOD:
LDR R2, = 0x081E0F08+1
BX R2
.ALIGN 2
.BUFFER: .WORD 0x020231CC
.TABLE: .BYTE 0x3A
.BYTE 0x3B
.BYTE 0x3C
.BYTE 0x3E
.BYTE 0x3F
.BYTE 0x3D
.HWORD 0
Side Quest Menu [FR]
This hack converts the PC item storage menu into a side quest menu, or task list, etc.
Here is the repository, the compilation instructions are included in the readme. You will need to define your own parameters/ram in src/headers/defs.asm and create your own tables of string pointers for all of the quest names, descriptions, item images, and quest details. I kept the item image part to allow hackers to include items that might be symbolic of their quest, eg. a scroll or key or pokeball.
To use in a script (XSE)
Code:writebytetooffset 0x1 <your questflag ram in src/headers/defs.asm> fadescreen 0x1 callasm 0x80EBCD9 waitstate
Enjoy! And please let me know of any bugs.
Pokemon Storage (outside the PC)
Basically, in it's current state, this is effectively a storage system. It stores Pokemon and can be interacted with by the player to withdraw or deposit Pokemon. It's standalone from the PC storage system and is also much smaller (I've limited it to 6, but it can go upto 47). Some things that you will be able to do with this routine (and conjunction with some smaller ones) include:
- Extra Pokemon storage, you can possibly get a little over 1 box worth
- Flash back battles (complete swap between stored Pokemon and party Pokemon with capabilities to swap back)
- Carry a party of greater than 6 Pokemon (you can use the 2nd party for HM slaves, or whatever)
- Trade evolver guy! He will take your Pokemon and then when you come back it can be evolved! (silent evolution ftw!)
- Separate Daycare holding larger amount of Pokemon at once
I've tested it thoroughly and there doesn't seem to be any problems. I will be adding more and more features into this because I believe that it has the potential to become a very useful feature.
Currently planning to add:
- Party swap completely all in one go (this will actually be in the very near future..maybe today even :P)
- Partial swapping (believe it or not, this is hard because there's no graphical support)
- Suggest me more :D
Spoiler:How to insert:
Compile into free space the following routine:
Spoiler:
Code:.text .align 2 .thumb .thumb_func main: push {r0, r4, lr} ldr r0, .FROM ldr r1, [r0] cmp r1, #0x0 @party beq addParty addStorage: ldr r0, .STORAGE sub r0, r0, #0x1 ldrb r1, [r0] cmp r1, #0x6 @limit of storage. Change depending on your space situation beq end add r0, r0, #0x1 mov r2, #0x50 cmp r1, #0x0 beq skipline sub r1, r1, #0x1 mul r1, r1, r2 add r0, r0, r1 @destination ldr r1, =(0x20370C0) ldrb r1, [r1] ldr r2, .PARTY mov r3, #0x64 mul r1, r1, r3 add r1, r1, r2 @source mov r2, #0x50 @size ldr r3, =(0x8040B08 +1) @func bl linker @need to fix up Player's party slots ldr r1, =(0x20370C0) ldrb r1, [r1] cmp r1, #0x5 beq writeLastZero mov r2, #0x5 sub r2, r2, r1 mov r3, #0x64 mul r2, r2, r3 @size ldr r0, .PARTY mul r1, r1, r3 add r0, r0, r1 @dest add r3, r3, r0 mov r1, r3 @src ldr r3, =(0x8040B08 +1) @func bl linker writeLastZero: ldr r0, =(0x2024478) mov r1, #0x0 mov r2, #0x64 ldr r3, =(0x81E5ED8 +1) bl linker correctCounters: ldr r0, .STORAGE sub r0, r0, #0x1 ldrb r2, [r0] @pks in storage counter ldr r1, =(0x2024029) ldrb r3, [r1] @pks in party add r2, r2, #0x1 strb r2, [r0] sub r3, r3, #0x1 strb r3, [r1] b end addParty: ldr r0, =(0x2024029) ldrb r0, [r0] cmp r0, #0x6 beq end ldr r1, .PARTY mov r2, #0x64 mul r0, r0, r2 add r0, r0, r1 @destination mov r4, r0 ldr r3, .STORAGE ldr r1, =(0x20370C0) @var 0x8004 determines which slot of storage to take from ldrb r1, [r1] mov r2, #0x50 @size mul r1, r1, r2 add r1, r1, r3 @source ldr r3, =(0x8040B08 +1) @func bl linker @update stats mov r0, r4 ldr r3, =(0x803E47C +1) bl linker @adjust the storage ldr r0, =(0x20370C0) ldrb r0, [r0] cmp r0, #0x5 @storage limit minus 1 beq writeZero mov r2, #0x5 sub r2, r2, r0 mov r3, #0x50 mul r2, r2, r3 @size ldr r1, .STORAGE mul r0, r0, r3 add r0, r0, r1 @dest add r3, r3, r0 mov r1, r3 @src ldr r3, =(0x8040B08 +1) @func bl linker writeZero: ldr r0, .STORAGE mov r1, #0xC8 lsl r1, r1, #0x1 add r0, r0, r1 mov r1, #0x0 mov r2, #0x50 ldr r3, =(0x81E5ED8 +1) bl linker updateCounters: ldr r0, .STORAGE sub r0, r0, #0x1 ldrb r2, [r0] ldr r1, =(0x2024029) ldrb r3, [r1] sub r2, r2, #0x1 strb r2, [r0] add r3, r3, #0x1 strb r3, [r1] end: pop {r0-r4, pc} linker: bx r3 .align 2 .FROM: .word 0x20270B8 + (0x8000 *2) .STORAGE: .word 0x203C001 @storage location .PARTY: .word 0x2024284 @player's party
Here's a compiled version:
Code:11 B5 38 48 01 68 00 29 32 D0 37 48 01 38 01 78 06 29 65 D0 01 30 50 22 51 43 40 18 34 49 09 78 32 4A 64 23 59 43 89 18 50 22 32 4B 00 F0 59 F8 2F 49 09 78 05 29 0B D0 05 22 52 1A 64 23 5A 43 2A 48 59 43 40 18 1B 18 19 1C 2A 4B 00 F0 49 F8 29 48 00 21 64 22 29 4B 00 F0 43 F8 22 48 01 38 02 78 27 49 0B 78 01 32 02 70 01 3B 0B 70 37 E0 23 48 00 78 06 28 33 D0 1C 49 64 22 50 43 40 18 04 1C 19 4B 1A 49 09 78 50 22 51 43 C9 18 19 4B 00 F0 27 F8 20 1C 1B 4B 00 F0 23 F8 14 48 00 78 05 28 0B D0 05 22 12 1A 50 23 5A 43 0E 49 58 43 40 18 1B 18 19 1C 0F 4B 00 F0 13 F8 0A 48 C8 21 49 00 40 18 00 21 50 22 0C 4B 00 F0 0A F8 06 48 01 38 02 78 0A 49 0B 78 01 3A 02 70 01 33 0B 70 1F BD 18 47 B8 70 03 02 01 C0 03 02 84 42 02 02 C0 70 03 02 09 0B 04 08 78 44 02 02 D9 5E 1E 08 29 40 02 02 7D E4 03 08
Usage:
The routine itself manages party storage and storage extraction. What you need to do is determine which one it performs.
If you want to remove a Pokemon from the party and into storage, setvar 0x8000 anything but zero
if you want to remove a Pokemon from storage, setvar 0x8000 0x0
Which variable you used can be changed, as always, by editing the pointer at the bottom of the routine.
The next thing is variable 0x8004. I use this variable to determine the slot of extraction for both the Party and the Storage system.
If I wanted to take the 3rd Pokemon in the party and put it in storage:
setvar 0x8004 0x2
setvar 0x8000 0x1
callasm 0x[routine] +1
The reason for this is to support easy use of special 0x9F. Though you can use copyvar, I suppose.
Finally, the routine writes to RAM a counter, which keeps track of how many Pokemon are in the storage.
This is written at 0x203C000, and can be retrieved by using the "copybyte" scripting command. For example:
copybyte 0x20370D0 0x203C000 'puts the storage counter into variable 0x800D (lastresult).
I tried to keep it simple, and do the work inside the routine, hopefully that paid off.
Here's a very poor sample script. Poor because it handles all the cases, but it doesn't display the system's full potential :P
Spoiler:
PKSV script.
Code:#dyn 0x740000 #org @start lock faceplayer copybyte 0x20370D0 0x203C000 ' storage count into last result compare LASTRESULT 0x0 if == jump @putOnly msgbox @add callstd MSG_YESNO 'want to take? compare LASTRESULT 0x1 if == jump @addParty jump @putOnly #org @putOnly countpokemon copyvar 0x8000 0x800D compare 0x8000 0x1 if <= jump @greetings msgbox @put callstd MSG_YESNO 'want to put? compare LASTRESULT 0x1 if == jump @addStorage msgbox @exit callstd MSG_NORMAL release end #org @addStorage setvar 0x8000 0x1 special 0x9F waitspecial countpokemon compare LASTRESULT 0x8004 if < jump @noSelection callasm 0x[routine +1] msgbox @gave callstd MSG_NORMAL release end #org @addParty setvar 0x8000 0x0 countpokemon compare LASTRESULT 0x6 if == jump @fullP msgbox @store callstd MSG_NORMAL setvar 0x8004 0x0 'gotta rework this to work a better way callasm 0x[routine +1] fanfare 0x101 msgbox @complete callstd MSG_NORMAL release end #org @greetings msgbox @hello callstd MSG_NORMAL release end #org @fullP msgbox @full callstd MSG_NORMAL release end #org @noSelection msgbox @exit callstd MSG_NORMAL release end #org @full = You're party is full! #org @hello = Hi, I can't do anything\nyour slots are full or empty\lor mine are full while yours are\lfull[.] #org @complete = I gave it back. #org @store = Alright, I'll give it to you. #org @gave = Thanks for giving me this. #org @exit = Alright, next time then. #org @add = Want your Pokemon back? #org @put = Want to give me a pokemon?
That's it for now. Keep an eye out for "addons" for this routine in the future :)
Sorry for the double post. I like seperate posts for my routines to keep the first post's links neat :x
special
for checking if a Pokémon in the player's party has Pokérus, for use in a Nurse Joy script.bpre-trade-whatever
repositories on my Github. I have privated the repos containing the standalone ASM versions, and deleted the posts about them. Sorry for any confusion.Colored Stats based on Nature[RUBY]To implement this feature, only one routine is needed:
![]()
Spoiler:
Code:.THUMB .ALIGN 2 PUSH {R0-R7} LDR R1, = 0x02018009 LDRB R1, [R1] MOV R0, #0x64 MUL R1, R0 LDR R0, = 0x03004360 ADD R0, R0, R1 PUSH {R1-R7} BL GET_NATURE POP {R1-R7} LDR R1, = 0x081FD070 MOV R2, #5 MUL R0, R2 ADD R5, R0, R1 MOV R4, #1 LOOP: LDR R1, = 0x2018009 LDRB R1, [R1] MOV R0, #0x64 MUL R0, R1 LDR R1, = 0x03004360 ADD R0, R0, R1 MOV R2, PC ADD R2, R2, #0x74 LDRB R1, [R2, R4] MOV R7, R1 GET_DATA: PUSH {R1-R7} BL GET_MON_DATA POP {R1-R7} MOV R1, R0 LDR R0, .BUFFER PUSH {R0-R7} BL INT_TO_DEC POP {R0-R7} MOV R0, R4 MOV R1, #3 PUSH {R1-R7} BL MOD POP {R1-R7} LSL R3, R0, #1 ADD R3, R3, #7 LDR R0, .BUFFER MOV R1, #0xF MOV R2, #22 CMP R4, #3 BLT COLORED ADD R2, R2, #9 COLORED: SUB R7, R7, #0x3B LDRB R7, [R5, R7] CMP R7, #0 BEQ PRINT_STR CMP R7, #0xFF BEQ LOWER UPPER: MOV R1, #0xB B PRINT_STR LOWER: MOV R1, #0xC PRINT_STR: PUSH {R0-R7} MOV R4, #36 BL PRINT POP {R0-R7} ADD R4, R4, #1 CMP R4, #6 BNE LOOP POP {R0-R7} LDR R0, = 0x080A0048+1 BX R0 GET_NATURE: LDR R1, = 0x0803F464+1 BX R1 PRINT: LDR R4, = 0x080A1F48+1 BX R4 GET_MON_DATA: LDR R2, = 0x0803CB60+1 BX R2 INT_TO_DEC: LDR R2, = 0x08006DDC+1 BX R2 MOD: LDR R2, = 0x081E0F08+1 BX R2 .ALIGN 2 .BUFFER: .WORD 0x020231CC .TABLE: .BYTE 0x3A .BYTE 0x3B .BYTE 0x3C .BYTE 0x3E .BYTE 0x3F .BYTE 0x3D .HWORD 0
After compiling the routine in a free offset, go to 0x09FFBC and overwrite the bytes with 00480047xxxxxx08.
Instead of the XX, will be entered the offset of previous routine + 1 and in Little Endian format (example: 08123456 -> 57 34 12 08).
Thanks. Though I would like to point out, to use them all you really need to know is how to insert them and what they do. I've documented a guide on how to insert for beginners (it's a link in the FAQ first post). If you don't understand something or don't see how some routine works, feel free to ask!
:3
I'm still not understanding the incentive to randomizing the starting Pokemon. I suppose it would be a small bit of "game change" if someone were to play your hack more than once, but the feature seems a little useless to me :/
Yeah it definitely could, and should read off of a table. I'll have to redo that little bit :x
Battle Modes addon: Delete fainted Pokemon
For those hackers who like minigames, this routine will delete Pokemon who have fainted in battle. There's a few things that need to happen for this effect to occur.
1) Pokemon who die in battle must be deleted
2) Eggs are spared
3) Pokemon who die outside of battle must be deleted (Poison or another similar effect)
However, if you put this in turn basis, you will NEED to make another routine to delete the Player's party if he/she loses the match. This is because the turn counter isn't reached on the last turn the player loses :x
If you're lazy like me you can just put this routine in as a battle by move addon and cover the whiteout case. Which is actually fine. It's rather fast, because it deletes the Pokemon as they die, so the worse case scenario is unforeseeable. And if a Pokemon dies it will only need to loop 5 times only doing operations on one of the 5 loops. If you're curious looping a maximum of 21 times is the worst case scenario (all 6 of your Pokemon die in 1 turn i.e never, and even if that did happen it'd be done in less .25 seconds). So it's fine to put in the battle by move section without expecting the game to lag.
How to insert:
Insert as addon to battle modes battle by move.
You will notice that near the start of the routine there is some commented out code (denoted by a prefix "@" symbol). That's for a flag check. Change [flag/4] = your flag/4 in hex if you want to toggle by flag. Of course the normal battle mode toggle flag should be used as well.
Spoiler:
Code:.text .align 2 .thumb .thumb_func main: push {r0-r5, lr} @mov r0, #0x[flag/4] @lsl r0, r0, #0x2 @ldr r3, =(0x806E6D0 +1) @bl linker @cmp r0, #0x0 @beq end ldr r0, =(0x2023E8A) ldrb r0, [r0] cmp r0, #0x0 beq realEnd mov r5, #0x0 loop: ldr r0, =(0x2024029) ldrb r4, [r0] cmp r5, r4 beq end ldr r0, =(0x2024284) mov r1, #0x64 mul r1, r1, r5 @slot add r0, r0, r1 @address mov r1, #0x39 @C_HP ldr r3, =(0x803FBE8 +1) bl linker cmp r0, #0x0 beq adjustSlots skip: add r5, r5, #0x1 b loop adjustSlots: cmp r5, #0x5 bge writeLastZero mov r2, #0x5 sub r2, r2, r5 mov r0, #0x64 mul r2, r2, r0 @size mov r1, r5 mul r1, r1, r0 ldr r0, =(0x2024284) add r0, r0, r1 @dest mov r1, #0x64 add r1, r0, r1 @src ldr r3, =(0x8040B08 +1) bl linker writeLastZero: ldr r0, =(0x2024478) mov r1, #0x0 mov r2, #0x64 ldr r3, =(0x81E5ED8 +1) bl linker correctCounters: ldr r0, =(0x2024029) ldrb r1, [r0] sub r1, r1, #0x1 strb r1, [r0] next: mov r5, #0x0 b loop end: ldr r0, =(0x2024029) ldrb r0, [r0] cmp r0, #0x0 bne realEnd mov r0, #0x5E lsl r0, r0, #0x4 ldr r3, =(0x806E6A8 +1) bl linker b realEnd linker: bx r3 realEnd: pop {r0-r5, pc} .align 2
Usage:
If you opted to use a flag, set it to allow further battles to delete Pokemon as they faint. If you didn't just sit back and let the unicorns inside the game do the rest.