pmd-red/src/dungeon_mon_spawn.c
2025-11-22 00:11:18 -05:00

1059 lines
32 KiB
C

#include "global.h"
#include "globaldata.h"
#include "dungeon_mon_spawn.h"
#include "constants/ability.h"
#include "constants/iq_skill.h"
#include "constants/move_id.h"
#include "constants/status.h"
#include "constants/type.h"
#include "constants/weather.h"
#include "structs/dungeon_entity.h"
#include "structs/str_dungeon.h"
#include "dungeon_vram.h"
#include "code_805D8C8.h"
#include "dungeon_mon_sprite_render.h"
#include "dungeon_config.h"
#include "run_dungeon.h"
#include "dungeon_range.h"
#include "dungeon_map_access.h"
#include "dungeon_misc.h"
#include "dungeon_logic.h"
#include "dungeon_random.h"
#include "dungeon_message.h"
#include "dungeon_pokemon_sprites.h"
#include "dungeon_util.h"
#include "exclusive_pokemon.h"
#include "file_system.h"
#include "move_orb_effects_1.h"
#include "moves.h"
#include "number_util.h"
#include "pokemon.h"
#include "pokemon_3.h"
#include "position_util.h"
#include "weather.h"
#include "dungeon_floor_spawns.h"
#include "dungeon_pos_data.h"
#include "dungeon_tilemap.h"
#include "dungeon_engine.h"
#include "dungeon_leveling.h"
#include "dungeon_8041AD0.h"
#include "dungeon_cutscene.h"
#include "dungeon_action_execution.h"
static s32 CalcSpeciesHPAtLevel(s32 species, s32 level);
static s32 CalcSpeciesAtkAtLevel(s32 species, s32 level, s32 categoryIndex);
static s32 CalcSpeciesDefAtLevel(s32 species, s32 level, s32 categoryIndex);
static void InitEntityFromSpawnInfo(bool8 a0, Entity *entity, struct MonSpawnInfo *monSpawnInfo, DungeonPos *pos);
void sub_806AD3C(void)
{
s32 r10;
s32 i, j;
SpawnPokemonData sp[64];
unkDungeon2F3C *structPtr = gDungeon->unk2F3C;
s32 var_24 = SetMonsterSpawnsArray(sp, 0);
for (i = 0; i < var_24; structPtr++, i++) {
structPtr->species = ExtractSpeciesIndex(&sp[i]);
structPtr->level = ExtractLevel(&sp[i]);
sub_8072AC8(structPtr->moves, structPtr->species, structPtr->level);
if (structPtr->moves[0] == MOVE_NOTHING) {
structPtr->moves[0] = MOVE_BLOWBACK;
}
structPtr->unkC = CalcSpeciesHPAtLevel(structPtr->species, structPtr->level);
for (j = 0; j < 2; j++) {
structPtr->unkE[j] = CalcSpeciesAtkAtLevel(structPtr->species, structPtr->level, j);
structPtr->unk10[j] = CalcSpeciesDefAtLevel(structPtr->species, structPtr->level, j);
}
}
for (; i < 64; structPtr++, i++) {
structPtr->species = 0;
}
for (i = 0; i < MONSTER_MAX; i++) {
gDungeon->expYieldRankings[i] = 0;
}
r10 = 1;
for (i = 0; i < var_24; i++) {
s32 j;
s32 expSpecies = -1;
s32 expGain = -1;
for (j = 0; j < var_24; j++) {
s32 species = ExtractSpeciesIndex(&sp[j]);
if (gDungeon->expYieldRankings[species] == 0) {
s32 level = ExtractLevel(&sp[j]);
s32 exp = CalculateEXPGain(species, level);
if (expGain < exp) {
expGain = exp;
expSpecies = species;
}
}
}
if (expSpecies < 0)
break;
gDungeon->expYieldRankings[expSpecies] = r10;
r10 += 2;
}
for (i = 0; i < MONSTER_MAX; i++) {
if (gDungeon->expYieldRankings[i] == 0) {
gDungeon->expYieldRankings[i] = 1;
}
}
}
static void sub_806AED8(Moves *moves, s16 *maxHPStat, u8 *atk, u8 *def, s16 _species, s32 level)
{
s32 i;
s32 moveCategory;
s32 species = _species;
unkDungeon2F3C *structPtr = gDungeon->unk2F3C;
for (i = 0; i < 64; i++) {
s16 loopSpecies;
structPtr = &gDungeon->unk2F3C[i];
loopSpecies = SpeciesId(structPtr->species);
if (structPtr->species == 0)
break;
if (loopSpecies == species && structPtr->level == level) {
s32 moveIndex;
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) {
InitPokemonMoveOrNullObject(&moves->moves[moveIndex], structPtr->moves[moveIndex]);
}
*maxHPStat = structPtr->unkC;
for (moveCategory = 0; moveCategory < 2; moveCategory++) {
atk[moveCategory] = structPtr->unkE[moveCategory];
def[moveCategory] = structPtr->unk10[moveCategory];
}
moves->struggleMoveFlags = 0;
return;
}
}
if (i == 64) {
s32 moveIndex;
u16 spMoves[MAX_MON_MOVES];
sub_8072AC8(spMoves, species, level);
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) {
InitPokemonMoveOrNullObject(&moves->moves[moveIndex], spMoves[moveIndex]);
}
*maxHPStat = CalcSpeciesHPAtLevel(species, level);
for (moveCategory = 0; moveCategory < 2; moveCategory++) {
atk[moveCategory] = CalcSpeciesAtkAtLevel(species, level, moveCategory);
def[moveCategory] = CalcSpeciesDefAtLevel(species, level, moveCategory);
}
}
else {
s32 moveIndex;
structPtr->species = species;
structPtr->level = level;
sub_8072AC8(structPtr->moves, species, level);
structPtr->unkC = CalcSpeciesHPAtLevel(structPtr->species, structPtr->level);
for (moveCategory = 0; moveCategory < 2; moveCategory++) {
structPtr->unkE[moveCategory] = CalcSpeciesAtkAtLevel(structPtr->species, structPtr->level, moveCategory);
structPtr->unk10[moveCategory] = CalcSpeciesDefAtLevel(structPtr->species, structPtr->level, moveCategory);
}
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) {
InitPokemonMoveOrNullObject(&moves->moves[moveIndex], structPtr->moves[moveIndex]);
}
*maxHPStat = structPtr->unkC;
for (moveCategory = 0; moveCategory < 2; moveCategory++) {
atk[moveCategory] = structPtr->unkE[moveCategory];
def[moveCategory] = structPtr->unk10[moveCategory];
}
}
moves->struggleMoveFlags = 0;
}
UNUSED static s32 sub_806B09C(SpawnPokemonData *unkPtr, bool8 a1)
{
s32 i, j;
s32 count = 0;
s16 *unk2Field;
SpawnPokemonData *loopPtr;
for (i = 0, unk2Field = unkPtr->randNum, loopPtr = unkPtr; i < MAX_TEAM_MEMBERS; i++) {
DungeonMon *monStructPtr = &gRecruitedPokemonRef->dungeonTeam[i];
if (DungeonMonExists(monStructPtr) && PokemonFlag2Struct2(monStructPtr))
{
for (j = 0; j < count; j++) {
if (ExtractSpeciesIndex(&unkPtr[j]) == monStructPtr->speciesNum)
break;
}
if (j == count) {
SetSpeciesLevelToExtract(loopPtr, 0, monStructPtr->speciesNum);
unk2Field[0] = 0;
unk2Field[1] = 0;
unk2Field += sizeof(*unkPtr) / sizeof(unkPtr->randNum[0]); // Hacky solution, because loopPtr->unk2[0/1] = 0; doesn't match
loopPtr++;
count++;
}
}
}
count = SetMonsterSpawnsArray(unkPtr, count);
if (a1) {
SetSpeciesLevelToExtract(&unkPtr[count], 1, MONSTER_DECOY);
unkPtr[count].randNum[0] = 0;
unkPtr[count].randNum[1] = 0;
count++;
}
return count;
}
void sub_806B168(void)
{
s32 count;
DungeonMon *monPtrs[MAX_TEAM_MEMBERS];
DungeonMon *monPtrs2[MAX_TEAM_MEMBERS]; // Leader and partner?
u8 playerSpawnRoomId = GetTile(gDungeon->playerSpawn.x, gDungeon->playerSpawn.y)->room;
s32 i;
for (i = 0; i < MAX_TEAM_MEMBERS; i++) {
DungeonMon *currMonPtr = &gRecruitedPokemonRef->dungeonTeam[i];
currMonPtr->unkC = i;
}
// Populate monPtrs
count = 0;
for (i = 0; i < MAX_TEAM_MEMBERS; i++) {
DungeonMon *currMonPtr = &gRecruitedPokemonRef->dungeonTeam[i];
if (DungeonMonExists(currMonPtr) && PokemonFlag2Struct2(currMonPtr)) {
monPtrs[count++] = currMonPtr;
}
}
for (; count < MAX_TEAM_MEMBERS; count++) {
monPtrs[count] = NULL;
}
// Populate monPtrs2
count = 0;
for (i = 0; i < MAX_TEAM_MEMBERS; i++) {
DungeonMon *currMonPtr = monPtrs[i];
if (currMonPtr != NULL && currMonPtr->isTeamLeader) {
monPtrs[i] = NULL;
monPtrs2[count++] = currMonPtr;
}
}
if (!gDungeon->unk644.canChangeLeader) {
for (i = 0; i < MAX_TEAM_MEMBERS; i++) {
DungeonMon *currMonPtr = monPtrs[i];
if (currMonPtr != NULL && currMonPtr->dungeonLocation.id == DUNGEON_JOIN_LOCATION_PARTNER) {
monPtrs[i] = NULL;
monPtrs2[count++] = currMonPtr;
}
}
}
for (i = 0; i < MAX_TEAM_MEMBERS; i++) {
DungeonMon *currMonPtr = monPtrs[i];
if (monPtrs[i] != NULL) {
monPtrs[i] = NULL;
monPtrs2[count++] = currMonPtr;
}
}
for (; count < MAX_TEAM_MEMBERS; count++) {
monPtrs2[count] = NULL;
}
// Do something else
for (i = 0; i < MAX_TEAM_MEMBERS; i++) {
DungeonMon *currMonPtr = monPtrs2[i];
if (currMonPtr != NULL) {
DungeonPos unkPosition;
s32 j;
const Tile *tile;
bool8 skipSecondLoop;
if (currMonPtr->speciesNum == MONSTER_CASTFORM_SNOWY || currMonPtr->speciesNum == MONSTER_CASTFORM_RAINY || currMonPtr->speciesNum == MONSTER_CASTFORM_SUNNY) {
currMonPtr->speciesNum = MONSTER_CASTFORM;
}
skipSecondLoop = FALSE;
j = 0;
while (1) {
unkPosition = gUnknown_80F4598[j];
if (unkPosition.x == 99)
break;
tile = GetTile(unkPosition.x + gDungeon->playerSpawn.x, unkPosition.y + gDungeon->playerSpawn.y);
if (tile->room == playerSpawnRoomId && !sub_807034C(currMonPtr->speciesNum, tile)) {
SpawnTeamMember(currMonPtr->speciesNum, unkPosition.x + gDungeon->playerSpawn.x, unkPosition.y + gDungeon->playerSpawn.y, currMonPtr, NULL, TRUE, 0);
skipSecondLoop = TRUE;
break;
}
j++;
}
if (skipSecondLoop)
continue;
j = 0;
while (1) {
unkPosition = gUnknown_80F4598[j];
if (unkPosition.x == 99)
break;
tile = GetTile(unkPosition.x + gDungeon->playerSpawn.x, unkPosition.y + gDungeon->playerSpawn.y);
if (!sub_807034C(currMonPtr->speciesNum, tile)) {
SpawnTeamMember(currMonPtr->speciesNum, unkPosition.x + gDungeon->playerSpawn.x, unkPosition.y + gDungeon->playerSpawn.y, currMonPtr, NULL, TRUE, 0);
break;
}
j++;
}
}
}
sub_806B678();
}
void sub_806B404(void)
{
Entity *leader;
s32 i;
DungeonMon *monPtrs[MAX_TEAM_MEMBERS];
u8 roomId;
DungeonPos pos;
s32 count = 0;
for (i = 0; i < MAX_TEAM_MEMBERS; i++) {
DungeonMon *currMonPtr = &gRecruitedPokemonRef->dungeonTeam[i];
if (DungeonMonExists(currMonPtr) && PokemonFlag2Struct2(currMonPtr) && currMonPtr->recruitedPokemonId == UNK_RECRUITED_POKEMON_ID_55AA) {
monPtrs[count++] = currMonPtr;
break;
}
}
for (; count < MAX_TEAM_MEMBERS; count++) {
monPtrs[count] = NULL;
}
gLeaderPointer = NULL;
leader = GetLeader();
if (EntityIsValid(leader)) {
pos.x = leader->pos.x;
pos.y = leader->pos.y;
}
else {
pos.x = gDungeon->playerSpawn.x;
pos.y = gDungeon->playerSpawn.y;
}
roomId = GetTile(pos.x, pos.y)->room;
for (i = 0; i < MAX_TEAM_MEMBERS; i++) {
DungeonPos unkPosition;
bool8 skipNextLoop;
const Tile *tile;
s32 j;
DungeonMon *currMonPtr = monPtrs[i];
if (currMonPtr != NULL && DungeonMonExists(currMonPtr) && PokemonFlag2Struct2(currMonPtr) && currMonPtr->recruitedPokemonId == UNK_RECRUITED_POKEMON_ID_55AA) {
currMonPtr->recruitedPokemonId = UNK_RECRUITED_POKEMON_ID_5AA5;
skipNextLoop = FALSE;
j = 0;
while (1) {
unkPosition = gUnknown_80F4598[j];
if (unkPosition.x == 99)
break;
tile = GetTile(unkPosition.x + pos.x, unkPosition.y + pos.y);
if (tile->room == roomId && !sub_807034C(currMonPtr->speciesNum, tile)) {
SpawnTeamMember(currMonPtr->speciesNum, unkPosition.x + pos.x, unkPosition.y + pos.y, currMonPtr, NULL, TRUE, 0);
skipNextLoop = TRUE;
break;
}
j++;
}
if (skipNextLoop)
continue;
skipNextLoop = FALSE;
j = 0;
while (1) {
unkPosition = gUnknown_80F4598[j];
if (unkPosition.x == 99)
break;
tile = GetTile(unkPosition.x + pos.x, unkPosition.y + pos.y);
if (!sub_807034C(currMonPtr->speciesNum, tile)) {
SpawnTeamMember(currMonPtr->speciesNum, unkPosition.x + pos.x, unkPosition.y + pos.y, currMonPtr, NULL, TRUE, 0);
skipNextLoop = TRUE;
break;
}
j++;
}
if (skipNextLoop)
continue;
for (j = 0; j <= 99; j++) {
if (sub_8083660(&unkPosition)) {
const Tile *tile = GetTile(unkPosition.x, unkPosition.y);
if (!sub_807034C(currMonPtr->speciesNum, tile)) {
SpawnTeamMember(currMonPtr->speciesNum, unkPosition.x, unkPosition.y, currMonPtr, NULL, TRUE, 0);
break;
}
}
}
}
}
}
void sub_806B678(void)
{
s32 i;
for (i = 0; i < MAX_TEAM_MEMBERS; i++) {
Entity *mon = gDungeon->teamPokemon[i];
if (EntityIsValid(mon)) {
EntityInfo *monInfo = GetEntInfo(mon);
if (monInfo->isTeamLeader) {
sub_803F4A0(mon);
gLeaderPosition = mon->pos;
break;
}
}
}
}
void SpawnWildMonsOnFloor(void)
{
struct MonSpawnInfo monSpawnInfo;
s32 i, j;
s32 x, y;
bool8 r8 = (gDungeon->unk644.unk44 != 0);
if (sub_80860A8(ITEM_MUSIC_BOX) && !HasRecruitedMon(MONSTER_MEW)) {
gDungeon->unk37FF = FALSE;
}
else {
gDungeon->unk37FF = TRUE;
}
x = DungeonRandInt(DUNGEON_MAX_SIZE_X);
y = DungeonRandInt(DUNGEON_MAX_SIZE_Y);
for (j = 0; j < DUNGEON_MAX_SIZE_Y; j++) {
y++;
if (y == DUNGEON_MAX_SIZE_Y) {y = 0;}
for (i = 0; i < DUNGEON_MAX_SIZE_X; i++) {
x++;
if (x == DUNGEON_MAX_SIZE_X) {x = 0;}
if (GetTile(x, y)->spawnOrVisibilityFlags.spawn & SPAWN_FLAG_MONSTER) {
bool8 r6 = FALSE;
if (r8) {
monSpawnInfo.species = gDungeon->unk644.unk44;
monSpawnInfo.level = 1;
monSpawnInfo.unk2 = 1;
r6 = TRUE;
}
else {
monSpawnInfo.species = GetRandomFloorMonsterId(0);
monSpawnInfo.level = 0;
monSpawnInfo.unk2 = 0;
}
monSpawnInfo.unk4 = 0;
monSpawnInfo.unk10 = 0;
monSpawnInfo.pos.x = x;
monSpawnInfo.pos.y = y;
if (r6 || sub_806AA0C(monSpawnInfo.species, TRUE)) {
if (SpawnWildMon(&monSpawnInfo, FALSE)) {
r8 = FALSE;
}
}
}
}
}
if (r8) {
gDungeon->unkA = 1;
}
}
Entity* SpawnWildMon(struct MonSpawnInfo *monSpawnInfo, bool8 a1)
{
Entity *entity;
EntityInfo *entityInfo;
const Tile *tile = GetTile(monSpawnInfo->pos.x, monSpawnInfo->pos.y);
if (sub_807034C(monSpawnInfo->species, tile))
return FALSE;
entity = sub_804550C(monSpawnInfo->species);
if (entity == NULL)
return FALSE;
InitEntityFromSpawnInfo(FALSE, entity, monSpawnInfo, &gLeaderPosition);
entityInfo = GetEntInfo(entity);
entityInfo->isNotTeamMember = TRUE;
sub_806AED8(&entityInfo->moves, &entityInfo->maxHPStat, entityInfo->atk, entityInfo->def, entityInfo->id, entityInfo->level);
entityInfo->HP = entityInfo->maxHPStat;
entityInfo->moveRandomly = monSpawnInfo->unk4;
if (!monSpawnInfo->unk2 && !a1 && !monSpawnInfo->unk10) {
s32 rand = DungeonRandInt(100);
if (GetChanceAsleep(monSpawnInfo->species) > rand) {
sub_8075BF4(entity, 0x7F);
sub_806CE68(entity, NUM_DIRECTIONS);
}
}
return entity;
}
bool8 SpawnTeamMember(s16 _species, s32 x, s32 y, DungeonMon *monPtr, Entity **a4, bool32 _a5, u32 _a6)
{
s32 i;
DungeonPos unkPosition;
struct MonSpawnInfo monSpawnInfo;
// s16 memes
s32 species = SpeciesId(_species);
bool8 a5 = _a5;
u8 a6 = _a6;
Entity *entity;
EntityInfo *entityInfo;
bool8 isTeamLeader = (monPtr->isTeamLeader != FALSE);
if (a4 != NULL) {
*a4 = NULL;
}
if (GetBaseSpecies(species) == MONSTER_DEOXYS_NORMAL) {
if (a5) {
species = gDungeon->deoxysForm;
}
else {
species = MONSTER_DEOXYS_NORMAL;
}
}
if (sub_807034C(species, GetTile(x, y)))
return FALSE;
entity = sub_80453AC(species);
if (entity == NULL)
return FALSE;
monSpawnInfo.pos.x = x;
monSpawnInfo.pos.y = y;
monSpawnInfo.species = species;
monSpawnInfo.level = monPtr->level;
monSpawnInfo.unk2 = 0;
monSpawnInfo.unk4 = 0;
monSpawnInfo.unk10 = 0;
unkPosition.x = gAdjacentTileOffsets[gUnknown_202F32C].x + x;
unkPosition.y = gAdjacentTileOffsets[gUnknown_202F32C].y + y;
InitEntityFromSpawnInfo(TRUE, entity, &monSpawnInfo, (isTeamLeader ? &unkPosition : &gLeaderPosition));
entityInfo = GetEntInfo(entity);
entityInfo->isNotTeamMember = FALSE;
entityInfo->shopkeeper = 0;
entityInfo->isTeamLeader = isTeamLeader;
entityInfo->unkF9 = a6;
gLeaderPointer = NULL;
if (isTeamLeader) {
gLeaderPosition.x = x;
gLeaderPosition.y = y;
}
entityInfo->HP = monPtr->unk10;
entityInfo->maxHPStat = monPtr->unk12;
entityInfo->exp = monPtr->currExp;
for (i = 0; i < 2; i++) {
entityInfo->atk[i] = monPtr->offense.att[i];
entityInfo->def[i] = monPtr->offense.def[i];
}
entityInfo->moves = monPtr->moves;
for (i = 0; i < MAX_MON_MOVES; i++) {
Move *move = &entityInfo->moves.moves[i];
if (MoveFlagExists(move)) {
move->moveFlags &= ~(MOVE_FLAG_LAST_USED);
move->moveFlags &= ~(MOVE_FLAG_REPLACE);
}
}
entityInfo->moves.struggleMoveFlags &= ~(MOVE_FLAG_LAST_USED);
entityInfo->moves.struggleMoveFlags &= ~(MOVE_FLAG_REPLACE);
entityInfo->level = monPtr->level;
entityInfo->IQ = monPtr->IQ;
entityInfo->tactic = monPtr->tacticIndex;
entityInfo->IQSkillMenuFlags = monPtr->IQSkills;
entityInfo->hiddenPower = monPtr->hiddenPower;
entityInfo->joinedAt = monPtr->dungeonLocation;
entityInfo->belly = monPtr->belly;
entityInfo->maxBelly = monPtr->maxBelly;
entityInfo->teamIndex = monPtr->unkC;
entityInfo->heldItem = monPtr->itemSlot;
entityInfo->unkF3 = 0;
entityInfo->unk64 = 0;
// Pickup Check
if (gDungeon->unk644.dungeonLocation.id != DUNGEON_TINY_WOODS
&& !IsFloorwideFixedRoom()
&& (entityInfo->abilities[0] == ABILITY_PICKUP || entityInfo->abilities[1] == ABILITY_PICKUP)
&& !ItemExists(&entityInfo->heldItem))
{
u32 pickUpItem = GetRandomFloorItem(0);
if (pickUpItem != ITEM_POKE) {
ItemIdToItem(&entityInfo->heldItem, pickUpItem, 0);
entityInfo->unkF3 = TRUE;
}
}
sub_806A6E8(entity);
LoadIQSkills(entity);
if (a4) {
*a4 = entity;
}
return TRUE;
}
void UpdateEntitySpecies(Entity *entity, s32 _species)
{
s32 species = (s16) _species;
s16 speciesMatch;
struct MonSpawnInfo monSpawnInfo;
EntityInfo *entInfo = GetEntInfo(entity);
DeletePokemonDungeonSprite(entInfo->dungeonSpriteId);
// s16 memes...
monSpawnInfo.species = 0;
speciesMatch = species;
ASM_MATCH_TRICK(species);
monSpawnInfo.species = speciesMatch;
monSpawnInfo.level = 0;
monSpawnInfo.unk2 = 0;
monSpawnInfo.pos = entity->pos;
monSpawnInfo.unk4 = 0;
{
s16 apparentSpeciesMatch;
s32 apparentSpecies = (s16) (GetMonsterApparentID(NULL, species));
UNUSED EntityInfo *entInfo;
entity->unk22 = 0;
entInfo = GetEntInfo(entity);
apparentSpeciesMatch = apparentSpecies;
GetEntInfo(entity)->apparentID = (s16) (apparentSpeciesMatch);
GetEntInfo(entity)->id = speciesMatch;
entity->axObj.spriteFile = GetSpriteData(apparentSpecies);
}
entity->axObj.unk42_animId1 = 7;
entity->axObj.unk44_direction1 = 0;
entity->axObj.unk43_animId2 = 0xFF;
entity->axObj.unk45_orientation = 1;
entity->axObj.unk47 = 1;
entity->unk1C = IntToF248(0);
if (entInfo->frozenClassStatus.status == STATUS_WRAP || entInfo->frozenClassStatus.status == STATUS_WRAPPED) {
sub_8076CB4(entInfo->unk9C);
}
InitEntityFromSpawnInfo((entInfo->isNotTeamMember == FALSE), entity, &monSpawnInfo, NULL);
sub_806AED8(&entInfo->moves, &entInfo->maxHPStat, entInfo->atk, entInfo->def, entInfo->id, entInfo->level);
entInfo->HP = entInfo->maxHPStat;
entInfo->shopkeeper = 0;
sub_80429E8(entity);
UpdateStatusIconFlags(entity);
}
static void InitEntityFromSpawnInfo(bool8 a0, Entity *entity, struct MonSpawnInfo *monSpawnInfo, DungeonPos *pos)
{
DungeonPos entityPos;
EntityInfo *entInfo;
gDungeon->unkC = 1;
entInfo = GetEntInfo(entity);
ResetMonEntityData(entInfo, TRUE);
entInfo->monsterBehavior = monSpawnInfo->unk2;
entity->isVisible = TRUE;
entity->unk22 = 0;
entity->prevPos.x = -1;
entity->prevPos.y = -1;
entity->pos.x = -2;
entity->pos.y = -2;
sub_80694C0(entity, monSpawnInfo->pos.x, monSpawnInfo->pos.y, 1);
UpdateEntityPixelPos(entity, NULL);
if (!a0) {
if (monSpawnInfo->species == MONSTER_KECLEON
&& !gDungeon->unk644.stoleFromKecleon
&& gDungeon->unk3A0A
&& !monSpawnInfo->unk2)
{
entInfo->shopkeeper = 1;
}
else {
entInfo->shopkeeper = 0;
}
if (GetBaseSpecies(entInfo->id) == MONSTER_DEOXYS_NORMAL) {
gDungeon->unk37FD = TRUE;
}
if (GetBaseSpecies(entInfo->id) == MONSTER_MEW) {
gDungeon->unk37FF = TRUE;
}
}
entInfo->dungeonSpriteId = gDungeon->unk37F0;
gDungeon->unk37F0++;
entInfo->unk9C = 0;
if (monSpawnInfo->level == 0) {
entInfo->level = GetSpawnedMonsterLevel(monSpawnInfo->species);
}
else {
entInfo->level = monSpawnInfo->level;
}
entInfo->moveRandomly = monSpawnInfo->unk4;
entInfo->IQ = 1;
SetDefaultIQSkills(&entInfo->IQSkillMenuFlags, FALSE);
GenerateHiddenPower(&entInfo->hiddenPower);
entInfo->maxHPStat = 1;
entInfo->HP = 1;
entInfo->belly = IntToFixedPoint(100);
entInfo->maxBelly = IntToFixedPoint(100);
if (pos != NULL) {
entInfo->targetPos = *pos;
}
else {
entInfo->targetPos.x = 0;
entInfo->targetPos.y = 0;
}
entInfo->flags = 0;
entInfo->aiAllySkip = FALSE;
entInfo->recalculateFollow = FALSE;
entInfo->numMoveTiles = 0;
entInfo->notMoving = 0;
entInfo->aiTarget.aiObjective = 0;
entInfo->aiTarget.aiTargetPos = entity->pos;
entInfo->aiTarget.aiTarget = NULL;
entInfo->aiTarget.unkC = 0;
entInfo->aiTarget.aiTargetSpawnGenID = 0;
entInfo->unkFF = 0;
entInfo->unk174 = 0;
entInfo->decoyAITracker = 0;
ResetMonEntityData(entInfo, TRUE);
ZeroOutItem(&entInfo->heldItem);
entInfo->unk64 = 0;
entInfo->statusIcons = 0;
entInfo->unk164 = 0xFF;
entInfo->unk165 = 0xFF;
entInfo->expGainedInTurn = 0;
entInfo->waiting = 0;
entInfo->bellyEmpty = FALSE;
entInfo->unk166 = 0;
entInfo->unk1F8 = 0;
entInfo->mobileTurnTimer = 0;
entInfo->attacking = FALSE;
entInfo->unk149 = FALSE;
entInfo->abilityEffectFlags = FALSE;
entInfo->terrifiedTurns = 0;
entInfo->useHeldItem = 0;
entInfo->unk14B = 0;
entInfo->unk14C = 1;
entInfo->visualFlags = 0;
entInfo->previousVisualFlags = 0;
entInfo->unk152 = 0;
entInfo->usedLinkedMovesCounter = 0;
entInfo->unk154 = 0;
entInfo->unk155 = 0;
entInfo->turnsSinceWarpScarfActivation = 0;
entInfo->perishSongTurns = 0;
entInfo->unkFE = 99;
entInfo->unk158 = 0;
entInfo->unk159 = 0;
entInfo->unk15A = 0;
CalcSpeedStage(entity);
entInfo->unk156 = 1;
entInfo->unk15C = 0;
entInfo->unk15E = 0;
entInfo->unk15D = 0;
entInfo->unk15F = 0;
entInfo->unk160 = 0;
if (entInfo->apparentID == MONSTER_DIGLETT || entInfo->apparentID == MONSTER_DUGTRIO) {
entInfo->unk156 = 0;
}
entityPos.x = entity->pos.x;
entityPos.y = entity->pos.y;
AddPokemonDungeonSprite(entInfo->dungeonSpriteId, entInfo->apparentID, &entityPos, gDungeon->unk181e8.priority);
LoadIQSkills(entity);
sub_806A898(entity, FALSE, FALSE);
}
void ResetMonEntityData(EntityInfo *entInfo, bool8 setStatsToOne)
{
s32 i;
bool8 hasForecast;
entInfo->sleepClassStatus.status = 0;
entInfo->sleepClassStatus.turns = 0;
entInfo->burnClassStatus.status = 0;
entInfo->burnClassStatus.turns = 0;
entInfo->burnClassStatus.damageCountdown = 0;
entInfo->burnClassStatus.badlyPoisonedDamageCount = 0;
entInfo->frozenClassStatus.status = 0;
entInfo->frozenClassStatus.turns = 0;
entInfo->frozenClassStatus.damageCountdown = 0;
entInfo->frozenClassStatus.unk4 = 34;
entInfo->cringeClassStatus.status = 0;
entInfo->cringeClassStatus.turns = 0;
entInfo->bideClassStatus.status = 0;
entInfo->reflectClassStatus.status = 0;
entInfo->reflectClassStatus.turns = 0;
entInfo->curseClassStatus.status = 0;
entInfo->curseClassStatus.turns = 0;
entInfo->curseClassStatus.damageCountdown = 0;
entInfo->leechSeedClassStatus.status = 0;
entInfo->leechSeedClassStatus.turns = 0;
entInfo->leechSeedClassStatus.damageCountdown = 0;
entInfo->sureShotClassStatus.status = 0;
entInfo->sureShotClassStatus.turns = 0;
entInfo->longTossClassStatus.status = 0;
entInfo->invisibleClassStatus.status = 0;
entInfo->invisibleClassStatus.turns = 0;
entInfo->blinkerClassStatus.status = 0;
entInfo->blinkerClassStatus.turns = 0;
entInfo->muzzled.muzzled = FALSE;
entInfo->muzzled.turns = 0;
entInfo->powerEars = FALSE;
entInfo->scanning = FALSE;
entInfo->stairSpotter = FALSE;
entInfo->stairSpotter = FALSE;
entInfo->unk164 = 0xFF;
entInfo->unk165 = 0xFF;
for (i = 0; i < NUM_SPEED_COUNTERS; i++) {
entInfo->speedUpCounters[i] = 0;
entInfo->speedDownCounters[i] = 0;
}
hasForecast = FALSE;
for (i = 0; i < 2; i++) {
entInfo->abilities[i] = GetPokemonAbility(entInfo->id, i);
if (entInfo->abilities[i] == ABILITY_FORECAST) {
hasForecast = TRUE;
}
if (setStatsToOne) {
entInfo->atk[i] = 1;
entInfo->def[i] = 1;
}
entInfo->offensiveStages[i] = 10;
entInfo->defensiveStages[i] = 10;
entInfo->hitChanceStages[i] = 10;
entInfo->offensiveMultipliers[i] = IntToF248(1);
entInfo->defensiveMultipliers[i] = IntToF248(1);
}
if (hasForecast) {
entInfo->types[0] = gCastformByWeather[GetApparentWeather(NULL)].type;
entInfo->types[1] = TYPE_NONE;
}
else {
for (i = 0; i < 2; i++) {
entInfo->types[i] = GetPokemonType(entInfo->id, i);
}
}
gDungeon->unkC = 1;
entInfo->flashFireBoost = 0;
entInfo->stockpileStage = 0;
entInfo->perishSongTurns = 0;
entInfo->speedBoostFrames = 0;
entInfo->grudge = 0;
entInfo->expMultiplier = EXP_HALVED;
entInfo->exposed = FALSE;
entInfo->isColorChanged = 0;
entInfo->bossFlag = 0;
entInfo->unkFF = 0;
}
void sub_806C1D8(void)
{
s32 i, j;
for (i = 0; i < MAX_TEAM_MEMBERS; i++) {
Entity *entity = gDungeon->teamPokemon[i];
if (EntityIsValid(entity)) {
EntityInfo *entInfo = GetEntInfo(entity);
s32 teamIndex = entInfo->teamIndex;
if (teamIndex >= 0) {
for (j = 0; j < MAX_MON_MOVES; j++) {
if (MoveFlagExists(&entInfo->moves.moves[j])) {
entInfo->moves.moves[j].moveFlags2 &= ~(1);
}
}
sub_806C264(teamIndex, entInfo);
}
}
}
}
void sub_806C264(s32 teamIndex, EntityInfo *entInfo)
{
s32 i;
DungeonMon *monPtr = &gRecruitedPokemonRef->dungeonTeam[teamIndex];
monPtr->unk10 = entInfo->HP;
monPtr->unk12 = entInfo->maxHPStat;
monPtr->currExp = entInfo->exp;
for (i = 0; i < 2; i++) {
monPtr->offense.att[i] = entInfo->atk[i];
monPtr->offense.def[i] = entInfo->def[i];
}
if (IS_CASTFORM_FORM_MONSTER(entInfo->apparentID)) {
monPtr->speciesNum = MONSTER_CASTFORM;
}
monPtr->moves = entInfo->moves;
monPtr->level = entInfo->level;
monPtr->IQ = entInfo->IQ;
monPtr->IQSkills = entInfo->IQSkillMenuFlags;
monPtr->tacticIndex = entInfo->tactic;
monPtr->hiddenPower = entInfo->hiddenPower;
monPtr->belly = entInfo->belly;
monPtr->maxBelly = entInfo->maxBelly;
monPtr->itemSlot = entInfo->heldItem;
if (monPtr->recruitedPokemonId == UNK_RECRUITED_POKEMON_ID_55AA) {
monPtr->recruitedPokemonId = UNK_RECRUITED_POKEMON_ID_5AA5;
}
}
// s16 species memes again
void sub_806C330(s32 _x, s32 _y, s16 _species, u32 _a3)
{
s32 i;
s32 x = _x;
s32 y = _y;
s32 species = (_species);
u8 a3 = _a3;
unkDungeon57C *strPtr = &gDungeon->unk57C;
for (i = 0; i < strPtr->unk40; i++) {
if (strPtr->unkArray[i].unk3 && strPtr->unkArray[i].unk4 == x && strPtr->unkArray[i].unk5 == y) {
s16 speciesMatchMe = SpeciesId(_species);
strPtr->unkArray[i].unk0 = speciesMatchMe;
strPtr->unkArray[i].unk2 = a3;
return;
}
}
if (strPtr->unk40 < UNK_DUNGEON57C_ARRAY_COUNT) {
strPtr->unkArray[strPtr->unk40].unk3 = TRUE;
strPtr->unkArray[strPtr->unk40].unk4 = x;
strPtr->unkArray[strPtr->unk40].unk5 = y;
strPtr->unkArray[strPtr->unk40].unk0 = species;
strPtr->unkArray[strPtr->unk40].unk2 = a3;
strPtr->unk40++;
}
}
void sub_806C3C0(void)
{
s32 i;
struct MonSpawnInfo spStruct;
unkDungeon57C *strPtr = &gDungeon->unk57C;
for (i = 0; i < strPtr->unk40; i++) {
if (strPtr->unkArray[i].unk3) {
spStruct.species = strPtr->unkArray[i].unk0;
spStruct.level = 0;
spStruct.pos.x = strPtr->unkArray[i].unk4;
spStruct.pos.y = strPtr->unkArray[i].unk5;
spStruct.unk2 = strPtr->unkArray[i].unk2;
spStruct.unk4 = 0;
spStruct.unk10 = 0;
SpawnWildMon(&spStruct, TRUE);
}
}
}
void sub_806C42C(void)
{
unkDungeon57C *strPtr = &gDungeon->unk57C;
strPtr->unk40 = 0;
}
static s32 CalcSpeciesHPAtLevel(s32 _species, s32 level)
{
LevelData levelData;
s32 i;
s32 species = SpeciesId(_species);
s32 hpCount = GetBaseHP(species);
for (i = 2; i <= level; i++) {
GetLvlUpEntry(&levelData, species, i);
hpCount += levelData.gainHP;
}
return hpCount;
}
static s32 CalcSpeciesAtkAtLevel(s32 _species, s32 level, s32 categoryIndex)
{
LevelData levelData;
s32 i;
s32 species = SpeciesId(_species);
s32 offensiveCount = GetBaseOffensiveStat(species, categoryIndex);
for (i = 2; i <= level; i++) {
GetLvlUpEntry(&levelData, species, i);
offensiveCount += levelData.gainAtt[categoryIndex];
}
return offensiveCount;
}
static s32 CalcSpeciesDefAtLevel(s32 _species, s32 level, s32 categoryIndex)
{
LevelData levelData;
s32 i;
s32 species = SpeciesId(_species);
s32 defensiveCount = GetBaseDefensiveStat(species, categoryIndex);
for (i = 2; i <= level; i++) {
GetLvlUpEntry(&levelData, species, i);
defensiveCount += levelData.gainDef[categoryIndex];
}
return defensiveCount;
}