1#include "Npc/KuriboGirl.h"
2
3#include <math/seadBoundBox.h>
4
5#include "Library/Area/AreaObj.h"
6#include "Library/Area/AreaObjGroup.h"
7#include "Library/Area/AreaObjUtil.h"
8#include "Library/Area/AreaShape.h"
9#include "Library/Camera/ActorCameraTarget.h"
10#include "Library/Camera/CameraUtil.h"
11#include "Library/Joint/JointControllerKeeper.h"
12#include "Library/Joint/JointSpringControllerHolder.h"
13#include "Library/LiveActor/ActorActionFunction.h"
14#include "Library/LiveActor/ActorAreaFunction.h"
15#include "Library/LiveActor/ActorClippingFunction.h"
16#include "Library/LiveActor/ActorInitUtil.h"
17#include "Library/LiveActor/ActorModelFunction.h"
18#include "Library/LiveActor/ActorPoseUtil.h"
19#include "Library/LiveActor/ActorSensorUtil.h"
20#include "Library/Math/MathUtil.h"
21#include "Library/Nerve/NerveSetupUtil.h"
22#include "Library/Nerve/NerveUtil.h"
23#include "Library/Placement/PlacementFunction.h"
24#include "Library/Player/PlayerUtil.h"
25
26#include "Item/Shine.h"
27#include "Util/DemoUtil.h"
28#include "Util/ItemUtil.h"
29#include "Util/PlayerUtil.h"
30#include "Util/SensorMsgFunction.h"
31
32namespace {
33NERVE_IMPL(KuriboGirl, WaitLoveAfter);
34NERVE_IMPL(KuriboGirl, Wait);
35NERVE_IMPL(KuriboGirl, Surprise);
36NERVE_IMPL(KuriboGirl, LoveLook);
37NERVE_IMPL(KuriboGirl, LookLoveAfter);
38NERVE_IMPL(KuriboGirl, Disappear);
39NERVE_IMPL(KuriboGirl, Hide);
40NERVE_IMPL(KuriboGirl, Appear);
41NERVE_IMPL(KuriboGirl, LoveFind);
42NERVE_IMPL(KuriboGirl, AppearShineStart);
43NERVE_IMPL(KuriboGirl, AppearShine);
44NERVE_IMPL(KuriboGirl, AppearShineEnd);
45NERVE_IMPL(KuriboGirl, LoveLookTurn);
46NERVE_IMPL(KuriboGirl, PreLoveFind);
47NERVE_IMPL(KuriboGirl, Find);
48
49NERVES_MAKE_STRUCT(KuriboGirl, WaitLoveAfter, Wait, Surprise, LoveLook, LookLoveAfter, Appear,
50 LoveLookTurn, PreLoveFind, Find);
51
52NERVES_MAKE_NOSTRUCT(KuriboGirl, Disappear, Hide, LoveFind, AppearShineStart, AppearShine,
53 AppearShineEnd);
54} // namespace
55
56KuriboGirl::KuriboGirl(const char* name) : al::LiveActor(name) {}
57
58void KuriboGirl::init(const al::ActorInitInfo& actorInitInfo) {
59 al::initActorWithArchiveName(actor: this, initInfo: actorInitInfo, archiveName: "KuriboGirl", suffix: nullptr);
60 mAreaObjGroup = al::createLinkAreaGroup(actor: this, initInfo: actorInitInfo, name: "FindArea", groupName: "子供エリアグループ",
61 areaName: "子供エリア");
62 mCamera = al::initObjectCamera(user: this, actorInitInfo, nullptr, nullptr);
63 mCameraTarget = al::createActorCameraTarget(actor: this, 0.0f);
64
65 al::tryGetArg(arg: &mIsMoveShine, initInfo: actorInitInfo, key: "isMoveShine");
66 mShineActor = rs::tryInitLinkShine(info: actorInitInfo, name: "ShineActor", linkIndex: 0);
67 if (mShineActor == nullptr) {
68 mIsAfterShineAppear = true;
69 al::initNerve(actor: this, nerve: &NrvKuriboGirl.WaitLoveAfter, maxStates: 0);
70 } else {
71 mShineMoveDir = new sead::Vector3f(al::getFront(actor: this));
72 al::initNerve(actor: this, nerve: &NrvKuriboGirl.Wait, maxStates: 0);
73 }
74
75 al::setHitSensorPosPtr(this, "WatchStart", &mHitSensorPos);
76 if (mAreaObjGroup == nullptr) {
77 mHitSensorPos.set(al::getTrans(actor: this));
78 al::setSensorRadius(this, "WatchStart", 900.0f);
79 } else {
80 sead::BoundBox3f boundBox;
81 mAreaObjGroup->getAreaObj(index: 0)->getAreaShape()->calcLocalBoundingBox(&boundBox);
82 sead::Vector3f areaShapeScale = mAreaObjGroup->getAreaObj(index: 0)->getAreaShape()->getScale();
83 boundBox.scaleX(sx: areaShapeScale.x);
84 boundBox.scaleY(sy: areaShapeScale.y);
85 boundBox.scaleZ(sz: areaShapeScale.z);
86
87 sead::Vector3f hitSensorPos = sead::Vector3f(0.0f, 0.0f, 0.0f);
88 mAreaObjGroup->getAreaObj(index: 0)->getAreaShape()->calcTrans(trans: &hitSensorPos);
89 hitSensorPos += sead::Vector3f(0.0f, boundBox.getHalfSizeY(), 0.0f);
90 mHitSensorPos.set(hitSensorPos);
91 al::setSensorRadius(
92 this, "WatchStart",
93 sead::Vector3f(boundBox.getSizeX(), boundBox.getSizeY(), boundBox.getSizeZ()).length() *
94 0.5f);
95 }
96 al::initJointControllerKeeper(this, 2);
97 mJointSpringControllerHolder = new al::JointSpringControllerHolder();
98 mJointSpringControllerHolder->init(this, "InitJointSpringCtrl");
99 al::initJointLocalYRotator(this, &mHeadRotator, "Head");
100 mInitialFront = al::getFront(actor: this);
101 makeActorAlive();
102}
103
104void KuriboGirl::control() {
105 if (!rs::isPlayerOnGround(this))
106 mFramesOnGround = 0;
107 else
108 mFramesOnGround++;
109 mIsPlayerNear = false;
110 mIsLoveLook = false;
111 mIsPlayerFar = true;
112}
113
114void KuriboGirl::exeWait() {
115 if (al::isFirstStep(user: this) && !al::isActionPlaying(actor: this, actionName: "Wait")) {
116 al::validateClipping(actor: this);
117 al::startAction(actor: this, actionName: "Wait");
118 }
119 lookFront();
120
121 al::LiveActor* nearestPlayerActor = al::tryFindNearestPlayerActor(this);
122 if (nearestPlayerActor != nullptr) {
123 if (rs::isPlayerHack(this) && !rs::isPlayerHackKuriboAny(this)) {
124 if (mAreaObjGroup == nullptr) {
125 sead::Vector3f distance = al::getTrans(actor: nearestPlayerActor) - al::getTrans(actor: this);
126 if (distance.length() < 900.0f) {
127 al::setNerve(user: this, nerve: &NrvKuriboGirl.Surprise);
128 return;
129 }
130 } else if (al::isInAreaObj(group: mAreaObjGroup, position: al::getTrans(actor: nearestPlayerActor))) {
131 al::setNerve(user: this, nerve: &NrvKuriboGirl.Surprise);
132 return;
133 }
134 }
135 if (rs::isPlayerHackTank(this)) {
136 al::setNerve(user: this, nerve: &NrvKuriboGirl.Surprise);
137 return;
138 }
139 }
140}
141
142void KuriboGirl::lookFront() {
143 mHeadRotator += (0.0f - mHeadRotator) * 0.1f;
144}
145
146void KuriboGirl::exeFind() {
147 if (al::isFirstStep(user: this))
148 al::startAction(actor: this, actionName: "Find");
149 lookPlayer();
150 if (al::isActionEnd(actor: this))
151 al::setNerve(user: this, nerve: &NrvKuriboGirl.LoveLook);
152}
153
154void KuriboGirl::lookPlayer() {
155 f32 angle = 0.0f;
156 sead::Vector3f nearestPlayerPos;
157 if (al::tryFindNearestPlayerPos(&nearestPlayerPos, this)) {
158 sead::Vector3f plane = nearestPlayerPos - al::getTrans(actor: this);
159 plane.y = 0;
160 al::tryNormalizeOrDirZ(vec: &plane);
161 angle = al::calcAngleOnPlaneDegree(a: al::getFront(actor: this), b: plane, vertical: sead::Vector3f::ey);
162
163 if (angle > 45.0) {
164 angle = 45.0;
165 if (!al::isNerve(user: this, nerve: &NrvKuriboGirl.LoveLookTurn))
166 al::setNerve(user: this, nerve: &NrvKuriboGirl.LoveLookTurn);
167 } else if (angle < -45.0) {
168 angle = -45.0;
169 if (!al::isNerve(user: this, nerve: &NrvKuriboGirl.LoveLookTurn))
170 al::setNerve(user: this, nerve: &NrvKuriboGirl.LoveLookTurn);
171 }
172 }
173 mHeadRotator += (angle - mHeadRotator) * 0.1f;
174}
175
176void KuriboGirl::exeLoveLook() {
177 if (al::isFirstStep(user: this) && !al::isActionPlaying(actor: this, actionName: "LoveLook"))
178 al::startAction(actor: this, actionName: "LoveLook");
179 lookPlayer();
180 if (!mIsLoveLook)
181 al::setNerve(user: this, nerve: &NrvKuriboGirl.Wait);
182}
183
184void KuriboGirl::exeLoveLookTurn() {
185 if (al::isFirstStep(user: this) && !al::isActionPlaying(actor: this, actionName: "LoveLookTurn"))
186 al::startAction(actor: this, actionName: "LoveLookTurn");
187
188 sead::Vector3f nearestPlayer;
189 if (al::tryFindNearestPlayerPos(&nearestPlayer, this)) {
190 sead::Vector3f plane = nearestPlayer - al::getTrans(actor: this);
191 plane.y = 0;
192 al::tryNormalizeOrDirZ(vec: &plane);
193 f32 angle = al::calcAngleOnPlaneDegree(a: al::getFront(actor: this), b: plane, vertical: sead::Vector3f::ey);
194 if (angle > 1.0f || angle < -1.0f) {
195 sead::Vector3f new_front = al::getFront(actor: this);
196 al::turnVecToVecDegree(&new_front, new_front, plane, 2.5f);
197 al::setFront(actor: this, front: new_front);
198 } else {
199 if (mIsAfterShineAppear)
200 al::setNerve(user: this, nerve: &NrvKuriboGirl.LookLoveAfter);
201 else
202 al::setNerve(user: this, nerve: &NrvKuriboGirl.LoveLook);
203 return;
204 }
205 }
206 lookPlayer();
207 if (!mIsLoveLook) {
208 if (mIsAfterShineAppear)
209 al::setNerve(user: this, nerve: &NrvKuriboGirl.WaitLoveAfter);
210 else
211 al::setNerve(user: this, nerve: &NrvKuriboGirl.Wait);
212 }
213}
214
215void KuriboGirl::exeSurprise() {
216 if (al::isFirstStep(user: this)) {
217 al::startAction(actor: this, actionName: "Surprise");
218 al::invalidateClipping(actor: this);
219 }
220 lookFront();
221
222 sead::Vector3f nearestPlayer;
223 if (al::tryFindNearestPlayerPos(&nearestPlayer, this)) {
224 sead::Vector3f new_front = nearestPlayer - al::getTrans(actor: this);
225 new_front.y = 0.0f;
226 al::tryNormalizeOrDirZ(vec: &new_front);
227 new_front += al::getFront(actor: this) * 3.0f;
228 al::tryNormalizeOrDirZ(vec: &new_front);
229 al::setFront(actor: this, front: new_front);
230 }
231 al::setNerveAtActionEnd(actor: this, nerve: &Disappear);
232}
233
234void KuriboGirl::exeDisappear() {
235 if (al::isFirstStep(user: this))
236 al::startAction(actor: this, actionName: "Disappear");
237 if (al::isActionEnd(actor: this))
238 al::setNerve(user: this, nerve: &Hide);
239}
240
241void KuriboGirl::exeHide() {
242 if (al::isFirstStep(user: this)) {
243 al::hideModel(actor: this);
244 al::invalidateHitSensor(this, "Body");
245 al::startHitReaction(actor: this, name: "消滅");
246 }
247 if (mIsPlayerFar && al::isNearPlayer(this, al::getSensorRadius(this, "WatchStart")))
248 mIsPlayerFar = false;
249 if (al::isGreaterEqualStep(user: this, step: 120) && mIsPlayerFar && !rs::isPlayerHackTank(this)) {
250 al::validateHitSensor(this, "Body");
251 al::setFront(actor: this, front: mInitialFront);
252 al::setNerve(user: this, nerve: &NrvKuriboGirl.Appear);
253 }
254}
255
256void KuriboGirl::exeAppear() {
257 if (al::isFirstStep(user: this)) {
258 al::startAction(actor: this, actionName: "Appear");
259 al::startHitReaction(actor: this, name: "出現");
260 al::showModel(actor: this);
261 }
262
263 lookFront();
264
265 if (al::isActionEnd(actor: this)) {
266 if (mIsAfterShineAppear)
267 al::setNerve(user: this, nerve: &NrvKuriboGirl.WaitLoveAfter);
268 else
269 al::setNerve(user: this, nerve: &NrvKuriboGirl.Wait);
270 }
271}
272
273void KuriboGirl::exePreLoveFind() {
274 if (rs::requestStartDemoNormal(this, false)) {
275 al::startCamera(user: this, ticket: mCamera, unk: -1);
276 al::setCameraTarget(user: this, target: mCameraTarget);
277 al::setNerve(user: this, nerve: &LoveFind);
278 }
279}
280
281void KuriboGirl::exeLoveFind() {
282 if (al::isFirstStep(user: this))
283 al::startAction(actor: this, actionName: "Find");
284 if (al::isActionEnd(actor: this))
285 al::setNerve(user: this, nerve: &AppearShineStart);
286}
287
288void KuriboGirl::exeAppearShineStart() {
289 if (al::isFirstStep(user: this))
290 al::startAction(actor: this, actionName: "AppearShineStart");
291 if (al::isActionEnd(actor: this))
292 al::setNerve(user: this, nerve: &AppearShine);
293}
294
295void KuriboGirl::exeAppearShine() {
296 if (al::isFirstStep(user: this)) {
297 if (mIsMoveShine) {
298 sead::Vector3f shineActorPosDelta = al::getTrans(actor: mShineActor) - al::getTrans(actor: this);
299
300 f32 angle;
301 sead::Vector3f nearestPlayer;
302 if (al::tryFindNearestPlayerPos(&nearestPlayer, this)) {
303 angle = al::calcAngleOnPlaneDegree(
304 a: *mShineMoveDir, b: nearestPlayer - al::getTrans(actor: this), vertical: sead::Vector3f::ey);
305 } else {
306 angle = al::calcAngleOnPlaneDegree(a: *mShineMoveDir, b: al::getFront(actor: this),
307 vertical: sead::Vector3f::ey);
308 }
309
310 al::rotateVectorDegreeY(&shineActorPosDelta, angle);
311
312 al::setTrans(actor: mShineActor, trans: al::getTrans(actor: this) + shineActorPosDelta);
313
314 sead::Vector3f shineActorPos = mShineActor->get_16c();
315 sead::Vector3f local_80 = shineActorPos - al::getTrans(actor: this);
316
317 al::rotateVectorDegreeY(&local_80, angle);
318 local_80 += al::getTrans(actor: this);
319
320 mShineActor->set_16c(local_80);
321 }
322 rs::appearPopupShineWithoutDemo(shine: mShineActor);
323 al::startAction(actor: this, actionName: "AppearShine");
324 mIsAfterShineAppear = true;
325 }
326 if (rs::isEndAppearShine(shine: mShineActor))
327 al::setNerve(user: this, nerve: &AppearShineEnd);
328}
329
330void KuriboGirl::exeAppearShineEnd() {
331 if (al::isFirstStep(user: this))
332 al::startAction(actor: this, actionName: "AppearShineEnd");
333 if (al::isActionEnd(actor: this)) {
334 rs::requestEndDemoNormal(this);
335 al::endCamera(user: this, ticket: mCamera, -1, false);
336 al::resetCameraTarget(user: this, target: mCameraTarget);
337 al::setNerve(user: this, nerve: &NrvKuriboGirl.LookLoveAfter);
338 }
339}
340
341void KuriboGirl::exeLookLoveAfter() {
342 if (al::isFirstStep(user: this) && !al::isActionPlaying(actor: this, actionName: "LoveAfter"))
343 al::startAction(actor: this, actionName: "LoveAfter");
344 lookPlayer();
345
346 if (!mIsPlayerNear)
347 al::setNerve(user: this, nerve: &NrvKuriboGirl.WaitLoveAfter);
348 else if (!rs::isPlayerHackKuriboAny(this))
349 al::setNerve(user: this, nerve: &NrvKuriboGirl.Surprise);
350}
351
352void KuriboGirl::exeWaitLoveAfter() {
353 if (al::isFirstStep(user: this) && !al::isActionPlaying(actor: this, actionName: "LoveAfter"))
354 al::startAction(actor: this, actionName: "LoveAfter");
355 lookFront();
356}
357
358void KuriboGirl::attackSensor(al::HitSensor* self, al::HitSensor* other) {
359 if (al::isSensorName(self, "WatchStart")) {
360 if (rs::sendMsgKuriboGirlLove(source: other, target: self) && isWatchStart(pos: al::getSensorPos(other))) {
361 mIsPlayerNear = true;
362 mIsPlayerFar = false;
363 if (al::isNerve(user: this, nerve: &NrvKuriboGirl.LoveLook) && mFramesOnGround >= 16)
364 al::setNerve(user: this, nerve: &NrvKuriboGirl.PreLoveFind);
365 else if (al::isNerve(user: this, nerve: &NrvKuriboGirl.WaitLoveAfter) && mFramesOnGround >= 16)
366 al::setNerve(user: this, nerve: &NrvKuriboGirl.LookLoveAfter);
367 return;
368 }
369
370 if (al::isSensorPlayerAll(other) && !rs::isPlayerHackKuriboAny(this) &&
371 isWatchStart(pos: al::getSensorPos(other))) {
372 mIsPlayerNear = true;
373 mIsPlayerFar = false;
374 if (al::isNerve(user: this, nerve: &NrvKuriboGirl.Wait) ||
375 al::isNerve(user: this, nerve: &NrvKuriboGirl.WaitLoveAfter))
376 al::setNerve(user: this, nerve: &NrvKuriboGirl.Surprise);
377 return;
378 }
379 }
380
381 if (al::isSensorName(self, "WatchFar") && rs::sendMsgKuriboGirlLove(source: other, target: self)) {
382 mIsLoveLook = true;
383 if (al::isNerve(user: this, nerve: &NrvKuriboGirl.Wait))
384 al::setNerve(user: this, nerve: &NrvKuriboGirl.Find);
385 return;
386 }
387 if (al::isSensorEnemyBody(self)) {
388 al::sendMsgPush(receiver: other, sender: self);
389 rs::sendMsgPushToPlayer(source: other, target: self);
390 return;
391 }
392 if (al::isSensorEnemyAttack(self) &&
393 (isNrvWait() || al::isNerve(user: this, nerve: &NrvKuriboGirl.Appear))) {
394 if (rs::sendMsgKuriboGirlAttack(source: other, target: self) && !al::isNerve(user: this, nerve: &NrvKuriboGirl.Appear))
395 al::setNerve(user: this, nerve: &NrvKuriboGirl.Surprise);
396 }
397}
398
399bool KuriboGirl::isWatchStart(sead::Vector3f pos) {
400 if (mAreaObjGroup != nullptr) {
401 if (al::isInAreaObj(group: mAreaObjGroup, position: pos))
402 return true;
403 } else {
404 f32 length = (pos - al::getTrans(actor: this)).length();
405 if (length < 900.0f)
406 return true;
407 }
408 return false;
409}
410
411bool KuriboGirl::isNrvWait() {
412 return al::isNerve(user: this, nerve: &NrvKuriboGirl.Wait) || al::isNerve(user: this, nerve: &NrvKuriboGirl.LoveLook) ||
413 al::isNerve(user: this, nerve: &NrvKuriboGirl.LoveLookTurn) ||
414 al::isNerve(user: this, nerve: &NrvKuriboGirl.LookLoveAfter) ||
415 al::isNerve(user: this, nerve: &NrvKuriboGirl.WaitLoveAfter);
416}
417
418bool KuriboGirl::receiveMsg(const al::SensorMsg* msg, al::HitSensor* other, al::HitSensor* self) {
419 if (rs::isMsgPlayerDisregardHomingAttack(msg) || rs::isMsgPlayerDisregardTargetMarker(msg) ||
420 al::isMsgPlayerDisregard(msg))
421 return true;
422
423 if ((al::isSensorPlayerAttack(other) || rs::isMsgHosuiAttack(msg)) &&
424 al::isSensorName(self, "EyeWarning")) {
425 mIsPlayerFar = false;
426 if (isNrvWait())
427 al::setNerve(user: this, nerve: &NrvKuriboGirl.Surprise);
428 return false;
429 }
430 if (al::isMsgPush(msg) && al::isSensorName(self, "Body"))
431 return true;
432 if (rs::isMsgCapAttack(msg) && al::isSensorName(self, "EyeWarning")) {
433 mIsPlayerFar = false;
434 if (isNrvWait())
435 al::setNerve(user: this, nerve: &NrvKuriboGirl.Surprise);
436 return false;
437 }
438 return false;
439}
440