A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
ObjectCarTGE.cc
1#include "ObjectCarTGE.hh"
2
3#include "game/field/ObjectCollisionCylinder.hh"
4#include "game/field/ObjectDirector.hh"
5#include "game/field/RailManager.hh"
6#include "game/field/obj/ObjectHighwayManager.hh"
7
8#include "game/kart/KartObject.hh"
9
10#include "game/system/RaceConfig.hh"
11
12namespace Kinoko::Field {
13
15ObjectCarTGE::ObjectCarTGE(const System::MapdataGeoObj &params)
16 : ObjectCollidable(params), StateManager(this, STATE_ENTRIES), m_auxCollision(nullptr),
17 m_carName{}, m_mdlName{}, m_carType(CarType::Normal), m_dummyId(ObjectId::None),
18 m_scaledTangentDir(EGG::Vector3f::zero), m_currSpeed(0.0f), m_up(EGG::Vector3f::zero),
19 m_tangent(EGG::Vector3f::zero) {
20 u32 carVariant = static_cast<u32>(params.setting(3));
21 m_highwayVel = static_cast<f32>(params.setting(2));
22 m_localVel = static_cast<f32>(params.setting(1));
23
24 s16 pathId = params.pathId();
25
26 // The base game returns before the StateManager sets its state entries.
27 // Since we handle this in the template specialization's constructor in Kinoko,
28 // we need to reset the StateManager parameters back to their default values before returning.
29 if (pathId == -1) {
30 m_nextStateId = -1;
31 m_currentStateId = 0;
32 m_currentFrame = 0;
33
34 return;
35 }
36
37 // The base game preemptively calculates collision for all points along the rail.
38 RailManager::Instance()->rail(pathId)->checkSphereFull();
39
40 const auto *name = getName();
41
42 if (strcmp(name, "car_body") == 0) {
43 snprintf(m_carName, sizeof(m_carName), "%s", "K_car_body");
44 m_carType = CarType::Normal;
45 } else if (strcmp(name, "kart_truck") == 0) {
46 snprintf(m_carName, sizeof(m_carName), "%s", "K_truck");
47 m_carType = CarType::Truck;
48 } else if (strcmp(name, "K_bomb_car") == 0) {
49 PANIC("Bomb cars are not implemented!");
50 }
51
52 const auto *resourceName = getResources();
53
54 switch (carVariant) {
55 case 0: {
56 if (strcmp(resourceName, "K_truck") == 0) {
57 snprintf(m_mdlName, sizeof(m_mdlName), "%s_b", "K_truck");
58 } else if (strcmp(resourceName, "K_car_body") == 0) {
59 snprintf(m_mdlName, sizeof(m_mdlName), "%s_b", "K_car");
60 }
61 } break;
62 case 1: {
63 if (strcmp(resourceName, "K_truck") == 0) {
64 snprintf(m_mdlName, sizeof(m_mdlName), "%s_o", "K_truck");
65 } else if (strcmp(resourceName, "K_car_body") == 0) {
66 snprintf(m_mdlName, sizeof(m_mdlName), "%s_r", "K_car");
67 }
68 } break;
69 case 2: {
70 if (strcmp(resourceName, "K_truck") == 0) {
71 snprintf(m_mdlName, sizeof(m_mdlName), "%s_g", "K_truck");
72 } else if (strcmp(resourceName, "K_car_body") == 0) {
73 snprintf(m_mdlName, sizeof(m_mdlName), "%s_y", "K_car");
74 }
75 } break;
76 default: {
77 PANIC("Bomb cars are not implemented!");
78 break;
79 }
80 }
81
82 switch (m_carType) {
83 case CarType::Normal:
84 m_dummyId = ObjectDirector::Instance()->flowTable().getIdFromName("car_body_dummy");
85 break;
86 case CarType::Truck:
87 m_dummyId = ObjectDirector::Instance()->flowTable().getIdFromName("kart_truck_dummy");
88 break;
89 default:
90 PANIC("Bomb cars are not implemented!");
91 break;
92 }
93
94 if (System::RaceConfig::Instance()->raceScenario().course == Course::Moonview_Highway) {
95 registerManagedObject();
96 }
97}
98
100ObjectCarTGE::~ObjectCarTGE() {
101 EGG::egg_delete(m_auxCollision);
102}
103
105void ObjectCarTGE::init() {
106 constexpr f32 HIT_ANGLE_TRUCK = 20.0f;
107 constexpr f32 HIT_ANGLE_NORMAL = 40.0f;
108
109 ASSERT(m_mapObj);
110 if (m_mapObj->pathId() == -1) {
111 return;
112 }
113
114 u16 idx = m_mapObj->setting(0);
115 m_railInterpolator->init(0.0f, idx);
116
117 auto *rail = RailManager::Instance()->rail(m_mapObj->pathId());
118 u16 speedSetting = rail->points()[idx].setting[1];
119 if (speedSetting == 1) {
120 m_railInterpolator->setCurrVel(m_highwayVel);
121 } else if (speedSetting == 0) {
122 m_railInterpolator->setCurrVel(m_localVel);
123 }
124
125 u16 curPointSpeedSetting = m_railInterpolator->curPoint().setting[1];
126 u16 nextPointSpeedSetting = m_railInterpolator->nextPoint().setting[1];
127
128 if (curPointSpeedSetting == 0 && nextPointSpeedSetting == 1) {
129 m_nextStateId = 1;
130 } else if (curPointSpeedSetting == 1 && nextPointSpeedSetting == 0) {
131 m_nextStateId = 2;
132 }
133
134 m_squashed = false;
135 setPos(m_railInterpolator->curPos());
136 m_currSpeed = m_railInterpolator->speed();
137 m_scaledTangentDir = m_railInterpolator->curTangentDir() * m_currSpeed;
138 m_hitAngle = (m_carType == CarType::Truck) ? HIT_ANGLE_TRUCK : HIT_ANGLE_NORMAL;
139}
140
142void ObjectCarTGE::calc() {
143 StateManager::calc();
144
145 if (m_railInterpolator->calc() == RailInterpolator::Status::SegmentEnd) {
146 u16 curPointSpeedSetting = m_railInterpolator->curPoint().setting[1];
147 u16 nextPointSpeedSetting = m_railInterpolator->nextPoint().setting[1];
148
149 if (curPointSpeedSetting == 0 && nextPointSpeedSetting == 1) {
150 m_nextStateId = 1;
151 } else if (curPointSpeedSetting == 1 && nextPointSpeedSetting == 0) {
152 m_nextStateId = 2;
153 }
154 }
155
156 calcPos();
157
158 m_hasAuxCollision = false;
159}
160
162void ObjectCarTGE::createCollision() {
163 constexpr f32 TRUCK_RADIUS = 190.0f;
164 constexpr f32 TRUCK_HEIGHT = 500.0f;
165 constexpr f32 NORMAL_RADIUS = 150.0f;
166 constexpr f32 NORMAL_HEIGHT = 200.0f;
167
168 ASSERT(m_carType == CarType::Truck || m_carType == CarType::Normal);
169
170 bool isTruck = (m_carType == CarType::Truck);
171
172 ObjectCollidable::createCollision();
173
174 f32 radius = isTruck ? TRUCK_RADIUS : NORMAL_RADIUS;
175 f32 height = isTruck ? TRUCK_HEIGHT : NORMAL_HEIGHT;
176
177 m_auxCollision = EGG::egg_new<ObjectCollisionCylinder>(radius, height, collisionCenter());
178}
179
181void ObjectCarTGE::calcCollisionTransform() {
182 auto *col = collision();
183
184 if (!col) {
185 return;
186 }
187
188 calcTransform();
189 col->transform(transform(), scale(), m_scaledTangentDir);
190 calcTransform();
191 EGG::Matrix34f mat;
192 SetRotTangentHorizontal(mat, transform().base(2), EGG::Vector3f::ey);
193 calcTransform();
194 mat.setBase(3, transform().base(3));
195 m_auxCollision->transform(mat, scale(), m_scaledTangentDir);
196}
197
199f32 ObjectCarTGE::getCollisionRadius() const {
200 constexpr f32 NORMAL_RADIUS = 600.0f;
201 constexpr f32 TRUCK_RADIUS = 1100.0f;
202
203 ASSERT(m_carType == CarType::Truck || m_carType == CarType::Normal);
204 return (m_carType == CarType::Truck) ? TRUCK_RADIUS : NORMAL_RADIUS;
205}
206
208Kart::Reaction ObjectCarTGE::onCollision(Kart::KartObject *kartObj, Kart::Reaction reactionOnKart,
209 Kart::Reaction /*reactionOnObj*/, EGG::Vector3f &hitDepth) {
210 constexpr u32 SQUASH_INVULNERABILITY = 200;
211
212 if (!m_hasAuxCollision) {
213 EGG::Vector3f hitDepthNorm = hitDepth;
214 hitDepthNorm.normalise2();
215
216 if (hitDepthNorm.y > 0.9f) {
217 return Kart::Reaction::UntrickableJumpPad;
218 }
219 }
220
221 if (m_highwayMgr && m_highwayMgr->squashTimer() < SQUASH_INVULNERABILITY) {
222 const auto &hitTable = ObjectDirector::Instance()->hitTableKart();
223 reactionOnKart = hitTable.reaction(hitTable.slot(static_cast<ObjectId>(m_dummyId)));
224 }
225
226 // In the base game, behavior branches on reactionOnObj, but for time trials it's always 0.
227 if (reactionOnKart != Kart::Reaction::None && reactionOnKart != Kart::Reaction::WallAllSpeed) {
228 m_squashed = true;
229 calcTransform();
230 EGG::Vector3f v2 = transform().base(2);
231 v2.y = 0.0f;
232 v2.normalise2();
233 EGG::Vector3f posDelta = kartObj->pos() - pos();
234 posDelta.y = 0.0f;
235 posDelta.normalise2();
236
237 if (v2.dot(posDelta) < EGG::Mathf::CosFIdx(0.7111111f * m_hitAngle) && !m_hasAuxCollision) {
238 reactionOnKart = Kart::Reaction::LaunchSpin;
239 }
240
241 hitDepth.setZero();
242 }
243
244 return reactionOnKart;
245}
246
248bool ObjectCarTGE::checkCollision(ObjectCollisionBase *lhs, EGG::Vector3f &dist) {
249 dist = EGG::Vector3f::zero;
250 bool hasCol = lhs->check(*m_collision, dist);
251
252 if (!hasCol) {
253 hasCol = lhs->check(*m_auxCollision, dist);
254 m_hasAuxCollision = hasCol;
255 }
256
257 return hasCol;
258}
259
261const EGG::Vector3f &ObjectCarTGE::collisionCenter() const {
262 static constexpr EGG::Vector3f CENTER_TRUCK = EGG::Vector3f(0.0f, 300.0f, 0.0f);
263 static constexpr EGG::Vector3f CENTER_NORMAL = EGG::Vector3f(0.0f, 100.0f, 0.0f);
264 static constexpr EGG::Vector3f CENTER_DEFAULT = EGG::Vector3f(0.0f, 0.0f, 0.0f);
265
266 switch (m_carType) {
267 case CarType::Truck:
268 return CENTER_TRUCK;
269 case CarType::Normal:
270 return CENTER_NORMAL;
271 default:
272 return CENTER_DEFAULT;
273 }
274}
275
276void ObjectCarTGE::enterStateStub() {}
277
278void ObjectCarTGE::calcStateStub() {}
279
285 m_currSpeed += TOLL_BOOTH_ACCEL;
286
287 if (m_currSpeed > m_highwayVel) {
288 m_currSpeed = m_highwayVel;
289 m_nextStateId = 0;
290 }
291
292 m_railInterpolator->setCurrVel(m_currSpeed);
293 m_scaledTangentDir = m_railInterpolator->curTangentDir() * m_currSpeed;
294}
295
301 m_currSpeed -= TOLL_BOOTH_ACCEL;
302
303 if (m_currSpeed < m_localVel) {
304 m_currSpeed = m_localVel;
305 m_nextStateId = 0;
306 }
307
308 m_railInterpolator->setCurrVel(m_currSpeed);
309 m_scaledTangentDir = m_railInterpolator->curTangentDir() * m_currSpeed;
310}
311
313void ObjectCarTGE::calcPos() {
314 constexpr f32 NORMAL_SPEED = 1500.0f;
315 constexpr f32 TRUCK_SPEED = 1600.0f;
316
317 f32 speed = (m_carType == CarType::Truck) ? TRUCK_SPEED : NORMAL_SPEED;
318 f32 t = speed * scale().z * 0.5f;
319
320 EGG::Vector3f curDir;
321 EGG::Vector3f curTangentDir;
322 m_railInterpolator->evalCubicBezierOnPath(t, curDir, curTangentDir);
323
324 const EGG::Vector3f curPos = m_railInterpolator->curPos();
325 EGG::Vector3f posDelta = curPos - curDir;
326 posDelta.normalise2();
327 m_tangent += 0.1f * (posDelta - m_tangent);
328 m_tangent.y = posDelta.y;
329 m_tangent.normalise2();
330
331 if (m_tangent.y > -0.05f) {
332 setPos(curPos - m_tangent * t * 0.5f);
333 } else {
334 setPos(curPos - m_tangent * t * 0.5f - EGG::Vector3f::ey * 5.0f);
335 }
336
337 m_up = OrthonormalBasis(m_tangent).base(1);
338 setMatrixTangentTo(m_up, m_tangent);
339}
340
341} // namespace Kinoko::Field
f32 m_localVel
Speed while off the highway.
f32 m_highwayVel
Speed while on the highway.
void calcState1()
The state when cars are speeding up.
void calcState2()
The state when cars are slowing down.
Base class that represents different "states" for an object.
Pertains to collision.
A 3D float vector.
Definition Vector.hh:107