diff --git a/src/code_80005FD0.c b/src/code_80005FD0.c index 639d45435..0e3c362dd 100644 --- a/src/code_80005FD0.c +++ b/src/code_80005FD0.c @@ -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]; diff --git a/src/ending/code_80280000.c b/src/ending/code_80280000.c index 927fcce2e..be1706ed5 100644 --- a/src/ending/code_80280000.c +++ b/src/ending/code_80280000.c @@ -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(); diff --git a/src/engine/CoreMath.h b/src/engine/CoreMath.h index 83dd540ee..fe589d87f 100644 --- a/src/engine/CoreMath.h +++ b/src/engine/CoreMath.h @@ -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 }; diff --git a/src/engine/GarbageCollector.cpp b/src/engine/GarbageCollector.cpp index 926620fe1..9832aa29d 100644 --- a/src/engine/GarbageCollector.cpp +++ b/src/engine/GarbageCollector.cpp @@ -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++; } -} \ No newline at end of file +} diff --git a/src/engine/GarbageCollector.h b/src/engine/GarbageCollector.h index 337d90e4a..c96f1c36a 100644 --- a/src/engine/GarbageCollector.h +++ b/src/engine/GarbageCollector.h @@ -8,4 +8,5 @@ void RunGarbageCollector(); void CleanActors(); +void CleanStaticMeshActors(); void CleanObjects(); diff --git a/src/engine/HM_Intro.cpp b/src/engine/HM_Intro.cpp index a1c9b3870..9341bce7a 100644 --- a/src/engine/HM_Intro.cpp +++ b/src/engine/HM_Intro.cpp @@ -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); diff --git a/src/engine/HM_Intro.h b/src/engine/HM_Intro.h index 0b4ae4d6d..227d64755 100644 --- a/src/engine/HM_Intro.h +++ b/src/engine/HM_Intro.h @@ -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" { diff --git a/src/engine/Matrix.cpp b/src/engine/Matrix.cpp index 354389db9..14cf7edfb 100644 --- a/src/engine/Matrix.cpp +++ b/src/engine/Matrix.cpp @@ -4,6 +4,8 @@ extern "C" { #include "common_structs.h" +#include "math_util.h" +#include "math_util_2.h" } void AddMatrix(std::vector& 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(); } } + diff --git a/src/engine/Matrix.h b/src/engine/Matrix.h index f72af76c4..31c37b52b 100644 --- a/src/engine/Matrix.h +++ b/src/engine/Matrix.h @@ -4,9 +4,11 @@ #include #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); diff --git a/src/engine/StaticMeshActor.cpp b/src/engine/StaticMeshActor.cpp new file mode 100644 index 000000000..840286856 --- /dev/null +++ b/src/engine/StaticMeshActor.cpp @@ -0,0 +1,34 @@ +#include "StaticMeshActor.h" +#include +#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; +} diff --git a/src/engine/StaticMeshActor.h b/src/engine/StaticMeshActor.h new file mode 100644 index 000000000..02573744f --- /dev/null +++ b/src/engine/StaticMeshActor.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include "CoreMath.h" +#include + +// 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(); + Pos = FVector(j.at("Position")[0].get(), j.at("Position")[1].get(), j.at("Position")[2].get()); + Rot = IRotator(j.at("Rotation")[0].get(), j.at("Rotation")[1].get(), j.at("Rotation")[2].get()); + Scale = FVector(j.at("Scale")[0].get(), j.at("Scale")[1].get(), j.at("Scale")[2].get()); + + // Deserialize the Model string + Model = j.at("Model").get(); + + // 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()); + //} else { + // If Collision is not present or is null, set it to nullptr + Collision = nullptr; + //} + } + + virtual void Draw(); + virtual void Destroy(); +}; diff --git a/src/engine/World.cpp b/src/engine/World.cpp index aae54ab10..487178057 100644 --- a/src/engine/World.cpp +++ b/src/engine/World.cpp @@ -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 +} diff --git a/src/engine/World.h b/src/engine/World.h index c68ee91ef..aec00c228 100644 --- a/src/engine/World.h +++ b/src/engine/World.h @@ -18,6 +18,7 @@ #include #include #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 Cups; size_t CupIndex = 1; + std::vector StaticMeshActors; std::vector Actors; std::vector Objects; std::vector Emitters; diff --git a/src/engine/actors/Ship.cpp b/src/engine/actors/Ship.cpp index 9ab5dd1a3..3948edecd 100644 --- a/src/engine/actors/Ship.cpp +++ b/src/engine/actors/Ship.cpp @@ -1,6 +1,7 @@ #include "Ship.h" #include +#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); diff --git a/src/engine/actors/Ship.h b/src/engine/actors/Ship.h index 59ceb68a8..4d4f9e8c0 100644 --- a/src/engine/actors/Ship.h +++ b/src/engine/actors/Ship.h @@ -28,7 +28,7 @@ public: FVector Spawn; FVector Pos; - FRotation Rot = {0, 0, 0}; + IRotator Rot = {0, 0, 0}; private: Gfx* _skin; }; diff --git a/src/engine/actors/SpaghettiShip.cpp b/src/engine/actors/SpaghettiShip.cpp index 717add1d1..357985487 100644 --- a/src/engine/actors/SpaghettiShip.cpp +++ b/src/engine/actors/SpaghettiShip.cpp @@ -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) {} diff --git a/src/engine/actors/SpaghettiShip.h b/src/engine/actors/SpaghettiShip.h index 1d0d656c5..3e5b4d382 100644 --- a/src/engine/actors/SpaghettiShip.h +++ b/src/engine/actors/SpaghettiShip.h @@ -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; }; diff --git a/src/engine/actors/Starship.h b/src/engine/actors/Starship.h index 390aed2ea..3599f0304 100644 --- a/src/engine/actors/Starship.h +++ b/src/engine/actors/Starship.h @@ -21,5 +21,5 @@ public: FVector Spawn; FVector Pos; - FRotation Rot = {0, 0, 0}; + IRotator Rot = {0, 0, 0}; }; diff --git a/src/engine/courses/BansheeBoardwalk.cpp b/src/engine/courses/BansheeBoardwalk.cpp index 505069d1f..a2f9b22d9 100644 --- a/src/engine/courses/BansheeBoardwalk.cpp +++ b/src/engine/courses/BansheeBoardwalk.cpp @@ -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))); } diff --git a/src/engine/courses/BigDonut.cpp b/src/engine/courses/BigDonut.cpp index 5cbb38c13..e95a0f222 100644 --- a/src/engine/courses/BigDonut.cpp +++ b/src/engine/courses/BigDonut.cpp @@ -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; diff --git a/src/engine/courses/BlockFort.cpp b/src/engine/courses/BlockFort.cpp index 67ef10d09..d10a0a1e6 100644 --- a/src/engine/courses/BlockFort.cpp +++ b/src/engine/courses/BlockFort.cpp @@ -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; diff --git a/src/engine/courses/BowsersCastle.cpp b/src/engine/courses/BowsersCastle.cpp index 384a85b1a..e12cee10b 100644 --- a/src/engine/courses/BowsersCastle.cpp +++ b/src/engine/courses/BowsersCastle.cpp @@ -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; diff --git a/src/engine/courses/ChocoMountain.cpp b/src/engine/courses/ChocoMountain.cpp index 3115c6fff..d5b38e24c 100644 --- a/src/engine/courses/ChocoMountain.cpp +++ b/src/engine/courses/ChocoMountain.cpp @@ -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; diff --git a/src/engine/courses/Course.cpp b/src/engine/courses/Course.cpp index 40cb4f1bd..81e811f82 100644 --- a/src/engine/courses/Course.cpp +++ b/src/engine/courses/Course.cpp @@ -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() { diff --git a/src/engine/courses/Course.h b/src/engine/courses/Course.h index 306940f01..e7e070535 100644 --- a/src/engine/courses/Course.h +++ b/src/engine/courses/Course.h @@ -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(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(Sequence); + + return j; + } + + // Function to load struct from JSON + void from_json(const nlohmann::json& j) { + Id = j.at("Id").get().c_str(); +// Name = j.at("Name").get().c_str(); + strncpy(Name, j.at("Name").get().c_str(), sizeof(Name) - 1); + Name[sizeof(Name) - 1] = '\0'; // Ensure null termination + +// DebugName = j.at("DebugName").get().c_str(); + strncpy(DebugName, j.at("DebugName").get().c_str(), sizeof(DebugName) - 1); + DebugName[sizeof(DebugName) - 1] = '\0'; // Ensure null termination + + // CourseLength = j.at("CourseLength").get().c_str(); + strncpy(CourseLength, j.at("CourseLength").get().c_str(), sizeof(CourseLength) - 1); + CourseLength[sizeof(CourseLength) - 1] = '\0'; // Ensure null termination + + AIBehaviour = j.at("AIBehaviour").get().c_str(); + MinimapTexture = j.at("MinimapTexture").get().c_str(); + LakituTowType = j.at("LakituTowType").get(); + MinimapDimensions.X = j.at("MinimapDimensions")[0].get(); + MinimapDimensions.Z = j.at("MinimapDimensions")[1].get(); + + AIMaximumSeparation = j.at("AIMaximumSeparation").get(); + AIMinimumSeparation = j.at("AIMinimumSeparation").get(); + NearPersp = j.at("NearPersp").get(); + FarPersp = j.at("FarPersp").get(); + + const auto temp = j.at("AIDistance").get>(); + + // 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(); + + // Deserialize PathSizes and other custom structs if needed + + D_0D009418[0] = j.at("D_0D009418")[0].get(); + D_0D009418[1] = j.at("D_0D009418")[1].get(); + D_0D009418[2] = j.at("D_0D009418")[2].get(); + D_0D009418[3] = j.at("D_0D009418")[3].get(); + + D_0D009568[0] = j.at("D_0D009568")[0].get(); + D_0D009568[1] = j.at("D_0D009568")[1].get(); + D_0D009568[2] = j.at("D_0D009568")[2].get(); + D_0D009568[3] = j.at("D_0D009568")[3].get(); + + D_0D0096B8[0] = j.at("D_0D0096B8")[0].get(); + D_0D0096B8[1] = j.at("D_0D0096B8")[1].get(); + D_0D0096B8[2] = j.at("D_0D0096B8")[2].get(); + D_0D0096B8[3] = j.at("D_0D0096B8")[3].get(); + + D_0D009808[0] = j.at("D_0D009808")[0].get(); + D_0D009808[1] = j.at("D_0D009808")[1].get(); + D_0D009808[2] = j.at("D_0D009808")[2].get(); + D_0D009808[3] = j.at("D_0D009808")[3].get(); + + // 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(); + MinimapFinishlineY = j.at("MinimapFinishlineY").get(); + //textures = nullptr; // Deserialize textures if present + Sequence = static_cast(j.at("Sequence").get()); + } + 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 diff --git a/src/engine/courses/DKJungle.cpp b/src/engine/courses/DKJungle.cpp index 20e0133e4..8a901c07c 100644 --- a/src/engine/courses/DKJungle.cpp +++ b/src/engine/courses/DKJungle.cpp @@ -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; diff --git a/src/engine/courses/DoubleDeck.cpp b/src/engine/courses/DoubleDeck.cpp index 014355544..852f88aa9 100644 --- a/src/engine/courses/DoubleDeck.cpp +++ b/src/engine/courses/DoubleDeck.cpp @@ -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; diff --git a/src/engine/courses/FrappeSnowland.cpp b/src/engine/courses/FrappeSnowland.cpp index fb108f392..b8dda2319 100644 --- a/src/engine/courses/FrappeSnowland.cpp +++ b/src/engine/courses/FrappeSnowland.cpp @@ -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; diff --git a/src/engine/courses/Harbour.cpp b/src/engine/courses/Harbour.cpp index 2020431ec..5f4b8a170 100644 --- a/src/engine/courses/Harbour.cpp +++ b/src/engine/courses/Harbour.cpp @@ -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)); diff --git a/src/engine/courses/KalimariDesert.cpp b/src/engine/courses/KalimariDesert.cpp index 5b9b62819..ec86ca419 100644 --- a/src/engine/courses/KalimariDesert.cpp +++ b/src/engine/courses/KalimariDesert.cpp @@ -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; diff --git a/src/engine/courses/KoopaTroopaBeach.cpp b/src/engine/courses/KoopaTroopaBeach.cpp index 3b027f37c..ec2891748 100644 --- a/src/engine/courses/KoopaTroopaBeach.cpp +++ b/src/engine/courses/KoopaTroopaBeach.cpp @@ -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; diff --git a/src/engine/courses/LuigiRaceway.cpp b/src/engine/courses/LuigiRaceway.cpp index 6da88f9d3..7149e37ea 100644 --- a/src/engine/courses/LuigiRaceway.cpp +++ b/src/engine/courses/LuigiRaceway.cpp @@ -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; diff --git a/src/engine/courses/MarioRaceway.cpp b/src/engine/courses/MarioRaceway.cpp index 7815c1211..7c83a6d41 100644 --- a/src/engine/courses/MarioRaceway.cpp +++ b/src/engine/courses/MarioRaceway.cpp @@ -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; diff --git a/src/engine/courses/MooMooFarm.cpp b/src/engine/courses/MooMooFarm.cpp index 5416e8922..577c5bb45 100644 --- a/src/engine/courses/MooMooFarm.cpp +++ b/src/engine/courses/MooMooFarm.cpp @@ -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; diff --git a/src/engine/courses/PodiumCeremony.cpp b/src/engine/courses/PodiumCeremony.cpp index cafac2424..6d352546d 100644 --- a/src/engine/courses/PodiumCeremony.cpp +++ b/src/engine/courses/PodiumCeremony.cpp @@ -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; diff --git a/src/engine/courses/RainbowRoad.cpp b/src/engine/courses/RainbowRoad.cpp index 3bac83cbe..e73a8f53c 100644 --- a/src/engine/courses/RainbowRoad.cpp +++ b/src/engine/courses/RainbowRoad.cpp @@ -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; diff --git a/src/engine/courses/RoyalRaceway.cpp b/src/engine/courses/RoyalRaceway.cpp index ab074af85..2bbabb85d 100644 --- a/src/engine/courses/RoyalRaceway.cpp +++ b/src/engine/courses/RoyalRaceway.cpp @@ -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; diff --git a/src/engine/courses/SherbetLand.cpp b/src/engine/courses/SherbetLand.cpp index 3f5868247..818e3dabd 100644 --- a/src/engine/courses/SherbetLand.cpp +++ b/src/engine/courses/SherbetLand.cpp @@ -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; diff --git a/src/engine/courses/Skyscraper.cpp b/src/engine/courses/Skyscraper.cpp index ce9fbb3fc..ca1c02cef 100644 --- a/src/engine/courses/Skyscraper.cpp +++ b/src/engine/courses/Skyscraper.cpp @@ -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; diff --git a/src/engine/courses/TestCourse.cpp b/src/engine/courses/TestCourse.cpp index 2ccb47329..7bbb3c1a5 100644 --- a/src/engine/courses/TestCourse.cpp +++ b/src/engine/courses/TestCourse.cpp @@ -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)); diff --git a/src/engine/courses/ToadsTurnpike.cpp b/src/engine/courses/ToadsTurnpike.cpp index 56b8a59f4..752e7c919 100644 --- a/src/engine/courses/ToadsTurnpike.cpp +++ b/src/engine/courses/ToadsTurnpike.cpp @@ -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; diff --git a/src/engine/courses/WarioStadium.cpp b/src/engine/courses/WarioStadium.cpp index 1e7beb672..c07eef1e7 100644 --- a/src/engine/courses/WarioStadium.cpp +++ b/src/engine/courses/WarioStadium.cpp @@ -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; diff --git a/src/engine/courses/YoshiValley.cpp b/src/engine/courses/YoshiValley.cpp index f055d6915..889e6a4f7 100644 --- a/src/engine/courses/YoshiValley.cpp +++ b/src/engine/courses/YoshiValley.cpp @@ -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; diff --git a/src/engine/editor/Editor.cpp b/src/engine/editor/Editor.cpp index d424876ed..f158a8edd 100644 --- a/src/engine/editor/Editor.cpp +++ b/src/engine/editor/Editor.cpp @@ -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(); + } } diff --git a/src/engine/editor/Editor.h b/src/engine/editor/Editor.h index 1352c9d34..9dd412279 100644 --- a/src/engine/editor/Editor.h +++ b/src/engine/editor/Editor.h @@ -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; diff --git a/src/engine/editor/SaveLevel.cpp b/src/engine/editor/SaveLevel.cpp new file mode 100644 index 000000000..93b4b596b --- /dev/null +++ b/src/engine/editor/SaveLevel.cpp @@ -0,0 +1,85 @@ +#include "SaveLevel.h" +#include "port/Game.h" +#include "CoreMath.h" +#include "World.h" +#include "GameObject.h" + +#include +#include +#include + +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); + } +} diff --git a/src/engine/editor/SaveLevel.h b/src/engine/editor/SaveLevel.h new file mode 100644 index 000000000..682df0910 --- /dev/null +++ b/src/engine/editor/SaveLevel.h @@ -0,0 +1,8 @@ +#include +#include "Course.h" + +namespace Editor { + void SaveLevel(); + void LoadLevel(); + void Load_AddStaticMeshActor(const nlohmann::json& actorJson); +} diff --git a/src/engine/objects/Bat.cpp b/src/engine/objects/Bat.cpp index 4acb6479c..764476064 100644 --- a/src/engine/objects/Bat.cpp +++ b/src/engine/objects/Bat.cpp @@ -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); diff --git a/src/engine/objects/Bat.h b/src/engine/objects/Bat.h index 383542231..d6b7e8774 100644 --- a/src/engine/objects/Bat.h +++ b/src/engine/objects/Bat.h @@ -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--; diff --git a/src/engine/objects/Object.cpp b/src/engine/objects/Object.cpp index d532dd566..4e4fd6766 100644 --- a/src/engine/objects/Object.cpp +++ b/src/engine/objects/Object.cpp @@ -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() { } diff --git a/src/engine/objects/Object.h b/src/engine/objects/Object.h index 2a89a7683..9dd44397b 100644 --- a/src/engine/objects/Object.h +++ b/src/engine/objects/Object.h @@ -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; diff --git a/src/engine/objects/TrashBin.cpp b/src/engine/objects/TrashBin.cpp index b6ca78005..c94d0352f 100644 --- a/src/engine/objects/TrashBin.cpp +++ b/src/engine/objects/TrashBin.cpp @@ -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; diff --git a/src/engine/objects/TrashBin.h b/src/engine/objects/TrashBin.h index 2ef500f92..3e878e1c0 100644 --- a/src/engine/objects/TrashBin.h +++ b/src/engine/objects/TrashBin.h @@ -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; diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index 175f251e7..d966ec445 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -45,8 +45,6 @@ float gInterpolationStep = 0.0f; GameEngine* GameEngine::Instance; GameEngine::GameEngine() { - std::vector archiveFiles; - const std::string main_path = Ship::Context::GetPathRelativeToAppDirectory("spaghetti.o2r"); const std::string assets_path = Ship::Context::GetPathRelativeToAppDirectory("ship.o2r"); diff --git a/src/port/Engine.h b/src/port/Engine.h index d116a4f81..59bdb262f 100644 --- a/src/port/Engine.h +++ b/src/port/Engine.h @@ -37,6 +37,7 @@ class GameEngine { std::vector banksTable; std::vector sequenceTable; std::vector audioSequenceTable; + std::vector archiveFiles; ImFont* fontStandard; ImFont* fontStandardLarger; diff --git a/src/port/Game.cpp b/src/port/Game.cpp index 2a91e4a25..34568295b 100644 --- a/src/port/Game.cpp +++ b/src/port/Game.cpp @@ -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(); diff --git a/src/port/Game.h b/src/port/Game.h index d07ae269d..f0887d415 100644 --- a/src/port/Game.h +++ b/src/port/Game.h @@ -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(); diff --git a/src/port/ui/ContentBrowser.cpp b/src/port/ui/ContentBrowser.cpp index 189d703dd..5e632b5fc 100644 --- a/src/port/ui/ContentBrowser.cpp +++ b/src/port/ui/ContentBrowser.cpp @@ -2,14 +2,19 @@ #include "port/ui/PortMenu.h" #include "UIWidgets.h" #include "libultraship/src/Context.h" +#include "port/Engine.h" #include #include +#include +#include #include #include #include "spdlog/formatter.h" #include #include +#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 myList = {"objects/*"}; + std::list 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); + } } } } + diff --git a/src/port/ui/ContentBrowser.h b/src/port/ui/ContentBrowser.h index d986aa0e3..bcae78b6c 100644 --- a/src/port/ui/ContentBrowser.h +++ b/src/port/ui/ContentBrowser.h @@ -7,9 +7,12 @@ class ContentBrowserWindow : public Ship::GuiWindow { public: using Ship::GuiWindow::GuiWindow; ~ContentBrowserWindow(); + + std::vector Content; protected: void InitElement() override {}; void DrawElement() override; void UpdateElement() override {}; + void FindContent(); }; } \ No newline at end of file diff --git a/src/port/ui/Tools.cpp b/src/port/ui/Tools.cpp index be1ce3269..b0b89eb0b 100644 --- a/src/port/ui/Tools.cpp +++ b/src/port/ui/Tools.cpp @@ -10,6 +10,8 @@ #include "spdlog/formatter.h" #include #include +#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(); + } } } diff --git a/src/port/ui/TrackProperties.cpp b/src/port/ui/TrackProperties.cpp index b641d2af4..5cf5238ed 100644 --- a/src/port/ui/TrackProperties.cpp +++ b/src/port/ui/TrackProperties.cpp @@ -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]); } } diff --git a/src/racing/actors.c b/src/racing/actors.c index aca608e37..1b65bbe69 100644 --- a/src/racing/actors.c +++ b/src/racing/actors.c @@ -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; diff --git a/src/racing/skybox_and_splitscreen.c b/src/racing/skybox_and_splitscreen.c index d0d52f77e..77b1c03ff 100644 --- a/src/racing/skybox_and_splitscreen.c +++ b/src/racing/skybox_and_splitscreen.c @@ -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: