A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
ObjectFireSnake.cc
1#include "ObjectFireSnake.hh"
2
3#include "game/field/CollisionDirector.hh"
4#include "game/field/ObjectDirector.hh"
5
6#include "game/system/RaceManager.hh"
7
8namespace Field {
9
11ObjectFireSnakeKid::ObjectFireSnakeKid(const System::MapdataGeoObj &params)
12 : ObjectCollidable(params) {}
13
15ObjectFireSnakeKid::~ObjectFireSnakeKid() = default;
16
18ObjectFireSnake::ObjectFireSnake(const System::MapdataGeoObj &params)
19 : ObjectProjectile(params), StateManager(this), m_maxAge(static_cast<s16>(params.setting(1))),
20 m_initialPos(params.pos()) {
21 if (ObjectDirector::Instance()->managedObjects().size() > 0) {
22 registerManagedObject();
23 }
24
25 for (u32 i = 0; i < 2; ++i) {
26 auto *&kid = m_kids[i];
27 kid = new ObjectFireSnakeKid(params);
28 kid->load();
29 f32 scale = (0.75f - 0.25f * static_cast<f32>(i)) * m_scale.y;
30 kid->setScale(EGG::Vector3f(scale, scale, scale));
31 }
32
33 m_prevTransforms.fill(EGG::Matrix34f::ident);
34}
35
37ObjectFireSnake::~ObjectFireSnake() = default;
38
40void ObjectFireSnake::init() {
41 m_nextStateId = 0;
42 enterDespawned();
43
44 m_sunPos.setZero();
45
46 calcTransform();
47 m_initRot = m_transform.base(0);
48 m_bounceDir = m_transform.base(0);
49}
50
52void ObjectFireSnake::calc() {
53 StateManager::calc();
54
55 if (isCollisionEnabled()) {
56 if (++m_age >= m_maxAge && m_currentStateId == 3) {
57 m_nextStateId = 0;
58 }
59 }
60
61 calcChildren();
62}
63
65void ObjectFireSnake::initProjectile(const EGG::Vector3f &pos) {
66 m_sunPos = pos;
67
68 m_xzSunDist = m_initialPos - m_sunPos;
69 m_xzSunDist.y = 0.0f;
70 m_xzSunDist.normalise2();
71
72 EGG::Vector3f xzDist = m_sunPos - m_initialPos;
73 xzDist.y = 0.0f;
74 f32 dist = xzDist.length();
75
76 m_fallAxis = RotateXZByYaw(F_PI / 2.0f, m_xzSunDist);
77 m_xzSpeed = dist * EGG::Mathf::sqrt(GRAVITY * 0.5f / (m_sunPos.y - m_initialPos.y));
78 m_fallDuration = static_cast<u16>(dist / m_xzSpeed);
79}
80
82void ObjectFireSnake::onLaunch() {
83 m_nextStateId = 1;
84}
85
87void ObjectFireSnake::enterDespawned() {
88 if (getUnit()) {
89 unregisterCollision();
90 }
91
92 m_visualPos = m_sunPos;
93 m_pos = m_sunPos;
94 m_flags.setBit(eFlags::Position);
95}
96
98void ObjectFireSnake::enterFalling() {
99 if (!getUnit()) {
100 loadAABB(0.0f);
101 }
102}
103
105void ObjectFireSnake::enterHighBounce() {
106 m_visualPos = m_initialPos;
107 m_pos = m_initialPos;
108 m_flags.setBit(eFlags::Position);
109
110 f32 rand = System::RaceManager::Instance()->random().getF32();
111 m_bounceDir = rand >= 0.5f ? m_initRot : -m_initRot;
112
113 m_age = 0;
114}
115
117void ObjectFireSnake::enterRest() {
118 constexpr f32 XZ_RANGE = 1000.0f * 1000.0f;
119
120 // If it strays too far from original position, start bouncing the opposite direction
121 if (m_visualPos.sqDistance(m_initialPos) >= XZ_RANGE) {
122 m_bounceDir = m_initialPos - m_visualPos;
123 m_bounceDir.normalise2();
124 } else {
125 // 45 degree random fluctuation, with a 50% chance to flip direction
126 auto &rand = System::RaceManager::Instance()->random();
127 f32 angle = rand.getF32(90.0f) - 45.0f;
128
129 if (rand.getF32() >= 0.5f) {
130 angle += 180.0f;
131 }
132
133 m_bounceDir = RotateXZByYaw(angle * DEG2RAD, m_initRot);
134 }
135}
136
138void ObjectFireSnake::calcFalling() {
139 EGG::Vector2f fallVel;
140 fallVel.y = -GRAVITY * static_cast<f32>(m_currentFrame);
141 f32 fallVelY = fallVel.y;
142 f32 posY = m_visualPos.y + fallVel.y;
143
144 if (posY <= m_initialPos.y) {
145 m_nextStateId = 2;
146 m_pos = m_visualPos * 0.5f + m_initialPos * 0.5f;
147 m_flags.setBit(eFlags::Position);
148 } else {
149 fallVel.x = m_xzSpeed;
150 m_visualPos.y = posY;
151 m_visualPos.x += m_xzSunDist.x * fallVel.x;
152 m_visualPos.z += m_xzSunDist.z * fallVel.x;
153 fallVel.normalise2();
154
155 f32 angle = fallVel.x;
156 EGG::Vector3f up = RotateAxisAngle(EGG::Mathf::acos(angle), m_fallAxis, EGG::Vector3f::ey);
157 auto axis = EGG::Vector3f(m_xzSunDist.x * m_xzSpeed, -fallVelY, m_xzSunDist.z * m_xzSpeed);
158 axis.normalise2();
159 auto spiral = RotateAxisAngle(50.0f * static_cast<f32>(m_currentFrame) * DEG2RAD, axis, up);
160
161 f32 scaledDuration = 0.9f * static_cast<f32>(m_fallDuration);
162 f32 phase = m_currentFrame - static_cast<u16>(scaledDuration);
163 f32 spiralRadius = phase < 0 ? 200.0f : 200.0f - (200.0f / scaledDuration) * phase;
164
165 m_pos = m_visualPos + spiral * spiralRadius;
166 m_flags.setBit(eFlags::Position);
167 }
168}
169
171void ObjectFireSnake::calcHighBounce() {
172 constexpr f32 INITIAL_VELOCITY = 1.6f * 60.0f;
173
174 calcBounce(INITIAL_VELOCITY);
175}
176
178void ObjectFireSnake::calcRest() {
179 if (m_currentFrame >= 30) {
180 m_nextStateId = 4;
181 }
182
183 calcTransform();
184
185 EGG::Vector3f tangent = Interpolate(0.3f, m_transform.base(2), m_bounceDir);
186 tangent.normalise2();
187
188 setMatrixTangentTo(EGG::Vector3f::ey, tangent);
189}
190
192void ObjectFireSnake::calcBounce() {
193 constexpr f32 INITIAL_VELOCITY = 60.0f;
194
195 calcBounce(INITIAL_VELOCITY);
196}
197
199void ObjectFireSnake::calcChildren() {
200 // Shift all matrices up by one index
201 for (size_t i = m_prevTransforms.size() - 1; i > 0; --i) {
202 m_prevTransforms[i] = m_prevTransforms[i - 1];
203 }
204
205 calcTransform();
206 m_prevTransforms[0] = m_transform;
207 m_prevTransforms[0].setBase(3, m_pos);
208
209 u32 idx = 10;
210 for (auto *&kid : m_kids) {
211 if (m_currentStateId == 1 && idx == m_currentFrame) {
212 if (!kid->getUnit()) {
213 kid->loadAABB(0.0f);
214 }
215 } else if (m_currentStateId == 0 && m_currentFrame == 0) {
216 if (kid->getUnit()) {
217 kid->unregisterCollision();
218 }
219 }
220
221 if (m_currentStateId == 1 && m_currentFrame < idx) {
222 calcTransform();
223 kid->setTransform(m_transform);
224 } else {
225 kid->setTransform(m_prevTransforms[idx]);
226 }
227
228 idx += 10;
229 }
230}
231
233void ObjectFireSnake::calcBounce(f32 initialVel) {
234 // Collision checks only occur after 10 frames in the bounce state.
235 constexpr u32 BOUNCE_COL_CHECK_DELAY = 10;
236 constexpr f32 RADIUS = 130.0f;
237 constexpr f32 BOUNCE_SPEED = 20.0f;
238
239 m_visualPos.x += m_bounceDir.x * BOUNCE_SPEED;
240 m_visualPos.z += m_bounceDir.z * BOUNCE_SPEED;
241 m_visualPos.y += initialVel - GRAVITY * static_cast<f32>(m_currentFrame);
242
243 CollisionInfo info;
244
245 if (m_currentFrame > BOUNCE_COL_CHECK_DELAY) {
246 bool hasCol = CollisionDirector::Instance()->checkSphereFull(RADIUS, m_visualPos,
247 EGG::Vector3f::inf, KCL_TYPE_64EBDFFF, &info, nullptr, 0);
248
249 if (hasCol) {
250 m_visualPos += info.tangentOff;
251 m_nextStateId = 3;
252 }
253 }
254
255 m_pos = m_visualPos;
256 m_flags.setBit(eFlags::Position);
257}
258
259} // namespace Field
Pertains to collision.
A 2D float vector.
Definition Vector.hh:12
A 3D float vector.
Definition Vector.hh:88
f32 length() const
The square root of the vector's dot product.
Definition Vector.hh:192