A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
RaceManager.cc
1#include "RaceManager.hh"
2
3#include "game/system/CourseMap.hh"
4#include "game/system/KPadDirector.hh"
5#include "game/system/RaceConfig.hh"
6#include "game/system/map/MapdataCheckPath.hh"
7#include "game/system/map/MapdataStartPoint.hh"
8
9#include "game/kart/KartObjectManager.hh"
10#include "game/kart/KartState.hh"
11
12namespace System {
13
15void RaceManager::init() {
16 m_player.init();
17}
18
22 u32 placement = 1;
23 u32 playerCount = 1;
24 u32 startPointIdx = 0;
25
26 MapdataStartPoint *kartpoint = CourseMap::Instance()->getStartPoint(startPointIdx);
27
28 if (kartpoint) {
29 kartpoint->findKartStartPoint(pos, angles, placement - 1, playerCount);
30 } else {
31 pos.setZero();
32 angles = EGG::Vector3f::ex;
33 }
34}
35
37void RaceManager::endPlayerRace(u32 /*idx*/) {
38 // We only have one player, so most of the logic is much simpler
39 m_stage = Stage::FinishGlobal;
40}
41
43void RaceManager::calc() {
44 constexpr u16 STAGE_INTRO_DURATION = 172;
45
46 m_timerManager.calc();
47 m_player.calc();
48
49 switch (m_stage) {
50 case Stage::Intro:
51 if (++m_introTimer >= STAGE_INTRO_DURATION) {
52 m_stage = Stage::Countdown;
53 KPadDirector::Instance()->startGhostProxies();
54 }
55 break;
56 case Stage::Countdown:
57 if (++m_timer >= STAGE_COUNTDOWN_DURATION) {
58 m_timerManager.setStarted(true);
59 m_stage = Stage::Race;
60 }
61 break;
62 case Stage::Race:
63 ++m_timer;
64 break;
65 default:
66 break;
67 }
68}
69
71MapdataJugemPoint *RaceManager::jugemPoint() const {
72 s8 jugemId = std::max<s8>(m_player.jugemId(), 0);
73 return System::CourseMap::Instance()->getJugemPoint(static_cast<u16>(jugemId));
74}
75
77RaceManager *RaceManager::CreateInstance() {
78 ASSERT(!s_instance);
79 s_instance = new RaceManager;
80 return s_instance;
81}
82
84void RaceManager::DestroyInstance() {
85 ASSERT(s_instance);
86 auto *instance = s_instance;
87 s_instance = nullptr;
88 delete instance;
89}
90
92RaceManager::RaceManager()
93 : m_random(RNG_SEED), m_stage(Stage::Intro), m_introTimer(0), m_timer(0) {}
94
96RaceManager::~RaceManager() {
97 if (s_instance) {
98 s_instance = nullptr;
99 WARN("RaceManager instance not explicitly handled!");
100 }
101}
102
104RaceManager::Player::Player() {
105 m_checkpointId = 0;
106 m_raceCompletion = 0.0f;
107 m_checkpointFactor = -1.0f;
108 m_checkpointStartLapCompletion = 0.0f;
109 m_lapCompletion = 0.999999f;
110
111 auto *courseMap = CourseMap::Instance();
112
113 if (courseMap->getCheckPointCount() > 0 && courseMap->getCheckPathCount() > 0) {
114 m_maxKcp = courseMap->checkPoint()->lastKcpType();
115 } else {
116 m_maxKcp = -1;
117 }
118
119 m_currentLap = 0;
120 m_maxLap = 1;
121 m_inputs = &KPadDirector::Instance()->playerInput();
122}
123
125void RaceManager::Player::init() {
126 auto *courseMap = CourseMap::Instance();
127
128 if (courseMap->getCheckPointCount() != 0 && courseMap->getCheckPathCount() != 0) {
129 const EGG::Vector3f &pos = Kart::KartObjectManager::Instance()->object(0)->pos();
130 f32 distanceRatio;
131 s16 checkpointId = courseMap->findSector(pos, 0, distanceRatio);
132
133 m_checkpointId = std::max<s16>(0, checkpointId);
134 m_jugemId = courseMap->getCheckPoint(m_checkpointId)->jugemIndex();
135 } else {
136 m_jugemId = 0;
137 }
138}
139
141void RaceManager::Player::calc() {
142 auto *courseMap = CourseMap::Instance();
143 const auto *kart = Kart::KartObjectManager::Instance()->object(0);
144
145 if (courseMap->getCheckPointCount() == 0 || courseMap->getCheckPathCount() == 0 ||
146 kart->status().onBit(Kart::eStatus::BeforeRespawn)) {
147 return;
148 }
149
150 f32 distanceRatio;
151 s16 checkpointId = courseMap->findSector(kart->pos(), m_checkpointId, distanceRatio);
152
153 if (checkpointId == -1) {
154 return;
155 }
156
157 if (m_checkpointFactor < 0.0f || m_checkpointId != checkpointId) {
158 calcCheckpoint(checkpointId, distanceRatio);
159 }
160
161 m_raceCompletion = static_cast<f32>(m_currentLap) +
162 (m_checkpointStartLapCompletion + m_checkpointFactor * distanceRatio);
163 m_raceCompletion = std::min(m_raceCompletion, static_cast<f32>(m_currentLap) + 0.99999f);
164}
165
171 ASSERT(lap <= m_lapTimers.size());
172
173 if (lap < 2) {
174 return m_lapTimers[0];
175 }
176
177 const Timer &currentLap = m_lapTimers[lap - 1];
178 const Timer &previousLap = m_lapTimers[lap - 2];
179 if (!currentLap.valid || !previousLap.valid) {
180 return Timer(std::numeric_limits<u16>::max(), 0, 0);
181 }
182
183 return currentLap - previousLap;
184}
185
187MapdataCheckPoint *RaceManager::Player::calcCheckpoint(u16 checkpointId, f32 distanceRatio) {
188 auto *courseMap = CourseMap::Instance();
189
190 u16 oldCheckpointId = m_checkpointId;
191 m_checkpointId = checkpointId;
192
193 f32 lapProportion = courseMap->checkPath()->lapProportion();
194 MapdataCheckPath *checkPath = courseMap->checkPath()->findCheckpathForCheckpoint(checkpointId);
195 m_checkpointFactor = checkPath->oneOverCount() * lapProportion;
196
197 m_checkpointStartLapCompletion = static_cast<f32>(checkPath->depth()) * lapProportion +
198 (m_checkpointFactor * static_cast<f32>(checkpointId - checkPath->start()));
199
200 f32 newLapCompletion = m_checkpointStartLapCompletion + distanceRatio * m_checkpointFactor;
201 f32 deltaLapCompletion = m_lapCompletion - newLapCompletion;
202
203 MapdataCheckPoint *newCheckpoint = courseMap->getCheckPoint(checkpointId);
204 const MapdataCheckPoint *oldCheckpoint = courseMap->getCheckPoint(oldCheckpointId);
205
206 s8 newJugemIdx = newCheckpoint->jugemIndex();
207 if (newJugemIdx >= 0) {
208 m_jugemId = newJugemIdx;
209 }
210
211 if (!newCheckpoint->isNormalCheckpoint()) {
212 if (newCheckpoint->checkArea() > m_maxKcp) {
213 m_maxKcp = newCheckpoint->checkArea();
214 } else if (m_maxKcp == courseMap->checkPoint()->lastKcpType()) {
215 if ((newCheckpoint->isFinishLine() &&
216 areCheckpointsSubsequent(oldCheckpoint, checkpointId)) ||
217 deltaLapCompletion > 0.95f) {
218 incrementLap();
219 }
220 }
221 }
222
223 if ((oldCheckpoint->isFinishLine() &&
224 areCheckpointsSubsequent(newCheckpoint, oldCheckpointId)) ||
225 deltaLapCompletion < -0.95f) {
226 decrementLap();
227 }
228
229 m_lapCompletion = newLapCompletion;
230
231 return newCheckpoint;
232}
233
235bool RaceManager::Player::areCheckpointsSubsequent(const MapdataCheckPoint *checkpoint,
236 u16 nextCheckpointId) const {
237 for (size_t i = 0; i < checkpoint->nextCount(); ++i) {
238 if (nextCheckpointId == checkpoint->nextPoint(i)->id()) {
239 return true;
240 }
241 }
242
243 return false;
244}
245
247void RaceManager::Player::decrementLap() {
248 auto *courseMap = CourseMap::Instance();
249
250 if (courseMap->getCheckPointCount() > 0 && courseMap->getCheckPathCount() > 0) {
251 m_maxKcp = courseMap->checkPoint()->lastKcpType();
252 } else {
253 m_maxKcp = -1;
254 }
255
256 --m_currentLap;
257}
258
260void RaceManager::Player::incrementLap() {
261 m_maxKcp = 0;
262 if (++m_currentLap <= m_maxLap) {
263 return;
264 }
265
266 const auto *kart = Kart::KartObjectManager::Instance()->object(0);
267 u16 addMs = CourseMap::Instance()->getCheckPointEntryOffsetMs(m_checkpointId, kart->pos(),
268 kart->prevPos());
269
270 const Timer &currentTimer = RaceManager::Instance()->timerManager().currentTimer();
271 Timer timer = currentTimer + static_cast<f32>(addMs);
272
273 // TODO: Handle this case more gracefully
274 ASSERT(static_cast<size_t>(m_maxLap - 1) < m_lapTimers.size());
275 m_lapTimers[m_maxLap - 1] = timer;
276
277 if (m_maxLap >= 3) {
278 endRace(timer);
279 } else {
280 m_maxLap = m_currentLap;
281 }
282}
283
285void RaceManager::Player::endRace(const Timer &finishTime) {
286 m_raceTimer = finishTime;
287 RaceManager::Instance()->endPlayerRace(0);
288}
289
290RaceManager *RaceManager::s_instance = nullptr;
291
292} // namespace System
void findKartStartPoint(EGG::Vector3f &pos, EGG::Vector3f &angles, u8 placement, u8 playerCount)
f32 m_checkpointFactor
The proportion of a lap for the current checkpoint.
Timer getLapSplit(size_t idx) const
Gets the lap split, which is the difference between the given lap and the previous one.
void findKartStartPoint(EGG::Vector3f &pos, EGG::Vector3f &angles)
@ BeforeRespawn
Set on respawn collision, cleared on position snap.
High-level handling for generic system operations, such as input reading, race configuration,...
Definition CourseMap.cc:5
A 3D float vector.
Definition Vector.hh:88
A simple struct to represent a lap or race finish time.