#include "TrackProperties.h" #include "port/ui/PortMenu.h" #include "UIWidgets.h" #include "libultraship/src/Context.h" #include #include #include #include #include "spdlog/formatter.h" #include #include #include "port/Game.h" extern "C" { #include "code_800029B0.h" #include "sounds.h" #include "external.h" #include "render_courses.h" #include "render_objects.h" } namespace Editor { TrackPropertiesWindow::~TrackPropertiesWindow() { SPDLOG_TRACE("destruct track properties window"); } void TrackPropertiesWindow::DrawElement() { static char idBuffer[256] = "mk:mario_raceway"; static char nameBuffer[256] = "Mario Raceway"; static char debugNameBuffer[256] = "m circuit"; static char lengthBuffer[256] = "567m"; ImGui::InputText("ID", idBuffer, IM_ARRAYSIZE(idBuffer)); ImGui::InputText("Name", gWorldInstance.CurrentCourse->Props.Name, IM_ARRAYSIZE(nameBuffer)); ImGui::InputText("Debug Name", gWorldInstance.CurrentCourse->Props.DebugName, IM_ARRAYSIZE(debugNameBuffer)); ImGui::InputText("Course Length", gWorldInstance.CurrentCourse->Props.CourseLength, IM_ARRAYSIZE(lengthBuffer)); ImGui::InputFloat("Water Level", &gWorldInstance.CurrentCourse->Props.WaterLevel); if (ImGui::CollapsingHeader("Camera")) { ImGui::InputFloat("Near Perspective", &gWorldInstance.CurrentCourse->Props.NearPersp); ImGui::InputFloat("Far Perspective", &gWorldInstance.CurrentCourse->Props.FarPersp); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::Text("Controls the far clipping distance for perspective rendering."); ImGui::Text("Disable culling enhancement needs to be off to see a difference."); ImGui::EndTooltip(); } } if (ImGui::CollapsingHeader("Environment")) { TrackPropertiesWindow::DrawLight(); } if (ImGui::CollapsingHeader("AI")) { ImGui::InputFloat("AI Max Separation", &gWorldInstance.CurrentCourse->Props.AIMaximumSeparation); ImGui::InputFloat("AI Min Separation", &gWorldInstance.CurrentCourse->Props.AIMinimumSeparation); ImGui::InputInt("AI Steering Sensitivity", (int*)&gWorldInstance.CurrentCourse->Props.AISteeringSensitivity); ImGui::Separator(); for (size_t i = 0; i < 32; i++) { ImGui::InputScalar(("Element " + std::to_string(i)).c_str(), ImGuiDataType_S16, &gWorldInstance.CurrentCourse->Props.AIDistance[i]); } } if (ImGui::CollapsingHeader("Random Junk")) { for (size_t i = 0; i < 4; i++) { ImGui::InputFloat(fmt::format("CurveTargetSpeed[{}]", i).c_str(), &gWorldInstance.CurrentCourse->Props.CurveTargetSpeed[i]); } ImGui::Separator(); for (size_t i = 0; i < 4; i++) { ImGui::InputFloat(fmt::format("NormalTargetSpeed[{}]", i).c_str(), &gWorldInstance.CurrentCourse->Props.NormalTargetSpeed[i]); } ImGui::Separator(); for (size_t i = 0; i < 4; i++) { ImGui::InputFloat(fmt::format("D_0D0096B8[{}]", i).c_str(), &gWorldInstance.CurrentCourse->Props.D_0D0096B8[i]); } ImGui::Separator(); for (size_t i = 0; i < 4; i++) { ImGui::InputFloat(fmt::format("OffTrackTargetSpeed[{}]", i).c_str(), &gWorldInstance.CurrentCourse->Props.OffTrackTargetSpeed[i]); } } float minimapColour[3]; RGB8ToFloat((u8*)&gWorldInstance.CurrentCourse->Props.Minimap.Colour, minimapColour); if (ImGui::CollapsingHeader("Minimap")) { ImGui::Text("Position"); ImGui::SameLine(); if (ImGui::DragInt2("##MinimapPosition", &gWorldInstance.CurrentCourse->Props.Minimap.Pos[0].X, 1.0f)) { } ImGui::Text("P2 Position"); ImGui::SameLine(); if (ImGui::DragInt2("##MinimapPosition2p", &gWorldInstance.CurrentCourse->Props.Minimap.Pos[1].X, 1.0f)) { } ImGui::Text("Player Markers"); ImGui::SameLine(); if (ImGui::DragInt2("##MinimapPlayers", &gWorldInstance.CurrentCourse->Props.Minimap.PlayerX, 1.0f)) { } ImGui::Text("Player Scale Factor"); ImGui::SameLine(); if (ImGui::DragFloat("##MinimapScaleFactor", &gWorldInstance.CurrentCourse->Props.Minimap.PlayerScaleFactor, 0.0001f)) { } ImGui::Text("Finishline"); ImGui::SameLine(); ImGui::DragFloat2("##MinimapFinishlineX", &gWorldInstance.CurrentCourse->Props.Minimap.FinishlineX, 1.0f); ImGui::Text("Colour"); ImGui::SameLine(); ImGui::ColorEdit3("##MinimapColour", minimapColour, 1.0f); } FloatToRGB8(minimapColour, (u8*)&gWorldInstance.CurrentCourse->Props.Minimap.Colour); // Convert and pass to ImGui ColorEdit3 float topRight[3], bottomRight[3], bottomLeft[3], topLeft[3]; float floorTopRight[3], floorBottomRight[3], floorBottomLeft[3], floorTopLeft[3]; // Convert RGB8 (0-255) to float (0.0f to 1.0f) RGB8ToFloat((u8*)&gWorldInstance.CurrentCourse->Props.Skybox.TopRight, topRight); RGB8ToFloat((u8*)&gWorldInstance.CurrentCourse->Props.Skybox.BottomRight, bottomRight); RGB8ToFloat((u8*)&gWorldInstance.CurrentCourse->Props.Skybox.BottomLeft, bottomLeft); RGB8ToFloat((u8*)&gWorldInstance.CurrentCourse->Props.Skybox.TopLeft, topLeft); RGB8ToFloat((u8*)&gWorldInstance.CurrentCourse->Props.Skybox.FloorTopRight, floorTopRight); RGB8ToFloat((u8*)&gWorldInstance.CurrentCourse->Props.Skybox.FloorBottomRight, floorBottomRight); RGB8ToFloat((u8*)&gWorldInstance.CurrentCourse->Props.Skybox.FloorBottomLeft, floorBottomLeft); RGB8ToFloat((u8*)&gWorldInstance.CurrentCourse->Props.Skybox.FloorTopLeft, floorTopLeft); if (ImGui::CollapsingHeader("Skybox")) { ImGui::ColorEdit3("Skybox Top Right", topRight); ImGui::ColorEdit3("Skybox Bottom Right", bottomRight); ImGui::ColorEdit3("Skybox Bottom Left", bottomLeft); ImGui::ColorEdit3("Skybox Top Left", topLeft); ImGui::ColorEdit3("Skybox Floor Top Right", floorTopRight); ImGui::ColorEdit3("Skybox Floor Bottom Right", floorBottomRight); ImGui::ColorEdit3("Skybox Floor Bottom Left", floorBottomLeft); ImGui::ColorEdit3("Skybox Floor Top Left", floorTopLeft); } // Convert the modified float values back to RGB8 (0-255) FloatToRGB8(topRight, (u8*)&gWorldInstance.CurrentCourse->Props.Skybox.TopRight); FloatToRGB8(bottomRight, (u8*)&gWorldInstance.CurrentCourse->Props.Skybox.BottomRight); FloatToRGB8(bottomLeft, (u8*)&gWorldInstance.CurrentCourse->Props.Skybox.BottomLeft); FloatToRGB8(topLeft, (u8*)&gWorldInstance.CurrentCourse->Props.Skybox.TopLeft); FloatToRGB8(floorTopRight, (u8*)&gWorldInstance.CurrentCourse->Props.Skybox.FloorTopRight); FloatToRGB8(floorBottomRight, (u8*)&gWorldInstance.CurrentCourse->Props.Skybox.FloorBottomRight); FloatToRGB8(floorBottomLeft, (u8*)&gWorldInstance.CurrentCourse->Props.Skybox.FloorBottomLeft); FloatToRGB8(floorTopLeft, (u8*)&gWorldInstance.CurrentCourse->Props.Skybox.FloorTopLeft); TrackPropertiesWindow::DrawMusic(); } void TrackPropertiesWindow::DrawMusic() { const char* items[] = { "None", "Title Screen", "Main Menu", "Raceways Wario Stadium", "Moo Moo Farm / Yoshi Valley", "Choco Mountain", "Koopa Troopa Beach", "Banshee Boardwalk", "Frappe Snowland", "Bowser's Castle", "Kalimari Desert", "Start Grid GP/VS", "Final Lap Fanfare", "Finish 1st Place", "Finish 2nd-4th Place", "Finish 5th-8th Place", "16", "Star Jingle", "Rainbow Road", "DK Jungle", "Game Over", "Toad's Turnpike", "Start Grid Time Attack", "VS Battle Results", "Losing Results", "Battle Arenas", "Award Ceremony Buildup", "Award Ceremony 1st-3rd", "Staff Roll", "Award Ceremony 4th-8th" }; const char* currentItem = MusicSeqToString(gWorldInstance.CurrentCourse->Props.Sequence); // Get the current selected value's string if (ImGui::BeginCombo("Music Sequence", currentItem)) { for (int i = 0; i < IM_ARRAYSIZE(items); ++i) { bool isSelected = (currentItem == items[i]); if (ImGui::Selectable(items[i], isSelected)) { // Update the sequence when an option is selected gWorldInstance.CurrentCourse->Props.Sequence = static_cast(i); play_sequence(gWorldInstance.CurrentCourse->Props.Sequence); // Call play_sequence with the updated sequence // Update currentItem after selection is made currentItem = items[i]; } if (isSelected) { ImGui::SetItemDefaultFocus(); } } ImGui::EndCombo(); } } // Enum to string conversion const char* TrackPropertiesWindow::MusicSeqToString(MusicSeq seq) { switch (seq) { case MUSIC_SEQ_00: return "None"; case MUSIC_SEQ_TITLE_SCREEN: return "Title Screen"; case MUSIC_SEQ_MAIN_MENU: return "Main Menu"; case MUSIC_SEQ_RACEWAYS_WARIO_STADIUM: return "Raceways Wario Stadium"; case MUSIC_SEQ_MOO_MOO_FARM_YOSHI_VALLEY: return "Moo Moo Farm / Yoshi Valley"; case MUSIC_SEQ_CHOCO_MOUNTAIN: return "Choco Mountain"; case MUSIC_SEQ_KOOPA_TROOPA_BEACH: return "Koopa Troopa Beach"; case MUSIC_SEQ_BANSHEE_BOARDWALK: return "Banshee Boardwalk"; case MUSIC_SEQ_FRAPPE_SNOWLAND: return "Frappe Snowland"; case MUSIC_SEQ_BOWSERS_CASTLE: return "Bowser's Castle"; case MUSIC_SEQ_KALIMARI_DESERT: return "Kalimari Desert"; case MUSIC_SEQ_START_GRID_GP_VS: return "Start Grid GP/VS"; case MUSIC_SEQ_FINAL_LAP_FANFARE: return "Final Lap Fanfare"; case MUSIC_SEQ_FINISH_1ST_PLACE: return "Finish 1st Place"; case MUSIC_SEQ_FINISH_2ND_4TH_PLACE: return "Finish 2nd-4th Place"; case MUSIC_SEQ_FINISH_5TH_8TH_PLACE: return "Finish 5th-8th Place"; case MUSIC_SEQ_16: return "16"; case MUSIC_SEQ_STAR_JINGLE: return "Star Jingle"; case MUSIC_SEQ_RAINBOW_ROAD: return "Rainbow Road"; case MUSIC_SEQ_DK_JUNGLE: return "DK Jungle"; case MUSIC_SEQ_GAME_OVER: return "Game Over"; case MUSIC_SEQ_TOADS_TURNPIKE: return "Toad's Turnpike"; case MUSIC_SEQ_START_GIRD_TIME_ATTACK: return "Start Grid Time Attack"; case MUSIC_SEQ_VS_BATTLE_RESULTS: return "VS Battle Results"; case MUSIC_SEQ_LOSING_RESULTS: return "Losing Results"; case MUSIC_SEQ_BATTLE_ARENAS: return "Battle Arenas"; case MUSIC_SEQ_AWARD_CEREMONY_BUILDUP: return "Award Ceremony Buildup"; case MUSIC_SEQ_AWARD_CEREMONY_1ST_3RD: return "Award Ceremony 1st-3rd"; case MUSIC_SEQ_STAFF_ROLL: return "Staff Roll"; case MUSIC_SEQ_AWARD_CEREMONY_4TH_8TH: return "Award Ceremony 4th-8th"; default: return "None"; } } void TrackPropertiesWindow::DrawLight() { // Convert and pass to ImGui ColorEdit3 for (int i = 0; i < 2; ++i) { float ambient[3], diffuse[3], direction[3]; RGB8ToFloat((u8*)&D_800DC610[i].a.l.col, ambient); RGB8ToFloat((u8*)&D_800DC610[i].l->l.col, diffuse); RGB8ToFloat((u8*)&D_800DC610[i].l->l.dir, direction); // Edit the ambient RGB color ImGui::Text("Light %d - Ambient Color", i + 1); ImGui::ColorEdit3(("Ambient Color " + std::to_string(i)).c_str(), ambient); // Modify ambient color // Edit the diffuse RGB color ImGui::Text("Light %d - Diffuse Color", i + 1); ImGui::ColorEdit3(("Diffuse Color " + std::to_string(i)).c_str(), diffuse); // Modify diffuse color // Edit the direction RGB color (this could be represented as a direction vector) ImGui::Text("Light %d - Direction", i + 1); ImGui::ColorEdit3(("Direction Color " + std::to_string(i)).c_str(), direction); // Modify direction vector color FloatToRGB8(ambient, (u8*)&D_800DC610[i].a.l.col); FloatToRGB8(ambient, (u8*)&D_800DC610[i].a.l.colc); FloatToRGB8(diffuse, (u8*)&D_800DC610[i].l->l.col); FloatToRGB8(diffuse, (u8*)&D_800DC610[i].l->l.colc); FloatToRGB8(direction, (u8*)&D_800DC610[i].l->l.dir); } } // Convert s16 color values to float (normalized to [0, 1] range) void TrackPropertiesWindow::RGB8ToFloat(const u8* src, float* dst) { for (int i = 0; i < 3; ++i) { dst[i] = src[i] / 255.0f; // Normalize to the range [0.0f, 1.0f] } } void TrackPropertiesWindow::FloatToRGB8(const float* src, u8* dst) { for (int i = 0; i < 3; ++i) { dst[i] = static_cast(src[i] * 255.0f); // Scale to [0, 255] range } } }