Add createmon Script Cmd, Support for 2v1Wild Battles (#4688)

* add createmon and setwilddoubleflag script cmds to support createing mons at given player or enemy slots and setting up 2v1 battles

* give givemon macro PARTY_SIZE for tests

* style fixes

* add createmon test

---------

Co-authored-by: ghoulslash <pokevoyager0@gmail.com>
This commit is contained in:
ghoulslash 2024-06-13 13:11:36 -04:00 committed by GitHub
parent 43310716af
commit 353727a805
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 131 additions and 27 deletions

View file

@ -997,7 +997,69 @@
@ Gives the player a Pokémon of the specified species and level, and allows to customize extra parameters. @ Gives the player a Pokémon of the specified species and level, and allows to customize extra parameters.
@ VAR_RESULT will be set to MON_GIVEN_TO_PARTY, MON_GIVEN_TO_PC, or MON_CANT_GIVE depending on the outcome. @ VAR_RESULT will be set to MON_GIVEN_TO_PARTY, MON_GIVEN_TO_PC, or MON_CANT_GIVE depending on the outcome.
.macro givemon species:req, level:req, item, ball, nature, abilityNum, gender, hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv, hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv, move1, move2, move3, move4, isShiny, ggMaxFactor, teraType .macro givemon species:req, level:req, item, ball, nature, abilityNum, gender, hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv, hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv, move1, move2, move3, move4, isShiny, ggMaxFactor, teraType
callnative ScrCmd_givemon callnative ScrCmd_createmon
.byte 0
.byte 6 @ PARTY_SIZE - assign to first empty slot
.set givemon_flags, 0
.2byte \species
.2byte \level
.ifnb \item; .set givemon_flags, givemon_flags | (1 << 0); .endif
.ifnb \ball; .set givemon_flags, givemon_flags | (1 << 1); .endif
.ifnb \nature; .set givemon_flags, givemon_flags | (1 << 2); .endif
.ifnb \abilityNum; .set givemon_flags, givemon_flags | (1 << 3); .endif
.ifnb \gender; .set givemon_flags, givemon_flags | (1 << 4); .endif
.ifnb \hpEv; .set givemon_flags, givemon_flags | (1 << 5); .endif
.ifnb \atkEv; .set givemon_flags, givemon_flags | (1 << 6); .endif
.ifnb \defEv; .set givemon_flags, givemon_flags | (1 << 7); .endif
.ifnb \speedEv; .set givemon_flags, givemon_flags | (1 << 8); .endif
.ifnb \spAtkEv; .set givemon_flags, givemon_flags | (1 << 9); .endif
.ifnb \spDefEv; .set givemon_flags, givemon_flags | (1 << 10); .endif
.ifnb \hpIv; .set givemon_flags, givemon_flags | (1 << 11); .endif
.ifnb \atkIv; .set givemon_flags, givemon_flags | (1 << 12); .endif
.ifnb \defIv; .set givemon_flags, givemon_flags | (1 << 13); .endif
.ifnb \speedIv; .set givemon_flags, givemon_flags | (1 << 14); .endif
.ifnb \spAtkIv; .set givemon_flags, givemon_flags | (1 << 15); .endif
.ifnb \spDefIv; .set givemon_flags, givemon_flags | (1 << 16); .endif
.ifnb \move1; .set givemon_flags, givemon_flags | (1 << 17); .endif
.ifnb \move2; .set givemon_flags, givemon_flags | (1 << 18); .endif
.ifnb \move3; .set givemon_flags, givemon_flags | (1 << 19); .endif
.ifnb \move4; .set givemon_flags, givemon_flags | (1 << 20); .endif
.ifnb \isShiny; .set givemon_flags, givemon_flags | (1 << 21); .endif
.ifnb \ggMaxFactor; .set givemon_flags, givemon_flags | (1 << 22); .endif
.ifnb \teraType; .set givemon_flags, givemon_flags | (1 << 23); .endif
.4byte givemon_flags
.ifnb \item; .2byte \item; .endif
.ifnb \ball; .2byte \ball; .endif
.ifnb \nature; .2byte \nature; .endif
.ifnb \abilityNum; .2byte \abilityNum; .endif
.ifnb \gender; .2byte \gender; .endif
.ifnb \hpEv; .2byte \hpEv; .endif
.ifnb \atkEv; .2byte \atkEv; .endif
.ifnb \defEv; .2byte \defEv; .endif
.ifnb \speedEv; .2byte \speedEv; .endif
.ifnb \spAtkEv; .2byte \spAtkEv; .endif
.ifnb \spDefEv; .2byte \spDefEv; .endif
.ifnb \hpIv; .2byte \hpIv; .endif
.ifnb \atkIv; .2byte \atkIv; .endif
.ifnb \defIv; .2byte \defIv; .endif
.ifnb \speedIv; .2byte \speedIv; .endif
.ifnb \spAtkIv; .2byte \spAtkIv; .endif
.ifnb \spDefIv; .2byte \spDefIv; .endif
.ifnb \move1; .2byte \move1; .endif
.ifnb \move2; .2byte \move2; .endif
.ifnb \move3; .2byte \move3; .endif
.ifnb \move4; .2byte \move4; .endif
.ifnb \isShiny; .2byte \isShiny; .endif
.ifnb \ggMaxFactor; .2byte \ggMaxFactor; .endif
.ifnb \teraType; .2byte \teraType; .endif
.endm
@ creates a mon for a given party and slot
@ otherwise
.macro createmon side:req, slot:req, species:req, level:req, item, ball, nature, abilityNum, gender, hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv, hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv, move1, move2, move3, move4, isShiny, ggMaxFactor, teraType
callnative ScrCmd_createmon
.byte \side @ 0 - player, 1 - opponent
.byte \slot @ 0-5
.set givemon_flags, 0 .set givemon_flags, 0
.2byte \species .2byte \species
.2byte \level .2byte \level
@ -2233,3 +2295,9 @@
.byte \sourceId .byte \sourceId
.byte \targetId .byte \targetId
.endm .endm
@ set the wild double battle flag
@ can be used in conjunection with createmon to set up a wild battle with 2 player mons vs. 1 enemy mon
.macro setwilddoubleflag
callnative ScriptSetDoubleBattleFlag
.endm

View file

@ -2,7 +2,6 @@
#define GUARD_SCRIPT_POKEMON_UTIL_H #define GUARD_SCRIPT_POKEMON_UTIL_H
u32 ScriptGiveMon(u16, u8, u16); u32 ScriptGiveMon(u16, u8, u16);
u32 ScriptGiveMonParameterized(u16, u8, u16, u8, u8, u8, u8, u8 *, u8 *, u16 *, bool8, bool8, u8);
u8 ScriptGiveEgg(u16); u8 ScriptGiveEgg(u16);
void CreateScriptedWildMon(u16, u8, u16); void CreateScriptedWildMon(u16, u8, u16);
void CreateScriptedDoubleWildMon(u16, u8, u16, u16, u8, u16); void CreateScriptedDoubleWildMon(u16, u8, u16, u16, u8, u16);

View file

@ -2467,3 +2467,8 @@ bool8 ScrCmd_warpwhitefade(struct ScriptContext *ctx)
ResetInitialPlayerAvatarState(); ResetInitialPlayerAvatarState();
return TRUE; return TRUE;
} }
void ScriptSetDoubleBattleFlag(struct ScriptContext *ctx)
{
sIsScriptedWildDouble = TRUE;
}

View file

@ -313,7 +313,11 @@ void SetTeraType(struct ScriptContext *ctx)
SetMonData(&gPlayerParty[partyIndex], MON_DATA_TERA_TYPE, &type); SetMonData(&gPlayerParty[partyIndex], MON_DATA_TERA_TYPE, &type);
} }
u32 ScriptGiveMonParameterized(u16 species, u8 level, u16 item, u8 ball, u8 nature, u8 abilityNum, u8 gender, u8 *evs, u8 *ivs, u16 *moves, bool8 isShiny, bool8 ggMaxFactor, u8 teraType) /* Creates a Pokemon via script
* if side/slot are assigned, it will create the mon at the assigned party location
* if slot == PARTY_SIZE, it will give the mon to first available party or storage slot
*/
static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u16 item, u8 ball, u8 nature, u8 abilityNum, u8 gender, u8 *evs, u8 *ivs, u16 *moves, bool8 isShiny, bool8 ggMaxFactor, u8 teraType)
{ {
u16 nationalDexNum; u16 nationalDexNum;
int sentToPc; int sentToPc;
@ -408,34 +412,43 @@ u32 ScriptGiveMonParameterized(u16 species, u8 level, u16 item, u8 ball, u8 natu
SetMonData(&mon, MON_DATA_OT_NAME, gSaveBlock2Ptr->playerName); SetMonData(&mon, MON_DATA_OT_NAME, gSaveBlock2Ptr->playerName);
SetMonData(&mon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender); SetMonData(&mon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender);
// find empty party slot to decide whether the Pokémon goes to the Player's party or the storage system. if (slot < PARTY_SIZE)
for (i = 0; i < PARTY_SIZE; i++)
{ {
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE) if (side == 0)
break; CopyMon(&gPlayerParty[slot], &mon, sizeof(struct Pokemon));
} else
if (i >= PARTY_SIZE) CopyMon(&gEnemyParty[slot], &mon, sizeof(struct Pokemon));
{ sentToPc = MON_GIVEN_TO_PARTY;
sentToPc = CopyMonToPC(&mon);
} }
else else
{ {
sentToPc = MON_GIVEN_TO_PARTY; // find empty party slot to decide whether the Pokémon goes to the Player's party or the storage system.
CopyMon(&gPlayerParty[i], &mon, sizeof(mon)); for (i = 0; i < PARTY_SIZE; i++)
gPlayerPartyCount = i + 1; {
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE)
break;
}
if (i >= PARTY_SIZE)
{
sentToPc = CopyMonToPC(&mon);
}
else
{
sentToPc = MON_GIVEN_TO_PARTY;
CopyMon(&gPlayerParty[i], &mon, sizeof(mon));
gPlayerPartyCount = i + 1;
}
} }
// set pokédex flags if (side == 0)
nationalDexNum = SpeciesToNationalPokedexNum(species);
switch (sentToPc)
{ {
case MON_GIVEN_TO_PARTY: // set pokédex flags
case MON_GIVEN_TO_PC: nationalDexNum = SpeciesToNationalPokedexNum(species);
GetSetPokedexFlag(nationalDexNum, FLAG_SET_SEEN); if (sentToPc != MON_CANT_GIVE)
GetSetPokedexFlag(nationalDexNum, FLAG_SET_CAUGHT); {
break; GetSetPokedexFlag(nationalDexNum, FLAG_SET_SEEN);
case MON_CANT_GIVE: GetSetPokedexFlag(nationalDexNum, FLAG_SET_CAUGHT);
break; }
} }
return sentToPc; return sentToPc;
@ -448,13 +461,17 @@ u32 ScriptGiveMon(u16 species, u8 level, u16 item)
MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1}; // ScriptGiveMonParameterized won't touch the stats' IV. MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1}; // ScriptGiveMonParameterized won't touch the stats' IV.
u16 moves[MAX_MON_MOVES] = {MOVE_NONE, MOVE_NONE, MOVE_NONE, MOVE_NONE}; u16 moves[MAX_MON_MOVES] = {MOVE_NONE, MOVE_NONE, MOVE_NONE, MOVE_NONE};
return ScriptGiveMonParameterized(species, level, item, ITEM_POKE_BALL, NUM_NATURES, NUM_ABILITY_PERSONALITY, MON_GENDERLESS, evs, ivs, moves, FALSE, FALSE, NUMBER_OF_MON_TYPES); return ScriptGiveMonParameterized(0, PARTY_SIZE, species, level, item, ITEM_POKE_BALL, NUM_NATURES, NUM_ABILITY_PERSONALITY, MON_GENDERLESS, evs, ivs, moves, FALSE, FALSE, NUMBER_OF_MON_TYPES);
} }
#define PARSE_FLAG(n, default_) (flags & (1 << (n))) ? VarGet(ScriptReadHalfword(ctx)) : (default_) #define PARSE_FLAG(n, default_) (flags & (1 << (n))) ? VarGet(ScriptReadHalfword(ctx)) : (default_)
void ScrCmd_givemon(struct ScriptContext *ctx) /* Give or create a mon to either player or opponent
*/
void ScrCmd_createmon(struct ScriptContext *ctx)
{ {
u8 side = ScriptReadByte(ctx);
u8 slot = ScriptReadByte(ctx);
u16 species = VarGet(ScriptReadHalfword(ctx)); u16 species = VarGet(ScriptReadHalfword(ctx));
u8 level = VarGet(ScriptReadHalfword(ctx)); u8 level = VarGet(ScriptReadHalfword(ctx));
@ -488,7 +505,7 @@ void ScrCmd_givemon(struct ScriptContext *ctx)
u8 ivs[NUM_STATS] = {hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv}; u8 ivs[NUM_STATS] = {hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv};
u16 moves[MAX_MON_MOVES] = {move1, move2, move3, move4}; u16 moves[MAX_MON_MOVES] = {move1, move2, move3, move4};
gSpecialVar_Result = ScriptGiveMonParameterized(species, level, item, ball, nature, abilityNum, gender, evs, ivs, moves, isShiny, ggMaxFactor, teraType); gSpecialVar_Result = ScriptGiveMonParameterized(side, slot, species, level, item, ball, nature, abilityNum, gender, evs, ivs, moves, isShiny, ggMaxFactor, teraType);
} }
#undef PARSE_FLAG #undef PARSE_FLAG

View file

@ -340,3 +340,18 @@ TEST("checkteratype/setteratype work")
); );
EXPECT(VarGet(VAR_RESULT) == TYPE_FIRE); EXPECT(VarGet(VAR_RESULT) == TYPE_FIRE);
} }
TEST("createmon [simple]")
{
ZeroPlayerPartyMons();
RUN_OVERWORLD_SCRIPT(
createmon 1, 0, SPECIES_WOBBUFFET, 100;
createmon 1, 1, SPECIES_WYNAUT, 10;
);
EXPECT_EQ(GetMonData(&gEnemyParty[0], MON_DATA_SPECIES), SPECIES_WOBBUFFET);
EXPECT_EQ(GetMonData(&gEnemyParty[0], MON_DATA_LEVEL), 100);
EXPECT_EQ(GetMonData(&gEnemyParty[1], MON_DATA_SPECIES), SPECIES_WYNAUT);
EXPECT_EQ(GetMonData(&gEnemyParty[1], MON_DATA_LEVEL), 10);
}