#include "global.h"
#include "code_806CD90.h"
#include "code_803E46C.h"
#include "code_805D8C8.h"
#include "constants/direction.h"
#include "constants/status.h"
#include "constants/ability.h"
#include "constants/type.h"
#include "constants/dungeon.h"
#include "constants/iq_skill.h"
#include "dungeon_util.h"
#include "code_803E668.h"
#include "exclusive_pokemon.h"
#include "code_8077274_1.h"
#include "code_8069D4C.h"
#include "code_8045A00.h"
#include "position_util.h"
#include "game_options.h"
#include "dungeon_items.h"
#include "status.h"
#include "pokemon_mid.h"
#include "pokemon.h"
#include "random.h"
#include "targeting.h"
#include "sprite.h"
#include "structs/str_dungeon.h"
#include "tile_types.h"
#include "dungeon_capabilities.h"
#include "dungeon_random.h"
#include "text_util.h"
#include "dungeon_visibility.h"
#include "code_804267C.h"
#include "move_effects_target.h"
#include "dungeon_pokemon_attributes.h"
#include "code_8041AD0.h"
#include "type_chart.h"
#include "dungeon_message.h"
#include "dungeon_map_access.h"

extern u8 gUnknown_202F221;

extern void sub_806BFC0(EntityInfo *, u32);

const u8 gUnknown_8106EEF[] = {0x03, 0x04, 0x05, 0x00, 0x00, 0x70, 0x6b, 0x73, 0x64, 0x69, 0x72, 0x30, 0x00 };

void sub_806CC10(void)
{
    EntityInfo *entityInfo;
    Entity *entity;
    s32 i;

    for (i = 0; i < DUNGEON_MAX_POKEMON; i++) {
        entity = gDungeon->activePokemon[i];

        if (EntityExists(entity)) {
            entityInfo = GetEntInfo(entity);

            if (entityInfo->unk166 != 0) {
                entityInfo->unk166--;
                if ((entityInfo->unk166 & 1) == 0) // If value is even:
                    sub_806CDD4(entity, 0, (entityInfo->action.direction - 1) & DIRECTION_MASK);
            }
        }
    }
}

void sub_806CC70(void)
{
    s32 i;
    Entity *entity;

    for (i = 0; i < DUNGEON_MAX_POKEMON; i++) {
        entity = gDungeon->activePokemon[i];

        if (EntityExists(entity))
            sub_806CCB4(entity, sub_806CEBC(entity));
    }
}

void sub_806CCB4(Entity *entity, u8 a1)
{
    s32 sVar1;
    bool8 flag;
    EntityInfo *info;

    info = GetEntInfo(entity);
    flag = gDungeon->unk181e8.hallucinating;

    if (entity == gDungeon->unk181e8.cameraTarget)
        flag = FALSE;

    entity->axObj.unk43_animId2 = a1;
    entity->axObj.unk42_animId1 = a1;
    entity->axObj.unk45_orientation = info->action.direction;
    entity->axObj.unk44_direction1 = info->action.direction;
    entity->axObj.unk47 = 0;
    sVar1 = entity->axObj.unk40_maybeAnimTimer;

    if (info->curseClassStatus.status != STATUS_DECOY && !flag)
        AxResInitFile(&entity->axObj.axdata,
                      entity->axObj.spriteFile, entity->axObj.unk42_animId1,
                      entity->axObj.unk44_direction1, sVar1,
                      Rand32Bit() & 3, FALSE);
    else
        AxResInitFile(&entity->axObj.axdata,
                      GetSpriteData(MONSTER_DECOY),
                      entity->axObj.unk42_animId1,
                      entity->axObj.unk44_direction1, sVar1, Rand32Bit() & 3,
                      FALSE);

    entity->axObj.unk46 = 0;
}

void sub_806CD90(void)
{
    s32 i;
    Entity *entity;

    for (i = 0; i < DUNGEON_MAX_POKEMON; i++) {
        entity = gDungeon->activePokemon[i];

        if (EntityExists(entity))
            sub_806CCB4(entity, sub_806CEBC(entity));
    }
}

void sub_806CDD4(Entity *entity, u8 a1, u32 direction)
{
    if (GetEntityType(entity) == ENTITY_MONSTER) {
        entity->axObj.unk42_animId1 = a1;

        if (direction < NUM_DIRECTIONS)
            entity->axObj.unk44_direction1 = direction;
    }
}

void sub_806CDFC(Entity *entity, u8 a1, u32 direction)
{
    if (GetEntityType(entity) == ENTITY_MONSTER) {
        if (entity->axObj.unk43_animId2 == a1 && entity->axObj.unk45_orientation == direction)
            return;
        sub_806CDD4(entity, a1, direction);
    }
}

void sub_806CE34(Entity *entity, u32 newDir)
{
    if (GetEntityType(entity) == ENTITY_MONSTER) {
        entity->axObj.unk42_animId1 = sub_806CEBC(entity);

        if (newDir < NUM_DIRECTIONS) {
            GetEntInfo(entity)->action.direction = newDir & DIRECTION_MASK;
            entity->axObj.unk44_direction1 = newDir & DIRECTION_MASK;
        }
    }
}

void sub_806CE68(Entity *entity, u32 newDir)
{
    if (GetEntityType(entity) == ENTITY_MONSTER) {
        entity->axObj.unk42_animId1 = sub_806CEBC(entity);

        if (newDir < NUM_DIRECTIONS)
            entity->axObj.unk44_direction1 = newDir;
    }
}

void sub_806CE94(Entity *entity, u32 newDir)
{
    if (GetEntityType(entity) == ENTITY_MONSTER) {
        entity->axObj.unk42_animId1 = 6;

        if (newDir < NUM_DIRECTIONS)
            entity->axObj.unk44_direction1 = newDir;
    }
}

u8 sub_806CEBC(Entity *entity)
{
    u8 sleepClassStatus;
    EntityInfo *entityInfo;

    entityInfo = GetEntInfo(entity);
    sleepClassStatus = entityInfo->sleepClassStatus.status;

    if (sleepClassStatus == STATUS_SLEEP || sleepClassStatus == STATUS_NAPPING || sleepClassStatus == STATUS_NIGHTMARE) {
        if (entityInfo->apparentID != MONSTER_SUDOWOODO || entityInfo->sleepClassStatus.turns != 0x7F)
            return 5;
        else
            return 7;
    }
    if (entityInfo->bideClassStatus.status == STATUS_BIDE)
        return 11;
    return 7;
}

void sub_806CEFC(Entity *entity, u32 newDir)
{
    GetEntInfo(entity)->action.direction = newDir & DIRECTION_MASK;
    sub_806CE68(entity, newDir & DIRECTION_MASK);
}

void sub_806CF18(Entity *entity)
{
    s32 i;
    EntityInfo *entityInfo;

    if (!EntityExists(entity))
        return;

    entityInfo = GetEntInfo(entity);
    for (i = 0; i < 100; i++) {
        sub_803E46C(33);

        if (!sub_808DA44(entityInfo->apparentID, entity->axObj.unk43_animId2))
            break;
    }
}

u8 sub_806CF54(Entity *entity)
{
    return GetEntInfo(entity)->unk204;
}

void sub_806CF60(void)
{
    Entity *entity;
    s32 i;

    for (i = 0; i < DUNGEON_MAX_POKEMON; i++) {
        entity = gDungeon->activePokemon[i];

        if (EntityExists(entity))
            sub_806CF98(entity);
    }
}

u32 sub_806CF98(Entity *entity)
{
    const Tile *mapTile;
    u32 shadowSize;
    u16 terrainType;
    EntityInfo *entityInfo;

    mapTile = GetTileAtEntitySafe(entity);
    terrainType = mapTile->terrainType & (TERRAIN_TYPE_NORMAL | TERRAIN_TYPE_SECONDARY);
    entityInfo = GetEntInfo(entity);
    shadowSize = GetShadowSize(entityInfo->apparentID);

    if (terrainType == (TERRAIN_TYPE_NORMAL | TERRAIN_TYPE_SECONDARY))
        return 6;

    if (terrainType == TERRAIN_TYPE_NORMAL) {
        if (IsWaterTileset())
            shadowSize = gUnknown_8106EEF[shadowSize];
    }
    else if (terrainType == TERRAIN_TYPE_SECONDARY && gDungeonWaterType[gDungeon->tileset] != DUNGEON_WATER_TYPE_LAVA)
        shadowSize = gUnknown_8106EEF[shadowSize];

    entityInfo->unk204 = shadowSize;
    return shadowSize;
}

static bool8 HandleDealingDamageInternal(Entity *attacker, Entity *target, struct DamageStruct *r5, bool32 isFalseSwipe, bool32 giveExp, s16 arg4_, s32 arg8);

extern void sub_807F43C(Entity *, Entity *);
extern void sub_8041B18(Entity *pokemon);
extern void sub_8041B90(Entity *pokemon);
extern void sub_8041D00(Entity *pokemon, Entity *target);
extern void DealDamageToEntity(Entity *, s32, u32, u32);
extern void SetShopkeeperAggression(Entity *, Entity *);
extern void sub_8042238(Entity *pokemon, Entity *target);
extern void sub_803ED30(s32, Entity *r0, u8, s32);
extern bool8 sub_806A458(Entity *);
extern bool8 sub_806F660(Entity *, Entity *);
extern bool8 sub_806A58C(s16 a0);
extern void sub_8071D4C(Entity *pokemon, Entity *target, s32 exp);
extern void sub_8042148(Entity *pokemon);
extern void sub_8068FE0(Entity *, u32, Entity *r2);
extern void sub_80694C0(Entity *, s32, s32, u32);
extern void sub_8042A24(Entity *r0);
extern void sub_806A390(Entity *r0);
extern void sub_806F63C(Entity *r0);
extern void sub_8078084(Entity * pokemon);
extern void sub_800DBBC(void);
extern void sub_8045C28(Item *Item, u8 itemID, u32 param_3);
extern void sub_80464C8(Entity *, DungeonPos *, Item *);
extern bool8 DoEnemiesEvolveWhenKOed(u8 dungeon);
extern bool8 sub_806FA5C(Entity *, Entity *, struct unkStruct_8069D4C *);
extern void EntityUpdateStatusSprites(Entity *);
extern bool8 sub_8045888(Entity *r0);

extern const s32 gUnknown_8106A4C;
extern const s16 gUnknown_80F4E10;
extern const s16 gUnknown_80F4E12;
extern const s16 gUnknown_80F4E14;
extern const s16 gUnknown_80F4E16;
extern const s16 gUnknown_80F4E18;
extern const s16 gUnknown_80F4E1A;
extern const s16 gUnknown_80F4E1C;
extern const s16 gUnknown_80F4E1E;
extern const s16 gUnknown_80F4E20;
extern const s16 gUnknown_80F4F8C;
extern const u8 *const gUnknown_80FCFA4;
extern const u8 *const gUnknown_80FCA90;
extern const u8 *const gUnknown_80F9600;
extern const u8 *const gUnknown_80F9614;
extern const u8 *const gUnknown_80F9630;
extern const u8 *const gUnknown_80F9654;
extern const u8 *const gUnknown_80F9670;
extern const u8 *const gUnknown_80F9614;
extern const u8 *const gUnknown_80F9688;
extern const u8 *const gUnknown_80FD484;
extern const u8 *const gUnknown_80F96A8;
extern const u8 *const gUnknown_8100548;
extern const u8 *const gUnknown_80FD46C;
extern const u8 *const gUnknown_810056C;
extern const u8 *const gUnknown_80F9E44;
extern const u8 *const gUnknown_80F9764[];
extern const u8 *const gUnknown_80F9CC0[];
extern const u8 *const gUnknown_80F9CEC[];
extern const u8 *const gUnknown_80F9DAC[];
extern const u8 *const gUnknown_80F9DF0[];
extern const u8 *const gUnknown_80F9D8C[];
extern const u8 *const gUnknown_80F9D84[];
extern const u8 *const gUnknown_80F9D28[];

void HandleDealingDamage(Entity *attacker, Entity *target, struct DamageStruct *dmgStruct, bool32 isFalseSwipe, bool32 giveExp, s16 arg4, bool32 arg8, s32 argC)
{
    bool32 r9;
    // Some compiler weirdness, because it won't match without creating arg4 again
    s32 r10 = arg4;
    s32 returnDmg = 0;

    if (HandleDealingDamageInternal(attacker, target, dmgStruct, isFalseSwipe, giveExp, r10, argC))
        return;
    if (dmgStruct->tookNoDamage)
        return;
    if (!EntityExists(attacker) || !EntityExists(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);
        sub_807F43C(target, attacker);
    }

    if (GetEntInfo(target)->bideClassStatus.status == STATUS_ENRAGED) {
        RaiseAttackStageTarget(attacker, target, gUnknown_8106A4C, 1);
    }

    if (!EntityExists(attacker) || !EntityExists(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 (HasAbility(target, ABILITY_ROUGH_SKIN))
            returnDmg += 2;

        if (returnDmg) {
            struct DamageStruct sp;

            TryDisplayDungeonLoggableMessage3(attacker, target, gUnknown_80FCFA4);
            sp.dmg = (dmgStruct->dmg * returnDmg) / 4;
            sp.type = dmgStruct->type;
            sp.residualDmgType = 6;
            sp.typeEffectiveness = 2;
            sp.isCrit = FALSE;
            sp.unkE = 0;
            sp.tookNoDamage = FALSE;
            HandleDealingDamageInternal(target, attacker, &sp, FALSE, giveExp, r10, argC);
        }
    }
    if (!EntityExists(attacker) || !EntityExists(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 (HasAbility(target, ABILITY_ARENA_TRAP)
            && !MonsterIsType(attacker, TYPE_FLYING)
            && !HasAbility(attacker, ABILITY_LEVITATE)
            && DungeonRandInt(100) < gUnknown_80F4E10)
        {
            attackerInfo->abilityEffectFlags |= ABILITY_FLAG_ARENA_TRAP;
        }
        if (HasAbility(target, ABILITY_SHADOW_TAG)
            && DungeonRandInt(100) < gUnknown_80F4E12)
        {
            attackerInfo->abilityEffectFlags |= ABILITY_FLAG_SHADOW_TAG;
        }
        if (HasAbility(target, ABILITY_MAGNET_PULL)
            && MonsterIsType(attacker, TYPE_STEEL)
            && DungeonRandInt(100) < gUnknown_80F4E14)
        {
            attackerInfo->abilityEffectFlags |= ABILITY_FLAG_MAGNET_PULL;
        }

        if (HasAbility(target, ABILITY_STATIC)
            && isPhysical
            && DungeonRandInt(100) < gUnknown_80F4E16)
        {
            attackerInfo->abilityEffectFlags |= ABILITY_FLAG_STATIC;
        }
        if (HasAbility(target, ABILITY_POISON_POINT)
            && DungeonRandInt(100) < gUnknown_80F4E18)
        {
            attackerInfo->abilityEffectFlags |= ABILITY_FLAG_POISON_POINT;
        }
        if (HasAbility(target, ABILITY_EFFECT_SPORE)
            && isPhysical
            && DungeonRandInt(100) < gUnknown_80F4E1A)
        {
            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 (HasAbility(target, ABILITY_FLAME_BODY)
            && DungeonRandInt(100) < gUnknown_80F4E1C)
        {
            attackerInfo->abilityEffectFlags |= ABILITY_FLAG_FLAME_BODY;
        }
        if (HasAbility(target, ABILITY_CUTE_CHARM)
            && isPhysical
            && DungeonRandInt(100) < gUnknown_80F4E1E)
        {
            attackerInfo->abilityEffectFlags |= ABILITY_FLAG_CUTE_CHARM;
        }
        if (HasAbility(target, ABILITY_STENCH)
            && DungeonRandInt(100) < gUnknown_80F4E20)
        {
            attackerInfo->abilityEffectFlags |= ABILITY_FLAG_STENCH;
        }
    }
    if (!EntityExists(attacker) || !EntityExists(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)->unk98 != targetInfo->leechSeedClassStatus.unk4) {
                targetInfo->leechSeedClassStatus.status = 0;
            }
            else {
                sub_8041D00(destBondTarget, target);
                DealDamageToEntity(destBondTarget, dmgStruct->dmg, 0xC, 0x1F9);
            }
        }
    }
}

static inline u8 ItemId(Item *item)
{
    return item->id;
}

static bool8 HandleDealingDamageInternal(Entity *attacker, Entity *target, struct DamageStruct *dmgStruct, bool32 isFalseSwipe, bool32 giveExp, s16 arg4_, s32 arg8)
{
    s32 hpBefore, hpChange;
    EntityInfo *targetData;
    s32 arg4 = arg4_;
    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 = gUnknown_80F4F8C;
    }

    if (arg4 != 0x20E && HasAbility(target, ABILITY_STURDY) && dmgStruct->dmg == 9999) {
        SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[1], target, 0);
        TryDisplayDungeonLoggableMessage3(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(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 ((HasAbility(target, ABILITY_VOLT_ABSORB) && dmgStruct->type == TYPE_ELECTRIC)) {
        HealTargetHP(attacker, target, dmgStruct->dmg, 0, 0);
        dmgStruct->tookNoDamage = TRUE;
        return FALSE;
    }
    else if (HasAbility(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(attacker, target, gUnknown_80F9614);
        }
        switch (dmgStruct->typeEffectiveness) {
            case EFFECTIVENESS_IMMUNE:
                TryDisplayDungeonLoggableMessage3(attacker, target, gUnknown_80F9630);
                break;
            case EFFECTIVENESS_RESIST:
                TryDisplayDungeonLoggableMessage3(attacker, target, gUnknown_80F9654);
                break;
            case EFFECTIVENESS_SUPER:
                TryDisplayDungeonLoggableMessage3(attacker, target, gUnknown_80F9670);
                break;
        }
    }

    SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[0], attacker, 0);
    SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[1], target, 0);
    if (dmgStruct->dmg == 0) {
        if (sub_8045888(attacker) && sub_8045888(target)) {
            if (targetData->unk152 == 0) {
                TryDisplayDungeonLoggableMessage3(attacker, target, gUnknown_80F9688);
            }
            sub_8042238(attacker, target);
        }
        else {
            if (targetData->unk152 == 0) {
                TryDisplayDungeonLoggableMessage3(attacker, target, gUnknown_80F9688);
            }
            sub_803E708(0x1E, 0x18);
        }
        dmgStruct->tookNoDamage = TRUE;
        return FALSE;
    }
    else if (dmgStruct->dmg == 9999) {
        if (arg8 != 0 && sub_8045888(target)) {
            unkTile = GetTileAtEntitySafe(target);
            sub_803E708(0x14, 0x18);
            unkTile->unk4 |= 4;
            sub_8049ED4();
        }
        if (targetData->unk152 == 0) {
            TryDisplayDungeonLoggableMessage3(attacker, target, gUnknown_80F96A8);
        }
        targetData->unkA0 = 999;
    }
    else {
        const u8 *str;

        gFormatArgs[0] = dmgStruct->dmg;
        str = gUnknown_80F9764[dmgStruct->residualDmgType];

        ASM_MATCH_TRICK(dmgStruct);

        targetData->unkA0 += dmgStruct->dmg;
        if (targetData->unkA0 > 999)
            targetData->unkA0 = 999;

        if (sub_8045888(target)) {
            if (dmgStruct->residualDmgType != 14) {
                sub_803ED30(-dmgStruct->dmg, target, 1, -1);
            }
            if (targetData->unk152 == 0 && str != NULL) {
                TryDisplayDungeonLoggableMessage3(attacker, target, str);
            }
        }
        else {
            if (targetData->unk152 == 0 && str != NULL) {
                TryDisplayDungeonLoggableMessage3(attacker, target, str);
            }
        }
    }

    if ((dmgStruct->residualDmgType != 14 || targetData->HP <= 1) && unkTile == NULL && sub_8045888(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(attacker, target, gUnknown_8100548);
        }
    }
    else if (isFalseSwipe == TRUE && targetData->HP == 0) {
        targetData->HP = 1;
        TryDisplayDungeonLoggableMessage3(attacker, target, gUnknown_810056C);
    }

    hpChange = hpBefore - targetData->HP;
    if (hpChange < 0)
        hpChange = 0;

    if (var_24 || unkTile != NULL)
        sub_803E708(0xA, 0x18);

    if (targetData->HP != 0) {
        if (var_24) {
            sub_806CE68(target, 8);
        }
        if (HasHeldItem(target, ITEM_JOY_RIBBON) && hpChange > 0 && dmgStruct->dmg != 9999) {
            sub_8071D4C(attacker, target, hpChange);
        }

        if (unkTile != NULL)
        {
            unkTile->unk4 &= ~(0x4);
            sub_8049ED4();
        }
        return FALSE;
    }

    // 0 HP
    if (targetData->invisibleClassStatus.status == STATUS_TRANSFORMED) {
        SendTransformEndMessage(attacker, target);
    }

    if (unkTile != NULL) {
        sub_803E708(0x14, 0x18);
        target->unk22 = 2;
        sub_803E708(0xA, 0x18);
        unkTile->unk4 &= ~(0x4);
        sub_8049ED4();
    }
    else if (var_24) {
        target->unk22 = 1;
        sub_803E708(0x1E, 0x18);
    }

    r8 = 1;
    if (EntityExists(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 == 19 || dmgStruct->residualDmgType == 20) {
        if (targetData->isNotTeamMember) {
            TryDisplayDungeonLoggableMessage3(attacker, target, gUnknown_80F9E44);
        }
        else {
            DisplayDungeonLoggableMessageTrue(attacker, gUnknown_80F9E44);
        }
    }
    else if (targetData->isNotTeamMember)
    {
        if (targetData->monsterBehavior == BEHAVIOR_RESCUE_TARGET) {
            DisplayDungeonLoggableMessageTrue(attacker, gUnknown_80F9DF0[r8]);
        }
        else {
            TryDisplayDungeonLoggableMessage3(attacker, target, gUnknown_80F9CC0[r8]);
        }
    }
    else {
        PokemonStruct2 *recruitedMon = &gRecruitedPokemonRef->pokemon2[targetData->teamIndex];
        if (targetData->isTeamLeader || (targetData->joinedAt.joinedAt == DUNGEON_JOIN_LOCATION_PARTNER && gDungeon->unk644.unk18 == 0)) {
            DisplayDungeonLoggableMessageTrue(attacker, gUnknown_80F9CEC[r8]);
        }
        else if (IsClientOrTeamBase(targetData->joinedAt.joinedAt)) {
            DisplayDungeonLoggableMessageTrue(attacker, gUnknown_80F9DAC[r8]);
        }
        else if (targetData->monsterBehavior == BEHAVIOR_RESCUE_TARGET) {
            DisplayDungeonLoggableMessageTrue(attacker, gUnknown_80F9DF0[r8]);
        }
        else if (sub_806A58C(recruitedMon->unkA)) {
            if (gDungeon->unk644.unk19 != 0) {
                DisplayDungeonLoggableMessageTrue(attacker, gUnknown_80F9D8C[r8]);
            }
            else {
                DisplayDungeonLoggableMessageTrue(attacker, gUnknown_80F9D84[r8]);
            }
        }
        else {
            DisplayDungeonLoggableMessageTrue(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.unk35 = 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);
                sub_806BFC0(targetData, 0);
                sub_800DBBC();
                sub_806A390(target);
                sub_806CCB4(target, sub_806CEBC(target));
                EntityUpdateStatusSprites(target);
                SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[1], target, 0);
                DisplayDungeonLoggableMessageTrue(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 (EntityExists(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;
                sub_8068FE0(teamMember, 0x221, target);
                sub_80694C0(target, monPos.x, monPos.y, 1);
                sub_804535C(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.unk35 = 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);
                sub_806BFC0(targetData, 0);
                sub_800DBBC();
                sub_806A390(target);
                sub_806CCB4(target, sub_806CEBC(target));
                EntityUpdateStatusSprites(target);
                SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[0], target, 0);
                SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[1], teamMember, 0);
                DisplayDungeonLoggableMessageTrue(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);
            sub_8045C28(reviverSeed, ITEM_PLAIN_SEED, 2);
            target->unk22 = 0;
            targetData->HP = targetData->maxHPStat;
            targetData->unk158 = 0;
            targetData->unk14C = 1;
            targetData->belly = targetData->maxBelly;
            if (targetData->isTeamLeader) {
                gDungeon->unk644.unk35 = 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);
            sub_806BFC0(targetData, 0);
            sub_800DBBC();
            sub_806A390(target);
            sub_806CCB4(target, sub_806CEBC(target));
            EntityUpdateStatusSprites(target);
            SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[1], target, 0);
            DisplayDungeonLoggableMessageTrue(attacker, gUnknown_80FD46C);
            sub_806F63C(target);
            return FALSE;
        }
    }

    if (!targetData->isTeamLeader && ItemExists(&targetData->heldItem)) {
        sub_80464C8(target, &target->pos, &targetData->heldItem);
        ZeroOutItem(&targetData->heldItem);
    }
    if (targetData->bossFlag) {
        gDungeon->unk644.bossSongIndex = 999;
    }

    // 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) {
            sub_80980B4(targetData->id);
        }
        if (targetData->grudge) {
            attackerData->unk14B = 1;
        }

        if (giveExp == TRUE) {
            if (!attackerData->isNotTeamMember) {
                if (targetData->isNotTeamMember) {
                    s32 i;

                    sub_8071D4C(attacker, attacker, exp);
                    for (i = 0; i < MAX_TEAM_MEMBERS; i++) {
                        Entity *teamMember = gDungeon->teamPokemon[i];
                        if (EntityExists(teamMember) && teamMember != attacker) {
                            sub_8071D4C(attacker, teamMember, exp);
                        }
                    }
                    r10 = TRUE;
                }
            }
            else {
                if (DoEnemiesEvolveWhenKOed(gDungeon->unk644.dungeonLocation.id)) {
                    attackerData->unk149 = 1;
                    gDungeon->unkD = 1;
                }
            }
        }
    }

    if (EntityExists(attacker) && GetEntityType(attacker) == ENTITY_MONSTER && !GetEntInfo(attacker)->isTeamLeader)
        r10 = FALSE;

    if (r10) {
        struct unkStruct_8069D4C sp;

        sub_8069D4C(&sp, target);
        if (sub_806F660(attacker, target)) {
            if (!sub_806FA5C(attacker, target, &sp)) {
                sub_8068FE0(target, 0x1F5, attacker);
            }
            else {
                gUnknown_202F221 = 1;
            }
        }
        else {
            sub_8068FE0(target, arg4, attacker);
        }
    }
    else {
        sub_8068FE0(target, arg4, attacker);
    }

    return TRUE;
}
