A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
ObjectDirector.cc
1#include "ObjectDirector.hh"
2
3#include "game/field/BoxColManager.hh"
4#include "game/field/ObjectDrivableDirector.hh"
5#include "game/field/obj/ObjectRegistry.hh"
6
7#include "game/kart/KartObject.hh"
8#include "game/kart/Reaction.hh"
9
10#include "game/system/CourseMap.hh"
11#include "game/system/RaceConfig.hh"
12
13namespace Kinoko::Field {
14
16void ObjectDirector::init() {
17 for (auto *&obj : m_objects) {
18 obj->init();
19 obj->calcModel();
20 }
21
22 ObjectDrivableDirector::Instance()->init();
23
24 // As a Kinoko performance optimization, we can skip updating seagull objects if there are no
25 // ObjectDrivable or ObjectKCL objects registered to ObjectDrivableDirector, since seagulls are
26 // only implemented in order to properly update drivable object transformation matrices.
27 if (!m_calcObjects.empty() && !ObjectDrivableDirector::Instance()->hasCalcObjects()) {
28 auto *newEnd = std::remove_if(m_calcObjects.begin(), m_calcObjects.end(),
29 [](const ObjectBase *obj) { return obj->id() == ObjectId::Seagull; });
30
31 while (m_calcObjects.end() != newEnd) {
32 m_calcObjects.pop_back();
33 }
34 }
35}
36
38void ObjectDirector::calc() {
39 for (auto *&obj : m_calcObjects) {
40 obj->calc();
41 }
42
43 for (auto *&obj : m_calcObjects) {
44 obj->calcModel();
45 }
46
47 ObjectDrivableDirector::Instance()->calc();
48}
49
51void ObjectDirector::addObject(ObjectCollidable *obj) {
52 if (obj->loadFlags().onBit(ObjectBase::eLoadFlags::Calc)) {
53 m_calcObjects.push_back(obj);
54 }
55
56 const auto *set = m_flowTable.set(m_flowTable.slot(obj->id()));
57
58 // In the base game, it's possible an object here will access slot -1 (e.g. Moonview Highway
59 // cars). We add a nullptr check here to prevent this.
60 if (set && set->mode != 0) {
61 if (obj->collision()) {
62 m_collisionObjects.push_back(obj);
63 }
64 }
65
66 m_objects.push_back(obj);
67}
68
69void ObjectDirector::addObjectNoImpl(ObjectBase *obj) {
70 m_objects.push_back(obj);
71}
72
74void ObjectDirector::addManagedObject(ObjectCollidable *obj) {
75 m_managedObjects.push_back(obj);
76}
77
79size_t ObjectDirector::checkKartObjectCollision(Kart::KartObject *kartObj,
80 ObjectCollisionConvexHull *convexHull) {
81 size_t count = 0;
82
83 while (ObjectCollidable *obj = BoxColManager::Instance()->getNextObject()) {
84 auto *objCollision = obj->collision();
85 if (!objCollision) {
86 continue;
87 }
88
89 obj->calcCollisionTransform();
90 if (!obj->checkCollision(convexHull, m_hitDepths[count])) {
91 continue;
92 }
93
94 // We have a collision, process it
95 // Assume that we are not in a star, mega, or bullet
96 Kart::Reaction reactionOnKart = m_hitTableKart.reaction(m_hitTableKart.slot(obj->id()));
97 Kart::Reaction reactionOnObj =
98 m_hitTableKartObject.reaction(m_hitTableKartObject.slot(obj->id()));
99
100 // The object might change the reaction states
101 obj->processKartReactions(kartObj, reactionOnKart, reactionOnObj);
102
103 Kart::Reaction reaction =
104 obj->onCollision(kartObj, reactionOnKart, reactionOnObj, m_hitDepths[count]);
105 m_reactions[count] = reaction;
106
107 if (reaction == Kart::Reaction::WallAllSpeed || reaction == Kart::Reaction::WallSpark) {
108 obj->onWallCollision(kartObj, m_hitDepths[count]);
109 } else {
110 obj->onObjectCollision(kartObj);
111 }
112
113 m_collidingObjects[count] = obj;
114 if (m_hitDepths[count].y < 0.0f) {
115 m_hitDepths[count].y = 0.0f;
116 }
117
118 ++count;
119 }
120
121 return count;
122}
123
125f32 ObjectDirector::distAboveRisingWater(f32 offset) const {
126 ASSERT(m_psea);
127 return offset - m_psea->pos().y;
128}
129
131f32 ObjectDirector::risingWaterKillPlaneHeight() const {
132 ASSERT(m_psea);
133 return m_psea->pos().y - 260.0f;
134}
135
137ObjectDirector *ObjectDirector::CreateInstance() {
138 ASSERT(!s_instance);
139 s_instance = EGG::egg_new<ObjectDirector>();
140
141 ObjectDrivableDirector::CreateInstance();
142
143 s_instance->createObjects();
144
145 return s_instance;
146}
147
149void ObjectDirector::DestroyInstance() {
150 ASSERT(s_instance);
151 auto *instance = s_instance;
152 s_instance = nullptr;
153 EGG::egg_delete(instance);
154
155 ObjectDrivableDirector::DestroyInstance();
156}
157
159ObjectDirector::ObjectDirector()
160 : m_flowTable("ObjFlow.bin"), m_hitTableKart("GeoHitTableKart.bin"),
161 m_hitTableKartObject("GeoHitTableKartObj.bin"), m_objects(MAX_UNIT_COUNT),
162 m_calcObjects(MAX_UNIT_COUNT), m_collisionObjects(MAX_UNIT_COUNT), m_psea(nullptr),
163 m_managedObjects(MAX_MANAGED_OBJECTS) {}
164
166ObjectDirector::~ObjectDirector() {
167 if (s_instance) {
168 s_instance = nullptr;
169 WARN("ObjectDirector instance not explicitly handled!");
170 }
171
172 for (auto *&obj : m_objects) {
173 EGG::egg_delete(obj);
174 }
175}
176
178void ObjectDirector::createObjects() {
179 const auto *courseMap = System::CourseMap::Instance();
180 size_t objectCount = courseMap->getGeoObjCount();
181
182 auto *objDrivableDir = ObjectDrivableDirector::Instance();
183 auto course = System::RaceConfig::Instance()->raceScenario().course;
184 bool rGV2 = course == Course::SNES_Ghost_Valley_2;
185 bool sun = false;
186
187 // The max pitch of the Chain Chomp only needs to be set once,
188 // so we set it here instead of in the Chain Chomp constructor.
189 s_wanwanMaxPitch = course == Course::GCN_Mario_Circuit ? -20.0f : -30.0f;
190
191 for (size_t i = 0; i < objectCount; ++i) {
192 const auto *pObj = courseMap->getGeoObj(i);
193 ASSERT(pObj);
194
195 // Assume one player - if the presence flag isn't set, don't construct it
196 if (!(pObj->presenceFlag() & 1)) {
197 continue;
198 }
199
200 // Prevent construction of objects with disabled or no collision
201 if (IsObjectBlacklisted(pObj->id())) {
202 continue;
203 }
204
205 // rGV2's blocks are created outside of the factory function
206 if (rGV2) {
207 switch (static_cast<ObjectId>(pObj->id())) {
208 case ObjectId::ObakeBlockSFCc:
209 case ObjectId::ObakeBlock2SFCc:
210 case ObjectId::ObakeBlock3SFCc: {
211 // Create the manager if this is the first block.
212 auto *obakeManager = objDrivableDir->obakeManager();
213 if (!obakeManager) {
214 objDrivableDir->createObakeManager(*pObj);
215 } else {
216 obakeManager->addBlock(*pObj);
217 }
218 } break;
219 default:
220 break;
221 }
222 } else {
223 ObjectBase *object = createObject(*pObj);
224 object->load();
225 }
226
227 if (static_cast<ObjectId>(pObj->id()) == ObjectId::SunDS) {
228 sun = true;
229 }
230 }
231
232 if (course == Course::Moonview_Highway) {
233 auto *highwayMgr = EGG::egg_new<ObjectHighwayManager>();
234 highwayMgr->load();
235 }
236
237 if (sun) {
238 auto *sunMgr = EGG::egg_new<ObjectSunManager>();
239 sunMgr->load();
240 }
241
242 if (course == Course::GBA_Shy_Guy_Beach) {
243 auto *shipMgr = EGG::egg_new<ObjectHeyhoShipManager>();
244 shipMgr->load();
245 }
246}
247
249ObjectBase *ObjectDirector::createObject(const System::MapdataGeoObj &params) {
250 ObjectId id = static_cast<ObjectId>(params.id());
251 switch (id) {
252 case ObjectId::Psea:
253 return EGG::egg_new<ObjectPsea>(params);
254 case ObjectId::Woodbox:
255 return EGG::egg_new<ObjectWoodbox>(params);
256 case ObjectId::WLWallGC:
257 return EGG::egg_new<ObjectWLWallGC>(params);
258 case ObjectId::CarA1:
259 case ObjectId::CarA2:
260 case ObjectId::CarA3:
261 return EGG::egg_new<ObjectCarA>(params);
262 case ObjectId::Basabasa:
263 return EGG::egg_new<ObjectBasabasa>(params);
264 case ObjectId::HeyhoShipGBA:
265 return EGG::egg_new<ObjectHeyhoShip>(params);
266 case ObjectId::KartTruck:
267 case ObjectId::CarBody:
268 return EGG::egg_new<ObjectCarTGE>(params);
269 case ObjectId::KoopaBall:
270 return EGG::egg_new<ObjectKoopaBall>(params);
271 case ObjectId::W_Woodbox:
272 return EGG::egg_new<ObjectWoodboxW>(params);
273 case ObjectId::SunDS:
274 return EGG::egg_new<ObjectSunDS>(params);
275 case ObjectId::ItemboxLine:
276 return EGG::egg_new<ObjectItemboxLine>(params);
277 case ObjectId::VolcanoBall:
278 return EGG::egg_new<ObjectVolcanoBallLauncher>(params);
279 case ObjectId::PenguinS:
280 return EGG::egg_new<ObjectPenguinS>(params);
281 case ObjectId::PenguinM:
282 return EGG::egg_new<ObjectPenguin>(params);
283 case ObjectId::Dossunc:
284 return EGG::egg_new<ObjectDossunc>(params);
285 case ObjectId::Boble:
286 return EGG::egg_new<ObjectBoble>(params);
287 case ObjectId::Hanachan:
288 return EGG::egg_new<ObjectHanachan>(params);
289 case ObjectId::Seagull:
290 return EGG::egg_new<ObjectBird>(params);
291 case ObjectId::Crab:
292 return EGG::egg_new<ObjectCrab>(params);
293 case ObjectId::Hwanwan:
294 return EGG::egg_new<ObjectHwanwanManager>(params);
295 case ObjectId::HeyhoBallGBA:
296 return EGG::egg_new<ObjectHeyhoBall>(params);
297 case ObjectId::DokanSFC:
298 return EGG::egg_new<ObjectDokan>(params);
299 case ObjectId::Pylon:
300 return EGG::egg_new<ObjectPylon>(params);
301 case ObjectId::OilSFC:
302 return EGG::egg_new<ObjectOilSFC>(params);
303 case ObjectId::ParasolR:
304 return EGG::egg_new<ObjectParasolR>(params);
305 case ObjectId::KoopaFigure64:
306 return EGG::egg_new<ObjectKoopaFigure64>(params);
307 case ObjectId::Kuribo:
308 return EGG::egg_new<ObjectKuribo>(params);
309 case ObjectId::Choropu:
310 case ObjectId::Choropu2:
311 return EGG::egg_new<ObjectChoropu>(params);
312 case ObjectId::Cow:
313 return EGG::egg_new<ObjectCowHerd>(params);
314 case ObjectId::PakkunF:
315 return EGG::egg_new<ObjectPakkunF>(params);
316 case ObjectId::WLFirebarGC:
317 case ObjectId::KoopaFirebar:
318 return EGG::egg_new<ObjectFirebar>(params);
319 case ObjectId::Wanwan:
320 return EGG::egg_new<ObjectWanwan>(params);
321 case ObjectId::Poihana:
322 return EGG::egg_new<ObjectPoihana>(params);
323 case ObjectId::Propeller:
324 return EGG::egg_new<ObjectPropeller>(params);
325 case ObjectId::DKRockGC:
326 return EGG::egg_new<ObjectRock>(params);
327 case ObjectId::Sanbo:
328 return EGG::egg_new<ObjectSanbo>(params);
329 case ObjectId::TruckWagon:
330 return EGG::egg_new<ObjectTruckWagon>(params);
331 case ObjectId::Heyho:
332 return EGG::egg_new<ObjectHeyho>(params);
333 case ObjectId::Press:
334 return EGG::egg_new<ObjectPress>(params);
335 case ObjectId::WLFireRingGC:
336 return EGG::egg_new<ObjectFireRing>(params);
337 case ObjectId::FireSnake:
338 return EGG::egg_new<ObjectFireSnake>(params);
339 case ObjectId::FireSnakeV:
340 return EGG::egg_new<ObjectFireSnakeV>(params);
341 case ObjectId::PuchiPakkun:
342 return EGG::egg_new<ObjectPuchiPakkun>(params);
343 case ObjectId::KinokoUd:
344 return EGG::egg_new<ObjectKinokoUd>(params);
345 case ObjectId::KinokoBend:
346 return EGG::egg_new<ObjectKinokoBend>(params);
347 case ObjectId::VolcanoRock:
348 return EGG::egg_new<ObjectVolcanoRock>(params);
349 case ObjectId::BulldozerL:
350 case ObjectId::BulldozerR:
351 return EGG::egg_new<ObjectBulldozer>(params);
352 case ObjectId::KinokoNm:
353 return EGG::egg_new<ObjectKinokoNm>(params);
354 case ObjectId::Crane:
355 return EGG::egg_new<ObjectCrane>(params);
356 case ObjectId::VolcanoPiece:
357 return EGG::egg_new<ObjectVolcanoPiece>(params);
358 case ObjectId::FlamePole:
359 return EGG::egg_new<ObjectFlamePoleFoot>(params);
360 case ObjectId::TwistedWay:
361 return EGG::egg_new<ObjectTwistedWay>(params);
362 case ObjectId::TownBridge:
363 return EGG::egg_new<ObjectTownBridge>(params);
364 case ObjectId::DKShip64:
365 return EGG::egg_new<ObjectShip64>(params);
366 case ObjectId::Turibashi:
367 return EGG::egg_new<ObjectTuribashi>(params);
368 case ObjectId::Aurora:
369 return EGG::egg_new<ObjectAurora>(params);
370 case ObjectId::DCPillar:
371 return EGG::egg_new<ObjectPillar>(params);
372 case ObjectId::Sandcone:
373 return EGG::egg_new<ObjectSandcone>(params);
374 case ObjectId::FlamePoleV:
375 case ObjectId::FlamePoleVBig:
376 return EGG::egg_new<ObjectFlamePoleV>(params);
377 case ObjectId::Ami:
378 return EGG::egg_new<ObjectAmi>(params);
379 case ObjectId::BeltEasy:
380 return EGG::egg_new<ObjectBeltEasy>(params);
381 case ObjectId::BeltCrossing:
382 return EGG::egg_new<ObjectBeltCrossing>(params);
383 case ObjectId::BeltCurveA:
384 return EGG::egg_new<ObjectBeltCurveA>(params);
385 case ObjectId::Escalator:
386 return EGG::egg_new<ObjectEscalator>(params);
387 case ObjectId::EscalatorGroup:
388 return EGG::egg_new<ObjectEscalatorGroup>(params);
389
390 // Non-specified objects are stock collidable objects by default
391 // However, we need to specify an impl, so we don't use default
392 case ObjectId::DummyPole:
393 case ObjectId::CastleTree1c:
394 case ObjectId::MarioTreeGCc:
395 case ObjectId::PeachTreeGCc:
396 case ObjectId::MarioGo64c:
397 case ObjectId::KinokoT1:
398 case ObjectId::PalmTree:
399 case ObjectId::Parasol:
400 case ObjectId::HeyhoTreeGBAc:
401 case ObjectId::GardenTreeDSc:
402 case ObjectId::DKtreeA64c:
403 case ObjectId::DKTreeB64c:
404 case ObjectId::TownTreeDsc:
405 case ObjectId::PakkunDokan:
406 return EGG::egg_new<ObjectCollidable>(params);
407 case ObjectId::WLDokanGC:
408 case ObjectId::Mdush:
409 return EGG::egg_new<ObjectKCL>(params);
410 default:
411 return EGG::egg_new<ObjectNoImpl>(params);
412 }
413}
414
415f32 ObjectDirector::s_wanwanMaxPitch;
416
417ObjectDirector *ObjectDirector::s_instance = nullptr;
418
419} // namespace Kinoko::Field
@ Calc
The state of the object should be recalculated every frame.
Definition ObjectBase.hh:30
Smallest convex shape that encloses a given set of points.
fixed_vector< ObjectBase * > m_objects
All objects live here.
fixed_vector< ObjectBase * > m_collisionObjects
Objects having collision live here too.
std::array< ObjectCollidable *, MAX_UNIT_COUNT > m_collidingObjects
Objects we are currently colliding with.
fixed_vector< ObjectBase * > m_calcObjects
Objects needing calc() live here too.
Pertains to collision.
Reaction
Represents the different reactions that can occur when a kart collides with an object.
Definition Reaction.hh:8
@ WallSpark
The kart is colliding with Wall3.
Definition Reaction.hh:32
@ WallAllSpeed
The kart collides with a wall and is knocked back.
Definition Reaction.hh:14