From 353727a805b5406595bf6bb266caa378a6ed9439 Mon Sep 17 00:00:00 2001 From: ghoulslash <41651341+ghoulslash@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:11:36 -0400 Subject: [PATCH] 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 --- asm/macros/event.inc | 70 ++++++++++++++++++++++++++++++++++- include/script_pokemon_util.h | 1 - src/scrcmd.c | 5 +++ src/script_pokemon_util.c | 67 ++++++++++++++++++++------------- test/pokemon.c | 15 ++++++++ 5 files changed, 131 insertions(+), 27 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index e2bf9a06e8..bab06a0da1 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -997,7 +997,69 @@ @ 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. .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 .2byte \species .2byte \level @@ -2233,3 +2295,9 @@ .byte \sourceId .byte \targetId .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 diff --git a/include/script_pokemon_util.h b/include/script_pokemon_util.h index 2f7c2f5bd5..6508295a2d 100644 --- a/include/script_pokemon_util.h +++ b/include/script_pokemon_util.h @@ -2,7 +2,6 @@ #define GUARD_SCRIPT_POKEMON_UTIL_H u32 ScriptGiveMon(u16, u8, u16); -u32 ScriptGiveMonParameterized(u16, u8, u16, u8, u8, u8, u8, u8 *, u8 *, u16 *, bool8, bool8, u8); u8 ScriptGiveEgg(u16); void CreateScriptedWildMon(u16, u8, u16); void CreateScriptedDoubleWildMon(u16, u8, u16, u16, u8, u16); diff --git a/src/scrcmd.c b/src/scrcmd.c index b060fa944d..b642f41ede 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -2467,3 +2467,8 @@ bool8 ScrCmd_warpwhitefade(struct ScriptContext *ctx) ResetInitialPlayerAvatarState(); return TRUE; } + +void ScriptSetDoubleBattleFlag(struct ScriptContext *ctx) +{ + sIsScriptedWildDouble = TRUE; +} diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 0946ec1d15..dba872543a 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -313,7 +313,11 @@ void SetTeraType(struct ScriptContext *ctx) 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; 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_GENDER, &gSaveBlock2Ptr->playerGender); - // find empty party slot to decide whether the Pokémon goes to the Player's party or the storage system. - for (i = 0; i < PARTY_SIZE; i++) + if (slot < PARTY_SIZE) { - if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE) - break; - } - if (i >= PARTY_SIZE) - { - sentToPc = CopyMonToPC(&mon); + if (side == 0) + CopyMon(&gPlayerParty[slot], &mon, sizeof(struct Pokemon)); + else + CopyMon(&gEnemyParty[slot], &mon, sizeof(struct Pokemon)); + sentToPc = MON_GIVEN_TO_PARTY; } else { - sentToPc = MON_GIVEN_TO_PARTY; - CopyMon(&gPlayerParty[i], &mon, sizeof(mon)); - gPlayerPartyCount = i + 1; + // find empty party slot to decide whether the Pokémon goes to the Player's party or the storage system. + for (i = 0; i < PARTY_SIZE; i++) + { + 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 - nationalDexNum = SpeciesToNationalPokedexNum(species); - switch (sentToPc) + if (side == 0) { - case MON_GIVEN_TO_PARTY: - case MON_GIVEN_TO_PC: - GetSetPokedexFlag(nationalDexNum, FLAG_SET_SEEN); - GetSetPokedexFlag(nationalDexNum, FLAG_SET_CAUGHT); - break; - case MON_CANT_GIVE: - break; + // set pokédex flags + nationalDexNum = SpeciesToNationalPokedexNum(species); + if (sentToPc != MON_CANT_GIVE) + { + GetSetPokedexFlag(nationalDexNum, FLAG_SET_SEEN); + GetSetPokedexFlag(nationalDexNum, FLAG_SET_CAUGHT); + } } 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. 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_) -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)); 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}; 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 diff --git a/test/pokemon.c b/test/pokemon.c index fa96b47f26..8419b9c7e1 100644 --- a/test/pokemon.c +++ b/test/pokemon.c @@ -340,3 +340,18 @@ TEST("checkteratype/setteratype work") ); 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); +}