Jambo51
Glory To Arstotzka
- 736
- Posts
- 15
- Years
- Seen Jan 28, 2018
I would like to note that the general framework for these hacks were based on an idea that a Brazilian hacker had, his name escapes me right now, but some credit must be given to him/her!
These routines DO NOT support the new RTC and Day and Night Feature developed by Prime unless you use the same RAM offsets. Even if you do, I would ideally like to redevelop these routines to use the new system Prime has developed as it would be more efficient.
FireRed (Extensively Tested)
Emerald (Basic Testing)
Level Loading Routine:
Insert at 0x080B4C76:
Pokémon Loading Routine
Overwrite the code at 0x080B500A onwards with the following:
In order for this to work properly, you need to update the RTC's time appropriately. But how to do this when Emerald only calls the time update routine when warping or connecting? Use the code shown in this post: https://www.pokecommunity.com/posts/6395043/. If you use that code and patch the time update routine onto the main loop of the game, the time updates once every frame! This means it works more like interdpth's RTC code for FireRed. A much better solution IMHO.
Ruby (Not Started Yet)
These routines DO NOT support the new RTC and Day and Night Feature developed by Prime unless you use the same RAM offsets. Even if you do, I would ideally like to redevelop these routines to use the new system Prime has developed as it would be more efficient.
FireRed (Extensively Tested)
Spoiler:
First up is the routine which will make the wild Pokémon's level load correctly, rather than loading random garbage data after the repointing process is complete.
Wild Pokémon Day/Night Level Loading Routine
So, insert the code below at 0x080828FA where the XXXXXX is replaced with the reverse hex pointer plus one of the newly inserted code.
This sorts the level loading part of the routine, but we haven't actually modified the Pokémon loading part of it yet. So, now we have to insert another routine to actually make that work.
This routine performs a similar function to the level modification part from above. Changing the pointer to the wild data into a pointer to a table of pointers to wild data.
Insert the assembled routine and then modify the data at 0x08082B48 so that it repoints to the new routine's location plus 1:
For the swarming code, you need to set the variable 0x4FFF to 0x2 or greater, and then set the variable 0x4FFE with the Pokémon number you want to be swarming.
eg. I want Taillow to swarm on Route 4:
The way the code works is that it always overwrites the standard Pokémon loading routine for the given map until you change one of the above parameters. How you do this is entirely up to you.
These 2 routines together allow you to have different pokémon at day and night, but read in more or less exactly the same way as before, meaning you can rely on it to produce common Pokémon more often than rare ones in the same way as any modified data in A-Map.
There is another, entirely optional, hack related to this, which is having different wild Pokémon encounter rates between day and night. This hack also reads the ability byte of your first Pokémon, looking for specific conditions to be met. If your lead Pokémon has certain abilities (the list isn't yet complete, and some of the effects still need some research) then the wild encounter rate can be halved or doubled. For all of the abilities listed below, they only apply if the Pokémon with the ability is in slot 1. Otherwise, it'll work as per usual.
Current list of supported abilities:
Anyhoo, the game reads the encounter rate like so:
This is for Route 1, but all encounter rates are stored in the same style:
But it only reads the first byte of this. What a waste of space! So, I figured we could use this space to store upto an extra 3 encounter rates, making a total of 4 for every map! (Remember, the maximum encounter rate is 255 or 0xFF, meaning that even with all of them set to maximum, it will fit in here snugly. :D
This code makes the bytes following the normal encounter rate become encounter rates too, separate from the normal one, and only read when the hour byte is above or below a certain value.
Insert the following hex string at 0x08083278:
Where one again, the XXXXXX is the reverse hex pointer to the new routine's location.
So, it marks them out like so for the 4 time set up I have created.
I left the day encounter rate at the start in order to have the routine be as compatible with the original rom's data as possible.
Anyway, the code reads this byte, then runs some simple checks (credit for that particular part of the code goes to HackMew as it is taken almost wholesale from his Flame Body hack, which I suggest you all try out!), and based on the results of these checks, either halves, doubles, or continues with the given encounter rate. It then returns to the original routine for writing to memory.
OK, now a quick lesson on how to get the tables set up right.
Again, for a benchmark I'll use Route 1's data, but it can be expanded to every single wild data table in the game.
This is the Route 1 grass wild data as standard in the rom:
In order for the game to read this properly with the new routines, you need to repoint the pointer to a free space location in the rom. A good location for this would be 0x08B00000 as it's empty and has more than enough free space to hold the new data.
So, at 0xB00000, copy and paste the pointer to the original wild data. This will serve as our daytime data. Then after it, place another pointer to an empty area of the rom. Anywhere will do, as long as you have enough free space to put the correct number of wild Pokémon in. That will make it work, although you should obviously fill the new table with Pokémon data. This new table serves as our night time data. So, now whenever we encounter a wild Pokémon at night, it'll be loaded from the night time data, rather than the day time data.
Here is one final, separate routine which writes the map you are currently in and the weather on said map to a ram offset so that the routines above can use them to determine whether certain conditional modifications should be activated. (This is because I couldn't find either written to the ram although they are obviously written to the ram at some point by the actual routine).
Insert the following code at 0x0805529A:
Wild Pokémon Day/Night Level Loading Routine
Code:
.text /*Basic THUMB Headers, should be included in all ASM Routines*/
.align 2
.thumb
.thumb_func
.global daynightlevelswitch
main:
sub r0, r0, r4 /*Subtracts the value of R4 from R0, giving us a clean pointer to use*/
ldr r2, hour /*Loads the ram offset 0x03005542 to R2*/
ldrb r2, [r2, #0x0] /*Loads the byte contained in the offset*/
cmp r2, #0x15 /*Compares R2 (The Hour) with 0x15 - 9PM - Change it if you want*/
bge night /*If greater than or equal to, go to "night"*/
cmp r2, #0x12
bge evening
cmp r2, #0x9 /*Compares R2 (The Hour) with 0x9 - 9AM - Change it if you want*/
bge day /*If greater than or equal to, go to "day"*/
cmp r2, #0x6
bge morning
night: add r2, r0, #0x4
b check
evening: add r2, r0, #0x0
add r2, #0xC
b check
morning: add r2, r0, #0x0
add r2, #0x8
b check
day: add r2, r0, #0x0
check: ldrb r1, [r2, #0x3] /*Loads the byte stored at the pointer's location plus 3 for checking*/
cmp r1, #0xFF
beq nodata
cmp r1, #0x8 /*Compares R2 with 0x08, the tell tale sign of a rom pointer, which is what should be stored here if you are using this hack*/
bne nextnormal /*If not equal, load as usual - Thanks Shiny Quagsire!*/
ldr r0, [r2, #0x0] /*Loads the word stored at the location pointed to in R2, the pointer to the wild data*/
nextnormal: add r0, r4, r0 /*Adds the number of bytes from the start of the table that the wild Pokémon slot the game has selected is*/
add r4, r0, #0x0 /*Adds the contents of R0 into R4*/
ldrb r0, [r4, #0x1] /*Loads the upper level limit for the Pokémon if they're stored systematically*/
ldrb r1, [r4, #0x0] /*Loads the lower level limit*/
cmp r0, r1 /*Compares the levels*/
bcc here /*If R0 < R1 goto "here"*/
ldrb r5, [r4, #0x0] /*Loads the lower level limit into R5*/
b here2
here: ldrb r5, [r4, #0x1] /*Reverses the lower and upper level limits to*/
ldrb r0, [r4, #0x0] /*avoid bugs if R0 was smaller than R1 above*/
here2: sub r4, r0, r5 /*Subtracts upper and lower level limits and stores results into R4*/
add r4, #0x1 /*Not entirely sure why, but it's from the original code, and is important for level loading, as leaving it out leads to odd bugs*/
lsl r4, r4, #0x18 /*This line and the next line make sure that you are only passing a byte to the next part of the routine, so it doesn't bug out*/
lsr r4, r4, #0x18
ldr r2, return /*Loads the return offset to R2*/
bx r2 /*Executes a Branch and Exchange goto to the offset contained within the register, ensuring it remains in THUMB mode by being 1 greater than the wanted location*/
var_decrypt: ldr r1, vardecrypt
bx r1
nodata: sub r2, r2, r0
cmp r2, #0xC
beq night
b day
.align
hour: .word 0x03005542
return: .word 0x08082915
vardecrypt: .word 0x0806E455
var_4fff: .word 0x00004FFF /*Replace this with whatever variable you would like to use*/
So, insert the code below at 0x080828FA where the XXXXXX is replaced with the reverse hex pointer plus one of the newly inserted code.
Code:
01 4A 10 47 00 00 XX XX XX 08
This sorts the level loading part of the routine, but we haven't actually modified the Pokémon loading part of it yet. So, now we have to insert another routine to actually make that work.
Code:
.text
.align 2
.thumb
.thumb_func
.global daynightwildswitch
main:
ldr r0, [r7, #0x4]
push {r0}
ldr r0, var_4fff
bl var_decrypt
ldrh r0, [r0, #0x0]
cmp r0, #0x1
bge swarm
pop {r0}
push {r2}
daynight: ldr r2, hour
ldrb r2, [r2, #0x0]
cmp r2, #0x15 /*9PM - Change it if you want*/
bge night
cmp r2, #0x12
bge evening
cmp r2, #0x9 /*9AM - Change it if you want*/
bge day
cmp r2, #0x6
bge morning
night: add r2, r0, #0x4
b check
evening: add r2, r0, #0x0
add r2, #0xC
b check
morning: add r2, r0, #0x0
add r2, #0x8
b check
day: add r2, r0, #0x0
check: ldrb r1, [r2, #0x3]
cmp r1, #0xFF
beq nodata
cmp r1, #0x8
bne nextnormal
ldr r0, [r2, #0x0]
nextnormal: add r0, r4, r0
ldrh r0, [r0, #0x2]
back: ldr r1, lastpokemon
strh r0, [r1, #0x0]
add r1, r5, #0x0
pop {r2}
ldr r3, Back
bx r3
swarm: pop {r0}
ldr r0, var_4ffe
bl var_decrypt
sub r2, r0, #0x2
ldr r1, currentmap
ldrb r1, [r1, #0x0]
ldrh r2, [r2, #0x0]
cmp r1, r2
bne daynightone
ldrh r0, [r0, #0x0]
b back
var_decrypt: ldr r1, vardecrypt
bx r1
daynightone: ldr r0, [r7, #0x4]
push {r0}
b daynight
nodata: sub r2, r2, r0
cmp r2, #0xC
beq night
b day
.align
Back: .word 0x08082b51
lastpokemon: .word 0x0300555C
hour: .word 0x03005542
vardecrypt: .word 0x0806E455
var_4fff: .word 0x00004FFF /*Replace this with whatever variable you would like to use*/
var_4ffe: .word 0x00004FFE /*Replace this with whatever variable you would like to use*/
currentmap: .word 0x03005558
This routine performs a similar function to the level modification part from above. Changing the pointer to the wild data into a pointer to a table of pointers to wild data.
Insert the assembled routine and then modify the data at 0x08082B48 so that it repoints to the new routine's location plus 1:
Code:
00490847XXXXXX08
For the swarming code, you need to set the variable 0x4FFF to 0x2 or greater, and then set the variable 0x4FFE with the Pokémon number you want to be swarming.
eg. I want Taillow to swarm on Route 4:
Code:
setvar 0x4FFF 0x2
setvar 0x4FFE PKMN_TAILLOW
setvar 0x4FFD 0x68
The way the code works is that it always overwrites the standard Pokémon loading routine for the given map until you change one of the above parameters. How you do this is entirely up to you.
These 2 routines together allow you to have different pokémon at day and night, but read in more or less exactly the same way as before, meaning you can rely on it to produce common Pokémon more often than rare ones in the same way as any modified data in A-Map.
There is another, entirely optional, hack related to this, which is having different wild Pokémon encounter rates between day and night. This hack also reads the ability byte of your first Pokémon, looking for specific conditions to be met. If your lead Pokémon has certain abilities (the list isn't yet complete, and some of the effects still need some research) then the wild encounter rate can be halved or doubled. For all of the abilities listed below, they only apply if the Pokémon with the ability is in slot 1. Otherwise, it'll work as per usual.
Current list of supported abilities:
Spoiler:
Arena Trap (Doubles)
Illuminate (Doubles)
Sand Veil (Halves in Sandstorm)
Stench (Halves)
Swarm (Doubles)
White Smoke (Halves)
Illuminate (Doubles)
Sand Veil (Halves in Sandstorm)
Stench (Halves)
Swarm (Doubles)
White Smoke (Halves)
Anyhoo, the game reads the encounter rate like so:
This is for Route 1, but all encounter rates are stored in the same style:
Code:
DD 00 00 00
This code makes the bytes following the normal encounter rate become encounter rates too, separate from the normal one, and only read when the hour byte is above or below a certain value.
Code:
.text
.align 2
.thumb
.thumb_func
.global daynightencounterratehack
main:
push {r0}
ldr r0, hour
ldrb r0, [r0, #0x0]
cmp r0, #0x15 /*9PM - Change it if you want*/
bge night
cmp r0, #0x12
bge evening
cmp r0, #0x9 /*9AM - Change it if you want*/
bge day
cmp r0, #0x6
bge morning
night: pop {r0}
ldrb r1, [r0, #0x3]
b check
evening: pop {r0}
ldrb r1, [r0, #0x2]
b check
morning: pop {r0}
ldrb r1, [r0, #0x1]
check: cmp r1, #0x0
beq day2
b new
day: pop {r0}
day2: ldrb r1, [r0, #0x0]
new: push {r0-r7}
ldr r0, partystart /*Everything from here until the next comment is HackMew's code*/
mov r5, #0x6
add r0, r4, #0x0
mov r1, #0x8
ldr r3, readdata
bl bxr3
add r6, r0, #0x0
add r0, r4, #0x0
mov r1, #0x2E
ldr r3, readdata
bl bxr3
add r1, r0, #0x0
add r0, r6, #0x0
ldr r3, getability
bl bxr3 /*End of HackMew's code*/
cmp r0, #0x1
beq divide
cmp r0, #0x8
beq sandveil
cmp r0, #0x38
beq double
cmp r0, #0x44
beq double
cmp r0, #0x47
beq double
cmp r0, #0x49
beq divide
pop {r0-r7}
return: cmp r1, #0x4F
bhi one
cmp r1, #0x9
bls two
add r0, r1, #0x0
ldr r1, place
bx r1
divide: pop {r0-r7}
push {r0}
push {r1}
push {r2}
add r0, r1, #0x0
mov r1, #0x2
mov r2, #0x0
swi #0x6
pop {r2}
pop {r1}
add r1, r0, #0x0
pop {r0}
b return
double: pop {r0-r7}
push {r2}
mov r2, #0x2
mul r1, r2
pop {r2}
b return
bxr3: bx r3 /*This one line of code is also HackMew's, but it somehow ended up down here*/
two: mov r0, #0x8
b Back
one: mov r0, #0x0
Back: ldr r4, placetwo
bx r4
sandveil: ldr r0, weatherlocation
ldrb r0, [r0, #0x0]
cmp r0, #0x8
bne no
b divide
no: pop {r0-r7}
b return
.align
placetwo: .word 0x080832CF
weatherlocation: .word 0x03005564
hour: .word 0x03005542
partystart: .word 0x02024284
readdata: .word 0x0803FBE9
getability: .word 0x08040D39
place: .word 0x080832BB
Insert the following hex string at 0x08083278:
Code:
00 49 08 47 XX XX XX 08
Where one again, the XXXXXX is the reverse hex pointer to the new routine's location.
So, it marks them out like so for the 4 time set up I have created.
Code:
DD MM EE NN
Anyway, the code reads this byte, then runs some simple checks (credit for that particular part of the code goes to HackMew as it is taken almost wholesale from his Flame Body hack, which I suggest you all try out!), and based on the results of these checks, either halves, doubles, or continues with the given encounter rate. It then returns to the original routine for writing to memory.
OK, now a quick lesson on how to get the tables set up right.
Again, for a benchmark I'll use Route 1's data, but it can be expanded to every single wild data table in the game.
This is the Route 1 grass wild data as standard in the rom:
Code:
15 00 00 00 60 8E 3C 08
In order for the game to read this properly with the new routines, you need to repoint the pointer to a free space location in the rom. A good location for this would be 0x08B00000 as it's empty and has more than enough free space to hold the new data.
So, at 0xB00000, copy and paste the pointer to the original wild data. This will serve as our daytime data. Then after it, place another pointer to an empty area of the rom. Anywhere will do, as long as you have enough free space to put the correct number of wild Pokémon in. That will make it work, although you should obviously fill the new table with Pokémon data. This new table serves as our night time data. So, now whenever we encounter a wild Pokémon at night, it'll be loaded from the night time data, rather than the day time data.
Here is one final, separate routine which writes the map you are currently in and the weather on said map to a ram offset so that the routines above can use them to determine whether certain conditional modifications should be activated. (This is because I couldn't find either written to the ram although they are obviously written to the ram at some point by the actual routine).
Code:
.text
.align 2
.thumb
.thumb_func
.global headerreadinghack
main:
stmia r1!, {r2, r3, r6}
ldr r0, [r0, #0x0]
push {r5}
push {r6}
lsl r6, r6, #0x8
lsr r6, r6, #0x18
ldr r5, currentmaploc
strb r6, [r5, #0xC]
pop {r6}
push {r6}
lsl r6, r6, #0x18
lsr r6, r6, #0x18
strb r6, [r5, #0x0]
pop {r6}
pop {r5}
mov r7, #0x4
str r0, [r1, #0x0]
ldr r1, [r5, #0x0]
ldrh r0, [r4, #0x12]
strh r0, [r1, #0x32]
ldr r0, returntooriginal
bx r0
.align
currentmaploc: .word 0x03005558
returntooriginal: .word 0x080552A7
Insert the following code at 0x0805529A:
Code:
01 4F 38 47 00 00 XX XX XX 08
Emerald (Basic Testing)
Spoiler:
Level Loading Routine:
Code:
.text
.align 2
.thumb
.thumb_func
.global daynightlevelswitch
main:
ldr r2, hour
ldrb r2, [r2, #0x0]
cmp r2, #0x21
bge night
cmp r2, #0x18
bge even
cmp r2, #0x9
bge day
cmp r2, #0x6
bge morn
night: add r2, r0, #0x4
b check
even: add r2, r0, #0x0
add r2, #0xC
b check
morn: add r2, r0, #0x0
add r2, #0x8
b check
day: add r2, r0, #0x0
check: ldrb r1, [r2, #0x3]
cmp r1, #0xFF
beq nodata
cmp r1, #0x8
bne nextnormal
ldr r0, [r2, #0x0]
nextnormal: add r4, r0, r4
ldrb r0, [r4, #0x1]
ldrb r1, [r4, #0x0]
cmp r0, r1
bcc here
ldrb r7, [r4, #0x0]
add r6, r0, #0x0
b here2
here: ldrb r7, [r4, #0x1]
ldrb r6, [r4, #0x0]
here2: sub r4, r6, r7
add r4, #0x1
lsl r4, r4, #0x18
lsr r4, r4, #0x18
ldr r2, return
bx r2
nodata: sub r2, r2, r0
cmp r2, #0xC
beq night
b day
night2: add r2, r0, #0x4
b check
timeupdate: ldr r1, time
bx r1
.align
hour: .word 0x03000DC4
return: .word 0x080B4C93
time: .word 0x0802F589
Insert at 0x080B4C76:
Code:
01 4A 10 47 00 00 XX XX XX XX
Pokémon Loading Routine
Code:
.text
.align 2
.thumb
.thumb_func
.global daynightwildswitch
main:
pop {r0}
ldrb r0, [r0, #0x0]
push {r3,r4}
lsl r4, r0, #0x2
ldr r0, [r5, #0x4]
daynight: ldr r1, hour
ldrb r1, [r1, #0x0]
cmp r1, #0x21
bge night
cmp r1, #0x18
bge evening
cmp r1, #0x9
bge day
cmp r1, #0x6
bge morning
night: add r1, r0, #0x4
b check
evening: add r1, r0, #0x0
add r2, #0xC
b check
day: add r1, r0, #0x0
b check
morning: add r1, r0, #0x0
add r2, #0x8
check: ldrb r3, [r1, #0x3]
cmp r3, #0xFF
beq nodata
cmp r3, #0x8
bne nextnormal
ldr r0, [r1, #0x0]
nextnormal: add r0, r0, r4
ldrh r0, [r0, #0x2]
pop {r3,r4}
add r1, r4, #0x0
ldr r6, Back
bx r6
nodata: sub r2, r2, r0
cmp r2, #0xC
beq night
b day
.align
Back: .word 0x080B5015
hour: .word 0x03000DC4
Overwrite the code at 0x080B500A onwards with the following:
Code:
01 B4 00 48 00 47 XX XX XX XX
In order for this to work properly, you need to update the RTC's time appropriately. But how to do this when Emerald only calls the time update routine when warping or connecting? Use the code shown in this post: https://www.pokecommunity.com/posts/6395043/. If you use that code and patch the time update routine onto the main loop of the game, the time updates once every frame! This means it works more like interdpth's RTC code for FireRed. A much better solution IMHO.
Ruby (Not Started Yet)
Spoiler:
Last edited: