Rando: Skeleton key (#3997)

* Initial Implementation

* Add temporary model to the skeleton key
This commit is contained in:
Pepe20129 2024-07-17 18:55:05 +02:00 committed by GitHub
parent 7595a5a65e
commit 8b6c183776
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 194 additions and 81 deletions

View File

@ -596,11 +596,13 @@ const std::vector<FlagTable> flagTables = {
{ RAND_INF_ZD_FISH_4, "RAND_INF_ZD_FISH_4" }, { RAND_INF_ZD_FISH_4, "RAND_INF_ZD_FISH_4" },
{ RAND_INF_ZD_FISH_5, "RAND_INF_ZD_FISH_5" }, { RAND_INF_ZD_FISH_5, "RAND_INF_ZD_FISH_5" },
{ RAND_INF_HAS_SKELETON_KEY, "RAND_INF_HAS_SKELETON_KEY" },
{ RAND_INF_LINKS_POCKET, "RAND_INF_LINKS_POCKET" }, { RAND_INF_LINKS_POCKET, "RAND_INF_LINKS_POCKET" },
{ RAND_INF_LEARNED_EPONA_SONG, "RAND_INF_LEARNED_EPONA_SONG" }, { RAND_INF_LEARNED_EPONA_SONG, "RAND_INF_LEARNED_EPONA_SONG" },
{ RAND_INF_DARUNIAS_JOY, "RAND_INF_DARUNIAS_JOY" }, { RAND_INF_DARUNIAS_JOY, "RAND_INF_DARUNIAS_JOY" },
{ RAND_INF_KING_ZORA_THAWED, "RAND_INF_KING_ZORA_THAWED" }, { RAND_INF_KING_ZORA_THAWED, "RAND_INF_KING_ZORA_THAWED" },
{ RAND_INF_HC_GREAT_FAIRY_REWARD, "RAND_INF_HC_GREAT_FAIRY_REWARD" }, { RAND_INF_HC_GREAT_FAIRY_REWARD, "RAND_INF_HC_GREAT_FAIRY_REWARD" },
{ RAND_INF_DMT_GREAT_FAIRY_REWARD, "RAND_INF_DMT_GREAT_FAIRY_REWARD" }, { RAND_INF_DMT_GREAT_FAIRY_REWARD, "RAND_INF_DMT_GREAT_FAIRY_REWARD" },
{ RAND_INF_DMC_GREAT_FAIRY_REWARD, "RAND_INF_DMC_GREAT_FAIRY_REWARD" }, { RAND_INF_DMC_GREAT_FAIRY_REWARD, "RAND_INF_DMC_GREAT_FAIRY_REWARD" },

View File

@ -29,8 +29,10 @@
#include "src/overlays/actors/ovl_En_Tp/z_en_tp.h" #include "src/overlays/actors/ovl_En_Tp/z_en_tp.h"
#include "src/overlays/actors/ovl_En_Firefly/z_en_firefly.h" #include "src/overlays/actors/ovl_En_Firefly/z_en_firefly.h"
#include "src/overlays/actors/ovl_En_Xc/z_en_xc.h" #include "src/overlays/actors/ovl_En_Xc/z_en_xc.h"
#include "src/overlays//actors/ovl_Fishing/z_fishing.h" #include "src/overlays/actors/ovl_Fishing/z_fishing.h"
#include "src/overlays/actors/ovl_Obj_Switch/z_obj_switch.h" #include "src/overlays/actors/ovl_Obj_Switch/z_obj_switch.h"
#include "src/overlays/actors/ovl_Door_Shutter/z_door_shutter.h"
#include "src/overlays/actors/ovl_En_Door/z_en_door.h"
#include "objects/object_link_boy/object_link_boy.h" #include "objects/object_link_boy/object_link_boy.h"
#include "objects/object_link_child/object_link_child.h" #include "objects/object_link_child/object_link_child.h"
@ -1712,6 +1714,24 @@ void RegisterRandomizerCompasses() {
}); });
} }
void RegisterSkeletonKey() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) {
Actor* actor = static_cast<Actor*>(refActor);
if (Flags_GetRandomizerInf(RAND_INF_HAS_SKELETON_KEY)) {
if (actor->id == ACTOR_EN_DOOR) {
EnDoor* door = (EnDoor*)actor;
door->lockTimer = 0;
} else if (actor->id == ACTOR_DOOR_SHUTTER) {
DoorShutter* shutterDoor = (DoorShutter*)actor;
if (shutterDoor->doorType == SHUTTER_KEY_LOCKED) {
shutterDoor->unk_16E = 0;
}
}
}
});
}
void InitMods() { void InitMods() {
RandomizerRegisterHooks(); RandomizerRegisterHooks();
TimeSaverRegisterHooks(); TimeSaverRegisterHooks();
@ -1760,4 +1780,5 @@ void InitMods() {
RegisterPatchHandHandler(); RegisterPatchHandHandler();
RegisterHurtContainerModeHandler(); RegisterHurtContainerModeHandler();
RegisterPauseMenuHooks(); RegisterPauseMenuHooks();
RegisterSkeletonKey();
} }

View File

@ -782,6 +782,10 @@ void GenerateItemPool() {
ctx->possibleIceTrapModels.push_back(RG_OCARINA_C_RIGHT_BUTTON); ctx->possibleIceTrapModels.push_back(RG_OCARINA_C_RIGHT_BUTTON);
} }
if (ctx->GetOption(RSK_SKELETON_KEY)) {
AddItemToMainPool(RG_SKELETON_KEY);
}
if (ctx->GetOption(RSK_SHUFFLE_SWIM)) { if (ctx->GetOption(RSK_SHUFFLE_SWIM)) {
AddItemToMainPool(RG_PROGRESSIVE_SCALE); AddItemToMainPool(RG_PROGRESSIVE_SCALE);
} }

View File

@ -541,5 +541,55 @@ extern "C" void Randomizer_DrawFishingPoleGI(PlayState* play, GetItemEntry* getI
Matrix_Pop(); Matrix_Pop();
CLOSE_DISPS(play->state.gfxCtx);
}
int skeletonKeyHue = 0;
// Runs every frame to update rainbow hue, taken from CosmeticsEditor.cpp.
Color_RGBA8 GetSkeletonKeyColor() {
float rainbowSpeed = 0.6f;
float frequency = 2 * M_PI / (360 * rainbowSpeed);
Color_RGBA8 color;
color.r = sin(frequency * skeletonKeyHue + 0) * 127 + 128;
color.g = sin(frequency * skeletonKeyHue + (2 * M_PI / 3)) * 127 + 128;
color.b = sin(frequency * skeletonKeyHue + (4 * M_PI / 3)) * 127 + 128;
color.a = 255;
skeletonKeyHue++;
if (skeletonKeyHue >= (360 * rainbowSpeed)) skeletonKeyHue = 0;
return color;
}
int test = 0;
extern "C" void Randomizer_DrawSkeletonKey(PlayState* play, GetItemEntry* getItemEntry) {
OPEN_DISPS(play->state.gfxCtx);
Color_RGBA8 color = GetSkeletonKeyColor();
Gfx_SetupDL_25Opa(play->state.gfxCtx);
test += 1;
if (test > 40) {
test -= 80;
}
Matrix_RotateZ(M_PI / 40 * test, MTXMODE_APPLY);
Matrix_RotateY(M_PI / 40 * test, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__),
G_MTX_MODELVIEW | G_MTX_LOAD);
gDPSetGrayscaleColor(POLY_OPA_DISP++, color.r, color.g, color.b, color.a);
gSPGrayscale(POLY_OPA_DISP++, true);
gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gGiSmallKeyDL);
gSPGrayscale(POLY_OPA_DISP++, false);
CLOSE_DISPS(play->state.gfxCtx); CLOSE_DISPS(play->state.gfxCtx);
} }

View File

@ -21,6 +21,7 @@ void Randomizer_DrawTriforcePieceGI(PlayState* play, GetItemEntry getItemEntry);
void Randomizer_DrawOcarinaButton(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawOcarinaButton(PlayState* play, GetItemEntry* getItemEntry);
void Randomizer_DrawBronzeScale(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawBronzeScale(PlayState* play, GetItemEntry* getItemEntry);
void Randomizer_DrawFishingPoleGI(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawFishingPoleGI(PlayState* play, GetItemEntry* getItemEntry);
void Randomizer_DrawSkeletonKey(PlayState* play, GetItemEntry* getItemEntry);
void Randomizer_DrawMysteryItem(PlayState* play, GetItemEntry getItemEntry); void Randomizer_DrawMysteryItem(PlayState* play, GetItemEntry getItemEntry);
#define GET_ITEM_MYSTERY \ #define GET_ITEM_MYSTERY \

View File

@ -289,6 +289,9 @@ void Rando::StaticData::InitItemTable() {
itemTable[RG_BRONZE_SCALE] = Item(RG_BRONZE_SCALE, Text{ "Bronze Scale", "!!!", "!!!" }, ITEMTYPE_ITEM, GI_SCALE_SILVER, true, &logic->ProgressiveWallet, RHT_BRONZE_SCALE, RG_BRONZE_SCALE, OBJECT_GI_SCALE, GID_SCALE_SILVER, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); itemTable[RG_BRONZE_SCALE] = Item(RG_BRONZE_SCALE, Text{ "Bronze Scale", "!!!", "!!!" }, ITEMTYPE_ITEM, GI_SCALE_SILVER, true, &logic->ProgressiveWallet, RHT_BRONZE_SCALE, RG_BRONZE_SCALE, OBJECT_GI_SCALE, GID_SCALE_SILVER, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER);
itemTable[RG_BRONZE_SCALE].SetCustomDrawFunc(Randomizer_DrawBronzeScale); itemTable[RG_BRONZE_SCALE].SetCustomDrawFunc(Randomizer_DrawBronzeScale);
itemTable[RG_SKELETON_KEY] = Item(RG_SKELETON_KEY, Text{ "Skeleton Key", "!!!", "!!!" }, ITEMTYPE_ITEM, GI_STONE_OF_AGONY, true, &logic->SkeletonKey, RHT_SKELETON_KEY, RG_SKELETON_KEY, OBJECT_GI_MAP, GID_STONE_OF_AGONY, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER);
itemTable[RG_SKELETON_KEY].SetCustomDrawFunc(Randomizer_DrawSkeletonKey);
itemTable[RG_DEKU_STICK_BAG] = Item(RG_DEKU_STICK_BAG, Text{ "Deku Stick Bag", "!!!", "!!!" }, ITEMTYPE_ITEM, GI_STICK_UPGRADE_30, true, &logic->ProgressiveStickBag, RHT_NONE, RG_DEKU_STICK_BAG, OBJECT_GI_STICK, GID_STICK, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); itemTable[RG_DEKU_STICK_BAG] = Item(RG_DEKU_STICK_BAG, Text{ "Deku Stick Bag", "!!!", "!!!" }, ITEMTYPE_ITEM, GI_STICK_UPGRADE_30, true, &logic->ProgressiveStickBag, RHT_NONE, RG_DEKU_STICK_BAG, OBJECT_GI_STICK, GID_STICK, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER);
itemTable[RG_DEKU_NUT_BAG] = Item(RG_DEKU_NUT_BAG, Text{ "Deku Nut Bag", "!!!", "!!!" }, ITEMTYPE_ITEM, GI_NUT_UPGRADE_30, true, &logic->ProgressiveNutBag, RHT_NONE, RG_DEKU_NUT_BAG, OBJECT_GI_NUTS, GID_NUTS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); itemTable[RG_DEKU_NUT_BAG] = Item(RG_DEKU_NUT_BAG, Text{ "Deku Nut Bag", "!!!", "!!!" }, ITEMTYPE_ITEM, GI_NUT_UPGRADE_30, true, &logic->ProgressiveNutBag, RHT_NONE, RG_DEKU_NUT_BAG, OBJECT_GI_NUTS, GID_NUTS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER);

View File

@ -411,6 +411,9 @@ namespace Rando {
} }
bool Logic::SmallKeys(RandomizerRegion dungeon, uint8_t requiredAmountGlitchless, uint8_t requiredAmountGlitched) { bool Logic::SmallKeys(RandomizerRegion dungeon, uint8_t requiredAmountGlitchless, uint8_t requiredAmountGlitched) {
if (SkeletonKey) {
return true;
}
switch (dungeon) { switch (dungeon) {
case RR_FOREST_TEMPLE: case RR_FOREST_TEMPLE:
/*if (IsGlitched && (GetDifficultyValueFromString(GlitchHookshotJump_Boots) >= static_cast<uint8_t>(GlitchDifficulty::INTERMEDIATE) || GetDifficultyValueFromString(GlitchHoverBoost) >= static_cast<uint8_t>(GlitchDifficulty::NOVICE) || /*if (IsGlitched && (GetDifficultyValueFromString(GlitchHookshotJump_Boots) >= static_cast<uint8_t>(GlitchDifficulty::INTERMEDIATE) || GetDifficultyValueFromString(GlitchHoverBoost) >= static_cast<uint8_t>(GlitchDifficulty::NOVICE) ||
@ -673,6 +676,9 @@ namespace Rando {
//Triforce Pieces //Triforce Pieces
TriforcePieces = 0; TriforcePieces = 0;
//Skeleton Key
SkeletonKey = false;
//Boss Souls //Boss Souls
CanSummonGohma = false; CanSummonGohma = false;
CanSummonKingDodongo = false; CanSummonKingDodongo = false;

View File

@ -171,6 +171,9 @@ class Logic {
// Triforce Pieces // Triforce Pieces
uint8_t TriforcePieces = 0; uint8_t TriforcePieces = 0;
// Skeleton Key
bool SkeletonKey = false;
// Boss Keys // Boss Keys
bool BossKeyForestTemple = false; bool BossKeyForestTemple = false;
bool BossKeyFireTemple = false; bool BossKeyFireTemple = false;

View File

@ -542,7 +542,6 @@ void Settings::CreateOptionDescriptions() {
mOptionDescriptions[RSK_KAK_50_SKULLS_HINT] = "Talking to the Cursed Resident in the Skultulla House who is saved after 50 tokens will tell you the reward"; mOptionDescriptions[RSK_KAK_50_SKULLS_HINT] = "Talking to the Cursed Resident in the Skultulla House who is saved after 50 tokens will tell you the reward";
mOptionDescriptions[RSK_KAK_100_SKULLS_HINT] = "Talking to the Cursed Resident in the Skultulla House who is saved after 100 tokens will tell you the reward"; mOptionDescriptions[RSK_KAK_100_SKULLS_HINT] = "Talking to the Cursed Resident in the Skultulla House who is saved after 100 tokens will tell you the reward";
mOptionDescriptions[RSK_MASK_SHOP_HINT] = "Reading the mask shop sign will tell you rewards from showing masks at the Deku Theatre."; mOptionDescriptions[RSK_MASK_SHOP_HINT] = "Reading the mask shop sign will tell you rewards from showing masks at the Deku Theatre.";
mOptionDescriptions[RSK_FULL_WALLETS] = "Start with a full wallet. All wallet upgrades come filled with rupees."; mOptionDescriptions[RSK_FULL_WALLETS] = "Start with a full wallet. All wallet upgrades come filled with rupees.";
mOptionDescriptions[RSK_BOMBCHUS_IN_LOGIC] = mOptionDescriptions[RSK_BOMBCHUS_IN_LOGIC] =
"Bombchus are properly considered in logic.\n" "Bombchus are properly considered in logic.\n"
@ -557,6 +556,7 @@ void Settings::CreateOptionDescriptions() {
mOptionDescriptions[RSK_BLUE_FIRE_ARROWS] = mOptionDescriptions[RSK_BLUE_FIRE_ARROWS] =
"Ice Arrows act like Blue Fire, making them able to melt red ice. " "Ice Arrows act like Blue Fire, making them able to melt red ice. "
"Item placement logic will respect this option, so it might be required to use this to progress."; "Item placement logic will respect this option, so it might be required to use this to progress.";
mOptionDescriptions[RSK_SKELETON_KEY] = "Adds a new item called the \"Skeleton Key\", it unlocks every dungeon door locked by a small key.";
mOptionDescriptions[RSK_SUNLIGHT_ARROWS] = mOptionDescriptions[RSK_SUNLIGHT_ARROWS] =
"Light Arrows can be used to light up the sun switches instead of using the Mirror Shield. " "Light Arrows can be used to light up the sun switches instead of using the Mirror Shield. "
"Item placement logic will respect this option, so it might be required to use this to progress."; "Item placement logic will respect this option, so it might be required to use this to progress.";

View File

@ -2954,7 +2954,7 @@ CustomMessage Randomizer::GetGoronMessage(u16 index) {
void Randomizer::CreateCustomMessages() { void Randomizer::CreateCustomMessages() {
// RANDTODO: Translate into french and german and replace GIMESSAGE_UNTRANSLATED // RANDTODO: Translate into french and german and replace GIMESSAGE_UNTRANSLATED
// with GIMESSAGE(getItemID, itemID, english, german, french). // with GIMESSAGE(getItemID, itemID, english, german, french).
const std::array<GetItemMessage, 76> getItemMessages = {{ const std::array<GetItemMessage, 77> getItemMessages = {{
GIMESSAGE(RG_GREG_RUPEE, ITEM_MASK_GORON, GIMESSAGE(RG_GREG_RUPEE, ITEM_MASK_GORON,
"You found %gGreg%w!", "You found %gGreg%w!",
"%gGreg%w! Du hast ihn wirklich gefunden!", "%gGreg%w! Du hast ihn wirklich gefunden!",
@ -3226,6 +3226,7 @@ void Randomizer::CreateCustomMessages() {
"Vous trouvez la %rtouche %y\xa6%r de&l'Ocarina%w! Vous pouvez&maintenant l'utiliser lorsque&vous en jouez!"), "Vous trouvez la %rtouche %y\xa6%r de&l'Ocarina%w! Vous pouvez&maintenant l'utiliser lorsque&vous en jouez!"),
GIMESSAGE_UNTRANSLATED(RG_BRONZE_SCALE, ITEM_SCALE_SILVER, "You got the %rBronze Scale%w!&The power of buoyancy is yours!"), GIMESSAGE_UNTRANSLATED(RG_BRONZE_SCALE, ITEM_SCALE_SILVER, "You got the %rBronze Scale%w!&The power of buoyancy is yours!"),
GIMESSAGE_UNTRANSLATED(RG_FISHING_POLE, ITEM_FISHING_POLE, "You found a lost %rFishing Pole%w!&Time to hit the pond!"), GIMESSAGE_UNTRANSLATED(RG_FISHING_POLE, ITEM_FISHING_POLE, "You found a lost %rFishing Pole%w!&Time to hit the pond!"),
GIMESSAGE_UNTRANSLATED(RG_SKELETON_KEY, ITEM_KEY_SMALL, "You found the %rSkeleton Key%w!"),
GIMESSAGE_UNTRANSLATED(RG_DEKU_STICK_BAG, ITEM_STICK, "You found the %rDeku Stick Bag%w!&You can now hold deku sticks!"), GIMESSAGE_UNTRANSLATED(RG_DEKU_STICK_BAG, ITEM_STICK, "You found the %rDeku Stick Bag%w!&You can now hold deku sticks!"),
GIMESSAGE_UNTRANSLATED(RG_DEKU_NUT_BAG, ITEM_NUT, "You found the %rDeku Nut Bag%w!&You can now hold deku nuts!"), GIMESSAGE_UNTRANSLATED(RG_DEKU_NUT_BAG, ITEM_NUT, "You found the %rDeku Nut Bag%w!&You can now hold deku nuts!"),
}}; }};

View File

@ -1979,6 +1979,7 @@ typedef enum {
RG_OCARINA_C_DOWN_BUTTON, RG_OCARINA_C_DOWN_BUTTON,
RG_OCARINA_C_LEFT_BUTTON, RG_OCARINA_C_LEFT_BUTTON,
RG_OCARINA_C_RIGHT_BUTTON, RG_OCARINA_C_RIGHT_BUTTON,
RG_SKELETON_KEY,
RG_FISHING_POLE, RG_FISHING_POLE,
RG_DEKU_STICK_BAG, RG_DEKU_STICK_BAG,
RG_DEKU_NUT_BAG, RG_DEKU_NUT_BAG,
@ -3244,6 +3245,7 @@ typedef enum {
RHT_OCARINA_C_RIGHT_BUTTON, RHT_OCARINA_C_RIGHT_BUTTON,
RHT_BRONZE_SCALE, RHT_BRONZE_SCALE,
RHT_FISHING_POLE, RHT_FISHING_POLE,
RHT_SKELETON_KEY,
RHT_EPONA, RHT_EPONA,
RHT_HINT_MYSTERIOUS, RHT_HINT_MYSTERIOUS,
RHT_MYSTERIOUS_ITEM, RHT_MYSTERIOUS_ITEM,
@ -3713,6 +3715,7 @@ typedef enum {
RSK_FISHSANITY_POND_COUNT, RSK_FISHSANITY_POND_COUNT,
RSK_FISHSANITY_AGE_SPLIT, RSK_FISHSANITY_AGE_SPLIT,
RSK_SHUFFLE_FISHING_POLE, RSK_SHUFFLE_FISHING_POLE,
RSK_SKELETON_KEY,
RSK_SHUFFLE_DEKU_STICK_BAG, RSK_SHUFFLE_DEKU_STICK_BAG,
RSK_SHUFFLE_DEKU_NUT_BAG, RSK_SHUFFLE_DEKU_NUT_BAG,
RSK_MAX RSK_MAX

View File

@ -257,11 +257,13 @@ typedef enum {
RAND_INF_ZD_FISH_4, RAND_INF_ZD_FISH_4,
RAND_INF_ZD_FISH_5, RAND_INF_ZD_FISH_5,
RAND_INF_HAS_SKELETON_KEY,
RAND_INF_LINKS_POCKET, RAND_INF_LINKS_POCKET,
RAND_INF_LEARNED_EPONA_SONG, RAND_INF_LEARNED_EPONA_SONG,
RAND_INF_DARUNIAS_JOY, RAND_INF_DARUNIAS_JOY,
RAND_INF_KING_ZORA_THAWED, RAND_INF_KING_ZORA_THAWED,
RAND_INF_HC_GREAT_FAIRY_REWARD, RAND_INF_HC_GREAT_FAIRY_REWARD,
RAND_INF_DMT_GREAT_FAIRY_REWARD, RAND_INF_DMT_GREAT_FAIRY_REWARD,
RAND_INF_DMC_GREAT_FAIRY_REWARD, RAND_INF_DMC_GREAT_FAIRY_REWARD,

View File

@ -184,6 +184,7 @@ void Settings::CreateOptions() {
// TODO: Compasses show rewards/woth, maps show dungeon mode // TODO: Compasses show rewards/woth, maps show dungeon mode
mOptions[RSK_BLUE_FIRE_ARROWS] = Option::Bool("Blue Fire Arrows", CVAR_RANDOMIZER_SETTING("BlueFireArrows"), mOptionDescriptions[RSK_BLUE_FIRE_ARROWS]); mOptions[RSK_BLUE_FIRE_ARROWS] = Option::Bool("Blue Fire Arrows", CVAR_RANDOMIZER_SETTING("BlueFireArrows"), mOptionDescriptions[RSK_BLUE_FIRE_ARROWS]);
mOptions[RSK_SUNLIGHT_ARROWS] = Option::Bool("Sunlight Arrows", CVAR_RANDOMIZER_SETTING("SunlightArrows"), mOptionDescriptions[RSK_SUNLIGHT_ARROWS]); mOptions[RSK_SUNLIGHT_ARROWS] = Option::Bool("Sunlight Arrows", CVAR_RANDOMIZER_SETTING("SunlightArrows"), mOptionDescriptions[RSK_SUNLIGHT_ARROWS]);
mOptions[RSK_SKELETON_KEY] = Option::Bool("Skeleton Key", CVAR_RANDOMIZER_SETTING("SkeletonKey"), mOptionDescriptions[RSK_SKELETON_KEY]);
mOptions[RSK_ITEM_POOL] = Option::U8("Item Pool", {"Plentiful", "Balanced", "Scarce", "Minimal"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ItemPool"), mOptionDescriptions[RSK_ITEM_POOL], WidgetType::Combobox, RO_ITEM_POOL_BALANCED); mOptions[RSK_ITEM_POOL] = Option::U8("Item Pool", {"Plentiful", "Balanced", "Scarce", "Minimal"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ItemPool"), mOptionDescriptions[RSK_ITEM_POOL], WidgetType::Combobox, RO_ITEM_POOL_BALANCED);
mOptions[RSK_ICE_TRAPS] = Option::U8("Ice Traps", {"Off", "Normal", "Extra", "Mayhem", "Onslaught"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("IceTraps"), mOptionDescriptions[RSK_ICE_TRAPS], WidgetType::Combobox, RO_ICE_TRAPS_NORMAL); mOptions[RSK_ICE_TRAPS] = Option::U8("Ice Traps", {"Off", "Normal", "Extra", "Mayhem", "Onslaught"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("IceTraps"), mOptionDescriptions[RSK_ICE_TRAPS], WidgetType::Combobox, RO_ICE_TRAPS_NORMAL);
// TODO: Remove Double Defense, Progressive Goron Sword // TODO: Remove Double Defense, Progressive Goron Sword
@ -764,7 +765,8 @@ void Settings::CreateOptions() {
&mOptions[RSK_BOMBCHUS_IN_LOGIC], &mOptions[RSK_BOMBCHUS_IN_LOGIC],
&mOptions[RSK_ENABLE_BOMBCHU_DROPS], &mOptions[RSK_ENABLE_BOMBCHU_DROPS],
&mOptions[RSK_BLUE_FIRE_ARROWS], &mOptions[RSK_BLUE_FIRE_ARROWS],
&mOptions[RSK_SUNLIGHT_ARROWS] &mOptions[RSK_SUNLIGHT_ARROWS],
&mOptions[RSK_SKELETON_KEY],
}, false, WidgetContainerType::COLUMN); }, false, WidgetContainerType::COLUMN);
mOptionGroups[RSG_GAMEPLAY_IMGUI_TABLE] = OptionGroup::SubGroup("Gameplay", { mOptionGroups[RSG_GAMEPLAY_IMGUI_TABLE] = OptionGroup::SubGroup("Gameplay", {
&mOptionGroups[RSG_TIMESAVERS_IMGUI], &mOptionGroups[RSG_TIMESAVERS_IMGUI],
@ -992,6 +994,7 @@ void Settings::CreateOptions() {
&mOptions[RSK_DAMAGE_MULTIPLIER], &mOptions[RSK_DAMAGE_MULTIPLIER],
&mOptions[RSK_BLUE_FIRE_ARROWS], &mOptions[RSK_BLUE_FIRE_ARROWS],
&mOptions[RSK_SUNLIGHT_ARROWS], &mOptions[RSK_SUNLIGHT_ARROWS],
&mOptions[RSK_SKELETON_KEY],
}); });
mOptionGroups[RSG_ITEM_POOL] = OptionGroup("Item Pool Settings", std::initializer_list<Option*>({ mOptionGroups[RSG_ITEM_POOL] = OptionGroup("Item Pool Settings", std::initializer_list<Option*>({
&mOptions[RSK_ITEM_POOL], &mOptions[RSK_ITEM_POOL],
@ -1210,6 +1213,7 @@ void Settings::CreateOptions() {
{ "Miscellaneous Settings:Hint Distribution", RSK_HINT_DISTRIBUTION }, { "Miscellaneous Settings:Hint Distribution", RSK_HINT_DISTRIBUTION },
{ "Miscellaneous Settings:Blue Fire Arrows", RSK_BLUE_FIRE_ARROWS }, { "Miscellaneous Settings:Blue Fire Arrows", RSK_BLUE_FIRE_ARROWS },
{ "Miscellaneous Settings:Sunlight Arrows", RSK_SUNLIGHT_ARROWS }, { "Miscellaneous Settings:Sunlight Arrows", RSK_SUNLIGHT_ARROWS },
{ "Miscellaneous Settings:Skeleton Key", RSK_SKELETON_KEY },
{ "Timesaver Settings:Skip Child Zelda", RSK_SKIP_CHILD_ZELDA }, { "Timesaver Settings:Skip Child Zelda", RSK_SKIP_CHILD_ZELDA },
{ "Start with Consumables", RSK_STARTING_CONSUMABLES }, { "Start with Consumables", RSK_STARTING_CONSUMABLES },
{ "Full Wallets", RSK_FULL_WALLETS }, { "Full Wallets", RSK_FULL_WALLETS },
@ -2359,6 +2363,7 @@ void Settings::ParseJson(nlohmann::json spoilerFileJson) {
case RSK_SKULLS_SUNS_SONG: case RSK_SKULLS_SUNS_SONG:
case RSK_BLUE_FIRE_ARROWS: case RSK_BLUE_FIRE_ARROWS:
case RSK_SUNLIGHT_ARROWS: case RSK_SUNLIGHT_ARROWS:
case RSK_SKELETON_KEY:
case RSK_BOMBCHUS_IN_LOGIC: case RSK_BOMBCHUS_IN_LOGIC:
case RSK_TOT_ALTAR_HINT: case RSK_TOT_ALTAR_HINT:
case RSK_GANONDORF_HINT: case RSK_GANONDORF_HINT:

View File

@ -2796,6 +2796,11 @@ u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
return Return_Item_Entry(giEntry, RG_NONE); return Return_Item_Entry(giEntry, RG_NONE);
} }
if (item == RG_SKELETON_KEY) {
Flags_SetRandomizerInf(RAND_INF_HAS_SKELETON_KEY);
return Return_Item_Entry(giEntry, RG_NONE);
}
if (item == RG_DEKU_STICK_BAG) { if (item == RG_DEKU_STICK_BAG) {
Inventory_ChangeUpgrade(UPG_STICKS, 1); Inventory_ChangeUpgrade(UPG_STICKS, 1);
INV_CONTENT(ITEM_STICK) = ITEM_STICK; INV_CONTENT(ITEM_STICK) = ITEM_STICK;
@ -5370,83 +5375,87 @@ void Interface_Draw(PlayState* play) {
} }
} }
switch (play->sceneNum) { //when having the skeleton key in rando, don't render the small key counter
case SCENE_FOREST_TEMPLE: if (!Flags_GetRandomizerInf(RAND_INF_HAS_SKELETON_KEY)) {
case SCENE_FIRE_TEMPLE: switch (play->sceneNum) {
case SCENE_WATER_TEMPLE: case SCENE_FOREST_TEMPLE:
case SCENE_SPIRIT_TEMPLE: case SCENE_FIRE_TEMPLE:
case SCENE_SHADOW_TEMPLE: case SCENE_WATER_TEMPLE:
case SCENE_BOTTOM_OF_THE_WELL: case SCENE_SPIRIT_TEMPLE:
case SCENE_ICE_CAVERN: case SCENE_SHADOW_TEMPLE:
case SCENE_GANONS_TOWER: case SCENE_BOTTOM_OF_THE_WELL:
case SCENE_GERUDO_TRAINING_GROUND: case SCENE_ICE_CAVERN:
case SCENE_THIEVES_HIDEOUT: case SCENE_GANONS_TOWER:
case SCENE_INSIDE_GANONS_CASTLE: case SCENE_GERUDO_TRAINING_GROUND:
case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: case SCENE_THIEVES_HIDEOUT:
case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE: case SCENE_INSIDE_GANONS_CASTLE:
case SCENE_TREASURE_BOX_SHOP: case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR:
if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] >= 0) { case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE:
s16 X_Margins_SKC; case SCENE_TREASURE_BOX_SHOP:
s16 Y_Margins_SKC;
if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.UseMargins"), 0) != 0) { if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] >= 0) {
if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosType"), 0) == 0) {X_Margins_SKC = Left_HUD_Margin;}; s16 X_Margins_SKC;
Y_Margins_SKC = Bottom_HUD_Margin; s16 Y_Margins_SKC;
} else { if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.UseMargins"), 0) != 0) {
X_Margins_SKC = 0; if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosType"), 0) == 0) {X_Margins_SKC = Left_HUD_Margin;};
Y_Margins_SKC = 0; Y_Margins_SKC = Bottom_HUD_Margin;
} } else {
s16 PosX_SKC_ori = OTRGetRectDimensionFromLeftEdge(26+X_Margins_SKC); X_Margins_SKC = 0;
s16 PosY_SKC_ori = 190+Y_Margins_SKC; Y_Margins_SKC = 0;
s16 PosX_SKC;
s16 PosY_SKC;
if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosType"), 0) != 0) {
PosY_SKC = CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosY"), 0)+Y_Margins_SKC;
if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosType"), 0) == 1) {//Anchor Left
if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.UseMargins"), 0) != 0) {X_Margins_SKC = Left_HUD_Margin;};
PosX_SKC = OTRGetDimensionFromLeftEdge(CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosX"), 0)+X_Margins_SKC);
} else if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosType"), 0) == 2) {//Anchor Right
if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.UseMargins"), 0) != 0) {X_Margins_SKC = Right_HUD_Margin;};
PosX_SKC = OTRGetDimensionFromRightEdge(CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosX"), 0)+X_Margins_SKC);
} else if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosType"), 0) == 3) {//Anchor None
PosX_SKC = CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosX"), 0);
} else if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosType"), 0) == 4) {//Hidden
PosX_SKC = -9999;
} }
} else { s16 PosX_SKC_ori = OTRGetRectDimensionFromLeftEdge(26+X_Margins_SKC);
PosY_SKC = PosY_SKC_ori; s16 PosY_SKC_ori = 190+Y_Margins_SKC;
PosX_SKC = PosX_SKC_ori; s16 PosX_SKC;
s16 PosY_SKC;
if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosType"), 0) != 0) {
PosY_SKC = CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosY"), 0)+Y_Margins_SKC;
if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosType"), 0) == 1) {//Anchor Left
if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.UseMargins"), 0) != 0) {X_Margins_SKC = Left_HUD_Margin;};
PosX_SKC = OTRGetDimensionFromLeftEdge(CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosX"), 0)+X_Margins_SKC);
} else if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosType"), 0) == 2) {//Anchor Right
if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.UseMargins"), 0) != 0) {X_Margins_SKC = Right_HUD_Margin;};
PosX_SKC = OTRGetDimensionFromRightEdge(CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosX"), 0)+X_Margins_SKC);
} else if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosType"), 0) == 3) {//Anchor None
PosX_SKC = CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosX"), 0);
} else if (CVarGetInteger(CVAR_COSMETIC("HUD.SmallKey.PosType"), 0) == 4) {//Hidden
PosX_SKC = -9999;
}
} else {
PosY_SKC = PosY_SKC_ori;
PosX_SKC = PosX_SKC_ori;
}
// Small Key Icon
gDPPipeSync(OVERLAY_DISP++);
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, keyCountColor.r,keyCountColor.g,keyCountColor.b, interfaceCtx->magicAlpha);
gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 20, 255); //We reset this here so it match user color :)
OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gSmallKeyCounterIconTex, 16, 16, PosX_SKC, PosY_SKC, 16, 16,
1 << 10, 1 << 10);
// Small Key Counter
gDPPipeSync(OVERLAY_DISP++);
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->magicAlpha);
gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE,
TEXEL0, 0, PRIMITIVE, 0);
interfaceCtx->counterDigits[2] = 0;
interfaceCtx->counterDigits[3] = gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex];
while (interfaceCtx->counterDigits[3] >= 10) {
interfaceCtx->counterDigits[2]++;
interfaceCtx->counterDigits[3] -= 10;
}
if (interfaceCtx->counterDigits[2] != 0) {
OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, ((u8*)((u8*)digitTextures[interfaceCtx->counterDigits[2]])), 8, 16, PosX_SKC+8, PosY_SKC, 8, 16, 1 << 10, 1 << 10);
}
OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, ((u8*)digitTextures[interfaceCtx->counterDigits[3]]), 8, 16, PosX_SKC+16, PosY_SKC, 8, 16, 1 << 10, 1 << 10);
} }
// Small Key Icon break;
gDPPipeSync(OVERLAY_DISP++); default:
break;
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, keyCountColor.r,keyCountColor.g,keyCountColor.b, interfaceCtx->magicAlpha); }
gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 20, 255); //We reset this here so it match user color :)
OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gSmallKeyCounterIconTex, 16, 16, PosX_SKC, PosY_SKC, 16, 16,
1 << 10, 1 << 10);
// Small Key Counter
gDPPipeSync(OVERLAY_DISP++);
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->magicAlpha);
gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE,
TEXEL0, 0, PRIMITIVE, 0);
interfaceCtx->counterDigits[2] = 0;
interfaceCtx->counterDigits[3] = gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex];
while (interfaceCtx->counterDigits[3] >= 10) {
interfaceCtx->counterDigits[2]++;
interfaceCtx->counterDigits[3] -= 10;
}
if (interfaceCtx->counterDigits[2] != 0) {
OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, ((u8*)((u8*)digitTextures[interfaceCtx->counterDigits[2]])), 8, 16, PosX_SKC+8, PosY_SKC, 8, 16, 1 << 10, 1 << 10);
}
OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, ((u8*)digitTextures[interfaceCtx->counterDigits[3]]), 8, 16, PosX_SKC+16, PosY_SKC, 8, 16, 1 << 10, 1 << 10);
}
break;
default:
break;
} }
// Rupee Counter // Rupee Counter

View File

@ -57,6 +57,9 @@ typedef struct EnDoor {
/* 0x01D4 */ EnDoorActionFunc actionFunc; /* 0x01D4 */ EnDoorActionFunc actionFunc;
} EnDoor; // size = 0x01D8 } EnDoor; // size = 0x01D8
#ifdef __cplusplus
extern "C"
#endif
void EnDoor_SetupType(EnDoor* enDoor, PlayState* play); void EnDoor_SetupType(EnDoor* enDoor, PlayState* play);
#endif #endif