Advertiser Content

Pokeemerald Move Tutor Expansion

Started by Buffel Saft June 7th, 2019 6:38 AM
  • 772 views
  • 3 replies

Buffel Saft

Male
Seen 16 Hours Ago
Posted 16 Hours Ago
570 posts
6.1 Years
Expanding the number of move tutors is surprisingly simple, but as there are a few ways it can be done I figured it'd be best as its own thread so that people can share their alternatives. This hasn't been made into a separate repository for a number of reasons, but I'll share the method here.

Step One: Define more Tutor moves
Spoiler:

Open src/data/pokemon/tutor_learnsets.h. At the top of the file you'll see two lists of moves, which look something like this:
#define TUTOR_MOVE_MEGA_PUNCH 0
#define TUTOR_MOVE_SWORDS_DANCE 1
#define TUTOR_MOVE_MEGA_KICK 2
#define TUTOR_MOVE_BODY_SLAM 3
#define TUTOR_MOVE_DOUBLE_EDGE 4
#define TUTOR_MOVE_COUNTER 5
#define TUTOR_MOVE_SEISMIC_TOSS 6
#define TUTOR_MOVE_MIMIC 7
#define TUTOR_MOVE_METRONOME 8
#define TUTOR_MOVE_SOFT_BOILED 9
#define TUTOR_MOVE_DREAM_EATER 10
#define TUTOR_MOVE_THUNDER_WAVE 11
#define TUTOR_MOVE_EXPLOSION 12
#define TUTOR_MOVE_ROCK_SLIDE 13
#define TUTOR_MOVE_SUBSTITUTE 14
...
const u16 gTutorMoves[] =
{
    [TUTOR_MOVE_MEGA_PUNCH] = MOVE_MEGA_PUNCH,
    [TUTOR_MOVE_SWORDS_DANCE] = MOVE_SWORDS_DANCE,
    [TUTOR_MOVE_MEGA_KICK] = MOVE_MEGA_KICK,
    [TUTOR_MOVE_BODY_SLAM] = MOVE_BODY_SLAM,
    [TUTOR_MOVE_DOUBLE_EDGE] = MOVE_DOUBLE_EDGE,
    [TUTOR_MOVE_COUNTER] = MOVE_COUNTER,
    [TUTOR_MOVE_SEISMIC_TOSS] = MOVE_SEISMIC_TOSS,
    [TUTOR_MOVE_MIMIC] = MOVE_MIMIC,
    [TUTOR_MOVE_METRONOME] = MOVE_METRONOME,
    [TUTOR_MOVE_SOFT_BOILED] = MOVE_SOFT_BOILED,
    [TUTOR_MOVE_DREAM_EATER] = MOVE_DREAM_EATER,
    [TUTOR_MOVE_THUNDER_WAVE] = MOVE_THUNDER_WAVE,
    [TUTOR_MOVE_EXPLOSION] = MOVE_EXPLOSION,
    [TUTOR_MOVE_ROCK_SLIDE] = MOVE_ROCK_SLIDE,
    [TUTOR_MOVE_SUBSTITUTE] = MOVE_SUBSTITUTE,
...
The first list assigns a number to each move, which you'll use when making a move tutor script. The second list tells the game which move corresponds to each of the numbers in the first list. To add more moves, simply add new entries to each list, or replace both entirely. You can add as many moves as you like; I'll be replacing the old list with 80 new moves as an example, which looks like this:

Spoiler:
#define TUTOR_MOVE_GRASS_PLEDGE	1
#define TUTOR_MOVE_FIRE_PLEDGE	2
#define TUTOR_MOVE_WATER_PLEDGE	3
#define TUTOR_MOVE_FRENZY_PLANT	4
#define TUTOR_MOVE_BLAST_BURN	5
#define TUTOR_MOVE_HYDRO_CANNON	6
#define TUTOR_MOVE_DRACO_METEOR	7
#define TUTOR_MOVE_DRAGON_ASCENT	8
#define TUTOR_MOVE_SECRET_SWORD	9
#define TUTOR_MOVE_RELIC_SONG	10
#define TUTOR_MOVE_FURY_CUTTER	11
#define TUTOR_MOVE_ROLLOUT	12
#define TUTOR_MOVE_SEISMIC_TOSS	13
#define TUTOR_MOVE_COVET	14
#define TUTOR_MOVE_VACUUM_WAVE	15
#define TUTOR_MOVE_SHOCK_WAVE	16
#define TUTOR_MOVE_BUG_BITE	17
#define TUTOR_MOVE_AIR_CUTTER	18
#define TUTOR_MOVE_SWIFT	19
#define TUTOR_MOVE_SNATCH	20
#define TUTOR_MOVE_MIMIC	21
#define TUTOR_MOVE_MUD_SLAP	22
#define TUTOR_MOVE_METRONOME	23
#define TUTOR_MOVE_OMINOUS_WIND	24
#define TUTOR_MOVE_SUPER_FANG	25
#define TUTOR_MOVE_COUNTER	26
#define TUTOR_MOVE_SIGNAL_BEAM	27
#define TUTOR_MOVE_DEFOG	28
#define TUTOR_MOVE_MAGIC_COAT	29
#define TUTOR_MOVE_GRAVITY	30
#define TUTOR_MOVE_SEED_BOMB	31
#define TUTOR_MOVE_DYNAMIC_PUNCH	32
#define TUTOR_MOVE_SYNTHESIS	33
#define TUTOR_MOVE_PAIN_SPLIT	34
#define TUTOR_MOVE_UPROAR	35
#define TUTOR_MOVE_HONE_CLAWS	36
#define TUTOR_MOVE_ENDEAVOR	37
#define TUTOR_MOVE_WORRY_SEED	38
#define TUTOR_MOVE_PSYCH_UP	39
#define TUTOR_MOVE_THUNDER_PUNCH	40
#define TUTOR_MOVE_FIRE_PUNCH	41
#define TUTOR_MOVE_ICE_PUNCH	42
#define TUTOR_MOVE_ICY_WIND	43
#define TUTOR_MOVE_ELECTROWEB	44
#define TUTOR_MOVE_LOW_KICK	45
#define TUTOR_MOVE_IRON_DEFENSE	46
#define TUTOR_MOVE_MAGNET_RISE	47
#define TUTOR_MOVE_TAILWIND	48
#define TUTOR_MOVE_ZEN_HEADBUTT	49
#define TUTOR_MOVE_DUAL_CHOP	50
#define TUTOR_MOVE_BODY_SLAM	51
#define TUTOR_MOVE_BRINE	52
#define TUTOR_MOVE_SWAGGER	53
#define TUTOR_MOVE_IRON_HEAD	54
#define TUTOR_MOVE_SOFT_BOILED	55
#define TUTOR_MOVE_LAST_RESORT	56
#define TUTOR_MOVE_ROLE_PLAY	57
#define TUTOR_MOVE_DRILL_RUN	58
#define TUTOR_MOVE_TRICK	59
#define TUTOR_MOVE_AQUA_TAIL	60
#define TUTOR_MOVE_SKY_ATTACK	61
#define TUTOR_MOVE_FOUL_PLAY	62
#define TUTOR_MOVE_DOUBLE_EDGE	63
#define TUTOR_MOVE_BOUNCE	64
#define TUTOR_MOVE_HEAL_BELL	65
#define TUTOR_MOVE_SUPERPOWER	66
#define TUTOR_MOVE_HELPING_HAND	67
#define TUTOR_MOVE_HEAT_WAVE	68
#define TUTOR_MOVE_OUTRAGE	69
#define TUTOR_MOVE_KNOCK_OFF	70
#define TUTOR_MOVE_LIQUIDATION	71
#define TUTOR_MOVE_HYPER_VOICE	72
#define TUTOR_MOVE_EARTH_POWER	73
#define TUTOR_MOVE_GUNK_SHOT	74
#define TUTOR_MOVE_AURA_SPHERE	75
#define TUTOR_MOVE_THROAT_CHOP	76
#define TUTOR_MOVE_GASTRO_ACID	77
#define TUTOR_MOVE_POWER_GEM	78
#define TUTOR_MOVE_HURRICANE	79

const u16 gTutorMoves[] =
{
	[TUTOR_MOVE_GRASS_PLEDGE] = MOVE_GRASS_PLEDGE,
	[TUTOR_MOVE_FIRE_PLEDGE] = MOVE_FIRE_PLEDGE,
	[TUTOR_MOVE_WATER_PLEDGE] = MOVE_WATER_PLEDGE,
	[TUTOR_MOVE_FRENZY_PLANT] = MOVE_FRENZY_PLANT,
	[TUTOR_MOVE_BLAST_BURN] = MOVE_BLAST_BURN,
	[TUTOR_MOVE_HYDRO_CANNON] = MOVE_HYDRO_CANNON,
	[TUTOR_MOVE_DRACO_METEOR] = MOVE_DRACO_METEOR,
	[TUTOR_MOVE_DRAGON_ASCENT] = MOVE_DRAGON_ASCENT,
	[TUTOR_MOVE_SECRET_SWORD] = MOVE_SECRET_SWORD,
	[TUTOR_MOVE_RELIC_SONG] = MOVE_RELIC_SONG,
	[TUTOR_MOVE_FURY_CUTTER] = MOVE_FURY_CUTTER,
	[TUTOR_MOVE_ROLLOUT] = MOVE_ROLLOUT,
	[TUTOR_MOVE_SEISMIC_TOSS] = MOVE_SEISMIC_TOSS,
	[TUTOR_MOVE_COVET] = MOVE_COVET,
	[TUTOR_MOVE_VACUUM_WAVE] = MOVE_VACUUM_WAVE,
	[TUTOR_MOVE_SHOCK_WAVE] = MOVE_SHOCK_WAVE,
	[TUTOR_MOVE_BUG_BITE] = MOVE_BUG_BITE,
	[TUTOR_MOVE_AIR_CUTTER] = MOVE_AIR_CUTTER,
	[TUTOR_MOVE_SWIFT] = MOVE_SWIFT,
	[TUTOR_MOVE_SNATCH] = MOVE_SNATCH,
	[TUTOR_MOVE_MIMIC] = MOVE_MIMIC,
	[TUTOR_MOVE_MUD_SLAP] = MOVE_MUD_SLAP,
	[TUTOR_MOVE_METRONOME] = MOVE_METRONOME,
	[TUTOR_MOVE_OMINOUS_WIND] = MOVE_OMINOUS_WIND,
	[TUTOR_MOVE_SUPER_FANG] = MOVE_SUPER_FANG,
	[TUTOR_MOVE_COUNTER] = MOVE_COUNTER,
	[TUTOR_MOVE_SIGNAL_BEAM] = MOVE_SIGNAL_BEAM,
	[TUTOR_MOVE_DEFOG] = MOVE_DEFOG,
	[TUTOR_MOVE_MAGIC_COAT] = MOVE_MAGIC_COAT,
	[TUTOR_MOVE_GRAVITY] = MOVE_GRAVITY,
	[TUTOR_MOVE_SEED_BOMB] = MOVE_SEED_BOMB,
	[TUTOR_MOVE_DYNAMIC_PUNCH] = MOVE_DYNAMIC_PUNCH,
	[TUTOR_MOVE_SYNTHESIS] = MOVE_SYNTHESIS,
	[TUTOR_MOVE_PAIN_SPLIT] = MOVE_PAIN_SPLIT,
	[TUTOR_MOVE_UPROAR] = MOVE_UPROAR,
	[TUTOR_MOVE_HONE_CLAWS] = MOVE_HONE_CLAWS,
	[TUTOR_MOVE_ENDEAVOR] = MOVE_ENDEAVOR,
	[TUTOR_MOVE_WORRY_SEED] = MOVE_WORRY_SEED,
	[TUTOR_MOVE_PSYCH_UP] = MOVE_PSYCH_UP,
	[TUTOR_MOVE_THUNDER_PUNCH] = MOVE_THUNDER_PUNCH,
	[TUTOR_MOVE_FIRE_PUNCH] = MOVE_FIRE_PUNCH,
	[TUTOR_MOVE_ICE_PUNCH] = MOVE_ICE_PUNCH,
	[TUTOR_MOVE_ICY_WIND] = MOVE_ICY_WIND,
	[TUTOR_MOVE_ELECTROWEB] = MOVE_ELECTROWEB,
	[TUTOR_MOVE_LOW_KICK] = MOVE_LOW_KICK,
	[TUTOR_MOVE_IRON_DEFENSE] = MOVE_IRON_DEFENSE,
	[TUTOR_MOVE_MAGNET_RISE] = MOVE_MAGNET_RISE,
	[TUTOR_MOVE_TAILWIND] = MOVE_TAILWIND,
	[TUTOR_MOVE_ZEN_HEADBUTT] = MOVE_ZEN_HEADBUTT,
	[TUTOR_MOVE_DUAL_CHOP] = MOVE_DUAL_CHOP,
	[TUTOR_MOVE_BODY_SLAM] = MOVE_BODY_SLAM,
	[TUTOR_MOVE_BRINE] = MOVE_BRINE,
	[TUTOR_MOVE_SWAGGER] = MOVE_SWAGGER,
	[TUTOR_MOVE_IRON_HEAD] = MOVE_IRON_HEAD,
	[TUTOR_MOVE_SOFT_BOILED] = MOVE_SOFT_BOILED,
	[TUTOR_MOVE_LAST_RESORT] = MOVE_LAST_RESORT,
	[TUTOR_MOVE_ROLE_PLAY] = MOVE_ROLE_PLAY,
	[TUTOR_MOVE_DRILL_RUN] = MOVE_DRILL_RUN,
	[TUTOR_MOVE_TRICK] = MOVE_TRICK,
	[TUTOR_MOVE_AQUA_TAIL] = MOVE_AQUA_TAIL,
	[TUTOR_MOVE_SKY_ATTACK] = MOVE_SKY_ATTACK,
	[TUTOR_MOVE_FOUL_PLAY] = MOVE_FOUL_PLAY,
	[TUTOR_MOVE_DOUBLE_EDGE] = MOVE_DOUBLE_EDGE,
	[TUTOR_MOVE_BOUNCE] = MOVE_BOUNCE,
	[TUTOR_MOVE_HEAL_BELL] = MOVE_HEAL_BELL,
	[TUTOR_MOVE_SUPERPOWER] = MOVE_SUPERPOWER,
	[TUTOR_MOVE_HELPING_HAND] = MOVE_HELPING_HAND,
	[TUTOR_MOVE_HEAT_WAVE] = MOVE_HEAT_WAVE,
	[TUTOR_MOVE_OUTRAGE] = MOVE_OUTRAGE,
	[TUTOR_MOVE_KNOCK_OFF] = MOVE_KNOCK_OFF,
	[TUTOR_MOVE_LIQUIDATION] = MOVE_LIQUIDATION,
	[TUTOR_MOVE_HYPER_VOICE] = MOVE_HYPER_VOICE,
	[TUTOR_MOVE_EARTH_POWER] = MOVE_EARTH_POWER,
	[TUTOR_MOVE_GUNK_SHOT] = MOVE_GUNK_SHOT,
	[TUTOR_MOVE_AURA_SPHERE] = MOVE_AURA_SPHERE,
	[TUTOR_MOVE_THROAT_CHOP] = MOVE_THROAT_CHOP,
	[TUTOR_MOVE_GASTRO_ACID] = MOVE_GASTRO_ACID,
	[TUTOR_MOVE_POWER_GEM] = MOVE_POWER_GEM,
	[TUTOR_MOVE_HURRICANE] = MOVE_HURRICANE,
};


Step Two: Edit/create a compatibility table
Spoiler:

This is the hard/tedious part, especially if you've got expanded Pokemon. Tutor compatibility is stored as an array of bit fields; each bit corresponds to a tutor move, as you defined in step one, and you'll need to generate these for each Pokemon and Tutor move in your game. There are a few ways to do this; personally I chose to sacrifice readable code for an easy way to generate a compatibility table, so that's what I'll be covering here. I'll be storing each Pokemon's list of learnable moves in a multi-dimensional array of 32-bit bit fields, which looks like this:
static const u32 sTutorLearnsets[][3] = {
[SPECIES_NONE] = {0x40300401, 0x40140021, 0x00000020},
[SPECIES_BULBASAUR] = {0x80600802, 0x80280042, 0x00000040},
[SPECIES_IVYSAUR] = {0x80600802, 0x80280042, 0x00000040},
[SPECIES_VENUSAUR] = {0x80600812, 0x80280042, 0x00000060},
[SPECIES_CHARMANDER] = {0x04682804, 0x80280311, 0x00000030},
[SPECIES_CHARMELEON] = {0x04682804, 0x80280311, 0x00000030},
[SPECIES_CHARIZARD] = {0x156C2824, 0x80290311, 0x00008810},
...
As I have 80 moves, each species will need three 32-bit integers to store all the moves it can learn. If you have a different number of moves, you can calculate how many you'll need with some simple math: No. of moves/32 rounded up. e.g 80/32 = 2.5, which rounds up to 3. If you're wondering why this mess is easier to work with than the decomp's existing format, it's because I've made an Excel spreadsheet which generates the entire array automatically, and makes mass editing of it quick and easy (though it's nowhere near as good if you need to make small adjustments later). The sheet is available here; feel free to use it and/or rip the compatibility data if you want it. If you don't have access to Excel you may want to stick with the existing table format, or create your own.

If you're using the spreadsheet's format, note that you should delete or comment out these lines of code, as they won't be needed:
#define TUTOR_LEARNSET(moves) ((u32)(moves))
#define TUTOR(move) ((u64)1 << (TUTOR_##move))


Step Three: Update the compatibility check code
Spoiler:

Open src/party_menu.c and navigate to the following function:
static bool8 CanLearnTutorMove(u16 species, u8 tutor)
{
    if (sTutorLearnsets[species] & (1 << tutor))
        return TRUE;
    else
        return FALSE;
}
Replace it with something like this:
static bool32 CanLearnTutorMove(u16 species, u8 tutor) // note the change to bool32

{

    if (tutor < 32)

    {

        u32 mask = 1 << tutor;

        return sTutorLearnsets[species][0] & mask;

    }

    else if (tutor < 64)

    {

        u32 mask = 1 << (tutor - 32);

        return sTutorLearnsets[species][1] & mask;

    }

    else // thanks to BluRose for suggesting this

    {

        u32 mask = 1 << (tutor - 64);

        return sTutorLearnsets[species][2] & mask;

    }

} 

}
This is essentially the same as the existing code used for TMs; if you'd like an explanation of how it works, let me know in the thread and I'll be happy to walk you through it.
Note that if you've got more than 96 tutor moves you'll need at least one more conditional (else if) branch to handle them.


And that's it! To see how to use a new tutor in a script, take a look at one of the existing ones in data/event_scripts.s (hint: change the setvar VAR_0x8005, <number> to the number of your newly defined tutor)

Also, thanks to Dizzy Egg for the awesome scrolling multichoice code used in the gif above!

BluRose

blu rass

Age 18
Male
rip x
Seen 3 Weeks Ago
Posted 3 Weeks Ago
764 posts
5.5 Years
hey, nice work as always~ awesome to see people doing things with the decomps, & i can't wait to see a good project come out of these

but why do the check if tutor is greater than anything when the else handles that for you?
Spoiler:

static bool32 CanLearnTutorMove(u16 speciesu8 tutor// note the change to bool32

{
    if (
tutor 32)
    {
        
u32 mask << tutor;
        return 
sTutorLearnsets[species][0] & mask;
    }
    else if (
tutor 64)
    {
        
u32 mask << (tutor 32);
        return 
sTutorLearnsets[species][1] & mask;
    }
    else
    {
        
u32 mask << (tutor 64);
        return 
sTutorLearnsets[species][2] & mask;
    }

am i cool yet
Seen 1 Day Ago
Posted June 7th, 2019
11 posts
2.7 Years
Hey!

That's a great work, I was talking about do something similar with BluRose before. Anyway I have to say that this is a weird way of lose a lot of space, because you're using a lot of data for each mon. Think that with the original way, you just need a couple bytes for each pokemon. But that's also functional, so, it's perfect like that.

I hope to see more of your work here :D

Buffel Saft

Male
Seen 16 Hours Ago
Posted 16 Hours Ago
570 posts
6.1 Years
hey, nice work as always~ awesome to see people doing things with the decomps, & i can't wait to see a good project come out of these

but why do the check if tutor is greater than anything when the else handles that for you?
Spoiler:

static bool32 CanLearnTutorMove(u16 speciesu8 tutor// note the change to bool32

{
    if (
tutor 32)
    {
        
u32 mask << tutor;
        return 
sTutorLearnsets[species][0] & mask;
    }
    else if (
tutor 64)
    {
        
u32 mask << (tutor 32);
        return 
sTutorLearnsets[species][1] & mask;
    }
    else
    {
        
u32 mask << (tutor 64);
        return 
sTutorLearnsets[species][2] & mask;
    }

Thanks! I'm definitely looking forward to seeing a full game made with the decomps too, there's so much impressive stuff being done for them already.

Yeah that's much better code, I'll update the OP to include it. Thanks for the constructive criticism!

Hey!

That's a great work, I was talking about do something similar with BluRose before. Anyway I have to say that this is a weird way of lose a lot of space, because you're using a lot of data for each mon. Think that with the original way, you just need a couple bytes for each pokemon. But that's also functional, so, it's perfect like that.

I hope to see more of your work here :D
Thanks! The only reason I went with this method was that I'd already made the spreadsheet for an old binary project, and didn't want to spend more time on the tutor compatibility part, so this was just the easiest solution. As you say it's definitely not the most efficient way to do it, but it works 😅. Could save some space using five 16-bit bitfields instead of three 32-bit ones, but I do plan to add a few more tutor moves later on so some of that wasted space will eventually be used.
Advertiser Content