diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 84e7f80e9..35949b4c6 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -34,7 +34,7 @@ jobs: sudo make install - name: Build run: | - cmake -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE=Debug + cmake -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE=Release cmake --build build-cmake -j - name: Create Package run: | diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e541c70c2..7910e61da 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -179,8 +179,8 @@ jobs: path: ./build-cmake - name: Build run: | - cmake -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE=Debug - cmake --build build-cmake --config Debug -j3 + cmake -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE=Release + cmake --build build-cmake --config Release -j3 (cd build-cmake && cpack -G External) wget -O gamecontrollerdb.txt https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt mv README.md readme.txt diff --git a/.gitignore b/.gitignore index c5f2109b0..a355234f3 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,7 @@ build/* *.sav *.o2r *.map +default.sav.bak .assets-local.txt build/ @@ -96,3 +97,6 @@ modding/ node_modules /src/port/build.c /properties.h +!/src/port/mods/ +mods/ +saves/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 501b71bcb..d2f571328 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,9 +137,9 @@ set(CMAKE_CXX_FLAGS_DEBUG "-g -ffast-math -DDEBUG") set(CMAKE_C_FLAGS_RELEASE "-O3 -ffast-math -DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -ffast-math -DNDEBUG") else() -set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG") -set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG") -set(CMAKE_OBJCXX_FLAGS_RELEASE "-O2 -DNDEBUG") +set(CMAKE_C_FLAGS_RELEASE "-O1 -DNDEBUG") +set(CMAKE_CXX_FLAGS_RELEASE "-O1 -DNDEBUG") +set(CMAKE_OBJCXX_FLAGS_RELEASE "-O1 -DNDEBUG") endif() # Set game compilation version @@ -153,6 +153,7 @@ add_compile_definitions( ENABLE_RUMBLE=1 F3D_OLD=1 F3D_GBI=1 + USE_GBI_TRACE=1 GBI_FLOATS=1 _LANGUAGE_C _USE_MATH_DEFINES diff --git a/README.md b/README.md index b4281f22b..4c4eb0f62 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ If you want to playtest a continuous integration build, you can find them at the # Special Thanks: +* [Kenix3](https://github.com/Kenix3) : for helping with the Engine development that were brought into other ports. * [HM64 Team](https://github.com/harbourMasters) : for creating Libultraship and helping with various issues. * [garrettjoecox](https://github.com/garrettjoecox) : for contributing with fixes to this port. * [inspectredc](https://github.com/inspectredc) : for contributing to the extraction of the game's assets. diff --git a/include/macro_presets.h b/include/macro_presets.h index 38b1eb69b..9d390b5e4 100644 --- a/include/macro_presets.h +++ b/include/macro_presets.h @@ -11,7 +11,7 @@ struct MacroPreset { /*0x06*/ s16 param; }; -struct MacroPreset MacroObjectPresets[] = { +extern struct MacroPreset MacroObjectPresets[] = { {bhvYellowCoin, MODEL_YELLOW_COIN, 0}, {bhvOneCoin, MODEL_YELLOW_COIN, 0}, {bhvMovingBlueCoin, MODEL_BLUE_COIN, 0}, diff --git a/src/engine/behavior_script.c b/src/engine/behavior_script.c index c7093b9ea..8576f05ed 100644 --- a/src/engine/behavior_script.c +++ b/src/engine/behavior_script.c @@ -1,4 +1,5 @@ #include +#include "port/hooks/list/PlayerEvent.h" #include "sm64.h" #include "behavior_data.h" @@ -984,20 +985,22 @@ void cur_obj_update(void) { } // Handle visibility of object - if (gCurrentObject->oRoom != -1) { - // If the object is in a room, only show it when Mario is in the room. - cur_obj_enable_rendering_if_mario_in_room(); - } else if ((objFlags & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) && gCurrentObject->collisionData == NULL) { - if (!(objFlags & OBJ_FLAG_ACTIVE_FROM_AFAR)) { - // If the object has a render distance, check if it should be shown. - if (distanceFromMario > gCurrentObject->oDrawingDistance) { - // Out of render distance, hide the object. - gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; - gCurrentObject->activeFlags |= ACTIVE_FLAG_FAR_AWAY; - } else if (gCurrentObject->oHeldState == HELD_FREE) { - // In render distance (and not being held), show the object. - gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; - gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY; + CALL_CANCELLABLE_EVENT(ModifyObjectVisibility, &gCurrentObject) { + if (gCurrentObject->oRoom != -1) { + // If the object is in a room, only show it when Mario is in the room. + cur_obj_enable_rendering_if_mario_in_room(); + } else if ((objFlags & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) && gCurrentObject->collisionData == NULL) { + if (!(objFlags & OBJ_FLAG_ACTIVE_FROM_AFAR)) { + // If the object has a render distance, check if it should be shown. + if (distanceFromMario > gCurrentObject->oDrawingDistance) { + // Out of render distance, hide the object. + gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; + gCurrentObject->activeFlags |= ACTIVE_FLAG_FAR_AWAY; + } else if (gCurrentObject->oHeldState == HELD_FREE) { + // In render distance (and not being held), show the object. + gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; + gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY; + } } } } diff --git a/src/engine/level_script.c b/src/engine/level_script.c index 378ae28d2..c58fe42a4 100644 --- a/src/engine/level_script.c +++ b/src/engine/level_script.c @@ -444,9 +444,7 @@ static void level_cmd_place_object(void) { if (sCurrAreaIndex != -1 && ((CMD_GET(u8, 2) & val7) || CMD_GET(u8, 2) == 0x1F)) { model = CMD_GET(u8, 3); - if (model == MODEL_STAR) { - int hi = 122; - } + spawnInfo = alloc_only_pool_alloc(sLevelPool, sizeof(struct SpawnInfo)); spawnInfo->startPos[0] = CMD_GET(s16, 4); diff --git a/src/game/behaviors/hidden_star.inc.c b/src/game/behaviors/hidden_star.inc.c index 759e36007..b9dd74ebd 100644 --- a/src/game/behaviors/hidden_star.inc.c +++ b/src/game/behaviors/hidden_star.inc.c @@ -55,7 +55,9 @@ void bhv_hidden_star_trigger_loop(void) { } void bhv_bowser_course_red_coin_star_loop(void) { - gRedCoinsCollected = o->oHiddenStarTriggerCounter; + CALL_CANCELLABLE_EVENT(ModifyRedCoinCount, &gRedCoinsCollected) { + gRedCoinsCollected = o->oHiddenStarTriggerCounter; + } switch (o->oAction) { case 0: diff --git a/src/game/behaviors/spawn_star.inc.c b/src/game/behaviors/spawn_star.inc.c index f2e0092ac..7e37ec0b5 100644 --- a/src/game/behaviors/spawn_star.inc.c +++ b/src/game/behaviors/spawn_star.inc.c @@ -30,9 +30,11 @@ void bhv_collect_star_init(void) { void bhv_collect_star_loop(void) { o->oFaceAngleYaw += 0x800; - if (o->oInteractStatus & INT_STATUS_INTERACTED) { - mark_obj_for_deletion(o); - o->oInteractStatus = 0; + CALL_CANCELLABLE_EVENT(ModifyObjectBehavior, o, MODEL_STAR) { + if (o->oInteractStatus & INT_STATUS_INTERACTED) { + mark_obj_for_deletion(o); + o->oInteractStatus = 0; + } } } @@ -167,7 +169,9 @@ void bhv_hidden_red_coin_star_init(void) { } void bhv_hidden_red_coin_star_loop(void) { - gRedCoinsCollected = o->oHiddenStarTriggerCounter; + CALL_CANCELLABLE_EVENT(ModifyRedCoinCount, &gRedCoinsCollected) { + gRedCoinsCollected = o->oHiddenStarTriggerCounter; + } switch (o->oAction) { case 0: @@ -184,5 +188,4 @@ void bhv_hidden_red_coin_star_loop(void) { } break; } - //CALL_CANCELLABLE_EVENT(ModifyObjectBehavior, o, MODEL_STAR); } diff --git a/src/game/level_update.c b/src/game/level_update.c index a0dd919a8..6b1c8a701 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -31,6 +31,7 @@ #include "port/ui/cvar_prefixes.h" #include "port/hooks/list/EngineEvent.h" +#include "port/hooks/list/PlayerEvent.h" #include "port/mods/PortEnhancements.h" #define PLAY_MODE_NORMAL 0 @@ -662,6 +663,7 @@ void initiate_painting_warp(void) { if (gMarioState->action & ACT_FLAG_INTANGIBLE) { play_painting_eject_sound(); } else if (pWarpNode->id != 0) { + CALL_EVENT(ChangeLevel, 0, pWarpNode, 0); warpNode = *pWarpNode; if (!(warpNode.destLevel & 0x80)) { @@ -869,11 +871,13 @@ void initiate_delayed_warp(void) { default: warpNode = area_get_warp_node(sSourceWarpNodeId); - - initiate_warp(warpNode->node.destLevel & 0x7F, warpNode->node.destArea, - warpNode->node.destNode, sDelayedWarpArg); + + CALL_EVENT(ChangeLevel, sSourceWarpNodeId, &warpNode->node, &sDelayedWarpArg); + initiate_warp(warpNode->node.destLevel & 0x7F, warpNode->node.destArea, warpNode->node.destNode, + sDelayedWarpArg); check_if_should_set_warp_checkpoint(&warpNode->node); + if (sWarpDest.type != WARP_TYPE_CHANGE_LEVEL) { level_set_transition(2, NULL); } diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index d1289dfca..02407f890 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -1,4 +1,5 @@ #include +#include "port/hooks/list/PlayerEvent.h" #include "sm64.h" #include "area.h" @@ -2317,7 +2318,7 @@ void cur_obj_call_action_function(void (*actionFunctions[])(void)) { } static struct Object *spawn_star_with_no_lvl_exit(s32 sp20, s32 sp24) { - struct Object *sp1C = spawn_object(o, MODEL_STAR, bhvSpawnedStarNoLevelExit); + struct Object* sp1C = spawn_object(o, MODEL_STAR, bhvSpawnedStarNoLevelExit); sp1C->oSparkleSpawnUnk1B0 = sp24; sp1C->oBehParams = o->oBehParams; sp1C->oBehParams2ndByte = sp20; diff --git a/src/game/save_file.c b/src/game/save_file.c index 218c36697..21ba5e03f 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -11,6 +11,7 @@ #include "level_table.h" #include "course_table.h" #include "rumble_init.h" +#include "port/data/Saves.h" #include "port/hooks/list/PlayerEvent.h" #include "port/mods/PortEnhancements.h" @@ -18,7 +19,8 @@ #define MENU_DATA_MAGIC 0x4849 #define SAVE_FILE_MAGIC 0x4441 -STATIC_ASSERT(sizeof(struct SaveBuffer) == EEPROM_SIZE, "eeprom buffer size must match"); +// @port: Disabled because save files are gonna be bigger soon +// STATIC_ASSERT(sizeof(struct SaveBuffer) == EEPROM_SIZE, "eeprom buffer size must match"); extern struct SaveBuffer gSaveBuffer; @@ -151,6 +153,8 @@ static void add_save_block_signature(void *buffer, s32 size, u16 magic) { * Copy main menu data from one backup slot to the other slot. */ static void restore_main_menu_data(s32 srcSlot) { + CALL(RestoreMainMenuData, srcSlot); + s32 destSlot = srcSlot ^ 1; // Compute checksum on source data @@ -164,6 +168,8 @@ static void restore_main_menu_data(s32 srcSlot) { } static void save_main_menu_data(void) { + CALL(SaveMainMenuData); + if (gMainMenuDataModified) { // Compute checksum add_save_block_signature(&gSaveBuffer.menuData[0], sizeof(gSaveBuffer.menuData[0]), MENU_DATA_MAGIC); @@ -237,6 +243,8 @@ static void touch_high_score_ages(s32 fileIndex) { * Copy save file data from one backup slot to the other slot. */ static void restore_save_file_data(s32 fileIndex, s32 srcSlot) { + CALL(RestoreSaveFileData, fileIndex, srcSlot); + s32 destSlot = srcSlot ^ 1; // Compute checksum on source data @@ -253,6 +261,8 @@ static void restore_save_file_data(s32 fileIndex, s32 srcSlot) { } void save_file_do_save(s32 fileIndex) { + CALL(SaveFileDoSave, fileIndex); + CALL_EVENT(OnGameFileSave, fileIndex); if (gSaveFileModified) { // Compute checksum @@ -300,40 +310,45 @@ void save_file_load_all(void) { gSaveFileModified = FALSE; bzero(&gSaveBuffer, sizeof(gSaveBuffer)); - read_eeprom_data(&gSaveBuffer, sizeof(gSaveBuffer)); - - // Verify the main menu data and create a backup copy if only one of the slots is valid. - validSlots = verify_save_block_signature(&gSaveBuffer.menuData[0], sizeof(gSaveBuffer.menuData[0]), MENU_DATA_MAGIC); - validSlots |= verify_save_block_signature(&gSaveBuffer.menuData[1], sizeof(gSaveBuffer.menuData[1]),MENU_DATA_MAGIC) << 1; - switch (validSlots) { - case 0: // Neither copy is correct - wipe_main_menu_data(); - break; - case 1: // Slot 0 is correct and slot 1 is incorrect - restore_main_menu_data(0); - break; - case 2: // Slot 1 is correct and slot 0 is incorrect - restore_main_menu_data(1); - break; - } - for (file = 0; file < NUM_SAVE_FILES; file++) { - // Verify the save file and create a backup copy if only one of the slots is valid. - validSlots = verify_save_block_signature(&gSaveBuffer.files[file][0], sizeof(gSaveBuffer.files[file][0]), SAVE_FILE_MAGIC); - validSlots |= verify_save_block_signature(&gSaveBuffer.files[file][1], sizeof(gSaveBuffer.files[file][1]), SAVE_FILE_MAGIC) << 1; + if(ShouldLoadOldSaveFile()) { + read_eeprom_data(&gSaveBuffer, EEPROM_SIZE); + + // Verify the main menu data and create a backup copy if only one of the slots is valid. + validSlots = verify_save_block_signature(&gSaveBuffer.menuData[0], MENU_SAVE_DATA_SIZE, MENU_DATA_MAGIC); + validSlots |= verify_save_block_signature(&gSaveBuffer.menuData[1], MENU_SAVE_DATA_SIZE, MENU_DATA_MAGIC) << 1; switch (validSlots) { case 0: // Neither copy is correct - save_file_erase(file); + wipe_main_menu_data(); break; case 1: // Slot 0 is correct and slot 1 is incorrect - restore_save_file_data(file, 0); + restore_main_menu_data(0); break; case 2: // Slot 1 is correct and slot 0 is incorrect - restore_save_file_data(file, 1); + restore_main_menu_data(1); break; } + + for (file = 0; file < NUM_SAVE_FILES; file++) { + // Verify the save file and create a backup copy if only one of the slots is valid. + validSlots = verify_save_block_signature(&gSaveBuffer.files[file][0], SAVE_FILE_SIZE, SAVE_FILE_MAGIC); + validSlots |= verify_save_block_signature(&gSaveBuffer.files[file][1], SAVE_FILE_SIZE, SAVE_FILE_MAGIC) << 1; + switch (validSlots) { + case 0: // Neither copy is correct + save_file_erase(file); + break; + case 1: // Slot 0 is correct and slot 1 is incorrect + restore_save_file_data(file, 0); + break; + case 2: // Slot 1 is correct and slot 0 is incorrect + restore_save_file_data(file, 1); + break; + } + } } + CALL(SaveFileLoadAll); + stub_save_file_1(); } diff --git a/src/game/save_file.h b/src/game/save_file.h index 135fc0783..4c86d6efa 100644 --- a/src/game/save_file.h +++ b/src/game/save_file.h @@ -2,15 +2,16 @@ #define SAVE_FILE_H #include +#include "port/Rando/Types.h" #include "types.h" #include "area.h" #include "course_table.h" -#include "port/Rando/Types.h" - -#define EEPROM_SIZE 0x800 +#define EEPROM_SIZE 0x200 +#define SAVE_FILE_SIZE 0x38 +#define MENU_SAVE_DATA_SIZE 0x20 #define NUM_SAVE_FILES 4 struct SaveBlockSignature { @@ -20,7 +21,17 @@ struct SaveBlockSignature { struct RandoSaveCheck { RandoItemId randoItemId; + RandoAct randoAct; bool obtained; + bool skipped; +}; + +struct RandoSaveEntrance { + RandoEntranceId randoEntranceId; + int16_t destinationId; + RandoEntranceType randoEntranceType; + WarpNodes deathWarpId; + int16_t deathArea; }; typedef enum { @@ -29,8 +40,9 @@ typedef enum { } SaveType; struct RandoSaveData { - bool placeHolder; - // struct RandoSaveCheck randoSaveChecks[RC_MAX]; + struct RandoSaveCheck randoSaveChecks[RC_MAX]; + struct RandoSaveEntrance randoSaveEntrances[RE_MAX]; + u32 finalSeed; // u32 randoSaveOptions[RO_MAX]; }; @@ -57,6 +69,8 @@ struct SaveFile { u8 courseCoinScores[COURSE_STAGES_COUNT]; struct SaveBlockSignature signature; + + // @port: Custom data needs to go after this point struct ShipSaveData shipSaveData; }; @@ -82,9 +96,12 @@ struct MainMenuSaveData { #endif // Pad to match the EEPROM size of 0x200 (10 bytes on JP/US, 8 bytes on EU) - u8 filler[EEPROM_SIZE / 2 - SUBTRAHEND - NUM_SAVE_FILES * (4 + sizeof(struct SaveFile))]; + u8 filler[EEPROM_SIZE / 2 - SUBTRAHEND - NUM_SAVE_FILES * (4 + SAVE_FILE_SIZE)]; struct SaveBlockSignature signature; + + // @port: Custom data needs to go after this point + struct ShipSaveData shipSaveData; }; struct SaveBuffer { @@ -144,6 +161,10 @@ struct WarpCheckpoint { /*0x04*/ u8 warpNode; }; +#ifdef __cplusplus +extern "C" { +#endif + extern struct WarpCheckpoint gWarpCheckpoint; extern s8 gMainMenuDataModified; @@ -197,4 +218,8 @@ void eu_set_language(u16 language); u16 eu_get_language(void); #endif +#ifdef __cplusplus +} +#endif + #endif // SAVE_FILE_H diff --git a/src/menu/file_select.c b/src/menu/file_select.c index c796fe22f..142320e62 100644 --- a/src/menu/file_select.c +++ b/src/menu/file_select.c @@ -29,6 +29,7 @@ #include "port/hooks/list/PlayerEvent.h" #include "port/mods/PortEnhancements.h" +extern struct SaveBuffer gSaveBuffer; /** * @file file_select.c @@ -1766,8 +1767,13 @@ void print_save_file_star_count(s8 fileIndex, s16 x, s16 y) { int_to_str(starCount, starCountText); print_hud_lut_string(HUD_LUT_GLOBAL, x + offset + 16, y, starCountText); } else { - // Print "new" text - print_hud_lut_string(HUD_LUT_GLOBAL, x, y, GameEngine_LoadTranslation("TEXT_NEW")); + if (gSaveBuffer.files[fileIndex][0].shipSaveData.saveType == SAVETYPE_RANDO) { + // Print "rando" text + print_hud_lut_string(HUD_LUT_GLOBAL, x, y, textRand); + } else { + // Print "new" text + print_hud_lut_string(HUD_LUT_GLOBAL, x, y, GameEngine_LoadTranslation("TEXT_NEW")); + } } } @@ -1831,10 +1837,22 @@ void print_main_menu_strings(void) { // Print file names gSPDisplayList(gDisplayListHead++, dl_menu_ia8_text_begin); gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, sTextBaseAlpha); - print_menu_generic_string(MARIOTEXT_X1, 65, GameEngine_LoadTranslation("TEXT_FILE_MARIO_A")); - print_menu_generic_string(MARIOTEXT_X2, 65, GameEngine_LoadTranslation("TEXT_FILE_MARIO_B")); - print_menu_generic_string(MARIOTEXT_X1, 105, GameEngine_LoadTranslation("TEXT_FILE_MARIO_C")); - print_menu_generic_string(MARIOTEXT_X2, 105, GameEngine_LoadTranslation("TEXT_FILE_MARIO_D")); + print_menu_generic_string(MARIOTEXT_X1, 65, + gSaveBuffer.files[SAVE_FILE_A][0].shipSaveData.saveType == SAVETYPE_RANDO + ? textMarioRando + : GameEngine_LoadTranslation("TEXT_FILE_MARIO_A")); + print_menu_generic_string(MARIOTEXT_X2, 65, + gSaveBuffer.files[SAVE_FILE_B][0].shipSaveData.saveType == SAVETYPE_RANDO + ? textMarioRando + : GameEngine_LoadTranslation("TEXT_FILE_MARIO_B")); + print_menu_generic_string(MARIOTEXT_X1, 105, + gSaveBuffer.files[SAVE_FILE_C][0].shipSaveData.saveType == SAVETYPE_RANDO + ? textMarioRando + : GameEngine_LoadTranslation("TEXT_FILE_MARIO_C")); + print_menu_generic_string(MARIOTEXT_X2, 105, + gSaveBuffer.files[SAVE_FILE_D][0].shipSaveData.saveType == SAVETYPE_RANDO + ? textMarioRando + : GameEngine_LoadTranslation("TEXT_FILE_MARIO_D")); gSPDisplayList(gDisplayListHead++, dl_menu_ia8_text_end); } diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index 78d01bf0b..32c83051a 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -86,12 +86,15 @@ OTRVersion ReadPortVersionFromOTR(std::string otrPath) { if (t != nullptr && t->IsLoaded) { auto stream = std::make_shared(t->Buffer->data(), t->Buffer->size()); auto reader = std::make_shared(stream); - Ship::Endianness endianness = (Ship::Endianness)reader->ReadUByte(); - reader->SetEndianness(endianness); + reader->SetEndianness(Ship::Endianness::Big); version.major = reader->ReadUInt16(); version.minor = reader->ReadUInt16(); version.patch = reader->ReadUInt16(); + } else { + SPDLOG_WARN("Failed to read portVersion file from O2R: {}", otrPath); } + } else { + SPDLOG_WARN("Failed to open O2R for version reading: {}", otrPath); } return version; @@ -101,10 +104,11 @@ OTRVersion ReadPortVersionFromOTR(std::string otrPath) { // For Windows/Mac/Linux if the version doesn't match, offer to OTRVersion DetectOTRVersion(std::string fileName) { bool isOtrOld = false; - std::string otrPath = Ship::Context::LocateFileAcrossAppDirs(fileName, "sm64"); + std::string otrPath = Ship::Context::LocateFileAcrossAppDirs(fileName); // Doesn't exist so nothing to do here if (!std::filesystem::exists(otrPath)) { + SPDLOG_WARN("O2R file not found at path: {}", otrPath); return { INT16_MAX, INT16_MAX, INT16_MAX }; } @@ -112,8 +116,7 @@ OTRVersion DetectOTRVersion(std::string fileName) { } bool VerifyArchiveVersion(OTRVersion version) { - return version.major != INT16_MAX && version.minor != INT16_MAX && - (version.major == gBuildVersionMajor || version.minor == gBuildVersionMinor); + return version.major == gBuildVersionMajor && version.minor == gBuildVersionMinor; } GameEngine::GameEngine() : dictionary(nullptr) { @@ -128,42 +131,37 @@ GameEngine::GameEngine() : dictionary(nullptr) { this->context->InitConsoleVariables(); // without this line the controldeck constructor failes in // ShipDeviceIndexMappingManager::UpdateControllerNamesFromConfig() -#if (_DEBUG) - auto defaultLogLevel = spdlog::level::debug; -#else - auto defaultLogLevel = spdlog::level::info; -#endif - auto logLevel = - static_cast(CVarGetInteger(CVAR_DEVELOPER_TOOLS("LogLevel"), defaultLogLevel)); - context->InitLogging(logLevel, logLevel); - Ship::Context::GetInstance()->GetLogger()->set_pattern("[%H:%M:%S.%e] [%s:%#] [%l] %v"); - SPDLOG_INFO("Starting Ghostship version {} (Branch: {} | Commit: {})", (char*)gBuildVersion, (char*)gGitBranch, - (char*)gGitCommitHash); - std::vector archiveFiles; const std::string main_path = Ship::Context::GetPathRelativeToAppDirectory("sm64.o2r"); const std::string assets_path = Ship::Context::LocateFileAcrossAppDirs("ghostship.o2r"); - bool shouldRegen = !VerifyArchiveVersion(DetectOTRVersion("sm64.o2r")); - -#ifdef _WIN32 - AllocConsole(); -#endif + OTRVersion curVer = DetectOTRVersion("sm64.o2r"); + bool shouldRegen = !VerifyArchiveVersion(curVer) && curVer.major != INT16_MAX; if (std::filesystem::exists(main_path) && !shouldRegen) { archiveFiles.push_back(main_path); } else { - if (shouldRegen && std::filesystem::exists(main_path)) { - std::filesystem::remove(main_path); - } + SPDLOG_WARN("Your ROM O2R is outdated, and needs to be re-extracted."); std::string msg = (shouldRegen ? "Your ROM O2R is outdated, and needs to be re-extracted.\n\n" : "") + std::string("Please provide a Super Mario 64 ROM.\n\nSupported Versions:\nUS\nJP\n\n" "Assets will be extracted into an O2R file."); if (ShowYesNoBox("Ghostship - Asset Extraction", msg.c_str()) == IDYES) { + if (shouldRegen && std::filesystem::exists(main_path)) { + std::filesystem::remove(main_path); + } +#ifdef _WIN32 + AllocConsole(); +#endif if (!GenAssetFile()) { +#if defined(_WIN32) && !defined(_DEBUG) + FreeConsole(); +#endif ShowMessage("Error", "An error occured, no O2R file was generated.\n\nExiting..."); exit(1); } else { +#if defined(_WIN32) && !defined(_DEBUG) + FreeConsole(); +#endif archiveFiles.push_back(main_path); } } else { @@ -195,6 +193,18 @@ GameEngine::GameEngine() : dictionary(nullptr) { } } +#if (_DEBUG) + auto defaultLogLevel = spdlog::level::debug; +#else + auto defaultLogLevel = spdlog::level::info; +#endif + auto logLevel = + static_cast(CVarGetInteger(CVAR_DEVELOPER_TOOLS("LogLevel"), defaultLogLevel)); + context->InitLogging(logLevel, logLevel); + Ship::Context::GetInstance()->GetLogger()->set_pattern("[%H:%M:%S.%e] [%s:%#] [%l] %v"); + SPDLOG_INFO("Starting Ghostship version {} (Branch: {} | Commit: {})", (char*)gBuildVersion, (char*)gGitBranch, + (char*)gGitCommitHash); + auto controlDeck = std::make_shared(); this->context->InitControlDeck(controlDeck); @@ -206,6 +216,7 @@ GameEngine::GameEngine() : dictionary(nullptr) { context->InitGfxDebugger(); context->InitFileDropMgr(); + context->InitCrashHandler(); this->context->InitAudio({ .SampleRate = 32000, .SampleLength = 512, .DesiredBuffered = 1100 }); @@ -407,6 +418,7 @@ void GameEngine::Create() { void GameEngine::Destroy() { GhostshipGui::Destroy(); + gsFast3dWindow = nullptr; Instance->context = nullptr; AudioExit(); #ifdef __SWITCH__ @@ -432,16 +444,14 @@ void GameEngine::StartFrame() const { } uint32_t GameEngine::GetInterpolationFPS() { - if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) { - return CVarGetInteger(CVAR_SETTING("InterpolationFPS"), 30); - } - if (CVarGetInteger(CVAR_SETTING("MatchRefreshRate"), 0)) { return Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(); + } else if (CVarGetInteger(CVAR_VSYNC_ENABLED, 1) || + !Ship::Context::GetInstance()->GetWindow()->CanDisableVerticalSync()) { + return std::min(Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(), + CVarGetInteger(CVAR_SETTING("InterpolationFPS"), 30)); } - - return std::min(Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(), - CVarGetInteger(CVAR_SETTING("InterpolationFPS"), 30)); + return CVarGetInteger(CVAR_SETTING("InterpolationFPS"), 30); } // Audio @@ -760,7 +770,7 @@ extern "C" uint8_t* GameEngine_LoadTranslation(const char* key) { return dictionary->at(key).data(); } -extern "C" int GameEngine_OTRSigCheck(const char* data) { +extern "C" bool GameEngine_OTRSigCheck(const char* data) { return Ship::Context::GetInstance()->GetResourceManager()->OtrSignatureCheck(data); } diff --git a/src/port/Engine.h b/src/port/Engine.h index 37e8bd2b6..d4e4f40eb 100644 --- a/src/port/Engine.h +++ b/src/port/Engine.h @@ -114,7 +114,7 @@ uint8_t* GameEngine_LoadActName(uint32_t actId); uint8_t* GameEngine_LoadLevelName(uint32_t levelId); struct DialogEntry* GameEngine_LoadDialog(uint32_t dialogId); uint8_t* GameEngine_LoadTranslation(const char* key); -int GameEngine_OTRSigCheck(const char* imgData); +bool GameEngine_OTRSigCheck(const char* imgData); struct Animation* GameEngine_LoadAnimation(uint32_t animId); void GameEngine_GfxPrint(const char* str, void* printer, void (*printImpl)(void*, char)); void* GameEngine_GetExactDataByName(const char* path); diff --git a/src/port/GBIMiddleware.cpp b/src/port/GBIMiddleware.cpp index 685813db2..1f1d6dad1 100644 --- a/src/port/GBIMiddleware.cpp +++ b/src/port/GBIMiddleware.cpp @@ -61,7 +61,7 @@ extern "C" void gSPDisplayList(Gfx* pkt, Gfx* dl) { auto res = std::static_pointer_cast(resource); dl = &res->Instructions[0]; #ifdef USE_GBI_TRACE - printf("DisplayList: %s\n", imgData); + // printf("DisplayList: %s\n", imgData); for (int i = 0; i < res->Instructions.size(); i++) { auto gfx = &res->Instructions[i]; gfx->words.trace.file = imgData; diff --git a/src/port/GameExtractor.cpp b/src/port/GameExtractor.cpp index 9ca561186..4b1d8572a 100644 --- a/src/port/GameExtractor.cpp +++ b/src/port/GameExtractor.cpp @@ -190,7 +190,6 @@ std::optional GameExtractor::ValidateChecksum() const { void GameExtractor::WritePortVersion() { auto writer = LUS::BinaryWriter(); writer.SetEndianness(Torch::Endianness::Big); - writer.Write((uint8_t)1); writer.Write((uint16_t)gBuildVersionMajor); writer.Write((uint16_t)gBuildVersionMinor); writer.Write((uint16_t)gBuildVersionPatch); diff --git a/src/port/Rando/CheckTracker/CheckTracker.cpp b/src/port/Rando/CheckTracker/CheckTracker.cpp index 2156fe321..990a60910 100644 --- a/src/port/Rando/CheckTracker/CheckTracker.cpp +++ b/src/port/Rando/CheckTracker/CheckTracker.cpp @@ -4,28 +4,46 @@ #include "port/ui/UIWidgets.hpp" #include +extern "C" { +#include "include/assets/textures/segment2.h" +} + namespace GhostshipGui { extern std::shared_ptr mRandoCheckTrackerWindow; } #define WIDGET_COLOR UIWidgets::Colors(CVarGetInteger("gSettings.Menu.Theme", 5)) +#define DEFAULT_COLLECTED_COLOR Color_RGBA8(100, 255, 100, 255) +#define DEFAULT_SKIPPED_COLOR Color_RGBA8(255, 100, 255, 255) +#define DEFAULT_ITEM_COLOR Color_RGBA8(79, 0, 221, 255) #define CVAR_NAME_SHOW_CHECK_TRACKER "gWindows.CheckTracker" #define CVAR_NAME_TRACKER_OPACITY "gRando.CheckTracker.Opacity" #define CVAR_NAME_TRACKER_SCALE "gRando.CheckTracker.Scale" #define CVAR_NAME_SHOW_CURRENT_LEVEL "gRando.CheckTracker.ShowCurrentLevel" #define CVAR_NAME_COLLECTED_COLOR "gRando.CheckTracker.CollectedColor" +#define CVAR_NAME_SKIPPED_COLOR "gRando.CheckTracker.SkippedColor" +#define CVAR_NAME_ITEM_COLOR "gRando.CheckTracker.ItemColor" #define CVAR_SHOW_CHECK_TRACKER CVarGetInteger(CVAR_NAME_SHOW_CHECK_TRACKER, 0) #define CVAR_TRACKER_OPACITY CVarGetFloat(CVAR_NAME_TRACKER_OPACITY, 0.5f) #define CVAR_TRACKER_SCALE CVarGetFloat(CVAR_NAME_TRACKER_SCALE, 1.0f) #define CVAR_SHOW_CURRENT_LEVEL CVarGetInteger(CVAR_NAME_SHOW_CURRENT_LEVEL, 0) -#define CVAR_COLLECTED_COLOR CVarGetColor(CVAR_NAME_COLLECTED_COLOR ".Value", { 100, 255, 100, 255 }) +#define CVAR_COLLECTED_COLOR CVarGetColor(CVAR_NAME_COLLECTED_COLOR ".Value", DEFAULT_COLLECTED_COLOR) +#define CVAR_SKIPPED_COLOR CVarGetColor(CVAR_NAME_SKIPPED_COLOR ".Value", DEFAULT_SKIPPED_COLOR) +#define CVAR_ITEM_COLOR CVarGetColor(CVAR_NAME_ITEM_COLOR ".Value", DEFAULT_ITEM_COLOR) + +std::vector> defaultColorList = { + { CVAR_NAME_COLLECTED_COLOR, DEFAULT_COLLECTED_COLOR, "Check Obtained" }, + { CVAR_NAME_SKIPPED_COLOR, DEFAULT_SKIPPED_COLOR, "Check Skipped" }, + { CVAR_NAME_ITEM_COLOR, DEFAULT_ITEM_COLOR, "Obtained Item" }, +}; std::map levelIdList = { { LEVEL_BOB, "Bob Omb Battlefield" }, { LEVEL_WF, "Whomp's Fortress" }, { LEVEL_JRB, "Jolly Rodger's Bay" }, + { LEVEL_CASTLE, "Castle Interior" }, { LEVEL_CCM, "Cool Cool Mountain" }, { LEVEL_BBH, "Big Boo's Haunt" }, { LEVEL_HMC, "Hazy Maze Cave" }, @@ -49,33 +67,71 @@ std::map levelIdList = { { LEVEL_SA, "Secret Aquarium" }, }; +bool trackerPopoutState = false; ImVec4 trackerBG = ImVec4{ 0, 0, 0, 0.5f }; float trackerScale = 1.0f; -std::vector checkTrackerList; +bool expandState = true; void DrawCheckTrackerList() { - for (auto& level : checkTrackerList) { - if (CVAR_SHOW_CURRENT_LEVEL && level.levelId != gCurrLevelNum) { + if (Rando::Logic::shuffledPool.empty()) { + return; + } + for (auto& [id, name] : levelIdList) { + if (CVAR_SHOW_CURRENT_LEVEL && id != gCurrLevelNum) { continue; } - ImGui::PushID(level.levelId); + + ImGui::PushID(id); ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0, 0, 0, 0)); ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0, 0, 0, 0.5f)); - if (ImGui::CollapsingHeader(level.levelName.c_str(), - ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Leaf)) { + ImGui::SetNextItemOpen(expandState, ImGuiCond_Always); + if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Indent(20.0f); - if (ImGui::BeginTable("CheckList", 1)) { + if (ImGui::BeginTable("CheckList", 2)) { + ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, (16.0f * trackerScale)); + ImGui::TableSetupColumn("Check"); + ImGui::TableNextColumn(); - for (auto& [checkId, checkName, obtained] : level.randoCheckNameList) { - ImVec4 textColor = obtained ? VecFromRGBA8(CVAR_COLLECTED_COLOR) + for (auto& entry : Rando::Logic::shuffledPool) { + RandoSaveCheck& randoSaveCheck = RANDO_SAVE_CHECKS(selectedFileNum)[entry.randoCheckId]; + if (Rando::StaticData::Checks[entry.randoCheckId].levelId != id) { + continue; + } + ImVec4 checkTextColor = randoSaveCheck.obtained + ? VecFromRGBA8(CVAR_COLLECTED_COLOR) : UIWidgets::ColorValues.at(UIWidgets::Colors::White); - ImGui::TextColored(textColor, checkName.c_str()); - if (obtained) { + ImVec4 itemTextColor = randoSaveCheck.obtained + ? VecFromRGBA8(CVAR_ITEM_COLOR) + : UIWidgets::ColorValues.at(UIWidgets::Colors::Indigo); + if (randoSaveCheck.skipped) { + checkTextColor = itemTextColor = VecFromRGBA8(CVAR_SKIPPED_COLOR); + } + const char* texture = randoSaveCheck.randoItemId == RI_STAR ? texture_hud_char_star + : randoSaveCheck.randoItemId == RI_COIN_RED ? "Red Coin Icon" + : "Blue Coin Icon"; + ImTextureID textureId = + Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(texture); + + ImGui::BeginGroup(); + ImGui::Image(textureId, ImVec2(16.0f * trackerScale, 16.0f * trackerScale)); + ImGui::TableNextColumn(); + ImGui::TextColored(checkTextColor, Rando::StaticData::Checks[entry.randoCheckId].name); + if (randoSaveCheck.obtained) { + ImGui::SameLine(); + RandoItemId randoItemId = Rando::StaticData::GetShuffledRandoItem(entry.randoCheckId); + ImGui::TextColored(itemTextColor, "(%s)", Rando::StaticData::Items[randoItemId].name); + } else if (randoSaveCheck.skipped) { ImGui::SameLine(); - RandoItemId randoItemId = Rando::StaticData::GetShuffledRandoItem(level.levelId - 1, checkId); - ImGui::TextColored(UIWidgets::ColorValues.at(UIWidgets::Colors::LightBlue), "(%s)", - Rando::StaticData::Items[randoItemId].name); + ImGui::TextColored(itemTextColor, "(%s)", "Skipped"); + } + ImGui::Dummy(ImVec2(ImGui::GetContentRegionAvail().x, 0)); + ImGui::EndGroup(); + ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImGui::IsItemHovered() + ? IM_COL32(255, 255, 0, 128) + : IM_COL32(255, 255, 255, 0)); + if (ImGui::IsItemClicked()) { + randoSaveCheck.skipped = !randoSaveCheck.skipped; } ImGui::TableNextColumn(); } @@ -92,26 +148,6 @@ namespace Rando { namespace CheckTracker { -void SetCheckTrackerList() { - for (auto& [id, name] : levelIdList) { - CheckTrackerObject entry; - entry.levelId = id; - entry.levelName = name; - - for (auto& [randoCheckId, randoStaticCheck] : Rando::StaticData::Checks) { - if (entry.levelId == randoStaticCheck.levelId) { - if (!Rando::Logic::IsCheckShuffled(randoCheckId)) { - continue; - } - - entry.randoCheckNameList.push_back( - { randoCheckId, convertEnumToReadableName(randoStaticCheck.name), false }); - } - } - checkTrackerList.push_back(entry); - } -} - void CheckTrackerWindow::Draw() { if (!CVAR_SHOW_CHECK_TRACKER) { return; @@ -128,7 +164,7 @@ void CheckTrackerWindow::Draw() { if (ImGui::Begin("Check Tracker", nullptr, ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoFocusOnAppearing)) { trackerBG.w = ImGui::IsWindowDocked() ? 1.0f : CVAR_TRACKER_OPACITY; ImGui::SetWindowFontScale(trackerScale); - if (checkTrackerList.empty()) { + if (Rando::Logic::shuffledPool.empty()) { ImGui::TextColored(UIWidgets::ColorValues.at(UIWidgets::Colors::Orange), "No Rando Save Loaded"); ImGui::End(); ImGui::PopStyleColor(4); @@ -150,21 +186,42 @@ void CheckTrackerWindow::Draw() { void SettingsWindow::DrawElement() { if (CVarGetInteger("gWindows.CheckTracker", 0)) { - UIWidgets::WindowButton("Disable Check Tracker", "gWindows.CheckTracker", - GhostshipGui::mRandoCheckTrackerWindow, + trackerPopoutState = true; + UIWidgets::WindowButton("Return Check Tracker", "gWindows.CheckTracker", GhostshipGui::mRandoCheckTrackerWindow, { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red }); } else { - UIWidgets::WindowButton("Enable Check Tracker", "gWindows.CheckTracker", GhostshipGui::mRandoCheckTrackerWindow, + trackerPopoutState = false; + UIWidgets::WindowButton("Popout Check Tracker", "gWindows.CheckTracker", GhostshipGui::mRandoCheckTrackerWindow, { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Green }); } if (ImGui::BeginTable("Settings Table", 2)) { ImGui::TableSetupColumn("col1", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("col2", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableNextColumn(); + + ImGui::SeparatorText("Check Tracker"); + if (!trackerPopoutState) { + if (ImGui::BeginChild("Checks", ImVec2(ImGui::GetContentRegionAvail().x, 0))) { + DrawCheckTrackerList(); + ImGui::EndChild(); + } + } else { + ImGui::TextColored(UIWidgets::ColorValues.at(WIDGET_COLOR), "Tracker popped out"); + } + ImGui::TableNextColumn(); ImGui::SeparatorText("Check Settings"); UIWidgets::CVarCheckbox("Only Show Current Level", CVAR_NAME_SHOW_CURRENT_LEVEL); + if (UIWidgets::Button( + "Expand All", + UIWidgets::ButtonOptions{}.Color(WIDGET_COLOR).Size(ImVec2(ImGui::GetContentRegionAvail().x / 2, 0)))) { + expandState = true; + } + ImGui::SameLine(); + if (UIWidgets::Button("Collapse All", UIWidgets::ButtonOptions{}.Color(WIDGET_COLOR))) { + expandState = false; + } - ImGui::TableNextColumn(); ImGui::SeparatorText("Window Settings"); // UIWidgets::CVarCheckbox("Show Search", CVAR_NAME_SHOW_SEARCH, // UIWidgets::CheckboxOptions().DefaultValue(true)); UIWidgets::CVarCheckbox("Show Check Type Filters", @@ -196,16 +253,34 @@ void SettingsWindow::DrawElement() { trackerScale = CVAR_TRACKER_SCALE; } - UIWidgets::CVarColorPicker("##CollectedColor", CVAR_NAME_COLLECTED_COLOR, { 100, 255, 100, 255 }, true); - ImGui::SameLine(); - ImGui::Text("Check Obtained Color"); + int16_t colorIndex = 0; + for (auto& [cvar, color, label] : defaultColorList) { + std::string cvarText = cvar; + cvarText += ".Value"; + std::string colorText = label; + colorText += " Color"; + std::string widgetLabel = "##"; + widgetLabel += std::to_string(colorIndex); + ImGui::PushID(colorIndex); + UIWidgets::CVarColorPicker(widgetLabel.c_str(), cvar, color, true); + ImGui::SameLine(); + if (UIWidgets::Button(ICON_FA_REFRESH, { .size = ImVec2(32.0f, 32.0f), .color = WIDGET_COLOR })) { + CVarSetColor(cvarText.c_str(), color); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } + ImGui::SameLine(); + ImGui::Text(colorText.c_str()); + ImGui::PopID(); + colorIndex++; + } ImGui::EndTable(); } } bool isInitialized = false; void Init() { + trackerPopoutState = CVarGetInteger("gWindows.CheckTracker", 0); trackerBG = { 0, 0, 0, CVAR_TRACKER_OPACITY }; trackerScale = CVAR_TRACKER_SCALE; } diff --git a/src/port/Rando/CheckTracker/CheckTracker.h b/src/port/Rando/CheckTracker/CheckTracker.h index 1eb9ce346..0babc84d6 100644 --- a/src/port/Rando/CheckTracker/CheckTracker.h +++ b/src/port/Rando/CheckTracker/CheckTracker.h @@ -4,21 +4,12 @@ #include "port/Rando/Rando.h" #include -typedef struct { - int16_t levelId; - std::string levelName; - std::vector> randoCheckNameList; -} CheckTrackerObject; - -extern std::vector checkTrackerList; - namespace Rando { namespace CheckTracker { void Init(); //void OnFileLoad(); -void SetCheckTrackerList(); class CheckTrackerWindow : public Ship::GuiWindow { public: diff --git a/src/port/Rando/CustomItem/CustomItem.cpp b/src/port/Rando/CustomItem/CustomItem.cpp index cee886977..aa53ac483 100644 --- a/src/port/Rando/CustomItem/CustomItem.cpp +++ b/src/port/Rando/CustomItem/CustomItem.cpp @@ -1,6 +1,7 @@ #include "CustomItem.h" #include "port/Rando/Logic/Logic.h" #include "port/Rando/CheckTracker/CheckTracker.h" +#include "port/ui/Notification.h" extern "C" { #include "game/object_list_processor.h" @@ -11,6 +12,9 @@ extern "C" { #include "audio/external.h" #include "game/sound_init.h" #include "game/mario.h" + +// Asset Headers +#include "include/assets/textures/segment2.h" } std::map spawnedRandoObjects; @@ -20,24 +24,36 @@ std::vector starActParams = { 0, 16777216, 33554432, 50331648, 67108864, 83886080, 100663296, }; +struct Object* AssignParentObject() { + for (auto& [randoCheck, spawnedObj] : spawnedRandoObjects) { + if (Rando::StaticData::Checks[randoCheck].randoCheckType == RCTYPE_STAR_RED_COIN) { + return spawnedObj; + } + } + return NULL; +} + +void CreateCollectNotification(const char* texture, std::string text, ImVec4 textColor) { + if (texture == texture_hud_char_coin && CustomItem::redCoinsCollected > 1) { + text += "'s"; + } + + text += " collected!"; + + Notification::Emit({ .itemIcon = texture, .message = text, .messageColor = textColor }); +} + void CustomItem::ClearSpawnedObjects() { spawnedRandoObjects.clear(); } void CustomItem::ObjectCollected(int16_t type, struct MarioState* mario, struct Object* object) { - // TODO: Implement Check Tracker functionality using this. - SPDLOG_INFO("Check ID: {} - Collected", std::to_string(object->unused1)); - for (auto& level : checkTrackerList) { - bool checkFound = false; - for (auto& [checkId, checkName, obtained] : level.randoCheckNameList) { - if (checkId == object->unused1) { - obtained = true; - checkFound = true; - break; - } - } - if (checkFound) { - break; + bool previousCheckState = false; + for (auto& shuffled : Rando::Logic::shuffledPool) { + if (shuffled.randoCheckId == object->unused1) { + previousCheckState = shuffled.obtained; + shuffled.obtained = true; + RANDO_SAVE_CHECKS(selectedFileNum)[shuffled.randoCheckId].obtained = true; } } @@ -46,7 +62,15 @@ void CustomItem::ObjectCollected(int16_t type, struct MarioState* mario, struct spawn_object(object, MODEL_SPARKLES, bhvGoldenCoinSparkles); if (object->behavior == bhvRedCoin) { CustomItem::redCoinsCollected++; + CreateCollectNotification("Red Coin Icon", std::to_string(CustomItem::redCoinsCollected) + "x Red Coin", + ImVec4(1, 0, 0, 1)); + if (CustomItem::redCoinsCollected != 8) { + if (object->parentObj == nullptr) { + object->parentObj = object; + } + object->parentObj->oHiddenStarTriggerCounter = redCoinsCollected; + struct Object* spawnNumber; spawnNumber = spawn_object_relative(CustomItem::redCoinsCollected, 0, 0, 0, object, MODEL_NUMBER, bhvOrangeNumber); @@ -56,90 +80,62 @@ void CustomItem::ObjectCollected(int16_t type, struct MarioState* mario, struct } play_sound(SOUND_MENU_COLLECT_RED_COIN + ((CustomItem::redCoinsCollected - 1) << 16), gGlobalSoundSource); + } else { + CreateCollectNotification("Blue Coin Icon", "Blue Coin", ImVec4(0, 0, 1, 1)); } mario->numCoins += object->oDamageOrCoinValue; mario->healCounter += 4 * object->oDamageOrCoinValue; - if (COURSE_IS_MAIN_COURSE(gCurrCourseNum) && mario->numCoins - object->oDamageOrCoinValue < 100 && - mario->numCoins >= 100) { - Rando::StaticData::RandoStaticCheck randoStaticCheck = - Rando::StaticData::GetShuffledRandoStaticCheck(0, 0, 0); - if (randoStaticCheck.randoCheckId != RC_UNKNOWN) { - int16_t modelId = Rando::StaticData::GetModelByRandoItem(randoStaticCheck.randoItemId); - const BehaviorScript* behavior = modelId == MODEL_BLUE_COIN ? bhvHiddenBlueCoin - : modelId == MODEL_STAR - ? bhvSpawnedStarNoLevelExit - : Rando::StaticData::GetBehaviorByModel(modelId); - - CustomItem::SpawnObject(modelId, behavior, object->rawData.asF32[0x6], - object->rawData.asF32[0x7] + 250, object->rawData.asF32[0x8], NULL, - randoStaticCheck.randoCheckId, randoStaticCheck.actData); - } - } break; case TYPE_STAR: { + CreateCollectNotification(texture_hud_char_star, "Course " + std::to_string(object->unused2) + " Star", + ImVec4(1, 1, 0, 1)); + play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource); spawn_object(object, MODEL_NONE, bhvStarKeyCollectionPuffSpawner); - bool shouldExitLevel = (object->oInteractionSubtype & INT_SUBTYPE_NO_EXIT) == 0 || - !CVarGetInteger("gEnhancements.StarNoExit", 0); - bool isGrandStar = (object->oInteractionSubtype & INT_SUBTYPE_GRAND_STAR) != 0; - u32 starGrabAction = ACT_STAR_DANCE_EXIT; - int16_t starAct = object->unused2; - - if (shouldExitLevel) { - mario->hurtCounter = 0; - mario->healCounter = 0; - if (mario->capTimer > 1) { - mario->capTimer = 1; - } - } - - if (!shouldExitLevel) { - starGrabAction = ACT_STAR_DANCE_NO_EXIT; - } - - if (mario->action & ACT_FLAG_SWIMMING) { - starGrabAction = ACT_STAR_DANCE_WATER; - } - if (mario->action & ACT_FLAG_METAL_WATER) { - starGrabAction = ACT_STAR_DANCE_WATER; - } + if (!previousCheckState && !CVarGetInteger("gRandoSettings.SkipRandoGI", 0)) { + uint32_t marioAction = ACT_STAR_DANCE_NO_EXIT; + switch (gMarioState->action) { + case ACT_FLAG_SWIMMING: + case ACT_FLAG_METAL_WATER: + marioAction = ACT_STAR_DANCE_WATER; + break; + case ACT_FLAG_AIR: + marioAction = ACT_FALL_AFTER_STAR_GRAB; + break; + default: + marioAction = ACT_STAR_DANCE_NO_EXIT; + break; + } - if (mario->action & ACT_FLAG_AIR) { - starGrabAction = ACT_FALL_AFTER_STAR_GRAB; + set_mario_action(gMarioState, marioAction, 1); } - // TODO: Save to file once JSON Saves are implemented. - save_file_collect_star_or_key(mario->numCoins, starAct); + save_file_collect_star_or_key(mario->numCoins, object->unused2); mario->numStars = save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1); + save_file_do_save(selectedFileNum); - if (shouldExitLevel) { - drop_queued_background_music(); - fadeout_level_music(126); - } - - play_sound(SOUND_MENU_STAR_SOUND, mario->marioObj->header.gfx.cameraToObject); - if (!ROM_JP) { - update_mario_sound_and_camera(mario); - } - - if (isGrandStar) { - set_mario_action(mario, ACT_JUMBO_STAR_CUTSCENE, 0); - } else { - set_mario_action(mario, starGrabAction, !shouldExitLevel + 2 * isGrandStar); - } break; } default: break; } + if (spawnedRandoObjects.find((RandoCheckId)object->unused1) != spawnedRandoObjects.end()) { + spawnedRandoObjects.erase((RandoCheckId)object->unused1); + } + object->activeFlags = ACTIVE_FLAG_DEACTIVATED; + object->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; + obj_mark_for_deletion(object); } void CustomItem::SetBehavior(struct Object* object, u32 modelId, RandoCheckId randoCheckId, RandoAct randoAct) { if (Rando::Logic::IsBlueSwitchActivated(randoCheckId)) { - object->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; + object->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; object->oIntangibleTimer = -1; + if (modelId == MODEL_STAR) { + object->oBehParams = starActParams[randoAct]; + } } else { switch (modelId) { case MODEL_BLUE_COIN: @@ -149,9 +145,6 @@ void CustomItem::SetBehavior(struct Object* object, u32 modelId, RandoCheckId ra break; case MODEL_STAR: object->oBehParams = starActParams[randoAct]; - if (!CVarGetInteger("gEnhancements.StarNoExit", 0)) { - object->oInteractionSubtype |= INT_SUBTYPE_NO_EXIT; - } break; default: break; @@ -170,6 +163,9 @@ void CustomItem::SpawnObject(u32 modelId, const BehaviorScript* behavior, s16 x, if (param != NULL) { object->oBehParams2ndByte = param; } + if (modelId == MODEL_STAR) { + object->oInteractionSubtype |= INT_SUBTYPE_NO_EXIT; + } spawnedRandoObjects.insert({ randoCheckId, object }); } diff --git a/src/port/Rando/CustomItem/CustomItem.h b/src/port/Rando/CustomItem/CustomItem.h index e8d9e3657..2e41ab905 100644 --- a/src/port/Rando/CustomItem/CustomItem.h +++ b/src/port/Rando/CustomItem/CustomItem.h @@ -4,6 +4,8 @@ #include "game/area.h" #include "game/save_file.h" +extern std::vector starActParams; + namespace CustomItem { extern int16_t redCoinsCollected; diff --git a/src/port/Rando/GhostshipMenuRando.cpp b/src/port/Rando/GhostshipMenuRando.cpp index 525f7d4cf..5e29456f8 100644 --- a/src/port/Rando/GhostshipMenuRando.cpp +++ b/src/port/Rando/GhostshipMenuRando.cpp @@ -1,6 +1,9 @@ #include "port/ui/GhostshipMenu.h" -//#include "port/Rando/Rando.h" #include "port/Rando/CheckTracker/CheckTracker.h" +#include "port/Rando/Spoiler/Spoiler.h" +#include "port/ui/Notification.h" + +#define WIDGET_COLOR UIWidgets::Colors(CVarGetInteger("gSettings.Menu.Theme", 5)) namespace GhostshipGui { @@ -28,12 +31,77 @@ void GhostshipMenu::AddMenuRando() { .Options(ComboboxOptions() .Tooltip("Sets the Logic type for the seed.") .ComboMap(Rando::StaticData::logicOptions) - .DefaultIndex(RO_LOGIC_GLITCHLESS)); + .DefaultIndex(RO_LOGIC_GLITCHLESS) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Near)); - path = { "Rando", "Shuffle Options", SECTION_COLUMN_1 }; + AddWidget(path, "SeperatorBar", WIDGET_SEPARATOR); + + AddWidget(path, "Generate Spoiler Log", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_RANDOMIZER_SETTING("GenerateLog")) + .RaceDisable(false) + .Options(CheckboxOptions().Tooltip("Generates a Spoiler Log in the randomizer folder.").DefaultValue(true)); + // TODO: populate combobox with existing spoiler logs + AddWidget(path, "Load Existing Spoiler Log", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_RANDOMIZER_SETTING("UseExistingLog")) + .RaceDisable(false) + .Options(CheckboxOptions().Tooltip("Uses a Spoiler Log in the randomizer folder.")) + .PreFunc([](WidgetInfo& info) { + info.options->Disabled(CVarGetInteger(CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), 0)); + }); + AddWidget(path, "Manual Seed Entry", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_RANDOMIZER_SETTING("ManualSeedEntry")) + .RaceDisable(false) + .Options(CheckboxOptions().Tooltip("Generates a seed using the provided input.")); + AddWidget(path, "Seed", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { + ImGui::BeginDisabled(!CVarGetInteger(CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), 0)); + UIWidgets::PushStyleInput(WIDGET_COLOR); + ImGui::InputText("##ManualSeed", seedString, MAX_SEED_STRING_SIZE, ImGuiInputTextFlags_CallbackCharFilter, + UIWidgets::TextFilters::FilterAlphaNum); + UIWidgets::Tooltip("Characters from a-z, A-Z, and 0-9 are supported.\n" + "Character limit is 1023, after which the seed will be truncated.\n"); + ImGui::SameLine(); + if (UIWidgets::Button(ICON_FA_ERASER, UIWidgets::ButtonOptions() + .Size(UIWidgets::Sizes::Inline) + .Color(WIDGET_COLOR) + .Padding(ImVec2(10.f, 6.f)))) { + memset(seedString, 0, MAX_SEED_STRING_SIZE); + } + if (strnlen(seedString, MAX_SEED_STRING_SIZE) == 0) { + ImGui::SameLine(17.0f); + ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "Leave blank for random seed"); + } + UIWidgets::PopStyleInput(); + ImGui::EndDisabled(); + }); + AddWidget(path, "Create Spoiler Log", WIDGET_BUTTON) + .Callback([](WidgetInfo& info) { + nlohmann::json spoiler = Rando::Spoiler::GenerateFromPoolGeneration(Rando::Logic::shuffledPool); + std::string fileName = std::to_string(spoiler["finalSeed"].get()).c_str(); + fileName += ".json"; + Rando::Spoiler::SaveToFile(fileName, spoiler); + Notification::Emit({ .prefix = fileName + " ", + .message = "Spoiler Log created.", + .messageColor = ImVec4(0, 0.3f, 0.85f, 1) }); + }) + .Options(ButtonOptions().Tooltip("Creates a Spoiler Log from the current SaveFile.")) + .PreFunc([](WidgetInfo& info) { info.options->Disabled(Rando::Logic::shuffledPool.empty()); }); + + AddWidget(path, "SeperatorBar", WIDGET_SEPARATOR); + + path = { "Rando", "Enhancements", SECTION_COLUMN_1 }; AddSidebarEntry("Rando", path.sidebarName, 1); path.column = SECTION_COLUMN_1; + AddWidget(path, "Skip Get Item Cutscene", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_RANDOMIZER_SETTING("SkipRandoGI")) + .RaceDisable(false) + .Options(CheckboxOptions().Tooltip("Skips the cutscene when collecting a Star.")); + + path = { "Rando", "Shuffle Options", SECTION_COLUMN_1 }; + AddSidebarEntry("Rando", path.sidebarName, 2); + path.column = SECTION_COLUMN_1; + AddWidget(path, "Item Options", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Shuffle Stars", WIDGET_CVAR_CHECKBOX) .CVar(Rando::StaticData::Options[RO_SHUFFLE_STARS].cvar) @@ -54,6 +122,26 @@ void GhostshipMenu::AddMenuRando() { .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Shuffles Blue Coins into the Item Pool.")); + path.column = SECTION_COLUMN_2; + + AddWidget(path, "Entrance Options", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Shuffle Bowser Levels", WIDGET_CVAR_CHECKBOX) + .CVar(Rando::StaticData::Options[RO_SHUFFLE_ENTRANCES_BOWSER].cvar) + .RaceDisable(false) + .Options(CheckboxOptions().Tooltip("Shuffles Bowser Entrances.")); + AddWidget(path, "Shuffle Cap Unlock Levels", WIDGET_CVAR_CHECKBOX) + .CVar(Rando::StaticData::Options[RO_SHUFFLE_ENTRANCES_CAP].cvar) + .RaceDisable(false) + .Options(CheckboxOptions().Tooltip("Shuffles Cap Unlock Entrances.")); + AddWidget(path, "Shuffle Paintings", WIDGET_CVAR_CHECKBOX) + .CVar(Rando::StaticData::Options[RO_SHUFFLE_ENTRANCES_PAINTING].cvar) + .RaceDisable(false) + .Options(CheckboxOptions().Tooltip("Shuffles Painting Entrances including Rainbow Ride.")); + AddWidget(path, "Shuffle Secret Levels", WIDGET_CVAR_CHECKBOX) + .CVar(Rando::StaticData::Options[RO_SHUFFLE_ENTRANCES_SECRET].cvar) + .RaceDisable(false) + .Options(CheckboxOptions().Tooltip("Shuffles Castle Secret Entrances.")); + path = { "Rando", "Check Tracker", SECTION_COLUMN_1 }; AddSidebarEntry("Rando", path.sidebarName, 1); path.column = SECTION_COLUMN_1; diff --git a/src/port/Rando/Logic/GeneratePools.cpp b/src/port/Rando/Logic/GeneratePools.cpp index 620d4a822..9c2885bf8 100644 --- a/src/port/Rando/Logic/GeneratePools.cpp +++ b/src/port/Rando/Logic/GeneratePools.cpp @@ -12,24 +12,80 @@ extern "C" { namespace Rando { namespace Logic { -// Initial Shuffling containers +// Initial Check Shuffling containers std::vector> shuffledList; std::vector shuffledLevelList; std::vector shuffledChecks; std::vector> shuffledItems; +// Initial Entrance Shuffling containers +std::vector entranceIds; +std::vector levelIds; + // Final Shuffle List std::vector shuffledPool; +std::vector shuffledEntrances; + +uint32_t GetRandoSeed(const std::string& input) { + if (finalSeed > 0) { + return finalSeed; + } -void ShuffleRandoItems(std::vector>& shuffledItems) { std::random_device rd; - std::mt19937 g(rd()); + if (CVarGetInteger("gRandoSettings.ManualSeedEntry", 0)) { + if (input.empty()) { + return rd(); + } else { + return Ship_Hash(input); + } + } + + return rd(); +} + +void ShuffleRandoItems(std::vector>& shuffledItems, const std::string& input) { + uint32_t seed = GetRandoSeed(input); + + std::mt19937 g(seed); std::shuffle(shuffledItems.begin(), shuffledItems.end(), g); + + finalSeed = seed; +} + +void ShuffleRandoEntrances(std::vector& shuffledLevels, const std::string& input) { + uint32_t seed = GetRandoSeed(input); + + std::mt19937 g(seed); + std::shuffle(shuffledLevels.begin(), shuffledLevels.end(), g); + + finalSeed = seed; +} + +void InitializeSaveChecks() { + for (auto& [randoCheckId, randoStaticCheck] : Rando::StaticData::Checks) { + RandoSaveCheck randoSaveCheck = { .randoItemId = randoStaticCheck.randoItemId, + .randoAct = randoStaticCheck.actData, + .obtained = false, + .skipped = false }; + RANDO_SAVE_CHECKS(selectedFileNum)[randoCheckId] = randoSaveCheck; + } +} + +void InitializeSaveEntrances() { + for (auto& [randoEntranceId, randoStaticEntrance] : Rando::StaticData::Entrances) { + RandoSaveEntrance randoSaveEntrance = { .randoEntranceId = randoEntranceId, + .destinationId = randoStaticEntrance.destinationId, + .randoEntranceType = randoStaticEntrance.randoEntranceType, + .deathWarpId = randoStaticEntrance.deathWarpId, + .deathArea = randoStaticEntrance.deathArea }; + RANDO_SAVE_ENTRANCES(selectedFileNum)[randoEntranceId] = randoSaveEntrance; + } } void GenerateShuffleList() { shuffledPool.clear(); + shuffledEntrances.clear(); for (int i = LEVEL_UNKNOWN_1; i < LEVEL_UNKNOWN_38; i++) { shuffledLevelList.clear(); @@ -73,31 +129,65 @@ void GenerateShuffleList() { } if (!shuffledItems.empty()) { for (int v = 0; v < shuffledChecks.size(); v++) { - shuffledLevelList.push_back({ shuffledChecks[v], shuffledItems[v].first, shuffledItems[v].second }); + shuffledLevelList.push_back( + { shuffledChecks[v], shuffledItems[v].first, shuffledItems[v].second, false, false }); } shuffledList.push_back(shuffledLevelList); } } + for (auto& [randoEntranceId, randoStaticEntrance] : Rando::StaticData::Entrances) { + if (randoEntranceId == RE_UNKNOWN) { + continue; + } + + if (randoStaticEntrance.randoEntranceType == RETYPE_BOWSER && + CVarGetInteger(Rando::StaticData::Options[RO_SHUFFLE_ENTRANCES_BOWSER].cvar, 0) == RO_GENERIC_OFF) { + continue; + } + + if (randoStaticEntrance.randoEntranceType == RETYPE_CAP && + CVarGetInteger(Rando::StaticData::Options[RO_SHUFFLE_ENTRANCES_CAP].cvar, 0) == RO_GENERIC_OFF) { + continue; + } + + if (randoStaticEntrance.randoEntranceType == RETYPE_PAINTING && + CVarGetInteger(Rando::StaticData::Options[RO_SHUFFLE_ENTRANCES_PAINTING].cvar, 0) == RO_GENERIC_OFF) { + continue; + } + + if (randoStaticEntrance.randoEntranceType == RETYPE_SECRET && + CVarGetInteger(Rando::StaticData::Options[RO_SHUFFLE_ENTRANCES_SECRET].cvar, 0) == RO_GENERIC_OFF) { + continue; + } + + entranceIds.push_back(randoStaticEntrance.randoEntranceId); + levelIds.push_back(randoStaticEntrance.destinationId); + } + switch (CVarGetInteger(Rando::StaticData::Options[RO_LOGIC].cvar, 0)) { case RO_LOGIC_GLITCHLESS: - break; case RO_LOGIC_NO_LOGIC: - ApplyNoLogicToSaveContext(shuffledList); + ApplyNoLogicToSaveContext(shuffledList, levelIds); break; default: break; } - - nlohmann::json spoilerLog = Rando::Spoiler::GenerateFromPoolGeneration(shuffledPool); - if (spoilerLog.empty()) { - Notification::Emit( - { .message = "Error: No Spoiler Log was created.", .messageColor = ImVec4(0.85f, 0.3f, 0, 1) }); - } else { - std::string fileName = spoilerLog["fileNum"].get() + ".json"; - Rando::Spoiler::SaveToFile(fileName, spoilerLog); - Notification::Emit( - { .prefix = fileName + " ", .message = "Spoiler Log created.", .messageColor = ImVec4(0, 0.3f, 0.85f, 1) }); + gSaveBuffer.files[selectedFileNum][0].shipSaveData.randoSaveData.finalSeed = finalSeed; + + if (CVarGetInteger("gRandoSettings.GenerateLog", 0)) { + nlohmann::json spoilerLog = Rando::Spoiler::GenerateFromPoolGeneration(shuffledPool); + if (spoilerLog.empty()) { + Notification::Emit( + { .message = "Error: No Spoiler Log was created.", .messageColor = ImVec4(0.85f, 0.3f, 0, 1) }); + } else { + std::string fileName = std::to_string(spoilerLog["finalSeed"].get()).c_str(); + fileName += ".json"; + Rando::Spoiler::SaveToFile(fileName, spoilerLog); + Notification::Emit({ .prefix = fileName + " ", + .message = "Spoiler Log created.", + .messageColor = ImVec4(0, 0.3f, 0.85f, 1) }); + } } } diff --git a/src/port/Rando/Logic/Logic.h b/src/port/Rando/Logic/Logic.h index f4e723743..3503ec10d 100644 --- a/src/port/Rando/Logic/Logic.h +++ b/src/port/Rando/Logic/Logic.h @@ -8,23 +8,36 @@ struct LevelShuffleEntry { RandoCheckId randoCheckId; RandoItemId randoItemId; RandoAct randoAct; + bool obtained; + bool skipped; }; namespace Rando { namespace Logic { +// Initial Check Shuffling containers extern std::vector> shuffledList; extern std::vector shuffledLevelList; - -extern std::vector shuffledPool; extern std::vector shuffledChecks; extern std::vector> shuffledItems; -void ShuffleRandoItems(std::vector>& shuffledItems); +// Initial Entrance Shuffling containers +extern std::vector entranceIds; +extern std::vector levelIds; + +// Final Shuffle List +extern std::vector shuffledPool; +extern std::vector shuffledEntrances; + +void ShuffleRandoItems(std::vector>& shuffledItems, const std::string& input); +void ShuffleRandoEntrances(std::vector& shuffledLevels, const std::string& input); +void InitializeSaveChecks(); +void InitializeSaveEntrances(); void GenerateShuffleList(); -void ApplyNoLogicToSaveContext(std::vector>& initialPool); +void ApplyNoLogicToSaveContext(std::vector>& initialPool, + std::vector& initialLevelPool); // Logic Operators inline bool IsBlueSwitchActivated(RandoCheckId randoCheckId) { diff --git a/src/port/Rando/Logic/NoLogic.cpp b/src/port/Rando/Logic/NoLogic.cpp index 47985981e..b32e89023 100644 --- a/src/port/Rando/Logic/NoLogic.cpp +++ b/src/port/Rando/Logic/NoLogic.cpp @@ -8,29 +8,50 @@ namespace Rando { namespace Logic { -void ApplyNoLogicToSaveContext(std::vector>& initialPool) { - std::vector pool; +void ApplyNoLogicToSaveContext(std::vector>& initialPool, + std::vector& initialLevelPool) { + std::vector levelpool; std::vector checkPool; std::vector> itemPool; - for (auto& level : initialPool) { - checkPool.clear(); - itemPool.clear(); + std::vector entrancePool; - for (auto& entry : level) { - checkPool.push_back(entry.randoCheckId); - itemPool.push_back({ entry.randoItemId, entry.randoAct }); - } + if (!initialPool.empty()) { + for (auto& level : initialPool) { + checkPool.clear(); + itemPool.clear(); + + for (auto& entry : level) { + checkPool.push_back(entry.randoCheckId); + itemPool.push_back({ entry.randoItemId, entry.randoAct }); + } - if (!itemPool.empty()) { - ShuffleRandoItems(itemPool); - for (int v = 0; v < checkPool.size(); v++) { - pool.push_back({ checkPool[v], itemPool[v].first, itemPool[v].second }); + if (!itemPool.empty()) { + ShuffleRandoItems(itemPool, seedString); + for (int v = 0; v < checkPool.size(); v++) { + levelpool.push_back({ checkPool[v], itemPool[v].first, itemPool[v].second, false }); + } } } } - shuffledPool = pool; + + if (!initialLevelPool.empty()) { + ShuffleRandoEntrances(initialLevelPool, seedString); + for (int e = 0; e < initialLevelPool.size(); e++) { + RandoSaveEntrance randoSaveEntrance; + randoSaveEntrance.randoEntranceId = entranceIds[e]; + randoSaveEntrance.destinationId = initialLevelPool[e]; + randoSaveEntrance.randoEntranceType = + Rando::StaticData::Entrances[randoSaveEntrance.randoEntranceId].randoEntranceType; + randoSaveEntrance.deathWarpId = Rando::StaticData::Entrances[randoSaveEntrance.randoEntranceId].deathWarpId; + entrancePool.push_back(randoSaveEntrance); + } + } + + shuffledPool = levelpool; + shuffledEntrances = entrancePool; initialPool.clear(); + initialLevelPool.clear(); } } // namespace Logic diff --git a/src/port/Rando/MiscBehavior/MiscBehavior.cpp b/src/port/Rando/MiscBehavior/MiscBehavior.cpp index c21a5fa42..05b455ad7 100644 --- a/src/port/Rando/MiscBehavior/MiscBehavior.cpp +++ b/src/port/Rando/MiscBehavior/MiscBehavior.cpp @@ -1,7 +1,81 @@ #include "MiscBehavior.h" +#include "port/hooks/list/EngineEvent.h" + +extern "C" { +#include "game/ingame_menu.h" +#include "game/level_update.h" +void initiate_warp(s16 destLevel, s16 destArea, s16 destWarpNode, s32 arg3); +} + +static RandoEntranceId currentEntrance = RE_UNKNOWN; // Entry point for the module, run once on game boot void Rando::MiscBehavior::Init() { Rando::MiscBehavior::OnFileLoad(); Rando::MiscBehavior::OnFileSave(); + + REGISTER_LISTENER(ModifyRedCoinCount, EVENT_PRIORITY_NORMAL, [](IEvent* event) { + ModifyRedCoinCount* ev = (ModifyRedCoinCount*)event; + if (!IS_RANDO(selectedFileNum)) { + return; + } + + *(ev->redCoinsCollected) = CustomItem::redCoinsCollected; + event->cancelled = true; + }); + + REGISTER_LISTENER(LevelScriptExecute, EVENT_PRIORITY_NORMAL, [](IEvent* event) { + LevelScriptExecute* ev = (LevelScriptExecute*)event; + if (ev->command == 17) { + if (gCurrLevelNum == LEVEL_CASTLE || gCurrLevelNum == LEVEL_CASTLE_COURTYARD || + gCurrLevelNum == LEVEL_CASTLE_GROUNDS) { + gRedCoinsCollected = 0; + CustomItem::redCoinsCollected = 0; + CustomItem::ClearSpawnedObjects(); + } + } + }); + + REGISTER_LISTENER(ChangeLevel, EVENT_PRIORITY_NORMAL, [](IEvent* event) { + ChangeLevel* ev = (ChangeLevel*)event; + if (!IS_RANDO(selectedFileNum)) { + return; + } + SPDLOG_INFO("currentLevel: {}", std::to_string(currentEntrance)); + SPDLOG_INFO("Source: {}", std::to_string(ev->sourceWarpNode)); + SPDLOG_INFO("Current Destination: {}", std::to_string(ev->warpNode->destLevel)); + + // TODO: Death Exits + if (ev->sourceWarpNode > 0 && currentEntrance != RE_UNKNOWN) { + Rando::StaticData::RandoStaticEntrance randoStaticEntrance = Rando::StaticData::Entrances[currentEntrance]; + + ev->warpNode->destNode = randoStaticEntrance.deathWarpId; + ev->warpNode->destArea = randoStaticEntrance.deathArea; + + switch (currentEntrance) { + case RE_BBH: + ev->warpNode->destLevel = LEVEL_CASTLE_COURTYARD; + break; + case RE_VCUTM: + ev->warpNode->destLevel = LEVEL_CASTLE_GROUNDS; + break; + default: + ev->warpNode->destLevel = LEVEL_CASTLE; + break; + } + + currentEntrance = RE_UNKNOWN; + } else { + RandoEntranceId randoEntranceId; + for (auto& [randoEntranceId, randoStaticEntrance] : Rando::StaticData::Entrances) { + if (randoStaticEntrance.destinationId == ev->warpNode->destLevel) { + // TODO: Handle Tiny Huge Islands size changes, Wet Dry World water level, and Tick Tock Clocks + // clock. + ev->warpNode->destLevel = RANDO_SAVE_ENTRANCES(selectedFileNum)[randoEntranceId].destinationId; + currentEntrance = randoEntranceId; + break; + } + } + } + }); } diff --git a/src/port/Rando/MiscBehavior/OnFileLoad.cpp b/src/port/Rando/MiscBehavior/OnFileLoad.cpp index a19210380..14d49356c 100644 --- a/src/port/Rando/MiscBehavior/OnFileLoad.cpp +++ b/src/port/Rando/MiscBehavior/OnFileLoad.cpp @@ -1,7 +1,6 @@ #include "MiscBehavior.h" #include "port/Rando/Logic/Logic.h" #include "port/Rando/Spoiler/Spoiler.h" -#include "port/Rando/CheckTracker/CheckTracker.h" #include "port/ui/Notification.h" extern "C" { @@ -21,32 +20,78 @@ bool SpoilerExistsForFileNum(std::string fileName) { void Rando::MiscBehavior::OnFileLoad() { REGISTER_LISTENER(OnGameFileLoad, EVENT_PRIORITY_NORMAL, [](IEvent* event) { OnGameFileLoad* ev = (OnGameFileLoad*)event; + selectedFileNum = ev->fileNum - 1; + if (!CVarGetInteger("gRandoSettings.Enabled", 0)) { - gSaveBuffer.files[ev->fileNum - 1]->shipSaveData.saveType = SAVETYPE_VANILLA; + gSaveBuffer.files[selectedFileNum]->shipSaveData.saveType = SAVETYPE_VANILLA; + Rando::Logic::shuffledPool.clear(); return; } - selectedFileNum = ev->fileNum - 1; - std::string fileName = std::to_string(selectedFileNum) + ".json"; - bool logExists = SpoilerExistsForFileNum(fileName); - if (!IS_RANDO(selectedFileNum)) { gSaveBuffer.files[selectedFileNum]->shipSaveData.saveType = SAVETYPE_RANDO; - if (!logExists) { + Rando::Logic::InitializeSaveChecks(); + Rando::Logic::InitializeSaveEntrances(); + + if (CVarGetInteger("gRandoSettings.ManualSeedEntry", 0)) { Rando::Logic::GenerateShuffleList(); + } else if (CVarGetInteger("gRandoSettings.UseExistingLog", 0)) { + std::string fileName = std::to_string(selectedFileNum) + ".json"; + bool logExists = SpoilerExistsForFileNum(fileName); + if (!logExists) { + Rando::Logic::GenerateShuffleList(); + } else { + nlohmann::json loadedSpoiler = Rando::Spoiler::LoadFromFile(fileName); + Rando::Logic::shuffledPool = Rando::Spoiler::GenerateFromSpoilerLog(loadedSpoiler); + } } else { - nlohmann::json loadedSpoiler = Rando::Spoiler::LoadFromFile(fileName); - Rando::Logic::shuffledPool = Rando::Spoiler::GenerateFromSpoilerLog(loadedSpoiler); + Rando::Logic::GenerateShuffleList(); } - Rando::CheckTracker::SetCheckTrackerList(); - } - // TODO: Inject Save File with spoiler data - // gSaveBuffer.files[ev->fileNum]->shipSaveData.randoSaveData.isRando = true; + for (auto& pool : Rando::Logic::shuffledPool) { + RandoSaveCheck randoSaveCheck; + randoSaveCheck.randoItemId = pool.randoItemId; + randoSaveCheck.randoAct = pool.randoAct; + randoSaveCheck.obtained = pool.obtained; + randoSaveCheck.skipped = pool.skipped; - // bcopy(&gSaveBuffer.files[ev->fileNum][0], &gSaveBuffer.files[ev->fileNum][1], - // sizeof(gSaveBuffer.files[ev->fileNum][1])); + RANDO_SAVE_CHECKS(selectedFileNum)[pool.randoCheckId] = randoSaveCheck; + } - // write_eeprom_data(&gSaveBuffer.menuData[ev->fileNum], sizeof(gSaveBuffer.menuData[ev->fileNum])); + if (!Rando::Logic::shuffledEntrances.empty()) { + for (auto& entrance : Rando::Logic::shuffledEntrances) { + RandoSaveEntrance randoSaveEntrance; + randoSaveEntrance.randoEntranceId = entrance.randoEntranceId; + randoSaveEntrance.destinationId = entrance.destinationId; + randoSaveEntrance.randoEntranceType = entrance.randoEntranceType; + randoSaveEntrance.deathWarpId = entrance.deathWarpId; + randoSaveEntrance.deathArea = entrance.deathArea; + + RANDO_SAVE_ENTRANCES(selectedFileNum)[entrance.randoEntranceId] = randoSaveEntrance; + } + } + + Notification::Emit( + { .message = "Spoiler written to Save File.", .messageColor = ImVec4(0, 0.85f, 0.3f, 1) }); + save_file_do_save(selectedFileNum); + } else { + Rando::Logic::shuffledPool.clear(); + for (size_t i = 0; i < RC_MAX; i++) { + RandoSaveCheck randoSaveCheck = RANDO_SAVE_CHECKS(selectedFileNum)[i]; + LevelShuffleEntry entry; + entry.randoCheckId = (RandoCheckId)i; + entry.randoItemId = randoSaveCheck.randoItemId; + entry.randoAct = randoSaveCheck.randoAct; + entry.obtained = randoSaveCheck.obtained; + entry.skipped = randoSaveCheck.skipped; + Rando::Logic::shuffledPool.push_back(entry); + } + + Rando::Logic::shuffledEntrances.clear(); + for (size_t e = 0; e < RE_MAX; e++) { + RandoSaveEntrance randoSaveEntrance = RANDO_SAVE_ENTRANCES(selectedFileNum)[e]; + Rando::Logic::shuffledEntrances.push_back(randoSaveEntrance); + } + } }); } \ No newline at end of file diff --git a/src/port/Rando/MiscBehavior/OnFileSave.cpp b/src/port/Rando/MiscBehavior/OnFileSave.cpp index 55b1a6644..32fb3457e 100644 --- a/src/port/Rando/MiscBehavior/OnFileSave.cpp +++ b/src/port/Rando/MiscBehavior/OnFileSave.cpp @@ -1,5 +1,9 @@ #include "MiscBehavior.h" +#include "port/Rando/Logic/Logic.h" +#include "port/Rando/Spoiler/Spoiler.h" +#include "port/ui/Notification.h" + void Rando::MiscBehavior::OnFileSave() { REGISTER_LISTENER(OnGameFileSave, EVENT_PRIORITY_NORMAL, [](IEvent* event) { OnGameFileSave* ev = (OnGameFileSave*)event; @@ -7,6 +11,15 @@ void Rando::MiscBehavior::OnFileSave() { return; } + for (auto& pool : Rando::Logic::shuffledPool) { + RandoSaveCheck randoSaveCheck; + randoSaveCheck.randoItemId = pool.randoItemId; + randoSaveCheck.randoAct = pool.randoAct; + randoSaveCheck.obtained = pool.obtained; + + RANDO_SAVE_CHECKS(selectedFileNum)[pool.randoCheckId] = randoSaveCheck; + } + // TODO: Inject Save File with spoiler data // gSaveBuffer.files[ev->fileNum]->shipSaveData.randoSaveData.isRando = true; diff --git a/src/port/Rando/ObjectBehavior/BlueCoinSwitch.cpp b/src/port/Rando/ObjectBehavior/BlueCoinSwitch.cpp index dc240853c..be9fc663b 100644 --- a/src/port/Rando/ObjectBehavior/BlueCoinSwitch.cpp +++ b/src/port/Rando/ObjectBehavior/BlueCoinSwitch.cpp @@ -31,8 +31,8 @@ static struct ObjectHitbox randoCollectStarHitbox = { void Rando::ObjectBehavior::ModifyBlueCoinSwitchBehavior() { for (auto& [randoCheckId, object] : spawnedRandoObjects) { if (Rando::Logic::IsBlueSwitchActivated(randoCheckId)) { - object->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; object->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; + object->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; object->oIntangibleTimer = 0; if (object->behavior == bhvRedCoin) { diff --git a/src/port/Rando/ObjectBehavior/ObjectBehavior.cpp b/src/port/Rando/ObjectBehavior/ObjectBehavior.cpp index db1a49f6b..34367e8f8 100644 --- a/src/port/Rando/ObjectBehavior/ObjectBehavior.cpp +++ b/src/port/Rando/ObjectBehavior/ObjectBehavior.cpp @@ -11,11 +11,11 @@ extern "C" { extern MarioState* gMarioState; } -static bool isInitialized = false; +extern std::map spawnedRandoObjects; void LogOutSpawns(std::string type, int16_t model, int16_t posX, int16_t posY, int16_t posZ) { if (model != MODEL_STAR && model != MODEL_RED_COIN && model != MODEL_RED_COIN_NO_SHADOW && - model != MODEL_BLUE_COIN && model != MODEL_BLUE_COIN_NO_SHADOW) { + model != MODEL_BLUE_COIN && model != MODEL_BLUE_COIN_NO_SHADOW && model != MODEL_BOWSER_KEY) { return; } std::string locationStr = std::to_string(posX) + ", " + std::to_string(posY) + ", " + std::to_string(posZ); @@ -72,7 +72,6 @@ void Rando::ObjectBehavior::Init() { if (!IS_RANDO(selectedFileNum)) { return; } - LogOutSpawns("Object", (int16_t)ev->model, ev->posX, ev->posY, ev->posZ); if (ev->model == MODEL_EXCLAMATION_BOX) { return; } @@ -88,16 +87,56 @@ void Rando::ObjectBehavior::Init() { return; } - LogOutSpawns("Star", MODEL_STAR, ev->posX, ev->posY, ev->posZ); ModifySpawnedObject(&event->cancelled, ev->posX, ev->posY, ev->posZ, NULL); }); + REGISTER_LISTENER(SpawnCoinStar, EVENT_PRIORITY_NORMAL, [](IEvent* event) { + SpawnCoinStar* ev = (SpawnCoinStar*)event; + LogOutSpawns("?", MODEL_STAR, ev->posX, ev->posY, ev->posZ); + if (!IS_RANDO(selectedFileNum)) { + return; + } + + Rando::StaticData::RandoStaticCheck randoStaticCheck; + RandoCheckId randoCheckId = RC_UNKNOWN; + + if (gCurrLevelNum == LEVEL_CASTLE) { + randoCheckId = Rando::StaticData::GetCheckByLocation(ev->posX, ev->posY, ev->posZ); + if (randoCheckId == RC_UNKNOWN) { + randoStaticCheck = Rando::StaticData::GetShuffledRandoStaticCheck( + 0, 0, RANDO_SAVE_CHECKS(selectedFileNum)[RC_CASTLE_STAR_04_MIPS_FIRST].obtained); + } + } else { + randoStaticCheck = Rando::StaticData::GetShuffledRandoStaticCheck(0, 0, 0); + } + + if (!Rando::Logic::IsCheckShuffled(randoStaticCheck.randoCheckId) || + randoStaticCheck.randoCheckId == RC_UNKNOWN || randoStaticCheck.randoItemId == RI_UNKNOWN) { + return; + } + + int32_t modelId = Rando::StaticData::GetModelByRandoItem(randoStaticCheck.randoItemId); + const BehaviorScript* behavior = modelId == MODEL_BLUE_COIN ? bhvHiddenBlueCoin + : modelId == MODEL_STAR ? bhvSpawnedStarNoLevelExit + : Rando::StaticData::GetBehaviorByModel(modelId); + + if (randoStaticCheck.posX == 0 && randoStaticCheck.posY == 0 && randoStaticCheck.posZ == 0) { + randoStaticCheck.posX = ev->posX; + randoStaticCheck.posY = ev->posY; + randoStaticCheck.posZ = ev->posZ; + } + + CustomItem::SpawnObject(modelId, behavior, randoStaticCheck.posX, randoStaticCheck.posY, randoStaticCheck.posZ, + NULL, randoStaticCheck.randoCheckId, randoStaticCheck.actData); + + event->cancelled = true; + }); + REGISTER_LISTENER(ModifyDefaultStar, EVENT_PRIORITY_NORMAL, [](IEvent* event) { ModifyDefaultStar* ev = (ModifyDefaultStar*)event; if (!IS_RANDO(selectedFileNum)) { return; } - LogOutSpawns("Default Star", MODEL_STAR, ev->posX, ev->posY, ev->posZ); ModifySpawnedObject(&event->cancelled, ev->posX, ev->posY, ev->posZ, ev->param); }); @@ -120,6 +159,7 @@ void Rando::ObjectBehavior::Init() { Rando::ObjectBehavior::ModifyRedCoinBehavior(&event->cancelled, ev->object); break; case MODEL_STAR: + case MODEL_TRANSPARENT_STAR: Rando::ObjectBehavior::ModifyStarBehavior(&event->cancelled, ev->object); break; default: @@ -128,34 +168,13 @@ void Rando::ObjectBehavior::Init() { } }); - REGISTER_LISTENER(LevelScriptExecute, EVENT_PRIORITY_NORMAL, [](IEvent* event) { - LevelScriptExecute* ev = (LevelScriptExecute*)event; - bool isLevelInitializing = false; - - switch (ev->command) { - case 17: - isLevelInitializing = true; - break; - case 18: - if (isLevelInitializing) { - CustomItem::redCoinsCollected = 0; - CustomItem::ClearSpawnedObjects(); - isLevelInitializing = false; - } - break; - default: - break; - } - - if (isInitialized || ev->command != 36) { + REGISTER_LISTENER(ModifyObjectVisibility, EVENT_PRIORITY_NORMAL, [](IEvent* event) { + ModifyObjectVisibility* ev = (ModifyObjectVisibility*)event; + if (!IS_RANDO(selectedFileNum)) { return; } - - if (!isInitialized) { - LOAD_MODEL_FROM_GEO(MODEL_STAR, star_geo); - LOAD_MODEL_FROM_GEO(MODEL_RED_COIN, red_coin_geo); - LOAD_MODEL_FROM_GEO(MODEL_BLUE_COIN, blue_coin_geo); - isInitialized = true; + if (ev->object->unused1 != RC_UNKNOWN) { + event->cancelled = true; } }); } diff --git a/src/port/Rando/ObjectBehavior/RedCoin.cpp b/src/port/Rando/ObjectBehavior/RedCoin.cpp index 5ed78b806..e228ec0ea 100644 --- a/src/port/Rando/ObjectBehavior/RedCoin.cpp +++ b/src/port/Rando/ObjectBehavior/RedCoin.cpp @@ -7,8 +7,8 @@ extern std::map spawnedRandoObjects; void Rando::ObjectBehavior::ModifyRedCoinBehavior(bool* shouldCancel, struct Object* obj) { if (obj->unused1 != RC_UNKNOWN) { if (Rando::Logic::IsBlueSwitchActivated((RandoCheckId)obj->unused1)) { - spawnedRandoObjects.at((RandoCheckId)obj->unused1)->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; - spawnedRandoObjects.at((RandoCheckId)obj->unused1)->oIntangibleTimer = -1; + obj->header.gfx.node.flags = spawnedRandoObjects.at((RandoCheckId)obj->unused1)->header.gfx.node.flags; + obj->oIntangibleTimer = -1; *(shouldCancel) = true; return; } diff --git a/src/port/Rando/ObjectBehavior/Star.cpp b/src/port/Rando/ObjectBehavior/Star.cpp index 283c0500e..e2a59e99a 100644 --- a/src/port/Rando/ObjectBehavior/Star.cpp +++ b/src/port/Rando/ObjectBehavior/Star.cpp @@ -11,10 +11,10 @@ extern std::map spawnedRandoObjects; void Rando::ObjectBehavior::ModifyStarBehavior(bool* shouldCancel, struct Object* obj) { if (obj->unused1 != RC_UNKNOWN) { if (Rando::Logic::IsBlueSwitchActivated((RandoCheckId)obj->unused1)) { - spawnedRandoObjects.at((RandoCheckId)obj->unused1)->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; - spawnedRandoObjects.at((RandoCheckId)obj->unused1)->oIntangibleTimer = -1; - if (!CVarGetInteger("gEnhancements.StarNoExit", 0)) { - spawnedRandoObjects.at((RandoCheckId)obj->unused1)->oInteractionSubtype |= INT_SUBTYPE_NO_EXIT; + obj->header.gfx.node.flags = spawnedRandoObjects.at((RandoCheckId)obj->unused1)->header.gfx.node.flags; + obj->oIntangibleTimer = -1; + if (CVarGetInteger("gEnhancements.StarNoExit", 0)) { + obj->oInteractionSubtype |= INT_SUBTYPE_NO_EXIT; } *(shouldCancel) = true; return; diff --git a/src/port/Rando/Rando.cpp b/src/port/Rando/Rando.cpp index ba323faac..1a24c4cd8 100644 --- a/src/port/Rando/Rando.cpp +++ b/src/port/Rando/Rando.cpp @@ -1,34 +1,28 @@ #include "Rando.h" #include -#include "port/mods/PortEnhancements.h" -// #include "2s2h/GameInteractor/GameInteractor.h" #include "ObjectBehavior/ObjectBehavior.h" #include "MiscBehavior/MiscBehavior.h" -// #include "Rando/Spoiler/Spoiler.h" #include "port/Rando/CheckTracker/CheckTracker.h" #include "port/ShipInit.hpp" -int16_t selectedFileNum = 0; +#include +#include +namespace fs = std::filesystem; -// When a save is loaded, we want to unregister all hooks and re-register them if it's a rando save -// void OnSaveLoadHandler(s16 fileNum) { -// Rando::MiscBehavior::OnFileLoad(); -// Rando::ActorBehavior::OnFileLoad(); -// Rando::CheckTracker::OnFileLoad(); -// -// // Re-initalizes enhancements that are effected by the save being rando or not -// ShipInit::Init("IS_RANDO"); -// } +int16_t selectedFileNum = 0; // Entry point for the module, run once on game boot void Rando::Init() { + fs::path dir("randomizer"); + if (!fs::exists(dir)) { + fs::create_directory(dir); + } + // Rando::Spoiler::RefreshOptions(); Rando::MiscBehavior::Init(); Rando::ObjectBehavior::Init(); Rando::CheckTracker::Init(); // Ship::Context::GetInstance()->GetFileDropMgr()->RegisterDropHandler(Rando::Spoiler::HandleFileDropped); - - // GameInteractor::Instance->RegisterGameHook(OnSaveLoadHandler); } // RandoCheckId Rando::FindItemPlacement(RandoItemId randoItemId) { diff --git a/src/port/Rando/Rando.h b/src/port/Rando/Rando.h index c4dd5f893..bc6e07680 100644 --- a/src/port/Rando/Rando.h +++ b/src/port/Rando/Rando.h @@ -20,6 +20,7 @@ extern struct SaveBuffer gSaveBuffer; #define IS_RANDO(fileNum) (gSaveBuffer.files[fileNum]->shipSaveData.saveType == SAVETYPE_RANDO) #define RANDO_SAVE_CHECKS(fileNum) gSaveBuffer.files[fileNum]->shipSaveData.randoSaveData.randoSaveChecks +#define RANDO_SAVE_ENTRANCES(fileNum) gSaveBuffer.files[fileNum]->shipSaveData.randoSaveData.randoSaveEntrances #define RANDO_SAVE_OPTIONS(fileNum) gSaveBuffer.files[fileNum]->shipSaveData.randoSaveData.randoSaveOptions // #define RANDO_EVENTS gSaveContext.save.shipSaveInfo.rando.randoEvents // #define RANDO_STARTING_ITEMS gSaveContext.save.shipSaveInfo.rando.randoStartingItems diff --git a/src/port/Rando/Spoiler/File.cpp b/src/port/Rando/Spoiler/File.cpp index ae00e234c..293c4b57e 100644 --- a/src/port/Rando/Spoiler/File.cpp +++ b/src/port/Rando/Spoiler/File.cpp @@ -1,7 +1,10 @@ #include "Spoiler.h" +#include "port/ui/Notification.h" #include +#include const std::string appShortName = "sm64"; +namespace fs = std::filesystem; namespace Rando { @@ -18,13 +21,20 @@ void SaveToFile(const std::string& fileName, nlohmann::json spoiler) { } nlohmann::json LoadFromFile(const std::string& fileName) { + nlohmann::json spoiler; std::string spoilerFilePath = Ship::Context::GetPathRelativeToAppDirectory("randomizer/" + fileName, appShortName); std::ifstream fileStream(spoilerFilePath); + + if (!fs::exists(spoilerFilePath)) { + return spoiler; + } + if (!fileStream.is_open()) { - throw std::runtime_error("Failed to open spoiler file"); + Notification::Emit( + { .message = "Error: Failed to open spoiler file.", .messageColor = ImVec4(0.85f, 0.3f, 0, 1) }); + return spoiler; } - nlohmann::json spoiler; try { fileStream >> spoiler; } catch (nlohmann::json::exception& e) { diff --git a/src/port/Rando/Spoiler/Generate.cpp b/src/port/Rando/Spoiler/Generate.cpp index 162a73435..2401f11f2 100644 --- a/src/port/Rando/Spoiler/Generate.cpp +++ b/src/port/Rando/Spoiler/Generate.cpp @@ -11,8 +11,12 @@ nlohmann::json GenerateFromPoolGeneration(std::vector& shuffl nlohmann::json spoiler; spoiler["type"] = "GHOSTSHIP_RANDO_SPOILER"; spoiler["fileNum"] = std::to_string(selectedFileNum); - // spoiler["commitHash"] = gSaveContext.save.shipSaveInfo.commitHash; - // spoiler["finalSeed"] = gSaveContext.save.shipSaveInfo.rando.finalSeed; + if (CVarGetInteger("gRandoSettings.ManualSeedEntry", 0)) { + spoiler["seed"] = seedString; + } else { + spoiler["seed"] = ""; + } + spoiler["finalSeed"] = gSaveBuffer.files[selectedFileNum][0].shipSaveData.randoSaveData.finalSeed; // TODO: Add once JSON Saves are in. // spoiler["options"] = nlohmann::json::object(); @@ -67,6 +71,7 @@ std::vector GenerateFromSpoilerLog(nlohmann::json spoiler) { levelEntry.randoItemId = itemId; } } + levelEntry.obtained = false; spoilerChecks.push_back(levelEntry); } } diff --git a/src/port/Rando/Spoiler/Spoiler.h b/src/port/Rando/Spoiler/Spoiler.h index 543bee3cb..53e1378fa 100644 --- a/src/port/Rando/Spoiler/Spoiler.h +++ b/src/port/Rando/Spoiler/Spoiler.h @@ -10,8 +10,6 @@ namespace Rando { namespace Spoiler { -// extern std::vector spoilerOptions; -// void RefreshOptions(); nlohmann::json GenerateFromPoolGeneration(std::vector& shuffledPool); std::vector GenerateFromSpoilerLog(nlohmann::json spoiler); void SaveToFile(const std::string& fileName, nlohmann::json spoiler); diff --git a/src/port/Rando/StaticData/Checks.cpp b/src/port/Rando/StaticData/Checks.cpp index 69a37a0ef..948baba41 100644 --- a/src/port/Rando/StaticData/Checks.cpp +++ b/src/port/Rando/StaticData/Checks.cpp @@ -22,365 +22,370 @@ std::array CheckNames = std::array(); // clang-format off std::map Checks = { - RC(RC_UNKNOWN, RCTYPE_UNKNOWN, LEVEL_UNKNOWN_1, RA_ACT_NONE, RI_UNKNOWN, 0, 0, 0), - RC(RC_AQUA_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, -2400, -4607, -2400), - RC(RC_AQUA_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, -2400, -4607, 2400), - RC(RC_AQUA_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, 2400, -4607, 2400), - RC(RC_AQUA_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, 2400, -4607, -2400), - RC(RC_AQUA_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, 0, -2200, -1500), - RC(RC_AQUA_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, -1500, -2200, 0), - RC(RC_AQUA_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, 0, -2200, 1500), - RC(RC_AQUA_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, 1500, -2200, 0), - RC(RC_AQUA_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_SA, RA_ACT_01, RI_STAR, 0, -4250, 0), - RC(RC_BBH_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_BBH, RA_ACT_NONE, RI_COIN_BLUE, 1400, 1840, 1100), - RC(RC_BBH_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_BBH, RA_ACT_NONE, RI_COIN_BLUE, 1700, 1840, 1100), - RC(RC_BBH_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_BBH, RA_ACT_NONE, RI_COIN_BLUE, 0, 1840, 1100), - RC(RC_BBH_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_BBH, RA_ACT_NONE, RI_COIN_BLUE, -400, 1840, 1100), - RC(RC_BBH_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, -1164, 0, 2325), - RC(RC_BBH_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, 2540, 820, 2174), - RC(RC_BBH_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, 2600, 815, -380), - RC(RC_BBH_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, 2950, 815, -1250), - RC(RC_BBH_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, -1500, 550, 960), - RC(RC_BBH_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, -420, 550, 60), - RC(RC_BBH_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, 2856, 50, 2144), - RC(RC_BBH_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, 50, 900, -1400), - RC(RC_BBH_STAR_01_GHOST_HUNT, RCTYPE_STAR_DEFAULT, LEVEL_BBH, RA_ACT_01, RI_STAR, 980, 1100, 250), - RC(RC_BBH_STAR_02_MERRY_GO_ROUND, RCTYPE_STAR_DEFAULT, LEVEL_BBH, RA_ACT_02, RI_STAR, -1600, -2100, 205), - RC(RC_BBH_STAR_03_HAUNTED_BOOKS, RCTYPE_STAR_DEFAULT, LEVEL_BBH, RA_ACT_03, RI_STAR, -2030, 1350, 1940), - RC(RC_BBH_STAR_04_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_BBH, RA_ACT_04, RI_STAR, -204, 1100, 1576), - RC(RC_BBH_STAR_05_BALCONY, RCTYPE_STAR_DEFAULT, LEVEL_BBH, RA_ACT_05, RI_STAR, 700, 3200, 1900), - RC(RC_BBH_STAR_06_SECRET_ROOM, RCTYPE_STAR_DEFAULT, LEVEL_BBH, RA_ACT_06, RI_STAR, 1370, 2000, -320), - RC(RC_BBH_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_BBH, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_BITDW_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -3100, -2900, 4520), - RC(RC_BITDW_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -4740, 1050, -2130), - RC(RC_BITDW_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -3120, 1160, -2570), - RC(RC_BITDW_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -4613, 1220, -427), - RC(RC_BITDW_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -1000, 1933, 466), - RC(RC_BITDW_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -6475, 125, 890), - RC(RC_BITDW_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -7810, -3100, 4900), - RC(RC_BITDW_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, 1500, 1066, -166), - RC(RC_BITDW_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_BITDW, RA_ACT_01, RI_STAR, 7180, 3000, 0), - RC(RC_BITFS_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, 2888, -1375, 310), - RC(RC_BITFS_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, 1230, -1900, -1747), - RC(RC_BITFS_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, 5800, -2000, 0), - RC(RC_BITFS_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, -1846, 1600, -476), - RC(RC_BITFS_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, -4210, 2615, 280), - RC(RC_BITFS_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, -1390, 3575, -420), - RC(RC_BITFS_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, 3065, 4610, 92), - RC(RC_BITFS_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, 4330, -790, -50), - RC(RC_BITFS_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_BITFS, RA_ACT_01, RI_STAR, 1200, 5700, 160), - RC(RC_BITS_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, 1590, -1120, -2055), - RC(RC_BITS_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, -2960, -3360, 0), - RC(RC_BITS_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, -298, -2600, -1600), - RC(RC_BITS_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, 7618, 4850, -1909), - RC(RC_BITS_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, -136, 3109, -909), - RC(RC_BITS_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, -3330, 4050, -900), - RC(RC_BITS_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, -4830, 2103, -1030), - RC(RC_BITS_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, 348, 5921, -4585), - RC(RC_BOB_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, -5500, 768, 2400), - RC(RC_BOB_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, -6500, 768, 2400), - RC(RC_BOB_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, 1135, 1920, -7161), - RC(RC_BOB_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, 250, 1000, 1920), - RC(RC_BOB_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, 1600, 980, 4440), - RC(RC_BOB_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, -2755, 0, -4083), - RC(RC_BOB_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, 4934, 1352, 861), - RC(RC_BOB_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, 4100, 3672, 1629), - RC(RC_BOB_STAR_01_KING_BOBOMB, RCTYPE_STAR_DEFAULT, LEVEL_BOB, RA_ACT_01, RI_STAR, 2000, 4500, -4500), - RC(RC_BOB_STAR_02_KOOPA_RACE, RCTYPE_STAR_DEFAULT, LEVEL_BOB, RA_ACT_02, RI_STAR, 3030, 4500, -4600), - RC(RC_BOB_STAR_03_ISLAND, RCTYPE_STAR_DEFAULT, LEVEL_BOB, RA_ACT_03, RI_STAR, 5540, 3350, 1200), - RC(RC_BOB_STAR_04_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_BOB, RA_ACT_04, RI_STAR, -6000, 1000, 2400), - RC(RC_BOB_STAR_05_WINGS, RCTYPE_STAR_DEFAULT, LEVEL_BOB, RA_ACT_05, RI_STAR, -6600, 1000, 1250), - RC(RC_BOB_STAR_06_CHAIN_CHOMP, RCTYPE_STAR_DEFAULT, LEVEL_BOB, RA_ACT_06, RI_STAR, 1550, 1200, 300), - RC(RC_BOB_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_BOB, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_CCM_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_CCM, RA_ACT_NONE, RI_COIN_BLUE, 3400, -640, -740), - RC(RC_CCM_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_CCM, RA_ACT_NONE, RI_COIN_BLUE, 3400, -320, -1740), - RC(RC_CCM_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_CCM, RA_ACT_NONE, RI_COIN_BLUE, -3160, 6267, -6120), - RC(RC_CCM_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, -488, 3100, -2305), - RC(RC_CCM_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, -3120, -1200, -2080), - RC(RC_CCM_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, 2940, -1400, 2630), - RC(RC_CCM_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, -600, -505, 1940), - RC(RC_CCM_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, -3814, -2112, 6427), - RC(RC_CCM_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, 2700, -4600, 1230), - RC(RC_CCM_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, 1990, -4040, 4950), - RC(RC_CCM_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, 4408, -4550, -1534), - RC(RC_CCM_STAR_01_SLIDING_AWAY, RCTYPE_STAR_DEFAULT, LEVEL_CCM, RA_ACT_NONE, RI_STAR, 2500, -4350, 5750), - RC(RC_CCM_STAR_02_LOST_PENGUIN, RCTYPE_STAR_DEFAULT, LEVEL_CCM, RA_ACT_NONE, RI_STAR, 3167, -4300, 5108), - RC(RC_CCM_STAR_03_PENGUIN_RACE, RCTYPE_STAR_DEFAULT, LEVEL_CCM, RA_ACT_NONE, RI_STAR, -7339, -5700, -6774), - RC(RC_CCM_STAR_04_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_CCM, RA_ACT_NONE, RI_STAR, 4200, -927, 400), - RC(RC_CCM_STAR_05_LOST_HEAD, RCTYPE_STAR_DEFAULT, LEVEL_CCM, RA_ACT_NONE, RI_STAR, -4700, -1024, 1890), - RC(RC_CCM_STAR_06_WALL_KICKS, RCTYPE_STAR_DEFAULT, LEVEL_CCM, RA_ACT_NONE, RI_STAR, -2000, -2200, -3000), - RC(RC_CCM_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_CCM, RA_ACT_NONE, RI_STAR, 0, 0, 0), - RC(RC_COTMC_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, 200, -291, -5600), - RC(RC_COTMC_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, 980, 260, -3430), - RC(RC_COTMC_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, -540, -352, -5940), - RC(RC_COTMC_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, -300, 450, -6240), - RC(RC_COTMC_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, -200, -400, -6680), - RC(RC_COTMC_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, 250, 450, -6400), - RC(RC_COTMC_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, 540, -361, -6340), - RC(RC_COTMC_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, 980, 260, -3810), - RC(RC_COTMC_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_COTMC, RA_ACT_01, RI_STAR, 0, -200, -7000), - RC(RC_DDD_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_DDD, RA_ACT_NONE, RI_COIN_BLUE, 1414, 1020, 622), - RC(RC_DDD_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_DDD, RA_ACT_NONE, RI_COIN_BLUE, 1814, 1020, 622), - RC(RC_DDD_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_DDD, RA_ACT_NONE, RI_COIN_BLUE, 1814, 1020, 1222), - RC(RC_DDD_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_DDD, RA_ACT_NONE, RI_COIN_BLUE, 1414, 1020, 1222), - RC(RC_DDD_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_DDD, RA_ACT_NONE, RI_COIN_BLUE, 1414, 1020, 922), - RC(RC_DDD_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_DDD, RA_ACT_NONE, RI_COIN_BLUE, 1814, 1020, 922), - RC(RC_DDD_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 4680, 1155, -2200), - RC(RC_DDD_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 3525, 1155, -2250), - RC(RC_DDD_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 3850, 1111, 3550), - RC(RC_DDD_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 4400, 1288, 3595), - RC(RC_DDD_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 2974, 1015, 3584), - RC(RC_DDD_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 5550, 1015, 3575), - RC(RC_DDD_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 3300, 1111, -1900), - RC(RC_DDD_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 2100, 1025, -1600), - RC(RC_DDD_STAR_01_BOWSERS_SUB, RCTYPE_STAR_DEFAULT, LEVEL_DDD, RA_ACT_01, RI_STAR, 3900, 850, -600), - RC(RC_DDD_STAR_02_CHESTS, RCTYPE_STAR_DEFAULT, LEVEL_DDD, RA_ACT_02, RI_STAR, -1900, -4000, -1400), - RC(RC_DDD_STAR_03_RED_COINS, RCTYPE_STAR_DEFAULT, LEVEL_DDD, RA_ACT_03, RI_STAR, 5513, 1200, 900), - RC(RC_DDD_STAR_04_JET_STREAM, RCTYPE_STAR_RED_COIN, LEVEL_DDD, RA_ACT_04, RI_STAR, 3400, -3200, -500), - RC(RC_DDD_STAR_05_MANTA_RAY, RCTYPE_STAR_DEFAULT, LEVEL_DDD, RA_ACT_05, RI_STAR, -3180, -3600, 120), - RC(RC_DDD_STAR_06_CAPS, RCTYPE_STAR_DEFAULT, LEVEL_DDD, RA_ACT_06, RI_STAR, 2030, -3700, -2780), - RC(RC_DDD_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_DDD, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_HMC_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 3945, -1023, -2918), - RC(RC_HMC_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 3800, -1023, -4009), - RC(RC_HMC_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 3036, -858, -4118), - RC(RC_HMC_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 2018, -921, -4154), - RC(RC_HMC_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 1836, -921, -4700), - RC(RC_HMC_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 1836, -721, -4700), - RC(RC_HMC_BLUE_COIN_07, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 1836, -521, -4700), - RC(RC_HMC_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 2140, 2048, 6540), - RC(RC_HMC_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 2900, 2048, 6500), - RC(RC_HMC_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 6400, 2090, 5320), - RC(RC_HMC_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 6820, 1024, 3300), - RC(RC_HMC_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 6260, 1124, 4960), - RC(RC_HMC_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 4080, 1124, 5760), - RC(RC_HMC_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 4640, 2250, 3980), - RC(RC_HMC_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 2960, 1124, 5140), - RC(RC_HMC_STAR_01_BEAST, RCTYPE_STAR_DEFAULT, LEVEL_HMC, RA_ACT_01, RI_STAR, -3600, -4000, 3600), - RC(RC_HMC_STAR_02_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_HMC, RA_ACT_02, RI_STAR, 4000, 300, 5000), - RC(RC_HMC_STAR_03_METAL_HEAD, RCTYPE_STAR_DEFAULT, LEVEL_HMC, RA_ACT_03, RI_STAR, 6200, -4400, 2300), - RC(RC_HMC_STAR_04_TOXIC_MAZE, RCTYPE_STAR_DEFAULT, LEVEL_HMC, RA_ACT_04, RI_STAR, -2100, 2100, -7550), - RC(RC_HMC_STAR_05_EMERGENCY_EXIT, RCTYPE_STAR_DEFAULT, LEVEL_HMC, RA_ACT_05, RI_STAR, -6500, 2700, -1600), - RC(RC_HMC_STAR_06_ROLLING_ROCKS, RCTYPE_STAR_DEFAULT, LEVEL_HMC, RA_ACT_06, RI_STAR, -5000, 3050, -6700), - RC(RC_HMC_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_HMC, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_JRB_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_JRB, RA_ACT_NONE, RI_COIN_BLUE, 5900, -2966, -7347), - RC(RC_JRB_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_JRB, RA_ACT_NONE, RI_COIN_BLUE, 5300, -2966, -7347), - RC(RC_JRB_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_JRB, RA_ACT_NONE, RI_COIN_BLUE, 4700, -2966, -7347), - RC(RC_JRB_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_JRB, RA_ACT_NONE, RI_COIN_BLUE, 4100, -2966, -7347), - RC(RC_JRB_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_JRB, RA_ACT_NONE, RI_COIN_BLUE, 3500, -2966, -7347), - RC(RC_JRB_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_JRB, RA_ACT_NONE, RI_COIN_BLUE, 2900, -2966, -7347), - RC(RC_JRB_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, 4900, 1800, 4700), - RC(RC_JRB_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, -2535, 2110, 6113), - RC(RC_JRB_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, -1845, -1003, 3518), - RC(RC_JRB_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, -546, 530, 5930), - RC(RC_JRB_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, 648, -490, 4256), - RC(RC_JRB_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, 3642, -5103, 3175), - RC(RC_JRB_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, 4400, 2200, 1300), - RC(RC_JRB_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, 5400, 2200, 1300), - RC(RC_JRB_STAR_01_SUNKEN_SHIP, RCTYPE_STAR_DEFAULT, LEVEL_JRB, RA_ACT_01, RI_STAR, 0, 1600, 3000), - RC(RC_JRB_STAR_02_EEL, RCTYPE_STAR_DEFAULT, LEVEL_JRB, RA_ACT_02, RI_STAR, 6833, -3654, 2230), - RC(RC_JRB_STAR_03_OCEAN_CAVE, RCTYPE_STAR_DEFAULT, LEVEL_JRB, RA_ACT_03, RI_STAR, -1800, -2500, -1700), - RC(RC_JRB_STAR_04_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_JRB, RA_ACT_04, RI_STAR, 4900, 2400, 800), - RC(RC_JRB_STAR_05_STONE_PILLAR, RCTYPE_STAR_DEFAULT, LEVEL_JRB, RA_ACT_05, RI_STAR, 1540, 2160, 2130), - RC(RC_JRB_STAR_06_JET_STREAM, RCTYPE_STAR_DEFAULT, LEVEL_JRB, RA_ACT_06, RI_STAR, 5000, -4800, 2500), - RC(RC_JRB_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_JRB, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_LLL_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -5800, 220, 250), - RC(RC_LLL_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -5100, 220, 250), - RC(RC_LLL_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -5100, 220, 1000), - RC(RC_LLL_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -4400, 220, 1000), - RC(RC_LLL_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -4400, 220, 1750), - RC(RC_LLL_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -5100, 220, 1750), - RC(RC_LLL_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -5800, 220, 1750), - RC(RC_LLL_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -5800, 220, 1000), - RC(RC_LLL_STAR_01_BIG_BULLY, RCTYPE_STAR_DEFAULT, LEVEL_LLL, RA_ACT_01, RI_STAR, 0, 950, -6800), - RC(RC_LLL_STAR_02_LITTLE_BULLIES, RCTYPE_STAR_DEFAULT, LEVEL_LLL, RA_ACT_02, RI_STAR, 3700, 600, -5500), - RC(RC_LLL_STAR_03_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_LLL, RA_ACT_03, RI_STAR, -4400, 350, 250), - RC(RC_LLL_STAR_04_LOG_ROLLING, RCTYPE_STAR_DEFAULT, LEVEL_LLL, RA_ACT_04, RI_STAR, 3100, 400, 7900), - RC(RC_LLL_STAR_05_VOLCANO_FOOT, RCTYPE_STAR_DEFAULT, LEVEL_LLL, RA_ACT_05, RI_STAR, 2523, 3850, -901), - RC(RC_LLL_STAR_06_VOLCANO_ELEVATOR, RCTYPE_STAR_DEFAULT, LEVEL_LLL, RA_ACT_06, RI_STAR, 1800, 3400, 1450), - RC(RC_LLL_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_LLL, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_PSS_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_PSS, RA_ACT_NONE, RI_COIN_BLUE, 780, 5613, -5600), - RC(RC_PSS_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_PSS, RA_ACT_NONE, RI_COIN_BLUE, 2360, 5979, -5600), - RC(RC_PSS_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_PSS, RA_ACT_NONE, RI_COIN_BLUE, -809, 5245, -5600), - RC(RC_PSS_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_PSS, RA_ACT_NONE, RI_COIN_BLUE, -2409, 4877, -5600), - RC(RC_PSS_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_PSS, RA_ACT_NONE, RI_COIN_BLUE, -4445, 4490, -5536), - RC(RC_PSS_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_PSS, RA_ACT_NONE, RI_COIN_BLUE, -6263, 4150, -4009), - RC(RC_PSS_STAR_01_BOX, RCTYPE_STAR_DEFAULT, LEVEL_PSS, RA_ACT_01, RI_STAR, -6385, -4200, 5770), - RC(RC_PSS_STAR_02_SPEED_TIME, RCTYPE_STAR_DEFAULT, LEVEL_PSS, RA_ACT_02, RI_STAR, -6358, -4300, 4700), - RC(RC_RR_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_RR, RA_ACT_NONE, RI_COIN_BLUE, -6500, 1700, 0), - RC(RC_RR_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_RR, RA_ACT_NONE, RI_COIN_BLUE, -6200, 1700, 0), - RC(RC_RR_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_RR, RA_ACT_NONE, RI_COIN_BLUE, -5900, 1700, 0), - RC(RC_RR_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_RR, RA_ACT_NONE, RI_COIN_BLUE, -5600, 1700, 0), - RC(RC_RR_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_RR, RA_ACT_NONE, RI_COIN_BLUE, -6750, -1495, 0), - RC(RC_RR_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_RR, RA_ACT_NONE, RI_COIN_BLUE, -5300, 1700, 0), - RC(RC_RR_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -5850, -1000, -50), - RC(RC_RR_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -2950, -190, -50), - RC(RC_RR_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -5850, 1150, 50), - RC(RC_RR_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -5050, 800, -50), - RC(RC_RR_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -4550, 650, -50), - RC(RC_RR_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -5300, 200, -50), - RC(RC_RR_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -3550, 1000, -50), - RC(RC_RR_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -5800, -400, -50), - RC(RC_RR_STAR_01_CRUISER, RCTYPE_STAR_DEFAULT, LEVEL_RR, RA_ACT_01, RI_STAR, 1450, 3400, -2352), - RC(RC_RR_STAR_02_BIG_HOUSE, RCTYPE_STAR_DEFAULT, LEVEL_RR, RA_ACT_02, RI_STAR, -4200, 6700, -4450), - RC(RC_RR_STAR_03_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_RR, RA_ACT_03, RI_STAR, -5150, -1400, 0), - RC(RC_RR_STAR_04_SWINGIN_BREEZE, RCTYPE_STAR_DEFAULT, LEVEL_RR, RA_ACT_04, RI_STAR, -5850, -700, 4950), - RC(RC_RR_STAR_05_TRIANGLES, RCTYPE_STAR_DEFAULT, LEVEL_RR, RA_ACT_05, RI_STAR, 3700, -400, 6600), - RC(RC_RR_STAR_06_OVER_THE_RAINBOW, RCTYPE_STAR_DEFAULT, LEVEL_RR, RA_ACT_06, RI_STAR, 5000, 4100, 4440), - RC(RC_RR_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_RR, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_SL_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, 4750, 1061, -5230), - RC(RC_SL_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, 416, 1050, -4522), - RC(RC_SL_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, -6560, 2040, -5080), - RC(RC_SL_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, -6760, 2040, -1360), - RC(RC_SL_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, -6880, 1857, 1000), - RC(RC_SL_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, -4211, 1092, -4723), - RC(RC_SL_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, -6271, 1390, 4764), - RC(RC_SL_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, -529, 1050, -5329), - RC(RC_SL_STAR_01_BIG_HEAD, RCTYPE_STAR_DEFAULT, LEVEL_SL, RA_ACT_01, RI_STAR, 700, 4500, 690), - RC(RC_SL_STAR_02_BULLY, RCTYPE_STAR_DEFAULT, LEVEL_SL, RA_ACT_02, RI_STAR, 130, 1600, -4335), - RC(RC_SL_STAR_03_DEEP_FREEZE, RCTYPE_STAR_DEFAULT, LEVEL_SL, RA_ACT_03, RI_STAR, 4350, 1350, 4350), - RC(RC_SL_STAR_04_FREEZING_POND, RCTYPE_STAR_DEFAULT, LEVEL_SL, RA_ACT_04, RI_STAR, -4700, 1300, 5850), - RC(RC_SL_STAR_05_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_SL, RA_ACT_05, RI_STAR, 5000, 1200, 0), - RC(RC_SL_STAR_06_IGLOO, RCTYPE_STAR_DEFAULT, LEVEL_SL, RA_ACT_06, RI_STAR, 0, 500, 1000), - RC(RC_SL_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_SL, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_SSL_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_SSL, RA_ACT_NONE, RI_COIN_BLUE, 0, 0, 2381), - RC(RC_SSL_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_SSL, RA_ACT_NONE, RI_COIN_BLUE, 0, 100, 2381), - RC(RC_SSL_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_SSL, RA_ACT_NONE, RI_COIN_BLUE, 0, 200, 2381), - RC(RC_SSL_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, -4500, 1500, -3500), - RC(RC_SSL_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, -4500, 1500, 1500), - RC(RC_SSL_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, 500, 1500, -3500), - RC(RC_SSL_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, 500, 1500, 1500), - RC(RC_SSL_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, 3380, 10, -4900), - RC(RC_SSL_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, -5270, -170, -6780), - RC(RC_SSL_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, -7450, 10, 7560), - RC(RC_SSL_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, 5900, 70, 2311), - RC(RC_SSL_STAR_01_BIG_BIRD, RCTYPE_STAR_DEFAULT, LEVEL_SSL, RA_ACT_01, RI_STAR, -5550, 300, -930), - RC(RC_SSL_STAR_02_PYRAMID_TOP, RCTYPE_STAR_DEFAULT, LEVEL_SSL, RA_ACT_02, RI_STAR, -2050, 1200, -580), - RC(RC_SSL_STAR_03_PYRAMID_INSIDE, RCTYPE_STAR_DEFAULT, LEVEL_SSL, RA_ACT_03, RI_STAR, 500, 5050, -500), - RC(RC_SSL_STAR_04_FOUR_PILLARS, RCTYPE_STAR_DEFAULT, LEVEL_SSL, RA_ACT_04, RI_STAR, 0, -900, -3700), - RC(RC_SSL_STAR_05_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_SSL, RA_ACT_05, RI_STAR, 6000, 800, 3500), - RC(RC_SSL_STAR_06_PYRAMID_PUZZLE, RCTYPE_STAR_DEFAULT, LEVEL_SSL, RA_ACT_06, RI_STAR, 900, 1400, 2350), - RC(RC_SSL_STAR_07_100_COIN, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_THI_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_THI, RA_ACT_NONE, RI_COIN_BLUE, -540, 1500, 1240), - RC(RC_THI_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_THI, RA_ACT_NONE, RI_COIN_BLUE, -840, 1500, 1440), - RC(RC_THI_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, 1038, 1122, 998), - RC(RC_THI_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, 1590, 1078, 160), - RC(RC_THI_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, 1038, 1112, -823), - RC(RC_THI_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, -238, 1122, -1319), - RC(RC_THI_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, -523, 824, -500), - RC(RC_THI_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, -1838, 1068, 338), - RC(RC_THI_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, -240, 1500, 1040), - RC(RC_THI_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, -1914, 1360, -1909), - RC(RC_THI_STAR_01_PIRANHA, RCTYPE_STAR_DEFAULT, LEVEL_THI, RA_ACT_01, RI_STAR, -6300, -1850, -6300), - RC(RC_THI_STAR_02_ISLAND_TOP, RCTYPE_STAR_DEFAULT, LEVEL_THI, RA_ACT_02, RI_STAR, 2600, 3500, -2400), - RC(RC_THI_STAR_03_KOOPA_REMATCH, RCTYPE_STAR_DEFAULT, LEVEL_THI, RA_ACT_03, RI_STAR, 7100, -1300, -6000), - RC(RC_THI_STAR_04_SECRETS, RCTYPE_STAR_DEFAULT, LEVEL_THI, RA_ACT_04, RI_STAR, 0, -700, -4500), - RC(RC_THI_STAR_05_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_THI, RA_ACT_05, RI_STAR, -1800, 800, -1500), - RC(RC_THI_STAR_06_WIGGLER, RCTYPE_STAR_DEFAULT, LEVEL_THI, RA_ACT_06, RI_STAR, 0, 2048, 0), - RC(RC_THI_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_THI, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_TOTWC_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, -100, 800, -4000), - RC(RC_TOTWC_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, -3500, 0, 100), - RC(RC_TOTWC_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, 100, -800, 3000), - RC(RC_TOTWC_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, 4500, 1600, -100), - RC(RC_TOTWC_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, 100, 800, -4000), - RC(RC_TOTWC_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, -3500, 0, -100), - RC(RC_TOTWC_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, -100, -800, 3000), - RC(RC_TOTWC_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, 4500, 1600, 100), - RC(RC_TOTWC_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_TOTWC, RA_ACT_01, RI_STAR, 800, -1700, 0), - RC(RC_TTC_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, 0, 20, -1400), - RC(RC_TTC_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, -250, 20, -1700), - RC(RC_TTC_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, 250, 20, -1700), - RC(RC_TTC_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, 0, 20, -2000), - RC(RC_TTC_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, 500, 20, -2000), - RC(RC_TTC_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, -500, 20, -2000), - RC(RC_TTC_BLUE_COIN_07, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, 0, 20, -1700), - RC(RC_TTC_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 800, -4400, 1900), - RC(RC_TTC_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 800, -3700, 1900), - RC(RC_TTC_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 800, -3000, 1900), - RC(RC_TTC_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 1780, -3300, 1000), - RC(RC_TTC_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 1388, -3300, 1428), - RC(RC_TTC_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 200, -3000, 2000), - RC(RC_TTC_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 200, -3700, 2000), - RC(RC_TTC_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 200, -4400, 2000), - RC(RC_TTC_STAR_01_CAGE, RCTYPE_STAR_DEFAULT, LEVEL_TTC, RA_ACT_01, RI_STAR, -1450, -1130, -1050), - RC(RC_TTC_STAR_02_PENDULUMS, RCTYPE_STAR_DEFAULT, LEVEL_TTC, RA_ACT_02, RI_STAR, -1850, 300, -950), - RC(RC_TTC_STAR_03_HAND, RCTYPE_STAR_DEFAULT, LEVEL_TTC, RA_ACT_03, RI_STAR, -1300, -2250, -1300), - RC(RC_TTC_STAR_04_THWOMP, RCTYPE_STAR_DEFAULT, LEVEL_TTC, RA_ACT_04, RI_STAR, 2200, 7300, 2210), - RC(RC_TTC_STAR_05_MOVING_BARS, RCTYPE_STAR_DEFAULT, LEVEL_TTC, RA_ACT_05, RI_STAR, -1050, 2400, -790), - RC(RC_TTC_STAR_06_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_TTC, RA_ACT_06, RI_STAR, 1815, -3200, 800), - RC(RC_TTC_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_TTC, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_TTM_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, 1450, -2750, -4250), - RC(RC_TTM_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, 2550, -2750, -3400), - RC(RC_TTM_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, 4150, -2700, -2800), - RC(RC_TTM_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, -1600, -1650, -2500), - RC(RC_TTM_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, -1200, -1600, -3250), - RC(RC_TTM_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, -1850, 100, -2850), - RC(RC_TTM_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, -1500, -800, -2750), - RC(RC_TTM_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, 1840, -2880, -3600), - RC(RC_TTM_STAR_01_MOUNTAIN_SCALE, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_01, RI_STAR, 1200, 2600, 150), - RC(RC_TTM_STAR_02_MONKEY_CAGE, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_02, RI_STAR, 2500, -1200, 1300), - RC(RC_TTM_STAR_03_RED_COINS, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_03, RI_STAR, -3250, -2500, -3700), - RC(RC_TTM_STAR_04_MOUNTAINSIDE, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_04, RI_STAR, -2900, -2700, 3650), - RC(RC_TTM_STAR_05_BRIDGE_VIEW, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_05, RI_STAR, 1800, 1200, 1050), - RC(RC_TTM_STAR_06_MUSHROOM, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_06, RI_STAR, 7300, -3100, 1300), - RC(RC_TTM_STAR_07_100_COIN, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_VCUTM_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, -5920, 4575, -4618), - RC(RC_VCUTM_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, -4740, 3581, -3854), - RC(RC_VCUTM_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, -4727, -227, -1161), - RC(RC_VCUTM_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, -3418, -559, -836), - RC(RC_VCUTM_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, -600, -1400, -6300), - RC(RC_VCUTM_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, 900, -1400, -6300), - RC(RC_VCUTM_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, 2750, -800, -6300), - RC(RC_VCUTM_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, 4509, 0, -5527), - RC(RC_VCUTM_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_VCUTM, RA_ACT_01, RI_STAR, 4600, 250, -4500), - RC(RC_WDW_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_WDW, RA_ACT_NONE, RI_COIN_BLUE, 1124, 400, -218), - RC(RC_WDW_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_WDW, RA_ACT_NONE, RI_COIN_BLUE, 1124, 400, 830), - RC(RC_WDW_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_WDW, RA_ACT_NONE, RI_COIN_BLUE, 832, 400, 1194), - RC(RC_WDW_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_WDW, RA_ACT_NONE, RI_COIN_BLUE, 1124, 400, 282), - RC(RC_WDW_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_WDW, RA_ACT_NONE, RI_COIN_BLUE, 420, 400, 1504), - RC(RC_WDW_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_WDW, RA_ACT_NONE, RI_COIN_BLUE, -12, 400, 1808), - RC(RC_WDW_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, -3720, -1131, 520), - RC(RC_WDW_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, -2930, -850, 2560), - RC(RC_WDW_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, 100, -1771, -1500), - RC(RC_WDW_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, 1820, -1387, 1020), - RC(RC_WDW_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, -370, -1795, -180), - RC(RC_WDW_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, -1180, -1795, -180), - RC(RC_WDW_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, -780, -2155, 2860), - RC(RC_WDW_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, -780, -1020, 3400), - RC(RC_WDW_STAR_01_ARROW_LIFTS, RCTYPE_STAR_DEFAULT, LEVEL_WDW, RA_ACT_01, RI_STAR, -2200, 2600, 3500), - RC(RC_WDW_STAR_02_TOWN_TOP, RCTYPE_STAR_DEFAULT, LEVEL_WDW, RA_ACT_02, RI_STAR, 1550, 4350, 100), - RC(RC_WDW_STAR_03_SECRETS, RCTYPE_STAR_DEFAULT, LEVEL_WDW, RA_ACT_03, RI_STAR, 3360, 1580, 2660), - RC(RC_WDW_STAR_04_ELEVATOR, RCTYPE_STAR_DEFAULT, LEVEL_WDW, RA_ACT_04, RI_STAR, 890, 3400, -2040), - RC(RC_WDW_STAR_05_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_WDW, RA_ACT_05, RI_STAR, -770, -1600, 3600), - RC(RC_WDW_STAR_06_DOWNTOWN, RCTYPE_STAR_DEFAULT, LEVEL_WDW, RA_ACT_06, RI_STAR, 2180, -840, 3720), - RC(RC_WDW_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_WDW, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_WF_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_WF, RA_ACT_NONE, RI_COIN_BLUE, -2500, 450, -1150), - RC(RC_WF_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_WF, RA_ACT_NONE, RI_COIN_BLUE, -2500, 450, -900), - RC(RC_WF_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_WF, RA_ACT_NONE, RI_COIN_BLUE, -2500, 450, -650), - RC(RC_WF_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_WF, RA_ACT_NONE, RI_COIN_BLUE, -2500, 450, -1400), - RC(RC_WF_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, -250, 2650, 2970), - RC(RC_WF_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, 1746, 3620, -3120), - RC(RC_WF_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, 1277, 2600, 1350), - RC(RC_WF_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, 1585, 2595, -80), - RC(RC_WF_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, 3350, 3000, -1520), - RC(RC_WF_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, 2700, 3600, -900), - RC(RC_WF_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, 3770, 1380, 650), - RC(RC_WF_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, -270, 1720, 2250), - RC(RC_WF_STAR_01_WHOMPS_BLOCK, RCTYPE_STAR_DEFAULT, LEVEL_WF, RA_ACT_01, RI_STAR, 180, 3880, 340), - RC(RC_WF_STAR_02_FORTRESS_TOP, RCTYPE_STAR_DEFAULT, LEVEL_WF, RA_ACT_02, RI_STAR, 300, 5550, 0), - RC(RC_WF_STAR_03_WILD_BLUE, RCTYPE_STAR_DEFAULT, LEVEL_WF, RA_ACT_03, RI_STAR, -2500, 1500, -750), - RC(RC_WF_STAR_04_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_WF, RA_ACT_04, RI_STAR, 4600, 550, 2500), - RC(RC_WF_STAR_05_CAGED_ISLAND, RCTYPE_STAR_DEFAULT, LEVEL_WF, RA_ACT_05, RI_STAR, 2880, 4300, 190), - RC(RC_WF_STAR_06_WALL, RCTYPE_STAR_DEFAULT, LEVEL_WF, RA_ACT_06, RI_STAR, 590, 2450, 2650), - RC(RC_WF_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_WF, RA_ACT_COIN, RI_STAR, 0, 0, 0), - RC(RC_WMOTR_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, -2980, 3990, -4248), - RC(RC_WMOTR_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, 2735, 3140, -3085), - RC(RC_WMOTR_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, -3640, 4600, -4200), - RC(RC_WMOTR_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, 4400, 240, 80), - RC(RC_WMOTR_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, 3440, -2680, 5240), - RC(RC_WMOTR_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, -600, -1360, 5040), - RC(RC_WMOTR_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, 320, 1725, 40), - RC(RC_WMOTR_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, -2560, 4600, -4800), - RC(RC_WMOTR_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_WMOTR, RA_ACT_01, RI_STAR, -160, 1950, -470), + RC(RC_UNKNOWN, RCTYPE_UNKNOWN, LEVEL_UNKNOWN_1, RA_ACT_NONE, RI_UNKNOWN, 0, 0, 0), + RC(RC_AQUA_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, -2400, -4607, -2400), + RC(RC_AQUA_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, -2400, -4607, 2400), + RC(RC_AQUA_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, 2400, -4607, 2400), + RC(RC_AQUA_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, 2400, -4607, -2400), + RC(RC_AQUA_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, 0, -2200, -1500), + RC(RC_AQUA_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, -1500, -2200, 0), + RC(RC_AQUA_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, 0, -2200, 1500), + RC(RC_AQUA_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_SA, RA_ACT_NONE, RI_COIN_RED, 1500, -2200, 0), + RC(RC_AQUA_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_SA, RA_ACT_01, RI_STAR, 0, -4250, 0), + RC(RC_BBH_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_BBH, RA_ACT_NONE, RI_COIN_BLUE, 1400, 1840, 1100), + RC(RC_BBH_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_BBH, RA_ACT_NONE, RI_COIN_BLUE, 1700, 1840, 1100), + RC(RC_BBH_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_BBH, RA_ACT_NONE, RI_COIN_BLUE, 0, 1840, 1100), + RC(RC_BBH_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_BBH, RA_ACT_NONE, RI_COIN_BLUE, -400, 1840, 1100), + RC(RC_BBH_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, -1164, 0, 2325), + RC(RC_BBH_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, 2540, 820, 2174), + RC(RC_BBH_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, 2600, 815, -380), + RC(RC_BBH_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, 2950, 815, -1250), + RC(RC_BBH_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, -1500, 550, 960), + RC(RC_BBH_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, -420, 550, 60), + RC(RC_BBH_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, 2856, 50, 2144), + RC(RC_BBH_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_BBH, RA_ACT_NONE, RI_COIN_RED, 50, 900, -1400), + RC(RC_BBH_STAR_01_GHOST_HUNT, RCTYPE_STAR_DEFAULT, LEVEL_BBH, RA_ACT_01, RI_STAR, 980, 1100, 250), + RC(RC_BBH_STAR_02_MERRY_GO_ROUND, RCTYPE_STAR_DEFAULT, LEVEL_BBH, RA_ACT_02, RI_STAR, -1600, -2100, 205), + RC(RC_BBH_STAR_03_HAUNTED_BOOKS, RCTYPE_STAR_DEFAULT, LEVEL_BBH, RA_ACT_03, RI_STAR, -2030, 1350, 1940), + RC(RC_BBH_STAR_04_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_BBH, RA_ACT_04, RI_STAR, -204, 1100, 1576), + RC(RC_BBH_STAR_05_BALCONY, RCTYPE_STAR_DEFAULT, LEVEL_BBH, RA_ACT_05, RI_STAR, 700, 3200, 1900), + RC(RC_BBH_STAR_06_SECRET_ROOM, RCTYPE_STAR_DEFAULT, LEVEL_BBH, RA_ACT_06, RI_STAR, 1370, 2000, -320), + RC(RC_BBH_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_BBH, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_BITDW_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -3100, -2900, 4520), + RC(RC_BITDW_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -4740, 1050, -2130), + RC(RC_BITDW_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -3120, 1160, -2570), + RC(RC_BITDW_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -4613, 1220, -427), + RC(RC_BITDW_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -1000, 1933, 466), + RC(RC_BITDW_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -6475, 125, 890), + RC(RC_BITDW_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, -7810, -3100, 4900), + RC(RC_BITDW_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_BITDW, RA_ACT_NONE, RI_COIN_RED, 1500, 1066, -166), + RC(RC_BITDW_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_BITDW, RA_ACT_01, RI_STAR, 7180, 3000, 0), + RC(RC_BITFS_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, 2888, -1375, 310), + RC(RC_BITFS_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, 1230, -1900, -1747), + RC(RC_BITFS_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, 5800, -2000, 0), + RC(RC_BITFS_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, -1846, 1600, -476), + RC(RC_BITFS_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, -4210, 2615, 280), + RC(RC_BITFS_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, -1390, 3575, -420), + RC(RC_BITFS_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, 3065, 4610, 92), + RC(RC_BITFS_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_BITFS, RA_ACT_NONE, RI_COIN_RED, 4330, -790, -50), + RC(RC_BITFS_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_BITFS, RA_ACT_01, RI_STAR, 1200, 5700, 160), + RC(RC_BITS_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, 1590, -1120, -2055), + RC(RC_BITS_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, -2960, -3360, 0), + RC(RC_BITS_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, -298, -2600, -1600), + RC(RC_BITS_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, 7618, 4850, -1909), + RC(RC_BITS_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, -136, 3109, -909), + RC(RC_BITS_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, -3330, 4050, -900), + RC(RC_BITS_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, -4830, 2103, -1030), + RC(RC_BITS_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_BITS, RA_ACT_NONE, RI_COIN_RED, 348, 5921, -4585), + RC(RC_BOB_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, -5500, 768, 2400), + RC(RC_BOB_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, -6500, 768, 2400), + RC(RC_BOB_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, 1135, 1920, -7161), + RC(RC_BOB_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, 250, 1000, 1920), + RC(RC_BOB_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, 1600, 980, 4440), + RC(RC_BOB_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, -2755, 0, -4083), + RC(RC_BOB_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, 4934, 1352, 861), + RC(RC_BOB_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_BOB, RA_ACT_NONE, RI_COIN_RED, 4100, 3672, 1629), + RC(RC_BOB_STAR_01_KING_BOBOMB, RCTYPE_STAR_DEFAULT, LEVEL_BOB, RA_ACT_01, RI_STAR, 2000, 4500, -4500), + RC(RC_BOB_STAR_02_KOOPA_RACE, RCTYPE_STAR_DEFAULT, LEVEL_BOB, RA_ACT_02, RI_STAR, 3030, 4500, -4600), + RC(RC_BOB_STAR_03_ISLAND, RCTYPE_STAR_DEFAULT, LEVEL_BOB, RA_ACT_03, RI_STAR, 5540, 3350, 1200), + RC(RC_BOB_STAR_04_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_BOB, RA_ACT_04, RI_STAR, -6000, 1000, 2400), + RC(RC_BOB_STAR_05_WINGS, RCTYPE_STAR_DEFAULT, LEVEL_BOB, RA_ACT_05, RI_STAR, -6600, 1000, 1250), + RC(RC_BOB_STAR_06_CHAIN_CHOMP, RCTYPE_STAR_DEFAULT, LEVEL_BOB, RA_ACT_06, RI_STAR, 1550, 1200, 300), + RC(RC_BOB_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_BOB, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_CASTLE_STAR_01_TOAD_BASEMENT, RCTYPE_STAR_DEFAULT, LEVEL_CASTLE, RA_ACT_01, RI_STAR, 1787, -1381, -1957), + RC(RC_CASTLE_STAR_02_TOAD_2ND_FLOOR, RCTYPE_STAR_DEFAULT, LEVEL_CASTLE, RA_ACT_02, RI_STAR, -977, 1203, 2569), + RC(RC_CASTLE_STAR_03_TOAD_3RD_FLOOR, RCTYPE_STAR_DEFAULT, LEVEL_CASTLE, RA_ACT_03, RI_STAR, -1584, 2253, 7157), + RC(RC_CASTLE_STAR_04_MIPS_FIRST, RCTYPE_STAR_DEFAULT, LEVEL_CASTLE, RA_ACT_04, RI_STAR, 0, 0, 0), + RC(RC_CASTLE_STAR_05_MIPS_SECOND, RCTYPE_STAR_DEFAULT, LEVEL_CASTLE, RA_ACT_05, RI_STAR, 0, 0, 1), + RC(RC_CCM_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_CCM, RA_ACT_NONE, RI_COIN_BLUE, 3400, -640, -740), + RC(RC_CCM_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_CCM, RA_ACT_NONE, RI_COIN_BLUE, 3400, -320, -1740), + RC(RC_CCM_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_CCM, RA_ACT_NONE, RI_COIN_BLUE, -3160, 6267, -6120), + RC(RC_CCM_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, -488, 3100, -2305), + RC(RC_CCM_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, -3120, -1200, -2080), + RC(RC_CCM_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, 2940, -1400, 2630), + RC(RC_CCM_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, -600, -505, 1940), + RC(RC_CCM_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, -3814, -2112, 6427), + RC(RC_CCM_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, 2700, -4600, 1230), + RC(RC_CCM_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, 1990, -4040, 4950), + RC(RC_CCM_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_CCM, RA_ACT_NONE, RI_COIN_RED, 4408, -4550, -1534), + RC(RC_CCM_STAR_01_SLIDING_AWAY, RCTYPE_STAR_DEFAULT, LEVEL_CCM, RA_ACT_01, RI_STAR, 2500, -4350, 5750), + RC(RC_CCM_STAR_02_LOST_PENGUIN, RCTYPE_STAR_DEFAULT, LEVEL_CCM, RA_ACT_02, RI_STAR, 3167, -4300, 5108), + RC(RC_CCM_STAR_03_PENGUIN_RACE, RCTYPE_STAR_DEFAULT, LEVEL_CCM, RA_ACT_03, RI_STAR, -7339, -5700, -6774), + RC(RC_CCM_STAR_04_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_CCM, RA_ACT_04, RI_STAR, 4200, -927, 400), + RC(RC_CCM_STAR_05_LOST_HEAD, RCTYPE_STAR_DEFAULT, LEVEL_CCM, RA_ACT_05, RI_STAR, -4700, -1024, 1890), + RC(RC_CCM_STAR_06_WALL_KICKS, RCTYPE_STAR_DEFAULT, LEVEL_CCM, RA_ACT_06, RI_STAR, -2000, -2200, -3000), + RC(RC_CCM_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_CCM, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_COTMC_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, 200, -291, -5600), + RC(RC_COTMC_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, 980, 260, -3430), + RC(RC_COTMC_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, -540, -352, -5940), + RC(RC_COTMC_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, -300, 450, -6240), + RC(RC_COTMC_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, -200, -400, -6680), + RC(RC_COTMC_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, 250, 450, -6400), + RC(RC_COTMC_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, 540, -361, -6340), + RC(RC_COTMC_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_COTMC, RA_ACT_NONE, RI_COIN_RED, 980, 260, -3810), + RC(RC_COTMC_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_COTMC, RA_ACT_01, RI_STAR, 0, -200, -7000), + RC(RC_DDD_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_DDD, RA_ACT_NONE, RI_COIN_BLUE, 1414, 1020, 622), + RC(RC_DDD_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_DDD, RA_ACT_NONE, RI_COIN_BLUE, 1814, 1020, 622), + RC(RC_DDD_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_DDD, RA_ACT_NONE, RI_COIN_BLUE, 1814, 1020, 1222), + RC(RC_DDD_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_DDD, RA_ACT_NONE, RI_COIN_BLUE, 1414, 1020, 1222), + RC(RC_DDD_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_DDD, RA_ACT_NONE, RI_COIN_BLUE, 1414, 1020, 922), + RC(RC_DDD_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_DDD, RA_ACT_NONE, RI_COIN_BLUE, 1814, 1020, 922), + RC(RC_DDD_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 4680, 1155, -2200), + RC(RC_DDD_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 3525, 1155, -2250), + RC(RC_DDD_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 3850, 1111, 3550), + RC(RC_DDD_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 4400, 1288, 3595), + RC(RC_DDD_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 2974, 1015, 3584), + RC(RC_DDD_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 5550, 1015, 3575), + RC(RC_DDD_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 3300, 1111, -1900), + RC(RC_DDD_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_DDD, RA_ACT_NONE, RI_COIN_RED, 2100, 1025, -1600), + RC(RC_DDD_STAR_01_BOWSERS_SUB, RCTYPE_STAR_DEFAULT, LEVEL_DDD, RA_ACT_01, RI_STAR, 3900, 850, -600), + RC(RC_DDD_STAR_02_CHESTS, RCTYPE_STAR_DEFAULT, LEVEL_DDD, RA_ACT_02, RI_STAR, -1900, -4000, -1400), + RC(RC_DDD_STAR_03_RED_COINS, RCTYPE_STAR_DEFAULT, LEVEL_DDD, RA_ACT_03, RI_STAR, 5513, 1200, 900), + RC(RC_DDD_STAR_04_JET_STREAM, RCTYPE_STAR_RED_COIN, LEVEL_DDD, RA_ACT_04, RI_STAR, 3400, -3200, -500), + RC(RC_DDD_STAR_05_MANTA_RAY, RCTYPE_STAR_DEFAULT, LEVEL_DDD, RA_ACT_05, RI_STAR, -3180, -3600, 120), + RC(RC_DDD_STAR_06_CAPS, RCTYPE_STAR_DEFAULT, LEVEL_DDD, RA_ACT_06, RI_STAR, 2030, -3700, -2780), + RC(RC_DDD_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_DDD, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_HMC_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 3945, -1023, -2918), + RC(RC_HMC_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 3800, -1023, -4009), + RC(RC_HMC_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 3036, -858, -4118), + RC(RC_HMC_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 2018, -921, -4154), + RC(RC_HMC_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 1836, -921, -4700), + RC(RC_HMC_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 1836, -721, -4700), + RC(RC_HMC_BLUE_COIN_07, RCTYPE_COIN_BLUE, LEVEL_HMC, RA_ACT_NONE, RI_COIN_BLUE, 1836, -521, -4700), + RC(RC_HMC_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 2140, 2048, 6540), + RC(RC_HMC_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 2900, 2048, 6500), + RC(RC_HMC_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 6400, 2090, 5320), + RC(RC_HMC_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 6820, 1024, 3300), + RC(RC_HMC_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 6260, 1124, 4960), + RC(RC_HMC_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 4080, 1124, 5760), + RC(RC_HMC_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 4640, 2250, 3980), + RC(RC_HMC_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_HMC, RA_ACT_NONE, RI_COIN_RED, 2960, 1124, 5140), + RC(RC_HMC_STAR_01_BEAST, RCTYPE_STAR_DEFAULT, LEVEL_HMC, RA_ACT_01, RI_STAR, -3600, -4000, 3600), + RC(RC_HMC_STAR_02_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_HMC, RA_ACT_02, RI_STAR, 4000, 300, 5000), + RC(RC_HMC_STAR_03_METAL_HEAD, RCTYPE_STAR_DEFAULT, LEVEL_HMC, RA_ACT_03, RI_STAR, 6200, -4400, 2300), + RC(RC_HMC_STAR_04_TOXIC_MAZE, RCTYPE_STAR_DEFAULT, LEVEL_HMC, RA_ACT_04, RI_STAR, -2100, 2100, -7550), + RC(RC_HMC_STAR_05_EMERGENCY_EXIT, RCTYPE_STAR_DEFAULT, LEVEL_HMC, RA_ACT_05, RI_STAR, -6500, 2700, -1600), + RC(RC_HMC_STAR_06_ROLLING_ROCKS, RCTYPE_STAR_DEFAULT, LEVEL_HMC, RA_ACT_06, RI_STAR, -5000, 3050, -6700), + RC(RC_HMC_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_HMC, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_JRB_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_JRB, RA_ACT_NONE, RI_COIN_BLUE, 5900, -2966, -7347), + RC(RC_JRB_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_JRB, RA_ACT_NONE, RI_COIN_BLUE, 5300, -2966, -7347), + RC(RC_JRB_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_JRB, RA_ACT_NONE, RI_COIN_BLUE, 4700, -2966, -7347), + RC(RC_JRB_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_JRB, RA_ACT_NONE, RI_COIN_BLUE, 4100, -2966, -7347), + RC(RC_JRB_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_JRB, RA_ACT_NONE, RI_COIN_BLUE, 3500, -2966, -7347), + RC(RC_JRB_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_JRB, RA_ACT_NONE, RI_COIN_BLUE, 2900, -2966, -7347), + RC(RC_JRB_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, 4900, 1800, 4700), + RC(RC_JRB_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, -2535, 2110, 6113), + RC(RC_JRB_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, -1845, -1003, 3518), + RC(RC_JRB_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, -546, 530, 5930), + RC(RC_JRB_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, 648, -490, 4256), + RC(RC_JRB_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, 3642, -5103, 3175), + RC(RC_JRB_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, 4400, 2200, 1300), + RC(RC_JRB_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_JRB, RA_ACT_NONE, RI_COIN_RED, 5400, 2200, 1300), + RC(RC_JRB_STAR_01_SUNKEN_SHIP, RCTYPE_STAR_DEFAULT, LEVEL_JRB, RA_ACT_01, RI_STAR, 0, 1600, 3000), + RC(RC_JRB_STAR_02_EEL, RCTYPE_STAR_DEFAULT, LEVEL_JRB, RA_ACT_02, RI_STAR, 6833, -3654, 2230), + RC(RC_JRB_STAR_03_OCEAN_CAVE, RCTYPE_STAR_DEFAULT, LEVEL_JRB, RA_ACT_03, RI_STAR, -1800, -2500, -1700), + RC(RC_JRB_STAR_04_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_JRB, RA_ACT_04, RI_STAR, 4900, 2400, 800), + RC(RC_JRB_STAR_05_STONE_PILLAR, RCTYPE_STAR_DEFAULT, LEVEL_JRB, RA_ACT_05, RI_STAR, 1540, 2160, 2130), + RC(RC_JRB_STAR_06_JET_STREAM, RCTYPE_STAR_DEFAULT, LEVEL_JRB, RA_ACT_06, RI_STAR, 5000, -4800, 2500), + RC(RC_JRB_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_JRB, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_LLL_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -5800, 220, 250), + RC(RC_LLL_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -5100, 220, 250), + RC(RC_LLL_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -5100, 220, 1000), + RC(RC_LLL_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -4400, 220, 1000), + RC(RC_LLL_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -4400, 220, 1750), + RC(RC_LLL_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -5100, 220, 1750), + RC(RC_LLL_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -5800, 220, 1750), + RC(RC_LLL_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_LLL, RA_ACT_NONE, RI_COIN_RED, -5800, 220, 1000), + RC(RC_LLL_STAR_01_BIG_BULLY, RCTYPE_STAR_DEFAULT, LEVEL_LLL, RA_ACT_01, RI_STAR, 0, 950, -6800), + RC(RC_LLL_STAR_02_LITTLE_BULLIES, RCTYPE_STAR_DEFAULT, LEVEL_LLL, RA_ACT_02, RI_STAR, 3700, 600, -5500), + RC(RC_LLL_STAR_03_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_LLL, RA_ACT_03, RI_STAR, -4400, 350, 250), + RC(RC_LLL_STAR_04_LOG_ROLLING, RCTYPE_STAR_DEFAULT, LEVEL_LLL, RA_ACT_04, RI_STAR, 3100, 400, 7900), + RC(RC_LLL_STAR_05_VOLCANO_FOOT, RCTYPE_STAR_DEFAULT, LEVEL_LLL, RA_ACT_05, RI_STAR, 2523, 3850, -901), + RC(RC_LLL_STAR_06_VOLCANO_ELEVATOR, RCTYPE_STAR_DEFAULT, LEVEL_LLL, RA_ACT_06, RI_STAR, 1800, 3400, 1450), + RC(RC_LLL_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_LLL, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_PSS_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_PSS, RA_ACT_NONE, RI_COIN_BLUE, 780, 5613, -5600), + RC(RC_PSS_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_PSS, RA_ACT_NONE, RI_COIN_BLUE, 2360, 5979, -5600), + RC(RC_PSS_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_PSS, RA_ACT_NONE, RI_COIN_BLUE, -809, 5245, -5600), + RC(RC_PSS_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_PSS, RA_ACT_NONE, RI_COIN_BLUE, -2409, 4877, -5600), + RC(RC_PSS_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_PSS, RA_ACT_NONE, RI_COIN_BLUE, -4445, 4490, -5536), + RC(RC_PSS_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_PSS, RA_ACT_NONE, RI_COIN_BLUE, -6263, 4150, -4009), + RC(RC_PSS_STAR_01_BOX, RCTYPE_STAR_DEFAULT, LEVEL_PSS, RA_ACT_01, RI_STAR, -6385, -4200, 5770), + RC(RC_PSS_STAR_02_SPEED_TIME, RCTYPE_STAR_DEFAULT, LEVEL_PSS, RA_ACT_02, RI_STAR, -6358, -4300, 4700), + RC(RC_RR_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_RR, RA_ACT_NONE, RI_COIN_BLUE, -6500, 1700, 0), + RC(RC_RR_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_RR, RA_ACT_NONE, RI_COIN_BLUE, -6200, 1700, 0), + RC(RC_RR_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_RR, RA_ACT_NONE, RI_COIN_BLUE, -5900, 1700, 0), + RC(RC_RR_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_RR, RA_ACT_NONE, RI_COIN_BLUE, -5600, 1700, 0), + RC(RC_RR_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_RR, RA_ACT_NONE, RI_COIN_BLUE, -6750, -1495, 0), + RC(RC_RR_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_RR, RA_ACT_NONE, RI_COIN_BLUE, -5300, 1700, 0), + RC(RC_RR_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -5850, -1000, -50), + RC(RC_RR_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -2950, -190, -50), + RC(RC_RR_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -5850, 1150, 50), + RC(RC_RR_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -5050, 800, -50), + RC(RC_RR_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -4550, 650, -50), + RC(RC_RR_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -5300, 200, -50), + RC(RC_RR_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -3550, 1000, -50), + RC(RC_RR_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_RR, RA_ACT_NONE, RI_COIN_RED, -5800, -400, -50), + RC(RC_RR_STAR_01_CRUISER, RCTYPE_STAR_DEFAULT, LEVEL_RR, RA_ACT_01, RI_STAR, 1450, 3400, -2352), + RC(RC_RR_STAR_02_BIG_HOUSE, RCTYPE_STAR_DEFAULT, LEVEL_RR, RA_ACT_02, RI_STAR, -4200, 6700, -4450), + RC(RC_RR_STAR_03_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_RR, RA_ACT_03, RI_STAR, -5150, -1400, 0), + RC(RC_RR_STAR_04_SWINGIN_BREEZE, RCTYPE_STAR_DEFAULT, LEVEL_RR, RA_ACT_04, RI_STAR, -5850, -700, 4950), + RC(RC_RR_STAR_05_TRIANGLES, RCTYPE_STAR_DEFAULT, LEVEL_RR, RA_ACT_05, RI_STAR, 3700, -400, 6600), + RC(RC_RR_STAR_06_OVER_THE_RAINBOW, RCTYPE_STAR_DEFAULT, LEVEL_RR, RA_ACT_06, RI_STAR, 5000, 4100, 4440), + RC(RC_RR_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_RR, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_SL_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, 4750, 1061, -5230), + RC(RC_SL_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, 416, 1050, -4522), + RC(RC_SL_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, -6560, 2040, -5080), + RC(RC_SL_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, -6760, 2040, -1360), + RC(RC_SL_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, -6880, 1857, 1000), + RC(RC_SL_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, -4211, 1092, -4723), + RC(RC_SL_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, -6271, 1390, 4764), + RC(RC_SL_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_SL, RA_ACT_NONE, RI_COIN_RED, -529, 1050, -5329), + RC(RC_SL_STAR_01_BIG_HEAD, RCTYPE_STAR_DEFAULT, LEVEL_SL, RA_ACT_01, RI_STAR, 700, 4500, 690), + RC(RC_SL_STAR_02_BULLY, RCTYPE_STAR_DEFAULT, LEVEL_SL, RA_ACT_02, RI_STAR, 130, 1600, -4335), + RC(RC_SL_STAR_03_DEEP_FREEZE, RCTYPE_STAR_DEFAULT, LEVEL_SL, RA_ACT_03, RI_STAR, 4350, 1350, 4350), + RC(RC_SL_STAR_04_FREEZING_POND, RCTYPE_STAR_DEFAULT, LEVEL_SL, RA_ACT_04, RI_STAR, -4700, 1300, 5850), + RC(RC_SL_STAR_05_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_SL, RA_ACT_05, RI_STAR, 5000, 1200, 0), + RC(RC_SL_STAR_06_IGLOO, RCTYPE_STAR_DEFAULT, LEVEL_SL, RA_ACT_06, RI_STAR, 0, 500, 1000), + RC(RC_SL_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_SL, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_SSL_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_SSL, RA_ACT_NONE, RI_COIN_BLUE, 0, 0, 2381), + RC(RC_SSL_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_SSL, RA_ACT_NONE, RI_COIN_BLUE, 0, 100, 2381), + RC(RC_SSL_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_SSL, RA_ACT_NONE, RI_COIN_BLUE, 0, 200, 2381), + RC(RC_SSL_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, -4500, 1500, -3500), + RC(RC_SSL_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, -4500, 1500, 1500), + RC(RC_SSL_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, 500, 1500, -3500), + RC(RC_SSL_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, 500, 1500, 1500), + RC(RC_SSL_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, 3380, 10, -4900), + RC(RC_SSL_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, -5270, -170, -6780), + RC(RC_SSL_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, -7450, 10, 7560), + RC(RC_SSL_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_NONE, RI_COIN_RED, 5900, 70, 2311), + RC(RC_SSL_STAR_01_BIG_BIRD, RCTYPE_STAR_DEFAULT, LEVEL_SSL, RA_ACT_01, RI_STAR, -5550, 300, -930), + RC(RC_SSL_STAR_02_PYRAMID_TOP, RCTYPE_STAR_DEFAULT, LEVEL_SSL, RA_ACT_02, RI_STAR, -2050, 1200, -580), + RC(RC_SSL_STAR_03_PYRAMID_INSIDE, RCTYPE_STAR_DEFAULT, LEVEL_SSL, RA_ACT_03, RI_STAR, 500, 5050, -500), + RC(RC_SSL_STAR_04_FOUR_PILLARS, RCTYPE_STAR_DEFAULT, LEVEL_SSL, RA_ACT_04, RI_STAR, 0, -900, -3700), + RC(RC_SSL_STAR_05_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_SSL, RA_ACT_05, RI_STAR, 6000, 800, 3500), + RC(RC_SSL_STAR_06_PYRAMID_PUZZLE, RCTYPE_STAR_DEFAULT, LEVEL_SSL, RA_ACT_06, RI_STAR, 900, 1400, 2350), + RC(RC_SSL_STAR_07_100_COIN, RCTYPE_COIN_RED, LEVEL_SSL, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_THI_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_THI, RA_ACT_NONE, RI_COIN_BLUE, -540, 1500, 1240), + RC(RC_THI_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_THI, RA_ACT_NONE, RI_COIN_BLUE, -840, 1500, 1440), + RC(RC_THI_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, 1038, 1122, 998), + RC(RC_THI_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, 1590, 1078, 160), + RC(RC_THI_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, 1038, 1112, -823), + RC(RC_THI_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, -238, 1122, -1319), + RC(RC_THI_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, -523, 824, -500), + RC(RC_THI_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, -1838, 1068, 338), + RC(RC_THI_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, -240, 1500, 1040), + RC(RC_THI_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_THI, RA_ACT_NONE, RI_COIN_RED, -1914, 1360, -1909), + RC(RC_THI_STAR_01_PIRANHA, RCTYPE_STAR_DEFAULT, LEVEL_THI, RA_ACT_01, RI_STAR, -6300, -1850, -6300), + RC(RC_THI_STAR_02_ISLAND_TOP, RCTYPE_STAR_DEFAULT, LEVEL_THI, RA_ACT_02, RI_STAR, 2600, 3500, -2400), + RC(RC_THI_STAR_03_KOOPA_REMATCH, RCTYPE_STAR_DEFAULT, LEVEL_THI, RA_ACT_03, RI_STAR, 7100, -1300, -6000), + RC(RC_THI_STAR_04_SECRETS, RCTYPE_STAR_DEFAULT, LEVEL_THI, RA_ACT_04, RI_STAR, 0, -700, -4500), + RC(RC_THI_STAR_05_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_THI, RA_ACT_05, RI_STAR, -1800, 800, -1500), + RC(RC_THI_STAR_06_WIGGLER, RCTYPE_STAR_DEFAULT, LEVEL_THI, RA_ACT_06, RI_STAR, 0, 2048, 0), + RC(RC_THI_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_THI, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_TOTWC_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, -100, 800, -4000), + RC(RC_TOTWC_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, -3500, 0, 100), + RC(RC_TOTWC_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, 100, -800, 3000), + RC(RC_TOTWC_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, 4500, 1600, -100), + RC(RC_TOTWC_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, 100, 800, -4000), + RC(RC_TOTWC_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, -3500, 0, -100), + RC(RC_TOTWC_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, -100, -800, 3000), + RC(RC_TOTWC_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_TOTWC, RA_ACT_NONE, RI_COIN_RED, 4500, 1600, 100), + RC(RC_TOTWC_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_TOTWC, RA_ACT_01, RI_STAR, 800, -1700, 0), + RC(RC_TTC_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, 0, 20, -1400), + RC(RC_TTC_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, -250, 20, -1700), + RC(RC_TTC_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, 250, 20, -1700), + RC(RC_TTC_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, 0, 20, -2000), + RC(RC_TTC_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, 500, 20, -2000), + RC(RC_TTC_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, -500, 20, -2000), + RC(RC_TTC_BLUE_COIN_07, RCTYPE_COIN_BLUE, LEVEL_TTC, RA_ACT_NONE, RI_COIN_BLUE, 0, 20, -1700), + RC(RC_TTC_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 800, -4400, 1900), + RC(RC_TTC_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 800, -3700, 1900), + RC(RC_TTC_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 800, -3000, 1900), + RC(RC_TTC_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 1780, -3300, 1000), + RC(RC_TTC_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 1388, -3300, 1428), + RC(RC_TTC_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 200, -3000, 2000), + RC(RC_TTC_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 200, -3700, 2000), + RC(RC_TTC_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_TTC, RA_ACT_NONE, RI_COIN_RED, 200, -4400, 2000), + RC(RC_TTC_STAR_01_CAGE, RCTYPE_STAR_DEFAULT, LEVEL_TTC, RA_ACT_01, RI_STAR, -1450, -1130, -1050), + RC(RC_TTC_STAR_02_PENDULUMS, RCTYPE_STAR_DEFAULT, LEVEL_TTC, RA_ACT_02, RI_STAR, -1850, 300, -950), + RC(RC_TTC_STAR_03_HAND, RCTYPE_STAR_DEFAULT, LEVEL_TTC, RA_ACT_03, RI_STAR, -1300, -2250, -1300), + RC(RC_TTC_STAR_04_THWOMP, RCTYPE_STAR_DEFAULT, LEVEL_TTC, RA_ACT_04, RI_STAR, 2200, 7300, 2210), + RC(RC_TTC_STAR_05_MOVING_BARS, RCTYPE_STAR_DEFAULT, LEVEL_TTC, RA_ACT_05, RI_STAR, -1050, 2400, -790), + RC(RC_TTC_STAR_06_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_TTC, RA_ACT_06, RI_STAR, 1815, -3200, 800), + RC(RC_TTC_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_TTC, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_TTM_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, 1450, -2750, -4250), + RC(RC_TTM_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, 2550, -2750, -3400), + RC(RC_TTM_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, 4150, -2700, -2800), + RC(RC_TTM_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, -1600, -1650, -2500), + RC(RC_TTM_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, -1200, -1600, -3250), + RC(RC_TTM_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, -1850, 100, -2850), + RC(RC_TTM_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, -1500, -800, -2750), + RC(RC_TTM_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_NONE, RI_COIN_RED, 1840, -2880, -3600), + RC(RC_TTM_STAR_01_MOUNTAIN_SCALE, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_01, RI_STAR, 1200, 2600, 150), + RC(RC_TTM_STAR_02_MONKEY_CAGE, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_02, RI_STAR, 2500, -1200, 1300), + RC(RC_TTM_STAR_03_RED_COINS, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_03, RI_STAR, -3250, -2500, -3700), + RC(RC_TTM_STAR_04_MOUNTAINSIDE, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_04, RI_STAR, -2900, -2700, 3650), + RC(RC_TTM_STAR_05_BRIDGE_VIEW, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_05, RI_STAR, 1800, 1200, 1050), + RC(RC_TTM_STAR_06_MUSHROOM, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_06, RI_STAR, 7300, -3100, 1300), + RC(RC_TTM_STAR_07_100_COIN, RCTYPE_COIN_RED, LEVEL_TTM, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_VCUTM_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, -5920, 4575, -4618), + RC(RC_VCUTM_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, -4740, 3581, -3854), + RC(RC_VCUTM_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, -4727, -227, -1161), + RC(RC_VCUTM_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, -3418, -559, -836), + RC(RC_VCUTM_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, -600, -1400, -6300), + RC(RC_VCUTM_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, 900, -1400, -6300), + RC(RC_VCUTM_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, 2750, -800, -6300), + RC(RC_VCUTM_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_VCUTM, RA_ACT_NONE, RI_COIN_RED, 4509, 0, -5527), + RC(RC_VCUTM_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_VCUTM, RA_ACT_01, RI_STAR, 4600, 250, -4500), + RC(RC_WDW_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_WDW, RA_ACT_NONE, RI_COIN_BLUE, 1124, 400, -218), + RC(RC_WDW_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_WDW, RA_ACT_NONE, RI_COIN_BLUE, 1124, 400, 830), + RC(RC_WDW_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_WDW, RA_ACT_NONE, RI_COIN_BLUE, 832, 400, 1194), + RC(RC_WDW_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_WDW, RA_ACT_NONE, RI_COIN_BLUE, 1124, 400, 282), + RC(RC_WDW_BLUE_COIN_05, RCTYPE_COIN_BLUE, LEVEL_WDW, RA_ACT_NONE, RI_COIN_BLUE, 420, 400, 1504), + RC(RC_WDW_BLUE_COIN_06, RCTYPE_COIN_BLUE, LEVEL_WDW, RA_ACT_NONE, RI_COIN_BLUE, -12, 400, 1808), + RC(RC_WDW_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, -3720, -1131, 520), + RC(RC_WDW_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, -2930, -850, 2560), + RC(RC_WDW_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, 100, -1771, -1500), + RC(RC_WDW_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, 1820, -1387, 1020), + RC(RC_WDW_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, -370, -1795, -180), + RC(RC_WDW_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, -1180, -1795, -180), + RC(RC_WDW_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, -780, -2155, 2860), + RC(RC_WDW_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_WDW, RA_ACT_NONE, RI_COIN_RED, -780, -1020, 3400), + RC(RC_WDW_STAR_01_ARROW_LIFTS, RCTYPE_STAR_DEFAULT, LEVEL_WDW, RA_ACT_01, RI_STAR, -2200, 2600, 3500), + RC(RC_WDW_STAR_02_TOWN_TOP, RCTYPE_STAR_DEFAULT, LEVEL_WDW, RA_ACT_02, RI_STAR, 1550, 4350, 100), + RC(RC_WDW_STAR_03_SECRETS, RCTYPE_STAR_DEFAULT, LEVEL_WDW, RA_ACT_03, RI_STAR, 3360, 1580, 2660), + RC(RC_WDW_STAR_04_ELEVATOR, RCTYPE_STAR_DEFAULT, LEVEL_WDW, RA_ACT_04, RI_STAR, 890, 3400, -2040), + RC(RC_WDW_STAR_05_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_WDW, RA_ACT_05, RI_STAR, -770, -1600, 3600), + RC(RC_WDW_STAR_06_DOWNTOWN, RCTYPE_STAR_DEFAULT, LEVEL_WDW, RA_ACT_06, RI_STAR, 2180, -840, 3720), + RC(RC_WDW_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_WDW, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_WF_BLUE_COIN_01, RCTYPE_COIN_BLUE, LEVEL_WF, RA_ACT_NONE, RI_COIN_BLUE, -2500, 450, -1150), + RC(RC_WF_BLUE_COIN_02, RCTYPE_COIN_BLUE, LEVEL_WF, RA_ACT_NONE, RI_COIN_BLUE, -2500, 450, -900), + RC(RC_WF_BLUE_COIN_03, RCTYPE_COIN_BLUE, LEVEL_WF, RA_ACT_NONE, RI_COIN_BLUE, -2500, 450, -650), + RC(RC_WF_BLUE_COIN_04, RCTYPE_COIN_BLUE, LEVEL_WF, RA_ACT_NONE, RI_COIN_BLUE, -2500, 450, -1400), + RC(RC_WF_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, -250, 2650, 2970), + RC(RC_WF_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, 1746, 3620, -3120), + RC(RC_WF_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, 1277, 2600, 1350), + RC(RC_WF_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, 1585, 2595, -80), + RC(RC_WF_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, 3350, 3000, -1520), + RC(RC_WF_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, 2700, 3600, -900), + RC(RC_WF_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, 3770, 1380, 650), + RC(RC_WF_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_WF, RA_ACT_NONE, RI_COIN_RED, -270, 1720, 2250), + RC(RC_WF_STAR_01_WHOMPS_BLOCK, RCTYPE_STAR_DEFAULT, LEVEL_WF, RA_ACT_01, RI_STAR, 180, 3880, 340), + RC(RC_WF_STAR_02_FORTRESS_TOP, RCTYPE_STAR_DEFAULT, LEVEL_WF, RA_ACT_02, RI_STAR, 300, 5550, 0), + RC(RC_WF_STAR_03_WILD_BLUE, RCTYPE_STAR_DEFAULT, LEVEL_WF, RA_ACT_03, RI_STAR, -2500, 1500, -750), + RC(RC_WF_STAR_04_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_WF, RA_ACT_04, RI_STAR, 4600, 550, 2500), + RC(RC_WF_STAR_05_CAGED_ISLAND, RCTYPE_STAR_DEFAULT, LEVEL_WF, RA_ACT_05, RI_STAR, 2880, 4300, 190), + RC(RC_WF_STAR_06_WALL, RCTYPE_STAR_DEFAULT, LEVEL_WF, RA_ACT_06, RI_STAR, 590, 2450, 2650), + RC(RC_WF_STAR_07_100_COIN, RCTYPE_STAR_DEFAULT, LEVEL_WF, RA_ACT_COIN, RI_STAR, 0, 0, 0), + RC(RC_WMOTR_RED_COIN_01, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, -2980, 3990, -4248), + RC(RC_WMOTR_RED_COIN_02, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, 2735, 3140, -3085), + RC(RC_WMOTR_RED_COIN_03, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, -3640, 4600, -4200), + RC(RC_WMOTR_RED_COIN_04, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, 4400, 240, 80), + RC(RC_WMOTR_RED_COIN_05, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, 3440, -2680, 5240), + RC(RC_WMOTR_RED_COIN_06, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, -600, -1360, 5040), + RC(RC_WMOTR_RED_COIN_07, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, 320, 1725, 40), + RC(RC_WMOTR_RED_COIN_08, RCTYPE_COIN_RED, LEVEL_WMOTR, RA_ACT_NONE, RI_COIN_RED, -2560, 4600, -4800), + RC(RC_WMOTR_STAR_RED_COINS, RCTYPE_STAR_RED_COIN, LEVEL_WMOTR, RA_ACT_01, RI_STAR, -160, 1950, -470), }; // clang-format on @@ -396,14 +401,26 @@ RandoCheckId GetCheckByLocation(int16_t posX, int16_t posY, int16_t posZ) { return RC_UNKNOWN; } +RandoCheckId GetCheckByLevelAct(int16_t levelId, int16_t randoAct) { + for (auto& [randoCheckId, randoStaticCheck] : Rando::StaticData::Checks) { + if (gCurrLevelNum != randoStaticCheck.levelId) { + continue; + } + if (randoAct == randoStaticCheck.actData) { + return randoCheckId; + } + } + return RC_UNKNOWN; +} + RandoStaticCheck GetShuffledRandoStaticCheck(s16 x, s16 y, s16 z) { RandoStaticCheck randoStaticCheck; RandoCheckId randoCheckId = Rando::StaticData::GetCheckByLocation(x, y, z); int16_t levelId = Rando::StaticData::Checks[randoCheckId].levelId; randoStaticCheck = Rando::StaticData::Checks[randoCheckId]; - randoStaticCheck.randoItemId = Rando::StaticData::GetShuffledRandoItem(levelId - 1, randoCheckId); - randoStaticCheck.actData = Rando::StaticData::GetShuffledRandoAct(levelId - 1, randoCheckId); + randoStaticCheck.randoItemId = Rando::StaticData::GetShuffledRandoItem(randoCheckId); + randoStaticCheck.actData = Rando::StaticData::GetShuffledRandoAct(randoCheckId); return randoStaticCheck; } diff --git a/src/port/Rando/StaticData/Entrances.cpp b/src/port/Rando/StaticData/Entrances.cpp new file mode 100644 index 000000000..06d6208bd --- /dev/null +++ b/src/port/Rando/StaticData/Entrances.cpp @@ -0,0 +1,55 @@ +#include "StaticData.h" +#include "port/ShipUtils.h" + +#include "port/hooks/list/PlayerEvent.h" +#include "port/mods/PortEnhancements.h" + +extern "C" { +#include "game/area.h" +} + +namespace Rando { + +namespace StaticData { +// std::array CheckNames = std::array(); + +#define RE(id, destId, type, death, deathArea) \ + { \ + id, { \ + id, #id, destId, type, death, deathArea \ + } \ + } + +// clang-format off +std::map Entrances = { + RE(RE_UNKNOWN, LEVEL_UNKNOWN_1, RETYPE_NONE, WARP_NODE_00, REAREA_NONE), + RE(RE_BBH, LEVEL_BBH, RETYPE_PAINTING, WARP_NODE_0B, REAREA_01), + RE(RE_BITDW, LEVEL_BITDW, RETYPE_BOWSER, WARP_NODE_25, REAREA_01), + RE(RE_BITFS, LEVEL_BITFS, RETYPE_BOWSER, WARP_NODE_68, REAREA_03), + RE(RE_BITS, LEVEL_BITS, RETYPE_BOWSER, WARP_NODE_6B, REAREA_02), + RE(RE_BOB, LEVEL_BOB, RETYPE_PAINTING, WARP_NODE_64, REAREA_01), + RE(RE_CCM, LEVEL_CCM, RETYPE_PAINTING, WARP_NODE_65, REAREA_01), + RE(RE_COTMC, LEVEL_COTMC, RETYPE_CAP, WARP_NODE_66, REAREA_03), + RE(RE_DDD, LEVEL_DDD, RETYPE_PAINTING, WARP_NODE_67, REAREA_03), + RE(RE_HMC, LEVEL_HMC, RETYPE_PAINTING, WARP_NODE_66, REAREA_03), + RE(RE_JRB, LEVEL_JRB, RETYPE_PAINTING, WARP_NODE_67, REAREA_01), + RE(RE_LLL, LEVEL_LLL, RETYPE_PAINTING, WARP_NODE_64, REAREA_03), + RE(RE_PSS, LEVEL_PSS, RETYPE_SECRET, WARP_NODE_23, REAREA_01), + RE(RE_RR, LEVEL_RR, RETYPE_PAINTING, WARP_NODE_6C, REAREA_02), + RE(RE_SA, LEVEL_SA, RETYPE_SECRET, WARP_NODE_28, REAREA_01), + RE(RE_SL, LEVEL_SL, RETYPE_PAINTING, WARP_NODE_68, REAREA_02), + RE(RE_SSL, LEVEL_SSL, RETYPE_PAINTING, WARP_NODE_65, REAREA_03), + RE(RE_THI, LEVEL_THI, RETYPE_PAINTING, WARP_NODE_69, REAREA_02), + RE(RE_TOTWC, LEVEL_TOTWC, RETYPE_CAP, WARP_NODE_23, REAREA_01), + RE(RE_TTC, LEVEL_TTC, RETYPE_PAINTING, WARP_NODE_67, REAREA_02), + RE(RE_TTM, LEVEL_TTM, RETYPE_PAINTING, WARP_NODE_66, REAREA_02), + RE(RE_VCUTM, LEVEL_VCUTM, RETYPE_CAP, WARP_NODE_06, REAREA_01), + RE(RE_WDW, LEVEL_WDW, RETYPE_PAINTING, WARP_NODE_64, REAREA_02), + RE(RE_WF, LEVEL_WF, RETYPE_PAINTING, WARP_NODE_66, REAREA_01), + RE(RE_WMOTR, LEVEL_WMOTR, RETYPE_SECRET, WARP_NODE_0A, REAREA_02), +}; + +// clang-format on + +} // namespace StaticData +} // namespace Rando \ No newline at end of file diff --git a/src/port/Rando/StaticData/Items.cpp b/src/port/Rando/StaticData/Items.cpp index 6ecd77676..26447728a 100644 --- a/src/port/Rando/StaticData/Items.cpp +++ b/src/port/Rando/StaticData/Items.cpp @@ -60,12 +60,8 @@ int16_t GetModelByBehavior(const BehaviorScript* behavior) { return NULL; } -RandoItemId GetShuffledRandoItem(int16_t levelId, RandoCheckId randoCheckId) { - if (levelId < 0 || levelId >= Rando::Logic::shuffledList.size()) { - return RI_UNKNOWN; - } - - for (auto& entry : Rando::Logic::shuffledList[levelId]) { +RandoItemId GetShuffledRandoItem(RandoCheckId randoCheckId) { + for (auto& entry : Rando::Logic::shuffledPool) { if (entry.randoCheckId == randoCheckId) { return entry.randoItemId; } @@ -73,12 +69,8 @@ RandoItemId GetShuffledRandoItem(int16_t levelId, RandoCheckId randoCheckId) { return RI_UNKNOWN; } -RandoAct GetShuffledRandoAct(int16_t levelId, RandoCheckId randoCheckId) { - if (levelId < 0 || levelId >= Rando::Logic::shuffledList.size()) { - return RA_ACT_NONE; - } - - for (auto& entry : Rando::Logic::shuffledList[levelId]) { +RandoAct GetShuffledRandoAct(RandoCheckId randoCheckId) { + for (auto& entry : Rando::Logic::shuffledPool) { if (entry.randoCheckId == randoCheckId) { return entry.randoAct; } diff --git a/src/port/Rando/StaticData/Options.cpp b/src/port/Rando/StaticData/Options.cpp index 49c69932f..0fda09d4b 100644 --- a/src/port/Rando/StaticData/Options.cpp +++ b/src/port/Rando/StaticData/Options.cpp @@ -13,11 +13,15 @@ namespace StaticData { // clang-format off std::map Options = { - RO(RO_LOGIC, RO_LOGIC_NO_LOGIC), - RO(RO_SHUFFLE_COINS_BLUE, RO_GENERIC_OFF), - RO(RO_SHUFFLE_COINS_RED, RO_GENERIC_OFF), - RO(RO_SHUFFLE_RED_COIN_STARS, RO_GENERIC_OFF), - RO(RO_SHUFFLE_STARS, RO_GENERIC_OFF), + RO(RO_LOGIC, RO_LOGIC_NO_LOGIC), + RO(RO_SHUFFLE_COINS_BLUE, RO_GENERIC_OFF), + RO(RO_SHUFFLE_COINS_RED, RO_GENERIC_OFF), + RO(RO_SHUFFLE_ENTRANCES_BOWSER, RO_GENERIC_OFF), + RO(RO_SHUFFLE_ENTRANCES_CAP, RO_GENERIC_OFF), + RO(RO_SHUFFLE_ENTRANCES_PAINTING, RO_GENERIC_OFF), + RO(RO_SHUFFLE_ENTRANCES_SECRET, RO_GENERIC_OFF), + RO(RO_SHUFFLE_RED_COIN_STARS, RO_GENERIC_OFF), + RO(RO_SHUFFLE_STARS, RO_GENERIC_OFF), }; std::unordered_map logicOptions = { diff --git a/src/port/Rando/StaticData/StaticData.h b/src/port/Rando/StaticData/StaticData.h index ab370e5f1..b4f94d7fb 100644 --- a/src/port/Rando/StaticData/StaticData.h +++ b/src/port/Rando/StaticData/StaticData.h @@ -45,14 +45,27 @@ struct RandoStaticItem { }; RandoCheckId GetCheckByLocation(int16_t posX, int16_t posY, int16_t posZ); +RandoCheckId GetCheckByLevelAct(int16_t levelId, int16_t randoAct); extern std::map Items; int16_t GetModelByRandoItem(RandoItemId randoItem); const BehaviorScript *GetBehaviorByModel(int16_t modelId); int16_t GetModelByBehavior(const BehaviorScript* behavior); -RandoItemId GetShuffledRandoItem(int16_t currentCourse, RandoCheckId randoCheckId); -RandoAct GetShuffledRandoAct(int16_t currentCourse, RandoCheckId randoCheckId); +RandoItemId GetShuffledRandoItem(RandoCheckId randoCheckId); +RandoAct GetShuffledRandoAct(RandoCheckId randoCheckId); + +struct RandoStaticEntrance { + RandoEntranceId randoEntranceId; + const char* name; + int16_t destinationId; + RandoEntranceType randoEntranceType; + WarpNodes deathWarpId; + int16_t deathArea; +}; + +extern std::map Entrances; + struct RandoStaticOption { RandoOptionId randoOptionId; diff --git a/src/port/Rando/Types.h b/src/port/Rando/Types.h index 7b25d9eca..0cdb3a460 100644 --- a/src/port/Rando/Types.h +++ b/src/port/Rando/Types.h @@ -81,6 +81,11 @@ typedef enum { RC_BOB_STAR_05_WINGS, RC_BOB_STAR_06_CHAIN_CHOMP, RC_BOB_STAR_07_100_COIN, + RC_CASTLE_STAR_01_TOAD_BASEMENT, + RC_CASTLE_STAR_02_TOAD_2ND_FLOOR, + RC_CASTLE_STAR_03_TOAD_3RD_FLOOR, + RC_CASTLE_STAR_04_MIPS_FIRST, + RC_CASTLE_STAR_05_MIPS_SECOND, RC_CCM_BLUE_COIN_01, RC_CCM_BLUE_COIN_02, RC_CCM_BLUE_COIN_03, @@ -373,6 +378,50 @@ typedef enum { RC_MAX, } RandoCheckId; +typedef enum { + RETYPE_NONE, + RETYPE_BOWSER, + RETYPE_CAP, + RETYPE_PAINTING, + RETYPE_SECRET, +} RandoEntranceType; + +typedef enum { + REAREA_NONE, + REAREA_01, + REAREA_02, + REAREA_03, +} RandoEntranceDeathArea; + +typedef enum { + RE_UNKNOWN, + RE_BBH, + RE_BITDW, + RE_BITFS, + RE_BITS, + RE_BOB, + RE_CCM, + RE_COTMC, + RE_DDD, + RE_HMC, + RE_JRB, + RE_LLL, + RE_PSS, + RE_RR, + RE_SA, + RE_SL, + RE_SSL, + RE_THI, + RE_TOTWC, + RE_TTC, + RE_TTM, + RE_VCUTM, + RE_WDW, + RE_WF, + RE_WMOTR, + RE_MAX, +} RandoEntranceId; + typedef enum { RITYPE_UNKNOWN, RITYPE_COIN_BLUE, @@ -397,6 +446,10 @@ typedef enum { RO_LOGIC, RO_SHUFFLE_COINS_BLUE, RO_SHUFFLE_COINS_RED, + RO_SHUFFLE_ENTRANCES_BOWSER, + RO_SHUFFLE_ENTRANCES_CAP, + RO_SHUFFLE_ENTRANCES_PAINTING, + RO_SHUFFLE_ENTRANCES_SECRET, RO_SHUFFLE_RED_COIN_STARS, RO_SHUFFLE_STARS, RO_MAX, @@ -433,4 +486,77 @@ typedef enum { TYPE_STAR, } RandoObjectType; +// TODO: Remove once Decomp Update is done, this lives in level_update.h + +typedef enum { + WARP_NODE_00, + WARP_NODE_01, + WARP_NODE_02, + WARP_NODE_03, + WARP_NODE_04, + WARP_NODE_05, + WARP_NODE_06, + WARP_NODE_07, + WARP_NODE_08, + WARP_NODE_09, + WARP_NODE_0A, + WARP_NODE_0B, + WARP_NODE_0C, + WARP_NODE_0D, + WARP_NODE_0E, + WARP_NODE_0F, + WARP_NODE_10, + WARP_NODE_11, + WARP_NODE_14 = 0x14, + WARP_NODE_15, + WARP_NODE_16, + WARP_NODE_17, + WARP_NODE_18, + WARP_NODE_19, + WARP_NODE_1A, + WARP_NODE_1B, + WARP_NODE_1C, + WARP_NODE_1D, + WARP_NODE_1E, + WARP_NODE_1F, + WARP_NODE_20, + WARP_NODE_21, + WARP_NODE_22, + WARP_NODE_23, + WARP_NODE_24, + WARP_NODE_25, + WARP_NODE_26, + WARP_NODE_27, + WARP_NODE_28, + WARP_NODE_29, + WARP_NODE_2A, + WARP_NODE_32 = 0x32, + WARP_NODE_33, + WARP_NODE_34, + WARP_NODE_35, + WARP_NODE_36, + WARP_NODE_37, + WARP_NODE_38, + WARP_NODE_3A = 0x3A, + WARP_NODE_64 = 0x64, + WARP_NODE_65, + WARP_NODE_66, + WARP_NODE_67, + WARP_NODE_68, + WARP_NODE_69, + WARP_NODE_6B = 0x6B, + WARP_NODE_6C, + WARP_NODE_6D, + + // Special Warps + WARP_NODE_SUCCESS = 0xF0, + WARP_NODE_DEATH = 0xF1, + WARP_NODE_TOTWC = 0xF2, + WARP_NODE_WARP_FLOOR = 0xF3, + WARP_NODE_CREDITS_START = 0xF8, + WARP_NODE_CREDITS_MIN = WARP_NODE_CREDITS_START, + WARP_NODE_CREDITS_NEXT = 0xF9, + WARP_NODE_CREDITS_END = 0xFA +} WarpNodes; + #endif // RANDO_TYPES_H diff --git a/src/port/ShipUtils.cpp b/src/port/ShipUtils.cpp index becf3805b..8023ae685 100644 --- a/src/port/ShipUtils.cpp +++ b/src/port/ShipUtils.cpp @@ -2,7 +2,11 @@ #include extern "C" { +#include "sm64.h" #include "macros.h" +#include "include/assets/textures/segment2.h" +#include "include/level_table.h" +#include "include/course_table.h" } constexpr f32 fourByThree = 4.0f / 3.0f; @@ -11,11 +15,44 @@ extern "C" bool Ship_IsCStringEmpty(const char* str) { return str == NULL || str[0] == '\0'; } +char seedString[MAX_SEED_STRING_SIZE]; +u32 finalSeed = 0; + +extern uint32_t Ship_Hash(std::string str) { + // FNV-1a + const size_t len = str.size(); + uint32_t hval = 0x811c9dc5; + for (size_t pos = 0; pos < len; pos++) { + hval ^= (uint32_t)str[pos]; + hval *= 0x01000193; + } + return hval; +} + std::vector levelAbbreviations = { "BOB", "WF", "JRB", "CCM", "BBH", "HMC", "LLL", "SSL", "DDD", "SL", "WDW", "TTM", "THI", "TTC", "RR", "BITDW", "BITFS", "BITS", "PSS", "COTMC", "TOTWC", "VCUTM", "WMOTR", "SA", }; +std::map levelToCourseMap = { + { LEVEL_BBH, COURSE_BBH }, { LEVEL_CASTLE, COURSE_NONE }, { LEVEL_CCM, COURSE_CCM }, + { LEVEL_HMC, COURSE_HMC }, { LEVEL_SSL, COURSE_SSL }, { LEVEL_BOB, COURSE_BOB }, + { LEVEL_SL, COURSE_SL }, { LEVEL_WDW, COURSE_WDW }, { LEVEL_JRB, COURSE_JRB }, + { LEVEL_THI, COURSE_THI }, { LEVEL_TTC, COURSE_TTC }, { LEVEL_RR, COURSE_RR }, + { LEVEL_BITDW, COURSE_BITDW }, { LEVEL_VCUTM, COURSE_VCUTM }, { LEVEL_BITFS, COURSE_BITFS }, + { LEVEL_SA, COURSE_SA }, { LEVEL_BITS, COURSE_BITS }, { LEVEL_LLL, COURSE_LLL }, + { LEVEL_DDD, COURSE_DDD }, { LEVEL_WF, COURSE_WF }, { LEVEL_PSS, COURSE_PSS }, + { LEVEL_COTMC, COURSE_COTMC }, { LEVEL_TOTWC, COURSE_TOTWC }, { LEVEL_WMOTR, COURSE_WMOTR }, + { LEVEL_TTM, COURSE_TTM }, +}; + +int16_t Ship_GetCourseByLevel(int16_t levelId) { + if (levelToCourseMap.find(levelId) != levelToCourseMap.end()) { + return levelToCourseMap.at(levelId); + } + return COURSE_NONE; +} + // Build vertex coordinates for a quad command // In order of top left, top right, bottom left, then bottom right // Supports flipping the texture horizontally @@ -90,3 +127,28 @@ std::string convertEnumToReadableName(const std::string& input) { return result; } + +std::array miscellaneousTextures = { + texture_hud_char_star, + texture_hud_char_mario_head, + texture_hud_char_coin, +}; + +std::array digitList = { texture_hud_char_0, texture_hud_char_1, texture_hud_char_2, + texture_hud_char_3, texture_hud_char_4, texture_hud_char_5, + texture_hud_char_6, texture_hud_char_7, texture_hud_char_8, + texture_hud_char_9 }; + +void LoadGuiTextures() { + for (const auto entry : miscellaneousTextures) { + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry, entry, ImVec4(1, 1, 1, 1)); + } + for (const auto entry : digitList) { + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry, entry, ImVec4(1, 1, 1, 1)); + } + + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("Red Coin Icon", texture_hud_char_coin, + ImVec4(1, 0, 0, 1)); + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("Blue Coin Icon", texture_hud_char_coin, + ImVec4(0.20f, 0.20f, 1, 1)); +} diff --git a/src/port/ShipUtils.h b/src/port/ShipUtils.h index 860aff009..40396de4f 100644 --- a/src/port/ShipUtils.h +++ b/src/port/ShipUtils.h @@ -5,13 +5,21 @@ #ifdef __cplusplus +#define MAX_SEED_STRING_SIZE 1024 +extern char seedString[MAX_SEED_STRING_SIZE]; +extern u32 finalSeed; + +uint32_t Ship_Hash(std::string str); + void LoadGuiTextures(); +extern std::array digitList; extern "C" { #endif bool Ship_IsCStringEmpty(const char* str); std::string convertEnumToReadableName(const std::string& input); +int16_t Ship_GetCourseByLevel(int16_t levelId); #ifdef __cplusplus } diff --git a/src/port/data/SaveConversion.h b/src/port/data/SaveConversion.h new file mode 100644 index 000000000..b50d188a1 --- /dev/null +++ b/src/port/data/SaveConversion.h @@ -0,0 +1,144 @@ +#pragma once + +#include "game/save_file.h" +#include "port/Rando/Rando.h" +#include "port/Rando/Spoiler/Spoiler.h" + +#include +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(RandoSaveCheck, randoItemId, randoAct, obtained, skipped) +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(RandoSaveEntrance, randoEntranceId, destinationId, randoEntranceType, deathWarpId, deathArea) +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(RandoSaveData, randoSaveChecks, randoSaveEntrances, finalSeed) + +using json = nlohmann::json; + +#define SAVE_FILE_VERSION 1 + +static std::string entries[] = { + "CG", "BOB", "WF", "JRB", "CCM", "BBH", "HMC", "LLL", "SSL", "DDD", "SL", + "WDW", "TTM", "THI", "TTC", "RR", "BITDW", "BITFS", "BITS", "PSS", "COTMC", + "TOTWC", "VCUTM", "WMOTR", "SA", "CAKE_END" +}; + +template +T GetSafeEntry(const json& node, const std::string& key) { + if(!node.contains(key)) { + auto dump = nlohmann::json(node).dump(4); + throw std::runtime_error("JSON missing the '" + key + "' entry\nProblematic JSON:\n" + dump); + } + + return node.at(key).get(); +} + +template +T GetSafeEntry(const json& node, const std::string& key, const T& def) { + if(!node.contains(key)) { + return def; + } + + return node.at(key).get(); +} + +inline void from_json(const json& j, ShipSaveData& save) { + save.saveType = j.value("saveType", SAVETYPE_VANILLA); + + if (save.saveType == SAVETYPE_RANDO) { + j["randoSaveData"].get_to(save.randoSaveData); + } +} + +inline void to_json(json& j, const ShipSaveData& save) { + j = json{ { "saveType", save.saveType } }; + + if (save.saveType == SAVETYPE_RANDO) { + j["randoSaveData"] = save.randoSaveData; + } +} + +inline void to_json(json& j, const SaveFile& save) { + json stars = {}; + json coins = {}; + json cap = { + { "x", save.capPos[0] }, + { "y", save.capPos[1] }, + { "z", save.capPos[2] }, + }; + + for (size_t i = 0; i < COURSE_COUNT; i++) { + stars[entries[i]] = save.courseStars[i]; + } + + for (size_t i = 0; i < COURSE_STAGES_COUNT; i++) { + coins[entries[i]] = save.courseCoinScores[i]; + } + + j = json{ + { "version", SAVE_FILE_VERSION }, + { "capLevel", save.capLevel }, + { "capArea", save.capArea }, + { "capPos", cap }, + { "flags", save.flags }, + { "courseStars", stars }, + { "courseCoinScores", coins }, + { "shipSaveData", save.shipSaveData } + }; +} + +inline void LoadSaveFileV1(const json& j, SaveFile& save) { + json capPosJson = GetSafeEntry(j, "capPos"); + save.capLevel = GetSafeEntry(j, "capLevel", 0); + save.capArea = GetSafeEntry(j, "capArea", 0); + save.capPos[0] = GetSafeEntry(capPosJson, "x", 0); + save.capPos[1] = GetSafeEntry(capPosJson, "y", 0); + save.capPos[2] = GetSafeEntry(capPosJson, "z", 0); + save.flags = GetSafeEntry(j, "flags", 0); + + json starsJson = GetSafeEntry(j, "courseStars"); + for (size_t i = 0; i < COURSE_COUNT; i++) { + save.courseStars[i] = GetSafeEntry(starsJson, entries[i]); + } + + json coinsJson = GetSafeEntry(j, "courseCoinScores"); + for (size_t i = 0; i < COURSE_STAGES_COUNT; i++) { + save.courseCoinScores[i] = GetSafeEntry(coinsJson, entries[i]); + } + + if (j.contains("shipSaveData")) { + j["shipSaveData"].get_to(save.shipSaveData); + } +} + +inline void from_json(const json& j, SaveFile& save) { + uint32_t version = GetSafeEntry(j, "version"); + if(version == 1) { + LoadSaveFileV1(j, save); + } +} + +inline void to_json(json& j, const MainMenuSaveData& menu) { + json coins = json::array(); + for (size_t i = 0; i < NUM_SAVE_FILES; i++) { + coins.push_back(menu.coinScoreAges[i]); + } + + j = json{ + { "version", SAVE_FILE_VERSION }, + { "coinScoreAges", coins }, + { "soundMode", menu.soundMode } + }; +} + +inline void LoadMainMenuSaveDataV1(const json& j, MainMenuSaveData& menu) { + json coinsJson = GetSafeEntry(j, "coinScoreAges"); + for (size_t i = 0; i < NUM_SAVE_FILES; i++) { + menu.coinScoreAges[i] = coinsJson.at(i).get(); + } + + menu.soundMode = GetSafeEntry(j, "soundMode", 0); +} + +inline void from_json(const json& j, MainMenuSaveData& menu) { + uint32_t version = GetSafeEntry(j, "version"); + if(version == 1) { + LoadMainMenuSaveDataV1(j, menu); + } +} \ No newline at end of file diff --git a/src/port/data/Saves.cpp b/src/port/data/Saves.cpp new file mode 100644 index 000000000..b44bdd1f9 --- /dev/null +++ b/src/port/data/Saves.cpp @@ -0,0 +1,113 @@ +#include "Saves.h" + +#include "sm64.h" +#include "port/ShipInit.hpp" +#include "port/data/SaveConversion.h" + +#include +#include +namespace fs = std::filesystem; + +extern "C" { +extern struct SaveBuffer gSaveBuffer; +} + +static void Init() { + // Create saves directory if it doesn't exist + fs::path dir("saves"); + if (!fs::exists(dir)) { + fs::create_directory(dir); + } +} + +extern "C" { +void RestoreMainMenuData(int32_t srcSlot) { + int32_t destSlot = srcSlot ^ 1; + + bcopy(&gSaveBuffer.menuData[srcSlot], &gSaveBuffer.menuData[destSlot], sizeof(gSaveBuffer.menuData[destSlot])); +} + +void RestoreSaveFileData(int32_t fileIndex, int32_t srcSlot) { + int32_t destSlot = srcSlot ^ 1; + + bcopy(&gSaveBuffer.files[fileIndex][srcSlot], &gSaveBuffer.files[fileIndex][destSlot], + sizeof(gSaveBuffer.files[fileIndex][destSlot])); +} + +void SaveFileDoSave(int32_t fileIndex) { + std::ofstream file(fs::path("saves/save_" + std::to_string(fileIndex) + ".json"), std::ios::out); + if (!file.is_open()) { + return; + } + + json j = gSaveBuffer.files[fileIndex][0]; + file << j.dump(1); + file.close(); +} + +bool ShouldLoadOldSaveFile(void) { + fs::path save("default.sav"); + return fs::exists(save); +} + +void SaveFileLoadAll(void) { + fs::path save("default.sav"); + if (fs::exists(save)) { + for (int32_t fileIndex = 0; fileIndex < NUM_SAVE_FILES; fileIndex++) { + SaveFileDoSave(fileIndex); + } + // Move old save files to backup + fs::rename(save, "default.sav.bak"); + return; + } + + // Read save files + for (int32_t fileIndex = 0; fileIndex < NUM_SAVE_FILES; fileIndex++) { + fs::path filepath = fs::path("saves/save_" + std::to_string(fileIndex) + ".json"); + if (!fs::exists(filepath)) { + continue; + } + + std::ifstream file(filepath, std::ios::in); + if (!file.is_open()) { + continue; + } + + json j; + file >> j; + // Migrate Existing Saves to Include Vanilla ShipSaveData. + if (!j.contains("shipSaveData")) { + j["shipSaveData"] = json::object(); + j["shipSaveData"]["saveType"] = SAVETYPE_VANILLA; + } + + gSaveBuffer.files[fileIndex][0] = j.get(); + file.close(); + } + + // Read global save file + fs::path globalpath = fs::path("saves/global.json"); + if (fs::exists(globalpath)) { + std::ifstream file(globalpath, std::ios::in); + if (file.is_open()) { + json j; + file >> j; + gSaveBuffer.menuData[0] = j.get(); + file.close(); + } + } +} + +void SaveMainMenuData(void) { + std::ofstream file(fs::path("saves/global.json"), std::ios::out); + if (!file.is_open()) { + return; + } + + json j = gSaveBuffer.menuData[0]; + file << j.dump(1); + file.close(); +} +} + +static RegisterShipInitFunc initFunc(Init); \ No newline at end of file diff --git a/src/port/data/Saves.h b/src/port/data/Saves.h new file mode 100644 index 000000000..37ea2a5f1 --- /dev/null +++ b/src/port/data/Saves.h @@ -0,0 +1,22 @@ +#pragma once + +#include "game/save_file.h" + +#define CALL(func, ...) \ + func(__VA_ARGS__); \ + return; + +#ifdef __cplusplus +extern "C" { +#endif + +void RestoreMainMenuData(int32_t srcSlot); +void RestoreSaveFileData(int32_t fileIndex, int32_t srcSlot); +void SaveFileDoSave(int32_t fileIndex); +void SaveFileLoadAll(void); +void SaveMainMenuData(void); +bool ShouldLoadOldSaveFile(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/src/port/hooks/list/PlayerEvent.h b/src/port/hooks/list/PlayerEvent.h index f8eb21590..d1ef1d264 100644 --- a/src/port/hooks/list/PlayerEvent.h +++ b/src/port/hooks/list/PlayerEvent.h @@ -52,6 +52,20 @@ DEFINE_EVENT(ModifyObjectBehavior, int16_t model; ); +DEFINE_EVENT(ModifyRedCoinCount, + int8_t* redCoinsCollected; +); + +DEFINE_EVENT(ModifyObjectVisibility, + struct Object* object; +); + +DEFINE_EVENT(ChangeLevel, + int16_t sourceWarpNode; + struct WarpNode* warpNode; + int32_t* delayedWarpArg; +); + DEFINE_EVENT(OnGameFileLoad, s32 fileNum; ); diff --git a/src/port/importer/AssetArrayFactory.cpp b/src/port/importer/AssetArrayFactory.cpp index 6b704acb8..2df78f15e 100644 --- a/src/port/importer/AssetArrayFactory.cpp +++ b/src/port/importer/AssetArrayFactory.cpp @@ -18,7 +18,12 @@ ResourceFactoryBinaryAssetArrayV0::ReadResource(std::shared_ptr file auto count = reader->ReadUInt32(); for (size_t i = 0; i < count; i++) { - auto asset = Ship::Context::GetInstance()->GetResourceManager()->LoadResource(reader->ReadUInt64()); + auto path = ResourceGetNameByCrc(reader->ReadUInt64()); + if (path == nullptr) { + array->mPtrs.push_back(0); + continue; + } + auto asset = Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(path); if (asset != nullptr) { auto data = asset->GetInitData(); if (data->Type == (uint32_t)Fast::ResourceType::Texture) { diff --git a/src/port/mods/PortEnhancements.cpp b/src/port/mods/PortEnhancements.cpp index a21692ea6..9701af681 100644 --- a/src/port/mods/PortEnhancements.cpp +++ b/src/port/mods/PortEnhancements.cpp @@ -9,6 +9,10 @@ #include "assets/bin/segment2.h" #include "port/ShipInit.hpp" #include "port/Rando/Rando.h" +#include "port/ShipUtils.h" + +int8_t textRand[] = { 0x1B, 0x0A, 0x17, 0x0D, 0xFF }; +int8_t textMarioRando[] = { 0x1B, 0x0A, 0x17, 0x0D, 0x18, 0x16, 0x12, 0x23, 0x0E, 0x1B, 0xFF }; static const Mtx matrix_patch_identity = { { { 1.0f, 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 0.0f, 1.0f } } @@ -103,10 +107,14 @@ void PortEnhancements_Register() { REGISTER_EVENT(SpawnCoinStar); REGISTER_EVENT(ModifyDefaultStar); REGISTER_EVENT(ModifyObjectBehavior); + REGISTER_EVENT(ModifyRedCoinCount); + REGISTER_EVENT(ModifyObjectVisibility); + REGISTER_EVENT(ChangeLevel); REGISTER_EVENT(OnGameFileLoad); REGISTER_EVENT(OnGameFileSave); Rando::Init(); + LoadGuiTextures(); } static RegisterShipInitFunc initFunc(PortEnhancements_Init); \ No newline at end of file diff --git a/src/port/mods/PortEnhancements.h b/src/port/mods/PortEnhancements.h index 435c8da3a..abee9f458 100644 --- a/src/port/mods/PortEnhancements.h +++ b/src/port/mods/PortEnhancements.h @@ -1,9 +1,14 @@ #pragma once +#include #ifdef __cplusplus extern "C" { #endif +extern int8_t textRand[]; +extern int8_t textMarioRando[]; + + void PortEnhancements_Register(); void PortEnhancements_Init(); void PortEnhancements_Exit(); diff --git a/src/port/ui/GhostshipMenuDevTools.cpp b/src/port/ui/GhostshipMenuDevTools.cpp index ceeeb037c..ae861abb3 100644 --- a/src/port/ui/GhostshipMenuDevTools.cpp +++ b/src/port/ui/GhostshipMenuDevTools.cpp @@ -105,21 +105,6 @@ void GhostshipMenu::AddMenuDevTools() { .WindowName("Save Editor") .HideInSearch(true) .Options(WindowButtonOptions().Tooltip("Enables the separate Save Editor Window.")); - - path.sidebarName = "Model Spawn"; - AddSidebarEntry("Dev Tools", path.sidebarName, 1); - AddWidget(path, "Spawn Red Coin", WIDGET_BUTTON) - .Callback([](WidgetInfo& info) { - spawn_object_abs_with_rot(&gMacroObjectDefaultParent, 0, 215, bhvRedCoin, (int16_t)gMarioStates->pos[0], - (int16_t)gMarioStates->pos[1] + 250, (int16_t)gMarioStates->pos[2], 0, 0, 0); - }) - .Options(ButtonOptions().Tooltip("Spawns a Red Coin.")); - AddWidget(path, "Spawn Star", WIDGET_BUTTON) - .Callback([](WidgetInfo& info) { - spawn_object_abs_with_rot(&gMacroObjectDefaultParent, 0, 122, bhvStar, (int16_t)gMarioStates->pos[0], - (int16_t)gMarioStates->pos[1] + 250, (int16_t)gMarioStates->pos[2], 0, 0, 0); - }) - .Options(ButtonOptions().Tooltip("Spawns a Star.")); } } // namespace GhostshipGui diff --git a/src/port/ui/Notification.cpp b/src/port/ui/Notification.cpp index 79f7a1056..2f4bc399d 100644 --- a/src/port/ui/Notification.cpp +++ b/src/port/ui/Notification.cpp @@ -1,6 +1,12 @@ #include "Notification.h" #include +extern "C" { +#include "audio/external.h" +#include "game/sound_init.h" +#include "include/sounds.h" +} + namespace Notification { static uint32_t nextId = 0; @@ -131,7 +137,7 @@ void Emit(Options notification) { } notifications.push_back(notification); if (!notification.mute) { - // TODO: play game notification sound + play_sound(SOUND_MENU_READ_A_SIGN, gGlobalSoundSource); } } diff --git a/src/port/ui/SaveEditor.cpp b/src/port/ui/SaveEditor.cpp index b30e78b6e..871760302 100644 --- a/src/port/ui/SaveEditor.cpp +++ b/src/port/ui/SaveEditor.cpp @@ -1,5 +1,12 @@ #include "SaveEditor.h" #include "UIWidgets.hpp" +#include "port/ui/Notification.h" +#include "port/ShipUtils.h" + +#include "port/Rando/Rando.h" +#include "port/Rando/Logic/Logic.h" +#include "port/Rando/CustomItem/CustomItem.h" +#include "port/Rando/StaticData/StaticData.h" #include #include @@ -9,11 +16,14 @@ extern "C" { #include "game/save_file.h" +#include "include/assets/textures/segment2.h" extern s16 gCurrSaveFileNum; extern SaveBuffer gSaveBuffer; extern MarioState* gMarioState; } +#define WIDGET_COLOR UIWidgets::Colors(CVarGetInteger("gSettings.Menu.Theme", 5)) + #define DEFINE_COURSE(_0, _1, name) name, #define DEFINE_COURSES_END() #define DEFINE_BONUS_COURSE(_0, _1, name) name, @@ -24,6 +34,32 @@ static char courseNames[][31] = { #undef DEFINE_COURSES_END #undef DEFINE_BONUS_COURSE +bool shouldPopUpOpen = false; +RandoCheckId popUpId = RC_UNKNOWN; +std::map objectMap = { + { RI_COIN_BLUE, "Blue Coin Icon" }, + { RI_COIN_RED, "Red Coin Icon" }, + { RI_STAR, texture_hud_char_star }, +}; + +void ModifyStarFlags(bool isObtained, int16_t courseNum, int16_t starAct, int16_t fileNum) { + if (isObtained) { + if (courseNum == COURSE_NONE) { + gSaveBuffer.files[fileNum][0].flags |= (1 << starAct); + } else { + gSaveBuffer.files[fileNum][0].courseStars[courseNum] |= (1 << starAct); + } + } else { + if (courseNum == COURSE_NONE) { + gSaveBuffer.files[fileNum][0].flags &= ~(1 << starAct); + } else { + gSaveBuffer.files[fileNum][0].courseStars[courseNum] &= ~(1 << starAct); + } + } + gMarioState->numStars = save_file_get_total_star_count(fileNum, COURSE_MIN - 1, COURSE_MAX - 1); + save_file_do_save(fileNum); +} + void DrawFlagTableArray32(const FlagTable& flagTable, uint16_t row, uint32_t& flags) { ImGui::PushID((std::to_string(row) + flagTable.name).c_str()); for (int32_t flagIndex = 0; flagIndex < 32; flagIndex++) { @@ -33,82 +69,231 @@ void DrawFlagTableArray32(const FlagTable& flagTable, uint16_t row, uint32_t& fl ImGui::PushID(flagIndex); bool hasDescription = !!flagTable.flagDescriptions.contains(flagIndex); uint32_t bitMask = 1 << flagIndex; - ImGui::PushStyleColor(ImGuiCol_FrameBg, - hasDescription ? ImVec4(0.16f, 0.29f, 0.48f, 0.54f) : ImVec4(0.16f, 0.29f, 0.48f, 0.24f)); + + ImGui::BeginDisabled(!hasDescription); + UIWidgets::PushStyleCheckbox(WIDGET_COLOR); bool flag = (flags & bitMask) != 0; - if (ImGui::Checkbox("##check", &flag)) { + if (UIWidgets::Checkbox("##check", &flag)) { if (flag) { flags |= bitMask; } else { flags &= ~bitMask; } } - ImGui::PopStyleColor(); if (ImGui::IsItemHovered() && hasDescription) { ImGui::BeginTooltip(); ImGui::Text("%s", UIWidgets::WrappedText(flagTable.flagDescriptions.at(flagIndex), 60).c_str()); ImGui::EndTooltip(); } + UIWidgets::PopStyleCheckbox(); + ImGui::EndDisabled(); ImGui::PopID(); } ImGui::PopID(); } -void SaveEditorWindow::DrawElement() { - if (CVarGetInteger(CVAR_WINDOW("SaveEditor"), 0)) { - ImGui::SetNextWindowSize(ImVec2(497, 532), ImGuiCond_FirstUseEver); - if (ImGui::Begin("Save Editor")) { - - ImGui::BeginTabBar("##saveEditorTabs"); - if (ImGui::BeginTabItem("Main Save & Mario Flags")) { - ImGui::Text("Mario Flags"); - DrawFlagTableArray32(flagTables[1], 0, gMarioState->flags); - ImGui::Text("Save File Flags"); - DrawFlagTableArray32(flagTables[0], 0, gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags); - ImGui::EndTabItem(); +void RandoSaveFile() { + gSaveFileModified = true; + save_file_do_save(selectedFileNum); + gMarioState->numStars = save_file_get_total_star_count(selectedFileNum, COURSE_MIN - 1, COURSE_MAX - 1); +} + +void HandlePopUpContext(RandoCheckId randoCheckId) { + if (shouldPopUpOpen && ImGui::BeginPopup("ObjectSubMenu")) { + for (auto& [randoItemId, textureId] : objectMap) { + if (ImGui::ImageButton(std::to_string(randoItemId).c_str(), + Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(textureId), + ImVec2(32.0f, 32.0f))) { + // randoStaticCheck.randoItemId = randoItemId; + auto findIt = + std::find_if(Rando::Logic::shuffledPool.begin(), Rando::Logic::shuffledPool.end(), + [&](const LevelShuffleEntry& entry) { return entry.randoCheckId == randoCheckId; }); + if (findIt != Rando::Logic::shuffledPool.end()) { + findIt->randoItemId = randoItemId; + RANDO_SAVE_CHECKS(selectedFileNum)[randoCheckId].randoItemId = randoItemId; + RandoSaveFile(); + } + ImGui::CloseCurrentPopup(); + shouldPopUpOpen = false; + } + ImGui::SameLine(); + } + ImGui::EndPopup(); + } + if (shouldPopUpOpen && ImGui::BeginPopup("ActSubMenu")) { + for (int i = 0; i < RA_ACT_MAX; i++) { + if (ImGui::ImageButton( + std::to_string(i).c_str(), + Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(digitList[i + 1]), + ImVec2(32.0f, 32.0f))) { + // randoStaticCheck.randoItemId = randoItemId; + auto findIt = + std::find_if(Rando::Logic::shuffledPool.begin(), Rando::Logic::shuffledPool.end(), + [&](const LevelShuffleEntry& entry) { return entry.randoCheckId == randoCheckId; }); + if (findIt != Rando::Logic::shuffledPool.end()) { + findIt->randoAct = (RandoAct)i; + RANDO_SAVE_CHECKS(selectedFileNum)[randoCheckId].randoAct = (RandoAct)i; + RandoSaveFile(); + } + ImGui::CloseCurrentPopup(); + shouldPopUpOpen = false; } + ImGui::SameLine(); + } + ImGui::EndPopup(); + } +} + +void SaveEditorWindow::DrawElement() { + if (gMarioSpawnInfo->model == NULL) { + ImGui::TextColored(UIWidgets::ColorValues.at(UIWidgets::Colors::Orange), "No Save File Loaded"); + return; + } + + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f)); + UIWidgets::PushStyleTabs(WIDGET_COLOR); + ImGui::BeginTabBar("##saveEditorTabs"); + if (ImGui::BeginTabItem("Main Save & Mario Flags")) { + ImGui::Text("Mario Flags"); + DrawFlagTableArray32(flagTables[1], 0, gMarioState->flags); + ImGui::Text("Save File Flags"); + DrawFlagTableArray32(flagTables[0], 0, gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags); + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Course Stars & Coins")) { + if (IS_RANDO(gCurrSaveFileNum - 1)) { + ImGui::SeparatorText("Rando Save Loaded, use the Rando Tab to make changes"); + } + ImGui::BeginDisabled(IS_RANDO(gCurrSaveFileNum - 1)); + for (int i = 0; i < COURSE_COUNT; i++) { + ImGui::PushID(i); + ImGui::Text("%s", courseNames[i]); + if (ImGui::BeginTable("Course Stars", 8, ImGuiTableFlags_SizingFixedFit)) { + ImGui::TableNextColumn(); + for (int s = 0; s < 8; s++) { + if (s <= 6) { + std::string labelStr = "##courseStars" + std::to_string(s); + const char* label = labelStr.c_str(); + bool isChecked = gSaveBuffer.files[gCurrSaveFileNum - 1][0].courseStars[i] & (1 << s); - if (ImGui::BeginTabItem("Course Stars & Coins")) { - for (int i = 0; i < COURSE_COUNT; i++) { - ImGui::Text("%s", courseNames[i]); - std::string invisibleLabelStr = "##courseStars" + std::string(courseNames[i]); - const char* invisibleLabel = invisibleLabelStr.c_str(); - UIWidgets::DrawFlagArray8( - invisibleLabel, gSaveBuffer.files[gCurrSaveFileNum - 1][0].courseStars[COURSE_NUM_TO_INDEX(i)]); - if (i < COURSE_STAGES_COUNT) { - ImGui::SameLine(); - std::string invisibleLabelStr2 = "##courseCoins" + std::string(courseNames[i]); - const char* invisibleLabel2 = invisibleLabelStr2.c_str(); - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4); - ImGui::InputScalar( - invisibleLabel2, ImGuiDataType_U8, - &gSaveBuffer.files[gCurrSaveFileNum - 1][0].courseCoinScores[COURSE_NUM_TO_INDEX(i)], NULL, - NULL, "%u"); + UIWidgets::PushStyleCheckbox(WIDGET_COLOR); + if (UIWidgets::Checkbox(label, &isChecked)) { + ModifyStarFlags(isChecked, i, s, gCurrSaveFileNum - 1); + } + UIWidgets::PopStyleCheckbox(); + } else { + std::string labelStr2 = "##courseCoins" + std::to_string(s); + const char* label2 = labelStr2.c_str(); + int32_t coinCount = gSaveBuffer.files[gCurrSaveFileNum - 1][0].courseCoinScores[i]; + + UIWidgets::PushStyleInput(WIDGET_COLOR); + if (UIWidgets::InputInt(label2, &coinCount, + UIWidgets::InputOptions{} + .Size(ImVec2(50.0f, 0)) + .LabelPosition(UIWidgets::LabelPositions::None))) {} + UIWidgets::PopStyleInput(); } + ImGui::TableNextColumn(); } - ImGui::EndTabItem(); + ImGui::EndTable(); } + ImGui::PopID(); + } + ImGui::EndDisabled(); + ImGui::EndTabItem(); + } + + if (!Rando::Logic::shuffledPool.empty()) { + if (ImGui::BeginTabItem("Rando")) { + if (ImGui::BeginChild("RandoChild")) { + if (ImGui::BeginTable("Rando Save Editor", 4, ImGuiTableFlags_SizingFixedFit)) { + ImGui::TableSetupColumn("Obtained", ImGuiTableColumnFlags_WidthFixed, 32.0f); + ImGui::TableSetupColumn("Check Name"); + ImGui::TableSetupColumn("Item Name"); + ImGui::TableSetupColumn("Course Num", ImGuiTableColumnFlags_WidthFixed, 32.0f); - if (ImGui::BeginTabItem("Rando Helpers")) { - ImGui::SeparatorText("Mario's Position"); - if (ImGui::BeginTable("PosTable", 3, ImGuiTableFlags_SizingFixedFit)) { - ImGui::TableNextColumn(); - ImGui::Text("X: %.2f", gMarioState->pos[0]); - ImGui::TableNextColumn(); - ImGui::Text("Y: %.2f", gMarioState->pos[1]); ImGui::TableNextColumn(); - ImGui::Text("Z: %.2f", gMarioState->pos[2]); + for (auto& entry : Rando::Logic::shuffledPool) { + if (entry.randoCheckId == RC_UNKNOWN) { + continue; + } + Rando::StaticData::RandoStaticCheck randoStaticCheck = + Rando::StaticData::Checks[entry.randoCheckId]; + const char* texture = entry.randoItemId == RI_STAR ? texture_hud_char_star + : entry.randoItemId == RI_COIN_RED ? "Red Coin Icon" + : "Blue Coin Icon"; + + ImGui::PushID(entry.randoCheckId); + if (UIWidgets::Checkbox("##obtained", &entry.obtained)) { + bool toggleTo = entry.obtained; + int16_t courseNumber = Ship_GetCourseByLevel(randoStaticCheck.levelId); + + RANDO_SAVE_CHECKS(selectedFileNum)[entry.randoCheckId].obtained = toggleTo; + + if (entry.randoItemId == RI_STAR) { + ModifyStarFlags(toggleTo, courseNumber, entry.randoAct, selectedFileNum); + } else if ((entry.randoItemId == RI_COIN_BLUE || entry.randoItemId == RI_COIN_RED) && + randoStaticCheck.levelId == gCurrLevelNum) { + int16_t coinChange = entry.randoItemId == RI_COIN_BLUE ? 5 : 2; + if (toggleTo) { + gMarioState->numCoins += coinChange; + } else { + gMarioState->numCoins -= coinChange; + if (gMarioState->numCoins < 0) { + gMarioState->numCoins = 0; + } + } + } + RandoSaveFile(); + + Notification::Emit({ .itemIcon = texture, + .message = Rando::StaticData::Items[entry.randoItemId].name, + .suffix = toggleTo ? "obtained." : "removed." }); + } + + ImGui::TableNextColumn(); + ImGui::TextColored(entry.obtained ? UIWidgets::ColorValues.at(UIWidgets::Colors::Green) + : UIWidgets::ColorValues.at(UIWidgets::Colors::White), + Rando::StaticData::Checks[entry.randoCheckId].name); + + ImGui::TableNextColumn(); + if (ImGui::ImageButton( + randoStaticCheck.name, + Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(texture), + ImVec2(32.0f, 32.0f))) { + popUpId = randoStaticCheck.randoCheckId; + shouldPopUpOpen = true; + ImGui::OpenPopup("ObjectSubMenu"); + } + HandlePopUpContext(popUpId); + + ImGui::TableNextColumn(); + if (entry.randoItemId == RI_STAR) { + if (ImGui::ImageButton( + std::to_string(entry.randoAct).c_str(), + Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( + digitList[entry.randoAct + 1]), + ImVec2(32.0f, 32.0f))) { + popUpId = randoStaticCheck.randoCheckId; + shouldPopUpOpen = true; + ImGui::OpenPopup("ActSubMenu"); + } + } + ImGui::PopID(); + ImGui::TableNextColumn(); + } ImGui::EndTable(); } - ImGui::SeparatorText("Set Data"); - if (UIWidgets::Button("Set Coins to x99")) { - gMarioState->numCoins = 99; - } - ImGui::EndTabItem(); + ImGui::EndChild(); } - - ImGui::EndTabBar(); + ImGui::EndTabItem(); } - ImGui::End(); } + + ImGui::EndTabBar(); + UIWidgets::PopStyleTabs(); + ImGui::PopStyleColor(3); }