From db01cf3d6fec1ef7f963e7e063251fd3e1e0a21b Mon Sep 17 00:00:00 2001 From: AltoXorg <56553686+Alto1772@users.noreply.github.com> Date: Sun, 10 Aug 2025 01:25:46 +0800 Subject: [PATCH 01/16] Fix non-portable (#532) * Fix for non-portable builds * submodule update Torch, [TEMP] libultraship * submodule update libultraship * place assets in exe's dir on post build --- CMakeLists.txt | 10 +++++--- libultraship | 2 +- src/port/Engine.cpp | 4 +-- src/port/GameExtractor.cpp | 5 +++- src/port/pak.cpp | 50 +++++++++++++++++++++++--------------- torch | 2 +- 6 files changed, 45 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b86e36ec2a..66396af390 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,9 +93,6 @@ set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) endif() -execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/config.yml" "${CMAKE_BINARY_DIR}/config.yml") -execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/yamls/" "${CMAKE_BINARY_DIR}/yamls/") - if (MSVC) set(CPP "${CMAKE_C_COMPILER}" "/EP") else() @@ -656,6 +653,13 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") endif() endif() +add_custom_command( + TARGET ${PROJECT_NAME} POST_BUILD + COMMENT "Copying asset yamls..." + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/config.yml" "$/config.yml" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/yamls/" "$/yamls/" +) + if(NOT CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") include(ExternalProject) ExternalProject_Add(TorchExternal diff --git a/libultraship b/libultraship index 5e33d3e7cd..09dfab5fb2 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 5e33d3e7cd0396f847923cd6c471eaf324e90351 +Subproject commit 09dfab5fb2a9a047a6e268dc9db2daad9b2ce5f0 diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index 7deb0c5c93..2bebc766dd 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -76,6 +76,8 @@ bool CreateDirectoryRecursive(std::string const& dirName, std::error_code& err) } GameEngine::GameEngine() { + // Initialize context properties early to recognize paths properly for non-portable builds + this->context = Ship::Context::CreateUninitializedInstance("Spaghetti Kart", "spaghettify", "spaghettify.cfg.json"); const std::string main_path = Ship::Context::GetPathRelativeToAppDirectory("mk64.o2r"); const std::string assets_path = Ship::Context::LocateFileAcrossAppDirs("spaghetti.o2r"); @@ -121,8 +123,6 @@ GameEngine::GameEngine() { } } - this->context = Ship::Context::CreateUninitializedInstance("Spaghetti Kart", "spaghettify", "spaghettify.cfg.json"); - this->context->InitConfiguration(); // without this line InitConsoleVariables fails at Config::Reload() this->context->InitConsoleVariables(); // without this line the controldeck constructor failes in // ShipDeviceIndexMappingManager::UpdateControllerNamesFromConfig() diff --git a/src/port/GameExtractor.cpp b/src/port/GameExtractor.cpp index 1fa9e80e82..9e942333d5 100644 --- a/src/port/GameExtractor.cpp +++ b/src/port/GameExtractor.cpp @@ -56,7 +56,10 @@ std::optional GameExtractor::ValidateChecksum() const { } bool GameExtractor::GenerateOTR() const { - Companion::Instance = new Companion(this->mGameData, ArchiveType::O2R, false); + const std::string assets_path = Ship::Context::GetAppBundlePath(); + const std::string game_path = Ship::Context::GetAppDirectoryPath(); + + Companion::Instance = new Companion(this->mGameData, ArchiveType::O2R, false, assets_path, game_path); try { Companion::Instance->Init(ExportType::Binary); diff --git a/src/port/pak.cpp b/src/port/pak.cpp index 69e0a242b7..c549a98682 100644 --- a/src/port/pak.cpp +++ b/src/port/pak.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -15,10 +16,19 @@ typedef struct ControllerPak { FILE* file; } ControllerPak; +std::string Pfs_PakFile_GetPath(u8 file_no) { + return Ship::Context::GetPathRelativeToAppDirectory(std::format("controllerPak_file_{}.sav", file_no)); +} + +std::string Pfs_PakHeader_GetPath() { + return Ship::Context::GetPathRelativeToAppDirectory("controllerPak_header.sav"); +} + bool Pfs_PakHeader_Write(u32* file_size, u32* game_code, u16* company_code, u8* ext_name, u8* game_name, u8 fileIndex) { ControllerPak pak; - pak.header = fopen("controllerPak_header.sav", "w+b"); + std::string filename = Pfs_PakHeader_GetPath(); + pak.header = fopen(filename.c_str(), "w+b"); if (!pak.header) { return false; @@ -51,7 +61,8 @@ bool Pfs_PakHeader_Read(u32* file_size, u32* game_code, u16* company_code, char* u8 fileIndex) { ControllerPak pak; - pak.header = fopen("controllerPak_header.sav", "rb"); + std::string filename = Pfs_PakHeader_GetPath(); + pak.header = fopen(filename.c_str(), "rb"); if(!pak.header) { return false; @@ -91,12 +102,13 @@ extern "C" s32 osPfsInit(OSMesgQueue* queue, OSPfs* pfs, int channel) { pfs->status = PFS_INITIALIZED; ControllerPak pak; + std::string header_file = Pfs_PakHeader_GetPath(); - pak.header = fopen("controllerPak_header.sav", "rb"); + pak.header = fopen(header_file.c_str(), "rb"); // If a header file doesn't exist, create it. if(!pak.header) { - pak.header = fopen("controllerPak_header.sav", "w+b"); + pak.header = fopen(header_file.c_str(), "w+b"); if (!pak.header) { return PFS_ERR_INVALID; } @@ -108,8 +120,9 @@ extern "C" s32 osPfsInit(OSMesgQueue* queue, OSPfs* pfs, int channel) { extern "C" s32 osPfsFreeBlocks(OSPfs* pfs, s32* bytes_not_used) { ControllerPak pak; + std::string header_file = Pfs_PakHeader_GetPath(); - pak.header = fopen("controllerPak_header.sav", "rb"); + pak.header = fopen(header_file.c_str(), "rb"); if (!pak.header) { return PFS_ERR_INVALID; @@ -178,9 +191,8 @@ extern "C" s32 osPfsAllocateFile(OSPfs* pfs, u16 company_code, u32 game_code, u8 } /* Create empty file */ - char filename[100]; - sprintf(filename, "controllerPak_file_%d.sav", freeFileIndex); - pak.file = fopen(filename, "w+b"); + std::string filename = Pfs_PakFile_GetPath(freeFileIndex); + pak.file = fopen(filename.c_str(), "w+b"); if (!pak.file) { return PFS_ERR_INVALID; @@ -214,9 +226,8 @@ extern "C" s32 osPfsFileState(OSPfs* pfs, s32 file_no, OSPfsState* state) { // games call this function 16 times, once per file // fills the incoming state with the information inside the header of the pak. - char filename[100]; - sprintf(filename, "controllerPak_file_%d.sav", file_no); - FILE* file = fopen(filename, "rb"); + std::string filename = Pfs_PakFile_GetPath(file_no); + FILE* file = fopen(filename.c_str(), "rb"); if (file) { fclose(file); } else { @@ -275,10 +286,9 @@ extern "C" s32 osPfsFindFile(OSPfs* pfs, u16 company_code, u32 game_code, u8* ga extern "C" s32 osPfsReadWriteFile(OSPfs* pfs, s32 file_no, u8 flag, int offset, int size_in_bytes, u8* data_buffer) { ControllerPak pak; + std::string filename = Pfs_PakFile_GetPath(file_no); - char filename[100]; - sprintf(filename, "controllerPak_file_%d.sav", file_no); - pak.file = fopen(filename, flag == 0 ? "r+b" : "w+b"); + pak.file = fopen(filename.c_str(), flag == 0 ? "r+b" : "w+b"); if (!pak.file) { return PFS_ERR_INVALID; @@ -332,8 +342,8 @@ extern "C" s32 osPfsDeleteFile(OSPfs* pfs, u16 company_code, u32 game_code, u8* u32 file_size_ = 0; u32 game_code_ = 0; u16 company_code_ = 0; - char ext_name_[4] = { 0 }; - char game_name_[16] = { 0 }; + char ext_name_[EXT_NAME_SIZE] = { 0 }; + char game_name_[GAME_NAME_SIZE] = { 0 }; if (!Pfs_PakHeader_Read(&file_size_, &game_code_, &company_code_, ext_name_, game_name_, i)) { return PFS_ERR_INVALID; @@ -345,7 +355,8 @@ extern "C" s32 osPfsDeleteFile(OSPfs* pfs, u16 company_code, u32 game_code, u8* if ((game_code == game_code_) && (strcmp((const char*) game_name, (const char*) game_name_) == 0) && strcmp((const char*) ext_name, (const char*) ext_name_) == 0) { // File found - pak.header = fopen("controllerPak_header.sav", "w+b"); + std::string header_file = Pfs_PakHeader_GetPath(); + pak.header = fopen(header_file.c_str(), "w+b"); if(!pak.header) { return PFS_ERR_INVALID; @@ -365,10 +376,9 @@ extern "C" s32 osPfsDeleteFile(OSPfs* pfs, u16 company_code, u32 game_code, u8* free(zero_block); fclose(pak.header); - char filename[100]; - sprintf(filename, "controllerPak_file_%d.sav", i); - remove(filename); + std::string filename = Pfs_PakFile_GetPath(i); + remove(filename.c_str()); return PFS_NO_ERROR; } diff --git a/torch b/torch index 5773373b36..cd92cc0f16 160000 --- a/torch +++ b/torch @@ -1 +1 @@ -Subproject commit 5773373b3620e4a6bc6c92fdc4690d66741c086d +Subproject commit cd92cc0f161c5e79e36f5dda0d0029edd3fc8d50 From f70c9100b080a655fc71e4033206a5eef3844521 Mon Sep 17 00:00:00 2001 From: Varuuna Date: Wed, 24 Sep 2025 00:24:59 +0200 Subject: [PATCH 02/16] Added fades for 3P4P menuing (#543) * Added default menu transition for 3P4P options * Forgot to change the condition * formatting * Reverted change on single frame black Added comments --- src/menu_items.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/menu_items.c b/src/menu_items.c index 10aecd04bc..c367587929 100644 --- a/src/menu_items.c +++ b/src/menu_items.c @@ -5100,6 +5100,10 @@ void draw_fade_in(s32 arg0, s32 arg1, s32 arg2) { gDisplayListHead, x - (w / 2), y - (h / 2), rightEdge + ((w / 2) + x), (h / 2) + y, color->red, color->green, color->blue, 0xFF - (gCurrentTransitionTime[arg0] * 0xFF / gTransitionDuration[arg0])); + } else if ((arg0 == 4)) { // arg0 is the screenId. Why is it 4 in the Menu? + gDisplayListHead = draw_box_wide(gDisplayListHead, x - (w / 2), y - (h / 2), (w / 2) + x, (h / 2) + y, + color->red, color->green, color->blue, + 0xFF - (gCurrentTransitionTime[arg0] * 0xFF / gTransitionDuration[arg0])); } break; } @@ -5546,6 +5550,9 @@ void func_8009D77C(s32 arg0, s32 arg1, s32 arg2) { gDisplayListHead = draw_box_wide_pause_background(gDisplayListHead, var_t3 - temp_v1, var_t4 - temp_t8, rightEdge + someMath0, someMath1, temp_v0_2->red, temp_v0_2->green, temp_v0_2->blue, var_t2); + } else if ((arg0 == 4)) { // arg0 is the screenId. Why is it 4 in the Menu? + gDisplayListHead = draw_box_wide(gDisplayListHead, var_t3 - temp_v1, var_t4 - temp_t8, someMath0, someMath1, + temp_v0_2->red, temp_v0_2->green, temp_v0_2->blue, var_t2); } break; } From 6f133d204560a78867c95caed16bc9cacf8c07cf Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Thu, 25 Sep 2025 15:16:38 +0200 Subject: [PATCH 03/16] Fix ci (#549) * try fix ci * use fmt instead * try another fix for fmt * fix link and detect for c++ 20 * invert condition on format --- .github/workflows/main.yml | 46 ++++++++++++++++++++++++++++++++++++-- CMakeLists.txt | 2 +- README.md | 3 ++- src/port/pak.cpp | 8 ++++++- 4 files changed, 54 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 413678a19c..7808db1f3b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -80,7 +80,7 @@ jobs: name: spaghetti-windows path: spaghetti-${{ matrix.config }} - build-macos: + build-macos-arm64: needs: generate-port-o2r runs-on: macOS-latest strategy: @@ -119,7 +119,49 @@ jobs: if: matrix.config == 'Release' uses: actions/upload-artifact@v4 with: - name: spaghetti-mac-x64 + name: spaghetti-mac-arm64 + path: spaghetti-${{ matrix.config }} + + build-macos-intel: + needs: generate-port-o2r + runs-on: macOS-13 + strategy: + matrix: + config: [Release, Debug] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + submodules: recursive + - name: Install dependencies + run: brew install ninja cmake + - name: Install vcpkg + uses: lukka/run-vcpkg@v11.5 + with: + vcpkgDirectory: '${{ github.workspace }}/vcpkg' + - name: Build + run: | + cmake -H. -Bbuild-cmake -GNinja -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_BUILD_TYPE=${{ matrix.config }} -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake + cmake --build build-cmake --config ${{ matrix.config }} -j3 + - name: Download spaghetti.o2r + uses: actions/download-artifact@v4 + with: + name: spaghetti.o2r + path: ./build-cmake + - name: Create Package + run: | + mkdir spaghetti-${{ matrix.config }} + mv build-cmake/Spaghettify spaghetti-${{ matrix.config }}/ + mv build-cmake/spaghetti.o2r spaghetti-${{ matrix.config }}/ + mv config.yml spaghetti-${{ matrix.config }}/ + mv yamls spaghetti-${{ matrix.config }}/ + wget -O spaghetti-${{ matrix.config }}/gamecontrollerdb.txt https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/master/gamecontrollerdb.txt + - name: Publish packaged artifacts + if: matrix.config == 'Release' + uses: actions/upload-artifact@v4 + with: + name: spaghetti-mac-intel-x64 path: spaghetti-${{ matrix.config }} build-linux: diff --git a/CMakeLists.txt b/CMakeLists.txt index 66396af390..10d8bc91bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,7 +87,7 @@ include(cmake/automate-vcpkg.cmake) set(VCPKG_TRIPLET x64-windows-static) set(VCPKG_TARGET_TRIPLET x64-windows-static) vcpkg_bootstrap() -vcpkg_install_packages(zlib bzip2 libzip libpng sdl2 sdl2-net glew glfw3 nlohmann-json tinyxml2 spdlog libogg libvorbis) +vcpkg_install_packages() set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME}) set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) diff --git a/README.md b/README.md index 7d0addec57..a5891391ff 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,8 @@ If you want to playtest a continuous integration build, you can find them at the * [Windows](https://nightly.link/HarbourMasters/SpaghettiKart/workflows/main/main/spaghetti-windows.zip?status=completed) * [Linux](https://nightly.link/HarbourMasters/SpaghettiKart/workflows/main/main/spaghetti-linux-x64.zip?status=completed) -* [macOS](https://nightly.link/HarbourMasters/SpaghettiKart/workflows/main/main/spaghetti-mac-x64.zip?status=completed) +* [macOS-arm64](https://nightly.link/HarbourMasters/SpaghettiKart/workflows/main/main/spaghetti-mac-arm64.zip?status=completed) +* [macOS-intel](https://nightly.link/HarbourMasters/SpaghettiKart/workflows/main/main/spaghetti-mac-intel-x64.zip?status=completed) * [Switch](https://nightly.link/HarbourMasters/SpaghettiKart/workflows/main/main/Spaghettify-switch.zip?status=completed) Maintainers: [MegaMech](https://www.github.com/MegaMech), [Coco](https://www.github.com/coco875), [Kirito](https://github.com/KiritoDv) diff --git a/src/port/pak.cpp b/src/port/pak.cpp index c549a98682..4adedd6a01 100644 --- a/src/port/pak.cpp +++ b/src/port/pak.cpp @@ -1,6 +1,12 @@ #include #include +#if __has_include() +#include +#define fmt(...) fmt::format(__VA_ARGS__) +#else #include +#define fmt(...) std::format(__VA_ARGS__) +#endif #include #include @@ -17,7 +23,7 @@ typedef struct ControllerPak { } ControllerPak; std::string Pfs_PakFile_GetPath(u8 file_no) { - return Ship::Context::GetPathRelativeToAppDirectory(std::format("controllerPak_file_{}.sav", file_no)); + return Ship::Context::GetPathRelativeToAppDirectory(fmt("controllerPak_file_{}.sav", file_no)); } std::string Pfs_PakHeader_GetPath() { From dbcf1acd2fa05f1303d88a8267566cb4f4fdb119 Mon Sep 17 00:00:00 2001 From: Spodi Date: Fri, 26 Sep 2025 18:11:13 +0200 Subject: [PATCH 04/16] Fix Multi-Window not checked by default (#548) Last time i tested with a new config file this was actually on, despite it showing off. So now it shows ON by default :) Co-authored-by: MegaMech --- src/port/ui/PortMenu.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/port/ui/PortMenu.cpp b/src/port/ui/PortMenu.cpp index 1bbb56637a..606cdab586 100644 --- a/src/port/ui/PortMenu.cpp +++ b/src/port/ui/PortMenu.cpp @@ -323,8 +323,9 @@ void PortMenu::AddSettings() { .CVar(CVAR_ENABLE_MULTI_VIEWPORTS) .PreFunc( [](WidgetInfo& info) { info.isHidden = mPortMenu->disabledMap.at(DISABLE_FOR_NO_MULTI_VIEWPORT).active; }) - .Options(CheckboxOptions().Tooltip( - "Allows multiple ImGui windows to be opened at once (Does not effect the game or the splitscreen modes). Requires a reload to take effect.")); + .Options(CheckboxOptions() + .Tooltip("Allows multiple windows to be opened at once. Requires a reload to take effect.") + .DefaultValue(true)); AddWidget(path, "Texture Filter (Needs reload)", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_TEXTURE_FILTER) .Options(ComboboxOptions().Tooltip("Sets the applied Texture Filtering.").ComboMap(textureFilteringMap)); From 72b5773d4d76385e330a62c100805fad2df1916e Mon Sep 17 00:00:00 2001 From: Varuuna Date: Fri, 26 Sep 2025 18:14:45 +0200 Subject: [PATCH 05/16] Replace FrameInterpolation Pop/Push with RecordOpen/CloseChild (#542) * Replaced Pop/Push for Yoshi Egg and Mario Sign actors * Removed comments * applied suggestions --------- Co-authored-by: MegaMech --- src/actors/mario_sign/render.inc.c | 5 ++-- src/actors/yoshi_egg/render.inc.c | 8 +++--- src/animation.c | 4 +-- src/port/interpolation/FrameInterpolation.cpp | 26 +++++++++---------- src/port/interpolation/FrameInterpolation.h | 4 +-- 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/actors/mario_sign/render.inc.c b/src/actors/mario_sign/render.inc.c index 0bf10bf881..19b0a004e5 100644 --- a/src/actors/mario_sign/render.inc.c +++ b/src/actors/mario_sign/render.inc.c @@ -25,7 +25,7 @@ void render_actor_mario_sign(Camera* arg0, UNUSED Mat4 arg1, struct Actor* arg2) } if (!(unk < 0.0f)) { - FrameInterpolation_RecordMatrixPush(mtx); + FrameInterpolation_RecordOpenChild("mario_sign", TAG_OBJECT(arg2)); gSPSetGeometryMode(gDisplayListHead++, G_SHADING_SMOOTH); gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING); @@ -33,7 +33,6 @@ void render_actor_mario_sign(Camera* arg0, UNUSED Mat4 arg1, struct Actor* arg2) if (render_set_position(mtx, 0) != 0) { gSPDisplayList(gDisplayListHead++, d_course_mario_raceway_dl_sign); } - FrameInterpolation_RecordMatrixPop(mtx); - + FrameInterpolation_RecordCloseChild(); } } diff --git a/src/actors/yoshi_egg/render.inc.c b/src/actors/yoshi_egg/render.inc.c index f39524aa34..7352d618f0 100644 --- a/src/actors/yoshi_egg/render.inc.c +++ b/src/actors/yoshi_egg/render.inc.c @@ -53,7 +53,7 @@ void render_actor_yoshi_egg(Camera* arg0, Mat4 arg1, struct YoshiValleyEgg* egg, sp5C[1] = egg->eggRot; sp5C[2] = 0; - FrameInterpolation_RecordMatrixPush(sp60); + FrameInterpolation_RecordOpenChild("yoshi_egg", TAG_OBJECT(egg)); mtxf_pos_rotation_xyz(sp60, egg->pos, sp5C); if (render_set_position(sp60, 0) == 0) { @@ -62,18 +62,18 @@ void render_actor_yoshi_egg(Camera* arg0, Mat4 arg1, struct YoshiValleyEgg* egg, gSPSetGeometryMode(gDisplayListHead++, G_LIGHTING); gSPDisplayList(gDisplayListHead++, d_course_yoshi_valley_dl_16D70); - FrameInterpolation_RecordMatrixPop(sp60); + FrameInterpolation_RecordCloseChild(); } else { arg1[3][0] = egg->pos[0]; arg1[3][1] = egg->pos[1]; arg1[3][2] = egg->pos[2]; - FrameInterpolation_RecordMatrixPush(arg1); + FrameInterpolation_RecordOpenChild("yoshi_egg2", TAG_OBJECT(egg)); if (render_set_position(arg1, 0) != 0) { gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING); gSPDisplayList(gDisplayListHead++, d_course_yoshi_valley_dl_egg_lod0); } - FrameInterpolation_RecordMatrixPop(arg1); + FrameInterpolation_RecordCloseChild(); } } diff --git a/src/animation.c b/src/animation.c index f3b8f82ddf..ce50602339 100644 --- a/src/animation.c +++ b/src/animation.c @@ -96,7 +96,7 @@ void render_limb_or_add_mtx(Armature* arg0, s16* arg1, AnimationLimbVector arg2, } angle[i] = arg1[arg2[i].indexCycle + some_offset]; } - FrameInterpolation_RecordMatrixPush(modelMatrix); + FrameInterpolation_RecordOpenChild("animation", TAG_OBJECT(arg0)); mtxf_translate_rotate2(modelMatrix, pos, angle); //convert_to_fixed_point_matrix_animation(&gGfxPool->mtxHud[gMatrixHudCount], modelMatrix); sMatrixStackSize += 1; @@ -108,7 +108,7 @@ void render_limb_or_add_mtx(Armature* arg0, s16* arg1, AnimationLimbVector arg2, model = (virtualModel); gSPDisplayList(gDisplayListHead++, model); } - FrameInterpolation_RecordMatrixPop(modelMatrix); + FrameInterpolation_RecordCloseChild(); } void render_armature(Armature* animation, Animation* arg1, s16 timeCycle) { diff --git a/src/port/interpolation/FrameInterpolation.cpp b/src/port/interpolation/FrameInterpolation.cpp index fdd01ff826..c13e0a5a12 100644 --- a/src/port/interpolation/FrameInterpolation.cpp +++ b/src/port/interpolation/FrameInterpolation.cpp @@ -686,13 +686,13 @@ void FrameInterpolation_RecordActorPosRotMatrix(void) { next_is_actor_pos_rot_matrix = true; } -void FrameInterpolation_RecordMatrixPush(Mat4* matrix) { - if (!check_if_recording()) { - return; - } - - append(Op::MatrixPush).matrix_ptr = { (Mat4**) matrix }; -} +//void FrameInterpolation_RecordMatrixPush(Mat4* matrix) { +// if (!check_if_recording()) { +// return; +// } +// +// append(Op::MatrixPush).matrix_ptr = { (Mat4**) matrix }; +//} void FrameInterpolation_RecordMarker(const char* file, int line) { if (!check_if_recording()) { @@ -702,12 +702,12 @@ void FrameInterpolation_RecordMarker(const char* file, int line) { append(Op::Marker).marker = { file, line }; } -void FrameInterpolation_RecordMatrixPop(Mat4* matrix) { - if (!check_if_recording()) { - return; - } - append(Op::MatrixPop).matrix_ptr = { (Mat4**) matrix }; -} +//void FrameInterpolation_RecordMatrixPop(Mat4* matrix) { +// if (!check_if_recording()) { +// return; +// } +// append(Op::MatrixPop).matrix_ptr = { (Mat4**) matrix }; +//} void FrameInterpolation_RecordMatrixPut(MtxF* src) { if (!check_if_recording()) { diff --git a/src/port/interpolation/FrameInterpolation.h b/src/port/interpolation/FrameInterpolation.h index 35bf5867d7..51045a1a9e 100644 --- a/src/port/interpolation/FrameInterpolation.h +++ b/src/port/interpolation/FrameInterpolation.h @@ -52,9 +52,9 @@ void FrameInterpolation_RecordMatrixPosRotScaleXY(Mat4* matrix, s32 x, s32 y, u1 void FrameInterpolation_Record_SetTextMatrix(Mat4* matrix, f32 x, f32 y, f32 arg3, f32 arg4); -void FrameInterpolation_RecordMatrixPush(Mat4* matrix); +//void FrameInterpolation_RecordMatrixPush(Mat4* matrix); -void FrameInterpolation_RecordMatrixPop(Mat4* matrix); +//void FrameInterpolation_RecordMatrixPop(Mat4* matrix); void FrameInterpolation_RecordMatrixMult(Mat4* matrix, MtxF* mf, u8 mode); From 760fa3bf5226c2a6052a5d4d53fd0e27f2911e60 Mon Sep 17 00:00:00 2001 From: MegaMech Date: Sat, 27 Sep 2025 08:09:25 -0600 Subject: [PATCH 06/16] Update PortMenu.cpp (#552) --- src/port/ui/PortMenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/port/ui/PortMenu.cpp b/src/port/ui/PortMenu.cpp index 606cdab586..f0ef3fb838 100644 --- a/src/port/ui/PortMenu.cpp +++ b/src/port/ui/PortMenu.cpp @@ -324,7 +324,7 @@ void PortMenu::AddSettings() { .PreFunc( [](WidgetInfo& info) { info.isHidden = mPortMenu->disabledMap.at(DISABLE_FOR_NO_MULTI_VIEWPORT).active; }) .Options(CheckboxOptions() - .Tooltip("Allows multiple windows to be opened at once. Requires a reload to take effect.") + .Tooltip("Allows multiple ImGui windows to be opened at once (Does not effect the game or the splitscreen modes). Requires a reload to take effect.") .DefaultValue(true)); AddWidget(path, "Texture Filter (Needs reload)", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_TEXTURE_FILTER) From de9c5d510175bba11ab8704e67be3a3a94f9149e Mon Sep 17 00:00:00 2001 From: MegaMech Date: Sun, 9 Nov 2025 19:07:44 -0700 Subject: [PATCH 07/16] Implement SpawnParams struct (#536) * Impl SpawnParams * Added json submodule * Update json * Update * Works * Remove comment * Works refactor * Snowman and thwomp working * Impl hot air balloon * More progress * All OObjects are done * cleanup * Refactor 2Dpath to normal path * Update nlohmann json & fix compile * Rest of actors * MORE CHANGES * Finish actors * Done PR, some fix to collision viewer * Impl falling rocks * Add const * wip editor refactor * Property work * continue * Overridable editor properties * Actor saving/loading works now * Fix light alignment * Clarification * Impl penguin * params impl signs * properties impl falling rock * More property impls * impl air balloon * Add spawnParams to OObject Translate * Snowman translate better * impl hedgehog properly * properties impl trophy * thwomp progress * Finish impl properties * Fix compile * Fix cursor collisions * Move registered actors * Rename pathPoint XYZ to xyz * Fix editor pause bug * Clean up * Review comments * Remove SpawnParams struct from actor classes * Rename * Player Label First Iteration * Work now * Working 3d text * Fix boo bug * Finish AText actor * Fix spawnparams compile * Register AText * Finish Text Actor * Fix thwomp interpolation * Fix compile * Fix crab and hedgehog * Fix loading flagpole * Fix Hot Air Balloon * Turn zbuffer on for AText * Update --------- Co-authored-by: MegaMech <7255464+MegaMech@users.noreply.github.com> --- CMakeLists.txt | 2 + include/actor_types.h | 4 +- include/defines.h | 9 + include/waypoints.h | 6 +- src/actors/blue_and_red_shells/update.inc.c | 18 +- src/actors/kiwano_fruit/update.inc.c | 6 +- src/actors/piranha_plant/render.inc.c | 14 +- src/actors/trees/render.inc.c | 18 +- src/camera.c | 11 +- src/code_800029B0.c | 51 +- src/code_800029B0.h | 1 + src/code_80005FD0.c | 483 +++++++------- src/code_80005FD0.h | 15 +- src/code_80057C60.c | 24 +- src/effects.c | 30 +- src/ending/code_80280000.c | 3 +- src/ending/code_80281780.c | 2 +- src/engine/Actor.cpp | 67 +- src/engine/Actor.h | 37 +- src/engine/AllActors.h | 3 + src/engine/CoreMath.h | 23 +- src/engine/GarbageCollector.cpp | 29 +- src/engine/HM_Intro.cpp | 2 - src/engine/Matrix.cpp | 51 +- src/engine/Matrix.h | 1 + src/engine/RegisteredActors.cpp | 372 +++++++++++ src/engine/RegisteredActors.h | 3 + src/engine/Registry.cpp | 91 +-- src/engine/Registry.h | 39 +- src/engine/Rulesets.cpp | 33 +- src/engine/SpawnParams.h | 132 ++++ src/engine/StaticMeshActor.cpp | 2 + src/engine/StaticMeshActor.h | 1 + src/engine/World.cpp | 48 +- src/engine/World.h | 16 +- src/engine/actors/Banana.cpp | 15 +- src/engine/actors/Banana.h | 2 +- src/engine/actors/BowserStatue.cpp | 1 + src/engine/actors/BowserStatue.h | 2 + src/engine/actors/Cloud.cpp | 95 ++- src/engine/actors/Cloud.h | 28 +- src/engine/actors/FallingRock.cpp | 231 +++++++ src/engine/actors/FallingRock.h | 50 ++ src/engine/actors/Finishline.cpp | 33 +- src/engine/actors/Finishline.h | 21 +- src/engine/actors/MarioSign.cpp | 36 +- src/engine/actors/MarioSign.h | 21 +- src/engine/actors/Ship.cpp | 125 +++- src/engine/actors/Ship.h | 27 +- src/engine/actors/SpaghettiShip.cpp | 21 +- src/engine/actors/SpaghettiShip.h | 17 +- src/engine/actors/Starship.cpp | 122 +++- src/engine/actors/Starship.h | 25 +- src/engine/actors/Text.cpp | 588 ++++++++++++++++++ src/engine/actors/Text.h | 149 +++++ src/engine/actors/Tree.cpp | 1 + src/engine/actors/WarioSign.cpp | 28 +- src/engine/actors/WarioSign.h | 17 +- src/engine/courses/BansheeBoardwalk.cpp | 30 +- src/engine/courses/BigDonut.cpp | 16 +- src/engine/courses/BlockFort.cpp | 16 +- src/engine/courses/BowsersCastle.cpp | 81 ++- src/engine/courses/ChocoMountain.cpp | 21 +- src/engine/courses/Course.cpp | 50 +- src/engine/courses/Course.h | 17 +- src/engine/courses/DKJungle.cpp | 18 +- src/engine/courses/DoubleDeck.cpp | 16 +- src/engine/courses/FrappeSnowland.cpp | 54 +- src/engine/courses/Harbour.cpp | 81 --- src/engine/courses/KalimariDesert.cpp | 24 +- src/engine/courses/KoopaTroopaBeach.cpp | 43 +- src/engine/courses/LuigiRaceway.cpp | 22 +- src/engine/courses/MarioRaceway.cpp | 36 +- src/engine/courses/MooMooFarm.cpp | 16 +- src/engine/courses/PodiumCeremony.cpp | 33 +- src/engine/courses/RainbowRoad.cpp | 16 +- src/engine/courses/RoyalRaceway.cpp | 27 +- src/engine/courses/SherbetLand.cpp | 78 +-- src/engine/courses/Skyscraper.cpp | 16 +- src/engine/courses/TestCourse.cpp | 83 +-- src/engine/courses/ToadsTurnpike.cpp | 33 +- src/engine/courses/WarioStadium.cpp | 30 +- src/engine/courses/YoshiValley.cpp | 61 +- src/engine/editor/Collision.cpp | 43 +- src/engine/editor/Collision.h | 6 +- src/engine/editor/Editor.cpp | 78 ++- src/engine/editor/Editor.h | 11 +- src/engine/editor/EditorMath.cpp | 16 +- src/engine/editor/GameObject.cpp | 29 +- src/engine/editor/GameObject.h | 30 +- src/engine/editor/Gizmo.cpp | 341 ++++++---- src/engine/editor/Gizmo.h | 11 +- src/engine/editor/Handles.cpp | 2 - src/engine/editor/Handles.h | 2 - src/engine/editor/Light.cpp | 29 +- src/engine/editor/Light.h | 5 - src/engine/editor/ObjectPicker.cpp | 178 ++++-- src/engine/editor/ObjectPicker.h | 7 +- src/engine/editor/SceneManager.cpp | 142 ++++- src/engine/editor/SceneManager.h | 24 +- src/engine/objects/Bat.cpp | 7 +- src/engine/objects/Bat.h | 13 +- src/engine/objects/BombKart.cpp | 222 ++++--- src/engine/objects/BombKart.h | 37 +- src/engine/objects/Boos.cpp | 80 ++- src/engine/objects/Boos.h | 23 +- src/engine/objects/ChainChomp.cpp | 3 +- src/engine/objects/CheepCheep.cpp | 61 +- src/engine/objects/CheepCheep.h | 26 +- src/engine/objects/Crab.cpp | 47 +- src/engine/objects/Crab.h | 15 +- src/engine/objects/Flagpole.cpp | 24 +- src/engine/objects/Flagpole.h | 18 +- src/engine/objects/GrandPrixBalloons.cpp | 6 +- src/engine/objects/GrandPrixBalloons.h | 11 +- src/engine/objects/Hedgehog.cpp | 99 ++- src/engine/objects/Hedgehog.h | 19 +- src/engine/objects/HotAirBalloon.cpp | 15 +- src/engine/objects/HotAirBalloon.h | 15 +- src/engine/objects/Object.cpp | 79 ++- src/engine/objects/Object.h | 21 + src/engine/objects/Penguin.cpp | 128 +++- src/engine/objects/Penguin.h | 28 +- src/engine/objects/Podium.cpp | 5 +- src/engine/objects/Podium.h | 11 +- src/engine/objects/Seagull.cpp | 15 +- src/engine/objects/Seagull.h | 12 +- src/engine/objects/Snowman.cpp | 54 +- src/engine/objects/Snowman.h | 14 +- src/engine/objects/Thwomp.cpp | 185 +++++- src/engine/objects/Thwomp.h | 33 +- src/engine/objects/TrashBin.cpp | 13 +- src/engine/objects/TrashBin.h | 17 +- src/engine/objects/Trophy.cpp | 111 +++- src/engine/objects/Trophy.h | 22 +- src/engine/vehicles/Boat.cpp | 96 ++- src/engine/vehicles/Boat.h | 34 +- src/engine/vehicles/Bus.cpp | 110 +++- src/engine/vehicles/Bus.h | 31 +- src/engine/vehicles/Car.cpp | 113 +++- src/engine/vehicles/Car.h | 31 +- src/engine/vehicles/TankerTruck.cpp | 110 +++- src/engine/vehicles/TankerTruck.h | 31 +- src/engine/vehicles/Train.cpp | 196 ++++-- src/engine/vehicles/Train.h | 46 +- src/engine/vehicles/Truck.cpp | 110 +++- src/engine/vehicles/Truck.h | 31 +- src/engine/vehicles/Utils.cpp | 30 + src/engine/vehicles/Utils.h | 1 + src/enhancements/collision_viewer.c | 6 +- src/enhancements/freecam/freecam.cpp | 13 +- src/enhancements/freecam/freecam_engine.c | 2 +- src/main.c | 89 +-- src/menu_items.c | 18 +- src/port/Game.cpp | 51 +- src/port/Game.h | 9 +- src/port/interpolation/FrameInterpolation.h | 3 +- .../importers/TrackPathPointFactory.cpp | 12 +- src/port/ui/ContentBrowser.cpp | 324 ++++++++-- src/port/ui/ContentBrowser.h | 3 + src/port/ui/DefaultProperties.cpp | 283 +++++++++ src/port/ui/DefaultProperties.h | 5 + src/port/ui/Properties.cpp | 126 ++-- src/port/ui/Properties.h | 26 +- src/port/ui/SceneExplorer.cpp | 54 +- src/port/ui/Tools.cpp | 105 +++- src/port/ui/Tools.h | 1 + src/racing/actors.c | 219 +++++-- src/racing/actors.h | 12 +- src/racing/collision.c | 1 - src/racing/race_logic.c | 10 + src/racing/render_courses.c | 8 +- src/racing/skybox_and_splitscreen.c | 50 +- src/racing/skybox_and_splitscreen.h | 3 +- src/spawn_players.c | 8 +- src/update_objects.c | 6 +- vcpkg.json | 8 +- 177 files changed, 6975 insertions(+), 2113 deletions(-) create mode 100644 src/engine/RegisteredActors.cpp create mode 100644 src/engine/RegisteredActors.h create mode 100644 src/engine/SpawnParams.h create mode 100644 src/engine/actors/FallingRock.cpp create mode 100644 src/engine/actors/FallingRock.h create mode 100644 src/engine/actors/Text.cpp create mode 100644 src/engine/actors/Text.h create mode 100644 src/port/ui/DefaultProperties.cpp create mode 100644 src/port/ui/DefaultProperties.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 10d8bc91bb..5c20121677 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,7 @@ set(CMAKE_OSX_ARCHITECTURES=arm64;x86_64) # Set the C++ standard and enable the MSVC parallel build option set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") +set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD 11 CACHE STRING "The C standard to use") set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Spaghettify) set(PROJECT_TEAM "MegaMech") @@ -309,6 +310,7 @@ else() add_executable(${PROJECT_NAME} ${ALL_FILES}) endif() + ################################################################################ # MSVC runtime library ################################################################################ diff --git a/include/actor_types.h b/include/actor_types.h index e9f8844def..40bca60506 100644 --- a/include/actor_types.h +++ b/include/actor_types.h @@ -62,9 +62,9 @@ enum ActorType { ACTOR_MARIO_SIGN, ACTOR_UNKNOWN_0x18, ACTOR_PALM_TREE, - ACTOR_UNKNOWN_0x1A, + ACTOR_TREE_LUIGI_RACEWAY, ACTOR_UNKNOWN_0x1B, - ACTOR_TREE_BOWSERS_CASTLE, + ACTOR_TREE_PEACH_CASTLE, ACTOR_TREE_FRAPPE_SNOWLAND, ACTOR_CACTUS1_KALAMARI_DESERT, ACTOR_CACTUS2_KALAMARI_DESERT, diff --git a/include/defines.h b/include/defines.h index 60c21af83b..11ca54efcd 100644 --- a/include/defines.h +++ b/include/defines.h @@ -112,6 +112,15 @@ #define FOUR_PLAYERS_SELECTED 4 #define SELECTED_PLAYER_DEFINES_TOTAL 5 +// Camera index into cameras array +enum CameraId { + CAMERA_ONE = 0, + CAMERA_TWO, + CAMERA_THREE, + CAMERA_FOUR, + CAMERA_FREECAM +}; + enum PlayerId { PLAYER_NONE = -1, PLAYER_ONE = 0, diff --git a/include/waypoints.h b/include/waypoints.h index 0d5a09e092..3798eae9ea 100644 --- a/include/waypoints.h +++ b/include/waypoints.h @@ -12,9 +12,9 @@ enum { }; typedef struct { - /* 0x00 */ s16 posX; - /* 0x02 */ s16 posY; - /* 0x04 */ s16 posZ; + /* 0x00 */ s16 x; + /* 0x02 */ s16 y; + /* 0x04 */ s16 z; /* 0x06 */ u16 trackSectionId; } TrackPathPoint; // size = 0x08 diff --git a/src/actors/blue_and_red_shells/update.inc.c b/src/actors/blue_and_red_shells/update.inc.c index 87192384b2..92ccd76d2d 100644 --- a/src/actors/blue_and_red_shells/update.inc.c +++ b/src/actors/blue_and_red_shells/update.inc.c @@ -30,9 +30,9 @@ void func_802B3B44(struct ShellActor* shell) { Vec3f origPos; currentWaypoint = shell->pathIndex; - temp_f2 = gCurrentTrackPath[currentWaypoint].posX; - temp_f12 = gCurrentTrackPath[currentWaypoint].posY; - temp_f28 = gCurrentTrackPath[currentWaypoint].posZ; + temp_f2 = gCurrentTrackPath[currentWaypoint].x; + temp_f12 = gCurrentTrackPath[currentWaypoint].y; + temp_f28 = gCurrentTrackPath[currentWaypoint].z; nextWaypoint = currentWaypoint + 1; if (nextWaypoint >= gSelectedPathCount) { @@ -44,9 +44,9 @@ void func_802B3B44(struct ShellActor* shell) { temp_f24 = temp_f28 - shell->pos[2]; temp_f0 = (temp_f20 * temp_f20) + (temp_f22 * temp_f22) + (temp_f24 * temp_f24); if (temp_f0 > 400.0f) { - temp_f18_3 = gCurrentTrackPath[nextWaypoint].posX; - temp_f16_3 = gCurrentTrackPath[nextWaypoint].posY; - temp_f26 = gCurrentTrackPath[nextWaypoint].posZ; + temp_f18_3 = gCurrentTrackPath[nextWaypoint].x; + temp_f16_3 = gCurrentTrackPath[nextWaypoint].y; + temp_f26 = gCurrentTrackPath[nextWaypoint].z; temp_f12_0 = temp_f18_3 - shell->pos[0]; temp_f12_1 = temp_f16_3 - shell->pos[1]; @@ -97,9 +97,9 @@ void func_802B3B44(struct ShellActor* shell) { shell->pos[2] = temp_f28; shell->pathIndex = nextWaypoint; } else { - temp_f18_3 = gCurrentTrackPath[nextWaypoint].posX; - temp_f16_3 = gCurrentTrackPath[nextWaypoint].posY; - temp_f26 = gCurrentTrackPath[nextWaypoint].posZ; + temp_f18_3 = gCurrentTrackPath[nextWaypoint].x; + temp_f16_3 = gCurrentTrackPath[nextWaypoint].y; + temp_f26 = gCurrentTrackPath[nextWaypoint].z; shell->pos[0] = (temp_f2 + temp_f18_3) * 0.5f; shell->pos[1] = ((temp_f12 + temp_f16_3) * 0.5f) + shell->boundingBoxSize; diff --git a/src/actors/kiwano_fruit/update.inc.c b/src/actors/kiwano_fruit/update.inc.c index 68df96d7d8..5c9a974180 100644 --- a/src/actors/kiwano_fruit/update.inc.c +++ b/src/actors/kiwano_fruit/update.inc.c @@ -31,9 +31,9 @@ void update_actor_kiwano_fruit(struct KiwanoFruit* fruit) { fruit->velocity[0] = 80.0f; case 1: nearestWaypoint = gNearestPathPointByPlayerId[(u16) (player - gPlayerOne)]; - temp_f2 = player->pos[0] - gCurrentTrackPath[nearestWaypoint].posX; - temp_f16 = player->pos[1] - gCurrentTrackPath[nearestWaypoint].posY; - temp_f14 = player->pos[2] - gCurrentTrackPath[nearestWaypoint].posZ; + temp_f2 = player->pos[0] - gCurrentTrackPath[nearestWaypoint].x; + temp_f16 = player->pos[1] - gCurrentTrackPath[nearestWaypoint].y; + temp_f14 = player->pos[2] - gCurrentTrackPath[nearestWaypoint].z; temp_f12 = fruit->velocity[0] / sqrtf((temp_f2 * temp_f2) + (temp_f16 * temp_f16) + (temp_f14 * temp_f14)); temp_f2 *= temp_f12; temp_f16 *= temp_f12; diff --git a/src/actors/piranha_plant/render.inc.c b/src/actors/piranha_plant/render.inc.c index ed22810953..16cd42025b 100644 --- a/src/actors/piranha_plant/render.inc.c +++ b/src/actors/piranha_plant/render.inc.c @@ -4,6 +4,18 @@ #include #include +const char* sPiranhaPlantTextures[] = { + gTexturePiranhaPlant1, + gTexturePiranhaPlant2, + gTexturePiranhaPlant3, + gTexturePiranhaPlant4, + gTexturePiranhaPlant5, + gTexturePiranhaPlant6, + gTexturePiranhaPlant7, + gTexturePiranhaPlant8, + gTexturePiranhaPlant9 +}; + /** * @brief Renders the piranha plant actor. * Actor used in Mario Raceway and Royal Raceway. @@ -115,7 +127,7 @@ void render_actor_piranha_plant(Camera* arg0, Mat4 arg1, struct PiranhaPlant* ar if (animationFrame > 8) { animationFrame = 8; } - addr = D_802BA058 + (animationFrame << 0xB); + addr = LOAD_ASSET(sPiranhaPlantTextures[animationFrame]); gDPLoadTextureBlock(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(addr), G_IM_FMT_CI, G_IM_SIZ_8b, 32, 64, 0, G_TX_MIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); diff --git a/src/actors/trees/render.inc.c b/src/actors/trees/render.inc.c index 9939d14eff..db059deedb 100644 --- a/src/actors/trees/render.inc.c +++ b/src/actors/trees/render.inc.c @@ -162,8 +162,7 @@ void render_actor_tree_moo_moo_farm(Camera* camera, Mat4 arg1, struct Actor* arg } } -// have all the properties of the tree -void func_80299864(Camera* camera, Mat4 arg1, struct Actor* arg2) { +void render_actor_tree_luigi_raceway(Camera* camera, Mat4 arg1, struct Actor* arg2) { f32 temp_f0; s16 temp_v0 = arg2->flags; @@ -195,27 +194,18 @@ void func_80299864(Camera* camera, Mat4 arg1, struct Actor* arg2) { // Based on the TLUT being loaded above, this ought to be be another // tree related DL, presumably one found in a course other than Moo Moo farm // 0x0600FC70 - - //! @warning Possible bug: - // Previous incorrectly set to: - // d_course_moo_moo_farm_mole_tlut - // Unless both courses use this actor and use the same addr for the texture. - // Just in-case changed the code into a switch to prevent future crashes. - // This comment can be removed when this is confirmed to work. - if (IsLuigiRaceway()) { - gSPDisplayList(gDisplayListHead++, d_course_luigi_raceway_dl_FC70); - } + gSPDisplayList(gDisplayListHead++, d_course_luigi_raceway_dl_FC70); } } /** - * @brief Renders the tree actor in Bowser's Castle. + * @brief Renders Peach's Castle trees in Royal Raceway. * * @param camera * @param arg1 * @param arg2 */ -void render_actor_tree_bowser_castle(Camera* camera, Mat4 arg1, struct Actor* arg2) { +void render_actor_tree_peach_castle(Camera* camera, Mat4 arg1, struct Actor* arg2) { f32 temp_f0; s16 temp_v0 = arg2->flags; diff --git a/src/camera.c b/src/camera.c index 0d61e54aa1..ddae9302ab 100644 --- a/src/camera.c +++ b/src/camera.c @@ -377,10 +377,10 @@ void func_8001CA78(UNUSED Player* player, Camera* camera, Vec3f arg2, f32* arg3, if (IsToadsTurnpike()) { var_f14 = sp5C[0]; } else { - var_f14 = sp5C[0] + temp_s2->posX; + var_f14 = sp5C[0] + temp_s2->x; } temp_f16 = D_80165230[7] + sp5C[2]; - temp_f18 = sp5C[1] + (temp_s2->posY + D_80164A30); + temp_f18 = sp5C[1] + (temp_s2->y + D_80164A30); arg2[0] += (var_f14 - camera->lookAt[0]) * 1; arg2[1] += (temp_f18 - camera->lookAt[1]) * 1; arg2[2] += (temp_f16 - camera->lookAt[2]) * 1; @@ -388,10 +388,10 @@ void func_8001CA78(UNUSED Player* player, Camera* camera, Vec3f arg2, f32* arg3, if (IsToadsTurnpike()) { var_f14 = sp68[0]; } else { - var_f14 = sp68[0] + temp_s2->posX; + var_f14 = sp68[0] + temp_s2->x; } temp_f16 = D_80165230[7] + sp68[2]; - temp_f18 = sp68[1] + (temp_s2->posY + D_80164A30 + 6.0f); + temp_f18 = sp68[1] + (temp_s2->y + D_80164A30 + 6.0f); move_f32_towards(&D_80164A30, 0, 0.02f); posX = camera->pos[0]; *arg3 = ((var_f14 - posX) * 1) + posX; @@ -1113,8 +1113,7 @@ void func_8001EE98(Player* player, Camera* camera, s8 index) { func_8001E8E8(camera, player, index); break; } - freecam(camera, player, index); // Runs func_8001E45C when freecam is disabled - //func_8001E45C(camera, player, index); + func_8001E45C(camera, player, index); break; case 8: // Transition start func_8001E0C4(camera, player, index); diff --git a/src/code_800029B0.c b/src/code_800029B0.c index c90e24a377..7a878a392a 100644 --- a/src/code_800029B0.c +++ b/src/code_800029B0.c @@ -61,7 +61,7 @@ struct UnkStruct_800DC5EC* D_800DC5F0 = &D_8015F480[1]; struct UnkStruct_800DC5EC* D_800DC5F4 = &D_8015F480[2]; struct UnkStruct_800DC5EC* D_800DC5F8 = &D_8015F480[3]; u16 gIsGamePaused = false; // true if the game is paused and false if the game is not paused -bool gIsEditorPaused = false; +bool gIsEditorPaused = true; u8* pAppNmiBuffer = (u8*) &osAppNmiBuffer; s32 gIsMirrorMode = 0; @@ -188,6 +188,7 @@ void setup_race(void) { int i; LUSLOG_DEBUG("Setup Race!", 0); + LUSLOG_DEBUG("Game Speed: %d", gTickLogic); gPlayerCountSelection1 = gPlayerCount; if (gGamestate != RACING) { @@ -230,7 +231,7 @@ void setup_race(void) { D_80152308 = 0; D_802BA038 = -1; D_802BA048 = 0; - func_802A74BC(); + set_screen(); func_802A4D18(); func_80091FA4(); init_actors_and_load_textures(); @@ -266,6 +267,52 @@ void setup_race(void) { } } +void setup_editor(void) { + LUSLOG_DEBUG("Setup Editor!", 0); + LUSLOG_DEBUG("Game Speed: %d", gTickLogic); + + gPlayerCountSelection1 = 1; + if (gGamestate != RACING) { + gIsMirrorMode = 0; + } + + gActiveScreenMode = gScreenModeSelection; + if (gCurrentCourseId != gCurrentlyLoadedCourseId) { + D_80150120 = 0; + gCurrentlyLoadedCourseId = gCurrentCourseId; + gNextFreeMemoryAddress = gFreeMemoryResetAnchor; + load_course(gCurrentCourseId); + course_init(); + gFreeMemoryCourseAnchor = gNextFreeMemoryAddress; + } else { + gNextFreeMemoryAddress = gFreeMemoryCourseAnchor; + } + + if (gIsMirrorMode) { + gCourseDirection = -1.0f; + } else { + gCourseDirection = 1.0f; + } + + // Cow related + D_8015F702 = 0; + D_8015F700 = 200; + + func_80005310(); + func_8003D080(); + init_hud(); + gRaceState = RACE_INIT; + gNumSpawnedShells = 0; + D_800DC5B8 = 0; + D_80152308 = 0; + D_802BA038 = -1; + D_802BA048 = 0; + set_editor_screen(); + func_802A4D18(); + func_80091FA4(); + init_actors_and_load_textures(); +} + void func_80002DAC(void) { CM_SomeSounds(); diff --git a/src/code_800029B0.h b/src/code_800029B0.h index 85fa36b6e4..331e0b513b 100644 --- a/src/code_800029B0.h +++ b/src/code_800029B0.h @@ -30,6 +30,7 @@ struct UnkStruct_800DC5EC { void func_800029B0(void); void setup_race(void); +void setup_editor(void); void func_80002DAC(void); void clear_nmi_buffer(void); void credits_spawn_actors(void); diff --git a/src/code_80005FD0.c b/src/code_80005FD0.c index ed8781451d..984b6a1973 100644 --- a/src/code_80005FD0.c +++ b/src/code_80005FD0.c @@ -144,8 +144,10 @@ s16 D_801634EC; s32 D_801634F0; s32 D_801634F4; TrackPositionFactorInstruction gPlayerTrackPositionFactorInstruction[10]; -Path2D* gVehicle2DPathPoint; -s32 gVehicle2DPathLength; +TrackPathPoint* gVehicle2DPathPoint; +s32 gVehicle2DPathSize; +TrackPathPoint* gVehiclePath; +size_t gVehiclePathSize; TrainStuff gTrainList[NUM_TRAINS]; u16 isCrossingTriggeredByIndex[NUM_CROSSINGS]; u16 sCrossingActiveTimer[NUM_CROSSINGS]; @@ -1704,7 +1706,7 @@ void update_player(s32 playerId) { gPlayerTrackPositionFactorInstruction[playerId].unkC = 0.0f; } gPlayerPathY[playerId] = - gTrackPaths[gPlayerPathIndex][gNearestPathPointByPlayerId[playerId]].posY + 4.3f; + gTrackPaths[gPlayerPathIndex][gNearestPathPointByPlayerId[playerId]].y + 4.3f; if ((D_801631F8[playerId] == 1) && (D_801631E0[playerId] == false)) { set_player_height(player); } @@ -1880,8 +1882,8 @@ void update_player(s32 playerId) { case 4: /* switch 3 */ pathPoint = &gTrackPaths[playerId][(gNearestPathPointByPlayerId[playerId] + 0xA) % gPathCountByPathIndex[playerId]]; - gOffsetPosition[0] = pathPoint->posX; - gOffsetPosition[2] = pathPoint->posZ; + gOffsetPosition[0] = pathPoint->x; + gOffsetPosition[2] = pathPoint->z; break; } } @@ -2197,10 +2199,10 @@ f32 calculate_track_position_factor(f32 posX, f32 posZ, u16 waypointIndex, s32 p thing1 = &gTrackLeftPaths[pathIndex][waypointIndex]; thing2 = &gTrackRightPaths[pathIndex][waypointIndex]; - x1 = thing1->posX; - z1 = thing1->posZ; - x2 = thing2->posX; - z2 = thing2->posZ; + x1 = thing1->x; + z1 = thing1->z; + x2 = thing2->x; + z2 = thing2->z; squaredDistance = ((x2 - x1) * (x2 - x1)) + ((z2 - z1) * (z2 - z1)); if (squaredDistance < 0.01f) { @@ -2238,11 +2240,11 @@ void calculate_track_offset_position(u16 waypointIndex, f32 arg1, f32 arg2, s16 TrackPathPoint* waypointOne; waypointOne = &gTrackPaths[pathIndex][waypointIndex]; - waypointOneX = waypointOne->posX; - waypointOneZ = waypointOne->posZ; + waypointOneX = waypointOne->x; + waypointOneZ = waypointOne->z; waypointTwo = &gTrackPaths[pathIndex][(waypointIndex + 1) % gSelectedPathCount]; - waypointTwoX = waypointTwo->posX; - waypointTwoZ = waypointTwo->posZ; + waypointTwoX = waypointTwo->x; + waypointTwoZ = waypointTwo->z; zdiff = waypointTwoZ - waypointOneZ; xdiff = waypointTwoX - waypointOneX; if (xdiff && xdiff) {} @@ -2275,11 +2277,11 @@ void set_track_offset_position(u16 waypointIndex, f32 arg1, s16 pathIndex) { path1 = &gTrackLeftPaths[pathIndex][waypointIndex]; path2 = &gTrackRightPaths[pathIndex][waypointIndex]; - x1 = (f32) path1->posX; - z1 = (f32) path1->posZ; + x1 = (f32) path1->x; + z1 = (f32) path1->z; - x2 = (f32) path2->posX; - z2 = (f32) path2->posZ; + x2 = (f32) path2->x; + z2 = (f32) path2->z; waypointIndex += 1; waypointIndex = waypointIndex % gPathCountByPathIndex[pathIndex]; @@ -2287,11 +2289,11 @@ void set_track_offset_position(u16 waypointIndex, f32 arg1, s16 pathIndex) { path1 = &gTrackLeftPaths[pathIndex][waypointIndex]; path2 = &gTrackRightPaths[pathIndex][waypointIndex]; - x3 = (f32) path1->posX; - z3 = (f32) path1->posZ; + x3 = (f32) path1->x; + z3 = (f32) path1->z; - x4 = (f32) path2->posX; - z4 = (f32) path2->posZ; + x4 = (f32) path2->x; + z4 = (f32) path2->z; temp_f0 = 0.5f - (arg1 / 2.0f); temp_f12 = 1.0f - temp_f0; @@ -2314,16 +2316,16 @@ s16 func_8000BD94(f32 posX, f32 posY, f32 posZ, s32 pathIndex) { pathWaypoints = gTrackPaths[pathIndex]; pathWaypointCount = gPathCountByPathIndex[pathIndex]; considerWaypoint = &pathWaypoints[0]; - x_dist = (f32) considerWaypoint->posX - posX; - y_dist = (f32) considerWaypoint->posY - posY; - z_dist = (f32) considerWaypoint->posZ - posZ; + x_dist = (f32) considerWaypoint->x - posX; + y_dist = (f32) considerWaypoint->y - posY; + z_dist = (f32) considerWaypoint->z - posZ; minimumSquaredDistance = (x_dist * x_dist) + (y_dist * y_dist) + (z_dist * z_dist); nearestWaypointIndex = 0; for (considerWaypointIndex = 1; considerWaypointIndex < pathWaypointCount; considerWaypoint++, considerWaypointIndex++) { - x_dist = (f32) considerWaypoint->posX - posX; - y_dist = (f32) considerWaypoint->posY - posY; - z_dist = (f32) considerWaypoint->posZ - posZ; + x_dist = (f32) considerWaypoint->x - posX; + y_dist = (f32) considerWaypoint->y - posY; + z_dist = (f32) considerWaypoint->z - posZ; considerSquaredDistance = (x_dist * x_dist) + (y_dist * y_dist) + (z_dist * z_dist); if (considerSquaredDistance < minimumSquaredDistance) { nearestWaypointIndex = considerWaypointIndex; @@ -2362,9 +2364,9 @@ s16 find_closest_path_point_track_section(f32 posX, f32 posY, f32 posZ, u16 trac considerWaypointIndex++, considerWaypoint++) { if ((considerWaypoint->trackSectionId == trackSectionId) || (IsPodiumCeremony())) { var_t1 = 1; - x_dist = (f32) considerWaypoint->posX - posX; - y_dist = (f32) considerWaypoint->posY - posY; - z_dist = (f32) considerWaypoint->posZ - posZ; + x_dist = (f32) considerWaypoint->x - posX; + y_dist = (f32) considerWaypoint->y - posY; + z_dist = (f32) considerWaypoint->z - posZ; considerSquaredDistance = (x_dist * x_dist) + (y_dist * y_dist) + (z_dist * z_dist); if (considerSquaredDistance < minimumSquaredDistance) { nearestWaypointIndex = considerWaypointIndex; @@ -2382,9 +2384,9 @@ s16 find_closest_path_point_track_section(f32 posX, f32 posY, f32 posZ, u16 trac for (considerWaypointIndex = 0; considerWaypointIndex < pathWaypointCount; considerWaypointIndex++, considerWaypoint++) { if (considerWaypoint->trackSectionId == trackSectionId) { - x_dist = (f32) considerWaypoint->posX - posX; - y_dist = (f32) considerWaypoint->posY - posY; - z_dist = (f32) considerWaypoint->posZ - posZ; + x_dist = (f32) considerWaypoint->x - posX; + y_dist = (f32) considerWaypoint->y - posY; + z_dist = (f32) considerWaypoint->z - posZ; considerSquaredDistance = (x_dist * x_dist) + (y_dist * y_dist) + (z_dist * z_dist); if (considerSquaredDistance < minimumSquaredDistance) { nearestWaypointIndex = considerWaypointIndex; @@ -2401,16 +2403,16 @@ s16 find_closest_path_point_track_section(f32 posX, f32 posY, f32 posZ, u16 trac pathWaypoints = gTrackPaths[0]; pathWaypointCount = gPathCountByPathIndex[0]; considerWaypoint = &pathWaypoints[0]; - x_dist = (f32) considerWaypoint->posX - posX; - y_dist = (f32) considerWaypoint->posY - posY; - z_dist = (f32) considerWaypoint->posZ - posZ; + x_dist = (f32) considerWaypoint->x - posX; + y_dist = (f32) considerWaypoint->y - posY; + z_dist = (f32) considerWaypoint->z - posZ; minimumSquaredDistance = (x_dist * x_dist) + (y_dist * y_dist) + (z_dist * z_dist); nearestWaypointIndex = 0; for (considerWaypointIndex = 1; considerWaypointIndex < pathWaypointCount; considerWaypoint++, considerWaypointIndex++) { - x_dist = (f32) considerWaypoint->posX - posX; - y_dist = (f32) considerWaypoint->posY - posY; - z_dist = (f32) considerWaypoint->posZ - posZ; + x_dist = (f32) considerWaypoint->x - posX; + y_dist = (f32) considerWaypoint->y - posY; + z_dist = (f32) considerWaypoint->z - posZ; considerSquaredDistance = (x_dist * x_dist) + (y_dist * y_dist) + (z_dist * z_dist); if (considerSquaredDistance < minimumSquaredDistance) { nearestWaypointIndex = considerWaypointIndex; @@ -2455,9 +2457,9 @@ s16 update_path_index_with_track(f32 posX, f32 posY, f32 posZ, s16 waypointIndex considerIndex = (searchIndex + pathWaypointCount) % pathWaypointCount; considerWaypoint = &pathWaypoints[considerIndex]; if (considerWaypoint->trackSectionId == trackSectionId) { - x_dist = considerWaypoint->posX - posX; - y_dist = considerWaypoint->posY - posY; - z_dist = considerWaypoint->posZ - posZ; + x_dist = considerWaypoint->x - posX; + y_dist = considerWaypoint->y - posY; + z_dist = considerWaypoint->z - posZ; squaredDistance = (x_dist * x_dist) + (y_dist * y_dist) + (z_dist * z_dist); if (squaredDistance < minimumDistance) { minimumDistance = squaredDistance; @@ -2497,9 +2499,9 @@ s16 update_path_index(f32 posX, f32 posY, f32 posZ, s16 waypointIndex, s32 pathI // This is done to ensure we access gTrackPaths at a valid index considerIndex = (searchIndex + pathWaypointCount) % pathWaypointCount; considerWaypoint = &pathWaypoints[considerIndex]; - x_dist = considerWaypoint->posX - posX; - y_dist = considerWaypoint->posY - posY; - z_dist = considerWaypoint->posZ - posZ; + x_dist = considerWaypoint->x - posX; + y_dist = considerWaypoint->y - posY; + z_dist = considerWaypoint->z - posZ; squaredDistance = (x_dist * x_dist) + (y_dist * y_dist) + (z_dist * z_dist); if (squaredDistance < minimumDistance) { minimumDistance = squaredDistance; @@ -2576,9 +2578,9 @@ s16 update_player_path(f32 posX, f32 posY, f32 posZ, s16 waypointIndex, Player* if (D_801631E0[playerId] == 1) { if (player->unk_0CA & 1) { temp_v1 = &gTrackPaths[pathIndex][waypointIndex]; - player->pos[0] = (f32) temp_v1->posX; - player->pos[1] = (f32) temp_v1->posY; - player->pos[2] = (f32) temp_v1->posZ; + player->pos[0] = (f32) temp_v1->x; + player->pos[1] = (f32) temp_v1->y; + player->pos[2] = (f32) temp_v1->z; player->unk_0CA &= ~0x0001; return waypointIndex; } @@ -2594,18 +2596,18 @@ s16 update_player_path(f32 posX, f32 posY, f32 posZ, s16 waypointIndex, Player* newWaypoint = find_closest_path_point_track_section(posX, posY, posZ, gPlayersTrackSectionId[playerId], &pathIndex); temp_v1 = &gTrackPaths[pathIndex][newWaypoint]; - player->pos[0] = (f32) temp_v1->posX; - player->pos[1] = (f32) temp_v1->posY; - player->pos[2] = (f32) temp_v1->posZ; + player->pos[0] = (f32) temp_v1->x; + player->pos[1] = (f32) temp_v1->y; + player->pos[2] = (f32) temp_v1->z; } } else { newWaypoint = update_path_index(posX, posY, posZ, waypointIndex, pathIndex); if (newWaypoint == -1) { newWaypoint = func_8000BD94(posX, posY, posZ, pathIndex); temp_v1 = &gTrackPaths[pathIndex][newWaypoint]; - posX = (f32) temp_v1->posX; - posY = (f32) temp_v1->posY; - posZ = (f32) temp_v1->posZ; + posX = (f32) temp_v1->x; + posY = (f32) temp_v1->y; + posZ = (f32) temp_v1->z; player->pos[0] = posX; player->pos[1] = posY; player->pos[2] = posZ; @@ -2633,17 +2635,17 @@ s16 find_closest_vehicles_path_point(f32 xPos, UNUSED f32 yPos, f32 zPos, s16 wa s16 realIndex; s16 minimumIndex; s16 considerIndex; - Path2D* considerWaypoint; + TrackPathPoint* considerWaypoint; minimumDistance = 250000.0f; minimumIndex = -1; for (realIndex = waypointIndex - 2; realIndex < waypointIndex + 7; realIndex++) { considerIndex = realIndex; if (realIndex < 0) { - considerIndex = realIndex + gVehicle2DPathLength; + considerIndex = realIndex + gVehiclePathSize; } - considerIndex %= gVehicle2DPathLength; - considerWaypoint = &gVehicle2DPathPoint[considerIndex]; + considerIndex %= gVehiclePathSize; + considerWaypoint = &gVehiclePath[considerIndex]; xdiff = considerWaypoint->x - xPos; zdiff = considerWaypoint->z - zPos; considerSquaredDistance = (xdiff * xdiff) + (zdiff * zdiff); @@ -2815,7 +2817,7 @@ s16 func_8000D6D0(Vec3f position, s16* waypointIndex, f32 speed, f32 arg3, s16 p set_track_offset_position(waypoint2, arg3, pathIndex); temp1 = gOffsetPosition[0]; temp2 = gOffsetPosition[2]; - midY = (path[waypoint1].posY + path[waypoint2].posY) * 0.5f; + midY = (path[waypoint1].y + path[waypoint2].y) * 0.5f; midX = (pad3 + temp1) * 0.5f; midZ = (pad4 + temp2) * 0.5f; xdiff = midX - oldPosX; @@ -2881,7 +2883,7 @@ s16 func_8000D940(Vec3f pos, s16* waypointIndex, f32 speed, f32 arg3, s16 pathIn set_track_offset_position(waypoint2, arg3, pathIndex); thing1 = gOffsetPosition[0]; thing2 = gOffsetPosition[2]; - midY = (gTrackPaths[pathIndex][waypoint1].posY + gTrackPaths[pathIndex][waypoint2].posY) * 0.5f; + midY = (gTrackPaths[pathIndex][waypoint1].y + gTrackPaths[pathIndex][waypoint2].y) * 0.5f; midX = (pad2 + thing1) * 0.5f; midZ = (pad3 + thing2) * 0.5f; xdiff = midX - temp_f20; @@ -2917,17 +2919,20 @@ s16 update_vehicle_following_path(Vec3f pos, s16* waypointIndex, f32 speed) { UNUSED s32 stackPadding7; UNUSED s32 stackPadding8; f32 farWaypointAverageX; + f32 farWaypointAverageY; f32 farWaypointAverageZ; f32 x_dist; f32 y_dist; + f32 z_dist; f32 distance; f32 newX; + f32 newY; f32 newZ; s16 newWaypointIndex; s16 farWaypoint1; s16 farWaypoint2; - Path2D* temp_a0; - Path2D* temp_a2; + TrackPathPoint* temp_a0; + TrackPathPoint* temp_a2; Vec3f sp38; origXPos = pos[0]; @@ -2938,24 +2943,33 @@ s16 update_vehicle_following_path(Vec3f pos, s16* waypointIndex, f32 speed) { sp38[2] = pos[2]; newWaypointIndex = find_closest_vehicles_path_point(origXPos, origYPos, origZPos, *waypointIndex); *waypointIndex = newWaypointIndex; - farWaypoint1 = (newWaypointIndex + 3) % gVehicle2DPathLength; - farWaypoint2 = (newWaypointIndex + 4) % gVehicle2DPathLength; - temp_a0 = &gVehicle2DPathPoint[farWaypoint1]; - temp_a2 = &gVehicle2DPathPoint[farWaypoint2]; + farWaypoint1 = (newWaypointIndex + 3) % gVehiclePathSize; + farWaypoint2 = (newWaypointIndex + 4) % gVehiclePathSize; + temp_a0 = &gVehiclePath[farWaypoint1]; + temp_a2 = &gVehiclePath[farWaypoint2]; farWaypointAverageX = (temp_a0->x + temp_a2->x) * 0.5f; + farWaypointAverageY = (temp_a0->y + temp_a2->y) * 0.5f; farWaypointAverageZ = (temp_a0->z + temp_a2->z) * 0.5f; x_dist = farWaypointAverageX - origXPos; - y_dist = farWaypointAverageZ - origZPos; - distance = sqrtf((x_dist * x_dist) + (y_dist * y_dist)); + y_dist = farWaypointAverageY - origYPos; + z_dist = farWaypointAverageZ - origZPos; + distance = sqrtf((x_dist * x_dist) + (z_dist * z_dist)); if (distance > 0.01f) { newX = ((x_dist * speed) / distance) + origXPos; - newZ = ((y_dist * speed) / distance) + origZPos; + newY = ((y_dist * speed) / distance) + origYPos; + newZ = ((z_dist * speed) / distance) + origZPos; } else { newX = origXPos; + newY = origYPos; newZ = origZPos; } pos[0] = newX; - pos[1] = origYPos; + // These tracks use a 2D path. So we need fallback for that. + if (IsKalimariDesert() || IsDkJungle()) { + pos[1] = pos[1]; + } else { + pos[1] = newY; + } pos[2] = newZ; return get_angle_between_path(sp38, pos); } @@ -2978,14 +2992,14 @@ void set_bomb_kart_spawn_positions(void) { startingYPos = spawn_actor_on_surface(startingXPos, 2000.0f, startingZPos); } else if (IsPodiumCeremony()) { temp_v0 = &gTrackPaths[3][bombKartSpawn->waypointIndex]; - startingXPos = temp_v0->posX; - startingYPos = temp_v0->posY; - startingZPos = temp_v0->posZ; + startingXPos = temp_v0->x; + startingYPos = temp_v0->y; + startingZPos = temp_v0->z; } else { temp_v0 = &gTrackPaths[0][bombKartSpawn->waypointIndex]; - startingXPos = temp_v0->posX; - startingYPos = temp_v0->posY; - startingZPos = temp_v0->posZ; + startingXPos = temp_v0->x; + startingYPos = temp_v0->y; + startingZPos = temp_v0->z; } gBombKarts[var_s3].bombPos[0] = startingXPos; @@ -3130,18 +3144,18 @@ void func_8000DF8C(s32 bombKartId) { sp118 = coss(temp_t6) * 25.0; temp_f0_3 = sins(temp_t6) * 25.0; temp_v0_2 = &gTrackPaths[0][spCA]; - var_f22 = temp_v0_2->posX + sp118; + var_f22 = temp_v0_2->x + sp118; var_f20 = bombKart->yPos + 3.5f; - var_f24 = temp_v0_2->posZ + temp_f0_3; + var_f24 = temp_v0_2->z + temp_f0_3; D_80162FB0[0] = var_f22; D_80162FB0[1] = var_f20; D_80162FB0[2] = var_f24; temp_t7 = (((var_s1 + 1) % 360) * 0xFFFF) / 360; sp118 = coss(temp_t7) * 25.0; temp_f0_3 = sins(temp_t7) * 25.0; - D_80162FC0[0] = temp_v0_2->posX + sp118; - D_80162FC0[1] = temp_v0_2->posY; - D_80162FC0[2] = temp_v0_2->posZ + temp_f0_3; + D_80162FC0[0] = temp_v0_2->x + sp118; + D_80162FC0[1] = temp_v0_2->y; + D_80162FC0[2] = temp_v0_2->z + temp_f0_3; spC2 = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; break; case 2: @@ -3150,18 +3164,18 @@ void func_8000DF8C(s32 bombKartId) { sp118 = coss(temp_t6) * 25.0; temp_f0_3 = sins(temp_t6) * 25.0; temp_v0_2 = &gTrackPaths[0][spCA]; - var_f22 = temp_v0_2->posX + sp118; + var_f22 = temp_v0_2->x + sp118; var_f20 = bombKart->yPos + 3.5f; - var_f24 = temp_v0_2->posZ + temp_f0_3; + var_f24 = temp_v0_2->z + temp_f0_3; D_80162FB0[0] = var_f22; D_80162FB0[1] = var_f20; D_80162FB0[2] = var_f24; temp_t7 = (((var_s1 + 1) % 360) * 0xFFFF) / 360; sp118 = coss(temp_t7) * 25.0; temp_f0_3 = sins(temp_t7) * 25.0; - D_80162FC0[0] = temp_v0_2->posX + sp118; - D_80162FC0[1] = temp_v0_2->posY; - D_80162FC0[2] = temp_v0_2->posZ + temp_f0_3; + D_80162FC0[0] = temp_v0_2->x + sp118; + D_80162FC0[1] = temp_v0_2->y; + D_80162FC0[2] = temp_v0_2->z + temp_f0_3; spC2 = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; break; case 3: @@ -3179,13 +3193,13 @@ void func_8000DF8C(s32 bombKartId) { } if (((s32) spCA) < 0x1A) { temp_v0_2 = &gTrackPaths[3][(spCA + 1) % gPathCountByPathIndex[3]]; - D_80162FB0[0] = temp_v0_2->posX; - D_80162FB0[1] = temp_v0_2->posY; - D_80162FB0[2] = temp_v0_2->posZ; + D_80162FB0[0] = temp_v0_2->x; + D_80162FB0[1] = temp_v0_2->y; + D_80162FB0[2] = temp_v0_2->z; temp_v0_4 = &gTrackPaths[3][(spCA + 2) % gPathCountByPathIndex[3]]; - D_80162FC0[0] = temp_v0_4->posX; - D_80162FC0[1] = temp_v0_4->posY; - D_80162FC0[2] = temp_v0_4->posZ; + D_80162FC0[0] = temp_v0_4->x; + D_80162FC0[1] = temp_v0_4->y; + D_80162FC0[2] = temp_v0_4->z; spC2 = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; } else { D_80162FB0[0] = var_f22; @@ -3216,13 +3230,13 @@ void func_8000DF8C(s32 bombKartId) { break; case 4: temp_v0_2 = &gTrackPaths[0][spCA]; - D_80162FB0[0] = temp_v0_2->posX; - D_80162FB0[1] = temp_v0_2->posY; - D_80162FB0[2] = temp_v0_2->posZ; + D_80162FB0[0] = temp_v0_2->x; + D_80162FB0[1] = temp_v0_2->y; + D_80162FB0[2] = temp_v0_2->z; temp_v0_4 = &gTrackPaths[0][(spCA + 1) % gPathCountByPathIndex[0]]; - D_80162FC0[0] = temp_v0_4->posX; - D_80162FC0[1] = temp_v0_4->posY; - D_80162FC0[2] = temp_v0_4->posZ; + D_80162FC0[0] = temp_v0_4->x; + D_80162FC0[1] = temp_v0_4->y; + D_80162FC0[2] = temp_v0_4->z; var_f20 += 3.0f - (var_s1 * 0.3f); spC2 = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; break; @@ -3467,13 +3481,14 @@ void init_course_path_point(void) { D_80163368[3] = (s32) ptr->unk6; temp = ptr->unk8; - gVehicle2DPathPoint = get_next_available_memory_addr(temp * 4); + gVehicle2DPathPoint = get_next_available_memory_addr(temp * sizeof(TrackPathPoint)); + gVehiclePath = gVehicle2DPathPoint; // Podium ceremony appears to allocate 1 * 8 bytes of data. Which would be aligned to 0x10. for (i = 0; i < 4; i++) { - gTrackPaths[i] = get_next_available_memory_addr(D_80163368[i] * 8); - gTrackLeftPaths[i] = get_next_available_memory_addr(D_80163368[i] * 8); - gTrackRightPaths[i] = get_next_available_memory_addr(D_80163368[i] * 8); + gTrackPaths[i] = get_next_available_memory_addr(D_80163368[i] * sizeof(TrackPathPoint)); + gTrackLeftPaths[i] = get_next_available_memory_addr(D_80163368[i] * sizeof(TrackPathPoint)); + gTrackRightPaths[i] = get_next_available_memory_addr(D_80163368[i] * sizeof(TrackPathPoint)); gTrackSectionTypes[i] = get_next_available_memory_addr(D_80163368[i] * 2); gPathExpectedRotation[i] = get_next_available_memory_addr(D_80163368[i] * 2); gTrackConsecutiveCurveCounts[i] = get_next_available_memory_addr(D_80163368[i] * 2); @@ -3660,7 +3675,7 @@ void init_players(void) { gBestRankedHumanPlayer = 0; gIncrementUpdatePlayer = 0; D_8016337C = 0; - gPathStartZ = (f32) gTrackPaths[0][0].posZ; // [i][2] + gPathStartZ = (f32) gTrackPaths[0][0].z; // [i][2] D_801634F0 = 0; D_801634F4 = 0; D_80163488 = 0; @@ -3699,24 +3714,15 @@ void load_track_path(s32 pathIndex) { if (CM_GetProps()->AIMaximumSeparation >= 1.0f) { pathDest = gTrackPaths[pathIndex]; bInvalidPath = 1; - if (!IsPodiumCeremony()) { - - TrackPathPoint* pathSrc = CM_GetProps()->PathTable2[pathIndex]; - if (pathSrc == NULL) { - printf("code_80005FD0.c: Path %d in Course::PathTable2, was NULL.\n Your track is missing a path\n", - pathIndex); - } - var_v0 = process_path_data(pathDest, pathSrc); - gPathCountByPathIndex[pathIndex] = (u16) var_v0; - } else { + if (IsPodiumCeremony()) { // Only podium ceremony // Course path included in course_data which has already been loaded into memory. // This is how we get the addr to our path data. - path = CM_GetProps()->PathTable[pathIndex]; + path = CM_GetProps()->PathTable2[pathIndex]; ptr = path; for (i = 0; i < 3000; i++, ptr++) { - if ((u16) ptr->posX == 0x8000) { + if ((u16) ptr->x == 0x8000) { sp24 = i - 1; bInvalidPath = 0; break; @@ -3732,6 +3738,15 @@ void load_track_path(s32 pathIndex) { } else { printf("PathTable is invalid. It has %d path points\n It may also be missing the end tag.\n", i); } + } else { // ALL TRACKS + TrackPathPoint* pathSrc = CM_GetProps()->PathTable2[pathIndex]; + if (pathSrc == NULL) { + printf("code_80005FD0.c: Path %d in Course::PathTable2, was NULL.\n Your track is missing a path\n", + pathIndex); + } + + var_v0 = process_path_data(pathDest, pathSrc); + gPathCountByPathIndex[pathIndex] = (u16) var_v0; } } } @@ -3762,26 +3777,26 @@ void calculate_track_boundaries(s32 pathIndex) { var_s1 = &gTrackLeftPaths[pathIndex][0]; var_s2 = &gTrackRightPaths[pathIndex][0]; for (waypointIndex = 0; waypointIndex < gPathCountByPathIndex[pathIndex]; waypointIndex++, var_s1++, var_s2++) { - x1 = waypoint->posX; - y1 = waypoint->posY; - z1 = waypoint->posZ; + x1 = waypoint->x; + y1 = waypoint->y; + z1 = waypoint->z; waypoint++; nextWaypoint = &gTrackPaths[pathIndex][(waypointIndex + 1) % ((s32) gPathCountByPathIndex[pathIndex])]; - x2 = nextWaypoint->posX; - y2 = nextWaypoint->posY; - z2 = nextWaypoint->posZ; + x2 = nextWaypoint->x; + y2 = nextWaypoint->y; + z2 = nextWaypoint->z; x_dist = x2 - x1; z_dist = z2 - z1; neg_x_dist = x1 - x2; neg_z_dist = z1 - z2; xz_dist = sqrtf((x_dist * x_dist) + (z_dist * z_dist)); temp_f16 = (f32) ((y1 + y2) * 0.5); - var_s1->posX = ((waypointWidth * z_dist) / xz_dist) + x1; - var_s1->posY = temp_f16; - var_s1->posZ = ((waypointWidth * neg_x_dist) / xz_dist) + z1; - var_s2->posX = ((waypointWidth * neg_z_dist) / xz_dist) + x1; - var_s2->posY = temp_f16; - var_s2->posZ = ((waypointWidth * x_dist) / xz_dist) + z1; + var_s1->x = ((waypointWidth * z_dist) / xz_dist) + x1; + var_s1->y = temp_f16; + var_s1->z = ((waypointWidth * neg_x_dist) / xz_dist) + z1; + var_s2->x = ((waypointWidth * neg_z_dist) / xz_dist) + x1; + var_s2->y = temp_f16; + var_s2->z = ((waypointWidth * x_dist) / xz_dist) + z1; } } } @@ -3814,23 +3829,23 @@ f32 calculate_track_curvature(s32 pathIndex, u16 waypointIndex) { waypoint1 = &pathWaypoints[waypointIndex]; waypoint2 = &pathWaypoints[(waypointIndex + 1) % waypointCount]; waypoint3 = &pathWaypoints[(waypointIndex + 2) % waypointCount]; - x1 = waypoint1->posX; - z1 = waypoint1->posZ; - x2 = waypoint2->posX; - z2 = waypoint2->posZ; - x3 = waypoint3->posX; - z3 = waypoint3->posZ; + x1 = waypoint1->x; + z1 = waypoint1->z; + x2 = waypoint2->x; + z2 = waypoint2->z; + x3 = waypoint3->x; + z3 = waypoint3->z; temp_f8_2 = (((x2 + x3) * 0.5) - x1); temp_f10 = (((z2 + z3) * 0.5) - z1); waypoint1 = &pathWaypoints[(waypointIndex + 3) % waypointCount]; waypoint2 = &pathWaypoints[(waypointIndex + 4) % waypointCount]; waypoint3 = &pathWaypoints[(waypointIndex + 5) % waypointCount]; - x1 = waypoint1->posX; - z1 = waypoint1->posZ; - x2 = waypoint2->posX; - z2 = waypoint2->posZ; - x3 = waypoint3->posX; - z3 = waypoint3->posZ; + x1 = waypoint1->x; + z1 = waypoint1->z; + x2 = waypoint2->x; + z2 = waypoint2->z; + x3 = waypoint3->x; + z3 = waypoint3->z; temp_f10_2 = (((x2 + x3) * 0.5) - x1); temp_f8 = (((z2 + z3) * 0.5) - z1); root1 = sqrtf((temp_f10 * temp_f10) + (temp_f8_2 * temp_f8_2)); @@ -3901,13 +3916,13 @@ s16 calculate_angle_path(s32 pathIndex, s32 waypointIndex) { TrackPathPoint* temp_v0; temp_v0 = &gTrackPaths[pathIndex][waypointIndex]; - sp30[0] = temp_v0->posX; - sp30[1] = temp_v0->posY; - sp30[2] = temp_v0->posZ; + sp30[0] = temp_v0->x; + sp30[1] = temp_v0->y; + sp30[2] = temp_v0->z; temp_v0 = &gTrackPaths[pathIndex][(waypointIndex + 1) % gPathCountByPathIndex[pathIndex]]; - sp24[0] = temp_v0->posX; - sp24[1] = temp_v0->posY; - sp24[2] = temp_v0->posZ; + sp24[0] = temp_v0->x; + sp24[1] = temp_v0->y; + sp24[2] = temp_v0->z; ret = get_angle_between_two_vectors(sp30, sp24); return -ret; } @@ -3999,20 +4014,20 @@ s32 func_80011014(TrackPathPoint* pathDest, TrackPathPoint* path, s32 numPathPoi UNUSED TrackPathPoint* dest; var_f30 = 0.0f; var_s0 = 0; - temp_f20 = (f32) path[0].posX; - temp_f22 = (f32) path[0].posZ; + temp_f20 = (f32) path[0].x; + temp_f22 = (f32) path[0].z; var_f28 = func_80010F40(temp_f20, 2000.0f, temp_f22, 0, 0); for (i = 0; i < numPathPoints; i++) { point1 = &path[i % numPathPoints]; point2 = &path[(i + 1) % numPathPoints]; point3 = &path[(s32) (i + 2) % numPathPoints]; - x1 = (f32) point1->posX; - z1 = (f32) point1->posZ; - x2 = (f32) point2->posX; - z2 = (f32) point2->posZ; - x3 = (f32) point3->posX; - z3 = (f32) point3->posZ; + x1 = (f32) point1->x; + z1 = (f32) point1->z; + x2 = (f32) point2->x; + z2 = (f32) point2->z; + x3 = (f32) point3->x; + z3 = (f32) point3->z; temp = 0.05 / (sqrtf(((x2 - x1) * (x2 - x1)) + ((z2 - z1) * (z2 - z1))) + (sqrtf(((x3 - x2) * (x3 - x2)) + ((z3 - z2) * (z3 - z2))))); @@ -4035,14 +4050,14 @@ s32 func_80011014(TrackPathPoint* pathDest, TrackPathPoint* path, s32 numPathPoi if ((var_f30 > 20.0f) || ((i == 0) && (j == 0.0))) { if (gIsMirrorMode) { // temp_f12 = -temp_f24_2; - pathDest->posX = (s16) -temp_f24_2; + pathDest->x = (s16) -temp_f24_2; var_f20_2 = func_80010FA0(-temp_f24_2, var_f28, x1_2, 0, var_s0); } else { - pathDest->posX = (s16) temp_f24_2; + pathDest->x = (s16) temp_f24_2; var_f20_2 = func_80010FA0(temp_f24_2, var_f28, x1_2, 0, var_s0); } - pathDest->posZ = (s16) temp_f22; + pathDest->z = (s16) temp_f22; pathDest->trackSectionId = get_track_section_id(D_80162E70.meshIndexZX); if (var_f20_2 < -500.0) { @@ -4076,7 +4091,7 @@ s32 func_80011014(TrackPathPoint* pathDest, TrackPathPoint* path, s32 numPathPoi } } var_f28 = var_f20_2; - pathDest->posY = (s16) (s32) var_f20_2; + pathDest->y = (s16) (s32) var_f20_2; var_f30 = 0.0f; pathDest++; var_s0 += 1; @@ -4097,29 +4112,29 @@ s32 process_path_data(TrackPathPoint* dest, TrackPathPoint* src) { var_v1 = 0; for (var_v0 = 0; var_v0 < 0x7D0; var_v0++) { - temp_a0 = src->posX; - temp_a2 = src->posY; - temp_a3 = src->posZ; + temp_a0 = src->x; + temp_a2 = src->y; + temp_a3 = src->z; temp_t0 = src->trackSectionId; src++; if (((temp_a0 & 0xFFFF) == 0x8000) && ((temp_a2 & 0xFFFF) == 0x8000) && ((temp_a3 & 0xFFFF) == 0x8000)) { break; } if (gIsMirrorMode != 0) { - dest->posX = -temp_a0; + dest->x = -temp_a0; } else { - dest->posX = temp_a0; + dest->x = temp_a0; } var_v1++; - dest->posY = temp_a2; - dest->posZ = temp_a3; + dest->y = temp_a2; + dest->z = temp_a3; dest->trackSectionId = temp_t0; dest++; } return var_v1; } -s32 generate_2d_path(Path2D* pathDest, TrackPathPoint* pathSrc, s32 numWaypoints) { +s32 generate_2d_path(TrackPathPoint* pathDest, TrackPathPoint* pathSrc, s32 numWaypoints) { f32 temp_f14_3; f32 temp_f16_2; UNUSED s32 pad; @@ -4149,20 +4164,20 @@ s32 generate_2d_path(Path2D* pathDest, TrackPathPoint* pathSrc, s32 numWaypoints s32 nbElement; f32 sp7C; - spA8 = pathSrc[0].posX; - spA0 = pathSrc[0].posZ; + spA8 = pathSrc[0].x; + spA0 = pathSrc[0].z; nbElement = 0; for (i = 0; i < numWaypoints; i++) { point1 = &pathSrc[((i % numWaypoints))]; point2 = &pathSrc[(((i + 1) % numWaypoints))]; point3 = &pathSrc[(((i + 2) % numWaypoints))]; - x1 = point1->posX; - z1 = point1->posZ; - x2 = point2->posX; - z2 = point2->posZ; - x3 = point3->posX; - z3 = point3->posZ; + x1 = point1->x; + z1 = point1->z; + x2 = point2->x; + z2 = point2->z; + x3 = point3->x; + z3 = point3->z; sp7C = 0.05 / (sqrtf(((x2 - x1) * (x2 - x1)) + ((z2 - z1) * (z2 - z1))) + sqrtf(((x3 - x2) * (x3 - x2)) + ((z3 - z2) * (z3 - z2)))); @@ -4369,20 +4384,24 @@ void func_80011EC0(s32 arg0, Player* player, s32 arg2, UNUSED u16 arg3) { #define GET_PATH_LENGTH(waypoint) \ for (i = 0;; i++) { \ - if ((u16) waypoint[i].posX == 0x8000) { \ + if ((u16) waypoint[i].x == 0x8000) { \ break; \ } \ } void generate_train_path(void) { s32 i; - Path2D* temp; + TrackPathPoint* temp; TrackPathPoint* waypoint = (TrackPathPoint*) LOAD_ASSET(d_course_kalimari_desert_track_unknown_waypoints); GET_PATH_LENGTH(waypoint) temp = gVehicle2DPathPoint; - gVehicle2DPathLength = generate_2d_path(temp, waypoint, i - 1); + gVehicle2DPathSize = generate_2d_path(temp, waypoint, i - 1); D_80162EB0 = spawn_actor_on_surface(temp[0].x, 2000.0f, temp[0].z); + + // Set the trains path + gVehiclePath = gVehicle2DPathPoint; + gVehiclePathSize = gVehicle2DPathSize; } void generate_ferry_path(void) { @@ -4393,8 +4412,12 @@ void generate_ferry_path(void) { GET_PATH_LENGTH(waypoint) - gVehicle2DPathLength = generate_2d_path(gVehicle2DPathPoint, waypoint, i - 1); + gVehicle2DPathSize = generate_2d_path(gVehicle2DPathPoint, waypoint, i - 1); D_80162EB2 = -40; + + // Set the boats path + gVehiclePath = gVehicle2DPathPoint; + gVehiclePathSize = gVehicle2DPathSize; } void spawn_vehicle_on_road(Vec3f position, Vec3s rotation, Vec3f velocity, s32 waypointIndex, @@ -4420,10 +4443,16 @@ void spawn_vehicle_on_road(Vec3f position, Vec3s rotation, Vec3f velocity, s32 w velocity[2] = position[2] - origZPos; } -void set_vehicle_pos_path_point(TrainCarStuff* trainCar, Path2D* posXZ, u16 waypoint) { - trainCar->position[0] = (f32) posXZ->x; - trainCar->position[1] = (f32) D_80162EB0; - trainCar->position[2] = (f32) posXZ->z; +void set_vehicle_pos_path_point(TrainCarStuff* trainCar, TrackPathPoint* pos, u16 waypoint) { + trainCar->position[0] = (f32) pos->x; + + // Fallback for these tracks because they use a 2D path + if (IsKalimariDesert() || IsDkJungle()) { + trainCar->position[1] = (f32) D_80162EB0; + } else { + trainCar->position[1] = (f32) pos->y; + } + trainCar->position[2] = (f32) pos->z; trainCar->actorIndex = -1; trainCar->waypointIndex = waypoint; trainCar->isActive = 0; @@ -4440,7 +4469,7 @@ void set_vehicle_pos_path_point(TrainCarStuff* trainCar, Path2D* posXZ, u16 wayp void init_vehicles_trains(size_t i, size_t numCarriages, f32 speed) { u16 waypointOffset; TrainCarStuff* ptr1; - Path2D* pos; + TrackPathPoint* pos; s32 j; gTrainList[i].numCarriages = numCarriages; @@ -4448,24 +4477,24 @@ void init_vehicles_trains(size_t i, size_t numCarriages, f32 speed) { // for (i = 0; i < NUM_TRAINS; i++) { // outputs 160 or 392 depending on the train. // Wraps the value around to always output a valid waypoint. - waypointOffset = (((i * gVehicle2DPathLength) / 2) + 160) % gVehicle2DPathLength; + waypointOffset = (((i * gVehiclePathSize) / 2) + 160) % gVehiclePathSize; // 120.0f is about the maximum usable value gTrainList[i].speed = speed; for (j = 0; j < gTrainList[i].numCarriages; j++) { waypointOffset += 4; ptr1 = &gTrainList[i].passengerCars[j]; - pos = &gVehicle2DPathPoint[waypointOffset]; + pos = &gVehiclePath[waypointOffset]; set_vehicle_pos_path_point(ptr1, pos, waypointOffset); } // Smaller offset for the tender waypointOffset += 3; ptr1 = &gTrainList[i].tender; - pos = &gVehicle2DPathPoint[waypointOffset]; + pos = &gVehiclePath[waypointOffset]; set_vehicle_pos_path_point(ptr1, pos, waypointOffset); waypointOffset += 4; ptr1 = &gTrainList[i].locomotive; - pos = &gVehicle2DPathPoint[waypointOffset]; + pos = &gVehiclePath[waypointOffset]; set_vehicle_pos_path_point(ptr1, pos, waypointOffset); // Only use locomotive unless overwritten below. @@ -4669,7 +4698,7 @@ void func_80013054(void) { isCrossingTriggeredByIndex[1] = 0; for (i = 0; i < NUM_TRAINS; i++) { - temp_f16 = gTrainList[i].locomotive.waypointIndex / ((f32) gVehicle2DPathLength); + temp_f16 = gTrainList[i].locomotive.waypointIndex / ((f32) gVehiclePathSize); temp_f18 = 0.72017354f; temp_f12 = 0.42299348f; @@ -4718,12 +4747,12 @@ void check_ai_crossing_distance(s32 playerId) { void init_vehicles_ferry(void) { PaddleBoatStuff* paddleBoat; s32 i; - Path2D* temp_a2; + TrackPathPoint* temp_a2; u16 temp; for (i = 0; i < NUM_ACTIVE_PADDLE_BOATS; i++) { temp = i * 0xB4; paddleBoat = &gPaddleBoats[i]; - temp_a2 = &gVehicle2DPathPoint[temp]; + temp_a2 = &gVehiclePath[temp]; paddleBoat->position[0] = temp_a2->x; paddleBoat->position[1] = D_80162EB2; paddleBoat->position[2] = temp_a2->z; @@ -4746,7 +4775,7 @@ void init_vehicles_ferry(void) { void update_vehicle_paddle_boats(void) { PaddleBoatStuff* paddleBoat; - Path2D* waypoint; + TrackPathPoint* waypoint; s32 i; struct Actor* paddleBoatActor; f32 temp_f26; @@ -4792,7 +4821,7 @@ void update_vehicle_paddle_boats(void) { sp94[0] = temp_f26; sp94[1] = temp_f28; sp94[2] = temp_f30; - waypoint = &gVehicle2DPathPoint[(paddleBoat->waypointIndex + 5) % gVehicle2DPathLength]; + waypoint = &gVehiclePath[(paddleBoat->waypointIndex + 5) % gVehiclePathSize]; sp88[0] = (f32) waypoint->x; sp88[1] = (f32) D_80162EB0; sp88[2] = (f32) waypoint->z; @@ -4891,9 +4920,9 @@ void initialize_toads_turnpike_vehicle(f32 speedA, f32 speedB, s32 numVehicles, waypointOffset = (((i * numWaypoints) / numVehicles) + arg3) % numWaypoints; veh = &vehicleList[i]; temp_v0 = &waypointList[waypointOffset]; - veh->position[0] = (f32) temp_v0->posX; - veh->position[1] = (f32) temp_v0->posY; - veh->position[2] = (f32) temp_v0->posZ; + veh->position[0] = (f32) temp_v0->x; + veh->position[1] = (f32) temp_v0->y; + veh->position[2] = (f32) temp_v0->z; veh->actorIndex = -1; veh->waypointIndex = waypointOffset; veh->unused = 0; @@ -5344,8 +5373,8 @@ void func_80014D30(s32 cameraId, s32 pathIndex) { cameraWaypoint = gNearestPathPointByCameraId[cameraId]; temp_v0 = &gTrackPaths[pathIndex][cameraWaypoint]; - check_bounding_collision(&cameras[cameraId].collision, 10.0f, (f32) temp_v0->posX, (f32) temp_v0->posY + 30.0f, - (f32) temp_v0->posZ); + check_bounding_collision(&cameras[cameraId].collision, 10.0f, (f32) temp_v0->x, (f32) temp_v0->y + 30.0f, + (f32) temp_v0->z); } void func_80014DE4(s32 cameraIndex) { @@ -5565,7 +5594,7 @@ void func_80015544(s32 playerId, f32 arg1, s32 cameraId, s32 pathIndex) { D_801645F8[cameraId] = gOffsetPosition[0]; D_80164638[cameraId] = gOffsetPosition[2]; - temp_f2 = (f32) gTrackPaths[pathIndex][gNearestPathPointByCameraId[cameraId]].posY; + temp_f2 = (f32) gTrackPaths[pathIndex][gNearestPathPointByCameraId[cameraId]].y; temp_f12 = spawn_actor_on_surface(gOffsetPosition[0], (f32) (temp_f2 + 30.0), gOffsetPosition[2]); if ((temp_f12 < (temp_f2 - 20.0)) || (temp_f12 >= 3000.0)) { @@ -5646,7 +5675,7 @@ void func_80015A9C(s32 playerId, f32 arg1, s32 cameraId, s16 pathIndex) { set_track_offset_position(gNearestPathPointByCameraId[cameraId], arg1, pathIndex); D_801645F8[cameraId] = gOffsetPosition[0]; - D_80164618[cameraId] = (f32) gTrackPaths[pathIndex][gNearestPathPointByCameraId[cameraId]].posY; + D_80164618[cameraId] = (f32) gTrackPaths[pathIndex][gNearestPathPointByCameraId[cameraId]].y; D_80164638[cameraId] = gOffsetPosition[2]; D_80164648[cameraId] = gPlayers[playerId].speed / 5.0f; @@ -5708,7 +5737,7 @@ void func_80015C94(Camera* camera, UNUSED Player* unusedPlayer, UNUSED s32 arg2, set_track_offset_position(waypoint2, D_80164688[cameraId], pathIndex); midX += gOffsetPosition[0] * 0.5; midZ += gOffsetPosition[2] * 0.5; - midY = (gTrackPaths[pathIndex][waypoint1].posY + gTrackPaths[pathIndex][waypoint2].posY) / 2.0; + midY = (gTrackPaths[pathIndex][waypoint1].y + gTrackPaths[pathIndex][waypoint2].y) / 2.0; xdiff = midX - D_801645F8[cameraId]; ydiff = midY - D_80164618[cameraId]; zdiff = midZ - D_80164638[cameraId]; @@ -5755,7 +5784,7 @@ void func_800162CC(s32 playerId, f32 arg1, s32 cameraId, s16 pathIndex) { set_track_offset_position(gNearestPathPointByCameraId[cameraId], arg1, pathIndex); D_801645F8[cameraId] = gOffsetPosition[0]; - D_80164618[cameraId] = (f32) gTrackPaths[pathIndex][gNearestPathPointByCameraId[cameraId]].posY; + D_80164618[cameraId] = (f32) gTrackPaths[pathIndex][gNearestPathPointByCameraId[cameraId]].y; D_80164638[cameraId] = gOffsetPosition[2]; D_80164658[cameraId] = gPlayers[playerId].speed; @@ -5836,7 +5865,7 @@ void func_80016494(Camera* camera, UNUSED Player* unusedPlayer, UNUSED s32 arg2, set_track_offset_position(waypoint2, D_80164688[cameraId], pathIndex); midX += gOffsetPosition[0] * 0.5; midZ += gOffsetPosition[2] * 0.5; - midY = (gTrackPaths[pathIndex][waypoint1].posY + gTrackPaths[pathIndex][waypoint2].posY) / 2.0; + midY = (gTrackPaths[pathIndex][waypoint1].y + gTrackPaths[pathIndex][waypoint2].y) / 2.0; xdiff = midX - D_801645F8[cameraId]; ydiff = midY - D_80164618[cameraId]; zdiff = midZ - D_80164638[cameraId]; @@ -5900,7 +5929,7 @@ void func_80016C3C(UNUSED s32 playerId, UNUSED f32 arg1, s32 cameraId) { gNearestPathPointByCameraId[cameraId] %= temp_s0; set_track_offset_position(gNearestPathPointByCameraId[cameraId], D_80164688[cameraId], 0); D_801645F8[cameraId] = gOffsetPosition[0]; - D_80164618[cameraId] = gTrackPaths[0][gNearestPathPointByCameraId[cameraId]].posY; + D_80164618[cameraId] = gTrackPaths[0][gNearestPathPointByCameraId[cameraId]].y; path = gTrackPaths; D_80164638[cameraId] = gOffsetPosition[2]; D_80164658[cameraId] = 16.666666f; @@ -5915,7 +5944,7 @@ void func_80016C3C(UNUSED s32 playerId, UNUSED f32 arg1, s32 cameraId) { temp_f12 += gOffsetPosition[2] * 0.5; sp48 = (gNearestPathPointByCameraId[cameraId] + 0x5) % temp_s0; sp44 = (gNearestPathPointByCameraId[cameraId] + 0x6) % temp_s0; - sp54 = (path[0][sp48].posY + path[0][sp44].posY) * 0.5f; + sp54 = (path[0][sp48].y + path[0][sp44].y) * 0.5f; camera = cameras; camera += cameraId; camera->lookAt[0] = temp_f2; @@ -5982,7 +6011,7 @@ void func_80017054(Camera* camera, UNUSED Player* player, UNUSED s32 index, s32 waypoint1 = (gNearestPathPointByCameraId[cameraId] + 5) % sp58; waypoint2 = (gNearestPathPointByCameraId[cameraId] + 6) % sp58; - lookAtY = (gTrackPaths[pathIndex][waypoint1].posY + gTrackPaths[pathIndex][waypoint2].posY) * 0.5f; + lookAtY = (gTrackPaths[pathIndex][waypoint1].y + gTrackPaths[pathIndex][waypoint2].y) * 0.5f; waypoint1 = (gNearestPathPointByCameraId[cameraId] + 1) % sp58; waypoint2 = (gNearestPathPointByCameraId[cameraId] + 2) % sp58; set_track_offset_position(waypoint1, D_80164688[cameraId], pathIndex); @@ -5991,7 +6020,7 @@ void func_80017054(Camera* camera, UNUSED Player* player, UNUSED s32 index, s32 set_track_offset_position(waypoint2, D_80164688[cameraId], pathIndex); camX += gOffsetPosition[0] * 0.5; camZ += gOffsetPosition[2] * 0.5; - camY = (gTrackPaths[pathIndex][waypoint1].posY + gTrackPaths[pathIndex][waypoint2].posY) * 0.5f; + camY = (gTrackPaths[pathIndex][waypoint1].y + gTrackPaths[pathIndex][waypoint2].y) * 0.5f; diffX = camX - D_801645F8[cameraId]; diffY = camY - D_80164618[cameraId]; @@ -6044,7 +6073,7 @@ void func_80017720(s32 playerId, UNUSED f32 arg1, s32 cameraId, s16 pathIndex) { set_track_offset_position(gNearestPathPointByCameraId[cameraId], gTrackPositionFactor[playerId], pathIndex); D_801645F8[cameraId] = gOffsetPosition[0]; - D_80164618[cameraId] = (f32) gTrackPaths[pathIndex][gNearestPathPointByCameraId[cameraId]].posY; + D_80164618[cameraId] = (f32) gTrackPaths[pathIndex][gNearestPathPointByCameraId[cameraId]].y; D_80164638[cameraId] = gOffsetPosition[2]; D_80164658[cameraId] = gPlayers[playerId].speed; @@ -6116,7 +6145,7 @@ void func_800178F4(Camera* camera, UNUSED Player* unusedPlayer, UNUSED s32 arg2, set_track_offset_position(waypoint2, D_80164688[cameraId], pathIndex); midX += gOffsetPosition[0] * 0.5; midZ += gOffsetPosition[2] * 0.5; - midY = (gTrackPaths[pathIndex][waypoint1].posY + gTrackPaths[pathIndex][waypoint2].posY) / 2.0; + midY = (gTrackPaths[pathIndex][waypoint1].y + gTrackPaths[pathIndex][waypoint2].y) / 2.0; xdiff = midX - D_801645F8[cameraId]; ydiff = midY - D_80164618[cameraId]; zdiff = midZ - D_80164638[cameraId]; @@ -6159,7 +6188,7 @@ void func_80017F10(s32 playerId, UNUSED f32 arg1, s32 cameraId, s16 pathIndex) { set_track_offset_position(gNearestPathPointByCameraId[cameraId], gTrackPositionFactor[playerId], pathIndex); D_801645F8[cameraId] = gOffsetPosition[0]; - D_80164618[cameraId] = (f32) gTrackPaths[pathIndex][gNearestPathPointByCameraId[cameraId]].posY; + D_80164618[cameraId] = (f32) gTrackPaths[pathIndex][gNearestPathPointByCameraId[cameraId]].y; D_80164638[cameraId] = gOffsetPosition[2]; D_80164658[cameraId] = gPlayers[playerId].speed; @@ -6231,7 +6260,7 @@ void func_800180F0(Camera* camera, UNUSED Player* unusedPlayer, UNUSED s32 arg2, set_track_offset_position(waypoint2, D_80164688[cameraId], pathIndex); midX += gOffsetPosition[0] * 0.5; midZ += gOffsetPosition[2] * 0.5; - midY = (gTrackPaths[pathIndex][waypoint1].posY + gTrackPaths[pathIndex][waypoint2].posY) / 2.0; + midY = (gTrackPaths[pathIndex][waypoint1].y + gTrackPaths[pathIndex][waypoint2].y) / 2.0; xdiff = midX - D_801645F8[cameraId]; ydiff = midY - D_80164618[cameraId]; zdiff = midZ - D_80164638[cameraId]; @@ -6274,7 +6303,7 @@ void func_80018718(s32 playerId, UNUSED f32 arg1, s32 cameraId, s16 pathIndex) { pathIndex); D_801645F8[cameraId] = gOffsetPosition[0]; - D_80164618[cameraId] = (f32) gTrackPaths[pathIndex][gNearestPathPointByCameraId[cameraId]].posY; + D_80164618[cameraId] = (f32) gTrackPaths[pathIndex][gNearestPathPointByCameraId[cameraId]].y; D_80164638[cameraId] = gOffsetPosition[2]; D_80164658[cameraId] = gPlayers[playerId].speed; @@ -6362,7 +6391,7 @@ void func_800188F4(Camera* camera, UNUSED Player* unusePlayer, UNUSED s32 arg2, calculate_track_offset_position(waypoint2, D_80164688[cameraId], 60.0f, pathIndex); midX += gOffsetPosition[0] * 0.5; midZ += gOffsetPosition[2] * 0.5; - midY = (gTrackPaths[pathIndex][waypoint1].posY + gTrackPaths[pathIndex][waypoint2].posY) / 2.0; + midY = (gTrackPaths[pathIndex][waypoint1].y + gTrackPaths[pathIndex][waypoint2].y) / 2.0; xdiff = midX - D_801645F8[cameraId]; ydiff = midY - D_80164618[cameraId]; zdiff = midZ - D_80164638[cameraId]; @@ -6412,7 +6441,7 @@ void func_80019118(s32 playerId, f32 arg1, s32 cameraId, UNUSED s16 pathIndex) { set_track_offset_position(gNearestPathPointByCameraId[cameraId], arg1, 0); D_801645F8[cameraId] = gOffsetPosition[0]; D_80164638[cameraId] = gOffsetPosition[2]; - temp_f2 = (f32) gTrackPaths[0][gNearestPathPointByCameraId[cameraId]].posY; + temp_f2 = (f32) gTrackPaths[0][gNearestPathPointByCameraId[cameraId]].y; temp_f12 = spawn_actor_on_surface(gOffsetPosition[0], (f32) (temp_f2 + 30.0), gOffsetPosition[2]); @@ -6464,9 +6493,9 @@ void func_8001933C(Camera* camera, UNUSED Player* playerArg, UNUSED s32 arg2, s3 camera->pos[1] = D_80164618[cameraId]; camera->pos[2] = D_80164638[cameraId]; waypoint = &gTrackPaths[pathIndex][cameraWaypoint]; - camera->lookAt[0] = (player->pos[0] * 0.8) + (0.2 * waypoint->posX); - camera->lookAt[1] = (player->pos[1] * 0.8) + (0.2 * waypoint->posY); - camera->lookAt[2] = (player->pos[2] * 0.8) + (0.2 * waypoint->posZ); + camera->lookAt[0] = (player->pos[0] * 0.8) + (0.2 * waypoint->x); + camera->lookAt[1] = (player->pos[1] * 0.8) + (0.2 * waypoint->y); + camera->lookAt[2] = (player->pos[2] * 0.8) + (0.2 * waypoint->z); func_80014D30(cameraId, pathIndex); xdiff = camera->lookAt[0] - camera->pos[0]; ydiff = camera->lookAt[1] - camera->pos[1]; @@ -6487,10 +6516,10 @@ void func_8001969C(UNUSED s32 playerId, UNUSED f32 arg1, s32 cameraId, UNUSED s1 waypoint = &gTrackPaths[0][gNearestPathPointByCameraId[cameraId]]; - D_801645F8[cameraId] = waypoint->posX; + D_801645F8[cameraId] = waypoint->x; //! @bug Adding an (f32) cast changes asm, why? - D_80164618[cameraId] = waypoint->posY + 10.0; - D_80164638[cameraId] = waypoint->posZ; + D_80164618[cameraId] = waypoint->y + 10.0; + D_80164638[cameraId] = waypoint->z; D_80164648[cameraId] = 0.0f; D_80164678[cameraId] = 0; } @@ -6506,9 +6535,9 @@ void func_80019760(Camera* camera, UNUSED Player* player, UNUSED s32 arg2, s32 c camera->pos[1] = D_80164618[cameraId]; camera->pos[2] = D_80164638[cameraId]; temp_v1 = &(*gTrackPaths)[gNearestPathPointByCameraId[cameraId]]; - camera->lookAt[0] = (f32) temp_v1->posX; - camera->lookAt[1] = (f32) temp_v1->posY; - camera->lookAt[2] = (f32) temp_v1->posZ; + camera->lookAt[0] = (f32) temp_v1->x; + camera->lookAt[1] = (f32) temp_v1->y; + camera->lookAt[2] = (f32) temp_v1->z; func_80014D30(cameraId, 0); xdiff = camera->lookAt[0] - camera->pos[0]; ydiff = camera->lookAt[1] - camera->pos[1]; @@ -7227,9 +7256,9 @@ void cpu_use_item_strategy(s32 playerId) { pathPoint = &gTrackPaths[gPathIndexByPlayerId[0]] [(gNearestPathPointByPlayerId[gBestRankedHumanPlayer] + 30) % gPathCountByPathIndex[gPathIndexByPlayerId[gBestRankedHumanPlayer]]]; - BANANA_ACTOR(actor)->velocity[0] = (pathPoint->posX - player->pos[0]) / 20.0; - BANANA_ACTOR(actor)->velocity[1] = ((pathPoint->posY - player->pos[1]) / 20.0) + 4.0; - BANANA_ACTOR(actor)->velocity[2] = (pathPoint->posZ - player->pos[2]) / 20.0; + BANANA_ACTOR(actor)->velocity[0] = (pathPoint->x - player->pos[0]) / 20.0; + BANANA_ACTOR(actor)->velocity[1] = ((pathPoint->y - player->pos[1]) / 20.0) + 4.0; + BANANA_ACTOR(actor)->velocity[2] = (pathPoint->z - player->pos[2]) / 20.0; BANANA_ACTOR(actor)->pos[1] = player->pos[1]; func_800C92CC(playerId, SOUND_ARG_LOAD(0x29, 0x00, 0x80, 0x09)); func_800C98B8(player->pos, player->velocity, SOUND_ARG_LOAD(0x19, 0x01, 0x80, 0x14)); @@ -7736,10 +7765,10 @@ void func_8001BE78(void) { break; } temp_s0 = &gTrackPaths[i][gNearestPathPointByPlayerId[i]]; - temp_s1->pos[0] = (f32) temp_s0->posX; + temp_s1->pos[0] = (f32) temp_s0->x; temp_s1->pos[1] = - spawn_actor_on_surface((f32) temp_s0->posX, 2000.0f, (f32) temp_s0->posZ) + temp_s1->boundingBoxSize; - temp_s1->pos[2] = (f32) temp_s0->posZ; + spawn_actor_on_surface((f32) temp_s0->x, 2000.0f, (f32) temp_s0->z) + temp_s1->boundingBoxSize; + temp_s1->pos[2] = (f32) temp_s0->z; temp_s1->rotation[1] = (s16) *gPathExpectedRotation[i]; apply_cpu_turn(temp_s1, 0); temp_s1++; diff --git a/src/code_80005FD0.h b/src/code_80005FD0.h index e0496a8063..ec262131f2 100644 --- a/src/code_80005FD0.h +++ b/src/code_80005FD0.h @@ -44,11 +44,6 @@ typedef struct { u16 unk6; } UnkStruct_46D0; -typedef struct { - s16 x; - s16 z; -} Path2D; - enum CpuItemStrategyEnum { CPU_STRATEGY_WAIT_NEXT_ITEM = 0, @@ -181,7 +176,7 @@ f32 func_80010FA0(f32, f32, f32, s32, s32); s32 func_80011014(TrackPathPoint*, TrackPathPoint*, s32, s32); s32 process_path_data(TrackPathPoint*, TrackPathPoint*); -s32 generate_2d_path(Path2D*, TrackPathPoint*, s32); +s32 generate_2d_path(TrackPathPoint*, TrackPathPoint*, s32); void copy_courses_cpu_behaviour(void); void reset_cpu_behaviour_none(s32); void reset_cpu_behaviour(s32); @@ -194,7 +189,7 @@ void generate_train_path(void); void generate_ferry_path(void); void spawn_vehicle_on_road(Vec3f position, Vec3s rotation, Vec3f velocity, s32 waypointIndex, s32 someMultiplierTheSequel, f32 speed); -void set_vehicle_pos_path_point(TrainCarStuff*, Path2D*, u16); +void set_vehicle_pos_path_point(TrainCarStuff*, TrackPathPoint*, u16); void init_vehicles_trains(size_t, size_t, f32); void sync_train_components(TrainCarStuff*, s16); void update_vehicle_trains(void); @@ -375,8 +370,10 @@ extern s16 D_801634EC; extern s32 D_801634F0; extern s32 D_801634F4; extern TrackPositionFactorInstruction gPlayerTrackPositionFactorInstruction[]; -extern Path2D* gVehicle2DPathPoint; -extern s32 gVehicle2DPathLength; +extern TrackPathPoint* gVehicle2DPathPoint; +extern s32 gVehicle2DPathSize; +extern TrackPathPoint* gVehiclePath; +extern size_t gVehiclePathSize; extern u16 isCrossingTriggeredByIndex[]; extern u16 sCrossingActiveTimer[]; extern s32 D_80163DD8[]; diff --git a/src/code_80057C60.c b/src/code_80057C60.c index 9f56a9fee0..1c45d7ba4e 100644 --- a/src/code_80057C60.c +++ b/src/code_80057C60.c @@ -543,12 +543,18 @@ void render_object(u32 arg0) { } void render_object_p1(void) { + size_t playerIdx = PLAYER_ONE; + gDPSetTexturePersp(gDisplayListHead++, G_TP_PERSP); - gSPMatrix(gDisplayListHead++, GetPerspMatrix(PLAYER_ONE), - G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); - gSPMatrix(gDisplayListHead++, GetLookAtMatrix(PLAYER_ONE), - G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + if (CVarGetInteger("gFreecam", 0) == true) { + playerIdx = CAMERA_FREECAM; + } + + gSPMatrix(gDisplayListHead++, GetPerspMatrix(playerIdx), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(gDisplayListHead++, GetLookAtMatrix(playerIdx), + G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); // if (gGamestate == ENDING) { // //func_80055F48(PLAYER_ONE); @@ -639,10 +645,16 @@ void render_player_snow_effect(u32 arg0) { } void render_player_snow_effect_one(void) { + size_t playerIdx = PLAYER_ONE; gDPSetTexturePersp(gDisplayListHead++, G_TP_PERSP); - gSPMatrix(gDisplayListHead++, GetPerspMatrix(PLAYER_ONE), + + if (CVarGetInteger("gFreecam", 0) == true) { + playerIdx = CAMERA_FREECAM; + } + + gSPMatrix(gDisplayListHead++, GetPerspMatrix(playerIdx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); - gSPMatrix(gDisplayListHead++, GetLookAtMatrix(PLAYER_ONE), + gSPMatrix(gDisplayListHead++, GetLookAtMatrix(playerIdx), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); if (gGamestate != ENDING) { render_snowing_effect(PLAYER_ONE); diff --git a/src/effects.c b/src/effects.c index 1e3a9e0882..422769a0d1 100644 --- a/src/effects.c +++ b/src/effects.c @@ -1711,15 +1711,15 @@ void func_80090178(Player* player, s8 playerId, Vec3f arg2, Vec3f arg3) { if (IsYoshiValley()) { test = player->nearestPathPointId; temp_v1 = &gTrackPaths[gCopyPathIndexByPlayerId[playerId]][test]; - arg2[0] = temp_v1->posX; - arg2[1] = temp_v1->posY; - arg2[2] = temp_v1->posZ; + arg2[0] = temp_v1->x; + arg2[1] = temp_v1->y; + arg2[2] = temp_v1->z; temp_v1 = &gTrackPaths[gCopyPathIndexByPlayerId[playerId]] [(player->nearestPathPointId + 5) % (gPathCountByPathIndex[gCopyPathIndexByPlayerId[playerId]] + 1)]; - arg3[0] = temp_v1->posX; - arg3[1] = temp_v1->posY; - arg3[2] = temp_v1->posZ; + arg3[0] = temp_v1->x; + arg3[1] = temp_v1->y; + arg3[2] = temp_v1->z; } else if (IsBlockFort()) { arg2[0] = spF8[playerId]; arg2[1] = 0.0f; @@ -1751,13 +1751,13 @@ void func_80090178(Player* player, s8 playerId, Vec3f arg2, Vec3f arg3) { } else { test = player->nearestPathPointId; temp_v1 = &gTrackPaths[0][test]; - arg2[0] = temp_v1->posX; - arg2[1] = temp_v1->posY; - arg2[2] = temp_v1->posZ; + arg2[0] = temp_v1->x; + arg2[1] = temp_v1->y; + arg2[2] = temp_v1->z; temp_v1 = &gTrackPaths[0][(player->nearestPathPointId + 5) % (gPathCountByPathIndex[0] + 1)]; - arg3[0] = temp_v1->posX; - arg3[1] = temp_v1->posY; - arg3[2] = temp_v1->posZ; + arg3[0] = temp_v1->x; + arg3[1] = temp_v1->y; + arg3[2] = temp_v1->z; } } @@ -1925,9 +1925,9 @@ void func_80090970(Player* player, s8 playerId, s8 arg2) { } if (player->unk_0C8 == 0x00FC) { waypoint = gTrackPaths[0]; - player->pos[0] = waypoint->posX; - player->pos[1] = waypoint->posY; - player->pos[2] = waypoint->posZ; + player->pos[0] = waypoint->x; + player->pos[1] = waypoint->y; + player->pos[2] = waypoint->z; } move_f32_towards(&player->pos[1], (player->unk_074 + player->boundingBoxSize) - 2.0f, 0.04f); player->unk_0C8++; diff --git a/src/ending/code_80280000.c b/src/ending/code_80280000.c index afca94f694..f1ad2fb06f 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_DrawActors(D_800DC5EC->camera); CM_DrawStaticMeshActors(); render_object(PLAYER_ONE + SCREEN_MODE_1P); render_player_snow_effect(PLAYER_ONE + SCREEN_MODE_1P); @@ -138,7 +139,7 @@ void load_credits(void) { D_800DC5B4 = 1; creditsRenderMode = 1; func_802A4D18(); - func_802A74BC(); + set_screen(); camera->unk_B4 = 60.0f; gCameraZoom[0] = 60.0f; D_800DC5EC->screenWidth = SCREEN_WIDTH; diff --git a/src/ending/code_80281780.c b/src/ending/code_80281780.c index 163cbb21db..b5b65e038f 100644 --- a/src/ending/code_80281780.c +++ b/src/ending/code_80281780.c @@ -104,7 +104,7 @@ void setup_podium_ceremony(void) { gGotoMenu = 0xFFFF; D_80287554 = 0; func_802A4D18(); - func_802A74BC(); + set_screen(); camera->unk_B4 = 60.0f; gCameraZoom[0] = 60.0f; D_800DC5EC->screenWidth = SCREEN_WIDTH; diff --git a/src/engine/Actor.cpp b/src/engine/Actor.cpp index f3254fed0e..c2526f3921 100644 --- a/src/engine/Actor.cpp +++ b/src/engine/Actor.cpp @@ -1,13 +1,35 @@ #include #include "Matrix.h" - #include "Actor.h" +#include "engine/World.h" + +// Editor +#include "engine/editor/Collision.h" extern "C" { #include "math_util.h" } AActor::AActor() {} +AActor::AActor(SpawnParams params) { + ResourceName = "mk:actor"; // This needs to be overridden in derived classes + SpawnPos = params.Location.value_or(FVector{0.0f, 0.0f, 0.0f}); + SpawnRot = params.Rotation.value_or(IRotator{0, 0, 0}); + SpawnScale = params.Scale.value_or(FVector(0, 0, 0)); + Speed = params.Speed.value_or(0.0f); +} + +void AActor::BeginPlay() { + // This makes actors clickable in the editor + if (CVarGetInteger("gEditorEnabled", false) == true) { + if ((nullptr != Model) && (Model[0] != '\0')) { + // Prevent collision mesh from being generated extra times. + if (Triangles.size() == 0) { + Editor::GenerateCollisionMesh(this, (Gfx*)LOAD_ASSET_RAW(Model), 1.0f); + } + } + } +} // Virtual functions to be overridden by derived classes void AActor::Tick() { } @@ -20,15 +42,14 @@ void AActor::Draw(Camera *camera) { ApplyMatrixTransformations(mtx, *(FVector*)Pos, *(IRotator*)Rot, Scale); if (render_set_position(mtx, 0) != 0) { - gSPDisplayList(gDisplayListHead++, Model); + gSPDisplayList(gDisplayListHead++, (Gfx*)Model); } } } void AActor::Collision(Player* player, AActor* actor) {} void AActor::VehicleCollision(s32 playerId, Player* player){} void AActor::Destroy() { - // Set uuid to zero. - memset(uuid, 0, sizeof(uuid)); + bPendingDestroy = true; } bool AActor::IsMod() { return false; } void AActor::SetLocation(FVector pos) { @@ -39,3 +60,41 @@ void AActor::SetLocation(FVector pos) { FVector AActor::GetLocation() const { return FVector(Pos[0], Pos[1], Pos[2]); } + +IRotator AActor::GetRotation() const { + IRotator rot; + rot.Set(Rot[0], Rot[1], Rot[2]); + return rot; +} + +FVector AActor::GetScale() const { + return Scale; +} + +void AActor::SetSpawnParams(SpawnParams& params) { + params.Name = ResourceName; + params.Location = SpawnPos; + params.Rotation = SpawnRot; + params.Scale = SpawnScale; + params.Speed = Speed; +} + +void AActor::Translate(FVector pos) { + SpawnPos = pos; + Pos[0] = pos.x; + Pos[1] = pos.y; + Pos[2] = pos.z; +} + +void AActor::Rotate(IRotator rot) { + SpawnRot = rot; + Rot[0] = rot.pitch; + Rot[1] = rot.yaw; + Rot[2] = rot.roll; +} + +void AActor::SetScale(FVector scale) { + SpawnScale = scale; + Scale = scale; +} + diff --git a/src/engine/Actor.h b/src/engine/Actor.h index fb0a9f89e2..0cbd294cdc 100644 --- a/src/engine/Actor.h +++ b/src/engine/Actor.h @@ -1,7 +1,8 @@ #pragma once #include -#include "CoreMath.h" +#include "engine/SpawnParams.h" +#include "engine/editor/EditorMath.h" extern "C" { #include "macros.h" @@ -9,10 +10,8 @@ extern "C" { #include "camera.h" #include "common_structs.h" - class AActor { public: - /* 0x00 */ s16 Type = 0; /* 0x02 */ s16 Flags; /* 0x04 */ s16 Unk_04; @@ -24,25 +23,51 @@ class AActor { /* 0x18 */ Vec3f Pos; /* 0x24 */ Vec3f Velocity = {0, 0, 0}; /* 0x30 */ Collision Unk30; + /* 0x */ const char* Model = ""; uint8_t uuid[16]; const char* Name = ""; + const char* ResourceName = ""; + FVector SpawnPos = {0.0f, 0.0f, 0.0f}; + IRotator SpawnRot = {0, 0, 0}; + FVector SpawnScale = {1.0f, 1.0f, 1.0f}; + FVector Scale = {1, 1, 1}; + float Speed = 0.0f; + std::vector Triangles; - Gfx* Model = NULL; + bool bPendingDestroy = false; virtual ~AActor() = default; // Virtual destructor for proper cleanup in derived classes explicit AActor(); + explicit AActor(SpawnParams params); + /** + * Make sure you call this in derived classes! + * Usage: + * MyActor::SetSpawnParams(SetSpawnParams& params) { + * AActor::SetSpawnParams(params); // Calls default implementation + * } + */ + virtual void SetSpawnParams(SpawnParams& params); + virtual void BeginPlay(); virtual void Tick(); virtual void Draw(Camera*); virtual void Collision(Player* player, AActor* actor); virtual void VehicleCollision(s32 playerId, Player* player); void SetLocation(FVector pos); - FVector GetLocation() const; virtual void Destroy(); virtual bool IsMod(); + + /** Editor functions **/ + FVector GetLocation() const; + IRotator GetRotation() const; + FVector GetScale() const; + void Translate(FVector pos); + void Rotate(IRotator rot); + void SetScale(FVector scale); + virtual void DrawEditorProperties() { DrawDefaultEditorProperties(); }; }; -} \ No newline at end of file +} diff --git a/src/engine/AllActors.h b/src/engine/AllActors.h index f2b228b570..e408e86b82 100644 --- a/src/engine/AllActors.h +++ b/src/engine/AllActors.h @@ -1,5 +1,7 @@ #pragma once +#include "actors/Banana.h" +#include "actors/FallingRock.h" #include "actors/MarioSign.h" #include "actors/WarioSign.h" #include "actors/Cloud.h" @@ -8,6 +10,7 @@ #include "actors/Starship.h" #include "actors/Ship.h" #include "actors/Tree.h" +#include "actors/Text.h" #include "vehicles/Train.h" #include "vehicles/Boat.h" #include "vehicles/Bus.h" diff --git a/src/engine/CoreMath.h b/src/engine/CoreMath.h index 2c232aab9a..626b498aea 100644 --- a/src/engine/CoreMath.h +++ b/src/engine/CoreMath.h @@ -1,6 +1,10 @@ #ifndef CORE_MATH_H #define CORE_MATH_H +#ifdef __cplusplus +#include +#endif + #include /** @@ -10,6 +14,14 @@ * */ +struct RGBA8 { + uint8_t r, g, b, a; +#ifdef __cplusplus + NLOHMANN_DEFINE_TYPE_INTRUSIVE(RGBA8, r, g, b, a) +#endif +}; + + /** * * Applies pos, rot, and scale @@ -63,6 +75,7 @@ struct FVector { FVector() : x(0), y(0), z(0) {} FVector(float x, float y, float z) : x(x), y(y), z(z) {} + NLOHMANN_DEFINE_TYPE_INTRUSIVE(FVector, x, y, z) #endif // __cplusplus }; @@ -94,6 +107,7 @@ struct FVector2D { FVector2D() : x(0), z(0) {} FVector2D(float x, float z) : x(x), z(z) {} + NLOHMANN_DEFINE_TYPE_INTRUSIVE(FVector2D, x, z) #endif // __cplusplus }; @@ -118,12 +132,14 @@ typedef struct IVector2D { /** * This struct immediately converts float pitch/yaw/roll in degrees to n64 int16_t binary angles 0-0xFFFF == 0-360 degrees * ToDegrees() Receive an FRotator of float degrees back. - * Set() Set an n64 int16_t binary angles 0-0xFFFF + * Set() to update an IRotator using n64 int16_t binary angles 0-0xFFFF (ex. IRotator.Set(0, 0x4000, 0) for Y 90 degrees) */ struct IRotator { uint16_t pitch, yaw, roll; #ifdef __cplusplus + NLOHMANN_DEFINE_TYPE_INTRUSIVE(IRotator, pitch, yaw, roll) + IRotator& operator=(const IRotator& other) { pitch = other.pitch; yaw = other.yaw; @@ -158,7 +174,7 @@ struct IRotator { /** * Use IRotator unless you want to do some math in degrees. - * Always use ToBinary() or Rotator when sending into matrices or apply translation functions + * Always use ToBinary() or IRotator when sending into matrices or apply translation functions * Convert from IRotator to FRotator float degrees by doing FRotator(myIRotator); */ struct FRotator { @@ -196,9 +212,10 @@ struct FRotator { * Usage: IPathSpan(point1, point2) --> IPathSpan(40, 65) */ struct IPathSpan { - int Start, End; + int32_t Start, End; #ifdef __cplusplus + NLOHMANN_DEFINE_TYPE_INTRUSIVE(IPathSpan, Start, End) // Default Constructor IPathSpan() : Start(0), End(0) {} diff --git a/src/engine/GarbageCollector.cpp b/src/engine/GarbageCollector.cpp index 9832aa29d2..e7f2d99753 100644 --- a/src/engine/GarbageCollector.cpp +++ b/src/engine/GarbageCollector.cpp @@ -2,21 +2,30 @@ #include "World.h" void RunGarbageCollector() { - //CleanActors(); + CleanActors(); CleanObjects(); CleanStaticMeshActors(); } void CleanActors() { - // for (auto actor = gWorldInstance.Actors.begin(); actor != gWorldInstance.Actors.end();) { - // OObject* act = *actor; // Get a mutable copy - // if (act->PendingDestroy) { - // delete act; - // actor = gWorldInstance.Objects.erase(actor); // Remove from container - // continue; - // } - // actor++; - // } + for (auto actor = gWorldInstance.Actors.begin(); actor != gWorldInstance.Actors.end();) { + AActor* act = *actor; // Get a mutable copy + if (act->bPendingDestroy) { + if (act->IsMod()) { // C++ actor + delete act; + actor = gWorldInstance.Actors.erase(actor); // Remove from container + } else { // Old C actor + act->Flags = 0; + act->Type = 0; + act->Name = ""; + act->ResourceName = ""; + actor++; // Manually advance the iterator since no deletion happens here + } + gNumActors -= 1; + continue; + } + actor++; + } } void CleanStaticMeshActors() { diff --git a/src/engine/HM_Intro.cpp b/src/engine/HM_Intro.cpp index 51d3d2bbda..1e05d1639e 100644 --- a/src/engine/HM_Intro.cpp +++ b/src/engine/HM_Intro.cpp @@ -65,8 +65,6 @@ void HarbourMastersIntro::HM_InitIntro() { 0x7F, 0x30, 0x80, 0x60, 20, 10, 0x49, 0x49, 0x49 ); - - //gEditor.AddObject("lus", &lusPos, &lusRot, &lusScale, nullptr, 1, Editor::GameObject::CollisionType::BOUNDING_BOX, 10, &DespawnValue, -1); } void HarbourMastersIntro::HM_TickIntro() { diff --git a/src/engine/Matrix.cpp b/src/engine/Matrix.cpp index 954b0fd90c..44a9a17990 100644 --- a/src/engine/Matrix.cpp +++ b/src/engine/Matrix.cpp @@ -119,6 +119,56 @@ void ApplyMatrixTransformations(Mat4 mtx, FVector pos, IRotator rot, FVector sca mtx[3][3] = 1.0f; } +/* + * Spherical billboarding + * Rotates the object to face the camera + * Rotates on all three axis + */ +void ApplySphericalBillBoard(Mat4 mat, FVector pos, FVector scale, s32 cameraIndex) { + Mtx* lookAt = GetLookAtMatrix(cameraIndex); + Mat4 lookAtF; + guMtxL2F((float(*)[4])&lookAtF, lookAt); + + // Camera Right + mat[0][0] = lookAtF[0][0]; + mat[1][0] = lookAtF[0][1]; + mat[2][0] = lookAtF[0][2]; + mat[3][0] = 0; + + // Camera Up + mat[0][1] = lookAtF[1][0]; + mat[1][1] = lookAtF[1][1]; + mat[2][1] = lookAtF[1][2]; + mat[3][1] = 0; + + // Camera Forward + mat[0][2] = lookAtF[2][0]; + mat[1][2] = lookAtF[2][1]; + mat[2][2] = lookAtF[2][2]; + mat[3][2] = 0; + + mat[0][3] = 0; + mat[1][3] = 0; + mat[2][3] = 0; + mat[3][3] = 1; + + // Set position + mat[3][0] = pos.x; + mat[3][1] = pos.y; + mat[3][2] = pos.z; + + // Apply scaling + mat[0][0] *= scale.x; + mat[1][0] *= scale.x; + mat[2][0] *= scale.x; + mat[0][1] *= scale.y; + mat[1][1] *= scale.y; + mat[2][1] *= scale.y; + mat[0][2] *= scale.z; + mat[1][2] *= scale.z; + mat[2][2] *= scale.z; +} + void AddLocalRotation(Mat4 mat, IRotator rot) { f32 sin_pitch = sins(rot.pitch); f32 cos_pitch = coss(rot.pitch); @@ -141,7 +191,6 @@ void AddLocalRotation(Mat4 mat, IRotator rot) { mat[2][2] = (cos_pitch * cos_yaw); } - // API extern "C" { diff --git a/src/engine/Matrix.h b/src/engine/Matrix.h index ede94b5b0d..c58f41343d 100644 --- a/src/engine/Matrix.h +++ b/src/engine/Matrix.h @@ -9,6 +9,7 @@ #ifdef __cplusplus extern "C" { void ApplyMatrixTransformations(Mat4 mtx, FVector pos, IRotator rot, FVector scale); +void ApplySphericalBillBoard(Mat4 mat, FVector pos, FVector scale, s32 cameraIndex); void AddLocalRotation(Mat4 mat, IRotator rot); #endif void ClearMatrixPools(void); diff --git a/src/engine/RegisteredActors.cpp b/src/engine/RegisteredActors.cpp new file mode 100644 index 0000000000..7e7f0a11e3 --- /dev/null +++ b/src/engine/RegisteredActors.cpp @@ -0,0 +1,372 @@ +#include "SpawnParams.h" +#include "engine/CoreMath.h" +#include "Registry.h" +#include "engine/World.h" + +#include "AllActors.h" + +extern "C" { +#include "common_structs.h" +#include "actors.h" +#include "actor_types.h" +} + +void RegisterGameActors() { + RegisterActor("mk:item_box", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + spawn_item_box(pos); + } + ); + + RegisterActor("mk:fake_item_box", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + spawn_fake_item_box(pos); + } + ); + + RegisterActor("mk:thwomp", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OThwomp(params)); + } + ); + + RegisterActor("mk:snowman", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OSnowman(params)); + } + ); + + RegisterActor("mk:hot_air_balloon", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OHotAirBalloon(params)); + } + ); + + RegisterActor("mk:hedgehog", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OHedgehog(params)); + } + ); + + RegisterActor("mk:grand_prix_balloons", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OGrandPrixBalloons(params)); + } + ); + + RegisterActor("mk:flagpole", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OFlagpole(params)); + } + ); + + RegisterActor("mk:crab", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OCrab(params)); + } + ); + + RegisterActor("mk:cheep_cheep", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OCheepCheep(params)); + } + ); + + RegisterActor("mk:bomb_kart", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OBombKart(params)); + } + ); + + RegisterActor("mk:bat", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OBat(params)); + } + ); + + RegisterActor("mk:boos", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OBoos(params)); + } + ); + + RegisterActor("mk:trophy", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OTrophy(params)); + } + ); + + RegisterActor("mk:trash_bin", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OTrashBin(params)); + } + ); + + RegisterActor("mk:seagull", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OSeagull(params)); + } + ); + + RegisterActor("mk:chain_chomp", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OChainChomp()); + } + ); + + RegisterActor("mk:podium", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OPodium(params)); + } + ); + + RegisterActor("mk:penguin", + [](const SpawnParams& params) { + gWorldInstance.AddObject(new OPenguin(params)); + } + ); + + RegisterActor("mk:banana", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new ABanana(params)); + } + ); + + RegisterActor("mk:mario_sign", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new AMarioSign(params)); + } + ); + + RegisterActor("mk:wario_sign", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new AWarioSign(params)); + } + ); + + RegisterActor("mk:falling_rock", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new AFallingRock(params)); + } + ); + + RegisterActor("mk:yoshi_egg", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_YOSHI_EGG); + } + ); + + RegisterActor("mk:piranha_plant", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_PIRANHA_PLANT); + } + ); + + RegisterActor("mk:tree_mario_raceway", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_TREE_MARIO_RACEWAY); + } + ); + + RegisterActor("mk:tree_yoshi_valley", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_TREE_YOSHI_VALLEY); + } + ); + + RegisterActor("mk:tree_royal_raceway", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_TREE_ROYAL_RACEWAY); + } + ); + + RegisterActor("mk:tree_moo_moo_farm", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_TREE_MOO_MOO_FARM); + } + ); + + RegisterActor("mk:palm_tree", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_PALM_TREE); + } + ); + + RegisterActor("mk:unknown_0x1a", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_TREE_LUIGI_RACEWAY); + } + ); + + RegisterActor("mk:unknown_0x1b", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_UNKNOWN_0x1B); + } + ); + + RegisterActor("mk:tree_peach_castle", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_TREE_PEACH_CASTLE); + } + ); + + RegisterActor("mk:tree_frappe_snowland", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_TREE_FRAPPE_SNOWLAND); + } + ); + + RegisterActor("mk:cactus1_kalamari_desert", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_CACTUS1_KALAMARI_DESERT); + } + ); + + RegisterActor("mk:cactus2_kalamari_desert", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_CACTUS2_KALAMARI_DESERT); + } + ); + + RegisterActor("mk:cactus3_kalamari_desert", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_CACTUS3_KALAMARI_DESERT); + } + ); + + RegisterActor("mk:bush_bowsers_castle", + [](const SpawnParams& params) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + Vec3f pos = { loc.x, loc.y, loc.z }; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + add_actor_to_empty_slot(pos, rot, vel, ACTOR_BUSH_BOWSERS_CASTLE); + } + ); + + RegisterActor("mk:train", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new ATrain(params)); + } + ); + + RegisterActor("mk:paddle_boat", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new ABoat(params)); + } + ); + + RegisterActor("mk:car", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new ACar(params)); + } + ); + + RegisterActor("mk:truck", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new ATankerTruck(params)); + } + ); + + RegisterActor("mk:tanker_truck", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new ATankerTruck(params)); + } + ); + + RegisterActor("mk:bus", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new ATankerTruck(params)); + } + ); + + RegisterActor("hm:spaghetti_ship", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new ASpaghettiShip(params)); + } + ); + + RegisterActor("hm:ship", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new AShip(params)); + } + ); + + RegisterActor("hm:starship", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new AStarship(params)); + } + ); + + RegisterActor("hm:cloud", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new ACloud(params)); + } + ); + + RegisterActor("hm:text", + [](const SpawnParams& params) { + gWorldInstance.AddActor(new AText(params)); + } + ); +} diff --git a/src/engine/RegisteredActors.h b/src/engine/RegisteredActors.h new file mode 100644 index 0000000000..6315bef5ad --- /dev/null +++ b/src/engine/RegisteredActors.h @@ -0,0 +1,3 @@ +#pragma once + +void RegisterGameActors(); diff --git a/src/engine/Registry.cpp b/src/engine/Registry.cpp index 0ea05c9d5a..9a68264d8d 100644 --- a/src/engine/Registry.cpp +++ b/src/engine/Registry.cpp @@ -1,59 +1,32 @@ -// #include -// #include -// #include -// #include - -// #include "Registry.h" -// #include "Course.h" -// #include "port/Game.h" - -// template Registry::Registry() { -// } - -// template void Registry::Add(std::string id, std::function fn) { -// // need to handle duplicate registration -// ContentVault[id] = fn; -// } - -// template -// void Registry::AddArgs(std::string id) { -// ArgsVault[id] = [](Args&&... args) -> T* { -// return new T(std::forward(args)...); -// }; -// } - -// template -// T* Registry::Get(std::string id) { -// auto it = ContentVault.find(id); -// if (it != ContentVault.end()) { -// return it->second(); -// } -// return nullptr; -// } - -// // Available Registries -// Registry Courses; -// Registry Cups; -// Registry Actors; - -// void AddCourse(std::string id, Course* course) { -// Courses.Add(id, [course]() { return course; }); -// course->Id = id; -// } - -// void AddCup(std::string id, Cup* cup) { -// Cups.Add(id, [cup]() { return cup; }); -// //cup->Id = id; -// } - -// void AddActor(std::string id, AActor* actor) { -// Actors.Add(id, [actor]() { return actor; }); -// } - -// void AddStockContent() { -// AddActor("mk:banana", new AMarioSign({0, 0, 0})); -// } - -// template class Registry; -// template class Registry; -// template class Registry; +#include +#include +#include + +#include "Registry.h" +#include "engine/CoreMath.h" + +extern "C" { +#include "actors.h" +#include "actor_types.h" +} + +std::unordered_map gActorRegistry; + +void RegisterActor(const std::string& name, + std::function spawnFunc) +{ + gActorRegistry[name] = { spawnFunc }; +} + +void Registry_SpawnActor(SpawnParams& params) { + auto it = gActorRegistry.find(params.Name); + if (it != gActorRegistry.end() && it->second.spawnFunc) { + printf("[Registry] Spawned %s\n", params.Name.c_str()); + it->second.spawnFunc(params); + } +} + +// @arg name Must be a resource name such as mk:car +bool Registry_Find(const std::string& name) { + return gActorRegistry.find(name) != gActorRegistry.end(); +} diff --git a/src/engine/Registry.h b/src/engine/Registry.h index f55818dc34..c6ee95be22 100644 --- a/src/engine/Registry.h +++ b/src/engine/Registry.h @@ -1,33 +1,14 @@ -// #pragma once +#pragma once -// #include "Course.h" -// #include "Cup.h" -// #include -// #include "AllActors.h" -// #include "Actor.h" -// #include "objects/Object.h" +#include +#include "SpawnParams.h" -// class AActor; // <-- Forward delare +struct ActorRegistryEntry { + std::function spawnFunc; +}; -// template class Registry { -// public: -// Registry(); -// void Add(std::string name, std::function); - -// template -// void AddArgs(std::string id); -// T* Get(std::string id); -// private: -// std::unordered_map> ContentVault; -// std::map> ArgsVault; -// }; +extern std::unordered_map gActorRegistry; -// void AddCourse(std::string id, Course* course); -// void AddCup(std::string id, Cup* cup); -// void AddActor(std::string id, AActor* actor); - -// void AddStockContent(); - -// extern Registry Courses; -// extern Registry Cups; -// extern Registry Actors; +void Registry_SpawnActor(SpawnParams& params); +void RegisterActor(const std::string& name, + std::function spawnFunc); diff --git a/src/engine/Rulesets.cpp b/src/engine/Rulesets.cpp index acdbfde2af..23ea0a6cff 100644 --- a/src/engine/Rulesets.cpp +++ b/src/engine/Rulesets.cpp @@ -1,6 +1,8 @@ #include "Rulesets.h" #include "objects/Thwomp.h" #include "objects/Trophy.h" +#include "objects/BombKart.h" +#include "actors/Text.h" extern "C" { #include "code_800029B0.h" @@ -29,7 +31,7 @@ void Rulesets::PostInit() { for (auto object : gWorldInstance.Objects) { if (OThwomp* thwomp = dynamic_cast(object)) { gObjectList[thwomp->_objectIndex].unk_0D5 = OThwomp::States::JAILED; // Sets all the thwomp behaviour flags to marty - thwomp->State = OThwomp::States::JAILED; + thwomp->Behaviour = OThwomp::States::JAILED; } } } @@ -37,12 +39,37 @@ void Rulesets::PostInit() { if (CVarGetInteger("gAllBombKartsChase", false) == true) { for (auto object : gWorldInstance.Objects) { if (OBombKart* kart = dynamic_cast(object)) { - kart->State = OBombKart::States::CHASE; + kart->Behaviour = OBombKart::States::CHASE; } } } if (CVarGetInteger("gGoFish", false) == true) { - gWorldInstance.AddObject(new OTrophy(FVector(0,0,0), OTrophy::TrophyType::GOLD, OTrophy::Behaviour::GO_FISH)); + OTrophy::Spawn(FVector(0,0,0), OTrophy::TrophyType::GOLD, OTrophy::Behaviour::GO_FISH); + } + + if (CVarGetInteger("gPlayerNames", false) == true) { + + std::string playerNames[NUM_PLAYERS] = { + "Player 1", "Player 2", "Player 3", + "Player 4", "Player 5", "Player 6", + "Player 7", "Player 8" + }; + + for (size_t i = 0; i < NUM_PLAYERS; i++) { + // text, pos, scale, mode, playerIndex + AText* text = AText::Spawn(playerNames[i], FVector(0, 0, 0), FVector(0.15f, 0.15f, 0.15f), AText::TextMode::FOLLOW_PLAYER, i); + text->ScaleX = 1.0f; + text->Scale.x = 0.15f; + text->Scale.y = 0.15f; + text->Scale.z = 0.15f; + text->Animate = false; // Cycle between colours similar to grand prix title text + + // White + for (size_t j = 0; j < 4; j++) { + text->TextColour[j] = {255, 255, 255, 255}; + } + + } } } diff --git a/src/engine/SpawnParams.h b/src/engine/SpawnParams.h new file mode 100644 index 0000000000..1b87ea03ad --- /dev/null +++ b/src/engine/SpawnParams.h @@ -0,0 +1,132 @@ +#pragma once + +#include +#include +#include +#include +#include "CoreMath.h" + +extern "C" { +#include "common_structs.h" +} + +// Helper function to handle std::optional deserialization +template +void get_optional_to(const nlohmann::json& j, const char* key, std::optional& opt_val) { + if (j.contains(key) && !j.at(key).is_null()) { + opt_val = j.at(key).get(); + } +} + +// Helper function to handle std::optional serialization +template +void set_optional_from(nlohmann::json& j, const char* key, const std::optional& opt_val) { + if (opt_val.has_value()) { + j[key] = opt_val.value(); + } +} + +// Used to save and load all game actors to the scene file +struct SpawnParams { + std::string Name; // Must use format mk:actor_name for stock game, mymodname:myactorname for mods + std::optional Type; // OObject type (ex. Emperor penguin, sliding penguin) or literal actor type for AActors + std::optional Behaviour; + std::optional Skin; + + std::optional Location; + std::optional Rotation; // int16_t + std::optional Scale; + std::optional Velocity; // Used by some AActors + std::optional PatrolStart; // OCrab + std::optional PatrolEnd; // OCrab & Hedgehog + std::optional PathSpan; // Cheep Cheep + + // Thwomps + std::optional PrimAlpha; // Thwomp + std::optional BoundingBoxSize; + + // Boos + std::optional Count; // vehicles + std::optional LeftExitSpan; // Disable boo + std::optional TriggerSpan; // Activate boos + std::optional RightExitSpan; // Disable boo + + + // Vehicles + std::optional PathIndex; // 0-3 Place vehicle this path + std::optional PathPoint; // Path point index + std::optional Bool; // train tender + std::optional Bool2; + std::optional Speed; // Train + std::optional SpeedB; // cars, trucks, buses, etc. + std::optional FVec2; + + std::optional Colour; + std::optional Colour2; + std::optional Colour3; + std::optional Colour4; + + void from_json(const nlohmann::json& j) { + j.at("Name").get_to(Name); + get_optional_to(j, "Type", Type); + get_optional_to(j, "Behaviour", Behaviour); + get_optional_to(j, "Skin", Skin); + get_optional_to(j, "Location", Location); + get_optional_to(j, "Rotation", Rotation); + get_optional_to(j, "Scale", Scale); + get_optional_to(j, "Velocity", Velocity); + get_optional_to(j, "PatrolStart", PatrolStart); + get_optional_to(j, "PatrolEnd", PatrolEnd); + get_optional_to(j, "PathSpan", PathSpan); + get_optional_to(j, "PrimAlpha", PrimAlpha); + get_optional_to(j, "BoundingBoxSize", BoundingBoxSize); + get_optional_to(j, "Count", Count); + get_optional_to(j, "LeftExitSpan", LeftExitSpan); + get_optional_to(j, "TriggerSpan", TriggerSpan); + get_optional_to(j, "RightExitSpan", RightExitSpan); + get_optional_to(j, "PathIndex", PathIndex); + get_optional_to(j, "PathPoint", PathPoint); + get_optional_to(j, "Bool", Bool); + get_optional_to(j, "Bool2", Bool2); + get_optional_to(j, "Speed", Speed); + get_optional_to(j, "SpeedB", SpeedB); + get_optional_to(j, "FVec2", FVec2); + get_optional_to(j, "Colour", Colour); + get_optional_to(j, "Colour2", Colour2); + get_optional_to(j, "Colour3", Colour3); + get_optional_to(j, "Colour4", Colour4); + } + + nlohmann::json to_json() const { + nlohmann::json j; + j["Name"] = Name; + set_optional_from(j, "Type", Type); + set_optional_from(j, "Behaviour", Behaviour); + set_optional_from(j, "Skin", Skin); + set_optional_from(j, "Location", Location); + set_optional_from(j, "Rotation", Rotation); + set_optional_from(j, "Scale", Scale); + set_optional_from(j, "Velocity", Velocity); + set_optional_from(j, "PatrolStart", PatrolStart); + set_optional_from(j, "PatrolEnd", PatrolEnd); + set_optional_from(j, "PathSpan", PathSpan); + set_optional_from(j, "PrimAlpha", PrimAlpha); + set_optional_from(j, "BoundingBoxSize", BoundingBoxSize); + set_optional_from(j, "Count", Count); + set_optional_from(j, "LeftExitSpan", LeftExitSpan); + set_optional_from(j, "TriggerSpan", TriggerSpan); + set_optional_from(j, "RightExitSpan", RightExitSpan); + set_optional_from(j, "PathIndex", PathIndex); + set_optional_from(j, "PathPoint", PathPoint); + set_optional_from(j, "Bool", Bool); + set_optional_from(j, "Bool2", Bool2); + set_optional_from(j, "Speed", Speed); + set_optional_from(j, "SpeedB", SpeedB); + set_optional_from(j, "FVec2", FVec2); + set_optional_from(j, "Colour", Colour); + set_optional_from(j, "Colour2", Colour2); + set_optional_from(j, "Colour3", Colour3); + set_optional_from(j, "Colour4", Colour4); + return j; + } +}; diff --git a/src/engine/StaticMeshActor.cpp b/src/engine/StaticMeshActor.cpp index 2e5fa11197..2ebad5b0c7 100644 --- a/src/engine/StaticMeshActor.cpp +++ b/src/engine/StaticMeshActor.cpp @@ -9,6 +9,8 @@ extern "C" { } 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) { + Name = "StaticMesh"; + ResourceName = "hm:static_mesh"; } diff --git a/src/engine/StaticMeshActor.h b/src/engine/StaticMeshActor.h index 4007aa2c10..f745a63f14 100644 --- a/src/engine/StaticMeshActor.h +++ b/src/engine/StaticMeshActor.h @@ -9,6 +9,7 @@ class StaticMeshActor { public: std::string Name; + std::string ResourceName; FVector Pos; IRotator Rot; FVector Scale; diff --git a/src/engine/World.cpp b/src/engine/World.cpp index 100b1275dd..0430edf6d5 100644 --- a/src/engine/World.cpp +++ b/src/engine/World.cpp @@ -126,14 +126,7 @@ void World::PreviousCourse() { AActor* World::AddActor(AActor* actor) { Actors.push_back(actor); - - if (actor->Model != NULL) { - gEditor.AddObject(actor->Name, (FVector*) &actor->Pos, (IRotator*)&actor->Rot, &actor->Scale, - (Gfx*) LOAD_ASSET_RAW(actor->Model), 1.0f, Editor::GameObject::CollisionType::VTX_INTERSECT, - 0.0f, (int32_t*) &actor->Type, 0); - } else { - gEditor.AddObject(actor->Name, (FVector*) &actor->Pos, (IRotator*)&actor->Rot, &actor->Scale, nullptr, 1.0f, Editor::GameObject::CollisionType::VTX_INTERSECT, 0.0f, (int32_t*)&actor->Type, 0); - } + actor->BeginPlay(); return Actors.back(); } @@ -146,12 +139,9 @@ struct Actor* World::AddBaseActor() { return reinterpret_cast(reinterpret_cast(Actors.back()) + sizeof(void*)); } -void World::AddEditorObject(Actor* actor, const char* name) { - if (actor->model != NULL) { - gEditor.AddObject(name, (FVector*) &actor->pos, (IRotator*)&actor->rot, nullptr, (Gfx*)LOAD_ASSET_RAW(actor->model), 1.0f, Editor::GameObject::CollisionType::VTX_INTERSECT, 0.0f, (int32_t*)&actor->type, 0); - } else { - gEditor.AddObject(name, (FVector*) &actor->pos, (IRotator*)&actor->rot, nullptr, nullptr, 1.0f, Editor::GameObject::CollisionType::VTX_INTERSECT, 0.0f, (int32_t*)&actor->type, 0); - } +void World::ActorBeginPlay(Actor* actor) { + AActor* act = ConvertActorToAActor(actor); + act->BeginPlay(); } /** @@ -189,8 +179,6 @@ 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(); - auto gameObj = gEditor.AddObject(actor->Name.c_str(), &actor->Pos, &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; } @@ -200,29 +188,14 @@ void World::DrawStaticMeshActors() { } } -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); - if (object->_objectIndex != -1) { - Object* cObj = &gObjectList[object->_objectIndex]; - - if (cObj->model != NULL) { - gEditor.AddObject(object->Name, (FVector*) &cObj->origin_pos[0], (IRotator*)&cObj->orientation, nullptr, (Gfx*)LOAD_ASSET_RAW(cObj->model), 1.0f, Editor::GameObject::CollisionType::VTX_INTERSECT, 0.0f, &object->_objectIndex, -1); - } else { - gEditor.AddObject(object->Name, (FVector*) &cObj->origin_pos[0], (IRotator*)&cObj->orientation, nullptr, nullptr, 1.0f, Editor::GameObject::CollisionType::VTX_INTERSECT, 0.0f, &object->_objectIndex, -1); - } - } + // This is an example of how to get the C object. + // However, nothing is being done with it, so it's been commented out. + // if (object->_objectIndex != -1) { + // Object* cObj = &gObjectList[object->_objectIndex]; + // } return Objects.back(); } @@ -267,7 +240,7 @@ void World::DrawParticles(s32 cameraId) { // Sets OObjects or AActors static member variables back to default values void World::Reset() { for (const auto& object : Objects) { - object->Reset(); + object->Reset(); // Used for OPenguin } } @@ -280,7 +253,6 @@ Object* World::GetObjectByIndex(size_t index) { } void World::ClearWorld(void) { - World::DeleteStaticMeshActors(); CM_CleanWorld(); // for (size_t i = 0; i < ARRAY_COUNT(gCollisionMesh); i++) { diff --git a/src/engine/World.h b/src/engine/World.h index bd55a5166a..52a307be52 100644 --- a/src/engine/World.h +++ b/src/engine/World.h @@ -5,16 +5,8 @@ #include "engine/courses/Course.h" #include "objects/Object.h" #include "Cup.h" -#include "vehicles/Train.h" -#include "vehicles/Car.h" -#include "objects/BombKart.h" #include "PlayerBombKart.h" -#include "vehicles/Train.h" #include "TrainCrossing.h" -#include "objects/Thwomp.h" -#include "objects/Penguin.h" -#include "objects/Seagull.h" -#include "objects/Lakitu.h" #include #include #include "Actor.h" @@ -33,7 +25,6 @@ class Cup; // <-- Forward declaration class OObject; class Course; class StaticMeshActor; -class AVehicle; class OBombKart; class TrainCrossing; class OLakitu; @@ -43,8 +34,8 @@ class World { typedef struct Matrix { Mtx Screen2D; // Orthogonal projection for UI, skybox, and such Mtx Ortho; - std::array Persp; - std::array LookAt; + std::array Persp; + std::array LookAt; std::array Karts; // Eight players * four screens std::array Shadows; // Eight players * four screens std::deque Hud; @@ -63,7 +54,7 @@ typedef struct Matrix { AActor* AddActor(AActor* actor); struct Actor* AddBaseActor(); - void AddEditorObject(Actor* actor, const char* name); + void ActorBeginPlay(Actor* actor); AActor* GetActor(size_t index); void TickActors(); @@ -72,7 +63,6 @@ typedef struct Matrix { 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); diff --git a/src/engine/actors/Banana.cpp b/src/engine/actors/Banana.cpp index 6c7f837d1d..d338d332e8 100644 --- a/src/engine/actors/Banana.cpp +++ b/src/engine/actors/Banana.cpp @@ -10,19 +10,22 @@ void update_actor_banana(struct BananaActor*); void render_actor_banana(Camera*, float[4][4], struct BananaActor*); } -ABanana::ABanana(uint16_t playerId, const float pos[3], const s16 rot[3], const float velocity[3]) { +ABanana::ABanana(const SpawnParams& params) : AActor(params) { Name = "Banana"; - // Initialize the BananaActor's position, rotation, and velocity - std::copy(pos, pos + 3, Pos); - //std::copy(rot, rot + 3, this->a.rot); - std::copy(velocity, velocity + 3, Velocity); + ResourceName = "mk:banana"; + + FVector pos = params.Location.value_or(FVector(0, 0, 0)); + Pos[0] = pos.x; Pos[1] = pos.y; Pos[2] = pos.z; + + FVector vel = params.Velocity.value_or(FVector(0, 0, 0)); + Velocity[0] = vel.x; Velocity[1] = vel.y; Velocity[2] = vel.z; Type = 6; // ACTOR_BANANA Flags = -0x8000; Unk_04 = 20; State = HELD_BANANA; - PlayerId = playerId; + PlayerId = 0; // Don't remember why this is here //this->a.unk_08 = 0.0f; Flags |= 0x4000 | 0x1000; diff --git a/src/engine/actors/Banana.h b/src/engine/actors/Banana.h index d8c55e9cbc..266cbf4760 100644 --- a/src/engine/actors/Banana.h +++ b/src/engine/actors/Banana.h @@ -9,7 +9,7 @@ class ABanana : public AActor { uint16_t PlayerId; // Constructor - ABanana(uint16_t playerId, const float pos[3], const s16 rot[3], const float velocity[3]); + ABanana(const SpawnParams& params); virtual ~ABanana() override = default; // Virtual functions to be overridden by derived classes diff --git a/src/engine/actors/BowserStatue.cpp b/src/engine/actors/BowserStatue.cpp index c680c4c254..af9036fc3b 100644 --- a/src/engine/actors/BowserStatue.cpp +++ b/src/engine/actors/BowserStatue.cpp @@ -14,6 +14,7 @@ Gfx gBowserStatueGfx[162]; ABowserStatue::ABowserStatue(FVector pos, ABowserStatue::Behaviour behaviour) { Name = "Bowser Statue"; + ResourceName = "mk:bowser_statue"; Pos = pos; ABowserStatue::Behaviour _behaviour = behaviour; } diff --git a/src/engine/actors/BowserStatue.h b/src/engine/actors/BowserStatue.h index 869f6910fa..51d58c0f1c 100644 --- a/src/engine/actors/BowserStatue.h +++ b/src/engine/actors/BowserStatue.h @@ -13,6 +13,8 @@ extern "C" { extern Vtx gBowserStatueVtx[717]; extern Gfx gBowserStatueGfx[162]; +// The data for this actor is generated and cut out from the actual track geography +// That generator is currently commented out. So this actor is not usable atm. class ABowserStatue : public AActor { public: enum Behaviour { diff --git a/src/engine/actors/Cloud.cpp b/src/engine/actors/Cloud.cpp index 63139f89d1..34f6466c9c 100644 --- a/src/engine/actors/Cloud.cpp +++ b/src/engine/actors/Cloud.cpp @@ -2,7 +2,8 @@ #include "Cloud.h" #include "engine/Actor.h" -#include "World.h" +#include "engine/World.h" +#include "port/Game.h" extern "C" { #include "macros.h" @@ -10,24 +11,48 @@ extern "C" { #include "math_util.h" #include "actor_types.h" #include "actors.h" +#include "other_textures.h" extern f32 gKartHopInitialVelocityTable[]; extern f32 gKartGravityTable[]; } -ACloud::ACloud(FVector pos) { - Name = "Cloud"; - Pos[0] = pos.x; - Pos[1] = pos.y; - Pos[2] = pos.z; - Rot[0] = 0; - Rot[1] = 0; - Rot[2] = 0; +ACloud::ACloud(const SpawnParams& params) : AActor(params) { + Name = "Cloud"; + ResourceName = "hm:cloud"; + FVector pos = params.Location.value_or(FVector(0, 0, 0)); + Pos[0] = pos.x; + Pos[1] = pos.y; + Pos[2] = pos.z; + + Rot[0] = 0; + Rot[1] = 0; + Rot[2] = 0; + + TimerLength = params.Type.value_or(500); // How long the effect lasts + Hop = params.Speed.value_or(3.0f); // How long the effect lasts + Gravity = params.SpeedB.value_or(200.0f); // How long the effect lasts // Flags = -0x8000 | 0x4000; BoundingBoxSize = 2.0f; } +void ACloud::SetSpawnParams(SpawnParams& params) { + AActor::SetSpawnParams(params); + params.Name = ResourceName; + params.Type = TimerLength; + params.Speed = Hop; + params.SpeedB = Gravity; +} + +extern Gfx cloud_mesh[]; +void ACloud::BeginPlay() { + // Prevent collision mesh from being generated extra times. + if (Triangles.size() == 0) { + Editor::GenerateCollisionMesh(this, (Gfx*)cloud_mesh, 1.0f); + } +} + void ACloud::Tick() { Rot[1] += 0x200; @@ -35,7 +60,7 @@ void ACloud::Tick() { Timer++; // Increment timer } - if (Timer > 500) { // Time has expired, reset the actor and player + if (Timer > TimerLength) { // Time has expired, reset the actor and player PickedUp = false; if (_player) { gKartHopInitialVelocityTable[_player->characterId] = OldHop; // reset back to normal @@ -46,8 +71,6 @@ void ACloud::Tick() { } } -extern Gfx cloud_mesh[]; - void ACloud::Draw(Camera* camera) { Mat4 mtx; @@ -71,7 +94,9 @@ void ACloud::Collision(Player* player, AActor* actor) { OldHop = gKartHopInitialVelocityTable[player->characterId]; OldGravity = gKartGravityTable[player->characterId]; + // Hop height gKartHopInitialVelocityTable[player->characterId] = Hop; + // How strong gravity is gKartGravityTable[player->characterId] = Gravity; } } @@ -133,9 +158,53 @@ Gfx mat_cloud_cutout[] = { Gfx cloud_mesh[] = { // gsSPClearGeometryMode(G_LIGHTING), // gsSPVertex(cloud_mesh_vtx_cull + 0, 8, 0), - gsSPSetGeometryMode(G_LIGHTING), + //gsSPSetGeometryMode(G_LIGHTING), // gsSPCullDisplayList(0, 7), gsSPDisplayList(mat_cloud_cutout), gsSPDisplayList(cloud_mesh_tri_0), gsSPEndDisplayList(), }; + +void ACloud::DrawEditorProperties() { + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = FVector(Pos[0], Pos[1], Pos[2]); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Effect Timer"); + ImGui::SameLine(); + int32_t type = static_cast(TimerLength); + if (ImGui::InputInt("##Type", &type)) { + TimerLength = static_cast(type); + } + + ImGui::Text("Hop"); + ImGui::SameLine(); + + if (ImGui::DragFloat("##Speed", &Hop, 0.01f)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeed")) { + Hop = 0.0f; + } + + ImGui::Text("Gravity"); + ImGui::Text("Higher value = stronger gravity"); + + if (ImGui::DragFloat("##SpeedB", &Gravity, 1.0f)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeedB")) { + Gravity = 0.0f; + } +} \ No newline at end of file diff --git a/src/engine/actors/Cloud.h b/src/engine/actors/Cloud.h index 4805c08b9e..fb334b680e 100644 --- a/src/engine/actors/Cloud.h +++ b/src/engine/actors/Cloud.h @@ -3,6 +3,7 @@ #include #include "engine/Actor.h" #include "CoreMath.h" +#include "engine/World.h" extern "C" { #include "macros.h" @@ -13,27 +14,38 @@ extern "C" { class ACloud : public AActor { public: - - - // Constructor - ACloud(FVector pos); + ACloud(const SpawnParams& params); virtual ~ACloud() override = default; - // Virtual functions to be overridden by derived classes + // This is simply a helper function to keep Spawning code clean + static inline ACloud* Spawn(FVector pos, uint16_t time, f32 hop, f32 gravity) { + SpawnParams params = { + .Name = "hm:cloud", + .Type = time, // How long the effect is active + .Location = pos, + .Speed = hop, // How high you hop + .SpeedB = gravity, // How much gravity is effected + }; + return static_cast(gWorldInstance.AddActor(new ACloud(params))); + } + virtual void Tick() override; virtual void Draw(Camera*) override; + virtual void BeginPlay() override; + virtual void SetSpawnParams(SpawnParams& params) override; + virtual void DrawEditorProperties() override; virtual void Collision(Player* player, AActor* actor) override; virtual bool IsMod() override; bool PickedUp = false; + uint32_t TimerLength = 500; uint32_t Timer = 0; - + Player* _player = NULL; - + f32 Hop = 3.0f; f32 Gravity = 200.0f; - f32 OldHop = 0; f32 OldGravity = 0; diff --git a/src/engine/actors/FallingRock.cpp b/src/engine/actors/FallingRock.cpp new file mode 100644 index 0000000000..80e9682944 --- /dev/null +++ b/src/engine/actors/FallingRock.cpp @@ -0,0 +1,231 @@ +#include "FallingRock.h" + +#include +#include "CoreMath.h" +#include +#include "port/interpolation/FrameInterpolation.h" +#include "port/Game.h" + +extern "C" { +#include "common_structs.h" +#include "math_util.h" +#include "main.h" +#include "actor_types.h" +#include "code_800029B0.h" +#include "collision.h" +#include "code_800029B0.h" +#include "external.h" +} + +size_t AFallingRock::_count = 0; + +AFallingRock::AFallingRock(SpawnParams params) : AActor(params) { + Type = ACTOR_FALLING_ROCK; + Name = "Falling Rock"; + ResourceName = "mk:falling_rock"; + + FVector pos = params.Location.value_or(FVector(0, 0, 0)); + TimerLength = params.Behaviour.value_or(80); + Pos[0] = pos.x * gCourseDirection; + Pos[1] = pos.y + 10.0f; + Pos[2] = pos.z; + State = _count; + func_802AAAAC(&Unk30); + + Flags = -0x8000; + Flags |= 0x4000; + BoundingBoxSize = 10.0f; + Model = d_course_choco_mountain_dl_falling_rock; + + _count += 1; +} + +void AFallingRock::SetSpawnParams(SpawnParams& params) { + AActor::SetSpawnParams(params); + params.Behaviour = TimerLength; +} + +void AFallingRock::Reset() { + RespawnTimer = TimerLength; + FVector pos = SpawnPos; + Pos[0] = (f32) pos.x * gCourseDirection; + Pos[1] = (f32) pos.y + 10.0f; + Pos[2] = (f32) pos.z; + vec3f_set(Velocity, 0, 0, 0); + vec3s_set(Rot, 0, 0, 0); +} + +bool AFallingRock::IsMod() { + return true; +} + +/** + * @brief Updates the falling rock actor. + * Actor used in Choco Mountain. + * + * @param rock + */ +void AFallingRock::Tick() { + Vec3f unkVec; + f32 pad0; + f32 pad1; + + if (RespawnTimer != 0) { + RespawnTimer -= 1; + return; + } + if (Pos[1] < CM_GetWaterLevel(Pos, NULL)) { + AFallingRock::Reset(); + } + Rot[0] += (s16) ((Velocity[2] * 5461.0f) / 20.0f); + Rot[2] += (s16) ((Velocity[0] * 5461.0f) / 20.0f); + Velocity[1] -= 0.1; + if (Velocity[1] < (-2.0f)) { + Velocity[1] = -2.0f; + } + Pos[0] += Velocity[0]; + Pos[1] += Velocity[1]; + Pos[2] += Velocity[2]; + pad1 = Velocity[1]; + check_bounding_collision(&Unk30, 10.0f, Pos[0], Pos[1], Pos[2]); + pad0 = Unk30.surfaceDistance[2]; + if (pad0 < 0.0f) { + unkVec[0] = -Unk30.orientationVector[0]; + unkVec[1] = -Unk30.orientationVector[1]; + unkVec[2] = -Unk30.orientationVector[2]; + Pos[0] += unkVec[0] * Unk30.surfaceDistance[2]; + Pos[1] += unkVec[1] * Unk30.surfaceDistance[2]; + Pos[2] += unkVec[2] * Unk30.surfaceDistance[2]; + adjust_pos_orthogonally(unkVec, pad0, Velocity, 2.0f); + Velocity[1] = -1.2f * pad1; + func_800C98B8(Pos, Velocity, SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x0F)); + } + pad0 = Unk30.surfaceDistance[0]; + if (pad0 < 0.0f) { + unkVec[1] = -Unk30.unk48[1]; + if (unkVec[1] == 0.0f) { + Velocity[1] *= -1.2f; + return; + } else { + unkVec[0] = -Unk30.unk48[0]; + unkVec[2] = -Unk30.unk48[2]; + Pos[0] += unkVec[0] * Unk30.surfaceDistance[0]; + Pos[1] += unkVec[1] * Unk30.surfaceDistance[0]; + Pos[2] += unkVec[2] * Unk30.surfaceDistance[0]; + adjust_pos_orthogonally(unkVec, pad0, Velocity, 2.0f); + Velocity[1] = -1.2f * pad1; + func_800C98B8(Pos, Velocity, SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x0F)); + } + } + pad0 = Unk30.surfaceDistance[1]; + if (pad0 < 0.0f) { + unkVec[1] = -Unk30.unk54[1]; + if (unkVec[1] == 0.0f) { + Velocity[1] *= -1.2f; + } else { + unkVec[0] = -Unk30.unk54[0]; + unkVec[2] = -Unk30.unk54[2]; + Pos[0] += unkVec[0] * Unk30.surfaceDistance[1]; + Pos[1] += unkVec[1] * Unk30.surfaceDistance[1]; + Pos[2] += unkVec[2] * Unk30.surfaceDistance[1]; + pad1 = Velocity[1]; + adjust_pos_orthogonally(unkVec, pad0, Velocity, 2.0f); + Velocity[1] = -1.2f * pad1; + func_800C98B8(Pos, Velocity, SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x0F)); + } + } +} + + + +/** + * @brief Renders the falling rock actor. + * Actor used in Choco Mountain. + * + * @param camera + * @param rock + */ +void AFallingRock::Draw(Camera* camera) { + Vec3s sp98; + Vec3f sp8C; + Mat4 mtx; + f32 height; + UNUSED s32 pad[4]; + + if (RespawnTimer != 0) { + return; + } + + height = is_within_render_distance(camera->pos, Pos, camera->rot[1], 400.0f, gCameraZoom[camera - camera1], + 4000000.0f); + + if (CVarGetInteger("gNoCulling", 0) == 1) { + height = CLAMP(height, 0.0f, 250000.0f); + } + + if (height < 0.0f) { + return; + } + + if (height < 250000.0f) { + + if (Unk30.unk34 == 1) { + sp8C[0] = Pos[0]; + sp8C[2] = Pos[2]; + height = calculate_surface_height(sp8C[0], Pos[1], sp8C[2], Unk30.meshIndexZX); + sp98[0] = 0; + sp98[1] = 0; + sp98[2] = 0; + sp8C[1] = height + 2.0f; + + FrameInterpolation_RecordOpenChild("rock_shadow", (uintptr_t) this); + mtxf_pos_rotation_xyz(mtx, sp8C, sp98); + if (render_set_position(mtx, 0) == 0) { + FrameInterpolation_RecordCloseChild(); + return; + } + gSPDisplayList(gDisplayListHead++, (Gfx*)d_course_choco_mountain_dl_6F88); + FrameInterpolation_RecordCloseChild(); + } + } + + // @port: Tag the transform. + FrameInterpolation_RecordOpenChild("rock", (uintptr_t) this); + + mtxf_pos_rotation_xyz(mtx, Pos, Rot); + if (render_set_position(mtx, 0) == 0) { + // @port Pop the transform id. + FrameInterpolation_RecordCloseChild(); + return; + } + gSPDisplayList(gDisplayListHead++, (Gfx*)d_course_choco_mountain_dl_falling_rock); + + // @port Pop the transform id. + FrameInterpolation_RecordCloseChild(); +} + +void AFallingRock::DrawEditorProperties() { + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = SpawnPos; + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Respawn Timer"); + ImGui::SameLine(); + + int32_t behaviour = static_cast(TimerLength); + + if (ImGui::InputInt("##Behaviour", &behaviour)) { + TimerLength = static_cast(behaviour); + } +} diff --git a/src/engine/actors/FallingRock.h b/src/engine/actors/FallingRock.h new file mode 100644 index 0000000000..ffb8444e2a --- /dev/null +++ b/src/engine/actors/FallingRock.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include "engine/Actor.h" +#include "CoreMath.h" +#include "engine/SpawnParams.h" +#include "engine/CoreMath.h" +#include "engine/World.h" + +class World; +extern World gWorldInstance; + +extern "C" { +#include "common_structs.h" +} + +// Falls from the sky bouncing off of geography until it goes through water. +// Then after a brief period of time, respawns. +class AFallingRock : public AActor { +public: + + explicit AFallingRock(SpawnParams params); + ~AFallingRock() { + _count -= 1; + }; + + // This is simply a helper function to keep Spawning code clean + // @arg respawnTimer default game used 60, 120, 180 as the timer. Time until respawn after reaching the bottom? + static inline AFallingRock* Spawn(FVector pos, int16_t respawnTimer) { + SpawnParams params = { + .Name = "mk:falling_rock", + .Behaviour = respawnTimer, + .Location = pos, + }; + return static_cast(gWorldInstance.AddActor(new AFallingRock(params))); + } + + int16_t TimerLength = 80; + + virtual void SetSpawnParams(SpawnParams& params) override; + virtual bool IsMod() override; + virtual void Tick() override; + virtual void Draw(Camera*) override; + virtual void DrawEditorProperties() override; + void Reset(); + + private: + uint32_t RespawnTimer = 0; + static size_t _count; +}; diff --git a/src/engine/actors/Finishline.cpp b/src/engine/actors/Finishline.cpp index 6a82f03f01..4164b5f3b4 100644 --- a/src/engine/actors/Finishline.cpp +++ b/src/engine/actors/Finishline.cpp @@ -21,30 +21,41 @@ extern f32 gKartGravityTable[]; size_t AFinishline::_count = 0; -AFinishline::AFinishline(std::optional pos) { +AFinishline::AFinishline(const SpawnParams& params) : AActor(params) { Name = "Finishline"; + ResourceName = "mk:finishline"; - if (pos.has_value()) { + if (params.Location.has_value()) { + FVector pos = params.Location.value_or(FVector(0, 0, 0)); // Set spawn point to the provided position - Pos[0] = D_8015F8D0[0] = pos.value().x; - Pos[1] = D_8015F8D0[1] = pos.value().y - 15; - Pos[2] = D_8015F8D0[2] = pos.value().z; + Pos[0] = D_8015F8D0[0] = pos.x; + Pos[1] = D_8015F8D0[1] = pos.y - 15; + Pos[2] = D_8015F8D0[2] = pos.z; } else { // Set spawn point to the tracks first path point. - Pos[0] = D_8015F8D0[0] = gCurrentTrackPath->posX; - Pos[1] = D_8015F8D0[1] = (f32) (gCurrentTrackPath->posY - 15); - Pos[2] = D_8015F8D0[2] = gCurrentTrackPath->posZ; + Pos[0] = D_8015F8D0[0] = gCurrentTrackPath->x; + Pos[1] = D_8015F8D0[1] = (f32) (gCurrentTrackPath->y - 15); + Pos[2] = D_8015F8D0[2] = gCurrentTrackPath->z; } - Rot[0] = 0; - Rot[1] = 0; - Rot[2] = 0; + IRotator rot = params.Rotation.value_or(IRotator(0, 0, 0)); + + Rot[0] = rot.pitch; + Rot[1] = rot.yaw; + Rot[2] = rot.roll; Flags = -0x8000 | 0x4000; BoundingBoxSize = 0.0f; } +void AFinishline::BeginPlay() { + // Prevent collision mesh from being generated extra times. + if (Triangles.size() == 0) { + Editor::GenerateCollisionMesh(this, (Gfx*)LOAD_ASSET_RAW(D_0D001B90), 1.0f); + } +} + void AFinishline::Tick() { } diff --git a/src/engine/actors/Finishline.h b/src/engine/actors/Finishline.h index 3c0e84de82..ec99b637f2 100644 --- a/src/engine/actors/Finishline.h +++ b/src/engine/actors/Finishline.h @@ -3,6 +3,7 @@ #include #include "CoreMath.h" #include "engine/Actor.h" +#include "engine/World.h" extern "C" { #include "macros.h" @@ -17,13 +18,31 @@ class AFinishline : public AActor { * Default behaviour places the finishline at the first waypoint. * @arg pos, optional. Sets a custom position */ - AFinishline(std::optional pos); + AFinishline(const SpawnParams& params); virtual ~AFinishline() override = default; + // This is simply a helper function to keep Spawning code clean + static inline AFinishline* Spawn(FVector pos, IRotator rot) { + SpawnParams params = { + .Name = "mk:finishline", + .Location = pos, + .Rotation = rot, + }; + return static_cast(gWorldInstance.AddActor(new AFinishline(params))); + } + + static inline AFinishline* Spawn() { + SpawnParams params = { + .Name = "mk:finishline", + }; + return static_cast(gWorldInstance.AddActor(new AFinishline(params))); + } + // Virtual functions to be overridden by derived classes virtual void Tick() override; virtual void Draw(Camera*) override; + virtual void BeginPlay() override; virtual void Collision(Player* player, AActor* actor) override; virtual bool IsMod() override; diff --git a/src/engine/actors/MarioSign.cpp b/src/engine/actors/MarioSign.cpp index f39e4f69bb..b5fc51860b 100644 --- a/src/engine/actors/MarioSign.cpp +++ b/src/engine/actors/MarioSign.cpp @@ -2,33 +2,60 @@ #include #include +#include "CoreMath.h" extern "C" { #include "common_structs.h" #include "math_util.h" #include "main.h" #include "actor_types.h" +#include "code_800029B0.h" +#include "collision.h" } -AMarioSign::AMarioSign(FVector pos) { +AMarioSign::AMarioSign(const SpawnParams& params) : AActor(params) { Type = ACTOR_MARIO_SIGN; Name = "Mario Sign"; - Pos[0] = pos.x; + ResourceName = "mk:mario_sign"; + Model = d_course_mario_raceway_dl_sign; + + Speed = params.Speed.value_or(182); + + FVector pos = params.Location.value_or(FVector(0, 0, 0)); + Pos[0] = pos.x * gCourseDirection; Pos[1] = pos.y; Pos[2] = pos.z; + + IRotator rot = params.Rotation.value_or(IRotator(0, 0, 0)); + Rot[0] = rot.pitch; + Rot[1] = rot.yaw; + Rot[2] = rot.roll; + + Scale = params.Scale.value_or(FVector(1.0f, 1.0f, 1.0f)); + + func_802AAAAC(&Unk30); + Flags = -0x8000; Flags |= 0x4000; } +bool AMarioSign::IsMod() { + return true; +} + +void AMarioSign::SetSpawnParams(SpawnParams& params) { + AActor::SetSpawnParams(params); +} + void AMarioSign::Tick() { if ((Flags & 0x800) == 0) { if ((Flags & 0x400) != 0) { Pos[1] += 4.0f; if (Pos[1] > 800.0f) { Flags |= 0x800; - Rot[1] += 1820; + Rot[1] += Speed * 10; // Originally 1820 } } else { - Rot[1] += 182; + Rot[1] += Speed; // Originally 182 } } } @@ -49,6 +76,7 @@ void AMarioSign::Draw(Camera *camera) { gSPSetGeometryMode(gDisplayListHead++, G_SHADING_SMOOTH); gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING); mtxf_pos_rotation_xyz(sp40, Pos, Rot); + if (render_set_position(sp40, 0) != 0) { gSPDisplayList(gDisplayListHead++, (Gfx*)d_course_mario_raceway_dl_sign); } diff --git a/src/engine/actors/MarioSign.h b/src/engine/actors/MarioSign.h index 099894dc19..f8864ad765 100644 --- a/src/engine/actors/MarioSign.h +++ b/src/engine/actors/MarioSign.h @@ -3,6 +3,10 @@ #include #include "engine/Actor.h" #include "CoreMath.h" +#include "engine/World.h" + +class World; +extern World gWorldInstance; extern "C" { #include "common_structs.h" @@ -12,8 +16,23 @@ class AMarioSign : public AActor { public: virtual ~AMarioSign() = default; - explicit AMarioSign(FVector pos); + explicit AMarioSign(const SpawnParams& params); + + // This is simply a helper function to keep Spawning code clean + static inline AMarioSign* Spawn(FVector pos, IRotator rot, FVector velocity, FVector scale) { + SpawnParams params = { + .Name = "mk:mario_sign", + .Location = pos, + .Rotation = rot, + .Scale = scale, + .Velocity = velocity, + .Speed = 182, + }; + return static_cast(gWorldInstance.AddActor(new AMarioSign(params))); + } + virtual bool IsMod() override; + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; virtual void Draw(Camera*) override; }; diff --git a/src/engine/actors/Ship.cpp b/src/engine/actors/Ship.cpp index 5af3553e21..8cbf0d8d60 100644 --- a/src/engine/actors/Ship.cpp +++ b/src/engine/actors/Ship.cpp @@ -2,6 +2,7 @@ #include #include "CoreMath.h" +#include "port/Game.h" #include "Matrix.h" extern "C" { @@ -13,29 +14,48 @@ extern "C" { #include "courses/harbour/ship3_model.h" } -AShip::AShip(FVector pos, AShip::Skin skin) { - Spawn = pos; - Spawn.y += 10; +AShip::AShip(const SpawnParams& params) : AActor(params) { + ResourceName = "hm:ship"; + FVector pos = params.Location.value_or(FVector(0, 0, 0)); + //Spawn.y += 10; Pos[0] = pos.x; Pos[1] = pos.y; Pos[2] = pos.z; Scale = FVector(0.4, 0.4, 0.4); - - switch(skin) { - case GHOSTSHIP: + + SpawnSkin = static_cast(params.Type.value_or(0)); + switch(SpawnSkin) { + case Skin::GHOSTSHIP: Name = "Ghostship"; _skin = ghostship_Plane_mesh; break; - case SHIP2: + case Skin::SHIP2: Name = "Ship_1"; _skin = ship2_SoH_mesh; break; - case SHIP3: + case Skin::SHIP3: Name = "Ship_2"; _skin = ship3_2Ship_mesh; break; } - Model = _skin; + Model = (const char*)_skin; +} + +void AShip::SetSpawnParams(SpawnParams& params) { + AActor::SetSpawnParams(params); + params.Name = ResourceName; + params.Type = static_cast(SpawnSkin); + params.Location = SpawnPos; + params.Rotation = SpawnRot; + params.Scale = SpawnScale; + params.Speed = Speed; +} + +void AShip::BeginPlay() { + // Prevent collision mesh from being generated extra times. + if (Triangles.size() == 0) { + Editor::GenerateCollisionMesh(this, (Gfx*)_skin, Scale.y); + } } void AShip::Tick() { @@ -54,3 +74,90 @@ void AShip::Tick() { } bool AShip::IsMod() { return true; } + +void AShip::DrawEditorProperties() { + ImGui::Text("Ship Type"); + ImGui::SameLine(); + + int32_t type = static_cast(SpawnSkin); + const char* items[] = { "Ghostship", "Ship 2", "Ship 3" }; + + if (ImGui::Combo("##Type", &type, items, IM_ARRAYSIZE(items))) { + SpawnSkin = static_cast(type); + + switch(SpawnSkin) { + case Skin::GHOSTSHIP: + Name = "Ghostship"; + _skin = ghostship_Plane_mesh; + break; + case Skin::SHIP2: + Name = "Ship_1"; + _skin = ship2_SoH_mesh; + break; + case Skin::SHIP3: + Name = "Ship_2"; + _skin = ship3_2Ship_mesh; + break; + } + Model = (const char*)_skin; + } + + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = FVector(Pos[0], Pos[1], Pos[2]); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Rotation"); + ImGui::SameLine(); + + IRotator objRot = GetRotation(); + + // Convert to temporary int values (to prevent writing 32bit values to 16bit variables) + int rot[3] = { + objRot.pitch, + objRot.yaw, + objRot.roll + }; + + if (ImGui::DragInt3("##Rotation", rot, 5.0f)) { + for (size_t i = 0; i < 3; i++) { + // Wrap around 0–65535 + rot[i] = (rot[i] % 65536 + 65536) % 65536; + } + IRotator newRot; + newRot.Set( + static_cast(rot[0]), + static_cast(rot[1]), + static_cast(rot[2]) + ); + Rotate(newRot); + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetRot")) { + IRotator rot = IRotator(0, 0, 0); + Rotate(rot); + } + + FVector scale = GetScale(); + ImGui::Text("Scale "); + ImGui::SameLine(); + + ImGui::DragFloat3("##Scale", (float*)&scale, 0.1f); + SetScale(scale); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetScale")) { + FVector scale = FVector(0.4f, 0.4f, 0.4f); + SetScale(scale); + } +} diff --git a/src/engine/actors/Ship.h b/src/engine/actors/Ship.h index fe5743922a..c1f082c786 100644 --- a/src/engine/actors/Ship.h +++ b/src/engine/actors/Ship.h @@ -2,8 +2,9 @@ #include #include -#include "engine/Actor.h" #include "CoreMath.h" +#include "engine/Actor.h" +#include "engine/World.h" extern "C" { #include "common_structs.h" @@ -19,16 +20,28 @@ class AShip : public AActor { SHIP3, }; - explicit AShip(FVector pos, AShip::Skin); + explicit AShip(const SpawnParams& params); virtual ~AShip() = default; + // This is simply a helper function to keep Spawning code clean + static inline AShip* Spawn(FVector pos, IRotator rot, FVector scale, int16_t skin) { + SpawnParams params = { + .Name = "hm:ship", + .Type = skin, // which ship model to use + .Location = pos, + .Rotation = rot, + .Scale = scale, + }; + return static_cast(gWorldInstance.AddActor(new AShip(params))); + } + + AShip::Skin SpawnSkin = Skin::GHOSTSHIP; + + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; + virtual void BeginPlay() override; virtual bool IsMod() override; - - FVector Spawn; - //FVector Pos; - ///IRotator Rot = {0, 0, 0}; - //FVector Scale = {0.4, 0.4, 0.4}; + virtual void DrawEditorProperties() override; private: Gfx* _skin; }; diff --git a/src/engine/actors/SpaghettiShip.cpp b/src/engine/actors/SpaghettiShip.cpp index b09ada3199..63ed048ec4 100644 --- a/src/engine/actors/SpaghettiShip.cpp +++ b/src/engine/actors/SpaghettiShip.cpp @@ -10,14 +10,22 @@ extern "C" { #include "courses/harbour/ship_model.h" } -ASpaghettiShip::ASpaghettiShip(FVector pos) { +ASpaghettiShip::ASpaghettiShip(const SpawnParams& params) : AActor(params) { Name = "Spaghetti Ship"; + ResourceName = "hm:spaghetti_ship"; + BoundingBoxSize = 3.0f; + + FVector pos = params.Location.value_or(FVector(0, 0, 0)); Pos[0] = pos.x; Pos[1] = pos.y; Pos[2] = pos.z; - Spawn = pos; - Spawn.y += 10; - Scale = {0.4, 0.4, 0.4}; + + Scale = params.Scale.value_or(FVector(0.4f, 0.4f, 0.4f)); + + IRotator rot = params.Rotation.value_or(IRotator(0, 0, 0)); + Rot[0] = rot.pitch; + Rot[1] = rot.yaw; + Rot[2] = rot.roll; } void ASpaghettiShip::Tick() { @@ -28,8 +36,9 @@ void ASpaghettiShip::Tick() { angle += speed; // Increment the angle to move in a circle // Update the position based on a circular path - Pos[0] = Spawn.x + radius * cosf(angle); - Pos[2] = Spawn.z + radius * sinf(angle); + FVector spawn = SpawnPos; + Pos[0] = spawn.x + radius * cosf(angle); + Pos[2] = spawn.z + radius * sinf(angle); // Rotate to face forward along the circle Rot[1] = -static_cast(angle * (32768.0f / M_PI / 2.0f)); diff --git a/src/engine/actors/SpaghettiShip.h b/src/engine/actors/SpaghettiShip.h index 70bd047d75..fef2e05a5a 100644 --- a/src/engine/actors/SpaghettiShip.h +++ b/src/engine/actors/SpaghettiShip.h @@ -2,8 +2,9 @@ #include #include -#include "engine/Actor.h" #include "CoreMath.h" +#include "engine/Actor.h" +#include "engine/World.h" extern "C" { #include "common_structs.h" @@ -12,13 +13,23 @@ extern "C" { class ASpaghettiShip : public AActor { public: - explicit ASpaghettiShip(FVector pos); + explicit ASpaghettiShip(const SpawnParams& params); virtual ~ASpaghettiShip() = default; + // This is simply a helper function to keep Spawning code clean + static inline ASpaghettiShip* Spawn(FVector pos, IRotator rot, FVector scale) { + SpawnParams params = { + .Name = "hm:spaghetti_ship", + .Location = pos, + .Rotation = rot, + .Scale = scale, + }; + return static_cast(gWorldInstance.AddActor(new ASpaghettiShip(params))); + } + virtual void Tick() override; virtual void Draw(Camera*) override; virtual bool IsMod() override; - FVector Spawn; IRotator WheelRot = {0, 0, 0}; }; diff --git a/src/engine/actors/Starship.cpp b/src/engine/actors/Starship.cpp index e104d0eb68..7723ac2d50 100644 --- a/src/engine/actors/Starship.cpp +++ b/src/engine/actors/Starship.cpp @@ -2,6 +2,7 @@ #include #include "Matrix.h" +#include "port/Game.h" extern "C" { #include "common_structs.h" @@ -10,26 +11,44 @@ extern "C" { #include "courses/harbour/starship_model.h" } -AStarship::AStarship(FVector pos) { +AStarship::AStarship(const SpawnParams& params) : AActor(params) { Name = "Starship"; - Spawn = pos; + ResourceName = "hm:starship"; + FVector pos = params.Location.value_or(FVector(0, 0, 0)); SetLocation(pos); - Scale = FVector(1.5, 1.5, 1.5); - Model = starship_Cube_mesh; + IRotator rot = params.Rotation.value_or(IRotator(0, 0, 0)); + Rot[0] = rot.pitch; + Rot[1] = rot.yaw; + Rot[2] = rot.roll; + Scale = params.Scale.value_or(FVector(0, 0, 0)); + Speed = params.Speed.value_or(0.01f); + SpeedB = params.SpeedB.value_or(150.0f); + Model = (const char*)starship_Cube_mesh; +} + +void AStarship::SetSpawnParams(SpawnParams& params) { + AActor::SetSpawnParams(params); + params.SpeedB = SpeedB; +} + +void AStarship::BeginPlay() { + // Prevent collision mesh from being generated extra times. + if (Triangles.size() == 0) { + Editor::GenerateCollisionMesh(this, (Gfx*)Model, 1.0f); + } } void AStarship::Tick() { static float angle = 0.0f; - float radius = 150.0f; - float speed = 0.01f; - angle += speed; + angle += Speed; // Move relative to the initial position FVector pos = GetLocation(); - pos.x = Spawn.x + radius * cosf(angle); - pos.z = Spawn.z + radius * sinf(angle); + FVector spawn = SpawnPos; + pos.x = spawn.x + SpeedB * cosf(angle); + pos.z = spawn.z + SpeedB * sinf(angle); SetLocation(pos); // Keep y from changing (or adjust it if necessary) @@ -40,3 +59,88 @@ void AStarship::Tick() { } bool AStarship::IsMod() { return true; } + +void AStarship::DrawEditorProperties() { + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = FVector(Pos[0], Pos[1], Pos[2]); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Rotation"); + ImGui::SameLine(); + + IRotator objRot = GetRotation(); + + // Convert to temporary int values (to prevent writing 32bit values to 16bit variables) + int rot[3] = { + objRot.pitch, + objRot.yaw, + objRot.roll + }; + + if (ImGui::DragInt3("##Rotation", rot, 5.0f)) { + for (size_t i = 0; i < 3; i++) { + // Wrap around 0–65535 + rot[i] = (rot[i] % 65536 + 65536) % 65536; + } + IRotator newRot; + newRot.Set( + static_cast(rot[0]), + static_cast(rot[1]), + static_cast(rot[2]) + ); + Rotate(newRot); + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetRot")) { + IRotator rot = IRotator(0, 0, 0); + Rotate(rot); + } + + FVector scale = GetScale(); + ImGui::Text("Scale "); + ImGui::SameLine(); + + ImGui::DragFloat3("##Scale", (float*)&scale, 0.1f); + SetScale(scale); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetScale")) { + FVector scale = FVector(0.4f, 0.4f, 0.4f); + SetScale(scale); + } + + ImGui::Text("Speed"); + ImGui::SameLine(); + + float speed = Speed; + if (ImGui::DragFloat("##Speed", &speed, 0.01f)) { + Speed = speed; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeed")) { + Speed = 0.0f; + } + + ImGui::Text("Radius"); + ImGui::SameLine(); + + float speed2 = SpeedB; + if (ImGui::DragFloat("##SpeedB", &speed2, 5.0f)) { + SpeedB = speed2; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeedB")) { + SpeedB = 0.0f; + } +} diff --git a/src/engine/actors/Starship.h b/src/engine/actors/Starship.h index 71b8983580..c76c10c68f 100644 --- a/src/engine/actors/Starship.h +++ b/src/engine/actors/Starship.h @@ -2,8 +2,9 @@ #include #include -#include "engine/Actor.h" #include "CoreMath.h" +#include "engine/Actor.h" +#include "engine/World.h" extern "C" { #include "common_structs.h" @@ -12,11 +13,27 @@ extern "C" { class AStarship : public AActor { public: - explicit AStarship(FVector pos); + explicit AStarship(const SpawnParams& params); virtual ~AStarship() = default; + // This is simply a helper function to keep Spawning code clean + static inline AStarship* Spawn(FVector pos, IRotator rot, FVector scale, f32 speed, f32 radius) { + SpawnParams params = { + .Name = "hm:starship", + .Location = pos, + .Rotation = rot, + .Scale = scale, + .Speed = speed, + .SpeedB = radius, + }; + return static_cast(gWorldInstance.AddActor(new AStarship(params))); + } + + float SpeedB; + + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; virtual bool IsMod() override; - - FVector Spawn; + virtual void BeginPlay() override; + virtual void DrawEditorProperties() override; }; diff --git a/src/engine/actors/Text.cpp b/src/engine/actors/Text.cpp new file mode 100644 index 0000000000..2d2fab0b01 --- /dev/null +++ b/src/engine/actors/Text.cpp @@ -0,0 +1,588 @@ +#include +#include +#include + +#include "Text.h" + +#include "port/interpolation/FrameInterpolation.h" +#include "engine/Matrix.h" +#include "engine/editor/EditorMath.h" + +extern "C" { +#include "defines.h" +#include "main.h" +#include "menu_items.h" +#include "assets/data_segment2.h" +#include "render_player.h" +#include "math_util.h" +#include "assets/texture_data_2.h" +#include "render_objects.h" +#include "common_structs.h" +#include "code_80005FD0.h" +} + +AText::AText(const SpawnParams& params) : AActor(params) { + Name = "Text"; + ResourceName = "hm:text"; + + SpawnPos = params.Location.value_or(FVector(0.0f, 100.0f, 0.0f)); + Pos[0] = SpawnPos.x; + Pos[1] = SpawnPos.y; + Pos[2] = SpawnPos.z; + + SpawnRot = params.Rotation.value_or(IRotator(0, 0, 0)); + Rot[0] = SpawnRot.pitch; + Rot[1] = SpawnRot.yaw; + Rot[2] = SpawnRot.roll; + + SpawnScale = params.Scale.value_or(FVector(1.0f, 1.0f, 1.0f)); + Scale = SpawnScale; + + ScaleX = params.FVec2.value_or(FVector{1.0f, 0.0f, 0.0f}).x; + + Mode = static_cast(params.Type.value_or(0)); // STATIONARY + + Animate = params.Bool.value_or(false); + FaceCamera = params.Bool2.value_or(true); + + WidthOffset = params.Speed.value_or(0.0f); + HeightOffset = params.SpeedB.value_or(8.0f); + + FVector options = params.Velocity.value_or(FVector{1.0f, 14000.0f, 0.0f}); + LetterSpacing = options.x; + Far = options.y; + Close = options.z; + + TextColour[0] = params.Colour.value_or(RGBA8{255, 255, 255, 255}); + TextColour[1] = params.Colour2.value_or(RGBA8{255, 255, 255, 255}); + TextColour[2] = params.Colour3.value_or(RGBA8{255, 255, 255, 255}); + TextColour[3] = params.Colour4.value_or(RGBA8{255, 255, 255, 255}); + + PlayerIndex = static_cast(params.Behaviour.value_or(0)); + if (PlayerIndex < 0 || PlayerIndex >= NUM_PLAYERS) { + PlayerIndex = 0; + } + + Text = ValidateString(params.Skin.value_or("Harbour Masters")); + AText::Print3D((char*)Text.c_str(), 0, CENTER_TEXT_MODE_2); +} + +/** + * Filters out bad characters (allows a-z, A-Z, 0-9, space) + * Returns "Blank Text" for blank input + * Returns "Invalid" if no valid input found + * Limits str to 20 characters + * + * The font does support some symbols and other language characters + * But these need to be checked thoroughly before white-listing. + */ +std::string AText::ValidateString(const std::string_view& s) { + if (s.empty()) { return "Blank Text"; } + + Text.clear(); + + for (char c : s) { + if (std::isalpha(static_cast(c)) || + std::isdigit(static_cast(c)) || + c == ' ') + { + Text += c; + if (Text.size() >= 20) { + break; + }; + } + } + + // No valid characters found + if (Text.empty()) { + return "Invalid"; + } + return Text; +} + +/* + * Most changes during runtime require a refresh because the text is generated statically + * with the intention of this code being somewhat performant + */ +void AText::Refresh() { + AText::TextureList.clear(); + AText::Print3D((char*)Text.c_str(), 0, CENTER_TEXT_MODE_2); +} + +bool AText::IsMod() { return true; } + +void AText::SetSpawnParams(SpawnParams& params) { + AActor::SetSpawnParams(params); + params.Name = ResourceName; + params.Type = static_cast(Mode); + params.Behaviour = PlayerIndex; + params.Skin = Text; + + params.Colour = TextColour[0]; + params.Colour2 = TextColour[1]; + params.Colour3 = TextColour[2]; + params.Colour4 = TextColour[3]; + + params.Speed = WidthOffset; + params.SpeedB = HeightOffset; + + params.Velocity = {LetterSpacing, Far, Close}; + params.FVec2 = {ScaleX, 0.0f, 0.0f}; + + params.Bool = Animate; + params.Bool2 = FaceCamera; +} + +void AText::Tick() { + switch(Mode) { + case STATIONARY: + break; // Do nothing + case FOLLOW_PLAYER: + AText::FollowPlayer(); + break; + } +} + +void AText::FollowPlayer() { + Pos[0] = gPlayers[PlayerIndex].pos[0] + WidthOffset; + Pos[1] = gPlayers[PlayerIndex].pos[1] + HeightOffset; + Pos[2] = gPlayers[PlayerIndex].pos[2]; + + // Animate text if player is in first place + // if (gGPCurrentRaceRankByPlayerId[PlayerIndex] == 0) { + // Animate = true; + // } else { + // Animate = false; + // } +} + +void AText::Draw(Camera* camera) { + switch(Mode) { + case STATIONARY: + break; // Do nothing + case FOLLOW_PLAYER: + if (PlayerIndex == camera->playerId) { + return; // Do not draw the local players own name + } + if ((gPlayers[PlayerIndex].effects & BOO_EFFECT) == BOO_EFFECT) { + FadeState = FADE_OUT; + AText::DrawText3D(camera); + return; // Skip expensive calculations below + } + break; + } + + f32 distance = is_within_render_distance(camera->pos, (float*)&Pos[0], camera->rot[1], Close, + gCameraZoom[camera - camera1], Far); + + if (distance == -1.0f) { + Dist = DistanceProps::TOO_FAR; + } else if (distance < Close) { + Dist = DistanceProps::TOO_CLOSE; + } else { + Dist = DistanceProps::ACTIVE; + } + + if (Dist != PrevState) { + PrevState = Dist; + if ((distance == -1.0f) || (distance < Close)) { + FadeState = FADE_OUT; + } else { + FadeState = FADE_IN; + } + } + + AText::DrawText3D(camera); +} + +/** + * These have been refactored for efficiency purposes. + * The new method uses 1 matrix to display the whole string + * And then setting vertex data is done during the setup/constructor phase, + * instead of during rendering + * This requires a refresh if the data ever changes + * + * This method is more efficient because the original version does all this work + * during the render phase. Now it's done during the actor spawn phase with the exception + * if any changes are made at runtime. + */ +void AText::Print3D(char* text, s32 tracking, s32 mode) { + char* temp_string = text; + s32 stringWidth = 0; + s32 glyphIndex; + s32 sp60; + + s32 column = 0; + s32 row = 0; + + if (text == NULL) { + // @port if invalid text is loaded it will skip rendering it. + return; + } + + while (*temp_string != '\0') { + glyphIndex = char_to_glyph_index(temp_string); + if (glyphIndex >= 0) { + stringWidth += ((gGlyphDisplayWidth[glyphIndex] + tracking) * ScaleX); + } else if ((glyphIndex != -2) && (glyphIndex == -1)) { + stringWidth += ((tracking + 7) * ScaleX); + } else { + return; + } + if (glyphIndex >= 0x30) { + temp_string += 2; + } else { + temp_string += 1; + } + } + + switch (mode) { + case LEFT_TEXT: + //! FAKE: + do { + } while (0); + case RIGHT_TEXT: + column -= stringWidth; + break; + case CENTER_TEXT_MODE_1: + case CENTER_TEXT_MODE_2: + column -= stringWidth / 2; + break; + default: + break; + } + + if (mode < 3) { + sp60 = 1; + } else { + sp60 = 2; + } + + while (*text != '\0') { + glyphIndex = char_to_glyph_index(text); + if (glyphIndex >= 0) { + AText::PrintLetter3D(gGlyphTextureLUT[glyphIndex], + column, row, sp60); + column = column + (s32) ((gGlyphDisplayWidth[glyphIndex] + tracking) * ScaleX); + } else if ((glyphIndex != -2) && (glyphIndex == -1)) { + column = column + (s32) ((tracking + 7) * ScaleX); + } else { + return; + } + if (glyphIndex >= 0x30) { + text += 2; + } else { + text += 1; + } + } + SetupVtx(); // position each letter +} + +void AText::PrintLetter3D(MenuTexture* glyphTexture, f32 column, f32 row, s32 mode) { + s32 var_v0; + u8* temp_v0_2; + f32 thing0; + f32 thing1; + MenuTexture* texture; + + texture = glyphTexture; + while (texture->textureData != NULL) { + if (texture->textureData != 0) { + f32 col = texture->dX + column; + + TextureList.emplace_back(CharacterList{ + (const char*) texture->textureData, + col, + texture->dY + row, + texture->width, + texture->height, + mode, + }); + } + texture++; + } +} + +void AText::SetupVtx() { + for (CharacterList& character : TextureList) { + + Vtx* vtxPtr; + switch (character.width) { + default: + vtxPtr = (Vtx*)&AText::myVtx[4]; + break; + case 16: + vtxPtr = (Vtx*)&AText::myVtx[4]; + break; + case 26: + vtxPtr = (Vtx*)&AText::myVtx[0]; + break; + case 30: + vtxPtr = (Vtx*)&AText::myVtx[8]; + break; + } + // printf("col %f width %d span %d\n", character.column, character.width, character.span); + // memcpy the vtx data into the unique vtx data for this letter + Vtx* vtxSrc = (Vtx*)vtxPtr; + memcpy(&character.vtx, vtxSrc, sizeof(Vtx) * 4); + + for (size_t i = 0; i < 4; i++) { + // Set the location for this letter (beside the previous letter) center the text over the anchor point + float span = (character.column * LetterSpacing); + character.vtx[i].v.ob[0] += (s16)span; + + // Set the colour for this letter + character.vtx[i].v.cn[0] = TextColour[i].r; + character.vtx[i].v.cn[1] = TextColour[i].g; + character.vtx[i].v.cn[2] = TextColour[i].b; + character.vtx[i].v.cn[3] = TextColour[i].a; + } + } +} + +void AText::DrawText3D(Camera* camera) { // Based on func_80095BD0 + Mat4 mtx; + + if (FaceCamera) { + ApplySphericalBillBoard(mtx, *(FVector*)Pos, Scale, camera->cameraId); + } else { + ApplyMatrixTransformations(mtx, *(FVector*)Pos, *(IRotator*)Rot, Scale); + } + + AddObjectMatrix(mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + FrameInterpolation_RecordOpenChild("actor_text", TAG_LETTER(this)); + gSPDisplayList(gDisplayListHead++, (Gfx*)D_020077A8); + switch (1) { + case 1: + gSPDisplayList(gDisplayListHead++, (Gfx*)D_020077F8); + break; + case 2: + gSPDisplayList(gDisplayListHead++, (Gfx*)D_02007818); + break; + } + + for (CharacterList& tex : TextureList) { + //printf("tex texture %p width %d height %d mode %d col %f\n", tex.Texture, tex.width, tex.height, tex.mode, tex.column); + gDPLoadTextureTile_4b(gDisplayListHead++, (Gfx*)tex.Texture, G_IM_FMT_I, tex.width, 0, 0, 0, tex.width, tex.height + 2, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + // gSPClearGeometryMode(gDisplayListHead++, G_ZBUFFER); + + if (Animate) { + AnimateColour(tex.vtx); + } + + switch(FadeState) { + case FADE_IN: + AText::FadeIn(tex.vtx); + break; + case FADE_OUT: + AText::FadeOut(tex.vtx); + break; + } + + gSPVertex(gDisplayListHead++, (uintptr_t)tex.vtx, 4, 0); + gSP2Triangles(gDisplayListHead++, 0, 2, 1, 0, 0, 3, 2, 0); + + // gSPSetGeometryMode(gDisplayListHead++, G_ZBUFFER); + } + + gSPDisplayList(gDisplayListHead++, (Gfx*)D_020077D8); + FrameInterpolation_RecordCloseChild(); +} + +void AText::AnimateColour(Vtx* vtx) { + u8 r = (sin(gGlobalTimer * 0.1f) * 0.5f + 0.5f) * 200; + u8 g = (sin(gGlobalTimer * 0.1f + 2.0f) * 0.5f + 0.5f) * 200; + u8 b = (sin(gGlobalTimer * 0.1f + 4.0f) * 0.5f + 0.5f) * 200; + + for (size_t i = 0; i < 4; i++) { + vtx[i].v.cn[0] = r; + vtx[i].v.cn[1] = g; + vtx[i].v.cn[2] = b; + } +} + +#define fadeSpeed 16 +void AText::FadeIn(Vtx* vtx) { + uint8_t alpha = vtx[0].v.cn[3]; + if (alpha + fadeSpeed > 255) { + alpha = 255; + FadeState = NO_FADE; + } else { + alpha += fadeSpeed; + } + + // Apply alpha to all 4 vertices + for (size_t i = 0; i < 4; i++) { + vtx[i].v.cn[3] = alpha; + } +} + +void AText::FadeOut(Vtx* vtx) { + uint8_t alpha = vtx[0].v.cn[3]; + if (alpha < fadeSpeed) { + alpha = 0; + FadeState = NO_FADE; + } else { + alpha -= fadeSpeed; + } + + // Apply alpha to all 4 vertices + for (size_t i = 0; i < 4; i++) { + vtx[i].v.cn[3] = alpha; + } +} +#undef fadeSpeed + +void AText::DrawEditorProperties() { + bool updated = false; + ImGui::Text("Text"); + ImGui::SameLine(); + + char text[21] = ""; + strncpy(text, Text.c_str(), sizeof(text)); + ImGui::InputText("Enter text", text, IM_ARRAYSIZE(text)); + + Text = std::string(text); + + ImGui::Text("Mode"); + ImGui::SameLine(); + + int32_t mode = static_cast(Mode); + const char* items[] = { "STATIONARY", "FOLLOW PLAYER" }; + + if (ImGui::Combo("##Type", &mode, items, IM_ARRAYSIZE(items))) { + Mode = static_cast(mode); + updated = true; + } + + ImGui::DragFloat("Far Render Dist", &Far); + ImGui::DragFloat("Close Render Dist", &Close); + + ImGui::Checkbox("Face Camera", &FaceCamera); + if (!FaceCamera) { + ImGui::Text("Rotation"); + ImGui::SameLine(); + + IRotator objRot = GetRotation(); + + // Convert to temporary int values (to prevent writing 32bit values to 16bit variables) + int rot[3] = { + objRot.pitch, + objRot.yaw, + objRot.roll + }; + + if (ImGui::DragInt3("##Rotation", rot, 5.0f)) { + for (size_t i = 0; i < 3; i++) { + // Wrap around 0–65535 + rot[i] = (rot[i] % 65536 + 65536) % 65536; + } + IRotator newRot; + newRot.Set( + static_cast(rot[0]), + static_cast(rot[1]), + static_cast(rot[2]) + ); + Rotate(newRot); + } + } + + switch(mode) { + case STATIONARY: { + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = GetLocation(); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + break; + } + case FOLLOW_PLAYER: + // Allow setting PlayerIndex + int32_t playerIdx = PlayerIndex + 1; + + // Draw the input box (ImGui input limited between 1 and 8) + ImGui::SetNextItemWidth(100); + if (ImGui::InputInt("Follow Player", &playerIdx)) { + // Clamp display value to [1, 8] + if (playerIdx < 1) playerIdx = 1; + if (playerIdx > 8) playerIdx = 8; + + // Update the internal value (0–7) + PlayerIndex = playerIdx - 1; + } + + ImGui::DragFloat("Width Offset", &WidthOffset); + ImGui::DragFloat("Height Offset", &HeightOffset); + break; + } + + DrawColourEditor(&updated); + + ImGui::Text("Transform Settings"); + ImGui::Separator(); + + ImGui::SetNextItemWidth(100); + ImGui::DragFloat("Scale X", &ScaleX, 0.1f, -5.0f, 5.0f, "%.2f"); + + ImGui::SetNextItemWidth(100); + if (ImGui::DragFloat("Letter Spacing", &LetterSpacing, 0.1f, 0.0f, 5.0f, "%.2f")) { + updated = true; + } + + if (updated) { + Refresh(); + } +} + +void AText::DrawColourEditor(bool* updated) { + ImGui::Checkbox("Use single colour", &SingleColour); + + if (SingleColour) + { + // Convert 8bit colours to float + ImVec4 colour( + TextColour[0].r / 255.0f, + TextColour[0].g / 255.0f, + TextColour[0].b / 255.0f, + TextColour[0].a / 255.0f + ); + + // Single color input + ImGui::ColorEdit4("Colour", (float*)&colour); + // Apply same color to all vertices + for (int i = 0; i < 4; i++) { + TextColour[i].r = FloatToU8(colour.x); + TextColour[i].g = FloatToU8(colour.y); + TextColour[i].b = FloatToU8(colour.z); + TextColour[i].a = FloatToU8(colour.w); + } + *updated = true; + + } else { + // Separate color pickers for each vertex + for (int i = 0; i < 4; i++) + { + ImVec4 colour2( + TextColour[i].r / 255.0f, + TextColour[i].g / 255.0f, + TextColour[i].b / 255.0f, + TextColour[i].a / 255.0f + ); + char label[32]; + snprintf(label, sizeof(label), "Vtx %d Colour", i); + if (ImGui::ColorEdit4(label, (float*)&colour2)) { + TextColour[i].r = FloatToU8(colour2.x); + TextColour[i].g = FloatToU8(colour2.y); + TextColour[i].b = FloatToU8(colour2.z); + TextColour[i].a = FloatToU8(colour2.w); + *updated = true; + } + } + } +} \ No newline at end of file diff --git a/src/engine/actors/Text.h b/src/engine/actors/Text.h new file mode 100644 index 0000000000..7a9ac74075 --- /dev/null +++ b/src/engine/actors/Text.h @@ -0,0 +1,149 @@ +#pragma once + +#include +#include "engine/Actor.h" +#include "src/textures.h" +#include "engine/CoreMath.h" +#include "port/Game.h" + +class AText : public AActor { +public: + enum TextMode : int16_t { + STATIONARY, + FOLLOW_PLAYER + }; + + enum FadeMode : int16_t { + NO_FADE, + FADE_IN, + FADE_OUT + }; + + enum DistanceProps : int16_t { + TOO_CLOSE, + ACTIVE, + TOO_FAR + }; + + DistanceProps Dist = ACTIVE; + DistanceProps PrevState = ACTIVE; + FadeMode FadeState = NO_FADE; + + struct CharacterList { + const char* Texture; + f32 column; + f32 row; + u32 width; + u32 height; + s32 mode; + Vtx vtx[4]; + }; + + std::vector TextureList; + + std::string Text; // The text to be displayed + TextMode Mode; + uint32_t PlayerIndex; + float WidthOffset = 0.0f; + float HeightOffset = 8.0f; // Place text above player + f32 ScaleX = 1.0f; + f32 LetterSpacing = 1.0f; + f32 Far = 14000.0f; + f32 Close = 350.0f; + + bool FaceCamera = true; + bool Animate = false; + bool SingleColour = true; // Only used for imGUI to show more colour options + // 1 colour for each of the 4 vtx + // This allows setting each vtx colour individually + RGBA8 TextColour[4]; + + // Constructor + AText(const SpawnParams& params); + virtual ~AText() override = default; + + /** + * This is simply a helper function to keep Spawning code clean + * Main parameters usage: + * + * PlayerIndex is only used if textMode is FOLLOW_PLAYER + * + * Other available options: + * + * TextColour - Colour of the text (1 colour for each vertex) + * ScaleX - Font scale Y + * LetterSpacing - Space between letters + * HeightOffset - Height above player when in FOLLOW_PLAYER mode + * WidthOffset - Left/right offset of the text when in FOLLOW_PLAYER mode + * Far - Skip rendering if camera is too far away + * Close - Skip rendering if too close to the camera + * Animate - Cycle colours similar to the grand prix title text + * + * Other options usage: + * + * AText* text = AText::Spawn("Hello World", FVector(0, 0, 0), etc.); + * text->Animate = true; + * text->Far = 14000.0f; + * text->ScaleX = 0.1f; // Recommend in the range of 0-2.0f. Default 1.0f + * text->LetterSpacing = 1.0f; // Default 1.0f + * + * For one single colour, set all vertices to the same colour. + * text->TextColour[0] = {255, 0, 0, 255}; // Red + * text->TextColour[1] = {0, 255, 0, 255}; // Green + * text->TextColour[2] = {0, 0, 255, 255}; // Blue + * text->TextColour[3] = {255, 255, 0, 255}; // Yellow + * + * The above will result in a gradient between red, green, blue, and yellow + * For transparency {0, 0, 0, 100} <-- alpha value of 100 will render semi-transparent black text. + * + */ + static inline AText* Spawn(std::string text, FVector pos, FVector scale, AText::TextMode textMode, int16_t playerIndex) { + SpawnParams params = { + .Name = "hm:text", + .Type = static_cast(textMode), + .Behaviour = static_cast(playerIndex), + .Skin = text, + .Location = pos, + .Scale = scale, + }; + return static_cast(gWorldInstance.AddActor(new AText(params))); + } + + // Virtual functions to be overridden by derived classes + virtual void Tick() override; + virtual void Draw(Camera* camera) override; + virtual void SetSpawnParams(SpawnParams& params) override; + virtual bool IsMod() override; + virtual void DrawEditorProperties() override; + void DrawColourEditor(bool* updated); + void FollowPlayer(); + + std::string ValidateString(const std::string_view& text); + void Refresh(); + void Print3D(char* text, s32 tracking, s32 mode); + void PrintLetter3D(MenuTexture* glyphTexture, f32 column, f32 row, s32 mode); + void SetupVtx(); + void DrawText3D(Camera* camera); // Based on func_80095BD0 + void AnimateColour(Vtx* vtx); // Animate the vtx colours + void FadeIn(Vtx* vtx); + void FadeOut(Vtx* vtx); + + inline uint8_t FloatToU8(float v) { + return (uint8_t)(v * 255.0f); + } + + Vtx myVtx[12] = { // D_02007BB8 + {{{ 0, 16, 0}, 0, { 0, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{26, 16, 0}, 0, {1600, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{26, 0, 0}, 0, {1600, 960}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 0, 0, 0}, 0, { 0, 960}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 0, 16, 0}, 0, { 0, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{16, 16, 0}, 0, {960, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{16, 0, 0}, 0, {960, 960}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 0, 0, 0}, 0, { 0, 960}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 0, 32, 0}, 0, { 0, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{30, 32, 0}, 0, {1856, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{30, 0, 0}, 0, {1856, 1984}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 0, 0, 0}, 0, { 0, 1984}, {0xff, 0xff, 0xff, 0xff}}}, + }; +}; diff --git a/src/engine/actors/Tree.cpp b/src/engine/actors/Tree.cpp index faffd07985..69c3db5b8a 100644 --- a/src/engine/actors/Tree.cpp +++ b/src/engine/actors/Tree.cpp @@ -11,6 +11,7 @@ extern "C" { ATree::ATree(Vec3f pos, Gfx* displaylist, f32 drawDistance, f32 minDrawDistance, const char* tlut = nullptr) { Name = "Tree"; + ResourceName = "mk:tree"; Pos[0] = pos[0]; Pos[1] = pos[1]; Pos[2] = pos[2]; diff --git a/src/engine/actors/WarioSign.cpp b/src/engine/actors/WarioSign.cpp index 0e83e114f4..66cffcb4b7 100644 --- a/src/engine/actors/WarioSign.cpp +++ b/src/engine/actors/WarioSign.cpp @@ -8,18 +8,40 @@ extern "C" { #include "math_util.h" #include "main.h" #include "actor_types.h" +#include "code_800029B0.h" +#include "collision.h" } -AWarioSign::AWarioSign(FVector pos) { +AWarioSign::AWarioSign(const SpawnParams& params) : AActor(params) { Type = ACTOR_WARIO_SIGN; Name = "Wario Sign"; - Pos[0] = pos.x; + ResourceName = "mk:wario_sign"; + Model = d_course_wario_stadium_dl_sign; + + Speed = params.Speed.value_or(182); + + FVector pos = params.Location.value_or(FVector(0, 0, 0)); + Pos[0] = pos.x * gCourseDirection; Pos[1] = pos.y; Pos[2] = pos.z; + + IRotator rot = params.Rotation.value_or(IRotator(0, 0, 0)); + Rot[0] = rot.pitch; + Rot[1] = rot.yaw; + Rot[2] = rot.roll; + + Scale = params.Scale.value_or(FVector(1.0f, 1.0f, 1.0f)); + + func_802AAAAC(&Unk30); + Flags = -0x8000; +} + +bool AWarioSign::IsMod() { + return true; } void AWarioSign::Tick() { - Rot[1] += 0xB6; + Rot[1] += Speed; } void AWarioSign::Draw(Camera *camera) { diff --git a/src/engine/actors/WarioSign.h b/src/engine/actors/WarioSign.h index 634b2132e6..511d2506d6 100644 --- a/src/engine/actors/WarioSign.h +++ b/src/engine/actors/WarioSign.h @@ -3,6 +3,7 @@ #include #include "engine/Actor.h" #include "CoreMath.h" +#include "engine/World.h" extern "C" { #include "common_structs.h" @@ -12,8 +13,22 @@ class AWarioSign : public AActor { public: virtual ~AWarioSign() = default; - explicit AWarioSign(FVector pos); + explicit AWarioSign(const SpawnParams& params); + // This is simply a helper function to keep Spawning code clean + static inline AWarioSign* Spawn(FVector pos, IRotator rot, FVector velocity, FVector scale) { + SpawnParams params = { + .Name = "mk:wario_sign", + .Location = pos, + .Rotation = rot, + .Scale = scale, + .Velocity = velocity, + .Speed = 182, + }; + return static_cast(gWorldInstance.AddActor(new AWarioSign(params))); + } + + virtual bool IsMod() override; virtual void Tick() override; virtual void Draw(Camera*) override; }; diff --git a/src/engine/courses/BansheeBoardwalk.cpp b/src/engine/courses/BansheeBoardwalk.cpp index 205fe24c13..b9634cd603 100644 --- a/src/engine/courses/BansheeBoardwalk.cpp +++ b/src/engine/courses/BansheeBoardwalk.cpp @@ -164,8 +164,8 @@ void BansheeBoardwalk::LoadTextures() { void BansheeBoardwalk::BeginPlay() { spawn_all_item_boxes((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_banshee_boardwalk_item_box_spawns)); - gWorldInstance.AddObject(new OCheepCheep(FVector(xOrientation * -1650.0, -200.0f, -1650.0f), - OCheepCheep::CheepType::RACE, IPathSpan(160, 170))); + OCheepCheep::Spawn(FVector(xOrientation * -1650.0, -200.0f, -1650.0f), + OCheepCheep::Behaviour::RACE, IPathSpan(160, 170)); OTrashBin::Behaviour bhv; if (gModeSelection == TIME_TRIALS) { @@ -175,27 +175,25 @@ void BansheeBoardwalk::BeginPlay() { } if (gIsMirrorMode) { - gWorldInstance.AddObject(new OTrashBin(FVector(1765.0f, 45.0f, 195.0f), IRotator(0, 180, 0), 1.0f, bhv)); + OTrashBin::Spawn(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), IRotator(0, 0, 0), 1.0f, bhv)); + OTrashBin::Spawn(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), 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))); + OBat::Spawn(FVector(0,0,0), IRotator(0, 0, 90)); + OBoos::Spawn(5, IPathSpan(180, 190), IPathSpan(200, 210), IPathSpan(280, 290)); + OBoos::Spawn(5, IPathSpan(490, 500), IPathSpan(510, 520), IPathSpan(620, 630)); } if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][110], 110, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][190], 190, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][250], 250, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][475], 475, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][610], 610, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 110, 3, 0.8333333f); + OBombKart::Spawn(0, 190, 1, 0.8333333f); + OBombKart::Spawn(0, 250, 3, 0.8333333f); + OBombKart::Spawn(0, 475, 1, 0.8333333f); + OBombKart::Spawn(0, 610, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } } diff --git a/src/engine/courses/BigDonut.cpp b/src/engine/courses/BigDonut.cpp index ed2d1c7fc8..1c8fd5b763 100644 --- a/src/engine/courses/BigDonut.cpp +++ b/src/engine/courses/BigDonut.cpp @@ -132,15 +132,13 @@ void BigDonut::BeginPlay() { spawn_all_item_boxes((ActorSpawnData*) LOAD_ASSET_RAW(d_course_big_donut_item_box_spawns)); if (gModeSelection == VERSUS) { - FVector pos = {0, 0, 0}; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][20], 20, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][40], 40, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][60], 60, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][80], 80, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][100], 100, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][120], 120, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][140], 140, 0, 1.0f)); + OBombKart::Spawn(0, 20, 0, 1.0f); + OBombKart::Spawn(0, 40, 0, 1.0f); + OBombKart::Spawn(0, 60, 0, 1.0f); + OBombKart::Spawn(0, 80, 0, 1.0f); + OBombKart::Spawn(0, 100, 0, 1.0f); + OBombKart::Spawn(0, 120, 0, 1.0); + OBombKart::Spawn(0, 140, 0, 1.0f); } } diff --git a/src/engine/courses/BlockFort.cpp b/src/engine/courses/BlockFort.cpp index ae1b9bfb93..0ec9cb5558 100644 --- a/src/engine/courses/BlockFort.cpp +++ b/src/engine/courses/BlockFort.cpp @@ -127,15 +127,13 @@ void BlockFort::BeginPlay() { spawn_all_item_boxes((ActorSpawnData*) LOAD_ASSET_RAW(d_course_block_fort_item_box_spawns)); if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][20], 20, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][40], 40, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][60], 60, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][80], 80, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][100], 100, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][120], 120, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][140], 140, 0, 1.0f)); + OBombKart::Spawn(0, 20, 0, 1.0f); + OBombKart::Spawn(0, 40, 0, 1.0f); + OBombKart::Spawn(0, 60, 0, 1.0f); + OBombKart::Spawn(0, 80, 0, 1.0f); + OBombKart::Spawn(0, 100, 0, 1.0f); + OBombKart::Spawn(0, 120, 0, 1.0f); + OBombKart::Spawn(0, 140, 0, 1.0f); } } diff --git a/src/engine/courses/BowsersCastle.cpp b/src/engine/courses/BowsersCastle.cpp index 47f26cf9f1..9258683ce7 100644 --- a/src/engine/courses/BowsersCastle.cpp +++ b/src/engine/courses/BowsersCastle.cpp @@ -4,7 +4,8 @@ #include #include "BowsersCastle.h" -#include "World.h" +#include "engine/World.h" +#include "engine/courses/Course.h" #include "engine/actors/Finishline.h" #include "engine/objects/BombKart.h" #include "engine/objects/Thwomp.h" @@ -169,54 +170,52 @@ void BowsersCastle::BeginPlay() { switch (gCCSelection) { case CC_100: case CC_EXTRA: - gWorldInstance.AddObject(new OThwomp(0x0320, 0xf92a, 0xC000, 1.0f, OThwomp::States::STATIONARY, 0)); - gWorldInstance.AddObject(new OThwomp(0x044c, 0xf92a, 0xC000, 1.0f, OThwomp::States::STATIONARY, 1)); - gWorldInstance.AddObject(new OThwomp(0x02bc, 0xf95c, 0xC000, 1.0f, OThwomp::States::MOVE_AND_ROTATE, 0)); - gWorldInstance.AddObject(new OThwomp(0x04b0, 0xf8f8, 0xC000, 1.0f, OThwomp::States::MOVE_AND_ROTATE, 1)); - gWorldInstance.AddObject(new OThwomp(0x04b0, 0xf5ba, 0xC000, 1.0f, OThwomp::States::MOVE_FAR, 0)); - gWorldInstance.AddObject(new OThwomp(0x04b0, 0xf592, 0xC000, 1.0f, OThwomp::States::MOVE_FAR, 1)); - gWorldInstance.AddObject(new OThwomp(0x091a, 0xf5bf, 0xC000, 1.0f, OThwomp::States::STATIONARY_FAST, 0)); - gWorldInstance.AddObject(new OThwomp(0x091a, 0xf597, 0xC000, 1.0f, OThwomp::States::STATIONARY_FAST, 1)); - gWorldInstance.AddObject(new OThwomp(0x0596, 0xf92f, 0xC000, 1.5f, OThwomp::States::JAILED, 0)); - gWorldInstance.AddObject(new OThwomp(0x082a, 0xf9f2, 0x4000, 1.0f, OThwomp::States::SLIDE, 0)); - gWorldInstance.AddObject(new OThwomp(0x073a, 0xf9f2, 0x4000, 1.0f, OThwomp::States::SLIDE, 1)); + OThwomp::Spawn(0x0320, 0xf92a, 0xC000, 1.0f, OThwomp::States::STATIONARY, 0); + OThwomp::Spawn(0x044c, 0xf92a, 0xC000, 1.0f, OThwomp::States::STATIONARY, 1); + OThwomp::Spawn(0x02bc, 0xf95c, 0xC000, 1.0f, OThwomp::States::MOVE_AND_ROTATE, 0); + OThwomp::Spawn(0x04b0, 0xf8f8, 0xC000, 1.0f, OThwomp::States::MOVE_AND_ROTATE, 1); + OThwomp::Spawn(0x04b0, 0xf5ba, 0xC000, 1.0f, OThwomp::States::MOVE_FAR, 0); + OThwomp::Spawn(0x04b0, 0xf592, 0xC000, 1.0f, OThwomp::States::MOVE_FAR, 1); + OThwomp::Spawn(0x091a, 0xf5bf, 0xC000, 1.0f, OThwomp::States::STATIONARY_FAST, 0); + OThwomp::Spawn(0x091a, 0xf597, 0xC000, 1.0f, OThwomp::States::STATIONARY_FAST, 1); + OThwomp::Spawn(0x0596, 0xf92f, 0xC000, 1.5f, OThwomp::States::JAILED, 0); + OThwomp::Spawn(0x082a, 0xf9f2, 0x4000, 1.0f, OThwomp::States::SLIDE, 0); + OThwomp::Spawn(0x073a, 0xf9f2, 0x4000, 1.0f, OThwomp::States::SLIDE, 1); break; case CC_50: - gWorldInstance.AddObject(new OThwomp(0x3B6, 0xF92A, 0xC000, 1.0f, OThwomp::States::STATIONARY, 0)); - gWorldInstance.AddObject(new OThwomp(0x0352, 0xf95c, 0xC000, 1.0f, OThwomp::States::MOVE_AND_ROTATE, 0)); - gWorldInstance.AddObject(new OThwomp(0x04b0, 0xf5ba, 0xC000, 1.0f, OThwomp::States::MOVE_FAR, 0)); - gWorldInstance.AddObject(new OThwomp(0x04b0, 0xf592, 0xC000, 1.0f, OThwomp::States::MOVE_FAR, 1)); - gWorldInstance.AddObject(new OThwomp(0x091a, 0xf5b0, 0xC000, 1.0f, OThwomp::States::STATIONARY_FAST, 0)); - gWorldInstance.AddObject(new OThwomp(0x0596, 0xf92f, 0xC000, 1.5f, OThwomp::States::JAILED, 0)); - gWorldInstance.AddObject(new OThwomp(0x082a, 0xf9f2, 0x4000, 1.0f, OThwomp::States::SLIDE , 0)); - gWorldInstance.AddObject(new OThwomp(0x073a, 0xf9f2, 0x4000, 1.0f, OThwomp::States::SLIDE, 1)); + OThwomp::Spawn(0x3B6, 0xF92A, 0xC000, 1.0f, OThwomp::States::STATIONARY, 0); + OThwomp::Spawn(0x0352, 0xf95c, 0xC000, 1.0f, OThwomp::States::MOVE_AND_ROTATE, 0); + OThwomp::Spawn(0x04b0, 0xf5ba, 0xC000, 1.0f, OThwomp::States::MOVE_FAR, 0); + OThwomp::Spawn(0x04b0, 0xf592, 0xC000, 1.0f, OThwomp::States::MOVE_FAR, 1); + OThwomp::Spawn(0x091a, 0xf5b0, 0xC000, 1.0f, OThwomp::States::STATIONARY_FAST, 0); + OThwomp::Spawn(0x0596, 0xf92f, 0xC000, 1.5f, OThwomp::States::JAILED, 0); + OThwomp::Spawn(0x082a, 0xf9f2, 0x4000, 1.0f, OThwomp::States::SLIDE , 0); + OThwomp::Spawn(0x073a, 0xf9f2, 0x4000, 1.0f, OThwomp::States::SLIDE, 1); break; case CC_150: - gWorldInstance.AddObject(new OThwomp(0x0320, 0xf92a, 0xC000, 1.0f, OThwomp::States::STATIONARY, 0)); - gWorldInstance.AddObject(new OThwomp(0x044c, 0xf92a, 0xC000, 1.0f, OThwomp::States::STATIONARY, 1)); - gWorldInstance.AddObject(new OThwomp(0x02bc, 0xf95c, 0xC000, 1.0f, OThwomp::States::MOVE_AND_ROTATE, 0)); - gWorldInstance.AddObject(new OThwomp(0x04b0, 0xf8f8, 0xC000, 1.0f, OThwomp::States::MOVE_AND_ROTATE, 1)); - gWorldInstance.AddObject(new OThwomp(0x04b0, 0xf5ba, 0xC000, 1.0f, OThwomp::States::MOVE_FAR, 0)); - gWorldInstance.AddObject(new OThwomp(0x04b0, 0xf592, 0xC000, 1.0f, OThwomp::States::MOVE_FAR, 1)); - gWorldInstance.AddObject(new OThwomp(0x091a, 0xf5c9, 0xC000, 1.0f, OThwomp::States::STATIONARY_FAST, 0)); - gWorldInstance.AddObject(new OThwomp(0x091a, 0xf5ab, 0xC000, 1.0f, OThwomp::States::STATIONARY_FAST, 1)); - gWorldInstance.AddObject(new OThwomp(0x091a, 0xf58d, 0xC000, 1.0f, OThwomp::States::STATIONARY_FAST, 2)); - gWorldInstance.AddObject(new OThwomp(0x0596, 0xf92f, 0xC000, 1.5f, OThwomp::States::JAILED, 0)); - gWorldInstance.AddObject(new OThwomp(0x082a, 0xf9f2, 0x4000, 1.0f, OThwomp::States::SLIDE, 0)); - gWorldInstance.AddObject(new OThwomp(0x073a, 0xf9f2, 0x4000, 1.0f, OThwomp::States::SLIDE, 1)); + OThwomp::Spawn(0x0320, 0xf92a, 0xC000, 1.0f, OThwomp::States::STATIONARY, 0); + OThwomp::Spawn(0x044c, 0xf92a, 0xC000, 1.0f, OThwomp::States::STATIONARY, 1); + OThwomp::Spawn(0x02bc, 0xf95c, 0xC000, 1.0f, OThwomp::States::MOVE_AND_ROTATE, 0); + OThwomp::Spawn(0x04b0, 0xf8f8, 0xC000, 1.0f, OThwomp::States::MOVE_AND_ROTATE, 1); + OThwomp::Spawn(0x04b0, 0xf5ba, 0xC000, 1.0f, OThwomp::States::MOVE_FAR, 0); + OThwomp::Spawn(0x04b0, 0xf592, 0xC000, 1.0f, OThwomp::States::MOVE_FAR, 1); + OThwomp::Spawn(0x091a, 0xf5c9, 0xC000, 1.0f, OThwomp::States::STATIONARY_FAST, 0); + OThwomp::Spawn(0x091a, 0xf5ab, 0xC000, 1.0f, OThwomp::States::STATIONARY_FAST, 1); + OThwomp::Spawn(0x091a, 0xf58d, 0xC000, 1.0f, OThwomp::States::STATIONARY_FAST, 2); + OThwomp::Spawn(0x0596, 0xf92f, 0xC000, 1.5f, OThwomp::States::JAILED, 0); + OThwomp::Spawn(0x082a, 0xf9f2, 0x4000, 1.0f, OThwomp::States::SLIDE, 0); + OThwomp::Spawn(0x073a, 0xf9f2, 0x4000, 1.0f, OThwomp::States::SLIDE, 1); break; } if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][50], 50, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][150], 150, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][200], 200, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][260], 260, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][435], 435, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 50, 3, 0.8333333f); + OBombKart::Spawn(0, 150, 1, 0.8333333f); + OBombKart::Spawn(0, 200, 3, 0.8333333f); + OBombKart::Spawn(0, 260, 1, 0.8333333f); + OBombKart::Spawn(0, 435, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } } diff --git a/src/engine/courses/ChocoMountain.cpp b/src/engine/courses/ChocoMountain.cpp index 6152d99110..8fa1c1d192 100644 --- a/src/engine/courses/ChocoMountain.cpp +++ b/src/engine/courses/ChocoMountain.cpp @@ -8,6 +8,7 @@ #include "engine/objects/BombKart.h" #include "choco_mountain_data.h" #include "engine/actors/Finishline.h" +#include "engine/actors/FallingRock.h" extern "C" { #include "main.h" @@ -177,18 +178,18 @@ void ChocoMountain::LoadTextures() { void ChocoMountain::BeginPlay() { spawn_all_item_boxes((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_choco_mountain_item_box_spawns)); - spawn_falling_rocks((struct ActorSpawnData*)LOAD_ASSET_RAW((const char*)d_course_choco_mountain_falling_rock_spawns)); + AFallingRock::Spawn(FVector(2019, 156, 164), 60); + AFallingRock::Spawn(FVector(2018, 155, 379), 120); + AFallingRock::Spawn(FVector(1996, 146, 505), 180); if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][140], 140, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][165], 165, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][330], 330, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][550], 550, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][595], 595, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 140, 3, 0.8333333f); + OBombKart::Spawn(0, 165, 1, 0.8333333f); + OBombKart::Spawn(0, 330, 3, 0.8333333f); + OBombKart::Spawn(0, 550, 1, 0.8333333f); + OBombKart::Spawn(0, 595, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } } diff --git a/src/engine/courses/Course.cpp b/src/engine/courses/Course.cpp index aa87ac9b3e..3ad991f0a6 100644 --- a/src/engine/courses/Course.cpp +++ b/src/engine/courses/Course.cpp @@ -6,6 +6,8 @@ #include "port/Game.h" #include "port/resource/type/TrackPathPointData.h" #include "port/resource/type/TrackSections.h" +#include "engine/editor/SceneManager.h" +#include "Registry.h" extern "C" { #include "main.h" @@ -24,6 +26,7 @@ extern "C" { #include "collision.h" #include "actors.h" #include "math_util.h" +#include "code_80005FD0.h" extern StaffGhost* d_mario_raceway_staff_ghost; } @@ -99,6 +102,7 @@ Course::Course() { Props.PathTable2[1] = NULL; Props.PathTable2[2] = NULL; Props.PathTable2[3] = NULL; + Props.PathTable2[4] = NULL; Props.Clouds = NULL; Props.CloudList = NULL; @@ -115,6 +119,7 @@ void Course::Load(Vtx* vtx, Gfx* gfx) { void Course::LoadO2R(std::string trackPath) { if (!trackPath.empty()) { + SceneFilePtr = (trackPath + "/scene.json"); TrackSectionsPtr = (trackPath + "/data_track_sections"); std::string path_file = (trackPath + "/data_paths").c_str(); @@ -125,22 +130,19 @@ void Course::LoadO2R(std::string trackPath) { auto& paths = res->PathList; size_t i = 0; + u16* ptr = &Props.PathSizes.unk0; for (auto& path : paths) { - if (i == 0) { - Props.PathSizes.unk0 = path.size(); - Props.PathTable[0] = (TrackPathPoint*) path.data(); - Props.PathTable[1] = NULL; - Props.PathTable[2] = NULL; - Props.PathTable[3] = NULL; - Props.PathTable2[0] = (TrackPathPoint*) path.data(); - Props.PathTable2[1] = NULL; - Props.PathTable2[2] = NULL; - Props.PathTable2[3] = NULL; + if (i >= ARRAY_COUNT(Props.PathTable2)) { + printf("[Course.cpp] The game can only import 5 paths. Found more than 5. Skipping the rest\n"); + break; // Only 5 paths allowed. 4 track, 1 vehicle } + ptr[i] = path.size(); + Props.PathTable2[i] = (TrackPathPoint*) path.data(); i += 1; } } + gVehiclePathSize = Props.PathSizes.unk0; // This is likely incorrect. } else { printf("Course.cpp: LoadO2R: trackPath str is empty\n"); @@ -149,6 +151,10 @@ void Course::LoadO2R(std::string trackPath) { // Load stock and o2r tracks void Course::Load() { + // Re-load scenefile in-case changes were made in the editor + if (!SceneFilePtr.empty()) { + Editor::LoadLevel(this, SceneFilePtr); + } // Load from O2R if (!TrackSectionsPtr.empty()) { @@ -219,6 +225,7 @@ void Course::Load() { // C++ version of parse_course_displaylists() void Course::ParseCourseSections(TrackSectionsO2R* sections, size_t size) { + printf("\n[Track] Generating Collision Meshes...\n"); for (size_t i = 0; i < (size / sizeof(TrackSectionsO2R)); i++) { if (sections[i].flags & 0x8000) { D_8015F59C = 1; // single-sided wall @@ -235,10 +242,11 @@ void Course::ParseCourseSections(TrackSectionsO2R* sections, size_t size) { } else { D_8015F5A4 = 0; } - printf("LOADING DL %s\n", sections[i].addr.c_str()); + printf(" %s\n", sections[i].addr.c_str()); generate_collision_mesh((Gfx*) LOAD_ASSET_RAW(sections[i].addr.c_str()), sections[i].surfaceType, sections[i].sectionId); } + printf("[Track] Collision Mesh Generation Complete!\n\n"); } void Course::TestPath() { @@ -251,9 +259,9 @@ void Course::TestPath() { Vec3f vel = { 0, 0, 0 }; for (size_t i = 0; i < gPathCountByPathIndex[0]; i++) { - x = gTrackPaths[0][i].posX; - y = gTrackPaths[0][i].posY; - z = gTrackPaths[0][i].posZ; + x = gTrackPaths[0][i].x; + y = gTrackPaths[0][i].y; + z = gTrackPaths[0][i].z; if (((x & 0xFFFF) == 0x8000) && ((y & 0xFFFF) == 0x8000) && ((z & 0xFFFF) == 0x8000)) { break; @@ -289,7 +297,21 @@ void Course::LoadTextures() { } void Course::BeginPlay() { + printf("[Track] BeginPlay\n"); TestPath(); + this->SpawnActors(); +} + +// Spawns actors from SpawnParams set by the scene file in SceneManager.cpp +void Course::SpawnActors() { + for (const auto& actor : SpawnList) { + auto it = gActorRegistry.find(actor.Name); + if (it != gActorRegistry.end() && it->second.spawnFunc) { + it->second.spawnFunc(actor); + } else { + printf("Actor not found %s\n", actor.Name.c_str()); + } + } } void Course::InitClouds() { diff --git a/src/engine/courses/Course.h b/src/engine/courses/Course.h index 2370d916cf..80e61fdbdc 100644 --- a/src/engine/courses/Course.h +++ b/src/engine/courses/Course.h @@ -1,10 +1,13 @@ #ifndef ENGINE_COURSE_H #define ENGINE_COURSE_H -#include +#include #include "CoreMath.h" #ifdef __cplusplus +#include "engine/SpawnParams.h" +#include +#include #include "engine/objects/Lakitu.h" #include "port/resource/type/TrackSections.h" extern "C" { @@ -81,8 +84,8 @@ typedef struct Properties { Vec4f NormalTargetSpeed; Vec4f D_0D0096B8; Vec4f OffTrackTargetSpeed; - TrackPathPoint* PathTable[4]; - TrackPathPoint* PathTable2[4]; + TrackPathPoint* PathTable[4]; // Only used for podium ceremony + TrackPathPoint* PathTable2[5]; // The fifth entry is for vehicles uint8_t* CloudTexture; CloudData *Clouds; CloudData *CloudList; @@ -286,6 +289,7 @@ typedef struct Properties { #ifdef __cplusplus + class World; // <-- Forward declare class Course { @@ -304,8 +308,14 @@ class Course { const course_texture* textures = nullptr; bool bSpawnFinishline = true; std::optional FinishlineSpawnPoint; + + // O2R Loading + std::shared_ptr RootArchive; + std::string SceneFilePtr; std::string TrackSectionsPtr; + bool bIsMod = false; + std::vector SpawnList; virtual ~Course() = default; @@ -322,6 +332,7 @@ class Course { * Actor spawning should go here. */ virtual void BeginPlay(); + void SpawnActors(); virtual void TestPath(); virtual void InitClouds(); virtual void UpdateClouds(s32, Camera*); diff --git a/src/engine/courses/DKJungle.cpp b/src/engine/courses/DKJungle.cpp index 7be091ad37..261f4b672e 100644 --- a/src/engine/courses/DKJungle.cpp +++ b/src/engine/courses/DKJungle.cpp @@ -205,18 +205,16 @@ void DKJungle::BeginPlay() { // The original game only ran vehicle logic every second frame. // Thus the speed gets divided by two to set speed to match properly - gWorldInstance.AddActor(new ABoat((0.6666666f)/4, 0)); + ABoat::Spawn((0.6666666f)/4, 0, 0, ABoat::SpawnMode::POINT); if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][50], 50, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][100], 100, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][150], 150, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][190], 190, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][250], 250, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 50, 3, 0.8333333f); + OBombKart::Spawn(0, 100, 1, 0.8333333f); + OBombKart::Spawn(0, 150, 3, 0.8333333f); + OBombKart::Spawn(0, 190, 1, 0.8333333f); + OBombKart::Spawn(0, 250, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } } } diff --git a/src/engine/courses/DoubleDeck.cpp b/src/engine/courses/DoubleDeck.cpp index 5331d4bc67..e92bc22d8d 100644 --- a/src/engine/courses/DoubleDeck.cpp +++ b/src/engine/courses/DoubleDeck.cpp @@ -129,15 +129,13 @@ void DoubleDeck::BeginPlay() { spawn_all_item_boxes((ActorSpawnData*)LOAD_ASSET_RAW(d_course_double_deck_item_box_spawns)); if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][20], 20, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][40], 40, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][60], 60, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][80], 80, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][100], 100, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][120], 120, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][140], 140, 0, 1.0f)); + OBombKart::Spawn(0, 20, 0, 1.0f); + OBombKart::Spawn(0, 40, 0, 1.0f); + OBombKart::Spawn(0, 60, 0, 1.0f); + OBombKart::Spawn(0, 80, 0, 1.0f); + OBombKart::Spawn(0, 100, 0, 1.0f); + OBombKart::Spawn(0, 120, 0, 1.0f); + OBombKart::Spawn(0, 140, 0, 1.0f); } } diff --git a/src/engine/courses/FrappeSnowland.cpp b/src/engine/courses/FrappeSnowland.cpp index c4185b165f..1be4bf04a5 100644 --- a/src/engine/courses/FrappeSnowland.cpp +++ b/src/engine/courses/FrappeSnowland.cpp @@ -145,37 +145,35 @@ void FrappeSnowland::BeginPlay() { spawn_all_item_boxes((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_frappe_snowland_item_box_spawns)); if (gGamestate != CREDITS_SEQUENCE) { - gWorldInstance.AddObject(new OSnowman(FVector(697, 0, -1684))); - gWorldInstance.AddObject(new OSnowman(FVector(82, 0, -2245))); - gWorldInstance.AddObject(new OSnowman(FVector(27, 5, -2067))); - gWorldInstance.AddObject(new OSnowman(FVector(-656, 0, -1735))); - gWorldInstance.AddObject(new OSnowman(FVector(-1497, 0, -83))); - gWorldInstance.AddObject(new OSnowman(FVector(-1643, 0, -25))); - gWorldInstance.AddObject(new OSnowman(FVector(-1547, 0, -20))); - gWorldInstance.AddObject(new OSnowman(FVector(-1445, 0, -10))); - gWorldInstance.AddObject(new OSnowman(FVector(-1502, 0, 61))); - gWorldInstance.AddObject(new OSnowman(FVector(-1429, 0, 79))); - gWorldInstance.AddObject(new OSnowman(FVector(-1586, 0, 71))); - gWorldInstance.AddObject(new OSnowman(FVector(-1471, 0, 157))); - gWorldInstance.AddObject(new OSnowman(FVector(-1539, 0, 175))); - gWorldInstance.AddObject(new OSnowman(FVector(-1484, 0, 303))); - gWorldInstance.AddObject(new OSnowman(FVector(-1442, 0, 358))); - gWorldInstance.AddObject(new OSnowman(FVector(-1510, 0, 426))); - gWorldInstance.AddObject(new OSnowman(FVector(-665, 0, 830))); - gWorldInstance.AddObject(new OSnowman(FVector(-701, 3, 853))); - gWorldInstance.AddObject(new OSnowman(FVector(-602, 0, 929))); + OSnowman::Spawn(FVector(697, 0, -1684)); + OSnowman::Spawn(FVector(82, 0, -2245)); + OSnowman::Spawn(FVector(27, 5, -2067)); + OSnowman::Spawn(FVector(-656, 0, -1735)); + OSnowman::Spawn(FVector(-1497, 0, -83)); + OSnowman::Spawn(FVector(-1643, 0, -25)); + OSnowman::Spawn(FVector(-1547, 0, -20)); + OSnowman::Spawn(FVector(-1445, 0, -10)); + OSnowman::Spawn(FVector(-1502, 0, 61)); + OSnowman::Spawn(FVector(-1429, 0, 79)); + OSnowman::Spawn(FVector(-1586, 0, 71)); + OSnowman::Spawn(FVector(-1471, 0, 157)); + OSnowman::Spawn(FVector(-1539, 0, 175)); + OSnowman::Spawn(FVector(-1484, 0, 303)); + OSnowman::Spawn(FVector(-1442, 0, 358)); + OSnowman::Spawn(FVector(-1510, 0, 426)); + OSnowman::Spawn(FVector(-665, 0, 830)); + OSnowman::Spawn(FVector(-701, 3, 853)); + OSnowman::Spawn(FVector(-602, 0, 929)); } if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][50], 50, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][100], 100, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][150], 150, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][290], 290, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][350], 350, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 50, 3, 0.8333333f); + OBombKart::Spawn(0, 100, 1, 0.8333333f); + OBombKart::Spawn(0, 150, 3, 0.8333333f); + OBombKart::Spawn(0, 290, 1, 0.8333333f); + OBombKart::Spawn(0, 350, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } } diff --git a/src/engine/courses/Harbour.cpp b/src/engine/courses/Harbour.cpp index d1f455b936..db81d065b7 100644 --- a/src/engine/courses/Harbour.cpp +++ b/src/engine/courses/Harbour.cpp @@ -620,75 +620,8 @@ void Harbour::Load() { void Harbour::LoadTextures() { dma_textures(gTextureTrees1, 0x0000035BU, 0x00000800U); // 0x03009000 - D_802BA058 = dma_textures(gTexturePiranhaPlant1, 0x000003E8U, 0x00000800U); // 0x03009800 - dma_textures(gTexturePiranhaPlant2, 0x000003E8U, 0x00000800U); // 0x0300A000 - dma_textures(gTexturePiranhaPlant3, 0x000003E8U, 0x00000800U); // 0x0300A800 - dma_textures(gTexturePiranhaPlant4, 0x000003E8U, 0x00000800U); // 0x0300B000 - dma_textures(gTexturePiranhaPlant5, 0x000003E8U, 0x00000800U); // 0x0300B800 - dma_textures(gTexturePiranhaPlant6, 0x000003E8U, 0x00000800U); // 0x0300C000 - dma_textures(gTexturePiranhaPlant7, 0x000003E8U, 0x00000800U); // 0x0300C800 - dma_textures(gTexturePiranhaPlant8, 0x000003E8U, 0x00000800U); // 0x0300D000 - dma_textures(gTexturePiranhaPlant9, 0x000003E8U, 0x00000800U); // 0x0300D800 } -Path2D harbour_path2D[] = { - { 0, 0}, - { 0, -100}, - { 0, -200}, - { 0, -300}, - { 0, -400}, - { 0, -500}, - { 0, -600}, - { 0, -700}, - { 0, -800}, - { 0, -900}, - { 0, -1000}, - { 0, -1096}, // Main point 1 - { 100, -1090}, - { 200, -1085}, - { 300, -1080}, - { 400, -1075}, - { 500, -1072}, // Curve begins to smooth here - { 600, -1068}, - { 700, -1065}, - { 800, -1063}, - { 900, -1061}, - { 984, -1060}, // Main point 2 - { 990, -900}, - { 995, -800}, - { 997, -700}, - { 998, -600}, - { 999, -500}, - { 999, -400}, - { 999, -300}, - { 999, -200}, - { 999, -100}, - { 999, 0}, - { 999, 100}, - { 999, 200}, - { 999, 300}, - { 999, 400}, - { 999, 500}, - { 999, 600}, - { 999, 700}, - { 999, 800}, - { 999, 900}, - { 999, 940}, // Main point 3 - { 900, 945}, - { 800, 945}, - { 700, 947}, - { 600, 948}, - { 500, 949}, - { 400, 949}, - { 300, 949}, - { 200, 950}, - { 100, 950}, - { 0, 950}, // Main point 4 - - // End of path - { -32768, -32768 } // Terminator -}; - void Harbour::BeginPlay() { struct ActorSpawnData itemboxes[] = { { 200, 1500, 200 , 0}, @@ -742,22 +675,8 @@ void Harbour::BeginPlay() { // gWorldInstance.AddObject(new OBoos(10, IPathSpan(0, 5), IPathSpan(18, 23), IPathSpan(25, 50))); - // gVehicle2DPathPoint = harbour_path2D; - //gVehicle2DPathLength = 53; - // D_80162EB0 = spawn_actor_on_surface(harbour_path2D[0].x, 2000.0f, harbour_path2D[0].z); - - - // DEBUG ONLY TO VISUALIZE PATH - // for (size_t i = 0; i < ARRAY_COUNT(harbour_path); i++) { - // if (i % 10 == 1) { - // f32 height = spawn_actor_on_surface(harbour_path[i].posX, 2000.0f, harbour_path[i].posZ); - // Vec3f itemPos = {harbour_path[i].posX, height, harbour_path[i].posZ}; - // add_actor_to_empty_slot(itemPos, rot, vel, ACTOR_ITEM_BOX); - // } - // } //gWorldInstance.AddActor(new AShip(FVector(-1694, -111, 1451), AShip::Skin::GHOSTSHIP)); //gWorldInstance.AddActor(new AShip(FVector(2811, -83, 966), AShip::Skin::SHIP2)); - //gWorldInstance.AddObject(new OGrandPrixBalloons(FVector(16, -136, -34))); } void Harbour::WhatDoesThisDo(Player* player, int8_t playerId) { diff --git a/src/engine/courses/KalimariDesert.cpp b/src/engine/courses/KalimariDesert.cpp index 3ecb3f03d9..4886174f1d 100644 --- a/src/engine/courses/KalimariDesert.cpp +++ b/src/engine/courses/KalimariDesert.cpp @@ -4,7 +4,7 @@ #include #include "KalimariDesert.h" -#include "World.h" +#include "engine/World.h" #include "engine/actors/Finishline.h" #include "engine/objects/BombKart.h" #include "kalimari_desert_data.h" @@ -198,7 +198,9 @@ void KalimariDesert::BeginPlay() { // Spawn two trains for (size_t i = 0; i < _numTrains; ++i) { - uint32_t waypoint = CalculateWaypointDistribution(i, _numTrains, gVehicle2DPathLength, centerWaypoint); + // outputs 160 for train 1 and 392 for train 2. + // If using more trains, it wraps the value to always output a valid waypoint. + uint32_t waypoint = CalculateWaypointDistribution(i, _numTrains, gVehiclePathSize, centerWaypoint); if (CVarGetInteger("gMultiplayerNoFeatureCuts", 0) == false) { // Multiplayer modes have no tender and no carriages @@ -214,19 +216,17 @@ void KalimariDesert::BeginPlay() { } } - gWorldInstance.AddActor(new ATrain(_tender, _numCarriages, 2.5f, waypoint)); + ATrain::Spawn(_tender, _numCarriages, 2.5f, 0, waypoint, ATrain::SpawnMode::POINT); } if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][50], 50, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][138], 138, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][280], 280, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][404], 404, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][510], 510, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 50, 3, 0.8333333f); + OBombKart::Spawn(0, 138, 1, 0.8333333f); + OBombKart::Spawn(0, 280, 3, 0.8333333f); + OBombKart::Spawn(0, 404, 1, 0.8333333f); + OBombKart::Spawn(0, 510, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } } } diff --git a/src/engine/courses/KoopaTroopaBeach.cpp b/src/engine/courses/KoopaTroopaBeach.cpp index e80676d414..ca1c19f9e6 100644 --- a/src/engine/courses/KoopaTroopaBeach.cpp +++ b/src/engine/courses/KoopaTroopaBeach.cpp @@ -9,6 +9,7 @@ #include "engine/actors/Finishline.h" #include "engine/objects/BombKart.h" #include "engine/objects/Crab.h" +#include "engine/objects/Seagull.h" #include "assets/koopa_troopa_beach_data.h" extern "C" { @@ -164,42 +165,40 @@ void KoopaTroopaBeach::BeginPlay() { spawn_palm_trees((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_koopa_troopa_beach_tree_spawn)); if (gGamestate != CREDITS_SEQUENCE) { - gWorldInstance.AddObject(new OCrab(FVector2D(-1809, 625), FVector2D(-1666, 594))); - gWorldInstance.AddObject(new OCrab(FVector2D(-1852, 757), FVector2D(-1620, 740))); - gWorldInstance.AddObject(new OCrab(FVector2D(-1478, 1842), FVector2D(-1453, 1833))); - gWorldInstance.AddObject(new OCrab(FVector2D(-1418, 1967), FVector2D(-1455, 1962))); - gWorldInstance.AddObject(new OCrab(FVector2D(-1472, 2112), FVector2D(-1417, 2100))); - gWorldInstance.AddObject(new OCrab(FVector2D(-1389, 2152), FVector2D(-1335, 2136))); - gWorldInstance.AddObject(new OCrab(FVector2D(218, 693), FVector2D(69, 696))); - gWorldInstance.AddObject(new OCrab(FVector2D(235, 528), FVector2D(24, 501))); - gWorldInstance.AddObject(new OCrab(FVector2D(268, 406), FVector2D(101, 394))); - gWorldInstance.AddObject(new OCrab(FVector2D(223, 318), FVector2D(86, 308))); + OCrab::Spawn(FVector2D(-1809, 625), FVector2D(-1666, 594)); + OCrab::Spawn(FVector2D(-1852, 757), FVector2D(-1620, 740)); + OCrab::Spawn(FVector2D(-1478, 1842), FVector2D(-1453, 1833)); + OCrab::Spawn(FVector2D(-1418, 1967), FVector2D(-1455, 1962)); + OCrab::Spawn(FVector2D(-1472, 2112), FVector2D(-1417, 2100)); + OCrab::Spawn(FVector2D(-1389, 2152), FVector2D(-1335, 2136)); + OCrab::Spawn(FVector2D(218, 693), FVector2D(69, 696)); + OCrab::Spawn(FVector2D(235, 528), FVector2D(24, 501)); + OCrab::Spawn(FVector2D(268, 406), FVector2D(101, 394)); + OCrab::Spawn(FVector2D(223, 318), FVector2D(86, 308)); } if (gGamestate == CREDITS_SEQUENCE) { for (size_t i = 0; i < NUM_SEAGULLS; i++) { - gWorldInstance.AddObject(new OSeagull(FVector(-360.0f, 60.0f, -1300.0f))); + OSeagull::Spawn(FVector(-360.0f, 60.0f, -1300.0f)); } } else { // Normal gameplay for (size_t i = 0; i < 4; i++) { - gWorldInstance.AddObject(new OSeagull(FVector(-985.0f, 15.0f, 1200.0f))); + OSeagull::Spawn(FVector(-985.0f, 15.0f, 1200.0f)); } for (size_t i = 0; i < 6; i++) { - gWorldInstance.AddObject(new OSeagull(FVector(328.0f, 20.0f, 2541.0f))); + OSeagull::Spawn(FVector(328.0f, 20.0f, 2541.0f)); } } if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][60], 60, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][120], 120, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][200], 200, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][280], 280, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][435], 435, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 60, 1, 0.8333333f); + OBombKart::Spawn(0, 120, 1, 0.8333333f); + OBombKart::Spawn(0, 200, 3, 0.8333333f); + OBombKart::Spawn(0, 280, 1, 0.8333333f); + OBombKart::Spawn(0, 435, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } } diff --git a/src/engine/courses/LuigiRaceway.cpp b/src/engine/courses/LuigiRaceway.cpp index 39b17a81d7..cb2fd3e144 100644 --- a/src/engine/courses/LuigiRaceway.cpp +++ b/src/engine/courses/LuigiRaceway.cpp @@ -182,22 +182,20 @@ void LuigiRaceway::BeginPlay() { spawn_all_item_boxes((struct ActorSpawnData*) LOAD_ASSET_RAW(d_course_luigi_raceway_item_box_spawns)); if (gGamestate == CREDITS_SEQUENCE) { - gWorldInstance.AddObject(new OHotAirBalloon(FVector(-1250.0f, 0.0f, 1110.0f))); + OHotAirBalloon::Spawn(FVector(-1250.0f, 0.0f, 1110.0f)); } else { // Normal gameplay - gWorldInstance.AddObject(new OHotAirBalloon(FVector(-176.0, 0.0f, -2323.0f))); - gWorldInstance.AddObject(new OGrandPrixBalloons(FVector(-140, -44, -215))); + OHotAirBalloon::Spawn(FVector(-176.0, 0.0f, -2323.0f)); + OGrandPrixBalloons::Spawn(FVector(-140, -44, -215)); } if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][50], 50, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][200], 200, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][305], 305, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][440], 440, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][515], 515, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 50, 1, 0.8333333f); + OBombKart::Spawn(0, 200, 3, 0.8333333f); + OBombKart::Spawn(0, 305, 1, 0.8333333f); + OBombKart::Spawn(0, 440, 3, 0.8333333f); + OBombKart::Spawn(0, 515, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } } diff --git a/src/engine/courses/MarioRaceway.cpp b/src/engine/courses/MarioRaceway.cpp index 867c2734a0..2afba1e9f3 100644 --- a/src/engine/courses/MarioRaceway.cpp +++ b/src/engine/courses/MarioRaceway.cpp @@ -6,6 +6,7 @@ #include "MarioRaceway.h" #include "World.h" #include "engine/actors/Finishline.h" +#include "engine/actors/MarioSign.h" #include "engine/objects/Object.h" #include "engine/objects/BombKart.h" #include "engine/objects/GrandPrixBalloons.h" @@ -177,15 +178,6 @@ void MarioRaceway::Load() { void MarioRaceway::LoadTextures() { dma_textures(gTextureTrees1, 0x0000035BU, 0x00000800U); // 0x03009000 - D_802BA058 = dma_textures(gTexturePiranhaPlant1, 0x000003E8U, 0x00000800U); // 0x03009800 - dma_textures(gTexturePiranhaPlant2, 0x000003E8U, 0x00000800U); // 0x0300A000 - dma_textures(gTexturePiranhaPlant3, 0x000003E8U, 0x00000800U); // 0x0300A800 - dma_textures(gTexturePiranhaPlant4, 0x000003E8U, 0x00000800U); // 0x0300B000 - dma_textures(gTexturePiranhaPlant5, 0x000003E8U, 0x00000800U); // 0x0300B800 - dma_textures(gTexturePiranhaPlant6, 0x000003E8U, 0x00000800U); // 0x0300C000 - dma_textures(gTexturePiranhaPlant7, 0x000003E8U, 0x00000800U); // 0x0300C800 - dma_textures(gTexturePiranhaPlant8, 0x000003E8U, 0x00000800U); // 0x0300D000 - dma_textures(gTexturePiranhaPlant9, 0x000003E8U, 0x00000800U); // 0x0300D800 } void MarioRaceway::BeginPlay() { @@ -197,26 +189,22 @@ void MarioRaceway::BeginPlay() { spawn_foliage((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_mario_raceway_tree_spawns)); spawn_piranha_plants((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_mario_raceway_piranha_plant_spawns)); spawn_all_item_boxes((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_mario_raceway_item_box_spawns)); - vec3f_set(position, 150.0f, 40.0f, -1300.0f); - position[0] *= gCourseDirection; - add_actor_to_empty_slot(position, rotation, velocity, ACTOR_MARIO_SIGN); - vec3f_set(position, 2520.0f, 0.0f, 1240.0f); - position[0] *= gCourseDirection; - add_actor_to_empty_slot(position, rotation, velocity, ACTOR_MARIO_SIGN); + + AMarioSign::Spawn(FVector(150.0f, 40.0f, -1300.0f), IRotator(0, 0, 0), FVector(0, 0, 0), FVector(1.0f, 1.0f, 1.0f)); + AMarioSign::Spawn(FVector(2520.0f, 0.0f, 1240.0f), IRotator(0, 0, 0), FVector(0, 0, 0), FVector(1.0f, 1.0f, 1.0f)); if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][40], 40, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][100], 100, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][265], 265, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][285], 285, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][420], 420, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 40, 3, 0.8333333f); + OBombKart::Spawn(0, 100, 3, 0.8333333f); + OBombKart::Spawn(0, 265, 3, 0.8333333f); + OBombKart::Spawn(0, 285, 1, 0.8333333f); + OBombKart::Spawn(0, 420, 1, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } if (gGamestate != CREDITS_SEQUENCE) { - gWorldInstance.AddObject(new OGrandPrixBalloons(FVector(0, 5, -240))); + OGrandPrixBalloons::Spawn(FVector(0, 5, -240)); } } diff --git a/src/engine/courses/MooMooFarm.cpp b/src/engine/courses/MooMooFarm.cpp index f3c4dc1bc4..5166442d4e 100644 --- a/src/engine/courses/MooMooFarm.cpp +++ b/src/engine/courses/MooMooFarm.cpp @@ -323,15 +323,13 @@ void MooMooFarm::BeginPlay() { } if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][50], 50, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][140], 140, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][225], 225, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][316], 316, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][434], 434, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 50, 3, 0.8333333f); + OBombKart::Spawn(0, 140, 3, 0.8333333f); + OBombKart::Spawn(0, 225, 3, 0.8333333f); + OBombKart::Spawn(0, 316, 3, 0.8333333f); + OBombKart::Spawn(0, 434, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } } diff --git a/src/engine/courses/PodiumCeremony.cpp b/src/engine/courses/PodiumCeremony.cpp index 9e75dec237..c41d969045 100644 --- a/src/engine/courses/PodiumCeremony.cpp +++ b/src/engine/courses/PodiumCeremony.cpp @@ -138,10 +138,10 @@ PodiumCeremony::PodiumCeremony() { Props.PathTable[2] = (TrackPathPoint*)LOAD_ASSET_RAW(podium_ceremony_path_3); Props.PathTable[3] = (TrackPathPoint*)LOAD_ASSET_RAW(podium_ceremony_path_4); - Props.PathTable2[0] = NULL; - Props.PathTable2[1] = NULL; - Props.PathTable2[2] = NULL; - Props.PathTable2[3] = NULL; + Props.PathTable2[0] = (TrackPathPoint*)LOAD_ASSET_RAW(podium_ceremony_path); + Props.PathTable2[1] = (TrackPathPoint*)LOAD_ASSET_RAW(podium_ceremony_path_2); + Props.PathTable2[2] = (TrackPathPoint*)LOAD_ASSET_RAW(podium_ceremony_path_3); + Props.PathTable2[3] = (TrackPathPoint*)LOAD_ASSET_RAW(podium_ceremony_path_4); Props.CloudTexture = (u8*) gTextureExhaust4; Props.Clouds = NULL; // no clouds @@ -175,9 +175,9 @@ void PodiumCeremony::BeginPlay() { spawn_all_item_boxes((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_royal_raceway_item_box_spawns)); spawn_piranha_plants((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_royal_raceway_piranha_plant_spawn)); - gWorldInstance.AddObject(new OCheepCheep(FVector((f32)-3202, (f32)19, (f32)-478), OCheepCheep::CheepType::PODIUM_CEREMONY, IPathSpan(0, 0))); - gWorldInstance.AddObject(new OPodium(FVector((f32)-3202, (f32)19, (f32)-478))); - + OCheepCheep::Spawn(FVector((f32)-3202, (f32)19, (f32)-478), OCheepCheep::Behaviour::PODIUM_CEREMONY, IPathSpan(0, 0)); + OPodium::Spawn(FVector((f32)-3202, (f32)19, (f32)-478)); + FVector pos = {0, 90.0f, 0}; OTrophy::TrophyType type = OTrophy::TrophyType::BRONZE; @@ -197,19 +197,18 @@ void PodiumCeremony::BeginPlay() { break; } - OTrophy* trophy = reinterpret_cast(gWorldInstance.AddObject(new OTrophy(pos, type, OTrophy::Behaviour::PODIUM_CEREMONY))); + OTrophy::Spawn(pos, type, OTrophy::Behaviour::PODIUM_CEREMONY); - FVector kart = { 0, 0, 0 }; - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[3][3], 3, OBombKart::States::PODIUM_CEREMONY, 1.25f)); - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[3][40], 40, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[3][60], 60, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[3][80], 80, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[3][100], 100, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[3][120], 120, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[3][140], 140, 0, 1.0f)); + OBombKart::Spawn(3, 3, OBombKart::States::PODIUM_CEREMONY, 1.25f); + OBombKart::Spawn(3, 40, 0, 1.0f); + OBombKart::Spawn(3, 60, 0, 1.0f); + OBombKart::Spawn(3, 80, 0, 1.0f); + OBombKart::Spawn(3, 100, 0, 1.0f); + OBombKart::Spawn(3, 120, 0, 1.0f); + OBombKart::Spawn(3, 140, 0, 1.0f); if (gGamestate != CREDITS_SEQUENCE) { - gWorldInstance.AddObject(new OGrandPrixBalloons(FVector(-64, 5, -330))); + OGrandPrixBalloons::Spawn(FVector(-64, 5, -330)); } } diff --git a/src/engine/courses/RainbowRoad.cpp b/src/engine/courses/RainbowRoad.cpp index e5ec039bdd..f2f99b488a 100644 --- a/src/engine/courses/RainbowRoad.cpp +++ b/src/engine/courses/RainbowRoad.cpp @@ -156,15 +156,13 @@ void RainbowRoad::BeginPlay() { } if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][50], 50, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][100], 100, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][150], 150, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][200], 200, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][250], 250, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 50, 3, 0.8333333f); + OBombKart::Spawn(0, 100, 1, 0.8333333f); + OBombKart::Spawn(0, 150, 3, 0.8333333f); + OBombKart::Spawn(0, 200, 1, 0.8333333f); + OBombKart::Spawn(0, 250, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } } diff --git a/src/engine/courses/RoyalRaceway.cpp b/src/engine/courses/RoyalRaceway.cpp index 1473cbf4f5..1fc6a10b90 100644 --- a/src/engine/courses/RoyalRaceway.cpp +++ b/src/engine/courses/RoyalRaceway.cpp @@ -173,15 +173,6 @@ void RoyalRaceway::Load() { void RoyalRaceway::LoadTextures() { dma_textures(gTextureTrees3, 0x000003E8U, 0x00000800U); // 0x03009000 dma_textures(gTextureTrees7, 0x000003E8U, 0x00000800U); // 0x03009800 - D_802BA058 = dma_textures(gTexturePiranhaPlant1, 0x000003E8U, 0x00000800U); // 0x0300A000 - dma_textures(gTexturePiranhaPlant2, 0x000003E8U, 0x00000800U); // 0x0300A800 - dma_textures(gTexturePiranhaPlant3, 0x000003E8U, 0x00000800U); // 0x0300B000 - dma_textures(gTexturePiranhaPlant4, 0x000003E8U, 0x00000800U); // 0x0300B800 - dma_textures(gTexturePiranhaPlant5, 0x000003E8U, 0x00000800U); // 0x0300C000 - dma_textures(gTexturePiranhaPlant6, 0x000003E8U, 0x00000800U); // 0x0300C800 - dma_textures(gTexturePiranhaPlant7, 0x000003E8U, 0x00000800U); // 0x0300D000 - dma_textures(gTexturePiranhaPlant8, 0x000003E8U, 0x00000800U); // 0x0300D800 - dma_textures(gTexturePiranhaPlant9, 0x000003E8U, 0x00000800U); // 0x0300E000 } void RoyalRaceway::BeginPlay() { @@ -190,18 +181,16 @@ void RoyalRaceway::BeginPlay() { spawn_piranha_plants((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_royal_raceway_piranha_plant_spawn)); if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][50], 50, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][100], 100, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][296], 296, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][400], 400, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][746], 746, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 50, 3, 0.8333333f); + OBombKart::Spawn(0, 100, 3, 0.8333333f); + OBombKart::Spawn(0, 296, 3, 0.8333333f); + OBombKart::Spawn(0, 400, 1, 0.8333333f); + OBombKart::Spawn(0, 746, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } if (gGamestate != CREDITS_SEQUENCE) { - gWorldInstance.AddObject(new OGrandPrixBalloons(FVector(-64, 5, -330))); + OGrandPrixBalloons::Spawn(FVector(-64, 5, -330)); } } diff --git a/src/engine/courses/SherbetLand.cpp b/src/engine/courses/SherbetLand.cpp index adcec51865..4d72e8095c 100644 --- a/src/engine/courses/SherbetLand.cpp +++ b/src/engine/courses/SherbetLand.cpp @@ -152,74 +152,44 @@ f32 SherbetLand::GetWaterLevel(FVector pos, Collision* collision) { void SherbetLand::BeginPlay() { spawn_all_item_boxes((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_sherbet_land_item_box_spawns)); - // Multiplayer does not spawn the big penguin + // Multiplayer does not spawn the big penguin... It does now! // if (gPlayerCountSelection1 == 1) { - FVector pos = {-383.0f, 2.0f, -690.0f}; - gWorldInstance.AddObject(new OPenguin(pos, 0, OPenguin::PenguinType::EMPEROR, OPenguin::Behaviour::STRUT)); + OPenguin::Spawn(FVector(-383.0f, 2.0f, -690.0f), 0, 0, 0.0f, OPenguin::PenguinType::EMPEROR, OPenguin::Behaviour::STRUT); // } - FVector pos2 = { -2960.0f, -80.0f, 1521.0f }; - auto penguin = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos2, 0x150, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE))); - auto penguin2 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos2, 0x150, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE))); - penguin->Diameter = penguin2->Diameter = 100.0f; + OPenguin::Spawn(FVector(-2960.0f, -80.0f, 1521.0f), 0x150, 0, 100.0f, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE); + OPenguin::Spawn(FVector(-2960.0f, -80.0f, 1521.0f), 0x150, 0, 100.0f, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE); - FVector pos3 = { -2490.0f, -80.0f, 1612.0f }; - auto penguin3 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos3, 0x100, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE))); - auto penguin4 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos3, 0x100, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE))); - penguin3->Diameter = penguin4->Diameter = 80.0f; + OPenguin::Spawn(FVector(-2490.0f, -80.0f, 1612.0f), 0x100, 0, 80.0f, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE); + OPenguin::Spawn(FVector(-2490.0f, -80.0f, 1612.0f), 0x100, 0, 80.0f, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE); - FVector pos4 = { -2098.0f, -80.0f, 1624.0f }; - auto penguin5 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos4, 0xFF00, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE))); - auto penguin6 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos4, 0xFF00, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE))); - penguin5->Diameter = penguin6->Diameter = 80.0f; - - - FVector pos5 = { -2080.0f, -80.0f, 1171.0f }; - auto penguin7 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos5, 0x150, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE))); - auto penguin8 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos5, 0x150, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE))); - penguin7->Diameter = penguin8->Diameter = 80.0f; + OPenguin::Spawn(FVector(-2098.0f, -80.0f, 1624.0f), 0xFF00, 0, 80.0f, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE); + OPenguin::Spawn(FVector(-2098.0f, -80.0f, 1624.0f), 0xFF00, 0, 80.0f, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE); + OPenguin::Spawn(FVector(-2080.0f, -80.0f, 1171.0f), 0x150, 0, 80.0f, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE); + OPenguin::Spawn(FVector(-2080.0f, -80.0f, 1171.0f), 0x150, 0, 80.0f, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE); if (gGamestate == CREDITS_SEQUENCE) { - FVector pos6 = { 380.0, 0.0f, -535.0f }; - auto penguin9 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos6, 0x9000, OPenguin::PenguinType::CREDITS, OPenguin::Behaviour::SLIDE3))); - penguin9->MirrorModeAngleOffset = -0x4000; + OPenguin::Spawn(FVector(380.0f, 0.0f, -535.0f), 0x9000, -0x4000, 0.0f, OPenguin::PenguinType::CREDITS, OPenguin::Behaviour::SLIDE3); } else { - FVector pos6 = { 146.0f, 0.0f, -380.0f }; - auto penguin9 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos6, 0x9000, OPenguin::PenguinType::CHICK, OPenguin::Behaviour::SLIDE3))); - penguin9->MirrorModeAngleOffset = -0x4000; + OPenguin::Spawn(FVector(146.0f, 0.0f, -380.0f), 0x9000, -0x4000, 0.0f, OPenguin::PenguinType::CHICK, OPenguin::Behaviour::SLIDE3); } - FVector pos7 = { 380.0f, 0.0f, -766.0f }; - auto penguin10 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos7, 0x5000, OPenguin::PenguinType::CHICK, OPenguin::Behaviour::SLIDE4))); - penguin10->MirrorModeAngleOffset = 0x8000; - - FVector pos8 = { -2300.0f, 0.0f, -210.0f }; - auto penguin11 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos8, 0xC000, OPenguin::PenguinType::CHICK, OPenguin::Behaviour::SLIDE6))); - penguin11->MirrorModeAngleOffset = 0x8000; - - FVector pos9 = { -2500.0f, 0.0f, -250.0f }; - auto penguin12 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos9, 0x4000, OPenguin::PenguinType::CHICK, OPenguin::Behaviour::SLIDE6))); - penguin12->MirrorModeAngleOffset = 0x8000; - - FVector pos10 = { -535.0f, 0.0f, 875.0f }; - auto penguin13 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos10, 0x8000, OPenguin::PenguinType::CHICK, OPenguin::Behaviour::SLIDE6))); - penguin13->MirrorModeAngleOffset = -0x4000; - - FVector pos11 = { -250.0f, 0.0f, 953.0f }; - auto penguin14 = reinterpret_cast(gWorldInstance.AddObject(new OPenguin(pos11, 0x9000, OPenguin::PenguinType::CHICK, OPenguin::Behaviour::SLIDE6))); - penguin14->MirrorModeAngleOffset = -0x4000; + OPenguin::Spawn(FVector(380.0f, 0.0f, -766.0f), 0x5000, 0x8000, 0.0f, OPenguin::PenguinType::CHICK, OPenguin::Behaviour::SLIDE4); + OPenguin::Spawn(FVector(-2300.0f, 0.0f, -210.0f), 0xC000, 0x8000, 0.0f, OPenguin::PenguinType::CHICK, OPenguin::Behaviour::SLIDE6); + OPenguin::Spawn(FVector(-2500.0f, 0.0f, -250.0f), 0x4000, 0x8000, 0.0f, OPenguin::PenguinType::CHICK, OPenguin::Behaviour::SLIDE6); + OPenguin::Spawn(FVector(-535.0f, 0.0f, 875.0f), 0x8000, -0x4000, 0.0f, OPenguin::PenguinType::CHICK, OPenguin::Behaviour::SLIDE6); + OPenguin::Spawn(FVector(-250.0f, 0.0f, 953.0f), 0x9000, -0x4000, 0.0f, OPenguin::PenguinType::CHICK, OPenguin::Behaviour::SLIDE6); if (gGamestate != CREDITS_SEQUENCE) { if (gModeSelection == VERSUS) { - FVector kart = { 0, 0, 0 }; - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[0][50], 50, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[0][100], 100, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[0][150], 150, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[0][200], 200, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[0][250], 250, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(kart, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 50, 3, 0.8333333f); + OBombKart::Spawn(0, 100, 1, 0.8333333f); + OBombKart::Spawn(0, 150, 3, 0.8333333f); + OBombKart::Spawn(0, 200, 1, 0.8333333f); + OBombKart::Spawn(0, 250, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } } } diff --git a/src/engine/courses/Skyscraper.cpp b/src/engine/courses/Skyscraper.cpp index 184110a2e6..53232257d5 100644 --- a/src/engine/courses/Skyscraper.cpp +++ b/src/engine/courses/Skyscraper.cpp @@ -153,15 +153,13 @@ void Skyscraper::BeginPlay() { spawn_all_item_boxes((ActorSpawnData*)LOAD_ASSET_RAW(d_course_skyscraper_item_box_spawns)); if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][20], 20, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][40], 40, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][60], 60, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][80], 80, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][100], 100, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][120], 120, 0, 1.0f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][140], 140, 0, 1.0f)); + OBombKart::Spawn(0, 20, 0, 1.0f); + OBombKart::Spawn(0, 40, 0, 1.0f); + OBombKart::Spawn(0, 60, 0, 1.0f); + OBombKart::Spawn(0, 80, 0, 1.0f); + OBombKart::Spawn(0, 100, 0, 1.0f); + OBombKart::Spawn(0, 120, 0, 1.0f); + OBombKart::Spawn(0, 140, 0, 1.0f); } } diff --git a/src/engine/courses/TestCourse.cpp b/src/engine/courses/TestCourse.cpp index cf9f251796..f4a6710ac8 100644 --- a/src/engine/courses/TestCourse.cpp +++ b/src/engine/courses/TestCourse.cpp @@ -29,6 +29,7 @@ #include "engine/objects/Crab.h" #include "engine/objects/Boos.h" #include "engine/objects/GrandPrixBalloons.h" +#include "engine/objects/Thwomp.h" extern "C" { #include "main.h" @@ -147,75 +148,8 @@ void TestCourse::Load() { void TestCourse::LoadTextures() { dma_textures(gTextureTrees1, 0x0000035BU, 0x00000800U); // 0x03009000 - D_802BA058 = dma_textures(gTexturePiranhaPlant1, 0x000003E8U, 0x00000800U); // 0x03009800 - dma_textures(gTexturePiranhaPlant2, 0x000003E8U, 0x00000800U); // 0x0300A000 - dma_textures(gTexturePiranhaPlant3, 0x000003E8U, 0x00000800U); // 0x0300A800 - dma_textures(gTexturePiranhaPlant4, 0x000003E8U, 0x00000800U); // 0x0300B000 - dma_textures(gTexturePiranhaPlant5, 0x000003E8U, 0x00000800U); // 0x0300B800 - dma_textures(gTexturePiranhaPlant6, 0x000003E8U, 0x00000800U); // 0x0300C000 - dma_textures(gTexturePiranhaPlant7, 0x000003E8U, 0x00000800U); // 0x0300C800 - dma_textures(gTexturePiranhaPlant8, 0x000003E8U, 0x00000800U); // 0x0300D000 - dma_textures(gTexturePiranhaPlant9, 0x000003E8U, 0x00000800U); // 0x0300D800 } -Path2D test_course_path2D[] = { - { 0, 0}, - { 0, -100}, - { 0, -200}, - { 0, -300}, - { 0, -400}, - { 0, -500}, - { 0, -600}, - { 0, -700}, - { 0, -800}, - { 0, -900}, - { 0, -1000}, - { 0, -1096}, // Main point 1 - { 100, -1090}, - { 200, -1085}, - { 300, -1080}, - { 400, -1075}, - { 500, -1072}, // Curve begins to smooth here - { 600, -1068}, - { 700, -1065}, - { 800, -1063}, - { 900, -1061}, - { 984, -1060}, // Main point 2 - { 990, -900}, - { 995, -800}, - { 997, -700}, - { 998, -600}, - { 999, -500}, - { 999, -400}, - { 999, -300}, - { 999, -200}, - { 999, -100}, - { 999, 0}, - { 999, 100}, - { 999, 200}, - { 999, 300}, - { 999, 400}, - { 999, 500}, - { 999, 600}, - { 999, 700}, - { 999, 800}, - { 999, 900}, - { 999, 940}, // Main point 3 - { 900, 945}, - { 800, 945}, - { 700, 947}, - { 600, 948}, - { 500, 949}, - { 400, 949}, - { 300, 949}, - { 200, 950}, - { 100, 950}, - { 0, 950}, // Main point 4 - - // End of path - { -32768, -32768 } // Terminator -}; - void TestCourse::BeginPlay() { struct ActorSpawnData itemboxes[] = { { 200, 1500, 200 , 0}, @@ -270,7 +204,7 @@ void TestCourse::BeginPlay() { // gWorldInstance.AddActor(new OSeagull(2, pos)); // gWorldInstance.AddActor(new OSeagull(3, pos)); // 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)); + OTrophy::Spawn(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), IRotator(0, 90, 0), 1.0f, OTrashBin::Behaviour::MUNCHING)); @@ -282,22 +216,17 @@ void TestCourse::BeginPlay() { // gWorldInstance.AddActor(new ABowserStatue(FVector(-200, 0, 0), ABowserStatue::Behaviour::CRUSH)); // gWorldInstance.AddObject(new OBoos(10, IPathSpan(0, 5), IPathSpan(18, 23), IPathSpan(25, 50))); - - gVehicle2DPathPoint = test_course_path2D; - gVehicle2DPathLength = 53; - D_80162EB0 = spawn_actor_on_surface(test_course_path2D[0].x, 2000.0f, test_course_path2D[0].z); + //OThwomp::Spawn(0, 0, 0, 1.0f, 0, 1, 7); //gWorldInstance.AddTrain(ATrain::TenderStatus::HAS_TENDER, 5, 2.5f, 0); //gWorldInstance.AddTrain(ATrain::TenderStatus::HAS_TENDER, 5, 2.5f, 8); - FVector pos2 = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos2, &gTrackPaths[0][25], 25, 4, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos2, &gTrackPaths[0][45], 45, 4, 0.8333333f)); + OBombKart::Spawn(0, 25, 4, 0.8333333f); + OBombKart::Spawn(0, 45, 4, 0.8333333f); // gWorldInstance.AddActor(new AShip(FVector(0, 0, 0), AShip::Skin::SHIP3)); -// gWorldInstance.AddObject(new OGrandPrixBalloons(FVector(0, 0, 0))); +// OGrandPrixBalloons::Spawn(FVector(0, 0, 0)); } void TestCourse::WhatDoesThisDo(Player* player, int8_t playerId) { diff --git a/src/engine/courses/ToadsTurnpike.cpp b/src/engine/courses/ToadsTurnpike.cpp index c9f8b99186..69fcc7724f 100644 --- a/src/engine/courses/ToadsTurnpike.cpp +++ b/src/engine/courses/ToadsTurnpike.cpp @@ -170,7 +170,7 @@ void ToadsTurnpike::BeginPlay() { spawn_all_item_boxes((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_toads_turnpike_item_box_spawns)); if (gGamestate != CREDITS_SEQUENCE) { - uint32_t waypoint; + uint32_t pathPoint; f32 a = ((gCCSelection * 90.0) / 216.0f) + 4.583333333333333; f32 b = ((gCCSelection * 90.0) / 216.0f) + 2.9166666666666665; a /= 2; // Normally vehicle logic is only ran every 2 frames. This slows the vehicles down to match. @@ -190,34 +190,33 @@ void ToadsTurnpike::BeginPlay() { } for (size_t i = 0; i < _numTrucks; i++) { - waypoint = CalculateWaypointDistribution(i, _numTrucks, gPathCountByPathIndex[0], 0); - gWorldInstance.AddActor(new ATruck(a, b, &gTrackPaths[0][0], waypoint)); + pathPoint = CalculateWaypointDistribution(i, _numTrucks, gPathCountByPathIndex[0], 0); + ATruck::Spawn(a, b, 0, pathPoint, ATruck::SpawnMode::POINT); } for (size_t i = 0; i < _numBuses; i++) { - waypoint = CalculateWaypointDistribution(i, _numBuses, gPathCountByPathIndex[0], 75); - gWorldInstance.AddActor(new ABus(a, b, &gTrackPaths[0][0], waypoint)); + pathPoint = CalculateWaypointDistribution(i, _numBuses, gPathCountByPathIndex[0], 75); + ABus::Spawn(a, b, 0, pathPoint, ABus::SpawnMode::POINT); } for (size_t i = 0; i < _numTankerTrucks; i++) { - waypoint = CalculateWaypointDistribution(i, _numTankerTrucks, gPathCountByPathIndex[0], 50); - gWorldInstance.AddActor(new ATankerTruck(a, b, &gTrackPaths[0][0], waypoint)); + pathPoint = CalculateWaypointDistribution(i, _numTankerTrucks, gPathCountByPathIndex[0], 50); + ATankerTruck::Spawn(a, b, 0, pathPoint, ATankerTruck::SpawnMode::POINT); } for (size_t i = 0; i < _numCars; i++) { - waypoint = CalculateWaypointDistribution(i, _numCars, gPathCountByPathIndex[0], 25); - gWorldInstance.AddActor(new ACar(a, b, &gTrackPaths[0][0], waypoint)); + pathPoint = CalculateWaypointDistribution(i, _numCars, gPathCountByPathIndex[0], 25); + ACar::Spawn(a, b, 0, pathPoint, ACar::SpawnMode::POINT); } if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][50], 50, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][100], 100, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][150], 150, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][200], 200, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][250], 250, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 50, 3, 0.8333333f); + OBombKart::Spawn(0, 100, 1, 0.8333333f); + OBombKart::Spawn(0, 150, 3, 0.8333333f); + OBombKart::Spawn(0, 200, 1, 0.8333333f); + OBombKart::Spawn(0, 250, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } } } diff --git a/src/engine/courses/WarioStadium.cpp b/src/engine/courses/WarioStadium.cpp index 8046e6325d..6904ad45b1 100644 --- a/src/engine/courses/WarioStadium.cpp +++ b/src/engine/courses/WarioStadium.cpp @@ -176,28 +176,18 @@ void WarioStadium::LoadTextures() { void WarioStadium::BeginPlay() { spawn_all_item_boxes((struct ActorSpawnData*) LOAD_ASSET_RAW(d_course_wario_stadium_item_box_spawns)); - FVector pos = { -131.0f, 83.0f, 286.0f }; - pos.x *= gCourseDirection; - gWorldInstance.AddActor(new AWarioSign(pos)); - - FVector pos2 = { -2353.0f, 72.0f, -1608.0f }; - pos2.x *= gCourseDirection; - gWorldInstance.AddActor(new AWarioSign(pos2)); - - FVector pos3 = { -2622.0f, 79.0f, 739.0f }; - pos3.x *= gCourseDirection; - gWorldInstance.AddActor(new AWarioSign(pos3)); + AWarioSign::Spawn(FVector(-131.0f, 83.0f, 286.0f), IRotator(0, 0, 0), FVector(0, 0, 0), FVector(1.0f, 1.0f, 1.0f)); + AWarioSign::Spawn(FVector(-2353.0f, 72.0f, -1608.0f), IRotator(0, 0, 0), FVector(0, 0, 0), FVector(1.0f, 1.0f, 1.0f)); + AWarioSign::Spawn(FVector(-2622.0f, 79.0f, 739.0f), IRotator(0, 0, 0), FVector(0, 0, 0), FVector(1.0f, 1.0f, 1.0f)); if (gModeSelection == VERSUS) { - FVector pos = { 0, 0, 0 }; - - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][50], 50, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][100], 100, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][150], 150, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][200], 200, 1, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][250], 250, 3, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); - gWorldInstance.AddObject(new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f)); + OBombKart::Spawn(0, 50, 3, 0.8333333f); + OBombKart::Spawn(0, 100, 1, 0.8333333f); + OBombKart::Spawn(0, 150, 3, 0.8333333f); + OBombKart::Spawn(0, 200, 1, 0.8333333f); + OBombKart::Spawn(0, 250, 3, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); + OBombKart::Spawn(0, 0, 0, 0.8333333f); } } diff --git a/src/engine/courses/YoshiValley.cpp b/src/engine/courses/YoshiValley.cpp index 71d7824400..4576b53ca1 100644 --- a/src/engine/courses/YoshiValley.cpp +++ b/src/engine/courses/YoshiValley.cpp @@ -160,26 +160,26 @@ void YoshiValley::BeginPlay() { if (gGamestate != CREDITS_SEQUENCE) { //! @bug Skip spawning in credits due to animation crash for now - gWorldInstance.AddObject(new OFlagpole(FVector(-902, 70, -1406), 0x3800)); - gWorldInstance.AddObject(new OFlagpole(FVector(-948, 70, -1533), 0x3800)); - gWorldInstance.AddObject(new OFlagpole(FVector(-2170, 0, 723), 0x400)); - gWorldInstance.AddObject(new OFlagpole(FVector(-2193, 0, 761), 0x400)); - - gWorldInstance.AddObject(new OHedgehog(FVector(-1683, -80, -88), FVector2D(-1650, -114), 9)); - gWorldInstance.AddObject(new OHedgehog(FVector(-1636, -93, -147), FVector2D(-1661, -151), 9)); - gWorldInstance.AddObject(new OHedgehog(FVector(-1628, -86, -108), FVector2D(-1666, -58), 9)); - gWorldInstance.AddObject(new OHedgehog(FVector(-1676, -69, -30), FVector2D(-1651, -26), 9)); - gWorldInstance.AddObject(new OHedgehog(FVector(-1227, -27, -989), FVector2D(-1194, -999), 26)); - gWorldInstance.AddObject(new OHedgehog(FVector(-1261, -41, -880), FVector2D(-1213, -864), 26)); - gWorldInstance.AddObject(new OHedgehog(FVector(-1342, -60, -830), FVector2D(-1249, -927), 26)); - gWorldInstance.AddObject(new OHedgehog(FVector(-1429, -78, -849), FVector2D(-1347, -866), 26)); - gWorldInstance.AddObject(new OHedgehog(FVector(-1492, -94, -774), FVector2D(-1427, -891), 26)); - gWorldInstance.AddObject(new OHedgehog(FVector(-1453, -87, -784), FVector2D(-1509, -809), 26)); - gWorldInstance.AddObject(new OHedgehog(FVector(-1488, 89, -852), FVector2D(-1464, -822), 26)); - gWorldInstance.AddObject(new OHedgehog(FVector(-1301, 47, -904), FVector2D(-1537, -854), 26)); - gWorldInstance.AddObject(new OHedgehog(FVector(-2587, 56, -259), FVector2D(-2624, -241), 28)); - gWorldInstance.AddObject(new OHedgehog(FVector(-2493, 94, -454), FVector2D(-2505, -397), 28)); - gWorldInstance.AddObject(new OHedgehog(FVector(-2477, 3, -57), FVector2D(-2539, -66), 28)); + OFlagpole::Spawn(FVector(-902, 70, -1406), 0x3800); + OFlagpole::Spawn(FVector(-948, 70, -1533), 0x3800); + OFlagpole::Spawn(FVector(-2170, 0, 723), 0x400); + OFlagpole::Spawn(FVector(-2193, 0, 761), 0x400); + + OHedgehog::Spawn(FVector(-1683, -80, -88), FVector2D(-1650, -114), 9); + OHedgehog::Spawn(FVector(-1636, -93, -147), FVector2D(-1661, -151), 9); + OHedgehog::Spawn(FVector(-1628, -86, -108), FVector2D(-1666, -58), 9); + OHedgehog::Spawn(FVector(-1676, -69, -30), FVector2D(-1651, -26), 9); + OHedgehog::Spawn(FVector(-1227, -27, -989), FVector2D(-1194, -999), 26); + OHedgehog::Spawn(FVector(-1261, -41, -880), FVector2D(-1213, -864), 26); + OHedgehog::Spawn(FVector(-1342, -60, -830), FVector2D(-1249, -927), 26); + OHedgehog::Spawn(FVector(-1429, -78, -849), FVector2D(-1347, -866), 26); + OHedgehog::Spawn(FVector(-1492, -94, -774), FVector2D(-1427, -891), 26); + OHedgehog::Spawn(FVector(-1453, -87, -784), FVector2D(-1509, -809), 26); + OHedgehog::Spawn(FVector(-1488, 89, -852), FVector2D(-1464, -822), 26); + OHedgehog::Spawn(FVector(-1301, 47, -904), FVector2D(-1537, -854), 26); + OHedgehog::Spawn(FVector(-2587, 56, -259), FVector2D(-2624, -241), 28); + OHedgehog::Spawn(FVector(-2493, 94, -454), FVector2D(-2505, -397), 28); + OHedgehog::Spawn(FVector(-2477, 3, -57), FVector2D(-2539, -66), 28); } if (gModeSelection == VERSUS) { @@ -187,20 +187,13 @@ void YoshiValley::BeginPlay() { // the original data has values here. // Note that the Y height is calculated automatically to place the kart on the surface - FVector pos = { -1533, 0, -682 }; - gWorldInstance.AddObject(new OBombKart(pos, NULL, 0, 0, 0.8333333f)); - FVector pos2 = { -1565, 0, -619 }; - gWorldInstance.AddObject(new OBombKart(pos2, NULL, 10, 0, 0.8333333f)); - FVector pos3 = { -1529, 0, -579 }; - gWorldInstance.AddObject(new OBombKart(pos3, NULL, 20, 0, 0.8333333f)); - FVector pos4 = { -1588, 0, -534 }; - gWorldInstance.AddObject(new OBombKart(pos4, NULL, 30, 0, 0.8333333f)); - FVector pos5 = { -1598, 0, -207 }; - gWorldInstance.AddObject(new OBombKart(pos5, NULL, 40, 0, 0.8333333f)); - FVector pos6 = { -1646, 0, -147 }; - gWorldInstance.AddObject(new OBombKart(pos6, NULL, 50, 0, 0.8333333f)); - FVector pos7 = { -2532, 0, -445 }; - gWorldInstance.AddObject(new OBombKart(pos7, NULL, 60, 0, 0.8333333f)); + OBombKart::Spawn(FVector(-1533, 0, -682), 0, 0.8333333f); + OBombKart::Spawn(FVector(-1565, 0, -619), 0, 0.8333333f); + OBombKart::Spawn(FVector(-1529, 0, -579), 0, 0.8333333f); + OBombKart::Spawn(FVector(-1588, 0, -534), 0, 0.8333333f); + OBombKart::Spawn(FVector(-1598, 0, -207), 0, 0.8333333f); + OBombKart::Spawn(FVector(-1646, 0, -147), 0, 0.8333333f); + OBombKart::Spawn(FVector(-2532, 0, -445), 0, 0.8333333f); } } diff --git a/src/engine/editor/Collision.cpp b/src/engine/editor/Collision.cpp index bf62d6273c..261aa40d70 100644 --- a/src/engine/editor/Collision.cpp +++ b/src/engine/editor/Collision.cpp @@ -4,13 +4,17 @@ #include #include "Matrix.h" +#include "engine/Actor.h" +#include "engine/objects/Object.h" +#include "engine/editor/GameObject.h" + extern "C" { #include "main.h" #include "other_textures.h" } namespace Editor { - void GenerateCollisionMesh(GameObject* object, Gfx* model, float scale) { + void GenerateCollisionMesh(std::variant object, Gfx* model, float scale) { int8_t opcode; uintptr_t lo; uintptr_t hi; @@ -19,6 +23,12 @@ namespace Editor { Vtx* vtx = NULL; size_t i = 0; bool run = true; + + //! @attention Objects will not be clickable if editor is enabled mid-race. + if (CVarGetInteger("gEditorEnabled", false) == true) { + return; + } + while (run) { i++; lo = ptr->words.w0; @@ -64,8 +74,11 @@ namespace Editor { FVector p1 = FVector(vtx[v1].v.ob[0], vtx[v1].v.ob[1], vtx[v1].v.ob[2]); FVector p2 = FVector(vtx[v2].v.ob[0], vtx[v2].v.ob[1], vtx[v2].v.ob[2]); FVector p3 = FVector(vtx[v3].v.ob[0], vtx[v3].v.ob[1], vtx[v3].v.ob[2]); - - object->Triangles.push_back({p1, p2, p3}); + std::visit([p1, p2, p3](auto* obj) { + if (obj) { + obj->Triangles.push_back({p1, p2, p3}); + } + }, object); break; } case G_TRI1_OTR: { @@ -82,9 +95,11 @@ namespace Editor { FVector p1 = FVector(vtx[v1].v.ob[0], vtx[v1].v.ob[1], vtx[v1].v.ob[2]); FVector p2 = FVector(vtx[v2].v.ob[0], vtx[v2].v.ob[1], vtx[v2].v.ob[2]); FVector p3 = FVector(vtx[v3].v.ob[0], vtx[v3].v.ob[1], vtx[v3].v.ob[2]); - - object->Triangles.push_back({p1, p2, p3}); - + std::visit([p1, p2, p3](auto* obj) { + if (obj) { + obj->Triangles.push_back({p1, p2, p3}); + } + }, object); break; } case G_TRI2: { @@ -109,8 +124,12 @@ namespace Editor { FVector p5 = FVector(vtx[v5].v.ob[0], vtx[v5].v.ob[1], vtx[v5].v.ob[2]); FVector p6 = FVector(vtx[v6].v.ob[0], vtx[v6].v.ob[1], vtx[v6].v.ob[2]); - object->Triangles.push_back({p1, p2, p3}); - object->Triangles.push_back({p4, p5, p6}); + std::visit([p1, p2, p3, p4, p5, p6](auto* obj) { + if (obj) { + obj->Triangles.push_back({p1, p2, p3}); + obj->Triangles.push_back({p4, p5, p6}); + } + }, object); break; } case G_QUAD: { @@ -128,8 +147,12 @@ namespace Editor { FVector p3 = FVector(vtx[v3].v.ob[0], vtx[v3].v.ob[1], vtx[v3].v.ob[2]); FVector p4 = FVector(vtx[v4].v.ob[0], vtx[v4].v.ob[1], vtx[v4].v.ob[2]); - object->Triangles.push_back({p1, p2, p3}); - object->Triangles.push_back({p1, p3, p4}); + std::visit([p1, p2, p3, p4](auto* obj) { + if (obj) { + obj->Triangles.push_back({p1, p2, p3}); + obj->Triangles.push_back({p1, p3, p4}); + } + }, object); break; } case G_ENDDL: diff --git a/src/engine/editor/Collision.h b/src/engine/editor/Collision.h index ee8fef4af5..d761bf04c9 100644 --- a/src/engine/editor/Collision.h +++ b/src/engine/editor/Collision.h @@ -3,6 +3,8 @@ #include #include #include "GameObject.h" +#include "engine/Actor.h" +#include "engine/objects/Object.h" #include "EditorMath.h" @@ -18,6 +20,6 @@ #define EDITOR_GFX_GET_OPCODE(var) ((uint32_t) ((var) & 0xFF000000)) namespace Editor { - void GenerateCollisionMesh(GameObject* object, Gfx* model, float scale); + void GenerateCollisionMesh(std::variant object, Gfx* model, float scale); void DebugCollision(GameObject* obj, FVector pos, IRotator rot, FVector scale, const std::vector& triangles); -} \ No newline at end of file +} diff --git a/src/engine/editor/Editor.cpp b/src/engine/editor/Editor.cpp index 1741cafa56..2fada8a291 100644 --- a/src/engine/editor/Editor.cpp +++ b/src/engine/editor/Editor.cpp @@ -24,9 +24,6 @@ extern "C" { } namespace Editor { - int gfx_create_framebuffer(uint32_t width, uint32_t height, uint32_t native_width, uint32_t native_height, - uint8_t resize); - Editor::Editor() { } @@ -39,21 +36,35 @@ namespace Editor { printf("Editor: Loading Editor...\n"); eObjectPicker.Load(); for (auto& object : eGameObjects) { - GenerateCollisionMesh(object, object->Model, 1.0f); + GenerateCollisionMesh(object, (Gfx*)object->Model, 1.0f); object->Load(); } + printf("Editor: Loading Complete!\n"); } - void Editor::Tick() { + void Editor::GenerateCollision() { + // for (auto& actor : gWorldInstance.Actors) { + // GenerateCollisionMesh(actor, (Gfx*)actor->Model, 1.0f); + // } + } + void Editor::Tick() { if (CVarGetInteger("gEditorEnabled", 0) == true) { bEditorEnabled = true; } else { bEditorEnabled = false; + gIsEditorPaused = false; // Prevents game being paused with the editor closed. return; } + // Set camera + if (CVarGetInteger("gFreecam", 0) == true) { + eCamera = &cameras[CAMERA_FREECAM]; + } else { + eCamera = &cameras[0]; + } + auto wnd = GameEngine::Instance->context->GetWindow(); static bool wasMouseDown = false; @@ -63,16 +74,16 @@ namespace Editor { Ship::Coords mousePos = wnd->GetMousePos(); bool isMouseDown = wnd->GetMouseState(Ship::LUS_MOUSE_BTN_LEFT); - auto it = std::remove_if(eGameObjects.begin(), eGameObjects.end(), - [](auto& object) { - if (*object->DespawnFlag == object->DespawnValue) { - delete object; // Free the pointed-to memory - return true; // Remove the pointer from the vector - } - return false; - }); + //auto it = std::remove_if(eGameObjects.begin(), eGameObjects.end(), + // [](auto& object) { + // if (*object->DespawnFlag == object->DespawnValue) { + // delete object; // Free the pointed-to memory + // return true; // Remove the pointer from the vector + // } + // return false; + // }); - eGameObjects.erase(it, eGameObjects.end()); + //eGameObjects.erase(it, eGameObjects.end()); if (isMouseDown && !wasMouseDown) { // Mouse just pressed (Pressed state) @@ -80,7 +91,7 @@ namespace Editor { isDragging = false; } - if (isMouseDown) { + if (isMouseDown) { // Mouse is being held (Held state) int dx = mousePos.x - mouseStartPos.x; int dy = mousePos.y - mouseStartPos.y; @@ -122,15 +133,16 @@ namespace Editor { } } - GameObject* Editor::AddObject(const char* name, FVector* pos, IRotator* rot, FVector* scale, Gfx* model, float collScale, GameObject::CollisionType collision, float boundingBoxSize, int32_t* despawnFlag, int32_t despawnValue) { + GameObject* Editor::AddObject(FVector pos, IRotator rot, FVector scale, const char* model, float collScale, GameObject::CollisionType collision, float boundingBoxSize) { //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); + + if (nullptr != model && model[0] != '\0') { + eGameObjects.push_back(new GameObject(pos, rot, scale, model, {}, collision, boundingBoxSize)); + GenerateCollisionMesh(eGameObjects.back(), (Gfx*)LOAD_ASSET_RAW(model), collScale); } else { // to bounding box or sphere collision - eGameObjects.push_back(new GameObject(name, pos, rot, scale, model, {}, GameObject::CollisionType::BOUNDING_BOX, - 10.0f, despawnFlag, despawnValue)); + eGameObjects.push_back(new GameObject(pos, rot, scale, model, {}, GameObject::CollisionType::BOUNDING_BOX, + 10.0f)); } return eGameObjects.back(); } @@ -140,27 +152,37 @@ namespace Editor { } void Editor::ClearObjects() { + ResetGizmo(); + for (auto& obj : eGameObjects) { delete obj; } eGameObjects.clear(); } + // Reset the gizmo + void Editor::ResetGizmo() { + eObjectPicker.eGizmo._selected = static_cast(nullptr); + eObjectPicker._selected = static_cast(nullptr); + eObjectPicker.eGizmo.Pos = FVector(0, 0, 0); + eObjectPicker.eGizmo.Enabled = false; + } + void Editor::DeleteObject() { - Gizmo* gizmo = &eObjectPicker.eGizmo; - if (gizmo->_selected && gizmo->_selected->DespawnFlag) { - *gizmo->_selected->DespawnFlag = gizmo->_selected->DespawnValue; - gizmo->_selected = nullptr; - eObjectPicker._selected = nullptr; - } + std::visit([this](auto* obj) { + if (nullptr != obj) { + gEditor.ResetGizmo(); // Unselect the object to prevent crashes + obj->Destroy(); + } + }, eObjectPicker.eGizmo._selected); } void Editor::ClearMatrixPool() { EditorMatrix.clear(); } - void Editor::SelectObjectFromSceneExplorer(GameObject* object) { + void Editor::SelectObjectFromSceneExplorer(std::variant object) { eObjectPicker._selected = object; eObjectPicker.eGizmo.Enabled = true; eObjectPicker.eGizmo.SetGizmoNoCursor(object); diff --git a/src/engine/editor/Editor.h b/src/engine/editor/Editor.h index 60e578b218..38af5b9151 100644 --- a/src/engine/editor/Editor.h +++ b/src/engine/editor/Editor.h @@ -5,7 +5,11 @@ #include #include "GameObject.h" + #ifdef __cplusplus +extern "C" { +#include "camera.h" +} #include "ObjectPicker.h" namespace Editor { @@ -22,16 +26,19 @@ namespace Editor { void Tick(); void Draw(); void Load(); - GameObject* AddObject(const char* name, FVector* pos, IRotator* rot, FVector* scale, Gfx* model, float collScale, GameObject::CollisionType collision, float boundingBoxSize, int32_t* despawnFlag, int32_t despawnValue); + void GenerateCollision(); + GameObject* AddObject(FVector pos, IRotator rot, FVector scale, const char* model, float collScale, GameObject::CollisionType collision, float boundingBoxSize); void AddLight(const char* name, FVector* pos, s8* rot); void ClearObjects(); + void ResetGizmo(); void RemoveObject(); - void SelectObjectFromSceneExplorer(GameObject* object); + void SelectObjectFromSceneExplorer(std::variant object); void SetLevelDimensions(s16 minX, s16 maxX, s16 minZ, s16 maxZ, s16 minY, s16 maxY); void ClearMatrixPool(); void DeleteObject(); bool bEditorEnabled = false; + Camera* eCamera = &cameras[0]; private: bool _draw = false; Vec3f _ray; diff --git a/src/engine/editor/EditorMath.cpp b/src/engine/editor/EditorMath.cpp index f541d92f20..38a1fff5ef 100644 --- a/src/engine/editor/EditorMath.cpp +++ b/src/engine/editor/EditorMath.cpp @@ -42,12 +42,13 @@ bool IsInGameScreen() { FVector ScreenRayTrace() { auto wnd = GameEngine::Instance->context->GetWindow(); - Camera* camera = &cameras[0]; + Camera* camera = gEditor.eCamera; Ship::Coords mouse = wnd->GetMousePos(); auto gfx_current_game_window_viewport = GetInterpreter()->mGameWindowViewport; mouse.x -= gfx_current_game_window_viewport.x; mouse.y -= gfx_current_game_window_viewport.y; + // Get screen dimensions uint32_t width = OTRGetGameViewportWidth(); uint32_t height = OTRGetGameViewportHeight(); @@ -285,7 +286,6 @@ std::optional QueryHandleIntersection(MtxF mtx, Ray ray, const Triangle if (IntersectRayTriangle(localRay, tri, t)) { FVector localClickPosition = localRay.Origin + localRay.Direction * t; FVector worldClickPosition = TransformVecByMatrix(localClickPosition, (float(*)[4])&mtx); - return worldClickPosition; // Stop checking objects if we selected a Gizmo handle } return std::nullopt; @@ -381,8 +381,9 @@ float CalculateAngle(const FVector& start, const FVector& end) { } void SetDirectionFromRotator(IRotator rot, s8 direction[3]) { + rot.yaw += 0xC000; //! @warning dumb hack to align the light properly float yaw = (rot.yaw) * (M_PI / 32768.0f); // Convert from n64 binary angles 0-0xFFFF 0-360 degrees to radians - float pitch = rot.pitch * (M_PI / 32768.0f); + float pitch = rot.pitch * (M_PI / 32768.0f); // Compute unit direction vector float x = cosf(yaw) * cosf(pitch); @@ -411,10 +412,11 @@ void SetRotatorFromDirection(FVector direction, IRotator* rot) { } FVector GetPositionAheadOfCamera(f32 dist) { - FVector pos = FVector(cameras[0].pos[0], cameras[0].pos[1], cameras[0].pos[2]); + Camera* camera = gEditor.eCamera; + FVector pos = FVector(camera->pos[0], camera->pos[1], camera->pos[2]); - f32 pitch = (cameras[0].rot[2] / 65535.0f) * 360.0f; - f32 yaw = (cameras[0].rot[1] / 65535.0f) * 360.0f; + f32 pitch = (camera->rot[2] / 65535.0f) * 360.0f; + f32 yaw = (camera->rot[1] / 65535.0f) * 360.0f; // Convert degrees to radians pitch = pitch * M_PI / 180.0f; @@ -422,7 +424,7 @@ FVector GetPositionAheadOfCamera(f32 dist) { // Compute forward vector FVector forward( - -sinf(yaw), // X + sinf(yaw), // X -sinf(pitch), // Y cosf(yaw) // Z (vertical component) ); diff --git a/src/engine/editor/GameObject.cpp b/src/engine/editor/GameObject.cpp index 6f52c8ac88..30a1722f4d 100644 --- a/src/engine/editor/GameObject.cpp +++ b/src/engine/editor/GameObject.cpp @@ -3,8 +3,7 @@ namespace Editor { - GameObject::GameObject(const char* name, FVector* pos, IRotator* rot, FVector* scale, Gfx* model, std::vector triangles, CollisionType collision, float boundingBoxSize, int32_t* despawnFlag, int32_t despawnValue) { - Name = name; + GameObject::GameObject(FVector pos, IRotator rot, FVector scale, const char* model, std::vector triangles, CollisionType collision, float boundingBoxSize) { Pos = pos; Rot = rot; Scale = scale; @@ -12,13 +11,6 @@ namespace Editor { Triangles = triangles; Collision = collision; BoundingBoxSize = boundingBoxSize; - DespawnFlag = despawnFlag; - DespawnValue = despawnValue; - } - - GameObject::GameObject(FVector* pos, Vec3s* rot) { - //Pos = pos; - //Rot = rot; } GameObject::GameObject() {}; @@ -27,4 +19,23 @@ namespace Editor { void GameObject::Tick(){}; + FVector GameObject::GetLocation() const { + return Pos; + }; + IRotator GameObject::GetRotation() const { + return Rot; + } + FVector GameObject::GetScale() const { + return Scale; + } + void GameObject::Translate(FVector pos) { + Pos = pos; + }; + void GameObject::Rotate(IRotator rot) { + Rot = rot; + }; + void GameObject::SetScale(FVector scale) { + Scale = scale; + }; + } // namespace Editor diff --git a/src/engine/editor/GameObject.h b/src/engine/editor/GameObject.h index ca08d8ba92..35ce90dbf0 100644 --- a/src/engine/editor/GameObject.h +++ b/src/engine/editor/GameObject.h @@ -6,6 +6,9 @@ #include "../CoreMath.h" #include "EditorMath.h" #include +#include "engine/SpawnParams.h" + +#include "src/port/ui/DefaultProperties.h" extern "C" { #include "common_structs.h" @@ -22,23 +25,32 @@ namespace Editor { BOUNDING_SPHERE }; - GameObject(const char* name, FVector* pos, IRotator* rot, FVector* scale, Gfx* model, std::vector triangles, CollisionType collision, float boundingBoxSize, int32_t* despawnFlag, int32_t despawnValue); - GameObject(FVector* pos, Vec3s* rot); + GameObject(FVector pos, IRotator rot, FVector scale, const char* model, std::vector triangles, CollisionType collision, float boundingBoxSize); GameObject(); virtual void Tick(); virtual void Draw(); virtual void Load() {}; + FVector GetLocation() const; + IRotator GetRotation() const; + FVector GetScale() const; + void Translate(FVector pos); + void Rotate(IRotator rot); + void SetScale(FVector scale); + void Destroy() {}; const char* Name; - FVector* Pos; - IRotator* Rot; - FVector* Scale; - Gfx* Model; + const char* ResourceName; + FVector SpawnPos = {0.0f, 0.0f, 0.0f}; + IRotator SpawnRot = {0, 0, 0}; + FVector SpawnScale = {1.0f, 1.0f, 1.0f}; + float Speed; + FVector Pos; + IRotator Rot; + FVector Scale; + const char* Model = ""; std::vector Triangles; CollisionType Collision; float BoundingBoxSize; - int32_t* DespawnFlag; - int32_t DespawnValue; - + virtual void DrawEditorProperties() { DrawDefaultEditorProperties(); }; }; } diff --git a/src/engine/editor/Gizmo.cpp b/src/engine/editor/Gizmo.cpp index 4987242f06..9c518b53b0 100644 --- a/src/engine/editor/Gizmo.cpp +++ b/src/engine/editor/Gizmo.cpp @@ -10,6 +10,10 @@ #include "port/Engine.h" #include #include +#include "engine/Matrix.h" +#include "engine/Actor.h" +#include "engine/objects/Object.h" +#include "engine/editor/GameObject.h" #include "engine/actors/Ship.h" @@ -29,34 +33,34 @@ namespace Editor { void Gizmo::Load() { /* Translate handle collision */ - RedCollision.Pos = &Pos; - RedCollision.Model = (Gfx*)"__OTR__editor/gizmo/translate_handle_red"; + RedCollision.Pos = Pos; + RedCollision.Model = "__OTR__editor/gizmo/translate_handle_red"; - GreenCollision.Pos = &Pos; - GreenCollision.Model = (Gfx*)"__OTR__editor/gizmo/translate_handle_green"; + GreenCollision.Pos = Pos; + GreenCollision.Model = "__OTR__editor/gizmo/translate_handle_green"; - BlueCollision.Pos = &Pos; - BlueCollision.Model = (Gfx*)"__OTR__editor/gizmo/translate_handle_blue"; + BlueCollision.Pos = Pos; + BlueCollision.Model = "__OTR__editor/gizmo/translate_handle_blue"; /* Rotate handle collision */ - RedRotateCollision.Pos = &Pos; - RedRotateCollision.Model = (Gfx*)"__OTR__editor/gizmo/rot_handle_red"; + RedRotateCollision.Pos = Pos; + RedRotateCollision.Model = "__OTR__editor/gizmo/rot_handle_red"; - GreenRotateCollision.Pos = &Pos; - GreenRotateCollision.Model = (Gfx*)"__OTR__editor/gizmo/rot_handle_green"; + GreenRotateCollision.Pos = Pos; + GreenRotateCollision.Model = "__OTR__editor/gizmo/rot_handle_green"; - BlueRotateCollision.Pos = &Pos; - BlueRotateCollision.Model = (Gfx*)"__OTR__editor/gizmo/rot_handle_blue"; + BlueRotateCollision.Pos = Pos; + BlueRotateCollision.Model = "__OTR__editor/gizmo/rot_handle_blue"; /* Scale handle collision */ - RedScaleCollision.Pos = &Pos; - RedScaleCollision.Model = (Gfx*)"__OTR__editor/gizmo/scale_handle_red"; + RedScaleCollision.Pos = Pos; + RedScaleCollision.Model = "__OTR__editor/gizmo/scale_handle_red"; - GreenScaleCollision.Pos = &Pos; - GreenScaleCollision.Model = (Gfx*)"__OTR__editor/gizmo/scale_handle_green"; + GreenScaleCollision.Pos = Pos; + GreenScaleCollision.Model = "__OTR__editor/gizmo/scale_handle_green"; - BlueScaleCollision.Pos = &Pos; - BlueScaleCollision.Model = (Gfx*)"__OTR__editor/gizmo/scale_handle_blue"; + BlueScaleCollision.Pos = Pos; + BlueScaleCollision.Model = "__OTR__editor/gizmo/scale_handle_blue"; GenerateCollisionMesh(&RedCollision, (Gfx*)LOAD_ASSET_RAW(RedCollision.Model), 1.0f); GenerateCollisionMesh(&GreenCollision, (Gfx*)LOAD_ASSET_RAW(GreenCollision.Model), 1.0f); @@ -89,178 +93,256 @@ void Gizmo::Tick() { } // Makes the gizmo visible -void Gizmo::SetGizmo(GameObject* object, Ray ray) { - _selected = object; +void Gizmo::SetGizmo(const std::variant& object, Ray ray) { _ray = ray.Direction; - Pos = FVector( - object->Pos->x, - object->Pos->y, - object->Pos->z - ); + std::visit([this](auto* obj) { + _selected = obj; + this->Pos = obj->GetLocation(); + }, object); } -void Gizmo::SetGizmoNoCursor(GameObject* object) { - _selected = object; - Pos = FVector( - object->Pos->x, - object->Pos->y, - object->Pos->z - ); +void Gizmo::SetGizmoNoCursor(const std::variant& object) { + std::visit([this](auto* obj) { + _selected = obj; + Pos = obj->GetLocation(); + }, object); } void Gizmo::Translate() { static float length = 180.0f; // Default value - // Prevent nullptr exceptions - if (_selected == NULL || _selected->Pos == NULL) { - return; - } + std::visit([this](auto* obj) { + Camera* camera = gEditor.eCamera; + float x, y, z = 0; + if (nullptr == obj) { + return; + } + + const FVector location = obj->GetLocation(); - if (Enabled) { length = sqrt( - pow(_selected->Pos->x - cameras[0].pos[0], 2) + - pow(_selected->Pos->y - cameras[0].pos[1], 2) + - pow(_selected->Pos->z - cameras[0].pos[2], 2) + pow(location.x - camera->pos[0], 2) + + pow(location.y - camera->pos[1], 2) + + pow(location.z - camera->pos[2], 2) ); - switch(SelectedHandle) { + switch(this->SelectedHandle) { case GizmoHandle::All_Axis: - _selected->Pos->x = (cameras[0].pos[0] + _ray.x * PickDistance) + _cursorOffset.x; - _selected->Pos->y = (cameras[0].pos[1] + _ray.y * PickDistance) + _cursorOffset.y; - _selected->Pos->z = (cameras[0].pos[2] + _ray.z * PickDistance) + _cursorOffset.z; if (CVarGetInteger("gEditorSnapToGround", 0) == true) { - _selected->Pos->y = SnapToSurface(_selected->Pos); + y = SnapToSurface(location); + } else { + y = ((camera->pos[1] + _ray.y * PickDistance) + _cursorOffset.y); } + + obj->Translate( + FVector( + ((camera->pos[0] + _ray.x * PickDistance) + _cursorOffset.x), + y, + ((camera->pos[2] + _ray.z * PickDistance) + _cursorOffset.z) + ) + ); break; case GizmoHandle::X_Axis: - _selected->Pos->x = (cameras[0].pos[0] + _ray.x * length) + _cursorOffset.x; if (CVarGetInteger("gEditorSnapToGround", 0) == true) { - _selected->Pos->y = SnapToSurface(_selected->Pos); + y = SnapToSurface(location); + } else { + y = location.y; // Preserve Y } + + obj->Translate( + FVector( + ((camera->pos[0] + _ray.x * length) + _cursorOffset.x), + y, + location.z // Preserve Z + ) + ); break; case GizmoHandle::Y_Axis: - _selected->Pos->y = (cameras[0].pos[1] + _ray.y * length) + _cursorOffset.y; + obj->Translate( + FVector( + location.x, // Preserve X + ((camera->pos[1] + _ray.y * length) + _cursorOffset.y), + location.z // Preserve Z + ) + ); break; case GizmoHandle::Z_Axis: - _selected->Pos->z = (cameras[0].pos[2] + _ray.z * length) + _cursorOffset.z; if (CVarGetInteger("gEditorSnapToGround", 0) == true) { - _selected->Pos->y = SnapToSurface(_selected->Pos); + y = SnapToSurface(location); + } else { + y = location.y; // Preserve Y } + obj->Translate( + FVector( + location.x, // Preserve X + y, + ((camera->pos[2] + _ray.z * length) + _cursorOffset.z) + ) + ); break; } - if (CVarGetInteger("gEditorBoundary", 0) == true) { - _selected->Pos->x = MAX(_selected->Pos->x, dimensions.MinX); - _selected->Pos->x = MIN(_selected->Pos->x, dimensions.MaxX); + FVector newLoc = obj->GetLocation(); + x = newLoc.x; + y = newLoc.y; + z = newLoc.z; - _selected->Pos->y = MAX(_selected->Pos->y, dimensions.MinY); - _selected->Pos->y = MIN(_selected->Pos->y, dimensions.MaxY); - _selected->Pos->z = MAX(_selected->Pos->z, dimensions.MinZ); - _selected->Pos->z = MIN(_selected->Pos->z, dimensions.MaxZ); + if (CVarGetInteger("gEditorBoundary", 0) == true) { +#define EDITOR_CLAMP(value, min, max) ((value) < (min) ? min : (value) > (max) ? max : value) + x = EDITOR_CLAMP(newLoc.x, dimensions.MinX, dimensions.MaxX); + y = EDITOR_CLAMP(newLoc.y, dimensions.MinY, dimensions.MaxY); + z = EDITOR_CLAMP(newLoc.z, dimensions.MinZ, dimensions.MaxZ); + obj->Translate(FVector(x, y, z)); +#undef EDITOR_CLAMP } - Pos = FVector( - _selected->Pos->x, - _selected->Pos->y, - _selected->Pos->z - ); - } + // Update the gizmo position + Pos = FVector(x, y, z); + + // Pass the _selected object into this lambda function + }, _selected); } -f32 Gizmo::SnapToSurface(FVector* pos) { +f32 Gizmo::SnapToSurface(const FVector pos) { float y; - y = spawn_actor_on_surface(pos->x, 2000.0f, pos->z); + y = spawn_actor_on_surface(pos.x, 2000.0f, pos.z); if (y == 3000.0f || y == -3000.0f) { - y = pos->y; + y = pos.y; } return y; } void Gizmo::Rotate() { - FVector cam = FVector(cameras[0].pos[0], cameras[0].pos[1], cameras[0].pos[2]); + std::visit([this](auto* obj) { + Camera* camera = gEditor.eCamera; + FVector cam = FVector(camera->pos[0], camera->pos[1], camera->pos[2]); + IRotator rot; - if (_selected == nullptr || _selected->Rot == nullptr) { - return; - } + if (nullptr == obj) { + return; + } - // Store initial scale at the beginning of the drag - if (ManipulationStart) { - ManipulationStart = false; - InitialRotation = *_selected->Rot; // Store initial rotation - } + // Store initial scale at the beginning of the drag + if (ManipulationStart) { + ManipulationStart = false; + InitialRotation = obj->GetRotation(); // Store initial rotation + } - // Initial click position - FVector clickPos = *_selected->Pos - _cursorOffset; + // Initial click position + FVector clickPos = obj->GetLocation() - _cursorOffset; - // Calculate difference - FVector diff = (cam + _ray * PickDistance) - clickPos; + // Calculate difference + FVector diff = (cam + _ray * PickDistance) - clickPos; - // Set rotation sensitivity - diff = diff * 100.0f; - switch (SelectedHandle) { - case GizmoHandle::X_Axis: - _selected->Rot->pitch = (uint16_t)InitialRotation.pitch + diff.x; - break; - case GizmoHandle::Y_Axis: - _selected->Rot->yaw = (uint16_t)InitialRotation.yaw + diff.y; - break; - case GizmoHandle::Z_Axis: - _selected->Rot->roll = (uint16_t)InitialRotation.roll + diff.z; + // Set rotation sensitivity + diff = diff * 100.0f; + switch (SelectedHandle) { + case GizmoHandle::X_Axis: + rot.Set( + (uint16_t)(InitialRotation.pitch + diff.x), + InitialRotation.yaw, + InitialRotation.roll + ); break; - } + case GizmoHandle::Y_Axis: + rot.Set( + InitialRotation.pitch, + (uint16_t)(InitialRotation.yaw + diff.y), + InitialRotation.roll + ); + break; + case GizmoHandle::Z_Axis: + rot.Set( + InitialRotation.pitch, + InitialRotation.yaw, + (uint16_t)(InitialRotation.roll + diff.z) + ); + break; + } + obj->Rotate(rot); + // Pass the _selected object into this lambda function + }, _selected); } void Gizmo::Scale() { - FVector cam = FVector(cameras[0].pos[0], cameras[0].pos[1], cameras[0].pos[2]); - if (_selected == nullptr || _selected->Scale == nullptr) { - return; - } + std::visit([this](auto* obj) { + Camera* camera = gEditor.eCamera; + FVector cam = FVector(camera->pos[0], camera->pos[1], camera->pos[2]); + if (nullptr == obj) { + return; + } - // Store initial scale at the beginning of the drag - if (ManipulationStart) { - ManipulationStart = false; - InitialScale = *_selected->Scale; - } + // Store initial scale at the beginning of the drag + if (ManipulationStart) { + ManipulationStart = false; + InitialScale = obj->GetScale(); + } - // Initial click position - FVector clickPos = *_selected->Pos - _cursorOffset; + FVector scale = obj->GetScale(); - // Calculate difference - FVector diff = (cam + _ray * PickDistance) - clickPos; + // Initial click position + FVector clickPos = obj->GetLocation() - _cursorOffset; - // Lower scaling sensitivity - diff = diff * 0.01f; + // Calculate difference + FVector diff = (cam + _ray * PickDistance) - clickPos; - switch (SelectedHandle) { - case GizmoHandle::X_Axis: - _selected->Scale->x = InitialScale.x + -diff.x; - break; - case GizmoHandle::Y_Axis: - _selected->Scale->y = InitialScale.y + diff.y; - break; - case GizmoHandle::Z_Axis: - _selected->Scale->z = InitialScale.z + -diff.z; - break; - case GizmoHandle::All_Axis: - float uniformScale = (diff.x - diff.y - diff.z) / 3.0f; - uniformScale *= 1.8; // Increased sensitivity - _selected->Scale->x = uniformScale; - _selected->Scale->y = uniformScale; - _selected->Scale->z = uniformScale; - break; - } + // Lower scaling sensitivity + diff = diff * 0.01f; + + switch (SelectedHandle) { + case GizmoHandle::X_Axis: + obj->SetScale( + FVector( + (InitialScale.x + -diff.x), + scale.y, + scale.z + ) + ); + break; + case GizmoHandle::Y_Axis: + obj->SetScale( + FVector( + scale.x, + (InitialScale.y + diff.y), + scale.z + ) + ); + break; + case GizmoHandle::Z_Axis: + obj->SetScale( + FVector( + scale.x, + scale.y, + (InitialScale.z + -diff.z) + ) + ); + break; + case GizmoHandle::All_Axis: + float uniformScale = (diff.x - diff.y - diff.z) / 3.0f; + uniformScale *= 1.8; // Increased sensitivity + obj->SetScale( + FVector( + uniformScale, + uniformScale, + uniformScale + ) + ); + break; + } + // Pass the _selected object into this lambda function + }, _selected); } void Gizmo::Draw() { if (Enabled) { DrawHandles(); - //DebugCollision(&RedCollision, Pos, {0, 0, 0}, {0.05f, 0.05f, 0.05f}, RedCollision.Triangles); - //DebugCollision(&BlueCollision, Pos, {90, 0, 0}, {0.05f, 0.05f, 0.05f}, BlueCollision.Triangles); - //DebugCollision(&GreenCollision, Pos, {0, 90, 0}, {0.05f, 0.05f, 0.05f}, GreenCollision.Triangles); - //DebugCollision(&RedRotateCollision, Pos, {0, 0, 0}, {0.15f, 0.15f, 0.15f}, RedRotateCollision.Triangles); + // DebugCollision(&RedCollision, Pos, {0, 0, 0}, {0.05f, 0.05f, 0.05f}, RedCollision.Triangles); + // DebugCollision(&BlueCollision, Pos, {90, 0, 0}, {0.05f, 0.05f, 0.05f}, BlueCollision.Triangles); + // DebugCollision(&GreenCollision, Pos, {0, 90, 0}, {0.05f, 0.05f, 0.05f}, GreenCollision.Triangles); + // DebugCollision(&RedRotateCollision, Pos, {0, 0, 0}, {0.15f, 0.15f, 0.15f}, RedRotateCollision.Triangles); //DebugCollision((uintptr_t)_selected, Pos, BlueRotateCollision.Triangles); //DebugCollision((uintptr_t)_selected, Pos, GreenRotateCollision.Triangles); } @@ -316,11 +398,12 @@ void Gizmo::DrawHandles() { Editor_AddMatrix(mainMtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); if (center) { + Camera* camera = gEditor.eCamera; Mat4 CenterMtx; Editor_MatrixIdentity(CenterMtx); // Calculate camera-to-object distance - FVector cameraDir = FVector(Pos.x - cameras[0].pos[0], Pos.y - cameras[0].pos[1], Pos.z - cameras[0].pos[2]); + FVector cameraDir = FVector(Pos.x - camera->pos[0], Pos.y - camera->pos[1], Pos.z - camera->pos[2]); cameraDir = cameraDir.Normalize(); IRotator centerRot; diff --git a/src/engine/editor/Gizmo.h b/src/engine/editor/Gizmo.h index 47ef99549d..f98b634b16 100644 --- a/src/engine/editor/Gizmo.h +++ b/src/engine/editor/Gizmo.h @@ -4,6 +4,9 @@ #include #include "Collision.h" #include "GameObject.h" +#include "engine/Actor.h" +#include "engine/objects/Object.h" +#include namespace Editor { @@ -28,13 +31,13 @@ class Gizmo { void Draw(); void Load(); - void SetGizmo(GameObject* object, Ray ray); - void SetGizmoNoCursor(GameObject* object); // Used for scene explorer selection + void SetGizmo(const std::variant& object, Ray ray); + void SetGizmoNoCursor(const std::variant& object); // Used for scene explorer selection void Translate(); void Rotate(); void Scale(); void DrawHandles(); - f32 SnapToSurface(FVector* pos); + f32 SnapToSurface(FVector pos); struct TrackDimensions { s16 MinX = -10000; @@ -78,7 +81,7 @@ class Gizmo { float HandleSize = 2.0f; FVector _ray; - GameObject* _selected = nullptr; + std::variant _selected; private: bool _draw = false; }; diff --git a/src/engine/editor/Handles.cpp b/src/engine/editor/Handles.cpp index 9074a2b459..532af6af6e 100644 --- a/src/engine/editor/Handles.cpp +++ b/src/engine/editor/Handles.cpp @@ -5,8 +5,6 @@ namespace Editor { Handles::Handles() { - Pos = &pos; - Rot = &rot; } void Handles::Load() { diff --git a/src/engine/editor/Handles.h b/src/engine/editor/Handles.h index 1faa4b59e3..ecd8f8f907 100644 --- a/src/engine/editor/Handles.h +++ b/src/engine/editor/Handles.h @@ -13,7 +13,5 @@ namespace Editor { virtual void Draw() override; virtual void Load() override; - FVector pos; - IRotator rot; }; } diff --git a/src/engine/editor/Light.cpp b/src/engine/editor/Light.cpp index 594383bd35..abd9ee85e8 100644 --- a/src/engine/editor/Light.cpp +++ b/src/engine/editor/Light.cpp @@ -4,6 +4,7 @@ #include "../CoreMath.h" #include #include "../World.h" +#include "engine/Matrix.h" #include "Light.h" #include "port/Engine.h" @@ -30,14 +31,16 @@ namespace Editor { size_t LightObject::NumLights = 0; - LightObject::LightObject(const char* name, FVector* pos, s8* direction) : GameObject(nullptr, nullptr) { + LightObject::LightObject(const char* name, FVector* pos, s8* direction) { Name = name; - Pos = &LightPos; - Rot = &LightRot; - Scale = &LightScale; + ResourceName = "editor:light"; - DespawnFlag = &_despawnFlag; - DespawnValue = -1; + Pos = FVector(0, 100, 0); + Rot = IRotator(0, 0, 0); + Scale = FVector(0.1, 0.1, 0.1); + + SpawnPos = Pos; + SpawnRot = Rot; Direction = direction; @@ -51,9 +54,10 @@ size_t LightObject::NumLights = 0; } void LightObject::Tick() { - SetDirectionFromRotator(*Rot, Direction); + SetDirectionFromRotator(Rot, Direction); } void LightObject::Draw() { + Camera* camera = gEditor.eCamera; Mat4 mtx_sun; Editor_MatrixIdentity(mtx_sun); gSPSetGeometryMode(gDisplayListHead++, G_SHADING_SMOOTH); @@ -61,23 +65,22 @@ size_t LightObject::NumLights = 0; // Calculate camera-to-object distance - FVector cameraDir = FVector(LightPos.x - cameras[0].pos[0], LightPos.y - cameras[0].pos[1], LightPos.z - cameras[0].pos[2]); + FVector cameraDir = FVector(Pos.x - camera->pos[0], Pos.y - camera->pos[1], Pos.z - camera->pos[2]); cameraDir = cameraDir.Normalize(); IRotator centerRot; SetRotatorFromDirection(cameraDir, ¢erRot); - // Account for object not facing the correct direction when exported + // The sun was exported facing the wrong direction. + // Thus, force the sun texture to face the camera. centerRot.yaw += 0x4000; - ApplyMatrixTransformations(mtx_sun, LightPos, centerRot, LightScale); + ApplyMatrixTransformations(mtx_sun, Pos, centerRot, Scale); Editor_AddMatrix(mtx_sun, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(gDisplayListHead++, sun_LightModel_mesh); // Draw Arrow Mat4 mtx_arrow; - IRotator rot = LightRot; - rot.yaw += 0x4000; - ApplyMatrixTransformations(mtx_arrow, LightPos, rot, LightScale); + ApplyMatrixTransformations(mtx_arrow, Pos, Rot, Scale); Editor_AddMatrix(mtx_arrow, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(gDisplayListHead++, (Gfx*)"__OTR__editor/light/sun_arrow"); } diff --git a/src/engine/editor/Light.h b/src/engine/editor/Light.h index 972942663c..3a177053e2 100644 --- a/src/engine/editor/Light.h +++ b/src/engine/editor/Light.h @@ -3,7 +3,6 @@ #include #include #include "Collision.h" -#include "Gizmo.h" #include "GameObject.h" namespace Editor { @@ -17,11 +16,7 @@ class LightObject : public GameObject { virtual void Load() override; static size_t NumLights; - FVector LightPos = FVector(0, 100, 0); - IRotator LightRot = IRotator(0, 0, 0); - FVector LightScale = FVector(0.1, 0.1, 0.1); s8* Direction; - s32 _despawnFlag = 0; u8 sun_sun_rgba32[16384] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, diff --git a/src/engine/editor/ObjectPicker.cpp b/src/engine/editor/ObjectPicker.cpp index 9cfeed5f81..a59cd507a4 100644 --- a/src/engine/editor/ObjectPicker.cpp +++ b/src/engine/editor/ObjectPicker.cpp @@ -2,7 +2,9 @@ #include #include "../CoreMath.h" #include -#include "../World.h" +#include "engine/World.h" +#include "engine/Actor.h" +#include "engine/objects/Object.h" #include "ObjectPicker.h" #include "port/Engine.h" @@ -33,8 +35,9 @@ void ObjectPicker::Tick() { } void ObjectPicker::SelectObject(std::vector objects) { + Camera* camera = gEditor.eCamera; Ray ray; - ray.Origin = FVector(cameras[0].pos[0], cameras[0].pos[1], cameras[0].pos[2]); + ray.Origin = FVector(camera->pos[0], camera->pos[1], camera->pos[2]); // This allows selection of objects in the scene explorer. // Otherwise this would still run when selecting buttons in editor windows. @@ -43,22 +46,23 @@ void ObjectPicker::SelectObject(std::vector objects) { ObjectPicker::FindObject(ray, objects); - if (_selected != nullptr) { - eGizmo.SetGizmo(_selected, ray); - eGizmo.Enabled = true; - } else { - //eGizmo.Disable(); - eGizmo.Enabled = false; - eGizmo._selected = nullptr; - } + std::visit([this, ray](auto* obj) { + if (obj) { + eGizmo.SetGizmo(_selected, ray); + eGizmo.Enabled = true; + } else { + eGizmo.Enabled = false; + _selected = static_cast(nullptr); + } + }, _selected); } } void ObjectPicker::DragHandle() { + Camera* camera = gEditor.eCamera; Ray ray; - ray.Origin = FVector(cameras[0].pos[0], cameras[0].pos[1], cameras[0].pos[2]); + ray.Origin = FVector(camera->pos[0], camera->pos[1], camera->pos[2]); ray.Direction = ScreenRayTrace(); - // Skip if a drag is already in progress if (eGizmo.SelectedHandle != Gizmo::GizmoHandle::None) { eGizmo._ray = ray.Direction; @@ -116,7 +120,6 @@ void ObjectPicker::DragHandle() { break; } - if (closestHandle != Gizmo::GizmoHandle::None && closestClickPos.has_value()) { eGizmo.SelectedHandle = closestHandle; eGizmo._ray = ray.Direction; @@ -126,32 +129,68 @@ void ObjectPicker::DragHandle() { } void ObjectPicker::Draw() { - if (_selected != NULL) { - eGizmo.Draw(); - } + std::visit([](auto* obj) { + if (obj) { + gEditor.eObjectPicker.eGizmo.Draw(); + } + }, _selected); if (Debug) { + Camera* camera = gEditor.eCamera; Mat4 CursorMtx; IRotator rot = IRotator(0,0,0); - FVector scale = FVector(0.1, 0.1, 0.1); + FVector scale = FVector(1, 1, 1); FVector ray = ScreenRayTrace(); - float x = (cameras[0].pos[0] + ray.x * 800); - float y = (cameras[0].pos[1] + ray.y * 800); - float z = (cameras[0].pos[2] + ray.z * 800); + float x = (camera->pos[0] + ray.x * 800); + float y = (camera->pos[1] + ray.y * 800); + float z = (camera->pos[2] + ray.z * 800); ApplyMatrixTransformations((float(*)[4])&CursorMtx, FVector(x, y, z), rot, scale); Editor_AddMatrix((float(*)[4])&CursorMtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); - gSPDisplayList(gDisplayListHead++, (Gfx*)"__OTR__tracks/sphere"); + gSPDisplayList(gDisplayListHead++, (Gfx*)"__OTR__gizmo/gizmo_center_button"); } } void ObjectPicker::FindObject(Ray ray, std::vector objects) { - bool found = false; - GameObject* closestObject = nullptr; - float closestDistance = FLT_MAX; + float distance = FLT_MAX; + std::variant object; + + _selected = static_cast(nullptr); - for (auto& object : objects) { + auto [hitActor, hitActorDist] = ObjectPicker::CheckAActorRay(ray); + if (hitActor) { + object = hitActor; + distance = hitActorDist; + } + + // OObjects + auto [hitObject, hitObjectDist] = ObjectPicker::CheckOObjectRay(ray); + if (hitObject && (hitObjectDist < distance)) { + object = hitObject; + distance = hitObjectDist; + } + + // Editor objects + auto [hitEditorObject, hitEditorObjectDist] = ObjectPicker::CheckEditorObjectRay(ray); + if (hitEditorObject && (hitEditorObjectDist < distance)) { + object = hitEditorObject; + distance = hitEditorObjectDist; + } + + // Set _selected from object variant + _selected = object; + std::visit([this](auto* obj) { + if (obj) { + } + }, object); +} + +std::pair ObjectPicker::CheckEditorObjectRay(Ray ray) { + GameObject* hitObject = nullptr; + float hitDistance = FLT_MAX; + + for (auto& object : gEditor.eGameObjects) { float boundingBox = object->BoundingBoxSize; if (boundingBox == 0.0f) { boundingBox = 2.0f; @@ -161,11 +200,10 @@ void ObjectPicker::FindObject(Ray ray, std::vector objects) { case GameObject::CollisionType::VTX_INTERSECT: for (const auto& tri : object->Triangles) { float t; - if (IntersectRayTriangleAndTransform(ray, *object->Pos, tri, t)) { - if (t < closestDistance) { - closestDistance = t; - closestObject = object; - printf("SELECTED OBJECT\n"); + if (IntersectRayTriangleAndTransform(ray, object->Pos, tri, t)) { + if (t < hitDistance) { + hitDistance = t; + hitObject = object; } } } @@ -173,18 +211,18 @@ void ObjectPicker::FindObject(Ray ray, std::vector objects) { case GameObject::CollisionType::BOUNDING_BOX: { float max = 2.0f; float min = -2.0f; - Vec3f boxMin = { object->Pos->x + boundingBox * min, - object->Pos->y + boundingBox * min, - object->Pos->z + boundingBox * min }; + Vec3f boxMin = { object->Pos.x + boundingBox * min, + object->Pos.y + boundingBox * min, + object->Pos.z + boundingBox * min }; - Vec3f boxMax = { object->Pos->x + boundingBox * max, - object->Pos->y + boundingBox * max, - object->Pos->z + boundingBox * max }; + Vec3f boxMax = { object->Pos.x + boundingBox * max, + object->Pos.y + boundingBox * max, + object->Pos.z + boundingBox * max }; float t; if (QueryCollisionRayActor(&ray.Origin.x, &ray.Direction.x, boxMin, boxMax, &t)) { - if (t < closestDistance) { - closestDistance = t; - closestObject = object; + if (t < hitDistance) { + hitDistance = t; + hitObject = object; printf("FOUND BOUNDING BOX OBJECT\n"); } break; @@ -196,12 +234,62 @@ void ObjectPicker::FindObject(Ray ray, std::vector objects) { break; } } - if (closestObject != nullptr) { - _selected = closestObject; - // printf("FOUND COLLISION %d\n", type); - } else { - // printf("NO COLLISION\n"); - _selected = nullptr; + return std::pair(hitObject, hitDistance); +} + +std::pair ObjectPicker::CheckOObjectRay(Ray ray) { + OObject* hitObject = nullptr; + float hitDistance = FLT_MAX; + + + return std::pair(hitObject, hitDistance); +} + +std::pair ObjectPicker::CheckAActorRay(Ray ray) { + AActor* hitActor = nullptr; + float hitDistance = FLT_MAX; + + for (auto actor : gWorldInstance.Actors) { + if ((actor->bPendingDestroy) && (!actor->IsMod())) { + continue; + } + + float boundingBox = actor->BoundingBoxSize; + if (boundingBox == 0.0f) { + boundingBox = 2.0f; + } + + if (actor->Triangles.size()) { + for (const auto& tri : actor->Triangles) { + float t; + if (IntersectRayTriangleAndTransform(ray, FVector(actor->Pos[0], actor->Pos[1], actor->Pos[2]), tri, t)) { + if (t < hitDistance) { + hitDistance = t; + hitActor = static_cast(actor); + } + } + } + } else { + float max = 1.2f; + float min = -1.2f; + Vec3f boxMin = { actor->Pos[0] + boundingBox * min, + actor->Pos[1] + boundingBox * min, + actor->Pos[2] + boundingBox * min }; + + Vec3f boxMax = { actor->Pos[0] + boundingBox * max, + actor->Pos[1] + boundingBox * max, + actor->Pos[2] + boundingBox * max }; + float t; + if (QueryCollisionRayActor(&ray.Origin.x, &ray.Direction.x, boxMin, boxMax, &t)) { + if (t < hitDistance) { + hitDistance = t; + hitActor = static_cast(actor); + } + } + } } + + return std::pair(hitActor, hitDistance); } + } diff --git a/src/engine/editor/ObjectPicker.h b/src/engine/editor/ObjectPicker.h index e96bddcbc8..0dbdca2150 100644 --- a/src/engine/editor/ObjectPicker.h +++ b/src/engine/editor/ObjectPicker.h @@ -5,6 +5,7 @@ #include "Collision.h" #include "Gizmo.h" #include "GameObject.h" +#include "engine/Matrix.h" namespace Editor { class ObjectPicker { @@ -16,13 +17,17 @@ namespace Editor { void Load(); void Tick(); Gizmo eGizmo; - GameObject* _selected; + std::variant _selected; private: bool _draw = false; GameObject* _lastSelected; s32 Inverse(MtxF* src, MtxF* dest); void Copy(MtxF* src, MtxF* dest); void Clear(MtxF* mf); + // actor, distance from camera + std::pair CheckAActorRay(Ray ray); + std::pair CheckOObjectRay(Ray ray); + std::pair CheckEditorObjectRay(Ray ray); bool Debug = false; }; } diff --git a/src/engine/editor/SceneManager.cpp b/src/engine/editor/SceneManager.cpp index ae41f2f1d4..bd34144741 100644 --- a/src/engine/editor/SceneManager.cpp +++ b/src/engine/editor/SceneManager.cpp @@ -1,11 +1,12 @@ #include "SceneManager.h" #include "port/Game.h" -#include "CoreMath.h" +#include "engine/CoreMath.h" #include "World.h" #include "GameObject.h" #include #include +#include // Must be before json.hpp #include #include "port/Engine.h" #include @@ -13,6 +14,19 @@ #include #include "port/resource/type/ResourceType.h" +#include "engine/vehicles/Train.h" + +#include "engine/objects/Object.h" +#include "engine/objects/Thwomp.h" +#include "engine/objects/Snowman.h" +#include + +extern "C" { +#include "common_structs.h" +#include "actors.h" +#include "actor_types.h" +} + namespace Editor { std::shared_ptr CurrentArchive; @@ -33,45 +47,42 @@ namespace Editor { } data["StaticMeshActors"] = staticMesh; - // nlohmann::json actors; - - // for (const auto& actor : gWorldInstance.Actors) { - // actors.push_back(actor->to_json()); - // } - // data["Actors"] = actors; + nlohmann::json actors; - // nlohmann::json objects; + SaveActors(actors); - // for (const auto& object : gWorldInstance.Objects) { - // objects.push_back(object->to_json()); - // } - // data["Objects"] = objects; + data["Actors"] = actors; try { - auto dat = data.dump(); + auto dat = data.dump(2); std::vector stringify; stringify.assign(dat.begin(), dat.end()); bool wrote = GameEngine::Instance->context->GetResourceManager()->GetArchiveManager()->WriteFile(CurrentArchive, SceneFile, stringify); if (wrote) { - printf("Successfully wrote scene file!\n Wrote: %s\n", SceneFile.c_str()); + // Tell the cache this needs to be reloaded + auto resource = GameEngine::Instance->context->GetResourceManager()->GetCachedResource(SceneFile); + if (resource) { + resource->Dirty(); + } } else { - printf("Failed to write scene file!\n"); + printf("[SceneManager::SaveLevel] Failed to write scene file!\n"); } } catch (const nlohmann::json::exception& e) { - printf("SceneManager::SaveLevel():\n JSON error during dump: %s\n", e.what()); + printf("[SceneManager::SaveLevel]\n JSON error during dump: %s\n", e.what()); } } else { printf("Could not save scene file, SceneFile or CurrentArchive not set\n"); } } - void LoadLevel(std::shared_ptr archive, Course* course, std::string sceneFile) { - SceneFile = sceneFile; - if (archive && (course != nullptr)) { + /** Do not use gWorldInstance.CurrentCourse during loading! The current track is not guaranteed! **/ + void LoadLevel(Course* course, std::string sceneFile) { + SceneFile = sceneFile; + if ((nullptr != course) && (nullptr != course->RootArchive)) { auto initData = std::make_shared(); - initData->Parent = archive; + initData->Parent = course->RootArchive; initData->Format = RESOURCE_FORMAT_BINARY; initData->ByteOrder = Ship::Endianness::Little; initData->Type = static_cast(Ship::ResourceType::Json); @@ -97,6 +108,20 @@ namespace Editor { std::cerr << "Props data not found in the JSON file!" << std::endl; } + /** Populate Track SpawnParams for spawning actors **/ + if (data.contains("Actors")) { + auto & actorsJson = data["Actors"]; + course->SpawnList.clear(); + for (const auto& actor : actorsJson) { + SpawnParams params; + params.from_json(actor); //(); + if (!params.Name.empty()) { + course->SpawnList.push_back(params); + } + } + SPDLOG_INFO("[SceneManager] Loaded Scene File!"); + } + // Load the Actors (deserialize them) if (data.contains("StaticMeshActors")) { auto& actorsJson = data["StaticMeshActors"]; @@ -106,7 +131,7 @@ namespace Editor { Load_AddStaticMeshActor(actorJson); } } else { - std::cerr << "Actors data not found in the JSON file!" << std::endl; + SPDLOG_INFO("[SceneManager::LoadLevel] [scene.json] This track contains no StaticMeshActors!"); } } } @@ -118,8 +143,6 @@ namespace Editor { 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, &actor->Rot, &actor->Scale, (Gfx*) nullptr, 1.0f, - GameObject::CollisionType::BOUNDING_BOX, 20.0f, (int32_t*) &actor->bPendingDestroy, (int32_t) 1); } void SetSceneFile(std::shared_ptr archive, std::string sceneFile) { @@ -127,11 +150,11 @@ namespace Editor { SceneFile = sceneFile; } - void LoadMinimap(std::shared_ptr archive, Course* course, std::string filePath) { + void LoadMinimap(Course* course, std::string filePath) { printf("LOADING MINIMAP %s\n", filePath.c_str()); - if (archive) { + if ((nullptr != course) && (nullptr != course->RootArchive)) { auto initData = std::make_shared(); - initData->Parent = archive; + initData->Parent = course->RootArchive; initData->Format = RESOURCE_FORMAT_BINARY; initData->ByteOrder = Ship::Endianness::Little; initData->Type = static_cast(MK64::ResourceType::Minimap); @@ -153,4 +176,71 @@ namespace Editor { } } } + + void SaveActors(nlohmann::json& actorList) { + for (const auto& actor : gWorldInstance.Actors) { + SpawnParams params{}; + bool alreadyProcessed = false; + + // Only some actors are supported for saving. + // Bananas and stuff don't make sense to be saved. + switch(actor->Type) { + case ACTOR_ITEM_BOX: + case ACTOR_FAKE_ITEM_BOX: + case ACTOR_TREE_MARIO_RACEWAY: + case ACTOR_TREE_YOSHI_VALLEY: + case ACTOR_TREE_ROYAL_RACEWAY: + case ACTOR_TREE_MOO_MOO_FARM: + case ACTOR_PALM_TREE: + case ACTOR_TREE_LUIGI_RACEWAY: // A plant? + case ACTOR_UNKNOWN_0x1B: + case ACTOR_TREE_PEACH_CASTLE: + case ACTOR_TREE_FRAPPE_SNOWLAND: + case ACTOR_CACTUS1_KALAMARI_DESERT: + case ACTOR_CACTUS2_KALAMARI_DESERT: + case ACTOR_CACTUS3_KALAMARI_DESERT: + case ACTOR_BUSH_BOWSERS_CASTLE: + params.Name = get_actor_resource_location_name(actor->Type); + params.Location = FVector(actor->Pos[0], actor->Pos[1], actor->Pos[2]); + if (!params.Name.empty()) { + actorList.push_back(params.to_json()); + } + alreadyProcessed = true; + break; + case ACTOR_PIRANHA_PLANT: + params.Name = get_actor_resource_location_name(actor->Type); + params.Location = FVector(actor->Pos[0], actor->Pos[1], actor->Pos[2]); + // params.Type = // Need this to use royal raceway version + actorList.push_back(params.to_json()); + alreadyProcessed = true; + break; + case ACTOR_YOSHI_EGG: + params.Name = get_actor_resource_location_name(actor->Type); + params.Location = FVector(actor->Velocity[0], actor->Pos[1], actor->Velocity[2]); // Velocity is pathCenter + if (!params.Name.empty()) { + actorList.push_back(params.to_json()); + } + alreadyProcessed = true; + break; + } + + if (!alreadyProcessed) { + actor->SetSpawnParams(params); + if (!params.Name.empty()) { + actorList.push_back(params.to_json()); + } + } + } + + for (const auto& object : gWorldInstance.Objects) { + SpawnParams params; + object->SetSpawnParams(params); + + // Unimplemented objects should not be added to the SpawnList + // The name field is required. If not set, then its not implemented yet. + if (!params.Name.empty()) { + actorList.push_back(params.to_json()); + } + } + } } diff --git a/src/engine/editor/SceneManager.h b/src/engine/editor/SceneManager.h index 58699107e8..6a6644a714 100644 --- a/src/engine/editor/SceneManager.h +++ b/src/engine/editor/SceneManager.h @@ -1,13 +1,21 @@ +#pragma once + #include +#include "CoreMath.h" #include "engine/courses/Course.h" +#include +#include namespace Editor { - void SaveLevel(); - void LoadLevel(std::shared_ptr archive, Course* course, std::string sceneFile); - void Load_AddStaticMeshActor(const nlohmann::json& actorJson); - void SetSceneFile(std::shared_ptr archive, std::string sceneFile); - void LoadMinimap(std::shared_ptr archive, Course* course, std::string filePath); - - extern std::shared_ptr CurrentArchive; // This is used to retrieve and write the scene data file - extern std::string SceneFile; + void SaveLevel(); + void LoadLevel(Course* course, std::string sceneFile); + void Load_AddStaticMeshActor(const nlohmann::json& actorJson); + void SetSceneFile(std::shared_ptr archive, std::string sceneFile); + void LoadMinimap(Course* course, std::string filePath); + + void SaveActors(nlohmann::json& actorList); + void SpawnActors(std::vector> spawnList); + + extern std::shared_ptr CurrentArchive; // This is used to retrieve and write the scene data file + extern std::string SceneFile; } diff --git a/src/engine/objects/Bat.cpp b/src/engine/objects/Bat.cpp index e4f3a2086a..25c28c7518 100644 --- a/src/engine/objects/Bat.cpp +++ b/src/engine/objects/Bat.cpp @@ -19,8 +19,13 @@ const char* sBoardwalkTexList[] = { gTextureBat1, gTextureBat2, gTextureBat3, gT size_t OBat::_count = 0; -OBat::OBat(const FVector& pos, const IRotator& rot) { +OBat::OBat(const SpawnParams& params) : OObject(params) { Name = "Bat"; + ResourceName = "mk:bat"; + + //! @warning this likely needs to be rot.Set() + IRotator rot = params.Rotation.value_or(IRotator(0, 0, 0)); + find_unused_obj_index(&_objectIndex); init_texture_object(_objectIndex, (uint8_t*) d_course_banshee_boardwalk_bat_tlut, sBoardwalkTexList, 0x20U, diff --git a/src/engine/objects/Bat.h b/src/engine/objects/Bat.h index d6b7e8774d..75318e5d84 100644 --- a/src/engine/objects/Bat.h +++ b/src/engine/objects/Bat.h @@ -28,7 +28,18 @@ extern "C" { */ class OBat : public OObject { public: - explicit OBat(const FVector& pos, const IRotator& rot); + + // This is simply a helper function to keep Spawning code clean + static inline OBat* Spawn(const FVector& pos, const IRotator& rot) { + SpawnParams params = { + .Name = "mk:bat", + .Location = pos, + .Rotation = rot, + }; + return static_cast(gWorldInstance.AddObject(new OBat(params))); + } + + explicit OBat(const SpawnParams& params); ~OBat() { _count--; diff --git a/src/engine/objects/BombKart.cpp b/src/engine/objects/BombKart.cpp index 78d4762a99..c465aea43e 100644 --- a/src/engine/objects/BombKart.cpp +++ b/src/engine/objects/BombKart.cpp @@ -29,52 +29,61 @@ extern s8 gPlayerCount; size_t OBombKart::_count = 0; -OBombKart::OBombKart(FVector pos, TrackPathPoint* waypoint, uint16_t waypointIndex, uint16_t state, f32 unk_3C) { +OBombKart::OBombKart(const SpawnParams& params) : OObject(params) { Name = "Bomb Kart"; + ResourceName = "mk:bomb_kart"; + _idx = _count; - Vec3f _pos = {0, 0, 0}; + uint32_t pathIndex = params.PathIndex.value_or(0); + uint32_t pathPoint = params.PathPoint.value_or(0); + FVector constPos; - if (waypoint) { // Spawn kart on waypoint - _pos[0] = waypoint->posX; - _pos[1] = waypoint->posY; - _pos[2] = waypoint->posZ; - } else { // Spawn kart on a surface with the provided position + // Spawn kart on a surface with the provided position + if (params.Location.has_value()) { + constPos = params.Location.value(); // Set height to the default value of 2000.0f unless Pos[1] is higher. // This allows placing these on very high surfaces. - f32 height = (pos.y > 2000.0f) ? pos.y : 2000.0f; - _pos[0] = pos.x; - _pos[1] = spawn_actor_on_surface(pos.x, height, pos.z); - _pos[2] = pos.z; + f32 height = (constPos.y > 2000.0f) ? constPos.y : 2000.0f; + constPos.y = spawn_actor_on_surface(constPos.x, height, constPos.z); + } else { // Spawn kart on waypoint + constPos.x = gTrackPaths[pathIndex][pathPoint].x; + constPos.y = gTrackPaths[pathIndex][pathPoint].y; + constPos.z = gTrackPaths[pathIndex][pathPoint].z; } - WaypointIndex = waypointIndex; - Unk_3C = unk_3C; - State = static_cast(state); - - Pos[0] = _pos[0]; - Pos[1] = _pos[1]; - Pos[2] = _pos[2]; - _spawnPos[0] = _pos[0]; - _spawnPos[1] = _pos[1]; - _spawnPos[2] = _pos[2]; - CenterY = _pos[1]; - WheelPos[0][0] = _pos[0]; - WheelPos[0][1] = _pos[1]; - WheelPos[0][2] = _pos[2]; - WheelPos[1][0] = _pos[0]; - WheelPos[1][1] = _pos[1]; - WheelPos[1][2] = _pos[2]; - WheelPos[2][0] = _pos[0]; - WheelPos[2][1] = _pos[1]; - WheelPos[2][2] = _pos[2]; - WheelPos[3][0] = _pos[0]; - WheelPos[3][1] = _pos[1]; - WheelPos[3][2] = _pos[2]; - check_bounding_collision(&_Collision, 2.0f, _pos[0], _pos[1], _pos[2]); + Behaviour = static_cast(params.Behaviour.value_or(OBombKart::States::COUNTERCLOCKWISE)); + SpeedB = params.SpeedB.value_or(2.7f); // Chase speed + + WaypointIndex = params.PathPoint.value_or(0); + Unk_3C = params.Speed.value_or(0); + + Pos[0] = constPos.x; + Pos[1] = constPos.y; + Pos[2] = constPos.z; + CenterY = constPos.y; + WheelPos[0][0] = constPos.x; + WheelPos[0][1] = constPos.y; + WheelPos[0][2] = constPos.z; + WheelPos[1][0] = constPos.x; + WheelPos[1][1] = constPos.y; + WheelPos[1][2] = constPos.z; + WheelPos[2][0] = constPos.x; + WheelPos[2][1] = constPos.y; + WheelPos[2][2] = constPos.z; + WheelPos[3][0] = constPos.x; + WheelPos[3][1] = constPos.y; + WheelPos[3][2] = constPos.z; + check_bounding_collision(&_Collision, 2.0f, constPos.x, constPos.y, constPos.z); find_unused_obj_index(&_objectIndex); + Object* object = &gObjectList[_objectIndex]; + + object->origin_pos[0] = Pos[0]; + object->origin_pos[1] = Pos[1]; + object->origin_pos[2] = Pos[2]; + _count++; } @@ -103,7 +112,7 @@ void OBombKart::Tick() { f32 sp94; f32 sp88; Vec3f newPos; - States state; + OBombKart::States state; u16 bounceTimer; UNUSED u16 sp4C; u16 temp_t6; @@ -112,7 +121,7 @@ void OBombKart::Tick() { TrackPathPoint* temp_v0_4; Player* player; - state = State; + state = Behaviour; if (state == States::DISABLED) { return; @@ -137,6 +146,7 @@ void OBombKart::Tick() { if ((((temp_f0 * temp_f0) + (temp_f2 * temp_f2)) + (temp_f12 * temp_f12)) < 25.0f) { circleTimer = 0; state = States::EXPLODE; + Behaviour = States::EXPLODE; player->soundEffects |= 0x400000; player->type &= ~0x2000; } @@ -151,6 +161,7 @@ void OBombKart::Tick() { temp_f12 = newPos[2] - player->pos[2]; if ((((temp_f0 * temp_f0) + (temp_f2 * temp_f2)) + (temp_f12 * temp_f12)) < 25.0f) { state = States::EXPLODE; + Behaviour = States::EXPLODE; circleTimer = 0; if (IsFrappeSnowland()) { player->soundEffects |= 0x01000000; @@ -163,44 +174,44 @@ void OBombKart::Tick() { } } switch(state) { - case States::CCW: + case States::COUNTERCLOCKWISE: circleTimer = (circleTimer + 356) % 360; temp_t6 = (circleTimer * 0xFFFF) / 360; sp118 = coss(temp_t6) * 25.0; temp_f0_3 = sins(temp_t6) * 25.0; temp_v0_2 = &gTrackPaths[0][waypoint]; - newPos[0] = temp_v0_2->posX + sp118; + newPos[0] = temp_v0_2->x + sp118; newPos[1] = CenterY + 3.5f; - newPos[2] = temp_v0_2->posZ + temp_f0_3; + newPos[2] = temp_v0_2->z + temp_f0_3; D_80162FB0[0] = newPos[0]; D_80162FB0[1] = newPos[1]; D_80162FB0[2] = newPos[2]; temp_t7 = (((circleTimer + 1) % 360) * 0xFFFF) / 360; sp118 = coss(temp_t7) * 25.0; temp_f0_3 = sins(temp_t7) * 25.0; - D_80162FC0[0] = temp_v0_2->posX + sp118; - D_80162FC0[1] = temp_v0_2->posY; - D_80162FC0[2] = temp_v0_2->posZ + temp_f0_3; + D_80162FC0[0] = temp_v0_2->x + sp118; + D_80162FC0[1] = temp_v0_2->y; + D_80162FC0[2] = temp_v0_2->z + temp_f0_3; someRot = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; break; - case States::CW: + case States::CLOCKWISE: circleTimer = (circleTimer + 4) % 360; temp_t6 = (circleTimer * 0xFFFF) / 360; sp118 = coss(temp_t6) * 25.0; temp_f0_3 = sins(temp_t6) * 25.0; temp_v0_2 = &gTrackPaths[0][waypoint]; - newPos[0] = temp_v0_2->posX + sp118; + newPos[0] = temp_v0_2->x + sp118; newPos[1] = CenterY + 3.5f; - newPos[2] = temp_v0_2->posZ + temp_f0_3; + newPos[2] = temp_v0_2->z + temp_f0_3; D_80162FB0[0] = newPos[0]; D_80162FB0[1] = newPos[1]; D_80162FB0[2] = newPos[2]; temp_t7 = (((circleTimer + 1) % 360) * 0xFFFF) / 360; sp118 = coss(temp_t7) * 25.0; temp_f0_3 = sins(temp_t7) * 25.0; - D_80162FC0[0] = temp_v0_2->posX + sp118; - D_80162FC0[1] = temp_v0_2->posY; - D_80162FC0[2] = temp_v0_2->posZ + temp_f0_3; + D_80162FC0[0] = temp_v0_2->x + sp118; + D_80162FC0[1] = temp_v0_2->y; + D_80162FC0[2] = temp_v0_2->z + temp_f0_3; someRot = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; break; case States::STATIONARY: @@ -218,13 +229,13 @@ void OBombKart::Tick() { } if (((s32) waypoint) < 0x1A) { temp_v0_2 = &gTrackPaths[3][(waypoint + 1) % gPathCountByPathIndex[3]]; - D_80162FB0[0] = temp_v0_2->posX; - D_80162FB0[1] = temp_v0_2->posY; - D_80162FB0[2] = temp_v0_2->posZ; + D_80162FB0[0] = temp_v0_2->x; + D_80162FB0[1] = temp_v0_2->y; + D_80162FB0[2] = temp_v0_2->z; temp_v0_4 = &gTrackPaths[3][(waypoint + 2) % gPathCountByPathIndex[3]]; - D_80162FC0[0] = temp_v0_4->posX; - D_80162FC0[1] = temp_v0_4->posY; - D_80162FC0[2] = temp_v0_4->posZ; + D_80162FC0[0] = temp_v0_4->x; + D_80162FC0[1] = temp_v0_4->y; + D_80162FC0[2] = temp_v0_4->z; someRot = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; } else { D_80162FB0[0] = newPos[0]; @@ -266,13 +277,13 @@ void OBombKart::Tick() { break; case States::EXPLODE: temp_v0_2 = &gTrackPaths[0][waypoint]; - D_80162FB0[0] = temp_v0_2->posX; - D_80162FB0[1] = temp_v0_2->posY; - D_80162FB0[2] = temp_v0_2->posZ; + D_80162FB0[0] = temp_v0_2->x; + D_80162FB0[1] = temp_v0_2->y; + D_80162FB0[2] = temp_v0_2->z; temp_v0_4 = &gTrackPaths[0][(waypoint + 1) % gPathCountByPathIndex[0]]; - D_80162FC0[0] = temp_v0_4->posX; - D_80162FC0[1] = temp_v0_4->posY; - D_80162FC0[2] = temp_v0_4->posZ; + D_80162FC0[0] = temp_v0_4->x; + D_80162FC0[1] = temp_v0_4->y; + D_80162FC0[2] = temp_v0_4->z; newPos[1] += 3.0f - (circleTimer * 0.3f); someRot = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; break; @@ -290,8 +301,9 @@ void OBombKart::Tick() { spA0 = temp_f2_4; sp94 = temp_f2_4; sp88 = temp_f2_4; - if (circleTimer >= 31) { + if (circleTimer > 30) { state = States::DISABLED; + Behaviour = States::DISABLED; } } else { sp118 = coss(0xFFFF - someRot) * 1.5f; @@ -326,7 +338,7 @@ void OBombKart::Tick() { WaypointIndex = waypoint; Unk_3C = unk_3C; SomeRot = someRot; - State = state; + // State = state; BounceTimer = bounceTimer; CircleTimer = circleTimer; } @@ -373,8 +385,8 @@ void OBombKart::Draw(s32 cameraId) { } // huh??? - s32 state = State; - if (State != States::DISABLED) { + OBombKart::States state = Behaviour; + if (state != States::DISABLED) { gObjectList[_objectIndex].pos[0] = Pos[0]; gObjectList[_objectIndex].pos[1] = Pos[1]; gObjectList[_objectIndex].pos[2] = Pos[2]; @@ -386,7 +398,7 @@ void OBombKart::Draw(s32 cameraId) { D_80183E80[2] = 0x8000; func_800563DC(_objectIndex, cameraId, 0x000000FF); OBombKart::SomeRender(camera->pos); - if (((u32) temp_s4 < 0x4E21U) && (state != BOMB_STATE_EXPLODED)) { + if (((u32) temp_s4 < 0x4E21U) && (state != OBombKart::States::EXPLODE)) { OBombKart::LoadMtx(); } } @@ -436,7 +448,9 @@ void OBombKart::Waypoint(s32 screenId) { playerWaypoint = gNearestPathPointByPlayerId[screenId]; playerHUD[screenId].unk_74 = 0; - if ((State == States::EXPLODE) || (State == States::DISABLED)) { return; }; + + OBombKart::States state = Behaviour; + if ((state == States::EXPLODE) || (state == States::DISABLED)) { return; }; bombWaypoint = WaypointIndex; waypointDiff = bombWaypoint - playerWaypoint; if ((waypointDiff < -5) || (waypointDiff > 0x1E)) { return; }; @@ -455,7 +469,7 @@ Player* OBombKart::FindTarget() { } void OBombKart::Chase(Player* player, Vec3f pos) { - const f32 speed = 2.7f; // Speed the kart uses in a chase + const f32 speed = SpeedB; // Speed the kart uses in a chase if (!player) return; // Ensure player is valid @@ -471,18 +485,18 @@ void OBombKart::Chase(Player* player, Vec3f pos) { // Reset distance if (xz_dist > 700.0f) { _target = NULL; - pos[0] = _spawnPos[0]; - pos[1] = _spawnPos[1]; - pos[2] = _spawnPos[2]; + pos[0] = gObjectList[_objectIndex].origin_pos[0]; + pos[1] = gObjectList[_objectIndex].origin_pos[1]; + pos[2] = gObjectList[_objectIndex].origin_pos[2]; return; } // Break off the chase if player has boo item if (player->effects & BOO_EFFECT) { _target = NULL; - pos[0] = _spawnPos[0]; - pos[1] = _spawnPos[1]; - pos[2] = _spawnPos[2]; + pos[0] = gObjectList[_objectIndex].origin_pos[0]; + pos[1] = gObjectList[_objectIndex].origin_pos[1]; + pos[2] = gObjectList[_objectIndex].origin_pos[2]; return; } @@ -509,4 +523,62 @@ void OBombKart::Chase(Player* player, Vec3f pos) { pos[2] = newPosition[2]; check_bounding_collision(&_Collision, 10.0f, pos[0], pos[1], pos[2]); -} \ No newline at end of file +} + +void OBombKart::Translate(FVector pos) { + Pos[0] = pos.x; + Pos[1] = pos.y; + Pos[2] = pos.z; + if (_objectIndex != -1) { + Object* object = &gObjectList[_objectIndex]; + object->pos[0] = pos.x; + object->pos[1] = pos.y; + object->pos[2] = pos.z; + object->origin_pos[0] = pos.x; + object->origin_pos[1] = pos.y; + object->origin_pos[2] = pos.z; + } else { + printf("Editor tried to translate null OObject\n"); + } +} + +void OBombKart::DrawEditorProperties() { + Object* obj = &gObjectList[_objectIndex]; + + ImGui::Text("Behaviour"); + ImGui::SameLine(); + + int32_t behaviour = static_cast(Behaviour); + const char* items[] = { "Disabled", "Counterclockwise", "Clockwise", "Stationary", "Chase", "Explode", "Podium" }; + + if (ImGui::Combo("##Behaviour", &behaviour, items, IM_ARRAYSIZE(items))) { + Behaviour = static_cast(behaviour); + } + + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = FVector(obj->pos[0], obj->pos[1], obj->pos[2]); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Chase Speed"); + ImGui::SameLine(); + + float speed = SpeedB; + if (ImGui::DragFloat("##Speed", &speed, 0.1f)) { + SpeedB = speed; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeed")) { + SpeedB = 2.7f; + } +} diff --git a/src/engine/objects/BombKart.h b/src/engine/objects/BombKart.h index 86c2c0ef37..20c8392a09 100644 --- a/src/engine/objects/BombKart.h +++ b/src/engine/objects/BombKart.h @@ -26,8 +26,8 @@ class OBombKart : public OObject { public: enum States : uint16_t { // 0,1,3,5 DISABLED, - CCW, - CW, + COUNTERCLOCKWISE, + CLOCKWISE, STATIONARY, CHASE, EXPLODE, @@ -49,8 +49,34 @@ class OBombKart : public OObject { f32 CenterY; // Center of the circle Collision _Collision; + + // This is simply a helper function to keep Spawning code clean + // Spawn object at a position + static inline OBombKart* Spawn(FVector pos, uint16_t behaviour, f32 unk_3C) { + SpawnParams params = { + .Name = "mk:bomb_kart", + .Behaviour = behaviour, + .Location = pos, + .Speed = unk_3C, // Only used for podium ceremony. Arbitrarily chose Speed for this + .SpeedB = 2.7f, // Chase speed + }; + return static_cast(gWorldInstance.AddObject(new OBombKart(params))); + } + + // Spawn object at a point along the tracks path + static inline OBombKart* Spawn(uint32_t pathIndex, uint32_t pathPoint, uint16_t behaviour, f32 unk_3C) { + SpawnParams params = { + .Name = "mk:bomb_kart", + .Behaviour = behaviour, + .PathIndex = pathIndex, + .PathPoint = pathPoint, + .Speed = unk_3C, // Only used for podium ceremony. Arbitrarily chose Speed for this + }; + return static_cast(gWorldInstance.AddObject(new OBombKart(params))); + } + // Set waypoint to NULL if using a spawn position and not a waypoint. - explicit OBombKart(FVector pos, TrackPathPoint* waypoint, uint16_t waypointIndex, uint16_t state, f32 unk_3C); + explicit OBombKart(const SpawnParams& params); ~OBombKart() { _count--; @@ -62,17 +88,20 @@ class OBombKart : public OObject { virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void Translate(FVector pos) override; + virtual void DrawEditorProperties() override; void DrawBattle(s32 cameraId); void SomeRender(Vec3f arg1); void LoadMtx(); void Waypoint(s32 screenId); + OBombKart::States Behaviour = OBombKart::States::COUNTERCLOCKWISE; + float SpeedB = 2.7f; private: static size_t _count; s32 _idx; Player* FindTarget(); void Chase(Player*, Vec3f pos); - Vec3f _spawnPos; Player* _target = NULL; }; diff --git a/src/engine/objects/Boos.cpp b/src/engine/objects/Boos.cpp index dbd380901c..a22b0edd01 100644 --- a/src/engine/objects/Boos.cpp +++ b/src/engine/objects/Boos.cpp @@ -23,12 +23,20 @@ extern "C" { size_t OBoos::_count = 0; -OBoos::OBoos(size_t numBoos, const IPathSpan& leftBoundary, const IPathSpan& active, const IPathSpan& rightBoundary) { +OBoos::OBoos(const SpawnParams& params) : OObject(params) { Name = "Boos"; + ResourceName = "mk:boos"; + + size_t numBoos = params.Count.value_or(5); + + ActiveZone = params.TriggerSpan.value_or(IPathSpan(30, 50)); + LeftTrigger = params.LeftExitSpan.value_or(IPathSpan(0, 10)); + RightTrigger = params.RightExitSpan.value_or(IPathSpan(80, 100)); + // Max five boos allowed due to limited splines // D_800E5D9C if (numBoos > 10) { - printf("Boos.cpp: Only 10 boos allowed.\n"); + printf("[Boos.cpp] Only 10 boos allowed.\n"); numBoos = 10; } @@ -40,9 +48,14 @@ OBoos::OBoos(size_t numBoos, const IPathSpan& leftBoundary, const IPathSpan& act } _numBoos = numBoos; - _leftBoundary = leftBoundary; - _active = active; - _rightBoundary = rightBoundary; +} + +void OBoos::SetSpawnParams(SpawnParams& params) { + OObject::SetSpawnParams(params); + params.Count = _numBoos; + params.LeftExitSpan = LeftTrigger; + params.TriggerSpan = ActiveZone; + params.RightExitSpan = RightTrigger; } void OBoos::Tick() { @@ -123,7 +136,8 @@ void OBoos::func_8007CA70(void) { if (_isActive == false) { _playerId = OBoos::func_8007C9F8(); point = &gNearestPathPointByPlayerId[_playerId]; - if ((*point > _active.Start) && (*point < _active.End)) { + + if ((*point > ActiveZone.Start) && (*point < ActiveZone.End)) { // First group entrance OBoos::BooStart(0, _playerId); } @@ -131,11 +145,14 @@ void OBoos::func_8007CA70(void) { if (_isActive == true) { point = &gNearestPathPointByPlayerId[_playerId]; - if ((*point > _leftBoundary.Start) && (*point < _leftBoundary.End)) { + // Left boundary + if ((*point > LeftTrigger.Start) && (*point < LeftTrigger.End)) { // First group exit reverse direction OBoos::BooExit(0); } - if ((*point > _rightBoundary.Start) && (*point < _rightBoundary.End)) { + + // Right boundary + if ((*point > RightTrigger.Start) && (*point < RightTrigger.End)) { // First group exit OBoos::BooExit(0); } @@ -274,3 +291,50 @@ void OBoos::BooExit(s32 group) { _isActive = false; } + +void OBoos::DrawEditorProperties() { + ImGui::Text("Num Boos"); + ImGui::SameLine(); + + int count = static_cast(_count); + if (ImGui::InputInt("##Count", &count)) { + // Clamp to uint32_t range (only lower bound needed if assuming positive values) + if (count < 0) count = 0; + if (count > 10) count = 10; + _count = static_cast(count); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetCount")) { + _count = 5; + } + + ImGui::Text("Left Exit Span"); + ImGui::SameLine(); + + if (ImGui::DragInt2("##LeftExitSpan", (int*)&LeftTrigger)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetLeftExitSpan")) { + LeftTrigger = IPathSpan(0, 0); + } + + ImGui::Text("Trigger Span"); + ImGui::SameLine(); + + if (ImGui::DragInt2("##TriggerSpan", (int*)&ActiveZone)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetTriggerSpan")) { + ActiveZone = IPathSpan(0, 0); + } + + ImGui::Text("Right Exit Span"); + ImGui::SameLine(); + + if (ImGui::DragInt2("##RightExitSpan", (int*)&RightTrigger)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetRightExitSpan")) { + RightTrigger = IPathSpan(0, 0); + } +} diff --git a/src/engine/objects/Boos.h b/src/engine/objects/Boos.h index cc49df4743..693405dcb1 100644 --- a/src/engine/objects/Boos.h +++ b/src/engine/objects/Boos.h @@ -36,7 +36,19 @@ extern "C" { */ class OBoos : public OObject { public: - explicit OBoos(size_t numBoos, const IPathSpan& leftBoundary, const IPathSpan& active, const IPathSpan& rightBoundary); + // This is simply a helper function to keep Spawning code clean + static inline OBoos* Spawn(size_t numBoos, const IPathSpan& leftBoundary, const IPathSpan& triggerBoundary, const IPathSpan& rightBoundary) { + SpawnParams params = { + .Name = "mk:boos", + .Count = numBoos, + .LeftExitSpan = leftBoundary, + .TriggerSpan = triggerBoundary, + .RightExitSpan = rightBoundary, + }; + return static_cast(gWorldInstance.AddObject(new OBoos(params))); + } + + explicit OBoos(const SpawnParams& params); ~OBoos() { _count--; @@ -46,8 +58,10 @@ class OBoos : public OObject { return _count; } + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void DrawEditorProperties() override; void func_800523B8(s32 objectIndex, s32 arg1, u32 arg2); void func_8007CA70(void); @@ -59,6 +73,9 @@ class OBoos : public OObject { void BooExit(s32 someIndex); void func_8007C550(s32 objectIndex); + IPathSpan LeftTrigger; + IPathSpan ActiveZone; + IPathSpan RightTrigger; private: FVector _pos; static size_t _count; @@ -68,8 +85,4 @@ class OBoos : public OObject { bool _isActive = false; s32 _playerId = 0; - - IPathSpan _leftBoundary; - IPathSpan _active; - IPathSpan _rightBoundary; }; diff --git a/src/engine/objects/ChainChomp.cpp b/src/engine/objects/ChainChomp.cpp index 0d7ec6bc9c..fd3faa622c 100644 --- a/src/engine/objects/ChainChomp.cpp +++ b/src/engine/objects/ChainChomp.cpp @@ -18,6 +18,7 @@ size_t OChainChomp::_count = 0; OChainChomp::OChainChomp() { Name = "Chain Chomp"; + ResourceName = "mk:chain_chomp"; _idx = _count; init_object(indexObjectList2[_count], 0); _objectIndex = indexObjectList2[_count]; @@ -92,7 +93,7 @@ void OChainChomp::func_80085878(s32 objectIndex, s32 arg1) { object->unk_084[8] = (arg1 * 0x12C) + 0x1F4; set_obj_origin_pos(objectIndex, 0.0f, -15.0f, 0.0f); temp_v0 = &gCurrentTrackPath[(u16) object->unk_084[8]]; - set_obj_origin_offset(objectIndex, temp_v0->posX, temp_v0->posY, temp_v0->posZ); + set_obj_origin_offset(objectIndex, temp_v0->x, temp_v0->y, temp_v0->z); set_obj_direction_angle(objectIndex, 0U, 0U, 0U); object->unk_034 = 4.0f; object->type = get_animation_length(d_rainbow_road_unk3, 0); diff --git a/src/engine/objects/CheepCheep.cpp b/src/engine/objects/CheepCheep.cpp index ed489dc88c..a2432f57ec 100644 --- a/src/engine/objects/CheepCheep.cpp +++ b/src/engine/objects/CheepCheep.cpp @@ -1,8 +1,10 @@ #include "CheepCheep.h" +#include "port/Game.h" #include "assets/banshee_boardwalk_data.h" #include "assets/common_data.h" + extern "C" { #include "math_util.h" #include "math_util_2.h" @@ -17,17 +19,22 @@ extern Vec3s D_800E634C[]; extern Lights1 D_800E45C0[]; } -OCheepCheep::OCheepCheep(const FVector& pos, CheepType type, IPathSpan span) { +OCheepCheep::OCheepCheep(const SpawnParams& params) : OObject(params) { Name = "Cheep Cheep"; - _type = type; - _spawnPos = pos; - _span = span; + ResourceName = "mk:cheep_cheep"; + _behaviour = static_cast(params.Behaviour.value_or(0)); +} + +void OCheepCheep::SetSpawnParams(SpawnParams& params) { + OObject::SetSpawnParams(params); + params.Behaviour = static_cast(_behaviour); + params.PathSpan = ActivationPoints; } void OCheepCheep::Tick() { // update_cheep_cheep s32 objectIndex; - switch (_type) { - case CheepType::RACE: + switch (_behaviour) { + case Behaviour::RACE: UNUSED s32 pad; OCheepCheep::func_8007BD04(0); @@ -35,7 +42,7 @@ void OCheepCheep::Tick() { // update_cheep_cheep OCheepCheep::func_8007BBBC(objectIndex); object_calculate_new_pos_offset(objectIndex); break; - case CheepType::PODIUM_CEREMONY: + case Behaviour::PODIUM_CEREMONY: objectIndex = indexObjectList2[0]; if (D_801658BC == 1) { D_801658BC = 0; @@ -115,9 +122,11 @@ void OCheepCheep::func_8007BD04(s32 playerId) { objectIndex = indexObjectList2[0]; if (gObjectList[objectIndex].state == 0) { - if (((s32) gNearestPathPointByPlayerId[playerId] >= _span.Start) && - ((s32) gNearestPathPointByPlayerId[playerId] <= _span.End)) { - set_obj_origin_pos(objectIndex, xOrientation * _spawnPos.x, _spawnPos.y, _spawnPos.z); + IPathSpan span = ActivationPoints; + if (((s32) gNearestPathPointByPlayerId[playerId] >= span.Start) && + ((s32) gNearestPathPointByPlayerId[playerId] <= span.End)) { + FVector pos = SpawnPos; + set_obj_origin_pos(objectIndex, xOrientation * pos.x, pos.y, pos.z); init_object(objectIndex, 1); } } @@ -242,3 +251,35 @@ void OCheepCheep::func_8007BFB0(s32 objectIndex) { object_add_velocity_offset_y(objectIndex); object_calculate_new_pos_offset(objectIndex); } + +void OCheepCheep::DrawEditorProperties() { + Object* obj = &gObjectList[_objectIndex]; + + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = FVector(obj->pos[0], obj->pos[1], obj->pos[2]); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + IPathSpan span = ActivationPoints; + + ImGui::Text("Path Span"); + ImGui::SameLine(); + + if (ImGui::DragInt2("##PathSpan", (int*)&span)) { + ActivationPoints = span; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathSpan")) { + ActivationPoints = IPathSpan(0.0f, 0.0f); + } +} diff --git a/src/engine/objects/CheepCheep.h b/src/engine/objects/CheepCheep.h index 32e9a5aeb4..4cbe8bd60d 100644 --- a/src/engine/objects/CheepCheep.h +++ b/src/engine/objects/CheepCheep.h @@ -3,6 +3,7 @@ #include #include #include "Object.h" +#include "engine/CoreMath.h" #include "World.h" @@ -18,20 +19,30 @@ extern "C" { class OCheepCheep : public OObject { public: - enum CheepType { + enum class Behaviour : int16_t { RACE, PODIUM_CEREMONY }; - enum Behaviour : uint16_t { - }; + // This is simply a helper function to keep Spawning code clean + static inline OCheepCheep* Spawn(const FVector& pos, Behaviour behaviour, IPathSpan span) { + SpawnParams params = { + .Name = "mk:cheep_cheep", + .Behaviour = static_cast(behaviour), + .Location = pos, + .PathSpan = span, + }; + return static_cast(gWorldInstance.AddObject(new OCheepCheep(params))); + } -public: + explicit OCheepCheep(const SpawnParams& params); - explicit OCheepCheep(const FVector& pos, CheepType type, IPathSpan span); + IPathSpan ActivationPoints; // Path points activation points + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void DrawEditorProperties() override; void func_8007BBBC(s32 objectIndex); void func_8007BD04(s32 playerId); void init_var_cheep_cheep(s32 objectIndex); @@ -41,8 +52,5 @@ class OCheepCheep : public OObject { private: s32 _idx; - CheepType _type; - FVector _spawnPos; - IPathSpan _span; - + Behaviour _behaviour; }; diff --git a/src/engine/objects/Crab.cpp b/src/engine/objects/Crab.cpp index 98c781b5ab..9e4e4687b7 100644 --- a/src/engine/objects/Crab.cpp +++ b/src/engine/objects/Crab.cpp @@ -26,24 +26,31 @@ extern "C" { size_t OCrab::_count = 0; -OCrab::OCrab(const FVector2D& start, const FVector2D& end) { +OCrab::OCrab(const SpawnParams& params) : OObject(params) { Name = "Crab"; + ResourceName = "mk:crab"; _idx = _count; - _start = start; - _end = end; + _start = params.PatrolStart.value_or(FVector2D(0, 0)); + _end = params.PatrolEnd.value_or(FVector2D(0, 0)); find_unused_obj_index(&_objectIndex); init_object(_objectIndex, 0); - gObjectList[_objectIndex].pos[0] = gObjectList[_objectIndex].origin_pos[0] = start.x * xOrientation; - gObjectList[_objectIndex].pos[2] = gObjectList[_objectIndex].origin_pos[2] = start.z; + gObjectList[_objectIndex].pos[0] = gObjectList[_objectIndex].origin_pos[0] = _start.x * xOrientation; + gObjectList[_objectIndex].pos[2] = gObjectList[_objectIndex].origin_pos[2] = _start.z; - gObjectList[_objectIndex].unk_01C[0] = end.x * xOrientation; - gObjectList[_objectIndex].unk_01C[2] = end.z; + gObjectList[_objectIndex].unk_01C[0] = _end.x * xOrientation; + gObjectList[_objectIndex].unk_01C[2] = _end.z; _count++; } +void OCrab::SetSpawnParams(SpawnParams& params) { + params.Name = std::string(ResourceName); + params.PatrolStart = _start; + params.PatrolEnd = _end; +} + void OCrab::Tick(void) { s32 objectIndex = _objectIndex; if (gObjectList[objectIndex].state != 0) { @@ -189,3 +196,29 @@ void OCrab::func_80082E18(s32 objectIndex) { func_80089F24(objectIndex); } } + +void OCrab::DrawEditorProperties() { + ImGui::Text("Start Location"); + ImGui::SameLine(); + + if (ImGui::DragFloat2("##PathSpan", (float*)&_start)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathSpan")) { + _start = FVector2D(0.0f, 0.0f); + gObjectList[_objectIndex].pos[0] = gObjectList[_objectIndex].origin_pos[0] = _start.x * xOrientation; + gObjectList[_objectIndex].pos[2] = gObjectList[_objectIndex].origin_pos[2] = _start.z; + } + + ImGui::Text("Patrol Location"); + ImGui::SameLine(); + + if (ImGui::DragFloat2("##PatrolLoc", (float*)&_end)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPatrolLoc")) { + _end = FVector2D(0.0f, 0.0f); + gObjectList[_objectIndex].unk_01C[0] = _end.x * xOrientation; + gObjectList[_objectIndex].unk_01C[2] = _end.z; + } +} diff --git a/src/engine/objects/Crab.h b/src/engine/objects/Crab.h index 4cadd3b875..d2c694ff17 100644 --- a/src/engine/objects/Crab.h +++ b/src/engine/objects/Crab.h @@ -30,10 +30,23 @@ extern "C" { */ class OCrab : public OObject { public: - explicit OCrab(const FVector2D& start, const FVector2D& end); + // This is simply a helper function to keep Spawning code clean + static inline OCrab* Spawn(const FVector2D& start, const FVector2D& end) { + SpawnParams params = { + .Name = "mk:crab", + .PatrolStart = start, + .PatrolEnd = end, + }; + return static_cast(gWorldInstance.AddObject(new OCrab(params))); + } + + explicit OCrab(const SpawnParams& params); virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void SetSpawnParams(SpawnParams& params) override; + virtual void DrawEditorProperties() override; + void DrawModel(s32 cameraId); void init_ktb_crab(s32 objectIndex); diff --git a/src/engine/objects/Flagpole.cpp b/src/engine/objects/Flagpole.cpp index acd74ba2b7..58581e3c9e 100644 --- a/src/engine/objects/Flagpole.cpp +++ b/src/engine/objects/Flagpole.cpp @@ -15,16 +15,24 @@ extern "C" { size_t OFlagpole::_count = 0; -OFlagpole::OFlagpole(const FVector& pos, s16 direction) { +OFlagpole::OFlagpole(const SpawnParams& params) : OObject(params) { Name = "Flagpole"; + ResourceName = "mk:flagpole"; _idx = _count; - _pos = pos; - _direction = direction; find_unused_obj_index(&_objectIndex); - init_object(_objectIndex, 0); + SpawnPos = params.Location.value_or(FVector(0, 0, 0)); + gObjectList[_objectIndex].pos[0] = SpawnPos.x; + gObjectList[_objectIndex].pos[1] = SpawnPos.y; + gObjectList[_objectIndex].pos[2] = SpawnPos.z; + + SpawnRot = params.Rotation.value_or(IRotator(0, 0, 0)); + gObjectList[_objectIndex].orientation[0] = SpawnRot.pitch; + gObjectList[_objectIndex].orientation[1] = SpawnRot.yaw; + gObjectList[_objectIndex].orientation[2] = SpawnRot.roll; + _count++; } @@ -49,7 +57,7 @@ void OFlagpole::Draw(s32 cameraId) { // func_80055228 void OFlagpole::func_80055164(s32 objectIndex) { // func_80055164 if (gObjectList[objectIndex].state >= 2) { gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D0077A0); - rsp_set_matrix_transformation(gObjectList[objectIndex].pos, gObjectList[objectIndex].direction_angle, + rsp_set_matrix_transformation(gObjectList[objectIndex].pos, gObjectList[objectIndex].orientation, gObjectList[objectIndex].sizeScaling); if (gIsGamePaused == 0) { gObjectList[objectIndex].unk_0A2 = render_animated_model((Armature*) gObjectList[objectIndex].model, @@ -67,9 +75,11 @@ void OFlagpole::func_80082F1C(s32 objectIndex) { gObjectList[objectIndex].vertex = (Vtx*) d_course_yoshi_valley_unk4; gObjectList[objectIndex].sizeScaling = 0.027f; object_next_state(objectIndex); - set_obj_origin_pos(objectIndex, _pos.x * xOrientation, _pos.y, _pos.z); + FVector pos = SpawnPos; + set_obj_origin_pos(objectIndex, pos.x * xOrientation, pos.y, pos.z); set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); - set_obj_direction_angle(objectIndex, 0U, _direction, 0U); + IRotator rot = SpawnRot; + set_obj_orientation(objectIndex, rot.pitch, rot.yaw, rot.roll); // changed from directional_angle to orientation for editor support } void OFlagpole::func_80083018(s32 objectIndex) { diff --git a/src/engine/objects/Flagpole.h b/src/engine/objects/Flagpole.h index 5824fcf181..db5621a2d4 100644 --- a/src/engine/objects/Flagpole.h +++ b/src/engine/objects/Flagpole.h @@ -17,9 +17,23 @@ extern "C" { #include "some_data.h" } +// This used to use directional_angle for rot. It now uses orientation for editor compatibility. +// There doesn't seem to be any reason this actor's behaviour would differ from this class OFlagpole : public OObject { public: - explicit OFlagpole(const FVector& pos, s16 direction); + explicit OFlagpole(const SpawnParams& params); + + // This is simply a helper function to keep Spawning code clean + static inline OFlagpole* Spawn(FVector pos, s16 direction) { + IRotator rot; + rot.Set(0, direction, 0); + SpawnParams params = { + .Name = "mk:flagpole", + .Location = pos, + .Rotation = rot, + }; + return static_cast(gWorldInstance.AddObject(new OFlagpole(params))); + } ~OFlagpole() { _count--; @@ -38,8 +52,6 @@ class OFlagpole : public OObject { void func_80083060(s32 objectIndex); private: - FVector _pos; - s16 _direction; static size_t _count; size_t _idx; }; diff --git a/src/engine/objects/GrandPrixBalloons.cpp b/src/engine/objects/GrandPrixBalloons.cpp index 1315589f2c..05ba880c68 100644 --- a/src/engine/objects/GrandPrixBalloons.cpp +++ b/src/engine/objects/GrandPrixBalloons.cpp @@ -17,8 +17,10 @@ extern "C" { size_t OGrandPrixBalloons::_count = 0; -OGrandPrixBalloons::OGrandPrixBalloons(const FVector& pos) { - Pos = pos; +OGrandPrixBalloons::OGrandPrixBalloons(const SpawnParams& params) : OObject(params) { + Name = "Grand Prix Balloons"; + ResourceName = "mk:grand_prix_balloons"; + Pos = params.Location.value_or(FVector(0, 0, 0)); _active = 1; if (gPlayerCount == 1) { diff --git a/src/engine/objects/GrandPrixBalloons.h b/src/engine/objects/GrandPrixBalloons.h index 93ec044792..04bac0915b 100644 --- a/src/engine/objects/GrandPrixBalloons.h +++ b/src/engine/objects/GrandPrixBalloons.h @@ -25,7 +25,16 @@ extern "C" { class OGrandPrixBalloons : public OObject { public: - explicit OGrandPrixBalloons(const FVector& pos); + explicit OGrandPrixBalloons(const SpawnParams& params); + + // This is simply a helper function to keep Spawning code clean + static inline OGrandPrixBalloons* Spawn(const FVector& pos) { + SpawnParams params = { + .Name = "mk:grand_prix_balloons", + .Location = pos, + }; + return static_cast(gWorldInstance.AddObject(new OGrandPrixBalloons(params))); + } ~OGrandPrixBalloons() { _count--; diff --git a/src/engine/objects/Hedgehog.cpp b/src/engine/objects/Hedgehog.cpp index 1c2aada9d0..a36853a913 100644 --- a/src/engine/objects/Hedgehog.cpp +++ b/src/engine/objects/Hedgehog.cpp @@ -1,5 +1,7 @@ #include "Hedgehog.h" -#include "World.h" +#include "engine/World.h" +#include "port/Game.h" +#include "port/interpolation/FrameInterpolation.h" extern "C" { #include "render_objects.h" @@ -11,60 +13,64 @@ extern "C" { #include "code_80086E70.h" #include "code_80057C60.h" } -#include "port/interpolation/FrameInterpolation.h" size_t OHedgehog::_count = 0; -OHedgehog::OHedgehog(const FVector& pos, const FVector2D& patrolPoint, s16 unk) { +OHedgehog::OHedgehog(const SpawnParams& params) : OObject(params) { Name = "Hedgehog"; + ResourceName = "mk:hedgehog"; _idx = _count; - _pos = pos; - - s32 objectId = indexObjectList2[_idx]; - _objectIndex = objectId; - init_object(objectId, 0); - gObjectList[objectId].pos[0] = gObjectList[objectId].origin_pos[0] = pos.x * xOrientation; - gObjectList[objectId].pos[1] = gObjectList[objectId].surfaceHeight = pos.y + 6.0; - gObjectList[objectId].pos[2] = gObjectList[objectId].origin_pos[2] = pos.z; - gObjectList[objectId].unk_0D5 = (u8) unk; - gObjectList[objectId].unk_09C = patrolPoint.x * xOrientation; - gObjectList[objectId].unk_09E = patrolPoint.z; + SpawnPos = params.Location.value_or(FVector(0, 0, 0)); + PatrolEnd = params.PatrolEnd.value_or(FVector2D(0, 0)); + + find_unused_obj_index(&_objectIndex); + + init_object(_objectIndex, 0); + gObjectList[_objectIndex].pos[0] = gObjectList[_objectIndex].origin_pos[0] = SpawnPos.x * xOrientation; + gObjectList[_objectIndex].pos[1] = gObjectList[_objectIndex].surfaceHeight = SpawnPos.y + 6.0; + gObjectList[_objectIndex].pos[2] = gObjectList[_objectIndex].origin_pos[2] = SpawnPos.z; + gObjectList[_objectIndex].unk_0D5 = (u8) params.Behaviour.value_or(9); + gObjectList[_objectIndex].unk_09C = PatrolEnd.x * xOrientation; + gObjectList[_objectIndex].unk_09E = PatrolEnd.z; _count++; } -void OHedgehog::Tick() { - s32 objectIndex = indexObjectList2[_idx]; +void OHedgehog::SetSpawnParams(SpawnParams& params) { + params.Name = std::string(ResourceName); + params.Location = SpawnPos; + params.PatrolEnd = PatrolEnd; +} - OHedgehog::func_800833D0(objectIndex, _idx); - OHedgehog::func_80083248(objectIndex); - OHedgehog::func_80083474(objectIndex); +void OHedgehog::Tick() { + OHedgehog::func_800833D0(_objectIndex, _idx); + OHedgehog::func_80083248(_objectIndex); + OHedgehog::func_80083474(_objectIndex); // This func clears a bit from all hedgehogs. This results in setting the height of all hedgehogs to zero. // The solution is to only clear the bit from the current instance; `self` or `this` // func_80072120(indexObjectList2, NUM_HEDGEHOGS); - clear_object_flag(objectIndex, 0x00600000); // The fix + clear_object_flag(_objectIndex, 0x00600000); // The fix } void OHedgehog::Draw(s32 cameraId) { - s32 objectIndex = indexObjectList2[_idx]; - u32 something = func_8008A364(objectIndex, cameraId, 0x4000U, 0x000003E8); + u32 something = func_8008A364(_objectIndex, cameraId, 0x4000U, 0x000003E8); if (CVarGetInteger("gNoCulling", 0) == 1) { something = MIN(something, 0x52211U - 1); } - if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { - set_object_flag(objectIndex, 0x00200000); + if (is_obj_flag_status_active(_objectIndex, VISIBLE) != 0) { + set_object_flag(_objectIndex, 0x00200000); if (something < 0x2711U) { - set_object_flag(objectIndex, 0x00000020); + set_object_flag(_objectIndex, 0x00000020); } else { - clear_object_flag(objectIndex, 0x00000020); + clear_object_flag(_objectIndex, 0x00000020); } if (something < 0x57E41U) { - set_object_flag(objectIndex, 0x00400000); + set_object_flag(_objectIndex, 0x00400000); } if (something < 0x52211U) { - OHedgehog::func_800555BC(objectIndex, cameraId); + OHedgehog::func_800555BC(_objectIndex, cameraId); } } } @@ -113,7 +119,7 @@ void OHedgehog::func_8004A870(s32 objectIndex, f32 arg1) { const char* sHedgehogTexList[] = { d_course_yoshi_valley_hedgehog }; -void OHedgehog::func_8008311C(s32 objectIndex, s32 arg1) { +void OHedgehog::func_8008311C(s32 objectIndex, s32 id) { Object* object; Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(common_vtx_hedgehog); @@ -128,7 +134,7 @@ void OHedgehog::func_8008311C(s32 objectIndex, s32 arg1) { object_next_state(objectIndex); set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); set_obj_orientation(objectIndex, 0U, 0U, 0x8000U); - object->unk_034 = ((arg1 % 6) * 0.1) + 0.5; + object->unk_034 = ((id % 6) * 0.1) + 0.5; func_80086E70(objectIndex); set_object_flag(objectIndex, 0x04000600); object->boundingBoxSize = 2; @@ -168,12 +174,12 @@ void OHedgehog::func_80083248(s32 objectIndex) { } } -void OHedgehog::func_800833D0(s32 objectIndex, s32 arg1) { +void OHedgehog::func_800833D0(s32 objectIndex, s32 id) { switch (gObjectList[objectIndex].state) { case 0: break; case 1: - OHedgehog::func_8008311C(objectIndex, arg1); + OHedgehog::func_8008311C(objectIndex, id); break; case 2: func_80072D3C(objectIndex, 0, 1, 4, -1); @@ -193,3 +199,32 @@ void OHedgehog::func_80083474(s32 objectIndex) { func_80089F24(objectIndex); } } + +void OHedgehog::DrawEditorProperties() { + Object* obj = &gObjectList[_objectIndex]; + + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = FVector(obj->pos[0], obj->pos[1], obj->pos[2]); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Patrol Location"); + ImGui::SameLine(); + + if (ImGui::DragFloat2("##PatrolLoc", (float*)&PatrolEnd)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPatrolLoc")) { + PatrolEnd = FVector2D(0.0f, 0.0f); + } +} \ No newline at end of file diff --git a/src/engine/objects/Hedgehog.h b/src/engine/objects/Hedgehog.h index 2057d894d1..7a90f9a913 100644 --- a/src/engine/objects/Hedgehog.h +++ b/src/engine/objects/Hedgehog.h @@ -20,11 +20,22 @@ extern "C" { /** * @arg pos FVector xyz spawn position * @arg patrolPoint FVector2D xz patrol to location. Actor automatically calculates the Y value - * @arg unk unknown. Likely actor type. + * @arg behaviour unknown, seems unused. */ class OHedgehog : public OObject { public: - explicit OHedgehog(const FVector& pos, const FVector2D& patrolPoint, s16 unk); + explicit OHedgehog(const SpawnParams& params); + + // This is simply a helper function to keep Spawning code clean + static inline OHedgehog* Spawn(const FVector& pos, const FVector2D& patrolPoint, s16 behaviour) { + SpawnParams params = { + .Name = "mk:hedgehog", + .Behaviour = behaviour, // Appears to be unused + .Location = pos, + .PatrolEnd = patrolPoint, + }; + return static_cast(gWorldInstance.AddObject(new OHedgehog(params))); + } ~OHedgehog() { _count--; @@ -36,6 +47,8 @@ class OHedgehog : public OObject { virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void SetSpawnParams(SpawnParams& params) override; + virtual void DrawEditorProperties() override; void func_800555BC(s32 objectIndex, s32 cameraId); void func_8004A870(s32 objectIndex, f32 arg1); @@ -47,7 +60,7 @@ class OHedgehog : public OObject { private: - FVector _pos; + FVector2D PatrolEnd; static size_t _count; size_t _idx; }; diff --git a/src/engine/objects/HotAirBalloon.cpp b/src/engine/objects/HotAirBalloon.cpp index f1e367d8d6..b30c8e3ed6 100644 --- a/src/engine/objects/HotAirBalloon.cpp +++ b/src/engine/objects/HotAirBalloon.cpp @@ -14,9 +14,11 @@ extern "C" { #include "actors.h" } -OHotAirBalloon::OHotAirBalloon(const FVector& pos) { +OHotAirBalloon::OHotAirBalloon(const SpawnParams& params) : OObject(params) { Name = "Hot Air Balloon"; - _pos = pos; + ResourceName = "mk:hot_air_balloon"; + + SpawnPos = params.Location.value_or(FVector{0.0f, 0.0f, 0.0f}); D_80165898 = 0; @@ -30,9 +32,14 @@ OHotAirBalloon::OHotAirBalloon(const FVector& pos) { find_unused_obj_index(&_objectIndex); + init_object(_objectIndex, 0); } +void OHotAirBalloon::SetSpawnParams(SpawnParams& params) { + OObject::SetSpawnParams(params); +} + void OHotAirBalloon::Tick() { s32 objectIndex = _objectIndex; @@ -100,10 +107,10 @@ void OHotAirBalloon::init_hot_air_balloon(s32 objectIndex) { gObjectList[objectIndex].sizeScaling = 1.0f; gObjectList[objectIndex].model = (Gfx*)d_course_luigi_raceway_dl_F960; if (gGamestate != CREDITS_SEQUENCE) { - set_obj_origin_pos(objectIndex, xOrientation * _pos.x, _pos.y, _pos.z); + set_obj_origin_pos(objectIndex, xOrientation * SpawnPos.x, SpawnPos.y, SpawnPos.z); set_obj_origin_offset(objectIndex, 0.0f, 300.0f, 0.0f); } else { - set_obj_origin_pos(objectIndex, xOrientation * _pos.x, _pos.y, _pos.z); + set_obj_origin_pos(objectIndex, xOrientation * SpawnPos.x, SpawnPos.y, SpawnPos.z); set_obj_origin_offset(objectIndex, 0.0f, 300.0f, 0.0f); } func_8008B844(objectIndex); diff --git a/src/engine/objects/HotAirBalloon.h b/src/engine/objects/HotAirBalloon.h index 301e111c48..7199a7cf44 100644 --- a/src/engine/objects/HotAirBalloon.h +++ b/src/engine/objects/HotAirBalloon.h @@ -19,16 +19,27 @@ extern "C" { class OHotAirBalloon : public OObject { public: - explicit OHotAirBalloon(const FVector& pos); + explicit OHotAirBalloon(const SpawnParams& params); + + // This is simply a helper function to keep Spawning code clean + static inline OHotAirBalloon* Spawn(FVector pos) { + SpawnParams params = { + .Name = "mk:hot_air_balloon", + .Location = pos, + }; + return static_cast(gWorldInstance.AddObject(new OHotAirBalloon(params))); + } virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void SetSpawnParams(SpawnParams& params) override; + void func_80055CCC(s32 objectIndex, s32 cameraId); void init_hot_air_balloon(s32 objectIndex); void func_80085534(s32 objectIndex); void func_80085768(s32 objectIndex); private: - FVector _pos; + FVector Pos; bool *_visible; }; diff --git a/src/engine/objects/Object.cpp b/src/engine/objects/Object.cpp index 4e4fd67661..d7f9ac6160 100644 --- a/src/engine/objects/Object.cpp +++ b/src/engine/objects/Object.cpp @@ -10,9 +10,25 @@ extern "C" { //GameActor() -OObject::OObject() {} +OObject::OObject() { +} +OObject::OObject(SpawnParams params) { + ResourceName = "mk:object"; // This needs to be overridden in derived classes + SpawnPos = params.Location.value_or(FVector{0.0f, 0.0f, 0.0f}); + SpawnRot = params.Rotation.value_or(IRotator{0, 0, 0}); + SpawnScale = params.Scale.value_or(FVector(0, 0, 0)); + Speed = params.Speed.value_or(0.0f); +} + +void OObject::SetSpawnParams(SpawnParams& params) { + params.Name = ResourceName; + params.Location = SpawnPos; + params.Rotation = SpawnRot; + params.Scale = SpawnScale; + params.Speed = Speed; +} - // Virtual functions to be overridden by derived classes +// Virtual functions to be overridden by derived classes void OObject::Tick() { } void OObject::Tick60fps() {} void OObject::Draw(s32 cameraId) { } @@ -21,3 +37,62 @@ void OObject::Destroy() { bPendingDestroy = true; } void OObject::Reset() { } + +FVector OObject::GetLocation() const { + if (_objectIndex != -1) { + Object* object = &gObjectList[_objectIndex]; + return FVector(object->pos[0], object->pos[1], object->pos[2]); + } + printf("Editor tried to get null OObject\n"); + return FVector(0, 0, 0); +}; + +IRotator OObject::GetRotation() const { + if (_objectIndex != -1) { + Object* object = &gObjectList[_objectIndex]; + return IRotator(object->orientation[0], object->orientation[1], object->orientation[2]); + } + printf("Editor tried to get null OObject\n"); + return IRotator(0, 0, 0); +} + +FVector OObject::GetScale() const { + if (_objectIndex != -1) { + Object* object = &gObjectList[_objectIndex]; + return FVector(object->sizeScaling, object->sizeScaling, object->sizeScaling); + } + printf("Editor tried to get null OObject\n"); + return FVector(0, 0, 0); +} + +void OObject::Translate(FVector pos) { + if (_objectIndex != -1) { + SpawnPos = pos; + + Object* object = &gObjectList[_objectIndex]; + + object->pos[0] = pos.x; + object->pos[1] = pos.y; + object->pos[2] = pos.z; + object->origin_pos[0] = pos.x; + object->origin_pos[1] = pos.y; + object->origin_pos[2] = pos.z; + } else { + printf("Editor tried to translate null OObject\n"); + } +} +void OObject::Rotate(IRotator rot) { + if (_objectIndex != -1) { + SpawnRot = rot; + Object* object = &gObjectList[_objectIndex]; + object->orientation[0] = rot.pitch; + object->orientation[1] = rot.yaw; + object->orientation[2] = rot.roll; + } else { + printf("Editor tried to rotate null OObject\n"); + } +} + +void OObject::SetScale(FVector scale) { + SpawnScale = scale; +} diff --git a/src/engine/objects/Object.h b/src/engine/objects/Object.h index 9dd44397b9..2a2a51e211 100644 --- a/src/engine/objects/Object.h +++ b/src/engine/objects/Object.h @@ -1,6 +1,10 @@ #pragma once #include +#include "engine/SpawnParams.h" + +// Editor +#include "engine/editor/EditorMath.h" extern "C" { #include "camera.h" @@ -12,17 +16,34 @@ class OObject { uint8_t uuid[16]; Object o; const char* Name = ""; + const char* ResourceName = ""; bool bPendingDestroy = false; s32 _objectIndex = -1; + const char* Model = ""; + + FVector SpawnPos = {0.0f, 0.0f, 0.0f}; + IRotator SpawnRot = {0, 0, 0}; + FVector SpawnScale = {1.0f, 1.0f, 1.0f}; + float Speed = 0.0f; + std::vector Triangles; virtual ~OObject() = default; explicit OObject(); + explicit OObject(SpawnParams params); + virtual void SetSpawnParams(SpawnParams& params); virtual void Tick(); virtual void Tick60fps(); virtual void Draw(s32 cameraId); virtual void Expire(); virtual void Destroy(); // Mark object for deletion at the start of the next frame virtual void Reset(); + FVector GetLocation() const; + IRotator GetRotation() const; + FVector GetScale() const; + virtual void Translate(FVector pos); + void Rotate(IRotator rot); + void SetScale(FVector scale); + virtual void DrawEditorProperties() { DrawDefaultEditorProperties(); }; }; diff --git a/src/engine/objects/Penguin.cpp b/src/engine/objects/Penguin.cpp index d283101b3f..34c7248dd3 100644 --- a/src/engine/objects/Penguin.cpp +++ b/src/engine/objects/Penguin.cpp @@ -33,12 +33,11 @@ extern "C" { extern s8 gPlayerCount; } - -OPenguin::OPenguin(FVector pos, u16 direction, PenguinType type, Behaviour behaviour) { +OPenguin::OPenguin(const SpawnParams& params) : OObject(params) { Name = "Penguin"; - _type = type; - _bhv = behaviour; - + ResourceName = "mk:penguin"; + FVector pos = params.Location.value_or(FVector(0, 0, 0)); + Speed = params.Speed.value_or(0); find_unused_obj_index(&_objectIndex); init_object(_objectIndex, 0); @@ -47,9 +46,9 @@ OPenguin::OPenguin(FVector pos, u16 direction, PenguinType type, Behaviour behav object->origin_pos[0] = pos.x * xOrientation; object->origin_pos[1] = pos.y; object->origin_pos[2] = pos.z; - object->unk_0C6 = direction; + object->unk_0C6 = params.Rotation.value_or(IRotator(0, 0, 0)).yaw; - switch(type) { + switch(static_cast(Type)) { case PenguinType::CHICK: object->surfaceHeight = 5.0f; object->sizeScaling = 0.04f; @@ -76,7 +75,7 @@ void OPenguin::Tick(void) { s32 objectIndex = _objectIndex; if (gObjectList[objectIndex].state != 0) { - if (_type == PenguinType::EMPEROR) { + if (Type == PenguinType::EMPEROR) { OPenguin::EmperorPenguin(objectIndex); } else { OPenguin::OtherPenguin(objectIndex); @@ -144,7 +143,7 @@ void OPenguin::Behaviours(s32 objectIndex) { // func_800850B0 Object* object; object = &gObjectList[objectIndex]; - switch (_bhv) { + switch (SpawnBhv) { case 1: // emperor OPenguin::func_80085080(objectIndex); break; @@ -379,9 +378,9 @@ void OPenguin::InitOtherPenguin(s32 objectIndex) { // This code has been significantly refactored from the original func_800845C8 // Into a switch statement instead of checking for the index of the penguin - switch(_bhv) { + switch(SpawnBhv) { case Behaviour::CIRCLE: - object->unk_01C[1] = Diameter; + object->unk_01C[1] = Speed; if (_toggle) { object->unk_0C4 = 0x8000; @@ -400,7 +399,7 @@ void OPenguin::InitOtherPenguin(s32 objectIndex) { case Behaviour::STRUT: if (gIsMirrorMode) { - object->unk_0C6 = MirrorModeAngleOffset; + object->unk_0C6 = SpawnRot.roll; // Roll is used to save mirror mode angle offset; } set_obj_direction_angle(objectIndex, 0U, object->unk_0C6 + 0x8000, 0U); @@ -417,3 +416,108 @@ void OPenguin::InitOtherPenguin(s32 objectIndex) { void OPenguin::Reset() { _toggle = false; } + +void OPenguin::DrawEditorProperties() { + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = GetLocation(); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Rotation"); + ImGui::SameLine(); + + IRotator objRot = GetRotation(); + + // Convert to temporary int values (to prevent writing 32bit values to 16bit variables) + int rot[3] = { + objRot.pitch, + objRot.yaw, + objRot.roll + }; + + if (ImGui::DragInt3("##Rotation", rot, 5.0f)) { + for (size_t i = 0; i < 3; i++) { + // Wrap around 0–65535 + rot[i] = (rot[i] % 65536 + 65536) % 65536; + } + IRotator newRot; + newRot.Set( + static_cast(rot[0]), + static_cast(rot[1]), + static_cast(rot[2]) + ); + Rotate(newRot); + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetRot")) { + IRotator rot = IRotator(0, 0, 0); + Rotate(rot); + } + + ImGui::Text("Spawn Mode"); + ImGui::SameLine(); + + int32_t type = static_cast(Type); + const char* items[] = { "CHICK", "ADULT", "CREDITS", "EMPEROR" }; + + if (ImGui::Combo("##Type", &type, items, IM_ARRAYSIZE(items))) { + Type = static_cast(type); + + // Update type values + Object* object = &gObjectList[this->_objectIndex]; + switch(static_cast(type)) { + case PenguinType::CHICK: + object->surfaceHeight = 5.0f; + object->sizeScaling = 0.04f; + object->boundingBoxSize = 4; + break; + case PenguinType::ADULT: + object->surfaceHeight = -80.0f; + object->sizeScaling = 0.08f; + object->boundingBoxSize = 4; + break; + case PenguinType::CREDITS: + object->surfaceHeight = -80.0f; + object->sizeScaling = 0.08f; + object->sizeScaling = 0.15f; + break; + case PenguinType::EMPEROR: + object->sizeScaling = 0.2f; + object->boundingBoxSize = 0x000C; + break; + } + } + + ImGui::Text("Spawn Mode"); + ImGui::SameLine(); + + int32_t behaviour = static_cast(SpawnBhv); + const char* items2[] = { "DISABLED", "STRUT", "CIRCLE", "SLIDE3", "SLIDE4", "UNK", "SLIDE6" }; + + if (ImGui::Combo("##Behaviour", &behaviour, items2, IM_ARRAYSIZE(items2))) { + SpawnBhv = static_cast(behaviour); + } + + ImGui::Text("Diameter"); + ImGui::SameLine(); + + float speed = Speed; + if (ImGui::DragFloat("##Speed", &speed, 0.1f)) { + Speed = speed; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeed")) { + Speed = 0.0f; + } +} diff --git a/src/engine/objects/Penguin.h b/src/engine/objects/Penguin.h index 91d5687b98..4c9c04dd6f 100644 --- a/src/engine/objects/Penguin.h +++ b/src/engine/objects/Penguin.h @@ -17,14 +17,14 @@ extern "C" { class OPenguin : public OObject { public: - enum PenguinType : uint32_t { + enum PenguinType : int16_t { CHICK, ADULT, CREDITS, EMPEROR }; - enum Behaviour : uint16_t { + enum Behaviour : int16_t { DISABLED, STRUT, // Emperor penguin CIRCLE, // Waddle in a circle @@ -35,14 +35,30 @@ class OPenguin : public OObject { }; public: - f32 Diameter = 0.0f; // Waddle in a circle around the spawn point at this diameter. - uint16_t MirrorModeAngleOffset; + explicit OPenguin(const SpawnParams& params); - explicit OPenguin(FVector pos, u16 direction, PenguinType type, Behaviour behaviour); + // This is simply a helper function to keep Spawning code clean + static inline OPenguin* Spawn(FVector pos, u16 direction, u16 mirrorModeAngleOffset, f32 diameter, PenguinType type, Behaviour behaviour) { + IRotator rot; + rot.Set(0, direction, mirrorModeAngleOffset); + SpawnParams params = { + .Name = "mk:penguin", + .Type = type, + .Behaviour = behaviour, + .Location = pos, + .Rotation = rot, + .Speed = diameter, // Diameter of the walking circle + }; + return static_cast(gWorldInstance.AddObject(new OPenguin(params))); + } + + PenguinType Type = PenguinType::CHICK; + Behaviour SpawnBhv = Behaviour::STRUT; virtual void Tick() override; virtual void Draw(s32 cameraId) override; virtual void Reset() override; + virtual void DrawEditorProperties() override; private: void Behaviours(s32 objectIndex); void EmperorPenguin(s32 objectIndex); @@ -55,6 +71,4 @@ class OPenguin : public OObject { void InitOtherPenguin(s32 objectIndex); static bool _toggle; - PenguinType _type; - Behaviour _bhv; }; diff --git a/src/engine/objects/Podium.cpp b/src/engine/objects/Podium.cpp index ec396ca537..d5e09d799c 100644 --- a/src/engine/objects/Podium.cpp +++ b/src/engine/objects/Podium.cpp @@ -25,9 +25,10 @@ extern Vec3s D_800E634C[]; // { 0xf380, 0x0013, 0xfe14 }, // }; -OPodium::OPodium(const FVector& pos) { +OPodium::OPodium(const SpawnParams& params) : OObject(params) { Name = "Podium"; - _pos = pos; + ResourceName = "mk:podium"; + _pos = params.Location.value_or(FVector(0, 0, 0)); find_unused_obj_index(&_podium1Index); find_unused_obj_index(&_podium2Index); diff --git a/src/engine/objects/Podium.h b/src/engine/objects/Podium.h index fafdfe63d1..f4f02fc8c8 100644 --- a/src/engine/objects/Podium.h +++ b/src/engine/objects/Podium.h @@ -24,7 +24,16 @@ class OPodium : public OObject { public: - explicit OPodium(const FVector& pos); + explicit OPodium(const SpawnParams& params); + + // This is simply a helper function to keep Spawning code clean + static inline OPodium* Spawn(const FVector& pos) { + SpawnParams params = { + .Name = "mk:podium", + .Location = pos, + }; + return static_cast(gWorldInstance.AddObject(new OPodium(params))); + } virtual void Tick() override; virtual void Draw(s32 cameraId) override; diff --git a/src/engine/objects/Seagull.cpp b/src/engine/objects/Seagull.cpp index 17c35c07d7..61a7f39ca8 100644 --- a/src/engine/objects/Seagull.cpp +++ b/src/engine/objects/Seagull.cpp @@ -31,12 +31,11 @@ SplineData* D_800E633C[] = { &D_800E6034, &D_800E60F0, &D_800E61B4, &D_800E6280 size_t OSeagull::_count = 0; -OSeagull::OSeagull(FVector pos) { +OSeagull::OSeagull(const SpawnParams& params) : OObject(params) { Name = "Seagull"; + ResourceName = "mk:seagull"; _idx = _count; - _pos.x = pos.x; - _pos.y = pos.y; - _pos.z = pos.z; + FVector pos = params.Location.value_or(FVector(0, 0, 0)); s16 randZ; s16 randX; @@ -49,6 +48,11 @@ OSeagull::OSeagull(FVector pos) { init_object(_objectIndex, 0); + Object* object = &gObjectList[_objectIndex]; + + object->pos[0] = pos.x; + object->pos[1] = pos.y; + object->pos[2] = pos.z; set_obj_origin_pos(_objectIndex, pos.x, pos.y, pos.z); if (_idx < (NUM_SEAGULLS / 2)) { @@ -171,7 +175,8 @@ void OSeagull::func_8008241C(s32 objectIndex, s32 arg1) { randY = random_int(0x0014); randZ = random_int(0x00C8) + -100.0; - set_obj_origin_pos(objectIndex, (randX + _pos.x) * xOrientation, randY + _pos.y, randZ + _pos.z); + FVector pos = SpawnPos; + set_obj_origin_pos(objectIndex, (randX + pos.x) * xOrientation, randY + pos.y, randZ + pos.z); set_obj_direction_angle(objectIndex, 0U, 0U, 0U); gObjectList[objectIndex].unk_034 = 1.0f; func_80086EF0(objectIndex); diff --git a/src/engine/objects/Seagull.h b/src/engine/objects/Seagull.h index 42474159e3..73d06a9006 100644 --- a/src/engine/objects/Seagull.h +++ b/src/engine/objects/Seagull.h @@ -19,7 +19,7 @@ extern "C" { //! @todo unk_0D5 needs to be a struct variable probably. What does it do? Behaviour? class OSeagull : public OObject { public: - explicit OSeagull(FVector pos); + explicit OSeagull(const SpawnParams& params); ~OSeagull() { _count--; @@ -29,6 +29,15 @@ class OSeagull : public OObject { return _count; } + // This is simply a helper function to keep Spawning code clean + static inline OSeagull* Spawn(const FVector& pos) { + SpawnParams params = { + .Name = "mk:seagull", + .Location = pos, + }; + return static_cast(gWorldInstance.AddObject(new OSeagull(params))); + } + virtual void Tick() override; virtual void Draw(s32 cameraId) override; @@ -38,7 +47,6 @@ class OSeagull : public OObject { void func_8008241C(s32 objectIndex, s32 arg1); void func_80082714(s32 objectIndex, s32 arg1); private: - FVector _pos; static size_t _count; s32 _idx; bool _toggle; diff --git a/src/engine/objects/Snowman.cpp b/src/engine/objects/Snowman.cpp index cbb708fbf9..dd98e2df0f 100644 --- a/src/engine/objects/Snowman.cpp +++ b/src/engine/objects/Snowman.cpp @@ -17,31 +17,32 @@ static const char* sSnowmanHeadList[] = { d_course_frappe_snowland_snowman_head size_t OSnowman::_count = 0; -OSnowman::OSnowman(const FVector& pos) { +OSnowman::OSnowman(const SpawnParams& params) : OObject(params) { Name = "Snowman"; + ResourceName = "mk:snowman"; _idx = _count; - _pos = pos; + Pos = params.Location.value_or(FVector(0, 0, 0)); find_unused_obj_index(&_headIndex); init_object(_headIndex, 0); _objectIndex = _headIndex; - gObjectList[_headIndex].origin_pos[0] = pos.x * xOrientation; - gObjectList[_headIndex].origin_pos[1] = pos.y + 5.0 + 3.0; - gObjectList[_headIndex].origin_pos[2] = pos.z; - gObjectList[_headIndex].pos[0] = pos.x * xOrientation; - gObjectList[_headIndex].pos[1] = pos.y + 5.0 + 3.0; - gObjectList[_headIndex].pos[2] = pos.z; + gObjectList[_headIndex].origin_pos[0] = Pos.x * xOrientation; + gObjectList[_headIndex].origin_pos[1] = Pos.y + 5.0 + 3.0; + gObjectList[_headIndex].origin_pos[2] = Pos.z; + gObjectList[_headIndex].pos[0] = Pos.x * xOrientation; + gObjectList[_headIndex].pos[1] = Pos.y + 5.0 + 3.0; + gObjectList[_headIndex].pos[2] = Pos.z; find_unused_obj_index(&_bodyIndex); init_object(_bodyIndex, 0); - gObjectList[_bodyIndex].origin_pos[0] = pos.x * xOrientation; - gObjectList[_bodyIndex].origin_pos[1] = pos.y + 3.0; - gObjectList[_bodyIndex].origin_pos[2] = pos.z; + gObjectList[_bodyIndex].origin_pos[0] = Pos.x * xOrientation; + gObjectList[_bodyIndex].origin_pos[1] = Pos.y + 3.0; + gObjectList[_bodyIndex].origin_pos[2] = Pos.z; gObjectList[_bodyIndex].unk_0D5 = 0; // Section Id no longer used. - gObjectList[_bodyIndex].pos[0] = pos.x * xOrientation; - gObjectList[_bodyIndex].pos[1] = pos.y + 3.0; - gObjectList[_bodyIndex].pos[2] = pos.z; + gObjectList[_bodyIndex].pos[0] = Pos.x * xOrientation; + gObjectList[_bodyIndex].pos[1] = Pos.y + 3.0; + gObjectList[_bodyIndex].pos[2] = Pos.z; _count++; } @@ -348,6 +349,31 @@ void OSnowman::func_80083B0C(s32 objectIndex) { set_object_flag(objectIndex, 0x04000210); } +void OSnowman::Translate(FVector pos) { + if ((_objectIndex != -1) && (_bodyIndex != -1)) { + SpawnPos = pos; + + Object* object = &gObjectList[_objectIndex]; + + object->pos[0] = pos.x; + object->pos[1] = pos.y; + object->pos[2] = pos.z; + object->origin_pos[0] = pos.x; + object->origin_pos[1] = pos.y; + object->origin_pos[2] = pos.z; + + object = &gObjectList[_bodyIndex]; + object->pos[0] = pos.x; + object->pos[1] = pos.y - 5.0; + object->pos[2] = pos.z; + object->origin_pos[0] = pos.x; + object->origin_pos[1] = pos.y - 5.0; + object->origin_pos[2] = pos.z; + } else { + printf("Editor tried to translate null OObject\n"); + } +} + void OSnowman::func_80083538(s32 objectIndex, Vec3f arg1, s32 arg2, s32 arg3) { Object* object; diff --git a/src/engine/objects/Snowman.h b/src/engine/objects/Snowman.h index f33ee3b53d..42217f040d 100644 --- a/src/engine/objects/Snowman.h +++ b/src/engine/objects/Snowman.h @@ -19,7 +19,16 @@ extern "C" { class OSnowman : public OObject { public: - explicit OSnowman(const FVector& pos); + // This is simply a helper function to keep Spawning code clean + static inline OSnowman* Spawn(FVector pos) { + SpawnParams params = { + .Name = "mk:snowman", + .Location = FVector(pos.x, pos.y, pos.z), + }; + return static_cast(gWorldInstance.AddObject(new OSnowman(params))); + } + + explicit OSnowman(const SpawnParams& params); ~OSnowman() { _count--; @@ -31,6 +40,7 @@ class OSnowman : public OObject { virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void Translate(FVector pos) override; void DrawHead(s32); void DrawBody(s32); @@ -47,7 +57,7 @@ class OSnowman : public OObject { void func_8008379C(s32 objectIndex); private: - FVector _pos; + FVector Pos; static size_t _count; size_t _idx; s32 _headIndex; diff --git a/src/engine/objects/Thwomp.cpp b/src/engine/objects/Thwomp.cpp index 2f8281cb61..b597f26ba0 100644 --- a/src/engine/objects/Thwomp.cpp +++ b/src/engine/objects/Thwomp.cpp @@ -2,6 +2,8 @@ #include #include "Thwomp.h" #include +#include "engine/courses/Course.h" +#include "engine/World.h" #include "port/Game.h" #include "port/interpolation/FrameInterpolation.h" @@ -48,32 +50,56 @@ s16 D_800E597C[] = { 0x0000, 0x0000, 0x4000, 0x8000, 0x8000, 0xc000 }; size_t OThwomp::_count = 0; size_t OThwomp::_rand = 0; -OThwomp::OThwomp(s16 x, s16 z, s16 direction, f32 scale, s16 behaviour, s16 primAlpha, u16 boundingBoxSize) { +OThwomp::OThwomp(const SpawnParams& params) : OObject(params) { // s16 x, s16 z, s16 direction, f32 scale, s16 behaviour, s16 primAlpha, u16 boundingBoxSize) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + IRotator rot = params.Rotation.value_or(IRotator{0, 0, 0}); + BoundingBoxSize = params.BoundingBoxSize.value_or(0); + Behaviour = static_cast(params.Behaviour.value_or(1)); + PrimAlpha = params.PrimAlpha.value_or(0); + FVector scale = params.Scale.value_or(FVector(0, 0, 0)); + Name = "Thwomp"; + ResourceName = "mk:thwomp"; + Model = "d_course_bowsers_castle_dl_thwomp"; _idx = _count; - _faceDirection = direction; - _boundingBoxSize = boundingBoxSize; - State = (States)behaviour; + _faceDirection = rot.yaw; find_unused_obj_index(&_objectIndex); s32 objectId = _objectIndex; init_object(objectId, 0); - gObjectList[objectId].origin_pos[0] = x * xOrientation; - gObjectList[objectId].origin_pos[2] = z; - gObjectList[objectId].unk_0D5 = behaviour; - gObjectList[objectId].primAlpha = primAlpha; - gObjectList[objectId].boundingBoxSize = boundingBoxSize + 5; - - if (scale == 0.0f) { - scale = 1.0f; + gObjectList[objectId].origin_pos[0] = loc.x * xOrientation; + gObjectList[objectId].origin_pos[2] = loc.z; + gObjectList[objectId].unk_0D5 = Behaviour; + gObjectList[objectId].primAlpha = PrimAlpha; + gObjectList[objectId].boundingBoxSize = BoundingBoxSize + 5; + + if (scale.y == 0.0f) { + scale.y = 1.0f; } - gObjectList[objectId].sizeScaling = scale; + gObjectList[objectId].sizeScaling = scale.y; _count++; } +void OThwomp::SetSpawnParams(SpawnParams& params) { + Object* object = &gObjectList[_objectIndex]; + params.Name = std::string(ResourceName); + params.Location = FVector( + object->origin_pos[0], + object->origin_pos[1], + object->origin_pos[2] + ); + IRotator rot; rot.Set(0, object->orientation[1], 0); + params.Rotation = rot; + params.Scale = FVector(0, object->sizeScaling, 0); + params.Behaviour = Behaviour; + params.PrimAlpha = PrimAlpha; + params.BoundingBoxSize = BoundingBoxSize; + +} + void OThwomp::Tick60fps() { // func_80081210 Player* player; s32 objectIndex; @@ -94,23 +120,23 @@ void OThwomp::Tick60fps() { // func_80081210 } if (gObjectList[_objectIndex].state != 0) { - switch (State) { - case STATIONARY: + switch(Behaviour) { + case States::STATIONARY: OThwomp::StationaryBehaviour(_objectIndex); break; - case MOVE_AND_ROTATE: + case States::MOVE_AND_ROTATE: OThwomp::MoveAndRotateBehaviour(_objectIndex); break; - case MOVE_FAR: + case States::MOVE_FAR: OThwomp::MoveFarBehaviour(_objectIndex); break; - case STATIONARY_FAST: + case States::STATIONARY_FAST: OThwomp::StationaryFastBehaviour(_objectIndex); break; - case JAILED: + case States::JAILED: OThwomp::JailedBehaviour(_objectIndex); break; - case SLIDE: + case States::SLIDE: OThwomp::SlidingBehaviour(_objectIndex); break; } @@ -138,8 +164,6 @@ void OThwomp::Tick60fps() { // func_80081210 OThwomp::AddParticles(_objectIndex); } - - if (_idx == 0) { for (var_s4 = 0; var_s4 < gObjectParticle2_SIZE; var_s4++) { // @port: Tag the transform. @@ -641,7 +665,7 @@ void OThwomp::func_80080B28(s32 objectIndex, s32 playerId) { } } else if ((temp_f0 <= 17.5) && (func_80072320(objectIndex, 1) != 0) && (is_within_horizontal_distance_of_player(objectIndex, player, - (player->speed * 0.5) + _boundingBoxSize) != 0)) { + (player->speed * 0.5) + BoundingBoxSize) != 0)) { if ((player->type & 0x8000) && !(player->type & 0x100)) { if (is_obj_flag_status_active(objectIndex, 0x04000000) != 0) { func_80072180(); @@ -677,8 +701,8 @@ void OThwomp::Draw(s32 cameraId) { Camera* camera; Object* object; - camera = &camera1[cameraId]; - if (cameraId == PLAYER_ONE) { + camera = &cameras[cameraId]; + if (cameraId == PLAYER_ONE || cameraId == 4) { // 4 == freecam clear_object_flag(objectIndex, 0x00070000); func_800722CC(objectIndex, 0x00000110); } @@ -704,7 +728,7 @@ void OThwomp::Draw(s32 cameraId) { objectIndex = gObjectParticle3[i]; if (objectIndex != NULL_OBJECT_ID) { object = &gObjectList[objectIndex]; - if ((object->state > 0) && (State == MOVE_FAR) && (gMatrixHudCount <= MTX_HUD_POOL_SIZE_MAX)) { + if ((object->state > 0) && (Behaviour == States::MOVE_FAR)) { rsp_set_matrix_transformation(object->pos, object->orientation, object->sizeScaling); gSPVertex(gDisplayListHead++, (uintptr_t) D_0D005C00, 3, 0); gSPDisplayList(gDisplayListHead++, (Gfx*) D_0D006930); @@ -723,7 +747,7 @@ void OThwomp::Draw(s32 cameraId) { objectIndex = gObjectParticle2[i]; if (objectIndex != NULL_OBJECT_ID) { object = &gObjectList[objectIndex]; - if ((object->state >= 2) && (State == MOVE_AND_ROTATE) && (gMatrixHudCount <= MTX_HUD_POOL_SIZE_MAX)) { + if ((object->state >= 2) && (Behaviour == States::MOVE_AND_ROTATE)) { func_8004B138(0x000000FF, 0x000000FF, 0x000000FF, (s32) object->primAlpha); D_80183E80[1] = func_800418AC(object->pos[0], object->pos[2], camera->pos); func_800431B0(object->pos, D_80183E80, object->sizeScaling, (Vtx*) D_0D005AE0); @@ -734,6 +758,7 @@ void OThwomp::Draw(s32 cameraId) { void OThwomp::DrawModel(s32 objectIndex) { if ((gObjectList[objectIndex].state >= 2) && (func_80072354(objectIndex, 0x00000040) != 0)) { + FrameInterpolation_RecordOpenChild("Thwomp_Main", (uintptr_t) TAG_THWOMP(this)); func_8004A7AC(objectIndex, 1.75f); rsp_set_matrix_transformation(gObjectList[objectIndex].pos, gObjectList[objectIndex].orientation, gObjectList[objectIndex].sizeScaling); @@ -743,6 +768,7 @@ void OThwomp::DrawModel(s32 objectIndex) { gDPLoadTLUT_pal256(gDisplayListHead++, d_course_bowsers_castle_thwomp_tlut); rsp_load_texture_mask((u8*) gObjectList[objectIndex].activeTexture, 0x00000010, 0x00000040, 4); gSPDisplayList(gDisplayListHead++, gObjectList[objectIndex].model); + FrameInterpolation_RecordCloseChild(); } } @@ -1448,3 +1474,108 @@ void OThwomp::func_8007E63C(s32 objectIndex) { break; } } + +void OThwomp::DrawEditorProperties() { + ImGui::Text("Behaviour"); + ImGui::SameLine(); + + int32_t behaviour = static_cast(Behaviour); + const char* items[] = { "Disabled", "Stationary", "Move and Rotate", "Move Far", "Stationary Fast", "Slide", "Jailed" }; + + if (ImGui::Combo("##Behaviour", &behaviour, items, IM_ARRAYSIZE(items))) { + Behaviour = static_cast(behaviour); + gObjectList[_objectIndex].unk_0D5 = static_cast(behaviour); + gObjectList[_objectIndex].state = behaviour; + } + + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = GetLocation(); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Rotation"); + ImGui::SameLine(); + + IRotator objRot = GetRotation(); + + // Convert to temporary int values (to prevent writing 32bit values to 16bit variables) + int rot[3] = { + objRot.pitch, + objRot.yaw, + objRot.roll + }; + + if (ImGui::DragInt3("##Rotation", rot, 5.0f)) { + for (size_t i = 0; i < 3; i++) { + // Wrap around 0–65535 + rot[i] = (rot[i] % 65536 + 65536) % 65536; + } + IRotator newRot; + newRot.Set( + static_cast(rot[0]), + static_cast(rot[1]), + static_cast(rot[2]) + ); + Rotate(newRot); + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetRot")) { + IRotator rot = IRotator(0, 0, 0); + Rotate(rot); + } + + FVector scale = GetScale(); + ImGui::Text("Scale "); + ImGui::SameLine(); + + if (ImGui::DragFloat3("##Scale", (float*)&scale, 0.1f)) { + SetScale(scale); + gObjectList[_objectIndex].sizeScaling = scale.y; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetScale")) { + FVector scale = FVector(1.0f, 1.0f, 1.0f); + SetScale(scale); + gObjectList[_objectIndex].sizeScaling = 1.0f; + } + + int32_t primAlpha = PrimAlpha; + ImGui::Text("Prim Alpha"); + ImGui::SameLine(); + + if (ImGui::InputInt("##PrimAlpha", (int*)&primAlpha)) { + PrimAlpha = static_cast(primAlpha); + gObjectList[_objectIndex].primAlpha = static_cast(primAlpha); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPrimAlpha")) { + PrimAlpha = 0; + gObjectList[_objectIndex].primAlpha = 0; + } + + int32_t boundingBoxSize = static_cast(BoundingBoxSize); + ImGui::Text("Bounding Box Size"); + ImGui::SameLine(); + + if (ImGui::InputInt("##BoundingBoxSize", (int*)&boundingBoxSize)) { + if (boundingBoxSize < 0) boundingBoxSize = 0; + BoundingBoxSize = static_cast(boundingBoxSize); + gObjectList[_objectIndex].boundingBoxSize = static_cast(boundingBoxSize); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetBoundingBoxSize")) { + BoundingBoxSize = 0; + gObjectList[_objectIndex].boundingBoxSize = 0; + } +} diff --git a/src/engine/objects/Thwomp.h b/src/engine/objects/Thwomp.h index 6822b4e31b..a76872093d 100644 --- a/src/engine/objects/Thwomp.h +++ b/src/engine/objects/Thwomp.h @@ -2,10 +2,15 @@ #include #include - #include "engine/World.h" +#include "engine/SpawnParams.h" +#include "engine/CoreMath.h" + #include "engine/objects/Object.h" +class World; +extern World gWorldInstance; + extern "C" { #include "macros.h" #include "main.h" @@ -42,9 +47,24 @@ class OThwomp : public OObject { JAILED // Has no collision }; - States State = States::DISABLED; + // This is simply a helper function to keep Spawning code clean + static inline OThwomp* Spawn(s16 x, s16 z, s16 direction, f32 scale, s16 behaviour, s16 primAlpha, u16 boundingBoxSize = 7) { + IRotator rot; + rot.Set(0, direction, 0); + + SpawnParams params = { + .Name = "mk:thwomp", + .Behaviour = behaviour, + .Location = FVector(x, 0, z), + .Rotation = rot, + .Scale = FVector(0, scale, 0), + .PrimAlpha = primAlpha, + .BoundingBoxSize = boundingBoxSize + }; + return static_cast(gWorldInstance.AddObject(new OThwomp(params))); + } - explicit OThwomp(s16 x, s16 z, s16 direction, f32 scale, s16 behaviour, s16 primAlpha, u16 boundingBoxSize = 7); + explicit OThwomp(const SpawnParams& params); ~OThwomp() { _count--; @@ -54,8 +74,10 @@ class OThwomp : public OObject { return _count; } + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick60fps() override; virtual void Draw(s32 cameraId) override; + virtual void DrawEditorProperties() override; void SetVisibility(s32 objectIndex); void func_80080B28(s32 objectIndex, s32 playerId); void DrawModel(s32); @@ -109,6 +131,10 @@ class OThwomp : public OObject { void func_8008078C(s32 objectIndex); void func_8007E63C(s32 objectIndex); + + u16 BoundingBoxSize; + OThwomp::States Behaviour; + int16_t PrimAlpha; private: static size_t _count; static size_t _rand; @@ -116,5 +142,4 @@ class OThwomp : public OObject { s16 _faceDirection; //! @todo Write this better. This effects the squish size and the bounding box size. // We should probably return to the programmer the pointer to the actor so they can do thwomp->squishSize = value. - u16 _boundingBoxSize; }; diff --git a/src/engine/objects/TrashBin.cpp b/src/engine/objects/TrashBin.cpp index 57c7022750..871151b8fe 100644 --- a/src/engine/objects/TrashBin.cpp +++ b/src/engine/objects/TrashBin.cpp @@ -21,12 +21,13 @@ extern "C" { #define DEGREES_FLOAT_TO_SHORT(Degrees) ((s16)((Degrees) * (0x8000 / 180.0f))) -OTrashBin::OTrashBin(const FVector& pos, const IRotator& rotation, f32 scale, OTrashBin::Behaviour bhv) { - Name = "Trashbin"; - _pos = pos; - _rot = rotation; - _scale = scale; - _bhv = bhv; +OTrashBin::OTrashBin(const SpawnParams& params) : OObject(params) { + Name = "Trash Bin"; + ResourceName = "mk:trash_bin"; + _pos = params.Location.value_or(FVector(0, 0, 0)); + _rot = params.Rotation.value_or(IRotator(0, 0, 0)); + _scale = params.Scale.value_or(FVector(0, 0, 0)).y; // Only y + _bhv = static_cast(params.Behaviour.value_or(0)); find_unused_obj_index(&_objectIndex); diff --git a/src/engine/objects/TrashBin.h b/src/engine/objects/TrashBin.h index 3e878e1c01..4f2ea41323 100644 --- a/src/engine/objects/TrashBin.h +++ b/src/engine/objects/TrashBin.h @@ -20,11 +20,24 @@ extern "C" { class OTrashBin : public OObject { public: - enum Behaviour { + enum Behaviour : int16_t { STATIC, // The lid stays shut MUNCHING // The lid opens/closes in a scary munching manner }; - explicit OTrashBin(const FVector& pos, const IRotator& rotation, f32 scale, OTrashBin::Behaviour bhv); + + // This is simply a helper function to keep Spawning code clean + static inline OTrashBin* Spawn(const FVector& pos, const IRotator& rot, f32 scale, OTrashBin::Behaviour bhv) { + SpawnParams params = { + .Name = "mk:trash_bin", + .Behaviour = bhv, + .Location = pos, + .Rotation = rot, + .Scale = FVector(0, scale, 0), + }; + return static_cast(gWorldInstance.AddObject(new OTrashBin(params))); + } + + explicit OTrashBin(const SpawnParams& params); virtual void Tick() override; virtual void Draw(s32 cameraId) override; diff --git a/src/engine/objects/Trophy.cpp b/src/engine/objects/Trophy.cpp index e3713158d7..ba54359532 100644 --- a/src/engine/objects/Trophy.cpp +++ b/src/engine/objects/Trophy.cpp @@ -20,12 +20,14 @@ extern "C" { #include "menu_items.h" } -OTrophy::OTrophy(const FVector& pos, TrophyType trophy, Behaviour bhv) { +OTrophy::OTrophy(const SpawnParams& params) : OObject(params) { Name = "Trophy"; - _trophy = trophy; - _spawnPos = pos; - _spawnPos.y += 16.0f; // Adjust the height so the trophy sits on the surface when positioned to 0,0,0 - _bhv = bhv; + ResourceName = "mk:trophy"; + _type = static_cast(params.Type.value_or(0)); + FVector spawnPos = params.Location.value_or(FVector(0, 0, 0)); + spawnPos.y += 16.0f; // Adjust the height so the trophy sits on the surface when positioned to 0,0,0 + SpawnPos = spawnPos; // Don't save the + 16.0f adjustment + _bhv = static_cast(params.Behaviour.value_or(0)); find_unused_obj_index(&_objectIndex); @@ -34,10 +36,10 @@ OTrophy::OTrophy(const FVector& pos, TrophyType trophy, Behaviour bhv) { // Thus this will need to be changed if that's not desired. gTrophyIndex = _objectIndex; - if (bhv == OTrophy::Behaviour::PODIUM_CEREMONY) { + if (_bhv == OTrophy::Behaviour::PODIUM_CEREMONY) { _toggleVisibility = &D_801658CE; } else { - _toggle = 1; + _toggle = true; _toggleVisibility = &_toggle; _isMod = true; } @@ -47,7 +49,7 @@ OTrophy::OTrophy(const FVector& pos, TrophyType trophy, Behaviour bhv) { init_object(_objectIndex, 0); } - switch (trophy) { + switch (_type) { case TrophyType::GOLD: gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl10; break; @@ -83,16 +85,23 @@ OTrophy::OTrophy(const FVector& pos, TrophyType trophy, Behaviour bhv) { } Object *object = &gObjectList[_objectIndex]; - object->origin_pos[0] = _spawnPos.x; - object->origin_pos[1] = _spawnPos.y; - object->origin_pos[2] = _spawnPos.z; - object->pos[0] = _spawnPos.x; - object->pos[1] = _spawnPos.y; - object->pos[2] = _spawnPos.z; + object->origin_pos[0] = spawnPos.x; + object->origin_pos[1] = spawnPos.y; + object->origin_pos[2] = spawnPos.z; + object->pos[0] = spawnPos.x; + object->pos[1] = spawnPos.y; + object->pos[2] = spawnPos.z; _emitter = reinterpret_cast(gWorldInstance.AddEmitter(new StarEmitter())); } +void OTrophy::SetSpawnParams(SpawnParams& params) { + OObject::SetSpawnParams(params); + Object *object = &gObjectList[_objectIndex]; + params.Type = _type; + params.Behaviour = _bhv; +} + void OTrophy::Tick() { // func_80086D80 s32 objectIndex = _objectIndex; s32 var_s0; @@ -123,8 +132,9 @@ void OTrophy::Tick() { // func_80086D80 case OTrophy::Behaviour::STATIONARY: if (gObjectList[objectIndex].state != 0) { gObjectList[objectIndex].sizeScaling = 0.025f; - set_obj_origin_pos(objectIndex, _spawnPos.x, - _spawnPos.y + 16.0, _spawnPos.z); + + set_obj_origin_pos(objectIndex, SpawnPos.x, + SpawnPos.y + 16.0, SpawnPos.z); set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); set_obj_direction_angle(objectIndex, 0U, 0U, 0U); gObjectList[objectIndex].unk_084[1] = 0x0200; @@ -221,9 +231,14 @@ void OTrophy::Draw(s32 cameraId) { if (*_toggleVisibility == true) { object = &gObjectList[listIndex]; if (object->state >= 2) { - gSPMatrix(gDisplayListHead++, GetPerspMatrix(0), + // Prevents a perspective glitch + if (CVarGetInteger("gFreecam", 0) == true) { + cameraId = CAMERA_FREECAM; + } + + gSPMatrix(gDisplayListHead++, GetPerspMatrix(cameraId), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); - gSPMatrix(gDisplayListHead++, GetLookAtMatrix(0), + gSPMatrix(gDisplayListHead++, GetLookAtMatrix(cameraId), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); mtxf_set_matrix_transformation(someMatrix1, object->pos, object->direction_angle, object->sizeScaling); //convert_to_fixed_point_matrix(&gGfxPool->mtxHud[gMatrixHudCount], someMatrix1); @@ -234,7 +249,7 @@ void OTrophy::Draw(s32 cameraId) { gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D0077A0); gSPDisplayList(gDisplayListHead++, object->model); - gSPMatrix(gDisplayListHead++, GetLookAtMatrix(0), + gSPMatrix(gDisplayListHead++, GetLookAtMatrix(cameraId), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); mtxf_identity(someMatrix2); render_set_position(someMatrix2, 0); @@ -355,3 +370,61 @@ void OTrophy::func_80086C6C(s32 objectIndex) { _emitter->Emit(sp24, D_801658F4); } } + +void OTrophy::DrawEditorProperties() { + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = GetLocation(); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Cup"); + ImGui::SameLine(); + + int32_t type = static_cast(_type); + const char* items[] = { "Bronze", "Silver", "Gold", "Bronze 150", "Silver 150", "Gold 150" }; + + if (ImGui::Combo("##Type", &type, items, IM_ARRAYSIZE(items))) { + _type = static_cast(type); + + switch (_type) { + case TrophyType::GOLD: + gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl10; + break; + case TrophyType::SILVER: + gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl12; + break; + case TrophyType::BRONZE: + gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl14; + break; + case TrophyType::GOLD_150: + gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl11; + break; + case TrophyType::SILVER_150: + gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl13; + break; + case TrophyType::BRONZE_150: + gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl15; + break; + } + } + + ImGui::Text("Behaviour"); + ImGui::SameLine(); + + int32_t behaviour = static_cast(_bhv); + const char* items2[] = { "Podium Ceremony", "Stationary", "Opposing Dual-axis Rotation", "Single-axis Rotation", "Go Fish" }; + + if (ImGui::Combo("##Behaviour", &behaviour, items2, IM_ARRAYSIZE(items2))) { + _bhv = static_cast(behaviour); + } +} diff --git a/src/engine/objects/Trophy.h b/src/engine/objects/Trophy.h index 016115dcd2..628248edcd 100644 --- a/src/engine/objects/Trophy.h +++ b/src/engine/objects/Trophy.h @@ -19,7 +19,7 @@ extern "C" { class OTrophy : public OObject { public: - enum TrophyType { + enum TrophyType : int16_t { BRONZE, SILVER, GOLD, @@ -28,7 +28,7 @@ class OTrophy : public OObject { GOLD_150, }; - enum Behaviour { + enum Behaviour : int16_t { PODIUM_CEREMONY, STATIONARY, ROTATE, // A dual-axis opposing rotation @@ -36,10 +36,23 @@ class OTrophy : public OObject { GO_FISH, }; - explicit OTrophy(const FVector& pos, TrophyType trophy, Behaviour bhv); + // This is simply a helper function to keep Spawning code clean + static inline OTrophy* Spawn(const FVector& pos, TrophyType trophy, Behaviour bhv) { + SpawnParams params = { + .Name = "mk:trophy", + .Type = trophy, + .Behaviour = bhv, + .Location = pos, + }; + return static_cast(gWorldInstance.AddObject(new OTrophy(params))); + } + explicit OTrophy(const SpawnParams& params); + + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void DrawEditorProperties() override; void func_80086700(s32 objectIndex); void func_80086940(s32 objectIndex); void func_80086C14(s32 objectIndex); @@ -48,8 +61,7 @@ class OTrophy : public OObject { private: StarEmitter* _emitter; - TrophyType _trophy; - FVector _spawnPos; + TrophyType _type; Behaviour _bhv; int8_t _toggle; int8_t *_toggleVisibility; diff --git a/src/engine/vehicles/Boat.cpp b/src/engine/vehicles/Boat.cpp index 44e68da1a7..5a74dc6649 100644 --- a/src/engine/vehicles/Boat.cpp +++ b/src/engine/vehicles/Boat.cpp @@ -1,6 +1,8 @@ #include #include "Boat.h" #include +#include "Utils.h" +#include "port/Game.h" extern "C" { #include "macros.h" @@ -15,19 +17,35 @@ extern s8 gPlayerCount; } size_t ABoat::_count = 0; +std::map> ABoat::BoatCounts; -ABoat::ABoat(f32 speed, u32 waypoint) { +ABoat::ABoat(const SpawnParams& params) : AActor(params) { Name = "Paddle Steam Boat"; - Path2D* temp_a2; - u16 waypointOffset; + ResourceName = "mk:paddle_boat"; + BoundingBoxSize = 2.0f; + TrackPathPoint* temp_a2; Index = _count; - Speed = speed; + Speed = params.Speed.value_or(0); // Set to the default value std::fill(SmokeParticles, SmokeParticles + 128, NULL_OBJECT_ID); - waypointOffset = waypoint; - temp_a2 = &gVehicle2DPathPoint[waypointOffset]; + ABoat::SpawnMode spawnMode = static_cast(params.Type.value_or(SpawnMode::POINT)); + uint32_t pathIndex = params.PathIndex.value_or(0); + uint32_t pathPoint = 0; + + switch(spawnMode) { + case SpawnMode::POINT: // Spawn train at a specific path point + pathPoint = params.PathPoint.value_or(0); + BoatCounts[pathIndex].push_back(pathPoint); + break; + case SpawnMode::AUTO: // Automatically distribute trains based on a specific path point + pathPoint = GetVehiclePathPointDistributed(BoatCounts[pathIndex], gVehiclePathSize); + BoatCounts[pathIndex].push_back(pathPoint); + break; + } + + temp_a2 = &gVehicle2DPathPoint[pathPoint]; Position[0] = temp_a2->x; Position[1] = D_80162EB2; Position[2] = temp_a2->z; @@ -54,6 +72,14 @@ ABoat::ABoat(f32 speed, u32 waypoint) { _count++; } +void ABoat::SetSpawnParams(SpawnParams& params) { + AActor::SetSpawnParams(params); + params.Type = static_cast(SpawnType); + params.Speed = Speed; + params.PathIndex = PathIndex; + params.PathPoint = PathPoint; +} + void ABoat::Draw(Camera* camera) { } @@ -62,7 +88,7 @@ bool ABoat::IsMod() { } void ABoat::Tick() { - Path2D* waypoint; + TrackPathPoint* waypoint; struct Actor* paddleBoatActor; f32 temp_f26; f32 temp_f28; @@ -106,7 +132,7 @@ void ABoat::Tick() { sp94[0] = temp_f26; sp94[1] = temp_f28; sp94[2] = temp_f30; - waypoint = &gVehicle2DPathPoint[(WaypointIndex + 5) % gVehicle2DPathLength]; + waypoint = &gVehicle2DPathPoint[(WaypointIndex + 5) % gVehiclePathSize]; sp88[0] = (f32) waypoint->x; sp88[1] = (f32) D_80162EB0; sp88[2] = (f32) waypoint->z; @@ -194,4 +220,56 @@ s32 ABoat::AddSmoke(size_t ferryIndex, Vec3f pos, f32 velocity) { } return objectIndex; -} \ No newline at end of file +} + +void ABoat::DrawEditorProperties() { + ImGui::Text("Spawn Mode"); + ImGui::SameLine(); + + int32_t type = static_cast(SpawnType); + const char* items[] = { "POINT", "AUTO" }; + + if (ImGui::Combo("##Type", &type, items, IM_ARRAYSIZE(items))) { + SpawnType = static_cast(type); + } + + if (SpawnType == ABoat::SpawnMode::POINT) { + ImGui::Text("Path Index"); + ImGui::SameLine(); + + int pathIndex = static_cast(PathIndex); + if (ImGui::InputInt("##PathIndex", &pathIndex)) { + if (pathIndex < 0) pathIndex = 0; + PathIndex = static_cast(pathIndex); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathIndex")) { + PathIndex = 0; + } + + ImGui::Text("Path Point"); + ImGui::SameLine(); + + int pathPoint = static_cast(PathPoint); + if (ImGui::InputInt("##PathPoint", &pathPoint)) { + if (pathPoint < 0) pathPoint = 0; + PathPoint = static_cast(pathPoint); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathPoint")) { + PathPoint = 0; + } + } + + ImGui::Text("Speed"); + ImGui::SameLine(); + + float speed = Speed; + if (ImGui::DragFloat("##Speed", &speed, 0.1f)) { + Speed = speed; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeed")) { + Speed = 0.0f; + } +} diff --git a/src/engine/vehicles/Boat.h b/src/engine/vehicles/Boat.h index dcdd89977f..a82679faf0 100644 --- a/src/engine/vehicles/Boat.h +++ b/src/engine/vehicles/Boat.h @@ -3,6 +3,9 @@ #include #include "Actor.h" #include +#include "engine/SpawnParams.h" +#include "engine/CoreMath.h" +#include "engine/World.h" extern "C" { #include "main.h" @@ -12,7 +15,12 @@ extern "C" { class ABoat : public AActor { public: - const char* Type = "mk:boat"; + enum SpawnMode : uint16_t { + POINT, // Spawn boat at a specific path point + AUTO, // Automatically distribute boats based on a specific path point + }; + + const char* Type = "mk:paddle_boat"; size_t Index; bool IsActive; // The paddle wheel boat only shows up if the number of players is < 3 Vec3f Position; @@ -28,7 +36,7 @@ class ABoat : public AActor { int16_t AnotherSmokeTimer = 0; int16_t SmokeTimer = 0; - explicit ABoat(f32 speed, uint32_t waypoint); + explicit ABoat(const SpawnParams& params); ~ABoat() { _count--; @@ -38,12 +46,30 @@ class ABoat : public AActor { return _count; } + // This is simply a helper function to keep Spawning code clean + static inline ABoat* Spawn(f32 speed, uint32_t pathIndex, uint32_t pathPoint, ABoat::SpawnMode spawnMode) { + SpawnParams params = { + .Name = "mk:paddle_boat", + .Type = static_cast(spawnMode), + .PathIndex = pathIndex, + .PathPoint = pathPoint, + .Speed = speed, + }; + return static_cast(gWorldInstance.AddActor(new ABoat(params))); + } + + ABoat::SpawnMode SpawnType = ABoat::SpawnMode::AUTO; + uint32_t PathIndex = 0; + uint32_t PathPoint = 0; + + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; virtual void Draw(Camera* camera) override; virtual void VehicleCollision(s32 playerId, Player* player) override; virtual s32 AddSmoke(size_t, Vec3f, f32); virtual bool IsMod() override; + virtual void DrawEditorProperties() override; private: static size_t _count; - -}; \ No newline at end of file + static std::map> BoatCounts; +}; diff --git a/src/engine/vehicles/Bus.cpp b/src/engine/vehicles/Bus.cpp index 57ff16bff1..07057d7b05 100644 --- a/src/engine/vehicles/Bus.cpp +++ b/src/engine/vehicles/Bus.cpp @@ -1,6 +1,8 @@ #include #include "Bus.h" #include +#include "engine/vehicles/Utils.h" +#include "port/Game.h" extern "C" { #include "macros.h" @@ -17,20 +19,39 @@ extern s8 gPlayerCount; } size_t ABus::_count = 0; +std::map> ABus::BusCounts; -ABus::ABus(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint) { +ABus::ABus(const SpawnParams& params) : AActor(params) { Name = "Bus"; + ResourceName = "mk:bus"; + BoundingBoxSize = 2.0f; TrackPathPoint* temp_v0; u16 waypointOffset; s32 numWaypoints = gPathCountByPathIndex[0]; Index = _count; + PathIndex = params.PathIndex.value_or(0); + PathPoint = 0; - waypointOffset = waypoint; - temp_v0 = &path[waypointOffset]; - Position[0] = (f32) temp_v0->posX; - Position[1] = (f32) temp_v0->posY; - Position[2] = (f32) temp_v0->posZ; + SpawnType = static_cast(params.Type.value_or(0)); + switch(SpawnType) { + case SpawnMode::POINT: // Spawn bus at a specific path point + PathPoint = params.PathPoint.value_or(0); + BusCounts[PathIndex].push_back(PathPoint); + break; + case SpawnMode::AUTO: // Automatically distribute buses based on a specific path point + printf("vehicle path size %d\n", gVehiclePathSize); + PathPoint = GetVehiclePathPointDistributed(BusCounts[PathIndex], gVehiclePathSize); + BusCounts[PathIndex].push_back(PathPoint); + printf("train spawn path point: %d\n", PathPoint); + break; + } + + waypointOffset = PathPoint; + temp_v0 = &gTrackPaths[PathIndex][PathPoint]; + Position[0] = (f32) temp_v0->x; + Position[1] = (f32) temp_v0->y; + Position[2] = (f32) temp_v0->z; ActorIndex = -1; WaypointIndex = waypointOffset; Velocity[0] = 0.0f; @@ -43,9 +64,9 @@ ABus::ABus(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint) { } SomeMultiplierTheSequel = (f32) ((f64) (f32) (SomeType - 1) * 0.6); if (((gCCSelection > CC_50) || (gModeSelection == TIME_TRIALS)) && (SomeType == 2)) { - Speed = speedA; + Speed = params.Speed.value_or(0); } else { - Speed = speedB; + Speed = params.SpeedB.value_or(0); } Rotation[0] = 0; Rotation[2] = 0; @@ -62,6 +83,15 @@ ABus::ABus(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint) { _count++; } +void ABus::SetSpawnParams(SpawnParams& params) { + params.Name = ResourceName; + params.Type = static_cast(SpawnType); + params.PathIndex = PathIndex; + params.PathPoint = PathPoint; + params.Speed = Speed; + params.SpeedB = SpeedB; +} + bool ABus::IsMod() { return true; } @@ -280,3 +310,67 @@ void ABus::VehicleCollision(s32 playerId, Player* player) { } } } + +void ABus::DrawEditorProperties() { + ImGui::Text("Spawn Mode"); + ImGui::SameLine(); + + int32_t type = static_cast(SpawnType); + const char* items[] = { "POINT", "AUTO" }; + + if (ImGui::Combo("##Type", &type, items, IM_ARRAYSIZE(items))) { + SpawnType = static_cast(type); + } + + if (SpawnType == ABus::SpawnMode::POINT) { + ImGui::Text("Path Index"); + ImGui::SameLine(); + + int pathIndex = static_cast(PathIndex); + if (ImGui::InputInt("##PathIndex", &pathIndex)) { + if (pathIndex < 0) pathIndex = 0; + PathIndex = static_cast(pathIndex); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathIndex")) { + PathIndex = 0; + } + + ImGui::Text("Path Point"); + ImGui::SameLine(); + + int pathPoint = static_cast(PathPoint); + if (ImGui::InputInt("##PathPoint", &pathPoint)) { + if (pathPoint < 0) pathPoint = 0; + PathPoint = static_cast(pathPoint); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathPoint")) { + PathPoint = 0; + } + } + + ImGui::Text("Speed"); + ImGui::SameLine(); + + float speed = Speed; + if (ImGui::DragFloat("##Speed", &speed, 0.1f)) { + Speed = speed; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeed")) { + Speed = 0.0f; + } + + ImGui::Text("SpeedB"); + ImGui::SameLine(); + + float speed2 = SpeedB; + if (ImGui::DragFloat("##SpeedB", &speed2, 0.1f)) { + SpeedB = speed2; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeedB")) { + SpeedB = 0.0f; + } +} \ No newline at end of file diff --git a/src/engine/vehicles/Bus.h b/src/engine/vehicles/Bus.h index e1920dd7ff..28603c522c 100644 --- a/src/engine/vehicles/Bus.h +++ b/src/engine/vehicles/Bus.h @@ -3,6 +3,9 @@ #include #include "Actor.h" #include +#include "engine/SpawnParams.h" +#include "engine/CoreMath.h" +#include "engine/World.h" extern "C" { #include "main.h" @@ -13,6 +16,11 @@ extern "C" { class ABus : public AActor { public: + enum SpawnMode : uint16_t { + POINT, // Spawn car at a specific path point + AUTO, // Automatically distribute cars based on a specific path point + }; + const char* Type; size_t Index; f32 Speed; @@ -30,7 +38,20 @@ class ABus : public AActor { f32 SomeArg4 = 12.5f; u32 SoundBits = SOUND_ARG_LOAD(0x51, 0x01, 0x80, 0x03); - explicit ABus(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint); + // This is simply a helper function to keep Spawning code clean + static inline ABus* Spawn(f32 speedA, f32 speedB, uint32_t pathIndex, uint32_t pathPoint, ABus::SpawnMode spawnMode) { + SpawnParams params = { + .Name = "mk:bus", + .Type = static_cast(spawnMode), + .PathIndex = pathIndex, + .PathPoint = pathPoint, + .Speed = speedA, + .SpeedB = speedB + }; + return static_cast(gWorldInstance.AddActor(new ABus(params))); + } + + explicit ABus(const SpawnParams& params); ~ABus() { _count--; @@ -40,11 +61,19 @@ class ABus : public AActor { return _count; } + ABus::SpawnMode SpawnType = ABus::SpawnMode::AUTO; + float SpeedB = 0.0f; + uint32_t PathIndex = 0; + uint32_t PathPoint = 0; + + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; virtual void Draw(Camera* camera) override; virtual void VehicleCollision(s32 playerId, Player* player) override; virtual bool IsMod() override; + virtual void DrawEditorProperties() override; private: static size_t _count; + static std::map> BusCounts; }; \ No newline at end of file diff --git a/src/engine/vehicles/Car.cpp b/src/engine/vehicles/Car.cpp index f5a9b6a4af..8873d5248a 100644 --- a/src/engine/vehicles/Car.cpp +++ b/src/engine/vehicles/Car.cpp @@ -1,6 +1,8 @@ #include #include "Car.h" #include +#include "engine/vehicles/Utils.h" +#include "port/Game.h" extern "C" { #include "macros.h" @@ -17,20 +19,40 @@ extern s8 gPlayerCount; } size_t ACar::_count = 0; +// pathIndex, array of spawn points +std::map> ACar::CarCounts; -ACar::ACar(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint) { +ACar::ACar(const SpawnParams& params) : AActor(params) { Name = "Car"; + ResourceName = "mk:car"; + BoundingBoxSize = 2.0f; TrackPathPoint* temp_v0; u16 waypointOffset; s32 numWaypoints = gPathCountByPathIndex[0]; Index = _count; + PathIndex = params.PathIndex.value_or(0); + PathPoint = 0; - waypointOffset = waypoint; - temp_v0 = &path[waypointOffset]; - Position[0] = (f32) temp_v0->posX; - Position[1] = (f32) temp_v0->posY; - Position[2] = (f32) temp_v0->posZ; + SpawnType = static_cast(params.Type.value_or(0)); + switch(SpawnType) { + case SpawnMode::POINT: // Spawn car at a specific path point + PathPoint = params.PathPoint.value_or(0); + CarCounts[PathIndex].push_back(PathPoint); + break; + case SpawnMode::AUTO: // Automatically distribute cars based on a specific path point + printf("vehicle path size %d\n", gVehiclePathSize); + PathPoint = GetVehiclePathPointDistributed(CarCounts[PathIndex], gVehiclePathSize); + CarCounts[PathIndex].push_back(PathPoint); + printf("train spawn path point: %d\n", PathPoint); + break; + } + + waypointOffset = PathPoint; + temp_v0 = &gTrackPaths[PathIndex][PathPoint]; + Position[0] = (f32) temp_v0->x; + Position[1] = (f32) temp_v0->y; + Position[2] = (f32) temp_v0->z; ActorIndex = -1; WaypointIndex = waypointOffset; Velocity[0] = 0.0f; @@ -43,9 +65,9 @@ ACar::ACar(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint) { } SomeMultiplierTheSequel = (f32) ((f64) (f32) (SomeType - 1) * 0.6); if (((gCCSelection > CC_50) || (gModeSelection == TIME_TRIALS)) && (SomeType == 2)) { - Speed = speedA; + Speed = params.Speed.value_or(0); } else { - Speed = speedB; + Speed = params.SpeedB.value_or(0); } Rotation[0] = 0; Rotation[2] = 0; @@ -62,6 +84,15 @@ ACar::ACar(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint) { _count++; } +void ACar::SetSpawnParams(SpawnParams& params) { + params.Name = "mk:car"; + params.Type = static_cast(SpawnType); + params.PathIndex = PathIndex; + params.PathPoint = PathPoint; + params.Speed = Speed; + params.SpeedB = SpeedB; +} + bool ACar::IsMod() { return true; } @@ -279,4 +310,68 @@ void ACar::VehicleCollision(s32 playerId, Player* player) { } } } -} \ No newline at end of file +} + +void ACar::DrawEditorProperties() { + ImGui::Text("Spawn Mode"); + ImGui::SameLine(); + + int32_t type = static_cast(SpawnType); + const char* items[] = { "POINT", "AUTO" }; + + if (ImGui::Combo("##Type", &type, items, IM_ARRAYSIZE(items))) { + SpawnType = static_cast(type); + } + + if (type == ACar::SpawnMode::POINT) { + ImGui::Text("Path Index"); + ImGui::SameLine(); + + int pathIndex = static_cast(PathIndex); + if (ImGui::InputInt("##PathIndex", &pathIndex)) { + if (pathIndex < 0) pathIndex = 0; + PathIndex = static_cast(pathIndex); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathIndex")) { + PathIndex = 0; + } + + ImGui::Text("Path Point"); + ImGui::SameLine(); + + int pathPoint = static_cast(PathPoint); + if (ImGui::InputInt("##PathPoint", &pathPoint)) { + if (pathPoint < 0) pathPoint = 0; + PathPoint = static_cast(pathPoint); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathPoint")) { + PathPoint = 0; + } + } + + ImGui::Text("Speed"); + ImGui::SameLine(); + + float speed = Speed; + if (ImGui::DragFloat("##Speed", &speed, 0.1f)) { + Speed = speed; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeed")) { + Speed = 0.0f; + } + + ImGui::Text("SpeedB"); + ImGui::SameLine(); + + float speed2 = SpeedB; + if (ImGui::DragFloat("##SpeedB", &speed2, 0.1f)) { + SpeedB = speed2; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeedB")) { + SpeedB = 0.0f; + } +} diff --git a/src/engine/vehicles/Car.h b/src/engine/vehicles/Car.h index 24badabb3b..f14af00ac2 100644 --- a/src/engine/vehicles/Car.h +++ b/src/engine/vehicles/Car.h @@ -3,6 +3,9 @@ #include #include "Actor.h" #include +#include "engine/SpawnParams.h" +#include "engine/CoreMath.h" +#include "engine/World.h" extern "C" { #include "main.h" @@ -13,7 +16,12 @@ extern "C" { class ACar : public AActor { public: - explicit ACar(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint); + enum SpawnMode : uint16_t { + POINT, // Spawn car at a specific path point + AUTO, // Automatically distribute cars based on a specific path point + }; + + explicit ACar(const SpawnParams& params); ~ACar() { _count--; @@ -23,6 +31,19 @@ class ACar : public AActor { return _count; } + // This is simply a helper function to keep Spawning code clean + static inline ACar* Spawn(f32 speedA, f32 speedB, uint32_t pathIndex, uint32_t pathPoint, ACar::SpawnMode spawnMode) { + SpawnParams params = { + .Name = "mk:car", + .Type = static_cast(spawnMode), + .PathIndex = pathIndex, + .PathPoint = pathPoint, + .Speed = speedA, + .SpeedB = speedB + }; + return static_cast(gWorldInstance.AddActor(new ACar(params))); + } + const char* Type; size_t Index; f32 Speed; @@ -40,11 +61,19 @@ class ACar : public AActor { f32 SomeArg4 = 8.5f; u32 SoundBits = SOUND_ARG_LOAD(0x51, 0x01, 0x80, 0x05); + ACar::SpawnMode SpawnType = ACar::SpawnMode::AUTO; + float SpeedB = 0.0f; + uint32_t PathIndex = 0; + uint32_t PathPoint = 0; + + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; virtual void Draw(Camera*) override; virtual void VehicleCollision(s32 playerId, Player* player) override; virtual bool IsMod() override; + virtual void DrawEditorProperties() override; private: static size_t _count; + static std::map> CarCounts; }; \ No newline at end of file diff --git a/src/engine/vehicles/TankerTruck.cpp b/src/engine/vehicles/TankerTruck.cpp index ed28b0d667..f8a3d3d2ae 100644 --- a/src/engine/vehicles/TankerTruck.cpp +++ b/src/engine/vehicles/TankerTruck.cpp @@ -1,6 +1,8 @@ #include #include "TankerTruck.h" #include +#include "engine/vehicles/Utils.h" +#include "port/Game.h" extern "C" { #include "macros.h" @@ -17,20 +19,39 @@ extern s8 gPlayerCount; } size_t ATankerTruck::_count = 0; +std::map> ATankerTruck::TruckCounts; -ATankerTruck::ATankerTruck(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint) { +ATankerTruck::ATankerTruck(const SpawnParams& params) : AActor(params) { Name = "Tanker Truck"; + ResourceName = "mk:tanker_truck"; + BoundingBoxSize = 2.0f; TrackPathPoint* temp_v0; u16 waypointOffset; s32 numWaypoints = gPathCountByPathIndex[0]; Index = _count; + PathIndex = params.PathIndex.value_or(0); + PathPoint = 0; - waypointOffset = waypoint; - temp_v0 = &path[waypointOffset]; - Position[0] = (f32) temp_v0->posX; - Position[1] = (f32) temp_v0->posY; - Position[2] = (f32) temp_v0->posZ; + ATankerTruck::SpawnMode spawnMode = static_cast(params.Type.value_or(0)); + switch(spawnMode) { + case SpawnMode::POINT: // Spawn truck at a specific path point + PathPoint = params.PathPoint.value_or(0); + TruckCounts[PathIndex].push_back(PathPoint); + break; + case SpawnMode::AUTO: // Automatically distribute trucks based on a specific path point + printf("vehicle path size %d\n", gVehiclePathSize); + PathPoint = GetVehiclePathPointDistributed(TruckCounts[PathIndex], gVehiclePathSize); + TruckCounts[PathIndex].push_back(PathPoint); + printf("train spawn path point: %d\n", PathPoint); + break; + } + + waypointOffset = PathPoint; + temp_v0 = &gTrackPaths[PathIndex][PathPoint]; + Position[0] = (f32) temp_v0->x; + Position[1] = (f32) temp_v0->y; + Position[2] = (f32) temp_v0->z; ActorIndex = -1; WaypointIndex = waypointOffset; Velocity[0] = 0.0f; @@ -43,9 +64,9 @@ ATankerTruck::ATankerTruck(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_ } SomeMultiplierTheSequel = (f32) ((f64) (f32) (SomeType - 1) * 0.6); if (((gCCSelection > CC_50) || (gModeSelection == TIME_TRIALS)) && (SomeType == 2)) { - Speed = speedA; + Speed = params.Speed.value_or(0); } else { - Speed = speedB; + Speed = params.SpeedB.value_or(0); } Rotation[0] = 0; Rotation[2] = 0; @@ -62,6 +83,15 @@ ATankerTruck::ATankerTruck(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_ _count++; } +void ATankerTruck::SetSpawnParams(SpawnParams& params) { + params.Name = ResourceName; + params.Type = static_cast(SpawnType); + params.PathIndex = PathIndex; + params.PathPoint = PathPoint; + params.Speed = Speed; + params.SpeedB = SpeedB; +} + bool ATankerTruck::IsMod() { return true; } @@ -280,3 +310,67 @@ void ATankerTruck::VehicleCollision(s32 playerId, Player* player) { } } } + +void ATankerTruck::DrawEditorProperties() { + ImGui::Text("Spawn Mode"); + ImGui::SameLine(); + + int32_t type = static_cast(SpawnType); + const char* items[] = { "POINT", "AUTO" }; + + if (ImGui::Combo("##Type", &type, items, IM_ARRAYSIZE(items))) { + SpawnType = static_cast(type); + } + + if (SpawnType == ATankerTruck::SpawnMode::POINT) { + ImGui::Text("Path Index"); + ImGui::SameLine(); + + int pathIndex = static_cast(PathIndex); + if (ImGui::InputInt("##PathIndex", &pathIndex)) { + if (pathIndex < 0) pathIndex = 0; + PathIndex = static_cast(pathIndex); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathIndex")) { + PathIndex = 0; + } + + ImGui::Text("Path Point"); + ImGui::SameLine(); + + int pathPoint = static_cast(PathPoint); + if (ImGui::InputInt("##PathPoint", &pathPoint)) { + if (pathPoint < 0) pathPoint = 0; + PathPoint = static_cast(pathPoint); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathPoint")) { + PathPoint = 0; + } + } + + ImGui::Text("Speed"); + ImGui::SameLine(); + + float speed = Speed; + if (ImGui::DragFloat("##Speed", &speed, 0.1f)) { + Speed = speed; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeed")) { + Speed = 0.0f; + } + + ImGui::Text("SpeedB"); + ImGui::SameLine(); + + float speed2 = SpeedB; + if (ImGui::DragFloat("##SpeedB", &speed2, 0.1f)) { + SpeedB = speed2; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeedB")) { + SpeedB = 0.0f; + } +} \ No newline at end of file diff --git a/src/engine/vehicles/TankerTruck.h b/src/engine/vehicles/TankerTruck.h index 85b501ead1..e8fdb74488 100644 --- a/src/engine/vehicles/TankerTruck.h +++ b/src/engine/vehicles/TankerTruck.h @@ -3,6 +3,9 @@ #include #include "Actor.h" #include +#include "engine/SpawnParams.h" +#include "engine/CoreMath.h" +#include "engine/World.h" extern "C" { #include "main.h" @@ -13,6 +16,11 @@ extern "C" { class ATankerTruck : public AActor { public: + enum SpawnMode : uint16_t { + POINT, // Spawn car at a specific path point + AUTO, // Automatically distribute cars based on a specific path point + }; + const char* Type; size_t Index; f32 Speed; @@ -30,7 +38,20 @@ class ATankerTruck : public AActor { f32 SomeArg4 = 12.5f; u32 SoundBits = SOUND_ARG_LOAD(0x51, 0x01, 0x80, 0x03); - explicit ATankerTruck(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint); + // This is simply a helper function to keep Spawning code clean + static inline ATankerTruck* Spawn(f32 speedA, f32 speedB, uint32_t pathIndex, uint32_t pathPoint, ATankerTruck::SpawnMode spawnMode) { + SpawnParams params = { + .Name = "mk:tanker_truck", + .Type = static_cast(spawnMode), + .PathIndex = pathIndex, + .PathPoint = pathPoint, + .Speed = speedA, + .SpeedB = speedB + }; + return static_cast(gWorldInstance.AddActor(new ATankerTruck(params))); + } + + explicit ATankerTruck(const SpawnParams& params); ~ATankerTruck() { _count--; @@ -40,11 +61,19 @@ class ATankerTruck : public AActor { return _count; } + ATankerTruck::SpawnMode SpawnType = ATankerTruck::SpawnMode::AUTO; + float SpeedB = 0.0f; + uint32_t PathIndex = 0; + uint32_t PathPoint = 0; + + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; virtual void Draw(Camera* camera) override; virtual void VehicleCollision(s32 playerId, Player* player) override; virtual bool IsMod() override; + virtual void DrawEditorProperties() override; private: static size_t _count; + static std::map> TruckCounts; }; \ No newline at end of file diff --git a/src/engine/vehicles/Train.cpp b/src/engine/vehicles/Train.cpp index ae249be815..6e07380336 100644 --- a/src/engine/vehicles/Train.cpp +++ b/src/engine/vehicles/Train.cpp @@ -4,6 +4,11 @@ #include "Train.h" #include +#include "engine/courses/Course.h" +#include "engine/vehicles/Utils.h" +#include "engine/World.h" +#include "port/Game.h" + extern "C" { #include "macros.h" #include "main.h" @@ -22,59 +27,77 @@ extern "C" { // #include "common_structs.h" } +// The two counts are so we can spawn trains at specific points or use auto distribution size_t ATrain::_count = 0; +// pathIndex, array of spawn points +std::map> ATrain::TrainCounts; -ATrain::ATrain(ATrain::TenderStatus tender, size_t numCarriages, f32 speed, uint32_t waypoint) { +ATrain::ATrain(const SpawnParams& params) : AActor(params) { Name = "Train"; - u16 waypointOffset; + ResourceName = "mk:train"; + BoundingBoxSize = 2.0f; TrainCarStuff* ptr1; - Path2D* pos; + TrackPathPoint* pos; Index = _count; - Speed = speed; + + PassengerCarsCount = params.Count.value_or(0); + bool tender = params.Bool.value_or(true); + + // The path to spawn the train at + uint32_t pathIndex = params.PathIndex.value_or(0); + // The point along the path to spawn the train at + uint32_t pathPoint = 0; + + SpawnType = static_cast(params.Type.value_or(SpawnMode::POINT)); + + switch(SpawnType) { + case SpawnMode::POINT: // Spawn train at a specific path point + pathPoint = params.PathPoint.value_or(0); + TrainCounts[pathIndex].push_back(pathPoint); + break; + case SpawnMode::AUTO: // Automatically distribute trains based on a specific path point + printf("vehicle path size %d\n", gVehiclePathSize); + pathPoint = GetVehiclePathPointDistributed(TrainCounts[pathIndex], gVehiclePathSize); + TrainCounts[pathIndex].push_back(pathPoint); + printf("train spawn path point: %d\n", pathPoint); + break; + } // Set to the default value std::fill(SmokeParticles, SmokeParticles + 128, NULL_OBJECT_ID); - for (size_t i = 0; i < numCarriages; i++) { + for (size_t i = 0; i < PassengerCarsCount; i++) { PassengerCars.push_back(TrainCarStuff()); } - // outputs 160 or 392 depending on the train. - // Wraps the value around to always output a valid waypoint. - waypointOffset = waypoint; - // 120.0f is about the maximum usable value for (size_t i = 0; i < PassengerCars.size(); i++) { - waypointOffset += 4; + pathPoint += 4; ptr1 = &PassengerCars[i]; - pos = &gVehicle2DPathPoint[waypointOffset]; - set_vehicle_pos_path_point(ptr1, pos, waypointOffset); + pos = &gVehicle2DPathPoint[pathPoint]; + set_vehicle_pos_path_point(ptr1, pos, pathPoint); } // Smaller offset for the tender - waypointOffset += 3; - pos = &gVehicle2DPathPoint[waypointOffset]; - set_vehicle_pos_path_point(&this->Tender, pos, waypointOffset); - waypointOffset += 4; - pos = &gVehicle2DPathPoint[waypointOffset]; - set_vehicle_pos_path_point(&Locomotive, pos, waypointOffset); + pathPoint += 3; + pos = &gVehicle2DPathPoint[pathPoint]; + set_vehicle_pos_path_point(&this->Tender, pos, pathPoint); + pathPoint += 4; + pos = &gVehicle2DPathPoint[pathPoint]; + set_vehicle_pos_path_point(&Locomotive, pos, pathPoint); // Only use locomotive unless overwritten below. - NumCars = LOCOMOTIVE_ONLY; - // Fall back in-case someone tries to spawn a train with carriages but no tender; not allowed. - if (numCarriages > 0) { + if (PassengerCarsCount > 0) { tender = HAS_TENDER; } Tender.isActive = static_cast(tender); - for (size_t i = 0; i < numCarriages; i++) { + for (size_t i = 0; i < PassengerCarsCount; i++) { PassengerCars[i].isActive = 1; } - NumCars = NUM_TENDERS + numCarriages; - AnotherSmokeTimer = 0; TrainCarStuff* tempLocomotive; @@ -126,6 +149,17 @@ ATrain::ATrain(ATrain::TenderStatus tender, size_t numCarriages, f32 speed, uint _count++; } +void ATrain::SetSpawnParams(SpawnParams& params) { + AActor::SetSpawnParams(params); + params.Name = "mk:train"; + params.Type = static_cast(SpawnType); + params.Bool = Tender.isActive; + params.Speed = Speed; + params.Count = PassengerCarsCount; + params.PathIndex = PathIndex; + params.PathPoint = PathPoint; +} + bool ATrain::IsMod() { return true; } @@ -144,29 +178,31 @@ void ATrain::SyncComponents(TrainCarStuff* trainCar, s16 orientationY) { trainCarActor->rot[1] = orientationY; } trainCarActor->velocity[0] = trainCar->velocity[0]; + trainCarActor->velocity[1] = trainCar->velocity[1]; trainCarActor->velocity[2] = trainCar->velocity[2]; } void ATrain::Tick() { - f32 temp_f20; TrainCarStuff* car; u16 oldWaypointIndex; s16 orientationYUpdate; - f32 temp_f22; s32 j; Vec3f smokePos; + FVector temp_f20 = { + Locomotive.position[0], + Locomotive.position[1], + Locomotive.position[2] + }; AnotherSmokeTimer += 1; oldWaypointIndex = (u16) Locomotive.waypointIndex; - temp_f20 = Locomotive.position[0]; - temp_f22 = Locomotive.position[2]; - orientationYUpdate = update_vehicle_following_path(Locomotive.position, (s16*) &Locomotive.waypointIndex, Speed); - Locomotive.velocity[0] = Locomotive.position[0] - temp_f20; - Locomotive.velocity[2] = Locomotive.position[2] - temp_f22; + Locomotive.velocity[0] = Locomotive.position[0] - temp_f20.x; + Locomotive.velocity[1] = Locomotive.position[1] - temp_f20.y; + Locomotive.velocity[2] = Locomotive.position[2] - temp_f20.z; sync_train_components(&Locomotive, orientationYUpdate); @@ -191,23 +227,27 @@ void ATrain::Tick() { car = &Tender; if (car->isActive == 1) { - temp_f20 = car->position[0]; - temp_f22 = car->position[2]; + temp_f20.x = car->position[0]; + temp_f20.y = car->position[1]; + temp_f20.z = car->position[2]; orientationYUpdate = update_vehicle_following_path(car->position, (s16*) &car->waypointIndex, Speed); - car->velocity[0] = car->position[0] - temp_f20; - car->velocity[2] = car->position[2] - temp_f22; + car->velocity[0] = car->position[0] - temp_f20.x; + car->velocity[1] = car->position[1] - temp_f20.y; + car->velocity[2] = car->position[2] - temp_f20.z; sync_train_components(car, orientationYUpdate); } for (j = 0; j < PassengerCars.size(); j++) { car = &PassengerCars[j]; if (car->isActive == 1) { - temp_f20 = car->position[0]; - temp_f22 = car->position[2]; + temp_f20.x = car->position[0]; + temp_f20.y = car->position[1]; + temp_f20.z = car->position[2]; orientationYUpdate = update_vehicle_following_path(car->position, (s16*) &car->waypointIndex, Speed); - car->velocity[0] = car->position[0] - temp_f20; - car->velocity[2] = car->position[2] - temp_f22; + car->velocity[0] = car->position[0] - temp_f20.x; + car->velocity[1] = car->position[1] - temp_f20.y; + car->velocity[2] = car->position[2] - temp_f20.z; sync_train_components(car, orientationYUpdate); } } @@ -274,3 +314,81 @@ s32 ATrain::AddSmoke(s32 trainIndex, Vec3f pos, f32 velocity) { } return objectIndex; } + +void ATrain::DrawEditorProperties() { + ImGui::Text("Passenger Cars"); + ImGui::SameLine(); + + int count = static_cast(PassengerCarsCount); + if (ImGui::InputInt("##Count", &count)) { + // Clamp to uint32_t range (only lower bound needed if assuming positive values) + if (count < 0) count = 0; + PassengerCarsCount = static_cast(count); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetCount")) { + PassengerCarsCount = 0; + } + + ImGui::Text("Spawn Mode"); + ImGui::SameLine(); + + int32_t type = static_cast(SpawnType); + const char* items[] = { "POINT", "AUTO" }; + + if (ImGui::Combo("##Type", &type, items, IM_ARRAYSIZE(items))) { + SpawnType = static_cast(type); + } + + if (type == ATrain::SpawnMode::POINT) { + ImGui::Text("Path Index"); + ImGui::SameLine(); + + int pathIndex = static_cast(PathIndex); + if (ImGui::InputInt("##PathIndex", &pathIndex)) { + if (pathIndex < 0) pathIndex = 0; + PathIndex = static_cast(pathIndex); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathIndex")) { + PathIndex = 0; + } + + ImGui::Text("Path Point"); + ImGui::SameLine(); + + int pathPoint = static_cast(PathPoint); + if (ImGui::InputInt("##PathPoint", &pathPoint)) { + if (pathPoint < 0) pathPoint = 0; + PathPoint = static_cast(pathPoint); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathPoint")) { + PathPoint = 0; + } + } + + ImGui::Text("Has Tender"); + ImGui::SameLine(); + + bool theBool = HasTender; + if (ImGui::Checkbox("##Bool", &theBool)) { + HasTender = static_cast(theBool); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetBool")) { + HasTender = TenderStatus::NO_TENDER; + } + + ImGui::Text("Speed"); + ImGui::SameLine(); + + float speed = Speed; + if (ImGui::DragFloat("##Speed", &speed, 0.1f)) { + Speed = speed; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeed")) { + Speed = 0.0f; + } +} diff --git a/src/engine/vehicles/Train.h b/src/engine/vehicles/Train.h index b5100586e1..1013ed4e43 100644 --- a/src/engine/vehicles/Train.h +++ b/src/engine/vehicles/Train.h @@ -1,8 +1,15 @@ #pragma once #include -#include "Actor.h" #include +#include "engine/SpawnParams.h" +#include "engine/CoreMath.h" +#include "engine/World.h" + +#include "Actor.h" + +class World; +extern World gWorldInstance; extern "C" { #include "main.h" @@ -16,6 +23,11 @@ extern "C" { class ATrain : public AActor { public: + enum SpawnMode : uint16_t { + POINT, // Spawn train at a specific path point + AUTO, // Automatically distribute trains based on a specific path point + }; + enum TenderStatus { NO_TENDER, HAS_TENDER, @@ -24,12 +36,14 @@ class ATrain : public AActor { TrainCarStuff Locomotive; TrainCarStuff Tender; std::vector PassengerCars; - f32 Speed; // 120.0f is about the maximum usable value + size_t PassengerCarsCount = 0; + ATrain::SpawnMode SpawnType = ATrain::SpawnMode::AUTO; + uint32_t PathIndex = 0; + uint32_t PathPoint = 0; + TenderStatus HasTender = TenderStatus::NO_TENDER; s32 SomeFlags; f32 SomeMultiplier; - size_t NumCars; // Non-locomotive car count? - const char* Type = "mk:train"; size_t Index; // Spawns the train in halves of the train path int32_t SmokeParticles[128]; @@ -37,7 +51,7 @@ class ATrain : public AActor { int16_t AnotherSmokeTimer = 0; int16_t SmokeTimer = 0; - explicit ATrain(ATrain::TenderStatus tender, size_t numCarriages, f32 speed, uint32_t waypoint); + explicit ATrain(const SpawnParams& params); ~ATrain() { _count--; @@ -47,13 +61,31 @@ class ATrain : public AActor { return _count; } + // This is simply a helper function to keep Spawning code clean + static inline ATrain* Spawn(ATrain::TenderStatus tender, size_t numCarriages, f32 speed, uint32_t pathIndex, uint32_t pathPoint, ATrain::SpawnMode spawnMode) { + SpawnParams params = { + .Name = "mk:train", + .Type = static_cast(spawnMode), + .Count = numCarriages, + .PathIndex = pathIndex, + .PathPoint = pathPoint, + .Bool = tender, + .Speed = speed, // 120.0f is about the maximum usable value + }; + return static_cast(gWorldInstance.AddActor(new ATrain(params))); + } + + virtual void SetSpawnParams(SpawnParams& params); virtual void Tick() override; virtual void Draw(Camera* camera) override; virtual void VehicleCollision(s32 playerId, Player* player) override; virtual bool IsMod() override; s32 AddSmoke(s32 trainIndex, Vec3f pos, f32 velocity); void SyncComponents(TrainCarStuff* trainCar, s16 orientationY); + virtual void DrawEditorProperties() override; private: - static size_t _count; -}; \ No newline at end of file + static size_t _count; // Total number of spawned trains +// pathIndex, array of spawn points + static std::map> TrainCounts; +}; diff --git a/src/engine/vehicles/Truck.cpp b/src/engine/vehicles/Truck.cpp index 2c7147d3e4..76914418b8 100644 --- a/src/engine/vehicles/Truck.cpp +++ b/src/engine/vehicles/Truck.cpp @@ -1,6 +1,8 @@ #include #include "Truck.h" #include +#include "engine/vehicles/Utils.h" +#include "port/Game.h" extern "C" { #include "macros.h" @@ -17,20 +19,39 @@ extern s8 gPlayerCount; } size_t ATruck::_count = 0; +std::map> ATruck::TruckCounts; -ATruck::ATruck(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint) { +ATruck::ATruck(const SpawnParams& params) : AActor(params) { Name = "Truck"; + ResourceName = "mk:truck"; + BoundingBoxSize = 2.0f; TrackPathPoint* temp_v0; u16 waypointOffset; s32 numWaypoints = gPathCountByPathIndex[0]; Index = _count; + PathIndex = params.PathIndex.value_or(0); + PathPoint = 0; - waypointOffset = waypoint; - temp_v0 = &path[waypointOffset]; - Position[0] = (f32) temp_v0->posX; - Position[1] = (f32) temp_v0->posY; - Position[2] = (f32) temp_v0->posZ; + SpawnType = static_cast(params.Type.value_or(0)); + switch(SpawnType) { + case SpawnMode::POINT: // Spawn truck at a specific path point + PathPoint = params.PathPoint.value_or(0); + TruckCounts[PathIndex].push_back(PathPoint); + break; + case SpawnMode::AUTO: // Automatically distribute trucks based on a specific path point + printf("vehicle path size %d\n", gVehiclePathSize); + PathPoint = GetVehiclePathPointDistributed(TruckCounts[PathIndex], gVehiclePathSize); + TruckCounts[PathIndex].push_back(PathPoint); + printf("train spawn path point: %d\n", PathPoint); + break; + } + + waypointOffset = PathPoint; + temp_v0 = &gTrackPaths[PathIndex][PathPoint]; + Position[0] = (f32) temp_v0->x; + Position[1] = (f32) temp_v0->y; + Position[2] = (f32) temp_v0->z; ActorIndex = -1; WaypointIndex = waypointOffset; Velocity[0] = 0.0f; @@ -43,9 +64,9 @@ ATruck::ATruck(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint) } SomeMultiplierTheSequel = (f32) ((f64) (f32) (SomeType - 1) * 0.6); if (((gCCSelection > CC_50) || (gModeSelection == TIME_TRIALS)) && (SomeType == 2)) { - Speed = speedA; + Speed = params.Speed.value_or(0); } else { - Speed = speedB; + Speed = params.SpeedB.value_or(0); } Rotation[0] = 0; Rotation[2] = 0; @@ -62,6 +83,15 @@ ATruck::ATruck(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint) _count++; } +void ATruck::SetSpawnParams(SpawnParams& params) { + params.Name = "mk:truck"; + params.Type = static_cast(SpawnType); + params.PathIndex = PathIndex; + params.PathPoint = PathPoint; + params.Speed = Speed; + params.SpeedB = SpeedB; +} + bool ATruck::IsMod() { return true; } @@ -280,3 +310,67 @@ void ATruck::VehicleCollision(s32 playerId, Player* player) { } } } + +void ATruck::DrawEditorProperties() { + ImGui::Text("Spawn Mode"); + ImGui::SameLine(); + + int32_t type = static_cast(SpawnType); + const char* items[] = { "POINT", "AUTO" }; + + if (ImGui::Combo("##Type", &type, items, IM_ARRAYSIZE(items))) { + SpawnType = static_cast(type); + } + + if (SpawnType == ATruck::SpawnMode::POINT) { + ImGui::Text("Path Index"); + ImGui::SameLine(); + + int pathIndex = static_cast(PathIndex); + if (ImGui::InputInt("##PathIndex", &pathIndex)) { + if (pathIndex < 0) pathIndex = 0; + PathIndex = static_cast(pathIndex); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathIndex")) { + PathIndex = 0; + } + + ImGui::Text("Path Point"); + ImGui::SameLine(); + + int pathPoint = static_cast(PathPoint); + if (ImGui::InputInt("##PathPoint", &pathPoint)) { + if (pathPoint < 0) pathPoint = 0; + PathPoint = static_cast(pathPoint); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathPoint")) { + PathPoint = 0; + } + } + + ImGui::Text("Speed"); + ImGui::SameLine(); + + float speed = Speed; + if (ImGui::DragFloat("##Speed", &speed, 0.1f)) { + Speed = speed; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeed")) { + Speed = 0.0f; + } + + ImGui::Text("SpeedB"); + ImGui::SameLine(); + + float speed2 = SpeedB; + if (ImGui::DragFloat("##SpeedB", &speed2, 0.1f)) { + SpeedB = speed2; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeedB")) { + SpeedB = 0.0f; + } +} diff --git a/src/engine/vehicles/Truck.h b/src/engine/vehicles/Truck.h index c9e6cb5ac1..c91fe54d83 100644 --- a/src/engine/vehicles/Truck.h +++ b/src/engine/vehicles/Truck.h @@ -3,6 +3,9 @@ #include #include "Actor.h" #include +#include "engine/SpawnParams.h" +#include "engine/CoreMath.h" +#include "engine/World.h" extern "C" { #include "main.h" @@ -13,6 +16,11 @@ extern "C" { class ATruck : public AActor { public: + enum SpawnMode : uint16_t { + POINT, // Spawn car at a specific path point + AUTO, // Automatically distribute cars based on a specific path point + }; + const char* Type; size_t Index; f32 Speed; @@ -30,7 +38,25 @@ class ATruck : public AActor { f32 SomeArg4 = 12.5f; u32 SoundBits = SOUND_ARG_LOAD(0x51, 0x01, 0x80, 0x03); - explicit ATruck(f32 speedA, f32 speedB, TrackPathPoint* path, uint32_t waypoint); + float SpeedB = 0.0f; + ATruck::SpawnMode SpawnType = ATruck::SpawnMode::AUTO; + uint32_t PathIndex = 0; + uint32_t PathPoint = 0; + + // This is simply a helper function to keep Spawning code clean + static inline ATruck* Spawn(f32 speedA, f32 speedB, uint32_t pathIndex, uint32_t pathPoint, ATruck::SpawnMode spawnMode) { + SpawnParams params = { + .Name = "mk:truck", + .Type = static_cast(spawnMode), + .PathIndex = pathIndex, + .PathPoint = pathPoint, + .Speed = speedA, + .SpeedB = speedB + }; + return static_cast(gWorldInstance.AddActor(new ATruck(params))); + } + + explicit ATruck(const SpawnParams& params); ~ATruck() { _count--; @@ -40,11 +66,14 @@ class ATruck : public AActor { return _count; } + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; virtual void Draw(Camera* camera) override; virtual void VehicleCollision(s32 playerId, Player* player) override; virtual bool IsMod() override; + virtual void DrawEditorProperties() override; private: static size_t _count; + static std::map> TruckCounts; }; \ No newline at end of file diff --git a/src/engine/vehicles/Utils.cpp b/src/engine/vehicles/Utils.cpp index d2a3ece576..6f51ea731c 100644 --- a/src/engine/vehicles/Utils.cpp +++ b/src/engine/vehicles/Utils.cpp @@ -10,3 +10,33 @@ extern "C" { uint32_t CalculateWaypointDistribution(size_t i, uint32_t numVehicles, size_t numWaypoints, uint32_t centerWaypoint) { return (uint32_t)(((i * numWaypoints) / numVehicles) + centerWaypoint) % numWaypoints; } + +uint32_t GetVehiclePathPointDistributed(std::vector& existingTrains, uint32_t numWaypoints) { + if (existingTrains.empty()) { + return 0; // first train at start + } + + // Sort trains along path + std::sort(existingTrains.begin(), existingTrains.end()); + + if (existingTrains.size() == 1) { + // Place train halfway around the path + return (existingTrains[0] + numWaypoints / 2) % numWaypoints; + } + + uint32_t bestGap = 0; + uint32_t bestPos = 0; + + for (size_t i = 0; i < existingTrains.size(); i++) { + uint32_t start = existingTrains[i]; + uint32_t end = existingTrains[(i + 1) % existingTrains.size()]; + uint32_t gap = (end + numWaypoints - start) % numWaypoints; + + if (gap > bestGap) { + bestGap = gap; + bestPos = (start + gap / 2) % numWaypoints; + } + } + + return bestPos; +} diff --git a/src/engine/vehicles/Utils.h b/src/engine/vehicles/Utils.h index e71c4a2887..e30fbed0bb 100644 --- a/src/engine/vehicles/Utils.h +++ b/src/engine/vehicles/Utils.h @@ -3,3 +3,4 @@ #include uint32_t CalculateWaypointDistribution(size_t i, uint32_t numVehicles, size_t numWaypoints, uint32_t centerWaypoint); +uint32_t GetVehiclePathPointDistributed(std::vector& existingTrains, uint32_t numWaypoints); diff --git a/src/enhancements/collision_viewer.c b/src/enhancements/collision_viewer.c index f45a9872f0..47c87a80ef 100644 --- a/src/enhancements/collision_viewer.c +++ b/src/enhancements/collision_viewer.c @@ -6,6 +6,7 @@ #include "main.h" #include #include +#include "engine/Matrix.h" #include "collision_viewer.h" #include "math_util.h" @@ -27,8 +28,8 @@ void render_collision(void) { G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_OPA_SURF | G_RM_AA_ZB_OPA_SURF2); gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE); - // Set matrix - gSPMatrix(gDisplayListHead++, &gIdentityMatrix, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); + mtxf_identity(matrix); + AddObjectMatrix(matrix, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); for (size_t i = 0; i < gCollisionMeshCount; i++) { // Load vertices for this tri @@ -57,7 +58,6 @@ void render_collision(void) { gCollisionMesh[i].vtx3->v.cn[0] = 50; gCollisionMesh[i].vtx3->v.cn[1] = 50; gCollisionMesh[i].vtx3->v.cn[2] = 50; - break; case DIRT: // gTexture64619C gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); diff --git a/src/enhancements/freecam/freecam.cpp b/src/enhancements/freecam/freecam.cpp index 2ada24895f..73b5a1c3de 100644 --- a/src/enhancements/freecam/freecam.cpp +++ b/src/enhancements/freecam/freecam.cpp @@ -5,6 +5,7 @@ #include #include #include "port/interpolation/FrameInterpolation.h" +#include "engine/Matrix.h" extern "C" { #include @@ -159,7 +160,7 @@ void freecam_mouse_manager(Camera* camera, Vec3f forwardVector) { } else { // Mouse controls // Calculate yaw (left/right) and pitch (up/down) changes if (wnd->GetMouseState(Ship::LUS_MOUSE_BTN_RIGHT)) { - yawChange = mouse.x * MOUSE_SENSITIVITY_X; + yawChange = -mouse.x * MOUSE_SENSITIVITY_X; pitchChange = mouse.y * MOUSE_SENSITIVITY_Y; } // Update rotational velocity based on mouse movement @@ -398,8 +399,6 @@ void freecam_update_controller(void) { // Note that D Pad as stick code has been removed. So if it's needed, it needs to be put back in. } -Mtx fPersp; -Mtx fLookAt; void freecam_render_setup(Camera* camera) { u16 perspNorm; Mat4 matrix; @@ -412,17 +411,17 @@ void freecam_render_setup(Camera* camera) { // Perspective (camera movement) FrameInterpolation_RecordOpenChild("freecam_persp", FrameInterpolation_GetCameraEpoch()); - guPerspective(&fPersp, &perspNorm, gCameraZoom[0], gScreenAspect, + guPerspective(GetPerspMatrix(4), &perspNorm, gCameraZoom[0], gScreenAspect, CM_GetProps()->NearPersp, CM_GetProps()->FarPersp, 1.0f); gSPPerspNormalize(gDisplayListHead++, perspNorm); - gSPMatrix(gDisplayListHead++, (&fPersp), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(gDisplayListHead++, GetPerspMatrix(4), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); FrameInterpolation_RecordCloseChild(); // LookAt (camera rotation) FrameInterpolation_RecordOpenChild("freecam_lookAt", FrameInterpolation_GetCameraEpoch()); - guLookAt(&fLookAt, camera->pos[0], camera->pos[1], camera->pos[2], camera->lookAt[0], + guLookAt(GetLookAtMatrix(4), camera->pos[0], camera->pos[1], camera->pos[2], camera->lookAt[0], camera->lookAt[1], camera->lookAt[2], camera->up[0], camera->up[1], camera->up[2]); - gSPMatrix(gDisplayListHead++, (&fLookAt), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + gSPMatrix(gDisplayListHead++, GetLookAtMatrix(4), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); FrameInterpolation_RecordCloseChild(); gDPPipeSync(gDisplayListHead++); diff --git a/src/enhancements/freecam/freecam_engine.c b/src/enhancements/freecam/freecam_engine.c index 56585e2f09..076c3163f0 100644 --- a/src/enhancements/freecam/freecam_engine.c +++ b/src/enhancements/freecam/freecam_engine.c @@ -57,7 +57,7 @@ void freecam_calculate_forward_vector_allow_rotation(Camera* camera, Vec3f forwa yaw = yaw * M_PI / 180.0f; // Calculate the forward vector based on yaw, ignoring pitch to keep height unchanged - forwardVector[0] = -sinf(yaw); + forwardVector[0] = sinf(yaw); forwardVector[1] = -sinf(pitch); // Keep the height unchanged forwardVector[2] = cosf(yaw); } diff --git a/src/main.c b/src/main.c index 8fefa60f39..076539b106 100644 --- a/src/main.c +++ b/src/main.c @@ -111,8 +111,8 @@ u8 gControllerBits; CollisionGrid gCollisionGrid[1024]; u16 gNumActors; u16 gMatrixObjectCount; -s32 gTickLogic; // Tick game physics at 60fps -s32 gTickVisuals; // Tick animations at 30fps +s32 gTickLogic = 2; +s32 gTickVisuals = 1; s32 gTickGame; f32 D_80150118; @@ -629,75 +629,6 @@ void game_init_clear_framebuffer(void) { clear_framebuffer(0); } -//! @deprecated -// This function was made to tick the game logic at native 60 fps. -// However, many game objects are not in that special tick loop and run at native 30fps. -// Thus adding `if (gTickVisuals) { // stuff here }` would prevent double speed and allow ticking visuals once every 30 fps. -// This does not however, create extra interpolated frames. Whereas a possible solution, it is not the best solution. -// This function should be cleaned up and removed, since frame interpolation now exists. -void calculate_updaterate(void) { - static u32 prevtime = 0; - static u32 remainder = 0; - static u32 logicAccumulator = 0; - static u32 visualsAccumulator = 0; - static u32 frameCounter = 0; // For tracking frames for logic updates - u32 now = SDL_GetTicks(); // Replaces osGetTime() - u32 frameRate = 0; - s32 total; - - // Get target FPS from configuration variable - s32 targetFPS = 30; - - if (targetFPS < 30) { - targetFPS = 30; - } - - // Detect frame rate based on time passed - if (now > prevtime) { - total = (now - prevtime) + remainder; - } else { - // Handle counter reset (shouldn't happen with SDL_GetTicks, but kept for logic parity) - total = (0xffffffff - prevtime) + 1 + now + remainder; - } - - prevtime = now; - - // Avoid division by zero - if (total > 0) { - // Calculate approximate frame rate (milliseconds per frame) - frameRate = 1000 / total; // Frame rate in frames per second - } else { - frameRate = targetFPS; // Fallback to target FPS - } - - // Default both to no updates - gTickLogic = 0; - gTickVisuals = 0; - - // Calculate the update rates based on target FPS - s32 logicUpdateInterval = 1000 / 60; // Time in ms between logic updates - s32 visualsUpdateInterval = 1000 / 30; // 30 FPS for visuals - - // Accumulate time for logic updates - logicAccumulator += total; - if (logicAccumulator >= logicUpdateInterval) { - logicAccumulator -= logicUpdateInterval; // Subtract full interval - if (targetFPS < 60) { - gTickLogic = 2; - } else { - gTickLogic = 2; // Perform logic update - } - } - - // Visual updates (based on 30 FPS equivalent) - visualsAccumulator += total; // Increment for each frame - if (visualsAccumulator >= visualsUpdateInterval) { // Check if it's time to update visuals - visualsAccumulator -= visualsUpdateInterval; - // gTickVisuals <-- Goes here to use the native 60fps system - } - gTickVisuals = 1; // Perform visual update -} - void display_debug_info(void) { u16 rotY; if (!gEnableDebugMode) { @@ -756,15 +687,13 @@ void process_game_tick(void) { // tick camera - // This looks like it should be in the switch. + // This looks like it should be in the switch below. // But it needs to be here for player 1 to work in all modes. - func_8001EE98(gPlayerOne, camera1, 0); - // Required if freecam was to have a new camera - //if (CVarGetInteger("gFreecam", 0) == true) { - // freecam(gFreecamCamera, gPlayerOne, 0); - //} else { - //func_8001EE98(gPlayerOne, camera1, 0); - //} + if (CVarGetInteger("gFreecam", 0) == true) { + freecam(gFreecamCamera, gPlayerOne, 0); + } else { + func_8001EE98(gPlayerOne, camera1, 0); + } // Editor requires this so the camera keeps moving while the game is paused. if (gIsEditorPaused == true) { @@ -804,6 +733,7 @@ void race_logic_loop(void) { ClearMatrixPools(); ClearObjectsMatrixPool(); Editor_ClearMatrix(); + Editor_CleanWorld(); // Clears all actors gMatrixObjectCount = 0; gMatrixEffectCount = 0; @@ -1271,7 +1201,6 @@ void thread5_iteration(void) { func_800CB2C4(); } #endif - calculate_updaterate(); if (GfxDebuggerIsDebugging()) { Graphics_PushFrame(gGfxPool->gfxPool); return; diff --git a/src/menu_items.c b/src/menu_items.c index c367587929..893ea83ed3 100644 --- a/src/menu_items.c +++ b/src/menu_items.c @@ -1521,9 +1521,13 @@ void func_80091FA4(void) { func_8009A344(); clear_menus(); func_80092258(); - add_menu_item(MENU_ITEM_TYPE_096, 0x00000064, 0x00000024, MENU_ITEM_PRIORITY_1); - add_menu_item(MENU_ITEM_TYPE_097, 0x00000064, 0x000000DD, MENU_ITEM_PRIORITY_1); - add_menu_item(MENU_ITEM_TYPE_098, 0, 0, MENU_ITEM_PRIORITY_0); + + // Do not display grand prix name/cup name in editor at race staging + if (CVarGetInteger("gEditorEnabled", 0) == false) { + add_menu_item(MENU_ITEM_TYPE_096, 0x00000064, 0x00000024, MENU_ITEM_PRIORITY_1); + add_menu_item(MENU_ITEM_TYPE_097, 0x00000064, 0x000000DD, MENU_ITEM_PRIORITY_1); + add_menu_item(MENU_ITEM_TYPE_098, 0, 0, MENU_ITEM_PRIORITY_0); + } add_menu_item(MENU_ITEM_PAUSE, 0, 0, MENU_ITEM_PRIORITY_0); if (gModeSelection == TIME_TRIALS) { add_menu_item(MENU_ITEM_TYPE_0BE, 0, 0, MENU_ITEM_PRIORITY_0); @@ -4988,10 +4992,10 @@ void func_8009C918(void) { D_8018E810[someIndex].y = D_8015F480[someIndex].screenHeight; } - D_8018E7E8[4].x = 0x00A0; - D_8018E7E8[4].y = 0x0078; - D_8018E810[4].x = 0x0140; - D_8018E810[4].y = 0x00F0; + D_8018E7E8[4].x = SCREEN_WIDTH / 2; + D_8018E7E8[4].y = SCREEN_HEIGHT / 2; + D_8018E810[4].x = SCREEN_WIDTH; + D_8018E810[4].y = SCREEN_HEIGHT; } void func_8009CA2C(void) { diff --git a/src/port/Game.cpp b/src/port/Game.cpp index 62dec7a166..2d42bb1ac1 100644 --- a/src/port/Game.cpp +++ b/src/port/Game.cpp @@ -49,6 +49,8 @@ #include "engine/editor/EditorMath.h" #include "engine/editor/SceneManager.h" #include "engine/Rulesets.h" +#include "engine/Registry.h" +#include "RegisteredActors.h" #ifdef _WIN32 #include @@ -74,6 +76,9 @@ extern "C" void Timer_Update(); // Create the world instance World gWorldInstance; +// Deferred cleaning when clearing all actors in the editor +bool bCleanWorld = false; + std::shared_ptr gPodiumCeremony; Cup* gMushroomCup; @@ -164,6 +169,7 @@ void CustomEngineInit() { // gModelLoader.Add(bowserStatueList); // gModelLoader.Load(); + RegisterGameActors(); } void CustomEngineDestroy() { @@ -204,10 +210,6 @@ void SetMarioRaceway(void) { gWorldInstance.CupIndex = 0; } -World* GetWorld(void) { - return &gWorldInstance; -} - u32 WorldNextCup(void) { return gWorldInstance.NextCup(); } @@ -367,10 +369,12 @@ void CM_TickActors() { } } -void CM_DrawActors(Camera* camera, struct Actor* actor) { - AActor* a = gWorldInstance.ConvertActorToAActor(actor); - if (a->IsMod()) { - a->Draw(camera); +void CM_DrawActors(Camera* camera) { + //AActor* a = gWorldInstance.ConvertActorToAActor(actor); + for (const auto& actor : gWorldInstance.Actors) { + if (actor->IsMod()) { + actor->Draw(camera); + } } } @@ -382,11 +386,18 @@ void CM_BeginPlay() { auto course = gWorldInstance.CurrentCourse; if (course) { + Editor::LoadLevel(course.get(), course->SceneFilePtr); + gRulesets.PreInit(); // Do not spawn finishline in credits or battle mode. And if bSpawnFinishline. if ((gGamestate != CREDITS_SEQUENCE) && (gModeSelection != BATTLE)) { if (course->bSpawnFinishline) { - gWorldInstance.AddActor(new AFinishline(course->FinishlineSpawnPoint)); + if (course->FinishlineSpawnPoint.has_value()) { + AFinishline::Spawn(course->FinishlineSpawnPoint.value(), IRotator(0, 0, 0)); + } else { + AFinishline::Spawn(); + } + } } gEditor.AddLight("Sun", nullptr, D_800DC610[1].l->l.dir); @@ -650,11 +661,13 @@ void CM_DeleteActor(size_t index) { * Clean up actors and other game objects. */ void CM_CleanWorld(void) { + printf("[Game.cpp] Clean World\n"); World* world = &gWorldInstance; for (auto& actor : world->Actors) { delete actor; } + gWorldInstance.Reset(); // Reset OObjects for (auto& object : world->Objects) { delete object; } @@ -678,15 +691,22 @@ void CM_CleanWorld(void) { gWorldInstance.Objects.clear(); gWorldInstance.Emitters.clear(); gWorldInstance.Lakitus.clear(); - gWorldInstance.Reset(); } struct Actor* CM_AddBaseActor() { return (struct Actor*) gWorldInstance.AddBaseActor(); } -void CM_AddEditorObject(struct Actor* actor, const char* name) { - gWorldInstance.AddEditorObject(actor, name); +void CM_ActorBeginPlay(struct Actor* actor) { + gWorldInstance.ActorBeginPlay(actor); +} + +void CM_ActorGenerateCollision(struct Actor* actor) { + AActor* act = gWorldInstance.ConvertActorToAActor(actor); + + if (act->Triangles.size() == 0) { + Editor::GenerateCollisionMesh(act, (Gfx*)LOAD_ASSET_RAW(act->Model), 1.0f); + } } void Editor_AddLight(s8* direction) { @@ -699,6 +719,13 @@ void Editor_ClearMatrix() { gEditor.ClearMatrixPool(); } +void Editor_CleanWorld() { + if (bCleanWorld) { + CM_CleanWorld(); + bCleanWorld = false; + } +} + size_t CM_GetActorSize() { return gWorldInstance.Actors.size(); } diff --git a/src/port/Game.h b/src/port/Game.h index ad4f61ca45..062b9aafbd 100644 --- a/src/port/Game.h +++ b/src/port/Game.h @@ -8,6 +8,8 @@ #ifdef __cplusplus #include "engine/editor/Editor.h" class Course; +struct Properties; +class World; extern "C" { #endif #include "camera.h" @@ -18,6 +20,7 @@ extern s32 gTrophyIndex; #ifdef __cplusplus extern Editor::Editor gEditor; extern HarbourMastersIntro gMenuIntro; +extern bool bCleanWorld; #endif Properties* CM_GetProps(); @@ -69,7 +72,7 @@ bool CM_DoesFinishlineExist(); void CM_InitClouds(); -void CM_DrawActors(Camera* camera, struct Actor* actor); +void CM_DrawActors(Camera* camera); void CM_DrawStaticMeshActors(); void CM_TickObjects(); @@ -81,6 +84,7 @@ void CM_DrawEditor(); void CM_Editor_SetLevelDimensions(s16 minX, s16 maxX, s16 minZ, s16 maxZ, s16 minY, s16 maxY); void CM_TickDraw(); void Editor_ClearMatrix(); +void Editor_CleanWorld(); void CM_TickParticles(void); void CM_DrawParticles(s32 cameraId); @@ -148,7 +152,8 @@ void SetCourseById(s32 course); struct Actor* CM_GetActor(size_t index); void CM_DeleteActor(size_t index); struct Actor* CM_AddBaseActor(); -void CM_AddEditorObject(struct Actor* actor, const char* name); +void CM_ActorBeginPlay(struct Actor* actor); +void CM_ActorGenerateCollision(struct Actor* actor); void Editor_AddLight(s8* direction); size_t CM_GetActorSize(); size_t CM_FindActorIndex(struct Actor* actor); diff --git a/src/port/interpolation/FrameInterpolation.h b/src/port/interpolation/FrameInterpolation.h index 51045a1a9e..320bd0f218 100644 --- a/src/port/interpolation/FrameInterpolation.h +++ b/src/port/interpolation/FrameInterpolation.h @@ -20,9 +20,10 @@ extern "C" { #define TAG_ITEM_ADDR(x) ((u32) 0x10000000 | (u32)x) #define TAG_SMOKE_DUST(x) ((u32) 0x20000000 | (u32) (x)) -#define TAG_LETTER(x) ((u32)0x30000000 | (u32) (uintptr_t) (x)) +#define TAG_LETTER(x) ((u32)0x30000000 | ((u32)(x) & 0x0FFFFFFF)) #define TAG_OBJECT(x) ((u32)0x40000000 | (u32) (uintptr_t) (x)) #define TAG_CLOUDS(x) ((u32)0x50000000 | (u32) (uintptr_t) (x)) +#define TAG_THWOMP(x) ((u32)0x60000000 | ((u32)(x) & 0x0FFFFFFF)) // Mask the bits so that the 7 can't get overridden #define TAG_TRACK(x) ((u32)0x70000000 | ((u32)(x) & 0x0FFFFFFF)) diff --git a/src/port/resource/importers/TrackPathPointFactory.cpp b/src/port/resource/importers/TrackPathPointFactory.cpp index bacda0074d..ccb29f2f3b 100644 --- a/src/port/resource/importers/TrackPathPointFactory.cpp +++ b/src/port/resource/importers/TrackPathPointFactory.cpp @@ -20,9 +20,9 @@ ResourceFactoryBinaryTrackPathPointsV0::ReadResource(std::shared_ptr for (uint32_t i = 0; i < count; i++) { TrackPathPoint data; - data.posX = reader->ReadInt16(); - data.posY = reader->ReadInt16(); - data.posZ = reader->ReadInt16(); + data.x = reader->ReadInt16(); + data.y = reader->ReadInt16(); + data.z = reader->ReadInt16(); data.trackSectionId = reader->ReadUInt16(); section->TrackPathPointList.push_back(data); @@ -52,9 +52,9 @@ ResourceFactoryXMLTrackPathPointsV0::ReadResource(std::shared_ptr fi while (pointElem != nullptr) { TrackPathPoint point; - point.posX = pointElem->IntAttribute("X"); - point.posY = pointElem->IntAttribute("Y"); - point.posZ = pointElem->IntAttribute("Z"); + point.x = pointElem->IntAttribute("X"); + point.y = pointElem->IntAttribute("Y"); + point.z = pointElem->IntAttribute("Z"); point.trackSectionId = pointElem->IntAttribute("ID"); waypointPath.push_back(point); // Push to temp vector diff --git a/src/port/ui/ContentBrowser.cpp b/src/port/ui/ContentBrowser.cpp index 2e09db01a7..e85f06d411 100644 --- a/src/port/ui/ContentBrowser.cpp +++ b/src/port/ui/ContentBrowser.cpp @@ -3,6 +3,7 @@ #include "UIWidgets.h" #include "libultraship/src/Context.h" #include "port/Engine.h" +#include "SpawnParams.h" #include #include @@ -19,7 +20,16 @@ #include "port/Game.h" #include "src/engine/editor/SceneManager.h" +#include "World.h" + +extern "C" { +#include "common_structs.h" +#include "actors.h" +#include "collision.h" +} + namespace Editor { + bool bIsTrainWindowOpen = false; // Global because member variables do not work in lambdas ContentBrowserWindow::~ContentBrowserWindow() { SPDLOG_TRACE("destruct content browser window"); @@ -79,46 +89,183 @@ namespace Editor { } } + // For C-actors + std::unordered_map> CActorList = { + { "Item Box", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_ITEM_BOX); + s32 height = spawn_actor_on_surface(position[0], position[1] + 10.0f, position[2]); + + Actor* actor = CM_GetActor(id); + actor->unk_08 = height; + actor->velocity[0] = position[1]; + actor->pos[1] = height - 20.0f; + + }}, + { "Fake Item Box", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_FAKE_ITEM_BOX); + + Actor* actor = CM_GetActor(id); + actor->state = 1; + actor->velocity[1] = position[1]; + }}, + { "Yoshi Egg", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_YOSHI_EGG); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + { "Piranha Plant", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_PIRANHA_PLANT); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + + { "Tree (Mario Raceway)", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_TREE_MARIO_RACEWAY); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + { "Tree (Yoshi Valley)", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_TREE_YOSHI_VALLEY); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + { "Tree (Royal Raceway)", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_TREE_ROYAL_RACEWAY); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + { "Tree (Moo Moo Farm)", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_TREE_MOO_MOO_FARM); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + { "Palm Tree", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_PALM_TREE); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + { "Tree (Luigi Raceway)", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_TREE_LUIGI_RACEWAY); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + { "Unknown Plant (0x1B)", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_UNKNOWN_0x1B); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + { "Tree (Peach's Castle)", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_TREE_PEACH_CASTLE); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + { "Tree (Frappe Snowland)", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_TREE_FRAPPE_SNOWLAND); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + { "Cactus 1 (Kalamari Desert)", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_CACTUS1_KALAMARI_DESERT); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + { "Cactus 2 (Kalamari Desert)", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_CACTUS2_KALAMARI_DESERT); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + { "Cactus 3 (Kalamari Desert)", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_CACTUS3_KALAMARI_DESERT); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + { "Bush (Bowser's Castle)", [](const FVector& pos) { + Vec3f position = {pos.x, pos.y, pos.z}; + Vec3s rot = {0, 0, 0}; + Vec3f vel = {0, 0, 0}; + s32 id = add_actor_to_empty_slot(position, rot, vel, ACTOR_BUSH_BOWSERS_CASTLE); + s32 height = spawn_actor_on_surface(position[0], position[1], position[2]); + }}, + }; + std::unordered_map> ActorList = { - { "Mario Sign", [](const FVector& pos) { return new AMarioSign(pos); } }, - { "Wario Sign", [](const FVector& pos) { return new AWarioSign(pos); } }, - { "Cloud", [](const FVector& pos) { return new ACloud(pos); } }, - { "Finishline", [](const FVector& pos) { return new AFinishline(pos); } }, - { "Ghostship", [](const FVector& pos) { return new AShip(pos, AShip::Skin::GHOSTSHIP); } }, - { "Ship_1", [](const FVector& pos) { return new AShip(pos, AShip::Skin::SHIP2); } }, - { "Ship_2", [](const FVector& pos) { return new AShip(pos, AShip::Skin::SHIP3); } }, - { "SpaghettiShip", [](const FVector& pos) { return new ASpaghettiShip(pos); } }, - { "Starship", [](const FVector& pos) { return new AStarship(pos); } }, - { "Train", [](const FVector& pos) { return new ATrain(ATrain::TenderStatus::HAS_TENDER, 4, 2.5f, 0); } }, - { "Boat", [](const FVector& pos) { return new ABoat((0.6666666f)/4, 0); } }, - { "Bus", [](const FVector& pos) { return new ABus(2.0f, 2.5f, &gTrackPaths[0][0], 0); } }, - { "Car", [](const FVector& pos) { return new ACar(2.0f, 2.5f, &gTrackPaths[0][0], 0); } }, - { "Truck", [](const FVector& pos) { return new ATruck(2.0f, 2.5f, &gTrackPaths[0][0], 0); } }, - { "Tanker Truck", [](const FVector& pos) { return new ATankerTruck(2.0f, 2.5f, &gTrackPaths[0][0], 0); } }, + // The banana gets attached to a player. This needs to be disconnected if this should be used in the editor +// { "Banana", [](const FVector& pos) { return gWorldInstance.AddActor(new ABanana(SpawnParams{.Name = "mk:banana", .Location = pos})); } }, + { "Falling Rock", [](const FVector& pos) { return AFallingRock::Spawn(pos, 80); } }, + { "Mario Sign", [](const FVector& pos) { return AMarioSign::Spawn(pos, IRotator(0, 0, 0), FVector(0, 0, 0), FVector(1.0f, 1.0f, 1.0f)); } }, + { "Wario Sign", [](const FVector& pos) { return AWarioSign::Spawn(pos, IRotator(0, 0, 0), FVector(0, 0, 0), FVector(1.0f, 1.0f, 1.0f)); } }, + { "Cloud", [](const FVector& pos) { return ACloud::Spawn(pos, 500, 3.0f, 200.0f); } }, + { "Finishline", [](const FVector& pos) { return AFinishline::Spawn(pos, IRotator(0, 0, 0)); } }, + { "Ghostship", [](const FVector& pos) { return AShip::Spawn(pos, IRotator(0, 0, 0), FVector(0.4f, 0.4f, 0.4f), AShip::Skin::GHOSTSHIP); } }, + { "Ship 1", [](const FVector& pos) { return AShip::Spawn(pos, IRotator(0, 0, 0), FVector(0.4f, 0.4f, 0.4f), AShip::Skin::SHIP2); } }, + { "Ship 2", [](const FVector& pos) { return AShip::Spawn(pos, IRotator(0, 0, 0), FVector(0.4f, 0.4f, 0.4f), AShip::Skin::SHIP3); } }, + { "SpaghettiShip", [](const FVector& pos) { return ASpaghettiShip::Spawn(pos, IRotator(0, 0, 0), FVector(0.4f, 0.4f, 0.4f)); } }, + { "Starship", [](const FVector& pos) { return AStarship::Spawn(pos, IRotator(0, 0, 0), FVector(1.5f, 1.5f, 1.5f), 0.01f, 150.0f); } }, + { "Train", [](const FVector& pos) { bIsTrainWindowOpen = true; return nullptr; } }, + { "Boat", [](const FVector& pos) { return ABoat::Spawn((0.6666666f)/4, 0, 0, ABoat::SpawnMode::AUTO); } }, + { "Bus", [](const FVector& pos) { return ABus::Spawn(2.0f, 2.5f, 0, 0, ABus::SpawnMode::AUTO); } }, + { "Car", [](const FVector& pos) { return ACar::Spawn(2.0f, 2.5f, 0, 0, ACar::SpawnMode::AUTO); } }, + { "Truck", [](const FVector& pos) { return ATruck::Spawn(2.0f, 2.5f, 0, 0, ATruck::SpawnMode::AUTO); } }, + { "Tanker Truck", [](const FVector& pos) { return ATankerTruck::Spawn(2.0f, 2.5f, 0, 0, ATankerTruck::SpawnMode::AUTO); } }, + { "Text", [](const FVector& pos) { return AText::Spawn("Harbour Masters", FVector(0, 0, 0), FVector(1.0f, 1.0f, 1.0f), AText::TextMode::STATIONARY, 0); } }, }; std::unordered_map> ObjectList = { - { "Bat", [](const FVector& pos) { return new OBat(pos, IRotator(0, 0, 0)); } }, - { "Bomb Kart", [](const FVector& pos) { return new OBombKart(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f); } }, - // { "Boos", [](const FVector& pos) { return new OBoos(pos, &gTrackPaths[0][0], 0, 0, 0.8333333f); } }, - { "CheepCheep", [](const FVector& pos) { return new OCheepCheep(pos, OCheepCheep::CheepType::RACE, IPathSpan(0, 10)); } }, - { "Crab", [](const FVector& pos) { return new OCrab(FVector2D(0, 10), FVector2D(20, 10)); } }, - { "ChainChomp", [](const FVector& pos) { return new OChainChomp(); } }, - { "Flagpole", [](const FVector& pos) { return new OFlagpole(pos, 0); } }, - { "Hedgehog", [](const FVector& pos) { return new OHedgehog(pos, FVector2D(0, 10), 0); } }, - { "HotAirBalloon", [](const FVector& pos) { return new OHotAirBalloon(pos); } }, + { "Bat", [](const FVector& pos) { return OBat::Spawn(pos, IRotator(0, 0, 0)); } }, + { "Bomb Kart", [](const FVector& pos) { return OBombKart::Spawn(pos, 1, 0.8333333f); } }, + { "Boos", [](const FVector& pos) { return OBoos::Spawn(5, IPathSpan(0, 50), IPathSpan(60, 90), IPathSpan(100, 140)); } }, + { "Cheep Cheep", [](const FVector& pos) { return OCheepCheep::Spawn(pos, OCheepCheep::Behaviour::RACE, IPathSpan(0, 10)); } }, + { "Crab", [](const FVector& pos) { return OCrab::Spawn(FVector2D(pos.x, pos.z), FVector2D(pos.x + 100, pos.z + 100)); } }, + + // Animation crash + // { "Chain Chomp", [](const FVector& pos) { return gWorldInstance.AddObject(new OChainChomp()); } }, + { "Flagpole", [](const FVector& pos) { return OFlagpole::Spawn(pos, 0); } }, + { "Hedgehog", [](const FVector& pos) { return OHedgehog::Spawn(pos, FVector2D(0, 10), 0); } }, + { "Hot Air Balloon", [](const FVector& pos) { return OHotAirBalloon::Spawn(pos); } }, { "Lakitu", [](const FVector& pos) { return new OLakitu(0, OLakitu::LakituType::STARTER); } }, // { "Mole", [](const FVector& pos) { return new OMole(pos, ); } }, // <-- Needs a group - { "Chick Penguin", [](const FVector& pos) { return new OPenguin(pos, 0, OPenguin::PenguinType::CHICK, OPenguin::Behaviour::SLIDE3); } }, - { "Penguin", [](const FVector& pos) { return new OPenguin(pos, 0, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE); } }, - { "Emperor Penguin", [](const FVector& pos) { return new OPenguin(pos, 0, OPenguin::PenguinType::EMPEROR, OPenguin::Behaviour::STRUT); } }, - { "Seagull", [](const FVector& pos) { return new OSeagull(pos); } }, - { "Thwomp", [](const FVector& pos) { return new OThwomp(pos.x, pos.z, 0, 1.0f, 0, 0, 2.0f); } }, - { "Trashbin", [](const FVector& pos) { return new OTrashBin(pos, IRotator(0, 0, 0), 1.0f, OTrashBin::Behaviour::MUNCHING); } }, - { "Trophy", [](const FVector& pos) { return new OTrophy(pos, OTrophy::TrophyType::GOLD_150, OTrophy::Behaviour::ROTATE2); } }, - { "Snowman", [](const FVector& pos) { return new OSnowman(pos); } }, - { "Podium", [](const FVector& pos) { return new OPodium(pos); } }, - { "Balloons", [](const FVector& pos) { return new OGrandPrixBalloons(pos); } }, + { "Penguin", [](const FVector& pos) { return OPenguin::Spawn(pos, 0x150, 0, 100.0f, OPenguin::PenguinType::ADULT, OPenguin::Behaviour::CIRCLE); } }, + { "Seagull", [](const FVector& pos) { return OSeagull::Spawn(pos); } }, + { "Thwomp", [](const FVector& pos) { return OThwomp::Spawn(pos.x, pos.z, 0, 1.0f, 1, 0, 7); } }, + { "Trash Bin", [](const FVector& pos) { return OTrashBin::Spawn(pos, IRotator(0, 0, 0), 1.0f, OTrashBin::Behaviour::MUNCHING); } }, + { "Trophy", [](const FVector& pos) { return OTrophy::Spawn(pos, OTrophy::TrophyType::GOLD_150, OTrophy::Behaviour::ROTATE2); } }, + { "Snowman", [](const FVector& pos) { return OSnowman::Spawn(pos); } }, + { "Podium", [](const FVector& pos) { return OPodium::Spawn(pos); } }, + { "Balloons", [](const FVector& pos) { return OGrandPrixBalloons::Spawn(pos); } }, }; void ContentBrowserWindow::AddTrackContent() { @@ -166,18 +313,34 @@ namespace Editor { FVector pos = GetPositionAheadOfCamera(300.0f); size_t i_actor = 0; + for (const auto& actor : CActorList) { + if ((i_actor != 0) && (i_actor % 8 == 0)) { + } else { + ImGui::SameLine(); + } + std::string label = fmt::format("{}##{}", actor.first, i_actor); + if (ImGui::Button(label.c_str())) { + actor.second(pos); + } + + i_actor += 1; + } + for (const auto& actor : ActorList) { - if ((i_actor != 0) && (i_actor % 10 == 0)) { + if ((i_actor != 0) && (i_actor % 8 == 0)) { } else { ImGui::SameLine(); } std::string label = fmt::format("{}##{}", actor.first, i_actor); if (ImGui::Button(label.c_str())) { - gWorldInstance.AddActor(actor.second(pos)); + //gWorldInstance.AddActor( + actor.second(pos); } i_actor += 1; } + + ContentBrowserWindow::TrainWindow(); } void ContentBrowserWindow::AddObjectContent() { @@ -192,7 +355,7 @@ namespace Editor { std::string label = fmt::format("{}##{}", object.first, i_object); if (ImGui::Button(label.c_str())) { - gWorldInstance.AddObject(object.second(pos)); + object.second(pos); } i_object += 1; } @@ -203,7 +366,7 @@ namespace Editor { size_t i_custom = 0; for (const auto& file : Content) { - if ((i_custom != 0) && (i_custom % 10 == 0)) { + if ((i_custom != 0) && (i_custom % 5 == 0)) { } else { ImGui::SameLine(); } @@ -239,14 +402,15 @@ namespace Editor { auto archive = manager->GetArchiveFromFile(sceneFile); auto course = std::make_shared(); + course->RootArchive = archive; course->LoadO2R(dir); - LoadLevel(archive, course.get(), sceneFile); - LoadMinimap(archive, course.get(), minimapFile); + LoadLevel(course.get(), sceneFile); + LoadMinimap(course.get(), minimapFile); Tracks.push_back({nullptr, course, sceneFile, name, dir, archive}); gWorldInstance.Courses.push_back(std::move(course)); } else { // The track does not have a valid scene file const std::string file = dir + "/data_track_sections"; - + // If the track has a data_track_sections file, // then it must at least be a valid track. // So lets add it as an uninitialized track. @@ -289,4 +453,82 @@ namespace Editor { } } } + + /** Actors that need config windows before spawning **/ + + ATrain* ContentBrowserWindow::TrainWindow() { + if (!bIsTrainWindowOpen) { + return nullptr; + } + + // Setup train window size and position + // Set window size constraints (min, max) + ImGui::SetNextWindowSizeConstraints(ImVec2(300, 200), ImVec2(FLT_MAX, FLT_MAX)); + + // Get the main viewport to find the center of the screen + ImGuiViewport* viewport = ImGui::GetMainViewport(); + + // Center the window + ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + + // Optional: auto-resize to content + ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_Appearing); // Initial size only + + if (ImGui::Begin("Spawn Train")) { + static int32_t numCarriages = 4; + static ATrain::TenderStatus tender = ATrain::TenderStatus::HAS_TENDER; // Can only disable tender if using no passenger cars + static int32_t pathIndex = 0; + static int32_t pathPoint = 0; + static ATrain::SpawnMode spawnMode = ATrain::SpawnMode::AUTO; + + // Num Carriage Input + if (ImGui::InputInt("Carriages", &numCarriages)) { + // Clamp to uint32_t range (only lower bound needed if assuming positive values) + if (numCarriages < 0) numCarriages = 0; + } + + // Setup for has tender settings + if (numCarriages > 0) { + ImGui::BeginDisabled(); + // Tender is required if there are any carriages + tender = ATrain::TenderStatus::HAS_TENDER; + } + + // Convert enum to bool + bool hasTender = (tender == ATrain::TenderStatus::HAS_TENDER); + if (ImGui::Checkbox("Has Tender", &hasTender)) { + tender = hasTender ? ATrain::TenderStatus::HAS_TENDER : ATrain::TenderStatus::NO_TENDER; + } + + if (numCarriages > 0) { + ImGui::EndDisabled(); + } + + // Set Spawn Mode + bool localSpawnMode = (spawnMode == ATrain::SpawnMode::AUTO); + if (ImGui::Checkbox("Place Train Automatically", &localSpawnMode)) { + spawnMode = localSpawnMode ? ATrain::SpawnMode::AUTO : ATrain::SpawnMode::POINT; + } + + // Set PathIndex and PathPoint + if (spawnMode == ATrain::SpawnMode::POINT) { + // PathIndex and PathPoint + if (ImGui::InputInt("Path Index", &pathIndex)) { + // Clamp to uint32_t range (only lower bound needed if assuming positive values) + if (pathIndex < 0) pathIndex = 0; + } + + if (ImGui::InputInt("Path Point", &pathPoint)) { + // Clamp to uint32_t range (only lower bound needed if assuming positive values) + if (pathPoint < 0) pathPoint = 0; + } + } + + if (ImGui::Button("Spawn")) { + bIsTrainWindowOpen = false; + return ATrain::Spawn(tender, numCarriages, 2.5f, (uint32_t)pathIndex, (uint32_t)pathPoint, spawnMode); + } + } + ImGui::End(); + } } diff --git a/src/port/ui/ContentBrowser.h b/src/port/ui/ContentBrowser.h index 958cb4bfa5..5fbdbb12a1 100644 --- a/src/port/ui/ContentBrowser.h +++ b/src/port/ui/ContentBrowser.h @@ -2,6 +2,7 @@ #include #include "engine/courses/Course.h" +#include "AllActors.h" namespace Editor { class ContentBrowserWindow : public Ship::GuiWindow { @@ -40,5 +41,7 @@ class ContentBrowserWindow : public Ship::GuiWindow { void FindTracks(); void FindContent(); void FolderButton(const char* label, bool& contentFlag, const ImVec2& size = ImVec2(80, 32)); + ATrain* TrainWindow(); + }; } diff --git a/src/port/ui/DefaultProperties.cpp b/src/port/ui/DefaultProperties.cpp new file mode 100644 index 0000000000..b713ec8441 --- /dev/null +++ b/src/port/ui/DefaultProperties.cpp @@ -0,0 +1,283 @@ +#include +#include "port/Game.h" + +// Draw default properties +void DrawDefaultEditorProperties() { + std::visit([](auto* obj) { + using T = std::decay_t; + if (nullptr == obj) { + return; + } + + // ImGui::Text("Type"); + // ImGui::SameLine(); + // int32_t type = static_cast(obj->SpawnType); + // if (ImGui::InputInt("##Type", &type)) { + // *params.Type = static_cast(type); + // } + + // if (params.Behaviour.has_value()) { + // ImGui::Text("Behaviour"); + // ImGui::SameLine(); + // int32_t behaviour = static_cast(params.Behaviour.value()); + // if (ImGui::InputInt("##Behaviour", &behaviour)) { + // *params.Behaviour = static_cast(behaviour); + // } + // } + + // if (params.Skin.has_value()) { + // ImGui::Text("Skin"); + // ImGui::SameLine(); + // // Copy the current string value into a buffer + // char buffer[256]; + // std::strncpy(buffer, params.Skin->c_str(), sizeof(buffer)); + // buffer[sizeof(buffer) - 1] = '\0'; // Ensure null-termination + + // if (ImGui::InputText("##Skin", buffer, sizeof(buffer))) { + // *params.Skin = std::string(buffer); + // } + // } + + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = obj->GetLocation(); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + obj->Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + obj->Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Rotation"); + ImGui::SameLine(); + + IRotator objRot = obj->GetRotation(); + + // Convert to temporary int values (to prevent writing 32bit values to 16bit variables) + int rot[3] = { + objRot.pitch, + objRot.yaw, + objRot.roll + }; + + if (ImGui::DragInt3("##Rotation", rot, 5.0f)) { + for (size_t i = 0; i < 3; i++) { + // Wrap around 0–65535 + rot[i] = (rot[i] % 65536 + 65536) % 65536; + } + IRotator newRot; + newRot.Set( + static_cast(rot[0]), + static_cast(rot[1]), + static_cast(rot[2]) + ); + obj->Rotate(newRot); + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetRot")) { + IRotator rot = IRotator(0, 0, 0); + obj->Rotate(rot); + } + + FVector scale = obj->GetScale(); + ImGui::Text("Scale "); + ImGui::SameLine(); + + ImGui::DragFloat3("##Scale", (float*)&scale, 0.1f); + obj->SetScale(scale); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetScale")) { + FVector scale = FVector(1.0f, 1.0f, 1.0f); + obj->SetScale(scale); + } + + // FVector velocity = obj->Velocity; + // ImGui::Text("Velocity"); + // ImGui::SameLine(); + + // ImGui::DragFloat3("##Velocity", (float*)&velocity, 0.1f); + // params.Velocity = velocity; + // ImGui::SameLine(); + // if (ImGui::Button(ICON_FA_UNDO "##ResetVelocity")) { + // params.Velocity = FVector(0.0f, 0.0f, 0.0f); + // } + + // FVector2D patrol = params.PatrolStart.value(); + + // ImGui::Text("Patrol Start"); + // ImGui::SameLine(); + + // ImGui::DragFloat2("##PatrolStart", (float*)&patrol); + // params.PatrolStart = patrol; + // if (ImGui::Button(ICON_FA_UNDO "##ResetPatrolStart")) { + // params.PatrolStart = FVector2D(0.0f, 0.0f); + // } + + // FVector2D patrol = params.PatrolEnd.value(); + + // ImGui::Text("Patrol End"); + // ImGui::SameLine(); + + // ImGui::DragFloat2("##PatrolEnd", (float*)&patrol); + // params.PatrolEnd = patrol; + // if (ImGui::Button(ICON_FA_UNDO "##ResetPatrolEnd")) { + // params.PatrolEnd = FVector2D(0.0f, 0.0f); + // } + + // IPathSpan span = params.PathSpan.value(); + + // ImGui::Text("Path Span"); + // ImGui::SameLine(); + + // if (ImGui::DragInt2("##PathSpan", (int*)&span)) { + // params.PathSpan = span; + // } + // ImGui::SameLine(); + // if (ImGui::Button(ICON_FA_UNDO "##ResetPathSpan")) { + // params.PathSpan = IPathSpan(0.0f, 0.0f); + // } + + // int32_t primAlpha = params.PrimAlpha.value(); + // ImGui::Text("Prim Alpha"); + // ImGui::SameLine(); + + // if (ImGui::InputInt("##PrimAlpha", (int*)&primAlpha)) { + // params.PrimAlpha = primAlpha; + // } + // ImGui::SameLine(); + // if (ImGui::Button(ICON_FA_UNDO "##ResetPrimAlpha")) { + // params.PrimAlpha = 0; + // } + + // int32_t boundingBoxSize = static_cast(params.BoundingBoxSize.value()); + // ImGui::Text("Bounding Box Size"); + // ImGui::SameLine(); + + // if (ImGui::InputInt("##BoundingBoxSize", (int*)&boundingBoxSize)) { + // if (boundingBoxSize < 0) boundingBoxSize = 0; + // params.BoundingBoxSize = static_cast(boundingBoxSize); + // } + // ImGui::SameLine(); + // if (ImGui::Button(ICON_FA_UNDO "##ResetBoundingBoxSize")) { + // params.BoundingBoxSize = 0; + // } + + // ImGui::Text("Count"); + // ImGui::SameLine(); + + // int count = static_cast(*params.Count); + // if (ImGui::InputInt("##Count", &count)) { + // // Clamp to uint32_t range (only lower bound needed if assuming positive values) + // if (count < 0) count = 0; + // params.Count = static_cast(count); + // } + // ImGui::SameLine(); + // if (ImGui::Button(ICON_FA_UNDO "##ResetCount")) { + // params.Count = 0; + // } + + // ImGui::Text("Left Exit Span"); + // ImGui::SameLine(); + + // IPathSpan leftExitSpan = *params.LeftExitSpan; + // if (ImGui::DragInt2("##LeftExitSpan", (int*)&leftExitSpan)) { + // params.LeftExitSpan = leftExitSpan; + // } + // ImGui::SameLine(); + // if (ImGui::Button(ICON_FA_UNDO "##ResetLeftExitSpan")) { + // params.LeftExitSpan = IPathSpan(0, 0); + // } + + // ImGui::Text("Trigger Span"); + // ImGui::SameLine(); + + // IPathSpan triggerSpan = *params.TriggerSpan; + // if (ImGui::DragInt2("##TriggerSpan", (int*)&triggerSpan)) { + // params.TriggerSpan = triggerSpan; + // } + // ImGui::SameLine(); + // if (ImGui::Button(ICON_FA_UNDO "##ResetTriggerSpan")) { + // params.TriggerSpan = IPathSpan(0, 0); + // } + + // ImGui::Text("Right Exit Span"); + // ImGui::SameLine(); + + // IPathSpan rightExitSpan = *params.RightExitSpan; + // if (ImGui::DragInt2("##RightExitSpan", (int*)&rightExitSpan)) { + // params.RightExitSpan = rightExitSpan; + // } + // ImGui::SameLine(); + // if (ImGui::Button(ICON_FA_UNDO "##ResetRightExitSpan")) { + // params.RightExitSpan = IPathSpan(0, 0); + // } + + // ImGui::Text("Path Index"); + // ImGui::SameLine(); + + // int pathIndex = static_cast(params.PathIndex.value()); + // if (ImGui::InputInt("##PathIndex", &pathIndex)) { + // if (pathIndex < 0) pathIndex = 0; + // params.PathIndex = static_cast(pathIndex); + // } + // ImGui::SameLine(); + // if (ImGui::Button(ICON_FA_UNDO "##ResetPathIndex")) { + // params.PathIndex = 0; + // } + + // ImGui::Text("Path Point"); + // ImGui::SameLine(); + + // int pathPoint = static_cast(params.PathPoint.value()); + // if (ImGui::InputInt("##PathPoint", &pathPoint)) { + // if (pathPoint < 0) pathPoint = 0; + // params.PathPoint = static_cast(pathPoint); + // } + // ImGui::SameLine(); + // if (ImGui::Button(ICON_FA_UNDO "##ResetPathPoint")) { + // params.PathPoint = 0; + // } + + // ImGui::Text("Bool"); + // ImGui::SameLine(); + + // bool theBool = params.Bool.value(); + // if (ImGui::Checkbox("##Bool", &theBool)) { + // params.Bool = theBool; + // } + // ImGui::SameLine(); + // if (ImGui::Button(ICON_FA_UNDO "##ResetBool")) { + // params.Bool = false; + // } + + // ImGui::Text("Speed"); + // ImGui::SameLine(); + + float speed = obj->Speed; + if (ImGui::DragFloat("##Speed", &speed, 0.1f)) { + obj->Speed = speed; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeed")) { + obj->Speed = 0.0f; + } + + // ImGui::Text("SpeedB"); + // ImGui::SameLine(); + + // float speed = params.SpeedB.value(); + // if (ImGui::DragFloat("##SpeedB", &speed, 0.1f)) { + // *params.SpeedB = speed; + // } + // ImGui::SameLine(); + // if (ImGui::Button(ICON_FA_UNDO "##ResetSpeedB")) { + // *params.SpeedB = 0.0f; + // } + }, gEditor.eObjectPicker.eGizmo._selected); +} diff --git a/src/port/ui/DefaultProperties.h b/src/port/ui/DefaultProperties.h new file mode 100644 index 0000000000..95ce5e2739 --- /dev/null +++ b/src/port/ui/DefaultProperties.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +void DrawDefaultEditorProperties(); diff --git a/src/port/ui/Properties.cpp b/src/port/ui/Properties.cpp index 6a8036dc24..f74f093fc3 100644 --- a/src/port/ui/Properties.cpp +++ b/src/port/ui/Properties.cpp @@ -2,6 +2,7 @@ #include "port/ui/PortMenu.h" #include "UIWidgets.h" #include "libultraship/src/Context.h" +#include #include #include @@ -11,10 +12,17 @@ #include #include +#include "engine/SpawnParams.h" #include "engine/editor/Editor.h" #include "port/Game.h" #include "src/engine/World.h" +#include "engine/vehicles/Train.h" + +extern "C" { +#include "actors.h" +} + namespace Editor { PropertiesWindow::~PropertiesWindow() { @@ -22,86 +30,58 @@ namespace Editor { } void PropertiesWindow::DrawElement() { - GameObject* selected = gEditor.eObjectPicker.eGizmo._selected; - if (nullptr == selected) { - return; - } + std::visit([this](auto* obj) { - ImGui::Begin("Properties"); - - if (selected->Pos) { - ImGui::Text("Location"); - ImGui::SameLine(); + using T = std::decay_t; // Get the type the pointer is pointing to + if (nullptr == obj) { + return; + } - bool positionChanged = ImGui::DragFloat3("##Location", &selected->Pos->x, 0.1f); + const char* title; - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { - selected->Pos->x = 0.0f; - selected->Pos->y = 0.0f; - selected->Pos->z = 0.0f; - positionChanged = true; // also counts as a change + // Get the actors display name, handling AActor (and the C version), OObject, and GameObject types + if constexpr (std::is_same_v) { + if (obj->IsMod()) { + title = obj->Name; + } else { + title = get_actor_display_name(obj->Type); + } + } else if constexpr (std::is_same_v) { + title = obj->Name; + } else if constexpr (std::is_same_v) { + title = obj->Name; + } else { + title = "Unknown type"; } - if (positionChanged) { - gEditor.eObjectPicker.eGizmo.Pos = *selected->Pos; - } - } - - if (selected->Rot) { - ImGui::Text("Rotation"); - ImGui::SameLine(); - - // Convert to temporary int values (to prevent writing 32bit values to 16bit variables) - int rot[3] = { - selected->Rot->pitch, - selected->Rot->yaw, - selected->Rot->roll - }; - - if (ImGui::DragInt3("##Rotation", rot, 5.0f)) { - for (size_t i = 0; i < 3; i++) { - // Wrap around 0–65535 - rot[i] = (rot[i] % 65536 + 65536) % 65536; - } + // Center Actor Title Text + float windowWidth = ImGui::GetWindowSize().x; + float textWidth = ImGui::CalcTextSize(title).x; - selected->Rot->pitch = static_cast(rot[0]); - selected->Rot->yaw = static_cast(rot[1]); - selected->Rot->roll = static_cast(rot[2]); - } + ImGui::SetCursorPosX((windowWidth - textWidth) * 0.5f); + // Display actor title + ImGui::Text(title); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_UNDO "##ResetRot")) { - selected->Rot->pitch = 0; - selected->Rot->yaw = 0; - selected->Rot->roll = 0; - } - } - - if (selected->Scale) { - ImGui::Text("Scale "); - ImGui::SameLine(); - - ImGui::DragFloat3("##Scale", &selected->Scale->x, 0.1f); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_UNDO "##ResetScale")) { - selected->Scale->x = 1.0f; - selected->Scale->y = 1.0f; - selected->Scale->z = 1.0f; + // Display actor resource name. ex. mk:cloud + if (obj->ResourceName[0] != '\0') { // Params is unset for some train components + ImGui::Text("Resource Name %s", obj->ResourceName); } - } - - if (selected->Collision == GameObject::CollisionType::BOUNDING_BOX) { - ImGui::Separator(); - ImGui::Text("Editor Bounding Box Size:"); - ImGui::PushID("BoundingBoxSize"); - ImGui::DragFloat("##BoundingBoxSize", &selected->BoundingBoxSize, 0.1f); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_UNDO)) { selected->BoundingBoxSize = 2.0f; } - ImGui::PopID(); - } - - ImGui::End(); + + obj->DrawEditorProperties(); + + }, gEditor.eObjectPicker.eGizmo._selected); + + // This allowed the user to alter the bounding box of the editor selection if it was too small. + // if (selected->Collision == GameObject::CollisionType::BOUNDING_BOX) { + // ImGui::Separator(); + // ImGui::Text("Editor Bounding Box Size:"); + // ImGui::PushID("BoundingBoxSize"); + // ImGui::DragFloat("##BoundingBoxSize", &selected->BoundingBoxSize, 0.1f); + // ImGui::SameLine(); + // if (ImGui::Button(ICON_FA_UNDO)) { selected->BoundingBoxSize = 2.0f; } + // ImGui::PopID(); + // } } -} \ No newline at end of file + +} diff --git a/src/port/ui/Properties.h b/src/port/ui/Properties.h index 260f891799..64af1b4f80 100644 --- a/src/port/ui/Properties.h +++ b/src/port/ui/Properties.h @@ -3,12 +3,36 @@ #include #include "port/Game.h" - namespace Editor { class PropertiesWindow : public Ship::GuiWindow { public: using Ship::GuiWindow::GuiWindow; ~PropertiesWindow(); + + const std::unordered_map SpawnLabels { + {"Name", "Name"}, + {"Type", "Type"}, + {"Behaviour", "Behaviour"}, + {"Skin", "Skin"}, + {"Location", "Location"}, + {"Rotation", "Rotation"}, + {"Scale", "Scale"}, + {"Velocity", "Velocity"}, + {"PatrolStart", "Patrol Start"}, + {"PatrolEnd", "Patrol End"}, + {"PathSpan", "Path Span"}, + {"PrimAlpha", "PrimAlpha"}, + {"BoundingBoxSize", "Bounding Box Size"}, + {"Count", "Count"}, + {"LeftExitSpan", "Left Exit Span"}, + {"TriggerSpan", "Trigger Span"}, + {"RightExitSpan", "Right Exit Span"}, + {"PathIndex", "Path Index"}, + {"PathPoint", "Path Point"}, + {"Bool", "Bool"}, + {"Speed", "Speed"}, + {"SpeedB", "SpeedB"}, + }; protected: void InitElement() override {}; void DrawElement() override; diff --git a/src/port/ui/SceneExplorer.cpp b/src/port/ui/SceneExplorer.cpp index 79b9c08e01..ef2809871f 100644 --- a/src/port/ui/SceneExplorer.cpp +++ b/src/port/ui/SceneExplorer.cpp @@ -14,6 +14,10 @@ #include "engine/editor/Editor.h" #include "port/Game.h" +extern "C" { +#include "actors.h" +} + namespace Editor { SceneExplorerWindow::~SceneExplorerWindow() { @@ -24,12 +28,56 @@ namespace Editor { ImGui::Text("Scene"); size_t id = 0; // id for now because we don't have unique names atm + + for (const auto& actor : gWorldInstance.Actors) { + std::string name; + if (nullptr != actor->Name && actor->Name[0] != '\0') { + name = std::string(actor->Name); + } else { + if (!actor->IsMod()) { + name = std::string(get_actor_display_name(actor->Type)); + } else { + name = "Unk Actor"; + } + } + + // Ensure unique label using index + std::string label = fmt::format("{}##{}", name, id); + + if (ImGui::Button(label.c_str())) { + gEditor.SelectObjectFromSceneExplorer(actor); + } + + id += 1; + } + + for (const auto& object : gWorldInstance.Objects) { + std::string name; + if (nullptr != object->Name && object->Name[0] != '\0') { + name = std::string(object->Name); + } else { + name = "Unk Object"; + } + + // Ensure unique label using index + std::string label = fmt::format("{}##{}", name, id); + + if (ImGui::Button(label.c_str())) { + gEditor.SelectObjectFromSceneExplorer(object); + } + id += 1; + } + for (auto& object : gEditor.eGameObjects) { - // Convert const char* to std::string before formatting - std::string objectName = object->Name ? object->Name : "Obj"; + std::string name; + if (nullptr != object->Name && object->Name[0] != '\0') { + name = std::string(object->Name); + } else { + name = "Unk Editor Obj"; + } // Ensure unique label using index - std::string label = fmt::format("{}##{}", objectName, id); + std::string label = fmt::format("{}##{}", name, id); if (ImGui::Button(label.c_str())) { gEditor.SelectObjectFromSceneExplorer(object); diff --git a/src/port/ui/Tools.cpp b/src/port/ui/Tools.cpp index 1b955b231f..9d58e619ee 100644 --- a/src/port/ui/Tools.cpp +++ b/src/port/ui/Tools.cpp @@ -35,7 +35,11 @@ namespace Editor { // Save button if (ImGui::Button(ICON_FA_FLOPPY_O, ImVec2(50, 25))) { - SaveLevel(); + if (gIsEditorPaused) { + SaveLevel(); + } else { + printf("[Editor] Cannot save during simulation\n Please switch back to edit mode!\n\n"); + } } ImGui::SameLine(); @@ -134,14 +138,41 @@ namespace Editor { // Play/pause button ImGui::PushStyleColor(ImGuiCol_Button, defaultColor); - if (ImGui::Button(gIsEditorPaused ? ICON_FA_PLAY : ICON_FA_PAUSE, ImVec2(50, 25))) { - + if (ImGui::Button(gIsEditorPaused ? ICON_FA_PLAY : ICON_FA_STOP, ImVec2(50, 25))) { + if (gIsEditorPaused) { + SaveLevel(); + } + + gEditor.ResetGizmo(); gIsEditorPaused = !gIsEditorPaused; + gIsInQuitToMenuTransition = 1; + gQuitToMenuTransitionCounter = 5; + gGotoMode = RACING; + } + ImGui::PopStyleColor(); + + ImGui::SameLine(); + + // Camera mode button (drive kart and freecam) + bool isVideoToolSelected = static_cast(CVarGetInteger("gFreecam", 0)); + ImGui::PushStyleColor(ImGuiCol_Button, defaultColor); + const char* videoToolLabel = isVideoToolSelected + ? ICON_FA_VIDEO_CAMERA " " ICON_FA_PAPER_PLANE + : ICON_FA_VIDEO_CAMERA " " ICON_FA_CAR; + + if (ImGui::Button(videoToolLabel, ImVec2(50, 25))) { + CVarSetInteger("gFreecam", !CVarGetInteger("gFreecam", 0)); } + ImGui::PopStyleColor(); ImGui::SameLine(); + // Alter the game speed + ToolsWindow::GameSpeed(); + + ImGui::SameLine(); + // Toggle hud ImGui::PushStyleColor(ImGuiCol_Button, gIsHUDVisible ? ImVec4(0.4f, 0.7f, 0.9f, 1.0f) : defaultColor); if (ImGui::Button("Toggle HUD", ImVec2(150, 25))) { @@ -156,5 +187,73 @@ namespace Editor { if (ImGui::Button(ICON_FA_TRASH_O, ImVec2(50, 25))) { gEditor.DeleteObject(); } + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::Text("Delete Selected Actor"); + ImGui::EndTooltip(); + } + + ImGui::SameLine(); + + // Delete All button + if (ImGui::Button(ICON_FA_INTERNET_EXPLORER, ImVec2(50, 25))) { + ImGui::OpenPopup("ConfirmDeleteAllPopup"); + } + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::Text("Delete All Actors"); + ImGui::EndTooltip(); + } + + // Confirmation Popup + if (ImGui::BeginPopupModal("ConfirmDeleteAllPopup", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("Are you sure you want to delete all actors?\nThis action can be undone if you do not save, and then reload the track."); + ImGui::Separator(); + + if (ImGui::Button("Yes", ImVec2(120, 0))) { + // Defer deletion until race_logic_loop + bCleanWorld = true; + gEditor.ResetGizmo(); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(120, 0))) { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + } + + // Fast Forward the game + void ToolsWindow::GameSpeed() { + ImVec4 defaultColor = ImGui::GetStyle().Colors[ImGuiCol_Button]; + + ImGui::PushStyleColor(ImGuiCol_Button, defaultColor); + + // Decrease + if (ImGui::Button("-", ImVec2(25, 25))) { + gTickLogic--; + if (gTickLogic < 1) gTickLogic = 1; // clamp min + } + + ImGui::SameLine(); + + // Label with current value + std::string label = "Game Speed: " + std::to_string(gTickLogic); + if (ImGui::Button(label.c_str(), ImVec2(120, 25))) { + gTickLogic = 2; // reset on click + } + + ImGui::SameLine(); + + // Increase + if (ImGui::Button("+", ImVec2(25, 25))) { + gTickLogic++; + } + + ImGui::PopStyleColor(); } } diff --git a/src/port/ui/Tools.h b/src/port/ui/Tools.h index b1afccf186..7ef3450a77 100644 --- a/src/port/ui/Tools.h +++ b/src/port/ui/Tools.h @@ -12,5 +12,6 @@ class ToolsWindow : public Ship::GuiWindow { void InitElement() override; void DrawElement() override; void UpdateElement() override {}; + void GameSpeed(); }; } diff --git a/src/racing/actors.c b/src/racing/actors.c index 05a56d68bd..11bd79203b 100644 --- a/src/racing/actors.c +++ b/src/racing/actors.c @@ -50,7 +50,6 @@ char* texture_red_shell[] = { texture_red_shell_0, texture_red_shell_1, texture_red_shell_2, texture_red_shell_3, texture_red_shell_4, texture_red_shell_5, texture_red_shell_6, texture_red_shell_7, }; -u8* D_802BA058; struct Actor* gActorHotAirBalloonItemBox; s8 gTLUTRedShell[512]; // tlut 256 @@ -237,47 +236,54 @@ void actor_init(struct Actor* actor, Vec3f startingPos, Vec3s startingRot, Vec3f actor->unk_08 = 17.0f; actor->model = d_course_moo_moo_farm_dl_tree; break; - case 26: + case ACTOR_TREE_LUIGI_RACEWAY: actor->flags |= 0x4000; actor->state = 0x0043; actor->boundingBoxSize = 3.0f; actor->unk_08 = 17.0f; + actor->model = d_course_luigi_raceway_dl_FC70; break; - case 28: + case ACTOR_TREE_PEACH_CASTLE: actor->state = 0x0043; actor->flags = -0x8000; actor->boundingBoxSize = 3.0f; actor->unk_08 = 17.0f; + actor->model = d_course_royal_raceway_dl_castle_tree; break; - case 33: + case ACTOR_BUSH_BOWSERS_CASTLE: actor->flags |= 0x4000; actor->state = 0x0043; actor->boundingBoxSize = 3.0f; actor->unk_08 = 17.0f; + actor->model = d_course_bowsers_castle_dl_bush; break; - case 29: + case ACTOR_TREE_FRAPPE_SNOWLAND: actor->flags |= 0x4000; actor->state = 0x0043; actor->boundingBoxSize = 3.0f; actor->unk_08 = 17.0f; + actor->model = d_course_frappe_snowland_dl_tree; break; - case 30: + case ACTOR_CACTUS1_KALAMARI_DESERT: actor->flags |= 0x4000; actor->state = 0x0019; actor->boundingBoxSize = 3.0f; actor->unk_08 = 7.0f; + actor->model = d_course_kalimari_desert_dl_cactus1; break; - case 31: + case ACTOR_CACTUS2_KALAMARI_DESERT: actor->flags |= 0x4000; actor->state = 0x0019; actor->boundingBoxSize = 3.0f; actor->unk_08 = 7.0f; + actor->model = d_course_kalimari_desert_dl_cactus2; break; - case 32: + case ACTOR_CACTUS3_KALAMARI_DESERT: actor->flags |= 0x4000; actor->state = 0x0019; actor->boundingBoxSize = 3.0f; actor->unk_08 = 7.0f; + actor->model = d_course_kalimari_desert_dl_cactus3; break; case ACTOR_PALM_TREE: actor->flags |= 0x4000; @@ -926,6 +932,18 @@ void spawn_palm_trees(struct ActorSpawnData* spawnData) { temp_v1 = (struct PalmTree*) CM_GetActor(temp); temp_v1->variant = temp_s0->someId; + switch(temp_v1->variant) { + case 0: + temp_v1->model = d_course_koopa_troopa_beach_dl_tree_trunk1; + break; + case 1: + temp_v1->model = d_course_koopa_troopa_beach_dl_tree_trunk2; + break; + case 2: + temp_v1->model = d_course_koopa_troopa_beach_dl_tree_trunk3; + break; + } + CM_ActorGenerateCollision(temp_v1); check_bounding_collision((Collision*) &temp_v1->unk30, 5.0f, temp_v1->pos[0], temp_v1->pos[1], temp_v1->pos[2]); func_802976EC((Collision*) &temp_v1->unk30, temp_v1->rot); temp_s0++; @@ -977,7 +995,7 @@ void spawn_foliage(struct ActorSpawnData* actor) { break; } } else if (IsLuigiRaceway()) { - actorType = 0x001A; + actorType = ACTOR_TREE_LUIGI_RACEWAY; } else if (IsMooMooFarm()) { actorType = 0x0013; } else if (IsKalimariDesert()) { @@ -1050,6 +1068,54 @@ void spawn_all_item_boxes(struct ActorSpawnData* spawnData) { } } +// Not from decomp +void spawn_item_box(Vec3f pos) { + Vec3f startingVelocity; + Vec3s startingRot; + // struct ItemBox *itemBox; + + if ((gModeSelection == TIME_TRIALS) || (gPlaceItemBoxes == 0) || (gGamestate == CREDITS_SEQUENCE)) { + return; + } + + pos[0] *= gCourseDirection; + + startingRot[0] = random_u16(); + startingRot[1] = random_u16(); + startingRot[2] = random_u16(); + s32 id = add_actor_to_empty_slot(pos, startingRot, startingVelocity, ACTOR_ITEM_BOX); + f32 height = spawn_actor_on_surface(pos[0], pos[1] + 10.0f, pos[2]); + + struct ItemBox* box = (struct ItemBox*) CM_GetActor(id); + + box->resetDistance = height; + box->origY = pos[1]; + box->pos[1] = height - 20.0f; +} + +// Not from decomp +void spawn_fake_item_box(Vec3f pos) { + Vec3f startingVelocity; + Vec3s startingRot; + // struct ItemBox *itemBox; + + if ((gModeSelection == TIME_TRIALS) || (gPlaceItemBoxes == 0) || (gGamestate == CREDITS_SEQUENCE)) { + return; + } + + pos[0] *= gCourseDirection; + + startingRot[0] = random_u16(); + startingRot[1] = random_u16(); + startingRot[2] = random_u16(); + s32 id = add_actor_to_empty_slot(pos, startingRot, startingVelocity, ACTOR_FAKE_ITEM_BOX); + f32 height = spawn_actor_on_surface(pos[0], pos[1], pos[2]); + + struct FakeItemBox* box = (struct FakeItemBox*) CM_GetActor(id); + box->state = 1; + box->targetY = pos[1]; +} + void init_kiwano_fruit(void) { Vec3f sp64; Vec3f sp58; @@ -1431,7 +1497,7 @@ s16 add_actor_to_empty_slot(Vec3f pos, Vec3s rot, Vec3f velocity, s16 actorType) gNumActors++; struct Actor* actor = CM_AddBaseActor(); actor_init(actor, pos, rot, velocity, actorType); - CM_AddEditorObject(actor, get_actor_name(actor->type)); + CM_ActorBeginPlay(actor); return (s16) CM_GetActorSize() - 1; // Return current index; } @@ -2159,7 +2225,7 @@ void evaluate_collision_between_player_actor(Player* player, struct Actor* actor case ACTOR_TREE_MOO_MOO_FARM: case ACTOR_PALM_TREE: case 26: - case ACTOR_TREE_BOWSERS_CASTLE: + case ACTOR_TREE_PEACH_CASTLE: case ACTOR_TREE_FRAPPE_SNOWLAND: case ACTOR_CACTUS1_KALAMARI_DESERT: case ACTOR_CACTUS2_KALAMARI_DESERT: @@ -2430,16 +2496,8 @@ void render_course_actors(struct UnkStruct_800DC5EC* arg0) { struct Actor* actor; UNUSED Vec3f sp4C = { 0.0f, 5.0f, 10.0f }; - // Freecam rotY is reversed in the engine for whatever reason - f32 sp48 = 0; - f32 temp_f0 = 0; - if (CVarGetInteger("gFreecam", 0) == true) { - sp48 = sins(-camera->rot[1] - 0x8000); - temp_f0 = coss(-camera->rot[1] - 0x8000); - } else { - sp48 = sins(camera->rot[1] - 0x8000); - temp_f0 = coss(camera->rot[1] - 0x8000); - } + f32 sp48 = sins(camera->rot[1] - 0x8000); + f32 temp_f0 = coss(camera->rot[1] - 0x8000); sBillBoardMtx[0][0] = temp_f0; sBillBoardMtx[0][2] = -sp48; @@ -2474,8 +2532,7 @@ void render_course_actors(struct UnkStruct_800DC5EC* arg0) { FrameInterpolation_RecordOpenChild(actor, i); switch (actor->type) { - default: // Draw custom actor - CM_DrawActors(D_800DC5EC->camera, actor); + default: // Skip custom actor break; case ACTOR_TREE_MARIO_RACEWAY: @@ -2490,11 +2547,11 @@ void render_course_actors(struct UnkStruct_800DC5EC* arg0) { case ACTOR_TREE_MOO_MOO_FARM: render_actor_tree_moo_moo_farm(camera, sBillBoardMtx, actor); break; - case ACTOR_UNKNOWN_0x1A: - func_80299864(camera, sBillBoardMtx, actor); + case ACTOR_TREE_LUIGI_RACEWAY: + render_actor_tree_luigi_raceway(camera, sBillBoardMtx, actor); break; - case ACTOR_TREE_BOWSERS_CASTLE: - render_actor_tree_bowser_castle(camera, sBillBoardMtx, actor); + case ACTOR_TREE_PEACH_CASTLE: + render_actor_tree_peach_castle(camera, sBillBoardMtx, actor); break; case ACTOR_BUSH_BOWSERS_CASTLE: render_actor_bush_bowser_castle(camera, sBillBoardMtx, actor); @@ -2511,8 +2568,8 @@ void render_course_actors(struct UnkStruct_800DC5EC* arg0) { case ACTOR_CACTUS3_KALAMARI_DESERT: render_actor_tree_cactus3_kalimari_desert(camera, sBillBoardMtx, actor); break; - case ACTOR_FALLING_ROCK: - render_actor_falling_rock(camera, (struct FallingRock*) actor); + case ACTOR_FALLING_ROCK: // now in C++ + //render_actor_falling_rock(camera, (struct FallingRock*) actor); break; case ACTOR_KIWANO_FRUIT: render_actor_kiwano_fruit(camera, sBillBoardMtx, actor); @@ -2598,8 +2655,8 @@ void update_course_actors(void) { } switch (actor->type) { - case ACTOR_FALLING_ROCK: - update_actor_falling_rocks((struct FallingRock*) actor); + case ACTOR_FALLING_ROCK: // now in C++ + //update_actor_falling_rocks((struct FallingRock*) actor); break; case ACTOR_GREEN_SHELL: update_actor_green_shell((struct ShellActor*) actor); @@ -2663,9 +2720,9 @@ void update_course_actors(void) { case ACTOR_TREE_ROYAL_RACEWAY: case ACTOR_TREE_MOO_MOO_FARM: case ACTOR_PALM_TREE: - case ACTOR_UNKNOWN_0x1A: // A plant? + case ACTOR_TREE_LUIGI_RACEWAY: // A plant? case ACTOR_UNKNOWN_0x1B: - case ACTOR_TREE_BOWSERS_CASTLE: + case ACTOR_TREE_PEACH_CASTLE: case ACTOR_TREE_FRAPPE_SNOWLAND: case ACTOR_CACTUS1_KALAMARI_DESERT: case ACTOR_CACTUS2_KALAMARI_DESERT: @@ -2682,7 +2739,7 @@ void update_course_actors(void) { check_player_use_item(); } -const char* get_actor_name(s32 id) { +const char* get_actor_display_name(s32 id) { switch (id) { case ACTOR_FALLING_ROCK: return "Falling Rock"; @@ -2704,6 +2761,14 @@ const char* get_actor_name(s32 id) { return "Train Tender"; case ACTOR_TRAIN_PASSENGER_CAR: return "Train Passenger Car"; + case ACTOR_CAR: + return "Car Component"; + case ACTOR_SCHOOL_BUS: + return "Bus Component"; + case ACTOR_TANKER_TRUCK: + return "Tanker Truck Component"; + case ACTOR_BOX_TRUCK: + return "Truck Component"; case ACTOR_ITEM_BOX: return "Item Box"; case ACTOR_HOT_AIR_BALLOON_ITEM_BOX: @@ -2734,12 +2799,12 @@ const char* get_actor_name(s32 id) { return "Tree (Moo Moo Farm)"; case ACTOR_PALM_TREE: return "Palm Tree"; - case ACTOR_UNKNOWN_0x1A: - return "Unknown Plant (0x1A)"; + case ACTOR_TREE_LUIGI_RACEWAY: + return "Tree (Luigi Raceway)"; case ACTOR_UNKNOWN_0x1B: - return "Unknown (0x1B)"; - case ACTOR_TREE_BOWSERS_CASTLE: - return "Tree (Bowser's Castle)"; + return "Unknown Plant (0x1B)"; + case ACTOR_TREE_PEACH_CASTLE: + return "Tree (Peach's Castle)"; case ACTOR_TREE_FRAPPE_SNOWLAND: return "Tree (Frappe Snowland)"; case ACTOR_CACTUS1_KALAMARI_DESERT: @@ -2756,3 +2821,79 @@ const char* get_actor_name(s32 id) { return "Obj"; } } + +// Returns a namespace:path +const char* get_actor_resource_location_name(s32 id) { + switch (id) { + case ACTOR_FALLING_ROCK: + return "mk:falling_rock"; + case ACTOR_GREEN_SHELL: + return "mk:green_shell"; + case ACTOR_RED_SHELL: + return "mk:red_shell"; + case ACTOR_BLUE_SPINY_SHELL: + return "mk:blue_spiny_shell"; + case ACTOR_KIWANO_FRUIT: + return "mk:kiwano_fruit"; + case ACTOR_BANANA: + return "mk:banana"; + case ACTOR_PADDLE_BOAT: + return "mk:paddle_boat"; + case ACTOR_TRAIN_ENGINE: + return "mk:train_engine"; + case ACTOR_TRAIN_TENDER: + return "mk:train_tender"; + case ACTOR_TRAIN_PASSENGER_CAR: + return "mk:train_passenger_car"; + case ACTOR_ITEM_BOX: + return "mk:item_box"; + case ACTOR_HOT_AIR_BALLOON_ITEM_BOX: + return "mk:hot_air_balloon_item_box"; + case ACTOR_FAKE_ITEM_BOX: + return "mk:fake_item_box"; + case ACTOR_PIRANHA_PLANT: + return "mk:piranha_plant"; + case ACTOR_BANANA_BUNCH: + return "mk:banana_bunch"; + case ACTOR_TRIPLE_GREEN_SHELL: + return "mk:triple_green_shell"; + case ACTOR_TRIPLE_RED_SHELL: + return "mk:triple_red_shell"; + case ACTOR_MARIO_SIGN: + return "mk:mario_sign"; + case ACTOR_WARIO_SIGN: + return "mk:wario_sign"; + case ACTOR_RAILROAD_CROSSING: + return "mk:railroad_crossing"; + case ACTOR_TREE_MARIO_RACEWAY: + return "mk:tree_mario_raceway"; + case ACTOR_TREE_YOSHI_VALLEY: + return "mk:tree_yoshi_valley"; + case ACTOR_TREE_ROYAL_RACEWAY: + return "mk:tree_royal_raceway"; + case ACTOR_TREE_MOO_MOO_FARM: + return "mk:tree_moo_moo_farm"; + case ACTOR_PALM_TREE: + return "mk:palm_tree"; + case ACTOR_TREE_LUIGI_RACEWAY: + return "mk:tree_luigi_raceway"; + case ACTOR_UNKNOWN_0x1B: + return "mk:unknown_0x1b"; + case ACTOR_TREE_PEACH_CASTLE: + return "mk:tree_peach_castle"; + case ACTOR_TREE_FRAPPE_SNOWLAND: + return "mk:tree_frappe_snowland"; + case ACTOR_CACTUS1_KALAMARI_DESERT: + return "mk:cactus1_kalamari_desert"; + case ACTOR_CACTUS2_KALAMARI_DESERT: + return "mk:cactus2_kalamari_desert"; + case ACTOR_CACTUS3_KALAMARI_DESERT: + return "mk:cactus3_kalamari_desert"; + case ACTOR_BUSH_BOWSERS_CASTLE: + return "mk:bush_bowsers_castle"; + case ACTOR_YOSHI_EGG: + return "mk:yoshi_egg"; + default: + return "mk:actor"; + } +} \ No newline at end of file diff --git a/src/racing/actors.h b/src/racing/actors.h index 8fa5741daa..84233759f7 100644 --- a/src/racing/actors.h +++ b/src/racing/actors.h @@ -41,8 +41,8 @@ void render_actor_tree_mario_raceway(Camera*, Mat4, struct Actor*); void render_actor_tree_yoshi_valley(Camera*, Mat4, struct Actor*); void render_actor_tree_royal_raceway(Camera*, Mat4, struct Actor*); void render_actor_tree_moo_moo_farm(Camera*, Mat4, struct Actor*); -void func_80299864(Camera*, Mat4, struct Actor*); -void render_actor_tree_bowser_castle(Camera*, Mat4, struct Actor*); +void render_actor_tree_luigi_raceway(Camera*, Mat4, struct Actor*); +void render_actor_tree_peach_castle(Camera*, Mat4, struct Actor*); void render_actor_bush_bowser_castle(Camera*, Mat4, struct Actor*); void render_actor_tree_frappe_snowland(Camera*, Mat4, struct Actor*); void render_actor_tree_cactus1_kalimari_desert(Camera*, Mat4, struct Actor*); @@ -74,6 +74,8 @@ void spawn_falling_rocks(struct ActorSpawnData*); void update_actor_falling_rocks(struct FallingRock*); void spawn_foliage(struct ActorSpawnData*); void spawn_all_item_boxes(struct ActorSpawnData*); +void spawn_item_box(Vec3f pos); +void spawn_fake_item_box(Vec3f pos); void init_kiwano_fruit(void); void destroy_all_actors(void); void spawn_course_actors(void); @@ -111,15 +113,13 @@ void render_actor_palm_tree(Camera*, Mat4, struct PalmTree*); void render_item_boxes(struct UnkStruct_800DC5EC*); void render_course_actors(struct UnkStruct_800DC5EC*); void update_course_actors(void); -const char* get_actor_name(s32); +const char* get_actor_display_name(s32 id); +const char* get_actor_resource_location_name(s32 id); // audio/external.c extern void func_800C98B8(Vec3f, Vec3f, u32); extern void func_800C99E0(Vec3f, s32); -extern u8* D_802BA050; -extern u8* D_802BA054; -extern u8* D_802BA058; extern struct Actor* gActorHotAirBalloonItemBox; extern s8 gTLUTRedShell[]; // tlut 256 extern u16 D_802BA260; // Box Truck sub-type? diff --git a/src/racing/collision.c b/src/racing/collision.c index e76ff4f61e..72421964cf 100644 --- a/src/racing/collision.c +++ b/src/racing/collision.c @@ -2007,7 +2007,6 @@ void generate_collision_mesh(Gfx* addr, s8 surfaceType, u16 sectionId) { const char* filePath = (const char*)hi; // Fast64 outputs garbage data. Lets skip that... if (is_cull_box(filePath)) { - printf("Skipped cull box\n"); gfx++; continue; } diff --git a/src/racing/race_logic.c b/src/racing/race_logic.c index b2f22a749f..2227aa797a 100644 --- a/src/racing/race_logic.c +++ b/src/racing/race_logic.c @@ -648,9 +648,19 @@ void func_8028F4E8(void) { } } +/** + * On race launch, the screen starts small and quickly gets bigger + * as an effect. + */ void func_8028F588(void) { s16 screenWidth; + if (CVarGetInteger("gEditorEnabled", 0) == true) { + D_800DC5EC->screenWidth = SCREEN_WIDTH; + D_800DC5EC->screenHeight = SCREEN_HEIGHT; + return; + } + switch (gActiveScreenMode) { /* irregular */ case SCREEN_MODE_1P: screenWidth = (s16) (s32) (320.0f * D_802BA034); diff --git a/src/racing/render_courses.c b/src/racing/render_courses.c index ee1506bc26..a37e98d825 100644 --- a/src/racing/render_courses.c +++ b/src/racing/render_courses.c @@ -223,9 +223,13 @@ void func_8029122C(struct UnkStruct_800DC5EC* arg0, s32 playerId) { // But that issue should be cleared up in render_screens() already. switch (playerId) { case PLAYER_ONE: - gSPMatrix(gDisplayListHead++, GetPerspMatrix(PLAYER_ONE), + size_t playerIdx = PLAYER_ONE; + if (CVarGetInteger("gFreecam", 0) == true) { + playerIdx = CAMERA_FREECAM; + } + gSPMatrix(gDisplayListHead++, GetPerspMatrix(playerIdx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); - gSPMatrix(gDisplayListHead++, GetLookAtMatrix(PLAYER_ONE), + gSPMatrix(gDisplayListHead++, GetLookAtMatrix(playerIdx), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); break; case PLAYER_TWO: diff --git a/src/racing/skybox_and_splitscreen.c b/src/racing/skybox_and_splitscreen.c index 09a4c733fb..129b55b11e 100644 --- a/src/racing/skybox_and_splitscreen.c +++ b/src/racing/skybox_and_splitscreen.c @@ -762,10 +762,10 @@ void setup_camera(Camera* camera, s32 playerId, s32 cameraId, struct UnkStruct_8 u16 perspNorm; // This allows freecam to create a new separate camera - // if (CVarGetInteger("gFreecam", 0) == true) { - // freecam_render_setup(gFreecamCamera); - // return; - // } + if (CVarGetInteger("gFreecam", 0) == true) { + freecam_render_setup(gFreecamCamera); + return; + } // Tag the camera for the interpolation engine FrameInterpolation_RecordOpenChild("camera", @@ -853,12 +853,12 @@ void render_screens(s32 mode, s32 cameraId, s32 playerId) { Camera* camera; // Required for freecam to have its own camera - //if (CVarGetInteger("gFreecam", 0) == true) { - // camera = &gFreecamCamera; - // cameraId = 4; - //} else { + if (CVarGetInteger("gFreecam", 0) == true) { + camera = &cameras[CAMERA_FREECAM]; + cameraId = CAMERA_FREECAM; + } else { camera = &cameras[cameraId]; - //} + } if (screenMode == SCREEN_MODE_2P_SPLITSCREEN_HORIZONTAL) { gSPSetGeometryMode(gDisplayListHead++, G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH); @@ -884,6 +884,7 @@ void render_screens(s32 mode, s32 cameraId, s32 playerId) { // Draw dynamic game objects render_course_actors(screen); + CM_DrawActors(D_800DC5EC->camera); CM_DrawStaticMeshActors(); render_object(mode); @@ -935,7 +936,8 @@ void render_screens(s32 mode, s32 cameraId, s32 playerId) { } } -void func_802A74BC(void) { +// Makes the screen small at the start of a race +void set_screen(void) { struct UnkStruct_800DC5EC* wrapper = &D_8015F480[0]; Player* player = &gPlayers[0]; Camera* camera = &cameras[0]; @@ -947,11 +949,22 @@ void func_802A74BC(void) { for (i = 0; i < 4; i++) { wrapper->controllers = controller; - wrapper->camera = camera; + if ((CVarGetInteger("gFreecam", 0) == true) && (i == PLAYER_ONE)) { + wrapper->camera = gFreecamCamera; + } else { + wrapper->camera = camera; + } wrapper->player = player; wrapper->unkC = unk; - wrapper->screenWidth = 4; - wrapper->screenHeight = 4; + + // Tick is not enabled in the editor, so the screen needs to begin at the proper size. + if ((CVarGetInteger("gEditorEnabled", 0) == true) && (gIsEditorPaused) && (i == PLAYER_ONE)) { + wrapper->screenWidth = SCREEN_WIDTH; + wrapper->screenHeight = SCREEN_HEIGHT; + } else { // Normal race start, screen is small + wrapper->screenWidth = 4; + wrapper->screenHeight = 4; + } wrapper->pathCounter = 1; switch (gActiveScreenMode) { @@ -1002,6 +1015,17 @@ void func_802A74BC(void) { } } +void set_editor_screen(void) { + struct UnkStruct_800DC5EC* wrapper = &D_8015F480[0]; + wrapper->controllers = gControllerOne; + wrapper->camera = gFreecamCamera; + wrapper->player = gPlayerOne; + wrapper->unkC = &D_8015F790[0]; + wrapper->screenWidth = SCREEN_WIDTH; + wrapper->screenHeight = SCREEN_HEIGHT; + wrapper->pathCounter = 1; +} + void copy_framebuffer(s32 arg0, s32 arg1, s32 width, s32 height, u16* source, u16* target) { s32 var_v1; s32 var_a1; diff --git a/src/racing/skybox_and_splitscreen.h b/src/racing/skybox_and_splitscreen.h index a648e0fe69..4771ec001c 100644 --- a/src/racing/skybox_and_splitscreen.h +++ b/src/racing/skybox_and_splitscreen.h @@ -10,7 +10,8 @@ /* Function Prototypes */ void func_802A4A0C(Vtx*, struct UnkStruct_800DC5EC*, s32, s32, f32*); - +void set_screen(void); +void set_editor_screen(void); void func_802A3730(struct UnkStruct_800DC5EC*); void func_802A38AC(void); void func_802A38B4(void); diff --git a/src/spawn_players.c b/src/spawn_players.c index f340e8978b..464049043c 100644 --- a/src/spawn_players.c +++ b/src/spawn_players.c @@ -878,9 +878,9 @@ void func_8003C0F0(void) { func_8000EEDC(); } else if (!IsPodiumCeremony()) { init_course_path_point(); - sp5E = (f32) gTrackPaths[0][0].posX; - sp5C = (f32) gTrackPaths[0][0].posZ; - sp5A = (f32) gTrackPaths[0][0].posY; + sp5E = (f32) gTrackPaths[0][0].x; + sp5C = (f32) gTrackPaths[0][0].z; + sp5A = (f32) gTrackPaths[0][0].y; if (IsToadsTurnpike()) { sp5E = 0; } @@ -1279,7 +1279,7 @@ void func_8003D080(void) { } // Init freecam - //freecam_init(player->pos[0], player->pos[1], player->pos[2], player->rotation[1], 1, 4); + freecam_init(player->pos[0], player->pos[1], player->pos[2], player->rotation[1], 1, 4); switch (gActiveScreenMode) { case SCREEN_MODE_1P: diff --git a/src/update_objects.c b/src/update_objects.c index fe823d602d..4b25d29704 100644 --- a/src/update_objects.c +++ b/src/update_objects.c @@ -2606,15 +2606,15 @@ void func_80078C70(s32 arg0) { } } -void func_8007ABFC(s32 playerId, s32 arg1) { +void func_8007ABFC(s32 playerId, s32 itemId) { s32 itemWindow; if (playerHUD[playerId].raceCompleteBool == false) { itemWindow = gItemWindowObjectByPlayerId[playerId]; if (func_80072354(itemWindow, 4) != 0) { init_object(itemWindow, 0); - if (arg1 != ITEM_NONE) { - playerHUD[playerId].itemOverride = arg1; + if (itemId != ITEM_NONE) { + playerHUD[playerId].itemOverride = itemId; } } func_800C9060(playerId, 0x19008406U); diff --git a/vcpkg.json b/vcpkg.json index ef3450b2aa..6da4818cb1 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -19,5 +19,11 @@ "libvorbis" ], "builtin-baseline": "2e58bb35ff7a3a037920d959ce20cb4d8c22319a", - "overrides": [] + "overrides": [ + { + "name": "nlohmann-json", + "version-string": "3.12.0", + "port-version": 1 + } + ] } From fb7bd7ba11f8c15d371c770cb12b4de73be9f156 Mon Sep 17 00:00:00 2001 From: MegaMech Date: Sun, 9 Nov 2025 20:40:09 -0700 Subject: [PATCH 08/16] Fix Sherbet (#558) * Update Game.cpp * Update Penguin.cpp --- src/engine/objects/Penguin.cpp | 5 ++++- src/port/Game.cpp | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/engine/objects/Penguin.cpp b/src/engine/objects/Penguin.cpp index 34c7248dd3..262446abb4 100644 --- a/src/engine/objects/Penguin.cpp +++ b/src/engine/objects/Penguin.cpp @@ -48,7 +48,10 @@ OPenguin::OPenguin(const SpawnParams& params) : OObject(params) { object->origin_pos[2] = pos.z; object->unk_0C6 = params.Rotation.value_or(IRotator(0, 0, 0)).yaw; - switch(static_cast(Type)) { + Type = static_cast(params.Type.value_or(0)); + SpawnBhv = static_cast(params.Behaviour.value_or(0)); + + switch(Type) { case PenguinType::CHICK: object->surfaceHeight = 5.0f; object->sizeScaling = 0.04f; diff --git a/src/port/Game.cpp b/src/port/Game.cpp index 2d42bb1ac1..37282346d1 100644 --- a/src/port/Game.cpp +++ b/src/port/Game.cpp @@ -704,8 +704,10 @@ void CM_ActorBeginPlay(struct Actor* actor) { void CM_ActorGenerateCollision(struct Actor* actor) { AActor* act = gWorldInstance.ConvertActorToAActor(actor); - if (act->Triangles.size() == 0) { - Editor::GenerateCollisionMesh(act, (Gfx*)LOAD_ASSET_RAW(act->Model), 1.0f); + if ((nullptr != act->Model) && (act->Model[0] != '\0')) { + if (act->Triangles.size() == 0) { + Editor::GenerateCollisionMesh(act, (Gfx*)LOAD_ASSET_RAW(act->Model), 1.0f); + } } } From ad860630668656a60fdbc43603602c35140440c5 Mon Sep 17 00:00:00 2001 From: MegaMech Date: Wed, 12 Nov 2025 16:11:28 -0700 Subject: [PATCH 09/16] Update Collision.cpp (#559) --- src/engine/editor/Collision.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/editor/Collision.cpp b/src/engine/editor/Collision.cpp index 261aa40d70..026a79bd08 100644 --- a/src/engine/editor/Collision.cpp +++ b/src/engine/editor/Collision.cpp @@ -158,6 +158,9 @@ namespace Editor { case G_ENDDL: run = false; break; + case G_MARKER: + ptr++; + break; } ptr++; From a9f69d86c866236504f28ace8dabfb4a71b650ec Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Fri, 14 Nov 2025 19:44:33 +0100 Subject: [PATCH 10/16] Update vcpkg.json (#561) --- vcpkg.json | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/vcpkg.json b/vcpkg.json index 6da4818cb1..fec076923a 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -18,12 +18,5 @@ "libogg", "libvorbis" ], - "builtin-baseline": "2e58bb35ff7a3a037920d959ce20cb4d8c22319a", - "overrides": [ - { - "name": "nlohmann-json", - "version-string": "3.12.0", - "port-version": 1 - } - ] + "builtin-baseline": "2e58bb35ff7a3a037920d959ce20cb4d8c22319a" } From c2755aee40bb37c8440ec58f260b4bd36bd6e256 Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Fri, 14 Nov 2025 21:37:04 +0100 Subject: [PATCH 11/16] Fix appImage asset extraction (#563) * Update vcpkg.json * fix appimage --- .github/workflows/main.yml | 2 -- cmake/configure-packaging.cmake | 8 ++++---- cmake/packaging.cmake | 4 +++- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7808db1f3b..e6b83cd71c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -352,8 +352,6 @@ jobs: name: spaghetti-linux-x64 path: | spaghetti.appimage - config.yml - yamls gamecontrollerdb.txt build-linux-docker: diff --git a/cmake/configure-packaging.cmake b/cmake/configure-packaging.cmake index f3d2c115af..9226db6869 100644 --- a/cmake/configure-packaging.cmake +++ b/cmake/configure-packaging.cmake @@ -1,6 +1,6 @@ set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) set(CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY 0) -set(CPACK_COMPONENTS_ALL "Starship") +set(CPACK_COMPONENTS_ALL "SpaghettiKart") if (CPACK_GENERATOR STREQUAL "External") list(APPEND CPACK_COMPONENTS_ALL "extractor" "appimage") @@ -21,9 +21,9 @@ set(CPACK_PACKAGING_INSTALL_PREFIX "/usr/bin") endif() if (CPACK_GENERATOR MATCHES "Bundle") - set(CPACK_BUNDLE_NAME "Starship") + set(CPACK_BUNDLE_NAME "SpaghettiKart") set(CPACK_BUNDLE_PLIST "macosx/Info.plist") - set(CPACK_BUNDLE_ICON "macosx/Starship.icns") - # set(CPACK_BUNDLE_STARTUP_COMMAND "macosx/Starship-macos.sh") + set(CPACK_BUNDLE_ICON "macosx/SpaghettiKart.icns") + # set(CPACK_BUNDLE_STARTUP_COMMAND "macosx/SpaghettiKart-macos.sh") set(CPACK_BUNDLE_APPLE_CERT_APP "-") endif() \ No newline at end of file diff --git a/cmake/packaging.cmake b/cmake/packaging.cmake index d699d89e70..924a974896 100644 --- a/cmake/packaging.cmake +++ b/cmake/packaging.cmake @@ -60,7 +60,7 @@ if (NOT LINUXDEPLOY_EXECUTABLE) message(STATUS "Downloading linuxdeploy") set(LINUXDEPLOY_EXECUTABLE ${CPACK_PACKAGE_DIRECTORY}/linuxdeploy/linuxdeploy) file(DOWNLOAD - https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20240109-1/linuxdeploy-x86_64.AppImage + https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20251107-1/linuxdeploy-x86_64.AppImage ${LINUXDEPLOY_EXECUTABLE} INACTIVITY_TIMEOUT 10 LOG ${CPACK_PACKAGE_DIRECTORY}/linuxdeploy/download.log @@ -69,6 +69,8 @@ if (NOT LINUXDEPLOY_EXECUTABLE) endif() execute_process( + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CPACK_PACKAGE_DIRECTORY}/config.yml" "${CPACK_TEMPORARY_DIRECTORY}/usr/bin/config.yml" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CPACK_PACKAGE_DIRECTORY}/yamls/" "${CPACK_TEMPORARY_DIRECTORY}/usr/bin/yamls/" COMMAND ${CMAKE_COMMAND} -E env OUTPUT=${CPACK_PACKAGE_FILE_NAME}.appimage From fffd3f7fe92c6eb45b68c0ca522066bf9c54abb2 Mon Sep 17 00:00:00 2001 From: MegaMech Date: Fri, 14 Nov 2025 14:50:33 -0700 Subject: [PATCH 12/16] RaceManager class (#562) * Create RaceManager.cpp * Create RaceManager class for race lifecycle management Added RaceManager class to manage race events lifecycle. * Refactor World class and implement ClearWorld method Refactor World class constructor and destructor. Implement ClearWorld method to delete all objects and reset state. * Add RaceManager to World class * Update ValidateString for editor mode checks Refactor ValidateString to handle editor mode and empty strings. * Update Text.cpp * Add SetText method to Text class * Document RunGarbageCollector function Added documentation for the RunGarbageCollector function. * Refactor Game.cpp by removing dead code Removed unused ruleset handling and clean-up code. * Update Game.h * Remove CM_SpawnFromLevelProps call * Update Text.cpp * Update World.cpp * Add Clean method to RaceManager class * Update RaceManager.cpp * Update World.cpp * Update World.h * Update World.cpp --- src/engine/GarbageCollector.cpp | 3 ++ src/engine/RaceManager.cpp | 77 ++++++++++++++++++++++++++++++ src/engine/RaceManager.h | 31 ++++++++++++ src/engine/World.cpp | 49 +++++++++++++++---- src/engine/World.h | 6 ++- src/engine/actors/Text.cpp | 19 ++++++-- src/engine/actors/Text.h | 2 + src/port/Game.cpp | 84 ++++----------------------------- src/port/Game.h | 6 +-- src/racing/actors.c | 3 +- 10 files changed, 183 insertions(+), 97 deletions(-) create mode 100644 src/engine/RaceManager.cpp create mode 100644 src/engine/RaceManager.h diff --git a/src/engine/GarbageCollector.cpp b/src/engine/GarbageCollector.cpp index e7f2d99753..d356602d3a 100644 --- a/src/engine/GarbageCollector.cpp +++ b/src/engine/GarbageCollector.cpp @@ -1,6 +1,9 @@ #include "GarbageCollector.h" #include "World.h" +/** + * Removes objects if they are marked for deletion + */ void RunGarbageCollector() { CleanActors(); CleanObjects(); diff --git a/src/engine/RaceManager.cpp b/src/engine/RaceManager.cpp new file mode 100644 index 0000000000..ae1ef828f4 --- /dev/null +++ b/src/engine/RaceManager.cpp @@ -0,0 +1,77 @@ +#include "RaceManager.h" + +#include "AllActors.h" +#include "World.h" +#include "port/Game.h" +#include "engine/editor/Editor.h" +#include "engine/editor/SceneManager.h" + +extern "C" { +#include "render_courses.h" +} + +RaceManager::RaceManager(World& world) : WorldContext(world) { +} + +void RaceManager::Load() { + if (WorldContext.CurrentCourse) { + WorldContext.CurrentCourse->Load(); + } +} + +void RaceManager::PreInit() { + // Ruleset options + if (CVarGetInteger("gDisableItemboxes", false) == true) { + gPlaceItemBoxes = false; + } else { + gPlaceItemBoxes = true; + } +} + +void RaceManager::BeginPlay() { + auto course = WorldContext.CurrentCourse; + + if (course) { + // Do not spawn finishline in credits or battle mode. And if bSpawnFinishline. + if ((gGamestate != CREDITS_SEQUENCE) && (gModeSelection != BATTLE)) { + if (course->bSpawnFinishline) { + if (course->FinishlineSpawnPoint.has_value()) { + AFinishline::Spawn(course->FinishlineSpawnPoint.value(), IRotator(0, 0, 0)); + } else { + AFinishline::Spawn(); + } + + } + } + gEditor.AddLight("Sun", nullptr, D_800DC610[1].l->l.dir); + + course->BeginPlay(); + } +} + +void RaceManager::PostInit() { + // Ruleset options + if (CVarGetInteger("gAllThwompsAreMarty", false) == true) { + for (auto object : gWorldInstance.Objects) { + if (OThwomp* thwomp = dynamic_cast(object)) { + gObjectList[thwomp->_objectIndex].unk_0D5 = OThwomp::States::JAILED; // Sets all the thwomp behaviour flags to marty + thwomp->Behaviour = OThwomp::States::JAILED; + } + } + } + + if (CVarGetInteger("gAllBombKartsChase", false) == true) { + for (auto object : gWorldInstance.Objects) { + if (OBombKart* kart = dynamic_cast(object)) { + kart->Behaviour = OBombKart::States::CHASE; + } + } + } + + if (CVarGetInteger("gGoFish", false) == true) { + OTrophy::Spawn(FVector(0,0,0), OTrophy::TrophyType::GOLD, OTrophy::Behaviour::GO_FISH); + } +} + +void RaceManager::Clean() { +} diff --git a/src/engine/RaceManager.h b/src/engine/RaceManager.h new file mode 100644 index 0000000000..fb38bb33cb --- /dev/null +++ b/src/engine/RaceManager.h @@ -0,0 +1,31 @@ +#pragma once + +class World; + +/** + * This may eventually become GameMode class + * + * The RaceManager orchestrates the event lifecycle + * It defines when a race begins, actors spawn, and the race ends. + * + * It does not define the game mode itself. + * + * You must call the base function if you want the + * default functionality to run + * Example: + * MyRaceManager::BeginPlay() { + * RaceManager::BeginPlay() // <-- Calls default functionality + * // My code here + * } + */ +class RaceManager { +public: + RaceManager(World& world); + virtual void Load(); // virtual required here in the base class to allow inherited classes to override + virtual void PreInit(); + virtual void BeginPlay(); + virtual void PostInit(); + virtual void Clean(); +protected: + World& WorldContext; +}; diff --git a/src/engine/World.cpp b/src/engine/World.cpp index 0430edf6d5..cca7a2199d 100644 --- a/src/engine/World.cpp +++ b/src/engine/World.cpp @@ -21,14 +21,17 @@ extern "C" { #include "mario_raceway_data.h" } -World::World() {} -World::~World() { - CM_CleanWorld(); -} - std::shared_ptr CurrentCourse; Cup* CurrentCup; +World::World() { + RaceManagerInstance = std::make_unique(*this); +} + +World::~World() { + ClearWorld(); +} + std::shared_ptr World::AddCourse(std::shared_ptr course) { gWorldInstance.Courses.push_back(course); return course; @@ -252,13 +255,39 @@ Object* World::GetObjectByIndex(size_t index) { return nullptr; // Or handle the error as needed } +// Deletes all objects from the world void World::ClearWorld(void) { - CM_CleanWorld(); + printf("[Game.cpp] Clean World\n"); + World* world = &gWorldInstance; + for (auto& actor : world->Actors) { + delete actor; + } - // for (size_t i = 0; i < ARRAY_COUNT(gCollisionMesh); i++) { + gWorldInstance.Reset(); // Reset OObjects + for (auto& object : world->Objects) { + delete object; + } - // } + for (auto& emitter : world->Emitters) { + delete emitter; + } + + for (auto& actor : world->StaticMeshActors) { + delete actor; + } + + for (size_t i = 0; i < ARRAY_COUNT(gWorldInstance.playerBombKart); i++) { + gWorldInstance.playerBombKart[i].state = PlayerBombKart::PlayerBombKartState::DISABLED; + gWorldInstance.playerBombKart[i]._primAlpha = 0; + } + + gEditor.ClearObjects(); + gWorldInstance.Actors.clear(); + gWorldInstance.StaticMeshActors.clear(); + gWorldInstance.Objects.clear(); + gWorldInstance.Emitters.clear(); + gWorldInstance.Lakitus.clear(); + + gWorldInstance.GetRaceManager().Clean(); - // gCollisionMesh - // Paths } diff --git a/src/engine/World.h b/src/engine/World.h index 52a307be52..4e1e6e06e3 100644 --- a/src/engine/World.h +++ b/src/engine/World.h @@ -9,6 +9,7 @@ #include "TrainCrossing.h" #include #include +#include "RaceManager.h" #include "Actor.h" #include "StaticMeshActor.h" #include "particles/ParticleEmitter.h" @@ -50,6 +51,9 @@ typedef struct Matrix { explicit World(); ~World(); + RaceManager& GetRaceManager() { return *RaceManagerInstance; } + void SetRaceManager(std::unique_ptr manager) { RaceManagerInstance = std::move(manager); } + std::shared_ptr AddCourse(std::shared_ptr course); AActor* AddActor(AActor* actor); @@ -130,7 +134,7 @@ typedef struct Matrix { std::vector> Courses; size_t CourseIndex = 0; // For browsing courses. private: - + std::unique_ptr RaceManagerInstance; }; extern World gWorldInstance; diff --git a/src/engine/actors/Text.cpp b/src/engine/actors/Text.cpp index 2d2fab0b01..06a74c54d9 100644 --- a/src/engine/actors/Text.cpp +++ b/src/engine/actors/Text.cpp @@ -77,7 +77,11 @@ AText::AText(const SpawnParams& params) : AActor(params) { * But these need to be checked thoroughly before white-listing. */ std::string AText::ValidateString(const std::string_view& s) { - if (s.empty()) { return "Blank Text"; } + if (CVarGetInteger("gIsEditorEnabled", false) == true) { + if (s.empty()) { return "Blank Text"; } + } else { + if (s.empty()) { return ""; } + } Text.clear(); @@ -100,6 +104,11 @@ std::string AText::ValidateString(const std::string_view& s) { return Text; } +void AText::SetText(std::string text) { + AText::ValidateString(text); + Refresh(); +} + /* * Most changes during runtime require a refresh because the text is generated statically * with the intention of this code being somewhat performant @@ -353,7 +362,9 @@ void AText::DrawText3D(Camera* camera) { // Based on func_80095BD0 FrameInterpolation_RecordOpenChild("actor_text", TAG_LETTER(this)); gSPDisplayList(gDisplayListHead++, (Gfx*)D_020077A8); - switch (1) { + + for (CharacterList& tex : TextureList) { + switch (tex.mode) { case 1: gSPDisplayList(gDisplayListHead++, (Gfx*)D_020077F8); break; @@ -361,8 +372,6 @@ void AText::DrawText3D(Camera* camera) { // Based on func_80095BD0 gSPDisplayList(gDisplayListHead++, (Gfx*)D_02007818); break; } - - for (CharacterList& tex : TextureList) { //printf("tex texture %p width %d height %d mode %d col %f\n", tex.Texture, tex.width, tex.height, tex.mode, tex.column); gDPLoadTextureTile_4b(gDisplayListHead++, (Gfx*)tex.Texture, G_IM_FMT_I, tex.width, 0, 0, 0, tex.width, tex.height + 2, 0, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, @@ -585,4 +594,4 @@ void AText::DrawColourEditor(bool* updated) { } } } -} \ No newline at end of file +} diff --git a/src/engine/actors/Text.h b/src/engine/actors/Text.h index 7a9ac74075..e0b4367098 100644 --- a/src/engine/actors/Text.h +++ b/src/engine/actors/Text.h @@ -118,6 +118,8 @@ class AText : public AActor { void DrawColourEditor(bool* updated); void FollowPlayer(); + void SetText(std::string text); + std::string ValidateString(const std::string_view& text); void Refresh(); void Print3D(char* text, s32 tracking, s32 mode); diff --git a/src/port/Game.cpp b/src/port/Game.cpp index 37282346d1..d3fb061e09 100644 --- a/src/port/Game.cpp +++ b/src/port/Game.cpp @@ -90,7 +90,6 @@ Cup* gBattleCup; ModelLoader gModelLoader; HarbourMastersIntro gMenuIntro; -Rulesets gRulesets; Editor::Editor gEditor; @@ -194,14 +193,6 @@ void HM_DrawIntro() { gMenuIntro.HM_DrawIntro(); } -void CM_SpawnFromLevelProps() { - // Spawning actors needs to be delayed to the correct time. - // And loadlevel needs to happen asap - - //Editor::LoadLevel(nullptr); - // Editor::SpawnFromLevelProps(); -} - // Set default course; mario raceway void SetMarioRaceway(void) { SetCourseById(0); @@ -239,10 +230,7 @@ const char* GetCupName(void) { } void LoadCourse() { - if (gWorldInstance.CurrentCourse) { - gRulesets.PreLoad(); - gWorldInstance.CurrentCourse->Load(); - } + gWorldInstance.GetRaceManager().Load(); } size_t GetCourseIndex() { @@ -383,28 +371,15 @@ void CM_DrawStaticMeshActors() { } void CM_BeginPlay() { - auto course = gWorldInstance.CurrentCourse; - - if (course) { - Editor::LoadLevel(course.get(), course->SceneFilePtr); - - gRulesets.PreInit(); - // Do not spawn finishline in credits or battle mode. And if bSpawnFinishline. - if ((gGamestate != CREDITS_SEQUENCE) && (gModeSelection != BATTLE)) { - if (course->bSpawnFinishline) { - if (course->FinishlineSpawnPoint.has_value()) { - AFinishline::Spawn(course->FinishlineSpawnPoint.value(), IRotator(0, 0, 0)); - } else { - AFinishline::Spawn(); - } - - } - } - gEditor.AddLight("Sun", nullptr, D_800DC610[1].l->l.dir); - - course->BeginPlay(); - gRulesets.PostInit(); + if (gWorldInstance.CurrentCourse) { + // This line should likely be moved. + // It's here so PreInit is after the scene file has been loaded + // It used to be at the start of BeginPlay + Editor::LoadLevel(gWorldInstance.CurrentCourse.get(), gWorldInstance.CurrentCourse->SceneFilePtr); } + gWorldInstance.GetRaceManager().PreInit(); + gWorldInstance.GetRaceManager().BeginPlay(); + gWorldInstance.GetRaceManager().PostInit(); } void CM_TickObjects() { @@ -451,16 +426,6 @@ void CM_DrawParticles(s32 cameraId) { } } -// Helps prevents users from forgetting to add a finishline to their course -bool CM_DoesFinishlineExist() { - for (AActor* actor : gWorldInstance.Actors) { - if (dynamic_cast(actor)) { - return true; - } - } - return false; -} - void CM_InitClouds() { if (gWorldInstance.CurrentCourse) { gWorldInstance.CurrentCourse->InitClouds(); @@ -661,36 +626,7 @@ void CM_DeleteActor(size_t index) { * Clean up actors and other game objects. */ void CM_CleanWorld(void) { - printf("[Game.cpp] Clean World\n"); - World* world = &gWorldInstance; - for (auto& actor : world->Actors) { - delete actor; - } - - gWorldInstance.Reset(); // Reset OObjects - for (auto& object : world->Objects) { - delete object; - } - - for (auto& emitter : world->Emitters) { - delete emitter; - } - - for (auto& actor : world->StaticMeshActors) { - delete actor; - } - - for (size_t i = 0; i < ARRAY_COUNT(gWorldInstance.playerBombKart); i++) { - gWorldInstance.playerBombKart[i].state = PlayerBombKart::PlayerBombKartState::DISABLED; - gWorldInstance.playerBombKart[i]._primAlpha = 0; - } - - gEditor.ClearObjects(); - gWorldInstance.Actors.clear(); - gWorldInstance.StaticMeshActors.clear(); - gWorldInstance.Objects.clear(); - gWorldInstance.Emitters.clear(); - gWorldInstance.Lakitus.clear(); + gWorldInstance.ClearWorld(); } struct Actor* CM_AddBaseActor() { diff --git a/src/port/Game.h b/src/port/Game.h index 062b9aafbd..0982d649b0 100644 --- a/src/port/Game.h +++ b/src/port/Game.h @@ -30,8 +30,6 @@ void HM_InitIntro(void); void HM_TickIntro(void); void HM_DrawIntro(void); -void CM_SpawnFromLevelProps(); - void CM_DisplayBattleBombKart(s32 playerId, s32 primAlpha); void CM_DrawBattleBombKarts(s32 cameraId); @@ -68,8 +66,6 @@ void CM_ActivateSecondLapLakitu(s32 playerId); void CM_ActivateFinalLapLakitu(s32 playerId); void CM_ActivateReverseLakitu(s32 playerId); -bool CM_DoesFinishlineExist(); - void CM_InitClouds(); void CM_DrawActors(Camera* camera); @@ -224,4 +220,4 @@ void CM_RunGarbageCollector(void); } #endif -#endif // _GAME_H \ No newline at end of file +#endif // _GAME_H diff --git a/src/racing/actors.c b/src/racing/actors.c index 11bd79203b..d9a59400a6 100644 --- a/src/racing/actors.c +++ b/src/racing/actors.c @@ -1318,7 +1318,6 @@ void init_actors_and_load_textures(void) { destroy_all_actors(); CM_CleanWorld(); - CM_SpawnFromLevelProps(); CM_BeginPlay(); spawn_course_actors(); } @@ -2896,4 +2895,4 @@ const char* get_actor_resource_location_name(s32 id) { default: return "mk:actor"; } -} \ No newline at end of file +} From 5f60e30b59497d54b23097f8dbe5009faa43504d Mon Sep 17 00:00:00 2001 From: MegaMech Date: Sat, 22 Nov 2025 18:13:33 -0700 Subject: [PATCH 13/16] Fix Moles and some UI Interpolation bugs (#565) * Update render_objects.c * Update FrameInterpolation.h * Update render_objects.c * Fix mac compile probably * Fix Mole Dirt Particles * Fix Mole Duplication Bug, probably * Fix drawing using wrong camera bug --- src/engine/objects/Mole.cpp | 17 +++++------ src/engine/objects/MoleGroup.cpp | 29 +++++++++++++++++-- src/engine/objects/MoleGroup.h | 5 +++- src/port/interpolation/FrameInterpolation.h | 10 ++++--- src/racing/skybox_and_splitscreen.c | 2 +- src/render_objects.c | 32 ++++++++------------- 6 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/engine/objects/Mole.cpp b/src/engine/objects/Mole.cpp index 9ee4db7e79..9bc470f90b 100644 --- a/src/engine/objects/Mole.cpp +++ b/src/engine/objects/Mole.cpp @@ -51,15 +51,12 @@ OMole::OMole(FVector pos, OMoleGroup* group) { _count++; } +/** + * Moles are ticked from OMoleGroup + * OMoleTick is func_800821AC + * Dirt particle tick is func_80081790 + */ void OMole::Tick() { - if (_idx == 0) { - for (size_t i = 0; i < gObjectParticle2_SIZE; i++) { - s32 objectIndex = gObjectParticle2[i]; - if (gObjectList[objectIndex].state != 0) { - OMole::func_80081790(objectIndex); - } - } - } } void OMole::Draw(s32 cameraId) { @@ -184,9 +181,9 @@ void OMole::func_8008153C(s32 objectIndex) { gObjectList[loopObjectIndex].activeTLUT = d_course_moo_moo_farm_mole_dirt; gObjectList[loopObjectIndex].tlutList = mole; gObjectList[loopObjectIndex].sizeScaling = 0.15f; - gObjectList[loopObjectIndex].velocity[1] = random_int(0x000AU); + gObjectList[loopObjectIndex].velocity[1] = random_int(10); gObjectList[loopObjectIndex].velocity[1] = (gObjectList[loopObjectIndex].velocity[1] * 0.1) + 4.8; - gObjectList[loopObjectIndex].unk_034 = random_int(5U); + gObjectList[loopObjectIndex].unk_034 = random_int(5); gObjectList[loopObjectIndex].unk_034 = (gObjectList[loopObjectIndex].unk_034 * 0.01) + 0.8; gObjectList[loopObjectIndex].orientation[1] = (0x10000 / sp70) * var_s1; gObjectList[loopObjectIndex].origin_pos[0] = gObjectList[objectIndex].origin_pos[0]; diff --git a/src/engine/objects/MoleGroup.cpp b/src/engine/objects/MoleGroup.cpp index 6e98edbbb2..af936ed3f0 100644 --- a/src/engine/objects/MoleGroup.cpp +++ b/src/engine/objects/MoleGroup.cpp @@ -8,12 +8,17 @@ extern "C" { #include "math_util_2.h" } -OMoleGroup::OMoleGroup(std::vector spawns) { +size_t OMoleGroup::_count = 0; + +OMoleGroup::OMoleGroup(std::vector& spawns) { + _idx = _count; for (auto& pos : spawns) { pos.x * xOrientation; OMole* ptr = reinterpret_cast(gWorldInstance.AddObject(new OMole(pos, this))); _moles.push_back({ptr, pos, false}); } + + _count += 1; } void OMoleGroup::Tick() { @@ -24,14 +29,32 @@ void OMoleGroup::Tick() { mole.Mole->func_800821AC(mole.Mole->_objectIndex, 1); } } + + /** + * This ticks the mole dirt particles. It must be ran *after* MoleGroup is done ticking. Otherwise, the dirt particle directions will not randomize + * The best solution would be for particles to be its own class. But that takes effort + * Instead, we wait until the last MoleGroup has ticked, then we tick the particles one time. + * + * Warning: Calling this more than one time per frame, will result in doubling the speed + */ + if (_idx == _count - 1) { + for (size_t i = 0; i < gObjectParticle2_SIZE; i++) { + s32 objectIndex = gObjectParticle2[i]; + if (gObjectList[objectIndex].state != 0) { + if (nullptr != _moles[0].Mole) { + _moles[0].Mole->func_80081790(objectIndex); + } + } + } + } } void OMoleGroup::func_80081FF4(s32 objectIndex) { init_object(objectIndex, 0); gObjectList[objectIndex].unk_04C = random_int(30) + 5; - s16 mole = random_int(_moles.size() - 1); - for (size_t i = 0; i < _moles.size() - 1; i++) { + s16 mole = random_int(_moles.size()); + for (size_t i = 0; i < _moles.size(); i++) { if (_moles[mole].Active == true) { mole++; if (mole == _moles.size()) { diff --git a/src/engine/objects/MoleGroup.h b/src/engine/objects/MoleGroup.h index dfb920985e..db14432cd9 100644 --- a/src/engine/objects/MoleGroup.h +++ b/src/engine/objects/MoleGroup.h @@ -16,7 +16,7 @@ class OMoleGroup : public OObject { bool Active; }; - explicit OMoleGroup(std::vector moles); + explicit OMoleGroup(std::vector& moles); virtual void Tick() override; @@ -24,4 +24,7 @@ class OMoleGroup : public OObject { std::vector _moles; +private: + static size_t _count; + size_t _idx; }; diff --git a/src/port/interpolation/FrameInterpolation.h b/src/port/interpolation/FrameInterpolation.h index 320bd0f218..7ca85eca0a 100644 --- a/src/port/interpolation/FrameInterpolation.h +++ b/src/port/interpolation/FrameInterpolation.h @@ -20,12 +20,14 @@ extern "C" { #define TAG_ITEM_ADDR(x) ((u32) 0x10000000 | (u32)x) #define TAG_SMOKE_DUST(x) ((u32) 0x20000000 | (u32) (x)) -#define TAG_LETTER(x) ((u32)0x30000000 | ((u32)(x) & 0x0FFFFFFF)) -#define TAG_OBJECT(x) ((u32)0x40000000 | (u32) (uintptr_t) (x)) -#define TAG_CLOUDS(x) ((u32)0x50000000 | (u32) (uintptr_t) (x)) -#define TAG_THWOMP(x) ((u32)0x60000000 | ((u32)(x) & 0x0FFFFFFF)) +#define TAG_LETTER(x) ((u32)0x30000000 | ((u32) (uintptr_t) (x) & 0x0FFFFFFF)) +#define TAG_OBJECT(x) ((u32)0x40000000 | (u32) (uintptr_t) (x)) +#define TAG_CLOUDS(x) ((u32)0x50000000 | (u32) (uintptr_t) (x)) +#define TAG_THWOMP(x) ((u32)0x60000000 | ((u32) (uintptr_t) (x) & 0x0FFFFFFF)) // Mask the bits so that the 7 can't get overridden #define TAG_TRACK(x) ((u32)0x70000000 | ((u32)(x) & 0x0FFFFFFF)) +#define TAG_MINIMAP_DOTS(x) ((u32)0x80000000 | ((u32)(x) & 0x0FFFFFFF)) +#define TAG_PORTRAITS(x) ((u32)0x90000000 | ((u32)(x) & 0x0FFFFFFF)) void FrameInterpolation_ShouldInterpolateFrame(bool shouldInterpolate); diff --git a/src/racing/skybox_and_splitscreen.c b/src/racing/skybox_and_splitscreen.c index 129b55b11e..185ac00688 100644 --- a/src/racing/skybox_and_splitscreen.c +++ b/src/racing/skybox_and_splitscreen.c @@ -884,7 +884,7 @@ void render_screens(s32 mode, s32 cameraId, s32 playerId) { // Draw dynamic game objects render_course_actors(screen); - CM_DrawActors(D_800DC5EC->camera); + CM_DrawActors(camera); CM_DrawStaticMeshActors(); render_object(mode); diff --git a/src/render_objects.c b/src/render_objects.c index 3b0758dc9a..f2ea3fa4a9 100644 --- a/src/render_objects.c +++ b/src/render_objects.c @@ -2655,6 +2655,7 @@ void func_8004E800(s32 playerId) { void func_8004E998(s32 playerId) { if (playerHUD[playerId].unk_81 != 0) { + FrameInterpolation_RecordOpenChild("Player place HUD2", playerId); if (playerHUD[playerId].lapCount != 3) { func_8004A384(playerHUD[playerId].rankX + playerHUD[playerId].slideRankX, playerHUD[playerId].rankY + playerHUD[playerId].slideRankY, 0U, @@ -2669,6 +2670,7 @@ void func_8004E998(s32 playerId) { D_0D015258[gGPCurrentRaceRankByPlayerId[playerId]], D_0D006030, 0x00000040, 0x00000040, 0x00000040, 0x00000040); } + FrameInterpolation_RecordCloseChild(); } } @@ -2809,9 +2811,6 @@ void draw_minimap_character(s32 arg0, s32 playerId, s32 characterId) { s32 center = 0; Player* player = &gPlayerOne[playerId]; - // @port Skip Interpolation, if interpolated later remove this tag - FrameInterpolation_ShouldInterpolateFrame(false); - if (player->type & (1 << 15)) { thing0 = player->pos[0] * CM_GetProps()->Minimap.PlayerScaleFactor; // gMinimapPlayerScale; thing1 = player->pos[2] * CM_GetProps()->Minimap.PlayerScaleFactor; // gMinimapPlayerScale; @@ -2827,6 +2826,7 @@ void draw_minimap_character(s32 arg0, s32 playerId, s32 characterId) { y = (CM_GetProps()->Minimap.Pos[arg0].Y - (CM_GetProps()->Minimap.Height / 2)) + CM_GetProps()->Minimap.PlayerY + (s16) (thing1); if (characterId != 8) { + FrameInterpolation_RecordOpenChild("minimap_dots", TAG_MINIMAP_DOTS( ((arg0 & 0x1) << 6) | ((playerId & 0x7) << 3) | (characterId & 0x7) )); if ((gGPCurrentRaceRankByPlayerId[playerId] == 0) && (gModeSelection != 3) && (gModeSelection != 1)) { func_80046424(x, y, player->rotation[1] + 0x8000, 1.0f, (u8*) common_texture_minimap_kart_character[characterId], common_vtx_player_minimap_icon, @@ -2836,17 +2836,17 @@ void draw_minimap_character(s32 arg0, s32 playerId, s32 characterId) { (u8*) common_texture_minimap_kart_character[characterId], common_vtx_player_minimap_icon, 8, 8, 8, 8); } + FrameInterpolation_RecordCloseChild(); } else { + FrameInterpolation_RecordOpenChild("minimap_dots2", TAG_MINIMAP_DOTS( ((arg0 & 0x1) << 6) | ((playerId & 0x7) << 3) | (characterId & 0x7) )); if (gGPCurrentRaceRankByPlayerId[playerId] == 0) { func_8004C450(x, y, 8, 8, (u8*) common_texture_minimap_progress[player->characterId]); } else { draw_hud_2d_texture_wide(x, y, 8, 8, (u8*) common_texture_minimap_progress[player->characterId]); } + FrameInterpolation_RecordCloseChild(); } } - - // @port Resume Interpolation, if interpolated later remove this tag - FrameInterpolation_ShouldInterpolateFrame(true); } #else GLOBAL_ASM("asm/non_matchings/render_objects/draw_minimap_character.s") @@ -3308,25 +3308,18 @@ void func_80050C68(void) { } } -#ifdef NON_MATCHING - -// Something about the handling of the `player` variable is weird. -// All commands are present and correct, 2 of them are out of position -// https://decomp.me/scratch/PvJ5D void func_80050E34(s32 playerId, s32 arg1) { s32 objectIndex; s32 spD0; s32 spCC; - UNUSED s32 stackPadding; s32 spC4; s32 lapCount; s32 characterId; s32 spB8; - s32 temp_v0_2; + s32 result; Object* object; - Player* player; + Player *player = &gPlayerOne[playerId]; - player = &gPlayerOne[playerId]; lapCount = gLapCountByPlayerId[playerId]; characterId = player->characterId; objectIndex = D_8018CE10[playerId].objectIndex; @@ -3337,13 +3330,14 @@ void func_80050E34(s32 playerId, s32 arg1) { spC4 = 0x00000078; } - temp_v0_2 = func_80050644(playerId, &spD0, &spCC); - if ((temp_v0_2 == 2) || (temp_v0_2 == 3)) { + result = func_80050644(playerId, &spD0, &spCC); + if ((result == 2) || (result == 3)) { spB8 = 1; } else { spB8 = 0; } + FrameInterpolation_RecordOpenChild("progress_portraits", TAG_PORTRAITS( ((playerId & 0x7) << 8) | ((characterId & 0x7) << 5) | (objectIndex & 0x1F) )); if ((IsYoshiValley()) && (lapCount < 3)) { gSPDisplayList(gDisplayListHead++, D_0D007DB8); gDPLoadTLUT_pal256(gDisplayListHead++, common_tlut_portrait_bomb_kart_and_question_mark); @@ -3390,10 +3384,8 @@ void func_80050E34(s32 playerId, s32 arg1) { gSPDisplayList(gDisplayListHead++, D_0D0069E0); } } + FrameInterpolation_RecordCloseChild(); } -#else -GLOBAL_ASM("asm/non_matchings/render_objects/func_80050E34.s") -#endif void func_800514BC(void) { s32 temp_a0; From 52554e21cc0b515c95cf8108ff6255703f5465e3 Mon Sep 17 00:00:00 2001 From: MegaMech Date: Sat, 22 Nov 2025 22:07:30 -0700 Subject: [PATCH 14/16] Fix Mole Spawns and Tick Rate (#566) * Fix Mole Spawns and Tick Rate * Fix Compile * Actually Fix Compile --- src/engine/courses/MooMooFarm.cpp | 198 ++++++++++-------------------- src/engine/objects/MoleGroup.cpp | 11 +- src/engine/objects/MoleGroup.h | 8 +- 3 files changed, 80 insertions(+), 137 deletions(-) diff --git a/src/engine/courses/MooMooFarm.cpp b/src/engine/courses/MooMooFarm.cpp index 5166442d4e..375a4e743f 100644 --- a/src/engine/courses/MooMooFarm.cpp +++ b/src/engine/courses/MooMooFarm.cpp @@ -172,47 +172,6 @@ void MooMooFarm::LoadTextures() { dma_textures(gTextureCow05Right, 0x00000400U, 0x00000800U); // 0x0300E800 } -// These are full arrays that are not used in the original game -static std::vector sMoleSpawns1 = { - { FVector(771, 20, -2022) }, - { FVector(807, 15, -2063) }, - { FVector(847, 18, -2040) }, - { FVector(913, 14, -2054) }, - { FVector(939, 21, -1997) }, - { FVector(941, 17, -2024) }, - { FVector(994, 17, -1994) }, - { FVector(863, 22, -2010) }, -}; - -static std::vector sMoleSpawns2 = { - { FVector(1500, 2, 1140) }, - { FVector(1510, 15, 1050) }, - { FVector(1609, 21, 935) }, - { FVector(1289, 3, 1269) }, - { FVector(1468, 22, 1046) }, - { FVector(1380, 12, 1154) }, - { FVector(1297, 19, 1170) }, - { FVector(1589, 11, 1004) }, - { FVector(1414, 3, 1185) }, - { FVector(1405, 4, 1254) }, - { FVector(1463, 8, 1118) }, -}; - -static std::vector sMoleSpawns3 = { - { FVector(701, 2, 1279) }, - { FVector(811, 8, 1278) }, - { FVector(791, 16, 1229) }, - { FVector(876, 15, 1266) }, - { FVector(984, 23, 1248) }, - { FVector(891, 20, 1242) }, - { FVector(920, 15, 1304) }, - { FVector(823, 6, 1327) }, - { FVector(717, 8, 1239) }, - { FVector(695, 19, 1176) }, - { FVector(628, 8, 1191) }, - { FVector(724, 4, 1339) }, -}; - void MooMooFarm::BeginPlay() { if (gPlayerCountSelection1 != 4) { spawn_foliage((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_moo_moo_farm_tree_spawn)); @@ -224,102 +183,79 @@ void MooMooFarm::BeginPlay() { } if (gGamestate != CREDITS_SEQUENCE) { + std::vector moleSpawns1 = { + { FVector(771, 20, -2022) }, + { FVector(807, 15, -2063) }, + { FVector(847, 18, -2040) }, + { FVector(913, 14, -2054) }, + { FVector(939, 21, -1997) }, + { FVector(941, 17, -2024) }, + { FVector(994, 17, -1994) }, + { FVector(863, 22, -2010) }, + }; + + std::vector moleSpawns2 = { + { FVector(1500, 2, 1140) }, + { FVector(1510, 15, 1050) }, + { FVector(1609, 21, 935) }, + { FVector(1289, 3, 1269) }, + { FVector(1468, 22, 1046) }, + { FVector(1380, 12, 1154) }, + { FVector(1297, 19, 1170) }, + { FVector(1589, 11, 1004) }, + { FVector(1414, 3, 1185) }, + { FVector(1405, 4, 1254) }, + { FVector(1463, 8, 1118) }, + }; + + std::vector moleSpawns3 = { + { FVector(701, 2, 1279) }, + { FVector(811, 8, 1278) }, + { FVector(791, 16, 1229) }, + { FVector(876, 15, 1266) }, + { FVector(984, 23, 1248) }, + { FVector(891, 20, 1242) }, + { FVector(920, 15, 1304) }, + { FVector(823, 6, 1327) }, + { FVector(717, 8, 1239) }, + { FVector(695, 19, 1176) }, + { FVector(628, 8, 1191) }, + { FVector(724, 4, 1339) }, + }; + + // How many moles can appear per tick? + size_t tick1, tick2, tick3; switch(gCCSelection) { - case CC_50: { - std::vector moleSpawns1_50 = { { FVector(771, 20, -2022) }, - { FVector(807, 15, -2063) }, - { FVector(847, 18, -2040) }, - { FVector(913, 14, -2054) } }; - - gWorldInstance.AddObject(new OMoleGroup(moleSpawns1_50)); - - std::vector moleSpawns2_50 = { { FVector(1500, 2, 1140) }, { FVector(1510, 15, 1050) }, - { FVector(1609, 21, 935) }, { FVector(1289, 3, 1269) }, - { FVector(1468, 22, 1046) }, { FVector(1380, 12, 1154) } }; - - gWorldInstance.AddObject(new OMoleGroup(moleSpawns2_50)); - - std::vector moleSpawns3_50 = { { FVector(701, 2, 1279) }, { FVector(811, 8, 1278) }, - { FVector(791, 16, 1229) }, { FVector(876, 15, 1266) }, - { FVector(984, 23, 1248) }, { FVector(891, 20, 1242) } }; - - gWorldInstance.AddObject(new OMoleGroup(moleSpawns3_50)); + case CC_50: + tick1 = 4; + tick2 = 6; + tick3 = 6; break; - } - case CC_100: { - std::vector moleSpawns1_100 = { { FVector(771, 20, -2022) }, - { FVector(807, 15, -2063) }, - { FVector(847, 18, -2040) }, - { FVector(913, 14, -2054) }, - { FVector(939, 21, -1997) } }; - - gWorldInstance.AddObject(new OMoleGroup(moleSpawns1_100)); - - std::vector moleSpawns2_100 = { { FVector(1500, 2, 1140) }, { FVector(1510, 15, 1050) }, - { FVector(1609, 21, 935) }, { FVector(1289, 3, 1269) }, - { FVector(1468, 22, 1046) }, { FVector(1380, 12, 1154) }, - { FVector(1297, 19, 1170) }, { FVector(1589, 11, 1004) } }; - - gWorldInstance.AddObject(new OMoleGroup(moleSpawns2_100)); - - std::vector moleSpawns3_100 = { { FVector(701, 2, 1279) }, { FVector(811, 8, 1278) }, - { FVector(791, 16, 1229) }, { FVector(876, 15, 1266) }, - { FVector(984, 23, 1248) }, { FVector(891, 20, 1242) }, - { FVector(920, 15, 1304) }, { FVector(823, 6, 1327) } }; - - gWorldInstance.AddObject(new OMoleGroup(moleSpawns3_100)); + case CC_100: + tick1 = 5; + tick2 = 8; + tick3 = 8; break; - } - case CC_150: { - std::vector moleSpawns1_150 = { { FVector(771, 20, -2022) }, - { FVector(807, 15, -2063) }, - { FVector(847, 18, -2040) }, - { FVector(913, 14, -2054) }, - { FVector(939, 21, -1997) } }; - - gWorldInstance.AddObject(new OMoleGroup(moleSpawns1_150)); - - std::vector moleSpawns2_150 = { { FVector(1500, 2, 1140) }, { FVector(1510, 15, 1050) }, - { FVector(1609, 21, 935) }, { FVector(1289, 3, 1269) }, - { FVector(1468, 22, 1046) }, { FVector(1380, 12, 1154) }, - { FVector(1297, 19, 1170) }, { FVector(1589, 11, 1004) } }; - - gWorldInstance.AddObject(new OMoleGroup(moleSpawns2_150)); - - std::vector moleSpawns3_150 = { { FVector(701, 2, 1279) }, { FVector(811, 8, 1278) }, - { FVector(791, 16, 1229) }, { FVector(876, 15, 1266) }, - { FVector(984, 23, 1248) }, { FVector(891, 20, 1242) }, - { FVector(920, 15, 1304) }, { FVector(823, 6, 1327) }, - { FVector(717, 8, 1239) }, { FVector(695, 19, 1176) } }; - - gWorldInstance.AddObject(new OMoleGroup(moleSpawns3_150)); + case CC_150: + tick1 = 5; + tick2 = 8; + tick3 = 10; break; - } - case CC_EXTRA: { - std::vector moleSpawns1_extra = { { FVector(771, 20, -2022) }, - { FVector(807, 15, -2063) }, - { FVector(847, 18, -2040) }, - { FVector(913, 14, -2054) }, - { FVector(939, 21, -1997) } }; - - gWorldInstance.AddObject(new OMoleGroup(moleSpawns1_extra)); - - std::vector moleSpawns2_extra = { { FVector(1500, 2, 1140) }, { FVector(1510, 15, 1050) }, - { FVector(1609, 21, 935) }, { FVector(1289, 3, 1269) }, - { FVector(1468, 22, 1046) }, { FVector(1380, 12, 1154) }, - { FVector(1297, 19, 1170) }, { FVector(1589, 11, 1004) } }; - - gWorldInstance.AddObject(new OMoleGroup(moleSpawns2_extra)); - - std::vector moleSpawns3_extra = { { FVector(701, 2, 1279) }, { FVector(811, 8, 1278) }, - { FVector(791, 16, 1229) }, { FVector(876, 15, 1266) }, - { FVector(984, 23, 1248) }, { FVector(891, 20, 1242) }, - { FVector(920, 15, 1304) }, { FVector(823, 6, 1327) } }; - - gWorldInstance.AddObject(new OMoleGroup(moleSpawns3_extra)); + case CC_EXTRA: + tick1 = 5; + tick2 = 8; + tick3 = 8; + break; + default: + tick1 = 4; + tick2 = 6; + tick3 = 6; break; - } } + + gWorldInstance.AddObject(new OMoleGroup(moleSpawns1, tick1)); + gWorldInstance.AddObject(new OMoleGroup(moleSpawns2, tick2)); + gWorldInstance.AddObject(new OMoleGroup(moleSpawns3, tick3)); } if (gModeSelection == VERSUS) { diff --git a/src/engine/objects/MoleGroup.cpp b/src/engine/objects/MoleGroup.cpp index af936ed3f0..d1b8cb27d3 100644 --- a/src/engine/objects/MoleGroup.cpp +++ b/src/engine/objects/MoleGroup.cpp @@ -10,8 +10,9 @@ extern "C" { size_t OMoleGroup::_count = 0; -OMoleGroup::OMoleGroup(std::vector& spawns) { +OMoleGroup::OMoleGroup(std::vector& spawns, size_t tickRate) { _idx = _count; + _tickRate = tickRate; for (auto& pos : spawns) { pos.x * xOrientation; OMole* ptr = reinterpret_cast(gWorldInstance.AddObject(new OMole(pos, this))); @@ -22,11 +23,11 @@ OMoleGroup::OMoleGroup(std::vector& spawns) { } void OMoleGroup::Tick() { - for (auto &mole : _moles) { - if (gObjectList[mole.Mole->_objectIndex].state == 0) { - func_80081FF4(mole.Mole->_objectIndex); + for (size_t i = 0; i < std::min(_tickRate, _moles.size()); i++) { + if (gObjectList[_moles[i].Mole->_objectIndex].state == 0) { + OMoleGroup::func_80081FF4(_moles[i].Mole->_objectIndex); } else { - mole.Mole->func_800821AC(mole.Mole->_objectIndex, 1); + _moles[i].Mole->func_800821AC(_moles[i].Mole->_objectIndex, 1); } } diff --git a/src/engine/objects/MoleGroup.h b/src/engine/objects/MoleGroup.h index db14432cd9..31d9c28196 100644 --- a/src/engine/objects/MoleGroup.h +++ b/src/engine/objects/MoleGroup.h @@ -16,7 +16,12 @@ class OMoleGroup : public OObject { bool Active; }; - explicit OMoleGroup(std::vector& moles); + /** + * TickRate: + * How many moles can pop out per frame. + * The mole must also be ready to jump to be considered. + */ + explicit OMoleGroup(std::vector& moles, size_t tickRate); virtual void Tick() override; @@ -24,6 +29,7 @@ class OMoleGroup : public OObject { std::vector _moles; + size_t _tickRate; private: static size_t _count; size_t _idx; From 96f02bd70f4f24ab3fa28d479ee0c7ff84928e33 Mon Sep 17 00:00:00 2001 From: MegaMech Date: Sun, 23 Nov 2025 13:18:51 -0700 Subject: [PATCH 15/16] Only Spawn Three ChainChomps for Rainbow Track (#569) * Update RainbowRoad.cpp * Update ChainChomp.cpp * Decrement actor counters in destructor * Update Finishline.cpp --- src/engine/actors/Finishline.cpp | 1 + src/engine/actors/Finishline.h | 4 +++- src/engine/courses/RainbowRoad.cpp | 4 ---- src/engine/objects/ChainChomp.cpp | 4 ++-- src/engine/objects/Crab.h | 3 +++ src/engine/objects/Mole.h | 3 +++ src/engine/objects/MoleGroup.h | 3 +++ 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/engine/actors/Finishline.cpp b/src/engine/actors/Finishline.cpp index 4164b5f3b4..52fa9c895b 100644 --- a/src/engine/actors/Finishline.cpp +++ b/src/engine/actors/Finishline.cpp @@ -74,6 +74,7 @@ void AFinishline::Draw(Camera *camera) { maxObjectsReached = render_set_position(mtx, 0) == 0; if (maxObjectsReached) { + FrameInterpolation_RecordCloseChild(); return; } diff --git a/src/engine/actors/Finishline.h b/src/engine/actors/Finishline.h index ec99b637f2..b5f8c2dfdc 100644 --- a/src/engine/actors/Finishline.h +++ b/src/engine/actors/Finishline.h @@ -20,7 +20,9 @@ class AFinishline : public AActor { */ AFinishline(const SpawnParams& params); - virtual ~AFinishline() override = default; + ~AFinishline() { + _count--; + } // This is simply a helper function to keep Spawning code clean static inline AFinishline* Spawn(FVector pos, IRotator rot) { diff --git a/src/engine/courses/RainbowRoad.cpp b/src/engine/courses/RainbowRoad.cpp index f2f99b488a..bfc64e976e 100644 --- a/src/engine/courses/RainbowRoad.cpp +++ b/src/engine/courses/RainbowRoad.cpp @@ -149,10 +149,6 @@ void RainbowRoad::BeginPlay() { gWorldInstance.AddObject(new OChainChomp()); gWorldInstance.AddObject(new OChainChomp()); gWorldInstance.AddObject(new OChainChomp()); - gWorldInstance.AddObject(new OChainChomp()); - gWorldInstance.AddObject(new OChainChomp()); - gWorldInstance.AddObject(new OChainChomp()); - gWorldInstance.AddObject(new OChainChomp()); } if (gModeSelection == VERSUS) { diff --git a/src/engine/objects/ChainChomp.cpp b/src/engine/objects/ChainChomp.cpp index fd3faa622c..9f750831ba 100644 --- a/src/engine/objects/ChainChomp.cpp +++ b/src/engine/objects/ChainChomp.cpp @@ -20,8 +20,8 @@ OChainChomp::OChainChomp() { Name = "Chain Chomp"; ResourceName = "mk:chain_chomp"; _idx = _count; - init_object(indexObjectList2[_count], 0); - _objectIndex = indexObjectList2[_count]; + init_object(indexObjectList2[_idx], 0); + _objectIndex = indexObjectList2[_idx]; _count++; } diff --git a/src/engine/objects/Crab.h b/src/engine/objects/Crab.h index d2c694ff17..08056f1144 100644 --- a/src/engine/objects/Crab.h +++ b/src/engine/objects/Crab.h @@ -41,6 +41,9 @@ class OCrab : public OObject { } explicit OCrab(const SpawnParams& params); + ~OCrab() { + _count--; + } virtual void Tick() override; virtual void Draw(s32 cameraId) override; diff --git a/src/engine/objects/Mole.h b/src/engine/objects/Mole.h index 1457c72cc6..da3a151a28 100644 --- a/src/engine/objects/Mole.h +++ b/src/engine/objects/Mole.h @@ -22,6 +22,9 @@ class OMoleGroup; class OMole : public OObject { public: explicit OMole(FVector pos, OMoleGroup* group); + ~OMole() { + _count--; + } virtual void Tick() override; virtual void Draw(s32 cameraId) override; diff --git a/src/engine/objects/MoleGroup.h b/src/engine/objects/MoleGroup.h index 31d9c28196..901f3c2f5e 100644 --- a/src/engine/objects/MoleGroup.h +++ b/src/engine/objects/MoleGroup.h @@ -22,6 +22,9 @@ class OMoleGroup : public OObject { * The mole must also be ready to jump to be considered. */ explicit OMoleGroup(std::vector& moles, size_t tickRate); + ~OMoleGroup() { + _count--; + } virtual void Tick() override; From ecec63d6c932dca8904fe6ad47dffa55081da3f5 Mon Sep 17 00:00:00 2001 From: patorcek14 Date: Wed, 26 Nov 2025 06:15:54 -0600 Subject: [PATCH 16/16] Initialize devcontainer with universal image git@github.com:HarbourMasters/SpaghettiKart.githttps://github.com/HarbourMasters/SpaghettiKart.gitgh pr checkout 531git@github.com --- .devcontainer/devcontainer.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..39bbd2681d --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "mcr.microsoft.com/devcontainers/universal:2", + "features": {} +}