1#include "Item/CoinCollect.h"
2
3#include <math/seadQuat.h>
4
5#include "Library/Collision/PartsConnector.h"
6#include "Library/Controller/PadRumbleFunction.h"
7#include "Library/Effect/EffectSystemInfo.h"
8#include "Library/LiveActor/ActorActionFunction.h"
9#include "Library/LiveActor/ActorClippingFunction.h"
10#include "Library/LiveActor/ActorCollisionFunction.h"
11#include "Library/LiveActor/ActorFlagFunction.h"
12#include "Library/LiveActor/ActorInitFunction.h"
13#include "Library/LiveActor/ActorInitUtil.h"
14#include "Library/LiveActor/ActorMovementFunction.h"
15#include "Library/LiveActor/ActorPoseUtil.h"
16#include "Library/LiveActor/ActorSensorUtil.h"
17#include "Library/Nature/NatureUtil.h"
18#include "Library/Nerve/NerveSetupUtil.h"
19#include "Library/Nerve/NerveUtil.h"
20#include "Library/Placement/PlacementFunction.h"
21#include "Library/Scene/SceneObjUtil.h"
22#include "Library/Se/SeFunction.h"
23#include "Library/Stage/StageSwitchUtil.h"
24#include "Library/Thread/FunctorV0M.h"
25
26#include "Item/CoinCollectEmpty.h"
27#include "Item/CoinCollectHintState.h"
28#include "Item/CoinCollectHolder.h"
29#include "Item/CoinCollectWatcher.h"
30#include "Item/CoinRotateCalculator.h"
31#include "Item/CoinStateCountUp.h"
32#include "MapObj/CapMessageShowInfo.h"
33#include "System/GameDataFunction.h"
34#include "Util/ExternalForceKeeper.h"
35#include "Util/ItemUtil.h"
36#include "Util/PlayerUtil.h"
37#include "Util/SensorMsgFunction.h"
38#include "Util/ShadowUtil.h"
39#include "Util/WaterSurfaceShadow.h"
40
41namespace {
42NERVE_IMPL(CoinCollect, Wait);
43NERVE_IMPL(CoinCollect, WaitAmiibo);
44NERVE_IMPL(CoinCollect, Got);
45NERVE_IMPL(CoinCollect, CountUp);
46NERVE_IMPL(CoinCollect, Blow);
47
48NERVES_MAKE_STRUCT(CoinCollect, Wait, CountUp, WaitAmiibo, Got, Blow);
49} // namespace
50
51CoinCollect::CoinCollect(const char* name) : al::LiveActor(name) {}
52
53void CoinCollect::init(const al::ActorInitInfo& initInfo) {
54 using CoinCollectFunctor = al::FunctorV0M<CoinCollect*, void (CoinCollect::*)()>;
55
56 al::initActorSceneInfo(actor: this, info: initInfo);
57 rs::createCoinCollectWatcher(objHolder: this);
58 rs::createCoinCollectHolder(objHolder: this);
59
60 al::getSceneObj<CoinCollectHolder>(user: this)->registerCoinCollect(this);
61
62 if (GameDataFunction::isGotCoinCollect(accessor: this, coinCollect: initInfo)) {
63 const char* archiveName = rs::getStageCoinCollectEmptyArchiveName(actor: this);
64 al::getSceneObj<CoinCollectWatcher>(user: this)->registerCoin(isCountUpCoin: true);
65 CoinCollectEmpty* coinCollectEmpty = new CoinCollectEmpty("コレクトコイン空", archiveName);
66 al::initCreateActorWithPlacementInfo(actor: coinCollectEmpty, initInfo);
67 makeActorDead();
68 mCoinCollectEmpty = coinCollectEmpty;
69 _198 = false;
70 return;
71 }
72
73 al::getSceneObj<CoinCollectWatcher>(user: this)->registerCoin(isCountUpCoin: false);
74 const char* archiveName = rs::getStageCoinCollectArchiveName(actor: this);
75 al::initActorWithArchiveName(actor: this, initInfo, archiveName, suffix: nullptr);
76 al::initNerve(actor: this, nerve: &NrvCoinCollect.Wait, maxStates: 2);
77
78 mStateCountUp = new CoinStateCountUp(this);
79 al::initNerveState(user: this, state: mStateCountUp, nerve: &NrvCoinCollect.CountUp, hostName: "カウントアップ状態");
80
81 mHintState = new CoinCollectHintState(this);
82 al::initNerveState(user: this, state: mHintState, nerve: &NrvCoinCollect.WaitAmiibo, hostName: "ヒント");
83
84 al::tryAddDisplayOffset(actor: this, initInfo);
85 mMtxConnector = al::tryCreateMtxConnector(actor: this, info: initInfo);
86
87 mQuat.set(al::getQuat(actor: this));
88
89 mPlacementId = al::createPlacementId(initInfo);
90 mExternalForceKeeper = new ExternalForceKeeper();
91
92 mRotateCalculator = new CoinRotateCalculator(this);
93
94 al::listenStageSwitchOnKill(user: this, action: CoinCollectFunctor(this, &CoinCollect::listenKill));
95 if (al::listenStageSwitchOnAppear(user: this, action: CoinCollectFunctor(this, &CoinCollect::listenAppear)))
96 makeActorDead();
97 else
98 makeActorAlive();
99
100 al::offCollide(actor: this);
101 mShadowLength = rs::setShadowDropLength(this, initInfo, "本体");
102 al::expandClippingRadiusByShadowLength(actor: this, nullptr, radius: mShadowLength);
103 mWaterSurfaceShadow = rs::tryCreateWaterSurfaceCoinShadow(initInfo);
104 al::setEffectNamedMtxPtr(this, "WaterSurface", &mSurfaceMatrix);
105}
106
107void CoinCollect::initAfterPlacement() {
108 if (mMtxConnector != nullptr)
109 al::attachMtxConnectorToCollision(mtxConnector: mMtxConnector, actor: this, false);
110}
111
112void CoinCollect::control() {
113 sead::Vector3f force = sead::Vector3f::zero;
114 mExternalForceKeeper->calcForce(force: &force);
115 mExternalForceKeeper->reset();
116
117 bool isNotWait = !al::isNerve(user: this, nerve: &NrvCoinCollect.Wait);
118 mRotateCalculator->update(force, checkWater: isNotWait);
119
120 if (!al::isNerve(user: this, nerve: &NrvCoinCollect.Got) && !al::isNerve(user: this, nerve: &NrvCoinCollect.Wait))
121 rs::tryUpdateWaterSurfaceCoinShadow(mWaterSurfaceShadow, this, mShadowLength);
122}
123
124bool CoinCollect::receiveMsg(const al::SensorMsg* message, al::HitSensor* other,
125 al::HitSensor* self) {
126 if (mExternalForceKeeper->receiveMsg(message, other, self))
127 return true;
128
129 if (rs::isMsgItemAmiiboKoopa(message)) {
130 sead::Vector3f direction = sead::Vector3f::zero;
131 al::calcDirBetweenSensorsH(&direction, other, self);
132 sead::Vector3f velocity = 20.0f * direction;
133 sead::Vector3f verticalVelocity = 40.0f * sead::Vector3f::ey;
134 verticalVelocity += velocity;
135 al::setVelocity(actor: this, vel: verticalVelocity);
136 al::setNerve(user: this, nerve: &NrvCoinCollect.Blow);
137 return true;
138 }
139 if (!al::isNerve(user: this, nerve: &NrvCoinCollect.Wait) && !al::isNerve(user: this, nerve: &NrvCoinCollect.WaitAmiibo))
140 return false;
141
142 if (rs::isMsgCapAttack(message) && !al::isNerve(user: this, nerve: &NrvCoinCollect.Got) &&
143 !al::isNerve(user: this, nerve: &NrvCoinCollect.CountUp)) {
144 al::invalidateClipping(actor: this);
145 al::setNerve(user: this, nerve: &NrvCoinCollect.Got);
146 return false;
147 }
148 if (rs::isMsgFishingItemGet(message) && !al::isNerve(user: this, nerve: &NrvCoinCollect.Got) &&
149 !al::isNerve(user: this, nerve: &NrvCoinCollect.CountUp)) {
150 al::invalidateClipping(actor: this);
151 al::setNerve(user: this, nerve: &NrvCoinCollect.CountUp);
152 return true;
153 }
154 if (rs::isMsgFishingLineTouch(message)) {
155 mRotateCalculator->addFishingLineTouch();
156 return true;
157 }
158 if (rs::isMsgItemGetAll(message) && !al::isNerve(user: this, nerve: &NrvCoinCollect.Got) &&
159 !al::isNerve(user: this, nerve: &NrvCoinCollect.CountUp)) {
160 al::invalidateClipping(actor: this);
161 al::setNerve(user: this, nerve: &NrvCoinCollect.Got);
162 return true;
163 }
164 return false;
165}
166
167void CoinCollect::endClipped() {
168 mRotateCalculator->reset();
169 al::LiveActor::endClipped();
170}
171
172void CoinCollect::appear() {
173 mRotateCalculator->reset();
174 al::LiveActor::appear();
175}
176
177void CoinCollect::makeActorAlive() {
178 if (mCoinCollectEmpty == nullptr && !al::isNerve(user: this, nerve: &NrvCoinCollect.Got))
179 al::LiveActor::makeActorAlive();
180}
181
182void CoinCollect::listenKill() {
183 kill();
184}
185
186void CoinCollect::listenAppear() {
187 appear();
188}
189
190const sead::Vector3f& CoinCollect::getTransForHint() const {
191 if (mCoinCollectEmpty != nullptr)
192 return al::getTrans(actor: mCoinCollectEmpty);
193
194 return al::getTrans(actor: this);
195}
196
197void CoinCollect::appearHelpAmiiboEffect() {
198 if (al::isDead(actor: this))
199 makeActorAlive();
200
201 al::invalidateClipping(actor: this);
202 al::startHitReaction(actor: this, name: "発光");
203 al::setNerve(user: this, nerve: &NrvCoinCollect.WaitAmiibo);
204}
205
206void CoinCollect::deleteHelpAmiiboEffect() {
207 mHintState->deleteHintEffect();
208}
209
210void CoinCollect::reappearHelpAmiiboEffect() {
211 mHintState->appearHintEffect();
212}
213
214bool CoinCollect::isEnableHint() const {
215 if (al::isDead(actor: this))
216 return true;
217
218 return al::isNerve(user: this, nerve: &NrvCoinCollect.Wait);
219}
220
221void CoinCollect::rotate() {
222 if (mMtxConnector != nullptr)
223 al::connectPoseQT(actor: this, mtxConnector: mMtxConnector);
224
225 al::setQuat(actor: this, quat: sead::Quatf::unit);
226 al::rotateQuatYDirDegree(actor: this, deg: mRotateCalculator->getRotate());
227}
228
229void CoinCollect::exeWait() {
230 if (al::isFirstStep(user: this)) {
231 al::validateClipping(actor: this);
232 rs::tryUpdateWaterSurfaceCoinShadow(mWaterSurfaceShadow, this, mShadowLength);
233 }
234
235 if (mMtxConnector != nullptr)
236 al::connectPoseQT(actor: this, mtxConnector: mMtxConnector);
237
238 rotate();
239
240 if (rs::isNearPlayerH(this, 2000.0f))
241 al::holdSe(this, "Shining");
242}
243
244void CoinCollect::exeWaitAmiibo() {
245 if (mMtxConnector != nullptr)
246 al::connectPoseQT(actor: this, mtxConnector: mMtxConnector);
247
248 rotate();
249
250 if (rs::isNearPlayerH(this, 2000.0f))
251 al::holdSe(this, "Shining");
252
253 if (al::updateNerveState(user: this))
254 al::setNerve(user: this, nerve: &NrvCoinCollect.Wait);
255}
256
257void CoinCollect::exeGot() {
258 if (al::isFirstStep(user: this)) {
259 rs::tryShowCapMsgCollectCoinGetFirst(this);
260 al::startAction(actor: this, actionName: "Got");
261 alPadRumbleFunction::startPadRumble(actor: this, name: "コッ(微弱)", near: 1000.0f, far: 5000.0f);
262 if (mWaterSurfaceShadow != nullptr)
263 mWaterSurfaceShadow->disappearShadow();
264 _198 = false;
265 }
266
267 if (mMtxConnector != nullptr)
268 al::connectPoseQT(actor: this, mtxConnector: mMtxConnector);
269
270 if (al::isActionEnd(actor: this)) {
271 CoinCollectWatcher* watcher = al::getSceneObj<CoinCollectWatcher>(user: this);
272 watcher->countup(actor: this);
273 GameDataFunction::addCoinCollect(writer: this, coinCollect: mPlacementId);
274 kill();
275 }
276}
277
278void CoinCollect::exeCountUp() {
279 if (al::isFirstStep(user: this))
280 alPadRumbleFunction::startPadRumble(actor: this, name: "コッ(弱)", near: 500.0f, far: 2000.0f);
281
282 if (!mIsSurfaceUpdated && !al::isInWater(this)) {
283 sead::Vector3f surface = sead::Vector3f::zero;
284 al::calcFindWaterSurface(&surface, nullptr, this, al::getTrans(actor: this), sead::Vector3f::ey,
285 200.0f);
286 mSurfaceMatrix.setBase(axis: 3, v: surface);
287 al::startHitReaction(actor: this, name: "水しぶき");
288 mIsSurfaceUpdated = true;
289 }
290 if (al::updateNerveState(user: this)) {
291 CoinCollectWatcher* watcher = al::getSceneObj<CoinCollectWatcher>(user: this);
292 watcher->countup(actor: this);
293 GameDataFunction::addCoinCollect(writer: this, coinCollect: mPlacementId);
294
295 rs::tryShowCapMsgCollectCoinGetFirst(this);
296 if (mWaterSurfaceShadow != nullptr)
297 mWaterSurfaceShadow->disappearShadow();
298
299 al::startHitReaction(actor: this, name: "釣り取得");
300 kill();
301 }
302}
303
304void CoinCollect::exeBlow() {
305 if (al::isFirstStep(user: this)) {
306 al::invalidateClipping(actor: this);
307 al::onCollide(actor: this);
308 }
309
310 if (mMtxConnector != nullptr)
311 al::connectPoseQT(actor: this, mtxConnector: mMtxConnector);
312
313 al::setQuat(actor: this, quat: sead::Quatf::unit);
314 al::rotateQuatYDirDegree(actor: this, deg: mRotateCalculator->getRotate());
315 al::addVelocityToGravity(actor: this, force: 1.5f);
316 al::scaleVelocity(actor: this, factor: 0.99f);
317
318 if (al::getVelocity(actor: this).y < 0.0f && al::isCollidedGround(this)) {
319 if (al::calcSpeed(actor: this) < 15.0f) {
320 if (mMtxConnector != nullptr)
321 al::attachMtxConnectorToCollision(mtxConnector: mMtxConnector, actor: this, false);
322 al::setVelocityZero(this);
323 al::setNerve(user: this, nerve: &NrvCoinCollect.Wait);
324 al::offCollide(actor: this);
325 return;
326 }
327 al::getVelocityPtr(actor: this)->y *= -0.75f;
328 }
329}
330