mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-12-26 19:54:21 +01:00
[trainer_parties] implement fully customizable npc trainer parties
fix nature related bug, fix hash generation, add tests
This commit is contained in:
parent
b9447f88fc
commit
aea5d79aa2
@ -1,6 +1,9 @@
|
|||||||
#ifndef GUARD_BATTLE_MAIN_H
|
#ifndef GUARD_BATTLE_MAIN_H
|
||||||
#define GUARD_BATTLE_MAIN_H
|
#define GUARD_BATTLE_MAIN_H
|
||||||
|
|
||||||
|
#include "pokemon.h"
|
||||||
|
#include "data.h"
|
||||||
|
|
||||||
struct TrainerMoney
|
struct TrainerMoney
|
||||||
{
|
{
|
||||||
u8 classId;
|
u8 classId;
|
||||||
@ -66,6 +69,7 @@ bool8 TryRunFromBattle(u8 battlerId);
|
|||||||
void SpecialStatusesClear(void);
|
void SpecialStatusesClear(void);
|
||||||
void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk);
|
void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk);
|
||||||
bool32 IsWildMonSmart(void);
|
bool32 IsWildMonSmart(void);
|
||||||
|
u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags);
|
||||||
|
|
||||||
extern struct MultiPartnerMenuPokemon gMultiPartnerParty[MULTI_PARTY_SIZE];
|
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
|
// 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
|
// party. Some trainer parties also specify held items and custom moves for each
|
||||||
// Pokémon.
|
// Pokémon.
|
||||||
#define F_TRAINER_PARTY_CUSTOM_MOVESET (1 << 0)
|
#define F_TRAINER_PARTY_CUSTOM_MOVESET (1 << 0)
|
||||||
#define F_TRAINER_PARTY_HELD_ITEM (1 << 1)
|
#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
|
#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 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_WIDTH(size)((size >> 4) * 8)
|
||||||
#define GET_MON_COORDS_HEIGHT(size)((size & 0xF) * 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
|
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 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_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 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
|
union TrainerMonPtr
|
||||||
{
|
{
|
||||||
@ -75,20 +96,21 @@ union TrainerMonPtr
|
|||||||
const struct TrainerMonNoItemCustomMoves *NoItemCustomMoves;
|
const struct TrainerMonNoItemCustomMoves *NoItemCustomMoves;
|
||||||
const struct TrainerMonItemDefaultMoves *ItemDefaultMoves;
|
const struct TrainerMonItemDefaultMoves *ItemDefaultMoves;
|
||||||
const struct TrainerMonItemCustomMoves *ItemCustomMoves;
|
const struct TrainerMonItemCustomMoves *ItemCustomMoves;
|
||||||
|
const struct TrainerMonCustomized *EverythingCustomized;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Trainer
|
struct Trainer
|
||||||
{
|
{
|
||||||
/*0x00*/ u8 partyFlags;
|
/*0x00*/ u32 aiFlags;
|
||||||
/*0x01*/ u8 trainerClass;
|
/*0x04*/ union TrainerMonPtr party;
|
||||||
/*0x02*/ u8 encounterMusic_gender; // last bit is gender
|
/*0x08*/ u16 items[MAX_TRAINER_ITEMS];
|
||||||
/*0x03*/ u8 trainerPic;
|
/*0x10*/ u8 trainerClass;
|
||||||
/*0x04*/ u8 trainerName[TRAINER_NAME_LENGTH + 1];
|
/*0x11*/ u8 encounterMusic_gender; // last bit is gender
|
||||||
/*0x10*/ u16 items[MAX_TRAINER_ITEMS];
|
/*0x12*/ u8 trainerPic;
|
||||||
/*0x18*/ bool8 doubleBattle;
|
/*0x13*/ u8 trainerName[TRAINER_NAME_LENGTH + 1];
|
||||||
/*0x1C*/ u32 aiFlags;
|
/*0x1E*/ bool8 doubleBattle:1;
|
||||||
/*0x20*/ u8 partySize;
|
u8 partyFlags:7;
|
||||||
/*0x24*/ union TrainerMonPtr party;
|
/*0x1F*/ u8 partySize;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TRAINER_ENCOUNTER_MUSIC(trainer)((gTrainers[trainer].encounterMusic_gender & 0x7F))
|
#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) CAT_(a, b)
|
||||||
#define CAT_(a, b) 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.
|
// This produces an error at compile-time if expr is zero.
|
||||||
// It looks like file.c:line: size of array `id' is negative
|
// It looks like file.c:line: size of array `id' is negative
|
||||||
#define STATIC_ASSERT(expr, id) typedef char id[(expr) ? 1 : -1];
|
#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(struct Sprite *sprite);
|
||||||
static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite);
|
static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite);
|
||||||
static void TrySpecialEvolution(void);
|
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_X = 0;
|
||||||
EWRAM_DATA u16 gBattle_BG0_Y = 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;
|
u32 personalityValue;
|
||||||
u8 fixedIV;
|
u8 fixedIV;
|
||||||
s32 i, j;
|
s32 i, j;
|
||||||
u8 monsCount;
|
u8 monsCount;
|
||||||
u16 ball;
|
s32 ball = -1;
|
||||||
|
if (battleTypeFlags & BATTLE_TYPE_TRAINER && !(battleTypeFlags & (BATTLE_TYPE_FRONTIER
|
||||||
if (trainerNum == TRAINER_SECRET_BASE)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & (BATTLE_TYPE_FRONTIER
|
|
||||||
| BATTLE_TYPE_EREADER_TRAINER
|
| BATTLE_TYPE_EREADER_TRAINER
|
||||||
| BATTLE_TYPE_TRAINER_HILL)))
|
| BATTLE_TYPE_TRAINER_HILL)))
|
||||||
{
|
{
|
||||||
if (firstTrainer == TRUE)
|
if (firstTrainer == TRUE)
|
||||||
ZeroEnemyPartyMons();
|
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;
|
monsCount = PARTY_SIZE / 2;
|
||||||
else
|
else
|
||||||
monsCount = gTrainers[trainerNum].partySize;
|
monsCount = trainer->partySize;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
monsCount = gTrainers[trainerNum].partySize;
|
monsCount = trainer->partySize;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < monsCount; i++)
|
for (i = 0; i < monsCount; i++)
|
||||||
{
|
{
|
||||||
|
u32 personalityHash = GeneratePartyHash(trainer, i);
|
||||||
if (gTrainers[trainerNum].doubleBattle == TRUE)
|
if (trainer->doubleBattle == TRUE)
|
||||||
personalityValue = 0x80;
|
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
|
personalityValue = 0x78; // Use personality more likely to result in a female Pokémon
|
||||||
else
|
else
|
||||||
personalityValue = 0x88; // Use personality more likely to result in a male Pokémon
|
personalityValue = 0x88; // Use personality more likely to result in a male Pokémon
|
||||||
|
|
||||||
for (j = 0; gTrainers[trainerNum].trainerName[j] != EOS; j++)
|
personalityValue += personalityHash << 8;
|
||||||
nameHash += gTrainers[trainerNum].trainerName[j];
|
switch (trainer->partyFlags)
|
||||||
|
|
||||||
switch (gTrainers[trainerNum].partyFlags)
|
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
const struct TrainerMonNoItemDefaultMoves *partyData = gTrainers[trainerNum].party.NoItemDefaultMoves;
|
const struct TrainerMonNoItemDefaultMoves *partyData = trainer->party.NoItemDefaultMoves;
|
||||||
|
|
||||||
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++)
|
|
||||||
nameHash += gSpeciesNames[partyData[i].species][j];
|
|
||||||
|
|
||||||
personalityValue += nameHash << 8;
|
|
||||||
fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
|
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);
|
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case F_TRAINER_PARTY_CUSTOM_MOVESET:
|
case F_TRAINER_PARTY_CUSTOM_MOVESET:
|
||||||
{
|
{
|
||||||
const struct TrainerMonNoItemCustomMoves *partyData = gTrainers[trainerNum].party.NoItemCustomMoves;
|
const struct TrainerMonNoItemCustomMoves *partyData = trainer->party.NoItemCustomMoves;
|
||||||
|
|
||||||
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++)
|
|
||||||
nameHash += gSpeciesNames[partyData[i].species][j];
|
|
||||||
|
|
||||||
personalityValue += nameHash << 8;
|
|
||||||
fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
|
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);
|
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:
|
case F_TRAINER_PARTY_HELD_ITEM:
|
||||||
{
|
{
|
||||||
const struct TrainerMonItemDefaultMoves *partyData = gTrainers[trainerNum].party.ItemDefaultMoves;
|
const struct TrainerMonItemDefaultMoves *partyData = trainer->party.ItemDefaultMoves;
|
||||||
|
|
||||||
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++)
|
|
||||||
nameHash += gSpeciesNames[partyData[i].species][j];
|
|
||||||
|
|
||||||
personalityValue += nameHash << 8;
|
|
||||||
fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
|
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);
|
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:
|
case F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM:
|
||||||
{
|
{
|
||||||
const struct TrainerMonItemCustomMoves *partyData = gTrainers[trainerNum].party.ItemCustomMoves;
|
const struct TrainerMonItemCustomMoves *partyData = trainer->party.ItemCustomMoves;
|
||||||
|
|
||||||
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++)
|
|
||||||
nameHash += gSpeciesNames[partyData[i].species][j];
|
|
||||||
|
|
||||||
personalityValue += nameHash << 8;
|
|
||||||
fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
|
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);
|
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;
|
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
|
#if B_TRAINER_CLASS_POKE_BALLS >= GEN_7
|
||||||
ball = (sTrainerBallTable[gTrainers[trainerNum].trainerClass]) ? sTrainerBallTable[gTrainers[trainerNum].trainerClass] : ITEM_POKE_BALL;
|
if (ball == -1)
|
||||||
SetMonData(&party[i], MON_DATA_POKEBALL, &ball);
|
{
|
||||||
|
ball = (sTrainerBallTable[trainer->trainerClass]) ? sTrainerBallTable[trainer->trainerClass] : ITEM_POKE_BALL;
|
||||||
|
SetMonData(&party[i], MON_DATA_POKEBALL, &ball);
|
||||||
|
}
|
||||||
#endif
|
#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)
|
void VBlankCB_Battle(void)
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "battle.h"
|
#include "battle.h"
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
#include "graphics.h"
|
#include "graphics.h"
|
||||||
|
#include "constants/abilities.h"
|
||||||
#include "constants/items.h"
|
#include "constants/items.h"
|
||||||
#include "constants/moves.h"
|
#include "constants/moves.h"
|
||||||
#include "constants/trainers.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