pmd-red/src/type_effectiveness.c
2024-07-20 10:18:26 -07:00

410 lines
12 KiB
C

#include "global.h"
#include "type_effectiveness.h"
#include "constants/ability.h"
#include "constants/status.h"
#include "constants/weather.h"
#include "structs/str_dungeon.h"
#include "dungeon_pokemon_attributes.h"
#include "dungeon_util.h"
#include "status.h"
#include "type_chart.h"
#include "weather.h"
u32 gUnknown_8106EFC[] = { 0x00, 0x00 };
u8 gUnknown_8106F04[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 };
u8 gUnknown_8106F0C[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00 };
u8 gUnknown_8106F14[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00 };
u8 gUnknown_8106F1C[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00 };
u8 gUnknown_8106F24[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfe, 0x7f, 0x01, 0x00 };
u8 gUnknown_8106F3C[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00};
u8 gUnknown_8106F44[] = {0x00, 0x00, 0x00, 0x00, 0x66, 0xe6, 0x00, 0x00};
u8 gUnknown_8106F4C[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00};
struct dumb_struct
{
u8 *unk0[NUM_EFFECTIVENESS];
};
struct dumb_struct gUnknown_8106F54 = {
gUnknown_8106F3C, // IMMUNE
gUnknown_8106F44, // RESIST
gUnknown_8106F04, // NEUTRAL
gUnknown_8106F4C, // SUPER
};
u8 gUnknown_8106F64[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00};
u32 gTypeEffectivenessMultipliers[] = {0, 1, 2, 4};
struct unkStruct_806D010
{
s32 unk0;
u32 unk4;
u32 effectiveness;
u8 unkC;
u8 unkD;
u8 unkE;
u8 unkF;
};
extern u8 *gUnknown_80FEDE8[];
extern u8 *gUnknown_80FEDC8[];
extern u8 *gUnknown_80FEDA8[];
extern u8 *gUnknown_80FED88[];
extern s32 gUnknown_80F54B4[NUM_EFFECTIVENESS][NUM_EFFECTIVENESS];
void sub_80522F4(Entity *, Entity *, u8 *);
void sub_80428D8(Entity *);
void sub_8042978(Entity *);
void sub_804298C(Entity *);
bool8 sub_800A2A0(s32 *);
void sub_800A34C(s32 *, s32 *, const u8 *);
void sub_800A020(s32 *, u32);
void sub_80428EC(Entity *);
bool8 sub_806E100(s32 *param_1, Entity *pokemon, Entity *target, u8 type, struct unkStruct_806D010 *param_5)
{
bool8 torrentFlag;
bool8 overgrowFlag;
bool8 swarmFlag;
bool8 blazeFlag;
bool8 torrentVisualFlag;
bool8 overgrowVisualFlag;
bool8 swarmVisualFlag;
bool8 blazeVisualFlag;
bool8 bVar4;
EntityInfo *targetInfo;
s32 effectiveness;
s32 index;
struct dumb_struct local_48;
s32 local_38 [2];
s32 hasWonderGuard;
s32 normalOrFightingType;
EntityInfo *pokemonInfo;
u8 weather;
s32 temp;
bVar4 = FALSE;
normalOrFightingType = FALSE;
pokemonInfo = pokemon->info;
targetInfo = target->info;
sub_800A020(param_1,1);
param_5->unkD = 0;
param_5->unkE = 0;
hasWonderGuard = FALSE;
if (!EntityExists(target)) {
return TRUE;
}
else {
if ((type == TYPE_NORMAL) || (type == TYPE_FIGHTING)) {
normalOrFightingType = TRUE;
}
if ((HasAbility(target, ABILITY_WONDER_GUARD)) && (type != TYPE_NONE)) {
hasWonderGuard = TRUE;
}
param_5->effectiveness = EFFECTIVENESS_NEUTRAL;
for (index = 0; index < 2; index++) {
local_48 = gUnknown_8106F54;
if (sub_800A2A0(param_1)) break;
if (((normalOrFightingType) && (targetInfo->types[index] == TYPE_GHOST)) && (targetInfo->exposed == FALSE)) {
effectiveness = EFFECTIVENESS_IMMUNE;
gDungeon->pokemonExposed = TRUE;
}
else {
effectiveness = gTypeEffectivenessChart[type][targetInfo->types[index]];
}
if (effectiveness != EFFECTIVENESS_NEUTRAL) {
sub_800A34C(param_1,param_1,local_48.unk0[effectiveness]);
}
local_38[index] = effectiveness;
gDungeon->unk13C[index] = effectiveness;
}
param_5->effectiveness = gUnknown_80F54B4[local_38[0]][local_38[1]];
bVar4 = TRUE;
if ((param_5->effectiveness != EFFECTIVENESS_SUPER) && (bVar4 = FALSE, hasWonderGuard)) {
temp = gUnknown_8106EFC[1];
param_1[0] = gUnknown_8106EFC[0];
param_1[1] = temp;
}
if (((type == TYPE_FIRE) || (type == TYPE_ICE)) && (HasAbility(target,ABILITY_THICK_FAT))) {
gDungeon->unk16D = TRUE;
sub_800A34C(param_1,param_1,gUnknown_8106F1C);
}
if ((type == TYPE_FIRE) && (GetFlashFireStatus(target) != FLASH_FIRE_STATUS_NONE)) {
gDungeon->fill16E[0] = TRUE;
sub_800A020(param_1,0);
param_5->effectiveness = EFFECTIVENESS_IMMUNE;
param_5->unkD = 0;
param_5->unkE = 1;
bVar4 = FALSE;
}
if ((type == TYPE_GROUND) && (HasAbility(target, ABILITY_LEVITATE))) {
gDungeon->fill16E[1] = TRUE;
sub_800A020(param_1,0);
param_5->effectiveness = EFFECTIVENESS_IMMUNE;
param_5->unkD = 0;
param_5->unkE = 1;
bVar4 = FALSE;
}
if ((type == TYPE_WATER) && (HasAbility(pokemon, ABILITY_TORRENT))) {
torrentFlag = pokemonInfo->maxHPStat / 4 >= pokemonInfo->HP;
torrentVisualFlag = SetVisualFlags(pokemonInfo,0x80,torrentFlag);
if (torrentFlag) {
gDungeon->fill16E[2] = TRUE;
sub_800A34C(param_1,param_1,gUnknown_8106F0C);
}
if (torrentVisualFlag) {
sub_80428EC(pokemon);
sub_80522F4(pokemon,target,*gUnknown_80FEDA8);
}
}
if ((type == TYPE_GRASS) && (HasAbility(pokemon, ABILITY_OVERGROW))) {
overgrowFlag = pokemonInfo->maxHPStat / 4 >= pokemonInfo->HP;
overgrowVisualFlag = SetVisualFlags(pokemonInfo,2,overgrowFlag);
if (overgrowFlag) {
gDungeon->fill16E[3] = TRUE;
sub_800A34C(param_1,param_1,gUnknown_8106F0C);
}
if (overgrowVisualFlag) {
sub_80428D8(pokemon);
sub_80522F4(pokemon,target,*gUnknown_80FED88);
}
}
if ((type == TYPE_BUG) && (HasAbility(pokemon, ABILITY_SWARM))) {
swarmFlag = pokemonInfo->maxHPStat / 4 >= pokemonInfo->HP;
swarmVisualFlag = SetVisualFlags(pokemonInfo,0x10,swarmFlag);
if (swarmFlag) {
gDungeon->fill16E[4] = TRUE;
sub_800A34C(param_1,param_1,gUnknown_8106F0C);
}
if (swarmVisualFlag) {
sub_8042978(pokemon);
sub_80522F4(pokemon,target,*gUnknown_80FEDC8);
}
}
if ((type == TYPE_FIRE) && (HasAbility(pokemon, ABILITY_BLAZE))) {
blazeFlag = pokemonInfo->maxHPStat / 4 >= pokemonInfo->HP;
blazeVisualFlag = SetVisualFlags(pokemonInfo,0x20,blazeFlag);
if (blazeFlag) {
gDungeon->fill16E[5] = TRUE;
sub_800A34C(param_1,param_1,gUnknown_8106F0C);
}
if (blazeVisualFlag) {
sub_804298C(pokemon);
sub_80522F4(pokemon,target,*gUnknown_80FEDE8);
}
}
if (!(sub_800A2A0(param_1)) && (MonsterIsType(pokemon, type))) {
gDungeon->fill16E[6] = TRUE;
sub_800A34C(param_1,param_1,gUnknown_8106F14);
}
weather = GetApparentWeather(pokemon);
if (weather == WEATHER_SUNNY) {
if (type == TYPE_FIRE) {
gDungeon->unk16C = TRUE;
sub_800A34C(param_1,param_1,gUnknown_8106F14);
}
else if (type == TYPE_WATER) {
gDungeon->unk16C = TRUE;
sub_800A34C(param_1,param_1,gUnknown_8106F1C);
}
}
if (weather == WEATHER_RAIN) {
if (type == TYPE_FIRE) {
gDungeon->unk16B = TRUE;
sub_800A34C(param_1,param_1,gUnknown_8106F1C);
}
else if (type == TYPE_WATER) {
gDungeon->unk16B = TRUE;
sub_800A34C(param_1,param_1,gUnknown_8106F14);
}
}
if ((weather == WEATHER_CLOUDY) && (type != TYPE_NORMAL)) {
sub_800A34C(param_1,param_1, gUnknown_8106F64);
gDungeon->unk16A = TRUE;
}
if (((gDungeon->mudSportTurns != 0) || (weather == WEATHER_FOG)) && (type == TYPE_ELECTRIC)) {
gDungeon->fill16E[7] = TRUE;
sub_800A34C(param_1,param_1,gUnknown_8106F1C);
}
if ((gDungeon->waterSportTurns != 0) && (type == TYPE_FIRE)) {
gDungeon->fill16E[8] = TRUE;
sub_800A34C(param_1,param_1,gUnknown_8106F1C);
}
if ((type == TYPE_ELECTRIC) && (pokemonInfo->charging.chargingStatus == STATUS_CHARGING)) {
gDungeon->fill16E[9] = TRUE;
sub_800A34C(param_1,param_1,gUnknown_8106F0C);
}
}
return bVar4;
}
s32 WeightWeakTypePicker(Entity *user, Entity *target, u8 moveType)
{
s32 weight = 1;
bool8 checkExposed = FALSE;
EntityInfo *userData;
EntityInfo *targetData;
u8 *targetTypes;
u8 *targetType;
u32 moveTypeOffset;
if (!EntityExists(target))
{
return 1;
}
if (moveType == TYPE_NORMAL || moveType == TYPE_FIGHTING)
{
checkExposed = TRUE;
}
userData = user->info;
targetData = target->info;
if (moveType == TYPE_FIRE && GetFlashFireStatus(target) != FLASH_FIRE_STATUS_NONE)
{
return 0;
}
if (moveType == TYPE_ELECTRIC && HasAbility(target, ABILITY_VOLT_ABSORB))
{
return 0;
}
if (moveType == TYPE_WATER && HasAbility(target, ABILITY_WATER_ABSORB))
{
return 0;
}
if (moveType == TYPE_GROUND && HasAbility(target, ABILITY_LEVITATE))
{
return 1;
}
targetTypes = targetData->types;
moveTypeOffset = moveType * NUM_TYPES * sizeof(s16);
targetType = targetData->types;
do
{
s32 effectiveness;
u32 typeEffectivenessMultipliers[NUM_EFFECTIVENESS];
memcpy(typeEffectivenessMultipliers, gTypeEffectivenessMultipliers, NUM_EFFECTIVENESS * sizeof(u32));
if (checkExposed && *targetType == TYPE_GHOST && !targetData->exposed)
{
effectiveness = 0;
gDungeon->pokemonExposed = TRUE;
}
else
{
effectiveness = gTypeEffectivenessChart[moveType][*targetType];
// Used to swap variable initialization order at the loop start.
effectiveness = *(s16*)(((s8*) gTypeEffectivenessChart) + moveTypeOffset + *targetType * 2);
}
if (weight == 0)
{
goto breakLoop;
}
weight *= typeEffectivenessMultipliers[effectiveness];
weight /= 2;
if (weight == 0)
{
// BUG: If the Pokémon's first type resists the move, the second type is ignored.
// This calculates type effectiveness incorrectly if the first type resists the move and the second type is weak to the move.
// For example, a Fire-type move is considered not very effective against a Rock/Bug-type like Anorith.
return 2;
}
} while ((s32)(++targetType) <= (s32)(targetTypes + 1));
breakLoop:
if ((moveType == TYPE_FIRE || moveType == TYPE_ICE) && HasAbility(target, ABILITY_THICK_FAT))
{
return 2;
}
if (moveType == TYPE_WATER && HasAbility(user, ABILITY_TORRENT))
{
s32 maxHPStat = userData->maxHPStat;
if (maxHPStat < 0)
{
maxHPStat += 3;
}
if (maxHPStat >> 2 >= userData->HP)
{
weight *= 2;
}
}
if (moveType == TYPE_GRASS && HasAbility(user, ABILITY_OVERGROW))
{
s32 maxHPStat = userData->maxHPStat;
if (maxHPStat < 0)
{
maxHPStat += 3;
}
if (maxHPStat >> 2 >= userData->HP)
{
weight *= 2;
}
}
if (moveType == TYPE_BUG && HasAbility(user, ABILITY_SWARM))
{
s32 maxHPStat = userData->maxHPStat;
if (maxHPStat < 0)
{
maxHPStat += 3;
}
if (maxHPStat >> 2 >= userData->HP)
{
weight *= 2;
}
}
if (moveType == TYPE_FIRE && HasAbility(user, ABILITY_BLAZE))
{
s32 maxHPStat = userData->maxHPStat;
if (maxHPStat < 0)
{
maxHPStat += 3;
}
if (maxHPStat >> 2 >= userData->HP)
{
weight *= 2;
}
}
if (weight == 0)
{
return 2;
}
if (MonsterIsType(user, moveType))
{
weight *= 2;
}
targetTypes = targetData->types;
if (GetApparentWeather(user) == WEATHER_SUNNY)
{
if (moveType == TYPE_FIRE)
{
weight *= 2;
}
else if (moveType == TYPE_WATER)
{
return 2;
}
}
if (gDungeon->mudSportTurns != 0 && moveType == TYPE_ELECTRIC)
{
return 2;
}
if (gDungeon->waterSportTurns != 0 && moveType == TYPE_FIRE)
{
return 2;
}
if (moveType == TYPE_ELECTRIC && userData->charging.chargingStatus == STATUS_CHARGING)
{
weight *= 2;
}
if (weight > 2)
{
weight = 3;
}
return weight + 2;
}