/**
 * d_a_boomerang.cpp
 * Item - Boomerang
 */

#include "d/dolzel.h" // IWYU pragma: keep
#include "d/actor/d_a_boomerang.h"
#include "d/d_camera.h"
#include "d/d_cc_d.h"
#include "d/d_material.h"
#include "d/actor/d_a_player_main.h"
#include "res/Object/Link.h"
#include "dolphin/gf/GF.h"
#include "f_op/f_op_camera.h"
#include "f_op/f_op_camera_mng.h"
#include "m_Do/m_Do_lib.h"

static cXyz l_blur_top(40.0f, 0.0f, 0.0f);
static cXyz l_blur_root(-40.0f, 0.0f, 0.0f);

/* 800E0C08-800E0D44       .text initBlur__18daBoomerang_blur_cFPA4_fs */
void daBoomerang_blur_c::initBlur(MtxP mtx, s16 yRot) {
    cMtx_multVec(mtx, &l_blur_top, &trail0_vtxArr0[0]);
    cMtx_multVec(mtx, &l_blur_root, &trail0_vtxArr1[0]);
    trail0_vtxArr0[1] = trail0_vtxArr0[0];
    trail0_vtxArr1[1] = trail0_vtxArr1[0];

    numTrailSegments = 0;

    pos.x = mtx[0][3];
    pos.y = mtx[1][3];
    pos.z = mtx[2][3];

    mDoMtx_stack_c::copy(mtx);
    mDoMtx_stack_c::YrotM(-(yRot * 2));
    mDoMtx_stack_c::multVec(&l_blur_top, &trail1_vtxArr0[0]);
    mDoMtx_stack_c::multVec(&l_blur_root, &trail1_vtxArr1[0]);
    trail1_vtxArr0[1] = trail1_vtxArr0[0];
    trail1_vtxArr1[1] = trail1_vtxArr1[0];
}

/* 800E0D44-800E101C       .text copyBlur__18daBoomerang_blur_cFPA4_fs */
void daBoomerang_blur_c::copyBlur(MtxP mtx, s16 yRot) {
    for (int i = 54; i >= 0; i--) {
        trail0_vtxArr0[SEGMENTS_PER_STEP + i] = trail0_vtxArr0[i];
        trail0_vtxArr1[SEGMENTS_PER_STEP + i] = trail0_vtxArr1[i];
        trail1_vtxArr0[SEGMENTS_PER_STEP + i] = trail1_vtxArr0[i];
        trail1_vtxArr1[SEGMENTS_PER_STEP + i] = trail1_vtxArr1[i];
    }

    float t = 0.0f;

    cXyz inPos;
    inPos.x = mtx[0][3];
    inPos.y = mtx[1][3];
    inPos.z = mtx[2][3];

    cXyz diff = pos - inPos;

    pos = inPos;

    mDoMtx_stack_c::push();

    s16 yRotPerStep = 0x633;

    int i;
    for (i = 0; i < SEGMENTS_PER_STEP; i++) {
        mDoMtx_stack_c::multVec(&l_blur_top, &trail0_vtxArr0[i]);
        mDoMtx_stack_c::multVec(&l_blur_root, &trail0_vtxArr1[i]);
        trail0_vtxArr0[i] += diff * t;
        trail0_vtxArr1[i] += diff * t;
        t += 1.0f / SEGMENTS_PER_STEP;
        mDoMtx_stack_c::YrotM(yRotPerStep);
    }

    t = 0.0f;

    mDoMtx_stack_c::pop();

    mDoMtx_stack_c::YrotM(-(yRot * 2));

    for (i = 0; i < SEGMENTS_PER_STEP; i++) {
        mDoMtx_stack_c::multVec(&l_blur_top, &trail1_vtxArr0[i]);
        mDoMtx_stack_c::multVec(&l_blur_root, &trail1_vtxArr1[i]);
        trail1_vtxArr0[i] += diff * t;
        trail1_vtxArr1[i] += diff * t;
        t += 1.0f / SEGMENTS_PER_STEP;
        mDoMtx_stack_c::YrotM(-yRotPerStep);
    }

    numTrailSegments += SEGMENTS_PER_STEP;

    if (numTrailSegments >= 59) {
        numTrailSegments = 58;
    }
}

/* 800E101C-800E13A4       .text draw__18daBoomerang_blur_cFv */
void daBoomerang_blur_c::draw() {
#include "assets/l_matDL__draw__18daBoomerang_blur_cFv.h"

    static GXVtxDescList l_vtxDescList[] ALIGN_DECL(32) = {
        {GX_VA_POS, GX_DIRECT},
        {GX_VA_TEX0, GX_DIRECT},
        {GX_VA_NULL, GX_NONE},
    };

    static GXVtxAttrFmtList l_vtxAttrFmtList[] ALIGN_DECL(32) = {
        {GX_VA_POS, GX_POS_XYZ, GX_F32, 0x00},
        {GX_VA_TEX0, GX_TEX_ST, GX_S16, 0x08},
        {GX_VA_NULL, GX_POS_XYZ, GX_F32, 0x00},
    };

    static GXTexObj texObj;

#if VERSION > VERSION_JPN
    j3dSys.reinitGX();
    GXSetNumIndStages(0);
#endif

    GXInitTexObj(&texObj, imageData, 16, 4, GX_TF_I4, GX_CLAMP, GX_CLAMP, FALSE);
    GXInitTexObjLOD(&texObj, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, FALSE, FALSE, GX_ANISO_1);
    GXLoadTexObj(&texObj, GX_TEXMAP0);

    GXCallDisplayList(l_matDL, 0x80);

    // Light yellow color for the trail.
    static GXColor color0 = {0xFF, 0xFF, 0x7B, 0x96};
    GFSetTevColor(GX_TEVREG0, color0);

    const s32 uCoordStep = 0xFF / ((numTrailSegments >> 1) + 1);
    s16 uCoord1 = uCoordStep;
    s16 uCoord0 = 0;

    GFSetVtxDescv(l_vtxDescList);
    GFSetVtxAttrFmtv(GX_VTXFMT0, l_vtxAttrFmtList);

    GFLoadPosMtxImm(j3dSys.getViewMtx(), GX_PNMTX0);

#define QUAD_VTX(xyz, u, v)                                                                                                                                    \
    GFPosition3f32((xyz).x, (xyz).y, (xyz).z);                                                                                                                 \
    GFTexCoord2s16((u), (v))

    {
        GFBegin(GX_QUADS, GX_VTXFMT0, numTrailSegments * 4 + 4);

        for (int i = numTrailSegments; i >= 0; i--) {
            QUAD_VTX(trail0_vtxArr0[i], uCoord1, 0x00);
            QUAD_VTX(trail0_vtxArr1[i], uCoord1, 0xFF);
            QUAD_VTX(trail0_vtxArr1[i + 1], uCoord0, 0xFF);
            QUAD_VTX(trail0_vtxArr0[i + 1], uCoord0, 0x00);

            uCoord0 = uCoord1;
            uCoord1 += uCoordStep;
        }

        GFEnd();
    }

    {
        uCoord1 = uCoordStep;
        uCoord0 = 0;

        GFBegin(GX_QUADS, GX_VTXFMT0, numTrailSegments * 4 + 4);

        for (int i = numTrailSegments; i >= 0; i--) {
            QUAD_VTX(trail1_vtxArr0[i], uCoord1, 0x00);
            QUAD_VTX(trail1_vtxArr1[i], uCoord1, 0xFF);
            QUAD_VTX(trail1_vtxArr1[i + 1], uCoord0, 0xFF);
            QUAD_VTX(trail1_vtxArr0[i + 1], uCoord0, 0x00);

            uCoord0 = uCoord1;
            uCoord1 += uCoordStep;
        }

        GFEnd();
    }

#if VERSION > VERSION_JPN
    J3DShape::resetVcdVatCache();
#endif
}

#include "assets/l_sightMatDL.h"
#include "assets/l_sightDL__d_a_boomerang.h"

/* 800E13A4-800E14F0       .text draw__25daBoomerang_sightPacket_cFv */
void daBoomerang_sightPacket_c::draw() {
    static GXVtxDescList l_vtxDescList[] ALIGN_DECL(32) = {
        {GX_VA_POS, GX_DIRECT},
        {GX_VA_TEX0, GX_DIRECT},
        {GX_VA_NULL, GX_NONE},
    };

    static GXVtxAttrFmtList l_vtxAttrFmtList[] ALIGN_DECL(32) = {
        {GX_VA_POS, GX_POS_XYZ, GX_S8, 0x00},
        {GX_VA_TEX0, GX_TEX_ST, GX_U8, 0x00},
        {GX_VA_NULL, GX_POS_XYZ, GX_F32, 0x00},
    };

    GXTexObj texObj;

    const GXBool mipmap = mImage->mipmapCount > 1 ? TRUE : FALSE;
    GXInitTexObj(&texObj, mTex, mImage->width, mImage->height, GXTexFmt(mImage->format), GXTexWrapMode(mImage->wrapS), GXTexWrapMode(mImage->wrapT), mipmap);
    GXInitTexObjLOD(&texObj, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, FALSE, FALSE, GX_ANISO_1);
    GXLoadTexObj(&texObj, GX_TEXMAP0);

    GXCallDisplayList(l_sightMatDL, 0x60);
    GFSetVtxDescv(l_vtxDescList);
    GFSetVtxAttrFmtv(GX_VTXFMT0, l_vtxAttrFmtList);

    GXColor color0 = {0xFF, 0xFF, 0x32, 0xFF};
    GXColor color1 = {0xFF, 0xFF, 0x32, 0xFF};

    for (int i = 0; i < BOOMERANG_LOCK_MAX; i++) {
        if (mSightOnFlg & (1 << i)) {
            color0.a = mScaleArr[i];
            color1.a = mScaleArr[i];
            GFSetTevColor(GX_TEVREG0, color0);
            GFSetTevColor(GX_TEVREG1, color1);
            GFLoadPosMtxImm(mMtxArr[i], GX_PNMTX0);
            GXCallDisplayList(l_sightDL, 0x20);
        }
    }
}

/* 800E14F0-800E1718       .text setSight__25daBoomerang_sightPacket_cFP4cXyzi */
void daBoomerang_sightPacket_c::setSight(cXyz* pPos, int n) {
    if (pPos != NULL) {
        s32 animFrame = mFrameArr[n];
        f32 pingPongAnim;
        f32 imageRot;
        if (animFrame < (u32)HALF_ANIM_LENGTH) {
            pingPongAnim = 1.0f - animFrame / (f32)HALF_ANIM_LENGTH;
            imageRot = -pingPongAnim;
        } else {
            pingPongAnim = (animFrame - HALF_ANIM_LENGTH) / (f32)HALF_ANIM_LENGTH;
            imageRot = pingPongAnim;
        }
        mScaleArr[n] = pingPongAnim * 30.0f + 150.0f;

        f32 scale = (pingPongAnim * 35.0f + 65.0f) * 0.01f;
        f32 scaleX = scale * mImage->width;
        f32 scaleY = scale * mImage->height;

        cXyz proj;
        mDoLib_project(pPos, &proj);
        mDoMtx_stack_c::transS(proj);
        mDoMtx_stack_c::scaleM(scaleX, scaleY, scale);

        mDoMtx_stack_c::ZrotM(cM_ssin(imageRot * 0x4000) * 0x8000);
        MTXCopy(mDoMtx_stack_c::get(), mMtxArr[n]);

        mSightOnFlg |= 1 << n;
    } else {
        mSightOnFlg &= ~(1 << n);
    }
}

/* 800E1718-800E1754       .text play__25daBoomerang_sightPacket_cFi */
void daBoomerang_sightPacket_c::play(int n) {
    u8* p = &mFrameArr[0];
    for (int i = 0; i < n; i++) {
        *p = *p + 1;
        if (*p == ANIM_LENGTH) {
            *p = 0;
        }
        p++;
    }
}

/* 800E1754-800E1998       .text draw__13daBoomerang_cFv */
BOOL daBoomerang_c::draw() {
    dComIfGd_setListP1();

    if (!mThirdPerson && mLockCnt != 0) {
        mSightPacket.resetSightOn();
        for (int i = mCurLockIdx; i < mLockCnt; i++) {
            fopAc_ac_c* pActor = mLockActors[i];
            if (pActor) {
                mSightPacket.setSight(&pActor->eyePos, i);
            } else {
                mSightPacket.setSight(NULL, i);
            }
        }

        dComIfGd_set2DXlu(&mSightPacket);
    }

    if (mBlur.numTrailSegments > 0) {
        dComIfGd_entryZSortXluList(&mBlur, current.pos);
    }

    if (daPy_getPlayerActorClass()->checkPlayerNoDraw() && fopAcM_GetParam(this) == Mode_Wait) {
        dComIfGd_setList();
        return TRUE;
    }

    dKy_getEnvlight().settingTevStruct(TEV_TYPE_ACTOR, &current.pos, &tevStr);
    dKy_getEnvlight().setLightTevColorType(mpModel, &tevStr);

    if (fopAcM_GetParam(this) == Mode_Wait && daPy_getPlayerLinkActorClass()->checkFreezeState()) {
        dMat_control_c::iceUpdateDL(mpModel, -1, NULL);
    } else {
        mDoExt_modelUpdateDL(mpModel);
    }

    dComIfGd_setList();

    if (mGroundY != -G_CM3D_F_INF && fopAcM_GetParam(this) == Mode_Throw) {
        cXyz* pFloorNrm = dComIfG_Bgsp()->GetTriPla(mGndChk)->GetNP();
        if (pFloorNrm) {
            dComIfGd_setSimpleShadow(&current.pos, mGroundY, 30.0f, pFloorNrm);
        }
    }

    return TRUE;
}

/* 800E1998-800E19B8       .text daBoomerang_Draw__FP13daBoomerang_c */
static BOOL daBoomerang_Draw(daBoomerang_c* i_this) {
    return i_this->draw();
}

/* 800E19B8-800E1A14       .text getFlyMax__13daBoomerang_cFv */
float daBoomerang_c::getFlyMax() {
    if (dComIfGp_checkPlayerStatus0(0, daPyStts0_SHIP_RIDE_e)) {
        return 5000.0f;
    }

    if (strcmp(dComIfGp_getStartStageName(), "GanonK") != 0) {
        return 2500.0f;
    }

    return 5000.0f;
}

/* 800E1A14-800E1AAC       .text rockLineCallback__13daBoomerang_cFP10fopAc_ac_c */
void daBoomerang_c::rockLineCallback(fopAc_ac_c* pHitActor) {
    if (fopAcM_GetParam(this) == Mode_Wait) {
        if (pHitActor) {
            setLockActor(pHitActor, TRUE);
        }
    } else {
        for (int i = 0; i < BOOMERANG_LOCK_MAX; i++) {
            if (mLockActors[i] == pHitActor) {
                mLockActorIDs[i] = -1;
                mLockActors[i] = NULL;
                if (i == mCurLockIdx) {
                    mJustHit = true;
                    mCurLockIdx++;
                }
            }
        }
    }
}

/* 800E1AAC-800E1AD0       .text daBoomerang_rockLineCallback__FP10fopAc_ac_cP12dCcD_GObjInfP10fopAc_ac_cP12dCcD_GObjInf */
void daBoomerang_rockLineCallback(fopAc_ac_c* i_actor, dCcD_GObjInf*, fopAc_ac_c* i_hit, dCcD_GObjInf*) {
    ((daBoomerang_c*)i_actor)->rockLineCallback(i_hit);
}

/* 800E1AD0-800E1B20       .text setAimActor__13daBoomerang_cFP10fopAc_ac_c */
void daBoomerang_c::setAimActor(fopAc_ac_c* i_actor) {
    resetLockActor();
    mThirdPerson = true;
    setLockActor(i_actor, FALSE);
}

/* 800E1B20-800E1C20       .text setLockActor__13daBoomerang_cFP10fopAc_ac_ci */
BOOL daBoomerang_c::setLockActor(fopAc_ac_c* i_actor, BOOL doSoundEffect) {
    if (mLockCnt >= BOOMERANG_LOCK_MAX) {
        return FALSE;
    }

    fpc_ProcID id = fopAcM_GetID(i_actor);

    for (int i = 0; i < mLockCnt; i++) {
        if (mLockActorIDs[i] == id) {
            return FALSE;
        }
    }

    mLockActorIDs[mLockCnt] = id;
    mLockActors[mLockCnt] = i_actor;
    mSightPacket.initFrame(mLockCnt);

    if (doSoundEffect) {
        static const u32 se_flg[BOOMERANG_LOCK_MAX] = {
            JA_SE_BOOM_LOCK_ON_1,
            JA_SE_BOOM_LOCK_ON_2,
            JA_SE_BOOM_LOCK_ON_3,
            JA_SE_BOOM_LOCK_ON_4,
            JA_SE_BOOM_LOCK_ON_5,
        };

        mDoAud_seStart(se_flg[mLockCnt]);
    }

    mLockCnt++;
    return TRUE;
}

/* 800E1C20-800E1C58       .text resetLockActor__13daBoomerang_cFv */
void daBoomerang_c::resetLockActor() {
    for (int i = 0; i < BOOMERANG_LOCK_MAX; i++) {
        mLockActorIDs[i] = fpcM_ERROR_PROCESS_ID_e;
        mLockActors[i] = NULL;
    }

    mLockCnt = 0;
    mCurLockIdx = 0;
}

/* 800E1C58-800E1CFC       .text setRoomInfo__13daBoomerang_cFv */
void daBoomerang_c::setRoomInfo() {
    mGndChk.SetPos(&current.pos);

    f32 groundY = dComIfG_Bgsp()->GroundCross(&mGndChk);
    s32 roomNo;
    if (groundY != -G_CM3D_F_INF) {
        roomNo = dComIfG_Bgsp()->GetRoomId(mGndChk);
        tevStr.mEnvrIdxOverride = dComIfG_Bgsp()->GetPolyColor(mGndChk);
    } else {
        roomNo = dComIfGp_roomControl_getStayNo();
    }

    tevStr.mRoomNo = roomNo;
    mStts.SetRoomId(roomNo);
    current.roomNo = roomNo;
}

/* 800E1CFC-800E1DA8       .text setKeepMatrix__13daBoomerang_cFv */
void daBoomerang_c::setKeepMatrix() {
    // Transform the boomerang to Link's hand.
    mDoMtx_stack_c::copy(daPy_getPlayerActorClass()->getLeftHandMatrix());
    mDoMtx_stack_c::transM(10.3f, 36.6f, 1.1f);
    mDoMtx_stack_c::XYZrotM(-0x39f4, -0x357, -0x2af3);
    MtxP mtx = mDoMtx_stack_c::get();
    mpModel->setBaseTRMtx(mtx);
    current.pos.set(mtx[0][3], mtx[1][3], mtx[2][3]);
}

/* 800E1DA8-800E1E6C       .text setAimPos__13daBoomerang_cFv */
void daBoomerang_c::setAimPos() {
    if (mIsReturning) {
        mTargetPos = daPy_getPlayerLinkActorClass()->getBoomerangCatchPos();
    } else {
        for (int i = mCurLockIdx; i < mLockCnt; i++) {
            if (mLockActors[i] != NULL) {
                mTargetPos = mLockActors[i]->eyePos;
                return;
            }
            mCurLockIdx++;
        }
    }
}

/* 800E1E6C-800E1F94       .text checkBgHit__13daBoomerang_cFP4cXyzP4cXyz */
void daBoomerang_c::checkBgHit(cXyz* pi_start, cXyz* pi_end) {
    mLinChk.Set(pi_start, pi_end, this);
    if (dComIfG_Bgsp()->LineCross(&mLinChk)) {
        current.pos.set(mLinChk.GetCross());
        dComIfGp_particle_setP1(dPa_name::ID_AK_JN_NG, &current.pos);
        mIsReturning = true;
        current.angle.y += 0x8000;
        shape_angle.y = current.angle.y;
        resetLockActor();
        u32 mtrlSndId = dComIfG_Bgsp()->GetMtrlSndId(mLinChk);
        s8 roomNo = current.roomNo;
        s8 reverb = dComIfGp_getReverb(roomNo);
        mDoAud_seStart(JA_SE_LK_W_WEP_HIT, &eyePos, mtrlSndId, reverb);
    }
}

/* 800E1F94-800E239C       .text procWait__13daBoomerang_cFv */
BOOL daBoomerang_c::procWait() {
    daPy_lk_c* pPlayer = daPy_getPlayerLinkActorClass();
    speedF = 0.0f;
    setKeepMatrix();

    if (mBlur.numTrailSegments > 0) {
        mBlur.numTrailSegments -= daBoomerang_blur_c::SEGMENTS_PER_STEP;
    }

    if (fopAcM_GetParam(this) == Mode_Throw) {
        speedF = 60.0f;
        mCps.ResetAtHit();
        mCps.OffAtNoTgHitInfSet();
        mIsReturning = false;
        unused_0xF2D = true;
        mJustHit = false;
        mCurLockIdx = 0;

        setAimPos();

        if (mCurLockIdx == mLockCnt) {
            resetLockActor();

            s16 angleY = pPlayer->getBodyAngleY() + pPlayer->shape_angle.y;
            s16 angleX = pPlayer->getBodyAngleX();

            f32 flyMax = getFlyMax();

            mTargetPos.set(
                current.pos.x + flyMax * cM_ssin(angleY) * cM_scos(angleX),
                current.pos.y - flyMax * cM_ssin(angleX),
                current.pos.z + flyMax * cM_scos(angleY) * cM_scos(angleX)
            );
        }

        mModelRotY = 0;

        cXyz diff = mTargetPos - current.pos;

        current.angle.x = cM_atan2s(-diff.y, diff.absXZ());
        if (mLockCnt == 0) {
            current.angle.y = cM_atan2s(diff.x, diff.z) + 0x3000;
            if (mThirdPerson) {
                mFreeFlyOut = false;
            } else {
                mFreeFlyOut = true;
            }
        } else {
            current.angle.y = cM_atan2s(diff.x, diff.z);
            mFreeFlyOut = false;
        }

        shape_angle.x = current.angle.x;
        shape_angle.y = current.angle.y;
        shape_angle.z = 0x2000;
        mModelRotZ = 0x2000;

        mProcFunc = &daBoomerang_c::procMove;

        mCancelFlg = false;

        mBlur.initBlur(mpModel->getBaseTRMtx(), mModelRotY);

        cXyz bgHitPos(pPlayer->current.pos.x, pPlayer->current.pos.y + 60.0f, pPlayer->current.pos.z);
        checkBgHit(&bgHitPos, &current.pos);

        procMove();
    } else {
        if (dCam_getBody()->Mode() != 0xB) {
            resetLockActor();
        } else {
            if (pPlayer->checkBoomerangRock()) {
                camera_process_class* pCamera = dComIfGp_getCamera(dComIfGp_getPlayerCameraID(0));
                cXyz eyePos = *fopCamM_GetEye_p(pCamera);
                cXyz topPos = pPlayer->getLineTopPos();
                mCps.OnAtNoTgHitInfSet();
                mCps.cM3dGCps::Set(eyePos, topPos, 3.0f);

                mCps.CalcAtVec();
                mCps.SetAtHitCallback(&daBoomerang_rockLineCallback);

                dComIfG_Ccsp()->Set(&mCps);
            }
        }
    }

    return TRUE;
}

/* 800E239C-800E2AF4       .text procMove__13daBoomerang_cFv */
BOOL daBoomerang_c::procMove() {
    static cXyz at_offset; // Never used.

    if (mCatchAndDelete) {
        fopAcM_delete(this);
        return TRUE;
    }

    if (!dComIfGp_event_runCheck()) {
        daPy_lk_c* pPlayer = daPy_getPlayerLinkActorClass();
        s16 s = mModelRotY;
        mModelRotY -= 0x1F00;
        if (s >= 0 && mModelRotY < 0) {
            s8 roomNo = current.roomNo;
            mDoAud_seStart(JA_SE_LK_BOOM_FLY, &eyePos, 0, dComIfGp_getReverb(roomNo));
        }
        setAimPos();

        cXyz diff = mTargetPos - current.pos;
        f32 dist = diff.abs();
        cXyz norm;
        if (dist > 0.1f) {
            norm = diff / dist;
        } else {
            norm = diff;
        }

        s16 angle = cM_atan2s(norm.x, norm.z);
        s16 currentAngle = angle - current.angle.y;

        if (mJustHit) {
            mJustHit = false;
            if (s16(angle - current.angle.y) > 0) {
                current.angle.y = angle - 0x3000;
            } else {
                current.angle.y = angle + 0x3000;
            }
            currentAngle = angle - current.angle.y;
        }

        if (mIsReturning) {
            if (dist < speedF * 2.0f) {
                if (pPlayer->returnBoomerang()) {
                    fopAcM_SetParam(this, Mode_Wait);
                    mThirdPerson = false;
                    mProcFunc = &daBoomerang_c::procWait;
                    mCps.SetAtHitCallback(NULL);
                } else {
                    mCatchAndDelete = true;
                }
            }
        } else {
            if (dist < speedF || (mCps.ChkAtHit() && mCps.GetAtHitAc() == mLockActors[mCurLockIdx])) {
                // Boomerang hit its current target.
                mJustHit = true;
                mLockActors[mCurLockIdx] = NULL;
                mLockActorIDs[mCurLockIdx] = fpcM_ERROR_PROCESS_ID_e;
                mCurLockIdx++;
                mFreeFlyOut = false;
            }
        }

        if (!mIsReturning && (mCancelFlg || (!mFreeFlyOut && mLockCnt <= mCurLockIdx) || mCps.ChkAtShieldHit())) {
            // Boomerang should return. It either was canceled, hit its last target, or hit a shield.
            mJustHit = true;
            mIsReturning = true;
            resetLockActor();
            mCancelFlg = false;
            mFreeFlyOut = false;
        }

        if (mJustHit) {
            current.angle.y = angle;
        } else {
            s16 newAngle;
            if (!mThirdPerson) {
                f32 a = 20.0f - dist * 2.0f / speedF;
                if (a < 0.0f) {
                    a = 0.0f;
                } else if (a > 18.0f) {
                    a = 20.0f + 40.0f * (a - 18.0f);
                }
                newAngle = a * 0x100 + 0x400;
            } else {
                newAngle = 0x4000;
            }

            if (currentAngle > newAngle) {
                currentAngle = newAngle;
            } else if (currentAngle < -newAngle) {
                currentAngle = -newAngle;
            }
            current.angle.y += currentAngle;
        }

        current.angle.x = cM_atan2s(-norm.y, norm.absXZ());

        current.pos.x += speedF * cM_scos(current.angle.x) * cM_ssin(current.angle.y);
        current.pos.y -= speedF * cM_ssin(current.angle.x);
        current.pos.z += speedF * cM_scos(current.angle.x) * cM_scos(current.angle.y);

        shape_angle.y = current.angle.y;
        shape_angle.x = current.angle.x;

        if (!mIsReturning) {
            checkBgHit(&old.pos, &current.pos);
        }

        currentAngle = -(currentAngle * 2);

        if (currentAngle > 0x100) {
            currentAngle = 0x2000;
        } else if (currentAngle < -0x100) {
            currentAngle = -0x2000;
        } else {
            currentAngle = mModelRotZ;
        }

        mModelRotZ = currentAngle;
        cLib_addCalcAngleS(&shape_angle.z, currentAngle, 0x10, 0x1000, 0x10);
    }

    mDoMtx_stack_c::transS(current.pos);
    mDoMtx_stack_c::ZXYrotM(shape_angle.x, shape_angle.y, shape_angle.z);
    mDoMtx_stack_c::YrotM(mModelRotY);
    mpModel->setBaseTRMtx(mDoMtx_stack_c::get());
    mCps.cM3dGCps::Set(old.pos, current.pos, 30.0f);
    mCps.CalcAtVec();

    if (!dComIfGp_event_runCheck()) {
        mBlur.copyBlur(mDoMtx_stack_c::get(), mModelRotY);
        dComIfG_Ccsp()->Set(&mCps);
        dComIfG_Ccsp()->SetMass(&mCps, 1);
    } else {
        mBlur.copyBlur(mDoMtx_stack_c::get(), 0);
        mCps.ResetAtHit();
    }

    mInWater = daPy_lk_c::setItemWaterEffect(this, mInWater, TRUE);

    return TRUE;
}

/* 800E2AF4-800E2BD0       .text execute__13daBoomerang_cFv */
BOOL daBoomerang_c::execute() {
    for (int i = 0; i < mLockCnt; i++) {
        mLockActors[i] = fopAcM_SearchByID(mLockActorIDs[i]);
    }

    if (mProcFunc) {
        (this->*mProcFunc)();
    }

    attention_info.position = current.pos;
    eyePos = current.pos;

    setRoomInfo();
    mSightPacket.play(mLockCnt);

    return TRUE;
}

/* 800E2BD0-800E2BF0       .text daBoomerang_Execute__FP13daBoomerang_c */
static BOOL daBoomerang_Execute(daBoomerang_c* i_this) {
    return i_this->execute();
}

/* 800E2BF0-800E2BF8       .text daBoomerang_IsDelete__FP13daBoomerang_c */
static BOOL daBoomerang_IsDelete(daBoomerang_c*) {
    return TRUE;
}

/* 800E2BF8-800E2C00       .text daBoomerang_Delete__FP13daBoomerang_c */
static BOOL daBoomerang_Delete(daBoomerang_c*) {
    return TRUE;
}

/* 800E2C00-800E2CC8       .text createHeap__13daBoomerang_cFv */
BOOL daBoomerang_c::createHeap() {
    J3DModelData* modelData = (J3DModelData*)dComIfG_getObjectRes("Link", dRes_INDEX_LINK_BDL_BOOMERANG_e);

    JUT_ASSERT(VERSION_SELECT(1543, 1539, 1546, 1546), modelData != NULL);

    mpModel = mDoExt_J3DModel__create(modelData, 0x80000, 0x37220202);
    if (mpModel == NULL)
        return FALSE;
    return TRUE;
}

/* 800E2CC8-800E2CE8       .text daBoomerang_createHeap__FP10fopAc_ac_c */
static BOOL daBoomerang_createHeap(fopAc_ac_c* i_this) {
    return static_cast<daBoomerang_c*>(i_this)->createHeap();
}

static dCcD_SrcCps l_at_cps_src = {
    // dCcD_SrcGObjInf
    {
        /* Flags             */ 0,
        /* SrcObjAt  Type    */ AT_TYPE_BOOMERANG,
        /* SrcObjAt  Atp     */ 1,
        /* SrcObjAt  SPrm    */ cCcD_AtSPrm_Set_e | cCcD_AtSPrm_VsEnemy_e | cCcD_AtSPrm_VsOther_e | cCcD_AtSPrm_NoTgHitInfSet_e,
        /* SrcObjTg  Type    */ 0,
        /* SrcObjTg  SPrm    */ 0,
        /* SrcObjCo  SPrm    */ 0,
        /* SrcGObjAt Se      */ dCcG_SE_WOOD,
        /* SrcGObjAt HitMark */ dCcG_AtHitMark_Unk1_e,
        /* SrcGObjAt Spl     */ dCcG_At_Spl_UNK0,
        /* SrcGObjAt Mtrl    */ 0,
        /* SrcGObjAt SPrm    */ 0,
        /* SrcGObjTg Se      */ 0,
        /* SrcGObjTg HitMark */ 0,
        /* SrcGObjTg Spl     */ dCcG_Tg_Spl_UNK0,
        /* SrcGObjTg Mtrl    */ 0,
        /* SrcGObjTg SPrm    */ 0,
        /* SrcGObjCo SPrm    */ 0,
    },
    // cM3dGCpsS
    {{
        /* Start  */ {0.0f, 0.0f, 0.0f},
        /* End    */ {0.0f, 0.0f, 0.0f},
        /* Radius */ 3.0f,
    }},
};

/* 800E2CE8-800E2EF0       .text create__13daBoomerang_cFv */
cPhs_State daBoomerang_c::create() {
    fopAcM_ct(this, daBoomerang_c);

    if (!fopAcM_entrySolidHeap(this, daBoomerang_createHeap, 0xD40)) {
        return cPhs_ERROR_e;
    }

    setKeepMatrix();
    fopAcM_SetMtx(this, mpModel->getBaseTRMtx());
    mProcFunc = &daBoomerang_c::procWait;
    mStts.Init(0x3C, 0xFF, this);

    mCps.Set(l_at_cps_src);
    mCps.SetStts(&mStts);
    mCps.SetAtHitCallback(&daBoomerang_rockLineCallback);

    mLinChk.ClrSttsRoofOff();

    for (int i = 0; i < BOOMERANG_LOCK_MAX; i++) {
        mLockActorIDs[i] = fpcM_ERROR_PROCESS_ID_e;
    }

    {
        ResTIMG* tmp_img = (ResTIMG*)dComIfG_getObjectRes("Link", dRes_INDEX_LINK_BTI_BLUR_e);

        JUT_ASSERT(VERSION_SELECT(1626, 1622, 1629, 1629), tmp_img != NULL);

        mBlur.imageData = (u8*)tmp_img + tmp_img->imageOffset;
    }

    {
        ResTIMG* tmp_img = (ResTIMG*)dComIfG_getObjectRes("Link", dRes_INDEX_LINK_BTI_ROCK_MARK_e);

        JUT_ASSERT(VERSION_SELECT(1634, 1630, 1637, 1637), tmp_img != NULL);

        mSightPacket.setTex((u8*)tmp_img + tmp_img->imageOffset);
        mSightPacket.setImage(tmp_img);
    }

    setRoomInfo();
    model = mpModel;

    return cPhs_COMPLEATE_e;
}

/* 800E2EF0-800E329C       .text __ct__13daBoomerang_cFv */
daBoomerang_c::daBoomerang_c() {
}

/* 800E33F0-800E3410       .text daBoomerang_Create__FP10fopAc_ac_c */
static cPhs_State daBoomerang_Create(fopAc_ac_c* i_this) {
    return ((daBoomerang_c*)i_this)->create();
}

static actor_method_class l_daBoomerang_Method = {
    (process_method_func)daBoomerang_Create,
    (process_method_func)daBoomerang_Delete,
    (process_method_func)daBoomerang_Execute,
    (process_method_func)daBoomerang_IsDelete,
    (process_method_func)daBoomerang_Draw,
};

actor_process_profile_definition g_profile_BOOMERANG = {
    /* Layer ID     */ fpcLy_CURRENT_e,
    /* List ID      */ 0x0007,
    /* List Prio    */ fpcPi_CURRENT_e,
    /* Proc Name    */ fpcNm_BOOMERANG_e,
    /* Proc SubMtd  */ &g_fpcLf_Method.base,
    /* Size         */ sizeof(daBoomerang_c),
    /* Size Other   */ 0,
    /* Parameters   */ 0,
    /* Leaf SubMtd  */ &g_fopAc_Method.base,
    /* Draw Prio    */ fpcDwPi_BOOMERANG_e,
    /* Actor SubMtd */ &l_daBoomerang_Method,
    /* Status       */ fopAcStts_UNK4000_e | fopAcStts_UNK40000_e,
    /* Group        */ fopAc_ACTOR_e,
    /* Cull Type    */ fopAc_CULLBOX_0_e,
};
