• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Forum moderator applications are now open! Click here for details.
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

Code: Trainers with EVs

DoesntKnowHowToPlay

Tiny Umbrella with Lots and Lots of Good
265
Posts
12
Years
  • Seen Feb 24, 2024
I see a lot of talk about how to make hacks "difficult". The typical solutions are to:

-give trainers teams full of legendaries
-make the level curve absurdly steep
-inflate the base stats of everything
-give the AI illegal moves and custom attacks with absurd BP
-make every dungeon a flash maze where you can't use flash

Because I hate everyone who plays romhacks for fun am a nice person, I've decided to release a bit of code that gives us another option- letting trainer pokemon have custom EVs. Normally, trainer pokemon have 0 EVs in every stat outside of the RSE Battle Tower/Frontier.

First you will need to insert this ASM:

Fire Red:
Spoiler:

Emerald:
Spoiler:

The bulk of this code is at xF90000- that part doesn't have to go there, it can be moved to wherever you have free space. Just make sure you change Method_Addr to whatever you change the offset to + 1.

You will also need to find x1000 bytes of space, and change the .EV_Table variable in the method to point to it (I used xF00000). This is where the spreads will go. The method gives you 256 spreads to use- this is not full control, but it is more than you are likely to need (the Emerald Battle Frontier only uses around 36 IIRC). The EV Spread table format is:

- Nature
- 3 unused bytes
- HP EV
- Attack EV
- Defense EV
- Speed EV
- Special Attack EV
- Special Defense EV
- What pokeball the mon comes out in
- 4 more unused bytes (I may update the method later to do something with these)

To assign an EV spread to a trainer's pokemon, set the value that was formerly their IVs to which slot in the table you want that pokemon to use. If you are using A-Trainer, this value is erroneously marked as AI Value. Note that the method only works for trainers with custom movesets and items- if they use default movesets or items, it will only affect IVs. It will still work if you manually give the trainer the moves it would have by default or explicitly define their mons as being empty-handed though.

This hack now allows custom natures. The natures are enumerated like so:
x0 - Arbitrary
x1 - Lonely (+Atk, -Def)
x2 - Brave (+Atk, -Speed)
x3 - Adamant (+Atk, -SpAtk)
x4 - Naughty (+Atk, -SpDef)
x5 - Bold (+Def, -Atk)
x6 - Docile (Neutral)
x7 - Relaxed (+Def, -Speed)
x8 - Impish (+Def, -SpAtk)
x9 - Lax (+Def, -SpDef)
xA - Timid (+Speed, -Atk)
xB - Hasty (+Speed, -Def)
xC - Serious (Neutral)
xD - Jolly (+Speed, -SpAtk)
xE - Naive (+Speed, -SpDef)
xF - Modest (+SpAtk, -Atk)
x10 - Mild (+SpAtk, -Def)
x11 - Quiet (+SpAtk, -Speed)
x12 - Bashful (Neutral)
x13 - Rash (+SpAtk, -SpDef)
x14 - Calm (+SpDef, -Atk)
x15 - Gentle (+SpDef, -Def)
x16 - Sassy (+SpDef, -Speed)
x17 - Careful (+SpDef, -SpAtk)
x18 - Quirky (Neutral)

Hardy is unavailable due to the way the method works, but that doesn't really matter since it's neutral. Higher values will crash the game. If 0 is used, the method does not touch nature, so it will be determined semi-randomly like normally.

It is worth nothing that this method allows you to give trainer pokemon more than 510 EVs if you are feeling exceptionally rude.
 
Last edited:
199
Posts
12
Years
  • Seen Jul 18, 2016
So is this correct in how it is formatted? For a second EV spread I would repeat the filler bytes on both sides?

Spoiler:


I also don't understand on how to select the specific spread you want.

To assign an EV spread to a trainer's pokemon, set the value that was formerly their IVs to which slot in the table you want that pokemon to use. If you are using A-Trainer, this value is erroneously marked as AI Value.

How do I find the slot that corresponds to the EV spread in the image above?
 

DoesntKnowHowToPlay

Tiny Umbrella with Lots and Lots of Good
265
Posts
12
Years
  • Seen Feb 24, 2024
That is correct for a 31 IV mon with 1530 EVs in a Master Ball. If that's the first entry in the EV table, you would make a pokemon use that spread by setting its IV value (AI Value in A-Trainer) to 0.
 
199
Posts
12
Years
  • Seen Jul 18, 2016
That is correct for a 31 IV mon with 1530 EVs in a Master Ball. If that's the first entry in the EV table, you would make a pokemon use that spread by setting its IV value (AI Value in A-Trainer) to 0.

Ah. So it was simple. Thank you. :)

EDIT:
Another problem. I assembled it and changed the things you said to change for Emerald and this was the result when I was going to insert it.

Spoiler:


^ Do I just copy the highlighted part or have I done something wrong that caused all those 0s

and this is what it looked like before assembling it. Not sure if I messed something up or what.

Spoiler:

I ask this because I went ahead and tried it out on the first rematch for roxanne, it didn't seem to work. I set the AI value to 0 since it was the very first spread as well.
 
Last edited:

DoesntKnowHowToPlay

Tiny Umbrella with Lots and Lots of Good
265
Posts
12
Years
  • Seen Feb 24, 2024
Any ASM compiled for a specific offset will produce a bunch of 00s before that offset in the binary. Since the method is in three parts, you'll need to go to each of them and put them where they belong. Also, you will want to move the method proper since x1E9F68 isn't free space in Emerald.
 

pawell6

The truthseeker
50
Posts
14
Years
Any ASM compiled for a specific offset will produce a bunch of 00s before that offset in the binary. Since the method is in three parts, you'll need to go to each of them and put them where they belong. Also, you will want to move the method proper since x1E9F68 isn't free space in Emerald.

So that means I have to go offsets 0x115f6, 0x3dc70, 0x1E9F68 in a rom and replace them with data from assembled bin file, even if those offsets aren't FF? I'm just asking, cause I don't want to ruin something in rom accidently.
 
199
Posts
12
Years
  • Seen Jul 18, 2016
Since the method is in three parts, you'll need to go to each of them and put them where they belong.

I think I am lost.
By parts you mean the offsets in the asm code?
I have a feeling you mean these three...
Spoiler:


I am not sure what you mean by "put them where they belong" either.


Also, you will want to move the method proper since x1E9F68 isn't free space in Emerald.

The image was from the assembled asm, not the rom. I probably should have noted that Dx.
 

DoesntKnowHowToPlay

Tiny Umbrella with Lots and Lots of Good
265
Posts
12
Years
  • Seen Feb 24, 2024
So that means I have to go offsets 0x115f6, 0x3dc70, 0x1E9F68 in a rom and replace them with data from assembled bin file, even if those offsets aren't FF? I'm just asking, cause I don't want to ruin something in rom accidently.

Mostly correct. The code at x115F6 alters the read of the IV value, so that it preserves the entire byte before stowing it in RAM (normally the game reduces it to 0-31 first), and thus should be replacing whatever is there. The code at x3DC70 is the jump to the main method, and thus should replace the data that's there. The stuff at x1E9F68 is not just a tweak of the game's code, but the actual new stuff itself, so you should put it in free space. As it happens the offset x1E9F68 has enough free space to hold the method (although it doesn't look like it immediately), but you could freely put it in FF space. On an Emerald ROM, you will need to change that to be somewhere in FF space or you will corrupt whatever it is that resides there.


I think I am lost.
By parts you mean the offsets in the asm code?
I have a feeling you mean these three...

What you need to do is go to those offsets in the .bin file, and copy what you see there into the ROM at the same offset you found it in the .bin. You also need to change x1E9F68 to be somewhere in free space before you compile- likewise you will need to change the pointer used by the jump to it to be (wherever you put the method) + 1.
 
199
Posts
12
Years
  • Seen Jul 18, 2016
Okay I will just list the steps I have been taking and you point out what is wrong and what to do with them.

1. I copy the ASM in the first post into a new notepad document.

2. I changed the .EV_Table variable from 0x08F00000 to 0x08E42710 (so that is the location of the spreads.)

3. I change the offsets in the method to what you put down. (0x115f6 -> x38936, 0x3dc70 -> x67d68, 0x3dc8b -> 0x67d83, x404d0 -> x6ad9c) (404d0 isn't in the method. It says "404d1." typo?)

4. You replied with the following.
What you need to do is go to those offsets in the .bin file, and copy what you see there into the ROM at the same offset you found it in the .bin. You also need to change x1E9F68 to be somewhere in free space before you compile- likewise you will need to change the pointer used by the jump to it to be (wherever you put the method) + 1.

So by that you mean I change the x1E9F68 in the asm to something like xFFFDF0 for example?(This is where I'll put the method)
Is the "pointer used by the jump" the 0x081E9F69 just above the x1E9F68? It looks like it is since it is 9 instead of 8 and you said +1 to it. Then I would replace the 0x081E9F69 with 0x08FFFDF1?
...and then I would go to the the offsets 0x38936, 0x67d68 and 0x1E9F68(which is now 0xFFFDF0) in the compiled .bin file and copy whatever data is there, go to those offsets in the ROM and overwrite them with the data from each offset in the .bin?

I did all of this to test it out and the game restarts when initiating the battle with the first Roxanne rematch. It did not do that before.
 

DoesntKnowHowToPlay

Tiny Umbrella with Lots and Lots of Good
265
Posts
12
Years
  • Seen Feb 24, 2024
(404d0 isn't in the method. It says "404d1." typo?)

Yeah, you want to change that to x6AD9D. My bad, was looking at older versions of the code when doing that.

So by that you mean I change the x1E9F68 in the asm to something like xFFFDF0 for example?(This is where I'll put the method)
Is the "pointer used by the jump" the 0x081E9F69 just above the x1E9F68? It looks like it is since it is 9 instead of 8 and you said +1 to it. Then I would replace the 0x081E9F69 with 0x08FFFDF1?
...and then I would go to the the offsets 0x38936, 0x67d68 and 0x1E9F68(which is now 0xFFFDF0) in the compiled .bin file and copy whatever data is there, go to those offsets in the ROM and overwrite them with the data from each offset in the .bin?

This looks pretty much correct, although if you didn't fix the x404d1 -> x6ad9d I would expect the game to crash.
 
199
Posts
12
Years
  • Seen Jul 18, 2016
I fixed that part that was a typo and it is still restarting when I start the roxanne battle.

I got some images of what I am copying from the assembled .bin. Am I not copying everything or something?

Spoiler:


Spoiler:


Spoiler:


and here is the asm before compiling.

Spoiler:

Thanks for bearing with me.
 

DoesntKnowHowToPlay

Tiny Umbrella with Lots and Lots of Good
265
Posts
12
Years
  • Seen Feb 24, 2024
THUMB commands are two bytes each- make sure in the first case that you also grab the 00 byte at the end.
 

DoesntKnowHowToPlay

Tiny Umbrella with Lots and Lots of Good
265
Posts
12
Years
  • Seen Feb 24, 2024
Neither of those is what I meant. The only byte you're missing is the 00 at the end of the first image- that is the second part of the last lsl r1, r1, #0x0 command. If it isn't changed, the last command becomes 09 FE (blh $0c12), which predictably crashes the game, instead of 09 00 (lsl r1, r1, #0x0), which is a no-op.
 
199
Posts
12
Years
  • Seen Jul 18, 2016
Neither of those is what I meant. The only byte you're missing is the 00 at the end of the first image- that is the second part of the last lsl r1, r1, #0x0 command. If it isn't changed, the last command becomes 09 FE (blh $0c12), which predictably crashes the game, instead of 09 00 (lsl r1, r1, #0x0), which is a no-op.

It is working now. Thank you very much for helping me. :D
 
91
Posts
14
Years
  • Seen Feb 22, 2023
First things first: This is awesome! Great work :)
But I still dare to ask if it's possible to also change the nature of a pokémon(like hardy lonely, ...) because this can bring an enormous strategic improvement. Otherwise the trainer(or gym leader in most cases, normal pokémon won't get much in terms of EVs I suppose) just ends up with having many points in offence while losing 10% of it due to the nature of the pokémon. I'm not familiar with the routines you edited and came up with, so I'm probably not able to do this myself.

But nevertheless this is a great routine and I'm looking forward to using it soon.

~SBird
 
199
Posts
12
Years
  • Seen Jul 18, 2016
So about the 33rd slot. Do I just leave it how it was (Full of FFs) or do I need to change something?

This is the 33rd slot here? (first EV spread is at the top)

Spoiler:
 

pawell6

The truthseeker
50
Posts
14
Years
So about the 33rd slot. Do I just leave it how it was (Full of FFs) or do I need to change something?

This is the 33rd slot here? (first EV spread is at the top)

Spoiler:

If there are only FF's all pokemons found in the wild will have 31 IV and 255 EV in all stat. You should make six 00's for ev. Using that method IV are no longer random.

The same thing happens when you receive pokemon from npc - ev and iv are from 33rd slot.
 

pawell6

The truthseeker
50
Posts
14
Years
There is something weird with table. When I try to type next spreads (0x00,0x01,0x02,0x03, ....) nothing change until 0x09 comes in (then the second spread seems to work). The third spread shows when I type 0x12 (18 in dec) and so on. This limitates number of possible spreads. Has anyone experienced similar issues?
 
199
Posts
12
Years
  • Seen Jul 18, 2016
There is something weird with table. When I try to type next spreads (0x00,0x01,0x02,0x03, ....) nothing change until 0x09 comes in (then the second spread seems to work). The third spread shows when I type 0x12 (18 in dec) and so on. This limitates number of possible spreads. Has anyone experienced similar issues?

Not sure what you mean exactly...Would you be able to get screenshots of whatever is happening?
 

DoesntKnowHowToPlay

Tiny Umbrella with Lots and Lots of Good
265
Posts
12
Years
  • Seen Feb 24, 2024
I've revised the code and updated the OP with it. This fixed wild/gift pokemon no longer having randomized IVs- there is also no risk of them getting EVs or custom balls if you forget to sanitize the 33rd entry in the table.

With regards to natures- I would very much like to do this, but I've run into complications. The main issue is that the personality value, which determines nature, is also used to encrypt other parts of the mon's data- if it gets changed, the rest of the struct has to be re-ordered. I intend to keep looking into it (I'll probably have to inject the method somewhere earlier in the code), but it'll take a bit.

There is something weird with table. When I try to type next spreads (0x00,0x01,0x02,0x03, ....) nothing change until 0x09 comes in (then the second spread seems to work). The third spread shows when I type 0x12 (18 in dec) and so on. This limitates number of possible spreads. Has anyone experienced similar issues?

Please elaborate- I'm not sure exactly what's going on here.
 
Back
Top