#include "d/a/e/d_a_e_remly.h"

#include "c/c_counter.h"
#include "c/c_lib.h"
#include "c/c_math.h"
#include "common.h"
#include "d/a/d_a_base.h"
#include "d/a/d_a_player.h"
#include "d/a/npc/d_a_npc_shinkan2.h"
#include "d/a/obj/d_a_obj_base.h"
#include "d/a/obj/d_a_obj_bomb.h"
#include "d/col/bg/d_bg_s.h"
#include "d/col/bg/d_bg_s_gnd_chk.h"
#include "d/col/bg/d_bg_s_wtr_chk.h"
#include "d/col/c/c_cc_d.h"
#include "d/col/cc/d_cc_s.h"
#include "d/d_camera.h"
#include "d/d_light_env.h"
#include "d/d_linkage.h"
#include "d/d_rumble.h"
#include "d/d_sc_game.h"
#include "d/d_stage_mgr.h"
#include "d/d_vec.h"
#include "d/flag/sceneflag_manager.h"
#include "d/flag/storyflag_manager.h"
#include "d/snd/d_snd_wzsound.h"
#include "d/t/d_t_stream.h"
#include "f/f_base.h"
#include "f/f_manager.h"
#include "f/f_profile_name.h"
#include "m/m3d/m3d.h"
#include "m/m3d/m_fanm.h"
#include "m/m3d/m_smdl.h"
#include "m/m_angle.h"
#include "m/m_mtx.h"
#include "m/m_quat.h"
#include "m/m_vec.h"
#include "nw4r/g3d/res/g3d_resanmtexpat.h"
#include "nw4r/g3d/res/g3d_resfile.h"
#include "nw4r/g3d/res/g3d_resmdl.h"
#include "nw4r/g3d/res/g3d_resnode.h"
#include "nw4r/math/math_arithmetic.h"
#include "rvl/GX/GXTypes.h"
#include "s/s_Math.h"
#include "toBeSorted/attention.h"
#include "toBeSorted/d_d3d.h"
#include "toBeSorted/d_emitter.h"
#include "toBeSorted/event.h"
#include "toBeSorted/event_manager.h"

SPECIAL_ACTOR_PROFILE(E_REMLY, dAcEremly_c, fProfile::E_REMLY, 0xE1, 0, 3);

static dCcD_SrcSph sSphSrc = {
    {{AT_TYPE_DAMAGE, 0xD, {0, 0, 0}, 2, 0, 0, 0, 0},
     {~(AT_TYPE_BUGNET | AT_TYPE_BEETLE | AT_TYPE_0x8000 | AT_TYPE_WIND), 0x303, {0, 0, 0x40F}, 8, 0},
     {0xE5}},
    {50.f},
};

struct dAcEremly_HIO_c {
    f32 _0x00;
    f32 _0x04;
    f32 _0x08;
    f32 _0x0C;
    f32 _0x10;
    f32 _0x14;
    f32 _0x18;
    f32 _0x1C;
    f32 _0x20;
    f32 _0x24;
    f32 _0x28;
    f32 _0x2C;
    f32 _0x30;
    f32 _0x34;
    f32 _0x38;
    f32 _0x3C;
    f32 _0x40;
    f32 _0x44;
    s16 _0x48;
    f32 _0x4C;
    f32 _0x50;
    f32 _0x54;
    f32 _0x58;
    s16 _0x5C;
    s16 _0x5E;
    s16 _0x60;
    s16 _0x62;
    s16 _0x64;
    f32 _0x68;
    f32 _0x6C;
    static const dAcEremly_HIO_c sInstance;
};
const dAcEremly_HIO_c dAcEremly_HIO_c::sInstance = {
    1.f,  1000.f, 500.0f, 200.0f, 120.0f, 250.0f, 400.0f, 400.0f, 600.0f, 0.8f, 5.0f, 20.0f, 10.0f, -8.0f, 20.0f,
    7.0f, 15.0f,  50.0f,  30,     1000.f, 300.f,  8.f,    1.3f,   300,    300,  30,   90,    90,    40.f,  0.f,
};

STATE_DEFINE(dAcEremly_c, Wait);
STATE_DEFINE(dAcEremly_c, Walk);
STATE_DEFINE(dAcEremly_c, Run);
STATE_DEFINE(dAcEremly_c, Escape);
STATE_DEFINE(dAcEremly_c, EscapeDash);
STATE_DEFINE(dAcEremly_c, Wind);
STATE_DEFINE(dAcEremly_c, Hold);
STATE_DEFINE(dAcEremly_c, Jump);
STATE_DEFINE(dAcEremly_c, Fly);
STATE_DEFINE(dAcEremly_c, Damage);
STATE_DEFINE(dAcEremly_c, Sleep);
STATE_DEFINE(dAcEremly_c, Scared);
STATE_DEFINE(dAcEremly_c, Stun);
STATE_DEFINE(dAcEremly_c, Water);
STATE_DEFINE(dAcEremly_c, Hear);
STATE_DEFINE(dAcEremly_c, NightSleepDemo);
STATE_DEFINE(dAcEremly_c, NightFoo);
STATE_DEFINE(dAcEremly_c, NightReflectionFoo);
STATE_DEFINE(dAcEremly_c, NightWait);
STATE_DEFINE(dAcEremly_c, NightWalk);
STATE_DEFINE(dAcEremly_c, NightRun);
STATE_DEFINE(dAcEremly_c, NightRet);
STATE_DEFINE(dAcEremly_c, NightJumpAttack);
STATE_DEFINE(dAcEremly_c, BirthWait);

void dAcEremly_c::callback_c::timingB(u32 nodeId, nw4r::g3d::WorldMtxManip *result, nw4r::g3d::ResMdl resMdl) {
    static u32 headNode = resMdl.GetResNode("head").GetID();

    if (nodeId == headNode) {
        mMtx_c m;
        result->GetMtx(m);
        m.ZXYrotM(mHeadRotation);
        result->SetMtx(m);
    }
}

bool dAcEremly_c::createHeap() {
    TRY_CREATE(mMdl.create3(*this, getOarcResFile("Remly"), "Remly", "RemlyWalk", 0x133));
    fn_80030980(mMdl.getModel(), 1, false);

    mMdl.getModel().setCallback(&mMdlCallback);

    nw4r::g3d::ResFile res = mMdl.getMdlFile();
    nw4r::g3d::ResMdl resMdl = mMdl.getModel().getResMdl();
    nw4r::g3d::ResAnmTexPat resTexPat = res.GetResAnmTexPat("RemlyWink");

    bool result;
    TRY_CREATE2(mTexPat.create(resMdl, resTexPat, &mAllocator, nullptr, 1));
    mMdl.getModel().setAnm(mTexPat);

    return true;
}

int dAcEremly_c::actorCreate() {
    s32 _weird_zero = 0;

    CREATE_ALLOCATOR(dAcEremly_c);

    u8 p = getFromParams(0, 0xF);
    mIsHorwellsPet = p != 15 ? p : 0;
    mSceneflag = getFromParams(4, 0xFF);

    s32 f = getFromParams(12, 0xFF);
    mPatrolAreaSize = f != 0xFF ? f * 100.f : 1000.f;

    mAcch.Set(this, 1, &mAcchCir);

    mBoundingBox.Set(mVec3_c(-150.f, -700.f, -150.f), mVec3_c(150.f, 150.f, 150.f));

    mAcceleration = -3.f;
    mMaxSpeed = -60.f;
    mScale.set(1.f, 1.f, 1.f);

    mScaleF = 1.f;
    mSlope.clear();
    mStts.SetRank(5);
    mSph.Set(sSphSrc);
    mSph.SetStts(mStts);

    mMdlCallback.mHeadRotation.clear();

    mMdl.getModel().setScale(mScale);
    mMdl.play();

    updateMatrix();
    mMdl.getModel().setLocalMtx(mWorldMtx);
    mMdl.getModel().calc(false);

    mSph.ClrAtSet();
    mLinkage.set(0, 0.f, 0.f, 0.f, nullptr);

    mStartingPos.set(mPosition.x, mPosition.y, mPosition.z);
    mTargetPosition.set(mPosition.x, mPosition.y, mPosition.z);
    mYPosition = mPosition.y;
    mStartingRot = mRotation;

    mHorwellRef.unlink();
    mNearbyBombRef.unlink();

    field_0xB64 = 1;
    mSph.ClrAtSet(); // Duplicated clear

    mEmitters[0].init(this);
    mEmitters[1].init(this);

    mWaterEffect.init(this, 100.f, mScale.x * 0.7f, 0.f);
    mWaterEffect.setIsSmall(true);

    field_0xA50.x = mPosition.x;
    field_0xA50.y = mPosition.y;
    field_0xA50.z = mPosition.z;
    mAcchCir.SetWall(mScaleF * 10.f, mScaleF * 60.f);

    mAcch.SetField_0xD4(100.f + _weird_zero);
    field_0xB04 = 60.f;

    if (mIsHorwellsPet == 0) {
        mAcch.SetGndThinCellingOff();
        if (dScGame_c::currentSpawnInfo.isNight()) {
            mTargetFiTextID = 1;
        } else {
            mTargetFiTextID = 0;
        }

        if (!isScary()) {
            setBattleBgmRelated(0);
            changeState(StateID_Sleep);
            mMdl.setAnm("RemlySleep", m3d::PLAY_MODE_4, 0.f);
            mAcchCir.SetWall(30.f, 50.f);
            return SUCCEEDED;
        }

        mScale.set(1.3f, 1.3f, 1.3f);

        mScaleF = 1.3f;
        mScaleFTarget = 1.3f;

        field_0xB64 = 2;

        (void)mMdl.getModel().getResMdl();
        applyScaryMat();

        mAnimTimer = 24.f + cM::rndF(24.f);
        if (mSceneflag == 0xFF || SceneflagManager::sInstance->checkBoolFlag(mRoomID, mSceneflag)) {
            changeState(StateID_NightWait);
        } else {
            changeState(StateID_NightSleepDemo);
        }
    } else if (mIsHorwellsPet == 1) {
        setBattleBgmRelated(0);
        changeState(StateID_Sleep);
    } else {
        changeState(StateID_Run);
    }
    return SUCCEEDED;
}

int dAcEremly_c::actorPostCreate() {
    if (mIsHorwellsPet == 1) {
        fBase_c *pActor = fManager_c::searchBaseByProfName(fProfile::NPC_SKN2);
        if (pActor != nullptr) {
            dAcNpcSkn2_c *pSkn2 = static_cast<dAcNpcSkn2_c *>(pActor);
            mHorwellRef.link(pSkn2);

            mAcch.ClrRoofNone();
            if (isHorwellRemlitQuestComplete()) {
                fn_177_79D0(false);

                mRotation.y = mAngle.y = cLib::targetAngleY(mPosition, mStartingPos);

                updateMatrix();
                mMdl.getModel().setLocalMtx(mWorldMtx);
                mMdl.getModel().calc(false);
                changeState(StateID_Wait);
                return SUCCEEDED;
            }

            changeState(StateID_Sleep);
            return SUCCEEDED;
        }

        mHorwellRef.unlink();
        mIsHorwellsPet = 0;
    }
    return SUCCEEDED;
}

int dAcEremly_c::doDelete() {
    return SUCCEEDED;
}

int dAcEremly_c::actorExecute() {
    s32 _weird_zero = 0;

    mHealth = 100;
    nw4r::g3d::ResMdl resMdl = mMdl.getModel().getResMdl();

    mStateMgr.executeState();
    if (mStateMgr.isState(StateID_BirthWait)) {
        return SUCCEEDED;
    }

    if (mAnimation == ANM_Sound) {
        mSpeed = 0.f;

        if (mMdl.getAnm().isStop()) {
            mMdl.setAnm("RemlyWaitStand", m3d::PLAY_MODE_4, 4.f);
            mAnimation = ANM_WaitStand;
            field_0xB66 = true;
            mAnimTimer = 48.f + cM::rndF(48.f);
            field_0xB6A = 1;
            changeState(StateID_Wait);
        }
    }

    if (!isState(StateID_Hold)) {
        mSph.OnCoSet();
    }

    if ((cCounter_c::GetGameFrame() & 0xF) == 0) {
        dAcBomb_c *pBomb = dAcPy_c::getBombWithinRadius(this, mPosition, 600.f);
        if (pBomb != nullptr) {
            mNearbyBombRef.link(pBomb);
            field_0xB6C = 1;
        } else {
            mNearbyBombRef.unlink();
        }
    }

    if (isState(StateID_EscapeDash) || isState(StateID_Water) || isState(StateID_Scared)) {
        if (fn_177_7650(false)) {
            if (isScary()) {
                deleteRequest();
                return SUCCEEDED;
            }
            changeState(StateID_BirthWait);
        }
    }

    if (0 == sLib::calcTimer(&mAnimSafeTimer)) {
        s32 damageTypeMaybe = fn_8002FDE0(mSph, nullptr);
        switch (damageTypeMaybe) {
            case 7: {
                if (isState(StateID_Fly) && mAnimation != ANM_WakeUp) {
                    if (mAnimation == ANM_Fly) {
                        mMdl.setAnm("RemlyFlyDamage", m3d::PLAY_MODE_4, 4.f);
                        mAnimation = ANM_FlyDamage;
                        mAnimSafeTimer = 48;
                    }
                } else {
                    if (mAnimation == ANM_Swim) {
                        mMdl.setAnm("RemlySwimDamage", m3d::PLAY_MODE_4, 4.f);
                        mAnimSafeTimer = 90;
                        mAnimation = ANM_SwimDamage;
                    } else {
                        mSph.ClrAtSet();
                        changeState(StateID_Wind);
                    }
                }
            } break;
            case 12: {
                if (!isState(StateID_Fly) && mAnimation != ANM_Swim && mAnimation != ANM_SwimDamage) {
                    if (mSph.ChkTgHit() && mSph.ChkTgAtHitType(AT_TYPE_GLITTERING_SPORES | AT_TYPE_SLINGSHOT)) {
                        mAngle.y = getXZAngleToPlayer();
                        mSpeed = -15.f;
                        changeState(StateID_Stun);
                        break;
                    }
                }
            }
            // fallthrough
            default: {
                if (damageTypeMaybe != 0) {
                    field_0xB52 = 8;
                    mAnimSafeTimer = 8;
                    dJEffManager_c::spawnHitMarkEffect(6, mSph, nullptr, true);

                    if (isState(StateID_Fly) && mAnimation != ANM_WakeUp) {
                        if (mAnimation == ANM_Fly) {
                            mMdl.setAnm("RemlyFlyDamage", m3d::PLAY_MODE_4, 4.f);
                            mAnimation = ANM_FlyDamage;
                        }
                    } else if (mAnimation == ANM_Swim || mAnimation == ANM_SwimDamage) {
                        mMdl.setAnm("RemlySwimDamage", m3d::PLAY_MODE_4, 4.f);
                        mAnimation = ANM_SwimDamage;
                    } else {
                        if (field_0xB64 != 0) {
                            field_0xB64--;
                            if (mSph.ChkTgHit() && mSph.ChkTgAtHitType(AT_TYPE_BOMB | AT_TYPE_ARROW)) {
                                field_0xB64 = 0;
                            }
                        }
                        field_0xB6A = 0;
                        mSph.ClrAtSet();

                        field_0xB68 = 1;
                        field_0xB6B = false;

                        if (isState(StateID_Scared)) {
                            field_0xB6B = true;
                        }

                        changeState(StateID_Damage);
                    }
                }
            } break;
        }
    }

    if (!isState(StateID_Hold)) {
        field_0xAA4.set(mPosition);
        calcVelocity();
        mPosition += mVelocity;
        mPosition += mStts.GetCcMove();
        mPosition += field_0xAF8;
        field_0xAF8 *= 0.7f;
    }

    if (isScary()) {
        if (field_0xB64 == 0 || fn_177_8AC0()) {
            if (mScaleFTarget != 1.0f) {
                mScaleFTimer = 7;
                mScaleFTarget = 1.f;
            }
        } else {
            if (mScaleFTarget != 1.3f) {
                mScaleFTimer = 7;
                mScaleFTarget = 1.3f;
            }
        }

        if (0 != sLib::calcTimer(&mScaleFTimer)) {
            sLib::addCalcScaledDiff(&mScaleF, mScaleFTarget, 1.f, 0.05f);
        }
    }

    mScale.set(mScaleF, mScaleF, mScaleF);

    mMdl.getModel().setScale(mScale);
    mMdl.play();
    if (!isState(StateID_Hold)) {
        mAcch.CrrPos(*dBgS::GetInstance());
        mWaterEffect.execute(mAcch.GetWtrGroundH(), mAcch.GetGroundH());
    } else {
        mLinkage.bushTpFunc(mAcch);
    }

    if (!isState(StateID_Hold) && !isState(StateID_NightSleepDemo) && !isState(StateID_Damage) &&
        !isState(StateID_Fly) && !isState(StateID_Jump)) {
        sLib::addCalcAngle(mRotation.y.ref(), mAngle.y, 2, 0x800);
    }

    if (!isState(StateID_Fly) && !isState(StateID_Hold) && !isState(StateID_BirthWait) && !isState(StateID_Water)) {
        if (!mAcch.ChkGndHit()) {
            if (fn_177_8980(100.f) && mNoGroundCounter == 0) {
                mNoGroundCounter = 1;
            }
            if (mNoGroundCounter != 0) {
                if (mIsHorwellsPet == 1 && !isHorwellRemlitQuestComplete() && EventManager::isInEvent()) {
                    setActorProperty(AC_PROP_0x1);
                    mPosition.set(mStartingPos.x, mStartingPos.y, mStartingPos.z);
                    mRotation.y = mStartingRot.y;
                    mAngle.y = mStartingRot.y;
                    changeState(StateID_Sleep);
                }
                if ((s32)++mNoGroundCounter > 30) {
                    changeState(StateID_Fly);
                }
            }

            mYPosition = mPosition.y;
        } else {
            sLib::chase(&mYPosition, mPosition.y, 10.f);
            checkSlope(false);

            if (mSlopeXTarget != 0 || mSlopeZTarget != 0) {
                mYPosition = mPosition.y;
            }
            mNoGroundCounter = 0;
        }
    } else {
        mSlopeXTarget = mSlopeZTarget = 0;
        mYPosition = mPosition.y + mYOffset;
    }

    if (!isUnderWater() && isState(StateID_Water)) {
        field_0xB6B = true;
    }

    sLib::addCalcAngle(mSlope.x.ref(), mSlopeXTarget, 2, 0x1000);
    sLib::addCalcAngle(mSlope.z.ref(), mSlopeZTarget, 2, 0x1000);

    mWorldMtx.transS(mPosition.x, mYPosition, mPosition.z);

    mWorldMtx.XrotM(mSlope.x);
    mWorldMtx.ZrotM(mSlope.z);
    mWorldMtx.ZXYrotM(mRotation);

    mMdl.getModel().setLocalMtx(mWorldMtx);
    mMdl.getModel().calc(false);
    playWink();

    f32 radius = 40.f + _weird_zero;
    mVec3_c center;

    center.x = mPosition.x;
    center.y = mPosition.y;
    center.z = mPosition.z;

    center.y += 30.f + _weird_zero;

    mMtx_c m;
    mSph.SetC(center);
    if (isState(StateID_Fly)) {
        mMdl.getModel().getNodeWorldMtx(resMdl.GetResNode("backbone3").GetID(), m);
        m.getTranslation(center);
        mSph.SetC(center);
    }
    mSph.SetR(radius * mScaleF);

    dCcS::GetInstance()->Set(&mSph);

    if (!(isScary() && field_0xB64 != 0) && !(isState(StateID_Hold) || mAnimation == ANM_Swim) &&
        !isState(StateID_Stun) && !isState(StateID_Jump)) {
        AttentionManager::GetInstance()->addPickUpTarget(*this, 3.f * radius * mScaleF);
        if (mLinkage.checkConnection(dLinkage_c::CONNECTION_1)) {
            changeState(StateID_Hold);
        }
    }

    if (mAnimation == ANM_Water) {
        mMdl.getModel().getNodeWorldMtx(resMdl.GetResNode("backbone02").GetID(), m);
        m.getTranslation(center);

        mEmitters[1].holdEffect(PARTICLE_RESOURCE_ID_MAPPING_859_, center, nullptr, nullptr, nullptr, nullptr);
    }

    mPositionCopy2.set(mPosition);
    mPositionCopy2.y += 30.f;

    mPositionCopy3.set(mPositionCopy2);
    mPositionCopy3.y += 50.f;

    if (isState(StateID_Fly)) {
        mPositionCopy3.y += 50.f;
    }

    if (!isState(StateID_Hold)) {
        if (!isScary()) {
            setInteractionFlags(INTERACT_0x2000);
        }
        fn_80030c20(3, 600.f, radius, -400.f, 400.f);
    }

    sLib::calcTimer(&field_0xB52);
    fn_80030400(mMdl.getModel(), 100, false, field_0xB52);
    return SUCCEEDED;
}

/// Ugh
inline f32 getValue0() {
    s32 _weird_zero = 0;
    return _weird_zero + 70.f;
}
int dAcEremly_c::draw() {
    s32 _weird_zero = 0;

    if (mStateMgr.isState(StateID_BirthWait)) {
        return SUCCEEDED;
    }

    if (mStateMgr.isState(StateID_Fly)) {
        if (EventManager::isInEvent()) {
            if (EventManager::isCurrentEvent("RescuePlayer") || EventManager::isCurrentEvent("RescuePlayerToUpward")) {
                return SUCCEEDED;
            }
        }
    }

    m3d::smdl_c &mdl = mMdl.getModel();
    drawModelType1(&mdl);

    // elp
    static mQuat_c shadow(0.f, getValue0(), 0.f, _weird_zero + 290.f);
    fn_8002edb0(mShadow, mdl, &shadow, -1, -1, mPosition.y - mAcch.GetGroundH());

    return SUCCEEDED;
}

void dAcEremly_c::initializeState_Wait() {
    mSomeCounter = 0;
    mAcceleration = -3.f;

    checkSlope(true);

    if (field_0xB66 || field_0xB6A) {
        mMdl.setAnm("RemlyWaitStand", m3d::PLAY_MODE_4, 4.f);
        mAnimation = ANM_WaitStand;

        mAnimTimer = cM::rndF(48.f) + 48.f;
    } else {
        mMdl.setAnm("RemlyWaitSit", m3d::PLAY_MODE_4, 10.f);
        mAnimation = ANM_WaitSit;

        field_0xB44 = cM::rndF(128.f) + 128.f;
    }
}
void dAcEremly_c::executeState_Wait() {
    const dAcPy_c *pPlayer = dAcPy_c::GetLink();
    if (field_0xB6A == 0) {
        calcHeadPitch(false);
    }
    sLib::addCalcScaled(&mSpeed, 0.7f, 5.f);

    if (isScary()) {
        changeState(StateID_EscapeDash);
        return;
    }

    if (fn_177_86C0()) {
        return;
    }

    if (mAnimation == ANM_WaitSitCry) {
        if (mMdl.getAnm().isStop()) {
            mMdl.setAnm("RemlyWaitSit", m3d::PLAY_MODE_4, 10.f);
            mAnimation = ANM_WaitSit;
        }
        return;
    }

    if (shouldBeScared()) {
        if (field_0xB6E == 0) {
            if (fn_800301b0(mPositionCopy2, mRotation.y + 0x8000, true, 140.f) != 0) {
                field_0xB6E = 1;
            } else if (isWithinPlayerRadius(600.f) || mNearbyBombRef.isLinked()) {
                changeState(StateID_Escape);
                field_0xB6A = 1;
            } else {
                calcHeadRotation(false, 0);
                mAnimTimer = 48.f + cM::rndF(48.f);
                field_0xB44 = 128;
                if (isWithinPlayerRadius(250.f)) {
                    changeState(StateID_Scared);
                }
            }
        } else if (calcHeadRotation(false, 0)) {
            changeState(StateID_Escape);
            field_0xB6A = 1;
            mAnimTimer = 20;
            field_0xB44 = 128;
        } else if (isWithinPlayerRadius(250.f)) {
            changeState(StateID_Scared);
        }
        return;
    }

    field_0xB6E = 0;
    calcHeadRotation(false, 0);
    if (field_0xB56 != 0 || fn_177_8F90()) {
        return;
    }

    if (0 == sLib::calcTimer(&mAnimTimer) && field_0xB66) {
        field_0xB66 = false;
        field_0xB6A = 0;
        field_0xB6E = 0;
        field_0xB44 = 128.f + cM::rndF(128.f);

        changeState(StateID_Wait);
        return;
    }

    if (mIsHorwellsPet == 1) {
        return;
    }
    if (sLib::calcTimer(&field_0xB44)) {
        return;
    }

    // Maybe Inline
    bool coPlayer = false;
    if (mSph.ChkCoHit()) {
        coPlayer = true;
        if (mSph.GetCoActor()->isActorPlayer()) {
            coPlayer = false;
        }
    }

    if (field_0xB50 == 0 && !coPlayer && !fn_177_8C20(getXZAngleToPlayer())) {
        if (mAng::absDiff2(getXZAngleToPlayer(), mAng(pPlayer->mRotation.y + 0x8000)) > 0x2000) {
            field_0xB69 = 1;
            field_0xB67 = 0;

            changeState(StateID_Walk);
            return;
        }
    }

    if (mAnimation == ANM_WaitSit) {
        mMdl.setAnm("RemlyWaitSitCry", m3d::PLAY_MODE_4, 10.f);
        mAnimation = ANM_WaitSitCry;
    }
    field_0xB44 = 128.f + cM::rndF(128.f);
}
void dAcEremly_c::finalizeState_Wait() {}

void dAcEremly_c::initializeState_Walk() {
    if (field_0xB67 != 0 && field_0xB6A == 0) {
        mMdl.setAnm("RemlyCryWalk", m3d::PLAY_MODE_4, 10.f);
        mAnimation = ANM_CryWalk;
    } else {
        mMdl.setAnm("RemlyWalk", m3d::PLAY_MODE_4, 10.f);
        mAnimation = ANM_RemlyWalk;
    }

    field_0xB32 = 0;
    field_0xB34 = 0;

    if (field_0xB69 != 0) {
        fn_177_77C0();
    }
    mSomeCounter = 0;
    field_0xB66 = true;
}
void dAcEremly_c::executeState_Walk() {
    s32 _weird_zero = 0;

    bool b0 = false;
    const dAcPy_c *pPlayer = dAcPy_c::GetLink();

    resetHeadRotation(true);

    if (fn_177_86C0()) {
        return;
    }

    if (!field_0xB6A && mAng::absDiff2(getXZAngleToPlayer(), mRotation.y) < 0x4000 && shouldBeScared() &&
        field_0xB6E == 0) {
        changeState(StateID_Escape);
        field_0xB6A = 1;
        return;
    }

    if (field_0xB69 != 0) {
        mMtx_c m;
        m.YrotS(field_0xB32);
        mVec3_c in(0.f, 0.f, 180.f), out;
        m.multVec(in, out);
        out += pPlayer->mPosition;
        mTargetPosition.set(out);

        if (fn_177_7040(2, 0.8f)) {
            field_0xB32 += field_0xB34;
        }
        if (mSph.ChkCoHit()) {
            b0 = true;
            if (mSph.GetCoActor()->isActorPlayer() &&
                mAng::absDiff2<mAng>(getXZAngleToPlayer(), pPlayer->mRotation.y + 0x8000) >
                    mAng(1024.f + cM::rndF(1024.f))) {
                b0 = false;
            }
        }

        if (b0 ||
            mAng::absDiff2<mAng>(getXZAngleToPlayer(), pPlayer->mRotation.y + 0x8000) <
                mAng(1024.f + cM::rndF(1024.f)) ||
            fn_177_8C20(mRotation.y)) {
            field_0xB69 = 0;
            changeState(StateID_Wait);
            return;
        }
    } else if (field_0xB6A == 0 && !fn_177_8C20(mRotation.y)) {
        calcHeadRotation(false, 0);

        if (isHorwellRemlitQuestComplete()) {
            mTargetPosition.set(mHorwellRef.get()->mPosition);
            fn_177_7040(2, 0.8f);
        } else {
            fn_177_7040(0, 0.8f);
        }
    } else {
        sLib::addCalcScaled(&mSpeed, 0.7f, 3.f + _weird_zero);
        fn_177_7040(0, 0.f);
        if (mAng::absDiff2<mAng>(getXZAngleToPlayer(), mRotation.y) < 0x400 && !calcHeadRotation(false, -1000)) {
            changeState(StateID_Wait);
            mMdl.setAnm("RemlyWaitSitCry", m3d::PLAY_MODE_4, 4.f);
            mAnimation = ANM_WaitSitCry;
        }
        return;
    }

    if (mAnimation == ANM_CryWalk && mMdl.getAnm().isStop()) {
        field_0xB67 = 0;
        mMdl.setAnm("RemlyWalk", m3d::PLAY_MODE_4, 2.f);
        mAnimation = ANM_RemlyWalk;
    }

    if (fn_177_8F90()) {
        return;
    }
}
void dAcEremly_c::finalizeState_Walk() {}

void dAcEremly_c::initializeState_Run() {
    mMdl.setAnm("RemlyRun", m3d::PLAY_MODE_4, 2.f);
    mAnimation = ANM_Run;
    mMdl.setRate(1.5f);
    field_0xB66 = true;
}
void dAcEremly_c::executeState_Run() {
    resetHeadRotation(true);
    calcHeadRotation(false, 0);
    if (fn_177_86C0()) {
        return;
    }
    if (shouldBeScared()) {
        if (isWithinPlayerRadius(600.f)) {
            changeState(StateID_Escape);
            field_0xB6A = 1;
        } else {
            changeState(StateID_Walk);
        }
        return;
    }
    if (isHorwellRemlitQuestComplete()) {
        mTargetPosition.set(mHorwellRef.get()->mPosition);
        fn_177_7040(2, 5.f);
    } else {
        fn_177_7040(0, 5.f);
    }
    if (fn_177_8F90()) {
        return;
    }
}
void dAcEremly_c::finalizeState_Run() {}

void dAcEremly_c::initializeState_Escape() {
    field_0xB6E = 0;
    mAcceleration = -3.f;
    mMdl.setAnm("RemlyRun", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_Run;
    mMdl.setRate(2.f);
    field_0xB66 = true;
    field_0xB6A = 0;
    field_0xB69 = 0;
    mAnimTimer = 0;
    mSomeCounter2 = 0;
}
void dAcEremly_c::executeState_Escape() {
    s32 _weird_zero = 0;

    resetHeadRotation(false);
    if (0 == sLib::calcTimer(&mAnimTimer)) {
        fn_177_7040(1, 20.f);
    } else {
        sLib::addCalcScaledDiff(&mSpeed, 20.f, 0.7f, 3.f);
    }

    if (fn_177_8C20(mRotation.y) || fn_177_8980(140.f)) {
        field_0xB6E = 1;
        if (field_0xB64 == 0) {
            changeState(StateID_Scared);
        } else {
            field_0xB69 = 0;
            changeState(StateID_Walk);
        }
        return;
    }

    if (field_0xB6A != 0) {
        f32 f = (_weird_zero + 400.f);
        field_0xB6C = 1;
        if (!isWithinTargetRadius(600.f + f) && !mNearbyBombRef.isLinked()) {
            field_0xB69 = 0;
            changeState(StateID_Walk);
        }
    }
}
void dAcEremly_c::finalizeState_Escape() {}

void dAcEremly_c::initializeState_EscapeDash() {
    mMdl.setAnm("RemlyRun", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_Run;
    mMdl.setRate(2.f);
    field_0xB66 = true;
    field_0xB6A = 0;
    field_0xB6B = false;
    mSomeCounter2 = 0;
}
void dAcEremly_c::executeState_EscapeDash() {
    resetHeadRotation(false);

    // Idk
    s32 a = getXZAngleToPlayer() + 0x8000;
    if (!field_0xB6B) {
        if (mAng::absDiff2<mAng>(a, mAngle.y) > 0x1000) {
            sLib::addCalcAngle(mAngle.y.ref(), a, 1, 0x1000);
        } else {
            field_0xB6B = true;
            mAngle.y = a;
        }
    }

    if (field_0xB6B) {
        mVec3_c v;
        sLib::addCalcScaledDiff(&mSpeed, (v = mPosition, 30.f), 0.7f, 3.f);

        if (fn_177_8C20(mRotation.y)) {
            changeState(StateID_Scared);
        } else if (fn_177_8980(200.f)) {
            changeState(StateID_Scared);
        }
    }
}
void dAcEremly_c::finalizeState_EscapeDash() {}

void dAcEremly_c::initializeState_Wind() {
    if (mAnimation != ANM_WindBack && mAnimation != ANM_Wind) {
        if (mAnimation == ANM_Scared || mAng::absDiff2<mAng>(getXZAngleToPlayer(), mRotation.y + 0x8000) < 0x6000) {
            mMdl.setAnm("RemlyWindBack", m3d::PLAY_MODE_4, 4.f);
            mAnimation = ANM_WindBack;
        } else {
            mMdl.setAnm("RemlyWind", m3d::PLAY_MODE_4, 4.f);
            mAnimation = ANM_Wind;
        }
        mAnimSafeTimer = 11;
    }
    mAnimTimer = 11;
}
void dAcEremly_c::executeState_Wind() {
    resetHeadRotation(false);
    sLib::addCalcScaled(&mSpeed, 0.7f, 5.f);
    if (mAnimation == ANM_Wind) {
        fn_177_7040(0, 0.f);
        sLib::addCalcAngle(mRotation.y.ref(), mAngle.y, 2, 0x800);
    }

    if (0 != sLib::calcTimer(&mAnimTimer)) {
        return;
    }

    if (field_0xB64 == 0) {
        changeState(StateID_EscapeDash);
        return;
    }

    if (!isScary()) {
        field_0xB66 = true;
        changeState(StateID_Wait);

        mMdl.setAnm("RemlyWaitStand", m3d::PLAY_MODE_4, 10.f);
        mAnimation = ANM_WaitStand;
        return;
    }

    if (isPlayerInNightRange(100.f)) {
        changeState(StateID_NightRun);
    } else {
        changeState(StateID_NightRet);
    }
}
void dAcEremly_c::finalizeState_Wind() {}

void dAcEremly_c::initializeState_Hold() {
    s32 _weird_zero = 0;

    mMdl.setAnm("RemlyHold", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_Hold;

    mSph.ClrCoSet();
    mYOffset = 0.f;
    mYPosition = mPosition.y;

    field_0xB6A = 0;
    field_0xB6C = 0;
    field_0xB56 = 0;
    field_0xB6E = 0;

    mStts.SetRank(5);
    mSlope.clear();
    mSlopeXTarget = mSlopeZTarget = 0;
    if (isScary() || field_0xB68) {
        mMdl.setAnm("RemlyHoldBata", m3d::PLAY_MODE_4, 4.f);
        mAnimation = ANM_HoldBata;
    }

    if (isScary()) {
        field_0xB64 = 2;
        field_0xB68 = 0;
    }

    mLinkage.field_0x90 = 13.f + _weird_zero;
    mLinkage.field_0x8C = 13.f + _weird_zero;
    if (mIsHorwellsPet == 1) {
        mLinkage.field_0x90 = 26.f + _weird_zero;
        mLinkage.field_0x8C = 13.f + _weird_zero;
    }

    mAcceleration = -1.75f + _weird_zero;
    mAnimTimer = 64.f + cM::rndF(64.f);
    mAnimSafeTimer = 90;
}
void dAcEremly_c::executeState_Hold() {
    resetHeadRotation(false);
    mAnimSafeTimer = 90;

    mRotation.y = mAngle.y = getXZAngleToPlayer();

    if (mAnimation == ANM_HoldMozo && mMdl.getAnm().isStop()) {
        mMdl.setAnm("RemlyHold", m3d::PLAY_MODE_4, 4.f);
        mAnimation = ANM_Hold;
    }

    if (0 == sLib::calcTimer(&mAnimTimer)) {
        if (mAnimation == ANM_Hold) {
            mMdl.setAnm("RemlyHoldMozo", m3d::PLAY_MODE_4, 4.f);
            mAnimation = ANM_HoldMozo;
        }
        mAnimTimer = 64.f + cM::rndF(64.f);
    }

    if (mLinkage.checkConnection(dLinkage_c::CONNECTION_1)) {
        return;
    }

    if (mLinkage.checkState(dLinkage_c::STATE_2)) {
        mAngle.y = getXZAngleToPlayer();
        mAngle.y += 0x8000;
        changeState(StateID_Jump);
        return;
    }

    if (isScary()) {
        mAcceleration = -3.f;
        changeState(StateID_NightFoo);
    } else if (field_0xB68 == 0) {
        mAcceleration = -3.f;
        mAnimTimer = 0;
        changeState(StateID_Wait);
    } else {
        mAcceleration = -3.f;
        changeState(StateID_EscapeDash);
    }
}
void dAcEremly_c::finalizeState_Hold() {}

void dAcEremly_c::initializeState_Jump() {
    mSomeCounter = 0;
    unsetActorProperty(AC_PROP_0x1);
    field_0xB50 = 90;
    mMdl.setAnm("RemlyHoldJumpStart", m3d::PLAY_MODE_4, 0.f);
    mAnimation = ANM_HoldJumpStart;
    mAnimSafeTimer = 90;
}
void dAcEremly_c::executeState_Jump() {
    s32 _weird_zero = 0;

    mAnimSafeTimer = 90;
    if (mAnimation == ANM_HoldJumpStart && !fn_177_8980(100.f) && (mAcch.ChkGndHit() || mVelocity.y < 0.f)) {
        mMdl.setAnm("RemlyHoldJumpEnd", m3d::PLAY_MODE_4, 0.f + _weird_zero);
        mAnimation = ANM_HoldJumpEnd;
        return;
    }

    sLib::addCalcScaled(&mSpeed, 0.3f, 0.2f);
    if (mSpeed && mAcch.ChkGndHit()) {
        spawnGroundEffect();
        mSpeed = 0.f;
    }

    if (mAcch.ChkGndHit() && mMdl.getAnm().isStop()) {
        mAngle.y = mRotation.y;
        setActorProperty(AC_PROP_0x1);
        mAcceleration = -3.f;

        if (isScary()) {
            changeState(StateID_NightFoo);
        } else if (field_0xB68 == 0) {
            changeState(StateID_Wait);
        } else {
            changeState(StateID_EscapeDash);
        }
    }
}
void dAcEremly_c::finalizeState_Jump() {}

void dAcEremly_c::initializeState_Fly() {
    s32 _weird_zero = 0;

    mAcch.ClrGndThinCellingOff();
    mFlySpeedCounter = 0;
    mFlyAngleCounter = 0;
    mNoGroundCounter = 0;
    unsetActorProperty(AC_PROP_0x1);

    field_0xB6B = false;
    mMdl.setAnm("RemlyFly", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_Fly;
    mMdl.setRate(1.3f);
    field_0xB66 = true;
    field_0xB6A = 0;
    mSomeCounter = 0;
    field_0xB68 = 0;

    if (isScary()) {
        field_0xB64 = 2;
    } else {
        field_0xB64 = 1;
    }

    mAcch.ClrRoofNone();

    f32 f = 200.f + _weird_zero;

    mYOffset = 0.f;
    mAnmStep = 0;

    mAcch.SetField_0xD4(f);

    mVelocity.x = mVelocity.z = 0.f;
    mSpeed = 0.f;
}
void dAcEremly_c::executeState_Fly() {
    s32 _weird_zero = 0;

    if (mIsHorwellsPet == 1 && !isHorwellRemlitQuestComplete() && EventManager::isInEvent()) {
        setActorProperty(AC_PROP_0x1);
        mPosition.set(mStartingPos.x, mStartingPos.y, mStartingPos.z);
        mAngle.y = mRotation.y = mStartingRot.y;
        changeState(StateID_Sleep);
        return;
    }

    resetHeadRotation(false);

    if (mAnimation == ANM_FlyDamage && mMdl.getAnm().isStop()) {
        mMdl.setAnm("RemlyFly", m3d::PLAY_MODE_4, 4.f);
        mAnimation = ANM_Fly;
        mMdl.setRate(1.3f);
    }

    if (mAcch.ChkWallHit(nullptr) || mAcch.ChkRoofHit()) {
        mAng a;
        if (mAcch.ChkWallHit(nullptr)) {
            a = mAcchCir.GetWallAngleY() + 0x8000;
        } else {
            a = mAngle.y + 0x8000;
        }

        mSomeCounter++;
        mVec3_c v(0.f, 0.f, 0.f);

        getXZCirclePoint(v, a, -10.f + _weird_zero);

        field_0xAF8.set(v);
    }

    mAng toStartingPosAngle = cLib::targetAngleY(mPosition, mStartingPos);

    switch (mAnmStep) {
        default: {
            sLib::addCalcScaledDiff(&mVelocity.y, 8.f, 0.7f, 2.f);
            sLib::addCalcScaled(&mAcceleration, 0.7f, 2.f);
            sLib::addCalcAngle(mAngle.y.ref(), getXZAngleToPlayer(), 2, 0x800);
            sLib::addCalcAngle(mRotation.y.ref(), mAngle.y, 2, 0x800);
            if (mStartingPos.y + 300.f < mPosition.y) {
                mAcceleration = 0.f;
                mVelocity.y = 0.f;
                mAnmStep++;
            }
        } break;
        case 1: {
            if (mSomeCounter < 120 && fn_177_7650(false)) {
                changeState(StateID_BirthWait);
                return;
            }

            f32 targetSpeed;
            if (mSomeCounter < 120 && mStartingPos.squareDistanceToXZ(mPosition) > 2500.f) {
                u32 angle = ++mFlySpeedCounter * 1000;
                targetSpeed = mAng(angle).sin();
                targetSpeed *= _weird_zero + 30.f;
            } else {
                targetSpeed = 0.f;
            }
            sLib::addCalcScaledDiff(&mYOffset, targetSpeed, 0.5f, 10.f);

            if (mSomeCounter < 120 && mStartingPos.squareDistanceToXZ(mPosition) > 2500.f) {
                s32 angle = ++mFlyAngleCounter * 300;
                f32 tmp = mAng(angle).cos();
                tmp *= 10000.f + _weird_zero;

                sLib::addCalcAngle(mAngle.y.ref(), toStartingPosAngle + tmp, 2, 0x800);
                sLib::addCalcAngle(mRotation.y.ref(), toStartingPosAngle, 2, 0x800);

                if (mAcch.ChkWallHit(nullptr)) {
                    sLib::addCalcScaledDiff(&mVelocity.y, 8.f, 0.7f, 2.f);
                    if (std::abs(mPosition.y - field_0xAA4.y) < 4.f) {
                        mSomeCounter++;
                    }
                } else {
                    mAcceleration = 0.f;
                    mVelocity.y = 0.f;
                    mSomeCounter = 0;
                }
                targetSpeed = 8.f;
            } else {
                sLib::addCalcScaledDiff(&mVelocity.y, -8.f, 0.7f, 2.f);

                targetSpeed = 0.f;

                if (mSomeCounter >= 120 && fn_177_8980(100.f) && !d3d::isOnScreen(mPosition)) {
                    changeState(StateID_BirthWait);
                    return;
                }
                if (mAcch.ChkGndHit()) {
                    mYOffset = 0.f;
                    mAcceleration = -3.f;
                    mSpeed = 0.f;
                    mAnmStep++;
                    mAcch.SetRoofNone();

                    setActorProperty(AC_PROP_0x1);
                    if (mSomeCounter >= 120) {
                        field_0xB64 = 0;
                        changeState(StateID_Scared);
                    } else {
                        if (isScary()) {
                            changeState(StateID_NightWait);
                        } else {
                            mMdl.setAnm("RemlyWakeUp", m3d::PLAY_MODE_4, 4.f);
                            mMdl.setFrame(mMdl.getAnm().getEndFrame() - 1.f);
                            mMdl.setRate(-1.f);
                            mAnimation = ANM_WakeUp;
                        }
                    }
                    return;
                }
            }
            sLib::addCalcScaledDiff(&mSpeed, targetSpeed, 0.7f, 3.f);
        } break;
        case 2: {
            if (mMdl.getAnm().checkFrame(0.f)) {
                setActorProperty(AC_PROP_0x1);
                mAngle.y = mRotation.y;
                changeState(StateID_Sleep);
            }

        } break;
    }
}
void dAcEremly_c::finalizeState_Fly() {}

void dAcEremly_c::initializeState_Damage() {
    mSomeCounter = 0;
    mAcceleration = -3.f;
    if (mAnimation == ANM_Scared) {
        mSomeCounter = 1;
    }

    if (!isScary()) {
        mSpeed = 7.f;
    } else {
        mSpeed = 15.f;
    }

    f32 blend = 10.f;
    if (field_0xB6B) {
        blend = 0.f;
        mSpeed = 0.f;
    }
    mStts.SetRank(5);
    field_0xB6B = false;
    if (mAcch.ChkGndHit()) {
        field_0xB6B = true;
    }
    mMdl.setAnm("RemlyDamage", m3d::PLAY_MODE_4, blend);
    mAnimation = ANM_Damage;
    mAngle.y = getXZAngleToPlayer() + 0x8000;
}
void dAcEremly_c::executeState_Damage() {
    if (mAcch.ChkGndHit()) {
        sLib::addCalcScaled(&mSpeed, 0.5f, 1.f);
        if (!field_0xB6B) {
            spawnGroundEffect();
            field_0xB6B = true;
        }
    }

    if (mSomeCounter != 0) {
        mAnimSafeTimer = 8;
        s32 targetAngle = getXZAngleToPlayer() + 0x8000;
        if (mAng::absDiff2<mAng>(targetAngle, mAngle.y) > 0x1000) {
            sLib::addCalcAngle(mRotation.y.ref(), targetAngle, 1, 0x1000);
        } else {
            mRotation.y = targetAngle;
        }
    }

    if (mSpeed < 0.1f && mMdl.getAnm().isStop()) {
        if (mSomeCounter) {
            changeState(StateID_EscapeDash);
            field_0xB6B = true;
        } else {
            mAngle.y = mRotation.y;
            if (isScary() && field_0xB64 != 0) {
                if (isPlayerInNightRange(100.f)) {
                    changeState(StateID_NightRun);
                } else {
                    changeState(StateID_NightRet);
                }
            } else {
                changeState(StateID_EscapeDash);
            }
        }
    }
}
void dAcEremly_c::finalizeState_Damage() {}

void dAcEremly_c::initializeState_Sleep() {
    mMdl.setAnm("RemlySleep", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_Sleep;
    mTexPat.setFrame(2.f, 0);
    mMdlCallback.mHeadRotation.clear();
}
void dAcEremly_c::executeState_Sleep() {
    if (mAnimation == ANM_WakeUp) {
        if (mMdl.getAnm().isStop()) {
            changeState(StateID_Wait);
        }
    } else if (fn_177_86C0() || fn_177_8F90() || mSph.ChkCoHit()) {
        if (mAnimation != ANM_WakeUp) {
            mMdl.setAnm("RemlyWakeUp", m3d::PLAY_MODE_4, 4.f);
            mAnimation = ANM_WakeUp;
        }
    }
}
void dAcEremly_c::finalizeState_Sleep() {}

void dAcEremly_c::initializeState_Scared() {
    mMdl.setAnm("RemlyScared", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_Scared;
    field_0xB66 = true;
    mSpeed = 0.f;
    mSomeCounter = 0;
    mStts.SetRank(11);
}
void dAcEremly_c::executeState_Scared() {
    if (isScary()) {
        return;
    }

    fn_177_86C0();
    if (mMdl.getAnm().getRate()) {
        resetHeadRotation(false);
    } else if (field_0xB64 != 0) {
        sLib::addCalcAngle(mMdlCallback.mHeadRotation.z.ref(), 8000, 20, 0x100);
        if (mSomeCounter > 80 && !shouldBeScared()) {
            field_0xB68 = 0;
            field_0xB66 = true;
            changeState(StateID_Wait);
        }
    }

    sLib::addCalcScaled(&mSpeed, 0.7f, 3.f);

    if (field_0xB64 == 0) {
        return;
    }

    if (!shouldBeScared()) {
        if (++mSomeCounter > 30) {
            mMdl.setRate(0.f);
        }
    } else {
        mSomeCounter = 0;
        mMdl.setRate(1.f);
    }
}
void dAcEremly_c::finalizeState_Scared() {}

void dAcEremly_c::initializeState_Stun() {
    field_0xB6B = false;
    mAcceleration = -3.f;
    if (mAnimation != ANM_Piyo1 && mAnimation != ANM_Piyo2) {
        mMdl.setAnm("RemlyPiyo1", m3d::PLAY_MODE_4, 4.f);
        mAnimation = ANM_Piyo1;
    } else {
        mMdl.setAnm("RemlyPiyo1", m3d::PLAY_MODE_4, 4.f);
        mAnimation = ANM_Piyo1;
        mMdl.setFrame(11.f);
        field_0xB6B = true;
    }

    if (!isScary()) {
        field_0xB64 = 0;
    }

    mStts.SetRank(11);
    checkSlope(true);
    field_0xB6A = 0;
    mSph.ClrAtSet();
    field_0xB68 = 1;
}
void dAcEremly_c::executeState_Stun() {
    switch (mAnimation) {
        default: {
            if (mMdl.getAnm().isStop()) {
                mMdl.setAnm("RemlyPiyo2", m3d::PLAY_MODE_4, 4.f);
                mAnimation = ANM_Piyo2;
                mAnimTimer = 300;
                checkSlope(true);
            }
        } break;
        case ANM_Piyo2: {
            if (sLib::calcTimer(&mAnimTimer)) {
                field_0xB6B = true;
            } else {
                mMdl.setAnm("RemlyPiyo3", m3d::PLAY_MODE_4, 4.f);
                mAnimation = ANM_Piyo3;
            }
        } break;
        case ANM_Piyo3: {
            if (mMdl.getAnm().checkFrame(36.f)) {
                mSpeed = 15.f;
            }
            if (mMdl.getAnm().isStop()) {
                mStts.SetRank(5);
                if (isScary()) {
                    if (isPlayerInNightRange(100.f)) {
                        changeState(StateID_NightRun);
                    } else {
                        changeState(StateID_NightRet);
                    }
                } else {
                    changeState(StateID_Escape);
                }
            }
        } break;
    }
    if (mAnimation != ANM_Piyo2 && mAnimation != ANM_Piyo3) {}

    sLib::addCalcScaled(&mSpeed, 0.5f, 1.f);
    if (field_0xB6B) {
        mVec3_c pos, scale;
        pos.set(mPosition);
        pos.y += 50.f;

        scale.set(mScale);
        scale *= 0.5f;
        mEmitters[0].holdEffect(PARTICLE_RESOURCE_ID_MAPPING_309_, pos, &mRotation, &scale, nullptr, nullptr);
        holdSound(SE_E_PIYORI_LV);
    }
}
void dAcEremly_c::finalizeState_Stun() {}

void dAcEremly_c::initializeState_Water() {
    mMdl.setAnm("RemlySwim", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_Swim;
    field_0xB6B = false;
    mAcceleration = 0.f;
    mSpeed = 0.f;
    mSph.ClrAtSet();
    field_0xB68 = 0;

    if (!isScary()) {
        field_0xB64 = 1;
    } else {
        field_0xB64 = 2;
    }

    mStts.SetRank(11);
    field_0xB6E = 0;
    field_0xB6A = 0;
    startSound(SE_ERemly_DIVE_WATER);
    field_0xB6F = 0;
    field_0xB66 = true;
}
void dAcEremly_c::executeState_Water() {
    s32 _weird_zero = 0;

    resetHeadRotation(false);
    if (field_0xB6B) {
        mVec3_c out(mVec3_c::Zero);
        mVec3_c pos;
        pos.set(mPosition);
        pos.y = mWaterHeight;
        if (dTgStream_c::getForce(pos, out)) {
            mAngle.y = out.atan2sX_Z();
            sLib::addCalcScaledDiff(&mSpeed, out.absXZ(), 0.2f, 0.2f);
            field_0xB6F = 1;
        } else if (field_0xB6F) {
            mStts.SetRank(5);
            mAcceleration = -3.f;

            if ((s32)++mNoGroundCounter > 30) {
                changeState(StateID_Fly);
            }
        } else {
            mSpeed = 0.f;
            mAcceleration = -3.f;
            mStts.SetRank(5);

            if (mAnimation != ANM_SwimDamage && mAnimation != ANM_Water) {
                mMdl.setAnm("RemlyWater", m3d::PLAY_MODE_4, 4.f);
                mAnimation = ANM_Water;

            } else if (mAnimation == ANM_SwimDamage || mMdl.getAnm().isStop()) {
                if (isScary()) {
                    field_0xB6E = 1;
                    changeState(StateID_Fly);
                } else {
                    changeState(StateID_Walk);
                }
            }
        }
    } else {
        if (mAnimation == ANM_SwimDamage && mMdl.getAnm().isStop()) {
            mMdl.setAnm("RemlySwim", m3d::PLAY_MODE_4, 10.f);
            mAnimation = ANM_Swim;
        }
        mSwimPosYCounter++;
        f32 yOffset = -35.f + _weird_zero;
        if (mAnimSafeTimer != 0) {
            yOffset -= 10.f + _weird_zero;
        }
        fn_177_7040(0, 0.8f);
        (void)dAcPy_c::GetLink()->mPosition.absXZTo(mPosition);
        if (isWithinTargetRadius(220.f)) {
            mSpeed = 0.f;
        }
        f32 yTarget = mWaterHeight + yOffset + mAng(mSwimPosYCounter * 2000).sin() * 3.f;
        sLib::addCalcScaledDiff(&mPosition.y, yTarget, 0.7f, 3.f);
        sLib::addCalcScaled(&mVelocity.y, 0.8f, 10.f);
    }
}
void dAcEremly_c::finalizeState_Water() {}

void dAcEremly_c::initializeState_Hear() {
    mSomeCounter = 0;
    mAnmStep = 0;
    switch (mAnimation) {
        default: {
            mMdl.setAnm("RemlyWaitSit", m3d::PLAY_MODE_4, 4.f);
            mAnimation = ANM_WaitSit;
            field_0xB66 = false;
        } // fallthrough
        case ANM_WaitSit: {
            mAnmStep = 1;
        } break;
        case ANM_CryWalk:
        case ANM_RemlyWalk:
        case ANM_Run:       {
            mMdl.setAnm("RemlyWaitStand", m3d::PLAY_MODE_4, 4.f);
            mAnimation = ANM_WaitStand;
            field_0xB66 = true;
            mAnimTimer = 24.f + cM::rndF(24.f);
        }
    }
}
void dAcEremly_c::executeState_Hear() {
    sLib::addCalcScaled(&mSpeed, 0.7f, 5.f);
    switch (mAnmStep) {
        case 0: {
            if (mSpeed < 0.2f && 0 == sLib::calcTimer(&mAnimTimer)) {
                mMdl.setAnm("RemlyWaitSit", m3d::PLAY_MODE_4, 10.f);
                mAnimation = ANM_WaitSit;
                field_0xB66 = false;
                mAnmStep++;
            }
        } break;
        case 1: {
            if (!calcHeadRotation(false, 0) && fn_177_86C0()) {
                if (mAnimation == ANM_WaitSitCry && mMdl.getAnm().isStop()) {
                    mMdl.setAnm("RemlyWaitSit", m3d::PLAY_MODE_4, 20.f);
                    mAnimation = ANM_WaitSit;
                } else {
                    if (mHeadTiltTimer > 50) {
                        mHeadTiltTimer = 50;
                        if (mAnimation == ANM_WaitSit) {
                            mMdl.setAnm("RemlyWaitSitCry", m3d::PLAY_MODE_4, 10.f);
                            mAnimation = ANM_WaitSitCry;
                        }
                    }
                    calcHeadPitch(false);
                }
            } else if (shouldBeScared()) {
                changeState(StateID_Escape);
                field_0xB6A = 1;
            } else {
                if (mAnimation != ANM_WaitSit) {
                    mMdl.setAnm("RemlyWaitSit", m3d::PLAY_MODE_4, 10.f);
                    mAnimation = ANM_WaitSit;
                }
                fn_177_8F90();
                resetHeadRotation(true);
            }
        } break;
    }
}
void dAcEremly_c::finalizeState_Hear() {}

void dAcEremly_c::initializeState_NightSleepDemo() {
    mMdl.setAnm("RemlySleep", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_SleepNight;
    mSph.ClrAtSet();
    mSph.ClrTgSet();
    mSph.ClrCoSet();
}
void dAcEremly_c::executeState_NightSleepDemo() {
    nightSleepDemoImpl();
}
void dAcEremly_c::finalizeState_NightSleepDemo() {}

void dAcEremly_c::initializeState_NightFoo() {
    mMdl.setAnm("RemlyFoo", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_Foo;
    mAnimTimer = 12.f + cM::rndF(12.f);
    field_0xB6D = 0;
    field_0xB6E = 0;
    field_0xB6B = false;
    mSph.ClrAtSet();
}
void dAcEremly_c::executeState_NightFoo() {
    const dAcPy_c *pPlayer = dAcPy_c::GetLink();

    resetHeadRotation(false);
    sLib::addCalcScaled(&mSpeed, 0.7f, 5.f);
    sLib::addCalcAngle(mAngle.y.ref(), getXZAngleToPlayer(), 2, 0x1000);
    sLib::addCalcAngle(mRotation.y.ref(), mAngle.y, 2, 0x1000);
    if (field_0xB6D == 0) {
        if (0 != sLib::calcTimer(&mAnimTimer)) {
            return;
        }
        if (mAng::absDiff2<s32>(getXZAngleToPlayer(), mRotation.y) > 0x400) {
            return;
        }
    } else {
        if (!mNearbyBombRef.isLinked()) {
            changeState(StateID_NightWait);
            return;
        }
        if (pPlayer->getPosition().absXZTo(mPosition) > mNearbyBombRef.get()->mPosition.absXZTo(mPosition)) {
            sLib::addCalcAngle(
                mAngle.y.ref(), cLib::targetAngleY(mPosition, mNearbyBombRef.get()->mPosition), 2, 0x800
            );
            return;
        }
    }
    if (field_0xB6E != 0) {
        changeState(StateID_Fly);
        return;
    }

    if (field_0xB6B || pPlayer->mPosition.absXZTo(mStartingPos) > mPatrolAreaSize + 500.f) {
        changeState(StateID_NightRet);
        return;
    }

    if (mAng::absDiff2<s32>(getXZAngleToPlayer(), mRotation.y) < 0x1000) {
        changeState(StateID_NightJumpAttack);
        if (!mAcch.ChkWallHit(nullptr) && fn_800301b0(mPositionCopy2, mRotation.y, true, 120.f) == 0) {
            return;
        }
        field_0xB6E = 1;
    }
}
void dAcEremly_c::finalizeState_NightFoo() {}

void dAcEremly_c::initializeState_NightReflectionFoo() {
    mMdl.setAnm("RemlyFoo", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_Foo;
    mMdl.setRate(0.f);
    mSpeed *= -1.f;
    mAnimTimer = 24.f + cM::rndF(24.f);
    field_0xB6B = false;
    if (mAcch.ChkGndHit()) {
        field_0xB6B = true;
    }
    mSph.ClrAtSet();
}
void dAcEremly_c::executeState_NightReflectionFoo() {
    const dAcPy_c *pPlayer = dAcPy_c::GetLink();
    resetHeadRotation(false);
    if (mAcch.ChkGndHit()) {
        sLib::addCalcScaled(&mSpeed, 0.5f, 2.f);
        if (!field_0xB6B) {
            spawnGroundEffect();
            field_0xB6B = true;
        }
    }

    if (!mMdl.getAnm().getRate()) {
        if (mSpeed < 0.1f) {
            mMdl.setRate(1.f);
        }
        return;
    }

    if (pPlayer->isRecovering() || sLib::calcTimer(&mAnimTimer)) {
        return;
    }

    if (pPlayer->mPosition.absXZTo(mStartingPos) > mPatrolAreaSize + 500.f || !isPlayerInNightRange(100.f)) {
        changeState(StateID_NightRet);
    } else {
        changeState(StateID_NightRun);
        mAnimTimer = 24.f + cM::rndF(24.f);
    }
}
void dAcEremly_c::finalizeState_NightReflectionFoo() {}

void dAcEremly_c::initializeState_NightWait() {
    checkSlope(true);
    mMdl.setAnm("RemlyWaitStandNight", m3d::PLAY_MODE_4, 4.f);
    mAnimTimer = 24.f + cM::rndF(24.f);
    mSph.ClrAtSet();
}
void dAcEremly_c::executeState_NightWait() {
    if (mNearbyBombRef.isLinked()) {
        changeState(StateID_NightRet);
        return;
    }

    if (isPlayerInNightRange(0.f)) {
        changeState(StateID_NightRun);
        return;
    }

    if (fn_177_86C0()) {
        changeState(StateID_NightFoo);
        field_0xB6D = 1;
        return;
    }

    if (0 == sLib::calcTimer(&mAnimTimer)) {
        changeState(StateID_NightWalk);
        return;
    }

    sLib::addCalcScaled(&mSpeed, 0.5f, 5.f);
}
void dAcEremly_c::finalizeState_NightWait() {}

// ???
inline bool compareDistance(const mVec3_c &v0, const mVec3_c &v1, f32 f) {
    return v0.squareDistanceToXZ(v1) > f * f;
}
void dAcEremly_c::initializeState_NightWalk() {
    mMdl.setAnm("RemlyWalkNight", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_WalkNight;

    mTargetPosition.set(mStartingPos);

    // odd
    if (!compareDistance(mPosition, mStartingPos, mPatrolAreaSize)) {
        mTargetPosition.x += cM::rndFX(mPatrolAreaSize * 0.5f);
        mTargetPosition.z += cM::rndFX(mPatrolAreaSize * 0.5f);
    }
}
void dAcEremly_c::executeState_NightWalk() {
    calcHeadRotation(true, 0);
    if (mNearbyBombRef.isLinked()) {
        changeState(StateID_NightRet);
        return;
    }

    s32 idk = fn_800301b0(mPositionCopy2, mRotation.y, true, 120.f);
    if (idk != 0) {
        mTargetPosition.set(getStartingPosition());
        if (idk == 1) {
            mAng a = cLib::targetAngleY(mPosition, getStartingPosition());
            if (mAng::absDiff2<mAng>(mRotation.y, a) < 0x400) {
                field_0xB6E = 1;
                changeState(StateID_Fly);
                return;
            }
        }
    }

    if (fn_177_7040(2, 0.8f)) {
        mTargetPosition.set(mStartingPos);
        if (idk == 0) {
            mTargetPosition.x += cM::rndFX(mPatrolAreaSize * 0.5f);
            mTargetPosition.z += cM::rndFX(mPatrolAreaSize * 0.5f);
        }
    }

    if (isPlayerInNightRange(0.f)) {
        changeState(StateID_NightRun);
    } else if (fn_177_86C0()) {
        changeState(StateID_NightFoo);
        field_0xB6D = 1;
    }
}
void dAcEremly_c::finalizeState_NightWalk() {}

void dAcEremly_c::initializeState_NightRun() {
    mMdl.setAnm("RemlyRunFast", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_RunFast;
    mSomeCounter2 = 0;
    field_0xB6B = false;
    mSph.ClrAtSet();
    mAcceleration = -3.f;
    mMdl.setRate(1.5f);
    mAnimTimer = 0;
    fn_177_77C0();
}
void dAcEremly_c::executeState_NightRun() {
    const dAcPy_c *pPlayer = dAcPy_c::GetLink();
    calcHeadRotation(false, 0);

    mMtx_c m;
    mVec3_c in, out;
    mVec3_c stuffs0, stuffs1;

    if (!field_0xB6B) {
        // What...
        const mVec3_c &target = dScGame_c::getCamera()->getTarget();
        f32 z = target.z;
        f32 y = target.y;
        f32 x = target.x;
        stuffs0.x = x;
        stuffs0.y = y;
        stuffs1.z = z;

        if (fn_177_7040(2, 10.f)) {
            field_0xB32 += field_0xB34;
        }

        if (fn_177_8C20(mRotation.y)) {
            changeState(StateID_NightFoo);
            return;
        }

        m.YrotS(field_0xB32);
        in.set(0.f, 0.f, 320.f);
        m.multVec(in, out);
        out += pPlayer->mPosition;
        mTargetPosition.set(out);
    }

    if (mNearbyBombRef.isLinked()) {
        changeState(StateID_NightRet);
        return;
    }

    if (!isPlayerInNightRange(100.f)) {
        changeState(StateID_NightFoo);
        field_0xB6B = true;
        return;
    }

    if (0 == sLib::calcTimer(&mAnimTimer) && isWithinPlayerRadius(400.f) && d3d::isOnScreen(mPosition)) {
        field_0xB6B = true;
    }

    if (field_0xB6B) {
        fn_177_7040(0, 5.0f);
        if (mAnimTimer == 0) {
            if (mAng::absDiff2<mAng>(getXZAngleToPlayer(), mRotation.y) < mAng(4608.f + cM::rndF(4096.f))) {
                changeState(StateID_NightFoo);
            }
        }
    }
}
void dAcEremly_c::finalizeState_NightRun() {}

void dAcEremly_c::initializeState_NightRet() {
    mMdl.setAnm("RemlyRunFast", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_RunFast;
    mTargetPosition.set(mStartingPos);
    field_0xB6B = false;
    mSomeCounter2 = 0;
}
void dAcEremly_c::executeState_NightRet() {
    resetHeadRotation(true);
    if (mNearbyBombRef.isLinked()) {
        mSpeed = 10.f;
        f32 radius = 500.f;
        mAngle.y = cLib::targetAngleY(mNearbyBombRef.get()->mPosition, mPosition);
        sLib::addCalcAngle(mRotation.y.ref(), mAngle.y, 2, 0x800);

        if (mNearbyBombRef.get()->mPosition.squareDistanceToXZ(mPosition) > radius * radius) {
            changeState(StateID_NightFoo);
            field_0xB6D = 1;
        }
        return;
    }

    if (fn_177_8C20(mRotation.y)) {
        changeState(StateID_NightFoo);
        field_0xB6E = 1;
        return;
    }

    if (fn_177_7040(2, 10.f)) {
        changeState(StateID_NightWait);
    }
}
void dAcEremly_c::finalizeState_NightRet() {}

void dAcEremly_c::initializeState_NightJumpAttack() {
    mSpeed = 20.f;
    mVelocity.y = 27.f + cM::rndF(3.f);
    mSph.OnAtSet();
    mMdl.setAnm("RemlyJump", m3d::PLAY_MODE_4, 0.f);
    mAnimation = ANM_Jump;
    field_0xB6E = 0;
    field_0xB32 = 0;
    field_0xB6B = false;
}
void dAcEremly_c::executeState_NightJumpAttack() {
    resetHeadRotation(false);
    if (mSph.ChkAtHit()) {
        if (mSph.GetAtFlag0x8()) {
            setBattleBgmRelated(0);
            mSpeed = -20.f;
            changeState(StateID_Stun);
            return;
        }

        if (mSph.GetAtFlag0x2()) {
            changeState(StateID_NightReflectionFoo);
            return;
        }
    }

    if (mAcch.ChkGndHit()) {
        spawnGroundEffect();
        if (field_0xB6E != 0 &&
            (mAcch.ChkWallHit(nullptr) || fn_800301b0(mPositionCopy2, mRotation.y, true, 120.f) != 0)) {
            changeState(StateID_NightFoo);
            field_0xB6B = true;
            return;
        }
        changeState(StateID_NightRun);
        mAnimTimer = 24.f + cM::rndF(24.f);
    }
}
void dAcEremly_c::finalizeState_NightJumpAttack() {}

void dAcEremly_c::initializeState_BirthWait() {
    mAnimTimer = 300;
    unsetActorProperty(AC_PROP_0x1);
    field_0xB64 = 2;
    field_0xB68 = 0;
    mMdl.setAnm("RemlyWaitStand", m3d::PLAY_MODE_4, 4.f);
    mAnimation = ANM_WaitStand;
}
void dAcEremly_c::executeState_BirthWait() {
    if (!sLib::calcTimer(&mAnimTimer) && fn_177_7650(true)) {
        mPosition.set(mStartingPos.x, mStartingPos.y, mStartingPos.z);
        mOldPosition.set(mStartingPos.x, mStartingPos.y, mStartingPos.z);
        mPosition.y += 10.f;
        mVelocity.set(0.f, 0.f, 0.f);
        mSpeed = 0.f;
        mAcceleration = -3.f;
        setActorProperty(AC_PROP_0x1);

        if (isScary()) {
            changeState(StateID_NightWait);
        } else if (mIsHorwellsPet == 1 && isHorwellRemlitQuestComplete()) {
            changeState(StateID_Wait);
        } else {
            changeState(StateID_Sleep);
        }
    }
}
void dAcEremly_c::finalizeState_BirthWait() {}

void dAcEremly_c::playWink() {
    if (isState(StateID_Sleep)) {
        return;
    }

    if (sLib::calcTimer(&mWinkTimer)) {
        mTexPat.play();
    } else {
        mWinkTimer = 40.f + cM::rndF(120.f);
        mTexPat.setFrame(0.f, 0);
    }
}

// NONMATCHING
bool dAcEremly_c::calcHeadRotation(bool lookAtTarget, const mAng &range) {
    const dAcPy_c *pPlayer = dAcPy_c::GetLink();

    mAng angleTargetX, angleTargetY;
    if (lookAtTarget) {
        angleTargetX = cLib::targetAngleX(mPosition, mTargetPosition) * 0.7f;

        angleTargetY = (mAng)cLib::targetAngleY(mPosition, mTargetPosition);
        angleTargetY -= mRotation.y;
    } else {
        angleTargetX = cLib::targetAngleX(mPosition, pPlayer->getHeadTranslation()) * 0.7f;

        angleTargetY = getXZAngleToPlayer();
        angleTargetY -= mRotation.y;
    }

    mVec3_c outpos;
    if (!dLightEnv_c::GetPInstance()->check_BPM8(&mPosition, &outpos)) {
        dAcObjBase_c *pActor = pPlayer->getBeetleInFlight();
        if (pActor) {
            field_0xA50.set(pActor->mPosition);
            field_0xB56 = 30;
        }
    }

    if (sLib::calcTimer(&field_0xB56)) {
        angleTargetX = cLib::targetAngleX(mPosition, field_0xA50) * 0.7f;
        angleTargetY = (mAng)cLib::targetAngleY(mPosition, field_0xA50);
        angleTargetY -= mRotation.y;
    }

    if (isHorwellRemlitQuestComplete()) {
        outpos.set(mHorwellRef.get()->mPosition);
        outpos.y += 100.f;

        angleTargetX = cLib::targetAngleX(mPosition, outpos) * 0.7f;
        angleTargetY = (mAng)cLib::targetAngleY(mPosition, outpos);
        angleTargetY -= mRotation.y;
        fn_177_79D0(true);
    }

    if (angleTargetX < 0) {
        angleTargetY = 0;
    } else if (angleTargetX > 2500) {
        angleTargetX = 2500;
    }
    sLib::addCalcAngle(mMdlCallback.mHeadRotation.z.ref(), angleTargetX, 2, 0x800);
    if (mAnimation == ANM_WaitSitCry) {
        return false;
    }

    bool b = false;
    s32 t0 = range + 8000;
    if (isState(StateID_Wait) || isState(StateID_Walk)) {
        t0 = range + 6000;
    }
    s16 t = t0;

    if (angleTargetY < -t) {
        angleTargetY = -t;
        b = true;
    } else if (angleTargetY > t) {
        angleTargetY = t;
        b = true;
    }
    sLib::addCalcAngle(mMdlCallback.mHeadRotation.y.ref(), angleTargetY, 2, 0x800);
    return b;
}

void dAcEremly_c::calcHeadPitch(bool b) {
    if (!b && shouldBeScared()) {
        return;
    }

    if (!sLib::calcTimer(&mHeadTiltTimer)) {
        mHeadTiltTimer = 100.f + cM::rndF(100.f);
        if (cM::rnd() < 0.3f) {
            mHeadTiltTarget = 0;
        } else if (mHeadTiltTarget == 0) {
            if (cM::rnd() < 0.5f) {
                mHeadTiltTarget = 5000;
            } else {
                mHeadTiltTarget = -5000;
            }
        } else {
            mHeadTiltTarget *= -1;
            mHeadTiltTimer = 50.f + cM::rndF(100.f);
        }
    }
    sLib::addCalcAngle(mMdlCallback.mHeadRotation.x.ref(), mHeadTiltTarget, 10, 0x200);
}

void dAcEremly_c::resetHeadRotation(bool bOnlyRotX) {
    sLib::addCalcAngle(mMdlCallback.mHeadRotation.x.ref(), 0, 2, 0x400);
    if (!bOnlyRotX) {
        sLib::addCalcAngle(mMdlCallback.mHeadRotation.y.ref(), 0, 2, 0x400);
        sLib::addCalcAngle(mMdlCallback.mHeadRotation.z.ref(), 0, 2, 0x400);
    }
}

// I do not like how this is written and somehow it matches
bool dAcEremly_c::fn_177_7040(u32 param0, f32 f) {
    const dAcPy_c *pPlayer = dAcPy_c::GetLink();

    f32 f0;
    s16 targetAngle;
    s16 targetRatio;
    s16 targetStepSize;

    targetAngle = getXZAngleToPlayer();
    if (param0 != 2) {
        mTargetPosition.set(pPlayer->mPosition);
    }

    if (param0 == 1) {
        targetAngle += 0x8000;
    } else if (param0 == 2) {
        targetAngle = cLib::targetAngleY(mPosition, mTargetPosition);
    }

    f0 = mAng(mAng::abs(mAngle.y - targetAngle)).sin();
    targetRatio = 4.f - std::abs(f0 * 2.f);

    f0 = mAng(mAng::abs(mAngle.y - targetAngle)).sin();
    targetStepSize = 2048.f + std::abs(f0 * 512.f);

    if (mAnimation == ANM_WalkNight || mAnimation == ANM_RemlyWalk) {
        f32 f = mAng(mAng::abs(mAngle.y - targetAngle)).sin();
        mMdl.setRate(f * 4.f + 1.f);
        targetRatio = 10;
        targetStepSize = 0x400;
    }

    if (mAnimation == ANM_RemlyWalk) {
        f32 f = mAng(mAng::abs(mAngle.y - targetAngle)).sin();
        mMdl.setRate(f + 1.f);
        targetRatio = 5;
        targetStepSize = 0x400;
    }

    if (mAnimation == ANM_Wind || mNearbyBombRef.isLinked()) {
        targetRatio = 1;
        targetStepSize = 0x2000;
    }

    sLib::addCalcAngle(mAngle.y.ref(), targetAngle, targetRatio, targetStepSize);
    f0 = 1.5f;
    f0 -= std::abs(mAng(mAng::abs(mAngle.y - targetAngle)).sin());
    sLib::addCalcScaledDiff(&mSpeed, f * f0, 0.7f, 3.f);
    if (mTargetPosition.squareDistanceToXZ(mPosition) < 10000.f) {
        return true;
    } else {
        return false;
    }
}

bool dAcEremly_c::isScary() {
    if (StoryflagManager::sInstance->getFlag(STORYFLAG_GRATITUDE_QUEST_FINISHED) &&
        (dScGame_c::isCurrentStage("F000") || dScGame_c::isCurrentStage("D000"))) {
        return false;
    }

    if (dScGame_c::currentSpawnInfo.isNight()) {
        return true;
    }

    return false;
}

bool dAcEremly_c::shouldBeScared() {
    if (isState(StateID_Sleep) || isScary() || field_0xB68) {
        return false;
    }

    dAcPy_c *pPlayer = dAcPy_c::GetLinkM();
    if (pPlayer->isUsingSword() || pPlayer->checkActionFlagsCont(0x2) || pPlayer->checkActionFlagsCont(0x80) ||
        pPlayer->isCarryingBomb() || pPlayer->isUsingWhip() || pPlayer->checkActionFlagsCont(0x4) ||
        pPlayer->checkActionFlagsCont(0x100) || pPlayer->checkActionFlagsCont(0x10)) {
        return true;
    }

    if (mNearbyBombRef.isLinked()) {
        return true;
    }

    field_0xB6C = 0;
    return false;
}

bool dAcEremly_c::isWithinTargetRadius(f32 radius) {
    if (isHorwellRemlitQuestComplete()) {
        mTargetPosition.set(mHorwellRef.get()->getPosition());
        if (mTargetPosition.squareDistanceToXZ(mPosition) < radius * radius) {
            return true;
        }
    }

    if (isWithinPlayerRadius(radius)) {
        return true;
    }

    return false;
}

bool dAcEremly_c::isHorwellRemlitQuestComplete() {
    if (mHorwellRef.isLinked() && mSceneflag != 0xFF) {
        if (SceneflagManager::sInstance->checkBoolFlag(mRoomID, mSceneflag)) {
            return true;
        }
    }
    return false;
}

bool dAcEremly_c::fn_177_7650(bool comparePlayer) const {
    f32 radius = 0.f;
    mVec3_c pos = mPosition;

    if (comparePlayer) {
        pos.set(mStartingPos);
    }

    if (d3d::isOnScreen(pos)) {
        return false;
    }

    radius = 40.f;
    radius *= 100.f;
    if (comparePlayer) {
        const dAcPy_c *pPlayer = dAcPy_c::GetLink();
        return pPlayer->mPosition.squareDistanceToXZ(pos) > radius * radius;
    } else {
        mVec3_c camPos = dScGame_c::getCamera()->getPosition();
        return camPos.squareDistanceToXZ(pos) > radius * radius;
    }
}

// NONMATCHING
void dAcEremly_c::fn_177_77C0() {
    const dAcPy_c *pPlayer = dAcPy_c::GetLink();
    s32 a0 = getXZAngleToPlayer().mVal + 0x8800;
    mAng a1 = getXZAngleToPlayer() + 0x7800;

    field_0xB32 = getXZAngleToPlayer() + 0x8000;
    field_0xB32 += cM::rndFX(0x3000);

    if (mAng::abs(a0 - pPlayer->mRotation.y) < mAng::abs(a1 - pPlayer->mRotation.y)) {
        field_0xB34 = mAng(0x800);
    } else {
        field_0xB34 = mAng(0xf800);
    }

    field_0xB32 += field_0xB34;
}

void dAcEremly_c::applyScaryMat() {
    mWinkTimer = 0;
    nw4r::g3d::ResFile fil = mMdl.getMdlFile();
    nw4r::g3d::ResMdl mdl = fil.GetResMdl("Remly");
    mMdl.getModel().setCullMode(m3d::getMatID(mdl, "mouth"), GX_CULL_BACK, false);
    mMdl.getModel().setCullMode(m3d::getMatID(mdl, "eye_scary"), GX_CULL_BACK, false);
    mMdl.getModel().setCullMode(m3d::getMatID(mdl, "night_set"), GX_CULL_BACK, false);

    mTexPat.setAnm(mMdl.getModel(), fil.GetResAnmTexPat("RemlyScaryFace"), 0, m3d::PLAY_MODE_4);
}

// NONMATCHING
void dAcEremly_c::fn_177_79D0(bool b) {
    mMtx_c m;
    mVec3_c in, out;
    if (mHorwellRef.isLinked()) {
        m.YrotS(mHorwellRef.get()->mRotationCopy.y + 0x5000);
        in.set(0.f, 0.f, 120.f);
        m.multVec(in, out);
        out += mHorwellRef.get()->getPosition();

        if (!b) {
            setPosition(out);
            setOldPosition(getStartingPosition());
            mYPosition = out.y;
            setStartingRotation(mRotation);
        }
        setStartingPosition(out);
        mTargetPosition.set(out);
    }
}

bool dAcEremly_c::isUnderWater() {
    s32 _weird_zero = 0;

    if (dBgS_WtrChk::CheckPos(&mPosition, true, 600.f + _weird_zero, -100.f + _weird_zero)) {
        mWaterHeight = dBgS_WtrChk::GetWaterHeight();

        if (mPosition.y < dBgS_WtrChk::GetWaterHeight() && mAcch.GetGroundH() < dBgS_WtrChk::GetWaterHeight()) {
            if (mAcch.GetGroundH() < -8.f + mWaterHeight) {
                if (isState(StateID_Hold)) {
                    mLinkage.forceRemove(this);
                }
                if (isState(StateID_Jump)) {
                    mAngle.y = mRotation.y;
                }
                if (!isState(StateID_Water)) {
                    changeState(StateID_Water);
                }
            }
            return true;
        }
    }
    return false;
}

void dAcEremly_c::nightSleepDemoImpl() {
    s32 _weird_zero = 0;

    if (mSceneflag == 0xFF || SceneflagManager::sInstance->checkBoolFlag(mRoomID, mSceneflag)) {
        return;
    }

    dAcPy_c *pPlayer = dAcPy_c::GetLinkM();
    dCamera_c *pCamera = dScGame_c::getCamera();

    f32 x, y, z;
    mVec3_c v, v2;
    mMtx_c m, m2;
    mAng a = mAngle.y - 0x4000;

    switch (mNightSleepDemoStep) {
        case 0: {
            if (!isWithinPlayerRadius(1500.f)) {
                break;
            }
            mNightSleepDemoStep++;
        } // fallthrough
        case 1: {
            {
                Event e("dAcEremly_c_nightRemly", 100, Event::makeEventFlag(1, 0x100001), nullptr, nullptr);
                EventManager::alsoSetAsCurrentEvent(this, &e, nullptr);
            }
            if (!EventManager::isInEvent(this, "dAcEremly_c_nightRemly")) {
                break;
            }
            mStartingRot = mRotation;
            EventManager::fn_8009FA10(this, EventManager::getSkipOnlyEvent(), nullptr);
            pPlayer->vt_0x2AC();
            mNightSleepDemoStep++;
        } // fallthrough
        case 2: {
            mMdl.setAnm("RemlyDemo", m3d::PLAY_MODE_4, 0.f);
            mAnimation = ANM_Demo;

            z = _weird_zero + -160.f;
            y = _weird_zero + 80.f;
            x = _weird_zero + 410.f;
            v.set(x, y, z);
            m.YrotS(a + 0x8000);
            m.multVec(v, field_0xAB0);

            field_0xAB0 += mStartingPos;
            field_0xAC8 = field_0xAB0;
            field_0xAE0 = field_0xAB0;

            x = _weird_zero + 30.f;
            y = _weird_zero + 30.f;
            z = _weird_zero + 30.f;
            v.set(z, y, x);

            m.YrotS(a);
            m.multVec(v, field_0xABC);

            field_0xABC += mStartingPos;
            field_0xAD4 = field_0xABC;
            field_0xAEC = field_0xABC;

            x = _weird_zero + -370.f;
            y = 0.f;
            z = 250.f;
            v.set(x, y, z);

            m.YrotS(a);
            m.multVec(v, v2);
            v2 += mStartingPos;

            mAng3_c rot(0, getXZAngleToPlayer() + 0x8000, 0);
            dAcPy_c::GetLinkM()->setPosRot(&v2, &rot, true, 0, 0);
            dAcPy_c::GetLinkM()->triggerMoveEventMaybe(13, 1, 0, 0, 0, 0, 0);
            mNightSleepDemoStep++;
        } // fallthrough
        case 3: {
            if (sLib::calcTimer(&mAnimTimer)) {
                break;
            }
            mAnimTimer = 60;
            mNightSleepDemoStep++;
        } // fallthrough
        case 4: {
            if (sLib::calcTimer(&mAnimTimer)) {
                if (mAnimTimer == 58) {
                    z = _weird_zero + 170.f;
                    x = _weird_zero + -120.f;
                    v.set(x, 0.f, z);
                    m.YrotS(a);
                    m.multVec(v, v2);
                    v2 += mStartingPos;
                    dAcPy_c::GetLinkM()->triggerMoveEventMaybe(2, 0, 0, &v2, 0, 0, 0);
                }
                break;
            }
            mAnimTimer = 100;
            mNightSleepDemoStep++;
        } // fallthrough
        case 5: {
            a = cLib::targetAngleY(field_0xAD4, field_0xAC8);
            m.YrotS(a);
            z = _weird_zero + -330.f;
            y = _weird_zero;
            x = _weird_zero;
            v.set(x, y, z);
            m.multVec(v, field_0xAE0);
            field_0xAE0 += field_0xAC8;
            if (sLib::calcTimer(&mAnimTimer)) {
                break;
            }
            mNightSleepDemoStep++;
        } // fallthrough
        case 6: {
            if (mMdl.getAnm().checkFrame(320.f)) {
                mMtx_c m3, m4;
                s32 scale = 95;
                m2.transS(0.5f, 0.5f, 0.f);
                y = scale / 100.f;
                x = scale / 100.f;
                m3.scaleS(x, y, 0.f);
                m2 += m3;
                m4.transS(-0.5f, -0.5f, 0.f);
                m2 += m4;
                dStageMgr_c::GetInstance()->procfn_800192F0(150, m2, 100);
                dRumble_c::start(dRumble_c::sRumblePreset1, 1);
            }
            if (mMdl.getAnm().isStop()) {
                mMdl.setRate(0.f);
                mNightSleepDemoStep = 10;
            }
            break;
        }
        case 10: {
            mSph.OnTgSet();
            mSph.OnCoSet();
            EventManager::finishEvent(this, nullptr);
            dScGame_c::getCamera()->fn_8019EA70(true);
            mAnimTimer = 3;
            mNightSleepDemoStep = 11;
            break;
        }
        case 11: {
            if (0 == sLib::calcTimer(&mAnimTimer)) {
                changeState(StateID_NightWait);
                mMdl.setAnm("RemlyWaitStandNight", m3d::PLAY_MODE_4, 0.f);
                mAngle.y = mStartingRot.y + -30000;
                mRotation.y = mAngle.y;
                if (mSceneflag != 0xFF) {
                    SceneflagManager::sInstance->setFlag(mRoomID, mSceneflag);
                }
            }
        }
    }

    if (mNightSleepDemoStep == 5) {
        cLib::addCalcPos2(&field_0xAB0, field_0xAE0, 0.3f, 10.f);
    }
    if (mNightSleepDemoStep == 2 || mNightSleepDemoStep == 3 || mNightSleepDemoStep == 4 || mNightSleepDemoStep == 5 ||
        mNightSleepDemoStep == 6 || mNightSleepDemoStep == 7 || mNightSleepDemoStep == 8 || mNightSleepDemoStep == 9) {
        pCamera->setEventCamView(field_0xABC, field_0xAB0, 50.f, 0.f);
        if (EventManager::fn_800A0B80()) {
            mMdl.setRate(0.f);
            mNightSleepDemoStep = 10;
        }
    }
}

void dAcEremly_c::checkSlope(bool b) {
    s32 _weird_zero = 0;
    if (!b && !mSpeed) {
        return;
    }

    if ((++mSlopeCheckCounter & 3) == 0) {
        dAcEnBase_c::checkSlope(mPosition, mSlopeXTarget, mSlopeZTarget, 4.f + _weird_zero);
        if ((u16)(mSlopeXTarget + 0x1800) > 0x3000U) {
            mSlopeXTarget = 0;
        } else if ((u16)(mSlopeZTarget + 0x1800) > 0x3000U) {
            mSlopeZTarget = 0;
        }
    }
}

void dAcEremly_c::spawnGroundEffect() {
    mMtx_c m;
    mVec3_c in, out;
    m.YrotS(mRotation.y);
    in.set(0.f, 0.f, 15.f);
    m.multVec(in, out);
    out += mPosition;

    dJEffManager_c::spawnGroundEffect(out, mPolyAttr0, mPolyAttr1, field_0x1B4, 1, mScale.x * 0.7f, field_0x1B0);
}

bool dAcEremly_c::fn_177_86C0() {
    mVec3_c outpos;
    if (dLightEnv_c::GetPInstance()->check_BPM8(&mPosition, &outpos)) {
        field_0xA50.set(outpos);

        if (!isScary()) {
            if (isState(StateID_Hear) || isState(StateID_Sleep)) {
                return true;
            }

            if (!dAcPy_c::GetLinkM()->isUsingHarp()) {
                if (!isState(StateID_Scared) && mAnimation != ANM_Sound) {
                    mMdl.setAnm("RemlySound", m3d::PLAY_MODE_4, 4.f);
                    mAnimation = ANM_Sound;
                    mSpeed = 0.f;
                }
            } else if (isState(StateID_Scared) ||
                       (isState(StateID_Wait) && mAng::absDiff2(getXZAngleToPlayer(), mRotation.y) < 0x400)) {
                if (mAnimation == ANM_Scared) {
                    if (++mHarpCounter > 40) {
                        field_0xB64 = 1;
                        mSomeCounter++;
                    }
                    return true;
                }
                changeState(StateID_Hear);
                return true;
            }
            return false;
        }
    }

    if (mHarpCounter > 0) {
        mHarpCounter--;
    }
    return false;
}

bool dAcEremly_c::fn_177_8980(f32 f) {
    dBgS_ObjGndChk gndChk;
    mVec3_c v(mPosition);

    v.y += 100.f;

    getXZCirclePoint(v, mRotation.y, f);

    gndChk.SetPos(&v);

    f32 cross = dBgS::GetInstance()->GroundCross(&gndChk);
    if (cross < mPosition.y - 1000.f || cross == -1e9f || dBgS::GetInstance()->GetGroundCode(gndChk) == 4) {
        return true;
    }
    return false;
}

bool dAcEremly_c::fn_177_8AC0() {
    bool b = (isAnimation(ANM_Fly)) || (isAnimation(ANM_FlyDamage)) || (isAnimation(ANM_HoldBata)) ||
             (isAnimation(ANM_HoldJumpStart)) || (isAnimation(ANM_HoldJumpEnd)) || (isAnimation(ANM_Run)) ||
             (isAnimation(ANM_Scared)) || (isAnimation(ANM_Swim)) || (isAnimation(ANM_SwimDamage)) ||
             (isAnimation(ANM_Water)) || (isAnimation(ANM_WindBack)) || (isAnimation(ANM_Piyo1)) ||
             (isAnimation(ANM_Piyo2)) || (isAnimation(ANM_Piyo3));
    if (b) {
        return true;
    } else {
        return false;
    }
}

bool dAcEremly_c::fn_177_8C20(mAng a) {
    s32 _weird_zero = 0;
    if (fn_800301b0(mPositionCopy2, a, true, mSpeed + (120.f + _weird_zero)) == 2) {
        if (std::abs(mPosition.y - field_0xAA4.y) < 4.f) {
            mSomeCounter++;
        }
        mStts.SetRank(11);
        return true;
    }

    if (field_0xB6C || field_0xB6A || isState(StateID_Escape) || isState(StateID_EscapeDash)) {
        mVec3_c v(mPosition);
        f32 f = 150.f + _weird_zero;
        getXZCirclePoint(v, mAngle.y, f);
        if (dBgS_WtrChk::CheckPos(&v, true, 600.f + _weird_zero, -100.f + _weird_zero)) {
            if (mPosition.y < dBgS_WtrChk::GetWaterHeight() && mAcch.GetGroundH() < dBgS_WtrChk::GetWaterHeight()) {
                return true;
            }
        }
    }

    if (fn_800301b0(mPositionCopy2, mRotation.y, true, 120.f) == 1 ||
        (mAcch.ChkWallHit(nullptr) && (mAng::abs(mAcchCir.GetWallAngleY() - mAng(mRotation.y + 0x8000)) < 0x800 ||
                                       (mSpeed && std::abs(mPosition.y - field_0xAA4.y) < 4.f)))) {
        if (mAng::absDiff2<mAng>(mRotation.y, cLib::targetAngleY(mPosition, mTargetPosition) < 0x200)) {
            if ((++mSomeCounter2 & 7) == 0) {
                mSomeCounter2 = 1;
                return true;
            }
        }
    } else {
        mSomeCounter2 = 0;
    }
    return false;
}

bool dAcEremly_c::fn_177_8F90() {
    if (field_0xB6A != 0) {
        return false;
    }
    if (sLib::calcTimer(&field_0xB50)) {
        return false;
    }
    const dAcPy_c *pPlayer = dAcPy_c::GetLink();

    if (isState(StateID_Sleep)) {
        if (isWithinTargetRadius(400.f) && std::abs(mPosition.y - pPlayer->mPosition.y) < 100.f) {
            return true;
        }
    } else if (isState(StateID_Walk)) {
        if (!isWithinTargetRadius(300.f)) {
            changeState(StateID_Run);
            return true;
        }

        if (!calcHeadRotation(false, -1000) && field_0xB69 == 0 && isWithinTargetRadius(120.f)) {
            sLib::addCalcScaled(&mSpeed, 0.7f, 5.f);
            if (++mSomeCounter < 20) {
                return false;
            }
            changeState(StateID_Wait);
            return true;
        }
    } else if (isState(StateID_Run)) {
        if (isWithinTargetRadius(200.f)) {
            field_0xB67 = 1;
            changeState(StateID_Walk);
            return true;
        }

        if (fn_177_8C20(mRotation.y)) {
            changeState(StateID_Wait);
            mMdl.setAnm("RemlyWaitSitCry", m3d::PLAY_MODE_4, 4.f);
            mAnimation = ANM_WaitSitCry;
            return true;
        }
    } else if (field_0xB6E == 0 && field_0xB6C == 0) {
        if (fn_177_8C20(getXZAngleToPlayer())) {
            return false;
        }

        if (mSomeCounter2 == 0) {
            (void)pPlayer->mPosition.absXZTo(mPosition);
            if (!isWithinTargetRadius(220.f)) {
                field_0xB67 = 1;
                changeState(StateID_Walk);
                return true;
            }
        }

        if (calcHeadRotation(false, 0)) {
            field_0xB67 = 1;
            changeState(StateID_Walk);
            return true;
        }
    }
    return false;
}

bool dAcEremly_c::isPlayerInNightRange(f32 f) {
    const dAcPy_c *pPlayer = dAcPy_c::GetLink();

    if (!pPlayer->isRecovering() && pPlayer->mPosition.absXZTo(mStartingPos) < mPatrolAreaSize &&
        std::abs(mPosition.y - pPlayer->mPosition.y) < f + 200.f) {
        return true;
    }

    return false;
}
