SMG-Decomp
A decompilation of Super Mario Galaxy 1
Loading...
Searching...
No Matches
CocoNut.cpp
1#include "Game/MapObj/CocoNut.hpp"
2#include "Game/LiveActor/Spine.hpp"
3#include "Game/NameObj/NameObjArchiveListCollector.hpp"
4#include "Game/Util.hpp"
5#include "JSystem/JMath.hpp"
6
7inline void negateInternalInline(const TVec3f &src, TVec3f *dst) {
8 JGeometry::negateInternal((f32 *)&src, (f32 *)dst);
9}
10
11CocoNut::CocoNut(const char *pName) : LiveActor(pName),
12 _8C(0.0f),
13 _90(0.0f),
14 _94(0.0f, 1.0f),
15 _D0(55.0f),
16 _D4(false),
17 _138(0),
18 _13C(0),
19 mSpawnPosition(gZeroVec),
20 _14C(false),
21 _150(gZeroVec),
22 mSphericalShadow(false),
23 mRespawnWhenOutOfView(false),
24 _15E(false),
25 mContinueRolling(false)
26{
27 _A0.identity();
28 _D8.identity();
29 _108.identity();
30}
31
32CocoNut::~CocoNut() {
33}
34
35void CocoNut::init(const JMapInfoIter &rIter) {
36 initMapToolInfo(rIter);
37 initModel();
38 MR::connectToSceneNoSilhouettedMapObjStrongLight(this);
39 MR::initLightCtrl(this);
40 initSensor();
41 initBinder(_D0, 0.0f, 0);
42 MR::onCalcGravity(this);
43 initEffect();
44 initSound(6, 0);
45
46 if (!mSphericalShadow) {
47 MR::initShadowVolumeCylinder(this, _D0);
48 }
49 else {
50 MR::initShadowVolumeSphere(this, _D0);
51 }
52
53 MR::setShadowDropLength(this, nullptr, 1500.0f);
54
55 s32 stack_8;
56 if (MR::getJMapInfoClippingGroupID(rIter, &stack_8)) {
57 MR::setGroupClipping(this, rIter, 32);
58 _15E = true;
59 }
60 else {
61 MR::setClippingFar200m(this);
62 }
63
64 MR::tryRegisterDemoCast(this, rIter);
65
66 if (!mSphericalShadow) {
67 initNerve(&NrvCocoNut::CocoNutNrvWaitOnBind::sInstance);
68 }
69 else {
70 initNerve(&NrvCocoNut::CocoNutNrvWait::sInstance);
71 }
72
73 makeActorAppeared();
74}
75
76void CocoNut::initAfterPlacement() {
77 TPos3f stack_50;
78
79 TVec3f gravity(mGravity);
80 MR::makeMtxTR(&stack_50.mMtx[0], this);
81
82 _94.set(stack_50.mMtx[0][2], stack_50.mMtx[1][2], stack_50.mMtx[2][2]);
83
84 if (MR::isSameDirection(_94, gravity, 0.01f)) {
85 TVec3f gravityNegated;
86 negateInternalInline(gravity, &gravityNegated);
87
88 TPos3f stack_20;
89 MR::makeMtxUpNoSupport(&stack_20, gravityNegated);
90 _94.set(stack_20.mMtx[0][2], stack_20.mMtx[1][2], stack_20.mMtx[2][2]);
91 }
92}
93
94void CocoNut::startClipped() {
95 if (isNerve(&NrvCocoNut::CocoNutNrvReplaceReady::sInstance)) {
96 if (!MR::isDemoActive()) {
97 _90 = 0.0f;
98 _8C = 0.0f;
99 _150.zero();
100 mVelocity.zero();
101 MR::onBind(this);
102 MR::onCalcGravity(this);
103 MR::showModel(this);
104 MR::validateHitSensors(this);
105 if (!mSphericalShadow) {
106 _D4 = false;
107 setNerve(&NrvCocoNut::CocoNutNrvWait::sInstance);
108 }
109 else {
110 setNerve(&NrvCocoNut::CocoNutNrvWaitOnBind::sInstance);
111 }
112 }
113 }
114 else if (mRespawnWhenOutOfView) {
115 statusToHide();
116 mPosition.set(mSpawnPosition);
117 setNerve(&NrvCocoNut::CocoNutNrvReplaceReady::sInstance);
118 }
119 LiveActor::startClipped();
120}
121
122void CocoNut::hit(const TVec3f &a1, f32 a2) {
123 setFrontVec(a1);
124
125 f32 var_f0;
126 if (a2 < 1.5f) {
127 var_f0 = 1.5f;
128 }
129 else if (a2 > 35.0f) {
130 var_f0 = 35.0f;
131 }
132 else {
133 var_f0 = a2;
134 }
135
136 _8C = var_f0;
137 if (!isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance)) {
138 setNerve(&NrvCocoNut::CocoNutNrvMove::sInstance);
139 }
140}
141
142bool CocoNut::isPossibleToHit(const TVec3f &a1, const TVec3f &a2, const TVec3f &a3) const {
143 TVec3f stack_2C;
144 TVec3f stack_20;
145 TVec3f stack_14;
146 TVec3f stack_8;
147
148 stack_2C.sub(a2, a1);
149 if (MR::normalizeOrZero(&stack_2C)) {
150 return false;
151 }
152 if (MR::normalizeOrZero(a3, &stack_14)) {
153 return false;
154 }
155 if (isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance)) {
156 if (MR::normalizeOrZero(mVelocity, &stack_8)) {
157 return false;
158 }
159 stack_20.sub(stack_14, stack_8);
160 if (MR::normalizeOrZero(&stack_20)) {
161 return false;
162 }
163 }
164 else {
165 stack_20.set(stack_14);
166 }
167 return stack_2C.dot(stack_20) < 0.0f;
168}
169
170f32 CocoNut::calcMoveSpeed() const {
171 return !isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance) ? 0.0f : MR::max(_8C, PSVECMag(_150.toCVec()));
172}
173
174void CocoNut::initSensor() {
175 initHitSensor(2);
176 MR::addHitSensor(this, "body", 0x17, 0x10, 65.0f * mScale.x, TVec3f(0.0f, 0.0f, 0.0f));
177 MR::addHitSensor(this, "eye", 0x7f, 0x10, 1.1f * mScale.x, TVec3f(0.0f, 0.0f, 0.0f));
178}
179
180void CocoNut::initModel() {
181 initModelManagerWithAnm(getModelName(), nullptr, false);
182}
183
184void CocoNut::initEffect() {
185 initEffectKeeper(0, "CocoNut", false);
186 MR::setEffectHostMtx(this, "RollingSmoke", _D8.toMtxPtr());
187 MR::setEffectHostMtx(this, "RollingSmokeAttrWater", _D8.toMtxPtr());
188 MR::setEffectHostMtx(this, "RollingSmokeAttrSand", _D8.toMtxPtr());
189 MR::setEffectHostMtx(this, "Land", _D8.toMtxPtr());
190 MR::setEffectHostMtx(this, "LandAttrWater", _D8.toMtxPtr());
191 MR::setEffectHostMtx(this, "WaterColumn", _D8.toMtxPtr());
192 MR::setEffectHostMtx(this, getBreakEffectName(), _D8.toMtxPtr());
193 MR::setEffectHostMtx(this, "SpinHitMark", _108.toMtxPtr());
194}
195
196#ifdef NON_MATCHING
197// hell function
198void CocoNut::updateRotate(float a1) {
199 TMtx34f stack_38;
200 TVec3f stack_2C;
201 TVec3f stack_20;
202 TVec3f stack_14;
203 TVec3f stack_8;
204
205 negateInternalInline(mGravity, &stack_20);
206 if (!MR::normalizeOrZero(mVelocity, &stack_2C) && !MR::isSameDirection(stack_2C, stack_20, 0.01f)) {
207
208 PSVECCrossProduct(stack_2C.toCVec(), stack_20.toCVec(), stack_14.toVec());
209
210 f32 temp1 = PSVECMag(mVelocity.toCVec()) * -180.0f;
211 f32 temp2 = a1 * temp1;
212 f32 f = PI_180 * (temp2 / (PI * getSize()));
213
214 stack_38.mMtx[0][3] = 0.0f;
215 stack_38.mMtx[1][3] = 0.0f;
216 stack_38.mMtx[2][3] = 0.0f;
217
218 stack_8.set(stack_14);
219 PSVECMag(stack_8.toCVec());
220 PSVECNormalize(stack_8.toCVec(), stack_8.toVec());
221
222 f32 fsin = sin(f);
223 f32 fcos = cos(f);
224 f32 rx = stack_8.x;
225 f32 ry = stack_8.y;
226 f32 rz = stack_8.z;
227 f32 fcos1 = 1.0f - fcos;
228
229 stack_38.mMtx[0][0] = (rx * rx * fcos1) + fcos;
230 stack_38.mMtx[0][1] = fcos1 * rx * ry - (fsin * rz);
231 stack_38.mMtx[0][2] = fcos1 * rx * rz + (fsin * ry);
232 stack_38.mMtx[1][0] = fcos1 * rx * ry + (fsin * rz);
233 stack_38.mMtx[1][1] = (ry * ry * fcos1) + fcos;
234 stack_38.mMtx[1][2] = fcos1 * ry * rz - (fsin * rx);
235 stack_38.mMtx[2][0] = fcos1 * rx * rz - (fsin * ry);
236 stack_38.mMtx[2][1] = fcos1 * ry * rz + (fsin * rx);
237 stack_38.mMtx[2][2] = (rz * rz * fcos1) + fcos;
238
239 _A0.concat(stack_38, _A0);
240 }
241}
242#endif
243
244void CocoNut::updateGravity() {
245 TVec3f stack_8;
246
247 f32 f31 = _13C ? 0.4f : 1.0f;
248 stack_8.set(mGravity);
249 f32 f0 = _90 + f31;
250
251 f32 f2 = f0 >= 25.0f ? 25.0f : f0;
252 _90 = f2;
253 stack_8.scale((f32)(f64)f2); // real
254 mVelocity.add(stack_8);
255}
256
257#ifdef NON_MATCHING
258// issues around MR::deleteEffect and PSVECNormalize calls
259void CocoNut::processMove() {
260 TVec3f stack_2C;
261 TVec3f stack_20;
262 TVec3f stack_14;
263 TVec3f stack_8;
264
265 if (isOnGround()) {
266 f32 temp_f31 = calcMoveSpeed();
267 bool temp_r31 = MR::isBindedGroundWater(this);
268 TVec3f *temp_r3_1 = MR::getGroundNormal(this);
269 if (2.5f < _90) {
270 if (mGravity.dot(*temp_r3_1) < 0.0f) {
271 _90 *= 0.5f;
272 s32 var_r5 = 100.0f * (temp_f31 / 35.0f);
273 if (var_r5 > 100) {
274 var_r5 = 100;
275 }
276 if (var_r5 > 0) {
277 if (temp_r31) {
278 MR::startSound(this, "SE_OJ_COCONUT_BOUND_WATER", var_r5, -1);
279 }
280 else {
281 MR::startSound(this, "SE_OJ_COCONUT_BOUND", var_r5, -1);
282 }
283 }
284 }
285 if (_138 >= 10 && 3.0f < _90) {
286 MR::emitEffect(this, "Land");
287 }
288 }
289 else {
290 _90 = 0.0f;
291 }
292 MR::emitEffect(this, "RollingSmoke");
293 s32 var_r5_2 = 100.0f * (temp_f31 / 35.0f);
294 if (var_r5_2 > 100) {
295 var_r5_2 = 100;
296 }
297 if (var_r5_2 >= 10) {
298 if (temp_r31) {
299 MR::startLevelSound(this, "SE_OJ_LV_COCONUT_ROLL_WATER", var_r5_2, -1, -1);
300 }
301 else {
302 MR::startLevelSound(this, "SE_OJ_LV_COCONUT_ROLL", var_r5_2, -1, -1);
303 }
304 }
305 _8C *= 0.925f;
306 _14C = MR::calcVelocityAreaMoveOnGround(&stack_2C, this);
307 if (_14C) {
308 _150.set(stack_2C);
309 _150.scaleInline(0.75f);
310 }
311 _138 = 0;
312 _13C = false;
313 updateRotate(1.0f);
314 }
315 else {
316 // volatile?
317 if (_138 < 10) {
318 _138++;
319 }
320 else {
321 MR::deleteEffect(this, "RollingSmoke");
322 }
323 updateRotate(0.75f);
324 }
325
326 if (getWallNormal(&stack_20) && _94.dot(stack_20) < 0.0f) {
327 stack_14.set(_94);
328 PSVECMag(stack_14.toCVec());
329 PSVECNormalize(stack_14.toCVec(), stack_14.toVec());
330
331 stack_8.set(stack_20);
332 PSVECMag(stack_8.toCVec());
333 PSVECNormalize(stack_8.toCVec(), stack_8.toVec());
334
335 f32 ok2 = -2.0f * stack_14.dot(stack_8);
336 JMAVECScaleAdd(stack_8.toCVec(), _94.toCVec(), _94.toVec(), ok2);
337
338 PSVECMag(_94.toCVec());
339 PSVECNormalize(_94.toCVec(), _94.toVec());
340 _94.normalize(_94);
341
342 _8C *= 0.8f;
343
344 MR::startSound(this, "SE_OJ_COCONUT_BOUND", 100.0f * (calcMoveSpeed() / 35.0f), -1);
345 }
346 setFrontVec(_94);
347 mVelocity.set(_94);
348 mVelocity.scale(_8C);
349
350 bool ok = _14C && _138 < 10;
351 if (!ok) {
352 _150.scaleInline(0.925f);
353 }
354 mVelocity.add(_150);
355 updateGravity();
356}
357#endif
358
359void CocoNut::setFrontVec(const TVec3f &a1) {
360 TVec3f stack_14;
361 TVec3f stack_8(mGravity);
362 if (!MR::normalizeOrZero(a1, &stack_14)) {
363 if (MR::isSameDirection(a1, stack_8, 0.01f)) {
364 _94.set(stack_14);
365 }
366 else {
367 MR::vecKillElement(stack_14, stack_8, &_94);
368 MR::normalize(&_94);
369 }
370 }
371}
372
373bool CocoNut::tryHit(HitSensor *pOtherSensor, HitSensor *pMySensor) {
374 CocoNut *nut = reinterpret_cast<CocoNut *>(pMySensor->mActor);
375 if (!isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance)) {
376 return false;
377 }
378 f32 moveSpeed = nut->calcMoveSpeed();
379 if (calcMoveSpeed() < moveSpeed) {
380 return false;
381 }
382 TVec3f *otherSensorPos = &pOtherSensor->mPosition;
383 TVec3f *mySensorPos = &pMySensor->mPosition;
384 if (!nut->isPossibleToHit(*mySensorPos, *otherSensorPos, mVelocity)) {
385 return false;
386 }
387
388 TVec3f stack_1C;
389 TVec3f stack_10;
390 f32 stack_C;
391 f32 stack_8;
392 calcHitSpeedAndFrontVec(&stack_C, &stack_8, &stack_1C, &stack_10, *otherSensorPos, *mySensorPos);
393 hit(stack_1C, stack_C);
394 nut->hit(stack_10, stack_8);
395 return true;
396}
397
398bool CocoNut::tryPushedFromActor(HitSensor *pOtherSensor, HitSensor *pMySensor) {
399 TVec3f stack_34;
400 TVec3f stack_28;
401 TVec3f stack_1C;
402 TVec3f stack_10;
403 f32 stack_C;
404 f32 stack_8;
405
406 TVec3f *otherSensorPos = &pOtherSensor->mPosition;
407 TVec3f *mySensorPos = &pMySensor->mPosition;
408 if (_13C) {
409 return false;
410 }
411 if (isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance)) {
412 stack_34.sub(*otherSensorPos, *mySensorPos);
413 MR::normalize(&stack_34);
414 if (0.0f < stack_34.dot(_94)) {
415 return false;
416 }
417 calcHitSpeedAndFrontVec(&stack_C, &stack_8, &stack_28, &stack_1C, *otherSensorPos, *mySensorPos);
418 hit(stack_28, stack_C);
419 }
420 else {
421 f32 mySensorRadius = pMySensor->mRadius;
422 f32 otherSensorRadius = pOtherSensor->mRadius;
423 if (((otherSensorRadius + mySensorRadius) - PSVECDistance(otherSensorPos->toCVec(), mySensorPos->toVec())) < 0.0f) {
424 return false;
425 }
426 stack_10.sub(*otherSensorPos, *mySensorPos);
427 if (MR::normalizeOrZero(&stack_10)) {
428 return false;
429 }
430 hit(stack_10, 1.5f);
431 }
432 return true;
433}
434
435void CocoNut::reviseFrontVec() {
436 HitSensor *sensor;
437
438 HitSensor *eye = getSensor("eye");
439 LiveActor *found_actor = nullptr;
440
441 for (int i = 0; i < eye->mSensorCount; i++) {
442 sensor = eye->mSensors[i];
443 if ((sensor->isType(0x26) || sensor->isType(0x56)) && !MR::isDead(sensor->mActor)) {
444 found_actor = sensor->mActor;
445 break;
446 }
447 }
448 if (found_actor == nullptr) {
449 return;
450 }
451
452 TVec3f stack_28(mGravity);
453 TVec3f stack_20;
454 TVec3f stack_14;
455 TVec3f stack_8;
456
457 stack_14.sub(found_actor->mPosition, this->mPosition);
458 if (!MR::isSameDirection(stack_14, stack_20, 0.01f)) {
459 MR::vecKillElement(stack_14, stack_20, &stack_8);
460 MR::normalize(&stack_8);
461 f32 temp_f31 = stack_8.dot(_94);
462 if (MR::cosDegree(15.0f) < temp_f31) {
463 JMAVECLerp(_94.toCVec(), stack_8.toCVec(), _94.toVec(), 0.8f);
464 }
465 }
466}
467
468void CocoNut::statusToWait() {
469 mVelocity.zero();
470 _90 = 0.0f;
471 _8C = 0.0f;
472 _150.zero();
473 MR::validateClipping(this);
474 if (!mSphericalShadow && !MR::isBindedGroundIce(this)) {
475 _D4 = true;
476 MR::offBind(this);
477 MR::offCalcGravity(this);
478 if (!isNerve(&NrvCocoNut::CocoNutNrvWait::sInstance)) {
479 setNerve(&NrvCocoNut::CocoNutNrvWait::sInstance);
480 }
481 }
482 else {
483 setNerve(&NrvCocoNut::CocoNutNrvWaitOnBind::sInstance);
484 }
485}
486
487void CocoNut::tryMoveEnd() {
488 if (isOnGround()) {
489 bool var_r3 = _14C && _138 < 10;
490 if (!var_r3 && calcMoveSpeed() < 1.5f && (!mContinueRolling || !isContactWithOtherCocoNut())) {
491 statusToWait();
492 return;
493 }
494 }
495 if (sendMsgToBindedSensor()) {
496 setNerve(&NrvCocoNut::CocoNutNrvBreak::sInstance);
497 return;
498 }
499 if (!tryDisappear()) {
500 _8C = MR::max(_8C, 1.5f);
501 }
502}
503
504bool CocoNut::tryDisappear() {
505 TVec3f stack_14;
506 stack_14.scale(-100.0f, mGravity);
507 if (MR::isInWater(this, stack_14)) {
508 setNerve(&NrvCocoNut::CocoNutNrvInWater::sInstance);
509 return true;
510 }
511 if (MR::isInDeath(this, TVec3f(0.0f, 0.0f, 0.0f))) {
512 setNerve(&NrvCocoNut::CocoNutNrvBreak::sInstance);
513 return true;
514 }
515 return false;
516}
517
518bool CocoNut::isValidPushedFromPlayer(const HitSensor *arg0, const HitSensor *arg1) const {
519 if (_90 < 0.0f) {
520 return false;
521 }
522 if (isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance) && MR::isLessStep(this, 15)) {
523 return false;
524 }
525 TVec3f *playerVelocity = MR::getPlayerVelocity();
526 f32 nutVelocitySquared = mVelocity.squared();
527 if (playerVelocity->squared() < nutVelocitySquared) {
528 return false;
529 }
530 TVec3f stack_14;
531 stack_14.sub(arg0->mPosition, arg1->mPosition);
532 if (MR::normalizeOrZero(&stack_14)) {
533 return false;
534 }
535 TVec3f stack_8;
536 if (MR::normalizeOrZero(*playerVelocity, &stack_8)) {
537 return false;
538 }
539 if (stack_14.dot(stack_8) < MR::cosDegree(45.0f)) {
540 return false;
541 }
542
543 return true;
544}
545
546#ifdef NON_MATCHING
547// the frsqrte is most likely an inlined function. possibly JGeometry::TUtil<f32>::sqrt
548void CocoNut::calcHitSpeedAndFrontVec(f32 *arg0, f32 *arg1, TVec3f *arg2, TVec3f *arg3, const TVec3f &arg4, const TVec3f &arg5) const {
549 TVec3f stack_14;
550 TVec3f stack_8;
551
552 arg3->sub(arg5, arg4);
553 MR::normalize(arg3);
554 stack_14.set(mGravity);
555 PSVECCrossProduct(arg3->toCVec(), stack_14.toCVec(), arg2->toVec());
556 MR::normalize(arg2);
557 if (MR::normalizeOrZero(mVelocity, &stack_8)) {
558 stack_8.set(_94);
559 }
560 f32 var_f30 = stack_8.dot(*arg2);
561 if (var_f30 < 0.0f) {
562 negateInternalInline(stack_14, &stack_14);
563 PSVECCrossProduct(arg3->toCVec(), stack_14.toCVec(), arg2->toVec());
564 MR::normalize(arg2);
565 var_f30 = stack_8.dot(*arg2);
566 }
567
568 f32 temp_f2 = 1.0f - (var_f30 * var_f30);
569
570 f32 var_f31;
571 if (temp_f2 > 0.0f) {
572 f32 temp_f31 = __frsqrte(temp_f2);
573 f32 temp_f3 = temp_f31 * temp_f2;
574 var_f31 = -((temp_f3 * temp_f31) - 3.0f) * temp_f3 * 0.5f;
575 } else {
576 var_f31 = temp_f2;
577 }
578
579 f32 temp_f1_2 = calcMoveSpeed();
580 *arg1 = temp_f1_2 * var_f31;
581 *arg0 = temp_f1_2 * var_f30;
582}
583#endif
584
585bool CocoNut::isOnGround() const {
586 if (0.0f < _90 && MR::isOnGround(this)) {
587 TVec3f *groundNormal = MR::getGroundNormal(this);
588 if (groundNormal->dot(mGravity) < MR::cosDegree(120.0f)) {
589 return true;
590 }
591 }
592 return false;
593}
594
595bool CocoNut::getWallNormal(TVec3f *arg0) const {
596 if (MR::isBindedWall(this)) {
597 arg0->set(*MR::getWallNormal(this));
598 return true;
599 }
600 if (0.0f < _90 && (MR::isOnGround(this)) && !isOnGround()) {
601 arg0->set(*MR::getGroundNormal(this));
602 return true;
603 }
604 return false;
605}
606
607bool CocoNut::sendMsgToBindedSensor() {
608 if (MR::isBindedGround(this)) {
609 return sendMsgEnemyAttackToBindedSensor(MR::getGroundSensor(this));
610 }
611 if (MR::isBindedWall(this)) {
612 return sendMsgEnemyAttackToBindedSensor(MR::getWallSensor(this));
613 }
614 if (MR::isBindedRoof(this)) {
615 return sendMsgEnemyAttackToBindedSensor(MR::getRoofSensor(this));
616 }
617 return 0;
618}
619
620bool CocoNut::sendMsgEnemyAttackToBindedSensor(HitSensor *arg0) {
621 if (_13C) {
622 return MR::sendMsgEnemyAttack(arg0, getSensor("body"));
623 }
624 return false;
625}
626
627bool CocoNut::isValidReceiveMsg(const HitSensor *a1) const {
628 return (
629 isNerve(&NrvCocoNut::CocoNutNrvWait::sInstance) ||
630 isNerve(&NrvCocoNut::CocoNutNrvWaitOnBind::sInstance) ||
631 isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance)
632 ) && a1 == getSensor("body");
633}
634
635const char *CocoNut::getModelName() {
636 bool watermelonMode = MR::isStarPieceCounterStop();
637 return watermelonMode ? "Watermelon" : "CocoNut";
638}
639
640const char *CocoNut::getBreakEffectName() {
641 bool watermelonMode = MR::isStarPieceCounterStop();
642 return watermelonMode ? "BreakWatermelon" : "CocoNutBreak";
643}
644
645void CocoNut::makeArchiveList(NameObjArchiveListCollector *pArchiveList, const JMapInfoIter &rIter) {
646 pArchiveList->addArchive(getModelName());
647}
648
650 _A0.mMtx[0][3] = this->mPosition.x;
651 _A0.mMtx[1][3] = this->mPosition.y;
652 _A0.mMtx[2][3] = this->mPosition.z;
653
654 MR::setBaseTRMtx(this, _A0);
655
656 if (isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance) && MR::isOnGround(this)) {
657 TVec3f *groundNormal = MR::getGroundNormal(this);
658 f32 temp_f31 = _D0;
659
660 TVec3f stack_8(*groundNormal);
661 stack_8.scale(temp_f31);
662
663 TVec3f stack_14;
664 stack_14.sub(mPosition, stack_8);
665
666 if (MR::isSameDirection(*groundNormal, _94, 0.01f)) {
667 MR::makeMtxUpNoSupportPos(&_D8, *groundNormal, stack_14);
668 }
669 else {
670 MR::makeMtxUpFrontPos(&_D8, *groundNormal, _94, stack_14);
671 }
672
673 f32 temp_f9 = mScale.x;
674 _D8.mMtx[0][0] *= temp_f9;
675 _D8.mMtx[0][1] *= temp_f9;
676 _D8.mMtx[0][2] *= temp_f9;
677 _D8.mMtx[1][0] *= temp_f9;
678 _D8.mMtx[1][1] *= temp_f9;
679 _D8.mMtx[1][2] *= temp_f9;
680 _D8.mMtx[2][0] *= temp_f9;
681 _D8.mMtx[2][1] *= temp_f9;
682 _D8.mMtx[2][2] *= temp_f9;
683 }
684}
685
686void CocoNut::attackSensor(HitSensor *arg0, HitSensor *arg1) {
687 if (isValidReceiveMsg(arg0)) {
688 if (MR::isSensorPlayer(arg1) && !isValidPushedFromPlayer(arg0, arg1)) {
689 if (!MR::isPlayerHipDropFalling()) {
690 MR::sendMsgPush(arg1, arg0);
691 }
692 }
693 else if (arg1->isType(0x17)) {
694 if (MR::sendMsgPush(arg1, arg0)) {
695 MR::startSound(this, "SE_OJ_COCONUT_HIT", -1, -1);
696 }
697 }
698 else {
699 if (_13C && isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance) && MR::sendMsgToEnemyAttackBlow(arg1, arg0)) {
700 MR::startSound(this, "SE_OJ_COCONUT_HIT", -1, -1);
701 setNerve(&NrvCocoNut::CocoNutNrvBreak::sInstance);
702 }
703 else {
704 MR::sendMsgPush(arg1, arg0);
705 }
706 }
707 }
708}
709
710bool CocoNut::receiveMsgPush(HitSensor *pMySensor, HitSensor *pOtherSensor) {
711 if (!isValidReceiveMsg(pOtherSensor)) {
712 return false;
713 }
714 if (pMySensor->isType(0x17)) {
715 return tryHit(pOtherSensor, pMySensor);
716 }
717 return tryPushedFromActor(pOtherSensor, pMySensor);
718}
719
720bool CocoNut::receiveMsgPlayerAttack(u32 a1, HitSensor *pOtherSensor, HitSensor *pMySensor) {
721 if (!isValidReceiveMsg(pMySensor)) {
722 return false;
723 }
724 if (!MR::isMsgPlayerHitAll(a1) && !MR::isMsgPlayerTrample(a1) && !MR::isMsgPlayerHipDrop(a1) && !MR::isMsgStarPieceReflect(a1)) {
725 return false;
726 }
727 f32 var_f31;
728 f32 var_f30;
729 if (MR::isMsgPlayerSpinAttack(a1)) {
730 var_f31 = 35.0f;
731 var_f30 = -20.0f;
732 MR::startSound(this, "SE_PM_SPIN_HIT", -1, -1);
733 MR::startSound(this, "SE_OJ_COCONUT_LAUNCH", -1, -1);
734 emitEffectSpinHit(pOtherSensor, pMySensor);
735 _13C = true;
736 }
737 else if (MR::isMsgPlayerHipDrop(a1) || MR::isMsgInvincibleAttack(a1)) {
738 var_f31 = 25.0f;
739 var_f30 = -15.0f;
740 MR::startSound(this, "SE_OJ_COCONUT_FLIP_M", -1, -1);
741 if (MR::isMsgPlayerHipDrop(a1)) {
742 MR::sendMsgAwayJump(pOtherSensor, pMySensor);
743 }
744 }
745 else {
746 var_f31 = 10.0f;
747 var_f30 = -5.0f;
748 MR::startSound(this, "SE_OJ_COCONUT_FLIP_S", -1, -1);
749 }
750 _8C = var_f31;
751 _90 = var_f30;
752 TVec3f stack_2C;
753 TVec3f stack_20;
754 TVec3f stack_14;
755 stack_2C.sub(pMySensor->mPosition, pOtherSensor->mPosition);
756 if (MR::isMsgStarPieceReflect(a1) && !MR::normalizeOrZero(stack_2C, &stack_20) && !MR::normalizeOrZero(pOtherSensor->mActor->mVelocity, &stack_14)) {
757 if (stack_20.dot(stack_14) < MR::cosDegree(60.0f)) {
758 TVec3f stack_8(stack_14);
759 stack_8.scale(65.0f);
760 stack_2C.add(stack_8);
761 }
762 }
763 setFrontVec(stack_2C);
764 if (MR::isMsgPlayerSpinAttack(a1)) {
765 reviseFrontVec();
766 }
767 setNerve(&NrvCocoNut::CocoNutNrvMove::sInstance);
768 return true;
769}
770
771bool CocoNut::receiveOtherMsg(u32 arg0, HitSensor *pOtherSensor, HitSensor *pMySensor) {
772 f32 temp_f0;
773 f32 temp_f1;
774 f32 var_f2;
775
776 if (!isValidReceiveMsg(pMySensor)) {
777 return false;
778 }
779 if (MR::isMsgPlayerKick(arg0)) {
780 if (isValidPushedFromPlayer(pMySensor, pOtherSensor)) {
781 TVec3f *playerVelocity = MR::getPlayerVelocity();
782 setFrontVec(*playerVelocity);
783 temp_f1 = _94.dot(*playerVelocity);
784 var_f2 = 18.0f;
785 temp_f0 = 1.1f * temp_f1;
786
787 _8C = MR::max(var_f2, temp_f0);
788 _90 = -(0.7f * temp_f1);
789 MR::startSound(this, "SE_OJ_COCONUT_FLIP_S", -1, -1);
790 setNerve(&NrvCocoNut::CocoNutNrvMove::sInstance);
791 return true;
792 }
793 }
794 else {
795 if (MR::isMsgHitmarkEmit(arg0)) {
796 return true;
797 }
798 }
799 return false;
800}
801
802void CocoNut::initMapToolInfo(const JMapInfoIter &rIter) {
803 MR::initDefaultPos(this, rIter);
804 MR::getJMapInfoArg0NoInit(rIter, &mSphericalShadow);
805 MR::getJMapInfoArg1NoInit(rIter, &mRespawnWhenOutOfView);
806 MR::getJMapInfoArg2NoInit(rIter, &mContinueRolling);
807 _D0 = 55.0f * mScale.x;
808 mSpawnPosition.set(mPosition);
809}
810
811void CocoNut::statusToHide() {
812 mVelocity.zero();
813 MR::offBind(this);
814 MR::offCalcGravity(this);
815 MR::hideModel(this);
816 MR::invalidateHitSensors(this);
817 MR::clearHitSensors(this);
818 MR::deleteEffectAll(this);
819}
820
821void CocoNut::emitEffectSpinHit(const HitSensor *pOtherSensor, const HitSensor *pMySensor) {
822 TVec3f point; // point 70% of the way between pOtherSensor and pMySensor
823 JMAVECLerp(pOtherSensor->mPosition.toCVec(), pMySensor->mPosition.toCVec(), point.toVec(), 0.7f);
824 _108.mMtx[0][0] = 1.0f;
825 _108.mMtx[1][0] = 0.0f;
826 _108.mMtx[2][0] = 0.0f;
827 _108.mMtx[0][1] = 0.0f;
828 _108.mMtx[1][1] = 1.0f;
829 _108.mMtx[2][1] = 0.0f;
830 _108.mMtx[0][2] = 0.0f;
831 _108.mMtx[1][3] = 0.0f;
832 _108.mMtx[2][3] = 1.0f;
833 _108.mMtx[0][3] = point.x;
834 _108.mMtx[1][3] = point.y;
835 _108.mMtx[2][3] = point.z;
836 MR::emitEffect(this, "SpinHitMark");
837}
838
839bool CocoNut::isContactWithOtherCocoNut() const {
840 HitSensor *body = getSensor("body");
841 for (int i = 0; i < body->mSensorCount; i++) {
842 HitSensor *sensor = body->mSensors[i];
843 if (body->mSensors[i]->isType(0x17)) {
844 return true;
845 }
846 }
847 return false;
848}
849
850void CocoNut::exeWait() {
851 if (MR::isFirstStep(this)) {
852 MR::deleteEffect(this, "RollingSmoke");
853 }
854 if (!_D4) {
855 if (tryDisappear()) {
856 return;
857 }
858 updateGravity();
859 }
860 if (!_D4 && MR::isOnGround(this)) {
861 statusToWait();
862 }
863}
864
865void CocoNut::exeWaitOnBind() {
866 if (MR::isFirstStep(this)) {
867 MR::deleteEffect(this, "RollingSmoke");
868 }
869 if (!tryDisappear()) {
870 updateGravity();
871 if (MR::isOnGround(this)) {
872 statusToWait();
873 }
874 }
875}
876
877void CocoNut::exeMove() {
878 if (MR::isFirstStep(this)) {
879 _138 = 0;
880 _14C = false;
881 _150.zero();
882 MR::onBind(this);
883 MR::onCalcGravity(this);
884 MR::calcGravity(this);
885 if (!_15E) {
886 MR::invalidateClipping(this);
887 }
888 }
889 if (_13C && MR::isStep(this, 1)) {
890 MR::stopScene(4);
891 }
892 processMove();
893 tryMoveEnd();
894}
895
896void CocoNut::exeInWater() {
897 if (MR::isFirstStep(this)) {
898 mVelocity.zero();
899 MR::offBind(this);
900 MR::offCalcGravity(this);
901 MR::hideModel(this);
902 MR::invalidateHitSensors(this);
903 MR::clearHitSensors(this);
904 MR::deleteEffectAll(this);
905 TVec3f gravityNegated;
906 negateInternalInline(mGravity, &gravityNegated);
907 MR::makeMtxUpNoSupportPos(&_D8, gravityNegated, mPosition);
908 MR::emitEffect(this, "WaterColumn");
909 MR::startSound(this, "SE_OJ_FALL_IN_WATER_M", -1, -1);
910 MR::releaseSoundHandle(this, "SE_OJ_FALL_IN_WATER_M");
911 }
912 if (!MR::isEffectValid(this, "WaterColumn")) {
913 mPosition.set(mSpawnPosition);
914 setNerve(&NrvCocoNut::CocoNutNrvReplaceReady::sInstance);
915 }
916}
917
918void CocoNut::exeBreak() {
919 if (MR::isFirstStep(this)) {
920 statusToHide();
921 TVec3f gravityNegated;
922 negateInternalInline(mGravity, &gravityNegated);
923 MR::makeMtxUpNoSupportPos(&_D8, gravityNegated, mPosition);
924 MR::emitEffect(this, getBreakEffectName());
925 }
926 if (!MR::isEffectValid(this, getBreakEffectName())) {
927 mPosition.set(mSpawnPosition);
928 setNerve(&NrvCocoNut::CocoNutNrvReplaceReady::sInstance);
929 }
930}
931
932namespace NrvCocoNut {
933 INIT_NERVE(CocoNutNrvWait)
934 INIT_NERVE(CocoNutNrvWaitOnBind)
935 INIT_NERVE(CocoNutNrvMove)
936 INIT_NERVE(CocoNutNrvInWater)
937 INIT_NERVE(CocoNutNrvBreak)
938 INIT_NERVE(CocoNutNrvReplaceReady)
939
940 void CocoNutNrvReplaceReady::execute(Spine *pSpine) const {
941 CocoNut *nut = reinterpret_cast<CocoNut*>(pSpine->mExecutor);
942 if (MR::isFirstStep(nut)) {
943 MR::validateClipping(nut);
944 }
945 }
946 void CocoNutNrvBreak::execute(Spine *pSpine) const {
947 CocoNut *nut = reinterpret_cast<CocoNut*>(pSpine->mExecutor);
948 nut->exeBreak();
949 }
950 void CocoNutNrvInWater::execute(Spine *pSpine) const {
951 CocoNut *nut = reinterpret_cast<CocoNut*>(pSpine->mExecutor);
952 nut->exeInWater();
953 }
954 void CocoNutNrvMove::execute(Spine *pSpine) const {
955 CocoNut *nut = reinterpret_cast<CocoNut*>(pSpine->mExecutor);
956 nut->exeMove();
957 }
958 void CocoNutNrvWaitOnBind::execute(Spine *pSpine) const {
959 CocoNut *nut = reinterpret_cast<CocoNut*>(pSpine->mExecutor);
960 nut->exeWaitOnBind();
961 }
962 void CocoNutNrvWait::execute(Spine *pSpine) const {
963 CocoNut *nut = reinterpret_cast<CocoNut*>(pSpine->mExecutor);
964 nut->exeWait();
965 }
966}
virtual void init(const JMapInfoIter &)
Intializes the NameObj and can set various settings and construct necessary classes.
Definition CocoNut.cpp:35
virtual void calcAndSetBaseMtx()
Calculates and sets the base matrix of the actor.
Definition CocoNut.cpp:649
The basis of a drawable actor that can contain states (see: Nerve)
Definition LiveActor.hpp:24
TVec3f mPosition
3D vector of the actor's position.
Definition LiveActor.hpp:95
TVec3f mScale
3D vector of the actor's scale.
Definition LiveActor.hpp:97
TVec3f mVelocity
3D vector of the actor's velocity.
Definition LiveActor.hpp:98
TVec3f mGravity
3D vector of the actor's gravity.
Definition LiveActor.hpp:99
HitSensor * getSensor(const char *pSensorName) const
Gets a sensor.
Stores archive names of NameObjs.
Definition Spine.hpp:9