summaryrefslogtreecommitdiff
path: root/src/type_effectiveness.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/type_effectiveness.c')
-rw-r--r--src/type_effectiveness.c178
1 files changed, 178 insertions, 0 deletions
diff --git a/src/type_effectiveness.c b/src/type_effectiveness.c
new file mode 100644
index 0000000..efc1ef9
--- /dev/null
+++ b/src/type_effectiveness.c
@@ -0,0 +1,178 @@
+#include "global.h"
+#include "type_effectiveness.h"
+
+#include "constants/ability.h"
+#include "constants/status.h"
+#include "constants/weather.h"
+#include "dungeon_global_data.h"
+#include "dungeon_pokemon_attributes.h"
+#include "dungeon_util.h"
+#include "type_chart.h"
+#include "weather.h"
+
+#define FLASH_FIRE_STATUS_NONE 0
+
+u32 gTypeEffectivenessMultipliers[] = {0, 1, 2, 4};
+
+extern u8 GetFlashFireStatus(struct DungeonEntity *pokemon);
+
+s32 WeightWeakTypePicker(struct DungeonEntity *user, struct DungeonEntity *target, u8 moveType)
+{
+ s32 weight = 1;
+ bool8 checkExposed = FALSE;
+ struct DungeonEntityData *userData;
+ struct DungeonEntityData *targetData;
+ u8 *targetTypes;
+ u8 *targetType;
+ u32 moveTypeOffset;
+ if (!EntityExists(target))
+ {
+ return 1;
+ }
+ if (moveType == TYPE_NORMAL || moveType == TYPE_FIGHTING)
+ {
+ checkExposed = TRUE;
+ }
+ userData = user->entityData;
+ targetData = target->entityData;
+ 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->exposedStatus)
+ {
+ effectiveness = 0;
+ gDungeonGlobalData->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 can cause type effectiveness to be calculated 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 maxHP = userData->maxHP;
+ if (maxHP < 0)
+ {
+ maxHP += 3;
+ }
+ if (maxHP >> 2 >= userData->HP)
+ {
+ weight *= 2;
+ }
+ }
+ if (moveType == TYPE_GRASS && HasAbility(user, ABILITY_OVERGROW))
+ {
+ s32 maxHP = userData->maxHP;
+ if (maxHP < 0)
+ {
+ maxHP += 3;
+ }
+ if (maxHP >> 2 >= userData->HP)
+ {
+ weight *= 2;
+ }
+ }
+ if (moveType == TYPE_BUG && HasAbility(user, ABILITY_SWARM))
+ {
+ s32 maxHP = userData->maxHP;
+ if (maxHP < 0)
+ {
+ maxHP += 3;
+ }
+ if (maxHP >> 2 >= userData->HP)
+ {
+ weight *= 2;
+ }
+ }
+ if (moveType == TYPE_FIRE && HasAbility(user, ABILITY_BLAZE))
+ {
+ s32 maxHP = userData->maxHP;
+ if (maxHP < 0)
+ {
+ maxHP += 3;
+ }
+ if (maxHP >> 2 >= userData->HP)
+ {
+ weight *= 2;
+ }
+ }
+ if (weight == 0)
+ {
+ return 2;
+ }
+ if (HasType(user, moveType))
+ {
+ weight *= 2;
+ }
+ targetTypes = targetData->types;
+ if (GetWeather(user) == WEATHER_SUNNY)
+ {
+ if (moveType == TYPE_FIRE)
+ {
+ weight *= 2;
+ }
+ else if (moveType == TYPE_WATER)
+ {
+ return 2;
+ }
+ }
+ if (gDungeonGlobalData->mudSportTurnsLeft != 0 && moveType == TYPE_ELECTRIC)
+ {
+ return 2;
+ }
+ if (gDungeonGlobalData->waterSportTurnsLeft != 0 && moveType == TYPE_FIRE)
+ {
+ return 2;
+ }
+ if (moveType == TYPE_ELECTRIC && userData->chargingStatus == CHARGING_STATUS_CHARGE)
+ {
+ weight *= 2;
+ }
+ if (weight > 2)
+ {
+ weight = 3;
+ }
+ return weight + 2;
+}