diff --git a/soh/soh/Enhancements/Holiday/Fredomato.cpp b/soh/soh/Enhancements/Holiday/Fredomato.cpp index f6a449f51..29fb92bea 100644 --- a/soh/soh/Enhancements/Holiday/Fredomato.cpp +++ b/soh/soh/Enhancements/Holiday/Fredomato.cpp @@ -147,6 +147,8 @@ static Vec3f FindValidPos(f32 distance) { return pos; } } + + return pos; } // TODO: If in hyrule field and treeChopper is on, teleport somewhere else in hyrule field diff --git a/soh/soh/Enhancements/Holiday/Grimey.cpp b/soh/soh/Enhancements/Holiday/Grimey.cpp index d8f26cacf..11d2c7873 100644 --- a/soh/soh/Enhancements/Holiday/Grimey.cpp +++ b/soh/soh/Enhancements/Holiday/Grimey.cpp @@ -105,11 +105,11 @@ static void OnConfigurationChanged() { COND_HOOK(OnPlayerUpdate, CVarGetInteger(CVAR("Hailstorm"), 0), []() { // Every frame has a 1/1000 chance to start a hailstorm if there isn't one already if (hailstormActiveTimer == 0 && rand() % 1000 == 0) { - hailstormActiveTimer = 20 * 10; // Lasts for 20 seconds + hailstormActiveTimer = 20 * 20; // Lasts for 20 seconds } if (hailstormActiveTimer > 0) { hailstormActiveTimer--; - if (rand() % 2 == 0) { + if (rand() % 3 == 0) { int spawned = 0; while (spawned < 1) { Vec3f pos = GET_PLAYER(gPlayState)->actor.world.pos; @@ -198,7 +198,7 @@ static void RegisterMenu() { SohGui::mSohMenu->AddWidget(path, "Hailstorm", WIDGET_CVAR_CHECKBOX) .CVar(CVAR("Hailstorm")) - .Options(UIWidgets::CheckboxOptions().Tooltip("Ever persistent hailstorm throughout hyrule")); + .Options(UIWidgets::CheckboxOptions().Tooltip("Occasional hailstorms throughout hyrule")); } static RegisterShipInitFunc initFunc(OnConfigurationChanged, { diff --git a/soh/soh/Enhancements/Holiday/NotProxySaw.cpp b/soh/soh/Enhancements/Holiday/NotProxySaw.cpp index a76875bb0..b34bade05 100644 --- a/soh/soh/Enhancements/Holiday/NotProxySaw.cpp +++ b/soh/soh/Enhancements/Holiday/NotProxySaw.cpp @@ -4,6 +4,7 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/custom-message/CustomMessageManager.h" #include "include/message_data_fmt.h" +#include "soh/Network/Archipelago/Archipelago.h" extern "C" { #include "macros.h" @@ -879,6 +880,10 @@ static void OnConfigurationChanged() { if (dialogIndex == -1) { if (affection >= TARGET_AFFECTION) { + gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME; + gSaveContext.ship.stats.gameComplete = true; + ArchipelagoClient::GetInstance().SendGameWon(); + gPlayState->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0; gSaveContext.nextCutsceneIndex = 0xFFF2; gPlayState->transitionTrigger = TRANS_TRIGGER_START; diff --git a/soh/soh/Enhancements/RogueLike/ActorBehavior/ActorBehavior.cpp b/soh/soh/Enhancements/RogueLike/ActorBehavior/ActorBehavior.cpp index 2173499a3..976b3263d 100644 --- a/soh/soh/Enhancements/RogueLike/ActorBehavior/ActorBehavior.cpp +++ b/soh/soh/Enhancements/RogueLike/ActorBehavior/ActorBehavior.cpp @@ -2,6 +2,7 @@ #include "soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/ShipInit.hpp" +#include "soh/Notification/Notification.h" extern "C" { #include "variables.h" @@ -38,7 +39,6 @@ static void MiscVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li case VB_GIVE_ITEM_FROM_MAN_ON_ROOF: case VB_GIVE_ITEM_FAIRY_OCARINA: case VB_GIVE_ITEM_WEIRD_EGG: - case VB_GIVE_ITEM_LIGHT_ARROW: case VB_GIVE_ITEM_STRENGTH_1: case VB_GIVE_ITEM_ZELDAS_LETTER: case VB_GIVE_ITEM_OCARINA_OF_TIME: @@ -49,6 +49,31 @@ static void MiscVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li *should = true; break; } + case VB_BE_ELIGIBLE_FOR_RAINBOW_BRIDGE: { + *should = true; + break; + } + case VB_GIVE_ITEM_LIGHT_ARROW: { + if (!gSaveContext.inventory.dungeonItems[SCENE_GANONS_TOWER]) { + Notification::Emit({ + .message = "You obtained Ganon's Boss Key!", + }); + gSaveContext.inventory.dungeonItems[SCENE_GANONS_TOWER] |= 1; + } + *should = false; + break; + } + case VB_BE_ELIGIBLE_FOR_LIGHT_ARROWS: { + *should = true; + + for (uint32_t reward : RogueLike::requiredRewards) { + if (!CHECK_QUEST_ITEM(reward)) { + *should = false; + } + } + + break; + } case VB_GIVE_ITEM_FROM_ANJU_AS_ADULT: { EnNiwLady* enNiwLady = va_arg(args, EnNiwLady*); Flags_SetItemGetInf(ITEMGETINF_2C); @@ -57,6 +82,11 @@ static void MiscVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li *should = false; break; } + case VB_GIVE_ITEM_FROM_ANJU_AS_CHILD: { + Flags_SetItemGetInf(ITEMGETINF_0C); + *should = false; + break; + } default: break; } @@ -75,6 +105,11 @@ static void OnEnemyDefeatHandler(void* actorRef) { static void InitActorBehavior() { COND_HOOK(OnEnemyDefeat, IS_ROGUELIKE, OnEnemyDefeatHandler); COND_HOOK(OnVanillaBehavior, IS_ROGUELIKE, MiscVanillaBehaviorHandler); + + COND_ID_HOOK(ShouldActorInit, ACTOR_DEMO_KEKKAI, IS_ROGUELIKE, [](void* actorRef, bool* should) { + // Prevent the barrier from initializing in Roguelike mode + *should = false; + }); } static RegisterShipInitFunc initFunc(InitActorBehavior, { "IS_ROGUELIKE" }); \ No newline at end of file diff --git a/soh/soh/Enhancements/RogueLike/ActorBehavior/Chests.cpp b/soh/soh/Enhancements/RogueLike/ActorBehavior/Chests.cpp index b4ba23cbf..2b009831a 100644 --- a/soh/soh/Enhancements/RogueLike/ActorBehavior/Chests.cpp +++ b/soh/soh/Enhancements/RogueLike/ActorBehavior/Chests.cpp @@ -38,6 +38,12 @@ static void InitChestsBehavior() { EnBox* enBox = va_arg(args, EnBox*); Actor* actor = (Actor*)enBox; Player* player = GET_PLAYER(gPlayState); + + auto getItemId = actor->params >> 5 & 0x7F; + if (getItemId == GI_KEY_BOSS || getItemId == GI_KEY_SMALL) { + return; + } + Player_SetupWaitForPutAway(gPlayState, player, func_8083A434_overridden); *should = false; }); diff --git a/soh/soh/Enhancements/RogueLike/Choices.hpp b/soh/soh/Enhancements/RogueLike/Choices.hpp index a3410e931..13201aa19 100644 --- a/soh/soh/Enhancements/RogueLike/Choices.hpp +++ b/soh/soh/Enhancements/RogueLike/Choices.hpp @@ -61,6 +61,22 @@ inline bool IsItemInSlot(uint32_t value) { return gSaveContext.inventory.items[SLOT(value)] == ITEM_NONE; } +inline void UpgradeMagic(uint32_t value) { + if (value == ITEM_MAGIC_SMALL) { + gSaveContext.isMagicAcquired = true; + gSaveContext.magicFillTarget = MAGIC_NORMAL_METER; + Magic_Fill(gPlayState); + } else if (value == ITEM_MAGIC_LARGE) { + if (!gSaveContext.isMagicAcquired) { + gSaveContext.isMagicAcquired = true; + } + gSaveContext.isDoubleMagicAcquired = true; + gSaveContext.magicFillTarget = MAGIC_DOUBLE_METER; + gSaveContext.magicLevel = 0; + Magic_Fill(gPlayState); + } +} + #define SONG_CHOICE(song, name) \ { "QUEST_SONG_" #song, name, ITEM_SONG_##song, CanSelectSong, GiveItem } @@ -148,6 +164,10 @@ inline std::vector Equipment = { [](int32_t _) { return CUR_UPG_VALUE(UPG_STRENGTH) == 1; }, GiveItem }, { "ITEM_GAUNTLETS_GOLD", "Golden Gauntlets", ITEM_GAUNTLETS_GOLD, [](int32_t _) { return CUR_UPG_VALUE(UPG_STRENGTH) == 2; }, GiveItem }, + { "ITEM_MAGIC_SMALL", "Magic", ITEM_MAGIC_SMALL, [](int32_t _) { return !gSaveContext.isMagicAcquired; }, + UpgradeMagic }, + { "ITEM_MAGIC_LARGE", "Large Magic", ITEM_MAGIC_LARGE, + [](int32_t _) { return gSaveContext.isMagicAcquired && !gSaveContext.isDoubleMagicAcquired; }, UpgradeMagic }, }; inline std::vector All = { diff --git a/soh/soh/Enhancements/RogueLike/GUI/GUI.cpp b/soh/soh/Enhancements/RogueLike/GUI/GUI.cpp index 94bf337f3..76577cce6 100644 --- a/soh/soh/Enhancements/RogueLike/GUI/GUI.cpp +++ b/soh/soh/Enhancements/RogueLike/GUI/GUI.cpp @@ -14,6 +14,18 @@ namespace SohGui { extern std::shared_ptr mSohMenu; } +#define QUEST_TO_TEXTURE(id) \ + { id, #id } + +std::map questToTexture = { + QUEST_TO_TEXTURE(QUEST_MEDALLION_FOREST), QUEST_TO_TEXTURE(QUEST_MEDALLION_FIRE), + QUEST_TO_TEXTURE(QUEST_MEDALLION_WATER), QUEST_TO_TEXTURE(QUEST_MEDALLION_SPIRIT), + QUEST_TO_TEXTURE(QUEST_MEDALLION_SHADOW), QUEST_TO_TEXTURE(QUEST_MEDALLION_LIGHT), + QUEST_TO_TEXTURE(QUEST_KOKIRI_EMERALD), QUEST_TO_TEXTURE(QUEST_GORON_RUBY), + QUEST_TO_TEXTURE(QUEST_ZORA_SAPPHIRE), QUEST_TO_TEXTURE(QUEST_STONE_OF_AGONY), + QUEST_TO_TEXTURE(QUEST_GERUDO_CARD), QUEST_TO_TEXTURE(QUEST_SKULL_TOKEN), +}; + void RogueLike::GUI::BeginFullscreenDimmed(const char* windowName) { ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->Pos); @@ -271,6 +283,23 @@ void RogueLike::GUI::HUDWindow::Draw() { TableCellVerticalCenteredText(ImVec4(0, 1, 0, 1), statValueStr.c_str()); } + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + TableCellVerticalCenteredText(ImVec4(1, 1, 1, 1), "Required Dungeon Rewards:"); + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + for (auto& reward : RogueLike::requiredRewards) { + if (reward != RogueLike::requiredRewards[0]) { + ImGui::SameLine(); + } + auto tint = CHECK_QUEST_ITEM(reward) ? ImVec4(1, 1, 1, 1) : ImVec4(1, 1, 1, 0.5f); + ImTextureID textureId = + Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(questToTexture[reward]); + ImGui::Image(textureId, ImVec2(25.0f, 25.0f), ImVec2(0.0f, 0.0f), ImVec2(1, 1), tint, + ImVec4(0, 0, 0, 0)); + } + ImGui::EndTable(); if (ImGui::BeginChild("QuestWindow", ImVec2(300.0f, 0))) { @@ -413,12 +442,15 @@ static void InitRogueLikeGUI() { path = { "Holiday", "RogueLike", SECTION_COLUMN_1 }; SohGui::mSohMenu->AddWidget(path, "RogueLikeLeft", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { ImGui::TextWrapped( - "RogueLike mode is a very unpolished proof of concept that enables you to play through the game doing " + "RogueLike mode is an unpolished proof of concept that enables you to play through the game doing " "various things to gain XP and gaining items and progression through random rolls instead of finding them " "at specific points in the game. There are various settings to tweak the balance on the right hand panel, " "the current balance has not really been heavily tested so feel free to experiment and share your " "findings. Also if there is interest some one is welcome to pick this up and polish it into a more " - "complete mode, all of it is open source."); + "complete mode, all of it is open source.\n\n" + "\n" + "To begin, start a new file and select the RogueLike quest mode. Your goal is to beat a random selection " + "of dungeons, which will grant you Ganon's boss key and allow you to finish the game."); ImGui::SeparatorText("Cheats:"); @@ -507,5 +539,5 @@ static void OnLoadGame() { }); } -static RegisterShipInitFunc initFunc(InitRogueLikeGUI, {}); +static RegisterMenuInitFunc menuInitFunc(InitRogueLikeGUI); static RegisterShipInitFunc initFunc2(OnLoadGame, { "IS_ROGUELIKE" }); diff --git a/soh/soh/Enhancements/RogueLike/MiscBehavior/OnLoadGame.cpp b/soh/soh/Enhancements/RogueLike/MiscBehavior/OnLoadGame.cpp index f264e35aa..ca204f980 100644 --- a/soh/soh/Enhancements/RogueLike/MiscBehavior/OnLoadGame.cpp +++ b/soh/soh/Enhancements/RogueLike/MiscBehavior/OnLoadGame.cpp @@ -15,6 +15,23 @@ static void OnLoadGame(int32_t fileNum) { Flags_SetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD); Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH); } + + // Pick 4 random rewards for this run, seeded by the filecreatedat + RogueLike::requiredRewards.clear(); + Random_Init(gSaveContext.ship.stats.fileCreatedAt); + std::vector rewards = { + QUEST_MEDALLION_FOREST, QUEST_MEDALLION_FIRE, QUEST_MEDALLION_WATER, + QUEST_MEDALLION_SPIRIT, QUEST_MEDALLION_SHADOW, QUEST_MEDALLION_LIGHT, + QUEST_KOKIRI_EMERALD, QUEST_GORON_RUBY, QUEST_ZORA_SAPPHIRE, + }; + + // Shuffle and pick first 4 + while (RogueLike::requiredRewards.size() < 4) { + size_t index = Random(0, rewards.size() - 1); + uint32_t reward = rewards[index]; + rewards.erase(rewards.begin() + index); + RogueLike::requiredRewards.push_back(reward); + } } static RegisterShipInitFunc diff --git a/soh/soh/Enhancements/RogueLike/RogueLike.cpp b/soh/soh/Enhancements/RogueLike/RogueLike.cpp index 1c876ab08..8c8c689d2 100644 --- a/soh/soh/Enhancements/RogueLike/RogueLike.cpp +++ b/soh/soh/Enhancements/RogueLike/RogueLike.cpp @@ -2,6 +2,8 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/ShipInit.hpp" +std::vector RogueLike::requiredRewards; + // Entry point for the module, run once on game boot static void InitRogueLike() { } diff --git a/soh/soh/Enhancements/RogueLike/RogueLike.h b/soh/soh/Enhancements/RogueLike/RogueLike.h index 1e7370401..6c513b63a 100644 --- a/soh/soh/Enhancements/RogueLike/RogueLike.h +++ b/soh/soh/Enhancements/RogueLike/RogueLike.h @@ -9,6 +9,8 @@ extern std::vector activeQuests; -namespace RogueLike {} // namespace RogueLike +namespace RogueLike { +extern std::vector requiredRewards; +} // namespace RogueLike #endif // ROGUELIKE_H diff --git a/soh/soh/Enhancements/randomizer/ShuffleGrass.cpp b/soh/soh/Enhancements/randomizer/ShuffleGrass.cpp index d82c3b393..07be081f9 100644 --- a/soh/soh/Enhancements/randomizer/ShuffleGrass.cpp +++ b/soh/soh/Enhancements/randomizer/ShuffleGrass.cpp @@ -233,11 +233,11 @@ void Rando::StaticData::RegisterGrassLocations() { locationTable[RC_KAK_GRASS_1] = Location::Grass(RC_KAK_GRASS_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1116, 1581), "Near Graveyard Grass 1", RHT_KAK_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GRASS_1)); locationTable[RC_KAK_GRASS_2] = Location::Grass(RC_KAK_GRASS_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1103, 1532), "Near Graveyard Grass 2", RHT_KAK_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GRASS_2)); locationTable[RC_KAK_GRASS_3] = Location::Grass(RC_KAK_GRASS_3, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1072, 1592), "Near Graveyard Grass 3", RHT_KAK_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GRASS_3)); - locationTable[RC_KAK_GRASS_4] = Location::Grass(RC_KAK_GRASS_4, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-800, 521), "Tree Grass 1", RHT_KAK_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GRASS_4)); - locationTable[RC_KAK_GRASS_5] = Location::Grass(RC_KAK_GRASS_5, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-826, 585), "Tree Grass 2", RHT_KAK_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GRASS_5)); - locationTable[RC_KAK_GRASS_6] = Location::Grass(RC_KAK_GRASS_6, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-827, 465), "Tree Grass 3", RHT_KAK_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GRASS_6)); - locationTable[RC_KAK_GRASS_7] = Location::Grass(RC_KAK_GRASS_7, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-883, 582), "Tree Grass 4", RHT_KAK_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GRASS_7)); - locationTable[RC_KAK_GRASS_8] = Location::Grass(RC_KAK_GRASS_8, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-887, 467), "Tree Grass 5", RHT_KAK_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GRASS_8)); + locationTable[RC_KAK_GRASS_4] = Location::Grass(RC_KAK_GRASS_4, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(820, 1421), "Tree Grass 1", RHT_KAK_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GRASS_4)); + locationTable[RC_KAK_GRASS_5] = Location::Grass(RC_KAK_GRASS_5, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(794, 1485), "Tree Grass 2", RHT_KAK_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GRASS_5)); + locationTable[RC_KAK_GRASS_6] = Location::Grass(RC_KAK_GRASS_6, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(793, 1365), "Tree Grass 3", RHT_KAK_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GRASS_6)); + locationTable[RC_KAK_GRASS_7] = Location::Grass(RC_KAK_GRASS_7, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(737, 1482), "Tree Grass 4", RHT_KAK_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GRASS_7)); + locationTable[RC_KAK_GRASS_8] = Location::Grass(RC_KAK_GRASS_8, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(733, 1367), "Tree Grass 5", RHT_KAK_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GRASS_8)); locationTable[RC_GY_GRASS_1] = Location::Grass(RC_GY_GRASS_1, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_GRAVEYARD, TWO_ACTOR_PARAMS(-1059, 732), "Graveyard Grass 1", RHT_GY_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_GRASS_1)); locationTable[RC_GY_GRASS_2] = Location::Grass(RC_GY_GRASS_2, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_GRAVEYARD, TWO_ACTOR_PARAMS(-1089, 769), "Graveyard Grass 2", RHT_GY_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_GRASS_2)); locationTable[RC_GY_GRASS_3] = Location::Grass(RC_GY_GRASS_3, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_GRAVEYARD, TWO_ACTOR_PARAMS(-1138, 761), "Graveyard Grass 3", RHT_GY_GRASS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_GRASS_3)); diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 751f3b07b..0c477b9a2 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -1182,6 +1182,10 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l break; } + if ((RandomizerGet)item00->itemEntry.getItemId == RG_TRIFORCE_PIECE) { + itemName = "a Christmas Ornament"; + } + Notification::Emit({ .message = message, .suffix = itemName, diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index 8f4bd656c..c08b6a1e4 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -403,7 +403,7 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_DEKU_STICK_CAPACITY_30] = Item(RG_DEKU_STICK_CAPACITY_30, Text{ "Deku Stick Capacity (30)", "Capacité de Bâtons Mojo (30)", "Deku-Stab-Kapazität (30)" }, ITEMTYPE_ITEM, GI_STICK_UPGRADE_30, true, LOGIC_PROGRESSIVE_STICK_BAG, RHT_DEKU_STICK_CAPACITY_30, ITEM_STICK_UPGRADE_30, OBJECT_GI_STICK, GID_STICK, 0x91, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_LESSER, MOD_NONE); itemTable[RG_MAGIC_SINGLE] = Item(RG_MAGIC_SINGLE, Text{ "Magic Meter", "Jauge de Magie", "Magische Kraft" }, ITEMTYPE_ITEM, 0x8A, true, LOGIC_PROGRESSIVE_MAGIC, RHT_MAGIC_SINGLE, RG_MAGIC_SINGLE, OBJECT_GI_MAGICPOT, GID_MAGIC_SMALL, 0xE4, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); itemTable[RG_MAGIC_DOUBLE] = Item(RG_MAGIC_DOUBLE, Text{ "Enhanced Magic Meter", "Jauge de Magie améliorée", "Verb. Magische Kraft" }, ITEMTYPE_ITEM, 0x8A, true, LOGIC_PROGRESSIVE_MAGIC, RHT_MAGIC_DOUBLE, RG_MAGIC_DOUBLE, OBJECT_GI_MAGICPOT, GID_MAGIC_LARGE, 0xE8, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); - itemTable[RG_TRIFORCE_PIECE] = Item(RG_TRIFORCE_PIECE, Text{ "Christmas Ornament", "Christmas Ornament", "Christmas Ornament" }, ITEMTYPE_ITEM, 0xDF, true, LOGIC_TRIFORCE_PIECES, RHT_TRIFORCE_PIECE, RG_TRIFORCE_PIECE, OBJECT_GI_BOMB_2, GID_TRIFORCE_PIECE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_TRIFORCE_PIECE] = Item(RG_TRIFORCE_PIECE, Text{ "Triforce Piece", "Triforce Piece", "Triforce-Splitter" }, ITEMTYPE_ITEM, 0xDF, true, LOGIC_TRIFORCE_PIECES, RHT_TRIFORCE_PIECE, RG_TRIFORCE_PIECE, OBJECT_GI_BOMB_2, GID_TRIFORCE_PIECE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); // Archipelago itemTable[RG_ARCHIPELAGO_ITEM_USEFUL] = Item(RG_ARCHIPELAGO_ITEM_USEFUL, Text{"Useful AP Item", "Useful AP Item", "Useful AP Item"}, ITEMTYPE_EVENT, GI_RUPEE_GREEN, false, LOGIC_NONE, RHT_NONE, RG_ARCHIPELAGO_ITEM_USEFUL, OBJECT_GI_LETTER, GID_LETTER_ZELDA, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index f0aa32984..eaeabb8c8 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -1214,6 +1214,12 @@ void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) { return; } + if (IS_ROGUELIKE && + ((flagType == FLAG_EVENT_CHECK_INF && flag == EVENTCHKINF_SPOKE_TO_SARIA_ON_BRIDGE) || + (flagType == FLAG_EVENT_CHECK_INF && flag == EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS))) { + return; + } + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) { switch (flagType) { case FLAG_EVENT_CHECK_INF: @@ -1260,6 +1266,10 @@ void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) { } } + if (IS_ROGUELIKE) { + return; + } + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) { switch (flagType) { case FLAG_RANDOMIZER_INF: diff --git a/soh/soh/Network/Anchor/DummyPlayer.cpp b/soh/soh/Network/Anchor/DummyPlayer.cpp index 8e5df3c8e..9192c3c02 100644 --- a/soh/soh/Network/Anchor/DummyPlayer.cpp +++ b/soh/soh/Network/Anchor/DummyPlayer.cpp @@ -158,13 +158,13 @@ void DummyPlayer_Update(Actor* actor, PlayState* play) { player->actor.world.pos.y += diff.y * player->actor.scale.y; } - if (player->modelGroup != Player_ActionToModelGroup(player, player->itemAction)) { + if (player->modelGroup != client.modelGroup) { // Hack to account for usage of gSaveContext s32 originalAge = gSaveContext.linkAge; gSaveContext.linkAge = client.linkAge; u8 originalButtonItem0 = gSaveContext.equips.buttonItems[0]; gSaveContext.equips.buttonItems[0] = client.buttonItem0; - Player_SetModelGroup(player, Player_ActionToModelGroup(player, player->itemAction)); + Player_SetModelGroup(player, client.modelGroup); gSaveContext.linkAge = originalAge; gSaveContext.equips.buttonItems[0] = originalButtonItem0; } diff --git a/soh/soh/Network/Anchor/Packets/GiveItem.cpp b/soh/soh/Network/Anchor/Packets/GiveItem.cpp index fbf048d7d..28dccc1bd 100644 --- a/soh/soh/Network/Anchor/Packets/GiveItem.cpp +++ b/soh/soh/Network/Anchor/Packets/GiveItem.cpp @@ -98,10 +98,17 @@ void Anchor::HandlePacket_GiveItem(nlohmann::json payload) { .suffix = SohUtils::GetItemName(getItemEntry.itemId), }); } else if (getItemEntry.modIndex == MOD_RANDOMIZER) { + std::string itemName = + Rando::StaticData::RetrieveItem((RandomizerGet)getItemEntry.getItemId).GetName().english; + + if ((RandomizerGet)getItemEntry.getItemId == RG_TRIFORCE_PIECE) { + itemName = "a Christmas Ornament"; + } + Notification::Emit({ .prefix = client.name, .message = "found", - .suffix = Rando::StaticData::RetrieveItem((RandomizerGet)getItemEntry.getItemId).GetName().english, + .suffix = itemName, }); } } diff --git a/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp b/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp index c05de0a23..d37dff64a 100644 --- a/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp +++ b/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp @@ -58,7 +58,7 @@ void Anchor::SendPacket_PlayerUpdate() { payload["currentShield"] = player->currentShield; payload["currentTunic"] = player->currentTunic; payload["stateFlags1"] = player->stateFlags1; - payload["stateFlags2"] = player->stateFlags2; + payload["stateFlags2"] = player->stateFlags2 & ~PLAYER_STATE2_DISABLE_DRAW; payload["buttonItem0"] = gSaveContext.equips.buttonItems[0]; payload["itemAction"] = player->itemAction; payload["heldItemAction"] = player->heldItemAction; diff --git a/soh/soh/Network/Archipelago/Archipelago.cpp b/soh/soh/Network/Archipelago/Archipelago.cpp index af4dfbe17..2a45c35d3 100644 --- a/soh/soh/Network/Archipelago/Archipelago.cpp +++ b/soh/soh/Network/Archipelago/Archipelago.cpp @@ -572,9 +572,15 @@ void ArchipelagoClient::OnItemGiven(uint32_t rc, GetItemEntry gi, uint8_t isGiSk break; } + std::string itemName = std::string(gSaveContext.ship.quest.data.archipelago.locations[rc].itemName); + + if (itemName == "Triforce Piece") { + itemName = "a Christmas Ornament"; + } + Notification::Emit( { .itemIcon = itemIcon, - .prefix = std::string(gSaveContext.ship.quest.data.archipelago.locations[rc].itemName), + .prefix = itemName, .message = " for ", .suffix = std::string(gSaveContext.ship.quest.data.archipelago.locations[rc].playerName) }); }