#include #include #include "Game.h" #include "port/Engine.h" #include #include #include "engine/World.h" #include "engine/AllTracks.h" #include "engine/tracks/PodiumCeremony.h" #include "engine/GarbageCollector.h" #include "engine/TrainCrossing.h" #include "engine/objects/BombKart.h" #include "engine/objects/Lakitu.h" #include "Smoke.h" #include "engine/HM_Intro.h" #include "engine/editor/Editor.h" #include "engine/editor/SceneManager.h" #include "engine/registry/RegisterContent.h" #include "engine/cameras/GameCamera.h" #include "engine/cameras/FreeCamera.h" #include "engine/cameras/TourCamera.h" #include "engine/cameras/LookBehindCamera.h" #include "engine/TrackBrowser.h" #include "engine/RandomItemTable.h" #include "engine/sky/Sky.h" #ifdef _WIN32 #include #endif extern "C" { #include "main.h" #include "audio/load.h" #include "audio/external.h" #include "render_courses.h" #include "menus.h" #include "update_objects.h" #include "spawn_players.h" #include "src/enhancements/collision_viewer.h" #include "code_800029B0.h" #include "code_80057C60.h" // #include "engine/wasm.h" } extern "C" void Graphics_PushFrame(Gfx* pool) { GameEngine::ProcessGfxCommands(pool); } // Create the world instance static World sWorldInstance; // Deferred cleaning when clearing all actors in the editor bool bCleanWorld = false; std::unique_ptr gMushroomCup; std::unique_ptr gFlowerCup; std::unique_ptr gStarCup; std::unique_ptr gSpecialCup; std::unique_ptr gBattleCup; HarbourMastersIntro gMenuIntro; TrackEditor::Editor gEditor; s32 gTrophyIndex = NULL_OBJECT_ID; /** Spawner Registries **/ Registry gTrackRegistry; Registry gActorRegistry; Registry gItemRegistry; /** Data Registries **/ DataRegistry gItemTableRegistry; std::unique_ptr gTrackBrowser; std::unique_ptr gSky; World* GetWorld() { return World::Instance; } void CustomEngineInit() { // Close the editor because lus remembers if it was open // This also turns off freecam gEditor.Disable(); gSky = std::make_unique(); RegisterTracks(gTrackRegistry); gTrackBrowser = std::make_unique(gTrackRegistry); TrackBrowser::Instance->FindCustomTracks(); TrackBrowser::Instance->Refresh(gTrackRegistry); gMushroomCup = std::make_unique("mk:mushroom_cup", "Mushroom Cup", std::vector{ "mk:luigi_raceway", "mk:moo_moo_farm", "mk:koopa_troopa_beach", "mk:kalimari_desert" }); gFlowerCup = std::make_unique("mk:flower_cup", "Flower Cup", std::vector{ "mk:toads_turnpike", "mk:frappe_snowland", "mk:choco_mountain", "mk:mario_raceway" }); gStarCup = std::make_unique("mk:star_cup", "Star Cup", std::vector{ "mk:wario_stadium", "mk:sherbet_land", "mk:royal_raceway", "mk:bowsers_castle" }); gSpecialCup = std::make_unique("mk:special_cup", "Special Cup", std::vector{ "mk:dk_jungle", "mk:yoshi_valley", "mk:banshee_boardwalk", "mk:rainbow_road" }); gBattleCup = std::make_unique("mk:battle_cup", "Battle Cup", std::vector{ "mk:big_donut", "mk:block_fort", "mk:double_deck", "mk:skyscraper" }); /* Validate Cup Track IDs */ gMushroomCup->ValidateTrackIds(gTrackRegistry); gFlowerCup->ValidateTrackIds(gTrackRegistry); gStarCup->ValidateTrackIds(gTrackRegistry); gSpecialCup->ValidateTrackIds(gTrackRegistry); gBattleCup->ValidateTrackIds(gTrackRegistry); /* Instantiate Cups */ GetWorld()->AddCup(gMushroomCup.get()); GetWorld()->AddCup(gFlowerCup.get()); GetWorld()->AddCup(gStarCup.get()); GetWorld()->AddCup(gSpecialCup.get()); GetWorld()->AddCup(gBattleCup.get()); SetMarioRaceway(); printf("[Game] Registering Game Content...\n"); RegisterActors(gActorRegistry); RegisterItems(gItemRegistry); RegisterItemTables(gItemTableRegistry); printf("[Game] Game Content Registered!\n"); } void CustomEngineDestroy() { gTrackRegistry.Clear(); gActorRegistry.Clear(); gMushroomCup.reset(); gFlowerCup.reset(); gStarCup.reset(); gSpecialCup.reset(); gBattleCup.reset(); } extern "C" { void HM_InitIntro() { gMenuIntro.HM_InitIntro(); } void HM_TickIntro() { gMenuIntro.HM_TickIntro(); } void HM_DrawIntro() { gMenuIntro.HM_DrawIntro(); } // Set default track; mario raceway void SetMarioRaceway(void) { SelectMarioRaceway(); GetWorld()->SetCurrentCup(gMushroomCup.get()); GetWorld()->GetCurrentCup()->CursorPosition = 3; GetWorld()->CupIndex = 0; } u32 WorldNextCup(void) { return GetWorld()->NextCup(); } u32 WorldPreviousCup(void) { return GetWorld()->PreviousCup(); } void CM_SetCup(void* cup) { GetWorld()->SetCurrentCup((Cup*) cup); } void* GetCup() { return GetWorld()->GetCurrentCup(); } u32 GetCupIndex(void) { return GetWorld()->GetCupIndex(); } void CM_SetCupIndex(size_t index) { GetWorld()->SetCupIndex(index); } const char* GetCupName(void) { return GetWorld()->GetCurrentCup()->Name; } void LoadTrack() { GetWorld()->GetRaceManager().Load(); } void CM_VehicleCollision(s32 playerId, Player* player) { for (auto& actor : GetWorld()->Actors) { if (actor) { actor->VehicleCollision(playerId, player); } } } void CM_BombKartsWaypoint(s32 cameraId) { for (auto& object : GetWorld()->Objects) { if (auto* kart = dynamic_cast(object.get())) { if (kart != nullptr) { kart->Waypoint(cameraId); } } } } void CM_DisplayBattleBombKart(s32 playerId, s32 primAlpha) { if ((playerId < 0) || (playerId > 4)) { return; } if (primAlpha == 0) { GetWorld()->mPlayerBombKart[playerId].state = PlayerBombKart::PlayerBombKartState::DISABLED; GetWorld()->mPlayerBombKart[playerId]._primAlpha = primAlpha; } else { GetWorld()->mPlayerBombKart[playerId].state = PlayerBombKart::PlayerBombKartState::ACTIVE; GetWorld()->mPlayerBombKart[playerId]._primAlpha = primAlpha; } } void CM_DrawBattleBombKarts(s32 cameraId) { for (size_t i = 0; i < gPlayerCount; i++) { GetWorld()->mPlayerBombKart[i].Draw(i, cameraId); } } void CM_ClearVehicles(void) { GetWorld()->Crossings.clear(); } void CM_CrossingTrigger() { for (auto& crossing : GetWorld()->Crossings) { if (crossing) { crossing->CrossingTrigger(); } } } void CM_AICrossingBehaviour(s32 playerId) { for (auto& crossing : GetWorld()->Crossings) { if (crossing) { crossing->AICrossingBehaviour(playerId); } } } s32 CM_GetCrossingOnTriggered(uintptr_t* crossing) { TrainCrossing* ptr = (TrainCrossing*) crossing; if (ptr) { return ptr->OnTriggered; } } /** * Tracks are rendered in two ways * 1) Track sections --> The scene is split into multiple sections and rendered piece by piece * 2) Full scene --> The entire scene is rendered at once * * Custom tracks only use the Render() method, and they only render the full scene. * They do not use DrawCredits() and they do not use track sections. */ void CM_DrawTrack(ScreenContext* screen) { if (nullptr == GetWorld()->GetTrack()) { return; } // Check if collision mesh rendering is enabled via CVar if (CVarGetInteger("gRenderCollisionMesh", 0)) { render_collision(); return; } // Custom tracks should never use DrawCredits(); if (GetWorld()->GetTrack()->IsMod()) { switch(screen->camera->renderMode) { default: GetWorld()->GetTrack()->Draw(screen); break; case RENDER_COLLISION_MESH: render_collision(); break; } } else { switch(screen->camera->renderMode) { case RENDER_FULL_SCENE: if (gModeSelection == BATTLE) { GetWorld()->GetTrack()->Draw(screen); } else { GetWorld()->GetTrack()->DrawCredits(); } case RENDER_TRACK_SECTIONS: GetWorld()->GetTrack()->Draw(screen); break; case RENDER_COLLISION_MESH: render_collision(); break; } } } void CM_TickActors() { if (GetWorld()->GetTrack()) { GetWorld()->TickActors(); } } void CM_DrawActors(Camera* camera) { //AActor* a = GetWorld()->ConvertActorToAActor(actor); for (const auto& actor : GetWorld()->Actors) { if (actor->IsMod()) { actor->Draw(camera); } } for (auto& camera : GetWorld()->Cameras) { if (auto* tourCam = dynamic_cast(camera.get())) { if (tourCam->IsActive()) { tourCam->Draw(); } } } } void CM_DrawStaticMeshActors() { GetWorld()->DrawStaticMeshActors(); } void CM_BeginPlay() { static bool tour = false; auto track = GetWorld()->GetTrack(); GetWorld()->Actors.clear(); if (nullptr == track) { return; } if (tour) { // GetWorld()->Cameras[2]->SetActive(true); // gScreenOneCtx->camera = GetWorld()->Cameras[2]->Get(); if (reinterpret_cast(GetWorld()->Cameras[2].get())->IsTourComplete()) { tour = false; gScreenOneCtx->pendingCamera = &cameras[0]; } } GetWorld()->GetRaceManager().PreInit(); GetWorld()->GetRaceManager().BeginPlay(); GetWorld()->GetRaceManager().PostInit(); } Camera* CM_GetPlayerCamera(s32 playerIndex) { for (auto& cam : GetWorld()->Cameras) { // Make sure this is a player camera and not a different type of camera if (typeid(*cam) == typeid(GameCamera)) { Camera* camera = cam->Get(); if (camera->playerId == playerIndex) { return camera; } } } return nullptr; } void CM_SetViewProjection(Camera* camera) { for (auto& gameCamera : GetWorld()->Cameras) { if (camera == gameCamera->Get()) { gameCamera->SetViewProjection(); } } } void CM_TickCameras() { GetWorld()->TickCameras(); } Camera* CM_AddCamera(Vec3f spawn, s16 rot, u32 mode) { if (GetWorld()->Cameras.size() >= NUM_CAMERAS) { printf("Reached the max number of cameras, %d\n", NUM_CAMERAS); return nullptr; } GetWorld()->Cameras.push_back(std::make_unique(FVector(spawn[0], spawn[1], spawn[2]), rot, mode)); return GetWorld()->Cameras.back()->Get(); } Camera* CM_AddFreeCamera(Vec3f spawn, s16 rot, u32 mode) { if (GetWorld()->Cameras.size() >= NUM_CAMERAS) { printf("Reached the max number of cameras, %d\n", NUM_CAMERAS); return nullptr; } GetWorld()->Cameras.push_back(std::make_unique(FVector(spawn[0], spawn[1], spawn[2]), rot, mode)); return GetWorld()->Cameras.back()->Get(); } Camera* CM_AddTourCamera(Vec3f spawn, s16 rot, u32 mode) { if (GetWorld()->Cameras.size() >= NUM_CAMERAS) { // This is to prevent soft locking the game printf("Reached the max number of cameras, %d\n", NUM_CAMERAS); if (GetWorld()->GetTrack()->bTourEnabled) { spawn_and_set_player_spawns(); } return nullptr; } if (nullptr == GetWorld()->GetTrack()) { // This is to prevent soft locking the game if (GetWorld()->GetTrack()->bTourEnabled) { spawn_and_set_player_spawns(); } return nullptr; } if (GetWorld()->GetTrack()->TourShots.size() == 0) { // This is to prevent soft locking the game if (GetWorld()->GetTrack()->bTourEnabled) { spawn_and_set_player_spawns(); } return nullptr; } GetWorld()->Cameras.push_back(std::make_unique(FVector(spawn[0], spawn[1], spawn[2]), rot, mode)); TourCamera* tour = static_cast(GetWorld()->Cameras.back().get()); tour->SetActive(true); return tour->Get(); } bool CM_IsTourEnabled() { if (nullptr != GetWorld()->GetTrack()) { if ((GetWorld()->GetTrack()->bTourEnabled) && (gTourComplete == false)) { return true; } else { return false; } } else { return false; } } Camera* CM_AddLookBehindCamera(Vec3f spawn, s16 rot, u32 mode) { if (GetWorld()->Cameras.size() >= NUM_CAMERAS) { printf("Reached the max number of cameras, %d\n", NUM_CAMERAS); return nullptr; } GetWorld()->Cameras.push_back(std::make_unique(FVector(spawn[0], spawn[1], spawn[2]), rot, mode)); return GetWorld()->Cameras.back()->Get(); } void CM_AttachCamera(Camera* camera, s32 playerIdx) { camera->playerId = playerIdx; } void CM_CameraSetActive(size_t idx, bool state) { if (idx < GetWorld()->Cameras.size()) { GetWorld()->Cameras[idx]->SetActive(state); } } void CM_SetFreeCamera(bool state) { for (auto& cam : GetWorld()->Cameras) { if (cam->Get() == gScreenOneCtx->freeCamera) { if (state) { gScreenOneCtx->pendingCamera = gScreenOneCtx->freeCamera; cam->SetActive(true); } else { if (nullptr != gScreenOneCtx->raceCamera) { if (gGamestate == RACING) { gScreenOneCtx->pendingCamera = gScreenOneCtx->raceCamera; cam->SetActive(false); } else { cam->SetActive(false); } } } } } } void CM_ActivateTourCamera(Camera* camera) { for (auto& cam : GetWorld()->Cameras) { if (cam->Get() == camera) { cam->SetActive(true); } } } void CM_TickObjects() { if (GetWorld()->GetTrack()) { GetWorld()->TickObjects(); } } // A couple objects such as lakitu are ticked inside of process_game_tick which support 60fps. // This is a fallback to support that. void CM_TickObjects60fps() { if (GetWorld()->GetTrack()) { GetWorld()->TickObjects60fps(); } } void CM_DrawObjects(s32 cameraId) { if (GetWorld()->GetTrack()) { GetWorld()->DrawObjects(cameraId); } } void CM_TickEditor() { gEditor.Tick(); } void CM_DrawEditor() { gEditor.Draw(); } void CM_TickParticles() { if (GetWorld()->GetTrack()) { GetWorld()->TickParticles(); } } void CM_DrawParticles(s32 cameraId) { if (GetWorld()->GetTrack()) { GetWorld()->DrawParticles(cameraId); } } void CM_RaceDrawSky(ScreenContext* screen, s32 someId) { // if (bDrawSkybox) { if (CVarGetInteger("gDrawSky", true) == true) { Sky::Instance->Draw(screen); if (gGamestate != CREDITS_SEQUENCE) { func_80057FC4(screen, someId); // DrawSkyActors } Sky::Instance->DrawFloor(screen); } } void CM_Waypoints(Player* player, int8_t playerId) { if (GetWorld()->GetTrack()) { GetWorld()->GetTrack()->Waypoints(player, playerId); } } void CM_SomeCollisionThing(Player* player, Vec3f arg1, Vec3f arg2, Vec3f arg3, f32* arg4, f32* arg5, f32* arg6, f32* arg7) { if (GetWorld()->GetTrack()) { GetWorld()->GetTrack()->SomeCollisionThing(player, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } } void CM_InitTrackObjects() { if (GetWorld()->GetTrack()) { GetWorld()->GetTrack()->InitTrackObjects(); } } void CM_TickTrackObjects() { if (GetWorld()->GetTrack()) { GetWorld()->GetTrack()->TickTrackObjects(); } TrainSmokeTick(); } void CM_DrawTrackObjects(s32 cameraId) { if (GetWorld()->GetTrack()) { GetWorld()->GetTrack()->DrawTrackObjects(cameraId); } TrainSmokeDraw(cameraId); } void CM_SomeSounds() { if (GetWorld()->GetTrack()) { GetWorld()->GetTrack()->SomeSounds(); } } void CM_CreditsSpawnActors() { if (GetWorld()->GetTrack()) { GetWorld()->GetTrack()->CreditsSpawnActors(); } } void CM_WhatDoesThisDo(Player* player, int8_t playerId) { if (GetWorld()->GetTrack()) { GetWorld()->GetTrack()->WhatDoesThisDo(player, playerId); } } void CM_WhatDoesThisDoAI(Player* player, int8_t playerId) { if (GetWorld()->GetTrack()) { GetWorld()->GetTrack()->WhatDoesThisDoAI(player, playerId); } } void CM_SetStaffGhost() { if (GetWorld()->GetTrack()) { GetWorld()->GetTrack()->SetStaffGhost(); } } // This should only be used for checking if the track has changed uintptr_t CM_GetTrack() { return (uintptr_t) (void*) GetWorld()->GetTrack(); } Properties* CM_GetProps() { if (GetWorld()->GetTrack()) { return &GetWorld()->GetTrack()->Props; } return NULL; } void CM_TickTrack() { if (GetWorld()->GetTrack()) { GetWorld()->GetTrack()->Tick(); } } void CM_DrawTransparency(ScreenContext* screen, uint16_t pathCounter, uint16_t cameraRot, uint16_t playerDirection) { if (GetWorld()->GetTrack()) { GetWorld()->GetTrack()->DrawTransparency(screen, pathCounter, cameraRot, playerDirection); } } /** * This should only be ran once per track, otherwise animation/timings might become sped up. */ void CM_SpawnStarterLakitu() { if ((gDemoMode) || (gGamestate == CREDITS_SEQUENCE)) { return; } for (size_t i = 0; i < gPlayerCountSelection1; i++) { // Retry does not respawn actors, therefore, re-use lakitu. if (auto it = GetWorld()->Lakitus.find(i); it != GetWorld()->Lakitus.end()) { if (it->second) { it->second->Activate(OLakitu::STARTER); } continue; // Already exists, skip spawning } auto lakitu = std::make_unique(i, OLakitu::LakituType::STARTER); GetWorld()->Lakitus[i] = lakitu.get(); GetWorld()->AddObject(std::move(lakitu)); } } // Checkered flag lakitu void CM_ActivateFinishLakitu(s32 playerId) { if ((gDemoMode) || (gGamestate == CREDITS_SEQUENCE)) { return; } GetWorld()->Lakitus[playerId]->Activate(OLakitu::LakituType::FINISH); } void CM_ActivateSecondLapLakitu(s32 playerId) { if ((gDemoMode) || (gGamestate == CREDITS_SEQUENCE)) { return; } GetWorld()->Lakitus[playerId]->Activate(OLakitu::LakituType::SECOND_LAP); } void CM_ActivateFinalLapLakitu(s32 playerId) { if ((gDemoMode) || (gGamestate == CREDITS_SEQUENCE)) { return; } GetWorld()->Lakitus[playerId]->Activate(OLakitu::LakituType::FINAL_LAP); } void CM_ActivateReverseLakitu(s32 playerId) { if ((gDemoMode) || (gGamestate == CREDITS_SEQUENCE)) { return; } GetWorld()->Lakitus[playerId]->Activate(OLakitu::LakituType::REVERSE); } size_t GetCupCursorPosition() { return GetWorld()->GetCurrentCup()->CursorPosition; } void SetCupCursorPosition(size_t position) { GetWorld()->GetCurrentCup()->SetTrack(position); // GetWorld()->CurrentCup->CursorPosition = position; } size_t GetCupSize() { return GetWorld()->GetCurrentCup()->GetSize(); } void* GetTrack(void) { return GetWorld()->GetTrack(); } struct Actor* CM_GetActor(size_t index) { if (index >= 0 && index < GetWorld()->Actors.size()) { AActor* actor = GetWorld()->Actors[index].get(); return reinterpret_cast(reinterpret_cast(actor) + sizeof(void*)); } else { throw std::runtime_error("GetActor() index out of bounds"); return NULL; } } size_t CM_FindActorIndex(Actor* actor) { // Move the ptr back to look at the vtable. // This gets us the proper C++ class instead of just the variables used in C. AActor* a = reinterpret_cast(reinterpret_cast(actor) - sizeof(void*)); auto& actors = GetWorld()->Actors; auto it = std::find_if(actors.begin(), actors.end(), [a](const std::unique_ptr& ptr) { return ptr.get() == a; }); if (it != actors.end()) { return std::distance(actors.begin(), it); } printf("FindActorIndex() actor not found\n"); return -1; } void CM_DeleteActor(size_t index) { auto& actors = GetWorld()->Actors; if (index < actors.size()) { actors.erase(actors.begin() + index); } } /** * Clean up actors and other game objects. */ void CM_CleanWorld(void) { GetWorld()->CleanWorld(); } void CM_CleanCameras(void) { GetWorld()->Cameras.clear(); } struct Actor* CM_AddBaseActor() { return (struct Actor*) GetWorld()->AddBaseActor(); } void CM_ActorBeginPlay(struct Actor* actor) { GetWorld()->ActorBeginPlay(actor); } void CM_ActorGenerateCollision(struct Actor* actor) { AActor* act = GetWorld()->ConvertActorToAActor(actor); if ((nullptr != act->Model) && (act->Model[0] != '\0')) { if (act->Triangles.size() == 0) { TrackEditor::GenerateCollisionMesh(act, (Gfx*)LOAD_ASSET_RAW(act->Model), 1.0f); } } } void Editor_AddLight(s8* direction) { static size_t i = 0; gEditor.AddLight(("Light "+std::to_string(i)).c_str(), nullptr, direction); i += 1; } void Editor_ClearMatrix() { gEditor.ClearMatrixPool(); } void Editor_CleanWorld() { if (bCleanWorld) { CM_CleanWorld(); bCleanWorld = false; } } size_t CM_GetActorSize() { return GetWorld()->Actors.size(); } void CM_ActorCollision(Player* player, Actor* actor) { AActor* a = GetWorld()->ConvertActorToAActor(actor); if (a->IsMod()) { a->Collision(player, a); } } f32 CM_GetWaterLevel(Vec3f pos, Collision* collision) { FVector fPos = {pos[0], pos[1], pos[2]}; return GetWorld()->GetTrack()->GetWaterLevel(fPos, collision); } // clang-format off bool IsMarioRaceway() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsLuigiRaceway() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsChocoMountain() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsBowsersCastle() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsBansheeBoardwalk() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsYoshiValley() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsFrappeSnowland() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsKoopaTroopaBeach() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsRoyalRaceway() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsMooMooFarm() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsToadsTurnpike() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsKalimariDesert() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsSherbetLand() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsRainbowRoad() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsWarioStadium() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsBlockFort() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsSkyscraper() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsDoubleDeck() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsDkJungle() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsBigDonut() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } bool IsPodiumCeremony() { return dynamic_cast(GetWorld()->GetTrack()) != nullptr; } void SelectMarioRaceway() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectLuigiRaceway() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectChocoMountain() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectBowsersCastle() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectBansheeBoardwalk() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectYoshiValley() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectFrappeSnowland() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectKoopaTroopaBeach() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectRoyalRaceway() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectMooMooFarm() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectToadsTurnpike() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectKalimariDesert() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectSherbetLand() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectRainbowRoad() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectWarioStadium() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectBlockFort() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectSkyscraper() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectDoubleDeck() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectDkJungle() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectBigDonut() { GetWorld()->SetCurrentTrack(std::make_unique()); } void SelectPodiumCeremony() { GetWorld()->SetCurrentTrack(std::make_unique()); } // clang-format on void* GetMushroomCup(void) { return gMushroomCup.get(); } void* GetFlowerCup(void) { return gFlowerCup.get(); } void* GetStarCup(void) { return gStarCup.get(); } void* GetSpecialCup(void) { return gSpecialCup.get(); } void* GetBattleCup(void) { return gBattleCup.get(); } // End of frame cleanup of actors, objects, etc. void CM_RunGarbageCollector(void) { RunGarbageCollector(); } void CM_ResetAudio(void) { if(HMAS_IsPlaying(HMAS_MUSIC)){ HMAS_AddEffect(HMAS_MUSIC, HMAS_EFFECT_VOLUME, HMAS_LINEAR, 10, 0); HMAS_AddEffect(HMAS_MUSIC, HMAS_EFFECT_STOP, HMAS_INSTANT, 1, 0); } // Fade out music for all sequences and music player indexes 0, and 1 for (size_t soundId = 0; soundId < MUSIC_SEQ_MAX; soundId++) { func_800C3448(0x10100000 | soundId); func_800C3448(0x11100000 | soundId); } } } void push_frame() { GameEngine::StartAudioFrame(); GameEngine::Instance->StartFrame(); thread5_iteration(); GameEngine::EndAudioFrame(); // thread5_game_loop(); // Graphics_ThreadUpdate();w } void CM_ThrowRuntimeError(const char* fmt, ...) { char error_mesg[2048]; va_list args; va_start(args, fmt); vsnprintf(error_mesg, sizeof(error_mesg), fmt, args); va_end(args); const char* crash_desc = "\nSpaghettiKart has crashed! Please upload the logs to the support channel in Discord."; strncat(error_mesg, crash_desc, sizeof(error_mesg) - strlen(error_mesg) - 1); SPDLOG_ERROR(error_mesg); SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "You dropped your plate of Spaghetti!", error_mesg, NULL ); exit(EXIT_FAILURE); } #ifdef _WIN32 int SDL_main(int argc, char** argv) { #else #if defined(__cplusplus) && defined(PLATFORM_IOS) extern "C" #endif int main(int argc, char* argv[]) { #endif #ifdef _WIN32 // Allow non-ascii characters for Windows setlocale(LC_ALL, ".UTF8"); #endif // load_wasm(); GameEngine::Create(); audio_init(); sound_init(); CustomEngineInit(); switch(CVarGetInteger("gSkipIntro", 0)) { case 0: gMenuSelection = HARBOUR_MASTERS_MENU; break; case 1: gMenuSelection = LOGO_INTRO_MENU; break; case 2: gMenuSelection = START_MENU; break; case 3: gMenuSelection = MAIN_MENU; break; } // Debug mode override gSkipIntro if (CVarGetInteger("gEnableDebugMode", 0) == true) { gMenuSelection = START_MENU; } thread5_game_loop(); gEditor.Load(); while (WindowIsRunning()) { push_frame(); } CustomEngineDestroy(); // GameEngine::Instance->ProcessFrame(push_frame); GameEngine::Instance->Destroy(); return 0; }