1#include "ObjectWanwan.hh"
3#include "game/field/CollisionDirector.hh"
4#include "game/field/ObjectDirector.hh"
6#include "game/kart/KartObject.hh"
8#include "game/system/RaceConfig.hh"
10#include "game/system/RaceManager.hh"
16 : ObjectCollidable(params), StateManager(this, STATE_ENTRIES), m_pitch(0.0f),
17 m_chainLength(static_cast<f32>(params.setting(0))),
18 m_attackDistance(4800.0f + static_cast<f32>(params.setting(2))),
19 m_attackArcTargetX(10.0f * static_cast<f32>(static_cast<s16>(params.setting(3)))),
20 m_attackArcTargetZ(10.0f * static_cast<f32>(static_cast<s16>(params.setting(4)))),
21 m_chainAttachMat(
EGG::Matrix34f::ident) {
26 m_idleDuration =
static_cast<u32
>(params.setting(5));
27 m_attackArc =
static_cast<f32
>(params.setting(6));
29 if (m_idleDuration == 0) {
33 if (m_attackArc == 0.0f) {
37 auto *pile =
new ObjectWanwanPile(m_pos, m_rot, m_scale);
40 m_anchor = m_pos + ANCHOR_OFFSET;
44 f32 sqLen = m_chainLength * m_chainLength + 300.0f * (300.0f * m_scale.y) * m_scale.y;
45 m_chainCount =
static_cast<u32
>(EGG::Mathf::sqrt(sqLen) / (CHAIN_LENGTH * m_scale.y));
46 if (m_chainCount > 0) {
50 m_initPos = m_anchor +
EGG::Vector3f(0.5f * -m_chainLength, 600.0f, 0.5f * -m_chainLength);
52 auto course = System::RaceConfig::Instance()->raceScenario().course;
53 if (course == Course::Mario_Circuit) {
57 m_attackArcCenter = pos * m_chainLength + m_anchor;
58 m_attackArcCenter.y = POS_OFFSET_MCWII.y;
59 }
else if (course == Course::GCN_Mario_Circuit) {
63 m_attackArcCenter = pos * m_chainLength + m_anchor;
64 m_attackArcCenter.y = POS_OFFSET_RMC.y;
66 m_attackArcCenter = m_anchor;
67 m_attackArcCenter.z += m_chainLength;
70 initTransformKeyframes();
74ObjectWanwan::~ObjectWanwan() =
default;
77void ObjectWanwan::init() {
79 m_chainAttachPos = m_initPos;
83 m_tangent = EGG::Vector3f::ex;
84 m_up = EGG::Vector3f::ey;
85 m_targetUp = EGG::Vector3f::ey;
86 m_touchingFloor =
false;
90 m_targetDir = EGG::Vector3f::ez;
93 m_attackStill =
false;
94 m_backDir = EGG::Vector3f::ex;
99void ObjectWanwan::calc() {
100 StateManager::calc();
106 calcChainAttachMat();
111 if (m_pos.y < m_anchor.y - 1000.0f) {
112 m_flags.setBit(eFlags::Position);
113 m_pos.y = m_anchor.y + 1000.0f;
119Kart::Reaction ObjectWanwan::onCollision(
Kart::KartObject *kartObj, Kart::Reaction reactionOnKart,
121 if (m_currentStateId == 1 && !m_attackStill) {
122 return reactionOnKart;
125 return kartObj->speedRatioCapped() < 0.5f ? Kart::Reaction::WallAllSpeed : reactionOnKart;
129void ObjectWanwan::enterWait() {
130 constexpr f32 ANGLE_RANGE = 0.33f * 60.0f;
131 constexpr f32 ANGLE_NORMALIZATION = 0.66f * 60.0f;
140 System::RaceManager::Instance()->random().getF32(ANGLE_RANGE) + ANGLE_NORMALIZATION;
143 if (CrossXZ(m_pos + m_tangent, m_pos, m_anchor) >= 0.0f) {
148 vStack_40.normalise2();
150 EGG::Vector3f vStack_4c = RotateXZByYaw(DEG2RAD * randAngle, vStack_40);
151 f32 fVar2 = m_chainLength < 3000.0f ? 0.5f : 0.7f;
152 m_target = vStack_4c * m_chainLength * fVar2 + m_anchor;
156void ObjectWanwan::enterAttack() {
164 m_attackStill =
false;
172void ObjectWanwan::enterBack() {
178 m_pitch = -ObjectDirector::Instance()->WanwanMaxPitch();
179 m_backDir = m_anchor - m_pos;
181 m_backDir.normalise2();
182 m_attackStill =
false;
188void ObjectWanwan::calcWait() {
189 constexpr f32 ANGLE_RANGE = 0.33f * 0.5f * 60.0f;
190 constexpr f32 ANGLE_NORMALIZATION = 0.66f * 0.5f * 60.0f;
196 f32 distFromTarget = F_PI;
197 if (targetDir.
squaredLength() > std::numeric_limits<f32>::epsilon()) {
201 if (!m_retarget && distFromTarget < 0.55f * m_chainLength) {
203 auto &rand = System::RaceManager::Instance()->random();
204 f32 angle = rand.getF32(ANGLE_RANGE) + ANGLE_NORMALIZATION;
205 if (CrossXZ(m_target, m_attackArcCenter, m_anchor) >= 0.0f) {
209 m_target = RotateXZByYaw(angle * DEG2RAD, relTarget) + m_anchor;
213 m_targetDir = targetDir;
225void ObjectWanwan::calcAttack() {
226 constexpr u32 ATTACK_DURATION = 120;
227 constexpr f32 PITCH_STEP = 25.0f;
229 if (m_currentFrame > ATTACK_DURATION) {
233 f32 maxPitch = ObjectDirector::Instance()->WanwanMaxPitch();
234 if (EGG::Mathf::abs(m_pitch) < EGG::Mathf::abs(maxPitch)) {
235 m_pitch -= maxPitch / PITCH_STEP;
238 calcTangent(10.0f * 0.04f);
241 if (m_chainTaut || m_attackStill) {
242 if (!m_attackStill) {
243 m_target = m_anchor + (m_chainAttachPos - m_anchor) * 2.0f;
244 m_targetDir = m_target - m_pos;
245 m_targetDir.y = 0.0f;
246 m_targetDir.normalise2();
249 f32 radius = posOffset.
length();
252 chainDir.normalise2();
253 m_flags.setBit(eFlags::Position);
254 m_pos.x = m_chainAttachPos.x + chainDir.x * radius;
255 m_pos.z = m_chainAttachPos.z + chainDir.z * radius;
258 if (tangent.
squaredLength() > std::numeric_limits<f32>::epsilon()) {
259 tangent.normalise2();
265 m_attackStill =
true;
269 m_vel.x = m_tangent.x * 120.0f;
270 m_vel.z = m_tangent.z * 120.0f;
275void ObjectWanwan::calcBack() {
276 if (EGG::Mathf::abs(m_pitch) > 2.0f) {
277 m_pitch += ObjectDirector::Instance()->WanwanMaxPitch() / 15.0f;
280 m_vel.x = m_backDir.x * m_speed * 1.5f;
281 m_vel.z = m_backDir.z * m_speed * 1.5f;
283 if (m_touchingFloor) {
285 m_accel += EGG::Vector3f::ey * 12.0f;
290 if (m_currentFrame > 90) {
296void ObjectWanwan::calcPos() {
297 m_vel += m_accel - GRAVITY;
299 m_flags.setBit(eFlags::Position);
304void ObjectWanwan::calcCollision() {
307 m_touchingFloor =
false;
311 auto *colDir = CollisionDirector::Instance();
313 bool hasCol = colDir->checkSphereFullPush(30.0f, pos, EGG::Vector3f::inf,
KCL_TYPE_FLOOR, &info,
320 m_touchingFloor =
true;
323 m_pos += EGG::Vector3f::ey * scale;
324 m_flags.setBit(eFlags::Position);
326 if (info.floorDist > -std::numeric_limits<f32>::min()) {
327 m_targetUp = info.floorNrm;
334void ObjectWanwan::calcMat() {
336 if (m_currentStateId == 1) {
337 SetRotTangentHorizontal(mat, EGG::Vector3f::ey, m_tangent);
339 SetRotTangentHorizontal(mat, m_up, m_tangent);
341 mat.
setBase(3, EGG::Vector3f::zero);
345 rtMat.
makeRT(rot, EGG::Vector3f::zero);
352void ObjectWanwan::calcChainAttachMat() {
353 u32 idx = m_currentStateId == 1 ? 0 : m_frame % 15;
358 mat.
setBase(3, EGG::Vector3f::zero);
359 EGG::Vector3f keyFramePos = m_transformKeyframes[idx].base(3);
361 mat.
setBase(3, posOffset + m_pos);
363 m_chainAttachMat = mat;
367void ObjectWanwan::calcSpeed() {
368 if (m_speed < 8.0f) {
370 m_accel.x = m_tangent.x * 0.5f;
371 m_accel.z = m_tangent.z * 0.5f;
376 m_vel.x = m_tangent.x * 8.0f;
377 m_vel.z = m_tangent.z * 8.0f;
382void ObjectWanwan::calcBounce() {
383 if (m_touchingFloor) {
385 m_accel += EGG::Vector3f::ey * 12.0f;
392void ObjectWanwan::calcTangent(f32 t) {
393 m_tangent = Interpolate(t, m_tangent, m_targetDir);
394 if (m_tangent.squaredLength() > std::numeric_limits<f32>::epsilon()) {
395 m_tangent.normalise2();
397 m_tangent = m_targetDir;
402void ObjectWanwan::calcUp(f32 t) {
403 m_up = Interpolate(t, m_up, m_targetUp);
404 if (m_up.squaredLength() > std::numeric_limits<f32>::epsilon()) {
407 m_up = EGG::Vector3f::ey;
412void ObjectWanwan::calcRandomTarget() {
413 f32 angle = System::RaceManager::Instance()->random().getF32(m_attackArc * 2.0f);
416 attackArcDir.y = 0.0f;
417 attackArcDir.normalise2();
418 EGG::Vector3f attackTargetDir = RotateXZByYaw((angle - m_attackArc) * DEG2RAD, attackArcDir);
419 m_target = m_anchor + attackTargetDir * m_attackDistance;
420 m_targetDir = m_target - m_pos;
421 m_targetDir.y = 0.0f;
422 m_targetDir.normalise2();
426void ObjectWanwan::initTransformKeyframes() {
427 std::array<f32, 15> xRot;
428 SampleHermiteInterp(0.0f, 10.0f, 2.0f, -1.111111f, std::span(xRot.begin(), 6));
429 SampleHermiteInterp(10.0f, -10.0f, -1.111111f, -1.111111f, std::span(xRot.begin() + 5, 5));
430 SampleHermiteInterp(-10.0f, 0.0f, -1.111111f, 2.0f, std::span(xRot.begin() + 9, 6));
432 std::array<f32, 15> yPos;
433 SampleHermiteInterp(0.0f, -27.35f, -9.1166658f, 3.75f, std::span(yPos.begin(), 4));
434 SampleHermiteInterp(-27.35f, 33.75f, 3.75f, 2.4863639f, std::span(yPos.begin() + 3, 7));
435 SampleHermiteInterp(33.75f, 0.0f, 2.4863639f, -6.75f, std::span(yPos.begin() + 9, 6));
437 std::array<f32, 15> zPos;
438 SampleHermiteInterp(0.0f, -33.75f, -6.75f, -4.8214278f, std::span(zPos.begin(), 6));
439 SampleHermiteInterp(-33.75f, -33.75f, -4.8214278f, 8.4375f, std::span(zPos.begin() + 5, 3));
440 SampleHermiteInterp(-33.75f, 0.0f, 8.4375f, 14.464999f, std::span(zPos.begin() + 7, 3));
441 SampleHermiteInterp(0.0f, 24.11f, 14.464999f, 0.0f, std::span(zPos.begin() + 9, 3));
442 SampleHermiteInterp(24.11f, 0.0f, 0.0f, -8.0366669f, std::span(zPos.begin() + 11, 4));
444 for (u8 i = 0; i < m_transformKeyframes.size(); ++i) {
448 m_transformKeyframes[i] = mat;
453void ObjectWanwan::calcAttackPos() {
454 constexpr f32 SCALED_CHAIN_LENGTH = CHAIN_LENGTH * SCALE;
456 if (m_currentStateId != 1) {
462 calcChainAttachPos(m_transform);
466 dist -= SCALED_CHAIN_LENGTH *
static_cast<f32
>(m_chainCount);
468 if (dist > 0.0f || m_attackStill) {
471 m_pos += dir * 35.0f;
472 m_flags.setBit(eFlags::Position);
478 pos -= mat.
base(2) * 250.0f * m_scale.x;
483 m_chainAttachPos = pos - backOffset + verticalOffset;
487void ObjectWanwan::SampleHermiteInterp(f32 start, f32 end, f32 startTangent, f32 endTangent,
488 std::span<f32> dst) {
492 f32 scalar = 1.0f /
static_cast<f32
>(dst.size() - 1);
494 for (u8 i = 1; i < dst.size() - 1; ++i) {
496 scalar *
static_cast<f32
>(i));
#define KCL_TYPE_FLOOR
0x20E80FFF - Any KCL that the player or items can drive/land on.
Matrix34f multiplyTo(const Matrix34f &rhs) const
Multiplies two matrices.
void setBase(size_t col, const Vector3f &base)
Sets one column of a matrix.
Vector3f base(size_t col) const
Get a particular column from a matrix.
void makeR(const Vector3f &r)
Sets 3x3 rotation matrix from a vector of Euler angles.
void makeRT(const Vector3f &r, const Vector3f &t)
Sets rotation-translation matrix.
Vector3f ps_multVector(const Vector3f &vec) const
Paired-singles impl. of multVector.
The highest level abstraction for a kart.
f32 Hermite(f32 p0, f32 m0, f32 p1, f32 m1, f32 t)
Evaluates a cubic Hermite curve at a given parameter t.
f32 normalise()
Normalizes the vector and returns the original length.
f32 length() const
The square root of the vector's dot product.
f32 squaredLength() const
The dot product between the vector and itself.