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 Kinoko::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_drivingWrongWay = false;
122 m_inputs = &KPadDirector::Instance()->playerInput();
123}
124
126void RaceManager::Player::init() {
127 auto *courseMap = CourseMap::Instance();
128
129 if (courseMap->getCheckPointCount() != 0 && courseMap->getCheckPathCount() != 0) {
130 const EGG::Vector3f &pos = Kart::KartObjectManager::Instance()->object(0)->pos();
131 f32 distanceRatio;
132 s16 checkpointId = courseMap->findSector(pos, 0, distanceRatio);
133
134 m_checkpointId = std::max<s16>(0, checkpointId);
135 m_jugemId = courseMap->getCheckPoint(m_checkpointId)->jugemIndex();
136 } else {
137 m_jugemId = 0;
138 }
139}
140
142void RaceManager::Player::calc() {
143 auto *courseMap = CourseMap::Instance();
144 const auto *kart = Kart::KartObjectManager::Instance()->object(0);
145
146 if (courseMap->getCheckPointCount() == 0 || courseMap->getCheckPathCount() == 0 ||
147 kart->status().onBit(Kart::eStatus::BeforeRespawn)) {
148 return;
149 }
150
151 f32 distanceRatio;
152 s16 checkpointId = courseMap->findSector(kart->pos(), m_checkpointId, distanceRatio);
153
154 if (checkpointId == -1) {
155 return;
156 }
157
158 System::MapdataCheckPoint *checkpoint = nullptr;
159
160 if (m_checkpointFactor < 0.0f || m_checkpointId != checkpointId) {
161 checkpoint = calcCheckpoint(checkpointId, distanceRatio);
162 } else {
163 checkpoint = CourseMap::Instance()->getCheckPoint(m_checkpointId);
164 }
165
166 m_raceCompletion = static_cast<f32>(m_currentLap) +
167 (m_checkpointStartLapCompletion + m_checkpointFactor * distanceRatio);
168 m_raceCompletion = std::min(m_raceCompletion, static_cast<f32>(m_currentLap) + 0.99999f);
169
170 const EGG::Vector3f &bodyFront = kart->bodyFront();
171 if (bodyFront.x != 0.0f && bodyFront.z != 0.0f) {
172 EGG::Vector2f frontXZ = EGG::Vector2f(bodyFront.x, bodyFront.z);
173 frontXZ.normalise();
174 m_drivingWrongWay = checkpoint->dir().dot(frontXZ) <= -0.5f;
175 }
176}
177
183 ASSERT(lap <= m_lapTimers.size());
184
185 if (lap < 2) {
186 return m_lapTimers[0];
187 }
188
189 const Timer &currentLap = m_lapTimers[lap - 1];
190 const Timer &previousLap = m_lapTimers[lap - 2];
191 if (!currentLap.valid || !previousLap.valid) {
192 return Timer(std::numeric_limits<u16>::max(), 0, 0);
193 }
194
195 return currentLap - previousLap;
196}
197
199MapdataCheckPoint *RaceManager::Player::calcCheckpoint(u16 checkpointId, f32 distanceRatio) {
200 auto *courseMap = CourseMap::Instance();
201
202 u16 oldCheckpointId = m_checkpointId;
203 m_checkpointId = checkpointId;
204
205 f32 lapProportion = courseMap->checkPath()->lapProportion();
206 MapdataCheckPath *checkPath = courseMap->checkPath()->findCheckpathForCheckpoint(checkpointId);
207 m_checkpointFactor = checkPath->oneOverCount() * lapProportion;
208
209 m_checkpointStartLapCompletion = static_cast<f32>(checkPath->depth()) * lapProportion +
210 (m_checkpointFactor * static_cast<f32>(checkpointId - checkPath->start()));
211
212 f32 newLapCompletion = m_checkpointStartLapCompletion + distanceRatio * m_checkpointFactor;
213 f32 deltaLapCompletion = m_lapCompletion - newLapCompletion;
214
215 MapdataCheckPoint *newCheckpoint = courseMap->getCheckPoint(checkpointId);
216 const MapdataCheckPoint *oldCheckpoint = courseMap->getCheckPoint(oldCheckpointId);
217
218 s8 newJugemIdx = newCheckpoint->jugemIndex();
219 if (newJugemIdx >= 0) {
220 m_jugemId = newJugemIdx;
221 }
222
223 if (!newCheckpoint->isNormalCheckpoint()) {
224 if (newCheckpoint->checkArea() > m_maxKcp) {
225 m_maxKcp = newCheckpoint->checkArea();
226 } else if (m_maxKcp == courseMap->checkPoint()->lastKcpType()) {
227 if ((newCheckpoint->isFinishLine() &&
228 areCheckpointsSubsequent(oldCheckpoint, checkpointId)) ||
229 deltaLapCompletion > 0.95f) {
230 incrementLap();
231 }
232 }
233 }
234
235 if ((oldCheckpoint->isFinishLine() &&
236 areCheckpointsSubsequent(newCheckpoint, oldCheckpointId)) ||
237 deltaLapCompletion < -0.95f) {
238 decrementLap();
239 }
240
241 m_lapCompletion = newLapCompletion;
242
243 return newCheckpoint;
244}
245
247bool RaceManager::Player::areCheckpointsSubsequent(const MapdataCheckPoint *checkpoint,
248 u16 nextCheckpointId) const {
249 for (size_t i = 0; i < checkpoint->nextCount(); ++i) {
250 if (nextCheckpointId == checkpoint->nextPoint(i)->id()) {
251 return true;
252 }
253 }
254
255 return false;
256}
257
259void RaceManager::Player::decrementLap() {
260 auto *courseMap = CourseMap::Instance();
261
262 if (courseMap->getCheckPointCount() > 0 && courseMap->getCheckPathCount() > 0) {
263 m_maxKcp = courseMap->checkPoint()->lastKcpType();
264 } else {
265 m_maxKcp = -1;
266 }
267
268 --m_currentLap;
269}
270
272void RaceManager::Player::incrementLap() {
273 m_maxKcp = 0;
274 if (++m_currentLap <= m_maxLap) {
275 return;
276 }
277
278 const auto *kart = Kart::KartObjectManager::Instance()->object(0);
279 u16 addMs = CourseMap::Instance()->getCheckPointEntryOffsetMs(m_checkpointId, kart->pos(),
280 kart->prevPos());
281
282 const Timer &currentTimer = RaceManager::Instance()->timerManager().currentTimer();
283 Timer timer = currentTimer + static_cast<f32>(addMs);
284
285 // TODO: Handle this case more gracefully
286 ASSERT(static_cast<size_t>(m_maxLap - 1) < m_lapTimers.size());
287 m_lapTimers[m_maxLap - 1] = timer;
288
289 if (m_maxLap >= 3) {
290 endRace(timer);
291 } else {
292 m_maxLap = m_currentLap;
293 }
294}
295
297void RaceManager::Player::endRace(const Timer &finishTime) {
298 m_raceTimer = finishTime;
299 RaceManager::Instance()->endPlayerRace(0);
300}
301
302RaceManager *RaceManager::s_instance = nullptr;
303
304} // namespace Kinoko::System
void findKartStartPoint(EGG::Vector3f &pos, EGG::Vector3f &angles, u8 placement, u8 playerCount)
Timer getLapSplit(size_t idx) const
Gets the lap split, which is the difference between the given lap and the previous one.
f32 m_checkpointFactor
The proportion of a lap for the current checkpoint.
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:107
A simple struct to represent a lap or race finish time.