-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
260 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
#include "MapdataCheckPoint.hh" | ||
|
||
#include "game/system/CourseMap.hh" | ||
|
||
namespace System { | ||
|
||
/// @addr{0x805154E4} | ||
MapdataCheckPoint::MapdataCheckPoint(const SData *data) | ||
: m_rawData(data), m_nextCount(0), m_prevCount(0) { | ||
u8 *unsafeData = reinterpret_cast<u8 *>(const_cast<SData *>(data)); | ||
EGG::RamStream stream = EGG::RamStream(unsafeData, sizeof(SData)); | ||
read(stream); | ||
m_midpoint = EGG::Vector2f((left().x + right().x) / 2.0f, (left().y + right().y) / 2.0f); | ||
m_dir = EGG::Vector2f(left().y - right().y, right().x - left().x); | ||
m_dir.normalise(); | ||
} | ||
|
||
void MapdataCheckPoint::read(EGG::Stream &stream) { | ||
m_left.read(stream); | ||
m_right.read(stream); | ||
m_jugemIndex = stream.read_u8(); | ||
m_type = stream.read_s8(); | ||
m_prevPt = stream.read_u8(); | ||
m_nextPt = stream.read_u8(); | ||
} | ||
|
||
void MapdataCheckPoint::setPlayerFlags(s32 /* playerIdx */) { | ||
m_flag = true; | ||
} | ||
|
||
const EGG::Vector2f &MapdataCheckPoint::left() const { | ||
return m_left; | ||
} | ||
|
||
const EGG::Vector2f &MapdataCheckPoint::right() const { | ||
return m_right; | ||
} | ||
|
||
u8 MapdataCheckPoint::jugemIndex() const { | ||
return m_jugemIndex; | ||
} | ||
|
||
s8 MapdataCheckPoint::type() const { | ||
return m_type; | ||
} | ||
|
||
bool MapdataCheckPoint::isNormalCheckpoint() const { | ||
return m_type == NORMAL_CHECKPOINT; | ||
} | ||
|
||
bool MapdataCheckPoint::isFinishLine() const { | ||
return m_type == FINISH_LINE; | ||
} | ||
|
||
/// @brief Computes @ref m_prevKcpId for each checkpoint. | ||
/// @details @ref m_flag indicates that a checkpoint has been visited, to prevent infinite | ||
/// recursion. | ||
/// @addr{0x80515A6C} | ||
void MapdataCheckPoint::linkPrevKcpIds(u8 prevKcpId) { | ||
m_prevKcpId = m_type == NORMAL_CHECKPOINT ? prevKcpId : m_type; | ||
|
||
setPlayerFlags(0); | ||
for (size_t i = 0; i < m_nextCount; i++) { | ||
MapdataCheckPoint *next = m_nextPoints[i].checkpoint; | ||
if (!next->m_flag) { | ||
next->linkPrevKcpIds(m_prevKcpId); | ||
} | ||
} | ||
} | ||
|
||
/// @brief Finds indices of the finish line and last key checkpoint; also calls initCheckpointLinks | ||
/// for all checkpoints | ||
/// @addr{Inlined in 0x80515244} fake. not real. it's not in the base game. in the base | ||
/// game, it's inlined into `init()`. i, kooshnoo, split it out because i wanted to. | ||
// I'm not sure if this should be split; it feels like a separate function, | ||
// but idk if i can somehow prove nintendo wrote it as a separate function, | ||
// and i don't know if we even care what nintendo extracted to a separate function and what they | ||
// inlined anyways. would the PR reviewer kindly please advise, thank you | ||
void MapdataCheckPointAccessor::findFinishAndLastKcp() { | ||
s8 lastKcpType = -1; | ||
s16 finishLineCheckpointId = -1; | ||
|
||
for (size_t ckptId = 0; ckptId < size(); ckptId++) { | ||
MapdataCheckPoint *checkpoint = get(ckptId); | ||
// @todo implement initCheckpointLinks | ||
// checkpoint->initCheckpointLinks(*this, ckptId); | ||
checkpoint = get(ckptId); | ||
|
||
if (checkpoint->isFinishLine()) { | ||
finishLineCheckpointId = ckptId; | ||
} | ||
|
||
lastKcpType = std::max(lastKcpType, checkpoint->type()); | ||
} | ||
|
||
m_lastKcpType = lastKcpType; | ||
m_finishLineCheckpointId = finishLineCheckpointId; | ||
} | ||
|
||
/// @addr{0x80515244} | ||
void MapdataCheckPointAccessor::init() { | ||
findFinishAndLastKcp(); | ||
MapdataCheckPoint *finishLine = get(m_finishLineCheckpointId); | ||
finishLine->linkPrevKcpIds(0); | ||
} | ||
|
||
MapdataCheckPointAccessor::MapdataCheckPointAccessor(const MapSectionHeader *header) | ||
: MapdataAccessorBase<MapdataCheckPoint, MapdataCheckPoint::SData>(header) { | ||
MapdataAccessorBase::init( | ||
reinterpret_cast<const MapdataCheckPoint::SData *>(m_sectionHeader + 1), | ||
parse<u16>(m_sectionHeader->count)); | ||
init(); | ||
} | ||
|
||
MapdataCheckPointAccessor::~MapdataCheckPointAccessor() = default; | ||
|
||
} // namespace System |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
#pragma once | ||
|
||
#include "game/system/map/MapdataAccessorBase.hh" | ||
|
||
#include <egg/math/Vector.hh> | ||
|
||
namespace System { | ||
|
||
class MapdataCheckPoint; | ||
class MapdataCheckPointAccessor; | ||
|
||
struct LinkedCheckpoint { | ||
MapdataCheckPoint *checkpoint; | ||
EGG::Vector2f p0diff; | ||
EGG::Vector2f p1diff; | ||
f32 distance; | ||
}; | ||
|
||
class MapdataCheckPoint { | ||
public: | ||
struct SData { | ||
EGG::Vector2f left; | ||
EGG::Vector2f right; | ||
u8 jugemIndex; | ||
s8 lapCheck; | ||
u8 prevPt; | ||
u8 nextPt; | ||
}; | ||
|
||
enum class SectorOccupancy { | ||
InsideSector, ///< If the player is inside the checkpoint quad | ||
OutsideSector, ///< If the player is not between the sides of the quad (may still be between | ||
///< this checkpoint and next); the player is likely in a different | ||
///< checkpoint group | ||
OutsideSector_BetweenSides, ///< If the player is between the sides of the quad, but NOT | ||
///< between this checkpoint and next; the player is likely in | ||
///< the same checkpoint group | ||
}; | ||
|
||
MapdataCheckPoint(const SData *data); | ||
void read(EGG::Stream &stream); | ||
void setPlayerFlags(s32 playerIdx); | ||
|
||
/// @beginGetters | ||
[[nodiscard]] const EGG::Vector2f &left() const; | ||
[[nodiscard]] const EGG::Vector2f &right() const; | ||
[[nodiscard]] u8 jugemIndex() const; | ||
[[nodiscard]] s8 type() const; | ||
[[nodiscard]] bool isNormalCheckpoint() const; | ||
[[nodiscard]] bool isFinishLine() const; | ||
/// @endGetters | ||
|
||
void linkPrevKcpIds(u8 prevKcpId); | ||
|
||
private: | ||
const SData *m_rawData; | ||
EGG::Vector2f m_left; | ||
EGG::Vector2f m_right; | ||
u8 m_jugemIndex; ///< Index of respawn point associated with this checkpoint. Players who die | ||
///< here will be respawned at this point. | ||
/// Either: | ||
/// - a @ref `NORMAL_CHECKPOINT` (0) used to calulate respawns, | ||
/// - a @ref `FINISH_LINE` (-1) which updates the lap count when crossed, or | ||
/// - a "key checkpoint" (1-127) used to ensure racers travel around the entire | ||
/// course before proceeding to the next lap. the type value represents the index, | ||
/// i.e. racers must pass checkpoint with @ref `m_type` 1, then 2, then 3 etc.. | ||
s8 m_type; | ||
u8 m_prevPt; | ||
u8 m_nextPt; | ||
u16 m_nextCount; | ||
u16 m_prevCount; | ||
EGG::Vector2f m_midpoint; | ||
EGG::Vector2f m_dir; | ||
bool m_flag; ///< visited flag, for recursive functions. | ||
u16 m_id; | ||
u8 m_prevKcpId; ///< @unused | ||
MapdataCheckPoint *m_prevPoints[6]; | ||
LinkedCheckpoint m_nextPoints[6]; | ||
|
||
static constexpr s8 NORMAL_CHECKPOINT = -1; ///< Only used for picking respawn position | ||
static constexpr s8 FINISH_LINE = 0; ///< Triggers a lap change | ||
}; | ||
|
||
class MapdataCheckPointAccessor | ||
: public MapdataAccessorBase<MapdataCheckPoint, MapdataCheckPoint::SData> { | ||
public: | ||
MapdataCheckPointAccessor(const MapSectionHeader *header); | ||
~MapdataCheckPointAccessor() override; | ||
|
||
[[nodiscard]] s8 lastKcpType() const; | ||
|
||
private: | ||
void findFinishAndLastKcp(); | ||
void init(); | ||
|
||
s8 m_lastKcpType; | ||
u16 m_finishLineCheckpointId; | ||
}; | ||
|
||
} // namespace System |