mirror of
https://github.com/pret/pokeplatinum.git
synced 2026-04-24 15:07:47 -05:00
317 lines
9.7 KiB
C
317 lines
9.7 KiB
C
#include "trainer_data.h"
|
|
|
|
#include "constants/battle.h"
|
|
#include "constants/pokemon.h"
|
|
#include "generated/trainer_message_types.h"
|
|
|
|
#include "struct_defs/trainer.h"
|
|
|
|
#include "data/trainer_class_genders.h"
|
|
|
|
#include "charcode_util.h"
|
|
#include "field_battle_data_transfer.h"
|
|
#include "heap.h"
|
|
#include "math_util.h"
|
|
#include "message.h"
|
|
#include "narc.h"
|
|
#include "party.h"
|
|
#include "pokemon.h"
|
|
#include "savedata.h"
|
|
#include "savedata_misc.h"
|
|
#include "strbuf.h"
|
|
|
|
static void TrainerData_BuildParty(FieldBattleDTO *dto, int battler, int heapID);
|
|
|
|
void Trainer_Encounter(FieldBattleDTO *dto, const SaveData *saveData, int heapID)
|
|
{
|
|
Trainer trdata;
|
|
MessageLoader *msgLoader = MessageLoader_Init(MESSAGE_LOADER_NARC_HANDLE, NARC_INDEX_MSGDATA__PL_MSG, TEXT_BANK_NPC_TRAINER_NAMES, heapID);
|
|
const charcode_t *rivalName = MiscSaveBlock_RivalName(SaveData_MiscSaveBlockConst(saveData));
|
|
|
|
for (int i = 0; i < MAX_BATTLERS; i++) {
|
|
if (!dto->trainerIDs[i]) {
|
|
continue;
|
|
}
|
|
|
|
Trainer_Load(dto->trainerIDs[i], &trdata);
|
|
dto->trainer[i] = trdata;
|
|
|
|
if (trdata.header.trainerType == TRAINER_CLASS_RIVAL) {
|
|
CharCode_Copy(dto->trainer[i].name, rivalName);
|
|
} else {
|
|
Strbuf *trainerName = MessageLoader_GetNewStrbuf(msgLoader, dto->trainerIDs[i]);
|
|
Strbuf_ToChars(trainerName, dto->trainer[i].name, TRAINER_NAME_LEN + 1);
|
|
Strbuf_Free(trainerName);
|
|
}
|
|
|
|
TrainerData_BuildParty(dto, i, heapID);
|
|
}
|
|
|
|
dto->battleType |= trdata.header.battleType;
|
|
MessageLoader_Free(msgLoader);
|
|
}
|
|
|
|
u32 Trainer_LoadParam(int trainerID, enum TrainerDataParam paramID)
|
|
{
|
|
// TODO: can this be trainerheader?
|
|
u32 result;
|
|
Trainer trdata;
|
|
|
|
Trainer_Load(trainerID, &trdata);
|
|
|
|
switch (paramID) {
|
|
case TRDATA_TYPE:
|
|
result = trdata.header.monDataType;
|
|
break;
|
|
|
|
case TRDATA_CLASS:
|
|
result = trdata.header.trainerType;
|
|
break;
|
|
|
|
case TRDATA_SPRITE:
|
|
result = trdata.header.sprite;
|
|
break;
|
|
|
|
case TRDATA_PARTY_SIZE:
|
|
result = trdata.header.partySize;
|
|
break;
|
|
|
|
case TRDATA_ITEM_1:
|
|
case TRDATA_ITEM_2:
|
|
case TRDATA_ITEM_3:
|
|
case TRDATA_ITEM_4:
|
|
result = trdata.header.items[paramID - TRDATA_ITEM_1];
|
|
break;
|
|
|
|
case TRDATA_AI_MASK:
|
|
result = trdata.header.aiMask;
|
|
break;
|
|
|
|
case TRDATA_BATTLE_TYPE:
|
|
result = trdata.header.battleType;
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BOOL Trainer_HasMessageType(int trainerID, enum TrainerMessageType msgType, int heapID)
|
|
{
|
|
NARC *narc; // must declare up here to match
|
|
u16 offset, data[2];
|
|
|
|
BOOL result = FALSE;
|
|
int size = NARC_GetMemberSizeByIndexPair(NARC_INDEX_POKETOOL__TRMSG__TRTBL, 0);
|
|
NARC_ReadFromMemberByIndexPair(&offset, NARC_INDEX_POKETOOL__TRMSG__TRTBLOFS, 0, trainerID * 2, 2);
|
|
narc = NARC_ctor(NARC_INDEX_POKETOOL__TRMSG__TRTBL, heapID);
|
|
|
|
while (offset != size) {
|
|
NARC_ReadFromMember(narc, 0, offset, 4, data);
|
|
|
|
if (data[0] == trainerID && data[1] == msgType) {
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (data[0] != trainerID) {
|
|
break;
|
|
}
|
|
|
|
offset += 4;
|
|
}
|
|
|
|
NARC_dtor(narc);
|
|
return result;
|
|
}
|
|
|
|
void Trainer_LoadMessage(int trainerID, enum TrainerMessageType msgType, Strbuf *strbuf, int heapID)
|
|
{
|
|
NARC *narc; // must declare up here to match
|
|
u16 offset, data[2];
|
|
|
|
int size = NARC_GetMemberSizeByIndexPair(NARC_INDEX_POKETOOL__TRMSG__TRTBL, 0);
|
|
NARC_ReadFromMemberByIndexPair(&offset, NARC_INDEX_POKETOOL__TRMSG__TRTBLOFS, 0, trainerID * 2, 2);
|
|
narc = NARC_ctor(NARC_INDEX_POKETOOL__TRMSG__TRTBL, heapID);
|
|
|
|
while (offset != size) {
|
|
NARC_ReadFromMember(narc, 0, offset, 4, data);
|
|
|
|
if (data[0] == trainerID && data[1] == msgType) {
|
|
MessageBank_GetStrbufFromNARC(NARC_INDEX_MSGDATA__PL_MSG, TEXT_BANK_NPC_TRAINER_MESSAGES, offset / 4, heapID, strbuf);
|
|
break;
|
|
}
|
|
|
|
offset += 4;
|
|
}
|
|
|
|
NARC_dtor(narc);
|
|
|
|
if (offset == size) {
|
|
Strbuf_Clear(strbuf);
|
|
}
|
|
}
|
|
|
|
void Trainer_Load(int trainerID, Trainer *trdata)
|
|
{
|
|
NARC_ReadWholeMemberByIndexPair(trdata, NARC_INDEX_POKETOOL__TRAINER__TRDATA, trainerID);
|
|
}
|
|
|
|
void Trainer_LoadParty(int trainerID, void *trparty)
|
|
{
|
|
NARC_ReadWholeMemberByIndexPair(trparty, NARC_INDEX_POKETOOL__TRAINER__TRPOKE, trainerID);
|
|
}
|
|
|
|
u8 TrainerClass_Gender(int trclass)
|
|
{
|
|
return sTrainerClassGender[trclass];
|
|
}
|
|
|
|
/**
|
|
* @brief Build the party for a trainer as loaded in the FieldBattleDTO struct.
|
|
*
|
|
* @param dto The parent FieldBattleDTO struct containing trainer data.
|
|
* @param battler Which battler's party is to be loaded.
|
|
* @param heapID Heap on which to perform any allocations.
|
|
*/
|
|
static void TrainerData_BuildParty(FieldBattleDTO *dto, int battler, int heapID)
|
|
{
|
|
// must make declarations C89-style to match
|
|
void *buf;
|
|
int i, j;
|
|
u32 genderMod, rnd, oldSeed;
|
|
u8 ivs;
|
|
Pokemon *mon;
|
|
|
|
oldSeed = LCRNG_GetSeed();
|
|
|
|
// alloc enough space to support the maximum possible data size
|
|
Party_InitWithCapacity(dto->parties[battler], MAX_PARTY_SIZE);
|
|
buf = Heap_Alloc(heapID, sizeof(TrainerMonWithMovesAndItem) * MAX_PARTY_SIZE);
|
|
mon = Pokemon_New(heapID);
|
|
|
|
Trainer_LoadParty(dto->trainerIDs[battler], buf);
|
|
|
|
// determine which magic gender-specific modifier to use for the RNG function
|
|
genderMod = TrainerClass_Gender(dto->trainer[battler].header.trainerType) == GENDER_FEMALE
|
|
? 120
|
|
: 136;
|
|
|
|
switch (dto->trainer[battler].header.monDataType) {
|
|
case TRDATATYPE_BASE: {
|
|
TrainerMonBase *trmon = (TrainerMonBase *)buf;
|
|
for (i = 0; i < dto->trainer[battler].header.partySize; i++) {
|
|
u16 species = trmon[i].species & 0x3FF;
|
|
u8 form = (trmon[i].species & 0xFC00) >> TRAINER_MON_FORM_SHIFT;
|
|
|
|
rnd = trmon[i].dv + trmon[i].level + species + dto->trainerIDs[battler];
|
|
LCRNG_SetSeed(rnd);
|
|
|
|
for (j = 0; j < dto->trainer[battler].header.trainerType; j++) {
|
|
rnd = LCRNG_Next();
|
|
}
|
|
|
|
rnd = (rnd << 8) + genderMod;
|
|
ivs = trmon[i].dv * MAX_IVS_SINGLE_STAT / MAX_DV;
|
|
|
|
Pokemon_InitWith(mon, species, trmon[i].level, ivs, TRUE, rnd, OTID_NOT_SHINY, 0);
|
|
Pokemon_SetBallSeal(trmon[i].cbSeal, mon, heapID);
|
|
Pokemon_SetValue(mon, MON_DATA_FORM, &form);
|
|
Party_AddPokemon(dto->parties[battler], mon);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TRDATATYPE_WITH_MOVES: {
|
|
TrainerMonWithMoves *trmon = (TrainerMonWithMoves *)buf;
|
|
for (i = 0; i < dto->trainer[battler].header.partySize; i++) {
|
|
u16 species = trmon[i].species & 0x3FF;
|
|
u8 form = (trmon[i].species & 0xFC00) >> TRAINER_MON_FORM_SHIFT;
|
|
|
|
rnd = trmon[i].dv + trmon[i].level + species + dto->trainerIDs[battler];
|
|
LCRNG_SetSeed(rnd);
|
|
|
|
for (j = 0; j < dto->trainer[battler].header.trainerType; j++) {
|
|
rnd = LCRNG_Next();
|
|
}
|
|
|
|
rnd = (rnd << 8) + genderMod;
|
|
ivs = trmon[i].dv * MAX_IVS_SINGLE_STAT / MAX_DV;
|
|
|
|
Pokemon_InitWith(mon, species, trmon[i].level, ivs, TRUE, rnd, OTID_NOT_SHINY, 0);
|
|
|
|
for (j = 0; j < 4; j++) {
|
|
Pokemon_SetMoveSlot(mon, trmon[i].moves[j], j);
|
|
}
|
|
|
|
Pokemon_SetBallSeal(trmon[i].cbSeal, mon, heapID);
|
|
Pokemon_SetValue(mon, MON_DATA_FORM, &form);
|
|
Party_AddPokemon(dto->parties[battler], mon);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TRDATATYPE_WITH_ITEM: {
|
|
TrainerMonWithItem *trmon = (TrainerMonWithItem *)buf;
|
|
for (i = 0; i < dto->trainer[battler].header.partySize; i++) {
|
|
u16 species = trmon[i].species & 0x3FF;
|
|
u8 form = (trmon[i].species & 0xFC00) >> TRAINER_MON_FORM_SHIFT;
|
|
|
|
rnd = trmon[i].dv + trmon[i].level + species + dto->trainerIDs[battler];
|
|
LCRNG_SetSeed(rnd);
|
|
|
|
for (j = 0; j < dto->trainer[battler].header.trainerType; j++) {
|
|
rnd = LCRNG_Next();
|
|
}
|
|
|
|
rnd = (rnd << 8) + genderMod;
|
|
ivs = trmon[i].dv * MAX_IVS_SINGLE_STAT / MAX_DV;
|
|
|
|
Pokemon_InitWith(mon, species, trmon[i].level, ivs, TRUE, rnd, OTID_NOT_SHINY, 0);
|
|
Pokemon_SetValue(mon, MON_DATA_HELD_ITEM, &trmon[i].item);
|
|
Pokemon_SetBallSeal(trmon[i].cbSeal, mon, heapID);
|
|
Pokemon_SetValue(mon, MON_DATA_FORM, &form);
|
|
Party_AddPokemon(dto->parties[battler], mon);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TRDATATYPE_WITH_MOVES_AND_ITEM: {
|
|
TrainerMonWithMovesAndItem *trmon = (TrainerMonWithMovesAndItem *)buf;
|
|
for (i = 0; i < dto->trainer[battler].header.partySize; i++) {
|
|
u16 species = trmon[i].species & 0x3FF;
|
|
u8 form = (trmon[i].species & 0xFC00) >> TRAINER_MON_FORM_SHIFT;
|
|
|
|
rnd = trmon[i].dv + trmon[i].level + species + dto->trainerIDs[battler];
|
|
LCRNG_SetSeed(rnd);
|
|
|
|
for (j = 0; j < dto->trainer[battler].header.trainerType; j++) {
|
|
rnd = LCRNG_Next();
|
|
}
|
|
|
|
rnd = (rnd << 8) + genderMod;
|
|
ivs = trmon[i].dv * MAX_IVS_SINGLE_STAT / MAX_DV;
|
|
|
|
Pokemon_InitWith(mon, species, trmon[i].level, ivs, TRUE, rnd, OTID_NOT_SHINY, 0);
|
|
Pokemon_SetValue(mon, MON_DATA_HELD_ITEM, &trmon[i].item);
|
|
|
|
for (j = 0; j < 4; j++) {
|
|
Pokemon_SetMoveSlot(mon, trmon[i].moves[j], j);
|
|
}
|
|
|
|
Pokemon_SetBallSeal(trmon[i].cbSeal, mon, heapID);
|
|
Pokemon_SetValue(mon, MON_DATA_FORM, &form);
|
|
Party_AddPokemon(dto->parties[battler], mon);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
Heap_Free(buf);
|
|
Heap_Free(mon);
|
|
LCRNG_SetSeed(oldSeed);
|
|
}
|