pokeemerald/src/battle_pike.c

857 lines
20 KiB
C
Raw Normal View History

2018-11-12 00:46:02 +01:00
#include "global.h"
#include "event_data.h"
#include "frontier_util.h"
#include "fieldmap.h"
#include "save.h"
#include "battle.h"
#include "random.h"
#include "task.h"
#include "battle_tower.h"
#include "party_menu.h"
#include "malloc.h"
#include "palette.h"
#include "script.h"
#include "constants/event_objects.h"
#include "constants/battle_frontier.h"
#include "constants/abilities.h"
#include "constants/rgb.h"
#define PIKE_ROOM_SINGLE_BATTLE 0
#define PIKE_ROOM_HEAL_FULL 1
#define PIKE_ROOM_NPC 2
#define PIKE_ROOM_STATUS 3
#define PIKE_ROOM_HEAL_PART 4
#define PIKE_ROOM_WILD_MONS 5
#define PIKE_ROOM_HARD_BATTLE 6
#define PIKE_ROOM_DOUBLE_BATTLE 7
#define PIKE_ROOM_BRAIN 8
// For the room with a status effect.
#define PIKE_STATUS_KIRLIA 0
#define PIKE_STATUS_DUSCLOPS 1
struct Unk0861231C
{
u16 unk0;
u8 unk2;
u8 unk3;
u8 unk4;
u8 unk5;
};
struct PikeWildMon
{
u16 species;
u8 unk2;
u16 moves[4];
};
extern void (* const gUnknown_086125F8[])(void);
extern const struct Unk0861231C gUnknown_0861231C[];
extern const struct PikeWildMon *const *const gUnknown_08612314[2];
extern const u16 gUnknown_086123E4[][6];
extern const u8 gUnknown_0861266C[];
extern bool8 (* const gUnknown_08612688[])(struct Task *);
// IWRAM bss
IWRAM_DATA u8 sRoomType;
IWRAM_DATA u8 sStatusMon;
IWRAM_DATA bool8 gUnknown_0300128E;
IWRAM_DATA u32 sStatusFlags;
IWRAM_DATA u8 gUnknown_03001294;
// This file's functions.
u8 GetNextRoomType(void);
void sub_81A82A4(u8);
u16 sub_81A7B58(void);
void sub_81A8374(void);
void sub_81A5030(u8);
void TryHealMons(u8 healCount);
void sub_81A7EE4(u8 taskId);
bool8 sub_81A8554(void);
bool8 sub_81A7974(void);
u8 sub_81A890C(u16 species);
bool8 sub_81A88B0(u8 monLevel);
u8 GetBattlePikeWildMonHeaderId(void);
bool32 TryGenerateBattlePikeWildMon(bool8 checkKeenEyeIntimidate);
bool8 InBattlePike(void);
// code
void sub_81A703C(void)
{
gUnknown_086125F8[gSpecialVar_0x8004]();
}
void sub_81A705C(void)
{
u8 roomType = GetNextRoomType();
sRoomType = roomType;
}
void sub_81A7070(void)
{
bool32 setPerson1, setPerson2;
u32 person1;
u16 person2;
VarSet(VAR_OBJ_GFX_ID_0, EVENT_OBJ_GFX_LINK_RECEPTIONIST);
VarSet(VAR_OBJ_GFX_ID_1, EVENT_OBJ_GFX_DUSCLOPS);
setPerson1 = TRUE;
setPerson2 = FALSE;
person1 = 0;
person2 = 0;
switch (sRoomType)
{
case PIKE_ROOM_SINGLE_BATTLE:
sub_81A82A4(0);
setPerson1 = FALSE;
break;
case PIKE_ROOM_HEAL_FULL:
person1 = EVENT_OBJ_GFX_LINK_RECEPTIONIST;
break;
case PIKE_ROOM_NPC:
person1 = (u8)(sub_81A7B58());
break;
case PIKE_ROOM_STATUS:
person1 = EVENT_OBJ_GFX_GENTLEMAN;
if (sStatusMon == PIKE_STATUS_DUSCLOPS)
person2 = EVENT_OBJ_GFX_DUSCLOPS;
else
person2 = EVENT_OBJ_GFX_KIRLIA;
setPerson2 = TRUE;
break;
case PIKE_ROOM_HEAL_PART:
person1 = EVENT_OBJ_GFX_GENTLEMAN;
break;
case PIKE_ROOM_WILD_MONS:
setPerson1 = FALSE;
break;
case PIKE_ROOM_HARD_BATTLE:
sub_81A82A4(1);
person2 = EVENT_OBJ_GFX_LINK_RECEPTIONIST;
setPerson1 = FALSE;
setPerson2 = TRUE;
break;
case PIKE_ROOM_DOUBLE_BATTLE:
sub_81A8374();
setPerson1 = FALSE;
break;
case PIKE_ROOM_BRAIN:
SetFrontierBrainEventObjGfx(FRONTIER_FACILITY_PIKE);
person2 = EVENT_OBJ_GFX_LINK_RECEPTIONIST;
setPerson1 = FALSE;
setPerson2 = TRUE;
break;
default:
return;
}
if (setPerson1 == TRUE)
VarSet(VAR_OBJ_GFX_ID_0, person1);
if (setPerson2 == TRUE)
VarSet(VAR_OBJ_GFX_ID_1, person2);
}
void sub_81A7140(void)
{
u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
switch (gSpecialVar_0x8005)
{
case 0:
gSpecialVar_Result = gSaveBlock2Ptr->frontier.field_E02;
break;
case 1:
gSpecialVar_Result = gSaveBlock2Ptr->frontier.pikeWinStreaks[gSaveBlock2Ptr->frontier.lvlMode];
break;
case 2:
gSpecialVar_Result = gSaveBlock2Ptr->frontier.pikeRecordStreaks[gSaveBlock2Ptr->frontier.lvlMode];
break;
case 3:
gSpecialVar_Result = gSaveBlock2Ptr->frontier.pikeTotalStreaks[gSaveBlock2Ptr->frontier.lvlMode];
break;
case 4:
if (lvlMode != FRONTIER_LVL_50)
gSpecialVar_Result = gSaveBlock2Ptr->frontier.field_CDC & 0x800;
else
gSpecialVar_Result = gSaveBlock2Ptr->frontier.field_CDC & 0x400;
break;
}
}
void sub_81A7248(void)
{
u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
switch (gSpecialVar_0x8005)
{
case 0:
gSaveBlock2Ptr->frontier.field_E02 = gSpecialVar_0x8006;
break;
case 1:
if (gSpecialVar_0x8006 <= 9999)
gSaveBlock2Ptr->frontier.pikeWinStreaks[gSaveBlock2Ptr->frontier.lvlMode] = gSpecialVar_0x8006;
break;
case 2:
if (gSpecialVar_0x8006 <= 9999 && gSaveBlock2Ptr->frontier.pikeRecordStreaks[gSaveBlock2Ptr->frontier.lvlMode] < gSpecialVar_0x8006)
gSaveBlock2Ptr->frontier.pikeRecordStreaks[gSaveBlock2Ptr->frontier.lvlMode] = gSpecialVar_0x8006;
break;
case 3:
if (gSpecialVar_0x8006 <= 9999)
gSaveBlock2Ptr->frontier.pikeTotalStreaks[gSaveBlock2Ptr->frontier.lvlMode] = gSpecialVar_0x8006;
break;
case 4:
if (lvlMode != FRONTIER_LVL_50)
{
if (gSpecialVar_0x8006)
gSaveBlock2Ptr->frontier.field_CDC |= 0x800;
else
gSaveBlock2Ptr->frontier.field_CDC &= ~(0x800);
}
else
{
if (gSpecialVar_0x8006)
gSaveBlock2Ptr->frontier.field_CDC |= 0x400;
else
gSaveBlock2Ptr->frontier.field_CDC &= ~(0x400);
}
break;
}
}
void sub_81A73B8(void)
{
if (gSaveBlock2Ptr->frontier.curChallengeBattleNum >= 15)
gSpecialVar_Result = TRUE;
else
gSpecialVar_Result = FALSE;
}
void sub_81A73EC(void)
{
gSpecialVar_Result = sRoomType;
}
void sub_81A7400(void)
{
gUnknown_0300128E = TRUE;
}
void sub_81A740C(void)
{
gUnknown_0300128E = FALSE;
}
void sub_81A7418(void)
{
gSaveBlock2Ptr->frontier.field_CA8 = gSpecialVar_0x8005;
VarSet(VAR_TEMP_0, 0);
gSaveBlock2Ptr->frontier.field_CA9_a = 1;
save_serialize_map();
TrySavingData(SAVE_LINK);
}
void nullsub_76(void)
{
}
void nullsub_124(void)
{
}
void sub_81A7468(void)
{
switch (sStatusFlags)
{
case STATUS1_FREEZE:
gSpecialVar_Result = 0;
break;
case STATUS1_BURN:
gSpecialVar_Result = 1;
break;
case STATUS1_TOXIC_POISON:
gSpecialVar_Result = 2;
break;
case STATUS1_PARALYSIS:
gSpecialVar_Result = 3;
break;
case STATUS1_SLEEP:
gSpecialVar_Result = 4;
break;
}
}
void sub_81A74CC(void)
{
gSpecialVar_Result = sStatusMon;
}
void sub_81A74E0(void)
{
u16 toHeal = (Random() % 2) + 1;
TryHealMons(toHeal);
gSpecialVar_Result = toHeal;
}
void sub_81A7508(void)
{
s32 id;
if (gSaveBlock2Ptr->frontier.curChallengeBattleNum <= 4)
id = gUnknown_0861231C[gUnknown_03001294].unk2;
else if (gSaveBlock2Ptr->frontier.curChallengeBattleNum <= 10)
id = gUnknown_0861231C[gUnknown_03001294].unk3;
else
id = gUnknown_0861231C[gUnknown_03001294].unk4;
FrontierSpeechToString(gUnknown_086123E4[id]);
}
void sub_81A7580(void)
{
CreateTask(sub_81A7EE4, 2);
}
void HealMon(struct Pokemon *mon)
{
u8 i;
u16 hp;
u8 ppBonuses;
u8 data[4];
for (i = 0; i < 4; i++)
data[i] = 0;
hp = GetMonData(mon, MON_DATA_MAX_HP);
data[0] = hp;
data[1] = hp >> 8;
SetMonData(mon, MON_DATA_HP, data);
ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES);
for (i = 0; i < 4; i++)
{
u16 move = GetMonData(mon, MON_DATA_MOVE1 + i);
data[0] = CalculatePPWithBonus(move, ppBonuses, i);
SetMonData(mon, MON_DATA_PP1 + i, data);
}
data[0] = 0;
data[1] = 0;
data[2] = 0;
data[3] = 0;
SetMonData(mon, MON_DATA_STATUS, data);
}
bool8 DoesAbilityPreventStatus(struct Pokemon *mon, u32 status)
{
u8 ability = GetMonAbility(mon);
bool8 ret = FALSE;
switch (status)
{
case STATUS1_FREEZE:
if (ability == ABILITY_MAGMA_ARMOR)
ret = TRUE;
break;
case STATUS1_BURN:
if (ability == ABILITY_WATER_VEIL)
ret = TRUE;
break;
case STATUS1_PARALYSIS:
if (ability == ABILITY_LIMBER)
ret = TRUE;
break;
case STATUS1_SLEEP:
if (ability == ABILITY_INSOMNIA || ability == ABILITY_VITAL_SPIRIT)
ret = TRUE;
break;
case STATUS1_TOXIC_POISON:
if (ability == ABILITY_IMMUNITY)
ret = TRUE;
break;
}
return ret;
}
bool8 DoesTypePreventStatus(u16 species, u32 status)
{
bool8 ret = FALSE;
switch (status)
{
case STATUS1_TOXIC_POISON:
if (gBaseStats[species].type1 == TYPE_STEEL || gBaseStats[species].type1 == TYPE_POISON
|| gBaseStats[species].type2 == TYPE_STEEL || gBaseStats[species].type2 == TYPE_POISON)
ret = TRUE;
break;
case STATUS1_FREEZE:
if (gBaseStats[species].type1 == TYPE_ICE || gBaseStats[species].type2 == TYPE_ICE)
ret = TRUE;
break;
case STATUS1_PARALYSIS:
if (gBaseStats[species].type1 == TYPE_GROUND || gBaseStats[species].type1 == TYPE_ELECTRIC
|| gBaseStats[species].type2 == TYPE_GROUND || gBaseStats[species].type2 == TYPE_ELECTRIC)
ret = TRUE;
break;
case STATUS1_BURN:
if (gBaseStats[species].type1 == TYPE_FIRE || gBaseStats[species].type2 == TYPE_FIRE)
ret = TRUE;
break;
case STATUS1_SLEEP:
break;
}
return ret;
}
bool8 TryInflictRandomStatus(void)
{
u8 j, i;
u8 count;
u8 indices[3];
u32 status;
u16 species;
bool8 statusChosen;
struct Pokemon *mon;
for (i = 0; i < 3; i++)
indices[i] = i;
for (j = 0; j < 10; j++)
{
u8 temp, id;
i = Random() % 3;
id = Random() % 3;
SWAP(indices[i], indices[id], temp);
}
if (gSaveBlock2Ptr->frontier.curChallengeBattleNum <= 4)
count = 1;
else if (gSaveBlock2Ptr->frontier.curChallengeBattleNum <= 9)
count = 2;
else
count = 3;
status = 0;
do
{
u8 rand;
statusChosen = FALSE;
rand = Random() % 100;
if (rand < 35)
sStatusFlags = STATUS1_TOXIC_POISON;
else if (rand < 60)
sStatusFlags = STATUS1_FREEZE;
else if (rand < 80)
sStatusFlags = STATUS1_PARALYSIS;
else if (rand < 90)
sStatusFlags = STATUS1_SLEEP;
else
sStatusFlags = STATUS1_BURN;
if (status != sStatusFlags)
{
status = sStatusFlags;
j = 0;
for (i = 0; i < 3; i++)
{
mon = &gPlayerParty[indices[i]];
if (pokemon_ailments_get_primary(GetMonData(mon, MON_DATA_STATUS)) == 0
&& GetMonData(mon, MON_DATA_HP) != 0)
{
j++;
species = GetMonData(mon, MON_DATA_SPECIES);
if (!DoesTypePreventStatus(species, sStatusFlags))
{
statusChosen = TRUE;
break;
}
}
if (j == count)
break;
}
if (j == 0)
return FALSE;
}
} while (!statusChosen);
switch (sStatusFlags)
{
case STATUS1_FREEZE:
sStatusMon = PIKE_STATUS_DUSCLOPS;
break;
case STATUS1_BURN:
if (Random() % 2 != 0)
sStatusMon = PIKE_STATUS_DUSCLOPS;
else
sStatusMon = PIKE_STATUS_KIRLIA;
break;
case STATUS1_PARALYSIS:
case STATUS1_SLEEP:
case STATUS1_TOXIC_POISON:
default:
sStatusMon = PIKE_STATUS_KIRLIA;
break;
}
j = 0;
for (i = 0; i < 3; i++)
{
mon = &gPlayerParty[indices[i]];
if (pokemon_ailments_get_primary(GetMonData(mon, MON_DATA_STATUS)) == 0
&& GetMonData(mon, MON_DATA_HP) != 0)
{
j++;
species = GetMonData(mon, MON_DATA_SPECIES);
if (!DoesAbilityPreventStatus(mon, sStatusFlags) && !DoesTypePreventStatus(species, sStatusFlags))
SetMonData(mon, MON_DATA_STATUS, &sStatusFlags);
}
if (j == count)
break;
}
return TRUE;
}
bool8 AtLeastOneHealthyMon(void)
{
u8 i;
u8 healthyMonsCount;
u8 count;
if (gSaveBlock2Ptr->frontier.curChallengeBattleNum <= 4)
count = 1;
else if (gSaveBlock2Ptr->frontier.curChallengeBattleNum <= 9)
count = 2;
else
count = 3;
healthyMonsCount = 0;
for (i = 0; i < 3; i++)
{
struct Pokemon *mon = &gPlayerParty[i];
if (pokemon_ailments_get_primary(GetMonData(mon, MON_DATA_STATUS)) == 0
&& GetMonData(mon, MON_DATA_HP) != 0)
{
healthyMonsCount++;
}
if (healthyMonsCount == count)
break;
}
if (healthyMonsCount == 0)
return FALSE;
else
return TRUE;
}
u8 GetNextRoomType(void)
{
u8 sp[8];
u8 i;
u8 ret;
u8 var;
u8 count;
u8 *allocated;
u8 r3;
if (gSaveBlock2Ptr->frontier.field_E10_2 == 8)
return gSaveBlock2Ptr->frontier.field_E10_2;
if (gSpecialVar_0x8007 == gSaveBlock2Ptr->frontier.field_E10_1)
{
if (gSaveBlock2Ptr->frontier.field_E10_2 == 3)
TryInflictRandomStatus();
return gSaveBlock2Ptr->frontier.field_E10_2;
}
for (i = 0; i < 8; i++)
sp[i] = 0;
count = 8;
var = gUnknown_0861266C[gSaveBlock2Ptr->frontier.field_E10_2];
for (i = 0; i < 8; i++)
{
if (gUnknown_0861266C[i] == var)
{
sp[i] = 1;
count--;
}
}
if (sp[7] != 1 && !sub_81A8554())
{
sp[7] = 1;
count--;
}
if (sp[3] != 1 && !AtLeastOneHealthyMon())
{
sp[3] = 1;
count--;
}
if (gSaveBlock2Ptr->frontier.field_E10_3)
{
if (sp[1] != 1)
{
sp[1] = 1;
count--;
}
if (sp[4] != 1)
{
sp[4] = 1;
count--;
}
}
allocated = AllocZeroed(count);
r3 = 0;
for (i = 0; i < 8; i++)
{
if (sp[i] == 0)
allocated[r3++] = i;
}
ret = allocated[Random() % count];
free(allocated);
if (ret == PIKE_ROOM_STATUS)
TryInflictRandomStatus();
return ret;
}
u16 sub_81A7B58(void)
{
gUnknown_03001294 = Random() % 25;
return gUnknown_0861231C[gUnknown_03001294].unk0;
}
u8 sub_81A7B84(void)
{
return gUnknown_0300128E;
}
bool32 TryGenerateBattlePikeWildMon(bool8 checkKeenEyeIntimidate)
{
s32 i;
s32 monLevel;
u8 headerId = GetBattlePikeWildMonHeaderId();
u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
const struct PikeWildMon *const *const wildMons = gUnknown_08612314[lvlMode];
u32 abilityBit;
s32 pikeMonId = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES, NULL);
pikeMonId = sub_81A890C(pikeMonId);
if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_50)
{
monLevel = GetHighestLevelInPlayerParty();
if (monLevel < 60)
{
monLevel = 60;
}
else
{
monLevel -= wildMons[headerId][pikeMonId].unk2;
if (monLevel < 60)
monLevel = 60;
}
}
else
{
monLevel = 50 - wildMons[headerId][pikeMonId].unk2;
}
if (checkKeenEyeIntimidate == TRUE && !sub_81A88B0(monLevel))
return FALSE;
SetMonData(&gEnemyParty[0],
MON_DATA_EXP,
&gExperienceTables[gBaseStats[wildMons[headerId][pikeMonId].species].growthRate][monLevel]);
if (gBaseStats[wildMons[headerId][pikeMonId].species].ability2)
abilityBit = Random() % 2;
else
abilityBit = 0;
SetMonData(&gEnemyParty[0], MON_DATA_ALT_ABILITY, &abilityBit);
for (i = 0; i < 4; i++)
SetMonMoveSlot(&gEnemyParty[0], wildMons[headerId][pikeMonId].moves[i], i);
CalculateMonStats(&gEnemyParty[0]);
return TRUE;
}
u8 GetBattlePikeWildMonHeaderId(void)
{
u8 headerId;
u8 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
u16 winStreak = gSaveBlock2Ptr->frontier.pikeWinStreaks[lvlMode];
if (winStreak <= 280)
headerId = 0;
else if (winStreak <= 560)
headerId = 1;
else if (winStreak <= 840)
headerId = 2;
else
headerId = 3;
return headerId;
}
void sub_81A7D54(u8 taskId)
{
while (gUnknown_08612688[gTasks[taskId].data[0]](&gTasks[taskId]));
}
bool8 sub_81A7D8C(struct Task *task)
{
if (task->data[6] == 0 || --task->data[6] == 0)
{
task->data[6] = task->data[1];
task->data[7] += task->data[4];
if (task->data[7] > 16)
task->data[7] = 16;
BlendPalettes(0xFFFFFFFF, task->data[7], RGB(11, 11, 11));
}
if (task->data[7] >= 16)
{
task->data[0]++;
task->data[6] = task->data[2];
}
return FALSE;
}
bool8 sub_81A7DE8(struct Task *task)
{
if (task->data[6] == 0 || --task->data[6] == 0)
{
task->data[6] = task->data[2];
task->data[7] -= task->data[5];
if (task->data[7] < 0)
task->data[7] = 0;
BlendPalettes(0xFFFFFFFF, task->data[7], RGB(11, 11, 11));
}
if (task->data[7] == 0)
{
if (--task->data[3] == 0)
{
DestroyTask(FindTaskIdByFunc(sub_81A7D54));
}
else
{
task->data[6] = task->data[1];
task->data[0] = 0;
}
}
return FALSE;
}
void sub_81A7E60(s16 a0, s16 a1, s16 a2, s16 a3, s16 a4)
{
u8 taskId = CreateTask(sub_81A7D54, 3);
gTasks[taskId].data[1] = a0;
gTasks[taskId].data[2] = a1;
gTasks[taskId].data[3] = a2;
gTasks[taskId].data[4] = a3;
gTasks[taskId].data[5] = a4;
gTasks[taskId].data[6] = a0;
}
bool8 sub_81A7EC4(void)
{
if (FindTaskIdByFunc(sub_81A7D54) == 0xFF)
return TRUE;
else
return FALSE;
}
void sub_81A7EE4(u8 taskId)
{
if (gTasks[taskId].data[0] == 0)
{
gTasks[taskId].data[0]++;
sub_81A7E60(0, 0, 3, 2, 2);
}
else
{
if (sub_81A7EC4())
{
EnableBothScriptContexts();
DestroyTask(taskId);
}
}
}
void TryHealMons(u8 healCount)
{
u8 j, i, k;
u8 indices[3];
if (healCount == 0)
return;
for (i = 0; i < 3; i++)
indices[i] = i;
for (k = 0; k < 10; k++)
{
u8 temp;
i = Random() % 3;
j = Random() % 3;
SWAP(indices[i], indices[j], temp);
}
for (i = 0; i < 3; i++)
{
bool32 canBeHealed = FALSE;
struct Pokemon *mon = &gPlayerParty[indices[i]];
u16 curr = GetMonData(mon, MON_DATA_HP);
u16 max = GetMonData(mon, MON_DATA_MAX_HP);
if (curr < max)
{
canBeHealed = TRUE;
}
else
{
if (pokemon_ailments_get_primary(GetMonData(mon, MON_DATA_STATUS)) != 0)
{
canBeHealed = TRUE;
}
else
{
u8 ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES);
for (j = 0; j < 4; j++)
{
u16 move = GetMonData(mon, MON_DATA_MOVE1 + j);
max = CalculatePPWithBonus(move, ppBonuses, j);
curr = GetMonData(mon, MON_DATA_PP1 + j);
if (curr < max)
{
canBeHealed = TRUE;
break;
}
}
}
}
if (canBeHealed == TRUE)
{
HealMon(&gPlayerParty[indices[i]]);
if (--healCount == 0)
break;
}
}
}
void sub_81A8090(void)
{
gSpecialVar_Result = InBattlePike();
}