Files
pmd-red/src/dungeon_damage.c
T
2026-10-02 16:00:07 -04:00

1575 lines
58 KiB
C

#include "global.h"
#include "globaldata.h"
#include "dungeon_damage.h"
#include "constants/ability.h"
#include "structs/str_dungeon.h"
#include "dungeon_logic.h"
#include "dungeon_move.h"
#include "pokemon_types.h"
#include "dungeon_message.h"
#include "string_format.h"
#include "constants/dungeon_exit.h"
#include "constants/move_id.h"
#include "constants/monster.h"
#include "constants/residual_damage.h"
#include "constants/status.h"
#include "constants/type.h"
#include "constants/iq_skill.h"
#include "math.h"
#include "number_util.h"
#include "dungeon_random.h"
#include "dungeon_items.h"
#include "dungeon_info.h"
#include "dungeon_util.h"
#include "dungeon_config.h"
#include "dungeon_strings.h"
#include "dungeon_misc.h"
#include "dungeon_leveling.h"
#include "weather.h"
#include "game_options.h"
#include "dungeon_mon_sprite_render.h"
#include "constants/direction.h"
#include "constants/dungeon.h"
#include "dungeon_vram.h"
#include "dungeon_8041AD0.h"
#include "code_805D8C8.h"
#include "dungeon_map_access.h"
#include "dungeon_data.h"
#include "move_orb_effects_1.h"
#include "pokemon.h"
#include "position_util.h"
#include "random.h"
#include "sprite.h"
#include "exclusive_pokemon.h"
#include "hurl_orb.h"
#include "dungeon_mon_spawn.h"
#include "dungeon_mon_recruit.h"
#include "move_orb_actions_1.h"
#include "move_orb_effects_2.h"
#include "move_orb_effects_5.h"
#include "dungeon_tilemap.h"
#include "effect_main.h"
static bool8 HandleDealingDamageInternal_Async(Entity *attacker, Entity *target, struct DamageStruct *r5, bool32 isFalseSwipe, bool32 giveExp, s16 dungeonExitReason_, s32 arg8);
static bool8 sub_806E100(s48_16 *param_1, Entity *pokemon, Entity *target, u8 type, DamageStruct *dmgStruct);
static void ResetDamageCalcWorkingValues(void);
static void sub_806F63C(Entity *param_1);
static const u32 gUnknown_8106EFC[] = { 0, 0 };
static const s48_16 gUnknown_8106F04 = { 0x0, 0x10000 };
static const s48_16 gUnknown_8106F0C = { 0x0, 0x20000 };
static const s48_16 gUnknown_8106F14 = { 0x0, 0x18000 };
static const s48_16 gUnknown_8106F1C = { 0x0, 0x8000 };
static const s48_16 gUnknown_8106F24 = { 0x0, 0x3E70000 };
UNUSED static const s48_16 sUnusedModifier1 = { 0, 0xC000 };
UNUSED static const s48_16 sUnusedModifier2 = { 0x0, 0x17FFE };
static const s48_16 gUnknown_8106F3C = {0x0, 0x8000};
static const s48_16 gUnknown_8106F44 = {0x0, 0xE666};
static const s48_16 gUnknown_8106F4C = {0x0, 0x18000};
void HandleDealingDamage_Async(Entity *attacker, Entity *target, struct DamageStruct *dmgStruct, bool32 isFalseSwipe, bool32 giveExp, s16 dungeonExitReason_, bool32 arg8, s32 argC)
{
bool32 r9;
// Some compiler weirdness, because it won't match without creating dungeonExitReason_ again
s32 dungeonExitReason = dungeonExitReason_;
s32 returnDmg = 0;
if (HandleDealingDamageInternal_Async(attacker, target, dmgStruct, isFalseSwipe, giveExp, dungeonExitReason, argC))
return;
if (dmgStruct->tookNoDamage)
return;
if (!EntityIsValid(attacker) || !EntityIsValid(target))
return;
if (GetEntityType(attacker) != ENTITY_MONSTER || GetEntityType(target) != ENTITY_MONSTER)
return;
r9 = arg8;
if (CheckVariousStatuses(target) || GetEntInfo(target)->bideClassStatus.status != 0)
r9 = FALSE;
if (r9
&& abs(attacker->pos.x - target->pos.x) <= 1 && abs(attacker->pos.y - target->pos.y) <= 1
&& attacker != target
&& IsTypePhysical(dmgStruct->type)
&& GetEntInfo(target)->reflectClassStatus.status == STATUS_VITAL_THROW)
{
sub_8042730(target, attacker);
HandleHurlOrb(target, attacker);
}
if (GetEntInfo(target)->bideClassStatus.status == STATUS_ENRAGED) {
BoostOffensiveStat(attacker, target, gStatIndexAtkDef, 1);
}
if (!EntityIsValid(attacker) || !EntityIsValid(target))
return;
if (r9
&& attacker != target
&& abs(attacker->pos.x - target->pos.x) <= 1 && abs(attacker->pos.y - target->pos.y) <= 1)
{
bool32 isPhysical = IsTypePhysical(dmgStruct->type);
if (GetEntInfo(target)->reflectClassStatus.status == STATUS_COUNTER && isPhysical) {
sub_8041B18(target);
returnDmg += 4;
}
if (GetEntInfo(target)->reflectClassStatus.status == STATUS_MINI_COUNTER && isPhysical) {
sub_8041B18(target);
returnDmg += 1;
}
if (GetEntInfo(target)->reflectClassStatus.status == STATUS_MIRROR_COAT && !isPhysical) {
sub_8041B90(target);
returnDmg += 4;
}
if (AbilityIsActive(target, ABILITY_ROUGH_SKIN))
returnDmg += 2;
if (returnDmg) {
struct DamageStruct sp;
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80FCFA4);
sp.dmg = (dmgStruct->dmg * returnDmg) / 4;
sp.type = dmgStruct->type;
sp.residualDmgType = RESIDUAL_DAMAGE_COUNTERATTACK;
sp.typeEffectiveness = 2;
sp.isCrit = FALSE;
sp.unkE = 0;
sp.tookNoDamage = FALSE;
HandleDealingDamageInternal_Async(target, attacker, &sp, FALSE, giveExp, dungeonExitReason, argC);
}
}
if (!EntityIsValid(attacker) || !EntityIsValid(target))
return;
// Check trapping / contact abilities
if (r9
&& attacker != target
&& abs(attacker->pos.x - target->pos.x) <= 1 && abs(attacker->pos.y - target->pos.y) <= 1)
{
bool32 isPhysical = IsTypePhysical(dmgStruct->type);
EntityInfo *attackerInfo = GetEntInfo(attacker);
if (AbilityIsActive(target, ABILITY_ARENA_TRAP)
&& !MonsterIsType(attacker, TYPE_FLYING)
&& !AbilityIsActive(attacker, ABILITY_LEVITATE)
&& DungeonRandInt(100) < gArenaTrapActivateChance)
{
attackerInfo->abilityEffectFlags |= ABILITY_FLAG_ARENA_TRAP;
}
if (AbilityIsActive(target, ABILITY_SHADOW_TAG)
&& DungeonRandInt(100) < gShadowTagActivateChance)
{
attackerInfo->abilityEffectFlags |= ABILITY_FLAG_SHADOW_TAG;
}
if (AbilityIsActive(target, ABILITY_MAGNET_PULL)
&& MonsterIsType(attacker, TYPE_STEEL)
&& DungeonRandInt(100) < gMagnetPullActivateChance)
{
attackerInfo->abilityEffectFlags |= ABILITY_FLAG_MAGNET_PULL;
}
if (AbilityIsActive(target, ABILITY_STATIC)
&& isPhysical
&& DungeonRandInt(100) < gStaticActivateChance)
{
attackerInfo->abilityEffectFlags |= ABILITY_FLAG_STATIC;
}
if (AbilityIsActive(target, ABILITY_POISON_POINT)
&& DungeonRandInt(100) < gPoisonPointActivateChance)
{
attackerInfo->abilityEffectFlags |= ABILITY_FLAG_POISON_POINT;
}
if (AbilityIsActive(target, ABILITY_EFFECT_SPORE)
&& isPhysical
&& DungeonRandInt(100) < gEffectSporeActivateChance)
{
s32 rnd = DungeonRandInt(3);
if (rnd < 1)
attackerInfo->abilityEffectFlags |= ABILITY_FLAG_EFFECT_SPORE_PSN;
else if (rnd < 2)
attackerInfo->abilityEffectFlags |= ABILITY_FLAG_EFFECT_SPORE_PRLZ;
else
attackerInfo->abilityEffectFlags |= ABILITY_FLAG_EFFECT_SPORE_SLP;
}
if (AbilityIsActive(target, ABILITY_FLAME_BODY)
&& DungeonRandInt(100) < gFlameBodyActivateChance)
{
attackerInfo->abilityEffectFlags |= ABILITY_FLAG_FLAME_BODY;
}
if (AbilityIsActive(target, ABILITY_CUTE_CHARM)
&& isPhysical
&& DungeonRandInt(100) < gCuteCharmActivateChance)
{
attackerInfo->abilityEffectFlags |= ABILITY_FLAG_CUTE_CHARM;
}
if (AbilityIsActive(target, ABILITY_STENCH)
&& DungeonRandInt(100) < gStenchActivateChance)
{
attackerInfo->abilityEffectFlags |= ABILITY_FLAG_STENCH;
}
}
if (!EntityIsValid(attacker) || !EntityIsValid(target))
return;
// Destiny Bond
if (r9) {
EntityInfo *targetInfo = GetEntInfo(target);
if (targetInfo->leechSeedClassStatus.status == STATUS_DESTINY_BOND) {
Entity *destBondTarget = gDungeon->activePokemon[targetInfo->leechSeedClassStatus.unk8];
if (destBondTarget == NULL) {
targetInfo->leechSeedClassStatus.status = 0;
}
else if (GetEntInfo(destBondTarget)->dungeonSpriteId != targetInfo->leechSeedClassStatus.unk4) {
targetInfo->leechSeedClassStatus.status = 0;
}
else {
sub_8041D00(destBondTarget, target);
DealDamageToEntity_Async(destBondTarget, dmgStruct->dmg, RESIDUAL_DAMAGE_DESTINY_BOND, DUNGEON_EXIT_FAINTED_FROM_DESTINY_BOND);
}
}
}
}
static inline u32 ItemId(Item *item)
{
return item->id;
}
static bool8 HandleDealingDamageInternal_Async(Entity *attacker, Entity *target, struct DamageStruct *dmgStruct, bool32 isFalseSwipe, bool32 giveExp, s16 dungeonExitReason_, s32 arg8)
{
s32 hpBefore, hpChange;
EntityInfo *targetData;
s32 dungeonExitReason = dungeonExitReason_;
bool32 var_24 = FALSE;
bool32 r10 = FALSE;
s32 r8 = 0;
Tile *unkTile = NULL;
dmgStruct->tookNoDamage = FALSE;
if (GetEntityType(target) != ENTITY_MONSTER) {
dmgStruct->tookNoDamage = TRUE;
return FALSE;
}
targetData = GetEntInfo(target);
TrySendImmobilizeSleepEndMsg(attacker, target);
SetShopkeeperAggression(attacker, target);
if (GetEntityType(attacker) == ENTITY_MONSTER
&& GetEntInfo(attacker)->sureShotClassStatus.status == STATUS_SET_DAMAGE
&& dmgStruct->unkE == 0)
{
dmgStruct->dmg = gSetDamageDmgValue;
}
if (dungeonExitReason != DUNGEON_EXIT_FAINTED_FROM_PERISH_SONG && AbilityIsActive(target, ABILITY_STURDY) && dmgStruct->dmg == 9999) {
SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[1], target, 0);
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80FCA90);
sub_8042238(attacker, target);
dmgStruct->tookNoDamage = TRUE;
return FALSE;
}
if (targetData->frozenClassStatus.status == STATUS_FROZEN) {
SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[1], target, 0);
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80F9600);
sub_8042238(attacker, target);
dmgStruct->tookNoDamage = TRUE;
return FALSE;
}
if ((targetData->sleepClassStatus.status == STATUS_SLEEP || targetData->sleepClassStatus.status == STATUS_NAPPING || targetData->sleepClassStatus.status == STATUS_NIGHTMARE)
&& targetData->sleepClassStatus.turns == 0x7F)
{
WakeUpPokemon(target);
}
if ((AbilityIsActive(target, ABILITY_VOLT_ABSORB) && dmgStruct->type == TYPE_ELECTRIC)) {
HealTargetHP(attacker, target, dmgStruct->dmg, 0, 0);
dmgStruct->tookNoDamage = TRUE;
return FALSE;
}
else if (AbilityIsActive(target, ABILITY_WATER_ABSORB) && dmgStruct->type == TYPE_WATER) {
HealTargetHP(attacker, target, dmgStruct->dmg, 0, 0);
dmgStruct->tookNoDamage = TRUE;
return FALSE;
}
if (targetData->unk152 == 0) {
if (dmgStruct->isCrit) {
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80F9614);
}
switch (dmgStruct->typeEffectiveness) {
case EFFECTIVENESS_IMMUNE:
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80F9630);
break;
case EFFECTIVENESS_RESIST:
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80F9654);
break;
case EFFECTIVENESS_SUPER:
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80F9670);
break;
}
}
SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[0], attacker, 0);
SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[1], target, 0);
if (dmgStruct->dmg == 0) {
if (ShouldDisplayEntity(attacker) && ShouldDisplayEntity(target)) {
if (targetData->unk152 == 0) {
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80F9688);
}
sub_8042238(attacker, target);
}
else {
if (targetData->unk152 == 0) {
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80F9688);
}
DungeonWaitFrames_Async(0x1E, 0x18);
}
dmgStruct->tookNoDamage = TRUE;
return FALSE;
}
else if (dmgStruct->dmg == 9999) {
if (arg8 != 0 && ShouldDisplayEntity(target)) {
unkTile = GetTileAtEntitySafe(target);
DungeonWaitFrames_Async(0x14, 0x18);
unkTile->spawnOrVisibilityFlags.spawn |= SPAWN_FLAG_TRAP;
UpdateTrapsVisibility();
}
if (targetData->unk152 == 0) {
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80F96A8);
}
targetData->unkA0 = 999;
}
else {
const u8 *str;
gFormatArgs[0] = dmgStruct->dmg;
str = gResidualDamageMessages[dmgStruct->residualDmgType];
ASM_MATCH_TRICK(dmgStruct);
targetData->unkA0 += dmgStruct->dmg;
if (targetData->unkA0 > 999)
targetData->unkA0 = 999;
if (ShouldDisplayEntity(target)) {
if (dmgStruct->residualDmgType != RESIDUAL_DAMAGE_HUNGER) {
sub_803ED30(-dmgStruct->dmg, target, 1, -1);
}
if (targetData->unk152 == 0 && str != NULL) {
TryDisplayDungeonLoggableMessage3_Async(attacker, target, str);
}
}
else {
if (targetData->unk152 == 0 && str != NULL) {
TryDisplayDungeonLoggableMessage3_Async(attacker, target, str);
}
}
}
if ((dmgStruct->residualDmgType != RESIDUAL_DAMAGE_HUNGER || targetData->HP <= 1) && unkTile == NULL && ShouldDisplayEntity(target)) {
if ((attacker->pos.x != target->pos.x || attacker->pos.y != target->pos.y) && GetEntityType(attacker) == ENTITY_MONSTER) {
bool32 unkBool = FALSE;
if (targetData->isTeamLeader) {
if (gGameOptionsRef->damageTurn != 0 && !sub_806A458(target)) {
if (CanSeeTarget(target, attacker))
unkBool = TRUE;
else
unkBool = FALSE;
}
}
else {
unkBool = FALSE;
}
if (targetData->blinkerClassStatus.status == STATUS_BLINKER || targetData->blinkerClassStatus.status == STATUS_CROSS_EYED)
unkBool = FALSE;
if (unkBool) {
EntityInfo *info = GetEntInfo(target);
info->action.direction = (GetDirectionTowardsPosition(&target->pos, &attacker->pos) & DIRECTION_MASK);
}
TargetTileInFront(target);
}
sub_806CDD4(target, 6, GetEntInfo(target)->action.direction);
sub_80420E8(target, dmgStruct);
var_24 = TRUE;
}
// HP goes down
hpBefore = targetData->HP;
if (targetData->HP > dmgStruct->dmg)
targetData->HP -= dmgStruct->dmg;
else
targetData->HP = 0;
if (targetData->reflectClassStatus.status == STATUS_ENDURING) {
if (targetData->HP == 0) {
targetData->HP = 1;
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_8100548);
}
}
else if (isFalseSwipe == TRUE && targetData->HP == 0) {
targetData->HP = 1;
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_810056C);
}
hpChange = hpBefore - targetData->HP;
if (hpChange < 0)
hpChange = 0;
if (var_24 || unkTile != NULL)
DungeonWaitFrames_Async(0xA, 0x18);
if (targetData->HP != 0) {
if (var_24) {
sub_806CE68(target, 8);
}
if (HasHeldItem(target, ITEM_JOY_RIBBON) && hpChange > 0 && dmgStruct->dmg != 9999) {
AddExpPoints(attacker, target, hpChange);
}
if (unkTile != NULL)
{
unkTile->spawnOrVisibilityFlags.spawn &= ~(SPAWN_FLAG_TRAP);
UpdateTrapsVisibility();
}
return FALSE;
}
// 0 HP
if (targetData->invisibleClassStatus.status == STATUS_TRANSFORMED) {
SendTransformEndMessage(attacker, target);
}
if (unkTile != NULL) {
DungeonWaitFrames_Async(0x14, 0x18);
target->unk22 = 2;
DungeonWaitFrames_Async(0xA, 0x18);
unkTile->spawnOrVisibilityFlags.spawn &= ~(SPAWN_FLAG_TRAP);
UpdateTrapsVisibility();
}
else if (var_24) {
target->unk22 = 1;
DungeonWaitFrames_Async(0x1E, 0x18);
}
r8 = 1;
if (EntityIsValid(attacker) && GetEntityType(attacker) == ENTITY_MONSTER && attacker != target)
r8 = 0;
targetData->unk14C = 0;
SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[0], attacker, 0);
SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[1], target, 0);
if (dmgStruct->residualDmgType == RESIDUAL_DAMAGE_MOVE_FAIL || dmgStruct->residualDmgType == RESIDUAL_DAMAGE_RECOIL) {
if (targetData->isNotTeamMember) {
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80F9E44);
}
else {
DisplayDungeonLoggableMessageTrue_Async(attacker, gUnknown_80F9E44);
}
}
else if (targetData->isNotTeamMember)
{
if (targetData->monsterBehavior == BEHAVIOR_RESCUE_TARGET) {
DisplayDungeonLoggableMessageTrue_Async(attacker, gUnknown_80F9DF0[r8]);
}
else {
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80F9CC0[r8]);
}
}
else {
DungeonMon *recruitedMon = &gRecruitedPokemonRef->dungeonTeam[targetData->teamIndex];
if (targetData->isTeamLeader || (targetData->joinedAt.id == DUNGEON_JOIN_LOCATION_PARTNER && !gDungeon->unk644.canChangeLeader)) {
DisplayDungeonLoggableMessageTrue_Async(attacker, gUnknown_80F9CEC[r8]);
}
else if (IsExperienceLocked(targetData->joinedAt.id)) {
DisplayDungeonLoggableMessageTrue_Async(attacker, gUnknown_80F9DAC[r8]);
}
else if (targetData->monsterBehavior == BEHAVIOR_RESCUE_TARGET) {
DisplayDungeonLoggableMessageTrue_Async(attacker, gUnknown_80F9DF0[r8]);
}
else if (sub_806A58C(recruitedMon->recruitedPokemonId)) {
if (gDungeon->unk644.unk19 != 0) {
DisplayDungeonLoggableMessageTrue_Async(attacker, gUnknown_80F9D8C[r8]);
}
else {
DisplayDungeonLoggableMessageTrue_Async(attacker, gUnknown_80F9D84[r8]);
}
}
else {
DisplayDungeonLoggableMessageTrue_Async(attacker, gUnknown_80F9D28[r8]);
}
}
if (targetData->frozenClassStatus.status == STATUS_WRAP || targetData->frozenClassStatus.status == STATUS_WRAPPED) {
sub_8076CB4(targetData->unk9C);
}
// Try revive items
if (targetData->heldItem.flags & ITEM_FLAG_EXISTS) {
// This do/while 0 loop has no effect other than matching asm, and can be removed.
do
{
if (!(ItemSticky(&targetData->heldItem)) && !(ItemInShop(&targetData->heldItem)) && ItemId(&targetData->heldItem) == ITEM_REVIVER_ORB) {
sub_8042148(target);
target->unk22 = 0;
targetData->HP = targetData->maxHPStat;
targetData->unk158 = 0;
targetData->unk14C = 1;
targetData->belly = targetData->maxBelly;
if (targetData->isTeamLeader) {
gDungeon->unk644.emptyBellyAlert = 0;
gDungeon->unk644.itemHoldersIdentified = 0;
}
ZeroOutItem(&targetData->heldItem);
if (targetData->curseClassStatus.status == STATUS_SNATCH) {
EndCurseClassStatus(attacker, target, STATUS_SNATCH);
}
else if (targetData->curseClassStatus.status == STATUS_DECOY) {
EndCurseClassStatus(attacker, target, STATUS_DECOY);
}
sub_8078084(target);
ResetMonEntityData(targetData, 0);
sub_800DBBC();
sub_806A390(target);
sub_806CCB4(target, sub_806CEBC(target));
UpdateStatusIconFlags(target);
SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[1], target, 0);
DisplayDungeonLoggableMessageTrue_Async(attacker, gUnknown_80FD46C);
sub_806F63C(target);
return FALSE;
}
} while(0);
}
if (targetData->isTeamLeader)
{
Item *heldItem = &targetData->heldItem;
if (ItemExists(heldItem) && !(ItemSticky(heldItem)) && ItemId(heldItem) == ITEM_POSSESS_ORB ) {
s32 i;
Entity *teamMember;
for (i = 0; i < MAX_TEAM_MEMBERS; i++) {
teamMember = gDungeon->teamPokemon[i];
if (EntityIsValid(teamMember)
&& teamMember != target
&& CanSeeTarget(target, teamMember)
&& abs(teamMember->pos.x - target->pos.x) <= 1 && abs(teamMember->pos.y - target->pos.y) <= 1)
{
break;
}
}
if (i != MAX_TEAM_MEMBERS) {
DungeonPos monPos; // r4
sub_8042148(target);
monPos = teamMember->pos;
HandleFaint_Async(teamMember, DUNGEON_EXIT_WAS_POSSESSED, target);
sub_80694C0(target, monPos.x, monPos.y, 1);
UpdateEntityPixelPos(target, NULL);
target->unk22 = 0;
targetData->HP = targetData->maxHPStat;
sub_8042A24(target);
targetData->unk158 = 0;
targetData->unk14C = 1;
targetData->belly = targetData->maxBelly;
if (targetData->isTeamLeader) {
gDungeon->unk644.emptyBellyAlert = 0;
gDungeon->unk644.itemHoldersIdentified = 0;
}
ZeroOutItem(heldItem);
if (targetData->curseClassStatus.status == STATUS_SNATCH) {
EndCurseClassStatus(attacker, target, STATUS_SNATCH);
}
else if (targetData->curseClassStatus.status == STATUS_DECOY) {
EndCurseClassStatus(attacker, target, STATUS_DECOY);
}
sub_8078084(target);
ResetMonEntityData(targetData, 0);
sub_800DBBC();
sub_806A390(target);
sub_806CCB4(target, sub_806CEBC(target));
UpdateStatusIconFlags(target);
SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[0], target, 0);
SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[1], teamMember, 0);
DisplayDungeonLoggableMessageTrue_Async(attacker, gUnknown_80FD484);
sub_806F63C(target);
return FALSE;
}
}
}
if (IqSkillIsEnabled(target, IQ_ITEM_MASTER)) {
s32 i; // r3
Item *reviverSeed = NULL; // r4
if (ItemExists(&targetData->heldItem) && !ItemSticky(&targetData->heldItem) && ItemId(&targetData->heldItem) == ITEM_REVIVER_SEED)
reviverSeed = &targetData->heldItem;
if (reviverSeed == NULL && !targetData->isNotTeamMember) {
for (i = 0; i < INVENTORY_SIZE; i++) {
Item *bagItem = &gTeamInventoryRef->teamItems[i];
if (!(bagItem->flags & ITEM_FLAG_EXISTS))
continue;
if (bagItem->flags & ITEM_FLAG_STICKY)
continue;
if (bagItem->id == ITEM_REVIVER_SEED) {
reviverSeed = bagItem;
break;
}
}
}
if (reviverSeed != NULL) {
sub_8042148(target);
CreateItemWithStickyChance(reviverSeed, ITEM_PLAIN_SEED, FORCE_STICKY_NEVER);
target->unk22 = 0;
targetData->HP = targetData->maxHPStat;
targetData->unk158 = 0;
targetData->unk14C = 1;
targetData->belly = targetData->maxBelly;
if (targetData->isTeamLeader) {
gDungeon->unk644.emptyBellyAlert = 0;
gDungeon->unk644.itemHoldersIdentified = 0;
}
if (targetData->curseClassStatus.status == STATUS_SNATCH) {
EndCurseClassStatus(attacker, target, STATUS_SNATCH);
}
else if (targetData->curseClassStatus.status == STATUS_DECOY) {
EndCurseClassStatus(attacker, target, STATUS_DECOY);
}
sub_8078084(target);
ResetMonEntityData(targetData, 0);
sub_800DBBC();
sub_806A390(target);
sub_806CCB4(target, sub_806CEBC(target));
UpdateStatusIconFlags(target);
SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[1], target, 0);
DisplayDungeonLoggableMessageTrue_Async(attacker, gUnknown_80FD46C);
sub_806F63C(target);
return FALSE;
}
}
if (!targetData->isTeamLeader && ItemExists(&targetData->heldItem)) {
SpawnDroppedItemWrapper(target, &target->pos, &targetData->heldItem);
ZeroOutItem(&targetData->heldItem);
}
if (targetData->bossFlag) {
gDungeon->unk644.bossSongIndex = STOP_BGM;
}
// Give exp
r10 = FALSE;
if (GetEntityType(attacker) == ENTITY_MONSTER) {
EntityInfo *attackerData = GetEntInfo(attacker);
s32 exp = CalculateEXPGain(targetData->id, targetData->level);
switch (targetData->expMultiplier) {
case EXP_BOOSTED:
exp *= 3;
exp /= 2;
break;
case EXP_HALVED:
exp /= 2;
break;
}
if (exp == 0)
exp = 1;
if (attackerData->isTeamLeader) {
SetMonSeenFlag(targetData->id);
}
if (targetData->grudge) {
attackerData->unk14B = 1;
}
if (giveExp == TRUE) {
if (!attackerData->isNotTeamMember) {
if (targetData->isNotTeamMember) {
s32 i;
AddExpPoints(attacker, attacker, exp);
for (i = 0; i < MAX_TEAM_MEMBERS; i++) {
Entity *teamMember = gDungeon->teamPokemon[i];
if (EntityIsValid(teamMember) && teamMember != attacker) {
AddExpPoints(attacker, teamMember, exp);
}
}
r10 = TRUE;
}
}
else {
if (DoEnemiesEvolveWhenKOed(gDungeon->unk644.dungeonLocation.id)) {
attackerData->unk149 = 1;
gDungeon->unkD = 1;
}
}
}
}
if (EntityIsValid(attacker) && GetEntityType(attacker) == ENTITY_MONSTER && !GetEntInfo(attacker)->isTeamLeader)
r10 = FALSE;
if (r10) {
struct unkStruct_8069D4C sp;
sub_8069D4C(&sp, target);
if (TryRecruitMonster(attacker, target)) {
if (!MonsterJoinSequence_Async(attacker, target, &sp)) {
HandleFaint_Async(target, DUNGEON_EXIT_LEFT_WITHOUT_BEING_BEFRIENDED, attacker);
}
else {
gUnknown_202F221 = 1;
}
}
else {
HandleFaint_Async(target, dungeonExitReason, attacker);
}
}
else {
HandleFaint_Async(target, dungeonExitReason, attacker);
}
return TRUE;
}
static bool8 sub_806E100(s48_16 *param_1, Entity *pokemon, Entity *target, u8 type, DamageStruct *dmgStruct)
{
bool8 torrentFlag;
bool8 overgrowFlag;
bool8 swarmFlag;
bool8 blazeFlag;
bool8 torrentVisualFlag;
bool8 overgrowVisualFlag;
bool8 swarmVisualFlag;
bool8 blazeVisualFlag;
bool8 bVar4;
EntityInfo *targetInfo;
s32 effectiveness;
s32 index;
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);
dmgStruct->isCrit = 0;
dmgStruct->unkE = 0;
hasWonderGuard = FALSE;
if (!EntityIsValid(target)) {
return TRUE;
}
else {
if ((type == TYPE_NORMAL) || (type == TYPE_FIGHTING)) {
normalOrFightingType = TRUE;
}
if ((AbilityIsActive(target, ABILITY_WONDER_GUARD)) && (type != TYPE_NONE)) {
hasWonderGuard = TRUE;
}
dmgStruct->typeEffectiveness = EFFECTIVENESS_NEUTRAL;
for (index = 0; index < 2; index++) {
const s48_16 *local_48[] = {&gUnknown_8106F3C, &gUnknown_8106F44, &gUnknown_8106F04, &gUnknown_8106F4C};
if (F48_16_IsZero(param_1)) break;
if (((normalOrFightingType) && (targetInfo->types[index] == TYPE_GHOST)) && (targetInfo->exposed == FALSE)) {
effectiveness = EFFECTIVENESS_IMMUNE;
gDungeon->damageCalcWorkingValues.pokemonExposed = TRUE;
}
else {
effectiveness = gTypeEffectivenessChart[type][targetInfo->types[index]];
}
if (effectiveness != EFFECTIVENESS_NEUTRAL) {
F48_16_SMul(param_1,param_1,local_48[effectiveness]);
}
local_38[index] = effectiveness;
gDungeon->damageCalcWorkingValues.unk13C[index] = effectiveness;
}
dmgStruct->typeEffectiveness = gEffectivenessChart[local_38[0]][local_38[1]];
bVar4 = TRUE;
if ((dmgStruct->typeEffectiveness != 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->damageCalcWorkingValues.unk16D = TRUE;
F48_16_SMul(param_1,param_1, &gUnknown_8106F1C);
}
if ((type == TYPE_FIRE) && (GetFlashFireStatus(target) != FLASH_FIRE_STATUS_NONE)) {
gDungeon->damageCalcWorkingValues.fill16E[0] = TRUE;
FP48_16_FromS32(param_1,0);
dmgStruct->typeEffectiveness = EFFECTIVENESS_IMMUNE;
dmgStruct->isCrit = 0;
dmgStruct->unkE = 1;
bVar4 = FALSE;
}
if ((type == TYPE_GROUND) && (AbilityIsActive(target, ABILITY_LEVITATE))) {
gDungeon->damageCalcWorkingValues.fill16E[1] = TRUE;
FP48_16_FromS32(param_1,0);
dmgStruct->typeEffectiveness = EFFECTIVENESS_IMMUNE;
dmgStruct->isCrit = 0;
dmgStruct->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->damageCalcWorkingValues.fill16E[2] = TRUE;
F48_16_SMul(param_1,param_1, &gUnknown_8106F0C);
}
if (torrentVisualFlag) {
sub_80428EC(pokemon);
TryDisplayDungeonLoggableMessage3_Async(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->damageCalcWorkingValues.fill16E[3] = TRUE;
F48_16_SMul(param_1,param_1, &gUnknown_8106F0C);
}
if (overgrowVisualFlag) {
sub_80428D8(pokemon);
TryDisplayDungeonLoggableMessage3_Async(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->damageCalcWorkingValues.fill16E[4] = TRUE;
F48_16_SMul(param_1,param_1, &gUnknown_8106F0C);
}
if (swarmVisualFlag) {
sub_8042978(pokemon);
TryDisplayDungeonLoggableMessage3_Async(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->damageCalcWorkingValues.fill16E[5] = TRUE;
F48_16_SMul(param_1,param_1, &gUnknown_8106F0C);
}
if (blazeVisualFlag) {
sub_804298C(pokemon);
TryDisplayDungeonLoggableMessage3_Async(pokemon,target,gUnknown_80FEDE8);
}
}
if (!(F48_16_IsZero(param_1)) && (MonsterIsType(pokemon, type))) {
gDungeon->damageCalcWorkingValues.fill16E[6] = TRUE;
F48_16_SMul(param_1,param_1, &gUnknown_8106F14);
}
weather = GetApparentWeather(pokemon);
if (weather == WEATHER_SUNNY) {
if (type == TYPE_FIRE) {
gDungeon->damageCalcWorkingValues.unk16C = TRUE;
F48_16_SMul(param_1,param_1, &gUnknown_8106F14);
}
else if (type == TYPE_WATER) {
gDungeon->damageCalcWorkingValues.unk16C = TRUE;
F48_16_SMul(param_1,param_1, &gUnknown_8106F1C);
}
}
if (weather == WEATHER_RAIN) {
if (type == TYPE_FIRE) {
gDungeon->damageCalcWorkingValues.unk16B = TRUE;
F48_16_SMul(param_1,param_1, &gUnknown_8106F1C);
}
else if (type == TYPE_WATER) {
gDungeon->damageCalcWorkingValues.unk16B = TRUE;
F48_16_SMul(param_1,param_1, &gUnknown_8106F14);
}
}
if ((weather == WEATHER_CLOUDY) && (type != TYPE_NORMAL)) {
static const s48_16 gUnknown_8106F64 = {0x0, 0xC000};
F48_16_SMul(param_1,param_1, &gUnknown_8106F64);
gDungeon->damageCalcWorkingValues.unk16A = TRUE;
}
if (((gDungeon->weather.mudSportTurns != 0) || (weather == WEATHER_FOG)) && (type == TYPE_ELECTRIC)) {
gDungeon->damageCalcWorkingValues.fill16E[7] = TRUE;
F48_16_SMul(param_1,param_1, &gUnknown_8106F1C);
}
if ((gDungeon->weather.waterSportTurns != 0) && (type == TYPE_FIRE)) {
gDungeon->damageCalcWorkingValues.fill16E[8] = TRUE;
F48_16_SMul(param_1,param_1, &gUnknown_8106F1C);
}
if ((type == TYPE_ELECTRIC) && (pokemonInfo->bideClassStatus.status == STATUS_CHARGING)) {
gDungeon->damageCalcWorkingValues.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;
s32 i;
bool8 checkExposed = FALSE;
EntityInfo *userData;
EntityInfo *targetData;
if (!EntityIsValid(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;
for (i = 0; i < 2; i++) {
s32 effectiveness;
u32 typeEffectivenessMultipliers[NUM_EFFECTIVENESS] = {0, 1, 2, 4};
if (checkExposed && targetData->types[i] == TYPE_GHOST && !targetData->exposed) {
effectiveness = 0;
gDungeon->damageCalcWorkingValues.pokemonExposed = TRUE;
}
else {
effectiveness = gTypeEffectivenessChart[moveType][targetData->types[i]];
}
if (weight == 0)
break;
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;
}
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 / 4 >= userData->HP)
weight *= 2;
}
if (moveType == TYPE_GRASS && AbilityIsActive(user, ABILITY_OVERGROW)) {
s32 maxHPStat = userData->maxHPStat;
if (maxHPStat / 4 >= userData->HP)
weight *= 2;
}
if (moveType == TYPE_BUG && AbilityIsActive(user, ABILITY_SWARM)) {
s32 maxHPStat = userData->maxHPStat;
if (maxHPStat / 4 >= userData->HP)
weight *= 2;
}
if (moveType == TYPE_FIRE && AbilityIsActive(user, ABILITY_BLAZE)) {
s32 maxHPStat = userData->maxHPStat;
if (maxHPStat / 4 >= userData->HP)
weight *= 2;
}
if (weight == 0)
return 2;
if (MonsterIsType(user, moveType))
weight *= 2;
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 >= 3)
weight = 3;
return weight + 2;
}
static void ApplyAtkDefStatBoosts(Entity *attacker, Entity *target, u8 moveType, s32 *atkStat, s32 *defStat, s32 rand)
{
bool32 isNotEnemy;
s32 atkMultiplier = 1;
s32 atkDivisor = 1;
s32 defMultiplier = 1;
s32 defDivisor = 1;
s32 splitIndex = (!IsTypePhysical(moveType)) ? 1 : 0;
if (AbilityIsActive(attacker, ABILITY_GUTS)) {
EntityInfo *entInfo = GetEntInfo(attacker);
bool8 gutsBoost = MonsterHasNegativeStatus(attacker);
bool8 visFlags_attacker_1 = SetVisualFlags(entInfo,1,gutsBoost);
if (gutsBoost) {
atkMultiplier = 2;
}
if (visFlags_attacker_1) {
sub_80428B0(attacker);
TryDisplayDungeonLoggableMessage3_Async(attacker,target, gUnknown_80FEE04); // Guts boosted its power
}
}
if ((AbilityIsActive(attacker, ABILITY_HUGE_POWER)) || (AbilityIsActive(attacker, ABILITY_PURE_POWER))) {
EntityInfo *entInfo = GetEntInfo(attacker);
bool32 hugePowerBoost = (rand < 33 && splitIndex == 0);
bool8 visFlags_attacker_2 = SetVisualFlags(entInfo,0x100,hugePowerBoost);
if (hugePowerBoost) {
atkMultiplier *= 3;
atkDivisor *= 2;
}
if (visFlags_attacker_2) {
sub_80428C4(attacker);
TryDisplayDungeonLoggableMessage3_Async(attacker,target, gUnknown_80FEE2C); // It's special ability boosted Attack
}
}
if ((AbilityIsActive(attacker, ABILITY_HUSTLE)) && (splitIndex == 0)) {
atkMultiplier *= 3;
atkDivisor *= 2;
}
if (GetEntInfo(attacker)->isNotTeamMember) {
isNotEnemy = FALSE;
}
else {
isNotEnemy = TRUE;
}
if ((AbilityIsActive(attacker, ABILITY_PLUS)) && (splitIndex == 1) && gDungeon->minusIsActive[isNotEnemy]) {
atkMultiplier *= 15;
atkDivisor *= 10;
}
if ((AbilityIsActive(attacker, ABILITY_MINUS)) && (splitIndex == 1) && gDungeon->plusIsActive[isNotEnemy]) {
atkMultiplier *= 15;
atkDivisor *= 10;
}
if (AbilityIsActive(target, ABILITY_INTIMIDATE) && (splitIndex == 0)) {
atkMultiplier *= 4;
atkDivisor *= 5;
}
if ((AbilityIsActive(target, ABILITY_MARVEL_SCALE)) && (splitIndex == 0)) {
EntityInfo *targetInfo = GetEntInfo(target);
bool8 hasNegStatus_target = MonsterHasNegativeStatus(target);
bool8 visFlags_target = SetVisualFlags(targetInfo, 8, hasNegStatus_target);
if (hasNegStatus_target) {
defMultiplier *= 3;
defDivisor *= 2;
}
if (visFlags_target) {
sub_8042940(target);
TryDisplayDungeonLoggableMessage3_Async(attacker,target, gUnknown_80FEE54); // Its special ability quickened attacks!
}
}
*atkStat *= atkMultiplier;
*defStat *= defMultiplier;
if (atkDivisor != 1) {
*atkStat = *atkStat / atkDivisor;
}
if (defDivisor != 1) {
*defStat = *defStat / defDivisor;
}
}
static inline void SetDamageOne(struct DamageStruct *dmgStruct, u8 moveType)
{
dmgStruct->dmg = 1;
dmgStruct->residualDmgType = RESIDUAL_DAMAGE_REGULAR;
dmgStruct->typeEffectiveness = EFFECTIVENESS_NEUTRAL;
dmgStruct->type = moveType;
dmgStruct->isCrit = FALSE;
dmgStruct->unkE = 0;
dmgStruct->tookNoDamage = FALSE;
}
void CalcDamage(Entity *attacker, Entity *target, u8 moveType, s32 movePower, s32 critChance, struct DamageStruct *dmgStruct, s24_8 arg8, u16 moveId, bool8 arg_10)
{
EntityInfo *attackerInfo = GetEntInfo(attacker);
EntityInfo *targetInfo = GetEntInfo(target);
s32 splitIndex = (!IsTypePhysical(moveType)) ? 1 : 0;
ResetDamageCalcWorkingValues();
if (!attackerInfo->isTeamLeader && FixedPointToInt(attackerInfo->belly) == 0) {
SetDamageOne(dmgStruct, moveType);
}
else if (moveId == MOVE_REGULAR_ATTACK && AbilityIsActive(target, ABILITY_WONDER_GUARD)) {
SetDamageOne(dmgStruct, moveType);
}
else {
s32 atkStatStage, defStatStage;
s24_8 statCalc;
s32 atkStat, defStat;
s32 rand;
s32 r6;
s48_16 unkSp1;
s48_16 unkSp2;
s48_16 unkSp3;
s48_16 unkSp4;
s48_16 unkSp5;
s48_16 unkSp6;
s48_16 unkSp7;
s48_16 unkSp8;
s48_16 unkSp9;
s48_16 unkSp10;
s48_16 unkSp11;
bool8 r5;
dmgStruct->type = moveType;
gDungeon->damageCalcWorkingValues.unk134 = moveType;
gDungeon->damageCalcWorkingValues.unk138 = splitIndex;
atkStatStage = attackerInfo->offensiveStages[splitIndex];
if (arg_10 && moveType == TYPE_FIRE) {
atkStatStage += attackerInfo->flashFireBoost;
gDungeon->damageCalcWorkingValues.rawDamageFormulaValues[2] = attackerInfo->flashFireBoost;
}
if (attackerInfo->apparentID == MONSTER_DEOXYS_ATTACK) {
atkStatStage += 2;
}
if (attackerInfo->apparentID == MONSTER_DEOXYS_DEFENSE) {
atkStatStage -= 2;
}
if (attackerInfo->apparentID == MONSTER_DEOXYS_SPEED) {
atkStatStage -= 2;
}
if (atkStatStage < 0) atkStatStage = 0;
if (atkStatStage > 20) atkStatStage = 20;
gDungeon->damageCalcWorkingValues.offensiveStages[0] = atkStatStage;
gDungeon->damageCalcWorkingValues.rawDamageFormulaValues[0] = attackerInfo->atk[splitIndex] + movePower;
statCalc = s24_8_mul(IntToF248(attackerInfo->atk[splitIndex] + movePower), gAtkStatStageMultipliers[atkStatStage]);
statCalc = s24_8_mul(statCalc, attackerInfo->offensiveMultipliers[splitIndex]);
atkStat = F248ToInt(statCalc);
defStatStage = targetInfo->defensiveStages[splitIndex];
if (splitIndex == 0 && targetInfo->bideClassStatus.status == STATUS_SKULL_BASH) {
gDungeon->damageCalcWorkingValues.unk17A = 1;
defStatStage++;
}
if (targetInfo->apparentID == MONSTER_DEOXYS_ATTACK) {
defStatStage -= 2;
}
if (targetInfo->apparentID == MONSTER_DEOXYS_DEFENSE) {
defStatStage += 2;
}
if (targetInfo->apparentID == MONSTER_DEOXYS_SPEED) {
defStatStage -= 2;
}
if (defStatStage < 0) defStatStage = 0;
if (defStatStage > 20) defStatStage = 20;
gDungeon->damageCalcWorkingValues.offensiveStages[1] = defStatStage;
gDungeon->damageCalcWorkingValues.rawDamageFormulaValues[1] = targetInfo->def[splitIndex];
statCalc = s24_8_mul(IntToF248(targetInfo->def[splitIndex]), gDefStatStageMultipliers[defStatStage]);
statCalc = s24_8_mul(statCalc, targetInfo->defensiveMultipliers[splitIndex]);
defStat = F248ToInt(statCalc);
rand = DungeonRandInt(100);
if (splitIndex == 0) {
if (HasHeldItem(attacker, ITEM_POWER_BAND)) {
atkStat += gPowerBandBoost;
gDungeon->damageCalcWorkingValues.atkItemBoost += gPowerBandBoost;
}
if (HasHeldItem(attacker, ITEM_MUNCH_BELT)) {
atkStat += gMunchBeltBoost;
gDungeon->damageCalcWorkingValues.atkItemBoost += gMunchBeltBoost;
}
if (arg_10 && HasHeldItem(target, ITEM_DEF_SCARF)) {
defStat += gDefScarfBoost;
gDungeon->damageCalcWorkingValues.defItemBoost += gDefScarfBoost;
}
}
else {
if (arg_10 && HasHeldItem(target, ITEM_ZINC_BAND)) {
defStat += gZincBandBoost;
gDungeon->damageCalcWorkingValues.spDefItemBoost += gZincBandBoost;
}
if (HasHeldItem(attacker, ITEM_SPECIAL_BAND)) {
atkStat += gSpecialBandBoost;
gDungeon->damageCalcWorkingValues.spAtkItemBoost += gSpecialBandBoost;
}
if (HasHeldItem(attacker, ITEM_MUNCH_BELT)) {
atkStat += gMunchBeltBoost;
gDungeon->damageCalcWorkingValues.spAtkItemBoost += gMunchBeltBoost;
}
}
gDungeon->damageCalcWorkingValues.rawDamageFormulaValues[3] = atkStat;
gDungeon->damageCalcWorkingValues.rawDamageFormulaValues[4] = defStat;
if (atkStat < 0) atkStat = 0;
if (atkStat >= 999) atkStat = 999;
attackerInfo->previousVisualFlags &= ~(0x100);
attackerInfo->visualFlags &= ~(0x100);
ApplyAtkDefStatBoosts(attacker, target, moveType, &atkStat, &defStat, rand);
FP48_16_FromS32(&unkSp1, atkStat - defStat);
FP48_16_FromS32(&unkSp2, 8);
F48_16_SDiv(&unkSp1, &unkSp1, &unkSp2);
if (!attackerInfo->isNotTeamMember) {
FP48_16_FromS32(&unkSp2, attackerInfo->level);
unkSp3.hi = 0;
unkSp3.lo = 0xAAAA;
F48_16_SMul(&unkSp2, &unkSp2, &unkSp3);
r6 = (attackerInfo->level * 2) / 3;
}
else {
s32 unkAtkStat2 = attackerInfo->atk[splitIndex];
s32 unkDefStat2 = 1;
ApplyAtkDefStatBoosts(attacker, target, moveType, &unkAtkStat2, &unkDefStat2, rand);
FP48_16_FromS32(&unkSp2, unkAtkStat2);
FP48_16_FromS32(&unkSp3, 3);
F48_16_SDiv(&unkSp2, &unkSp2, &unkSp3);
r6 = unkAtkStat2 / 3;
}
FP48_16_Add(&unkSp4, &unkSp1, &unkSp2);
gDungeon->damageCalcWorkingValues.rawDamageFormulaValues[5] = r6;
gDungeon->damageCalcWorkingValues.rawDamageFormulaValues[6] = FP48_16_ToS32(&unkSp4);
unkSp5 = unkSp4;
unkSp7 = unkSp4;
F48_16_SMul(&unkSp5, &unkSp5, &unkSp5);
unkSp6.hi = 0;
unkSp6.lo = 0xCCC;
F48_16_SMul(&unkSp5, &unkSp5, &unkSp6);
FP48_16_FromS32(&unkSp6, 2);
F48_16_SMul(&unkSp7, &unkSp7, &unkSp6);
FP48_16_FromS32(&unkSp6, defStat);
FP48_16_Subtract(&unkSp7, &unkSp7, &unkSp6);
FP48_16_FromS32(&unkSp6, 10);
FP48_16_Add(&unkSp7, &unkSp7, &unkSp6);
FP48_16_Add(&unkSp8, &unkSp5, &unkSp7);
if (FP48_16_SLessThan(&gUnknown_8106F24, &unkSp8)) {
unkSp8 = gUnknown_8106F24;
}
if (FP48_16_SLessThan(&unkSp8, &gUnknown_8106F04)) {
unkSp8 = gUnknown_8106F04;
}
r5 = sub_806E100(&unkSp9, attacker, target, moveType, dmgStruct);
if (moveType == TYPE_FIRE) {
s32 flashFireStatus = GetFlashFireStatus(target);
if (flashFireStatus != FLASH_FIRE_STATUS_NONE && targetInfo->unk152 == 0 && arg_10) {
targetInfo->unk152 = 1;
SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[1], target, 0);
if (flashFireStatus == FLASH_FIRE_STATUS_MAXED) {
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80FAE00); // Fire moves won't become stronger!
}
else {
TryDisplayDungeonLoggableMessage3_Async(attacker, target, gUnknown_80FADD8); // m1 used Flash Fire to absorb fire!
}
}
}
if (arg_10) {
if (splitIndex == 0 && targetInfo->reflectClassStatus.status == STATUS_REFLECT) {
sub_8041B74(target);
F48_16_SMul(&unkSp9, &unkSp9, &gUnknown_8106F1C);
gDungeon->damageCalcWorkingValues.unk166 = 1;
}
if (splitIndex == 1 && targetInfo->reflectClassStatus.status == STATUS_LIGHT_SCREEN) {
sub_8041B5C(target);
F48_16_SMul(&unkSp9, &unkSp9, &gUnknown_8106F1C);
gDungeon->damageCalcWorkingValues.unk167 = 1;
}
}
// Check crit
if (!AbilityIsActive(target, ABILITY_BATTLE_ARMOR) && !AbilityIsActive(target, ABILITY_SHELL_ARMOR)) {
s32 critOdds;
if (attackerInfo->isNotTeamMember) {
critOdds = 0;
}
else {
critOdds = critChance;
}
if (attackerInfo->sureShotClassStatus.status == STATUS_FOCUS_ENERGY) {
critOdds = 999;
gDungeon->damageCalcWorkingValues.unk168 = 1;
}
else {
if (HasHeldItem(attacker, ITEM_SCOPE_LENS)) {
critOdds += gCritOddsScopeLensPatsyBand;
gDungeon->damageCalcWorkingValues.unk164 = 1;
}
if (HasHeldItem(target, ITEM_PATSY_BAND)) {
critOdds += gCritOddsScopeLensPatsyBand;
gDungeon->damageCalcWorkingValues.unk165 = 1;
}
if (r5 && IqSkillIsEnabled(attacker, IQ_TYPE_ADVANTAGE_MASTER)) {
critOdds = gCritOddsIqAdvantageMaster;
gDungeon->damageCalcWorkingValues.unk169 = 1;
}
}
if (DungeonRandInt(100) < critOdds) {
F48_16_SMul(&unkSp9, &unkSp9, &gUnknown_8106F14);
dmgStruct->isCrit = TRUE;
}
}
gDungeon->damageCalcWorkingValues.unk154 = FP48_16_ToS32(&unkSp8);
F48_16_SMul(&unkSp8, &unkSp8, &unkSp9);
{
// Ugly hack needed to match
#ifdef NONMATCHING
s24_8 arg8_Match;
#else
register s24_8 arg8_Match asm("r2");
#endif // NONMATCHING
gDungeon->damageCalcWorkingValues.unk15C = arg8_Match = arg8;
FP48_16_FromF248(&unkSp10, arg8_Match);
F48_16_SMul(&unkSp8, &unkSp8, &unkSp10);
}
// ALSO needed to match. unk694 chosen randomly and it worked with matching.
ASM_MATCH_TRICK(gDungeon->unk644.unk50);
gDungeon->damageCalcWorkingValues.unk150 = FP48_16_ToS32(&unkSp8);
{
s32 rnd = DungeonRandInt(0x4000);
unkSp9.hi = 0;
unkSp9.lo = 0xE000 + rnd;
}
F48_16_SMul(&unkSp8, &unkSp8, &unkSp9);
FP48_16_FromS32(&unkSp11, 100);
F48_16_SMul(&unkSp9, &unkSp11, &unkSp9);
gDungeon->damageCalcWorkingValues.unk158 = FP48_16_ToS32(&unkSp9);
dmgStruct->dmg = FP48_16_ToS32(&unkSp8);
dmgStruct->residualDmgType = RESIDUAL_DAMAGE_REGULAR;
if (dmgStruct->dmg == 0) {
dmgStruct->isCrit = FALSE;
}
}
}
void sub_806F2BC(Entity *attacker, Entity *target, u8 moveType, s32 a2, struct DamageStruct *dmgStruct)
{
s48_16 unkSp1;
s48_16 unkSp2;
s32 a2New = a2;
if (a2New <= 0) a2New = 1;
if (a2New > 999) a2New = 999;
dmgStruct->type = moveType;
sub_806E100(&unkSp1, attacker, target, moveType, dmgStruct);
FP48_16_FromS32(&unkSp2, a2New);
F48_16_SMul(&unkSp2, &unkSp2, &unkSp1);
dmgStruct->dmg = FP48_16_ToS32(&unkSp2);
dmgStruct->residualDmgType = RESIDUAL_DAMAGE_REGULAR;
}
void DealDamageToEntity_Async(Entity *entity, s32 dmg, s32 residualDmgType, s32 dungeonExitReason_)
{
Entity spEntity;
struct DamageStruct dmgStruct;
s32 dungeonExitReason = (s16) dungeonExitReason_;
sub_80457DC(&spEntity);
dmgStruct.dmg = dmg;
dmgStruct.typeEffectiveness = EFFECTIVENESS_NEUTRAL;
dmgStruct.type = TYPE_NONE;
dmgStruct.residualDmgType = residualDmgType;
dmgStruct.isCrit = FALSE;
dmgStruct.unkE = 0;
dmgStruct.tookNoDamage = FALSE;
HandleDealingDamage_Async(&spEntity, entity, &dmgStruct, FALSE, FALSE, dungeonExitReason, FALSE, 0);
}
void sub_806F370_Async(Entity *pokemon, Entity *target, s32 dmg, s32 giveExp, bool8 *tookNoDamage, u8 moveType, s16 dungeonExitReason_, s32 residualDmgType, s32 arg_10, s32 arg_14)
{
s32 i;
struct DamageStruct dmgStruct;
s32 dungeonExitReason = dungeonExitReason_;
s32 dmgNew = dmg;
dmgStruct.typeEffectiveness = EFFECTIVENESS_NEUTRAL;
dmgStruct.residualDmgType = residualDmgType;
dmgStruct.type = moveType;
dmgStruct.isCrit = FALSE;
dmgStruct.unkE = 0;
if (moveType != TYPE_NONE) {
s32 typeEffectiveness[2];
EntityInfo *targetInfo = GetEntInfo(target);
for (i = 0; i < 2; i++) {
s32 effectiv = gTypeEffectivenessChart[moveType][targetInfo->types[i]];
typeEffectiveness[i] = effectiv;
}
if (AbilityIsActive(target, ABILITY_WONDER_GUARD)) {
if (gEffectivenessChart[typeEffectiveness[0]][typeEffectiveness[1]] != EFFECTIVENESS_SUPER) {
dmgNew = 0;
}
}
}
dmgStruct.dmg = dmgNew;
if (dmgNew == 0) {
dmgStruct.tookNoDamage = TRUE;
}
else {
dmgStruct.tookNoDamage = FALSE;
}
HandleDealingDamage_Async(pokemon, target, &dmgStruct, FALSE, giveExp, dungeonExitReason, arg_10, arg_14);
if (tookNoDamage != NULL) {
*tookNoDamage = dmgStruct.tookNoDamage;
}
}
void SetShopkeeperAggression(Entity *pokemon, Entity *target)
{
EntityInfo *info;
info = GetEntInfo(target);
if(info->shopkeeper != SHOPKEEPER_MODE_NORMAL)
{
if(GetEntityType(pokemon) == ENTITY_MONSTER)
{
if(GetEntInfo(pokemon)->isNotTeamMember)
{
info->shopkeeper = SHOPKEEPER_MODE_ATTACK_ENEMIES;
}
else
{
info->shopkeeper = SHOPKEEPER_MODE_ATTACK_TEAM;
}
}
}
}
void sub_806F480(Entity *pokemon, bool8 attackEnemies)
{
EntityInfo *info = GetEntInfo(pokemon);
if (info->shopkeeper != SHOPKEEPER_MODE_NORMAL) {
if (attackEnemies) {
info->shopkeeper = SHOPKEEPER_MODE_ATTACK_ENEMIES;
}
else {
info->shopkeeper = SHOPKEEPER_MODE_ATTACK_TEAM;
}
}
}
u8 sub_806F4A4(Entity *pokemon, u8 type)
{
EntityInfo *info = GetEntInfo(pokemon);
s32 index;
if (MonsterIsType(pokemon, TYPE_GHOST)) {
if (type == TYPE_NORMAL || type == TYPE_FIGHTING)
if (!info->exposed)
return FALSE;
}
for (index = 0; index < 2; index++) {
if (gTypeEffectivenessChart[type][info->types[index]] == EFFECTIVENESS_IMMUNE)
return FALSE;
}
return TRUE;
}
static void ResetDamageCalcWorkingValues(void)
{
struct DamageCalcWorkingValues *damageCalcWorkingValues;
damageCalcWorkingValues = &gDungeon->damageCalcWorkingValues;
damageCalcWorkingValues->unk134 = 0;
damageCalcWorkingValues->unk138 = 0;
damageCalcWorkingValues->unk13C[0] = 0;
damageCalcWorkingValues->unk13C[1] = 0;
damageCalcWorkingValues->offensiveStages[0] = 0xA;
damageCalcWorkingValues->offensiveStages[1] = 0xA;
damageCalcWorkingValues->rawDamageFormulaValues[0] = 1;
damageCalcWorkingValues->rawDamageFormulaValues[1] = 1;
damageCalcWorkingValues->rawDamageFormulaValues[2] = 0;
damageCalcWorkingValues->rawDamageFormulaValues[4] = 0;
damageCalcWorkingValues->rawDamageFormulaValues[3] = 0;
damageCalcWorkingValues->rawDamageFormulaValues[5] = 0;
damageCalcWorkingValues->rawDamageFormulaValues[6] = 0;
damageCalcWorkingValues->unk150 = 0;
damageCalcWorkingValues->unk158 = 0;
damageCalcWorkingValues->unk15C = F248_ZERO;
damageCalcWorkingValues->atkItemBoost = 0;
damageCalcWorkingValues->spAtkItemBoost = 0;
damageCalcWorkingValues->defItemBoost = 0;
damageCalcWorkingValues->spDefItemBoost = 0;
damageCalcWorkingValues->unk164 = 0;
damageCalcWorkingValues->unk165 = 0;
damageCalcWorkingValues->unk166 = 0;
damageCalcWorkingValues->unk167 = 0;
damageCalcWorkingValues->unk168 = 0;
damageCalcWorkingValues->unk169 = 0;
damageCalcWorkingValues->unk16A = 0;
damageCalcWorkingValues->unk16C = 0;
damageCalcWorkingValues->unk16B = 0;
damageCalcWorkingValues->unk16D = 0;
damageCalcWorkingValues->fill16E[0] = 0;
damageCalcWorkingValues->fill16E[1] = 0;
damageCalcWorkingValues->fill16E[2] = 0;
damageCalcWorkingValues->fill16E[3] = 0;
damageCalcWorkingValues->fill16E[4] = 0;
damageCalcWorkingValues->fill16E[5] = 0;
damageCalcWorkingValues->fill16E[6] = 0;
damageCalcWorkingValues->fill16E[7] = 0;
damageCalcWorkingValues->fill16E[8] = 0;
damageCalcWorkingValues->fill16E[9] = 0;
damageCalcWorkingValues->fill16E[10] = 0;
damageCalcWorkingValues->pokemonExposed = FALSE;
damageCalcWorkingValues->unk17A = 0;
}
static const s32 gUnknown_8106F7C[] = {0, 0, 0, 1};
s32 sub_806F62C(s32 param_1)
{
return gUnknown_8106F7C[param_1];
}
static void sub_806F63C(Entity *param_1)
{
UnkDungeonGlobal_unk181E8_sub *temp = &gDungeon->unk181e8;
if (temp->cameraTarget == param_1) {
PointCameraToMonster(temp->cameraTarget);
}
}