Skip to content

How to create a new regular trainer battle

Cam edited this page Dec 13, 2020 · 2 revisions

HOW TO MAKE A REGULAR TRAINER BATTLE


If you're reading this then you've successfully been suckered into trying to make an actual game using one of pret's gen 3 decomps, poor sap that you are. Maybe your pokecrystal using gen 2 hacker friends told you how convenient and easy to use the pret projects were. And they're right! If you're using pokecrystal that is. But this isn't gen 2, this is gen 3, and gen 3 is a whole different beast.

One of the most immediately recognizable features of a Pokemon game are the battles you have against other trainers, would you like to know how to create and insert one of these into your romhack? Well you've come to the right place.


  • STEP 1: WRITE YOUR TRAINER'S QUOTES AND COME UP WITH THEIR TEAM

This part is simple, right? You just need to write them an opening, beaten, and closing quote like so:

Bug Catcher Tim

lv. 3 Spinarak, lv. 4 Joltik

"What's the matter? Scared of some little spiders?"

"Tangled in a web of defeat!"

"Arachnophobia? Now why would they need to go and invent a word like that?"

This is Tim, he likes spiders.


  • STEP 2: ADD THE TRAINER TO THE OVERWORLD USING PORYMAP

Crack open the map you plan on adding the trainer to in porymap and add a new object, adjust the sprite to your liking.

See there on the sidebar where it says Script (NULL)? That's where we'll be linking in the script later once we create it.

Set the trainer type to TRAINER_TYPE_NORMAL and give your trainer a View Radius, typically 1-4 depending on how far you want them to be able to spot you.

To open up the map's scripts file from the decomp, click the button on porymap that says "Open Map Scripts".


  • STEP 3: WRITING YOUR TRAINER'S SCRIPT

For a stock, basic, totally regular trainer battle you can pretty much copy and paste this script from the original pokeemerald. You will want to change the code labels and constants here (which I'll go over) to match your new trainer's data and location.

Route2_EventScript_BCTim::

trainerbattle_single TRAINER_BC_TIM, Route2_Text_BCTimIntro, Route2_Text_BCTimDefeated
msgbox Route2_Text_BCTimPostBattle, MSGBOX_AUTOCLOSE
end`

The top line with the two colons is the script header, you'll want to change the location and trainer names; in this case Route 2 and BCTim to your own trainer's.

The second line is declaring a trainer battle using the trainerbattle_single (for a single battle) script macro. After the space is the OPPONENT CONSTANT in this case TRAINER_BC_TIM.

After the trainer constant are the first two TEXT POINTERS these correspond to the opening quote (when the trainer runs up to battle you) and the defeat quote (when they slide in on the battle screen after their last pokemon is defeated).

On the third line is the TEXT POINTER to the post-battle quote (the one when you talk to them on the overworld after beating them).

The fourth line is end it ends the script.


  • STEP 4: DEFINING YOUR TRAINER'S OPPONENT CONSTANT

In the script we used the opponent constant TRAINER_BC_TIM, but what is an opponent constant?

Well, an opponent constant is defined for every trainer battle in the game in include\constants\opponents.h

In this file you will see a list of defines for all the trainers. In this example I'll be replacing the first entry on the list after index 0. But before I do that, why am I replacing a trainer instead of just adding one to the end of the list?

Pokemon Emerald as it is already used up 855 out of a maximum 864 trainer battle slots. The reason these slots are limited as they come is because these opponent constants also handle the save flags for recognizing what trainers you've defeated already.

So with that out of the way lets replace number 1: TRAINER_SAWYER_1

Just replace the constant like so, easy right? WRONG!


  • STEP 5: REALIZING THAT THE TRAINER CONSTANT YOU JUST REPLACED IS A MATCH CALL TRAINER.

Noticed that TRAINER_SAWYER_1 has a _1 after it? That's because Sawyer is a Match Call trainer, an Emerald feature similar to the Pokegear rematch calls in gen 2. If you want to create your own Match Call trainers that's something I don't know how to do yet either so I'll save it for someone more competent (probably me in another month or so) to write a tutorial on.

You don't have to do this part if the trainer you replaced isn't on a match call, but here's where you have to do some code deleting.

in src\match_call.c starting on Line 115 you'll find a C data list of trainerIds and associated match call data. Crtl+F to find your opponent constant (TRAINER_SAWYER_1) then delete the codeblock starting and ending with the brackets { } there. I have absolutely no idea what this may break, but your game will not build unless you cull that.


  • STEP 6: CULLING WHEREVER IN THE GAME THAT OLD TRAINER OF YOURS WAS REFERENCED

Because we're replacing a trainer slot we also need to go hunt down anywhere that opponent constant was referenced and gut the scripts.

In my repo that I'm using as an example, all the hoenn maps themselves were already removed. But in your stock pokeemerald, you'll need to go to Mt. Chimney on porymap and delete the overworld sprite object associated with Hiker Sawyer.

You'll also need to comment out the lines where their scripts were. Like so:


  • STEP 7: ADDING IN THE NEW TEXT FOR YOUR TRAINER

In base pokeemerald, I guess because gamefreak decided to shove it all in another bank or something, all the text for trainers is stored in data\text\trainers.inc

You don't have to put your new trainer's text here. I mean you can if you want to be like gamefreak, but who wants to be like them? You can put your new text pointers right underneath your trainer script on your map's scripts file like so:

What's that? How do you format those lines of text? My son, my lad, let me teach you a trick to save you time and keep you from having to count out characters per line line a total troglodyte: abusing poryscript playground's automatic text formatting function!

Just go on this page and delete everything from the example script but the msgbox(format part, then paste in your new unformatted text like so and voila, instantly formatted text that won't overdraw the textboxes!:

I mean, technically you could write your whole script with poryscript too. But I don't know how to do that so at least you can format your text instantly with it.


  • STEP 8: TYING YOUR TRAINER SCRIPT TO THE TRAINER ON PORYMAP

Now, go back to porymap and change the part on your NPC that says Script (NULL) by copy and pasting the script header that points to your trainer's script here. In this case Route2_EventScript_BCTim.


  • STEP 9: DEFINING YOUR TRAINER'S SPRITE, ENCOUNTER MUSIC, TRAINER CLASS, AI, AND NAME

For the next step we'll open up src\data\trainers.h.

Here is where everything related to the trainer is linked to the opponent constant. You'll find another huge data list of every trainer in the game. We set quite a few parameters here:

.partyflags

I don't know what this one does! Every trainer in stock Emerald has it's value set to 0. Don't worry about it I guess.

.trainerClass

Self explanatory, there's a list of class constants to be found in include\constants\trainers.h

.encounterMusic_gender

The music that plays when the trainer walks up to battle. A list of constants can be found in include\constants\songs.h

.trainerPic

Which sprite the trainer uses, again a list is found in include\constants\trainers.h

.items

What items (if any) the trainer uses in battle, with a limit of four. For example here's Flannery's items in her first gym battle: {ITEM_HYPER_POTION, ITEM_HYPER_POTION, ITEM_NONE, ITEM_NONE},

A list can be found in include\constants\items.h

.doublebattle

FALSE or TRUE, you know the deal.

.aiFlags

These determine which AI scripts the trainer is allowed to follow, a list can be found in include\constants\battle_ai.h

.partySize and .party

These two are POINTERS that need to point to the relevant data for your trainer as labeled in trainer_parties.h

Which brings us to our last major part:


  • STEP 10: DEFINING THE TRAINER'S PARTY POKEMON

For this part we'll be in src\data\trainer_parties.h

Here we get to give the trainer we made their data! On our simple Bug Catcher Tim the two defined Pokemon only have two parameters .iv and .lvl along with their species constants.

.lvl is obviously the level, but if you look around at the parties in stock Emerald you'll quickly notice the .iv parameter isn't simply IVs from 0-31. This value actually runs from 0-255, here's a formula from the code someone handed me that explains how it calculates out usable IVs from these numbers:

fixedIV = partyData[i].iv * 31 / 255;

Understand?

Anyway, Tim is a simple fella with simple mons. But what about Gym Leaders and opponents with craftier strategies like custom movesets and held items on their Pokemon? Well, have a look at Flannery's team from stock Emerald:

As you can see, at the top where Tim had TrainerMonNoItemDefaultMoves, Flannery has TrainerMonItemCustomMoves to declare that she is a baller and uses Pokemon with items and custom moves.

the .moves and .item macros are also equally straightforward. Just be sure to also define the empty item and move slots as Flannery's team does here.


  • Step 11: COMPILE AND TEST THAT SUCKER INGAME

With all our preparations complete it's time to see Tim in the flesh. MAKE your game and see if he works!

I bet he does, after all, you followed a tutorial :^)

Clone this wiki locally