- 14
- Posts
- 12
- Years
- Seen Oct 4, 2020
Hello again.
For my hack, there are a few points in the story where it would be really nice if the player participated in a multi battle with and/or against some of the major characters. Pokemon Emerald is the only gen3 game (and the first game in the series), to implement this feature in the player/Steven v Maxie/Tabitha battle at the Mosdeep Space Center. Unfortunately for me, this battle is hard-coded into the game, meaning that from XSE you can only ever start that one battle that's stored in the ROM. The script looks something like this (got it from here annotated by me):
Since the battle is hard-coded unto the game, you only have that one to play with. You can change Steven's party pokemon simple enough, and change Maxie and Tabitha to two different trainers, and make your one tag battle a different tag battle. But if you want multiple tag battles like me, you're out of luck.
The now-deprecated and amazing (yet frustratingly undocumented) Pokemon Battle Engine Upgrade has implemented a way to have as many different custom tag battles as you want, but it doesn't work for what I want to do for a number of reasons. So I've decided to implement this feature on my own as well as some other niche battle types if I can figure it out (disable bag, disable exp, that kind of stuff). special 0x3E happens to queue a battle task to start later using whatever trainer battle information is currently in RAM. So I've been abusing it: if I can write some ASM that changes all the necessary flags to start the right kind of battle I want, hypothetically it should just do, yes?
Okay, so onto the problem.
So far, I can get a tag battle to load correctly with custom partner pokemon and custom opponents. However, when the battle starts, it looks like this (good)
and this (not good)
and this (also not good)
So first of all, I want to stop and acknowledge how funny this is. This is hilarious I mean they just stare at you all dopey like. Why is this the default behavior? Why is it not just programmed to load a default backsprite? Why is it even possible to load front sprites in the backsprite slots. Like... WHAT? And the game is doing it purposefully too; if you change the partner the partner sprite changes to some different frontsprite. Just... whhhyyyyyy.
The rest of the battle loads fine too. The frontsprites scroll off screen and throw a pokeball like nothing's wrong and the game plays as it should from there-on.
So TL;DR - how do I tell the game which backsprites I want it to load in ASM (or C, same thing at this point)? My expectation is that in RAM, there's a pointer somewhere for your backsprite and a pointer somewhere for the partner backsprite and two more for the palettes, and if I change those I change what gets loaded. If not, there's a set-up function I'm forgetting to call. But where?
I know thanks to staring at the Battle Engine Upgrade source code for days now that there's a variable in ram for storing the index of the player sprite (at 0x02022FF3) and one for the partner (at 0x02022FF2). But setting those does NOT make the game load the correct sprites. The Battle Engine Upgrade rewrites the entire battle system so it has its own way of handling this. I tried to copy what it does but it just segfaulted the ROM and it seems overly complicated and not applicable with the route I ended up taking.
If you don't know where these pointers to change are, some guidance on how to start looking for these pointers would be great too. I need to learn how to do research, it would make the process at lot easier. For now though, I'm pretty stuck.
Oh, and, if it helps, here's what the function I wrote that sets all the necessary tag battle flags looks like. Basically all the offsets I just stole from the Battle Engine Upgrade's ld file. It's in C, but I'll happily take and adapt a solution in ASM.
Thanks in advance
For my hack, there are a few points in the story where it would be really nice if the player participated in a multi battle with and/or against some of the major characters. Pokemon Emerald is the only gen3 game (and the first game in the series), to implement this feature in the player/Steven v Maxie/Tabitha battle at the Mosdeep Space Center. Unfortunately for me, this battle is hard-coded into the game, meaning that from XSE you can only ever start that one battle that's stored in the ROM. The script looks something like this (got it from here annotated by me):
Spoiler:
Code:
#dynamic 0xE41046
#org @start
lock
faceplayer
special 0x28 'save the player's party in memory
fadescreen 0x1
special 0x2A 'take the player to a screen where they can select up to three pokemon to participate
waitstate
compare LASTRESULT 0x0 'handle if they canceled out
if 0x5 goto @it
special 0x29
goto @no
#org @no
msgbox @1 0x6
release
end
#org @1
= hurry
#org @it
special 0xFB 'without this, the player's party doesn't load
setvar 0x8004 0x2 'this with special 0xEA makes it so your party keeps its damage after the fight
setvar 0x8005 0x4 'it's unclear what this parameter does
special 0xEA
setvar 0x8004 0x8 'this with special 0xEF loads the mosdeep steven fight
setvar 0x8005 0x0 'again, unclear what this does
special 0xEF
waitstate
setvar 0x8004 0x6
special 0xEA 'honestly have no idea what this special does but you need it. Probably reapplies the damage to the party?
special 0x29 'reload the player's party pokemon that did not participate, and reset the original order
copyvar 0x8000 LASTRESULT
compare 0x8000 0x1
if 0x1 goto @after
fadescreen 0x1 'handle loss, one of the few battles that keep going when you lose
special 0xCB
waitstate
release
end
#org @after
msgbox @2 0x6
release
end
#org @2
= thanks :^D
Since the battle is hard-coded unto the game, you only have that one to play with. You can change Steven's party pokemon simple enough, and change Maxie and Tabitha to two different trainers, and make your one tag battle a different tag battle. But if you want multiple tag battles like me, you're out of luck.
The now-deprecated and amazing (yet frustratingly undocumented) Pokemon Battle Engine Upgrade has implemented a way to have as many different custom tag battles as you want, but it doesn't work for what I want to do for a number of reasons. So I've decided to implement this feature on my own as well as some other niche battle types if I can figure it out (disable bag, disable exp, that kind of stuff). special 0x3E happens to queue a battle task to start later using whatever trainer battle information is currently in RAM. So I've been abusing it: if I can write some ASM that changes all the necessary flags to start the right kind of battle I want, hypothetically it should just do, yes?
Okay, so onto the problem.
So far, I can get a tag battle to load correctly with custom partner pokemon and custom opponents. However, when the battle starts, it looks like this (good)
![[PokeCommunity.com] [Emerald] Setting or changing player backsprite before a battle (tag battles, etc) [PokeCommunity.com] [Emerald] Setting or changing player backsprite before a battle (tag battles, etc)](https://i.ibb.co/HCQhqvD/REAL-SUCCESS.png)
and this (not good)
![[PokeCommunity.com] [Emerald] Setting or changing player backsprite before a battle (tag battles, etc) [PokeCommunity.com] [Emerald] Setting or changing player backsprite before a battle (tag battles, etc)](https://i.ibb.co/k9wr5K3/nope2.png)
and this (also not good)
![[PokeCommunity.com] [Emerald] Setting or changing player backsprite before a battle (tag battles, etc) [PokeCommunity.com] [Emerald] Setting or changing player backsprite before a battle (tag battles, etc)](https://i.ibb.co/w0FL85K/okayokay.png)
So first of all, I want to stop and acknowledge how funny this is. This is hilarious I mean they just stare at you all dopey like. Why is this the default behavior? Why is it not just programmed to load a default backsprite? Why is it even possible to load front sprites in the backsprite slots. Like... WHAT? And the game is doing it purposefully too; if you change the partner the partner sprite changes to some different frontsprite. Just... whhhyyyyyy.
The rest of the battle loads fine too. The frontsprites scroll off screen and throw a pokeball like nothing's wrong and the game plays as it should from there-on.
So TL;DR - how do I tell the game which backsprites I want it to load in ASM (or C, same thing at this point)? My expectation is that in RAM, there's a pointer somewhere for your backsprite and a pointer somewhere for the partner backsprite and two more for the palettes, and if I change those I change what gets loaded. If not, there's a set-up function I'm forgetting to call. But where?
I know thanks to staring at the Battle Engine Upgrade source code for days now that there's a variable in ram for storing the index of the player sprite (at 0x02022FF3) and one for the partner (at 0x02022FF2). But setting those does NOT make the game load the correct sprites. The Battle Engine Upgrade rewrites the entire battle system so it has its own way of handling this. I tried to copy what it does but it just segfaulted the ROM and it seems overly complicated and not applicable with the route I ended up taking.
If you don't know where these pointers to change are, some guidance on how to start looking for these pointers would be great too. I need to learn how to do research, it would make the process at lot easier. For now though, I'm pretty stuck.
Oh, and, if it helps, here's what the function I wrote that sets all the necessary tag battle flags looks like. Basically all the offsets I just stole from the Battle Engine Upgrade's ld file. It's in C, but I'll happily take and adapt a solution in ASM.
Spoiler:
Code:
// poorly named, does not start tag battle, merely makes it so the next battle that is loaded is a tag battle
void start_tag_battle(void) {
// clear the last 3 slots in the player's party
memset(&party_player[3], 0, sizeof(struct pokemon) * 3);
/* to keep things predictable, the trainer ids for the
* partner battles are just 0xABCD and whatever partner battle
* this is at the end
*/
uint32_t TID = (0xABCD << 0x10) | var_8004;
// this loop changes the last 3 members of the player's party to the
// partner pokemon, irrelevant to
for (int i = 0; i < 3; i++) {
// this is confirmed working, so it's irrelevant to the post
// ... ... ...
}
// not actually sure if this does anything, but I'm too afraid to change it
// function at 0x81D6180
battle_init();
// make sure the game knows this is a double battle
// with an AI partner and who the two opponents are
var_8015_trainer_opponent_A = var_8005;
trainer_opponent_B = var_8006;
// battle_flags at 0x02022FEC
battle_flags.trainer = 1;
battle_flags.double_battle = 1;
battle_flags.player_ingame_partner = 1;
battle_flags.player_partner = 1;
battle_flags.multibattle = 1;
// This is Youngster Connor from the battle frontier
// partner_slot at 0x02038BCE
partner_slot = 1;
// I tried setting these, but nothing changes
// these are numbers between 0 and 7
// partner_sprite at 0x02022FF2
partner_sprite = partner_table[var_8004].sprite;
// player_backsprite at 0x02022FF3
player_backsprite = sav2->gender;
}
Thanks in advance