mirror of
https://github.com/pret/pmd-red.git
synced 2026-03-26 12:05:17 -05:00
404 lines
12 KiB
C
404 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_message.h"
|
|
#include "dungeon_pokemon_attributes.h"
|
|
#include "dungeon_util.h"
|
|
#include "math.h"
|
|
#include "status.h"
|
|
#include "dungeon_config.h"
|
|
#include "weather.h"
|
|
|
|
|
|
u32 gUnknown_8106EFC[] = { 0x00, 0x00 };
|
|
s48_16 gUnknown_8106F04 = { 0x0, 0x10000 };
|
|
s48_16 gUnknown_8106F0C = { 0x0, 0x20000 };
|
|
s48_16 gUnknown_8106F14 = { 0x0, 0x18000 };
|
|
s48_16 gUnknown_8106F1C = { 0x0, 0x8000 };
|
|
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 };
|
|
s48_16 gUnknown_8106F3C = {0x0, 0x8000};
|
|
s48_16 gUnknown_8106F44 = {0x0, 0xE666};
|
|
s48_16 gUnknown_8106F4C = {0x0, 0x18000};
|
|
|
|
|
|
struct dumb_struct
|
|
{
|
|
s48_16 *unk0[NUM_EFFECTIVENESS];
|
|
};
|
|
|
|
struct dumb_struct gUnknown_8106F54 = {
|
|
&gUnknown_8106F3C, // IMMUNE
|
|
&gUnknown_8106F44, // RESIST
|
|
&gUnknown_8106F04, // NEUTRAL
|
|
&gUnknown_8106F4C, // SUPER
|
|
};
|
|
|
|
s48_16 gUnknown_8106F64 = {0x0, 0xC000};
|
|
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[];
|
|
|
|
void sub_80428D8(Entity *);
|
|
void sub_8042978(Entity *);
|
|
void sub_804298C(Entity *);
|
|
void sub_80428EC(Entity *);
|
|
|
|
bool8 sub_806E100(s48_16 *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 = GetEntInfo(pokemon);
|
|
targetInfo = GetEntInfo(target);
|
|
FP48_16_FromS32(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 ((AbilityIsActive(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 (F48_16_IsZero(param_1)) break;
|
|
if (((normalOrFightingType) && (targetInfo->types[index] == TYPE_GHOST)) && (targetInfo->exposed == FALSE)) {
|
|
effectiveness = EFFECTIVENESS_IMMUNE;
|
|
gDungeon->unk134.pokemonExposed = TRUE;
|
|
}
|
|
else {
|
|
effectiveness = gTypeEffectivenessChart[type][targetInfo->types[index]];
|
|
}
|
|
if (effectiveness != EFFECTIVENESS_NEUTRAL) {
|
|
F48_16_SMul(param_1,param_1,local_48.unk0[effectiveness]);
|
|
}
|
|
local_38[index] = effectiveness;
|
|
gDungeon->unk134.unk13C[index] = effectiveness;
|
|
}
|
|
|
|
param_5->effectiveness = gEffectivenessChart[local_38[0]][local_38[1]];
|
|
bVar4 = TRUE;
|
|
if ((param_5->effectiveness != EFFECTIVENESS_SUPER) && (bVar4 = FALSE, hasWonderGuard)) {
|
|
temp = gUnknown_8106EFC[1];
|
|
param_1->hi = gUnknown_8106EFC[0];
|
|
param_1->lo = temp;
|
|
}
|
|
|
|
if (((type == TYPE_FIRE) || (type == TYPE_ICE)) && (AbilityIsActive(target,ABILITY_THICK_FAT))) {
|
|
gDungeon->unk134.unk16D = TRUE;
|
|
F48_16_SMul(param_1,param_1, &gUnknown_8106F1C);
|
|
}
|
|
if ((type == TYPE_FIRE) && (GetFlashFireStatus(target) != FLASH_FIRE_STATUS_NONE)) {
|
|
gDungeon->unk134.fill16E[0] = TRUE;
|
|
FP48_16_FromS32(param_1,0);
|
|
param_5->effectiveness = EFFECTIVENESS_IMMUNE;
|
|
param_5->unkD = 0;
|
|
param_5->unkE = 1;
|
|
bVar4 = FALSE;
|
|
}
|
|
if ((type == TYPE_GROUND) && (AbilityIsActive(target, ABILITY_LEVITATE))) {
|
|
gDungeon->unk134.fill16E[1] = TRUE;
|
|
FP48_16_FromS32(param_1,0);
|
|
param_5->effectiveness = EFFECTIVENESS_IMMUNE;
|
|
param_5->unkD = 0;
|
|
param_5->unkE = 1;
|
|
bVar4 = FALSE;
|
|
}
|
|
if ((type == TYPE_WATER) && (AbilityIsActive(pokemon, ABILITY_TORRENT))) {
|
|
torrentFlag = pokemonInfo->maxHPStat / 4 >= pokemonInfo->HP;
|
|
torrentVisualFlag = SetVisualFlags(pokemonInfo,0x80,torrentFlag);
|
|
if (torrentFlag) {
|
|
gDungeon->unk134.fill16E[2] = TRUE;
|
|
F48_16_SMul(param_1,param_1, &gUnknown_8106F0C);
|
|
}
|
|
if (torrentVisualFlag) {
|
|
sub_80428EC(pokemon);
|
|
TryDisplayDungeonLoggableMessage3(pokemon,target,*gUnknown_80FEDA8);
|
|
}
|
|
}
|
|
if ((type == TYPE_GRASS) && (AbilityIsActive(pokemon, ABILITY_OVERGROW))) {
|
|
overgrowFlag = pokemonInfo->maxHPStat / 4 >= pokemonInfo->HP;
|
|
overgrowVisualFlag = SetVisualFlags(pokemonInfo,2,overgrowFlag);
|
|
if (overgrowFlag) {
|
|
gDungeon->unk134.fill16E[3] = TRUE;
|
|
F48_16_SMul(param_1,param_1, &gUnknown_8106F0C);
|
|
}
|
|
if (overgrowVisualFlag) {
|
|
sub_80428D8(pokemon);
|
|
TryDisplayDungeonLoggableMessage3(pokemon,target,*gUnknown_80FED88);
|
|
}
|
|
}
|
|
if ((type == TYPE_BUG) && (AbilityIsActive(pokemon, ABILITY_SWARM))) {
|
|
swarmFlag = pokemonInfo->maxHPStat / 4 >= pokemonInfo->HP;
|
|
swarmVisualFlag = SetVisualFlags(pokemonInfo,0x10,swarmFlag);
|
|
if (swarmFlag) {
|
|
gDungeon->unk134.fill16E[4] = TRUE;
|
|
F48_16_SMul(param_1,param_1, &gUnknown_8106F0C);
|
|
}
|
|
if (swarmVisualFlag) {
|
|
sub_8042978(pokemon);
|
|
TryDisplayDungeonLoggableMessage3(pokemon,target,*gUnknown_80FEDC8);
|
|
}
|
|
}
|
|
if ((type == TYPE_FIRE) && (AbilityIsActive(pokemon, ABILITY_BLAZE))) {
|
|
blazeFlag = pokemonInfo->maxHPStat / 4 >= pokemonInfo->HP;
|
|
blazeVisualFlag = SetVisualFlags(pokemonInfo,0x20,blazeFlag);
|
|
if (blazeFlag) {
|
|
gDungeon->unk134.fill16E[5] = TRUE;
|
|
F48_16_SMul(param_1,param_1, &gUnknown_8106F0C);
|
|
}
|
|
if (blazeVisualFlag) {
|
|
sub_804298C(pokemon);
|
|
TryDisplayDungeonLoggableMessage3(pokemon,target,*gUnknown_80FEDE8);
|
|
}
|
|
}
|
|
if (!(F48_16_IsZero(param_1)) && (MonsterIsType(pokemon, type))) {
|
|
gDungeon->unk134.fill16E[6] = TRUE;
|
|
F48_16_SMul(param_1,param_1, &gUnknown_8106F14);
|
|
}
|
|
weather = GetApparentWeather(pokemon);
|
|
if (weather == WEATHER_SUNNY) {
|
|
if (type == TYPE_FIRE) {
|
|
gDungeon->unk134.unk16C = TRUE;
|
|
F48_16_SMul(param_1,param_1, &gUnknown_8106F14);
|
|
}
|
|
else if (type == TYPE_WATER) {
|
|
gDungeon->unk134.unk16C = TRUE;
|
|
F48_16_SMul(param_1,param_1, &gUnknown_8106F1C);
|
|
}
|
|
}
|
|
if (weather == WEATHER_RAIN) {
|
|
if (type == TYPE_FIRE) {
|
|
gDungeon->unk134.unk16B = TRUE;
|
|
F48_16_SMul(param_1,param_1, &gUnknown_8106F1C);
|
|
}
|
|
else if (type == TYPE_WATER) {
|
|
gDungeon->unk134.unk16B = TRUE;
|
|
F48_16_SMul(param_1,param_1, &gUnknown_8106F14);
|
|
}
|
|
}
|
|
if ((weather == WEATHER_CLOUDY) && (type != TYPE_NORMAL)) {
|
|
F48_16_SMul(param_1,param_1, &gUnknown_8106F64);
|
|
gDungeon->unk134.unk16A = TRUE;
|
|
}
|
|
if (((gDungeon->weather.mudSportTurns != 0) || (weather == WEATHER_FOG)) && (type == TYPE_ELECTRIC)) {
|
|
gDungeon->unk134.fill16E[7] = TRUE;
|
|
F48_16_SMul(param_1,param_1, &gUnknown_8106F1C);
|
|
}
|
|
if ((gDungeon->weather.waterSportTurns != 0) && (type == TYPE_FIRE)) {
|
|
gDungeon->unk134.fill16E[8] = TRUE;
|
|
F48_16_SMul(param_1,param_1, &gUnknown_8106F1C);
|
|
}
|
|
if ((type == TYPE_ELECTRIC) && (pokemonInfo->bideClassStatus.status == STATUS_CHARGING)) {
|
|
gDungeon->unk134.fill16E[9] = TRUE;
|
|
F48_16_SMul(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 = GetEntInfo(user);
|
|
targetData = GetEntInfo(target);
|
|
if (moveType == TYPE_FIRE && GetFlashFireStatus(target) != FLASH_FIRE_STATUS_NONE)
|
|
{
|
|
return 0;
|
|
}
|
|
if (moveType == TYPE_ELECTRIC && AbilityIsActive(target, ABILITY_VOLT_ABSORB))
|
|
{
|
|
return 0;
|
|
}
|
|
if (moveType == TYPE_WATER && AbilityIsActive(target, ABILITY_WATER_ABSORB))
|
|
{
|
|
return 0;
|
|
}
|
|
if (moveType == TYPE_GROUND && AbilityIsActive(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->unk134.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) && AbilityIsActive(target, ABILITY_THICK_FAT))
|
|
{
|
|
return 2;
|
|
}
|
|
if (moveType == TYPE_WATER && AbilityIsActive(user, ABILITY_TORRENT))
|
|
{
|
|
s32 maxHPStat = userData->maxHPStat;
|
|
if (maxHPStat < 0)
|
|
{
|
|
maxHPStat += 3;
|
|
}
|
|
if (maxHPStat >> 2 >= userData->HP)
|
|
{
|
|
weight *= 2;
|
|
}
|
|
}
|
|
if (moveType == TYPE_GRASS && AbilityIsActive(user, ABILITY_OVERGROW))
|
|
{
|
|
s32 maxHPStat = userData->maxHPStat;
|
|
if (maxHPStat < 0)
|
|
{
|
|
maxHPStat += 3;
|
|
}
|
|
if (maxHPStat >> 2 >= userData->HP)
|
|
{
|
|
weight *= 2;
|
|
}
|
|
}
|
|
if (moveType == TYPE_BUG && AbilityIsActive(user, ABILITY_SWARM))
|
|
{
|
|
s32 maxHPStat = userData->maxHPStat;
|
|
if (maxHPStat < 0)
|
|
{
|
|
maxHPStat += 3;
|
|
}
|
|
if (maxHPStat >> 2 >= userData->HP)
|
|
{
|
|
weight *= 2;
|
|
}
|
|
}
|
|
if (moveType == TYPE_FIRE && AbilityIsActive(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->weather.mudSportTurns != 0 && moveType == TYPE_ELECTRIC)
|
|
{
|
|
return 2;
|
|
}
|
|
if (gDungeon->weather.waterSportTurns != 0 && moveType == TYPE_FIRE)
|
|
{
|
|
return 2;
|
|
}
|
|
if (moveType == TYPE_ELECTRIC && userData->bideClassStatus.status == STATUS_CHARGING)
|
|
{
|
|
weight *= 2;
|
|
}
|
|
if (weight > 2)
|
|
{
|
|
weight = 3;
|
|
}
|
|
return weight + 2;
|
|
}
|