Skip to content

Commit

Permalink
Add Checkpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
KooShnoo committed Nov 14, 2024
1 parent 5a2171c commit a2db4ca
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 10 deletions.
6 changes: 6 additions & 0 deletions source/egg/math/Vector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ f32 Vector2f::normalise() {
return len;
}

/// @brief Constructs a Vector2f by reading 8 bytes from the stream.
void Vector2f::read(Stream &stream) {
x = stream.read_f32();
y = stream.read_f32();
}

/// @brief The dot product between the vector and itself.
f32 Vector3f::dot() const {
return x * x + y * y + z * z;
Expand Down
4 changes: 3 additions & 1 deletion source/egg/math/Vector.hh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ struct Vector2f {
[[nodiscard]] f32 dot(const Vector2f &rhs) const;
[[nodiscard]] f32 dot() const;
[[nodiscard]] f32 length() const;
[[nodiscard]] f32 normalise();
f32 normalise();

void read(Stream &stream);

f32 x;
f32 y;
Expand Down
30 changes: 25 additions & 5 deletions source/game/system/CourseMap.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "CourseMap.hh"

#include "game/system/map/MapdataCannonPoint.hh"
#include "game/system/map/MapdataCheckPoint.hh"
#include "game/system/map/MapdataFileAccessor.hh"
#include "game/system/map/MapdataGeoObj.hh"
#include "game/system/map/MapdataStageInfo.hh"
Expand All @@ -17,11 +18,13 @@ void CourseMap::init() {
new MapdataFileAccessor(reinterpret_cast<const MapdataFileAccessor::SData *>(buffer));

constexpr u32 CANNON_POINT_SIGNATURE = 0x434e5054;
constexpr u32 CHECK_POINT_SIGNATURE = 0x434b5054;
constexpr u32 GEO_OBJ_SIGNATURE = 0x474f424a;
constexpr u32 START_POINT_SIGNATURE = 0x4b545054;
constexpr u32 STAGE_INFO_SIGNATURE = 0x53544749;

m_startPoint = parseStartPoint(START_POINT_SIGNATURE);
m_checkPoint = parseCheckPoint(CHECK_POINT_SIGNATURE);
m_geoObj = parseGeoObj(GEO_OBJ_SIGNATURE);
m_cannonPoint = parseCannonPoint(CANNON_POINT_SIGNATURE);
m_stageInfo = parseStageInfo(STAGE_INFO_SIGNATURE);
Expand All @@ -43,7 +46,7 @@ void CourseMap::init() {
}

/// @addr{0x80512FA4}
MapdataCannonPointAccessor *CourseMap::parseCannonPoint(u32 sectionName) {
MapdataCannonPointAccessor *CourseMap::parseCannonPoint(u32 sectionName) const {
const MapSectionHeader *sectionPtr = m_course->findSection(sectionName);

MapdataCannonPointAccessor *accessor = nullptr;
Expand All @@ -54,8 +57,20 @@ MapdataCannonPointAccessor *CourseMap::parseCannonPoint(u32 sectionName) {
return accessor;
}

/// @addr{0x80513640}
MapdataCheckPointAccessor *CourseMap::parseCheckPoint(u32 sectionName) const {
const MapSectionHeader *sectionPtr = m_course->findSection(sectionName);

MapdataCheckPointAccessor *accessor = nullptr;
if (sectionPtr) {
accessor = new MapdataCheckPointAccessor(sectionPtr);
}

return accessor;
}

/// @addr{0x805134C8}
MapdataGeoObjAccessor *CourseMap::parseGeoObj(u32 sectionName) {
MapdataGeoObjAccessor *CourseMap::parseGeoObj(u32 sectionName) const {
const MapSectionHeader *sectionPtr = m_course->findSection(sectionName);

MapdataGeoObjAccessor *accessor = nullptr;
Expand All @@ -67,7 +82,7 @@ MapdataGeoObjAccessor *CourseMap::parseGeoObj(u32 sectionName) {
}

/// @addr{0x80512D64}
MapdataStageInfoAccessor *CourseMap::parseStageInfo(u32 sectionName) {
MapdataStageInfoAccessor *CourseMap::parseStageInfo(u32 sectionName) const {
const MapSectionHeader *sectionPtr = m_course->findSection(sectionName);

MapdataStageInfoAccessor *accessor = nullptr;
Expand All @@ -79,7 +94,7 @@ MapdataStageInfoAccessor *CourseMap::parseStageInfo(u32 sectionName) {
}

/// @addr{0x80513F5C}
MapdataStartPointAccessor *CourseMap::parseStartPoint(u32 sectionName) {
MapdataStartPointAccessor *CourseMap::parseStartPoint(u32 sectionName) const {
const MapSectionHeader *sectionPtr = m_course->findSection(sectionName);

MapdataStartPointAccessor *accessor = nullptr;
Expand All @@ -90,11 +105,16 @@ MapdataStartPointAccessor *CourseMap::parseStartPoint(u32 sectionName) {
return accessor;
}

/// @addr{0x80518AF8}
/// @addr{0x80518AE0}
MapdataCannonPoint *CourseMap::getCannonPoint(u16 i) const {
return m_cannonPoint && m_cannonPoint->size() != 0 ? m_cannonPoint->get(i) : nullptr;
}

/// @addr{0x80515C24}
MapdataCheckPoint *CourseMap::getCheckPoint(u16 i) const {
return m_checkPoint && m_checkPoint->size() != 0 ? m_checkPoint->get(i) : nullptr;
}

/// @addr{0x80514148}
MapdataGeoObj *CourseMap::getGeoObj(u16 i) const {
return i < getGeoObjCount() ? m_geoObj->get(i) : nullptr;
Expand Down
13 changes: 9 additions & 4 deletions source/game/system/CourseMap.hh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace System {

class MapdataCannonPoint;
class MapdataCannonPointAccessor;
class MapdataCheckPoint;
class MapdataCheckPointAccessor;
class MapdataFileAccessor;
class MapdataGeoObj;
class MapdataGeoObjAccessor;
Expand All @@ -22,13 +24,15 @@ class MapdataStartPointAccessor;
class CourseMap : EGG::Disposer {
public:
void init();
[[nodiscard]] MapdataCannonPointAccessor *parseCannonPoint(u32 sectionName);
[[nodiscard]] MapdataGeoObjAccessor *parseGeoObj(u32 sectionName);
[[nodiscard]] MapdataStageInfoAccessor *parseStageInfo(u32 sectionName);
[[nodiscard]] MapdataStartPointAccessor *parseStartPoint(u32 sectionName);
[[nodiscard]] MapdataCannonPointAccessor *parseCannonPoint(u32 sectionName) const;
[[nodiscard]] MapdataCheckPointAccessor *parseCheckPoint(u32 sectionName) const;
[[nodiscard]] MapdataGeoObjAccessor *parseGeoObj(u32 sectionName) const;
[[nodiscard]] MapdataStageInfoAccessor *parseStageInfo(u32 sectionName) const;
[[nodiscard]] MapdataStartPointAccessor *parseStartPoint(u32 sectionName) const;

/// @beginGetters
[[nodiscard]] MapdataCannonPoint *getCannonPoint(u16 i) const;
[[nodiscard]] MapdataCheckPoint *getCheckPoint(u16 i) const;
[[nodiscard]] MapdataGeoObj *getGeoObj(u16 i) const;
[[nodiscard]] MapdataStageInfo *getStageInfo() const;
[[nodiscard]] MapdataStartPoint *getStartPoint(u16 i) const;
Expand All @@ -51,6 +55,7 @@ private:

MapdataFileAccessor *m_course;
MapdataStartPointAccessor *m_startPoint;
MapdataCheckPointAccessor *m_checkPoint;
MapdataGeoObjAccessor *m_geoObj;
MapdataCannonPointAccessor *m_cannonPoint;
MapdataStageInfoAccessor *m_stageInfo;
Expand Down
117 changes: 117 additions & 0 deletions source/game/system/map/MapdataCheckPoint.cc
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
100 changes: 100 additions & 0 deletions source/game/system/map/MapdataCheckPoint.hh
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

0 comments on commit a2db4ca

Please sign in to comment.