mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-11-16 11:37:40 +01:00
Implement Customizable NPC Trainer Parties (#2733)
This commit is contained in:
commit
dc9f0b5107
@ -1,6 +1,9 @@
|
||||
#ifndef GUARD_BATTLE_MAIN_H
|
||||
#define GUARD_BATTLE_MAIN_H
|
||||
|
||||
#include "pokemon.h"
|
||||
#include "data.h"
|
||||
|
||||
struct TrainerMoney
|
||||
{
|
||||
u8 classId;
|
||||
@ -66,6 +69,7 @@ bool8 TryRunFromBattle(u8 battlerId);
|
||||
void SpecialStatusesClear(void);
|
||||
void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk);
|
||||
bool32 IsWildMonSmart(void);
|
||||
u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags);
|
||||
|
||||
extern struct MultiPartnerMenuPokemon gMultiPartnerParty[MULTI_PARTY_SIZE];
|
||||
|
||||
|
@ -374,7 +374,12 @@
|
||||
// All trainer parties specify the IV, level, and species for each Pokémon in the
|
||||
// party. Some trainer parties also specify held items and custom moves for each
|
||||
// Pokémon.
|
||||
#define F_TRAINER_PARTY_CUSTOM_MOVESET (1 << 0)
|
||||
#define F_TRAINER_PARTY_HELD_ITEM (1 << 1)
|
||||
#define F_TRAINER_PARTY_CUSTOM_MOVESET (1 << 0)
|
||||
#define F_TRAINER_PARTY_HELD_ITEM (1 << 1)
|
||||
#define F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED (1 << 3)
|
||||
|
||||
// Trainer party defines
|
||||
#define TRAINER_MON_MALE 1
|
||||
#define TRAINER_MON_FEMALE 2
|
||||
|
||||
#endif // GUARD_TRAINERS_H
|
||||
|
@ -31,6 +31,26 @@ struct MonCoords
|
||||
#define MON_COORDS_SIZE(width, height)(DIV_ROUND_UP(width, 8) << 4 | DIV_ROUND_UP(height, 8))
|
||||
#define GET_MON_COORDS_WIDTH(size)((size >> 4) * 8)
|
||||
#define GET_MON_COORDS_HEIGHT(size)((size & 0xF) * 8)
|
||||
#define TRAINER_PARTY_IVS(hp, atk, def, speed, spatk, spdef) (hp | (atk << 5) | (def << 10) | (speed << 15) | (spatk << 20) | (spdef << 25))
|
||||
#define TRAINER_PARTY_EVS(hp, atk, def, speed, spatk, spdef) ((const u8[6]){hp,atk,def,spatk,spdef,speed})
|
||||
#define TRAINER_PARTY_NATURE(nature) (nature+1)
|
||||
|
||||
struct TrainerMonCustomized
|
||||
{
|
||||
const u8 *nickname;
|
||||
const u8 *ev;
|
||||
u32 iv;
|
||||
u16 moves[4];
|
||||
u16 species;
|
||||
u16 heldItem;
|
||||
u16 ability;
|
||||
u8 lvl;
|
||||
u8 ball;
|
||||
u8 friendship;
|
||||
u8 nature : 5;
|
||||
bool8 gender : 2;
|
||||
bool8 isShiny : 1;
|
||||
};
|
||||
|
||||
struct TrainerMonNoItemDefaultMoves
|
||||
{
|
||||
@ -68,6 +88,7 @@ struct TrainerMonItemCustomMoves
|
||||
#define NO_ITEM_CUSTOM_MOVES(party) { .NoItemCustomMoves = party }, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_CUSTOM_MOVESET
|
||||
#define ITEM_DEFAULT_MOVES(party) { .ItemDefaultMoves = party }, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_HELD_ITEM
|
||||
#define ITEM_CUSTOM_MOVES(party) { .ItemCustomMoves = party }, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM
|
||||
#define EVERYTHING_CUSTOMIZED(party) { .EverythingCustomized = party}, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED
|
||||
|
||||
union TrainerMonPtr
|
||||
{
|
||||
@ -75,20 +96,21 @@ union TrainerMonPtr
|
||||
const struct TrainerMonNoItemCustomMoves *NoItemCustomMoves;
|
||||
const struct TrainerMonItemDefaultMoves *ItemDefaultMoves;
|
||||
const struct TrainerMonItemCustomMoves *ItemCustomMoves;
|
||||
const struct TrainerMonCustomized *EverythingCustomized;
|
||||
};
|
||||
|
||||
struct Trainer
|
||||
{
|
||||
/*0x00*/ u8 partyFlags;
|
||||
/*0x01*/ u8 trainerClass;
|
||||
/*0x02*/ u8 encounterMusic_gender; // last bit is gender
|
||||
/*0x03*/ u8 trainerPic;
|
||||
/*0x04*/ u8 trainerName[TRAINER_NAME_LENGTH + 1];
|
||||
/*0x10*/ u16 items[MAX_TRAINER_ITEMS];
|
||||
/*0x18*/ bool8 doubleBattle;
|
||||
/*0x1C*/ u32 aiFlags;
|
||||
/*0x20*/ u8 partySize;
|
||||
/*0x24*/ union TrainerMonPtr party;
|
||||
/*0x00*/ u32 aiFlags;
|
||||
/*0x04*/ union TrainerMonPtr party;
|
||||
/*0x08*/ u16 items[MAX_TRAINER_ITEMS];
|
||||
/*0x10*/ u8 trainerClass;
|
||||
/*0x11*/ u8 encounterMusic_gender; // last bit is gender
|
||||
/*0x12*/ u8 trainerPic;
|
||||
/*0x13*/ u8 trainerName[TRAINER_NAME_LENGTH + 1];
|
||||
/*0x1E*/ bool8 doubleBattle:1;
|
||||
u8 partyFlags:7;
|
||||
/*0x1F*/ u8 partySize;
|
||||
};
|
||||
|
||||
#define TRAINER_ENCOUNTER_MUSIC(trainer)((gTrainers[trainer].encounterMusic_gender & 0x7F))
|
||||
|
@ -147,6 +147,9 @@
|
||||
#define CAT(a, b) CAT_(a, b)
|
||||
#define CAT_(a, b) a ## b
|
||||
|
||||
// Converts a string to a compound literal, essentially making it a pointer to const u8
|
||||
#define COMPOUND_STRING(str) (const u8[]) _(str)
|
||||
|
||||
// This produces an error at compile-time if expr is zero.
|
||||
// It looks like file.c:line: size of array `id' is negative
|
||||
#define STATIC_ASSERT(expr, id) typedef char id[(expr) ? 1 : -1];
|
||||
|
@ -118,6 +118,10 @@ static void HandleEndTurn_FinishBattle(void);
|
||||
static void SpriteCB_UnusedBattleInit(struct Sprite *sprite);
|
||||
static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite);
|
||||
static void TrySpecialEvolution(void);
|
||||
static u32 Crc32B (const u8 *data, u32 size);
|
||||
static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i);
|
||||
static void ModifyPersonalityForNature(u32 *personality, u32 newNature);
|
||||
static u32 GeneratePersonalityForGender(u32 gender, u32 species);
|
||||
|
||||
EWRAM_DATA u16 gBattle_BG0_X = 0;
|
||||
EWRAM_DATA u16 gBattle_BG0_Y = 0;
|
||||
@ -1869,72 +1873,129 @@ static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite)
|
||||
}
|
||||
}
|
||||
|
||||
static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer)
|
||||
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;
|
||||
u32 n;
|
||||
if (trainer->partyFlags == 0)
|
||||
{
|
||||
buffer = (const u8 *) &trainer->party.NoItemDefaultMoves[i];
|
||||
n = sizeof(*trainer->party.NoItemDefaultMoves);
|
||||
}
|
||||
else if (trainer->partyFlags == F_TRAINER_PARTY_CUSTOM_MOVESET)
|
||||
{
|
||||
buffer = (const u8 *) &trainer->party.NoItemCustomMoves[i];
|
||||
n = sizeof(*trainer->party.NoItemCustomMoves);
|
||||
}
|
||||
else if (trainer->partyFlags == F_TRAINER_PARTY_HELD_ITEM)
|
||||
{
|
||||
buffer = (const u8 *) &trainer->party.ItemDefaultMoves[i];
|
||||
n = sizeof(*trainer->party.ItemDefaultMoves);
|
||||
}
|
||||
else if (trainer->partyFlags == (F_TRAINER_PARTY_HELD_ITEM | F_TRAINER_PARTY_CUSTOM_MOVESET))
|
||||
{
|
||||
buffer = (const u8 *) &trainer->party.ItemCustomMoves[i];
|
||||
n = sizeof(*trainer->party.ItemCustomMoves);
|
||||
}
|
||||
else if (trainer->partyFlags == F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED)
|
||||
{
|
||||
buffer = (const u8 *) &trainer->party.EverythingCustomized[i];
|
||||
n = sizeof(*trainer->party.EverythingCustomized);
|
||||
}
|
||||
return Crc32B(buffer, n);
|
||||
}
|
||||
|
||||
static void ModifyPersonalityForNature(u32 *personality, u32 newNature)
|
||||
{
|
||||
u32 nature = GetNatureFromPersonality(*personality);
|
||||
s32 diff = abs(nature - newNature);
|
||||
s32 sign = (nature > newNature) ? 1 : -1;
|
||||
if (diff > NUM_NATURES / 2)
|
||||
{
|
||||
diff = NUM_NATURES - diff;
|
||||
sign *= -1;
|
||||
}
|
||||
*personality -= (diff * sign);
|
||||
}
|
||||
|
||||
static u32 GeneratePersonalityForGender(u32 gender, u32 species)
|
||||
{
|
||||
const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[species];
|
||||
if (gender == MON_MALE)
|
||||
return ((255 - speciesInfo->genderRatio) / 2) + speciesInfo->genderRatio;
|
||||
else
|
||||
return speciesInfo->genderRatio / 2;
|
||||
}
|
||||
|
||||
u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags)
|
||||
{
|
||||
u32 nameHash = 0;
|
||||
u32 personalityValue;
|
||||
u8 fixedIV;
|
||||
s32 i, j;
|
||||
u8 monsCount;
|
||||
u16 ball;
|
||||
|
||||
if (trainerNum == TRAINER_SECRET_BASE)
|
||||
return 0;
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & (BATTLE_TYPE_FRONTIER
|
||||
s32 ball = -1;
|
||||
if (battleTypeFlags & BATTLE_TYPE_TRAINER && !(battleTypeFlags & (BATTLE_TYPE_FRONTIER
|
||||
| BATTLE_TYPE_EREADER_TRAINER
|
||||
| BATTLE_TYPE_TRAINER_HILL)))
|
||||
{
|
||||
if (firstTrainer == TRUE)
|
||||
ZeroEnemyPartyMons();
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
|
||||
if (battleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
|
||||
{
|
||||
if (gTrainers[trainerNum].partySize > PARTY_SIZE / 2)
|
||||
if (trainer->partySize > PARTY_SIZE / 2)
|
||||
monsCount = PARTY_SIZE / 2;
|
||||
else
|
||||
monsCount = gTrainers[trainerNum].partySize;
|
||||
monsCount = trainer->partySize;
|
||||
}
|
||||
else
|
||||
{
|
||||
monsCount = gTrainers[trainerNum].partySize;
|
||||
monsCount = trainer->partySize;
|
||||
}
|
||||
|
||||
for (i = 0; i < monsCount; i++)
|
||||
{
|
||||
|
||||
if (gTrainers[trainerNum].doubleBattle == TRUE)
|
||||
u32 personalityHash = GeneratePartyHash(trainer, i);
|
||||
if (trainer->doubleBattle == TRUE)
|
||||
personalityValue = 0x80;
|
||||
else if (gTrainers[trainerNum].encounterMusic_gender & F_TRAINER_FEMALE)
|
||||
else if (trainer->encounterMusic_gender & F_TRAINER_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
|
||||
|
||||
for (j = 0; gTrainers[trainerNum].trainerName[j] != EOS; j++)
|
||||
nameHash += gTrainers[trainerNum].trainerName[j];
|
||||
|
||||
switch (gTrainers[trainerNum].partyFlags)
|
||||
personalityValue += personalityHash << 8;
|
||||
switch (trainer->partyFlags)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
const struct TrainerMonNoItemDefaultMoves *partyData = gTrainers[trainerNum].party.NoItemDefaultMoves;
|
||||
|
||||
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++)
|
||||
nameHash += gSpeciesNames[partyData[i].species][j];
|
||||
|
||||
personalityValue += nameHash << 8;
|
||||
const struct TrainerMonNoItemDefaultMoves *partyData = trainer->party.NoItemDefaultMoves;
|
||||
fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
|
||||
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
|
||||
break;
|
||||
}
|
||||
case F_TRAINER_PARTY_CUSTOM_MOVESET:
|
||||
{
|
||||
const struct TrainerMonNoItemCustomMoves *partyData = gTrainers[trainerNum].party.NoItemCustomMoves;
|
||||
|
||||
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++)
|
||||
nameHash += gSpeciesNames[partyData[i].species][j];
|
||||
|
||||
personalityValue += nameHash << 8;
|
||||
const struct TrainerMonNoItemCustomMoves *partyData = trainer->party.NoItemCustomMoves;
|
||||
fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
|
||||
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
|
||||
|
||||
@ -1947,12 +2008,7 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir
|
||||
}
|
||||
case F_TRAINER_PARTY_HELD_ITEM:
|
||||
{
|
||||
const struct TrainerMonItemDefaultMoves *partyData = gTrainers[trainerNum].party.ItemDefaultMoves;
|
||||
|
||||
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++)
|
||||
nameHash += gSpeciesNames[partyData[i].species][j];
|
||||
|
||||
personalityValue += nameHash << 8;
|
||||
const struct TrainerMonItemDefaultMoves *partyData = trainer->party.ItemDefaultMoves;
|
||||
fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
|
||||
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
|
||||
|
||||
@ -1961,12 +2017,7 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir
|
||||
}
|
||||
case F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM:
|
||||
{
|
||||
const struct TrainerMonItemCustomMoves *partyData = gTrainers[trainerNum].party.ItemCustomMoves;
|
||||
|
||||
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++)
|
||||
nameHash += gSpeciesNames[partyData[i].species][j];
|
||||
|
||||
personalityValue += nameHash << 8;
|
||||
const struct TrainerMonItemCustomMoves *partyData = trainer->party.ItemCustomMoves;
|
||||
fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
|
||||
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
|
||||
|
||||
@ -1979,18 +2030,88 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir
|
||||
}
|
||||
break;
|
||||
}
|
||||
case F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED:
|
||||
{
|
||||
const struct TrainerMonCustomized *partyData = trainer->party.EverythingCustomized;
|
||||
u32 otIdType = OT_ID_RANDOM_NO_SHINY;
|
||||
u32 fixedOtId = 0;
|
||||
if (partyData[i].gender == TRAINER_MON_MALE)
|
||||
personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[i].species);
|
||||
else if (partyData[i].gender == TRAINER_MON_FEMALE)
|
||||
personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[i].species);
|
||||
if (partyData[i].nature != 0)
|
||||
ModifyPersonalityForNature(&personalityValue, partyData[i].nature - 1);
|
||||
if (partyData[i].isShiny)
|
||||
{
|
||||
otIdType = OT_ID_PRESET;
|
||||
fixedOtId = HIHALF(personalityValue) ^ LOHALF(personalityValue);
|
||||
}
|
||||
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, 0, TRUE, personalityValue, otIdType, fixedOtId);
|
||||
SetMonData(&party[i], MON_DATA_HELD_ITEM, &partyData[i].heldItem);
|
||||
|
||||
// TODO: Figure out a default strategy when moves are not set, to generate a good moveset
|
||||
for (j = 0; j < MAX_MON_MOVES; ++j)
|
||||
{
|
||||
SetMonData(&party[i], MON_DATA_MOVE1 + j, &partyData[i].moves[j]);
|
||||
SetMonData(&party[i], MON_DATA_PP1 + j, &gBattleMoves[partyData[i].moves[j]].pp);
|
||||
}
|
||||
SetMonData(&party[i], MON_DATA_IVS, &(partyData[i].iv));
|
||||
if (partyData[i].ev != NULL)
|
||||
{
|
||||
SetMonData(&party[i], MON_DATA_HP_EV, &(partyData[i].ev[0]));
|
||||
SetMonData(&party[i], MON_DATA_ATK_EV, &(partyData[i].ev[1]));
|
||||
SetMonData(&party[i], MON_DATA_DEF_EV, &(partyData[i].ev[2]));
|
||||
SetMonData(&party[i], MON_DATA_SPATK_EV, &(partyData[i].ev[3]));
|
||||
SetMonData(&party[i], MON_DATA_SPDEF_EV, &(partyData[i].ev[4]));
|
||||
SetMonData(&party[i], 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(&party[i], MON_DATA_ABILITY_NUM, &j);
|
||||
}
|
||||
SetMonData(&party[i], MON_DATA_FRIENDSHIP, &(partyData[i].friendship));
|
||||
if (partyData[i].ball != ITEM_NONE)
|
||||
{
|
||||
ball = partyData[i].ball;
|
||||
SetMonData(&party[i], MON_DATA_POKEBALL, &ball);
|
||||
}
|
||||
if (partyData[i].nickname != NULL)
|
||||
{
|
||||
SetMonData(&party[i], MON_DATA_NICKNAME, partyData[i].nickname);
|
||||
}
|
||||
CalculateMonStats(&party[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#if B_TRAINER_CLASS_POKE_BALLS >= GEN_7
|
||||
ball = (sTrainerBallTable[gTrainers[trainerNum].trainerClass]) ? sTrainerBallTable[gTrainers[trainerNum].trainerClass] : ITEM_POKE_BALL;
|
||||
SetMonData(&party[i], MON_DATA_POKEBALL, &ball);
|
||||
if (ball == -1)
|
||||
{
|
||||
ball = (sTrainerBallTable[trainer->trainerClass]) ? sTrainerBallTable[trainer->trainerClass] : ITEM_POKE_BALL;
|
||||
SetMonData(&party[i], MON_DATA_POKEBALL, &ball);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
gBattleTypeFlags |= gTrainers[trainerNum].doubleBattle;
|
||||
}
|
||||
|
||||
return gTrainers[trainerNum].partySize;
|
||||
return trainer->partySize;
|
||||
}
|
||||
|
||||
static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer)
|
||||
{
|
||||
u8 retVal;
|
||||
if (trainerNum == TRAINER_SECRET_BASE)
|
||||
return 0;
|
||||
retVal = CreateNPCTrainerPartyFromTrainer(party, &gTrainers[trainerNum], firstTrainer, gBattleTypeFlags);
|
||||
|
||||
gBattleTypeFlags |= gTrainers[trainerNum].doubleBattle;
|
||||
}
|
||||
|
||||
void VBlankCB_Battle(void)
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "battle.h"
|
||||
#include "data.h"
|
||||
#include "graphics.h"
|
||||
#include "constants/abilities.h"
|
||||
#include "constants/items.h"
|
||||
#include "constants/moves.h"
|
||||
#include "constants/trainers.h"
|
||||
|
140
test/trainer_control.c
Normal file
140
test/trainer_control.c
Normal file
@ -0,0 +1,140 @@
|
||||
#include "global.h"
|
||||
#include "test.h"
|
||||
#include "battle.h"
|
||||
#include "battle_main.h"
|
||||
#include "data.h"
|
||||
#include "malloc.h"
|
||||
#include "string_util.h"
|
||||
#include "constants/item.h"
|
||||
#include "constants/abilities.h"
|
||||
#include "constants/trainers.h"
|
||||
#include "constants/battle.h"
|
||||
|
||||
|
||||
static const struct TrainerMonCustomized sTestParty1[] =
|
||||
{
|
||||
{
|
||||
.species = SPECIES_WOBBUFFET,
|
||||
.ball = ITEM_MASTER_BALL,
|
||||
.ability = ABILITY_TELEPATHY,
|
||||
.friendship = 42,
|
||||
.gender = TRAINER_MON_FEMALE,
|
||||
.heldItem = ITEM_ASSAULT_VEST,
|
||||
.isShiny = TRUE,
|
||||
.iv = TRAINER_PARTY_IVS(25,26,27,28,29,30),
|
||||
.ev = TRAINER_PARTY_EVS(252, 0, 0, 252, 4, 0),
|
||||
.lvl = 67,
|
||||
.moves = {MOVE_AIR_SLASH, MOVE_BARRIER, MOVE_SOLAR_BEAM, MOVE_EXPLOSION},
|
||||
.nature = TRAINER_PARTY_NATURE(NATURE_HASTY),
|
||||
.nickname = COMPOUND_STRING("Bubbles")
|
||||
},
|
||||
{
|
||||
.species = SPECIES_WOBBUFFET,
|
||||
.ability = ABILITY_SHADOW_TAG,
|
||||
.lvl = 5,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct TrainerMonNoItemDefaultMoves sTestParty2[] =
|
||||
{
|
||||
{
|
||||
.species = SPECIES_WOBBUFFET,
|
||||
.lvl = 5,
|
||||
},
|
||||
{
|
||||
.species = SPECIES_WOBBUFFET,
|
||||
.lvl = 6,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct Trainer sTestTrainer1 =
|
||||
{
|
||||
.trainerName = _("Test1"),
|
||||
.party = EVERYTHING_CUSTOMIZED(sTestParty1),
|
||||
};
|
||||
|
||||
static const struct Trainer sTestTrainer2 =
|
||||
{
|
||||
.trainerName = _("Test2"),
|
||||
.party = NO_ITEM_DEFAULT_MOVES(sTestParty2),
|
||||
};
|
||||
|
||||
TEST("CreateNPCTrainerPartyForTrainer generates customized Pokémon")
|
||||
{
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
u8 nickBuffer[20];
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainer1, TRUE, BATTLE_TYPE_TRAINER);
|
||||
EXPECT(IsMonShiny(&testParty[0]));
|
||||
EXPECT(!IsMonShiny(&testParty[1]));
|
||||
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_POKEBALL, 0) == ITEM_MASTER_BALL);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_POKEBALL, 0) == ITEM_POKE_BALL);
|
||||
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES, 0) == SPECIES_WOBBUFFET);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_SPECIES, 0) == SPECIES_WOBBUFFET);
|
||||
|
||||
EXPECT(GetMonAbility(&testParty[0]) == ABILITY_TELEPATHY);
|
||||
EXPECT(GetMonAbility(&testParty[1]) == ABILITY_SHADOW_TAG);
|
||||
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_FRIENDSHIP, 0) == 42);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_FRIENDSHIP, 0) == 0);
|
||||
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_HELD_ITEM, 0) == ITEM_ASSAULT_VEST);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_HELD_ITEM, 0) == ITEM_NONE);
|
||||
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_HP_IV, 0) == 25);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_ATK_IV, 0) == 26);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_DEF_IV, 0) == 27);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPEED_IV, 0) == 28);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPATK_IV, 0) == 29);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPDEF_IV, 0) == 30);
|
||||
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_HP_IV, 0) == 0);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_ATK_IV, 0) == 0);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_DEF_IV, 0) == 0);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_SPEED_IV, 0) == 0);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_SPATK_IV, 0) == 0);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_SPDEF_IV, 0) == 0);
|
||||
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_HP_EV, 0) == 252);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_ATK_EV, 0) == 0);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_DEF_EV, 0) == 0);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPEED_EV, 0) == 252);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPATK_EV, 0) == 4);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPDEF_EV, 0) == 0);
|
||||
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_HP_EV, 0) == 0);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_ATK_EV, 0) == 0);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_DEF_EV, 0) == 0);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_SPEED_EV, 0) == 0);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_SPATK_EV, 0) == 0);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_SPDEF_EV, 0) == 0);
|
||||
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL, 0) == 67);
|
||||
EXPECT(GetMonData(&testParty[1], MON_DATA_LEVEL, 0) == 5);
|
||||
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_MOVE1, 0) == MOVE_AIR_SLASH);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_MOVE2, 0) == MOVE_BARRIER);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_MOVE3, 0) == MOVE_SOLAR_BEAM);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_MOVE4, 0) == MOVE_EXPLOSION);
|
||||
|
||||
GetMonData(&testParty[0], MON_DATA_NICKNAME, nickBuffer);
|
||||
EXPECT(StringCompare(nickBuffer, COMPOUND_STRING("Bubbles")) == 0);
|
||||
|
||||
GetMonData(&testParty[1], MON_DATA_NICKNAME, nickBuffer);
|
||||
EXPECT(StringCompare(nickBuffer, COMPOUND_STRING("Wobbuffet")) == 0);
|
||||
|
||||
EXPECT(GetGenderFromSpeciesAndPersonality(GetMonData(&testParty[0], MON_DATA_SPECIES, 0), testParty[0].box.personality) == MON_FEMALE);
|
||||
|
||||
EXPECT(GetNature(&testParty[0]) == NATURE_HASTY);
|
||||
|
||||
Free(testParty);
|
||||
}
|
||||
|
||||
TEST("CreateNPCTrainerPartyForTrainer generates different personalities for different mons")
|
||||
{
|
||||
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainer2, TRUE, BATTLE_TYPE_TRAINER);
|
||||
EXPECT(testParty[0].box.personality != testParty[1].box.personality);
|
||||
Free(testParty);
|
||||
}
|
Loading…
Reference in New Issue
Block a user