#include "Library/Camera/CameraShaker.h" #include "Library/Base/StringUtil.h" #include "Library/Nerve/NerveSetupUtil.h" #include "Library/Nerve/NerveUtil.h" namespace { using namespace al; NERVE_IMPL(CameraShaker, Shake); NERVE_IMPL(CameraShaker, Wait); NERVE_IMPL(CameraShaker, ShakeLoop); NERVES_MAKE_NOSTRUCT(CameraShaker, Shake); NERVES_MAKE_STRUCT(CameraShaker, Wait, ShakeLoop); } // namespace namespace al { const CameraShaker::ShakeInfo WeakShakeLoop = {"弱", -1, 7.5f, 0.0007f, CameraShaker::ShakeDirection::Both}; const CameraShaker::ShakeInfo ShakeInfos[11] = { {"微弱", 15, 2.5f, 0.0015f, CameraShaker::ShakeDirection::Both}, {"微弱[短]", 10, 2.0f, 0.0008f, CameraShaker::ShakeDirection::Both}, {"弱", 25, 2.5f, 0.0025f, CameraShaker::ShakeDirection::Both}, {"中", 25, 2.5f, 0.004f, CameraShaker::ShakeDirection::Both}, {"強", 30, 3.0f, 0.008f, CameraShaker::ShakeDirection::Both}, {"最強", 45, 3.5f, 0.015f, CameraShaker::ShakeDirection::Both}, {"超最強", 45, 3.5f, 0.05f, CameraShaker::ShakeDirection::Both}, {"長い微弱", 60, 6.0f, 0.0025f, CameraShaker::ShakeDirection::Both}, {"長い弱", 60, 6.0f, 0.004f, CameraShaker::ShakeDirection::Both}, {"船内振動", 100, 6.0f, 0.0005f, CameraShaker::ShakeDirection::Both}, {"弱[縦]", 25, 2.5f, 0.004f, CameraShaker::ShakeDirection::Vertical}, }; CameraShaker::CameraShaker() : NerveExecutor("カメラ振動") { initNerve(&NrvCameraShaker.Wait, 0); mEditedShake = {"NULL", 0, 0.0f, 0.0f, ShakeDirection::Both}; } void CameraShaker::update(const char* shakeLoop) { if (shakeLoop) { mShakeLoop = isEqualString(shakeLoop, "弱") ? &WeakShakeLoop : nullptr; if (isNerve(this, &NrvCameraShaker.Wait)) setNerve(this, &NrvCameraShaker.ShakeLoop); } else { mShakeLoop = nullptr; if (isNerve(this, &NrvCameraShaker.ShakeLoop)) setNerve(this, &NrvCameraShaker.Wait); } updateNerve(); } void CameraShaker::startShakeByAction(const char* name, const char* unused1, const char* unused2, s32 steps) { startShakeByName(name, steps); } void CameraShaker::startShakeByName(const char* name, s32 steps) { s32 index = -1; if (isEqualString(name, "微弱")) index = 0; else if (isEqualString(name, "微弱[短]")) index = 1; else if (isEqualString(name, "弱")) index = 2; else if (isEqualString(name, "中")) index = 3; else if (isEqualString(name, "強")) index = 4; else if (isEqualString(name, "最強")) index = 5; else if (isEqualString(name, "超最強")) index = 6; else if (isEqualString(name, "長い微弱")) index = 7; else if (isEqualString(name, "長い弱")) index = 8; else if (isEqualString(name, "船内振動")) index = 9; else if (isEqualString(name, "弱[縦]")) index = 10; startShakeByIndex(index, steps); } void CameraShaker::startShakeByHitReaction(const char* name, const char* unused1, const char* unused2, s32 steps) { startShakeByName(name, steps); } void CameraShaker::exeWait() { if (isFirstStep(this)) { mActiveShake = nullptr; mShakeLoop = nullptr; } mOffset = {0.0f, 0.0f}; } void CameraShaker::exeShake() { if (isGreaterEqualStep(this, mActiveShake->steps)) { if (mShakeLoop) { setNerve(this, &NrvCameraShaker.ShakeLoop); } else { mOffset = {0.0f, 0.0f}; mActiveShake = nullptr; setNerve(this, &NrvCameraShaker.Wait); } return; } f32 shakeSpeed = (mActiveShake->speed * 360.0f) / mActiveShake->steps; f32 currentShakeStrength = sead::Mathf::cos(sead::Mathf::deg2rad(shakeSpeed * getNerveStep(this))); f32 shakeOffset = currentShakeStrength * (mActiveShake->strength * (mActiveShake->steps - getNerveStep(this)) / mActiveShake->steps); mOffset = {shakeOffset, shakeOffset}; if (mActiveShake->direction == ShakeDirection::Vertical) mOffset.x = 0.0f; } void CameraShaker::exeShakeLoop() { s32 nerveStep = getNerveStep(this); f32 shakeStep = nerveStep <= 0 ? 0.0f : nerveStep / mShakeLoop->speed * sead::Mathf::pi2(); f32 shakeOffset = mShakeLoop->strength * sead::Mathf::cos(shakeStep); mOffset = {shakeOffset, shakeOffset}; if (mShakeLoop->direction == ShakeDirection::Vertical) mOffset.x = 0.0f; } void CameraShaker::startShakeByIndex(s32 index, s32 steps) { const ShakeInfo& shake = ShakeInfos[index]; if (mActiveShake && (*mActiveShake > shake)) return; mActiveShake = &shake; if (steps >= 1) { // requires doing this copy to match // https://decomp.me/scratch/asjPP ShakeInfo shake2 = shake; mEditedShake = {shake.name, steps, shake.speed, shake.strength, shake.direction}; mActiveShake = &mEditedShake; mEditedShake.speed = ((f32)steps / (f32)shake2.steps) * shake.speed; } setNerve(this, &Shake); } } // namespace al