Editor tooling wip

This commit is contained in:
MegaMech
2025-03-18 17:38:00 -06:00
parent 3b343e3676
commit 2e1e2a1a4b
63 changed files with 746 additions and 139 deletions
-1
View File
@@ -1287,7 +1287,6 @@ s32 func_800088D8(s32 playerId, s16 lapNum, s16 currRank) {
if (var_t1 < 0 || var_t1 >= 8) {
return false;
}
printf("T1 %d\n", var_t1);
if (arg1_times_8 < 24) {
temp_a3 = &temp_a3[var_t1];
interp = gLapCompletionPercentByPlayerId[playerId];
+1
View File
@@ -71,6 +71,7 @@ void func_80280038(void) {
render_set_position(matrix, 0);
render_course(D_800DC5EC);
render_course_actors(D_800DC5EC);
CM_DrawStaticMeshActors();
render_object(PLAYER_ONE + SCREEN_MODE_1P);
render_player_snow_effect(PLAYER_ONE + SCREEN_MODE_1P);
ceremony_transition_sliding_borders();
+16 -6
View File
@@ -10,7 +10,11 @@
*
*/
/**
*
* Applies pos, rot, and scale
*
*/
struct FVector {
float x, y, z;
@@ -108,19 +112,25 @@ typedef struct IVector2D {
#endif // __cplusplus
} IVector2D;
struct FRotation {
float pitch, yaw, roll;
// IRot is an int16_t not a float.
struct IRotator {
int16_t pitch, yaw, roll;
#ifdef __cplusplus
FRotation& operator=(const FRotation& other) {
IRotator& operator=(const IRotator& other) {
pitch = other.pitch;
yaw = other.yaw;
roll = other.roll;
return *this;
}
FRotation() : pitch(0), yaw(0), roll(0) {}
FRotation(float p, float y, float r) : pitch(p), yaw(y), roll(r) {}
// Convert to binary rotator 0 --> INT16_MAX
[[nodiscard]] IRotator ToBinary() const {
return IRotator(pitch * INT16_MAX / 180, yaw * INT16_MAX / 180, roll * INT16_MAX / 180);
}
IRotator() : pitch(0), yaw(0), roll(0) {}
IRotator(int16_t p, int16_t y, int16_t r) : pitch(p), yaw(y), roll(r) {}
#endif // __cplusplus
};
+16 -2
View File
@@ -4,6 +4,7 @@
void RunGarbageCollector() {
//CleanActors();
CleanObjects();
CleanStaticMeshActors();
}
void CleanActors() {
@@ -18,14 +19,27 @@ void CleanActors() {
// }
}
void CleanStaticMeshActors() {
for (auto actor = gWorldInstance.StaticMeshActors.begin(); actor != gWorldInstance.StaticMeshActors.end();) {
StaticMeshActor* act = *actor; // Get a mutable copy
if (act->bPendingDestroy) {
delete act;
actor = gWorldInstance.StaticMeshActors.erase(actor); // Remove from container
continue;
} else {
actor++;
}
}
}
void CleanObjects() {
for (auto object = gWorldInstance.Objects.begin(); object != gWorldInstance.Objects.end();) {
OObject* obj = *object; // Get a mutable copy
if (obj->PendingDestroy) {
if (obj->bPendingDestroy) {
delete obj;
object = gWorldInstance.Objects.erase(object); // Remove from container
continue;
}
object++;
}
}
}
+1
View File
@@ -8,4 +8,5 @@
void RunGarbageCollector();
void CleanActors();
void CleanStaticMeshActors();
void CleanObjects();
+7 -7
View File
@@ -39,21 +39,21 @@ void HarbourMastersIntro::HM_InitIntro() {
_cameraAcceleration = 1.0f;
_pos = FVector(-1000, -205, -800); // -1000, -210, -800
_rot = FRotation(-5, 100, 0);
_rot = IRotator(-5, 100, 0);
_scale = 0.7f;
_trackScale = 2.0f;
_ship2Pos = FVector(300, -210, -1960);
_ship2Rot = FRotation(0, 45, 0);
_ship2Rot = IRotator(0, 45, 0);
_shipPos = FVector(20, -210, -1650);
_shipRot = FRotation(0, 45, 0);
_shipRot = IRotator(0, 45, 0);
_posHM64 = FVector(0, -500, -1000);
_rotHM64 = FRotation(0, -90, -4); // -0x2100
_rotHM64 = IRotator(0, -90, -4); // -0x2100
_hPos = FVector(-2000, 100, -4900);
_hRot = FRotation(0, 0, 0);
_hRot = IRotator(0, 0, 0);
ground_f3d_material_013_lights = gdSPDefLights1(
0x7F, 0x30, 0x80,
@@ -94,7 +94,7 @@ void HarbourMastersIntro::HM_TickIntro() {
}
// @args amplitudes
void HarbourMastersIntro::Bob(FVector& pos, FRotation& rot, f32 bobAmp, f32 bobSpeed, f32 tiltAmp, f32 tiltSpeed, f32 rollAmp, f32 rollSpeed) {
void HarbourMastersIntro::Bob(FVector& pos, IRotator& rot, f32 bobAmp, f32 bobSpeed, f32 tiltAmp, f32 tiltSpeed, f32 rollAmp, f32 rollSpeed) {
float time = (float)gGlobalTimer;
pos.y = -210 + bobAmp * sin(time * bobSpeed);
@@ -104,7 +104,7 @@ void HarbourMastersIntro::Bob(FVector& pos, FRotation& rot, f32 bobAmp, f32 bobS
rot.roll = rollAmp * sin(time * rollSpeed);
}
void HarbourMastersIntro::SpagBob(FVector& pos, FRotation& rot, f32 bobAmp, f32 bobSpeed, f32 tiltAmp, f32 tiltSpeed, f32 rollAmp, f32 rollSpeed) {
void HarbourMastersIntro::SpagBob(FVector& pos, IRotator& rot, f32 bobAmp, f32 bobSpeed, f32 tiltAmp, f32 tiltSpeed, f32 rollAmp, f32 rollSpeed) {
float time = (float)gGlobalTimer;
pos.y = -205 + bobAmp * sin(time * bobSpeed);
+7 -7
View File
@@ -24,8 +24,8 @@ public:
private:
void Setup();
void Sync();
void Bob(FVector& pos, FRotation& rot, f32 bobAmp, f32 bobSpeed, f32 tiltAmp, f32 tiltSpeed, f32 rollAmp, f32 rollSpeed);
void SpagBob(FVector& pos, FRotation& rot, f32 bobAmp, f32 bobSpeed, f32 tiltAmp, f32 tiltSpeed, f32 rollAmp, f32 rollSpeed);
void Bob(FVector& pos, IRotator& rot, f32 bobAmp, f32 bobSpeed, f32 tiltAmp, f32 tiltSpeed, f32 rollAmp, f32 rollSpeed);
void SpagBob(FVector& pos, IRotator& rot, f32 bobAmp, f32 bobSpeed, f32 tiltAmp, f32 tiltSpeed, f32 rollAmp, f32 rollSpeed);
void MoveCloserToCamera(float moveSpeed);
struct HMCamera {
@@ -42,21 +42,21 @@ private:
FVector _pos;
f32 _scale;
f32 _trackScale;
FRotation _rot;
IRotator _rot;
FVector _shipPos;
FRotation _shipRot;
IRotator _shipRot;
FVector _ship2Pos;
FRotation _ship2Rot;
IRotator _ship2Rot;
s32 _water = 0;
FVector _posHM64;
FRotation _rotHM64;
IRotator _rotHM64;
FVector _hPos;
FRotation _hRot;
IRotator _hRot;
};
extern "C" {
+46
View File
@@ -4,6 +4,8 @@
extern "C" {
#include "common_structs.h"
#include "math_util.h"
#include "math_util_2.h"
}
void AddMatrix(std::vector<Mtx>& stack, Mat4 mtx, s32 flags) {
@@ -58,6 +60,49 @@ Mtx* SetTextMatrix(f32 arg1, f32 arg2, f32 arg3, f32 arg4) {
return mtx;
}
void ApplyMatrixTransformations(Mat4 mtx, FVector pos, IRotator rot, FVector scale) {
f32 sine1, cosine1;
f32 sine2, cosine2;
f32 sine3, cosine3;
rot = rot.ToBinary();
// Compute the sine and cosine of the orientation (Euler angles)
sine1 = sins(rot.pitch);
cosine1 = coss(rot.pitch);
sine2 = sins(rot.yaw);
cosine2 = coss(rot.yaw);
sine3 = sins(rot.roll);
cosine3 = coss(rot.roll);
// Compute the rotation matrix
mtx[0][0] = (cosine2 * cosine3) + ((sine1 * sine2) * sine3);
mtx[1][0] = (-cosine2 * sine3) + ((sine1 * sine2) * cosine3);
mtx[2][0] = cosine1 * sine2;
mtx[3][0] = pos.x;
mtx[0][1] = cosine1 * sine3;
mtx[1][1] = cosine1 * cosine3;
mtx[2][1] = -sine1;
mtx[3][1] = pos.y;
mtx[0][2] = (-sine2 * cosine3) + ((sine1 * cosine2) * sine3);
mtx[1][2] = (sine2 * sine3) + ((sine1 * cosine2) * cosine3);
mtx[2][2] = cosine1 * cosine2;
mtx[3][2] = pos.z;
// Apply scaling: modify the diagonal of the rotation matrix
mtx[0][0] *= scale.x;
mtx[1][1] *= scale.y;
mtx[2][2] *= scale.z;
// Set the last row and column for the homogeneous coordinate system
mtx[0][3] = 0.0f;
mtx[1][3] = 0.0f;
mtx[2][3] = 0.0f;
mtx[3][3] = 1.0f;
}
// API
extern "C" {
@@ -122,3 +167,4 @@ extern "C" {
gWorldInstance.Mtx.Objects.clear();
}
}
+2
View File
@@ -4,9 +4,11 @@
#include <libultraship.h>
#include "common_structs.h"
#include "CoreMath.h"
#ifdef __cplusplus
extern "C" {
void ApplyMatrixTransformations(Mat4 mtx, FVector pos, IRotator rot, FVector scale);
#endif
void ClearMatrixPools(void);
void AddHudMatrix(Mat4 mtx, s32 flags);
+34
View File
@@ -0,0 +1,34 @@
#include "StaticMeshActor.h"
#include <libultra/gbi.h>
#include "Matrix.h"
extern "C" {
#include "main.h"
#include "math_util.h"
#include "math_util_2.h"
}
StaticMeshActor::StaticMeshActor(std::string name, FVector pos, IRotator rot, FVector scale, std::string model, int32_t* collision) : Name(name), Pos(pos), Rot(rot), Scale(scale), Model(""), Collision(collision) {
}
void StaticMeshActor::Draw() {
Mat4 mtx;
printf("DRAWING \n");
gSPSetGeometryMode(gDisplayListHead++, G_SHADING_SMOOTH);
gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING);
if (!Model.empty()) {
ApplyMatrixTransformations(mtx, Pos, Rot, Scale);
if (render_set_position(mtx, 0) != 0) {
gSPDisplayList(gDisplayListHead++, (Gfx*)Model.c_str());
}
}
FVector test = FVector(cameras[0].pos[0], cameras[0].pos[1], cameras[0].pos[2]);
FVector diff = test - Pos;
printf("Cam dist: %f", diff.Magnitude());
}
void StaticMeshActor::Destroy() {
bPendingDestroy = true;
}
+61
View File
@@ -0,0 +1,61 @@
#pragma once
#include <libultraship.h>
#include <libultra/gbi.h>
#include "CoreMath.h"
#include <nlohmann/json.hpp>
// todo: Make this class AStaticMeshActor : public AActor
class StaticMeshActor {
public:
std::string Name;
FVector Pos;
IRotator Rot;
FVector Scale;
std::string Model;
int32_t* Collision;
bool bPendingDestroy = false;
StaticMeshActor(std::string name, FVector pos, IRotator rot, FVector scale, std::string model, int32_t* collision);
nlohmann::json to_json() const {
nlohmann::json j;
// Serialize each field of the class
j["Name"] = Name;
j["Position"] = {Pos.x, Pos.y, Pos.z}; // Assuming FVector has x, y, z fields
j["Rotation"] = {Rot.pitch, Rot.yaw, Rot.roll}; // Assuming IRotator has pitch, yaw, roll fields
j["Scale"] = {Scale.x, Scale.y, Scale.z}; // Assuming FVector has x, y, z fields
j["Model"] = Model;
// If Collision is not null, serialize it
if (Collision != nullptr) {
j["Collision"] = *Collision; // Serialize the value that Collision points to
} else {
j["Collision"] = nullptr; // Handle the case where Collision is nullptr
}
return j;
}
void from_json(const nlohmann::json& j) {
Name = j.at("Name").get<std::string>();
Pos = FVector(j.at("Position")[0].get<float>(), j.at("Position")[1].get<float>(), j.at("Position")[2].get<float>());
Rot = IRotator(j.at("Rotation")[0].get<float>(), j.at("Rotation")[1].get<float>(), j.at("Rotation")[2].get<float>());
Scale = FVector(j.at("Scale")[0].get<float>(), j.at("Scale")[1].get<float>(), j.at("Scale")[2].get<float>());
// Deserialize the Model string
Model = j.at("Model").get<std::string>();
// Check if Collision is present in the JSON and deserialize it
//if (j.contains("Collision") && !j["Collision"].is_null()) {
// If Collision is a valid value, allocate memory for it and assign the value
// Collision = new int32_t(j.at("Collision").get<int32_t>());
//} else {
// If Collision is not present or is null, set it to nullptr
Collision = nullptr;
//}
}
virtual void Draw();
virtual void Destroy();
};
+37
View File
@@ -175,6 +175,31 @@ void World::TickActors() {
}
}
StaticMeshActor* World::AddStaticMeshActor(std::string name, FVector pos, IRotator rot, FVector scale, std::string model, int32_t* collision) {
StaticMeshActors.push_back(new StaticMeshActor(name, pos, rot, scale, model, collision));
auto actor = StaticMeshActors.back();
gEditor.AddObject(actor->Name.c_str(), &actor->Pos, (Vec3s*) &actor->Rot, &actor->Scale, (Gfx*) LOAD_ASSET_RAW(actor->Model.c_str()), 1.0f,
Editor::GameObject::CollisionType::VTX_INTERSECT, 0.0f, (int32_t*) &actor->bPendingDestroy, (int32_t) true);
return actor;
}
void World::DrawStaticMeshActors() {
for (const auto& actor: StaticMeshActors) {
actor->Draw();
}
}
void World::DeleteStaticMeshActors() {
for (auto it = StaticMeshActors.begin(); it != StaticMeshActors.end();) {
if ((*it)->bPendingDestroy) {
delete *it; // Deallocate memory for the actor
it = StaticMeshActors.erase(it); // Remove the pointer from the vector
} else {
++it; // Only increment the iterator if we didn't erase an element
}
}
}
OObject* World::AddObject(OObject* object) {
Objects.push_back(object);
@@ -242,3 +267,15 @@ Object* World::GetObjectByIndex(size_t index) {
//}
return nullptr; // Or handle the error as needed
}
void World::ClearWorld(void) {
World::DeleteStaticMeshActors();
CM_CleanWorld();
// for (size_t i = 0; i < ARRAY_COUNT(gCollisionMesh); i++) {
// }
// gCollisionMesh
// Paths
}
+8
View File
@@ -18,6 +18,7 @@
#include <memory>
#include <unordered_map>
#include "Actor.h"
#include "StaticMeshActor.h"
#include "particles/ParticleEmitter.h"
#include "editor/Editor.h"
@@ -31,6 +32,7 @@ extern "C" {
class Cup; // <-- Forward declaration
class OObject;
class Course;
class StaticMeshActor;
class AVehicle;
class OBombKart;
class TrainCrossing;
@@ -60,6 +62,10 @@ public:
AActor* ConvertActorToAActor(Actor* actor);
Actor* ConvertAActorToActor(AActor* actor);
void DrawStaticMeshActors();
StaticMeshActor* AddStaticMeshActor(std::string name, FVector pos, IRotator rot, FVector scale, std::string model, int32_t* collision);
void DeleteStaticMeshActors();
OObject* AddObject(OObject* object);
void TickObjects();
@@ -81,6 +87,7 @@ public:
void SetCourseFromCup();
World* GetWorld(void);
void ClearWorld(void);
// These are only for browsing through the course list
@@ -98,6 +105,7 @@ public:
std::vector<Cup*> Cups;
size_t CupIndex = 1;
std::vector<StaticMeshActor*> StaticMeshActors;
std::vector<AActor*> Actors;
std::vector<OObject*> Objects;
std::vector<ParticleEmitter*> Emitters;
+4 -2
View File
@@ -1,6 +1,7 @@
#include "Ship.h"
#include <libultra/gbi.h>
#include "CoreMath.h"
extern "C" {
#include "common_structs.h"
@@ -47,12 +48,13 @@ void AShip::Tick() {
void AShip::Draw(Camera *camera) {
Mat4 shipMtx;
Vec3f hullPos = {Pos.x, Pos.y, Pos.z};
Vec3s hullRot = {Rot.pitch, Rot.yaw, Rot.roll};
//Vec3s hullRot = {Rot.pitch, Rot.yaw, Rot.roll};
gSPSetGeometryMode(gDisplayListHead++, G_SHADING_SMOOTH);
gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING);
mtxf_pos_rotation_xyz(shipMtx, hullPos, hullRot);
IRotator rot = Rot.ToBinary();
mtxf_pos_rotation_xyz(shipMtx, hullPos, *(Vec3s*) &Rot);
mtxf_scale(shipMtx, 0.4);
if (render_set_position(shipMtx, 0) != 0) {
gSPDisplayList(gDisplayListHead++, _skin);
+1 -1
View File
@@ -28,7 +28,7 @@ public:
FVector Spawn;
FVector Pos;
FRotation Rot = {0, 0, 0};
IRotator Rot = {0, 0, 0};
private:
Gfx* _skin;
};
+2 -1
View File
@@ -45,7 +45,8 @@ void ASpaghettiShip::Draw(Camera *camera) {
gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING);
// empty/null mtx as a base
mtxf_pos_rotation_xyz(shipMtx, hullPos, hullRot);
IRotator iRot = Rot.ToBinary();
mtxf_pos_rotation_xyz(shipMtx, hullPos, *(Vec3s*)&iRot);
mtxf_scale(shipMtx, 0.4);
if (render_set_position(shipMtx, 0) != 0) {}
+2 -2
View File
@@ -21,8 +21,8 @@ public:
FVector Spawn;
FVector Pos;
FRotation Rot = {0, 0, 0};
FRotation WheelRot = {0, 0, 0};
IRotator Rot = {0, 0, 0};
IRotator WheelRot = {0, 0, 0};
private:
f32 scale;
};
+1 -1
View File
@@ -21,5 +21,5 @@ public:
FVector Spawn;
FVector Pos;
FRotation Rot = {0, 0, 0};
IRotator Rot = {0, 0, 0};
};
+8 -6
View File
@@ -73,9 +73,11 @@ BansheeBoardwalk::BansheeBoardwalk() {
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Id = "mk:banshee_boardwalk";
Props.Name = "banshee boardwalk";
Props.DebugName = "ghost";
Props.CourseLength = "747m";
Props.SetText(Props.Name, "banshee boardwalk", sizeof(Props.Name));
Props.SetText(Props.DebugName, "ghost", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "747m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D009058;
Props.AIMaximumSeparation = 40.0f;
Props.AIMinimumSeparation = 0.4f;
@@ -162,13 +164,13 @@ void BansheeBoardwalk::BeginPlay() {
}
if (gIsMirrorMode) {
gWorldInstance.AddObject(new OTrashBin(FVector(1765.0f, 45.0f, 195.0f), FRotation(0, 180.0f, 0), 1.0f, bhv));
gWorldInstance.AddObject(new OTrashBin(FVector(1765.0f, 45.0f, 195.0f), IRotator(0, 180, 0), 1.0f, bhv));
} else {
gWorldInstance.AddObject(new OTrashBin(FVector(-1765.0f, 45.0f, 70.0f), FRotation(0, 0, 0), 1.0f, bhv));
gWorldInstance.AddObject(new OTrashBin(FVector(-1765.0f, 45.0f, 70.0f), IRotator(0, 0, 0), 1.0f, bhv));
}
if ((gGamestate != CREDITS_SEQUENCE) && (gModeSelection != TIME_TRIALS)) {
gWorldInstance.AddObject(new OBat(FVector(0,0,0), FRotation(0, 0, 90.0f)));
gWorldInstance.AddObject(new OBat(FVector(0,0,0), IRotator(0, 0, 90)));
gWorldInstance.AddObject(new OBoos(5, IPathSpan(180, 190), IPathSpan(200, 210), IPathSpan(280, 290)));
gWorldInstance.AddObject(new OBoos(5, IPathSpan(490, 500), IPathSpan(510, 520), IPathSpan(620, 630)));
}
+4 -3
View File
@@ -46,9 +46,10 @@ BigDonut::BigDonut() {
Props.MinimapTexture = gTextureCourseOutlineBigDonut;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Name = "big donut";
Props.DebugName = "doughnut";
Props.CourseLength = "";
Props.SetText(Props.Name, "big donut", sizeof(Props.Name));
Props.SetText(Props.DebugName, "doughnut", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D008F18;
Props.AIMaximumSeparation = -1.0f;
Props.AIMinimumSeparation = 0.5f;
+3 -3
View File
@@ -48,10 +48,10 @@ BlockFort::BlockFort() {
Props.MinimapTexture = gTextureCourseOutlineBlockFort;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.SetText(Props.Name, "block fort", sizeof(Props.Name));
Props.SetText(Props.DebugName, "block", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "", sizeof(Props.CourseLength));
Props.Name = "block fort";
Props.DebugName = "block";
Props.CourseLength = "";
Props.AIBehaviour = D_0D008F18;
Props.AIMaximumSeparation = -1.0f;
Props.AIMinimumSeparation = 0.1f;
+5 -3
View File
@@ -75,9 +75,11 @@ BowsersCastle::BowsersCastle() {
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Id = "mk:bowsers_castle";
Props.Name = "bowser's castle";
Props.DebugName = "castle";
Props.CourseLength = "777m";
Props.SetText(Props.Name, "bowser's castle", sizeof(Props.Name));
Props.SetText(Props.DebugName, "castle", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "777m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D008FB8;
Props.AIMaximumSeparation = 35.0f;
Props.AIMinimumSeparation = 0.2f;
+4 -3
View File
@@ -67,9 +67,10 @@ ChocoMountain::ChocoMountain() {
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Id = "mk:choco_mountain";
Props.Name = "choco mountain";
Props.DebugName = "mountain";
Props.CourseLength = "687m";
Props.SetText(Props.Name, "choco mountain", sizeof(Props.Name));
Props.SetText(Props.DebugName, "mountain", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "687m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D008F80;
Props.AIMaximumSeparation = 35.0f;
Props.AIMinimumSeparation = 0.3f;
+11
View File
@@ -19,6 +19,7 @@ extern "C" {
#include "staff_ghosts.h"
#include "code_800029B0.h"
#include "render_courses.h"
#include "collision.h"
extern StaffGhost* d_mario_raceway_staff_ghost;
}
@@ -28,6 +29,7 @@ Course::Course() {
// Props.CourseLength = "567m";
// Props.Cup = FLOWER_CUP;
// Props.CupIndex = 3;
Props.TrackModel = NULL;
Props.LakituTowType = (s32) OLakitu::LakituTowType::NORMAL;
Props.AIBehaviour = D_0D008F28;
Props.AIMaximumSeparation = 50.0f;
@@ -86,6 +88,11 @@ void Course::Load(Vtx* vtx, Gfx* gfx) {
void Course::Load() {
if (Props.TrackModel) {
generate_collision_mesh_with_defaults((Gfx*)LOAD_ASSET_RAW(Props.TrackModel));
return;
}
size_t vtxSize = (ResourceGetSizeByName(this->vtx) / sizeof(CourseVtx)) * sizeof(Vtx);
size_t texSegSize;
@@ -227,7 +234,11 @@ void Course::Waypoints(Player* player, int8_t playerId) {
}
void Course::Render(struct UnkStruct_800DC5EC* arg0) {
if (Props.TrackModel) {
gSPDisplayList(gDisplayListHead++, (Gfx*)LOAD_ASSET_RAW(Props.TrackModel));
}
}
void Course::RenderCredits() {
}
void Course::Collision() {
+139 -4
View File
@@ -36,12 +36,12 @@ typedef struct SkyboxColours {
typedef struct Properties {
const char* Id;
const char* Name;
const char* DebugName;
const char* CourseLength;
char Name[128];
char DebugName[128];
char CourseLength[128];
const char* AIBehaviour;
const char* MinimapTexture;
s32 LakituTowType;
int32_t LakituTowType;
IVector2D MinimapDimensions;
float AIMaximumSeparation;
float AIMinimumSeparation;
@@ -63,6 +63,141 @@ typedef struct Properties {
SkyboxColours Skybox;
const course_texture *textures;
enum MusicSeq Sequence;
const char* TrackModel;
#ifdef __cplusplus
nlohmann::json to_json() const {
nlohmann::json j;
j["Id"] = Id ? Id : "";
j["Name"] = Name ? Name : "";
j["DebugName"] = DebugName ? DebugName : "";
j["CourseLength"] = CourseLength ? CourseLength : "";
j["AIBehaviour"] = AIBehaviour ? AIBehaviour : "";
j["MinimapTexture"] = MinimapTexture ? MinimapTexture : "";
j["LakituTowType"] = LakituTowType;
j["MinimapDimensions"] = {MinimapDimensions.X, MinimapDimensions.Z};
j["AIMaximumSeparation"] = AIMaximumSeparation;
j["AIMinimumSeparation"] = AIMinimumSeparation;
j["NearPersp"] = NearPersp;
j["FarPersp"] = FarPersp;
// AIDistance as a JSON array
j["AIDistance"] = std::vector<int16_t>(AIDistance, AIDistance + 32); // gAIDistances array size of 32
j["AISteeringSensitivity"] = AISteeringSensitivity;
// PathSizes - Assuming _struct_gCoursePathSizes_0x10 can be serialized similarly
// j["PathSizes"] = PathSizes; // Implement your serialization logic here
j["D_0D009418"] = { D_0D009418[0], D_0D009418[1], D_0D009418[2], D_0D009418[3] };
j["D_0D009568"] = { D_0D009568[0], D_0D009568[1], D_0D009568[2], D_0D009568[3] };
j["D_0D0096B8"] = { D_0D0096B8[0], D_0D0096B8[1], D_0D0096B8[2], D_0D0096B8[3] };
j["D_0D009808"] = { D_0D009808[0], D_0D009808[1], D_0D009808[2], D_0D009808[3] };
// Serialize arrays PathTable and PathTable2 (convert pointers into a JSON array if possible)
//j["PathTable"] = {{}};
//j["PathTable2"] = {{}};
// Populate PathTable and PathTable2
//j["Clouds"] = Clouds ? nlohmann::json{{"x", Clouds->x, "y", Clouds->y, "z", Clouds->z}} : nullptr;
//j["CloudList"] = CloudList ? nlohmann::json{{"x", CloudList->x, "y", CloudList->y, "z", CloudList->z}} : nullptr;
j["MinimapFinishlineX"] = MinimapFinishlineX;
j["MinimapFinishlineY"] = MinimapFinishlineY;
// SkyboxColors - assuming SkyboxColors can be serialized similarly
// j["Skybox"] = Skybox; // Implement your serialization logic here
j["Sequence"] = static_cast<int>(Sequence);
return j;
}
// Function to load struct from JSON
void from_json(const nlohmann::json& j) {
Id = j.at("Id").get<std::string>().c_str();
// Name = j.at("Name").get<std::string>().c_str();
strncpy(Name, j.at("Name").get<std::string>().c_str(), sizeof(Name) - 1);
Name[sizeof(Name) - 1] = '\0'; // Ensure null termination
// DebugName = j.at("DebugName").get<std::string>().c_str();
strncpy(DebugName, j.at("DebugName").get<std::string>().c_str(), sizeof(DebugName) - 1);
DebugName[sizeof(DebugName) - 1] = '\0'; // Ensure null termination
// CourseLength = j.at("CourseLength").get<std::string>().c_str();
strncpy(CourseLength, j.at("CourseLength").get<std::string>().c_str(), sizeof(CourseLength) - 1);
CourseLength[sizeof(CourseLength) - 1] = '\0'; // Ensure null termination
AIBehaviour = j.at("AIBehaviour").get<std::string>().c_str();
MinimapTexture = j.at("MinimapTexture").get<std::string>().c_str();
LakituTowType = j.at("LakituTowType").get<int>();
MinimapDimensions.X = j.at("MinimapDimensions")[0].get<float>();
MinimapDimensions.Z = j.at("MinimapDimensions")[1].get<float>();
AIMaximumSeparation = j.at("AIMaximumSeparation").get<float>();
AIMinimumSeparation = j.at("AIMinimumSeparation").get<float>();
NearPersp = j.at("NearPersp").get<float>();
FarPersp = j.at("FarPersp").get<float>();
const auto temp = j.at("AIDistance").get<std::vector<int16_t>>();
// Ensure the vector has 32 entries
if (temp.size() == 32) {
// Copy the data into the existing AIDistances array
std::copy(temp.begin(), temp.end(), AIDistance);
} else {
printf("Course::from_json() AIDistance array not size of 32\n");
}
AISteeringSensitivity = j.at("AISteeringSensitivity").get<uint32_t>();
// Deserialize PathSizes and other custom structs if needed
D_0D009418[0] = j.at("D_0D009418")[0].get<float>();
D_0D009418[1] = j.at("D_0D009418")[1].get<float>();
D_0D009418[2] = j.at("D_0D009418")[2].get<float>();
D_0D009418[3] = j.at("D_0D009418")[3].get<float>();
D_0D009568[0] = j.at("D_0D009568")[0].get<float>();
D_0D009568[1] = j.at("D_0D009568")[1].get<float>();
D_0D009568[2] = j.at("D_0D009568")[2].get<float>();
D_0D009568[3] = j.at("D_0D009568")[3].get<float>();
D_0D0096B8[0] = j.at("D_0D0096B8")[0].get<float>();
D_0D0096B8[1] = j.at("D_0D0096B8")[1].get<float>();
D_0D0096B8[2] = j.at("D_0D0096B8")[2].get<float>();
D_0D0096B8[3] = j.at("D_0D0096B8")[3].get<float>();
D_0D009808[0] = j.at("D_0D009808")[0].get<float>();
D_0D009808[1] = j.at("D_0D009808")[1].get<float>();
D_0D009808[2] = j.at("D_0D009808")[2].get<float>();
D_0D009808[3] = j.at("D_0D009808")[3].get<float>();
// Deserialize arrays PathTable and PathTable2 similarly
//Clouds = nullptr; // Deserialize if data is present
//CloudList = nullptr; // Deserialize if data is present
MinimapFinishlineX = j.at("MinimapFinishlineX").get<float>();
MinimapFinishlineY = j.at("MinimapFinishlineY").get<float>();
//textures = nullptr; // Deserialize textures if present
Sequence = static_cast<MusicSeq>(j.at("Sequence").get<int>());
}
void SetText(char* name, const char* title, size_t bufferSize) {
// Copy the title into the name buffer, ensuring it's null-terminated and within bounds
std::strncpy(name, title, bufferSize - 1);
name[bufferSize - 1] = '\0'; // Ensure the string is null-terminated
}
const char* GetName() {
return Name;
}
void New() {
SetText(Name, "", sizeof(Name));
SetText(DebugName, "", sizeof(DebugName));
SetText(CourseLength, "", sizeof(CourseLength));
}
#endif
} Properties;
#ifdef __cplusplus
+4 -3
View File
@@ -74,9 +74,10 @@ DKJungle::DKJungle() {
Props.MinimapTexture = gTextureCourseOutlineDksJungleParkway;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Name = "d.k.'s jungle parkway";
Props.DebugName = "jungle";
Props.CourseLength = "893m";
Props.SetText(Props.Name, "d.k.'s jungle parkway", sizeof(Props.Name));
Props.SetText(Props.DebugName, "jungle", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "893m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D0093C0;
Props.AIMaximumSeparation = 40.0f;
Props.AIMinimumSeparation = 0.1f;
+5 -3
View File
@@ -46,9 +46,11 @@ DoubleDeck::DoubleDeck() {
Props.MinimapTexture = gTextureCourseOutlineDoubleDeck;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Name = "double deck";
Props.DebugName = "deck";
Props.CourseLength = "";
Props.SetText(Props.Name, "double deck", sizeof(Props.Name));
Props.SetText(Props.DebugName, "deck", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D008F18;
Props.AIMaximumSeparation = -1.0f;
Props.AIMinimumSeparation = 0.5f;
+4 -3
View File
@@ -54,9 +54,10 @@ FrappeSnowland::FrappeSnowland() {
Props.MinimapTexture = gTextureCourseOutlineFrappeSnowland;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Name = "frappe snowland";
Props.DebugName = "snow";
Props.CourseLength = "734m";
Props.SetText(Props.Name, "frappe snowland", sizeof(Props.Name));
Props.SetText(Props.DebugName, "snow", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "734m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D0090F8;
Props.AIMaximumSeparation = 50.0f;
Props.AIMinimumSeparation = 0.3f;
+4 -4
View File
@@ -527,9 +527,9 @@ Harbour::Harbour() {
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Id = "mk:harbour";
Props.Name = "Harbour";
Props.DebugName = "harbour";
Props.CourseLength = "99m";
Props.SetText(Props.Name, "Harbour", sizeof(Props.Name));
Props.SetText(Props.DebugName, "harbour", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "99m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D008F28;
Props.AIMaximumSeparation = 50.0f;
@@ -727,7 +727,7 @@ void Harbour::BeginPlay() {
// gWorldInstance.AddObject(new OCheepCheep(FVector(0, 40, 0), OCheepCheep::CheepType::RACE, IPathSpan(0, 10)));
// gWorldInstance.AddObject(new OTrophy(FVector(0,0,0), OTrophy::TrophyType::GOLD, OTrophy::Behaviour::GO_FISH));
//gWorldInstance.AddObject(new OSnowman(FVector(0, 0, 0)));
//gWorldInstance.AddObject(new OTrashBin(FVector(0.0f, 0.0f, 0.0f), FRotation(0, 90, 0), 1.0f, OTrashBin::Behaviour::MUNCHING));
//gWorldInstance.AddObject(new OTrashBin(FVector(0.0f, 0.0f, 0.0f), IRotator(0, 90, 0), 1.0f, OTrashBin::Behaviour::MUNCHING));
//gWorldInstance.AddObject(new OHedgehog(FVector(0, 0, 0), FVector2D(0, -200), 9));
//gWorldInstance.AddObject(new OFlagpole(FVector(0, 0, -200), 0x400));
+4 -3
View File
@@ -59,9 +59,10 @@ KalimariDesert::KalimariDesert() {
Props.MinimapTexture = gTextureCourseOutlineKalimariDesert;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Name = "kalimari desert";
Props.DebugName = "desert";
Props.CourseLength = "753m";
Props.SetText(Props.Name, "kalimari desert", sizeof(Props.Name));
Props.SetText(Props.DebugName, "desert", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "753m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D009260;
Props.AIMaximumSeparation = 50.0f;
Props.AIMinimumSeparation = 0.3f;
+4 -3
View File
@@ -65,9 +65,10 @@ KoopaTroopaBeach::KoopaTroopaBeach() {
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Id = "mk:koopa_beach";
Props.Name = "koopa troopa beach";
Props.DebugName = "beach";
Props.CourseLength = "691m";
Props.SetText(Props.Name, "koopa troopa beach", sizeof(Props.Name));
Props.SetText(Props.DebugName, "beach", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "691m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D009158;
Props.AIMaximumSeparation = 50.0f;
Props.AIMinimumSeparation = 0.5f;
+4 -3
View File
@@ -91,9 +91,10 @@ LuigiRaceway::LuigiRaceway() {
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Id = "mk:luigi_raceway";
Props.Name = "luigi raceway";
Props.DebugName = "l circuit";
Props.CourseLength = "717m";
Props.SetText(Props.Name, "luigi raceway", sizeof(Props.Name));
Props.SetText(Props.DebugName, "l circuit", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "717m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D0091E8;
Props.AIMaximumSeparation = 50.0f;
Props.AIMinimumSeparation = 0.7f;
+3 -3
View File
@@ -77,9 +77,9 @@ MarioRaceway::MarioRaceway() {
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Id = "mk:mario_raceway";
Props.Name = "Mario Raceway";
Props.DebugName = "m circuit";
Props.CourseLength = "567m";
Props.SetText(Props.Name, "mario raceway", sizeof(Props.Name));
Props.SetText(Props.DebugName, "m circuit", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "567m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D008F28;
Props.AIMaximumSeparation = 50.0f;
+4 -3
View File
@@ -77,9 +77,10 @@ MooMooFarm::MooMooFarm() {
Props.MinimapTexture = gTextureCourseOutlineMooMooFarm;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Name = "moo moo farm";
Props.DebugName = "farm";
Props.CourseLength = "527m";
Props.SetText(Props.Name, "moo moo farm", sizeof(Props.Name));
Props.SetText(Props.DebugName, "farm", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "527m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D009210;
Props.AIMaximumSeparation = 50.0f;
Props.AIMinimumSeparation = 0.5f;
+4 -3
View File
@@ -92,9 +92,10 @@ PodiumCeremony::PodiumCeremony() {
Props.textures = podium_ceremony_textures;
Props.MinimapDimensions = IVector2D(0, 0);
Props.Name = "royal raceway";
Props.DebugName = "p circuit";
Props.CourseLength = "1025m";
Props.SetText(Props.Name, "royal raceway", sizeof(Props.Name));
Props.SetText(Props.DebugName, "p circuit", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "1025m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D009188;
Props.AIMaximumSeparation = 50.0f;
Props.AIMinimumSeparation = 0.4f;
+4 -3
View File
@@ -51,9 +51,10 @@ RainbowRoad::RainbowRoad() {
Props.MinimapTexture = gTextureCourseOutlineRainbowRoad;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Name = "rainbow road";
Props.DebugName = "rainbow";
Props.CourseLength = "2000m";
Props.SetText(Props.Name, "rainbow road", sizeof(Props.Name));
Props.SetText(Props.DebugName, "rainbow", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "2000m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D0092C8;
Props.AIMaximumSeparation = 50.0f;
Props.AIMinimumSeparation = 0.4f;
+4 -3
View File
@@ -88,9 +88,10 @@ RoyalRaceway::RoyalRaceway() {
Props.MinimapTexture = gTextureCourseOutlineRoyalRaceway;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Name = "royal raceway";
Props.DebugName = "p circuit";
Props.CourseLength = "1025m";
Props.SetText(Props.Name, "royal raceway", sizeof(Props.Name));
Props.SetText(Props.DebugName, "p circuit", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "1025m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D009188;
Props.AIMaximumSeparation = 50.0f;
Props.AIMinimumSeparation = 0.4f;
+3 -3
View File
@@ -51,9 +51,9 @@ SherbetLand::SherbetLand() {
Props.MinimapTexture = gTextureCourseOutlineSherbetLand;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Name = "sherbet land";
Props.DebugName = "sherbet";
Props.CourseLength = "756m";
Props.SetText(Props.Name, "sherbet land", sizeof(Props.Name));
Props.SetText(Props.DebugName, "sherbet", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "756m", sizeof(Props.CourseLength));
Props.LakituTowType = (s32)OLakitu::LakituTowType::ICE;
+4 -3
View File
@@ -67,9 +67,10 @@ Skyscraper::Skyscraper() {
Props.MinimapTexture = gTextureCourseOutlineSkyscraper;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Name = "skyscraper";
Props.DebugName = "skyscraper";
Props.CourseLength = "";
Props.SetText(Props.Name, "skyscraper", sizeof(Props.Name));
Props.SetText(Props.DebugName, "skyscraper", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D008F18;
Props.AIMaximumSeparation = -1.0f;
Props.AIMinimumSeparation = 0.5f;
+5 -4
View File
@@ -69,9 +69,10 @@ TestCourse::TestCourse() {
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Id = "mk:test_course";
Props.Name = "Test Course";
Props.DebugName = "test track";
Props.CourseLength = "100m";
Props.SetText(Props.Name, "Test Course", sizeof(Props.Name));
Props.SetText(Props.DebugName, "test track", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "100m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D008F28;
Props.AIMaximumSeparation = 50.0f;
@@ -266,7 +267,7 @@ void TestCourse::BeginPlay() {
// gWorldInstance.AddObject(new OCheepCheep(FVector(0, 40, 0), OCheepCheep::CheepType::RACE, IPathSpan(0, 10)));
// gWorldInstance.AddObject(new OTrophy(FVector(0,0,0), OTrophy::TrophyType::GOLD, OTrophy::Behaviour::GO_FISH));
//gWorldInstance.AddObject(new OSnowman(FVector(0, 0, 0)));
//gWorldInstance.AddObject(new OTrashBin(FVector(0.0f, 0.0f, 0.0f), FRotation(0, 90, 0), 1.0f, OTrashBin::Behaviour::MUNCHING));
//gWorldInstance.AddObject(new OTrashBin(FVector(0.0f, 0.0f, 0.0f), IRotator(0, 90, 0), 1.0f, OTrashBin::Behaviour::MUNCHING));
//gWorldInstance.AddObject(new OHedgehog(FVector(0, 0, 0), FVector2D(0, -200), 9));
//gWorldInstance.AddObject(new OFlagpole(FVector(0, 0, -200), 0x400));
+4 -3
View File
@@ -72,9 +72,10 @@ ToadsTurnpike::ToadsTurnpike() {
Props.MinimapTexture = gTextureCourseOutlineToadsTurnpike;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Name = "toad's turnpike";
Props.DebugName = "highway";
Props.CourseLength = "1036m";
Props.SetText(Props.Name, "toad's turnpike", sizeof(Props.Name));
Props.SetText(Props.DebugName, "highway", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "1036m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D009238;
Props.AIMaximumSeparation = 50.0f;
Props.AIMinimumSeparation = 0.5f;
+4 -3
View File
@@ -68,9 +68,10 @@ WarioStadium::WarioStadium() {
Props.MinimapTexture = gTextureCourseOutlineWarioStadium;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Name = "wario stadium";
Props.DebugName = "stadium";
Props.CourseLength = "1591m";
Props.SetText(Props.Name, "wario stadium", sizeof(Props.Name));
Props.SetText(Props.DebugName, "stadium", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "1591m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D009310;
Props.AIMaximumSeparation = 50.0f;
Props.AIMinimumSeparation = 0.6f;
+4 -3
View File
@@ -63,9 +63,10 @@ YoshiValley::YoshiValley() {
Props.MinimapTexture = gTextureCourseOutlineYoshiValley;
Props.MinimapDimensions = IVector2D(ResourceGetTexWidthByName(Props.MinimapTexture), ResourceGetTexHeightByName(Props.MinimapTexture));
Props.Name = "yoshi valley";
Props.DebugName = "maze";
Props.CourseLength = "772m";
Props.SetText(Props.Name, "yoshi valley", sizeof(Props.Name));
Props.SetText(Props.DebugName, "maze", sizeof(Props.DebugName));
Props.SetText(Props.CourseLength, "772m", sizeof(Props.CourseLength));
Props.AIBehaviour = D_0D0090B8;
Props.AIMaximumSeparation = 35.0f;
Props.AIMinimumSeparation = 0.0f;
+20 -3
View File
@@ -32,7 +32,7 @@ namespace Editor {
void Editor::Load() {
printf("Editor: Loading Editor...\n");
eObjectPicker.Load();;
eObjectPicker.Load();
for (auto& object : eGameObjects) {
GenerateCollisionMesh(object, object->Model, 1.0f);
object->Load();
@@ -52,7 +52,7 @@ namespace Editor {
eGameObjects.erase(
std::remove_if(eGameObjects.begin(), eGameObjects.end(),
[](const auto& object) { return (*object->DespawnFlag) == object->DespawnValue; printf("DELETED OBJ\n"); }),
[](const auto& object) { return (*object->DespawnFlag) == object->DespawnValue; }),
eGameObjects.end());
if (isMouseDown && !wasMouseDown) {
@@ -100,7 +100,9 @@ namespace Editor {
}
void Editor::AddObject(const char* name, FVector* pos, Vec3s* rot, FVector* scale, Gfx* model, float collScale, GameObject::CollisionType collision, float boundingBoxSize, int32_t* despawnFlag, int32_t despawnValue) {
if (model != NULL) {
//printf("After AddObj: Pos(%f, %f, %f), Name: %s, Model: %s\n",
// pos->x, pos->y, pos->z, name, model);
if (model != nullptr) {
eGameObjects.push_back(new GameObject(name, pos, rot, scale, model, {}, collision, boundingBoxSize, despawnFlag, despawnValue));
GenerateCollisionMesh(eGameObjects.back(), model, collScale);
} else { // to bounding box or sphere collision
@@ -117,6 +119,17 @@ namespace Editor {
for (auto& obj : eGameObjects) {
delete obj;
}
eGameObjects.clear();
}
void Editor::DeleteObject() {
GameObject* obj = eObjectPicker.eGizmo._selected;
if (obj) {
*obj->DespawnFlag = obj->DespawnValue;
obj = nullptr;
eObjectPicker._selected = nullptr;
}
}
void Editor::ClearMatrixPool() {
@@ -137,4 +150,8 @@ namespace Editor {
eObjectPicker.eGizmo.dimensions.MinZ = minZ + -1000;
eObjectPicker.eGizmo.dimensions.MaxZ = maxZ + 1000;
}
void Editor::NewTrack() {
auto course = gWorldInstance.CurrentCourse = new Course();
course->Props.New();
}
}
+3 -1
View File
@@ -10,7 +10,7 @@
#include "ObjectPicker.h"
namespace Editor {
class ObjectPicker;
class Editor {
public:
Editor();
@@ -29,6 +29,8 @@ public:
void SelectObjectFromSceneExplorer(GameObject* object);
void SetLevelDimensions(s16 minX, s16 maxX, s16 minZ, s16 maxZ, s16 minY, s16 maxY);
void ClearMatrixPool();
void DeleteObject();
void NewTrack();
private:
bool _draw = false;
+85
View File
@@ -0,0 +1,85 @@
#include "SaveLevel.h"
#include "port/Game.h"
#include "CoreMath.h"
#include "World.h"
#include "GameObject.h"
#include <iostream>
#include <fstream>
#include <nlohmann/json.hpp>
namespace Editor {
// SaveLevel::SaveLevel() {}
void SaveLevel() {
auto props = gWorldInstance.CurrentCourse->Props;
nlohmann::json data;
data["Props"] = props.to_json();
nlohmann::json actors;
/* for (const auto& actor : gWorldInstance.StaticMeshActors) {
actors.push_back(actor->to_json());
}
data["Actors"] = actors;*/
std::ofstream file("track.json");
file << data.dump(4); // Pretty print with indent
}
void LoadLevel() {
// Open the JSON file
std::ifstream file("track.json");
if (!file.is_open()) {
std::cerr << "Failed to open track.json for reading!" << std::endl;
return;
}
// Check if level data file is empty
if (file.peek() == std::ifstream::traits_type::eof()) {
return;
}
// Parse the JSON file into a nlohmann::json object
nlohmann::json data;
file >> data;
// Load the Props (deserialize it)
if (data.contains("Props")) {
auto& propsJson = data["Props"];
gWorldInstance.CurrentCourse->Props.from_json(propsJson); // Assuming you have a `from_json` function
} else {
std::cerr << "Props data not found in the JSON file!" << std::endl;
}
// Load the Actors (deserialize them)
if (data.contains("Actors")) {
auto& actorsJson = data["Actors"];
gWorldInstance.StaticMeshActors.clear(); // Clear existing actors, if any
for (const auto& actorJson : actorsJson) {
Load_AddStaticMeshActor(actorJson);
}
} else {
std::cerr << "Actors data not found in the JSON file!" << std::endl;
}
// Close the file after loading
file.close();
}
void Load_AddStaticMeshActor(const nlohmann::json& actorJson) {
gWorldInstance.StaticMeshActors.push_back(new StaticMeshActor("", FVector(0, 0, 0), IRotator(0, 0, 0), FVector(1, 1, 1), "", nullptr));
auto actor = gWorldInstance.StaticMeshActors.back();
actor->from_json(actorJson);
printf("After from_json: Pos(%f, %f, %f), Name: %s, Model: %s\n",
actor->Pos.x, actor->Pos.y, actor->Pos.z, actor->Name.c_str(), actor->Model.c_str());
gEditor.AddObject(actor->Name.c_str(), &actor->Pos, (Vec3s*)&actor->Rot, &actor->Scale, (Gfx*) nullptr, 1.0f,
GameObject::CollisionType::BOUNDING_BOX, 20.0f, (int32_t*) &actor->bPendingDestroy, (int32_t) 1);
}
}
+8
View File
@@ -0,0 +1,8 @@
#include <libultraship.h>
#include "Course.h"
namespace Editor {
void SaveLevel();
void LoadLevel();
void Load_AddStaticMeshActor(const nlohmann::json& actorJson);
}
+1 -1
View File
@@ -18,7 +18,7 @@ const char* sBoardwalkTexList[] = { gTextureBat1, gTextureBat2, gTextureBat3, gT
size_t OBat::_count = 0;
OBat::OBat(const FVector& pos, const FRotation& rot) {
OBat::OBat(const FVector& pos, const IRotator& rot) {
Name = "Bat";
find_unused_obj_index(&_objectIndex);
+2 -2
View File
@@ -21,14 +21,14 @@ extern "C" {
/**
* OBat
*
* FRotation does not appear to do anything.
* IRotator does not appear to do anything.
* Could not find where origin_pos was at.
* So pos does not work either
*
*/
class OBat : public OObject {
public:
explicit OBat(const FVector& pos, const FRotation& rot);
explicit OBat(const FVector& pos, const IRotator& rot);
~OBat() {
_count--;
+1 -1
View File
@@ -18,6 +18,6 @@ void OObject::Tick60fps() {}
void OObject::Draw(s32 cameraId) { }
void OObject::Expire() { }
void OObject::Destroy() {
PendingDestroy = true;
bPendingDestroy = true;
}
void OObject::Reset() { }
+1 -1
View File
@@ -12,7 +12,7 @@ public:
uint8_t uuid[16];
Object o;
const char* Name = "";
bool PendingDestroy = false;
bool bPendingDestroy = false;
s32 _objectIndex = -1;
virtual ~OObject() = default;
+1 -1
View File
@@ -20,7 +20,7 @@ extern "C" {
#define DEGREES_FLOAT_TO_SHORT(Degrees) ((s16)((Degrees) * (0x8000 / 180.0f)))
OTrashBin::OTrashBin(const FVector& pos, const FRotation& rotation, f32 scale, OTrashBin::Behaviour bhv) {
OTrashBin::OTrashBin(const FVector& pos, const IRotator& rotation, f32 scale, OTrashBin::Behaviour bhv) {
Name = "Trashbin";
_pos = pos;
_rot = rotation;
+2 -2
View File
@@ -24,7 +24,7 @@ public:
STATIC, // The lid stays shut
MUNCHING // The lid opens/closes in a scary munching manner
};
explicit OTrashBin(const FVector& pos, const FRotation& rotation, f32 scale, OTrashBin::Behaviour bhv);
explicit OTrashBin(const FVector& pos, const IRotator& rotation, f32 scale, OTrashBin::Behaviour bhv);
virtual void Tick() override;
virtual void Draw(s32 cameraId) override;
@@ -38,7 +38,7 @@ private:
Behaviour _bhv;
FVector _pos;
FRotation _rot;
IRotator _rot;
float _scale;
size_t _idx;
bool _drawBin = false;
-2
View File
@@ -45,8 +45,6 @@ float gInterpolationStep = 0.0f;
GameEngine* GameEngine::Instance;
GameEngine::GameEngine() {
std::vector<std::string> archiveFiles;
const std::string main_path = Ship::Context::GetPathRelativeToAppDirectory("spaghetti.o2r");
const std::string assets_path = Ship::Context::GetPathRelativeToAppDirectory("ship.o2r");
+1
View File
@@ -37,6 +37,7 @@ class GameEngine {
std::vector<CtlEntry*> banksTable;
std::vector<std::string> sequenceTable;
std::vector<AudioSequenceData*> audioSequenceTable;
std::vector<std::string> archiveFiles;
ImFont* fontStandard;
ImFont* fontStandardLarger;
+15
View File
@@ -47,6 +47,7 @@
#include "engine/editor/Editor.h"
#include "engine/editor/EditorMath.h"
#include "engine/editor/SaveLevel.h"
extern "C" {
#include "main.h"
@@ -211,6 +212,9 @@ void HM_DrawIntro() {
gMenuIntro.HM_DrawIntro();
}
void CM_LoadLevelProps() {
Editor::LoadLevel();
}
World* GetWorld(void) {
return &gWorldInstance;
@@ -377,6 +381,11 @@ void CM_DrawActors(Camera* camera, struct Actor* actor) {
}
}
void CM_DrawStaticMeshActors() {
printf("DRAW\n");
gWorldInstance.DrawStaticMeshActors();
}
void CM_BeginPlay() {
Course* course = gWorldInstance.CurrentCourse;
@@ -674,8 +683,14 @@ void CM_CleanWorld(void) {
for (auto& emitter : world->Emitters) {
delete emitter;
}
for (auto& actor : world->StaticMeshActors) {
delete actor;
}
gEditor.ClearObjects();
gWorldInstance.Actors.clear();
gWorldInstance.StaticMeshActors.clear();
gWorldInstance.Objects.clear();
gWorldInstance.Emitters.clear();
gWorldInstance.Lakitus.clear();
+3
View File
@@ -26,6 +26,8 @@ void HM_InitIntro(void);
void HM_TickIntro(void);
void HM_DrawIntro(void);
void CM_LoadLevelProps();
void CM_DisplayBattleBombKart(s32 playerId, s32 primAlpha);
void CM_DrawBattleBombKarts(s32 cameraId);
@@ -65,6 +67,7 @@ bool CM_DoesFinishlineExist();
void CM_InitClouds();
void CM_DrawActors(Camera* camera, struct Actor* actor);
void CM_DrawStaticMeshActors();
void CM_TickObjects();
void CM_TickObjects60fps();
+64 -3
View File
@@ -2,14 +2,19 @@
#include "port/ui/PortMenu.h"
#include "UIWidgets.h"
#include "libultraship/src/Context.h"
#include "port/Engine.h"
#include <imgui.h>
#include <map>
#include <filesystem>
#include <iostream>
#include <libultraship/libultraship.h>
#include <spdlog/fmt/fmt.h>
#include "spdlog/formatter.h"
#include <common_structs.h>
#include <defines.h>
#include "CoreMath.h"
#include "World.h"
namespace Editor {
@@ -18,9 +23,65 @@ namespace Editor {
}
void ContentBrowserWindow::DrawElement() {
ImGui::Text("This is your Content browser Lab editor window!");
if (ImGui::Button("Click Me")) {
// Handle button click (example)
static bool refresh = true;
// List the available mods/o2r files
// for (size_t i = 0; i < GameEngine::Instance->archiveFiles.size(); i++) {
// auto str = GameEngine::Instance->archiveFiles[i];
// if (str.starts_with("./mods/")) {
// ImGui::Text(GameEngine::Instance->archiveFiles[i].c_str());
// }
// }
if (ImGui::Button(ICON_FA_REFRESH)) {
refresh = true;
}
// Query content in o2r and add them to Content
if (refresh) {
refresh = false;
FindContent();
return;
}
// Display entries in Content
for (const auto& file : Content) {
if (ImGui::Button(file.c_str())) {
int coll;
//printf("ContentBrowser.cpp: name: %s\n", test.c_str());
printf("TEST %s\n", file.c_str());
std::string name = file.substr(file.find_last_of('/') + 1);
printf("NAME %s\n", name.c_str());
auto actor = gWorldInstance.AddStaticMeshActor(name, FVector(50, 100, 0), IRotator(0, 90, 0), FVector(1, 1, 1), "__OTR__" + file, &coll);
// This is required because ptr gets cleaned up.
actor->Model = "__OTR__" + file;
}
}
}
void ContentBrowserWindow::FindContent() {
std::list<std::string> myList = {"objects/*"};
std::list<std::string> myList2 = {""};
Content.clear();
auto ptr = GameEngine::Instance->context->GetResourceManager()->GetArchiveManager()->ListFiles(myList, myList2);
if (ptr) {
auto files = *ptr;
for (const auto& file : files) {
if (file.find("/mat_") != std::string::npos) {
continue;
} else if (file.size() >= 6 && file.substr(file.size() - 6, 5) == "_tri_" && isdigit(file.back())) {
// ends with _tri_#
continue;
} else if (file.size() >= 6 && file.substr(file.size() - 6, 5) == "_vtx_" && isdigit(file.back())) {
// ends with _vtx_#
continue;
}
Content.push_back(file);
}
}
}
}
+3
View File
@@ -7,9 +7,12 @@ class ContentBrowserWindow : public Ship::GuiWindow {
public:
using Ship::GuiWindow::GuiWindow;
~ContentBrowserWindow();
std::vector<std::string> Content;
protected:
void InitElement() override {};
void DrawElement() override;
void UpdateElement() override {};
void FindContent();
};
}
+25 -2
View File
@@ -10,6 +10,8 @@
#include "spdlog/formatter.h"
#include <common_structs.h>
#include <defines.h>
#include "port/Game.h"
#include "engine/editor/SaveLevel.h"
extern "C" {
#include "code_800029B0.h"
@@ -30,8 +32,22 @@ namespace Editor {
static bool toggleGroundSnap = CVarGetInteger("gEditorSnapToGround", 0);
static bool toggleBoundary = CVarGetInteger("gEditorBoundary", 0);
static int selectedTool = 0; // 0: Move, 1: Rotate, 2: Scale
static int selectedPlayTool = 0; // 0: Play, 1: Paused
if (ImGui::Button(ICON_FA_FILE_TEXT_O, ImVec2(50, 25))) {
gEditor.NewTrack();
gWorldInstance.ClearWorld();
gIsEditorPaused = true;
}
ImGui::SameLine();
// Save button
if (ImGui::Button(ICON_FA_FLOPPY_O, ImVec2(50, 25))) {
SaveLevel();
}
ImGui::SameLine();
ImVec4 defaultColor = ImGui::GetStyle().Colors[ImGuiCol_Button];
// Function to check and highlight the selected button
auto ToolButton = [&](const char* label, int id) {
@@ -141,5 +157,12 @@ namespace Editor {
gIsHUDVisible = !gIsHUDVisible;
}
ImGui::PopStyleColor();
ImGui::SameLine();
// Delete
if (ImGui::Button(ICON_FA_TRASH_O, ImVec2(50, 25))) {
gEditor.DeleteObject();
}
}
}
+16 -11
View File
@@ -28,15 +28,15 @@ namespace Editor {
}
void TrackPropertiesWindow::DrawElement() {
static char idBuffer[128] = "mk:mario_raceway";
static char nameBuffer[128] = "Mario Raceway";
static char debugNameBuffer[128] = "m circuit";
static char lengthBuffer[128] = "567m";
static char idBuffer[256] = "mk:mario_raceway";
static char nameBuffer[256] = "Mario Raceway";
static char debugNameBuffer[256] = "m circuit";
static char lengthBuffer[256] = "567m";
ImGui::InputText("ID", idBuffer, IM_ARRAYSIZE(idBuffer));
ImGui::InputText("Name", nameBuffer, IM_ARRAYSIZE(nameBuffer));
ImGui::InputText("Debug Name", debugNameBuffer, IM_ARRAYSIZE(debugNameBuffer));
ImGui::InputText("Course Length", lengthBuffer, IM_ARRAYSIZE(lengthBuffer));
ImGui::InputText("Name", gWorldInstance.CurrentCourse->Props.Name, IM_ARRAYSIZE(nameBuffer));
ImGui::InputText("Debug Name", gWorldInstance.CurrentCourse->Props.DebugName, IM_ARRAYSIZE(debugNameBuffer));
ImGui::InputText("Course Length", gWorldInstance.CurrentCourse->Props.CourseLength, IM_ARRAYSIZE(lengthBuffer));
ImGui::InputFloat("Minimap Finishline X Offset", &gWorldInstance.CurrentCourse->Props.MinimapFinishlineX);
ImGui::InputFloat("Minimap Finishline Y Offset", &gWorldInstance.CurrentCourse->Props.MinimapFinishlineY);
@@ -55,12 +55,17 @@ namespace Editor {
TrackPropertiesWindow::DrawLight();
}
if (ImGui::CollapsingHeader("AI")) {
ImGui::InputFloat("AI Max Separation", &gWorldInstance.CurrentCourse->Props.AIMaximumSeparation);
ImGui::InputFloat("AI Min Separation", &gWorldInstance.CurrentCourse->Props.AIMinimumSeparation);
ImGui::InputInt("AI Steering Sensitivity", (int*)&gWorldInstance.CurrentCourse->Props.AISteeringSensitivity);
ImGui::Separator();
for (size_t i = 0; i < 32; i++) {
ImGui::InputScalar(("Element " + std::to_string(i)).c_str(), ImGuiDataType_S16, &gWorldInstance.CurrentCourse->Props.AIDistance[i]);
}
}
if (ImGui::CollapsingHeader("Random Junk")) {
@@ -72,20 +77,20 @@ namespace Editor {
for (size_t i = 0; i < 4; i++) {
ImGui::InputFloat(fmt::format("D_0D009418[{}]", i).c_str(), &gWorldInstance.CurrentCourse->Props.D_0D009568[i]);
ImGui::InputFloat(fmt::format("D_0D009568[{}]", i).c_str(), &gWorldInstance.CurrentCourse->Props.D_0D009568[i]);
}
ImGui::Separator();
for (size_t i = 0; i < 4; i++) {
ImGui::InputFloat(fmt::format("D_0D009418[{}]", i).c_str(), &gWorldInstance.CurrentCourse->Props.D_0D0096B8[i]);
ImGui::InputFloat(fmt::format("D_0D0096B8[{}]", i).c_str(), &gWorldInstance.CurrentCourse->Props.D_0D0096B8[i]);
}
ImGui::Separator();
for (size_t i = 0; i < 4; i++) {
ImGui::InputFloat(fmt::format("D_0D009418[{}]", i).c_str(), &gWorldInstance.CurrentCourse->Props.D_0D009808[i]);
ImGui::InputFloat(fmt::format("D_0D009808[{}]", i).c_str(), &gWorldInstance.CurrentCourse->Props.D_0D009808[i]);
}
}
+2 -1
View File
@@ -1227,6 +1227,7 @@ void init_actors_and_load_textures(void) {
init_red_shell_texture();
destroy_all_actors();
CM_CleanWorld();
CM_LoadLevelProps();
CM_BeginPlay();
spawn_course_actors();
@@ -1387,7 +1388,7 @@ s16 try_remove_destructable_item(Vec3f pos, Vec3s rot, Vec3f velocity, s16 actor
return -1;
}
// returns actor index if any slot avaible returns -1
// returns actor index if any available actor type is -1
s16 add_actor_to_empty_slot(Vec3f pos, Vec3s rot, Vec3f velocity, s16 actorType) {
size_t index;
+1
View File
@@ -854,6 +854,7 @@ void render_screens(s32 mode, s32 cameraId, s32 playerId) {
render_set_position(matrix, 0);
}
render_course_actors(screen);
CM_DrawStaticMeshActors();
render_object(mode);
switch (screenId) {
case 0: