1#include "Enemy/PackunPoisonBall.h"
2
3#include "Library/Effect/EffectSystemInfo.h"
4#include "Library/LiveActor/ActorActionFunction.h"
5#include "Library/LiveActor/ActorClippingFunction.h"
6#include "Library/LiveActor/ActorCollisionFunction.h"
7#include "Library/LiveActor/ActorFlagFunction.h"
8#include "Library/LiveActor/ActorInitUtil.h"
9#include "Library/LiveActor/ActorModelFunction.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/Matrix/MatrixUtil.h"
16#include "Library/Nerve/NerveSetupUtil.h"
17#include "Library/Nerve/NerveUtil.h"
18
19#include "Util/SensorMsgFunction.h"
20
21namespace {
22NERVE_IMPL(PackunPoisonBall, Move)
23NERVE_IMPL(PackunPoisonBall, Paint)
24NERVE_IMPL(PackunPoisonBall, Fall)
25
26NERVES_MAKE_NOSTRUCT(PackunPoisonBall, Move, Paint, Fall)
27} // namespace
28
29PackunPoisonBall::PackunPoisonBall(al::LiveActor* parent, bool isBig)
30 : LiveActor("ポイズンパックンの毒だま"), mParent(parent), mIsBig(isBig),
31 mParabolicPath(new al::ParabolicPath()) {
32 getName();
33}
34
35void PackunPoisonBall::init(const al::ActorInitInfo& info) {
36 al::initActorWithArchiveName(actor: this, initInfo: info, archiveName: "PackunPoisonBall", suffix: mIsBig ? "Big" : nullptr);
37 al::initNerve(actor: this, nerve: &Move, maxStates: 1);
38 al::setEffectNamedMtxPtr(this, "CollidedWallMtx", &mEffectCollidedWallMtx);
39
40 makeActorDead();
41}
42
43void PackunPoisonBall::attackSensor(al::HitSensor* self, al::HitSensor* other) {
44 if (!al::isSensorEnemyAttack(self))
45 return;
46
47 if (al::isNerve(user: this, nerve: &Paint) &&
48 rs::sendMsgPaintTexture(source: other, target: self,
49 (s32)(al::calcNerveRate(user: this, max: 10) * (mIsBig ? 400.0f : 200.0f)),
50 mPaintAngle, 1)) {
51 return;
52 }
53
54 if (!al::isSensorEnemy(other) && !al::isSensorPlayer(other) && !al::isSensorMapObj(other))
55 return;
56
57 if (al::getSensorHost(other) == mParent)
58 return;
59
60 if (!mIsHack) {
61 if (rs::sendMsgEnemyAttackPoison(source: other, target: self)) {
62 al::startHitReaction(actor: this, name: "命中");
63 kill();
64 }
65 } else if (rs::sendMsgHackAttackPoison(source: other, target: self)) {
66 al::startHitReaction(actor: this, name: "命中");
67 kill();
68 }
69}
70
71bool PackunPoisonBall::receiveMsg(const al::SensorMsg* message, al::HitSensor* other,
72 al::HitSensor* self) {
73 if (al::isMsgPlayerDisregard(msg: message) || rs::isMsgPlayerDisregardHomingAttack(message))
74 return false;
75
76 if (rs::isMsgPlayerDisregardTargetMarker(message))
77 return true;
78
79 if (!al::isDead(actor: this) && !al::isNerve(user: this, nerve: &Paint) && rs::isMsgCapAttack(message)) {
80 al::startHitReaction(actor: this, name: "帽子で死亡");
81 rs::requestHitReactionToAttacker(message, self, other);
82 kill();
83 }
84
85 return false;
86}
87
88void PackunPoisonBall::killBySwitch() {
89 if (al::isDead(actor: this) || al::isNerve(user: this, nerve: &Paint))
90 return;
91
92 al::startHitReaction(actor: this, name: "消滅");
93 kill();
94}
95
96void PackunPoisonBall::setParam(const sead::Vector3f& trans, const sead::Quatf& quat, bool isHack,
97 f32 shootDist, f32 shootHorizontalSpeed, f32 shootHeight) {
98 mIsHack = isHack;
99 mShootDist = shootDist;
100 mShootHorizontalSpeed = shootHorizontalSpeed;
101 mShootHeight = shootHeight;
102
103 al::setTrans(actor: this, trans);
104 al::setQuat(actor: this, quat);
105}
106
107void PackunPoisonBall::appear() {
108 if (al::isAlive(actor: this))
109 return;
110
111 makeActorAlive();
112 al::showModelIfHide(actor: this);
113 al::startHitReaction(actor: this, name: "出現");
114 al::invalidateClipping(actor: this);
115 al::onCollide(actor: this);
116 al::setNerve(user: this, nerve: &Move);
117}
118
119void updateQuat(al::LiveActor* actor, const sead::Vector3f& dir);
120
121void PackunPoisonBall::exeMove() {
122 if (al::isFirstStep(user: this)) {
123 sead::Vector3f front = sead::Vector3f::ez;
124 al::calcQuatFront(front: &front, actor: mParent);
125
126 sead::Vector3f side = sead::Vector3f::ex;
127 al::calcQuatSide(side: &side, actor: mParent);
128
129 mTrans.set(al::getTrans(actor: this));
130
131 f32 shootDistOffset = mIsHack ? al::getRandom(min: -50.0f, max: 50.0f) : 0.0f;
132 f32 sideOffset = mIsHack ? al::getRandom(min: -100.0f, max: 100.0f) : 0.0f;
133 f32 heightOffset = mIsHack ? al::getRandom(min: -20.0f, max: 20.0f) : 0.0f;
134
135 mParabolicPath->initFromUpVector(
136 start: al::getTrans(actor: this),
137 end: al::getTrans(actor: this) + (shootDistOffset + mShootDist) * front + sideOffset * side,
138 up: -al::getGravity(actor: this), maxHeight: heightOffset + mShootHeight);
139
140 mParabolicPathTime = mParabolicPath->calcPathTimeFromHorizontalSpeed(frames: mShootHorizontalSpeed);
141 }
142
143 f32 rate = al::calcNerveRate(user: this, min: -1, max: mParabolicPathTime);
144 mParabolicPath->calcPosition(pos: al::getTransPtr(actor: this), prog: rate);
145
146 if (al::isCollidedWall(this)) {
147 al::makeMtxFrontUpPos(outMtx: &mEffectCollidedWallMtx, front: al::getCollidedWallNormal(this),
148 up: sead::Vector3f::ey, pos: al::getCollidedWallPos(this));
149 al::startHitReaction(actor: this, name: "壁で死亡");
150 kill();
151
152 return;
153 }
154
155 if (al::isCollidedGround(this)) {
156 bool isFloorPoison = al::isCollidedFloorCode(this, "Poison");
157 al::startHitReaction(actor: this, name: "着地");
158
159 if (isFloorPoison) {
160 kill();
161 } else {
162 al::deleteEffect(this, "PackunPoisonBallAttack");
163 al::setNerve(user: this, nerve: &Paint);
164 }
165
166 return;
167 }
168
169 if (al::isGreaterEqualStep(user: this, step: mParabolicPathTime)) {
170 al::setNerve(user: this, nerve: &Fall);
171
172 return;
173 }
174
175 sead::Vector3f dir = al::getTrans(actor: this);
176 dir -= mTrans;
177 updateQuat(actor: this, dir);
178 mTrans.set(al::getTrans(actor: this));
179}
180
181void updateQuat(al::LiveActor* actor, const sead::Vector3f& dir) {
182 sead::Vector3f front = dir;
183 if (!al::tryNormalizeOrZero(out: &front))
184 return;
185
186 sead::Quatf newQuat = al::getQuat(actor);
187 if (!al::isParallelDirection(a: front, b: sead::Vector3f::ey)) {
188 al::makeQuatFrontUp(outQuat: &newQuat, front, up: sead::Vector3f::ey);
189 } else {
190 sead::Vector3f side;
191 al::calcSideDir(side: &side, actor);
192 if (!al::isParallelDirection(a: front, b: side)) {
193 al::makeQuatFrontSide(outQuat: &newQuat, front, side: -side);
194 } else {
195 sead::Vector3f up;
196 al::calcUpDir(up: &up, actor);
197 if (!al::isParallelDirection(a: front, b: up))
198 al::makeQuatFrontUp(outQuat: &newQuat, front, up);
199 }
200 }
201
202 al::slerpQuat(al::getQuatPtr(actor), al::getQuat(actor), newQuat, 0.9f);
203}
204
205void PackunPoisonBall::exeFall() {
206 if (al::isFirstStep(user: this)) {
207 sead::Vector3f velocity = al::getTrans(actor: this);
208 velocity -= mTrans;
209 al::setVelocity(actor: this, vel: velocity);
210 }
211
212 al::addVelocityToGravity(actor: this, force: 0.98f);
213 al::scaleVelocity(actor: this, factor: 0.998f);
214
215 if (al::isGreaterStep(user: this, step: 250)) {
216 al::startHitReaction(actor: this, name: "消滅");
217 kill();
218
219 return;
220 }
221
222 if (al::isCollidedFloorCode(this, "Poison")) {
223 al::startHitReaction(actor: this, name: "着地");
224 kill();
225
226 return;
227 }
228
229 if (al::isCollided(this)) {
230 al::startHitReaction(actor: this, name: "着地");
231 al::deleteEffect(this, "PackunPoisonBallAttack");
232 al::setNerve(user: this, nerve: &Paint);
233
234 return;
235 }
236
237 updateQuat(actor: this, dir: al::getVelocity(actor: this));
238}
239
240void PackunPoisonBall::exePaint() {
241 if (al::isFirstStep(user: this)) {
242 al::setVelocityZero(this);
243 al::hideModelIfShow(actor: this);
244 mPaintAngle = al::getRandomRadian();
245 }
246
247 if (al::isGreaterEqualStep(user: this, step: 10))
248 kill();
249}
250