1#include "Item/CoinStack.h"
2
3#include "Library/LiveActor/ActorActionFunction.h"
4#include "Library/LiveActor/ActorClippingFunction.h"
5#include "Library/LiveActor/ActorInitInfo.h"
6#include "Library/LiveActor/ActorInitUtil.h"
7#include "Library/LiveActor/ActorModelFunction.h"
8#include "Library/LiveActor/ActorPoseUtil.h"
9#include "Library/Nerve/NerveSetupUtil.h"
10#include "Library/Nerve/NerveUtil.h"
11
12#include "Item/CoinStackGroup.h"
13#include "System/GameDataFunction.h"
14#include "Util/SensorMsgFunction.h"
15
16namespace {
17NERVE_IMPL(CoinStack, Wait);
18NERVE_IMPL(CoinStack, Float);
19NERVE_IMPL(CoinStack, Fall);
20NERVE_IMPL(CoinStack, Land);
21NERVE_IMPL(CoinStack, Collected);
22
23NERVES_MAKE_NOSTRUCT(CoinStack, Land, Collected);
24NERVES_MAKE_STRUCT(CoinStack, Wait, Fall, Float);
25} // namespace
26
27CoinStack::CoinStack(const char* name) : al::LiveActor(name) {}
28
29void CoinStack::init(const al::ActorInitInfo& initInfo) {
30 al::initActorWithArchiveName(actor: this, initInfo, archiveName: "CoinStack", suffix: nullptr);
31 al::initNerve(actor: this, nerve: &NrvCoinStack.Wait, maxStates: 0);
32 al::setClippingNearDistance(actor: this, near: -1.0f);
33 makeActorDead();
34}
35
36bool CoinStack::receiveMsg(const al::SensorMsg* message, al::HitSensor* other,
37 al::HitSensor* self) {
38 if (rs::isMsgItemGetAll(message)) {
39 al::invalidateClipping(actor: this);
40 al::setNerve(user: this, nerve: &Collected);
41 return true;
42 }
43
44 return false;
45}
46
47CoinStack* CoinStack::getBelow() {
48 return mStackBelow;
49}
50
51CoinStack* CoinStack::getAbove() {
52 return mStackAbove;
53}
54
55void CoinStack::makeStackAppear() {
56 CoinStack* stack = this;
57 do {
58 stack->makeActorAlive();
59 stack = stack->getAbove();
60 } while (stack != nullptr);
61}
62
63void CoinStack::makeStackDisappear() {
64 CoinStack* stack = this;
65 do {
66 stack->makeActorDead();
67 stack = stack->getAbove();
68 } while (stack != nullptr);
69}
70
71void CoinStack::changeAlpha(f32 alphaMask) {
72 CoinStack* stack = this;
73 do {
74 al::stopDitherAnimAutoCtrl(actor: stack);
75 al::setModelAlphaMask(actor: stack, alphaMask);
76 stack = stack->getAbove();
77 } while (stack != nullptr);
78}
79
80f32 CoinStack::getFallSpeed() {
81 if (!al::isNerve(user: this, nerve: &NrvCoinStack.Fall))
82 mFallSpeed = 0.0f;
83 else
84 mFallSpeed = sead::Mathf::min(a: mFallSpeed + 0.5, b: 20.0f);
85
86 return mFallSpeed;
87}
88
89void CoinStack::setAbove(CoinStack* stack) {
90 mStackAbove = stack;
91}
92
93void CoinStack::setBelow(CoinStack* stack) {
94 mStackBelow = stack;
95}
96
97void CoinStack::signalFall(u32 delay, f32 radius) {
98 mLandHeight -= *mExternalFallDistance;
99 f32 fallDistance = *mExternalFallDistance;
100 mClippingRadius = radius;
101 mClippingPos.y += fallDistance * -0.5f;
102
103 if (mStackAbove != nullptr)
104 mStackAbove->signalFall(delay: delay + 1, radius);
105
106 if (!al::isNerve(user: this, nerve: &NrvCoinStack.Float) && !al::isNerve(user: this, nerve: &NrvCoinStack.Fall)) {
107 mFloatDuration = delay * 5;
108 al::setNerve(user: this, nerve: &NrvCoinStack.Float);
109 }
110}
111
112void CoinStack::postInit(CoinStackGroup* coinStackGroup, const sead::Vector3f& transY,
113 CoinStack* below, const sead::Vector3f& clippingPos, f32 clippingRadius,
114 const f32* fallDistance) {
115 mExternalFallDistance = fallDistance;
116 mCoinStackGroup = coinStackGroup;
117 mStackBelow = below;
118 mClippingPos = clippingPos;
119 mClippingRadius = clippingRadius;
120 al::setClippingInfo(actor: this, clippingRadius, &mClippingPos);
121 al::setTrans(actor: this, trans: transY);
122 mLandHeight = transY.y;
123 mTransY = transY.y;
124 if (mStackBelow != nullptr)
125 mStackBelow->setAbove(this);
126}
127
128void CoinStack::exeWait() {
129 if (al::isFirstStep(user: this))
130 al::startAction(actor: this, actionName: "Wait");
131}
132
133void CoinStack::exeFloat() {
134 if (al::isLessStep(user: this, step: mFloatDuration))
135 return;
136
137 al::setNerve(user: this, nerve: &NrvCoinStack.Fall);
138}
139
140void CoinStack::exeFall() {
141 if (al::isFirstStep(user: this))
142 al::startAction(actor: this, actionName: "Fall");
143
144 if (mTransY < mLandHeight) {
145 al::setTransY(actor: this, y: mLandHeight);
146 al::setNerve(user: this, nerve: &Land);
147 return;
148 }
149
150 if (mStackBelow != nullptr && mTransY - mStackBelow->getTransY() < *mExternalFallDistance)
151 al::setNerve(user: this, nerve: &Land);
152 else
153 al::setTransY(actor: this, y: mTransY);
154}
155
156void CoinStack::exeLand() {
157 if (al::isFirstStep(user: this)) {
158 al::startAction(actor: this, actionName: "Land");
159 mFallSpeed = 0.0;
160 }
161
162 if (al::isActionEnd(actor: this)) {
163 if (mStackAbove == nullptr)
164 mCoinStackGroup->validateClipping();
165
166 al::setClippingInfo(actor: this, mClippingRadius, &mClippingPos);
167
168 al::Nerve* nerve;
169 if (mTransY > mLandHeight)
170 nerve = &NrvCoinStack.Fall;
171 else
172 nerve = &NrvCoinStack.Wait;
173
174 al::setNerve(user: this, nerve);
175 }
176}
177
178void CoinStack::exeCollected() {
179 al::startHitReaction(actor: this, name: "取得");
180 GameDataFunction::addCoin(writer: this, count: 5);
181 mClippingRadius = mCoinStackGroup->setStackAsCollected(this);
182
183 if (mStackBelow != nullptr)
184 mStackBelow->setAbove(mStackAbove);
185
186 if (mStackAbove != nullptr) {
187 mStackAbove->setBelow(mStackBelow);
188 mStackAbove->signalFall(delay: 0, radius: mClippingRadius);
189 }
190
191 kill();
192}
193