#include "global.h"
#include "assets/ast_landmaster.h"
#include "assets/ast_aquas.h"
#include "assets/ast_great_fox.h"
#include "assets/ast_versus.h"

Vec3f sShotViewPos;

void PlayerShot_SetupEffect351(Effect* effect, f32 xPos, f32 yPos, f32 zPos) {
    Effect_Initialize(effect);
    effect->obj.status = OBJ_INIT;
    effect->obj.id = OBJ_EFFECT_351;
    effect->obj.pos.x = xPos;
    effect->obj.pos.y = yPos;
    effect->obj.pos.z = zPos;
    effect->vel.z = 0.0f;
    effect->scale2 = 0.3f;
    effect->unk_4A = 100;
    effect->unk_48 = 3;

    if (Rand_ZeroOne() < 0.5f) {
        effect->unk_48 = -effect->unk_48;
    }
    Object_SetInfo(&effect->info, effect->obj.id);
}

void PlayerShot_SpawnEffect351(f32 xPos, f32 yPos, f32 zPos) {
    s32 i;

    for (i = 0; i < ARRAY_COUNT(gEffects); i++) {
        if (gEffects[i].obj.status == OBJ_FREE) {
            PlayerShot_SetupEffect351(&gEffects[i], xPos, yPos, zPos);
            break;
        }
    }
}

void PlayerShot_Initialize(PlayerShot* shot) {
    s32 i;
    u8* ptr = (u8*) shot;

    for (i = 0; i < sizeof(PlayerShot); i++, ptr++) {
        *ptr = 0;
    }
}

void PlayerShot_ExplodeBomb(PlayerShot* shot) {
    f32 var_fs0;
    f32 var_fv0;
    f32 var_fv1;
    s32 var_v0;

    if (shot->unk_5C == 0) {
        shot->vel.x = shot->vel.y = shot->vel.z = shot->obj.rot.x = shot->obj.rot.y = shot->obj.rot.z = 0.0f;
        shot->scale = 1.0f;
        shot->unk_5C = 1;
        shot->timer = 30;
        shot->unk_58 = 150;
        Audio_PlayBombExplodeSfx(shot->sourceId, shot->sfxSource);
        gScreenFlashTimer = 4;
        if (shot->obj.pos.y < (gGroundHeight + 450.0f)) {
            gCameraShake = 15;
            if (gGroundSurface == SURFACE_WATER) {
                Effect_Effect367_Spawn(shot->obj.pos.x, gGroundHeight + 2.0f, shot->obj.pos.z, shot->unk_48 * 0.1f,
                                       shot->unk_48 * 3.0f, 0);
                Effect_Effect367_Spawn(shot->obj.pos.x, gGroundHeight + 2.0f, shot->obj.pos.z, shot->unk_48 * 0.1f,
                                       shot->unk_48 * 3.0f, 5);
                Effect_Effect367_Spawn(shot->obj.pos.x, gGroundHeight + 2.0f, shot->obj.pos.z, shot->unk_48 * 0.1f,
                                       shot->unk_48 * 3.0f, 10);
                Effect_Effect372_Spawn2(shot->obj.pos.x, gGroundHeight, shot->obj.pos.z, shot->unk_48 * 0.05f,
                                        shot->unk_48 * 0.5f);
                Effect_SpawnTimedSfxAtPos(&shot->obj.pos, NA_SE_OB_WATER_BOUND_M);
            } else {
                func_enmy_80062B60(shot->obj.pos.x, shot->obj.pos.z, 0, shot->unk_48 * 3.0f);
            }
        }
        if (gCurrentLevel == LEVEL_BOLSE) {
            var_fv0 = 180.0f - (shot->obj.pos.y / 20.0f);
            if (var_fv0 < 50.0f) {
                var_fv0 = 50.0f;
            }
            var_fv1 = shot->obj.pos.y / 1000.0f;
            if (var_fv1 > 2.0f) {
                var_fv1 = 2.0f;
            }
            Effect_Effect385_Spawn(shot->obj.pos.x, gGroundHeight + 6.0f, shot->obj.pos.z,
                                   shot->unk_48 * (1.5f + var_fv1), var_fv0);
        }

        if (gVersusMode) {
            var_v0 = shot->sourceId + 10;
        } else {
            var_v0 = 0;
        }
        Effect_Effect384_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, shot->unk_48 * 1.5f, var_v0);
        if (gUseDynaFloor) {
            var_fs0 = 5.0f;
            if (shot->obj.pos.y > 300.0f) {
                var_fs0 = 1.0f;
            } else if (shot->obj.pos.y > 200.0f) {
                var_fs0 = 2.0f;
            } else if (shot->obj.pos.y > 100.0f) {
                var_fs0 = 3.0f;
            } else if (shot->obj.pos.y > 0.0f) {
                var_fs0 = 4.0f;
            }
            var_fs0 *= 2.0f;
            Effect_Effect382_Spawn(shot->obj.pos.x, shot->obj.pos.z, 50.0f, 0.0f, var_fs0);
            Effect_Effect382_Spawn(shot->obj.pos.x, shot->obj.pos.z, 46.0f, 19.0f, var_fs0);
            Effect_Effect382_Spawn(shot->obj.pos.x, shot->obj.pos.z, 35.0f, 35.0f, var_fs0);
            Effect_Effect382_Spawn(shot->obj.pos.x, shot->obj.pos.z, 19.0f, 46.0f, var_fs0);
            Effect_Effect382_Spawn(shot->obj.pos.x, shot->obj.pos.z, 0.0f, 50.0f, var_fs0);
            Effect_Effect382_Spawn(shot->obj.pos.x, shot->obj.pos.z, -50.0f, 0.0f, var_fs0);
            Effect_Effect382_Spawn(shot->obj.pos.x, shot->obj.pos.z, -46.0f, 19.0f, var_fs0);
            Effect_Effect382_Spawn(shot->obj.pos.x, shot->obj.pos.z, -35.0f, 35.0f, var_fs0);
            Effect_Effect382_Spawn(shot->obj.pos.x, shot->obj.pos.z, -19.0f, 46.0f, var_fs0);
        }
    }
}

void PlayerShot_Impact(PlayerShot* shot) {
    s32 var_v0_2;
    s32 i;

    if (shot->obj.id == PLAYERSHOT_LOCK_SEARCH) {
        Object_Kill(&shot->obj, shot->sfxSource);
        return;
    }

    shot->obj.pos.z = gShotHitPosZ; // strange order on the globals
    shot->obj.pos.y = gShotHitPosY;
    shot->obj.pos.x = gShotHitPosX;

    if ((shot->obj.id == PLAYERSHOT_BOMB) ||
        ((gCurrentLevel != LEVEL_AQUAS) && (shot->obj.id == PLAYERSHOT_LOCK_ON) && (shot->unk_5C != 0))) {
        if (shot->obj.id == PLAYERSHOT_LOCK_ON) {
            shot->obj.id = PLAYERSHOT_BOMB;
            shot->unk_5C = 0;
        }
        PlayerShot_ExplodeBomb(shot);
    } else if (shot->obj.id == PLAYERSHOT_LOCK_ON) {
        shot->scale = 7.5f;

        for (i = 0; i < ARRAY_COUNT(gActors); i++) {
            gActors[i].lockOnTimers[shot->sourceId] = 0;
        }

        shot->vel.x = shot->vel.y = shot->vel.z = 0.0f;

        if (gCurrentLevel == LEVEL_AQUAS) {
            gLight3R = gLight3G = gLight3B = 0;
            Effect_Effect384_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 10.0f, 4);
            Effect_Effect390_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 0.0f, 0.0f, 0.0f, 0.3f, 30);
        } else {
            if (gVersusMode) {
                var_v0_2 = shot->sourceId + 10;
            } else {
                var_v0_2 = 12;
            }
            Effect_Effect384_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 10.0f, var_v0_2);
            func_effect_8007D2C8(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 4.0f);
        }
    } else {
        shot->obj.status = SHOT_HITMARK;
        shot->unk_60 = 0;
    }
}

void PlayerShot_SetupEffect344(Effect* effect, f32 xPos, f32 yPos, f32 zPos, f32 yRot, f32 xRot, f32 scale, s32 unk44,
                               s32 time) {
    Effect_Initialize(effect);
    effect->obj.status = OBJ_INIT;
    effect->obj.id = OBJ_EFFECT_EXPLOSION_MARK_1;
    effect->obj.pos.x = xPos;
    effect->obj.pos.y = yPos;
    effect->obj.pos.z = zPos;
    effect->obj.rot.y = yRot;
    effect->obj.rot.x = xRot;
    effect->scale2 = scale * 0.5f;
    effect->unk_44 = unk44;
    effect->unk_46 = 80;
    effect->timer_50 = time;
    Object_SetInfo(&effect->info, effect->obj.id);
    Play_PlaySfxNoPlayer(effect->sfxSource, NA_SE_EXPLOSION_S);
}

void PlayerShot_SpawnEffect344(f32 xPos, f32 yPos, f32 zPos, f32 arg3, f32 arg4, f32 arg5, f32 yRot, f32 xRot,
                               f32 scale, s32 unk44, s32 time) {
    s32 i;

    if ((gGroundType != 4) && (gLevelType == LEVELTYPE_PLANET) && (gGroundSurface != SURFACE_WATER) &&
        (gCurrentLevel != LEVEL_SOLAR) && (gCurrentLevel != LEVEL_BOLSE) && (gCurrentLevel != LEVEL_TRAINING) &&
        (gCurrentLevel != LEVEL_ZONESS)) {
        for (i = 0; i < 50; i++) {
            if (gEffects[i].obj.status == OBJ_FREE) {
                PlayerShot_SetupEffect344(&gEffects[i], xPos, yPos, zPos, yRot, xRot, scale, unk44, time);
                break;
            }
        }
    }
}

void PlayerShot_LaserMark1_Setup(Effect* effect, f32 xPos, f32 yPos, f32 zPos, f32 yRot, f32 scale) {
    Effect_Initialize(effect);
    effect->obj.status = OBJ_INIT;
    effect->obj.id = OBJ_EFFECT_LASER_MARK_1;
    effect->obj.pos.x = xPos;
    effect->obj.pos.y = yPos;
    effect->obj.pos.z = zPos;
    effect->obj.rot.y = yRot;
    effect->scale2 = scale * 0.5f;
    effect->timer_50 = 80;
    Object_SetInfo(&effect->info, effect->obj.id);
    Play_PlaySfxNoPlayer(effect->sfxSource, NA_SE_EXPLOSION_S);
}

void PlayerShot_HitGround(f32 xPos, f32 yPos, f32 zPos, f32 yRot, f32 scale) {
    s32 i;

    if ((gGroundType != 4) && (gLevelType == LEVELTYPE_PLANET) && (gGroundSurface <= SURFACE_GRASS) &&
        (gCurrentLevel != LEVEL_TRAINING) && (gCurrentLevel != LEVEL_SOLAR) && (gCurrentLevel != LEVEL_ZONESS)) {
        for (i = 0; i < 50; i++) {
            if (gEffects[i].obj.status == OBJ_FREE) {
                PlayerShot_LaserMark1_Setup(&gEffects[i], xPos, yPos, zPos, yRot, scale);
                func_effect_8007D10C(xPos, yPos, zPos, 2.0f);
                break;
            }
        }
    }
    if (gCurrentLevel == LEVEL_BOLSE) {
        func_effect_8007D10C(xPos, yPos, zPos, 2.0f);
    }
}

s32 PlayerShot_CheckObjectHitbox(PlayerShot* shot, f32* hitboxData, Object* obj) {
    s32 count;
    f32 shotPx;
    f32 shotPy;
    f32 shotPz;
    f32 xySizeMod;
    f32 boxRotX;
    f32 boxRotY;
    f32 boxRotZ;
    f32 rotateBox;
    f32 checkDist;
    Vec3f spAC;
    Vec3f spA0;
    s32 i;
    Hitbox* hitbox;

    checkDist = 2000.0f;
    if ((gCurrentLevel == LEVEL_KATINA) || (gCurrentLevel == LEVEL_SECTOR_Y)) {
        checkDist = 5000.0f;
    } else if (gCurrentLevel == LEVEL_ZONESS) {
        checkDist = 3500.0f;
    }

    if ((fabsf(shot->obj.pos.z - obj->pos.z) < checkDist) && (fabsf(shot->obj.pos.x - obj->pos.x) < checkDist) &&
        (fabsf(shot->obj.pos.y - obj->pos.y) < checkDist)) {
        count = *hitboxData;
        if (count != 0) {
            xySizeMod = 10.0f;
            if ((shot->sourceId < 4) &&
                ((gLaserStrength[shot->sourceId] != LASERS_SINGLE) || (shot->obj.id == PLAYERSHOT_LOCK_ON))) {
                xySizeMod += 30.0f;
            }
            hitboxData++;
            for (i = 0; i < count; i++, hitboxData += 6) {
                rotateBox = 0.0f;
                boxRotZ = 0.0f;
                boxRotY = 0.0f;
                boxRotX = 0.0f;
                if (hitboxData[0] >= HITBOX_SHADOW) {
                    return 0;
                }
                if (hitboxData[0] == HITBOX_ROTATED) {
                    rotateBox = 1.0f;
                    boxRotX = hitboxData[1];
                    boxRotY = hitboxData[2];
                    boxRotZ = hitboxData[3];
                    hitboxData += 4;
                }
                if ((obj->rot.y == 0.0f) && (obj->rot.z == 0.0f) && (obj->rot.x == 0.0f) && (rotateBox == 0.0f)) {
                    shotPx = shot->obj.pos.x;
                    shotPy = shot->obj.pos.y;
                    shotPz = shot->obj.pos.z;
                } else {
                    Matrix_RotateZ(gCalcMatrix, -boxRotZ * M_DTOR, MTXF_NEW);
                    Matrix_RotateX(gCalcMatrix, -boxRotX * M_DTOR, MTXF_APPLY);
                    Matrix_RotateY(gCalcMatrix, -boxRotY * M_DTOR, MTXF_APPLY);
                    Matrix_RotateZ(gCalcMatrix, -obj->rot.z * M_DTOR, MTXF_APPLY);
                    Matrix_RotateX(gCalcMatrix, -obj->rot.x * M_DTOR, MTXF_APPLY);
                    Matrix_RotateY(gCalcMatrix, -obj->rot.y * M_DTOR, MTXF_APPLY);
                    spAC.x = shot->obj.pos.x - obj->pos.x;
                    spAC.y = shot->obj.pos.y - obj->pos.y;
                    spAC.z = shot->obj.pos.z - obj->pos.z;
                    Matrix_MultVec3fNoTranslate(gCalcMatrix, &spAC, &spA0);
                    shotPx = obj->pos.x + spA0.x;
                    shotPy = obj->pos.y + spA0.y;
                    shotPz = obj->pos.z + spA0.z;
                }
                hitbox = (Hitbox*) hitboxData;
                if ((fabsf(hitbox->z.offset + obj->pos.z - shotPz) < (hitbox->z.size + 50.0f)) &&
                    (fabsf(hitbox->x.offset + obj->pos.x - shotPx) < (hitbox->x.size + xySizeMod)) &&
                    (fabsf(hitbox->y.offset + obj->pos.y - shotPy) < (hitbox->y.size + xySizeMod))) {
                    if (shot->obj.id == PLAYERSHOT_LOCK_SEARCH) {
                        Object_Kill(&shot->obj, shot->sfxSource);
                        return 0;
                    }
                    if (!((obj->id >= OBJ_ACTOR_START) && (obj->id < OBJ_ACTOR_MAX))) {
                        PlayerShot_Impact(shot);
                    }
                    if ((obj->id == OBJ_SCENERY_CO_BUILDING_6) || ((obj->id == OBJ_SCENERY_CO_ARCH_3) && (i == 0)) ||
                        ((obj->id == OBJ_SCENERY_CO_BUILDING_1) && (i == 0)) ||
                        ((obj->id == OBJ_SCENERY_CO_BUILDING_4) && (i == 0)) ||
                        ((obj->id == OBJ_SCENERY_CO_BUILDING_3) && (i < 3)) || (obj->id == OBJ_SCENERY_CO_BUILDING_7) ||
                        (obj->id == OBJ_SCENERY_CO_BUILDING_5)) {
                        if (shot->obj.pos.x - (obj->pos.x + hitbox->x.offset) < -hitbox->x.size) {
                            if (hitbox->z.size + (obj->pos.z + hitbox->z.offset) < shot->obj.pos.z) {
                                shot->obj.pos.z = hitbox->z.size + (obj->pos.z + hitbox->z.offset) - 5.0f;
                            }
                            PlayerShot_SpawnEffect344((hitbox->x.offset + obj->pos.x) - (hitbox->x.size + 2.0f),
                                                      shot->obj.pos.y, shot->obj.pos.z, obj->pos.x, obj->pos.z,
                                                      obj->rot.y, obj->rot.y + 90.0f, 0.0f, 2.0f, 1, 20);
                            return i + 1;
                        }
                        if (hitbox->x.size < shot->obj.pos.x - (obj->pos.x + hitbox->x.offset)) {
                            if ((hitbox->z.size + (obj->pos.z + hitbox->z.offset)) < shot->obj.pos.z) {
                                shot->obj.pos.z = hitbox->z.size + (obj->pos.z + hitbox->z.offset) - 5.0f;
                            }
                            PlayerShot_SpawnEffect344(hitbox->x.size + (hitbox->x.offset + obj->pos.x) + 2.0f,
                                                      shot->obj.pos.y, shot->obj.pos.z, obj->pos.x, obj->pos.z,
                                                      obj->rot.y, obj->rot.y + 90.0f, 0.0f, 2.0f, 1, 20);
                            return i + 1;
                        }
                        if (((hitbox->y.size + (obj->pos.y + hitbox->y.offset)) - 10.0f) <= shot->obj.pos.y) {
                            shot->obj.pos.y = ((hitbox->y.size + (obj->pos.y + hitbox->y.offset)) - 10.0f);
                        }
                        PlayerShot_SpawnEffect344(shot->obj.pos.x, shot->obj.pos.y,
                                                  hitbox->z.size + (obj->pos.z + hitbox->z.offset) + 20.0f, obj->pos.x,
                                                  obj->pos.z, 0.0f, 0.0f, 0.0f, 2.0f, 1, 20);
                        return i + 1;
                    }
                    if (obj->id < OBJ_SCENERY_MAX) {
                        Effect_SpawnTimedSfxAtPos(&shot->obj.pos, NA_SE_EN_REFLECT);
                    }
                    return i + 1;
                }
            }
        }
    }
    return 0;
}

s32 PlayerShot_CheckEventHitbox(PlayerShot* shot, Actor* actor) {
    s32 i;
    f32* hitboxData;
    f32 shotPx;
    f32 shotPy;
    f32 shotPz;
    f32 xySizeMod;
    Vec3f sp7C;
    Vec3f sp70;
    Hitbox* hitbox;
    s32 count;

    if ((fabsf(shot->obj.pos.z - actor->obj.pos.z) < 2000.0f) &&
        (fabsf(shot->obj.pos.y - actor->obj.pos.y) < 2000.0f) &&
        (fabsf(shot->obj.pos.x - actor->obj.pos.x) < 2000.0f)) {
        hitboxData = actor->info.hitbox;

        count = *hitboxData++;
        if (count != 0) {
            xySizeMod = 0.0f;
            if (actor->info.unk_16 == 1) {
                xySizeMod = fabsf((actor->obj.pos.z + gPathProgress) / 50.0f) + 10.0f;
                if ((shot->sourceId < 4) &&
                    ((gLaserStrength[shot->sourceId] != LASERS_SINGLE) || (shot->obj.id == PLAYERSHOT_LOCK_ON))) {
                    xySizeMod += 30.0f;
                }
            }

            for (i = 0; i < count; i++, hitboxData += 6) {
                if (actor->info.unk_16 == 1) {
                    shotPx = shot->obj.pos.x;
                    shotPy = shot->obj.pos.y;
                    shotPz = shot->obj.pos.z;
                } else {
                    if (hitboxData[0] == HITBOX_ROTATED) {
                        Matrix_RotateZ(gCalcMatrix, -hitboxData[3] * M_DTOR, MTXF_NEW);
                        Matrix_RotateX(gCalcMatrix, -hitboxData[1] * M_DTOR, MTXF_APPLY);
                        Matrix_RotateY(gCalcMatrix, -hitboxData[2] * M_DTOR, MTXF_APPLY);
                        Matrix_RotateZ(gCalcMatrix, -actor->obj.rot.z * M_DTOR, MTXF_APPLY);
                        Matrix_RotateX(gCalcMatrix, -actor->obj.rot.x * M_DTOR, MTXF_APPLY);
                        Matrix_RotateY(gCalcMatrix, -actor->obj.rot.y * M_DTOR, MTXF_APPLY);
                        hitboxData += 4;
                    } else {
                        Matrix_RotateZ(gCalcMatrix, -actor->obj.rot.z * M_DTOR, MTXF_NEW);
                        Matrix_RotateX(gCalcMatrix, -actor->obj.rot.x * M_DTOR, MTXF_APPLY);
                        Matrix_RotateY(gCalcMatrix, -actor->obj.rot.y * M_DTOR, MTXF_APPLY);
                    }
                    if (((actor->vwork[29].z != 0.0f) || (actor->vwork[29].x != 0.0f) || (actor->rot_0F4.z != 0.0f) ||
                         (actor->vwork[29].y != 0.0f)) &&
                        (actor->eventType != EVID_A6_UMBRA_STATION)) {
                        Matrix_RotateZ(gCalcMatrix, -(actor->vwork[29].z + actor->rot_0F4.z) * M_DTOR, MTXF_APPLY);
                        Matrix_RotateX(gCalcMatrix, -actor->vwork[29].x * M_DTOR, MTXF_APPLY);
                        Matrix_RotateY(gCalcMatrix, -actor->vwork[29].y * M_DTOR, MTXF_APPLY);
                    }
                    sp7C.x = shot->obj.pos.x - actor->obj.pos.x;
                    sp7C.y = shot->obj.pos.y - actor->obj.pos.y;
                    sp7C.z = shot->obj.pos.z - actor->obj.pos.z;
                    Matrix_MultVec3fNoTranslate(gCalcMatrix, &sp7C, &sp70);
                    shotPx = actor->obj.pos.x + sp70.x;
                    shotPy = actor->obj.pos.y + sp70.y;
                    shotPz = actor->obj.pos.z + sp70.z;
                }

                hitbox = (Hitbox*) hitboxData;
                if ((fabsf(hitbox->z.offset + actor->obj.pos.z - shotPz) < (hitbox->z.size + 50.0f)) &&
                    (fabsf(hitbox->x.offset + actor->obj.pos.x - shotPx) < (hitbox->x.size + xySizeMod)) &&
                    (fabsf(hitbox->y.offset + actor->obj.pos.y - shotPy) < (hitbox->y.size + xySizeMod))) {
                    return i + 1;
                }
            }
        }
    }
    return 0;
}

s32 PlayerShot_CheckActorHitbox(PlayerShot* shot, Actor* actor) {
    Object* shotx = &shot->obj;
    f32 temp_fv1 = actor->obj.pos.x - shotx->pos.x;
    f32 temp_fa0 = actor->obj.pos.z - shotx->pos.z;
    f32 var_fa1;
    f32* hitboxData = actor->info.hitbox;

    if (((s32) hitboxData[0] != 0) && (sqrtf(SQ(temp_fv1) + SQ(temp_fa0)) <= 500.0f)) {
        if (shot->sourceId < 4) {
            temp_fv1 = actor->obj.pos.x - gPlayer[0].cam.eye.x;
            temp_fa0 = actor->obj.pos.z + gPathProgress - gPlayer[0].cam.eye.z;
            var_fa1 = (sqrtf(SQ(temp_fv1) + SQ(temp_fa0)) / 50.0f) + 20.0f + 10.0f;
            if (var_fa1 > 200.0f) {
                var_fa1 = 200.0f;
            }
            if (shotx->id == PLAYERSHOT_LOCK_ON) {
                var_fa1 = 30.0f;
            } else if (gLaserStrength[shot->sourceId] != LASERS_SINGLE) {
                var_fa1 += 30.0f;
            }
            if (fabsf(hitboxData[1] + actor->obj.pos.z - shotx->pos.z) < (hitboxData[2] + var_fa1)) {
                if (fabsf(hitboxData[3] + actor->obj.pos.y - shotx->pos.y) < (hitboxData[4] + var_fa1)) {
                    if (fabsf(hitboxData[5] + actor->obj.pos.x - shotx->pos.x) < (hitboxData[6] + var_fa1)) {
                        return 1;
                    }
                }
            }
        } else {
            if (fabsf(hitboxData[1] + actor->obj.pos.z - shotx->pos.z) < hitboxData[2]) {
                if (fabsf(hitboxData[3] + actor->obj.pos.y - shotx->pos.y) < hitboxData[4]) {
                    if (fabsf(hitboxData[5] + actor->obj.pos.x - shotx->pos.x) < hitboxData[6]) {
                        return 1;
                    }
                }
            }
        }
    }

    return 0;
}

bool PlayerShot_CheckSpriteHitbox(PlayerShot* shot, Sprite* sprite) {
    f32* temp_v0 = sprite->info.hitbox;

    if (((s32) temp_v0[0] != 0) && (fabsf(temp_v0[1] + sprite->obj.pos.z - shot->obj.pos.z) < temp_v0[2]) &&
        (fabsf(temp_v0[3] + sprite->obj.pos.y - shot->obj.pos.y) < temp_v0[4]) &&
        (fabsf(temp_v0[5] + sprite->obj.pos.x - shot->obj.pos.x) < temp_v0[6])) {
        return true;
    }
    return false;
}

bool PlayerShot_CheckPolyCollision(PlayerShot* shot, ObjectId objId, Object* obj) {
    Vec3f sp7C;
    Vec3f sp70;
    Vec3f sp64;
    Vec3f sp58;
    Vec3f sp4C;
    f32 sp44[2];
    Vec3f sp38;
    bool useCol2;
    s32 pad1;

    sp7C.x = obj->pos.x - shot->obj.pos.x;
    sp7C.z = obj->pos.z - shot->obj.pos.z;
    // undefined behavior: if this check fails, the function returns no value
    if (((fabsf(sp7C.x) < 1100.0f) && (fabsf(sp7C.z) < 1100.0f) && (shot->obj.pos.y < 900.0f)) ||
        (objId == OBJ_BOSS_SZ_GREAT_FOX) || (objId == ACTOR_EVENT_ID) || (objId == OBJ_SCENERY_ME_TUNNEL)) {
        Matrix_RotateY(gCalcMatrix, -obj->rot.y * M_DTOR, MTXF_NEW);
        sp7C.x = shot->obj.pos.x - obj->pos.x;
        sp7C.y = shot->obj.pos.y - obj->pos.y;
        sp7C.z = shot->obj.pos.z - obj->pos.z;
        Matrix_MultVec3fNoTranslate(gCalcMatrix, &sp7C, &sp70);
        sp7C.x = shot->vel.x;
        sp7C.y = shot->vel.y;
        sp7C.z = shot->vel.z;
        Matrix_MultVec3fNoTranslate(gCalcMatrix, &sp7C, &sp38);
        sp64.x = obj->pos.x + sp70.x;
        sp64.y = obj->pos.y + sp70.y;
        sp64.z = obj->pos.z + sp70.z;
        sp58.x = obj->pos.x;
        sp58.y = obj->pos.y;
        sp58.z = obj->pos.z;
        useCol2 = false;
        switch (objId) {
            case OBJ_ACTOR_ME_MOLAR_ROCK:
                objId = COL1_0;
                break;
            case OBJ_SCENERY_CO_BUMP_4:
            case OBJ_SCENERY_CO_BUMP_5:
                objId = COL2_1;
                useCol2 = true;
                break;
            case OBJ_SCENERY_FO_MOUNTAIN_2:
                objId = COL1_5;
                break;
            case OBJ_SCENERY_FO_MOUNTAIN_3:
                objId = COL1_6;
                break;
            case OBJ_BOSS_FO_BASE:
                objId = COL1_4;
                break;
            case OBJ_BOSS_VE2_BASE:
                objId = COL1_9;
                break;
            case OBJ_BOSS_BO_BASE:
                objId = COL1_7;
                break;
            case ACTOR_EVENT_ID:
                objId = COL1_3;
                break;
            case OBJ_BOSS_SZ_GREAT_FOX:
                objId = COL1_8;
                break;
            case OBJ_SCENERY_ME_TUNNEL:
                objId = COL1_1;
                break;
            case OBJ_SCENERY_AQ_CORAL_REEF_2:
                objId = COL2_18;
                useCol2 = true;
                break;
            case OBJ_SCENERY_VS_KA_FLBASE:
                objId = COL2_17;
                useCol2 = true;
                break;
            case OBJ_SCENERY_AQ_BUMP_1:
                objId = COL2_15;
                useCol2 = true;
                break;
            case OBJ_SCENERY_AQ_BUMP_2:
                objId = COL2_16;
                useCol2 = true;
                break;
            case OBJ_SCENERY_ZO_ISLAND:
                objId = COL2_7;
                useCol2 = true;
                break;
            case OBJ_SCENERY_CO_BUMP_2:
                objId = COL2_2;
                useCol2 = true;
                break;
            case OBJ_SCENERY_CO_BUMP_3:
            case OBJ_SCENERY_MA_TERRAIN_BUMP:
                objId = COL2_3;
                useCol2 = true;
                break;
            case OBJ_SCENERY_VS_PYRAMID_1:
                objId = COL2_4;
                useCol2 = true;
                break;
            case OBJ_SCENERY_VS_PYRAMID_2:
                objId = COL2_6;
                useCol2 = true;
                break;
            case OBJ_SCENERY_AQ_CORAL_REEF_1:
                objId = COL2_14;
                useCol2 = true;
                break;
            case OBJ_SCENERY_MA_FLOOR_1:
                objId = COL2_8;
                useCol2 = true;
                break;
            case OBJ_SCENERY_MA_FLOOR_3:
                objId = COL2_9;
                useCol2 = true;
                break;
            case OBJ_SCENERY_MA_FLOOR_2:
                objId = COL2_10;
                useCol2 = true;
                break;
            case OBJ_SCENERY_MA_FLOOR_4:
                objId = COL2_11;
                useCol2 = true;
                break;
            case OBJ_SCENERY_MA_WALL_3:
                objId = COL2_12;
                useCol2 = true;
                break;
            case OBJ_SCENERY_MA_FLOOR_5:
                objId = COL2_13;
                useCol2 = true;
                break;
            default:
                objId = COL2_0;
                useCol2 = true;
                break;
        }
        if (!useCol2) {
            if (func_col1_800998FC(&sp64, &sp58, &sp38, objId, &sp4C, sp44) > 0) {
                if (shot->obj.id == PLAYERSHOT_LOCK_SEARCH) {
                    Object_Kill(&shot->obj, shot->sfxSource);
                    return false;
                }
                PlayerShot_Impact(shot);
                Effect_SpawnTimedSfxAtPos(&shot->obj.pos, NA_SE_EN_REFLECT);
                if (gCurrentLevel == LEVEL_METEO) {
                    Matrix_RotateY(gCalcMatrix, obj->rot.y * M_DTOR, MTXF_NEW);
                    sp7C.x = sp4C.x - obj->pos.x;
                    sp7C.y = sp4C.y - obj->pos.y;
                    sp7C.z = sp4C.z - obj->pos.z;
                    Matrix_MultVec3fNoTranslate(gCalcMatrix, &sp7C, &sp70);
                    shot->obj.pos.x = obj->pos.x + sp70.x;
                    shot->obj.pos.y = obj->pos.y + sp70.y;
                    shot->obj.pos.z = obj->pos.z + sp70.z;
                }
                if (gCurrentLevel == LEVEL_FORTUNA) {
                    Effect_Effect362_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 3.0f);
                    Effect_Effect362_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 3.0f);
                }
                return true;
            }
        } else if (func_col2_800A3690(&sp64, &sp58, objId, &sp4C)) {
            if (shot->obj.id == PLAYERSHOT_LOCK_SEARCH) {
                Object_Kill(&shot->obj, shot->sfxSource);
                return false;
            }
            PlayerShot_Impact(shot);
            Effect_SpawnTimedSfxAtPos(&shot->obj.pos, NA_SE_EN_REFLECT);
            if (gCurrentLevel == LEVEL_FORTUNA) {
                Effect_Effect362_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 3.0f);
                Effect_Effect362_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 3.0f);
            }
            return true;
        }
        return false;
    }
    // return false;
}

void PlayerShot_ApplyDamageToActor(PlayerShot* shot, Actor* actor, s32 hitIndex) {
    Vec3f sp44 = { 0.0f, 0.0f, -100.0f };
    Vec3f sp38;

    actor->dmgType = DMG_BEAM;
    actor->dmgPart = hitIndex - 1;
    actor->timer_0C2 = 2;
    actor->damage = 10;
    if ((shot->sourceId < 4) && (gPlayer[shot->sourceId].form != FORM_LANDMASTER)) {
        switch (gLaserStrength[shot->sourceId]) {
            case LASERS_TWIN:
                actor->damage = 12;
                break;
            case LASERS_HYPER:
                actor->damage = 15;
                break;
        }
    } else if ((shot->sourceId >= NPC_SHOT_ID) && (gCurrentLevel == LEVEL_SECTOR_X)) {
        if ((gActors[shot->sourceId - NPC_SHOT_ID].obj.id == OBJ_ACTOR_EVENT) &&
            (gActors[shot->sourceId - NPC_SHOT_ID].iwork[12] == TEAM_ID_BILL)) {
            actor->damage = 30;
        }
    }
    if (shot->obj.id == PLAYERSHOT_GFOX_LASER) {
        actor->damage = 100;
    } else if (shot->obj.id == PLAYERSHOT_LOCK_ON) {
        if (gCurrentLevel == LEVEL_AQUAS) {
            actor->damage = 31;
        } else {
            actor->dmgType = DMG_EXPLOSION;
            actor->damage = 30;
            actor->lockOnTimers[shot->sourceId] = 0;
        }
    }
    actor->dmgSource = shot->sourceId + 1;
    actor->hitPos.x = shot->obj.pos.x;
    actor->hitPos.y = shot->obj.pos.y;
    actor->hitPos.z = shot->obj.pos.z;
    if (((actor->obj.id == OBJ_ACTOR_EVENT) && (actor->dmgPart == 0) &&
         ((actor->eventType == EVID_SY_ROBOT_1) || (actor->eventType == EVID_SY_ROBOT_2) ||
          (actor->eventType == EVID_SY_ROBOT_3))) ||
        ((actor->obj.id == OBJ_ACTOR_ALLRANGE) && (actor->fwork[23] > 1.0f)) ||
        ((actor->obj.id == OBJ_ACTOR_EVENT) && (actor->dmgPart == 0) && (actor->eventType == EVID_ME_FLIP_BOT)) ||
        ((actor->obj.id == OBJ_ACTOR_AQ_SCULPIN) && (shot->obj.id != PLAYERSHOT_LOCK_ON) &&
         ((actor->state < 3) || (actor->state >= 5))) ||
        ((actor->obj.id == OBJ_ACTOR_AQ_GAROA) && (shot->obj.id != PLAYERSHOT_LOCK_ON) && (actor->timer_0BC != 0))) {
        if ((actor->obj.id == OBJ_ACTOR_AQ_SCULPIN) && (actor->state < 2)) {
            actor->state = 2;
        }
        if ((shot->obj.id == PLAYERSHOT_LOCK_ON) || (shot->obj.id == PLAYERSHOT_BOMB)) {
            PlayerShot_Impact(shot);
        } else {
            shot->timer = 25;
            shot->obj.rot.y += 90.0f;
            shot->obj.rot.x = RAND_FLOAT(360.0f);
            Matrix_RotateY(gCalcMatrix, shot->obj.rot.y * M_DTOR, MTXF_NEW);
            Matrix_RotateX(gCalcMatrix, shot->obj.rot.x * M_DTOR, MTXF_APPLY);
            Matrix_MultVec3fNoTranslate(gCalcMatrix, &sp44, &sp38);
            shot->vel.x = sp38.x;
            shot->vel.y = sp38.y;
            shot->vel.z = sp38.z;
            shot->obj.pos.x += shot->vel.x * 5.0f;
            shot->obj.pos.y += shot->vel.y * 5.0f;
            shot->obj.pos.z += shot->vel.z * 5.0f;
            actor->timer_0C2 = 3;
            Effect_Effect393_Spawn(actor->obj.pos.x, actor->obj.pos.y, actor->obj.pos.z, 8.0f);
            AUDIO_PLAY_SFX(NA_SE_EN_REFLECT, actor->sfxSource, 4);
            Play_SpawnHitmark(gShotHitPosX, gShotHitPosY, gShotHitPosZ);
        }
    } else {
        PlayerShot_Impact(shot);
    }
}

void PlayerShot_CollisionCheck(PlayerShot* shot) {
    s32 i;
    s32 temp_v0;
    s32 spA4;
    Vec3f test;
    f32 sp94;
    Vec3f sp88;
    Vec3f sp7C;
    Actor* actor;
    Boss* boss;
    Scenery360* scenery360;
    Sprite* sprite;
    Scenery* scenery;
    Effect* effect;
    bool sp60;
    Player* player;

    sp60 = true;
    if (shot->obj.id == PLAYERSHOT_LOCK_SEARCH) {
        sp60 = false;
    }
    if (sp60) {
        for (i = 0, effect = gEffects; i < ARRAY_COUNT(gEffects); i++, effect++) {
            if ((effect->obj.status >= OBJ_ACTIVE) && (effect->info.unk_19 != 0) &&
                (fabsf(shot->obj.pos.z - effect->obj.pos.z) < 200.0f) &&
                (fabsf(shot->obj.pos.x - effect->obj.pos.x) < 100.0f) &&
                (fabsf(shot->obj.pos.y - effect->obj.pos.y) < 100.0f)) {
                if (effect->info.unk_19 == 2) {
                    effect->unk_44 = 1;
                } else {
                    Object_Kill(&effect->obj, effect->sfxSource);
                }
            }
        }
        for (i = 0, actor = &gActors[0]; i < ARRAY_COUNT(gActors); i++, actor++) {
            if ((actor->obj.status >= OBJ_ACTIVE) && (actor->timer_0C2 == 0)) {
                switch (actor->obj.id) {
                    case OBJ_ACTOR_ME_MOLAR_ROCK:
                        if (PlayerShot_CheckPolyCollision(shot, actor->obj.id, &actor->obj)) {
                            actor->dmgType = DMG_BEAM;
                            actor->dmgPart = shot->sourceId;
                            return;
                        }
                        break;
                    case OBJ_ACTOR_EVENT:
                        if (actor->eventType == EVID_SY_SHIP_2) {
                            if (PlayerShot_CheckPolyCollision(shot, ACTOR_EVENT_ID, &actor->obj)) {
                                actor->dmgType = DMG_BEAM;
                                actor->dmgPart = shot->sourceId;
                                return;
                            }
                        } else if (actor->eventType == EVID_ME_BIG_METEOR) {
                            test.x = fabsf(actor->obj.pos.x - shot->obj.pos.x);
                            test.y = fabsf(actor->obj.pos.y - shot->obj.pos.y);
                            test.z = fabsf(actor->obj.pos.z - shot->obj.pos.z);
                            if (VEC3F_MAG(&test) < 1000.0f) {
                                PlayerShot_ApplyDamageToActor(shot, actor, 0);
                            }
                        } else {
                            temp_v0 = PlayerShot_CheckEventHitbox(shot, actor);
                            if (temp_v0 != 0) {
                                PlayerShot_ApplyDamageToActor(shot, actor, temp_v0);
                                return;
                            }
                        }
                        break;
                    default:
                        if (actor->info.unk_16 != 0) {
                            if (actor->obj.id == OBJ_ACTOR_ALLRANGE) {
                                if ((actor->aiType >= AI360_WOLF) && (actor->aiType < AI360_ENEMY) &&
                                    (((gCurrentLevel == LEVEL_VENOM_2) &&
                                      (shot->sourceId <= NPC_SHOT_ID + AI360_PEPPY)) ||
                                     ((gCurrentLevel != LEVEL_VENOM_2) && (shot->sourceId == AI360_FOX))) &&
                                    (shot->obj.id != PLAYERSHOT_LOCK_ON) &&
                                    (fabsf(actor->obj.pos.x - shot->obj.pos.x) < 500.0f) &&
                                    (fabsf(actor->obj.pos.y - shot->obj.pos.y) < 300.0f) &&
                                    (fabsf(actor->obj.pos.z - shot->obj.pos.z) < 500.0f)) {
                                    if (gCurrentLevel == LEVEL_VENOM_2) {
                                        actor->iwork[17] = 1;
                                    } else if (Rand_ZeroOne() < 0.1f) {
                                        actor->iwork[17] = 1;
                                    }
                                }
                            }
                            temp_v0 = PlayerShot_CheckActorHitbox(shot, actor);
                            if (temp_v0 != 0) {
                                PlayerShot_ApplyDamageToActor(shot, actor, temp_v0);
                                return;
                            }
                        } else {
                            temp_v0 = PlayerShot_CheckObjectHitbox(shot, actor->info.hitbox, &actor->obj);
                            if (temp_v0 != 0) {
                                PlayerShot_ApplyDamageToActor(shot, actor, temp_v0);
                                return;
                            }
                        }
                        break;
                }
            }
        }
        if (gLevelMode == LEVELMODE_ALL_RANGE) {
            for (i = 0, player = gPlayer; i < gCamCount; i++, player++) {
                test.x = 50.0f;
                if (gVersusMode && (shot->sourceId < 4) &&
                    ((gLaserStrength[shot->sourceId] != LASERS_SINGLE) || (shot->obj.id == PLAYERSHOT_LOCK_ON))) {
                    test.x = 100.0f;
                }
                if (player->barrelRollAlpha >= 100) {
                    test.x = 150.0f;
                }
                if (player->form == FORM_LANDMASTER) {
                    test.x *= 2.0f;
                    test.y = test.x + 50.0f;
                } else if (player->form == FORM_ON_FOOT) {
                    test.y = test.x + 50.0f;
                } else {
                    test.y = test.x;
                }
                test.z = test.x;
                if ((i != shot->sourceId) &&
                    ((player->state_1C8 == PLAYERSTATE_1C8_ACTIVE) || (player->state_1C8 == PLAYERSTATE_1C8_DOWN)) &&
                    (fabsf(player->trueZpos - shot->obj.pos.z) < test.z) &&
                    (fabsf(player->pos.x - shot->obj.pos.x) < test.x) &&
                    (fabsf(player->pos.y - shot->obj.pos.y) < test.y)) {
                    if (player->barrelRollAlpha >= 100) {
                        shot->obj.rot.y = player->rot.y + player->yRot_114 + 180.0f + 90.0f;
                        shot->obj.rot.x = RAND_FLOAT(360.0f);
                        Matrix_RotateY(gCalcMatrix, shot->obj.rot.y * M_DTOR, MTXF_NEW);
                        Matrix_RotateX(gCalcMatrix, shot->obj.rot.x * M_DTOR, MTXF_APPLY);
                        sp88.x = sp88.y = 0.0f;
                        sp88.z = -400.0f / 3.0f;
                        Matrix_MultVec3fNoTranslate(gCalcMatrix, &sp88, &sp7C);
                        shot->vel.x = sp7C.x;
                        shot->vel.y = sp7C.y;
                        shot->vel.z = sp7C.z;
                        shot->obj.pos.x += 2.0f * shot->vel.x;
                        shot->obj.pos.y += 2.0f * shot->vel.y;
                        shot->obj.pos.z += 2.0f * shot->vel.z;
                        shot->timer = 5;
                        AUDIO_PLAY_SFX(NA_SE_ROLLING_REFLECT, player->sfxSource, 0);
                        Effect_Effect393_Spawn(player->pos.x, player->pos.y, player->trueZpos, 8.0f);
                    } else {
                        PlayerShot_Impact(shot);
                        if (player->hitTimer == 0) {
                            if (gPlayer[0].form == FORM_ON_FOOT) {
                                Player_ApplyDamage(player, 0, 60);
                            } else {
                                Player_ApplyDamage(player, 0, 20);
                            }
                            player->unk_284 = 1;
                            if (gCamCount != 1) {
                                player->attacker = shot->sourceId + 1;
                            }
                            player->knockback.x = shot->vel.x * 0.1f;
                            player->knockback.y = shot->vel.y * 0.1f;
                            player->knockback.z = shot->vel.z * 0.1f;
                            if (player->form != FORM_ON_FOOT) {
                                Effect_Effect341_Spawn(player->pos.x, player->pos.y, player->trueZpos, 2.0f);
                            }
                        }
                    }
                }
            }
        }
    }
    if ((gGroundType == 4) && Ground_801B6AEC(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z + gPathProgress)) {
        PlayerShot_Impact(shot);
        if (shot->obj.id != PLAYERSHOT_LOCK_SEARCH) {
            Effect_Effect359_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 2.0f, 255, 15, 0);
        }
    }
    if (gUseDynaFloor &&
        Play_CheckDynaFloorCollision(&sp94, &spA4, shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z)) {
        PlayerShot_Impact(shot);
        if (shot->obj.id != PLAYERSHOT_LOCK_SEARCH) {
            Effect_Effect381_Spawn(shot->obj.pos.x, sp94, shot->obj.pos.z, 1.0f);
        }
    }

    if ((gLevelMode == LEVELMODE_ALL_RANGE) && (shot->sourceId < 110)) {
        for (i = 0, scenery360 = gScenery360; i < 200; i++, scenery360++) {
            if (scenery360->obj.status == OBJ_ACTIVE) {
                if ((scenery360->obj.id == OBJ_SCENERY_AQ_CORAL_REEF_1) ||
                    (scenery360->obj.id == OBJ_SCENERY_VS_PYRAMID_2) ||
                    (scenery360->obj.id == OBJ_SCENERY_FO_MOUNTAIN_2) ||
                    (scenery360->obj.id == OBJ_SCENERY_FO_MOUNTAIN_3) ||
                    (scenery360->obj.id == OBJ_SCENERY_FO_MOUNTAIN_1) ||
                    (scenery360->obj.id == OBJ_SCENERY_VS_KA_FLBASE) ||
                    (scenery360->obj.id == OBJ_SCENERY_VE2_MOUNTAIN) || (scenery360->obj.id == OBJ_SCENERY_CO_BUMP_1) ||
                    (scenery360->obj.id == OBJ_SCENERY_CO_BUMP_3) || (scenery360->obj.id == OBJ_SCENERY_VS_PYRAMID_1)) {
                    PlayerShot_CheckPolyCollision(shot, scenery360->obj.id, &scenery360->obj);
                } else {
                    PlayerShot_CheckObjectHitbox(shot, scenery360->info.hitbox, &scenery360->obj);
                }
            }
        }
    } else {
        for (i = 0, scenery = gScenery; i < ARRAY_COUNT(gScenery); i++, scenery++) {
            if (scenery->obj.status == OBJ_ACTIVE) {
                if ((scenery->obj.id == OBJ_SCENERY_CO_BUMP_1) || (scenery->obj.id == OBJ_SCENERY_ME_TUNNEL) ||
                    (scenery->obj.id == OBJ_SCENERY_CO_BUMP_4) || (scenery->obj.id == OBJ_SCENERY_CO_BUMP_5) ||
                    (scenery->obj.id == OBJ_SCENERY_AQ_CORAL_REEF_2) || (scenery->obj.id == OBJ_SCENERY_AQ_BUMP_1) ||
                    (scenery->obj.id == OBJ_SCENERY_AQ_BUMP_2) || (scenery->obj.id == OBJ_SCENERY_ZO_ISLAND) ||
                    (scenery->obj.id == OBJ_SCENERY_CO_BUMP_2) || (scenery->obj.id == OBJ_SCENERY_CO_BUMP_3) ||
                    (scenery->obj.id == OBJ_SCENERY_MA_WALL_3) || (scenery->obj.id == OBJ_SCENERY_MA_FLOOR_5) ||
                    (scenery->obj.id == OBJ_SCENERY_CO_BUMP_3)) {
                    PlayerShot_CheckPolyCollision(shot, scenery->obj.id, &scenery->obj);
                } else {
                    temp_v0 = PlayerShot_CheckObjectHitbox(shot, scenery->info.hitbox, &scenery->obj);
                    if (temp_v0 != 0) {
                        scenery->dmgType = DMG_BEAM;
                        scenery->dmgPart = temp_v0 - 1;
                    }
                }
            }
        }
    }
    if (sp60) {
        for (i = 0, sprite = gSprites; i < ARRAY_COUNT(gSprites); i++, sprite++) {
            if (sprite->obj.status == OBJ_ACTIVE) {
                if (sprite->obj.id != OBJ_SPRITE_TI_CACTUS) {
                    if (PlayerShot_CheckSpriteHitbox(shot, sprite)) {
                        sprite->destroy = true;
                    }
                } else {
                    if (PlayerShot_CheckObjectHitbox(shot, sprite->info.hitbox, &sprite->obj) != 0) {
                        sprite->destroy = true;
                    }
                }
            }
        }
    }
    for (i = 0, boss = gBosses; i < ARRAY_COUNT(gBosses); i++, boss++) {
        if ((boss->obj.status == OBJ_ACTIVE) && (boss->timer_05A == 0)) {
            if ((boss->obj.id == OBJ_BOSS_FO_BASE) || (boss->obj.id == OBJ_BOSS_VE2_BASE) ||
                (boss->obj.id == OBJ_BOSS_BO_BASE) || (boss->obj.id == OBJ_BOSS_SZ_GREAT_FOX)) {
                PlayerShot_CheckPolyCollision(shot, boss->obj.id, &boss->obj);
            } else if (boss->obj.id == OBJ_BOSS_BO_BASE_SHIELD) {
                test.x = fabsf(boss->obj.pos.x - shot->obj.pos.x) * .8333333f;
                test.y = fabsf(boss->obj.pos.y - shot->obj.pos.y) * 2;
                test.z = fabsf(boss->obj.pos.z - shot->obj.pos.z) * 0.8333333f;
                if (VEC3F_MAG(&test) < 1500.0f) {
                    if (shot->obj.id == PLAYERSHOT_LOCK_SEARCH) {
                        Object_Kill(&shot->obj, shot->sfxSource);
                    } else {
                        boss->dmgType = DMG_BEAM;
                        PlayerShot_Impact(shot);
                        AUDIO_PLAY_SFX(NA_SE_EN_BARRIER_REFLECT, shot->sfxSource, 4);
                    }
                }
            } else {
                if (boss->obj.id == OBJ_BOSS_KA_SAUCERER) {
                    test.x = fabsf(boss->obj.pos.x - shot->obj.pos.x);
                    test.y = fabsf(boss->obj.pos.y - 300.0f - shot->obj.pos.y) * 7.42f;
                    test.z = fabsf(boss->obj.pos.z - shot->obj.pos.z);
                    if (VEC3F_MAG(&test) < 2700.0f) {
                        if (shot->obj.id == PLAYERSHOT_LOCK_SEARCH) {
                            Object_Kill(&shot->obj, shot->sfxSource);
                        } else {
                            boss->dmgType = DMG_BEAM;
                            boss->dmgPart = 100;
                            AUDIO_PLAY_SFX(NA_SE_EN_REFLECT, shot->sfxSource, 4);
                            PlayerShot_Impact(shot);
                        }
                    }
                }
                temp_v0 = PlayerShot_CheckObjectHitbox(shot, boss->info.hitbox, &boss->obj);
                if (temp_v0 != 0) {
                    if (shot->obj.id == PLAYERSHOT_LOCK_SEARCH) {
                        Object_Kill(&shot->obj, shot->sfxSource);
                    } else {
                        if ((boss->obj.id == OBJ_BOSS_KA_SAUCERER) && (shot->sourceId != TEAM_ID_FOX)) {
                            break;
                        }
                        boss->dmgType = DMG_BEAM;
                        if (shot->obj.id == PLAYERSHOT_BOMB) {
                            boss->dmgType = DMG_BOMB;
                            boss->damage = 20;
                        } else if (shot->obj.id == PLAYERSHOT_LOCK_ON) {
                            if (gCurrentLevel == LEVEL_AQUAS) {
                                boss->damage = 31;
                            } else {
                                boss->damage = 30;
                            }
                        } else {
                            boss->damage = 10;
                            if (shot->sourceId < 4) {
                                switch (gLaserStrength[shot->sourceId]) {
                                    case LASERS_SINGLE:
                                    case LASERS_TWIN:
                                        break;
                                    case LASERS_HYPER:
                                        boss->damage = 15;
                                        break;
                                }
                            }
                        }
                        if (boss->obj.id == OBJ_BOSS_SY_SHOGUN) {
                            boss->swork[19] = shot->sourceId;
                        }
                        boss->dmgPart = temp_v0 - 1;
                        if (boss->obj.id == OBJ_BOSS_AQ_UNK_301) {
                            boss->swork[5] = shot->obj.id;
                        } else if (boss->obj.id == OBJ_BOSS_VE1_GOLEMECH) {
                            boss->fwork[47] = shot->obj.pos.x;
                            boss->fwork[48] = shot->obj.pos.y;
                            boss->fwork[49] = shot->obj.pos.z;
                        }
                    }
                }
            }
        }
    }
}

void PlayerShot_DrawHitmark(PlayerShot* shot) {
    Vec3f sp4C = { 0.0f, 0.0f, 0.0f };
    s32 isDrawn = false;

    Matrix_Translate(gGfxMatrix, shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z + gPathProgress, MTXF_APPLY);
    Matrix_MultVec3f(gGfxMatrix, &sp4C, &sShotViewPos);
    if ((sShotViewPos.z < 0.0f) && (sShotViewPos.z > -10000.0f)) {
        if (fabsf(sShotViewPos.x) < (fabsf(sShotViewPos.z * 0.5f) + 500.0f)) {
            if (fabsf(sShotViewPos.y) < (fabsf(sShotViewPos.z * 0.5f) + 500.0f)) {
                isDrawn = true;
            }
        }
    }
    if (!isDrawn) {
        if (!gVersusMode) {
            Object_Kill(&shot->obj, shot->sfxSource);
            return;
        }
    } else {
        shot->obj.rot.y = -gPlayer[gPlayerNum].camYaw;
        Matrix_RotateY(gGfxMatrix, shot->obj.rot.y, MTXF_APPLY);
        Matrix_Scale(gGfxMatrix, 2.0f, 2.0f, 2.0f, MTXF_APPLY);
        if (shot->obj.id == PLAYERSHOT_7) {
            Matrix_Scale(gGfxMatrix, 0.2f, 0.2f, 0.2f, MTXF_APPLY);
        }
        Matrix_SetGfxMtx(&gMasterDisp);
        RCP_SetupDL_40();
        switch (shot->unk_60) {
            case 0:
                gSPDisplayList(gMasterDisp++, D_1026090);
                break;
            case 1:
                gSPDisplayList(gMasterDisp++, D_1025800);
                break;
            case 2:
                Matrix_Scale(gGfxMatrix, 1.5f, 0.7f, 1.0f, MTXF_APPLY);
                Matrix_SetGfxMtx(&gMasterDisp);
                gSPDisplayList(gMasterDisp++, D_1025800);
                break;
        }
    }
}

void PlayerShot_DrawLaser(PlayerShot* shot) {
    f32 width;
    f32 length;
    u8 var_a1;
    f32 twinLaserSeparation;
    Gfx* dList;

    dList = D_101AED0;
    if (gCamCount < 4) {
        RCP_SetupDL_21();
        twinLaserSeparation = 9.0f;
        if ((shot->unk_58 == 0) ||
            ((gCurrentLevel == LEVEL_METEO) && (gPlayer[0].state_1C8 == PLAYERSTATE_1C8_LEVEL_INTRO))) {
            width = 4.0f;
            length = 20.0f;
        } else {
            width = 2.0f;
            length = 10.0f;
        }
        if ((gLaserStrength[0] > LASERS_SINGLE) && (shot->unk_58 == 0)) {
            switch (gLaserStrength[0]) {
                case LASERS_TWIN:
                    break;
                case LASERS_HYPER:
                    dList = D_101AD20;
                    break;
            }
            if (gCurrentLevel == LEVEL_AQUAS) {
                twinLaserSeparation = 4.0f;
            }
            Matrix_Scale(gGfxMatrix, width, width, length, MTXF_APPLY);
            Matrix_RotateY(gGfxMatrix, M_PI, MTXF_APPLY);
            Matrix_Translate(gGfxMatrix, twinLaserSeparation, 0.f, 0.0f, MTXF_APPLY);
            Matrix_SetGfxMtx(&gMasterDisp);
            gSPDisplayList(gMasterDisp++, dList);
            Matrix_Translate(gGfxMatrix, -(2.0f * twinLaserSeparation), 0.0f, 0.f, MTXF_APPLY);
            Matrix_SetGfxMtx(&gMasterDisp);
            gSPDisplayList(gMasterDisp++, dList);
        } else {
            var_a1 = 0;
            if ((gCurrentLevel != LEVEL_KATINA) && (shot->sourceId > NPC_SHOT_ID + AI360_PEPPY) &&
                (shot->sourceId != NPC_SHOT_ID + AI360_KATT)) {
                if (gActors[shot->sourceId - NPC_SHOT_ID].obj.id != OBJ_ACTOR_EVENT) {
                    var_a1 = 1;
                } else if (gActors[shot->sourceId - NPC_SHOT_ID].iwork[12] == TEAM_ID_BILL) {
                    var_a1 = 2;
                }
            } else if (gCurrentLevel == LEVEL_KATINA) {
                if (gPlayer[0].state_1C8 != PLAYERSTATE_1C8_LEVEL_INTRO) {
                    if (shot->sourceId > NPC_SHOT_ID + AI360_PEPPY) {
                        if (gActors[shot->sourceId - NPC_SHOT_ID].animFrame == 0) {
                            var_a1 = 1;
                        }
                        if (gActors[shot->sourceId - NPC_SHOT_ID].animFrame == 2) {
                            var_a1 = 2;
                        }
                    }
                } else if (gActors[shot->sourceId - NPC_SHOT_ID].animFrame == 34) {
                    var_a1 = 1;
                }
            }
            switch (var_a1) {
                case 0:
                    Matrix_Scale(gGfxMatrix, width, width, length, MTXF_APPLY);
                    Matrix_RotateY(gGfxMatrix, M_PI, MTXF_APPLY);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, D_101AED0);
                    break;
                case 1:
                    Matrix_Translate(gGfxMatrix, 0.0f, 0.f, 150.0f, MTXF_APPLY);
                    Matrix_Scale(gGfxMatrix, 0.7f, 0.7f, 1.5f, MTXF_APPLY);
                    Matrix_RotateY(gGfxMatrix, M_PI, MTXF_APPLY);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, D_101ABD0);
                    break;
                case 2:
                    Matrix_Scale(gGfxMatrix, width, width, length, MTXF_APPLY);
                    Matrix_RotateY(gGfxMatrix, M_PI, MTXF_APPLY);
                    Matrix_Translate(gGfxMatrix, 25.0f, 0.0f, 0.f, MTXF_APPLY);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, D_101AED0);
                    dList = D_101AED0;
                    Matrix_Translate(gGfxMatrix, -50.0f, 0.0f, 0.f, MTXF_APPLY);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, dList);
                    break;
                case 3:
                    Matrix_Push(&gGfxMatrix);
                    Matrix_Translate(gGfxMatrix, 50.0f, 0.0f, 150.0f, MTXF_APPLY);
                    Matrix_Scale(gGfxMatrix, 0.7f, 0.7f, 1.5f, MTXF_APPLY);
                    Matrix_RotateY(gGfxMatrix, M_PI, MTXF_APPLY);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, D_101ABD0);
                    Matrix_Pop(&gGfxMatrix);
                    Matrix_Translate(gGfxMatrix, -50.0f, 0.0f, 150.0f, MTXF_APPLY);
                    Matrix_Scale(gGfxMatrix, 0.7f, 0.7f, 1.5f, MTXF_APPLY);
                    Matrix_RotateY(gGfxMatrix, M_PI, MTXF_APPLY);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, D_101ABD0);
                    break;
            }
        }
    } else {
        RCP_SetupDL_64();
        switch (shot->sourceId) {
            case 0:
                gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 255, 32, 255);
                break;
            case 1:
                gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 100, 200, 255);
                break;
            case 2:
                gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 32, 255, 32, 255);
                break;
            case 3:
                gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 32, 32, 255, 255);
                break;
        }
        Matrix_Scale(gGfxMatrix, 6.0f, 4.0f, 10.0f, MTXF_APPLY);
        Matrix_RotateY(gGfxMatrix, M_PI, MTXF_APPLY);
        Matrix_SetGfxMtx(&gMasterDisp);
        if (gLaserStrength[shot->sourceId] != LASERS_SINGLE) {
            Matrix_Translate(gGfxMatrix, 6.0f, 0.0f, 0.0f, MTXF_APPLY);
            Matrix_SetGfxMtx(&gMasterDisp);
            gSPDisplayList(gMasterDisp++, D_versus_302D120);
            Matrix_Translate(gGfxMatrix, -12.0f, 0.0f, 0.0f, MTXF_APPLY);
            Matrix_SetGfxMtx(&gMasterDisp);
            gSPDisplayList(gMasterDisp++, D_versus_302D120);
        } else {
            gSPDisplayList(gMasterDisp++, D_versus_302D120);
        }
    }
}

f32 D_800C9BD8[10] = { 100.0f, 100.0f, 0.0f, 0.0f, 0.0f, 50.0f, 0.0f, 40.0f, 30.0f, 100.0f };
u8 D_800C9C00[4] = { 255, 255, 32, 32 };
u8 D_800C9C04[4] = { 255, 32, 255, 32 };
u8 D_800C9C08[4] = { 32, 32, 32, 255 };
void PlayerShot_DrawShot(PlayerShot* shot) {
    Vec3f sp11C = { 0.0f, 0.0f, 0.0f };
    s32 pad[4];
    f32 var_fv1;
    s32 isDrawn = false;

    Matrix_Translate(gGfxMatrix, shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z + gPathProgress, MTXF_APPLY);
    Matrix_MultVec3f(gGfxMatrix, &sp11C, &sShotViewPos);
    if ((sShotViewPos.z < 0.0f) && (sShotViewPos.z > -10000.0f)) {
        var_fv1 = fabsf(sShotViewPos.x); // probably fake. The use of temps was likely different
        if (var_fv1 < (fabsf(sShotViewPos.z * 0.5f) + 500.0f)) {
            if (fabsf(sShotViewPos.y) < (fabsf(sShotViewPos.z * 0.5f) + 500.0f)) {
                isDrawn = true;
            }
        }
    }
    if (!isDrawn) {
        if ((shot->obj.id != PLAYERSHOT_BOMB) && (shot->timer < 10) && !gVersusMode) {
            Object_Kill(&shot->obj, shot->sfxSource);
        }
    } else {
        if ((shot->obj.id == PLAYERSHOT_TANK) || (shot->obj.id == PLAYERSHOT_ON_FOOT) ||
            (shot->obj.id == PLAYERSHOT_7)) {
            shot->obj.rot.y = RAD_TO_DEG(-gPlayer[gPlayerNum].camYaw);
            shot->obj.rot.x = RAD_TO_DEG(gPlayer[gPlayerNum].camPitch);
        }
        if (shot->obj.id != PLAYERSHOT_2) {
            Matrix_RotateY(gGfxMatrix, shot->obj.rot.y * M_DTOR, MTXF_APPLY);
            Matrix_RotateX(gGfxMatrix, shot->obj.rot.x * M_DTOR, MTXF_APPLY);
            Matrix_RotateZ(gGfxMatrix, shot->obj.rot.z * M_DTOR, MTXF_APPLY);
            if ((shot->obj.id == PLAYERSHOT_BOMB) && (shot->unk_5C == 0)) {
                Matrix_RotateZ(gGfxMatrix, shot->vec_2C.z * M_DTOR, MTXF_APPLY);
                Matrix_RotateY(gGfxMatrix, shot->vec_2C.y * M_DTOR, MTXF_APPLY);
                Matrix_RotateX(gGfxMatrix, shot->vec_2C.x * M_DTOR, MTXF_APPLY);
            }
        }
        Matrix_Translate(gGfxMatrix, 0.f, 0.f, D_800C9BD8[shot->obj.id], MTXF_APPLY);
        Matrix_SetGfxMtx(&gMasterDisp);
        switch (shot->obj.id) {
            case PLAYERSHOT_SINGLE_LASER:
            case PLAYERSHOT_TWIN_LASER:
                PlayerShot_DrawLaser(shot);
                break;
            case PLAYERSHOT_BOMB:
                if (shot->unk_5C == 0) {
                    RCP_SetupDL(&gMasterDisp, SETUPDL_29);
                    gSPDisplayList(gMasterDisp++, D_10231A0);
                    Matrix_Pop(&gGfxMatrix);
                    Matrix_Push(&gGfxMatrix);
                    Matrix_Translate(gGfxMatrix, shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z + gPathProgress,
                                     MTXF_APPLY);
                    Matrix_RotateY(gGfxMatrix, -gPlayer[gPlayerNum].camYaw, MTXF_APPLY);
                    Matrix_RotateX(gGfxMatrix, gPlayer[gPlayerNum].camPitch, MTXF_APPLY);
                    Matrix_RotateZ(gGfxMatrix, gGameFrameCount * 40.0f * M_DTOR, MTXF_APPLY);
                    if (((gGameFrameCount % 2) == 0)) {
                        Matrix_Scale(gGfxMatrix, 1.7f, 1.7f, 1.7f, MTXF_APPLY);
                    } else {
                        Matrix_Scale(gGfxMatrix, 1.2f, 1.2f, 1.2f, MTXF_APPLY);
                    }
                    RCP_SetupDL(&gMasterDisp, SETUPDL_49);
                    gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 255, 255, 255);
                    if (gVersusMode) {
                        switch (shot->sourceId) {
                            case 0:
                                gDPSetEnvColor(gMasterDisp++, 255, 255, 32, 128);
                                break;
                            case 1:
                                gDPSetEnvColor(gMasterDisp++, 255, 32, 32, 128);
                                break;
                            case 2:
                                gDPSetEnvColor(gMasterDisp++, 32, 255, 32, 128);
                                break;
                            case 3:
                                gDPSetEnvColor(gMasterDisp++, 32, 32, 255, 128);
                                break;
                        }
                    } else {
                        gDPSetEnvColor(gMasterDisp++, 0, 128, 255, 255);
                    }
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, aOrbDL);
                } else {
                    Matrix_Scale(gGfxMatrix, shot->scale, shot->scale, shot->scale, MTXF_APPLY);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    if (gVersusMode) {
                        RCP_SetupDL_49();
                        gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 255, 255, shot->unk_58);
                        switch (shot->sourceId) {
                            case 0:
                                gDPSetEnvColor(gMasterDisp++, 255, 255, 32, shot->unk_58);
                                break;
                            case 1:
                                gDPSetEnvColor(gMasterDisp++, 255, 32, 32, shot->unk_58);
                                break;
                            case 2:
                                gDPSetEnvColor(gMasterDisp++, 32, 255, 32, shot->unk_58);
                                break;
                            case 3:
                                gDPSetEnvColor(gMasterDisp++, 32, 32, 255, shot->unk_58);
                                break;
                        }
                    } else {
                        RCP_SetupDL_64_2();
                        gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 255, 255, shot->unk_58);
                    }
                    gSPDisplayList(gMasterDisp++, D_1031EC0);
                }
                break;
            case PLAYERSHOT_TANK:
                gSPClearGeometryMode(gMasterDisp++, G_CULL_BACK);
                RCP_SetupDL_40();
                if (gVersusMode) {
                    gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 255, 255, 255);
                    gDPSetEnvColor(gMasterDisp++, D_800C9C00[shot->sourceId], D_800C9C04[shot->sourceId],
                                   D_800C9C08[shot->sourceId], 255);
                    RCP_SetupDL(&gMasterDisp, SETUPDL_49);
                    if (gLaserStrength[shot->sourceId] != LASERS_SINGLE) {
                        Matrix_RotateZ(gGfxMatrix, gGameFrameCount * 48.0f * M_DTOR, MTXF_APPLY);
                        Matrix_Push(&gGfxMatrix);
                        Matrix_Translate(gGfxMatrix, 0.f, 50.0f, 0.0f, MTXF_APPLY);
                        Matrix_RotateZ(gGfxMatrix, gGameFrameCount * 70.0f * M_DTOR, MTXF_APPLY);
                        Matrix_Scale(gGfxMatrix, 2.0f, 2.0f, 2.0f, MTXF_APPLY);
                        Matrix_SetGfxMtx(&gMasterDisp);
                        gSPDisplayList(gMasterDisp++, D_versus_301AD60);
                        Matrix_Pop(&gGfxMatrix);
                        Matrix_Translate(gGfxMatrix, 0.0f, -50.0f, 0.0f, MTXF_APPLY);
                        Matrix_RotateZ(gGfxMatrix, gGameFrameCount * 70.0f * M_DTOR, MTXF_APPLY);
                        Matrix_Scale(gGfxMatrix, 2.0f, 2.0f, 2.0f, MTXF_APPLY);
                        Matrix_SetGfxMtx(&gMasterDisp);
                        gSPDisplayList(gMasterDisp++, D_versus_301AD60);
                    } else {
                        if (((gGameFrameCount % 2) == 0)) {
                            var_fv1 = M_PI;
                        } else {
                            var_fv1 = 0.0f;
                        }
                        Matrix_RotateY(gGfxMatrix, var_fv1, MTXF_APPLY);
                        Matrix_RotateZ(gGfxMatrix, 30.0f * M_DTOR, MTXF_APPLY);
                        Matrix_Scale(gGfxMatrix, 2.0f, 2.0f, 2.0f, MTXF_APPLY);
                        Matrix_SetGfxMtx(&gMasterDisp);
                        gSPDisplayList(gMasterDisp++, D_versus_301AD60);
                    }
                } else {
                    if (((gGameFrameCount % 2) == 0)) {
                        var_fv1 = M_PI;
                    } else {
                        var_fv1 = 0.0f;
                    }
                    Matrix_RotateY(gGfxMatrix, var_fv1, MTXF_APPLY);
                    Matrix_RotateZ(gGfxMatrix, 30.0f * M_DTOR, MTXF_APPLY);
                    Matrix_Scale(gGfxMatrix, 1.0f, 0.65f, 1.0f, MTXF_APPLY);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    RCP_SetupDL_68();
                    gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 255, 255, 255);
                    gDPSetEnvColor(gMasterDisp++, 0, 255, 0, 0);
                    gSPClearGeometryMode(gMasterDisp++, G_CULL_BACK);
                    gSPDisplayList(gMasterDisp++, D_landmaster_3006940);
                }
                break;
            case PLAYERSHOT_ON_FOOT:
            case PLAYERSHOT_7:
                Matrix_Scale(gGfxMatrix, shot->scale, shot->scale, shot->scale, MTXF_APPLY);
                Matrix_SetGfxMtx(&gMasterDisp);
                RCP_SetupDL(&gMasterDisp, SETUPDL_49);
                gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 255, 255, 255);
                gDPSetEnvColor(gMasterDisp++, D_800C9C00[shot->sourceId], D_800C9C04[shot->sourceId],
                               D_800C9C08[shot->sourceId], 255);
                if (gLaserStrength[shot->sourceId] != LASERS_SINGLE) {
                    Matrix_RotateZ(gGfxMatrix, gGameFrameCount * 48.0f * M_DTOR, MTXF_APPLY);
                    Matrix_Push(&gGfxMatrix);
                    Matrix_Translate(gGfxMatrix, 0.f, 40.0f, 0.0f, MTXF_APPLY);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, D_versus_301AEF0);
                    Matrix_Pop(&gGfxMatrix);
                    Matrix_Translate(gGfxMatrix, 0.f, -40.0f, 0.0f, MTXF_APPLY);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, D_versus_301AEF0);
                } else {
                    gSPDisplayList(gMasterDisp++, D_versus_301AEF0);
                }
                break;
            case PLAYERSHOT_LOCK_ON:
                Matrix_RotateY(gGfxMatrix, M_PI, MTXF_APPLY);
                Matrix_SetGfxMtx(&gMasterDisp);
                if (gCurrentLevel == LEVEL_AQUAS) {
                    RCP_SetupDL(&gMasterDisp, SETUPDL_60);
                    gSPDisplayList(gMasterDisp++, D_AQ_600DB80);
                    Matrix_Pop(&gGfxMatrix);
                    Matrix_Push(&gGfxMatrix);
                    Matrix_Translate(gGfxMatrix, shot->obj.pos.x, gGroundHeight, shot->obj.pos.z + gPathProgress,
                                     MTXF_APPLY);
                    Matrix_Scale(gGfxMatrix, shot->vec_2C.x, shot->vec_2C.y, shot->vec_2C.z, MTXF_APPLY);
                    RCP_SetupDL(&gMasterDisp, SETUPDL_49);
                    gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 32, 32, 0, shot->unk_58);
                    gDPSetEnvColor(gMasterDisp++, 32, 16, 16, shot->unk_58);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, aOrbDL);
                } else if (shot->unk_5C == 0) {
                    if (shot->scale > 1.5f) {
                        break;
                    }
                    Matrix_Pop(&gGfxMatrix);
                    Matrix_Push(&gGfxMatrix);
                    Matrix_Translate(gGfxMatrix, shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z + gPathProgress,
                                     MTXF_APPLY);
                    Matrix_RotateY(gGfxMatrix, -gPlayer[gPlayerNum].camYaw, MTXF_APPLY);
                    Matrix_RotateX(gGfxMatrix, gPlayer[gPlayerNum].camPitch, MTXF_APPLY);
                    Matrix_Push(&gGfxMatrix);
                    Matrix_Scale(gGfxMatrix, 20.0f, 20.0f, 20.0f, MTXF_APPLY);
                    RCP_SetupDL(&gMasterDisp, SETUPDL_49);
                    gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 255, 255, 255);
                    if (gVersusMode) {
                        switch (shot->sourceId) {
                            case 0:
                                gDPSetEnvColor(gMasterDisp++, 255, 255, 32, 255);
                                break;
                            case 1:
                                gDPSetEnvColor(gMasterDisp++, 255, 32, 32, 255);
                                break;
                            case 2:
                                gDPSetEnvColor(gMasterDisp++, 32, 255, 32, 255);
                                break;
                            case 3:
                                gDPSetEnvColor(gMasterDisp++, 32, 32, 255, 255);
                                break;
                        }
                    } else {
                        gDPSetEnvColor(gMasterDisp++, 0, 255, 0, 255);
                    }
                    Matrix_RotateZ(gGfxMatrix, gGameFrameCount * 53.0f * M_DTOR, MTXF_APPLY);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, aStarDL);
                    Matrix_RotateZ(gGfxMatrix, gGameFrameCount * -53.0f * 2.0f * M_DTOR, MTXF_APPLY);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, aStarDL);
                    Matrix_Pop(&gGfxMatrix);
                    if (((gGameFrameCount % 2) == 0)) {
                        Matrix_Scale(gGfxMatrix, 1.7f, 1.7f, 1.7f, MTXF_APPLY);
                    } else {
                        Matrix_Scale(gGfxMatrix, 1.3f, 1.3f, 1.3f, MTXF_APPLY);
                    }
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, aOrbDL);
                } else {
                    RCP_SetupDL(&gMasterDisp, SETUPDL_29);
                    gSPDisplayList(gMasterDisp++, D_10231A0);
                    Matrix_Pop(&gGfxMatrix);
                    Matrix_Push(&gGfxMatrix);
                    Matrix_Translate(gGfxMatrix, shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z + gPathProgress,
                                     MTXF_APPLY);
                    Matrix_RotateY(gGfxMatrix, -gPlayer[gPlayerNum].camYaw, MTXF_APPLY);
                    Matrix_RotateX(gGfxMatrix, gPlayer[gPlayerNum].camPitch, MTXF_APPLY);
                    if (((gGameFrameCount % 2) == 0)) {
                        Matrix_Scale(gGfxMatrix, 1.2f, 1.2f, 1.2f, MTXF_APPLY);
                    } else {
                        Matrix_Scale(gGfxMatrix, 1.5f, 1.5f, 1.5f, MTXF_APPLY);
                    }
                    RCP_SetupDL(&gMasterDisp, SETUPDL_49);
                    gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 255, 0, 255);
                    gDPSetEnvColor(gMasterDisp++, 255, 128, 128, 255);
                    Matrix_SetGfxMtx(&gMasterDisp);
                    gSPDisplayList(gMasterDisp++, aOrbDL);
                }
                break;
            case PLAYERSHOT_GFOX_LASER:
                Matrix_RotateY(gGfxMatrix, M_PI, MTXF_APPLY);
                Matrix_Scale(gGfxMatrix, 3.0f, 3.0f, 20.0f, MTXF_APPLY);
                Matrix_SetGfxMtx(&gMasterDisp);
                RCP_SetupDL_40();
                gSPDisplayList(gMasterDisp++, D_GREAT_FOX_E00DFB0);
                break;
        }
    }
}

void PlayerShot_Draw(PlayerShot* shot) {
    switch (shot->obj.status) {
        case SHOT_ACTIVE:
            PlayerShot_DrawShot(shot);
            break;
        case SHOT_HITMARK:
            PlayerShot_DrawHitmark(shot);
            break;
    }
}

void PlayerShot_UpdateHitmark(PlayerShot* shot) {
    shot->unk_60++;
    if (shot->unk_60 > 2) {
        Object_Kill(&shot->obj, shot->sfxSource);
    }
}

void PlayerShot_UpdateShot2(PlayerShot* shot, Player* player) {
    Vec3f sp5C;
    Vec3f sp50;
    Vec3f sp44;
    Vec3f sp38;

    switch (shot->unk_5C) {
        case 0:
            Matrix_RotateY(gCalcMatrix, (player->yRot_114 + player->rot.y + 180.0f) * M_DTOR, MTXF_NEW);
            Matrix_RotateX(gCalcMatrix, -(player->rot.x * M_DTOR), MTXF_APPLY);
            Matrix_RotateZ(gCalcMatrix, -((player->bankAngle + player->rockAngle) * M_DTOR), MTXF_APPLY);
            Matrix_Translate(gCalcMatrix, player->xShake, player->yBob, 0.0f, MTXF_APPLY);
            sp5C.x = 0.0f;
            sp5C.y = 0.0f;
            sp5C.z = player->baseSpeed + 200.0f;
            Matrix_MultVec3f(gCalcMatrix, &sp5C, &sp50);
            shot->vel.x = sp50.x;
            shot->vel.y = sp50.y;
            shot->vel.z = sp50.z - player->boostSpeed;
            sp5C.x = shot->unk_54;
            sp5C.y = -15.0f;
            sp5C.z = 0.0f;
            Matrix_MultVec3f(gCalcMatrix, &sp5C, &sp44);
            shot->unk_48 = player->pos.x + sp44.x;
            shot->unk_4C = player->pos.y + sp44.y;
            shot->unk_50 = player->trueZpos + sp44.z;
            shot->scale += 200.0f;
            if (shot->scale > 5000.0f) {
                shot->scale = 5000.0f;
            }
            for (sp5C.z = 0.0f; sp5C.z <= shot->scale; sp5C.z += 200.0f) {
                Matrix_MultVec3f(gCalcMatrix, &sp5C, &sp38);
                gShotHitPosZ = shot->obj.pos.z;
                gShotHitPosY = shot->obj.pos.y;
                gShotHitPosX = shot->obj.pos.x;
                shot->obj.pos.x = player->pos.x + sp38.x;
                shot->obj.pos.y = player->pos.y + sp38.y;
                shot->obj.pos.z = player->trueZpos + sp38.z;
                Matrix_Push(&gCalcMatrix);
                PlayerShot_CollisionCheck(shot);
                Matrix_Pop(&gCalcMatrix);
                if (shot->obj.pos.y < gGroundHeight) {
                    shot->unk_5C = 0;
                    PlayerShot_HitGround(shot->obj.pos.x, gGroundHeight + 2.0f, shot->obj.pos.z, shot->obj.rot.y, 2.0f);
                    break;
                } else if (shot->unk_5C == 2) {
                    shot->unk_5C = 0;
                    break;
                }
            }
            shot->timer = 30;
            shot->obj.rot.y = player->rot.y + player->yRot_114;
            if (!(gControllerHold->button & U_CBUTTONS) && !(gControllerHold->button & A_BUTTON)) {
                shot->unk_5C = 1;
            }
            break;
        case 1:
            shot->unk_48 += shot->vel.x;
            shot->unk_4C += shot->vel.y;
            shot->unk_50 += shot->vel.z;
            break;
        case 2:
            shot->obj.pos.x -= shot->vel.x;
            shot->obj.pos.y -= shot->vel.y;
            shot->obj.pos.z -= shot->vel.z;
            shot->unk_48 += shot->vel.x;
            shot->unk_4C += shot->vel.y;
            shot->unk_50 += shot->vel.z;
            if (((gGameFrameCount % 4) == 0)) {
                Play_SpawnHitmark(gShotHitPosX, gShotHitPosY, gShotHitPosZ);
            }
            if ((fabsf(shot->obj.pos.x - shot->unk_48) < 200.0f) && (fabsf(shot->obj.pos.y - shot->unk_4C) < 200.0f) &&
                (fabsf(shot->obj.pos.z - shot->unk_50) < 200.0f)) {
                Object_Kill(&shot->obj, shot->sfxSource);
                return;
            }
            break;
    }
    if (shot->timer == 0) {
        Object_Kill(&shot->obj, shot->sfxSource);
    }
    if (shot->unk_5C == 1) {
        if (shot->obj.pos.y < gGroundHeight) {
            PlayerShot_Impact(shot);
            PlayerShot_HitGround(shot->obj.pos.x, gGroundHeight + 2.0f, shot->obj.pos.z, shot->obj.rot.y, 2.0f);
            if (gCurrentLevel == LEVEL_FORTUNA) {
                Effect_Effect362_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 3.0f);
                Effect_Effect362_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 3.0f);
                Effect_Effect362_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 3.0f);
            }
        }
        PlayerShot_CollisionCheck(shot);
    }
}

void PlayerShot_UpdateBeam(PlayerShot* shot, s32 index) {
    Vec3f sp44;
    Vec3f sp38;

    if ((gGroundSurface == SURFACE_WATER) && (shot->obj.pos.y < (gGroundHeight + 50.0f)) && (index == 0)) {
        Effect_Effect372_Spawn1(shot->obj.pos.x, gGroundHeight, shot->obj.pos.z, 0.1f, 1.5f, shot->obj.rot.y + 20.0f);
        Effect_Effect372_Spawn1(shot->obj.pos.x, gGroundHeight, shot->obj.pos.z, 0.1f, 1.5f, shot->obj.rot.y - 20.0f);
    }
    if ((shot->obj.pos.y < gGroundHeight) && (gGroundType != 4)) {
        PlayerShot_Impact(shot);
        shot->obj.pos.y = gGroundHeight + 2;
        if (gCurrentLevel == LEVEL_BOLSE) {
            Effect_SpawnTimedSfxAtPos(&shot->obj.pos, NA_SE_EN_REFLECT);
        }
        if ((gCamCount != 4) && (gCurrentLevel != LEVEL_AQUAS)) {
            if ((shot->sourceId == TEAM_ID_FOX) && (gLaserStrength[0] != LASERS_SINGLE) &&
                (gPlayer[0].form != FORM_LANDMASTER)) {
                Matrix_RotateY(gCalcMatrix, shot->obj.rot.y * M_DTOR, MTXF_NEW);
                Matrix_RotateX(gCalcMatrix, shot->obj.rot.x * M_DTOR, MTXF_APPLY);
                Matrix_RotateZ(gCalcMatrix, shot->obj.rot.z * M_DTOR, MTXF_APPLY);
                sp44.y = sp44.z = 0.0f;
                sp44.x = 40.0f;
                Matrix_MultVec3fNoTranslate(gCalcMatrix, &sp44, &sp38);
                PlayerShot_HitGround(shot->obj.pos.x + sp38.x, gGroundHeight + 2.0f, shot->obj.pos.z + sp38.z,
                                     shot->obj.rot.y, 2.0f);
                sp44.x = -40.0f;
                Matrix_MultVec3fNoTranslate(gCalcMatrix, &sp44, &sp38);
                PlayerShot_HitGround(shot->obj.pos.x + sp38.x, gGroundHeight + 2.0f, shot->obj.pos.z + sp38.z,
                                     shot->obj.rot.y, 2.0f);
            } else {
                PlayerShot_HitGround(shot->obj.pos.x, gGroundHeight + 2.0f, shot->obj.pos.z, shot->obj.rot.y, 2.0f);
            }
        }
        if (gGroundSurface == SURFACE_WATER) {
            Object_Kill(&shot->obj, shot->sfxSource);
            return;
        }
        if (gCurrentLevel == LEVEL_FORTUNA) {
            Effect_Effect362_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 3.0f);
            Effect_Effect362_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 3.0f);
            Effect_Effect362_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 3.0f);
        }
        if (gCurrentLevel == LEVEL_AQUAS) {
            Effect_Effect364_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 3.0f);
            Effect_Effect364_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 3.0f);
            Effect_Effect364_Spawn(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, 3.0f);
            Aquas_Effect366_Spawn(RAND_FLOAT_CENTERED(10.0f) + shot->obj.pos.x,
                                  RAND_FLOAT_CENTERED(10.0f) + shot->obj.pos.y, shot->obj.pos.z, 0.8f, 0);
            Aquas_Effect366_Spawn(RAND_FLOAT_CENTERED(10.0f) + shot->obj.pos.x,
                                  RAND_FLOAT_CENTERED(10.0f) + shot->obj.pos.y, shot->obj.pos.z, 0.8f, 0);
            Aquas_Effect366_Spawn(RAND_FLOAT_CENTERED(10.0f) + shot->obj.pos.x,
                                  RAND_FLOAT_CENTERED(10.0f) + shot->obj.pos.y, shot->obj.pos.z, 0.8f, 0);
        }
    }
    if (shot->timer == 0) {
        Object_Kill(&shot->obj, shot->sfxSource);
        return;
    }
    if ((shot->sourceId < NPC_SHOT_ID + AI360_ENEMY) || ((shot->timer % 2) != 0)) {
        PlayerShot_CollisionCheck(shot);
    }
}

void PlayerShot_UpdateTank(PlayerShot* shot) {
    PlayerShot_UpdateBeam(shot, 0);
}

void PlayerShot_UpdateOnFoot(PlayerShot* shot) {
    PlayerShot_UpdateBeam(shot, 0);
    shot->unk_58 = 0;
}

void PlayerShot_UpdateShot7(PlayerShot* shot) {
    Vec3f sp3C;
    Vec3f sp30;
    Vec3f sp24;

    PlayerShot_UpdateBeam(shot, 0);
    sp3C.x = shot->obj.pos.x;
    sp3C.y = shot->obj.pos.y;
    sp3C.z = shot->obj.pos.z;
    sp30.x = 0.0f;
    sp30.y = 0.0f;
    sp30.z = 0.0f;
    if (!func_col2_800A3690(&sp3C, &sp30, COL2_5, &sp24)) {
        PlayerShot_Impact(shot);
    }
    shot->unk_58 = 0;
}

bool PlayerShot_FindLockTarget(PlayerShot* shot) {
    s32 i;
    s32 j;
    f32 lockRange;
    Actor* actor;
    Player* player;

    if (gLevelMode == LEVELMODE_ON_RAILS) {
        lockRange = 300.0f;
    } else {
        lockRange = 200.0f;
    }
    for (i = 0, actor = &gActors[0]; i < ARRAY_COUNT(gActors); i++, actor++) {
        if ((actor->obj.status == OBJ_ACTIVE) && (actor->info.targetOffset != 0.0f) &&
            ((actor->lockOnTimers[shot->sourceId] == 0) && (fabsf(shot->obj.pos.x - actor->obj.pos.x) <= lockRange) &&
             (fabsf(shot->obj.pos.y - (actor->obj.pos.y + actor->info.targetOffset)) <= lockRange) &&
             (fabsf(shot->obj.pos.z - actor->obj.pos.z) <= lockRange))) {
            actor->lockOnTimers[shot->sourceId] = 20;
            Object_PlayerSfx(gPlayer[shot->sourceId].sfxSource, NA_SE_LOCK_ON, shot->sourceId);
            for (j = 0; j < ARRAY_COUNT(gPlayerShots); j++) {
                if (gPlayerShots[j].obj.id == PLAYERSHOT_LOCK_SEARCH) {
                    Object_Kill(&gPlayerShots[j].obj, gPlayerShots[j].sfxSource);
                }
            }
            D_display_801615B8[shot->sourceId] = 3.0f;
            D_display_801615A8[shot->sourceId] = 300.0f;
            return true;
        }
    }
    if (gVersusMode) {
        for (i = 0, player = gPlayer; i < gCamCount; i++, player++) {
            if ((player->state_1C8 == PLAYERSTATE_1C8_ACTIVE) && !player->somersault &&
                (player->form != FORM_ON_FOOT) && (i != shot->sourceId) && (gVsLockOnTimers[i][shot->sourceId] == 0) &&
                (fabsf(shot->obj.pos.x - player->pos.x) <= lockRange) &&
                (fabsf(shot->obj.pos.y - player->pos.y) <= lockRange) &&
                (fabsf(shot->obj.pos.z - player->trueZpos) <= lockRange)) {
                gVsLockOnTimers[0][shot->sourceId] = gVsLockOnTimers[1][shot->sourceId] =
                    gVsLockOnTimers[2][shot->sourceId] = gVsLockOnTimers[3][shot->sourceId] = 0;
                gVsLockOnTimers[i][shot->sourceId] = 20;
                Object_PlayerSfx(gPlayer[shot->sourceId].sfxSource, NA_SE_LOCK_ON, shot->sourceId);
                for (j = 0; j < ARRAY_COUNT(gPlayerShots); j++) {
                    if (gPlayerShots[j].obj.id == PLAYERSHOT_LOCK_SEARCH) {
                        Object_Kill(&gPlayerShots[j].obj, gPlayerShots[j].sfxSource);
                    }
                }
                D_display_801615B8[shot->sourceId] = 3.0f;
                D_display_801615A8[shot->sourceId] = 300.0f;
                return true;
            }
        }
    }
    return false;
}

void PlayerShot_SearchLockOnTarget(PlayerShot* shot) {
    if (gVersusMode) {
        if ((shot->obj.pos.y < gGroundHeight) || PlayerShot_FindLockTarget(shot) ||
            !(gControllerHold[shot->sourceId].button & A_BUTTON) || (shot->timer == 0)) {
            Object_Kill(&shot->obj, shot->sfxSource);
        }
    } else {
        if ((shot->obj.pos.y < gGroundHeight) || PlayerShot_FindLockTarget(shot) ||
            !(gControllerHold[gMainController].button & A_BUTTON) || (shot->timer == 0)) {
            Object_Kill(&shot->obj, shot->sfxSource);
        }
    }
    PlayerShot_CollisionCheck(shot);
}

void PlayerShot_CheckBossHitbox(PlayerShot* shot) {
    s32 i;
    s32 j;
    f32* hitboxData;
    Boss* boss;
    Vec3f test;
    f32 var_fs2;
    Vec3f sp8C;
    Vec3f sp80;
    s32 var_s6;
    s32 count;

    boss = gBosses;
    for (i = 0; i < ARRAY_COUNT(gBosses); i++, boss++) {
        if ((boss->obj.status == OBJ_ACTIVE) && (boss->timer_05A == 0)) {
            if (boss->obj.id == OBJ_BOSS_KA_SAUCERER) {
                var_s6 = gGameFrameCount % 8U;
                var_fs2 = shot->scale * 40.0f;
            } else {
                var_s6 = gGameFrameCount % 16U;
                var_fs2 = shot->scale * 30.0f;
            }
            hitboxData = boss->info.hitbox;
            count = *hitboxData++;
            if (count != 0) {
                for (j = 0; j < count; j++, hitboxData += 6) {
                    if (hitboxData[0] == HITBOX_ROTATED) {
                        Matrix_RotateZ(gCalcMatrix, -hitboxData[3] * M_DTOR, MTXF_NEW);
                        Matrix_RotateX(gCalcMatrix, -hitboxData[1] * M_DTOR, MTXF_APPLY);
                        Matrix_RotateY(gCalcMatrix, -hitboxData[2] * M_DTOR, MTXF_APPLY);
                        Matrix_RotateZ(gCalcMatrix, -boss->obj.rot.z * M_DTOR, MTXF_APPLY);
                        Matrix_RotateX(gCalcMatrix, -boss->obj.rot.x * M_DTOR, MTXF_APPLY);
                        Matrix_RotateY(gCalcMatrix, -boss->obj.rot.y * M_DTOR, MTXF_APPLY);
                        hitboxData += 4;
                    } else {
                        Matrix_RotateZ(gCalcMatrix, -boss->obj.rot.z * M_DTOR, MTXF_NEW);
                        Matrix_RotateX(gCalcMatrix, -boss->obj.rot.x * M_DTOR, MTXF_APPLY);
                        Matrix_RotateY(gCalcMatrix, -boss->obj.rot.y * M_DTOR, MTXF_APPLY);
                    }
                    if ((j == var_s6) && (hitboxData[1] > -100.0f) && (hitboxData[3] > -100.0f)) {
                        sp8C.x = shot->obj.pos.x - boss->obj.pos.x;
                        sp8C.y = shot->obj.pos.y - boss->obj.pos.y;
                        sp8C.z = shot->obj.pos.z - boss->obj.pos.z;
                        Matrix_MultVec3fNoTranslate(gCalcMatrix, &sp8C, &sp80);
                        test.x = (hitboxData[4] + boss->obj.pos.x) - (boss->obj.pos.x + sp80.x);
                        test.y = (hitboxData[2] + boss->obj.pos.y) - (boss->obj.pos.y + sp80.y);
                        test.z = (hitboxData[0] + boss->obj.pos.z) - (boss->obj.pos.z + sp80.z);
                        if ((gLevelMode == LEVELMODE_ON_RAILS) && (test.z < 0.0f)) {
                            test.z *= 0.6f;
                        }
                        if (VEC3F_MAG(&test) < var_fs2) {
                            boss->dmgPart = j;
                            boss->dmgType = DMG_BOMB;
                            boss->damage = 20;
                        }
                    }
                }
            }
        }
    }
}

void PlayerShot_SetBombLight(PlayerShot* shot) {
    static u8 sPlayerReds[4] = { 255, 255, 32, 32 };
    static u8 sPlayerGreens[4] = { 255, 32, 255, 32 };
    static u8 sPlayerBlues[4] = { 32, 32, 32, 255 };
    f32 brightness;

    gLight3x = shot->obj.pos.x;
    gLight3y = shot->obj.pos.y;
    gLight3z = shot->obj.pos.z;
    if (gVersusMode) {
        gLight3R = sPlayerReds[shot->sourceId];
        gLight3G = sPlayerGreens[shot->sourceId];
        gLight3B = sPlayerBlues[shot->sourceId];
    } else {
        gLight3R = 90;
        gLight3G = 90;
        gLight3B = 255;
    }
    brightness = 0.7f;
    if (shot->unk_5C != 0) {
        brightness = 1.0f;
    }
    Math_SmoothStepToF(&gLight3Brightness, brightness, 1.0f, 0.08f, 0.001f);
}

void PlayerShot_ApplyExplosionDamage(PlayerShot* shot, s32 damage) {
    s32 i;
    f32 dx;
    f32 dy;
    f32 dz;
    s32 pad[2];
    Actor* actor;
    Sprite* sprite;
    Scenery* scenery;
    Effect* effect;
    Player* player;
    f32 radius = shot->scale * 60.0f;

    scenery = gScenery;
    for (i = 0; i < ARRAY_COUNT(gScenery); i++, scenery++) {
        if ((scenery->obj.status == OBJ_ACTIVE) && (scenery->obj.id == OBJ_SCENERY_CO_DOORS)) {
            dx = scenery->obj.pos.x - shot->obj.pos.x;
            dy = scenery->obj.pos.y - shot->obj.pos.y;
            dz = scenery->obj.pos.z - shot->obj.pos.z;
            if (sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < radius) {
                scenery->dmgType = DMG_EXPLOSION;
            }
            scenery->dmgPart = 0;
        }
    }
    sprite = gSprites;
    for (i = 0; i < ARRAY_COUNT(gSprites); i++, sprite++) {
        if ((sprite->obj.status == OBJ_ACTIVE) &&
            ((sprite->obj.id == OBJ_SPRITE_FO_POLE) || (sprite->obj.id == OBJ_SPRITE_TI_CACTUS) ||
             (sprite->obj.id == OBJ_SPRITE_CO_POLE) || (sprite->obj.id == OBJ_SPRITE_CO_TREE))) {
            dx = sprite->obj.pos.x - shot->obj.pos.x;
            dy = sprite->obj.pos.y - shot->obj.pos.y;
            dz = sprite->obj.pos.z - shot->obj.pos.z;
            if (sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < radius) {
                sprite->destroy = true;
            }
        }
    }
    actor = &gActors[0];
    for (i = 0; i < ARRAY_COUNT(gActors); i++, actor++) {
        if ((actor->obj.status == OBJ_ACTIVE) && (actor->timer_0C2 == 0) &&
            !((gCurrentLevel == LEVEL_MACBETH) && (OBJ_ACTOR_MA_LOCOMOTIVE <= actor->obj.id) &&
              (actor->obj.id < OBJ_ACTOR_MA_RAILROAD_SWITCH)) &&
            !((actor->obj.id == OBJ_ACTOR_EVENT) && (actor->iwork[12] != 0)) &&
            ((actor->scale >= 0.0f) || (actor->obj.id == OBJ_ACTOR_BO_SHIELD_REACTOR))) {
            dx = actor->obj.pos.x - shot->obj.pos.x;
            dy = actor->obj.pos.y - shot->obj.pos.y;
            dz = actor->obj.pos.z - shot->obj.pos.z;
            if ((gLevelMode == LEVELMODE_ON_RAILS) && (dz < 0.0f)) {
                dz *= 0.6f;
            }
            actor->hitPos.x = shot->obj.pos.x;
            actor->hitPos.y = shot->obj.pos.y;
            actor->hitPos.z = shot->obj.pos.z;
            if (sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < radius) {
                if ((actor->obj.id == OBJ_ACTOR_CO_RADAR) || (actor->obj.id == OBJ_ACTOR_ME_LASER_CANNON_1) ||
                    (actor->obj.id == OBJ_MISSILE_SEEK_TEAM) || (actor->obj.id == OBJ_ACTOR_ME_HOPBOT) ||
                    (actor->obj.id == OBJ_ACTOR_ME_METEO_BALL) || (actor->obj.id == OBJ_ACTOR_ME_LASER_CANNON_2) ||
                    ((actor->obj.id == OBJ_ACTOR_EVENT) && (actor->eventType == EVID_SUPPLY_CRATE)) ||
                    ((actor->obj.id == OBJ_ACTOR_EVENT) && (actor->eventType == EVID_SX_WARP_GATE)) ||
                    (actor->obj.id == OBJ_ACTOR_CO_MOLE_MISSILE)) {
                    actor->dmgType = DMG_EXPLOSION;
                    actor->dmgPart = 0;
                    actor->dmgSource = shot->sourceId + 1;
                    actor->damage = damage;

                    if (actor->info.bonus != 0) {
                        shot->bonus++;
                    }
                } else if ((actor->obj.id == OBJ_ACTOR_EVENT) && (actor->scale >= 0.5f) && (damage > 30)) {
                    actor->dmgSource = shot->sourceId + 1;
                    actor->vel.x = dx * 0.03f;
                    actor->vel.y = dy * 0.03f;
                    actor->vel.z = dz * 0.03f;
                    actor->obj.status = OBJ_DYING;
                    actor->timer_0BC = RAND_INT(15.0f) + 10;
                    actor->timer_0BE = 0;
                    actor->work_04C = 4;
                    actor->obj.rot.x = Math_ModF(actor->obj.rot.x, 360.0f);
                    if (actor->info.bonus != 0) {
                        shot->bonus++;
                    }
                } else {
                    actor->dmgType = DMG_EXPLOSION;
                    actor->dmgPart = 0;
                    actor->dmgSource = shot->sourceId + 1;
                    actor->damage = damage;
                    if (actor->info.bonus != 0) {
                        shot->bonus++;
                    }
                }
            }
        }
    }
    if (gCurrentLevel == LEVEL_MACBETH) {
        Macbeth_801AD144(shot);
    } else if (gCurrentLevel == LEVEL_VENOM_1) {
        Venom1_8019864C(shot);
    } else {
        PlayerShot_CheckBossHitbox(shot);
    }
    effect = gEffects;
    for (i = 0; i < ARRAY_COUNT(gEffects); i++, effect++) {
        if (effect->obj.status == OBJ_ACTIVE) {
            dx = effect->obj.pos.x - shot->obj.pos.x;
            dy = effect->obj.pos.y - shot->obj.pos.y;
            dz = effect->obj.pos.z - shot->obj.pos.z;
            if (sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < radius) {
                if (effect->info.unk_16 == 0) {
                    Object_Kill(&effect->obj, effect->sfxSource);
                }
                if (effect->info.unk_16 == 2) {
                    effect->obj.pos.x += dx * 0.03f;
                    if (!((gLevelType == LEVELTYPE_PLANET) && (effect->obj.pos.y < 100.0f))) {
                        effect->obj.pos.y += dy * 0.03f;
                    }
                    effect->obj.pos.z += dz * 0.03f;
                    if ((effect->obj.id == OBJ_EFFECT_392) && (effect->state == 0)) {
                        effect->state = 1;
                        effect->unk_44 = 176;
                        effect->unk_4C = 0;
                        effect->vel.x = effect->vel.y = effect->vel.z = 0.0f;
                        effect->scale2 = 20.0f;
                    }
                }
            }
        }
    }
    if (gVersusMode) {
        player = gPlayer;
        for (i = 0; i < gCamCount; i++, player++) {
            if ((i != shot->sourceId) && (player->state_1C8 == PLAYERSTATE_1C8_ACTIVE) && (player->hitTimer == 0)) {
                dx = player->pos.x - shot->obj.pos.x;
                dy = player->pos.y - shot->obj.pos.y;
                dz = player->trueZpos - shot->obj.pos.z;
                if (sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < radius) {
                    player->attacker = shot->sourceId + 1;
                    switch (player->form) {
                        case FORM_ARWING:
                            Player_ApplyDamage(player, 0, 80);
                            break;
                        case FORM_LANDMASTER:
                            Player_ApplyDamage(player, 0, 60);
                            break;
                        case FORM_ON_FOOT:
                            Player_ApplyDamage(player, 0, 180);
                            break;
                    }
                }
            }
        }
    }
}

void PlayerShot_UpdateBomb(PlayerShot* shot) {
    Vec3f test;
    f32 var_ft5;

    switch (shot->unk_5C) {
        case 0:
            if (shot->timer == 0) {
                PlayerShot_ExplodeBomb(shot);
                break;
            }
            if ((shot->obj.pos.y < gGroundHeight) && (gGroundType != 4)) {
                PlayerShot_ExplodeBomb(shot);
                break;
            }
            if ((gPlayer[shot->sourceId].form == FORM_LANDMASTER) || (gPlayer[shot->sourceId].form == FORM_ON_FOOT)) {
                shot->vel.y -= 1.0f;
                Math_SmoothStepToF(&shot->obj.rot.x, -90.0f, 0.05f, 1.0f, 0.0f);
            }
            if (shot->timer < 25) {
                if (gVersusMode) {
                    if (gControllerPress[shot->sourceId].button & gBombButton[shot->sourceId]) {
                        PlayerShot_ExplodeBomb(shot);
                        break;
                    }
                } else {
                    if (gControllerPress[gMainController].button & gBombButton[shot->sourceId]) {
                        PlayerShot_ExplodeBomb(shot);
                        break;
                    }
                }
            }
            if (!((gCurrentLevel == LEVEL_VENOM_ANDROSS) && (gBosses[0].obj.status == OBJ_ACTIVE) &&
                  (gBosses[0].state == 17))) {
                PlayerShot_CollisionCheck(shot);
            }
            PlayerShot_SetBombLight(shot);
            break;
        case 1:
            gGroundClipMode = 2;
            shot->obj.rot.y += 1.0f;
            Math_SmoothStepToF(&shot->scale, shot->unk_48, 0.05f, 1.5f, 0.001f);
            if ((shot->timer > 0) && (shot->timer < 30)) {
                if (!gVersusMode && ((gPlayer[0].state_1C8 == PLAYERSTATE_1C8_ACTIVE) ||
                                     (gPlayer[0].state_1C8 == PLAYERSTATE_1C8_U_TURN))) {
                    test.x = gPlayer[0].pos.x - shot->obj.pos.x;
                    test.y = gPlayer[0].pos.y - shot->obj.pos.y;
                    test.z = gPlayer[0].trueZpos - shot->obj.pos.z;
                    var_ft5 = VEC3F_MAG(&test) * 0.01f;
                    if (var_ft5 > 60.0f) {
                        var_ft5 = 60.0f;
                    }
                    var_ft5 = 60.0f - var_ft5;
                    Math_SmoothStepToF(&D_ctx_801779A8[gMainController], var_ft5 + 5.0f, 1.0f, 3.0f, 0.0f);
                }
                PlayerShot_SetBombLight(shot);
            }
            if (shot->timer == 0) {
                shot->unk_58 -= 8;
                if (shot->unk_58 < 0) {
                    shot->unk_58 = 0;
                    Object_Kill(&shot->obj, shot->sfxSource);
                    gGroundClipMode = 0;
                }
            }
            PlayerShot_ApplyExplosionDamage(shot, 50);
            break;
    }
}

Vec3f sLockOnPos = { 0.0f, 0.0f, 0.0f };

void PlayerShot_UpdateLockOnShot(PlayerShot* shot) {
    s32 i;
    s32 var_a3;
    f32 sp6C;
    f32 sp68;
    f32 sp64;
    f32 sp60;
    f32 sp5C;
    f32 sp58;
    Actor* actor;
    Player* player;

    if ((shot->obj.pos.y < gGroundHeight) || (shot->timer == 1)) {
        if ((gGroundSurface == SURFACE_WATER) && (shot->obj.pos.y < gGroundHeight)) {
            shot->unk_48 = 10.0f;
            Effect_Effect367_Spawn(shot->obj.pos.x, gGroundHeight + 2.0f, shot->obj.pos.z, shot->unk_48 * 0.1f,
                                   shot->unk_48 * 3.0f, 0);
            Effect_Effect367_Spawn(shot->obj.pos.x, gGroundHeight + 2.0f, shot->obj.pos.z, shot->unk_48 * 0.1f,
                                   shot->unk_48 * 3.0f, 5);
            Effect_Effect372_Spawn2(shot->obj.pos.x, gGroundHeight, shot->obj.pos.z, shot->unk_48 * 0.05f,
                                    shot->unk_48 * 0.5f);
            Effect_SpawnTimedSfxAtPos(&shot->obj.pos, NA_SE_OB_WATER_BOUND_M);
        }
        PlayerShot_Impact(shot);
    } else {
        var_a3 = 0;
        if (shot->unk_60 == 0) {
            for (i = 0, actor = &gActors[0]; i < ARRAY_COUNT(gActors); i++, actor++) {
                if ((actor->obj.status == OBJ_ACTIVE) && (actor->info.targetOffset != 0.0f) &&
                    (actor->lockOnTimers[shot->sourceId] != 0)) {
                    var_a3 = 1;
                    actor->lockOnTimers[shot->sourceId] = 2;
                    sLockOnPos = actor->obj.pos;
                    sLockOnPos.y += actor->info.targetOffset;
                }
            }
            for (i = 0, player = gPlayer; i < gCamCount; i++, player++) {
                if (((player->state_1C8 == PLAYERSTATE_1C8_ACTIVE) || (player->state_1C8 == PLAYERSTATE_1C8_U_TURN)) &&
                    (gVsLockOnTimers[i][shot->sourceId] != 0)) {
                    var_a3 = 1;
                    gVsLockOnTimers[i][shot->sourceId] = 2;
                    sLockOnPos.x = player->pos.x;
                    if (player->form == FORM_ARWING) {
                        sLockOnPos.y = player->pos.y;
                    } else {
                        sLockOnPos.y = player->pos.y + 30.0f;
                    }
                    sLockOnPos.z = player->trueZpos;
                }
            }
            if (var_a3 != 0) {
                sp6C = shot->obj.pos.x - sLockOnPos.x;
                sp68 = shot->obj.pos.y - sLockOnPos.y;
                sp64 = shot->obj.pos.z - sLockOnPos.z;
                sp58 = Math_RadToDeg(Math_Atan2F(sp6C, sp64));
                sp5C = Math_RadToDeg(-Math_Atan2F(sp68, sqrtf(SQ(sp6C) + SQ(sp64))));
                if (shot->vec_2C.y >= 360.0f) {
                    shot->vec_2C.y -= 360.0f;
                }
                if (shot->vec_2C.y < 0.0f) {
                    shot->vec_2C.y += 360.0f;
                }
                if (shot->vec_2C.x >= 360.0f) {
                    shot->vec_2C.x -= 360.0f;
                }
                if (shot->vec_2C.x < 0.0f) {
                    shot->vec_2C.x += 360.0f;
                }
                Math_SmoothStepToAngle(&shot->vec_2C.y, sp58, 1.0f, shot->unk_50, 0.0f);
                Math_SmoothStepToAngle(&shot->vec_2C.x, sp5C, 1.0f, shot->unk_50, 0.0f);
                shot->timer = 30;
            } else {
                shot->unk_60 = 1;
            }
        }
        Math_SmoothStepToF(&shot->unk_50, 360.0f, 1.0f, 3.0f, 0.f);
        if (shot->unk_60 != 0) {
            Math_SmoothStepToF(&shot->unk_54, 169.0f, 1.0f, 13.0f, 0.f);
        } else {
            Math_SmoothStepToF(&shot->unk_54, 91.0f, 1.0f, 7.7999997f, 0.f);
        }
        if (!((gPlayer[shot->sourceId].form == FORM_LANDMASTER) && (shot->unk_60 != 0))) {
            Vec3f sp44;
            Vec3f sp38;

            shot->obj.rot.y = shot->vec_2C.y;
            shot->obj.rot.x = shot->vec_2C.x;
            Matrix_RotateY(gCalcMatrix, shot->obj.rot.y * M_DTOR, MTXF_NEW);
            Matrix_RotateX(gCalcMatrix, shot->obj.rot.x * M_DTOR, MTXF_APPLY);
            sp44.x = sp44.y = 0.0f;
            sp44.z = -(shot->unk_54 + 40.0f);
            Matrix_MultVec3f(gCalcMatrix, &sp44, &sp38);
            shot->vel.x = sp38.x;
            shot->vel.y = sp38.y;
            shot->vel.z = sp38.z;
        }
        PlayerShot_CollisionCheck(shot);
        gLight3x = shot->obj.pos.x;
        gLight3y = shot->obj.pos.y;
        gLight3z = shot->obj.pos.z;
        gLight3R = 90;
        gLight3G = 180;
        gLight3B = 90;
        Math_SmoothStepToF(&gLight3Brightness, 0.6f, 1.0f, 0.08f, 0.001f);
    }
}

void PlayerShot_UpdateShot(PlayerShot* shot, s32 index) {
    s32 teamId;
    s32 bonus;

    shot->obj.pos.x += shot->vel.x;
    shot->obj.pos.y += shot->vel.y;
    shot->obj.pos.z += shot->vel.z;
    switch (shot->obj.id) {
        case PLAYERSHOT_SINGLE_LASER:
            PlayerShot_UpdateBeam(shot, index);
            break;
        case PLAYERSHOT_TWIN_LASER:
        case PLAYERSHOT_GFOX_LASER:
            PlayerShot_UpdateBeam(shot, index);
            break;
        case PLAYERSHOT_2:
            PlayerShot_UpdateShot2(shot, &gPlayer[shot->sourceId]);
            break;
        case PLAYERSHOT_BOMB:
            PlayerShot_UpdateBomb(shot);
            break;
        case PLAYERSHOT_LOCK_SEARCH:
            PlayerShot_SearchLockOnTarget(shot);
            break;
        case PLAYERSHOT_TANK:
            PlayerShot_UpdateTank(shot);
            break;
        case PLAYERSHOT_ON_FOOT:
            PlayerShot_UpdateOnFoot(shot);
            break;
        case PLAYERSHOT_7:
            PlayerShot_UpdateShot7(shot);
            break;
        case PLAYERSHOT_LOCK_ON:
            if (shot->scale > 1.5f) {
                PlayerShot_ApplyExplosionDamage(shot, 30);
                if (shot->bonus != 0) {
                    if (gVersusMode) {
                        gPlayerScores[shot->sourceId] += shot->bonus;
                    } else {
                        bonus = shot->bonus;
                        if (shot->bonus > 10) {
                            bonus = BONUS_TEXT_GREAT;
                        }
                        BonusText_Display(shot->obj.pos.x, shot->obj.pos.y, shot->obj.pos.z, bonus);
                        gHitCount += shot->bonus;
                    }
                    if ((shot->bonus >= 7) && !gBossActive && (gLevelMode == LEVELMODE_ON_RAILS) &&
                        ((gTeamShields[TEAM_ID_FALCO] > 0) || (gTeamShields[TEAM_ID_SLIPPY] > 0) ||
                         (gTeamShields[TEAM_ID_PEPPY] > 0))) {
                        do {
                            teamId = RAND_INT(2.9f) + 1;
                        } while (gTeamShields[teamId] <= 0);
                        switch (teamId) {
                            case TEAM_ID_FALCO:
                                Radio_PlayMessage(gMsg_ID_7100, RCID_FALCO);
                                break;
                            case TEAM_ID_SLIPPY:
                                Radio_PlayMessage(gMsg_ID_15252, RCID_SLIPPY);
                                break;
                            case TEAM_ID_PEPPY:
                                Radio_PlayMessage(gMsg_ID_17160, RCID_PEPPY);
                                break;
                        }
                    }
                }
                Object_Kill(&shot->obj, shot->sfxSource);
                Effect_SpawnTimedSfxAtPos(&shot->obj.pos, NA_SE_SPREAD_EXPLOSION);
            } else if (gCurrentLevel == LEVEL_AQUAS) {
                Aquas_801ABA40(shot);
            } else {
                PlayerShot_UpdateLockOnShot(shot);
            }
            break;
    }
}

void PlayerShot_Update(PlayerShot* shot) {
    s32 i;
    s32 ticks;

    switch (shot->obj.status) {
        case SHOT_FREE:
            break;

        case SHOT_ACTIVE:
            ticks = 1;
            switch (shot->obj.id) {
                case PLAYERSHOT_GFOX_LASER:
                    ticks = 4;
                    break;

                case PLAYERSHOT_SINGLE_LASER:
                case PLAYERSHOT_TWIN_LASER:
                    if ((shot->unk_58 == 0) || (gPlayer[0].state_1C8 == PLAYERSTATE_1C8_LEVEL_INTRO)) {
                        ticks = 4;
                    } else {
                        ticks = 3;
                    }
                    break;

                case PLAYERSHOT_TANK:
                    ticks = 2;
                    break;
            }

            for (i = 0; i < ticks && (shot->obj.status == SHOT_ACTIVE); i++) {
                if (shot->timer > 0) {
                    shot->timer--;
                }
                gShotHitPosZ = shot->obj.pos.z;
                gShotHitPosY = shot->obj.pos.y;
                gShotHitPosX = shot->obj.pos.x;
                PlayerShot_UpdateShot(shot, i);
            }
            break;

        case SHOT_HITMARK:
            PlayerShot_UpdateHitmark(shot);
            break;
    }
}

void PlayerShot_UpdateAll(void) {
    s32 i;

    for (i = 0; i < ARRAY_COUNT(gPlayerShots); i++) {
        gPlayerShots[i].index = i;
        PlayerShot_Update(&gPlayerShots[i]);
    }
}

void PlayerShot_DrawAll(void) {
    s32 i;

    for (i = 0; i < ARRAY_COUNT(gPlayerShots); i++) {
        gPlayerShots[i].index = i;
        if (gPlayerShots[i].obj.status != SHOT_FREE) {
            if (!((gReflectY < 0) && (gPlayerShots[i].obj.rot.x < -10.0f))) {
                Matrix_Push(&gGfxMatrix);
                PlayerShot_Draw(&gPlayerShots[i]);
                Matrix_Pop(&gGfxMatrix);
            }
            if (gReflectY > 0) {
                Object_SetSfxSourceToView(gPlayerShots[i].sfxSource, &sShotViewPos);
            }
        }
    }
}
