1#include "Npc/FukuwaraiFaceParts.h"
2
3#include "Library/Area/AreaObjUtil.h"
4#include "Library/Base/StringUtil.h"
5#include "Library/Collision/KCollisionServer.h"
6#include "Library/LiveActor/ActorActionFunction.h"
7#include "Library/LiveActor/ActorAnimFunction.h"
8#include "Library/LiveActor/ActorClippingFunction.h"
9#include "Library/LiveActor/ActorCollisionFunction.h"
10#include "Library/LiveActor/ActorFlagFunction.h"
11#include "Library/LiveActor/ActorInitUtil.h"
12#include "Library/LiveActor/ActorModelFunction.h"
13#include "Library/LiveActor/ActorMovementFunction.h"
14#include "Library/LiveActor/ActorPoseUtil.h"
15#include "Library/LiveActor/ActorSensorUtil.h"
16#include "Library/Math/MathUtil.h"
17#include "Library/Nerve/NerveSetupUtil.h"
18#include "Library/Nerve/NerveUtil.h"
19#include "Library/Placement/PlacementFunction.h"
20#include "Library/Se/SeFunction.h"
21
22#include "Player/PlayerHackStartShaderCtrl.h"
23#include "Util/Hack.h"
24#include "Util/SensorMsgFunction.h"
25
26namespace {
27NERVE_IMPL(FukuwaraiFaceParts, Appear)
28NERVE_IMPL(FukuwaraiFaceParts, Wait)
29NERVE_IMPL(FukuwaraiFaceParts, CaptureStart)
30NERVE_IMPL(FukuwaraiFaceParts, Place)
31NERVE_IMPL(FukuwaraiFaceParts, Reset)
32NERVE_IMPL_(FukuwaraiFaceParts, Answer, Appear)
33NERVE_IMPL(FukuwaraiFaceParts, Hide)
34NERVE_IMPL_(FukuwaraiFaceParts, Vanish, Hide)
35NERVE_IMPL(FukuwaraiFaceParts, CaptureMove)
36NERVE_IMPL_(FukuwaraiFaceParts, CaptureMoveFast, CaptureMove)
37NERVE_IMPL(FukuwaraiFaceParts, CaptureWait)
38NERVE_IMPL(FukuwaraiFaceParts, CaptureHackStart)
39
40NERVES_MAKE_NOSTRUCT(FukuwaraiFaceParts, Appear, Vanish, CaptureHackStart)
41NERVES_MAKE_STRUCT(FukuwaraiFaceParts, Wait, CaptureStart, Place, Reset, Answer, Hide, CaptureMove,
42 CaptureMoveFast, CaptureWait)
43} // namespace
44
45FukuwaraiPart KuriboEyebrowLeftPart = {.name: "FukuwaraiKuriboEyebrowLeft", .basePoints: 4.0f, .anglePoints: 12.0f, .distancePoints: 4.0f};
46FukuwaraiPart KuriboEyebrowRightPart = {.name: "FukuwaraiKuriboEyebrowRight", .basePoints: 4.0f, .anglePoints: 12.0f, .distancePoints: 4.0f};
47FukuwaraiPart KuriboEyeLeftPart = {.name: "FukuwaraiKuriboEyeLeft", .basePoints: 4.0f, .anglePoints: 12.0f, .distancePoints: 4.0f};
48FukuwaraiPart KuriboEyeRightPart = {.name: "FukuwaraiKuriboEyeRight", .basePoints: 4.0f, .anglePoints: 12.0f, .distancePoints: 4.0f};
49FukuwaraiPart KuriboMouthAngryPart = {.name: "FukuwaraiKuriboMouthAngry", .basePoints: 4.0f, .anglePoints: 12.0f, .distancePoints: 4.0f};
50
51FukuwaraiPart MarioEyebrowLeftPart = {.name: "FukuwaraiMarioEyebrowLeft", .basePoints: 3.0f, .anglePoints: 9.0f, .distancePoints: 3.0f};
52FukuwaraiPart MarioEyebrowRightPart = {.name: "FukuwaraiMarioEyebrowRight", .basePoints: 3.0f, .anglePoints: 9.0f, .distancePoints: 3.0f};
53FukuwaraiPart MarioEyeLeftPart = {.name: "FukuwaraiMarioEyeLeft", .basePoints: 3.0f, .anglePoints: 9.0f, .distancePoints: 3.0f};
54FukuwaraiPart MarioEyeRightPart = {.name: "FukuwaraiMarioEyeRight", .basePoints: 3.0f, .anglePoints: 9.0f, .distancePoints: 3.0f};
55FukuwaraiPart MarioMouthPart = {.name: "FukuwaraiMarioMouth", .basePoints: 3.0f, .anglePoints: 9.0f, .distancePoints: 3.0f};
56FukuwaraiPart MarioMustachePart = {.name: "FukuwaraiMarioMustache", .basePoints: 3.0f, .anglePoints: 9.0f, .distancePoints: 3.0f};
57FukuwaraiPart MarioNosePart = {.name: "FukuwaraiMarioNose", .basePoints: 2.0f, .anglePoints: 6.0f, .distancePoints: 2.0f};
58
59FukuwaraiFaceParts::FukuwaraiFaceParts(const char* name, al::AreaObjGroup* group)
60 : al::LiveActor(name), mFukuwaraiArea(group) {}
61
62void FukuwaraiFaceParts::init(const al::ActorInitInfo& info) {
63 const char* suffix = nullptr;
64 al::tryGetStringArg(arg: &suffix, initInfo: info, key: "Suffix");
65
66 al::initMapPartsActor(actor: this, initInfo: info, suffix);
67 al::initNerve(actor: this, nerve: &Appear, maxStates: 0);
68
69 mCapTargetInfo = rs::createCapTargetInfo(this, suffix);
70
71 al::CollisionPartsFilterSpecialPurpose* filter =
72 new al::CollisionPartsFilterSpecialPurpose("MoveLimit");
73 al::setColliderFilterCollisionParts(this, filter);
74
75 if (al::isExistLinkChild(initInfo: info, linkName: "TargetPos", index: 0)) {
76 sead::Vector3f rotation = sead::Vector3f::zero;
77 al::getLinkTR(trans: &mTargetPos, rotate: &rotation, initInfo: info, linkName: "TargetPos");
78 if (rotation.y < 0.0f)
79 rotation.y += 360.0f;
80 mTargetAngle = rotation.y;
81 }
82
83 if (al::getRotate(actor: this).y < 0.0f)
84 al::setRotateY(actor: this, y: al::getRotate(actor: this).y + 360.0f);
85
86 mTrans.set(al::getTrans(actor: this));
87 mRotation.set(al::getRotate(actor: this));
88
89 mPlayerHackStartShaderCtrl = new PlayerHackStartShaderCtrl(this, nullptr);
90
91 makeActorDead();
92}
93
94s32 calculateFukuwaraiPartPriority(const char* name) {
95 if (al::isEqualString(str1: name, str2: "FukuwaraiKuriboMouthAngry"))
96 return 0;
97 if (al::isEqualString(str1: name, str2: "FukuwaraiMarioMouth"))
98 return 1;
99 if (al::isEqualString(str1: name, str2: "FukuwaraiKuriboEyeRight"))
100 return 2;
101 if (al::isEqualString(str1: name, str2: "FukuwaraiKuriboMarioEyeRight"))
102 return 3;
103 if (al::isEqualString(str1: name, str2: "FukuwaraiMarioEyeRight"))
104 return 4;
105 if (al::isEqualString(str1: name, str2: "FukuwaraiKuriboEyeLeft"))
106 return 5;
107 if (al::isEqualString(str1: name, str2: "FukuwaraiKuriboMarioEyeLeft"))
108 return 6;
109 if (al::isEqualString(str1: name, str2: "FukuwaraiMarioEyeLeft"))
110 return 7;
111 if (al::isEqualString(str1: name, str2: "FukuwaraiKuriboEyebrowRight"))
112 return 8;
113 if (al::isEqualString(str1: name, str2: "FukuwaraiMarioEyebrowRight"))
114 return 9;
115 if (al::isEqualString(str1: name, str2: "FukuwaraiKuriboEyebrowLeft"))
116 return 10;
117 if (al::isEqualString(str1: name, str2: "FukuwaraiMarioEyebrowLeft"))
118 return 11;
119 if (al::isEqualString(str1: name, str2: "FukuwaraiMarioMustache"))
120 return 12;
121 if (al::isEqualString(str1: name, str2: "FukuwaraiMarioNose"))
122 return 13;
123 return -1;
124}
125
126s32 FukuwaraiFaceParts::getPriority() const {
127 return calculateFukuwaraiPartPriority(name: al::getModelName(actor: this));
128}
129
130void FukuwaraiFaceParts::control() {}
131
132bool FukuwaraiFaceParts::receiveMsg(const al::SensorMsg* message, al::HitSensor* other,
133 al::HitSensor* self) {
134 if (rs::tryReceiveMsgInitCapTargetAndSetCapTargetInfo(message, mCapTargetInfo))
135 return true;
136
137 if (rs::isMsgCapEnableLockOn(message))
138 return al::isNerve(user: this, nerve: &NrvFukuwaraiFaceParts.Wait);
139
140 if (al::isSensorName(self, "Body")) {
141 if (al::isNerve(user: this, nerve: &NrvFukuwaraiFaceParts.Wait)) {
142 if (al::isMsgPlayerDisregard(msg: message) || rs::isMsgPlayerDisregardHomingAttack(message))
143 return false;
144 } else {
145 if (al::isMsgPlayerDisregard(msg: message) || rs::isMsgPlayerDisregardHomingAttack(message))
146 return true;
147 }
148
149 if (rs::isMsgPlayerDisregardTargetMarker(message))
150 return true;
151 }
152
153 if (al::isNerve(user: this, nerve: &NrvFukuwaraiFaceParts.Wait)) {
154 if (rs::isMsgCapCancelLockOn(message))
155 return true;
156
157 if (rs::isMsgStartHack(message)) {
158 al::invalidateClipping(actor: this);
159 mIUsePlayerHack = rs::startHack(self, other, nullptr);
160 rs::startHackStartDemo(mIUsePlayerHack, this);
161 al::setNerve(user: this, nerve: &NrvFukuwaraiFaceParts.CaptureStart);
162 return true;
163 }
164
165 return false;
166 }
167
168 if (al::isNerve(user: this, nerve: &NrvFukuwaraiFaceParts.CaptureStart))
169 return false;
170
171 if (mIUsePlayerHack && (rs::isMsgCancelHack(message) || rs::isMsgHackMarioDemo(message) ||
172 rs::isMsgHackMarioDead(message))) {
173 rs::endHack(&mIUsePlayerHack);
174 al::validateClipping(actor: this);
175 if (al::isInAreaObj(group: mFukuwaraiArea, position: al::getTrans(actor: this))) {
176 sead::Vector3f trans = al::getTrans(actor: this);
177 trans.y = mTrans.y + calculateFukuwaraiPartPriority(name: al::getModelName(actor: this)) * 0.25f;
178 al::offCollide(actor: this);
179 al::resetPosition(actor: this, trans);
180
181 al::setNerve(user: this, nerve: &NrvFukuwaraiFaceParts.Place);
182 } else {
183 al::setNerve(user: this, nerve: &NrvFukuwaraiFaceParts.Reset);
184 }
185 return true;
186 }
187
188 return false;
189}
190
191// NON_MATCHING
192f32 FukuwaraiFaceParts::calcScore(bool isMario) const {
193 if (!isPlaced())
194 return 0.0f;
195
196 const char* name = al::getModelName(actor: this);
197 FukuwaraiPart bodyPart;
198
199 if (isMario) {
200 if (al::isEqualString(str1: name, str2: MarioEyebrowLeftPart.name))
201 bodyPart = MarioEyebrowLeftPart;
202 else if (al::isEqualString(str1: name, str2: MarioEyebrowRightPart.name))
203 bodyPart = MarioEyebrowRightPart;
204 else if (al::isEqualString(str1: name, str2: MarioEyeLeftPart.name))
205 bodyPart = MarioEyeLeftPart;
206 else if (!al::isEqualString(str1: name, str2: MarioEyeRightPart.name))
207 bodyPart = MarioEyeRightPart;
208 else if (al::isEqualString(str1: name, str2: MarioMouthPart.name))
209 bodyPart = MarioMouthPart;
210 else if (al::isEqualString(str1: name, str2: MarioMustachePart.name))
211 bodyPart = MarioMustachePart;
212 else if (al::isEqualString(str1: name, str2: MarioNosePart.name))
213 bodyPart = MarioNosePart;
214 else
215 return -5.0f;
216 } else {
217 if (al::isEqualString(str1: name, str2: KuriboEyebrowLeftPart.name))
218 bodyPart = KuriboEyebrowLeftPart;
219 else if (al::isEqualString(str1: name, str2: KuriboEyebrowRightPart.name))
220 bodyPart = KuriboEyebrowRightPart;
221 else if (al::isEqualString(str1: name, str2: KuriboEyeLeftPart.name))
222 bodyPart = KuriboEyeLeftPart;
223 else if (al::isEqualString(str1: name, str2: KuriboEyeRightPart.name))
224 bodyPart = KuriboEyeRightPart;
225 else if (al::isEqualString(str1: name, str2: KuriboMouthAngryPart.name))
226 bodyPart = KuriboMouthAngryPart;
227 else
228 return -5.0f;
229 }
230
231 return bodyPart.basePoints + bodyPart.distancePoints * calcScoreDistRate() +
232 bodyPart.anglePoints * calcScoreAngleRate();
233}
234
235bool FukuwaraiFaceParts::isPlaced() const {
236 return al::isNerve(user: this, nerve: &NrvFukuwaraiFaceParts.Place) ||
237 al::isNerve(user: this, nerve: &NrvFukuwaraiFaceParts.Hide);
238}
239
240f32 FukuwaraiFaceParts::calcScoreAngleRate() const {
241 f32 angle = al::getRotate(actor: this).y;
242
243 f32 difference;
244 if (angle > mTargetAngle)
245 difference = angle - mTargetAngle;
246 else
247 difference = mTargetAngle - angle;
248
249 f32 score = difference > 180.0f ? 360.0f - difference : difference;
250
251 if (al::isEqualString(str1: "FukuwaraiMarioNose", str2: al::getModelName(actor: this))) {
252 f32 clampedScore = score > 90.0f ? 180.0f - score : score;
253
254 return 1.0f - clampedScore / 90.0f;
255 }
256
257 return 1.0f - score / 180.0f;
258}
259
260f32 FukuwaraiFaceParts::calcScoreDistRate() const {
261 sead::Vector3f out;
262 al::verticalizeVec(out: &out, vertical: sead::Vector3f::ey, vec: al::getTrans(actor: this) - mTargetPos);
263
264 f32 score = out.length() / 500.0f;
265 score = sead::Mathf::clamp(value: score, low: 0.0f, high: 1.0f);
266
267 return sead::Mathf::square(t: 1.0f - score);
268}
269
270void FukuwaraiFaceParts::show() {
271 appear();
272 al::showModelIfHide(actor: this);
273 al::setNerve(user: this, nerve: &NrvFukuwaraiFaceParts.Answer);
274}
275
276void FukuwaraiFaceParts::reset() {
277 appear();
278 al::validateHitSensors(this);
279 al::showModelIfHide(actor: this);
280 al::resetRotatePosition(actor: this, rot: mRotation, trans: mTrans);
281 al::setNerve(user: this, nerve: &Appear);
282}
283
284void FukuwaraiFaceParts::vanish() {
285 al::setNerve(user: this, nerve: &Vanish);
286}
287
288void FukuwaraiFaceParts::exePlace() {
289 if (al::isFirstStep(user: this)) {
290 al::setVelocityZero(this);
291 al::invalidateHitSensors(this);
292 al::startAction(actor: this, actionName: "Decide");
293 }
294
295 if (al::isActionEnd(actor: this))
296 al::setNerve(user: this, nerve: &NrvFukuwaraiFaceParts.Hide);
297}
298
299void FukuwaraiFaceParts::exeReset() {
300 if (al::isFirstStep(user: this)) {
301 al::setVelocityZero(this);
302 al::startAction(actor: this, actionName: "SwoonStart");
303 }
304 if (al::isActionEnd(actor: this)) {
305 reset();
306 al::setNerve(user: this, nerve: &Appear);
307 }
308}
309
310void FukuwaraiFaceParts::exeAppear() {
311 if (al::isFirstStep(user: this))
312 al::startAction(actor: this,
313 actionName: al::isNerve(user: this, nerve: &NrvFukuwaraiFaceParts.Answer) ? "Answer" : "Appear");
314
315 if (al::isActionEnd(actor: this))
316 al::setNerve(user: this, nerve: &NrvFukuwaraiFaceParts.Wait);
317}
318
319void FukuwaraiFaceParts::exeWait() {
320 if (al::isFirstStep(user: this))
321 al::setVelocityZero(this);
322}
323
324void FukuwaraiFaceParts::exeCaptureWait() {
325 if (al::isFirstStep(user: this)) {
326 al::startAction(actor: this, actionName: "Wait");
327 al::onCollide(actor: this);
328 }
329
330 if (rs::isOnHackMoveStick(mIUsePlayerHack)) {
331 al::setNerve(user: this, nerve: &NrvFukuwaraiFaceParts.CaptureMove);
332 return;
333 }
334
335 al::scaleVelocity(actor: this, factor: 0.5f);
336 al::setTransY(actor: this, y: mTrans.y + 10.0f);
337 if (rs::isHoldHackAction(mIUsePlayerHack)) {
338 al::addRotateAndRepeatY(actor: this, deg: 2.0f);
339 al::holdSe(this, "RotateRev_loop");
340 }
341 if (rs::isHoldHackJump(mIUsePlayerHack)) {
342 al::addRotateAndRepeatY(actor: this, deg: -2.0f);
343 al::holdSe(this, "Rotate_loop");
344 }
345}
346
347void FukuwaraiFaceParts::exeCaptureMove() {
348 if (al::isFirstStep(user: this)) {
349 al::startAction(actor: this, actionName: "Move");
350 if (al::isNerve(user: this, nerve: &NrvFukuwaraiFaceParts.CaptureMoveFast))
351 al::setSklAnimFrameRate(this, 1.3f, 0);
352 else
353 al::setSklAnimFrameRate(this, 1.0f, 0);
354 }
355
356 if (!rs::isOnHackMoveStick(mIUsePlayerHack)) {
357 al::setNerve(user: this, nerve: &NrvFukuwaraiFaceParts.CaptureWait);
358 return;
359 }
360
361 sead::Vector3f dir = {0, 0, 0};
362 f32 accel = 7.0f;
363 if (al::isNerve(user: this, nerve: &NrvFukuwaraiFaceParts.CaptureMoveFast))
364 accel = 12.0f;
365 rs::addHackActorAccelStick(this, mIUsePlayerHack, &dir, accel, sead::Vector3f::ey);
366
367 al::scaleVelocity(actor: this, factor: 0.5f);
368 al::setTransY(actor: this, y: mTrans.y + 10.0f);
369
370 if (rs::isHoldHackAction(mIUsePlayerHack)) {
371 al::addRotateAndRepeatY(actor: this, deg: 2.0f);
372 al::holdSe(this, "RotateRev_loop");
373 }
374 if (rs::isHoldHackJump(mIUsePlayerHack)) {
375 al::addRotateAndRepeatY(actor: this, deg: -2.0f);
376 al::holdSe(this, "Rotate_loop");
377 }
378
379 if (al::isGreaterStep(user: this, step: 30))
380 al::setNerve(user: this, nerve: &NrvFukuwaraiFaceParts.CaptureMove);
381 else if (rs::isTriggerHackSwing(mIUsePlayerHack))
382 al::setNerve(user: this, nerve: &NrvFukuwaraiFaceParts.CaptureMoveFast);
383}
384
385void FukuwaraiFaceParts::exeHide() {
386 if (al::isFirstStep(user: this)) {
387 al::setVelocityZero(this);
388 al::hideModelIfShow(actor: this);
389 }
390}
391
392void FukuwaraiFaceParts::exeCaptureStart() {
393 if (rs::isHackStartDemoEnterMario(mIUsePlayerHack))
394 al::setNerve(user: this, nerve: &CaptureHackStart);
395}
396
397void FukuwaraiFaceParts::exeCaptureHackStart() {
398 if (al::isFirstStep(user: this)) {
399 al::startAction(actor: this, actionName: "HackStart");
400 mPlayerHackStartShaderCtrl->start();
401 }
402
403 mPlayerHackStartShaderCtrl->update();
404
405 if (al::isActionEnd(actor: this)) {
406 mPlayerHackStartShaderCtrl->end();
407 rs::endHackStartDemo(mIUsePlayerHack, this);
408 al::setNerve(user: this, nerve: &NrvFukuwaraiFaceParts.CaptureWait);
409 }
410}
411
412bool FukuwaraiFaceParts::isKuriboMarioParts() const {
413 return al::isEqualString(str1: al::getModelName(actor: this), str2: "FukuwaraiKuriboEyebrowLeft") ||
414 al::isEqualString(str1: al::getModelName(actor: this), str2: "FukuwaraiKuriboEyebrowRight") ||
415 al::isEqualString(str1: al::getModelName(actor: this), str2: "FukuwaraiKuriboMarioEyeLeft") ||
416 al::isEqualString(str1: al::getModelName(actor: this), str2: "FukuwaraiKuriboMarioEyeRight") ||
417 al::isEqualString(str1: al::getModelName(actor: this), str2: "FukuwaraiKuriboMouthAngry") ||
418 al::isEqualString(str1: al::getModelName(actor: this), str2: "FukuwaraiMarioMustache");
419}
420