Implemented Custom/Complex/Expanded GiveMon scripting command (#3924)

* Introducing an expanded givemon

* Added debug features to check a Pokémon's EV and IV

* Added a parameter to set a custom mon's gender

* Added a debug feature to clear the party

* Defined the EV/IV getters in gSpecials

* Added Gigantamax Factor toggle to givecustommon

* Updated Gigantamax Factor label in givecustommon macro

* Added tera type parameter to givecustommon

Misc. changes:
-Added a few harmless comments to CreateCustomMon for consistency reasons.

* Cleaned up the code inside CreateCustomMon a bit

Also updated the values assigned to the parameters of ScriptGiveCustomMon
This is temporary though. I'll probably end up turning them into 2byte parameters so they can be filled when the scripting command is called by using variables once I solve the bigger problem that the scripting command is currently facing.

* Foolproofed the Poké Ball check in CreateCustomMon

* Assigned a default gender to givecustommon
This solved the nasty issue by which the command wasn't working properly if you didn't fill in each parameter when calling givecustommon in a script.

* Reinforced the gender checks at CreateCustomMon

* Re-reinforced the gender checks at CreateCustomMon

* Compressed givecustommon and added tests

-Made givecustommon skip unspecified parameters.
-Added scripting variables support for every parameter.
-Added tests.

* Updated the default values of some ScriptGiveCustomMon parameters

* Replaced vanilla's givemon with givecustommon

Misc. Changes:
-Renamed CreateCustomMon to ScriptGiveMonParameterized.
 -The truth is that the function was never limited to creating the skeleton of a Pokémon like the actual CreateMon functions do, so that label was never correct. The function was always an expanded ScriptGiveMon.
-Moved the core functions to src/script_pokemon_util.c which is where they actually belong.
-Updated ScriptGiveMonParameterized a little to incorporateb changes that were applied to the original ScriptGiveMon, namely, Synchronize ability and form change handling.
-Introduced a new ScriptGiveMon to replace the original one.

* Corrected givecustommon tests

* Fixed the default IV values for the new givemon

* Updated DebugAction_Party_ClearParty for consistency with the other debug functions

* Updated the text strings used by the Check EV/IV debug features

---------

Co-authored-by: Martin Griffin <martinrgriffin@gmail.com>
Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
This commit is contained in:
LOuroboros 2024-02-08 05:11:13 -03:00 committed by GitHub
parent 8d4c3a8acb
commit 916e4814bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 449 additions and 66 deletions

View File

@ -984,16 +984,62 @@
.byte \textTop
.endm
@ Gives the player a Pokémon of the specified species and level, holding the specified item. The trailing 0s are unused 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.
.macro givemon species:req, level:req, item=ITEM_NONE
.byte 0x79
.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
.set givemon_flags, 0
.2byte \species
.byte \level
.2byte \item
.4byte 0
.4byte 0
.byte 0
.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
@ Gives the player an Egg of the specified species.

View File

@ -121,7 +121,7 @@ gScriptCmdTable::
.4byte ScrCmd_hidemonpic @ 0x76
.4byte ScrCmd_showcontestpainting @ 0x77
.4byte ScrCmd_braillemessage @ 0x78
.4byte ScrCmd_givemon @ 0x79
.4byte ScrCmd_nop1 @ 0x79
.4byte ScrCmd_giveegg @ 0x7a
.4byte ScrCmd_setmonmove @ 0x7b
.4byte ScrCmd_checkpartymove @ 0x7c

View File

@ -95,6 +95,48 @@ Debug_BoxFilledMessage::
Debug_BoxFilledMessage_Text:
.string "Storage boxes filled!$"
Debug_EventScript_CheckEV::
lockall
getpartysize
goto_if_eq VAR_RESULT, 0, Debug_HatchAnEgg_NoPokemon
special ChoosePartyMon
waitstate
goto_if_ge VAR_0x8004, PARTY_SIZE, Debug_EventScript_CheckEV_End
callnative Script_GetChosenMonOffensiveEV
msgbox Debug_EventScript_Text_OffensiveEV, MSGBOX_DEFAULT
callnative Script_GetChosenMonDefensiveEV
msgbox Debug_EventScript_Text_DefensiveEV, MSGBOX_DEFAULT
Debug_EventScript_CheckEV_End::
releaseall
end
Debug_EventScript_Text_OffensiveEV:
.string "ATK EV: {STR_VAR_1}, SPATK EV: {STR_VAR_2}, SPEED EV: {STR_VAR_3}$"
Debug_EventScript_Text_DefensiveEV:
.string "HP EV: {STR_VAR_1}, DEF EV: {STR_VAR_2}, SPDEF EV: {STR_VAR_3}$"
Debug_EventScript_CheckIV::
lockall
getpartysize
goto_if_eq VAR_RESULT, 0, Debug_HatchAnEgg_NoPokemon
special ChoosePartyMon
waitstate
goto_if_ge VAR_0x8004, PARTY_SIZE, Debug_EventScript_CheckIV_End
callnative Script_GetChosenMonOffensiveIV
msgbox Debug_EventScript_Text_OffensiveIV, MSGBOX_DEFAULT
callnative Script_GetChosenMonDefensiveIV
msgbox Debug_EventScript_Text_DefensiveIV, MSGBOX_DEFAULT
Debug_EventScript_CheckIV_End::
releaseall
end
Debug_EventScript_Text_OffensiveIV:
.string "ATK IV: {STR_VAR_1}, SPATK IV: {STR_VAR_2}, SPEED IV: {STR_VAR_3}$"
Debug_EventScript_Text_DefensiveIV:
.string "HP IV: {STR_VAR_1}, DEF IV: {STR_VAR_2}, SPDEF IV: {STR_VAR_3}$"
Debug_EventScript_Script_1::
end
@ -247,4 +289,3 @@ Debug_FlagsAndVarNotSetBattleConfigMessage_Text:
.string "Feature unavailable! Please define a\n"
.string "usable flag and a usable var in:\l"
.string "'include/config/battle.h'!$"

View File

@ -550,3 +550,7 @@ gSpecials::
def_special ObjectEventInteractionBerryHasWeed
def_special ObjectEventInteractionBerryHasPests
def_special CanWaterBerryPlot
def_special Script_GetChosenMonOffensiveEV
def_special Script_GetChosenMonDefensiveEV
def_special Script_GetChosenMonOffensiveIV
def_special Script_GetChosenMonDefensiveIV

View File

@ -1,12 +1,17 @@
#ifndef GUARD_SCRIPT_POKEMON_UTIL_H
#define GUARD_SCRIPT_POKEMON_UTIL_H
u8 ScriptGiveMon(u16, u8, u16, u32, u32, u8);
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);
void ScriptSetMonMoveSlot(u8, u16, u8);
void ReducePlayerPartyToSelectedMons(void);
void HealPlayerParty(void);
void Script_GetChosenMonOffensiveEV(void);
void Script_GetChosenMonDefensiveEV(void);
void Script_GetChosenMonOffensiveIV(void);
void Script_GetChosenMonDefensiveIV(void);
#endif // GUARD_SCRIPT_POKEMON_UTIL_H

View File

@ -966,7 +966,7 @@ static void CB2_GiveStarter(void)
*GetVarPointer(VAR_STARTER_MON) = gSpecialVar_Result;
starterMon = GetStarterPokemon(gSpecialVar_Result);
ScriptGiveMon(starterMon, 5, ITEM_NONE, 0, 0, 0);
ScriptGiveMon(starterMon, 5, ITEM_NONE);
ResetTasks();
PlayBattleBGM();
SetMainCallback2(CB2_StartFirstBattle);

View File

@ -127,6 +127,9 @@ enum PartyDebugMenu
DEBUG_PARTY_MENU_ITEM_HATCH_AN_EGG,
DEBUG_PARTY_MENU_ITEM_HEAL_PARTY,
DEBUG_PARTY_MENU_ITEM_POISON_MONS,
DEBUG_PARTY_MENU_ITEM_CHECK_EV,
DEBUG_PARTY_MENU_ITEM_CHECK_IV,
DEBUG_PARTY_MENU_ITEM_CLEAR_PARTY,
};
enum ScriptDebugMenu
@ -389,6 +392,9 @@ static void DebugAction_Party_MoveReminder(u8 taskId);
static void DebugAction_Party_HatchAnEgg(u8 taskId);
static void DebugAction_Party_HealParty(u8 taskId);
static void DebugAction_Party_PoisonMons(u8 taskId);
static void DebugAction_Party_CheckEV(u8 taskId);
static void DebugAction_Party_CheckIV(u8 taskId);
static void DebugAction_Party_ClearParty(u8 taskId);
static void DebugAction_FlagsVars_Flags(u8 taskId);
static void DebugAction_FlagsVars_FlagsSelect(u8 taskId);
@ -446,6 +452,8 @@ static void DebugAction_BerryFunctions_Weeds(u8 taskId);
extern const u8 Debug_FlagsNotSetOverworldConfigMessage[];
extern const u8 Debug_FlagsNotSetBattleConfigMessage[];
extern const u8 Debug_FlagsAndVarNotSetBattleConfigMessage[];
extern const u8 Debug_EventScript_CheckEV[];
extern const u8 Debug_EventScript_CheckIV[];
extern const u8 Debug_EventScript_Script_1[];
extern const u8 Debug_EventScript_Script_2[];
extern const u8 Debug_EventScript_Script_3[];
@ -541,6 +549,9 @@ static const u8 sDebugText_Party_MoveReminder[] = _("Move Reminder");
static const u8 sDebugText_Party_HatchAnEgg[] = _("Hatch an Egg");
static const u8 sDebugText_Party_HealParty[] = _("Heal party");
static const u8 sDebugText_Party_PoisonParty[] = _("Poison party");
static const u8 sDebugText_Party_CheckEV[] = _("Check EV");
static const u8 sDebugText_Party_CheckIV[] = _("Check IV");
static const u8 sDebugText_Party_ClearParty[] = _("Clear Party");
// Flags/Vars Menu
static const u8 sDebugText_FlagsVars_Flags[] = _("Set Flag XYZ…{CLEAR_TO 110}{RIGHT_ARROW}");
static const u8 sDebugText_FlagsVars_Flag[] = _("Flag: {STR_VAR_1}{CLEAR_TO 90}\n{STR_VAR_2}{CLEAR_TO 90}\n{STR_VAR_3}");
@ -736,6 +747,9 @@ static const struct ListMenuItem sDebugMenu_Items_Party[] =
[DEBUG_PARTY_MENU_ITEM_HATCH_AN_EGG] = {sDebugText_Party_HatchAnEgg, DEBUG_PARTY_MENU_ITEM_HATCH_AN_EGG},
[DEBUG_PARTY_MENU_ITEM_HEAL_PARTY] = {sDebugText_Party_HealParty, DEBUG_PARTY_MENU_ITEM_HEAL_PARTY},
[DEBUG_PARTY_MENU_ITEM_POISON_MONS] = {sDebugText_Party_PoisonParty, DEBUG_PARTY_MENU_ITEM_POISON_MONS},
[DEBUG_PARTY_MENU_ITEM_CHECK_EV] = {sDebugText_Party_CheckEV, DEBUG_PARTY_MENU_ITEM_CHECK_EV},
[DEBUG_PARTY_MENU_ITEM_CHECK_IV] = {sDebugText_Party_CheckIV, DEBUG_PARTY_MENU_ITEM_CHECK_IV},
[DEBUG_PARTY_MENU_ITEM_CLEAR_PARTY] = {sDebugText_Party_ClearParty, DEBUG_PARTY_MENU_ITEM_CLEAR_PARTY},
};
static const struct ListMenuItem sDebugMenu_Items_Scripts[] =
@ -902,6 +916,9 @@ static void (*const sDebugMenu_Actions_Party[])(u8) =
[DEBUG_PARTY_MENU_ITEM_HATCH_AN_EGG] = DebugAction_Party_HatchAnEgg,
[DEBUG_PARTY_MENU_ITEM_HEAL_PARTY] = DebugAction_Party_HealParty,
[DEBUG_PARTY_MENU_ITEM_POISON_MONS] = DebugAction_Party_PoisonMons,
[DEBUG_PARTY_MENU_ITEM_CHECK_EV] = DebugAction_Party_CheckEV,
[DEBUG_PARTY_MENU_ITEM_CHECK_IV] = DebugAction_Party_CheckIV,
[DEBUG_PARTY_MENU_ITEM_CLEAR_PARTY] = DebugAction_Party_ClearParty,
};
static void (*const sDebugMenu_Actions_Scripts[])(u8) =
@ -3277,7 +3294,7 @@ static void DebugAction_Give_Pokemon_SelectLevel(u8 taskId)
if (gTasks[taskId].tIsComplex == FALSE)
{
PlaySE(MUS_LEVEL_UP);
ScriptGiveMon(sDebugMonData->species, gTasks[taskId].tInput, ITEM_NONE, 0, 0, 0);
ScriptGiveMon(sDebugMonData->species, gTasks[taskId].tInput, ITEM_NONE);
// Set flag for user convenience
FlagSet(FLAG_SYS_POKEMON_GET);
Free(sDebugMonData);
@ -5075,4 +5092,21 @@ static void DebugAction_Party_PoisonMons(u8 taskId)
Debug_DestroyMenu_Full(taskId);
}
static void DebugAction_Party_CheckEV(u8 taskId)
{
Debug_DestroyMenu_Full_Script(taskId, Debug_EventScript_CheckEV);
}
static void DebugAction_Party_CheckIV(u8 taskId)
{
Debug_DestroyMenu_Full_Script(taskId, Debug_EventScript_CheckIV);
}
static void DebugAction_Party_ClearParty(u8 taskId)
{
ZeroPlayerPartyMons();
ScriptContext_Enable();
Debug_DestroyMenu_Full(taskId);
}
#endif //DEBUG_OVERWORLD_MENU == TRUE

View File

@ -1777,19 +1777,6 @@ bool8 ScrCmd_bufferboxname(struct ScriptContext *ctx)
return FALSE;
}
bool8 ScrCmd_givemon(struct ScriptContext *ctx)
{
u16 species = VarGet(ScriptReadHalfword(ctx));
u8 level = ScriptReadByte(ctx);
u16 item = VarGet(ScriptReadHalfword(ctx));
u32 unkParam1 = ScriptReadWord(ctx);
u32 unkParam2 = ScriptReadWord(ctx);
u8 unkParam3 = ScriptReadByte(ctx);
gSpecialVar_Result = ScriptGiveMon(species, level, item, unkParam1, unkParam2, unkParam3);
return FALSE;
}
bool8 ScrCmd_giveegg(struct ScriptContext *ctx)
{
u16 species = VarGet(ScriptReadHalfword(ctx));

View File

@ -23,6 +23,7 @@
#include "string_util.h"
#include "tv.h"
#include "wild_encounter.h"
#include "constants/abilities.h"
#include "constants/items.h"
#include "constants/battle_frontier.h"
@ -55,46 +56,6 @@ static void HealPlayerBoxes(void)
}
}
u8 ScriptGiveMon(u16 species, u8 level, u16 item, u32 unused1, u32 unused2, u8 unused3)
{
u16 nationalDexNum;
int sentToPc;
u8 heldItem[2];
struct Pokemon mon;
u16 targetSpecies;
if (OW_SYNCHRONIZE_NATURE >= GEN_6 && (gSpeciesInfo[species].eggGroups[0] == EGG_GROUP_NO_EGGS_DISCOVERED || OW_SYNCHRONIZE_NATURE == GEN_7))
CreateMonWithNature(&mon, species, level, USE_RANDOM_IVS, PickWildMonNature());
else
CreateMon(&mon, species, level, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0);
heldItem[0] = item;
heldItem[1] = item >> 8;
SetMonData(&mon, MON_DATA_HELD_ITEM, heldItem);
// In case a mon with a form changing item is given. Eg: SPECIES_ARCEUS_NORMAL with ITEM_SPLASH_PLATE will transform into SPECIES_ARCEUS_WATER upon gifted.
targetSpecies = GetFormChangeTargetSpecies(&mon, FORM_CHANGE_ITEM_HOLD, 0);
if (targetSpecies != SPECIES_NONE)
{
SetMonData(&mon, MON_DATA_SPECIES, &targetSpecies);
CalculateMonStats(&mon);
}
sentToPc = GiveMonToPlayer(&mon);
nationalDexNum = SpeciesToNationalPokedexNum(species);
// Don't set Pokédex flag for MON_CANT_GIVE
switch(sentToPc)
{
case MON_GIVEN_TO_PARTY:
case MON_GIVEN_TO_PC:
GetSetPokedexFlag(nationalDexNum, FLAG_SET_SEEN);
GetSetPokedexFlag(nationalDexNum, FLAG_SET_CAUGHT);
break;
}
return sentToPc;
}
u8 ScriptGiveEgg(u16 species)
{
struct Pokemon mon;
@ -327,3 +288,197 @@ void ToggleGigantamaxFactor(struct ScriptContext *ctx)
gSpecialVar_Result = TRUE;
}
}
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)
{
u16 nationalDexNum;
int sentToPc;
struct Pokemon mon;
u32 i;
u8 genderRatio = gSpeciesInfo[species].genderRatio;
u16 targetSpecies;
// check whether to use a specific nature or a random one
if (OW_SYNCHRONIZE_NATURE >= GEN_6 && (gSpeciesInfo[species].eggGroups[0] == EGG_GROUP_NO_EGGS_DISCOVERED || OW_SYNCHRONIZE_NATURE == GEN_7))
nature = PickWildMonNature();
else if (nature >= NUM_NATURES)
nature = Random() % NUM_NATURES;
// create a Pokémon with basic data
if ((gender == MON_MALE && genderRatio != MON_FEMALE && genderRatio != MON_GENDERLESS)
|| (gender == MON_FEMALE && genderRatio != MON_MALE && genderRatio != MON_GENDERLESS)
|| (gender == MON_GENDERLESS && genderRatio == MON_GENDERLESS))
CreateMonWithGenderNatureLetter(&mon, species, level, 32, gender, nature, 0);
else
CreateMonWithNature(&mon, species, level, 32, nature);
// shininess
SetMonData(&mon, MON_DATA_IS_SHINY, &isShiny);
// gigantamax factor
SetMonData(&mon, MON_DATA_GIGANTAMAX_FACTOR, &ggMaxFactor);
// tera type
if (teraType >= NUMBER_OF_MON_TYPES)
teraType = gSpeciesInfo[species].types[0];
SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType);
// EV and IV
for (i = 0; i < NUM_STATS; i++)
{
// EV
if (evs[i] <= MAX_PER_STAT_EVS)
SetMonData(&mon, MON_DATA_HP_EV + i, &evs[i]);
// IV
if (ivs[i] <= MAX_PER_STAT_IVS)
SetMonData(&mon, MON_DATA_HP_IV + i, &ivs[i]);
}
CalculateMonStats(&mon);
// moves
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (moves[i] == MOVE_NONE || moves[i] >= MOVES_COUNT)
continue;
SetMonMoveSlot(&mon, moves[i], i);
}
// ability
if (abilityNum >= NUM_ABILITY_SLOTS || GetAbilityBySpecies(species, abilityNum) == ABILITY_NONE)
{
do {
abilityNum = Random() % NUM_ABILITY_SLOTS; // includes hidden abilities
} while (GetAbilityBySpecies(species, abilityNum) == ABILITY_NONE);
}
SetMonData(&mon, MON_DATA_ABILITY_NUM, &abilityNum);
// ball
if (ball >= POKEBALL_COUNT)
ball = ITEM_POKE_BALL;
SetMonData(&mon, MON_DATA_POKEBALL, &ball);
// held item
SetMonData(&mon, MON_DATA_HELD_ITEM, &item);
// In case a mon with a form changing item is given. Eg: SPECIES_ARCEUS_NORMAL with ITEM_SPLASH_PLATE will transform into SPECIES_ARCEUS_WATER upon gifted.
targetSpecies = GetFormChangeTargetSpecies(&mon, FORM_CHANGE_ITEM_HOLD, 0);
if (targetSpecies != SPECIES_NONE)
SetMonData(&mon, MON_DATA_SPECIES, &targetSpecies);
// assign OT name and gender
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 (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)
{
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;
}
return sentToPc;
}
u32 ScriptGiveMon(u16 species, u8 level, u16 item)
{
u8 evs[NUM_STATS] = {0, 0, 0, 0, 0, 0};
u8 ivs[NUM_STATS] = {MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1, // We pass "MAX_PER_STAT_IVS + 1" here to ensure that
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_SLOTS, 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)
{
u16 species = VarGet(ScriptReadHalfword(ctx));
u8 level = VarGet(ScriptReadHalfword(ctx));
u32 flags = ScriptReadWord(ctx);
u16 item = PARSE_FLAG(0, ITEM_NONE);
u8 ball = PARSE_FLAG(1, ITEM_POKE_BALL);
u8 nature = PARSE_FLAG(2, NUM_NATURES);
u8 abilityNum = PARSE_FLAG(3, NUM_ABILITY_SLOTS);
u8 gender = PARSE_FLAG(4, MON_GENDERLESS); // TODO: Find a better way to assign a random gender.
u8 hpEv = PARSE_FLAG(5, 0);
u8 atkEv = PARSE_FLAG(6, 0);
u8 defEv = PARSE_FLAG(7, 0);
u8 speedEv = PARSE_FLAG(8, 0);
u8 spAtkEv = PARSE_FLAG(9, 0);
u8 spDefEv = PARSE_FLAG(10, 0);
u8 hpIv = PARSE_FLAG(11, Random() % MAX_PER_STAT_IVS + 1);
u8 atkIv = PARSE_FLAG(12, Random() % MAX_PER_STAT_IVS + 1);
u8 defIv = PARSE_FLAG(13, Random() % MAX_PER_STAT_IVS + 1);
u8 speedIv = PARSE_FLAG(14, Random() % MAX_PER_STAT_IVS + 1);
u8 spAtkIv = PARSE_FLAG(15, Random() % MAX_PER_STAT_IVS + 1);
u8 spDefIv = PARSE_FLAG(16, Random() % MAX_PER_STAT_IVS + 1);
u16 move1 = PARSE_FLAG(17, MOVE_NONE);
u16 move2 = PARSE_FLAG(18, MOVE_NONE);
u16 move3 = PARSE_FLAG(19, MOVE_NONE);
u16 move4 = PARSE_FLAG(20, MOVE_NONE);
bool8 isShiny = PARSE_FLAG(21, FALSE);
bool8 ggMaxFactor = PARSE_FLAG(22, FALSE);
u8 teraType = PARSE_FLAG(23, NUMBER_OF_MON_TYPES);
u8 evs[NUM_STATS] = {hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv};
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);
}
#undef PARSE_FLAG
void Script_GetChosenMonOffensiveEV(void)
{
ConvertIntToDecimalStringN(gStringVar1, GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_ATK_EV), STR_CONV_MODE_LEFT_ALIGN, 3);
ConvertIntToDecimalStringN(gStringVar2, GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPATK_EV), STR_CONV_MODE_LEFT_ALIGN, 3);
ConvertIntToDecimalStringN(gStringVar3, GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPEED_EV), STR_CONV_MODE_LEFT_ALIGN, 3);
}
void Script_GetChosenMonDefensiveEV(void)
{
ConvertIntToDecimalStringN(gStringVar1, GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_HP_EV), STR_CONV_MODE_LEFT_ALIGN, 3);
ConvertIntToDecimalStringN(gStringVar2, GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_DEF_EV), STR_CONV_MODE_LEFT_ALIGN, 3);
ConvertIntToDecimalStringN(gStringVar3, GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPDEF_EV), STR_CONV_MODE_LEFT_ALIGN, 3);
}
void Script_GetChosenMonOffensiveIV(void)
{
ConvertIntToDecimalStringN(gStringVar1, GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_ATK_IV), STR_CONV_MODE_LEFT_ALIGN, 3);
ConvertIntToDecimalStringN(gStringVar2, GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPATK_IV), STR_CONV_MODE_LEFT_ALIGN, 3);
ConvertIntToDecimalStringN(gStringVar3, GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPEED_IV), STR_CONV_MODE_LEFT_ALIGN, 3);
}
void Script_GetChosenMonDefensiveIV(void)
{
ConvertIntToDecimalStringN(gStringVar1, GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_HP_IV), STR_CONV_MODE_LEFT_ALIGN, 3);
ConvertIntToDecimalStringN(gStringVar2, GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_DEF_IV), STR_CONV_MODE_LEFT_ALIGN, 3);
ConvertIntToDecimalStringN(gStringVar3, GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPDEF_IV), STR_CONV_MODE_LEFT_ALIGN, 3);
}

View File

@ -180,3 +180,114 @@ TEST("togglegigantamaxfactor fails for Melmetal")
EXPECT(!VarGet(VAR_RESULT));
EXPECT(!GetMonData(&gPlayerParty[0], MON_DATA_GIGANTAMAX_FACTOR));
}
TEST("givemon [simple]")
{
ZeroPlayerPartyMons();
RUN_OVERWORLD_SCRIPT(
givemon SPECIES_WOBBUFFET, 100;
);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_WOBBUFFET);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_LEVEL), 100);
}
TEST("givemon [all]")
{
ZeroPlayerPartyMons();
RUN_OVERWORLD_SCRIPT(
givemon SPECIES_WOBBUFFET, 100, item=ITEM_LEFTOVERS, ball=ITEM_MASTER_BALL, nature=NATURE_BOLD, abilityNum=2, gender=MON_MALE, hpEv=1, atkEv=2, defEv=3, speedEv=4, spAtkEv=5, spDefEv=6, hpIv=7, atkIv=8, defIv=9, speedIv=10, spAtkIv=11, spDefIv=12, move1=MOVE_TACKLE, move2=MOVE_SPLASH, move3=MOVE_CELEBRATE, move4=MOVE_EXPLOSION, isShiny=TRUE, ggMaxFactor=TRUE, teraType=TYPE_FIRE;
);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_WOBBUFFET);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_LEVEL), 100);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_HELD_ITEM), ITEM_LEFTOVERS);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_POKEBALL), ITEM_MASTER_BALL);
EXPECT_EQ(GetNature(&gPlayerParty[0]), NATURE_BOLD);
EXPECT_EQ(GetMonAbility(&gPlayerParty[0]), gSpeciesInfo[SPECIES_WOBBUFFET].abilities[2]);
EXPECT_EQ(GetMonGender(&gPlayerParty[0]), MON_MALE);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_HP_EV), 1);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_ATK_EV), 2);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_DEF_EV), 3);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPEED_EV), 4);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPATK_EV), 5);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPDEF_EV), 6);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_HP_IV), 7);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_ATK_IV), 8);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_DEF_IV), 9);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPEED_IV), 10);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPATK_IV), 11);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPDEF_IV), 12);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_MOVE1), MOVE_TACKLE);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_MOVE2), MOVE_SPLASH);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_MOVE3), MOVE_CELEBRATE);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_MOVE4), MOVE_EXPLOSION);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_IS_SHINY), TRUE);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_GIGANTAMAX_FACTOR), TRUE);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_TERA_TYPE), TYPE_FIRE);
}
TEST("givemon [vars]")
{
ZeroPlayerPartyMons();
VarSet(VAR_TEMP_C, SPECIES_WOBBUFFET);
VarSet(VAR_TEMP_D, 100);
VarSet(VAR_0x8000, ITEM_LEFTOVERS);
VarSet(VAR_0x8001, ITEM_MASTER_BALL);
VarSet(VAR_0x8002, NATURE_BOLD);
VarSet(VAR_0x8003, 2);
VarSet(VAR_0x8004, MON_MALE);
VarSet(VAR_0x8005, 1);
VarSet(VAR_0x8006, 2);
VarSet(VAR_0x8007, 3);
VarSet(VAR_0x8008, 4);
VarSet(VAR_0x8009, 5);
VarSet(VAR_0x800A, 6);
VarSet(VAR_0x800B, 7);
VarSet(VAR_TEMP_0, 8);
VarSet(VAR_TEMP_1, 9);
VarSet(VAR_TEMP_2, 10);
VarSet(VAR_TEMP_3, 11);
VarSet(VAR_TEMP_4, 12);
VarSet(VAR_TEMP_5, MOVE_TACKLE);
VarSet(VAR_TEMP_6, MOVE_SPLASH);
VarSet(VAR_TEMP_7, MOVE_CELEBRATE);
VarSet(VAR_TEMP_8, MOVE_EXPLOSION);
VarSet(VAR_TEMP_9, TRUE);
VarSet(VAR_TEMP_A, TRUE);
VarSet(VAR_TEMP_B, TYPE_FIRE);
RUN_OVERWORLD_SCRIPT(
givemon VAR_TEMP_C, VAR_TEMP_D, item=VAR_0x8000, ball=VAR_0x8001, nature=VAR_0x8002, abilityNum=VAR_0x8003, gender=VAR_0x8004, hpEv=VAR_0x8005, atkEv=VAR_0x8006, defEv=VAR_0x8007, speedEv=VAR_0x8008, spAtkEv=VAR_0x8009, spDefEv=VAR_0x800A, hpIv=VAR_0x800B, atkIv=VAR_TEMP_0, defIv=VAR_TEMP_1, speedIv=VAR_TEMP_2, spAtkIv=VAR_TEMP_3, spDefIv=VAR_TEMP_4, move1=VAR_TEMP_5, move2=VAR_TEMP_6, move3=VAR_TEMP_7, move4=VAR_TEMP_8, isShiny=VAR_TEMP_9, ggMaxFactor=VAR_TEMP_A, teraType=VAR_TEMP_B;
);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_WOBBUFFET);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_LEVEL), 100);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_HELD_ITEM), ITEM_LEFTOVERS);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_POKEBALL), ITEM_MASTER_BALL);
EXPECT_EQ(GetNature(&gPlayerParty[0]), NATURE_BOLD);
EXPECT_EQ(GetMonAbility(&gPlayerParty[0]), gSpeciesInfo[SPECIES_WOBBUFFET].abilities[2]);
EXPECT_EQ(GetMonGender(&gPlayerParty[0]), MON_MALE);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_HP_EV), 1);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_ATK_EV), 2);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_DEF_EV), 3);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPEED_EV), 4);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPATK_EV), 5);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPDEF_EV), 6);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_HP_IV), 7);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_ATK_IV), 8);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_DEF_IV), 9);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPEED_IV), 10);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPATK_IV), 11);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPDEF_IV), 12);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_MOVE1), MOVE_TACKLE);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_MOVE2), MOVE_SPLASH);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_MOVE3), MOVE_CELEBRATE);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_MOVE4), MOVE_EXPLOSION);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_IS_SHINY), TRUE);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_GIGANTAMAX_FACTOR), TRUE);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_TERA_TYPE), TYPE_FIRE);
}