#include "common.h"
#include "battle/battle.h"
#include "script_api/battle.h"
#include "effects.h"

HitResult calc_item_check_hit(void) {
    BattleStatus* battleStatus = &gBattleStatus;
    ActorState* state = &battleStatus->playerActor->state;
    s32 actorID = battleStatus->currentTargetID;
    s8 currentTargetPartS8;
    u32 currentTargetPart;
    Actor* actor;
    ActorPart* actorPart;

    battleStatus->currentTargetID2 = battleStatus->currentTargetID;
    currentTargetPart = currentTargetPartS8 = battleStatus->currentTargetPart;
    battleStatus->currentTargetPart2 = currentTargetPartS8;

    actor = get_actor(actorID);
    if (actor == NULL) {
        return HIT_RESULT_HIT;
    }

    actorPart = get_actor_part(actor, currentTargetPart);
    ASSERT(actorPart != NULL);

    if (actorPart->eventFlags & ACTOR_EVENT_FLAG_ILLUSORY) {
        return HIT_RESULT_MISS;
    }

    if (actor->transparentStatus == STATUS_TRANSPARENT) {
        return HIT_RESULT_MISS;
    }

    if (actor->stoneStatus == STATUS_STONE) {
        sfx_play_sound_at_position(SOUND_IMMUNE, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
        return HIT_RESULT_IMMUNE;
    }

    if ((battleStatus->currentAttackElement & DAMAGE_TYPE_JUMP)
        && (actorPart->eventFlags & ACTOR_EVENT_FLAG_SPIKY_TOP))
    {
        sfx_play_sound_at_position(SOUND_HIT_NORMAL, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
        return HIT_RESULT_LANDED_ON_SPIKE;
    }

    return HIT_RESULT_HIT;
}

HitResult calc_item_damage_enemy(void) {
    BattleStatus* battleStatus = &gBattleStatus;
    Actor* player = battleStatus->playerActor;
    s32 currentTargetID = battleStatus->currentTargetID;
    Actor* partner = battleStatus->partnerActor;
    s32 currentTargetPartID = battleStatus->currentTargetPart;
    s32 partImmuneToElement;
    s32 sp1C = FALSE;
    s32 actorClass;
    s32 isFireDamage = FALSE;
    s32 isWaterDamage = FALSE;
    s32 isShockDamage = FALSE;
    s32 isIceDamage = FALSE;
    Actor* target;
    ActorPart* targetPart;
    Evt* script;
    s32 attackDamage;
    s32 temp;
    s32 targetDefense;
    ActorState* state;
    s32 dispatchEvent;
    s32 wasStatusInflicted;
    s32 hitResult;

    battleStatus->wasStatusInflicted = FALSE;
    battleStatus->lastAttackDamage = 0;
    battleStatus->attackerActorID = player->actorID;
    battleStatus->currentTargetID2 = battleStatus->currentTargetID;
    battleStatus->currentTargetPart2 = battleStatus->currentTargetPart;
    target = get_actor(currentTargetID);
    wasStatusInflicted = FALSE;

    if (target == NULL) {
        return HIT_RESULT_HIT;
    }

    targetPart = get_actor_part(target, currentTargetPartID);
    if (targetPart == NULL) {
        PANIC();
    }

    target->lastDamageTaken = 0;
    actorClass = currentTargetID & ACTOR_CLASS_MASK;
    if (!(gBattleStatus.flags1 & BS_FLAGS1_PARTNER_ACTING)) {
        state = &player->state;
    } else {
        state = &partner->state;
    }

    if (battleStatus->currentAttackElement & DAMAGE_TYPE_FIRE) {
        fx_ring_blast(0, state->goalPos.x, state->goalPos.y, state->goalPos.z + 5.0f, 1.0f, 24);
        isFireDamage = TRUE;
    }
    if (battleStatus->currentAttackElement & DAMAGE_TYPE_SHOCK) {
        apply_shock_effect(target);
        isShockDamage = TRUE;
    }
    if (battleStatus->currentAttackElement & DAMAGE_TYPE_WATER) {
        fx_water_splash(0, state->goalPos.x, state->goalPos.y, state->goalPos.z + 5.0f, 1.0f, 24);
        isWaterDamage = TRUE;
    }
    if (battleStatus->currentAttackElement & DAMAGE_TYPE_ICE) {
        fx_big_snowflakes(0, state->goalPos.x, state->goalPos.y, state->goalPos.z + 5.0f);
        isIceDamage = TRUE;
    }

    if (!(battleStatus->currentAttackElement & DAMAGE_TYPE_REMOVE_BUFFS)) {
        if ((targetPart->eventFlags & ACTOR_EVENT_FLAG_ILLUSORY)
            || (target->transparentStatus == STATUS_TRANSPARENT)
            || (targetPart->eventFlags & ACTOR_EVENT_FLAG_800)
            && !(battleStatus->currentAttackElement & DAMAGE_TYPE_QUAKE)
        ) {
            return HIT_RESULT_MISS;
        }
    }

    if (target->stoneStatus == STATUS_STONE) {
        sfx_play_sound_at_position(SOUND_IMMUNE, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
        func_8024EFE0(state->goalPos.x, state->goalPos.y, state->goalPos.z, 0, 1, 1);
        show_damage_popup(state->goalPos.x, state->goalPos.y, state->goalPos.z, 0, 0);
        if (gBattleStatus.flags1 & (BS_FLAGS1_40 | BS_FLAGS1_200)) {
            return HIT_RESULT_1;
        }
        return HIT_RESULT_HIT;
    }

    if (targetPart->elementalImmunities & battleStatus->currentAttackElement) {
        partImmuneToElement = TRUE;
    } else {
        partImmuneToElement = FALSE;
    }

    if (targetPart->eventFlags & (ACTOR_EVENT_FLAG_ENCHANTED | ACTOR_EVENT_FLAG_STAR_ROD_ENCHANTED)) {
        battleStatus->currentAttackElement &= ~DAMAGE_TYPE_IGNORE_DEFENSE;
    }

    temp = get_defense(target, targetPart->defenseTable, battleStatus->currentAttackElement);
    if (!(battleStatus->currentAttackElement & DAMAGE_TYPE_IGNORE_DEFENSE)) {
        temp += target->defenseBoost;
    }

    attackDamage = battleStatus->currentAttackDamage;
    if (attackDamage > 99) {
        attackDamage = 99;
    }
    if (attackDamage <= 0) {
        temp = 0;
    }

    attackDamage -= temp;
    target->hpChangeCounter = 0;

    if (attackDamage <= 0) {
        target->hpChangeCounter = 0;
        hitResult = HIT_RESULT_NO_DAMAGE;

        if (!(battleStatus->currentAttackElement & DAMAGE_TYPE_STATUS_ALWAYS_HITS)) {
            dispatchEvent = EVENT_ZERO_DAMAGE;
            sfx_play_sound_at_position(SOUND_IMMUNE, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
            battleStatus->lastAttackDamage = 0;
        } else {
            dispatchEvent = EVENT_ZERO_DAMAGE;
            battleStatus->lastAttackDamage = 0;
        }
    } else {
        target->damageCounter += attackDamage;
        target->hpChangeCounter -= attackDamage;
        battleStatus->lastAttackDamage = 0;
        dispatchEvent = EVENT_HIT_COMBO;
        hitResult = HIT_RESULT_HIT;

        if (!(targetPart->flags & ACTOR_PART_FLAG_2000)
            && !partImmuneToElement
            && !(targetPart->targetFlags & ACTOR_PART_FLAG_4)
        ) {
            target->currentHP -= attackDamage;
            if (target->currentHP <= 0) {
                target->currentHP = 0;
                dispatchEvent = EVENT_DEATH;
            }
        }
        battleStatus->lastAttackDamage += attackDamage;
        target->lastDamageTaken = battleStatus->lastAttackDamage;
        target->hpChangeCounter = 0;
    }

    if (targetPart->flags & ACTOR_PART_FLAG_2000) {
        dispatch_event_actor(target, dispatchEvent);
        func_8024EFE0(state->goalPos.x, state->goalPos.y, state->goalPos.z, 0, 1, 3);
        sfx_play_sound_at_position(SOUND_IMMUNE, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
        return HIT_RESULT_NO_DAMAGE;
    }
    
    if (battleStatus->currentAttackElement & DAMAGE_TYPE_DEATH) {
        battleStatus->lastAttackDamage = 0;
        dispatchEvent = EVENT_DEATH;
        hitResult = HIT_RESULT_HIT;
    }

    if (gBattleStatus.flags1 & BS_FLAGS1_SP_EVT_ACTIVE) {
        if (dispatchEvent == EVENT_HIT_COMBO) {
            dispatchEvent = EVENT_HIT;
        }
        if (dispatchEvent == EVENT_ZERO_DAMAGE) {
            dispatchEvent = EVENT_IMMUNE;
        }
        if (target->currentHP <= 0 && dispatchEvent == EVENT_IMMUNE) {
            dispatchEvent = EVENT_DEATH;
        }
    } else if (dispatchEvent == EVENT_DEATH) {
        dispatchEvent = EVENT_HIT_COMBO;
    }

    if (gBattleStatus.flags1 & BS_FLAGS1_SP_EVT_ACTIVE) {
        if (battleStatus->currentAttackElement & DAMAGE_TYPE_REMOVE_BUFFS) {
            dispatchEvent = EVENT_IMMUNE;
            if (targetPart->eventFlags & ACTOR_EVENT_FLAG_ENCHANTED) {
                dispatchEvent = EVENT_STAR_BEAM;
                wasStatusInflicted = TRUE;
            }
            if (targetPart->eventFlags & ACTOR_EVENT_FLAG_ATTACK_CHARGED) {
                dispatchEvent = EVENT_STAR_BEAM;
                wasStatusInflicted = TRUE;
            }
            if (targetPart->eventFlags & ACTOR_EVENT_FLAG_STAR_ROD_ENCHANTED) {
                dispatchEvent = EVENT_INVUNERABLE_TAUNT;
            }
            hitResult = HIT_RESULT_HIT;
        }
    }

    if (battleStatus->currentAttackElement & DAMAGE_TYPE_REMOVE_BUFFS) {
        if (gBattleStatus.flags1 & BS_FLAGS1_SP_EVT_ACTIVE) {
            if ((target->attackBoost > 0 || target->defenseBoost > 0) ||
                ((target->staticStatus == 0 && target->transparentStatus != 0) || target->staticStatus != 0))
            {
                target->attackBoost = 0;
                target->defenseBoost = 0;
                target->isGlowing = FALSE;
                dispatchEvent = EVENT_HIT;
                if (target->staticStatus != 0) {
                    target->staticStatus = 0;
                    target->staticDuration = 0;
                    remove_status_static(target->hudElementDataIndex);
                }
                if (target->transparentStatus != 0) {
                    target->transparentStatus = 0;
                    target->transparentDuration = 0;
                    remove_status_transparent(target->hudElementDataIndex);
                }
                wasStatusInflicted = TRUE;
                hitResult = HIT_RESULT_HIT;
            }
        }
    }

    if (gBattleStatus.flags1 & BS_FLAGS1_SP_EVT_ACTIVE) {
        if (battleStatus->currentAttackElement & DAMAGE_TYPE_PEACH_BEAM) {
            dispatchEvent = EVENT_IMMUNE;
            if (targetPart->eventFlags & ACTOR_EVENT_FLAG_ENCHANTED) {
                dispatchEvent = EVENT_PEACH_BEAM;
                wasStatusInflicted = TRUE;
            }
            if (targetPart->eventFlags & ACTOR_EVENT_FLAG_STAR_ROD_ENCHANTED) {
                dispatchEvent = EVENT_PEACH_BEAM;
                wasStatusInflicted = TRUE;
            }
        }
    }

    if (gBattleStatus.flags1 & BS_FLAGS1_SP_EVT_ACTIVE) {
        if (battleStatus->currentAttackElement & DAMAGE_TYPE_SPIN_SMASH) {
            if (dispatchEvent == EVENT_HIT) {
                dispatchEvent = EVENT_SPIN_SMASH_HIT;
            }
            if (dispatchEvent == EVENT_DEATH) {
                dispatchEvent = EVENT_SPIN_SMASH_DEATH;
            }
        }
    }

    if ((gBattleStatus.flags1 & BS_FLAGS1_SP_EVT_ACTIVE)
        && (battleStatus->currentAttackElement & (DAMAGE_TYPE_JUMP | DAMAGE_TYPE_POW))
        && (targetPart->eventFlags & ACTOR_EVENT_FLAG_GROUNDABLE)
    ) {
        if (dispatchEvent == EVENT_HIT) {
            dispatchEvent = EVENT_FALL_TRIGGER;
        }
        if (dispatchEvent == EVENT_IMMUNE) {
            dispatchEvent = EVENT_FALL_TRIGGER;
        }
    }

    if ((gBattleStatus.flags1 & BS_FLAGS1_SP_EVT_ACTIVE)
        && (battleStatus->currentAttackElement & DAMAGE_TYPE_POW)
        && (targetPart->eventFlags & ACTOR_EVENT_FLAG_RIDING_BROOMSTICK)
    ) {
        if (dispatchEvent == EVENT_HIT) {
            dispatchEvent = EVENT_FALL_TRIGGER;
        }
        if (dispatchEvent == EVENT_IMMUNE) {
            dispatchEvent = EVENT_FALL_TRIGGER;
        }
    }

    if ((gBattleStatus.flags1 & BS_FLAGS1_SP_EVT_ACTIVE)
        && (battleStatus->currentAttackElement & (DAMAGE_TYPE_JUMP | DAMAGE_TYPE_POW | DAMAGE_TYPE_QUAKE))
        && (targetPart->eventFlags & ACTOR_EVENT_FLAG_FLIPABLE)
    ) {
        if (dispatchEvent == EVENT_HIT) {
            dispatchEvent = EVENT_FLIP_TRIGGER;
        }
        if (dispatchEvent == EVENT_IMMUNE) {
            dispatchEvent = EVENT_FLIP_TRIGGER;
        }
    }

    if (gBattleStatus.flags1 & BS_FLAGS1_SP_EVT_ACTIVE) {
        if (battleStatus->currentAttackElement & DAMAGE_TYPE_FIRE) {
            if (dispatchEvent == EVENT_HIT) {
                dispatchEvent = EVENT_BURN_HIT;
            }
            if (dispatchEvent == EVENT_DEATH) {
                dispatchEvent = EVENT_BURN_DEATH;
            }
            isFireDamage = TRUE;
        }
    }

    if ((gBattleStatus.flags1 & BS_FLAGS1_SP_EVT_ACTIVE)
        && battleStatus->lastAttackDamage >= 0
        && dispatchEvent != EVENT_DEATH
        && dispatchEvent != EVENT_SPIN_SMASH_DEATH
        && dispatchEvent != EVENT_EXPLODE_TRIGGER
    ) {
        // Using this macro because it's nicer to look at, and it also is necessary to wrap the first 5 of these in a
        // do-while-0 OR to wrap each one individually. It's more likely that it's a macro instead, and much cleaner
        #define INFLICT_STATUS(STATUS_TYPE) \
            do { \
                if ((battleStatus->currentAttackStatus & STATUS_FLAG_##STATUS_TYPE) && \
                    try_inflict_status(target, STATUS_##STATUS_TYPE, STATUS_##STATUS_TYPE##_TURN_MOD)) { \
                    wasStatusInflicted = TRUE; \
                } \
            } while (0);

        INFLICT_STATUS(SHRINK);
        INFLICT_STATUS(POISON);
        INFLICT_STATUS(STONE);
        INFLICT_STATUS(SLEEP);
        INFLICT_STATUS(STOP);
        INFLICT_STATUS(STATIC);
        INFLICT_STATUS(FEAR);
        INFLICT_STATUS(PARALYZE);
        INFLICT_STATUS(DIZZY);

        #undef INFLICT_STATUS

        if (wasStatusInflicted) {
            if (dispatchEvent == EVENT_ZERO_DAMAGE) {
                dispatchEvent = EVENT_HIT_COMBO;
            }
            if (dispatchEvent == EVENT_IMMUNE) {
                dispatchEvent = EVENT_HIT;
            }
        }

    }

    temp = target->actorBlueprint->spookChance;
    temp = (battleStatus->statusChance * temp) / 100;

    if ((gBattleStatus.flags1 & BS_FLAGS1_SP_EVT_ACTIVE) && (battleStatus->currentAttackElement & DAMAGE_TYPE_FEAR)) {
        if (rand_int(99) < temp
            && (target->debuff != STATUS_FEAR
                && target->debuff != STATUS_DIZZY
                && target->debuff != STATUS_PARALYZE
                && target->debuff != STATUS_SLEEP
                && target->debuff != STATUS_FROZEN
                && target->debuff != STATUS_STOP)
            && !(target->flags & ACTOR_FLAG_FLIPPED))
        {
            dispatchEvent = EVENT_SCARE_AWAY;
            hitResult = HIT_RESULT_HIT;
            sp1C = TRUE;
            gBattleStatus.flags1 |= BS_FLAGS1_SP_EVT_ACTIVE | BS_FLAGS1_10 | BS_FLAGS1_8 | BS_FLAGS1_ACTORS_VISIBLE;
            sfx_play_sound_at_position(SOUND_231, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
            wasStatusInflicted = TRUE;
            gBattleStatus.flags1 |= BS_FLAGS1_40;
        } else {
            dispatchEvent = EVENT_IMMUNE;
            hitResult = HIT_RESULT_NO_DAMAGE;
        }
    }

    battleStatus->wasStatusInflicted = wasStatusInflicted;

    if ((sp1C
        && (gBattleStatus.flags1 & (BS_FLAGS1_40 | BS_FLAGS1_200)))
        || ((gBattleStatus.flags1 & (BS_FLAGS1_40 | BS_FLAGS1_200)) && !(gBattleStatus.flags1 & BS_FLAGS1_80))
    ) {
        if (battleStatus->lastAttackDamage > 0) {
            sfx_play_sound_at_position(SOUND_231, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
        }

        if (battleStatus->lastAttackDamage > 0 || (battleStatus->currentAttackElement & DAMAGE_TYPE_STATUS_ALWAYS_HITS) && sp1C) {
            if (gBattleStatus.flags1 & BS_FLAGS1_40) {
                func_802667F0(0, target, state->goalPos.x, state->goalPos.y, state->goalPos.z);
            } else {
                func_802667F0(3, target, state->goalPos.x, state->goalPos.y, state->goalPos.z);
            }
        }
    }

    if (gBattleStatus.flags1 & BS_FLAGS1_SP_EVT_ACTIVE) {
        func_80266970(target);
    }

    dispatch_event_actor(target, dispatchEvent);

    if (actorClass == ACTOR_PARTNER) {
        if ((battleStatus->lastAttackDamage > 0) && (gBattleStatus.flags1 & BS_FLAGS1_SP_EVT_ACTIVE)) {
            inflict_status(target, STATUS_DAZE, battleStatus->lastAttackDamage);
        }
    }

    if (!(target->flags & ACTOR_FLAG_NO_DMG_POPUP)) {
        if (battleStatus->lastAttackDamage == 0) {
            if (!sp1C && !wasStatusInflicted) {
                func_8024EFE0(state->goalPos.x, state->goalPos.y, state->goalPos.z, 0, 1, 3);
            }
        } else if (!partImmuneToElement) {
            if (battleStatus->currentAttackElement & (DAMAGE_TYPE_SMASH | DAMAGE_TYPE_NO_OTHER_DAMAGE_POPUPS)) {
                show_damage_popup(state->goalPos.x, state->goalPos.y, state->goalPos.z, battleStatus->lastAttackDamage, 0);
            } else {
                func_802664DC(state->goalPos.x, state->goalPos.y, state->goalPos.z, battleStatus->lastAttackDamage, 0);
            }
            if (!(targetPart->targetFlags & 4)) {
                func_802666E4(target, state->goalPos.x, state->goalPos.y, state->goalPos.z, battleStatus->lastAttackDamage);
            }
        }
    }

    if (battleStatus->lastAttackDamage > 0 && !partImmuneToElement) {
        func_80267018(target, 1);
        if (isFireDamage) {
            sfx_play_sound_at_position(SOUND_HIT_FIRE, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
        } else if (isShockDamage) {
            sfx_play_sound_at_position(SOUND_HIT_SHOCK, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
        } else if (isIceDamage) {
            sfx_play_sound_at_position(SOUND_HIT_ICE, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
        } else {
            sfx_play_sound_at_position(SOUND_HIT_NORMAL, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
        }
    }

    if ((battleStatus->lastAttackDamage <= 0 && !wasStatusInflicted) || (targetPart->flags & ACTOR_FLAG_2000)) {
        sfx_play_sound_at_position(SOUND_IMMUNE, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
    }

    if ((battleStatus->currentAttackStatus & STATUS_FLAG_SLEEP) && wasStatusInflicted) {
        script = start_script(&DoSleepHit, EVT_PRIORITY_A, 0);
        script->varTable[0] = state->goalPos.x;
        script->varTable[1] = state->goalPos.y;
        script->varTable[2] = state->goalPos.z;
        sfx_play_sound_at_position(SOUND_INFLICT_SLEEP, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
    }
    if ((battleStatus->currentAttackStatus & STATUS_FLAG_DIZZY) && wasStatusInflicted) {
        script = start_script(&DoDizzyHit, EVT_PRIORITY_A, 0);
        script->varTable[0] = state->goalPos.x;
        script->varTable[1] = state->goalPos.y;
        script->varTable[2] = state->goalPos.z;
        sfx_play_sound_at_position(SOUND_INFLICT_STATUS, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
    }
    if ((battleStatus->currentAttackStatus & STATUS_FLAG_PARALYZE) && wasStatusInflicted) {
        script = start_script(&DoParalyzeHit, EVT_PRIORITY_A, 0);
        script->varTable[0] = state->goalPos.x;
        script->varTable[1] = state->goalPos.y;
        script->varTable[2] = state->goalPos.z;
        sfx_play_sound_at_position(SOUND_INFLICT_STATUS, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
    }
    if ((battleStatus->currentAttackStatus & STATUS_FLAG_POISON) && wasStatusInflicted) {
        script = start_script(&DoPoisonHit, EVT_PRIORITY_A, 0);
        script->varTable[0] = state->goalPos.x;
        script->varTable[1] = state->goalPos.y;
        script->varTable[2] = state->goalPos.z;
        sfx_play_sound_at_position(SOUND_INFLICT_STATUS, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
    }
    if ((battleStatus->currentAttackStatus & STATUS_FLAG_STOP) && wasStatusInflicted) {
        script = start_script(&DoStopHit, EVT_PRIORITY_A, 0);
        script->varTable[0] = state->goalPos.x;
        script->varTable[1] = state->goalPos.y;
        script->varTable[2] = state->goalPos.z;
        sfx_play_sound_at_position(SOUND_INFLICT_STATUS, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
    }
    if ((battleStatus->currentAttackStatus & STATUS_FLAG_FROZEN) && wasStatusInflicted) {
        script = start_script(&DoFreezeHit, EVT_PRIORITY_A, 0);
        script->varTable[0] = state->goalPos.x;
        script->varTable[1] = state->goalPos.y;
        script->varTable[2] = state->goalPos.z;
        script->varTablePtr[3] = target;
        sfx_play_sound_at_position(SOUND_INFLICT_STATUS, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
    }
    if ((battleStatus->currentAttackStatus & STATUS_FLAG_SHRINK) && wasStatusInflicted) {
        script = start_script(&DoShrinkHit, EVT_PRIORITY_A, 0);
        script->varTable[0] = state->goalPos.x;
        script->varTable[1] = state->goalPos.y;
        script->varTable[2] = state->goalPos.z;
        script->varTablePtr[3] = target;
        sfx_play_sound_at_position(SOUND_INFLICT_STATUS, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
    }
    if ((battleStatus->currentAttackElement & DAMAGE_TYPE_SMASH) && (target->actorType == ACTOR_TYPE_GOOMNUT_TREE)) {
        sfx_play_sound_at_position(SOUND_SMASH_GOOMNUT_TREE, SOUND_SPACE_MODE_0, state->goalPos.x, state->goalPos.y, state->goalPos.z);
    }

    func_80266ADC(target);

    if (gBattleStatus.flags1 & (BS_FLAGS1_40 | BS_FLAGS1_200)) {
        if (hitResult == HIT_RESULT_HIT) {
            hitResult = HIT_RESULT_1;
        }
        if (hitResult == HIT_RESULT_NO_DAMAGE) {
            hitResult = HIT_RESULT_3;
        }
    }

    return hitResult;
}

ApiStatus ItemDamageEnemy(Evt* script, s32 isInitialCall) {
    BattleStatus* battleStatus = &gBattleStatus;
    Bytecode* args = script->ptrReadPos;
    s32 hitResultVarOut = *args++;
    s32 flags;
    Actor* actor;
    HitResult hitResult;

    battleStatus->currentAttackElement = *args++;
    battleStatus->currentAttackEventSuppression = 0;
    battleStatus->currentAttackStatus = *args++;
    battleStatus->currentAttackDamage = evt_get_variable(script, *args++);
    flags = *args++;

    if ((flags & (BS_FLAGS1_10 | BS_FLAGS1_SP_EVT_ACTIVE)) == (BS_FLAGS1_10 | BS_FLAGS1_SP_EVT_ACTIVE)) {
        gBattleStatus.flags1 |= BS_FLAGS1_10 | BS_FLAGS1_SP_EVT_ACTIVE;
    } else if (flags & BS_FLAGS1_10) {
        gBattleStatus.flags1 |= BS_FLAGS1_10;
        gBattleStatus.flags1 &= ~BS_FLAGS1_SP_EVT_ACTIVE;
    } else if (flags & BS_FLAGS1_SP_EVT_ACTIVE) {
        gBattleStatus.flags1 &= ~BS_FLAGS1_10;
        gBattleStatus.flags1 |= BS_FLAGS1_SP_EVT_ACTIVE;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_10;
        gBattleStatus.flags1 &= ~BS_FLAGS1_SP_EVT_ACTIVE;
    }

    if (flags & BS_FLAGS1_40) {
        gBattleStatus.flags1 |= BS_FLAGS1_40;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_40;
    }

    if (flags & BS_FLAGS1_200) {
        gBattleStatus.flags1 |= BS_FLAGS1_200;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_200;
    }

    if (flags & BS_FLAGS1_80) {
        gBattleStatus.flags1 |= BS_FLAGS1_80;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_80;
    }

    actor = get_actor(script->owner1.actorID);
    battleStatus->currentTargetID = actor->targetActorID;
    battleStatus->currentTargetPart = actor->targetPartIndex;
    battleStatus->statusChance = battleStatus->currentAttackStatus;

    if (battleStatus->statusChance == STATUS_CHANCE_NEVER) {
        battleStatus->statusChance = 0;
    }

    battleStatus->statusDuration = (battleStatus->currentAttackStatus & 0xF00) >> 8;

    hitResult = calc_item_damage_enemy();
    if (hitResult < 0) {
        return ApiStatus_FINISH;
    }

    evt_set_variable(script, hitResultVarOut, hitResult);
    if (!does_script_exist_by_ref(script)) {
        return ApiStatus_FINISH;
    }

    return ApiStatus_DONE2;
}

ApiStatus ItemSpookEnemy(Evt* script, s32 isInitialCall) {
    BattleStatus* battleStatus = &gBattleStatus;
    Bytecode* args = script->ptrReadPos;
    s32 hitResultVarOut = *args++;
    Actor* actor;
    HitResult hitResult;
    s32 flags;

    battleStatus->currentAttackElement = *args++;
    battleStatus->currentAttackEventSuppression = 0;
    battleStatus->currentAttackStatus = *args++;
    battleStatus->currentAttackStatus |= evt_get_variable(script, *args++);
    battleStatus->currentAttackDamage = evt_get_variable(script, *args++);
    flags = *args++;

    if ((flags & (BS_FLAGS1_10 | BS_FLAGS1_SP_EVT_ACTIVE)) == (BS_FLAGS1_10 | BS_FLAGS1_SP_EVT_ACTIVE)) {
        gBattleStatus.flags1 |= BS_FLAGS1_10 | BS_FLAGS1_SP_EVT_ACTIVE;
    } else if (flags & BS_FLAGS1_10) {
        gBattleStatus.flags1 |= BS_FLAGS1_10;
        gBattleStatus.flags1 &= ~BS_FLAGS1_SP_EVT_ACTIVE;
    } else if (flags & BS_FLAGS1_SP_EVT_ACTIVE) {
        gBattleStatus.flags1 &= ~BS_FLAGS1_10;
        gBattleStatus.flags1 |= BS_FLAGS1_SP_EVT_ACTIVE;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_10;
        gBattleStatus.flags1 &= ~BS_FLAGS1_SP_EVT_ACTIVE;
    }

    if (flags & BS_FLAGS1_40) {
        gBattleStatus.flags1 |= BS_FLAGS1_40;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_40;
    }

    if (flags & BS_FLAGS1_200) {
        gBattleStatus.flags1 |= BS_FLAGS1_200;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_200;
    }

    if (flags & BS_FLAGS1_80) {
        gBattleStatus.flags1 |= BS_FLAGS1_80;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_80;
    }

    actor = get_actor(script->owner1.actorID);
    battleStatus->currentTargetID = actor->targetActorID;
    battleStatus->currentTargetPart = actor->targetPartIndex;
    battleStatus->statusChance = battleStatus->currentAttackStatus;

    if (battleStatus->statusChance == STATUS_CHANCE_NEVER) {
        battleStatus->statusChance = 0;
    }

    battleStatus->statusDuration = (battleStatus->currentAttackStatus & 0xF00) >> 8;

    hitResult = calc_item_damage_enemy();
    if (hitResult < 0) {
        return ApiStatus_FINISH;
    }

    evt_set_variable(script, hitResultVarOut, hitResult);
    if (!does_script_exist_by_ref(script)) {
        return ApiStatus_FINISH;
    }

    return ApiStatus_DONE2;
}

ApiStatus ItemAfflictEnemy(Evt* script, s32 isInitialCall) {
    BattleStatus* battleStatus = &gBattleStatus;
    Bytecode* args = script->ptrReadPos;
    s32 hitResultVarOut = *args++;
    s32 flags;
    Actor* actor;
    HitResult hitResult;

    battleStatus->currentAttackElement = *args++;
    battleStatus->currentAttackEventSuppression = 0;
    battleStatus->currentAttackStatus = evt_get_variable(script, *args++);
    battleStatus->currentAttackDamage = evt_get_variable(script, *args++);
    flags = *args++;

    if ((flags & (BS_FLAGS1_10 | BS_FLAGS1_SP_EVT_ACTIVE)) == (BS_FLAGS1_10 | BS_FLAGS1_SP_EVT_ACTIVE)) {
        gBattleStatus.flags1 |= BS_FLAGS1_10 | BS_FLAGS1_SP_EVT_ACTIVE;
    } else if (flags & BS_FLAGS1_10) {
        gBattleStatus.flags1 |= BS_FLAGS1_10;
        gBattleStatus.flags1 &= ~BS_FLAGS1_SP_EVT_ACTIVE;
    } else if (flags & BS_FLAGS1_SP_EVT_ACTIVE) {
        gBattleStatus.flags1 &= ~BS_FLAGS1_10;
        gBattleStatus.flags1 |= BS_FLAGS1_SP_EVT_ACTIVE;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_10;
        gBattleStatus.flags1 &= ~BS_FLAGS1_SP_EVT_ACTIVE;
    }

    if (flags & BS_FLAGS1_40) {
        gBattleStatus.flags1 |= BS_FLAGS1_40;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_40;
    }

    if (flags & BS_FLAGS1_200) {
        gBattleStatus.flags1 |= BS_FLAGS1_200;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_200;
    }

    if (flags & BS_FLAGS1_80) {
        gBattleStatus.flags1 |= BS_FLAGS1_80;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_80;
    }

    actor = get_actor(script->owner1.actorID);
    battleStatus->currentTargetID = actor->targetActorID;
    battleStatus->currentTargetPart = actor->targetPartIndex;
    battleStatus->statusChance = battleStatus->currentAttackStatus;

    if (battleStatus->statusChance == STATUS_CHANCE_NEVER) {
        battleStatus->statusChance = 0;
    }

    battleStatus->statusDuration = (battleStatus->currentAttackStatus & 0xF00) >> 8;

    hitResult = calc_item_damage_enemy();
    if (hitResult < 0) {
        return ApiStatus_FINISH;
    }

    evt_set_variable(script, hitResultVarOut, hitResult);
    if (!does_script_exist_by_ref(script)) {
        return ApiStatus_FINISH;
    }

    return ApiStatus_DONE2;
}

ApiStatus ItemCheckHit(Evt* script, s32 isInitialCall) {
    BattleStatus* battleStatus = &gBattleStatus;
    Bytecode* args = script->ptrReadPos;
    s32 hitResultVarOut = *args++;
    s32 flags;
    Actor* actor;
    HitResult hitResult;

    battleStatus->currentAttackElement = *args++;
    battleStatus->currentAttackEventSuppression = 0;
    battleStatus->currentAttackStatus = *args++;
    battleStatus->currentAttackDamage = evt_get_variable(script, *args++);
    flags = *args++;

    if ((flags & (BS_FLAGS1_10 | BS_FLAGS1_SP_EVT_ACTIVE)) == (BS_FLAGS1_10 | BS_FLAGS1_SP_EVT_ACTIVE)) {
        gBattleStatus.flags1 |= BS_FLAGS1_10 | BS_FLAGS1_SP_EVT_ACTIVE;
    } else if (flags & BS_FLAGS1_10) {
        gBattleStatus.flags1 |= BS_FLAGS1_10;
        gBattleStatus.flags1 &= ~BS_FLAGS1_SP_EVT_ACTIVE;
    } else if (flags & BS_FLAGS1_SP_EVT_ACTIVE) {
        gBattleStatus.flags1 &= ~BS_FLAGS1_10;
        gBattleStatus.flags1 |= BS_FLAGS1_SP_EVT_ACTIVE;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_10;
        gBattleStatus.flags1 &= ~BS_FLAGS1_SP_EVT_ACTIVE;
    }

    if (flags & BS_FLAGS1_40) {
        gBattleStatus.flags1 |= BS_FLAGS1_40;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_40;
    }

    if (flags & BS_FLAGS1_200) {
        gBattleStatus.flags1 |= BS_FLAGS1_200;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_200;
    }

    if (flags & BS_FLAGS1_80) {
        gBattleStatus.flags1 |= BS_FLAGS1_80;
    } else {
        gBattleStatus.flags1 &= ~BS_FLAGS1_80;
    }

    actor = get_actor(script->owner1.actorID);
    battleStatus->currentTargetID = actor->targetActorID;
    battleStatus->currentTargetPart = actor->targetPartIndex;
    battleStatus->statusChance = battleStatus->currentAttackStatus;

    if (battleStatus->statusChance == STATUS_CHANCE_NEVER) {
        battleStatus->statusChance = 0;
    }

    battleStatus->statusDuration = (battleStatus->currentAttackStatus & 0xF00) >> 8;

    hitResult = calc_item_check_hit();
    if (hitResult < 0) {
        return ApiStatus_FINISH;
    }

    evt_set_variable(script, hitResultVarOut, hitResult);

    return ApiStatus_DONE2;
}
