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