- 789
- Posts
- 18
- Years
- Seen Apr 29, 2025
I wouldn't bother using this. It's a somewhat misguided way of doing this (it's way more flexible than necessary, while still being a bit brittle). Since hooking into the function that checks stone compatibility is so easy, you're better off just doing that and hard coding whatever evolution methods you want. I might put up a more detailed tutorial on how to do that if someone actually wants it, but I don't expect the demand is there.
Spoiler:
First, I should just get this out of the way - this is not a tutorial on how to create new evolutionary stones. For that, look here.
This is a tutorial on how to create custom stone-triggered evolution methods (STEMs). What exactly does that mean? While it is easy to create new evolutionary stones, they all work essentially the same as the normal ones. You can't, for example, have the Dawn Stone's gender specificness, without changing the routine that checks stone evolutions.
This also has the unfortunate side-effect that, if you ever decide you want more or less custom STEMs, such as a stone that only works if you know a certain move, you'd have to change the routine again. This is rather annoying.
Creating custom level up-triggered evolution methods is easier - you simply write code to check for something, and add the pointer to a table, and it'll just work. See here for more information.
Basically, what I did was a simple rewrite of the checking routine for Stone evolutions to make it easier to create custom evolution methods involving them. This should go without saying, but this requires knowledge of ASM.
Setting up my hack.
Creating custom STEMs.
Dealing with repointed / expanded evolution tables.
If you find any bugs, let me know. And, as always with ASM, make sure your backups are recent, so that if something does go wrong, you don't lose a lot of progress.
Before I forget, credit goes to Jambo51 for the offset of the checking routine (that I replaced to make this possible).
This is a tutorial on how to create custom stone-triggered evolution methods (STEMs). What exactly does that mean? While it is easy to create new evolutionary stones, they all work essentially the same as the normal ones. You can't, for example, have the Dawn Stone's gender specificness, without changing the routine that checks stone evolutions.
This also has the unfortunate side-effect that, if you ever decide you want more or less custom STEMs, such as a stone that only works if you know a certain move, you'd have to change the routine again. This is rather annoying.
Creating custom level up-triggered evolution methods is easier - you simply write code to check for something, and add the pointer to a table, and it'll just work. See here for more information.
Basically, what I did was a simple rewrite of the checking routine for Stone evolutions to make it easier to create custom evolution methods involving them. This should go without saying, but this requires knowledge of ASM.
Setting up my hack.
Spoiler:
I'm working on a small program to do this for you automatically, but for now, you'll have to do it by hand. Sorry.
This'll take a little work. First of all, here's the assembled code you'll need to insert:
To make this process as easy as possible, insert it at an offset ending in 0x00. I'll refer to this offset as ROUTINE.
You're probably wondering what the x's are. Much like the level-up routine uses a table of pointers, so does this. The x's are a pointer to this table, which we're about to make. First, we'll need some free space. How much? Well, just take the total number of evolution methods multiplied by 4 (the size of a pointer). In vanilla FR, there are 15 evolution methods, so if you're planning on adding three more, that'd make 18, so 18 * 4 = 72 bytes.
Find an appropriate spot - I'll refer to this location as TABLE. Note that there is no zeroth entry for the table - TABLE will point directly to the routine for evolution method id 0x1 (Happiness). You'll need to calculate two pointers: FAILED = ROUTINE + 0x25 and PASSED = ROUTINE + 0x2F. This is why I suggested inserting at an offset ending in 0x00 - FAILED and PASSED will be that same number, only ending with 0x25 and 0x2F, respectively. Simple.
Before we forget, replace the x's with TABLE, in reversed hex (little endian).
For the table, you'll need to insert 6 copies of FAILED, a copy of PASSED, and 8 more copies of FAILED. Remember to do them in reverse hex. This will be enough for all vanilla evolution methods. However, if you have any custom evolution methods (such as a level-up one), you'll need to add another copy of FAILED for every one of them as well.
Basically, FAILED will tell the algorithm to "move on, nothing to see here", while PASSED will initiate the check to see if the used stone and required stone match. This makes sense - outside of 0x7, none of the vanilla FR evolution methods have anything to do with stones, so we should insta-fail them. For 0x7, all that needs to be done is checking the stone itself.
The last thing is to actually set it so the game will run this routine. That's simple.
The y's are ROUTINE +1 in reverse hex.
Now, we're finally done setting this up.
If you want, you can go ahead and check that everything's working correctly. You should be able to use evolutionary stones just like you could before - at this point, there should be no change visible in-game.
This'll take a little work. First of all, here's the assembled code you'll need to insert:
Code:
F0 19 C0 00 C0 18 01 88 FF 29 0B D8 00 29 09 D0 0C 4B 01 39 89 00 59 58 6D 46 FF 23 1D 40 64 3D 07 4B 08 47 01 32 08 30 05 2A 05 D2 EB E7 43 88 4B 45 F7 D1 83 88 9A 46 00 48 00 47 A3 31 04 08 E9 FB 03 08 xx xx xx xx
To make this process as easy as possible, insert it at an offset ending in 0x00. I'll refer to this offset as ROUTINE.
You're probably wondering what the x's are. Much like the level-up routine uses a table of pointers, so does this. The x's are a pointer to this table, which we're about to make. First, we'll need some free space. How much? Well, just take the total number of evolution methods multiplied by 4 (the size of a pointer). In vanilla FR, there are 15 evolution methods, so if you're planning on adding three more, that'd make 18, so 18 * 4 = 72 bytes.
Find an appropriate spot - I'll refer to this location as TABLE. Note that there is no zeroth entry for the table - TABLE will point directly to the routine for evolution method id 0x1 (Happiness). You'll need to calculate two pointers: FAILED = ROUTINE + 0x25 and PASSED = ROUTINE + 0x2F. This is why I suggested inserting at an offset ending in 0x00 - FAILED and PASSED will be that same number, only ending with 0x25 and 0x2F, respectively. Simple.
Before we forget, replace the x's with TABLE, in reversed hex (little endian).
For the table, you'll need to insert 6 copies of FAILED, a copy of PASSED, and 8 more copies of FAILED. Remember to do them in reverse hex. This will be enough for all vanilla evolution methods. However, if you have any custom evolution methods (such as a level-up one), you'll need to add another copy of FAILED for every one of them as well.
Basically, FAILED will tell the algorithm to "move on, nothing to see here", while PASSED will initiate the check to see if the used stone and required stone match. This makes sense - outside of 0x7, none of the vanilla FR evolution methods have anything to do with stones, so we should insta-fail them. For 0x7, all that needs to be done is checking the stone itself.
The last thing is to actually set it so the game will run this routine. That's simple.
Code:
0x08043182: 01 48 00 47 C0 46 yy yy yy yy
The y's are ROUTINE +1 in reverse hex.
Now, we're finally done setting this up.
If you want, you can go ahead and check that everything's working correctly. You should be able to use evolutionary stones just like you could before - at this point, there should be no change visible in-game.
Creating custom STEMs.
Spoiler:
The pointers we used to create the table, PASSED and FAILED, are typically going to be the exit points of your routine. Unsurprisingly, if whatever check it performs fails, go to FAILED, and if it suceeded, go to PASSED.
In order for this to work correctly, you'll have to (not) do a couple things. Leave the stack as you found it - you're free to use it, of course, but pop whatever you push, and don't pop anything you didn't push. Leave R0 and R2 as you found them. Leave the high registers as you found them.
If you cause a stack misalignment, or modify R0, R2, or the high registers (without fixing them afterward), it will not work.
Now that that's out of the way, what is in the registers when you get them?
R0 has a pointer to the relevant evolution table entry. [R0, 0x0] is the evolution method id (don't worry about this), [R0, 0x2] is the required stone (don't worry about this), [R0, 0x4] is the evolution's species id (don't worry about this), and [R0, 0x6] is 0x0000 for padding, but can be used for whatever you want.
R8 has the Pokemon's party data.
R3 has 0x0803FBE9, the decrypter. I figured a bunch of STEMs would need it, so why not just provide it?
As a simple example, I will provide code for a Stone + Map evolution method. That is, it will only evolve if you use a particular stone while on a particular map and map bank. If you use the wrong stone, or are in the wrong map, it will not evolve your Pokemon.
The current map data is stored in DMA-protected memory, so I load that. I'm using the last two bytes of the evolution table entry to define the specific map and map bank. As you can see, it's just a simple compare, which determines whether it jumps to FAILED or PASSED.
After writing your routine, just assemble it and put it somewhere in the ROM. Put it's address +1 at the end of the table, and you should be able to use it in game without problems. Most of your routines will be pretty similar to the above, simply loading something and comparing, and then jumping to either PASSED or FAILED.
Like I mentioned before, each pointer in the table corresponds to a evolution method id. Vanilla FR will start out with 15 pointers, so the first one you insert will be 16 (0x10), and so on.
Congratulations, you've just made your own evolution method.
Of course, there's no Pokemon set to evolve using this method, so you'll have to change the evolution table so that they will. The simplest way to do this is with the Gen III Hacking Suite, however, as far as I know, it doesn't allow you to edit the padding bytes (which I'm using to store the required map data), so you'll have to do that in a Hex Editor.
In order for this to work correctly, you'll have to (not) do a couple things. Leave the stack as you found it - you're free to use it, of course, but pop whatever you push, and don't pop anything you didn't push. Leave R0 and R2 as you found them. Leave the high registers as you found them.
If you cause a stack misalignment, or modify R0, R2, or the high registers (without fixing them afterward), it will not work.
Now that that's out of the way, what is in the registers when you get them?
R0 has a pointer to the relevant evolution table entry. [R0, 0x0] is the evolution method id (don't worry about this), [R0, 0x2] is the required stone (don't worry about this), [R0, 0x4] is the evolution's species id (don't worry about this), and [R0, 0x6] is 0x0000 for padding, but can be used for whatever you want.
R8 has the Pokemon's party data.
R3 has 0x0803FBE9, the decrypter. I figured a bunch of STEMs would need it, so why not just provide it?
As a simple example, I will provide code for a Stone + Map evolution method. That is, it will only evolve if you use a particular stone while on a particular map and map bank. If you use the wrong stone, or are in the wrong map, it will not evolve your Pokemon.
Code:
.text
.align 2
.thumb
.thumb_func
map:
ldr r3, dma
ldr r3, [r3, 0x0]
ldrh r6, [r3, 0x4] @ load current map data
ldrh r7, [r0, 0x6] @ load required map data
cmp r6, r7
bne failed
ldr r5, pass
return:
bx r5
failed:
ldr r5, fail
b return
.align
dma: .word 0x03005008
fail: .word ROUTINE + 0x25
pass: .word ROUTINE + 0x2F
The current map data is stored in DMA-protected memory, so I load that. I'm using the last two bytes of the evolution table entry to define the specific map and map bank. As you can see, it's just a simple compare, which determines whether it jumps to FAILED or PASSED.
After writing your routine, just assemble it and put it somewhere in the ROM. Put it's address +1 at the end of the table, and you should be able to use it in game without problems. Most of your routines will be pretty similar to the above, simply loading something and comparing, and then jumping to either PASSED or FAILED.
Like I mentioned before, each pointer in the table corresponds to a evolution method id. Vanilla FR will start out with 15 pointers, so the first one you insert will be 16 (0x10), and so on.
Congratulations, you've just made your own evolution method.
Of course, there's no Pokemon set to evolve using this method, so you'll have to change the evolution table so that they will. The simplest way to do this is with the Gen III Hacking Suite, however, as far as I know, it doesn't allow you to edit the padding bytes (which I'm using to store the required map data), so you'll have to do that in a Hex Editor.
Dealing with repointed / expanded evolution tables.
Spoiler:
Replace the byte at ROUTINE + 0x28. It should be the number of evolutions per Pokemon. Replace the pointer at 0x08042F6C with that of your evolution table. It should specifically point to the zeroth entry.
Note that this still assumes 8 bytes per evolution in the table.
Note that this still assumes 8 bytes per evolution in the table.
If you find any bugs, let me know. And, as always with ASM, make sure your backups are recent, so that if something does go wrong, you don't lose a lot of progress.
Before I forget, credit goes to Jambo51 for the offset of the checking routine (that I replaced to make this possible).
Last edited: