1#include "Npc/VolleyballBall.h"
2
3#include "Library/Item/ItemUtil.h"
4#include "Library/Joint/JointControllerKeeper.h"
5#include "Library/LiveActor/ActorActionFunction.h"
6#include "Library/LiveActor/ActorClippingFunction.h"
7#include "Library/LiveActor/ActorCollisionFunction.h"
8#include "Library/LiveActor/ActorFlagFunction.h"
9#include "Library/LiveActor/ActorInitUtil.h"
10#include "Library/LiveActor/ActorMovementFunction.h"
11#include "Library/LiveActor/ActorPoseUtil.h"
12#include "Library/LiveActor/ActorSensorUtil.h"
13#include "Library/Math/MathUtil.h"
14#include "Library/Math/ParabolicPath.h"
15#include "Library/Nerve/NerveSetupUtil.h"
16#include "Library/Nerve/NerveUtil.h"
17
18#include "Npc/VolleyballNpc.h"
19#include "Util/SensorMsgFunction.h"
20
21namespace {
22NERVE_IMPL(VolleyballBall, Wait)
23NERVE_IMPL(VolleyballBall, Attack)
24NERVE_END_IMPL(VolleyballBall, OnGround)
25NERVE_IMPL(VolleyballBall, Return)
26NERVE_IMPL(VolleyballBall, ReturnSmash)
27NERVE_IMPL(VolleyballBall, ReturnEnd)
28NERVE_IMPL(VolleyballBall, Miss)
29NERVE_IMPL(VolleyballBall, MissReaction)
30NERVE_IMPL(VolleyballBall, Toss)
31NERVE_IMPL(VolleyballBall, TossEnd)
32NERVE_IMPL(VolleyballBall, Retry)
33NERVE_IMPL(VolleyballBall, RetryEnd)
34
35NERVES_MAKE_NOSTRUCT(VolleyballBall, ReturnEnd, Toss, TossEnd)
36NERVES_MAKE_STRUCT(VolleyballBall, Wait, Return, ReturnSmash, Attack, Miss, MissReaction, Retry,
37 OnGround, RetryEnd)
38} // namespace
39
40const sead::Vector3f sCoinDropOffset = {0.0f, 200.0f, 200.0f};
41
42VolleyballBall::VolleyballBall(const char* name) : al::LiveActor(name) {}
43
44void VolleyballBall::init(const al::ActorInitInfo& initInfo) {
45 al::initActorWithArchiveName(actor: this, initInfo, archiveName: "VolleyballBall", suffix: nullptr);
46 al::initNerve(actor: this, nerve: &NrvVolleyballBall.Wait, maxStates: 0);
47 mAttackPath = new al::ParabolicPath();
48 mReturnPath = new al::ParabolicPath();
49 al::initJointControllerKeeper(this, 1);
50 al::initJointGlobalQuatController(this, &mOrientation, "AllRoot");
51 makeActorDead();
52}
53
54void VolleyballBall::control() {}
55
56bool VolleyballBall::receiveMsg(const al::SensorMsg* message, al::HitSensor* other,
57 al::HitSensor* self) {
58 if (rs::isMsgSeedReflect(message)) {
59 rs::requestHitReactionToAttacker(message, self, other);
60 return true;
61 }
62
63 if (isMiss()) {
64 if ((rs::isMsgCapAttack(message) || al::isMsgPlayerSpinAttack(msg: message)) && mMissDelay < 1) {
65 rs::requestHitReactionToAttacker(message, self, other);
66 al::setNerve(user: this, nerve: &NrvVolleyballBall.Retry);
67 return true;
68 }
69 if (rs::isMsgCapAttack(message) || al::isMsgPlayerSpinAttack(msg: message) ||
70 rs::checkMsgNpcTrampleReactionAll(message, other, self, false) ||
71 rs::isMsgHosuiAttack(message) || rs::isMsgHosuiTrample(message)) {
72 rs::requestHitReactionToAttacker(message, self, other);
73 if (mMissReactionDelay < 1)
74 al::setNerve(user: this, nerve: &NrvVolleyballBall.MissReaction);
75 mMissReactionDelay = 3;
76 return true;
77 }
78 }
79
80 if (_138)
81 return false;
82
83 if (al::isSensorSimple(self))
84 return false;
85
86 if (!al::isNerve(user: this, nerve: &NrvVolleyballBall.Attack) &&
87 !al::isNerve(user: this, nerve: &NrvVolleyballBall.OnGround) &&
88 !al::isNerve(user: this, nerve: &NrvVolleyballBall.Return))
89 return false;
90
91 if (al::isNerve(user: this, nerve: &NrvVolleyballBall.Attack) && al::isLessStep(user: this, step: 20))
92 return false;
93
94 if (al::isMsgPlayerKick(msg: message) || rs::isMsgUpperPunchAll(message) ||
95 rs::isMsgCapAttack(message) || rs::isMsgPlayerBallToss(message) ||
96 al::isMsgPlayerSpinAttack(msg: message)) {
97 if (al::isNerve(user: this, nerve: &NrvVolleyballBall.OnGround) && al::isGreaterEqualStep(user: this, step: 15))
98 return true;
99
100 if (al::isSensorName(self, "Smash") && !al::isMsgPlayerSpinAttack(msg: message))
101 return false;
102
103 if (al::isMsgPlayerSpinAttack(msg: message) &&
104 al::getTrans(actor: mNpc).y <= al::getTrans(actor: this).y + -100.0f &&
105 al::getTrans(actor: mNpc).y <= al::getSensorPos(other).y + -30.0f) {
106 if (!al::isNerve(user: this, nerve: &NrvVolleyballBall.Return))
107 mNpc->addSuccessCount();
108 al::startHitReactionHitEffect(actor: this, name: "スマッシュ", other, self);
109 al::setNerve(user: this, nerve: &NrvVolleyballBall.ReturnSmash);
110 return true;
111 }
112
113 if (!al::isNerve(user: this, nerve: &NrvVolleyballBall.Return)) {
114 mNpc->addSuccessCount();
115 rs::requestHitReactionToAttacker(message, self, other);
116 al::startHitReactionHitEffect(actor: this, name: "リターン", other, self);
117 al::setNerve(user: this, nerve: &NrvVolleyballBall.Return);
118 return true;
119 }
120 }
121 return false;
122}
123
124void VolleyballBall::attackSensor(al::HitSensor* self, al::HitSensor* other) {
125 if (al::isNerve(user: this, nerve: &NrvVolleyballBall.Return) ||
126 al::isNerve(user: this, nerve: &NrvVolleyballBall.ReturnSmash) ||
127 al::isNerve(user: this, nerve: &NrvVolleyballBall.Attack))
128 return;
129
130 if (al::isSensorName(self, "Push") && !rs::sendMsgPushToPlayer(source: other, target: self))
131 al::sendMsgPush(receiver: other, sender: self);
132}
133
134void VolleyballBall::attack(const sead::Vector3f& startPosition, const sead::Vector3f& endPosition,
135 f32 attackSpeed) {
136 al::resetPosition(actor: this, trans: startPosition);
137 mEndPosition.set(endPosition);
138 mAttackPath->initFromUpVectorAddHeight(start: startPosition, end: mEndPosition, up: sead::Vector3f::ey, height: 500.0f);
139 mPathTime = mAttackPath->calcPathTimeFromAverageSpeed(frames: attackSpeed);
140 mAttackSpeed = attackSpeed;
141 al::invalidateClipping(actor: this);
142 al::setNerve(user: this, nerve: &NrvVolleyballBall.Attack);
143 appear();
144}
145
146void VolleyballBall::toss(const sead::Vector3f& startPosition, const sead::Vector3f& endPosition) {
147 al::resetPosition(actor: this, trans: startPosition);
148 mEndPosition.set(endPosition);
149 al::invalidateClipping(actor: this);
150 al::offCollide(actor: this);
151 al::setNerve(user: this, nerve: &Toss);
152 appear();
153}
154
155void VolleyballBall::reset() {
156 if (al::isAlive(actor: this) && !al::isNerve(user: this, nerve: &NrvVolleyballBall.RetryEnd)) {
157 al::startHitReaction(actor: this, name: "消滅");
158 kill();
159 }
160 al::validateClipping(actor: this);
161 _138 = false;
162 al::setNerve(user: this, nerve: &NrvVolleyballBall.Wait);
163}
164
165bool VolleyballBall::isMiss() const {
166 return al::isNerve(user: this, nerve: &NrvVolleyballBall.Miss) ||
167 al::isNerve(user: this, nerve: &NrvVolleyballBall.MissReaction);
168}
169
170bool VolleyballBall::isReturnEnd() const {
171 return al::isNerve(user: this, nerve: &ReturnEnd);
172}
173
174bool VolleyballBall::isTossEnd() const {
175 return al::isNerve(user: this, nerve: &TossEnd);
176}
177
178bool VolleyballBall::isActive() const {
179 return !al::isNerve(user: this, nerve: &NrvVolleyballBall.Wait);
180}
181
182bool VolleyballBall::isRetry() const {
183 return al::isNerve(user: this, nerve: &NrvVolleyballBall.Retry);
184}
185
186bool VolleyballBall::isRetryEnd() const {
187 return al::isNerve(user: this, nerve: &NrvVolleyballBall.RetryEnd);
188}
189
190void VolleyballBall::exeWait() {}
191
192void VolleyballBall::exeAttack() {
193 if (al::isFirstStep(user: this)) {
194 al::startAction(actor: this, actionName: "Reaction");
195 al::startHitReactionHitEffect(actor: this, name: "リターン",
196 pos: (al::getTrans(actor: this) + al::getTrans(actor: mNpc)) * 0.5f);
197 }
198
199 if (al::isActionPlaying(actor: this, actionName: "Reaction") && al::isActionEnd(actor: this))
200 al::startAction(actor: this, actionName: "FlyWait");
201
202 sead::Vector3f prevPos = al::getTrans(actor: this);
203 sead::Vector3f newPos = sead::Vector3f::zero;
204 mAttackPath->calcPosition(pos: &newPos, prog: al::getNerveStep(user: this) / (f32)mPathTime);
205 al::resetPosition(actor: this, trans: newPos);
206
207 sead::Quatf mOrientationCopy = mOrientation;
208 al::rotateQuatRollBall(&mOrientation, mOrientationCopy, prevPos - newPos, al::getGravity(actor: this),
209 90.0f);
210 if (al::isGreaterEqualStep(user: this, step: mPathTime)) {
211 al::resetPosition(actor: this, trans: mEndPosition);
212 al::setNerve(user: this, nerve: &NrvVolleyballBall.OnGround);
213 }
214}
215
216void VolleyballBall::exeOnGround() {
217 if (al::isFirstStep(user: this)) {
218 al::startAction(actor: this, actionName: "Wait");
219 al::setVelocityJump(actor: this, speed: 10.0f);
220 al::startHitReaction(actor: this, name: "着地");
221 al::onCollide(actor: this);
222 }
223
224 al::addVelocityToGravity(actor: this, force: 0.98f);
225 al::scaleVelocity(actor: this, factor: 0.98f);
226
227 if (al::isOnGround(this, 0)) {
228 mMissDelay = 60;
229 al::setNerve(user: this, nerve: &NrvVolleyballBall.Miss);
230 }
231}
232
233void VolleyballBall::endOnGround() {
234 al::setVelocityZero(this);
235 al::offCollide(actor: this);
236}
237
238void VolleyballBall::exeReturn() {
239 if (al::isFirstStep(user: this)) {
240 sead::Vector3f startPosition = mAttackPath->getStart();
241 mReturnPath->initFromUpVectorAddHeight(start: al::getTrans(actor: this), end: startPosition,
242 up: sead::Vector3f::ey, height: 500.0f);
243 al::startAction(actor: this, actionName: "Reaction");
244 }
245
246 if (al::isActionPlaying(actor: this, actionName: "Reaction") && al::isActionEnd(actor: this))
247 al::startAction(actor: this, actionName: "FlyWait");
248
249 sead::Vector3f prevPos = al::getTrans(actor: this);
250 sead::Vector3f newPos = sead::Vector3f::zero;
251 mReturnPath->calcPosition(pos: &newPos, prog: al::getNerveStep(user: this) / (f32)mPathTime);
252 al::resetPosition(actor: this, trans: newPos);
253
254 sead::Quatf mOrientationCopy = mOrientation;
255 al::rotateQuatRollBall(&mOrientation, mOrientationCopy, prevPos - newPos, al::getGravity(actor: this),
256 90.0f);
257 if (al::isGreaterEqualStep(user: this, step: mPathTime))
258 al::setNerve(user: this, nerve: &ReturnEnd);
259}
260
261void VolleyballBall::exeReturnSmash() {
262 sead::Vector3f targetVec = al::getTrans(actor: mNpc) - al::getTrans(actor: this);
263 if (al::isFirstStep(user: this)) {
264 sead::Vector3f direction = targetVec;
265 al::normalize(vec: &direction);
266 al::setVelocityToDirection(actor: this, dir: direction, speed: 50.0f);
267 al::startAction(actor: this, actionName: "Smash");
268 al::makeQuatFrontNoSupport(outQuat: &mOrientation, front: al::getVelocity(actor: this));
269 }
270
271 f32 targetLen = targetVec.length();
272 if (al::getVelocity(actor: this).length() > targetLen) {
273 s32 smashCoins = al::getRandom(min: 1, max: 4);
274 mLastSmashCoins = (mLastSmashCoins == 3 && smashCoins == 3) ? 1 : smashCoins;
275 if (mLastSmashCoins > 0) {
276 for (s32 i = mLastSmashCoins; i != 0; i--) {
277 sead::Vector3f frontDir = mNpc->getFrontDir();
278 al::rotateVectorDegreeY(&frontDir, al::getRandom(min: -80.0f, max: 80.0f));
279 sead::Vector3f pose = sCoinDropOffset;
280 al::multVecPose(posOut: &pose, actor: mNpc, posIn: pose);
281 al::appearItemTiming(actor: mNpc, "スマッシュ", pose, frontDir, sensor: nullptr);
282 }
283 }
284 al::resetPosition(actor: this, trans: mAttackPath->getStart());
285 mNpc->startSmashReaction();
286 al::setNerve(user: this, nerve: &ReturnEnd);
287 }
288}
289
290void VolleyballBall::exeReturnEnd() {
291 al::validateClipping(actor: this);
292 kill();
293}
294
295void VolleyballBall::exeMiss() {
296 if (al::isFirstStep(user: this)) {
297 al::startAction(actor: this, actionName: "Wait");
298 al::setVelocityZero(this);
299 }
300 mMissDelay--;
301}
302
303void VolleyballBall::exeMissReaction() {
304 if (al::isFirstStep(user: this))
305 al::startAction(actor: this, actionName: "Reaction");
306 mMissReactionDelay--;
307 mMissDelay--;
308 if (al::isActionEnd(actor: this))
309 al::setNerve(user: this, nerve: &NrvVolleyballBall.Miss);
310}
311
312void VolleyballBall::exeToss() {
313 if (al::isFirstStep(user: this))
314 al::setVelocityJump(actor: this, speed: 12.0f);
315 al::addVelocityToGravity(actor: this, force: 0.4f);
316 if (al::getVelocity(actor: this).y <= 0.0f && mEndPosition.y >= al::getTrans(actor: this).y) {
317 al::setVelocityZero(this);
318 al::resetPosition(actor: this, trans: mEndPosition);
319 al::setNerve(user: this, nerve: &TossEnd);
320 }
321}
322
323void VolleyballBall::exeTossEnd() {}
324
325void VolleyballBall::exeRetry() {
326 if (al::isFirstStep(user: this)) {
327 sead::Vector3f startPosition = mAttackPath->getStart();
328 mReturnPath->initFromUpVectorAddHeight(start: al::getTrans(actor: this), end: startPosition,
329 up: sead::Vector3f::ey, height: 500.0f);
330 al::startAction(actor: this, actionName: "Reaction");
331 mPathTime = mReturnPath->calcPathTimeFromAverageSpeed(frames: 12.0f);
332 }
333
334 if (al::isActionPlaying(actor: this, actionName: "Reaction") && al::isActionEnd(actor: this))
335 al::startAction(actor: this, actionName: "FlyWait");
336 sead::Vector3f prevPos = al::getTrans(actor: this);
337 sead::Vector3f newPos = sead::Vector3f::zero;
338 mReturnPath->calcPosition(pos: &newPos, prog: al::getNerveStep(user: this) / (f32)mPathTime);
339 al::resetPosition(actor: this, trans: newPos);
340
341 sead::Quatf mOrientationCopy = mOrientation;
342 al::rotateQuatRollBall(&mOrientation, mOrientationCopy, prevPos - newPos, al::getGravity(actor: this),
343 90.0f);
344 if (al::isGreaterEqualStep(user: this, step: mPathTime))
345 al::setNerve(user: this, nerve: &NrvVolleyballBall.RetryEnd);
346}
347
348void VolleyballBall::exeRetryEnd() {}
349