A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
CourseMap.cc
1#include "CourseMap.hh"
2
3#include "game/system/ResourceManager.hh"
4
5namespace System {
6
8void CourseMap::init() {
9 void *buffer = LoadFile("course.kmp");
10 m_course =
11 new MapdataFileAccessor(reinterpret_cast<const MapdataFileAccessor::SData *>(buffer));
12
13 constexpr u32 AREA_SIGNATURE = 0x41524541;
14 constexpr u32 CANNON_POINT_SIGNATURE = 0x434e5054;
15 constexpr u32 CHECK_PATH_SIGNATURE = 0x434b5048;
16 constexpr u32 CHECK_POINT_SIGNATURE = 0x434b5054;
17 constexpr u32 GEO_OBJ_SIGNATURE = 0x474f424a;
18 constexpr u32 JUGEM_POINT_SIGNATURE = 0x4a475054;
19 constexpr u32 START_POINT_SIGNATURE = 0x4b545054;
20 constexpr u32 POINT_INFO_SIGNATURE = 0x504f5449;
21 constexpr u32 STAGE_INFO_SIGNATURE = 0x53544749;
22
23 m_startPoint = parseMapdata<MapdataStartPointAccessor>(START_POINT_SIGNATURE);
24 m_checkPath = parseMapdata<MapdataCheckPathAccessor>(CHECK_PATH_SIGNATURE);
25 m_checkPoint = parseMapdata<MapdataCheckPointAccessor>(CHECK_POINT_SIGNATURE);
26 m_geoObj = parseMapdata<MapdataGeoObjAccessor>(GEO_OBJ_SIGNATURE);
27 m_pointInfo = parseMapdata<MapdataPointInfoAccessor>(POINT_INFO_SIGNATURE);
28 m_area = parseMapdata<MapdataAreaAccessor>(AREA_SIGNATURE);
29 m_jugemPoint = parseMapdata<MapdataJugemPointAccessor>(JUGEM_POINT_SIGNATURE);
30 m_cannonPoint = parseMapdata<MapdataCannonPointAccessor>(CANNON_POINT_SIGNATURE);
31 m_stageInfo = parseMapdata<MapdataStageInfoAccessor>(STAGE_INFO_SIGNATURE);
32
33 ASSERT(m_area);
34 m_area->sort();
35
36 MapdataStageInfo *stageInfo = getStageInfo();
37 constexpr u8 TRANSLATION_MODE_NARROW = 1;
38 if (stageInfo && stageInfo->translationMode() == TRANSLATION_MODE_NARROW) {
39 m_startTmpAngle = 25.0f;
40 m_startTmp2 = 250.0f;
41 m_startTmp3 = 0.0f;
42 } else {
43 m_startTmpAngle = 30.0f;
44 m_startTmp2 = 400.0f;
45 m_startTmp3 = 100.0f;
46 }
47
48 m_startTmp0 = 800.0f;
49 m_startTmp1 = 1200.0f;
50}
51
53s16 CourseMap::findSector(const EGG::Vector3f &pos, u16 checkpointIdx, f32 &distanceRatio) {
54 clearSectorChecked();
55
56 MapdataCheckPoint *checkpoint = getCheckPoint(checkpointIdx);
57 s16 id = -1;
58
60 checkpoint->checkSectorAndDistanceRatio(pos, distanceRatio);
61 checkpoint->setSearched();
62
63 switch (occupancy) {
64 // The player is fully inside the current checkpoint, so just set to current checkpoint
66 id = checkpoint->id();
67 break;
68
69 // The player is between the sides of the quad, but NOT between this checkpoint and
70 // next; player is likely in the same checkpoint group
72 id = findSectorBetweenSides(pos, checkpoint, distanceRatio);
73 break;
74
75 // The player is not between the sides of the quad (may still be between this checkpoint
76 // and next); player is likely in a different checkpoint group
78 id = findSectorOutsideSector(pos, checkpoint, distanceRatio);
79 break;
80
81 default:
82 break;
83 }
84
85 return id > -1 ? id : findSectorRegional(pos, checkpoint, distanceRatio);
86}
87
89s16 CourseMap::findRecursiveSector(const EGG::Vector3f &pos, s16 depth, bool searchBackwardsFirst,
90 MapdataCheckPoint *checkpoint, f32 &distanceRatio, bool playerIsForwards) const {
91 constexpr s16 MAX_DEPTH = 6;
92
93 if (depth >= 0 && depth > MAX_DEPTH) {
94 return -1;
95 }
96
99
100 if (!checkpoint->searched()) {
101 completion = checkpoint->checkSectorAndDistanceRatio(pos, distanceRatio);
102 checkpoint->setSearched();
103 }
104
105 // If player is inside current checkpoint, stop searching
107 return checkpoint->id();
108 }
109
110 // Search type 0: Search forwards first, then backwards
111 if (!searchBackwardsFirst) {
112 // If "player is forwards" but completion < 0, force completion to 0 and return
113 // current checkpoint (GHOST CHECKPOINT!)
114 if (playerIsForwards && completion == MapdataCheckPoint::SectorOccupancy::BetweenSides &&
115 distanceRatio < 0.0f) {
116 distanceRatio = 0.0f;
117 return checkpoint->id();
118 }
119
120 // Stop if current checkpoint is a KCP
121 if (checkpoint->checkArea() >= 0) {
122 return -1;
123 }
124
125 // If player is between the sides of the quad but NOT between this checkpoint and next, AND
126 // completion > 0, then "player is forwards"
127 bool forward = completion == MapdataCheckPoint::SectorOccupancy::BetweenSides &&
128 distanceRatio > 0.0f;
129
130 // Search forwards, including checkpoints already searched
131 s16 id = searchNextCheckpoint(pos, depth, checkpoint, distanceRatio, forward, false);
132
133 // If that fails, search backwards, excluding checkpoints already searched
134 return id == -1 ?
135 searchPrevCheckpoint(pos, depth, checkpoint, distanceRatio, forward, true) :
136 id;
137 }
138
139 // Search type 1: Search backwards first, then forwards
140
141 // If "player is backwards" flag is true but completion > 1, force completion to 1 and return
142 // current checkpoint (GHOST CHECKPOINT!)
143 if (playerIsForwards && completion == MapdataCheckPoint::SectorOccupancy::BetweenSides &&
144 distanceRatio > 1.0f) {
145 distanceRatio = 1.0f;
146 return checkpoint->id();
147 }
148
149 // Stop if current checkpoint is a KCP (skipped for online players, but they aren't supported)
150 if (checkpoint->checkArea() >= 0) {
151 return -1;
152 }
153
154 // If player is between the sides of the quad but NOT between this checkpoint and next, AND
155 // completion < 0, then set "player is backwards" flag
156 bool forward =
157 completion == MapdataCheckPoint::SectorOccupancy::BetweenSides && distanceRatio < 0.0f;
158
159 // Search backwards, including checkpoints already searched
160 s16 id = searchPrevCheckpoint(pos, depth, checkpoint, distanceRatio, forward, false);
161
162 // If that fails, search forwards, excluding checkpoints already searched
163 return id == -1 ? searchNextCheckpoint(pos, depth, checkpoint, distanceRatio, forward, true) :
164 id;
165}
166
168u16 CourseMap::getCheckPointEntryOffsetMs(u16 i, const EGG::Vector3f &pos,
169 const EGG::Vector3f &prevPos) const {
170 EGG::Vector2f prevPos_ = EGG::Vector2f(prevPos.x, prevPos.z);
171 EGG::Vector2f pos_ = EGG::Vector2f(pos.x, pos.z);
172
173 MapdataCheckPoint *checkPoint = getCheckPoint(i);
174 ASSERT(checkPoint);
175 return checkPoint->getEntryOffsetMs(prevPos_, pos_);
176}
177
178f32 CourseMap::getCheckPointEntryOffsetExact(u16 i, const EGG::Vector3f &pos,
179 const EGG::Vector3f &prevPos) const {
180 EGG::Vector2f prevPos_ = EGG::Vector2f(prevPos.x, prevPos.z);
181 EGG::Vector2f pos_ = EGG::Vector2f(pos.x, pos.z);
182
183 MapdataCheckPoint *checkPoint = getCheckPoint(i);
184 ASSERT(checkPoint);
185 return checkPoint->getEntryOffsetExact(prevPos_, pos_);
186}
187
189s16 CourseMap::getCurrentAreaID(s16 i, const EGG::Vector3f &pos, MapdataAreaBase::Type type) const {
190 // Check if we're colliding with the provided area ID
191 if (i >= 0) {
192 const MapdataAreaBase *area = getArea(i);
193 if (area->type() == type && area->test(pos)) {
194 return i;
195 }
196 }
197
198 // Search all areas of the same type
199 for (i = 0; i < getAreaCount(); ++i) {
200 const MapdataAreaBase *area = getAreaSorted(i);
201 if (area->type() == type && area->test(pos)) {
202 return area->index();
203 }
204 }
205
206 return -1;
207}
208
210CourseMap *CourseMap::CreateInstance() {
211 ASSERT(!s_instance);
212 s_instance = new CourseMap;
213 return s_instance;
214}
215
217void CourseMap::DestroyInstance() {
218 ASSERT(s_instance);
219 auto *instance = s_instance;
220 s_instance = nullptr;
221 delete instance;
222}
223
225CourseMap::CourseMap()
226 : m_course(nullptr), m_startPoint(nullptr), m_stageInfo(nullptr), m_startTmpAngle(0.0f),
227 m_startTmp0(0.0f), m_startTmp1(0.0f), m_startTmp2(0.0f), m_startTmp3(0.0f) {}
228
230CourseMap::~CourseMap() {
231 if (s_instance) {
232 s_instance = nullptr;
233 WARN("CourseMap instance not explicitly handled!");
234 }
235
236 delete m_course;
237 delete m_startPoint;
238 delete m_checkPath;
239 delete m_checkPoint;
240 delete m_pointInfo;
241 delete m_geoObj;
242 delete m_area;
243 delete m_jugemPoint;
244 delete m_cannonPoint;
245 delete m_stageInfo;
246}
247
248s16 CourseMap::findSectorBetweenSides(const EGG::Vector3f &pos, MapdataCheckPoint *checkpoint,
249 f32 &distanceRatio) {
250 s16 id = -1;
251
252 // Search order varies depending on whether player is closer to the next or previous checkpoint.
253 if (distanceRatio > 0.5f) {
254 // Step 1: Starting at current checkpoint, search forwards
255 id = searchNextCheckpoint(pos, 0, checkpoint, distanceRatio, false, false);
256
257 if (id != -1) {
258 return id;
259 }
260
261 // Step 2: If step 1 fails, start at next checkpoint(s) and search backwards
262 for (size_t i = 0; i < checkpoint->nextCount(); ++i) {
263 MapdataCheckPoint *next = checkpoint->nextPoint(i);
264
265 for (size_t j = 0; j < next->prevCount(); ++j) {
266 MapdataCheckPoint *prev = next->prevPoint(j);
267
268 if (prev == checkpoint) {
269 continue;
270 }
271
272 id = findRecursiveSector(pos, 1, true, prev, distanceRatio, false);
273 if (id != -1) {
274 return id;
275 }
276 }
277 }
278
279 // Step 3: If step 2 fails, start at previous checkpoint(s) and search forwards
280 for (size_t i = 0; i < checkpoint->prevCount(); ++i) {
281 MapdataCheckPoint *prev = checkpoint->prevPoint(i);
282
283 for (size_t j = 0; j < prev->nextCount(); ++j) {
284 MapdataCheckPoint *next = prev->nextPoint(j);
285
286 if (next == checkpoint) {
287 continue;
288 }
289
290 id = findRecursiveSector(pos, 1, false, next, distanceRatio, false);
291 if (id != -1) {
292 return id;
293 }
294 }
295 }
296
297 // Step 4: If step 3 fails, start at current checkpoint and search backwards
298 return searchPrevCheckpoint(pos, 0, checkpoint, distanceRatio, false, false);
299 } else {
300 // Step 1: Starting at current checkpoint, search backwards
301 id = searchPrevCheckpoint(pos, 0, checkpoint, distanceRatio, false, false);
302
303 if (id != -1) {
304 return id;
305 }
306
307 // Step 2: If step 1 fails, start at prev checkpoint(s) and search forwards
308 for (size_t i = 0; i < checkpoint->prevCount(); ++i) {
309 MapdataCheckPoint *prev = checkpoint->prevPoint(i);
310
311 for (size_t j = 0; j < prev->nextCount(); ++j) {
312 MapdataCheckPoint *next = prev->nextPoint(j);
313
314 if (next == checkpoint) {
315 continue;
316 }
317
318 id = findRecursiveSector(pos, 1, false, next, distanceRatio, false);
319
320 if (id != -1) {
321 return id;
322 }
323 }
324 }
325
326 // Step 3: If step 2 fails, start at next checkpoint(s) and search backwards
327 for (size_t i = 0; i < checkpoint->nextCount(); ++i) {
328 MapdataCheckPoint *next = checkpoint->nextPoint(i);
329
330 for (size_t j = 0; j < next->prevCount(); ++j) {
331 MapdataCheckPoint *prev = next->prevPoint(j);
332
333 if (prev == checkpoint) {
334 continue;
335 }
336
337 id = findRecursiveSector(pos, 1, true, prev, distanceRatio, false);
338
339 if (id != -1) {
340 return id;
341 }
342 }
343 }
344
345 // Step 4: If step 3 fails, start at current checkpoint and search forwards
346 return searchNextCheckpoint(pos, 0, checkpoint, distanceRatio, false, false);
347 }
348
349 return id;
350}
351
352s16 CourseMap::findSectorOutsideSector(const EGG::Vector3f &pos, MapdataCheckPoint *checkpoint,
353 f32 &distanceRatio) {
354 s16 id = -1;
355
356 // Step 1: Starting at next checkpoint(s), search backwards
357 for (size_t i = 0; i < checkpoint->nextCount(); ++i) {
358 MapdataCheckPoint *next = checkpoint->nextPoint(i);
359
360 for (size_t j = 0; j < next->prevCount(); ++j) {
361 MapdataCheckPoint *prev = next->prevPoint(j);
362
363 if (prev == checkpoint) {
364 continue;
365 }
366
367 id = findRecursiveSector(pos, 1, true, prev, distanceRatio, false);
368
369 if (id != -1) {
370 return id;
371 }
372 }
373 }
374
375 // Step 2: If step 1 fails, start at prev checkpoint(s) and search forwards
376 for (size_t i = 0; i < checkpoint->prevCount(); ++i) {
377 MapdataCheckPoint *prev = checkpoint->prevPoint(i);
378
379 for (size_t j = 0; j < prev->nextCount(); ++j) {
380 MapdataCheckPoint *next = prev->nextPoint(j);
381
382 if (next == checkpoint) {
383 continue;
384 }
385
386 id = findRecursiveSector(pos, 1, false, next, distanceRatio, false);
387
388 if (id != -1) {
389 return id;
390 }
391 }
392 }
393
394 // Step 3: If step 2 fails, start at next checkpoint(s) and search forwards
395 for (size_t i = 0; i < checkpoint->nextCount(); ++i) {
396 id = findRecursiveSector(pos, 1, false, checkpoint->nextPoint(i), distanceRatio, false);
397
398 if (id != -1) {
399 return id;
400 }
401 }
402
403 // Step 4: If step 3 fails, start at prev checkpoint(s) and search backwards
404 for (size_t i = 0; i < checkpoint->prevCount(); ++i) {
405 id = findRecursiveSector(pos, 1, true, checkpoint->prevPoint(i), distanceRatio, false);
406
407 if (id != -1) {
408 return id;
409 }
410 }
411
412 return id;
413}
414
415// If local search fails, remove depth limit and search all "loaded" checkpoints
416s16 CourseMap::findSectorRegional(const EGG::Vector3f &pos, MapdataCheckPoint *checkpoint,
417 f32 &distanceRatio) {
418 s16 id = -1;
419
420 // Step 1: Search all next checkpoints until player or key checkpoint is found
421 for (size_t i = 0; i < checkpoint->nextCount(); ++i) {
422 id = findRecursiveSector(pos, -1, false, checkpoint->nextPoint(i), distanceRatio, false);
423
424 if (id != -1) {
425 return id;
426 }
427 }
428
429 // Step 2: Search all previous checkpoints until player or key checkpoint is found
430 for (size_t i = 0; i < checkpoint->prevCount(); ++i) {
431 id = findRecursiveSector(pos, -1, true, checkpoint->prevPoint(i), distanceRatio, false);
432
433 if (id != -1) {
434 return id;
435 }
436 }
437
438 return id;
439}
440
442s16 CourseMap::searchNextCheckpoint(const EGG::Vector3f &pos, s16 depth,
443 const MapdataCheckPoint *checkpoint, f32 &completion, bool playerIsForwards,
444 bool useCache) const {
445 s16 id = -1;
446 depth = depth >= 0 ? depth + 1 : -1;
447
448 for (size_t i = 0; i < checkpoint->nextCount(); ++i) {
449 MapdataCheckPoint *next = checkpoint->nextPoint(i);
450
451 if (!useCache || !next->searched()) {
452 id = findRecursiveSector(pos, depth, false, next, completion, playerIsForwards);
453
454 if (id != -1) {
455 return id;
456 }
457 }
458 }
459
460 return id;
461}
462
464s16 CourseMap::searchPrevCheckpoint(const EGG::Vector3f &pos, s16 depth,
465 const MapdataCheckPoint *checkpoint, f32 &completion, bool playerIsForwards,
466 bool useCache) const {
467 s16 id = -1;
468 depth = depth >= 0 ? depth + 1 : -1;
469
470 for (size_t i = 0; i < checkpoint->prevCount(); ++i) {
471 MapdataCheckPoint *prev = checkpoint->prevPoint(i);
472
473 if (!useCache || !prev->searched()) {
474 id = findRecursiveSector(pos, depth, true, prev, completion, playerIsForwards);
475
476 if (id != -1) {
477 return id;
478 }
479 }
480 }
481
482 return id;
483}
484
486void CourseMap::clearSectorChecked() {
487 for (size_t i = 0; i < m_checkPoint->size(); ++i) {
488 getCheckPoint(i)->clearSearched();
489 }
490}
491
493void *CourseMap::LoadFile(const char *filename) {
494 return ResourceManager::Instance()->getFile(filename, nullptr, ArchiveId::Course);
495}
496
497CourseMap *CourseMap::s_instance = nullptr;
498
499} // namespace System
Contains course metadata, notably the starting position.
Definition CourseMap.hh:33
f32 getEntryOffsetExact(const EGG::Vector2f &prevPos, const EGG::Vector2f &pos) const
Finds the offset between the two positions that enter the checkpoint.
u16 getEntryOffsetMs(const EGG::Vector2f &prevPos, const EGG::Vector2f &pos) const
Finds the offset between the two positions that enter the checkpoint.
@ OutsideSector
Player is outside the given checkpoint group.
@ InsideSector
Player is inside the given checkpoint group.
High-level handling for generic system operations, such as input reading, race configuration,...
Definition CourseMap.cc:5
A 2D float vector.
Definition Vector.hh:12
A 3D float vector.
Definition Vector.hh:88