More last minute fixes (#37)

* More last minute fixes

* tweaks to RogueLike

* Add random GBK requirement
This commit is contained in:
Garrett Cox
2025-12-07 10:55:52 -06:00
committed by GitHub
parent 53162fde4a
commit 2b9acd888b
18 changed files with 167 additions and 19 deletions

View File

@@ -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

View File

@@ -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, {

View File

@@ -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;

View File

@@ -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" });

View File

@@ -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;
});

View File

@@ -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<ChoiceCard> 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<ChoiceCard> All = {

View File

@@ -14,6 +14,18 @@ namespace SohGui {
extern std::shared_ptr<SohMenu> mSohMenu;
}
#define QUEST_TO_TEXTURE(id) \
{ id, #id }
std::map<uint32_t, const char*> 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" });

View File

@@ -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<uint32_t> 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

View File

@@ -2,6 +2,8 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/ShipInit.hpp"
std::vector<uint32_t> RogueLike::requiredRewards;
// Entry point for the module, run once on game boot
static void InitRogueLike() {
}

View File

@@ -9,6 +9,8 @@
extern std::vector<RogueLikeQuestObject> activeQuests;
namespace RogueLike {} // namespace RogueLike
namespace RogueLike {
extern std::vector<uint32_t> requiredRewards;
} // namespace RogueLike
#endif // ROGUELIKE_H

View File

@@ -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));

View File

@@ -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,

View File

@@ -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);

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -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,
});
}
}

View File

@@ -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;

View File

@@ -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) });
}