mirror of
https://github.com/rh-hideout/pokeemerald-expansion.git
synced 2026-03-21 18:04:50 -05:00
Merge 0c9b378cb2 into 47cac73a61
This commit is contained in:
commit
15c24ce1bd
|
|
@ -103,9 +103,8 @@
|
|||
setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SET_DATA
|
||||
setvar VAR_0x8005, FRONTIER_DATA_SELECTED_MON_ORDER
|
||||
special CallFrontierUtilFunc @ saves the mon order, so the non-selected mons get restored afterwards
|
||||
setvar VAR_0x8004, SPECIAL_BATTLE_MULTI
|
||||
setvar VAR_0x8005, \type | MULTI_BATTLE_CHOOSE_MONS
|
||||
special DoSpecialTrainerBattle
|
||||
callnative BattleSetup_StartMultiBattle
|
||||
waitstate
|
||||
setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SAVE_PARTY
|
||||
special CallFrontierUtilFunc
|
||||
|
|
@ -132,9 +131,8 @@
|
|||
.endm
|
||||
|
||||
.macro multi_do_fixed type:req
|
||||
setvar VAR_0x8004, SPECIAL_BATTLE_MULTI
|
||||
setvar VAR_0x8005, \type
|
||||
special DoSpecialTrainerBattle
|
||||
callnative BattleSetup_StartMultiBattle
|
||||
waitstate
|
||||
setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SAVE_PARTY
|
||||
special CallFrontierUtilFunc
|
||||
|
|
|
|||
|
|
@ -107,9 +107,7 @@ void RunBattleScriptCommands(void);
|
|||
enum Type GetDynamicMoveType(struct Pokemon *mon, enum Move move, enum BattlerId battler, enum MonState monInBattle);
|
||||
void SetTypeBeforeUsingMove(enum Move move, enum BattlerId battler);
|
||||
bool32 IsWildMonSmart(void);
|
||||
u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags);
|
||||
void ModifyPersonalityForNature(u32 *personality, u32 newNature);
|
||||
u32 GeneratePersonalityForGender(u32 gender, enum Species species);
|
||||
void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMon *partyEntry);
|
||||
bool32 CanPlayerForfeitNormalTrainerBattle(void);
|
||||
bool32 DidPlayerForfeitNormalTrainerBattle(void);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define GUARD_BATTLE_SETUP_H
|
||||
|
||||
#include "battle_transition.h"
|
||||
#include "data.h"
|
||||
#include "gym_leader_rematch.h"
|
||||
|
||||
#define REMATCHES_COUNT 5
|
||||
|
|
@ -118,4 +119,6 @@ s32 FirstBattleTrainerIdToRematchTableId(const struct RematchTrainer *table, u16
|
|||
u16 GetRematchTrainerIdFromTable(const struct RematchTrainer *table, u16 firstBattleTrainerId);
|
||||
u8 GetRivalBattleFlags(void);
|
||||
|
||||
void CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer);
|
||||
|
||||
#endif // GUARD_BATTLE_SETUP_H
|
||||
|
|
|
|||
|
|
@ -357,7 +357,7 @@
|
|||
#define B_WILD_NATURAL_ENEMIES TRUE // If set to TRUE, certain wild mon species will attack other species when partnered in double wild battles (eg. Zangoose vs Seviper)
|
||||
#define B_AFFECTION_MECHANICS TRUE // In Gen6+, there's a stat called affection that can trigger different effects in battle. From LGPE onwards, those effects use friendship instead.
|
||||
#define B_TRAINER_CLASS_POKE_BALLS GEN_LATEST // In Gen7+, trainers will use certain types of Poké Balls depending on their trainer class.
|
||||
#define B_TRAINER_MON_RANDOM_ABILITY FALSE // If this is set to TRUE a random legal ability will be generated for a trainer mon
|
||||
#define B_TRAINER_MON_HIDDEN_ABILITY FALSE // If this is set to TRUE, hidden ability can be randomly rolled for trainers/partners who do not have a set ability. If FALSE, it's still random but can't get an hidden ability.
|
||||
#define B_OBEDIENCE_MECHANICS GEN_LATEST // In PLA+ (here Gen8+), obedience restrictions also apply to non-outsider Pokémon, albeit based on their level met rather than actual level
|
||||
#define B_USE_FROSTBITE FALSE // In PLA, Frostbite replaces Freeze. Enabling this flag does the same here. Moves can still be cherry-picked to either Freeze or Frostbite. Freeze-Dry, Secret Power & Tri Attack depend on this config.
|
||||
#define B_TOXIC_REVERSAL GEN_LATEST // In Gen5+, bad poison will change to regular poison at the end of battles.
|
||||
|
|
|
|||
20
include/trainer_util.h
Normal file
20
include/trainer_util.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef GUARD_TRAINER_UTIL_H
|
||||
#define GUARD_TRAINER_UTIL_H
|
||||
|
||||
struct TrainerGenerator
|
||||
{
|
||||
u8 gender:7;
|
||||
u8 isFrontier:1;
|
||||
u8 trainerClass;
|
||||
u8 padding;
|
||||
u8 name[TRAINER_NAME_LENGTH + 1];
|
||||
struct OriginalTrainerId otID;
|
||||
rng_value_t localRngState;
|
||||
};
|
||||
|
||||
u32 Crc32B(const u8 *data, u32 size);
|
||||
rng_value_t GeneratePartySeed(const struct Trainer *trainer);
|
||||
void GenerateMonFromTrainerMon(struct Pokemon *mon, const struct TrainerMon *trainerMon, struct TrainerGenerator *trainer);
|
||||
u32 GeneratePersonalityForGender(u32 gender, u32 species);
|
||||
|
||||
#endif // GUARD_TRAINER_UTIL_H
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
#include "string_util.h"
|
||||
#include "task.h"
|
||||
#include "text.h"
|
||||
#include "trainer_util.h"
|
||||
#include "constants/abilities.h"
|
||||
#include "constants/battle_frontier.h"
|
||||
#include "constants/battle_frontier_mons.h"
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@
|
|||
#include "test/battle.h"
|
||||
#include "test_runner.h"
|
||||
#include "text.h"
|
||||
#include "trainer_pools.h"
|
||||
#include "trainer_util.h"
|
||||
#include "trig.h"
|
||||
#include "tv.h"
|
||||
#include "util.h"
|
||||
|
|
@ -91,7 +91,6 @@ static void CB2_HandleStartMultiPartnerBattle(void);
|
|||
static void CB2_HandleStartMultiBattle(void);
|
||||
static void CB2_HandleStartBattle(void);
|
||||
static void TryCorrectShedinjaLanguage(struct Pokemon *mon);
|
||||
static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer);
|
||||
static void BattleMainCB1(void);
|
||||
static void CB2_EndLinkBattle(void);
|
||||
static void EndLinkBattleInSteps(void);
|
||||
|
|
@ -128,8 +127,6 @@ static void HandleEndTurn_BattleLost(void);
|
|||
static void HandleEndTurn_RanFromBattle(void);
|
||||
static void HandleEndTurn_MonFled(void);
|
||||
static void HandleEndTurn_FinishBattle(void);
|
||||
static u32 Crc32B (const u8 *data, u32 size);
|
||||
static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i);
|
||||
|
||||
EWRAM_DATA u16 gBattle_BG0_X = 0;
|
||||
EWRAM_DATA u16 gBattle_BG0_Y = 0;
|
||||
|
|
@ -593,18 +590,6 @@ static void CB2_InitBattleInternal(void)
|
|||
else
|
||||
SetMainCallback2(CB2_HandleStartBattle);
|
||||
|
||||
if (!DEBUG_OVERWORLD_MENU || (DEBUG_OVERWORLD_MENU && !gIsDebugBattle))
|
||||
{
|
||||
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED)))
|
||||
{
|
||||
CreateNPCTrainerParty(&gEnemyParty[0], TRAINER_BATTLE_PARAM.opponentA, TRUE);
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && !BATTLE_TWO_VS_ONE_OPPONENT)
|
||||
CreateNPCTrainerParty(&gEnemyParty[PARTY_SIZE / 2], TRAINER_BATTLE_PARAM.opponentB, FALSE);
|
||||
SetWildMonHeldItem();
|
||||
CalculateEnemyPartyCount();
|
||||
}
|
||||
}
|
||||
|
||||
gMain.inBattle = TRUE;
|
||||
gSaveBlock2Ptr->frontier.disableRecordBattle = FALSE;
|
||||
|
||||
|
|
@ -619,14 +604,16 @@ static void CB2_InitBattleInternal(void)
|
|||
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
|
||||
{
|
||||
if (!(gBattleTypeFlags & (BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_PYRAMID | BATTLE_TYPE_PIKE)))
|
||||
SetWildMonHeldItem();
|
||||
TryFormChange(&gEnemyParty[0], FORM_CHANGE_BEGIN_WILD_ENCOUNTER);
|
||||
if (IsDoubleBattle())
|
||||
TryFormChange(&gEnemyParty[1], FORM_CHANGE_BEGIN_WILD_ENCOUNTER);
|
||||
}
|
||||
|
||||
CalculateEnemyPartyCount();
|
||||
#if TESTING
|
||||
gPlayerPartyCount = CalculatePartyCount(gPlayerParty);
|
||||
gEnemyPartyCount = CalculatePartyCount(gEnemyParty);
|
||||
#endif
|
||||
|
||||
gBattleCommunication[MULTIUSE_STATE] = 0;
|
||||
|
|
@ -1848,33 +1835,6 @@ void CB2_QuitRecordedBattle(void)
|
|||
}
|
||||
}
|
||||
|
||||
static u32 Crc32B (const u8 *data, u32 size)
|
||||
{
|
||||
s32 i, j;
|
||||
u32 byte, crc, mask;
|
||||
|
||||
i = 0;
|
||||
crc = 0xFFFFFFFF;
|
||||
for (i = 0; i < size; ++i)
|
||||
{
|
||||
byte = data[i];
|
||||
crc = crc ^ byte;
|
||||
for (j = 7; j >= 0; --j)
|
||||
{
|
||||
mask = -(crc & 1);
|
||||
crc = (crc >> 1) ^ (0xEDB88320 & mask);
|
||||
}
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i)
|
||||
{
|
||||
const u8 *buffer = (const u8 *) &trainer->party[i];
|
||||
u32 n = sizeof(*trainer->party);
|
||||
return Crc32B(buffer, n);
|
||||
}
|
||||
|
||||
void ModifyPersonalityForNature(u32 *personality, u32 newNature)
|
||||
{
|
||||
u32 nature = GetNatureFromPersonality(*personality);
|
||||
|
|
@ -1888,214 +1848,6 @@ void ModifyPersonalityForNature(u32 *personality, u32 newNature)
|
|||
*personality -= (diff * sign);
|
||||
}
|
||||
|
||||
u32 GeneratePersonalityForGender(u32 gender, enum Species species)
|
||||
{
|
||||
const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[species];
|
||||
if (gender == MON_GENDERLESS)
|
||||
return 0;
|
||||
else if (gender == MON_MALE)
|
||||
return ((255 - speciesInfo->genderRatio) / 2) + speciesInfo->genderRatio;
|
||||
else
|
||||
return speciesInfo->genderRatio / 2;
|
||||
}
|
||||
|
||||
void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMon *partyEntry)
|
||||
{
|
||||
bool32 noMoveSet = TRUE;
|
||||
u32 j;
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; ++j)
|
||||
{
|
||||
if (partyEntry->moves[j] != MOVE_NONE)
|
||||
noMoveSet = FALSE;
|
||||
}
|
||||
if (noMoveSet)
|
||||
{
|
||||
GiveMonInitialMoveset(mon);
|
||||
// TODO: Figure out a default strategy when moves are not set, to generate a good moveset
|
||||
return;
|
||||
}
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; ++j)
|
||||
{
|
||||
u32 pp = GetMovePP(partyEntry->moves[j]);
|
||||
SetMonData(mon, MON_DATA_MOVE1 + j, &partyEntry->moves[j]);
|
||||
SetMonData(mon, MON_DATA_PP1 + j, &pp);
|
||||
}
|
||||
}
|
||||
|
||||
u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags)
|
||||
{
|
||||
u32 personalityValue;
|
||||
s32 i;
|
||||
u8 monsCount;
|
||||
if (battleTypeFlags & BATTLE_TYPE_TRAINER && !(battleTypeFlags & (BATTLE_TYPE_FRONTIER
|
||||
| BATTLE_TYPE_EREADER_TRAINER
|
||||
| BATTLE_TYPE_TRAINER_HILL)))
|
||||
{
|
||||
if (firstTrainer == TRUE)
|
||||
ZeroEnemyPartyMons();
|
||||
|
||||
if (battleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
|
||||
{
|
||||
if (trainer->partySize > PARTY_SIZE / 2)
|
||||
monsCount = PARTY_SIZE / 2;
|
||||
else
|
||||
monsCount = trainer->partySize;
|
||||
}
|
||||
else
|
||||
{
|
||||
monsCount = trainer->partySize;
|
||||
}
|
||||
|
||||
u32 monIndices[monsCount];
|
||||
DoTrainerPartyPool(trainer, monIndices, monsCount, battleTypeFlags);
|
||||
|
||||
for (i = 0; i < monsCount; i++)
|
||||
{
|
||||
u32 monIndex = monIndices[i];
|
||||
s32 ball = -1;
|
||||
u32 personalityHash = GeneratePartyHash(trainer, i);
|
||||
const struct TrainerMon *partyData = trainer->party;
|
||||
struct OriginalTrainerId otId = OTID_STRUCT_RANDOM_NO_SHINY;
|
||||
u32 abilityNum = 0;
|
||||
|
||||
if (trainer->battleType != TRAINER_BATTLE_TYPE_SINGLES)
|
||||
personalityValue = 0x80;
|
||||
else if (trainer->gender == TRAINER_GENDER_FEMALE)
|
||||
personalityValue = 0x78; // Use personality more likely to result in a female Pokémon
|
||||
else
|
||||
personalityValue = 0x88; // Use personality more likely to result in a male Pokémon
|
||||
|
||||
personalityValue += personalityHash << 8;
|
||||
if (partyData[monIndex].gender == TRAINER_MON_MALE)
|
||||
personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[monIndex].species);
|
||||
else if (partyData[monIndex].gender == TRAINER_MON_FEMALE)
|
||||
personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[monIndex].species);
|
||||
else if (partyData[monIndex].gender == TRAINER_MON_RANDOM_GENDER)
|
||||
personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(Random() & 1 ? MON_MALE : MON_FEMALE, partyData[monIndex].species);
|
||||
ModifyPersonalityForNature(&personalityValue, partyData[monIndex].nature);
|
||||
if (partyData[monIndex].isShiny)
|
||||
{
|
||||
otId.method = OT_ID_PRESET;
|
||||
otId.value = HIHALF(personalityValue) ^ LOHALF(personalityValue);
|
||||
}
|
||||
CreateMon(&party[i], partyData[monIndex].species, partyData[monIndex].lvl, personalityValue, otId);
|
||||
SetMonData(&party[i], MON_DATA_HELD_ITEM, &partyData[monIndex].heldItem);
|
||||
|
||||
CustomTrainerPartyAssignMoves(&party[i], &partyData[monIndex]);
|
||||
SetMonData(&party[i], MON_DATA_IVS, &(partyData[monIndex].iv));
|
||||
if (partyData[monIndex].ev != NULL)
|
||||
{
|
||||
SetMonData(&party[i], MON_DATA_HP_EV, &(partyData[monIndex].ev[0]));
|
||||
SetMonData(&party[i], MON_DATA_ATK_EV, &(partyData[monIndex].ev[1]));
|
||||
SetMonData(&party[i], MON_DATA_DEF_EV, &(partyData[monIndex].ev[2]));
|
||||
SetMonData(&party[i], MON_DATA_SPATK_EV, &(partyData[monIndex].ev[3]));
|
||||
SetMonData(&party[i], MON_DATA_SPDEF_EV, &(partyData[monIndex].ev[4]));
|
||||
SetMonData(&party[i], MON_DATA_SPEED_EV, &(partyData[monIndex].ev[5]));
|
||||
}
|
||||
if (partyData[monIndex].ability != ABILITY_NONE)
|
||||
{
|
||||
const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[monIndex].species];
|
||||
u32 maxAbilityNum = ARRAY_COUNT(speciesInfo->abilities);
|
||||
for (abilityNum = 0; abilityNum < maxAbilityNum; ++abilityNum)
|
||||
{
|
||||
if (speciesInfo->abilities[abilityNum] == partyData[monIndex].ability)
|
||||
break;
|
||||
}
|
||||
assertf(abilityNum < maxAbilityNum, "illegal ability %S for %S", gAbilitiesInfo[partyData[monIndex].ability].name, speciesInfo->speciesName);
|
||||
}
|
||||
else if (B_TRAINER_MON_RANDOM_ABILITY)
|
||||
{
|
||||
const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[monIndex].species];
|
||||
abilityNum = personalityHash % 3;
|
||||
while (speciesInfo->abilities[abilityNum] == ABILITY_NONE)
|
||||
{
|
||||
abilityNum--;
|
||||
}
|
||||
}
|
||||
SetMonData(&party[i], MON_DATA_ABILITY_NUM, &abilityNum);
|
||||
SetMonData(&party[i], MON_DATA_FRIENDSHIP, &(partyData[monIndex].friendship));
|
||||
if (partyData[monIndex].ball < POKEBALL_COUNT)
|
||||
{
|
||||
ball = partyData[monIndex].ball;
|
||||
SetMonData(&party[i], MON_DATA_POKEBALL, &ball);
|
||||
}
|
||||
if (partyData[monIndex].nickname != NULL)
|
||||
{
|
||||
SetMonData(&party[i], MON_DATA_NICKNAME, partyData[monIndex].nickname);
|
||||
}
|
||||
if (partyData[monIndex].isShiny)
|
||||
{
|
||||
bool32 data = TRUE;
|
||||
SetMonData(&party[i], MON_DATA_IS_SHINY, &data);
|
||||
}
|
||||
if (partyData[monIndex].dynamaxLevel > 0)
|
||||
{
|
||||
u32 data = partyData[monIndex].dynamaxLevel;
|
||||
if (partyData[monIndex].shouldUseDynamax)
|
||||
gBattleStruct->opponentMonCanDynamax |= 1 << i;
|
||||
SetMonData(&party[i], MON_DATA_DYNAMAX_LEVEL, &data);
|
||||
}
|
||||
if (partyData[monIndex].gigantamaxFactor)
|
||||
{
|
||||
u32 data = partyData[monIndex].gigantamaxFactor;
|
||||
SetMonData(&party[i], MON_DATA_GIGANTAMAX_FACTOR, &data);
|
||||
}
|
||||
if (partyData[monIndex].teraType > 0)
|
||||
{
|
||||
gBattleStruct->opponentMonCanTera |= 1 << i;
|
||||
enum Type data = partyData[monIndex].teraType;
|
||||
SetMonData(&party[i], MON_DATA_TERA_TYPE, &data);
|
||||
}
|
||||
CalculateMonStats(&party[i]);
|
||||
|
||||
if (B_TRAINER_CLASS_POKE_BALLS >= GEN_7 && ball == -1)
|
||||
{
|
||||
ball = gTrainerClasses[trainer->trainerClass].ball ?: ITEM_POKE_BALL;
|
||||
SetMonData(&party[i], MON_DATA_POKEBALL, &ball);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return trainer->partySize;
|
||||
}
|
||||
|
||||
static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer)
|
||||
{
|
||||
u8 retVal;
|
||||
if (trainerNum == TRAINER_SECRET_BASE)
|
||||
return 0;
|
||||
if (GetTrainerStructFromId(trainerNum)->overrideTrainer)
|
||||
{
|
||||
struct Trainer tempTrainer;
|
||||
memcpy(&tempTrainer, GetTrainerStructFromId(trainerNum), sizeof(struct Trainer));
|
||||
const struct Trainer *origTrainer = GetTrainerStructFromId(tempTrainer.overrideTrainer);
|
||||
|
||||
tempTrainer.party = origTrainer->party;
|
||||
|
||||
tempTrainer.poolSize = origTrainer->poolSize;
|
||||
if (tempTrainer.partySize == 0)
|
||||
tempTrainer.partySize = origTrainer->partySize;
|
||||
|
||||
retVal = CreateNPCTrainerPartyFromTrainer(party, (const struct Trainer *)(&tempTrainer), firstTrainer, gBattleTypeFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
retVal = CreateNPCTrainerPartyFromTrainer(party, GetTrainerStructFromId(trainerNum), firstTrainer, gBattleTypeFlags);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void CreateTrainerPartyForPlayer(void)
|
||||
{
|
||||
Script_RequestEffects(SCREFF_V1);
|
||||
|
||||
ZeroPlayerPartyMons();
|
||||
gPartnerTrainerId = gSpecialVar_0x8004;
|
||||
CreateNPCTrainerPartyFromTrainer(gPlayerParty, GetTrainerStructFromId(gSpecialVar_0x8004), TRUE, BATTLE_TYPE_TRAINER);
|
||||
}
|
||||
|
||||
void VBlankCB_Battle(void)
|
||||
{
|
||||
// Change gRngSeed every vblank unless the battle could be recorded.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@
|
|||
#include "data.h"
|
||||
#include "frontier_util.h"
|
||||
#include "difficulty.h"
|
||||
#include "malloc.h"
|
||||
#include "string_util.h"
|
||||
#include "trainer_util.h"
|
||||
#include "text.h"
|
||||
|
||||
#include "constants/abilities.h"
|
||||
|
|
@ -21,15 +23,25 @@ const struct Trainer gBattlePartners[DIFFICULTY_COUNT][PARTNER_COUNT] =
|
|||
|
||||
#define STEVEN_OTID 61226
|
||||
|
||||
static void MakePartnerGenerator(struct TrainerGenerator *trainerGen, const struct Trainer *trainer)
|
||||
{
|
||||
u32 otID;
|
||||
trainerGen->gender = trainer->gender;
|
||||
trainerGen->isFrontier = FALSE;
|
||||
StringCopyN(trainerGen->name, trainer->trainerName, TRAINER_NAME_LENGTH + 1);
|
||||
trainerGen->trainerClass = trainer->trainerClass;
|
||||
otID = Crc32B((const u8 *)trainer, sizeof(struct Trainer));
|
||||
trainerGen->otID = OTID_STRUCT_PRESET(otID);
|
||||
trainerGen->localRngState = LocalRandomSeed(otID);
|
||||
}
|
||||
|
||||
void FillPartnerParty(u16 trainerId)
|
||||
{
|
||||
s32 i, j, k;
|
||||
u32 firstIdPart = 0, secondIdPart = 0, thirdIdPart = 0;
|
||||
u32 ivs, level, personality;
|
||||
s32 i, j;
|
||||
u32 ivs, level;
|
||||
u16 monId;
|
||||
u32 otID;
|
||||
|
||||
u8 trainerName[(PLAYER_NAME_LENGTH * 3) + 1];
|
||||
enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(trainerId);
|
||||
SetFacilityPtrsGetLevel();
|
||||
|
||||
if (trainerId > TRAINER_PARTNER(PARTNER_NONE))
|
||||
|
|
@ -37,85 +49,16 @@ void FillPartnerParty(u16 trainerId)
|
|||
for (i = 0; i < 3; i++)
|
||||
ZeroMonData(&gPlayerParty[i + 3]);
|
||||
|
||||
for (i = 0; i < 3 && i < gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].partySize; i++)
|
||||
const struct Trainer *partner = GetTrainerStructFromId(trainerId);
|
||||
struct TrainerGenerator *partnerGen = AllocZeroed(sizeof(struct TrainerGenerator));
|
||||
MakePartnerGenerator(partnerGen, partner);
|
||||
if (trainerId == TRAINER_PARTNER(PARTNER_STEVEN))
|
||||
partnerGen->otID = OTID_STRUCT_PRESET(STEVEN_OTID);
|
||||
for (i = 0; i < 3 && i < partner->partySize; i++)
|
||||
{
|
||||
const struct TrainerMon *partyData = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].party;
|
||||
const u8 *partnerName = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName;
|
||||
|
||||
for (k = 0; partnerName[k] != EOS && k < 3; k++)
|
||||
{
|
||||
if (k == 0)
|
||||
{
|
||||
firstIdPart = partnerName[k];
|
||||
secondIdPart = partnerName[k];
|
||||
thirdIdPart = partnerName[k];
|
||||
}
|
||||
else if (k == 1)
|
||||
{
|
||||
secondIdPart = partnerName[k];
|
||||
thirdIdPart = partnerName[k];
|
||||
}
|
||||
else if (k == 2)
|
||||
{
|
||||
thirdIdPart = partnerName[k];
|
||||
}
|
||||
}
|
||||
if (trainerId == TRAINER_PARTNER(PARTNER_STEVEN))
|
||||
otID = STEVEN_OTID;
|
||||
else
|
||||
otID = ((firstIdPart % 72) * 1000) + ((secondIdPart % 23) * 10) + (thirdIdPart % 37) % 65536;
|
||||
|
||||
personality = Random32();
|
||||
if (partyData[i].gender == TRAINER_MON_MALE)
|
||||
personality = (personality & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[i].species);
|
||||
else if (partyData[i].gender == TRAINER_MON_FEMALE)
|
||||
personality = (personality & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[i].species);
|
||||
ModifyPersonalityForNature(&personality, partyData[i].nature);
|
||||
CreateMon(&gPlayerParty[i + 3], partyData[i].species, partyData[i].lvl, personality, OTID_STRUCT_PRESET(otID));
|
||||
j = partyData[i].isShiny;
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_IS_SHINY, &j);
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_HELD_ITEM, &partyData[i].heldItem);
|
||||
CustomTrainerPartyAssignMoves(&gPlayerParty[i + 3], &partyData[i]);
|
||||
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_IVS, &(partyData[i].iv));
|
||||
if (partyData[i].ev != NULL)
|
||||
{
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_HP_EV, &(partyData[i].ev[0]));
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_ATK_EV, &(partyData[i].ev[1]));
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_DEF_EV, &(partyData[i].ev[2]));
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_SPATK_EV, &(partyData[i].ev[3]));
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_SPDEF_EV, &(partyData[i].ev[4]));
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_SPEED_EV, &(partyData[i].ev[5]));
|
||||
}
|
||||
if (partyData[i].ability != ABILITY_NONE)
|
||||
{
|
||||
const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[i].species];
|
||||
u32 maxAbilities = ARRAY_COUNT(speciesInfo->abilities);
|
||||
for (j = 0; j < maxAbilities; j++)
|
||||
{
|
||||
if (speciesInfo->abilities[j] == partyData[i].ability)
|
||||
break;
|
||||
}
|
||||
if (j < maxAbilities)
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_ABILITY_NUM, &j);
|
||||
}
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_FRIENDSHIP, &(partyData[i].friendship));
|
||||
if (partyData[i].ball < POKEBALL_COUNT)
|
||||
{
|
||||
enum PokeBall ball = partyData[i].ball;
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_POKEBALL, &ball);
|
||||
}
|
||||
if (partyData[i].nickname != NULL)
|
||||
{
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_NICKNAME, partyData[i].nickname);
|
||||
}
|
||||
CalculateMonStats(&gPlayerParty[i + 3]);
|
||||
|
||||
StringCopy(trainerName, gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName);
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_NAME, trainerName);
|
||||
j = gBattlePartners[difficulty][SanitizeTrainerId(trainerId - TRAINER_PARTNER(PARTNER_NONE))].gender;
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_GENDER, &j);
|
||||
GenerateMonFromTrainerMon(&gPlayerParty[i + 3], &partner->party[i], partnerGen);
|
||||
}
|
||||
Free(partnerGen);
|
||||
}
|
||||
else if (trainerId == TRAINER_EREADER)
|
||||
{
|
||||
|
|
@ -126,7 +69,7 @@ void FillPartnerParty(u16 trainerId)
|
|||
{
|
||||
level = SetFacilityPtrsGetLevel();
|
||||
ivs = GetFrontierTrainerFixedIvs(trainerId);
|
||||
otID = Random32();
|
||||
u32 otID = Random32();
|
||||
for (i = 0; i < FRONTIER_MULTI_PARTY_SIZE; i++)
|
||||
{
|
||||
monId = gSaveBlock2Ptr->frontier.trainerIds[i + 18];
|
||||
|
|
|
|||
|
|
@ -1,52 +1,59 @@
|
|||
#include "global.h"
|
||||
#include "battle.h"
|
||||
#include "load_save.h"
|
||||
#include "battle_setup.h"
|
||||
#include "battle_tower.h"
|
||||
#include "battle_transition.h"
|
||||
#include "data.h"
|
||||
#include "main.h"
|
||||
#include "task.h"
|
||||
#include "safari_zone.h"
|
||||
#include "script.h"
|
||||
#include "event_data.h"
|
||||
#include "metatile_behavior.h"
|
||||
#include "field_player_avatar.h"
|
||||
#include "fieldmap.h"
|
||||
#include "follower_npc.h"
|
||||
#include "random.h"
|
||||
#include "starter_choose.h"
|
||||
#include "script_pokemon_util.h"
|
||||
#include "palette.h"
|
||||
#include "window.h"
|
||||
#include "event_object_movement.h"
|
||||
#include "event_scripts.h"
|
||||
#include "tv.h"
|
||||
#include "trainer_see.h"
|
||||
#include "field_message_box.h"
|
||||
#include "sound.h"
|
||||
#include "strings.h"
|
||||
#include "trainer_hill.h"
|
||||
#include "secret_base.h"
|
||||
#include "string_util.h"
|
||||
#include "overworld.h"
|
||||
#include "field_weather.h"
|
||||
#include "battle_tower.h"
|
||||
#include "gym_leader_rematch.h"
|
||||
#include "battle.h"
|
||||
#include "battle_frontier.h"
|
||||
#include "battle_pike.h"
|
||||
#include "battle_pyramid.h"
|
||||
#include "fldeff.h"
|
||||
#include "fldeff_misc.h"
|
||||
#include "field_control_avatar.h"
|
||||
#include "mirage_tower.h"
|
||||
#include "field_screen_effect.h"
|
||||
#include "data.h"
|
||||
#include "vs_seeker.h"
|
||||
#include "item.h"
|
||||
#include "battle_setup.h"
|
||||
#include "battle_partner.h"
|
||||
#include "battle_tower.h"
|
||||
#include "battle_transition.h"
|
||||
#include "event_data.h"
|
||||
#include "event_object_movement.h"
|
||||
#include "event_scripts.h"
|
||||
#include "fieldmap.h"
|
||||
#include "script.h"
|
||||
#include "field_name_box.h"
|
||||
#include "field_control_avatar.h"
|
||||
#include "field_message_box.h"
|
||||
#include "field_player_avatar.h"
|
||||
#include "field_screen_effect.h"
|
||||
#include "field_weather.h"
|
||||
#include "fishing.h"
|
||||
#include "fldeff.h"
|
||||
#include "fldeff_misc.h"
|
||||
#include "follower_npc.h"
|
||||
#include "gym_leader_rematch.h"
|
||||
#include "item.h"
|
||||
#include "load_save.h"
|
||||
#include "malloc.h"
|
||||
#include "metatile_behavior.h"
|
||||
#include "mirage_tower.h"
|
||||
#include "palette.h"
|
||||
#include "random.h"
|
||||
#include "safari_zone.h"
|
||||
#include "script.h"
|
||||
#include "script_pokemon_util.h"
|
||||
#include "secret_base.h"
|
||||
#include "sound.h"
|
||||
#include "starter_choose.h"
|
||||
#include "strings.h"
|
||||
#include "string_util.h"
|
||||
#include "task.h"
|
||||
#include "trainer_hill.h"
|
||||
#include "trainer_pools.h"
|
||||
#include "trainer_see.h"
|
||||
#include "trainer_util.h"
|
||||
#include "tv.h"
|
||||
#include "overworld.h"
|
||||
|
||||
#include "vs_seeker.h"
|
||||
#include "window.h"
|
||||
|
||||
#include "constants/battle_frontier.h"
|
||||
#include "constants/battle_setup.h"
|
||||
#include "constants/battle_special.h"
|
||||
#include "constants/event_objects.h"
|
||||
#include "constants/game_stat.h"
|
||||
#include "constants/items.h"
|
||||
|
|
@ -54,7 +61,7 @@
|
|||
#include "constants/trainers.h"
|
||||
#include "constants/trainer_hill.h"
|
||||
#include "constants/weather.h"
|
||||
#include "fishing.h"
|
||||
|
||||
|
||||
enum TransitionType
|
||||
{
|
||||
|
|
@ -88,6 +95,8 @@ static void RegisterTrainerInMatchCall(void);
|
|||
static void HandleRematchVarsOnBattleEnd(void);
|
||||
static const u8 *GetIntroSpeechOfApproachingTrainer(void);
|
||||
static const u8 *GetTrainerCantBattleSpeech(void);
|
||||
static void CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer);
|
||||
static void DoTrainerBattle(void);
|
||||
|
||||
EWRAM_DATA TrainerBattleParameter gTrainerBattleParameter = {0};
|
||||
EWRAM_DATA u16 gPartnerTrainerId = 0;
|
||||
|
|
@ -342,6 +351,41 @@ void BattleSetup_StartDoubleWildBattle(void)
|
|||
DoStandardWildBattle(TRUE);
|
||||
}
|
||||
|
||||
void BattleSetup_StartMultiBattle(void)
|
||||
{
|
||||
if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_WILD) // Player + AI against wild mon
|
||||
{
|
||||
gBattleTypeFlags = BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER;
|
||||
}
|
||||
else if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_1) // Player + AI against one trainer
|
||||
{
|
||||
TRAINER_BATTLE_PARAM.opponentB = 0xFFFF;
|
||||
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER;
|
||||
}
|
||||
else // MULTI_BATTLE_2_VS_2
|
||||
{
|
||||
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER;
|
||||
}
|
||||
|
||||
FillPartnerParty(gPartnerTrainerId);
|
||||
if (gSpecialVar_0x8005 & MULTI_BATTLE_CHOOSE_MONS) // Skip mons restoring(done in the script)
|
||||
gBattleScripting.specialTrainerBattleType = 0xFF;
|
||||
|
||||
if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_WILD)
|
||||
{
|
||||
CreateBattleStartTask(GetWildBattleTransition(), 0);
|
||||
IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
|
||||
IncrementGameStat(GAME_STAT_WILD_BATTLES);
|
||||
IncrementDailyWildBattles();
|
||||
TryUpdateGymLeaderRematchFromWild();
|
||||
}
|
||||
else
|
||||
{
|
||||
DoTrainerBattle();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BattleSetup_StartBattlePikeWildBattle(void)
|
||||
{
|
||||
DoBattlePikeWildBattle();
|
||||
|
|
@ -444,6 +488,9 @@ static void DoBattlePikeWildBattle(void)
|
|||
|
||||
static void DoTrainerBattle(void)
|
||||
{
|
||||
CreateNPCTrainerParty(&gEnemyParty[0], TRAINER_BATTLE_PARAM.opponentA, TRUE);
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && !BATTLE_TWO_VS_ONE_OPPONENT)
|
||||
CreateNPCTrainerParty(&gEnemyParty[PARTY_SIZE / 2], TRAINER_BATTLE_PARAM.opponentB, FALSE);
|
||||
CreateBattleStartTask(GetTrainerBattleTransition(), 0);
|
||||
IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
|
||||
IncrementGameStat(GAME_STAT_TRAINER_BATTLES);
|
||||
|
|
@ -2130,3 +2177,67 @@ void SetMultiTrainerBattle(struct ScriptContext *ctx)
|
|||
TRAINER_BATTLE_PARAM.defeatTextB = (u8*)ScriptReadWord(ctx);
|
||||
gPartnerTrainerId = TRAINER_PARTNER(ScriptReadHalfword(ctx));
|
||||
};
|
||||
|
||||
static void MakeTrainerGenerator(struct TrainerGenerator *trainerGen, const struct Trainer *trainer)
|
||||
{
|
||||
trainerGen->gender = trainer->gender;
|
||||
trainerGen->isFrontier = FALSE;
|
||||
StringCopyN(trainerGen->name, trainer->trainerName, TRAINER_NAME_LENGTH + 1);
|
||||
trainerGen->trainerClass = trainer->trainerClass;
|
||||
trainerGen->otID = OTID_STRUCT_RANDOM_NO_SHINY;
|
||||
trainerGen->localRngState = GeneratePartySeed(trainer);
|
||||
}
|
||||
|
||||
void CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer)
|
||||
{
|
||||
s32 i;
|
||||
u8 monsCount;
|
||||
|
||||
if (firstTrainer == TRUE)
|
||||
ZeroEnemyPartyMons();
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && trainer->partySize > PARTY_SIZE / 2)
|
||||
monsCount = PARTY_SIZE / 2;
|
||||
else
|
||||
monsCount = trainer->partySize;
|
||||
|
||||
u32 monIndices[monsCount];
|
||||
struct TrainerGenerator *trainerGen = AllocZeroed(sizeof(struct TrainerGenerator));
|
||||
MakeTrainerGenerator(trainerGen, trainer);
|
||||
DoTrainerPartyPool(trainer, monIndices, monsCount, gBattleTypeFlags);
|
||||
|
||||
for (i = 0; i < monsCount; i++)
|
||||
{
|
||||
u32 monIndex = monIndices[i];
|
||||
GenerateMonFromTrainerMon(&party[i], &trainer->party[monIndex], trainerGen);
|
||||
}
|
||||
Free(trainerGen);
|
||||
}
|
||||
|
||||
static void CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer)
|
||||
{
|
||||
if (!GetTrainerStructFromId(trainerNum)->overrideTrainer) {
|
||||
CreateNPCTrainerPartyFromTrainer(party, GetTrainerStructFromId(trainerNum), firstTrainer);
|
||||
return;
|
||||
}
|
||||
|
||||
struct Trainer tempTrainer;
|
||||
memcpy(&tempTrainer, GetTrainerStructFromId(trainerNum), sizeof(struct Trainer));
|
||||
const struct Trainer *origTrainer = GetTrainerStructFromId(tempTrainer.overrideTrainer);
|
||||
|
||||
tempTrainer.party = origTrainer->party;
|
||||
|
||||
tempTrainer.poolSize = origTrainer->poolSize;
|
||||
if (tempTrainer.partySize == 0)
|
||||
tempTrainer.partySize = origTrainer->partySize;
|
||||
CreateNPCTrainerPartyFromTrainer(party, (const struct Trainer *)(&tempTrainer), firstTrainer);
|
||||
}
|
||||
|
||||
void CreateTrainerPartyForPlayer(void)
|
||||
{
|
||||
Script_RequestEffects(SCREFF_V1);
|
||||
|
||||
ZeroPlayerPartyMons();
|
||||
gPartnerTrainerId = gSpecialVar_0x8004;
|
||||
CreateNPCTrainerPartyFromTrainer(gPlayerParty, GetTrainerStructFromId(gSpecialVar_0x8004), TRUE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,31 +96,8 @@ void DoSpecialTrainerBattle(void)
|
|||
#endif //FREE_BATTLE_TOWER_E_READER
|
||||
break;
|
||||
case SPECIAL_BATTLE_MULTI:
|
||||
if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_WILD) // Player + AI against wild mon
|
||||
{
|
||||
gBattleTypeFlags = BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER;
|
||||
}
|
||||
else if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_1) // Player + AI against one trainer
|
||||
{
|
||||
TRAINER_BATTLE_PARAM.opponentB = 0xFFFF;
|
||||
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER;
|
||||
}
|
||||
else // MULTI_BATTLE_2_VS_2
|
||||
{
|
||||
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER;
|
||||
}
|
||||
|
||||
FillPartnerParty(gPartnerTrainerId);
|
||||
CreateTask(Task_StartBattleAfterTransition, 1);
|
||||
PlayMapChosenOrBattleBGM(0);
|
||||
if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_WILD)
|
||||
BattleTransition_StartOnField(GetWildBattleTransition());
|
||||
else
|
||||
BattleTransition_StartOnField(GetTrainerBattleTransition());
|
||||
|
||||
if (gSpecialVar_0x8005 & MULTI_BATTLE_CHOOSE_MONS) // Skip mons restoring(done in the script)
|
||||
gBattleScripting.specialTrainerBattleType = 0xFF;
|
||||
break;
|
||||
default:
|
||||
errorf("Unknown special battle type %d", gSpecialVar_0x8004);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2165,11 +2165,13 @@ static void DebugAction_Trainers_TryBattle(u8 taskId)
|
|||
gBattleTypeFlags = BATTLE_TYPE_TRAINER;
|
||||
TRAINER_BATTLE_PARAM.opponentA = trainer1Id;
|
||||
TRAINER_BATTLE_PARAM.opponentB = 0xFFFF;
|
||||
CreateNPCTrainerPartyFromTrainer(&gEnemyParty[0], GetTrainerStructFromId(trainer1Id), TRUE);
|
||||
if (sDebugMenuListData->data[5] || partnerId != PARTNER_NONE || trainer2Id != TRAINER_NONE)
|
||||
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE;
|
||||
if (trainer2Id != TRAINER_NONE)
|
||||
{
|
||||
TRAINER_BATTLE_PARAM.opponentB = trainer2Id;
|
||||
CreateNPCTrainerPartyFromTrainer(&gEnemyParty[PARTY_SIZE / 2], GetTrainerStructFromId(trainer2Id), FALSE);
|
||||
gBattleTypeFlags |= BATTLE_TYPE_TWO_OPPONENTS;
|
||||
}
|
||||
if (partnerId != PARTNER_NONE)
|
||||
|
|
@ -4899,7 +4901,7 @@ const struct Trainer* GetDebugAiTrainer(void)
|
|||
static void DebugAction_Party_SetParty(u8 taskId)
|
||||
{
|
||||
ZeroPlayerPartyMons();
|
||||
CreateNPCTrainerPartyFromTrainer(gPlayerParty, &sDebugTrainers[DIFFICULTY_NORMAL][DEBUG_TRAINER_PLAYER], TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(gPlayerParty, &sDebugTrainers[DIFFICULTY_NORMAL][DEBUG_TRAINER_PLAYER], TRUE);
|
||||
ScriptContext_Enable();
|
||||
Debug_DestroyMenu_Full(taskId);
|
||||
}
|
||||
|
|
@ -4908,8 +4910,8 @@ static void DebugAction_Party_BattleSingle(u8 taskId)
|
|||
{
|
||||
ZeroPlayerPartyMons();
|
||||
ZeroEnemyPartyMons();
|
||||
CreateNPCTrainerPartyFromTrainer(gPlayerParty, &sDebugTrainers[DIFFICULTY_NORMAL][DEBUG_TRAINER_PLAYER], TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(gEnemyParty, GetDebugAiTrainer(), FALSE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(gPlayerParty, &sDebugTrainers[DIFFICULTY_NORMAL][DEBUG_TRAINER_PLAYER], TRUE);
|
||||
CreateNPCTrainerPartyFromTrainer(gEnemyParty, GetDebugAiTrainer(), FALSE);
|
||||
|
||||
gBattleTypeFlags = BATTLE_TYPE_TRAINER;
|
||||
if (sDebugTrainers[DIFFICULTY_NORMAL][DEBUG_TRAINER_AI].battleType == TRAINER_BATTLE_TYPE_DOUBLES)
|
||||
|
|
|
|||
|
|
@ -6081,60 +6081,57 @@ static inline bool32 CanFirstMonBoostHeldItemRarity(void)
|
|||
|
||||
void SetWildMonHeldItem(void)
|
||||
{
|
||||
if (!(gBattleTypeFlags & (BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_TRAINER | BATTLE_TYPE_PYRAMID | BATTLE_TYPE_PIKE)))
|
||||
u16 rnd;
|
||||
enum Species species;
|
||||
u16 count = (WILD_DOUBLE_BATTLE) ? 2 : 1;
|
||||
u16 i;
|
||||
bool32 itemHeldBoost = CanFirstMonBoostHeldItemRarity();
|
||||
u16 chanceNoItem = itemHeldBoost ? 20 : 45;
|
||||
u16 chanceNotRare = itemHeldBoost ? 80 : 95;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
u16 rnd;
|
||||
enum Species species;
|
||||
u16 count = (WILD_DOUBLE_BATTLE) ? 2 : 1;
|
||||
u16 i;
|
||||
bool32 itemHeldBoost = CanFirstMonBoostHeldItemRarity();
|
||||
u16 chanceNoItem = itemHeldBoost ? 20 : 45;
|
||||
u16 chanceNotRare = itemHeldBoost ? 80 : 95;
|
||||
if (GetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM) != ITEM_NONE)
|
||||
continue; // prevent overwriting previously set item
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
rnd = Random() % 100;
|
||||
species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES, 0);
|
||||
if (gMapHeader.mapLayoutId == LAYOUT_ALTERING_CAVE)
|
||||
{
|
||||
if (GetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM) != ITEM_NONE)
|
||||
continue; // prevent overwriting previously set item
|
||||
|
||||
rnd = Random() % 100;
|
||||
species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES, 0);
|
||||
if (gMapHeader.mapLayoutId == LAYOUT_ALTERING_CAVE)
|
||||
s32 alteringCaveId = GetWildMonTableIdInAlteringCave(species);
|
||||
if (alteringCaveId != 0)
|
||||
{
|
||||
s32 alteringCaveId = GetWildMonTableIdInAlteringCave(species);
|
||||
if (alteringCaveId != 0)
|
||||
{
|
||||
// In active Altering Cave, use special item list
|
||||
if (rnd < chanceNotRare)
|
||||
continue;
|
||||
SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &sAlteringCaveWildMonHeldItems[alteringCaveId].item);
|
||||
}
|
||||
else
|
||||
{
|
||||
// In inactive Altering Cave, use normal items
|
||||
if (rnd < chanceNoItem)
|
||||
continue;
|
||||
if (rnd < chanceNotRare)
|
||||
SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemCommon);
|
||||
else
|
||||
SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemRare);
|
||||
}
|
||||
// In active Altering Cave, use special item list
|
||||
if (rnd < chanceNotRare)
|
||||
continue;
|
||||
SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &sAlteringCaveWildMonHeldItems[alteringCaveId].item);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gSpeciesInfo[species].itemCommon == gSpeciesInfo[species].itemRare && gSpeciesInfo[species].itemCommon != ITEM_NONE)
|
||||
{
|
||||
// Both held items are the same, 100% chance to hold item
|
||||
// In inactive Altering Cave, use normal items
|
||||
if (rnd < chanceNoItem)
|
||||
continue;
|
||||
if (rnd < chanceNotRare)
|
||||
SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemCommon);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rnd < chanceNoItem)
|
||||
continue;
|
||||
if (rnd < chanceNotRare)
|
||||
SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemCommon);
|
||||
else
|
||||
SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemRare);
|
||||
}
|
||||
SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemRare);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gSpeciesInfo[species].itemCommon == gSpeciesInfo[species].itemRare && gSpeciesInfo[species].itemCommon != ITEM_NONE)
|
||||
{
|
||||
// Both held items are the same, 100% chance to hold item
|
||||
SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemCommon);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rnd < chanceNoItem)
|
||||
continue;
|
||||
if (rnd < chanceNotRare)
|
||||
SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemCommon);
|
||||
else
|
||||
SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gSpeciesInfo[species].itemRare);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
203
src/trainer_util.c
Normal file
203
src/trainer_util.c
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
#include "global.h"
|
||||
#include "main.h"
|
||||
#include "data.h"
|
||||
#include "move.h"
|
||||
#include "random.h"
|
||||
#include "string_util.h"
|
||||
#include "trainer_util.h"
|
||||
#include "text.h"
|
||||
|
||||
#include "constants/pokeball.h"
|
||||
|
||||
u32 Crc32B(const u8 *data, u32 size)
|
||||
{
|
||||
s32 i, j;
|
||||
u32 byte, crc, mask;
|
||||
|
||||
i = 0;
|
||||
crc = 0xFFFFFFFF;
|
||||
for (i = 0; i < size; ++i)
|
||||
{
|
||||
byte = data[i];
|
||||
crc = crc ^ byte;
|
||||
for (j = 7; j >= 0; --j)
|
||||
{
|
||||
mask = -(crc & 1);
|
||||
crc = (crc >> 1) ^ (0xEDB88320 & mask);
|
||||
}
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
rng_value_t GeneratePartySeed(const struct Trainer *trainer)
|
||||
{
|
||||
u32 seed = Crc32B((const u8 *)trainer, sizeof(struct Trainer)) ^ READ_OTID_FROM_SAVE;
|
||||
return LocalRandomSeed(seed);
|
||||
}
|
||||
|
||||
static void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMon *partyEntry)
|
||||
{
|
||||
bool32 noMoveSet = TRUE;
|
||||
u32 j;
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; ++j)
|
||||
{
|
||||
if (partyEntry->moves[j] != MOVE_NONE)
|
||||
noMoveSet = FALSE;
|
||||
}
|
||||
if (noMoveSet)
|
||||
{
|
||||
GiveMonInitialMoveset(mon);
|
||||
// TODO: Figure out a default strategy when moves are not set, to generate a good moveset
|
||||
return;
|
||||
}
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; ++j)
|
||||
{
|
||||
u32 pp = GetMovePP(partyEntry->moves[j]);
|
||||
SetMonData(mon, MON_DATA_MOVE1 + j, &partyEntry->moves[j]);
|
||||
SetMonData(mon, MON_DATA_PP1 + j, &pp);
|
||||
}
|
||||
}
|
||||
|
||||
u32 GeneratePersonalityForGender(u32 gender, u32 species)
|
||||
{
|
||||
const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[species];
|
||||
if (gender == MON_MALE)
|
||||
{
|
||||
assertf(speciesInfo->genderRatio < MON_FEMALE, "species %d cannot be male", species);
|
||||
return ((255 - speciesInfo->genderRatio) / 2) + speciesInfo->genderRatio;
|
||||
}
|
||||
if (gender == MON_FEMALE)
|
||||
{
|
||||
assertf(speciesInfo->genderRatio != MON_MALE && speciesInfo->genderRatio != MON_GENDERLESS, "species %d cannot be female", species);
|
||||
return speciesInfo->genderRatio / 2;
|
||||
}
|
||||
if (gender == MON_GENDERLESS)
|
||||
assertf(speciesInfo->genderRatio == MON_GENDERLESS, "species %d cannot be genderless", species);
|
||||
else
|
||||
errorf("GeneratePersonalityForGender called with invalid gender value %d", gender);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const u8 sModuloLUT[25] = {0, 21, 17, 13, 9, 5, 1, 22, 18, 14, 10, 6, 2, 23, 19, 15, 11, 7, 3, 24, 20, 16, 12, 8, 4};
|
||||
|
||||
static void ModifyPersonalityForNature(u32 *personality, s32 newNature)
|
||||
{
|
||||
s32 nature = GetNatureFromPersonality(*personality);
|
||||
s32 diff = abs(newNature - nature);
|
||||
s32 sign = (newNature > nature) ? 1 : -1;
|
||||
if (diff > NUM_NATURES / 2)
|
||||
{
|
||||
diff = NUM_NATURES - diff;
|
||||
sign *= -1;
|
||||
}
|
||||
*personality += (sModuloLUT[diff] * 0x100 * sign);
|
||||
}
|
||||
|
||||
static void SetCorrectAbilityNum(struct Pokemon *mon, u32 species, u32 ability)
|
||||
{
|
||||
const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[species];
|
||||
u32 abilityNum;
|
||||
u32 maxAbilityNum = ARRAY_COUNT(speciesInfo->abilities);
|
||||
for (abilityNum = 0; abilityNum < maxAbilityNum; ++abilityNum)
|
||||
{
|
||||
if (speciesInfo->abilities[abilityNum] == ability)
|
||||
break;
|
||||
}
|
||||
assertf(abilityNum < maxAbilityNum, "illegal ability %S for %S", gAbilitiesInfo[ability].name, speciesInfo->speciesName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetMonData(mon, MON_DATA_ABILITY_NUM, &abilityNum);
|
||||
}
|
||||
|
||||
void GenerateMonFromTrainerMon(struct Pokemon *mon, const struct TrainerMon *trainerMon, struct TrainerGenerator *trainer)
|
||||
{
|
||||
u32 data;
|
||||
u32 personality = (LocalRandom32(&trainer->localRngState) & 0xFFFFDF00) + 0x1000;
|
||||
u32 genderValue = 0;
|
||||
if (trainerMon->gender == TRAINER_MON_RANDOM_GENDER)
|
||||
genderValue = LocalRandom32(&trainer->localRngState) & 0x000000FF;
|
||||
else if (trainerMon->gender == TRAINER_MON_MALE)
|
||||
genderValue = GeneratePersonalityForGender(MON_MALE, trainerMon->species);
|
||||
else if (trainerMon->gender == TRAINER_MON_FEMALE)
|
||||
genderValue = GeneratePersonalityForGender(MON_FEMALE, trainerMon->species);
|
||||
else
|
||||
errorf("Unkwown trainer mon gender value %d", trainerMon->gender);
|
||||
personality |= genderValue;
|
||||
ModifyPersonalityForNature(&personality, trainerMon->nature);
|
||||
CreateMon(mon, trainerMon->species, trainerMon->lvl, personality, trainer->otID);
|
||||
if (trainerMon->nickname != NULL)
|
||||
SetMonData(mon, MON_DATA_NICKNAME, trainerMon->nickname);
|
||||
if (trainerMon->ev) //ev in struct TrainerMon are stored in Showdown order not GF order
|
||||
{
|
||||
SetMonData(mon, MON_DATA_HP_EV, &trainerMon->ev[0]);
|
||||
SetMonData(mon, MON_DATA_ATK_EV, &trainerMon->ev[1]);
|
||||
SetMonData(mon, MON_DATA_DEF_EV, &trainerMon->ev[2]);
|
||||
SetMonData(mon, MON_DATA_SPATK_EV, &trainerMon->ev[3]);
|
||||
SetMonData(mon, MON_DATA_SPDEF_EV, &trainerMon->ev[4]);
|
||||
SetMonData(mon, MON_DATA_SPEED_EV, &trainerMon->ev[5]);
|
||||
|
||||
/*
|
||||
for (u32 i = 0; i < NUM_STATS; i++)
|
||||
SetMonData(mon, MON_DATA_HP_EV + i, &trainerMon->ev[i]);
|
||||
*/
|
||||
}
|
||||
|
||||
SetMonData(mon, MON_DATA_IVS, &trainerMon->iv);
|
||||
CustomTrainerPartyAssignMoves(mon, trainerMon);
|
||||
SetMonData(mon, MON_DATA_HELD_ITEM, &trainerMon->heldItem);
|
||||
|
||||
if (trainerMon->ability)
|
||||
{
|
||||
SetCorrectAbilityNum(mon, trainerMon->species, trainerMon->ability);
|
||||
}
|
||||
else if (B_TRAINER_MON_HIDDEN_ABILITY)
|
||||
{
|
||||
do {
|
||||
data = Random() % NUM_ABILITY_SLOTS; // includes hidden abilities
|
||||
} while (GetAbilityBySpecies(trainerMon->species, data) == ABILITY_NONE);
|
||||
SetMonData(mon, MON_DATA_ABILITY_NUM, &data);
|
||||
}
|
||||
|
||||
if (trainerMon->ball < POKEBALL_COUNT)
|
||||
{
|
||||
data = trainerMon->ball;
|
||||
SetMonData(mon, MON_DATA_POKEBALL, &data);
|
||||
}
|
||||
else if (B_TRAINER_CLASS_POKE_BALLS >= GEN_7 && trainer->trainerClass && trainerMon->ball == POKEBALL_COUNT)
|
||||
{
|
||||
data = gTrainerClasses[trainer->trainerClass].ball ?: BALL_POKE;
|
||||
SetMonData(mon, MON_DATA_POKEBALL, &data);
|
||||
}
|
||||
else if (trainerMon->ball > POKEBALL_COUNT)
|
||||
{
|
||||
errorf("Invalid ball for %S in %S's party", GetMonData(mon, MON_DATA_NICKNAME), trainer->name);
|
||||
}
|
||||
|
||||
SetMonData(mon, MON_DATA_FRIENDSHIP, &trainerMon->friendship);
|
||||
|
||||
data = trainerMon->isShiny;
|
||||
SetMonData(mon, MON_DATA_IS_SHINY, &data);
|
||||
if (trainerMon->dynamaxLevel > 0)
|
||||
{
|
||||
data = trainerMon->dynamaxLevel;
|
||||
SetMonData(mon, MON_DATA_DYNAMAX_LEVEL, &data);
|
||||
}
|
||||
if (trainerMon->gigantamaxFactor)
|
||||
{
|
||||
data = trainerMon->gigantamaxFactor;
|
||||
SetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR, &data);
|
||||
}
|
||||
if (trainerMon->teraType)
|
||||
{
|
||||
data = trainerMon->teraType;
|
||||
SetMonData(mon, MON_DATA_TERA_TYPE, &data);
|
||||
}
|
||||
|
||||
CalculateMonStats(mon);
|
||||
SetMonData(mon, MON_DATA_OT_NAME, trainer->name);
|
||||
data = trainer->gender;
|
||||
SetMonData(mon, MON_DATA_OT_GENDER, &data);
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#include "global.h"
|
||||
#include "test/test.h"
|
||||
#include "battle.h"
|
||||
#include "battle_main.h"
|
||||
#include "battle_setup.h"
|
||||
#include "data.h"
|
||||
#include "malloc.h"
|
||||
#include "random.h"
|
||||
|
|
@ -18,7 +18,7 @@ TEST("CreateNPCTrainerPartyForTrainer generates customized Pokémon")
|
|||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = 3;
|
||||
u8 nickBuffer[20];
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(IsMonShiny(&testParty[0]));
|
||||
EXPECT(!IsMonShiny(&testParty[1]));
|
||||
|
||||
|
|
@ -94,7 +94,7 @@ TEST("CreateNPCTrainerPartyForTrainer generates different personalities for diff
|
|||
{
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = 3;
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(testParty[0].box.personality != testParty[1].box.personality);
|
||||
Free(testParty);
|
||||
}
|
||||
|
|
@ -120,7 +120,7 @@ TEST("Trainer Class Balls apply to the entire party")
|
|||
u32 j;
|
||||
u32 currTrainer = 14;
|
||||
const struct Trainer *trainer = GetTrainerStructFromId(currTrainer);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, trainer, TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, trainer, TRUE);
|
||||
for(j = 0; j < 6; j++)
|
||||
{
|
||||
EXPECT(GetMonData(&testParty[j], MON_DATA_POKEBALL, 0) == gTrainerClasses[trainer->trainerClass].ball);
|
||||
|
|
@ -133,7 +133,7 @@ TEST("Difficulty default to Normal if the trainer doesn't have a member for the
|
|||
SetCurrentDifficultyLevel(DIFFICULTY_EASY);
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = 4;
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_MEWTWO);
|
||||
Free(testParty);
|
||||
SetCurrentDifficultyLevel(DIFFICULTY_NORMAL);
|
||||
|
|
@ -144,7 +144,7 @@ TEST("Difficulty changes which party is used for enemy trainer if defined for th
|
|||
SetCurrentDifficultyLevel(DIFFICULTY_EASY);
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = 5;
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_METAPOD);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 1);
|
||||
Free(testParty);
|
||||
|
|
@ -156,7 +156,7 @@ TEST("Difficulty changes which party is used for enemy trainer if defined for th
|
|||
SetCurrentDifficultyLevel(DIFFICULTY_HARD);
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = 5;
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_ARCEUS);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 99);
|
||||
Free(testParty);
|
||||
|
|
@ -168,7 +168,7 @@ TEST("Difficulty changes which party is used for enemy trainer if defined for th
|
|||
SetCurrentDifficultyLevel(DIFFICULTY_NORMAL);
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = 5;
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_MEWTWO);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 50);
|
||||
Free(testParty);
|
||||
|
|
@ -179,7 +179,7 @@ TEST("Difficulty default to Normal if the partner doesn't have a member for the
|
|||
SetCurrentDifficultyLevel(DIFFICULTY_TEST);
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = TRAINER_PARTNER(1);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_METANG);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 42);
|
||||
Free(testParty);
|
||||
|
|
@ -191,7 +191,7 @@ TEST("Difficulty changes which party is used for partner if defined for the diff
|
|||
SetCurrentDifficultyLevel(DIFFICULTY_EASY);
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = TRAINER_PARTNER(1);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_METAPOD);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 1);
|
||||
Free(testParty);
|
||||
|
|
@ -203,7 +203,7 @@ TEST("Difficulty changes which party is used for partner if defined for the diff
|
|||
SetCurrentDifficultyLevel(DIFFICULTY_HARD);
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = TRAINER_PARTNER(1);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_ARCEUS);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 99);
|
||||
Free(testParty);
|
||||
|
|
@ -215,7 +215,7 @@ TEST("Difficulty changes which party is used for partner if defined for the diff
|
|||
SetCurrentDifficultyLevel(DIFFICULTY_NORMAL);
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = TRAINER_PARTNER(1);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_METANG);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 42);
|
||||
Free(testParty);
|
||||
|
|
@ -225,7 +225,7 @@ TEST("Trainer Party Pool generates a party from the trainer pool")
|
|||
{
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = 6;
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_EEVEE);
|
||||
Free(testParty);
|
||||
}
|
||||
|
|
@ -234,7 +234,7 @@ TEST("Trainer Party Pool picks a random lead and a random ace if tags exist in t
|
|||
{
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = 7;
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_ARON); // Lead
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_SPECIES) == SPECIES_WYNAUT); // Not Lead or Ace
|
||||
EXPECT(GetMonData(&testParty[2], MON_DATA_SPECIES) == SPECIES_EEVEE); // Ace
|
||||
|
|
@ -245,10 +245,12 @@ TEST("Trainer Party Pool picks according to custom rules")
|
|||
{
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = 8;
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE);
|
||||
gBattleTypeFlags = BATTLE_TYPE_DOUBLE;
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_TORKOAL); // Lead + Weather Setter
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_SPECIES) == SPECIES_BULBASAUR); // Lead + Weather Abuser
|
||||
EXPECT(GetMonData(&testParty[2], MON_DATA_SPECIES) == SPECIES_EEVEE); // Anything else
|
||||
gBattleTypeFlags = 0;
|
||||
Free(testParty);
|
||||
}
|
||||
|
||||
|
|
@ -256,7 +258,7 @@ TEST("Trainer Party Pool uses standard party creation if pool is illegal")
|
|||
{
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = 9;
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_WYNAUT);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_SPECIES) == SPECIES_WOBBUFFET);
|
||||
Free(testParty);
|
||||
|
|
@ -266,7 +268,7 @@ TEST("Trainer Party Pool can be pruned before picking")
|
|||
{
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = 10;
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_EEVEE);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_SPECIES) == SPECIES_WYNAUT);
|
||||
Free(testParty);
|
||||
|
|
@ -276,7 +278,7 @@ TEST("Trainer Party Pool can choose which functions to use for picking mons")
|
|||
{
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u32 currTrainer = 11;
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, GetTrainerStructFromId(currTrainer), TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_WYNAUT);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_SPECIES) == SPECIES_WOBBUFFET);
|
||||
Free(testParty);
|
||||
|
|
@ -297,7 +299,7 @@ TEST("CreateNPCTrainerPartyForTrainer generates default moves if no moves are sp
|
|||
const struct Trainer *trainer = GetTrainerStructFromId(currTrainer);
|
||||
ASSUME(trainer->party[0].moves[0] == MOVE_NONE);
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, trainer, TRUE, BATTLE_TYPE_TRAINER);
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, trainer, TRUE);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_MOVE1) != MOVE_NONE);
|
||||
Free(testParty);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user