diff options
| author | MegaMech <MegaMech@users.noreply.github.com> | 2025-11-09 19:07:44 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-11-09 19:07:44 -0700 |
| commit | de9c5d510175bba11ab8704e67be3a3a94f9149e (patch) | |
| tree | 34c42dbfe9f2a6eb5fbce5f6a31b532ff2a3f243 /src/port/ui/ContentBrowser.cpp | |
| parent | 760fa3bf5226c2a6052a5d4d53fd0e27f2911e60 (diff) | |
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>
Diffstat (limited to 'src/port/ui/ContentBrowser.cpp')
| -rw-r--r-- | src/port/ui/ContentBrowser.cpp | 324 |
1 files changed, 283 insertions, 41 deletions
diff --git a/src/port/ui/ContentBrowser.cpp b/src/port/ui/ContentBrowser.cpp index 2e09db01a..e85f06d41 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 <imgui.h> #include <map> @@ -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<std::string, std::function<void(const FVector&)>> 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<std::string, std::function<AActor*(const FVector&)>> 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<std::string, std::function<OObject*(const FVector&)>> 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>(); + 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(); + } } |
