1#include "Library/Camera/CameraShaker.h"
2
3#include "Library/Base/StringUtil.h"
4#include "Library/Nerve/NerveSetupUtil.h"
5#include "Library/Nerve/NerveUtil.h"
6
7namespace {
8using namespace al;
9
10NERVE_IMPL(CameraShaker, Shake);
11NERVE_IMPL(CameraShaker, Wait);
12NERVE_IMPL(CameraShaker, ShakeLoop);
13
14NERVES_MAKE_NOSTRUCT(CameraShaker, Shake);
15NERVES_MAKE_STRUCT(CameraShaker, Wait, ShakeLoop);
16} // namespace
17
18namespace al {
19
20const CameraShaker::ShakeInfo WeakShakeLoop = {.name: "弱", .steps: -1, .speed: 7.5f, .strength: 0.0007f,
21 .direction: CameraShaker::ShakeDirection::Both};
22
23const CameraShaker::ShakeInfo ShakeInfos[11] = {
24 {.name: "微弱", .steps: 15, .speed: 2.5f, .strength: 0.0015f, .direction: CameraShaker::ShakeDirection::Both},
25 {.name: "微弱[短]", .steps: 10, .speed: 2.0f, .strength: 0.0008f, .direction: CameraShaker::ShakeDirection::Both},
26 {.name: "弱", .steps: 25, .speed: 2.5f, .strength: 0.0025f, .direction: CameraShaker::ShakeDirection::Both},
27 {.name: "中", .steps: 25, .speed: 2.5f, .strength: 0.004f, .direction: CameraShaker::ShakeDirection::Both},
28 {.name: "強", .steps: 30, .speed: 3.0f, .strength: 0.008f, .direction: CameraShaker::ShakeDirection::Both},
29 {.name: "最強", .steps: 45, .speed: 3.5f, .strength: 0.015f, .direction: CameraShaker::ShakeDirection::Both},
30 {.name: "超最強", .steps: 45, .speed: 3.5f, .strength: 0.05f, .direction: CameraShaker::ShakeDirection::Both},
31 {.name: "長い微弱", .steps: 60, .speed: 6.0f, .strength: 0.0025f, .direction: CameraShaker::ShakeDirection::Both},
32 {.name: "長い弱", .steps: 60, .speed: 6.0f, .strength: 0.004f, .direction: CameraShaker::ShakeDirection::Both},
33 {.name: "船内振動", .steps: 100, .speed: 6.0f, .strength: 0.0005f, .direction: CameraShaker::ShakeDirection::Both},
34 {.name: "弱[縦]", .steps: 25, .speed: 2.5f, .strength: 0.004f, .direction: CameraShaker::ShakeDirection::Vertical},
35};
36
37CameraShaker::CameraShaker() : NerveExecutor("カメラ振動") {
38 initNerve(nerve: &NrvCameraShaker.Wait, stateCount: 0);
39 mEditedShake = {.name: "NULL", .steps: 0, .speed: 0.0f, .strength: 0.0f, .direction: ShakeDirection::Both};
40}
41
42void CameraShaker::update(const char* shakeLoop) {
43 if (shakeLoop) {
44 mShakeLoop = isEqualString(str1: shakeLoop, str2: "弱") ? &WeakShakeLoop : nullptr;
45 if (isNerve(user: this, nerve: &NrvCameraShaker.Wait))
46 setNerve(user: this, nerve: &NrvCameraShaker.ShakeLoop);
47 } else {
48 mShakeLoop = nullptr;
49 if (isNerve(user: this, nerve: &NrvCameraShaker.ShakeLoop))
50 setNerve(user: this, nerve: &NrvCameraShaker.Wait);
51 }
52
53 updateNerve();
54}
55
56void CameraShaker::startShakeByAction(const char* name, const char* unused1, const char* unused2,
57 s32 steps) {
58 startShakeByName(name, steps);
59}
60
61void CameraShaker::startShakeByName(const char* name, s32 steps) {
62 s32 index = -1;
63 if (isEqualString(str1: name, str2: "微弱"))
64 index = 0;
65 else if (isEqualString(str1: name, str2: "微弱[短]"))
66 index = 1;
67 else if (isEqualString(str1: name, str2: "弱"))
68 index = 2;
69 else if (isEqualString(str1: name, str2: "中"))
70 index = 3;
71 else if (isEqualString(str1: name, str2: "強"))
72 index = 4;
73 else if (isEqualString(str1: name, str2: "最強"))
74 index = 5;
75 else if (isEqualString(str1: name, str2: "超最強"))
76 index = 6;
77 else if (isEqualString(str1: name, str2: "長い微弱"))
78 index = 7;
79 else if (isEqualString(str1: name, str2: "長い弱"))
80 index = 8;
81 else if (isEqualString(str1: name, str2: "船内振動"))
82 index = 9;
83 else if (isEqualString(str1: name, str2: "弱[縦]"))
84 index = 10;
85
86 startShakeByIndex(index, steps);
87}
88
89void CameraShaker::startShakeByHitReaction(const char* name, const char* unused1,
90 const char* unused2, s32 steps) {
91 startShakeByName(name, steps);
92}
93
94void CameraShaker::exeWait() {
95 if (isFirstStep(user: this)) {
96 mActiveShake = nullptr;
97 mShakeLoop = nullptr;
98 }
99 mOffset = {0.0f, 0.0f};
100}
101
102void CameraShaker::exeShake() {
103 if (isGreaterEqualStep(user: this, step: mActiveShake->steps)) {
104 if (mShakeLoop) {
105 setNerve(user: this, nerve: &NrvCameraShaker.ShakeLoop);
106 } else {
107 mOffset = {0.0f, 0.0f};
108 mActiveShake = nullptr;
109 setNerve(user: this, nerve: &NrvCameraShaker.Wait);
110 }
111 return;
112 }
113
114 f32 shakeSpeed = (mActiveShake->speed * 360.0f) / mActiveShake->steps;
115 f32 currentShakeStrength =
116 sead::Mathf::cos(t: sead::Mathf::deg2rad(deg: shakeSpeed * getNerveStep(user: this)));
117 f32 shakeOffset =
118 currentShakeStrength *
119 (mActiveShake->strength * (mActiveShake->steps - getNerveStep(user: this)) / mActiveShake->steps);
120 mOffset = {shakeOffset, shakeOffset};
121 if (mActiveShake->direction == ShakeDirection::Vertical)
122 mOffset.x = 0.0f;
123}
124
125void CameraShaker::exeShakeLoop() {
126 s32 nerveStep = getNerveStep(user: this);
127 f32 shakeStep = nerveStep <= 0 ? 0.0f : nerveStep / mShakeLoop->speed * sead::Mathf::pi2();
128 f32 shakeOffset = mShakeLoop->strength * sead::Mathf::cos(t: shakeStep);
129 mOffset = {shakeOffset, shakeOffset};
130 if (mShakeLoop->direction == ShakeDirection::Vertical)
131 mOffset.x = 0.0f;
132}
133
134void CameraShaker::startShakeByIndex(s32 index, s32 steps) {
135 const ShakeInfo& shake = ShakeInfos[index];
136 if (mActiveShake && (*mActiveShake > shake))
137 return;
138
139 mActiveShake = &shake;
140
141 if (steps >= 1) {
142 // requires doing this copy to match
143 // https://decomp.me/scratch/asjPP
144 ShakeInfo shake2 = shake;
145 mEditedShake = {.name: shake.name, .steps: steps, .speed: shake.speed, .strength: shake.strength, .direction: shake.direction};
146 mActiveShake = &mEditedShake;
147 mEditedShake.speed = ((f32)steps / (f32)shake2.steps) * shake.speed;
148 }
149
150 setNerve(user: this, nerve: &Shake);
151}
152
153} // namespace al
154