Compare commits

...

41 Commits

Author SHA1 Message Date
Philip Dubé 0dc6989438 9.1.2 (#6160) 2026-01-19 00:10:09 +00:00
Jordan Longstaff 7850fa82df Modularize enemy defeat count hook & fix counting bugs (#5885) 2026-01-17 05:35:45 +00:00
aMannus 0821c2e315 Fix boss souls on item tracker (#6142) 2026-01-15 15:51:04 +00:00
OtherBlue 3e0225272f Make "Move in First Person" require "Right Stick Aiming" (#6104) 2026-01-10 01:03:03 +00:00
Garrett Cox 8584ced40b Proper fix for 2 handed idle animation (#6109) 2026-01-08 18:20:16 +00:00
Shishu the Dragon d62e8108fd Mod Menu: Fix empty list crash (#6015) 2026-01-01 19:54:34 +00:00
aMannus 5d63146267 Add setting updater for rando logic setting (#6030) 2025-12-31 01:44:01 +00:00
Jordan Longstaff 50c023b86b Modularize randomized enemy size hook & fix enemy health bars (#5887) 2025-12-31 01:43:20 +00:00
xxAtrain223 ace2f7869b Check for Pocket Cucco in HasItem. (#6049) 2025-12-28 09:20:16 -07:00
xxAtrain223 999f980d7c Fix 2 TimePass Issues (#6038)
* Set Kak time pass to false.

* Set lon lon time pass to false.
2025-12-27 12:10:57 +01:00
xxAtrain223 352a4e9260 Added small key doors special case for Thieves Hideout. (#6023) 2025-12-15 12:49:11 -07:00
Philip Dubé 05d865337c MQ forest: fix raised island GS logic (#6020) 2025-12-15 12:47:07 -07:00
Shishu the Dragon 9d8addca04 Update macOS build instructions (#6012) 2025-12-10 09:50:07 -07:00
Malkierian 0073736467 Fix entrance value assigned to DMT sign outside Dodongo's for hinting. (#5973) 2025-11-25 17:07:45 +00:00
Malkierian 2ee4e70b9a Fix menu header width and scrollbar calculations. (#5975) 2025-11-24 14:00:37 +00:00
Christopher Leggett c68c8f1284 Adds a toggle for the entrance labels on signs near loading zones. (#5974) 2025-11-23 16:45:19 +00:00
Malkierian daeb3a84c7 Can't use CVars before the CVar system is initialized. (#5962) 2025-11-14 12:45:48 -07:00
Philip Dubé 6ccdfc6051 Fix: ground jump for haunted wasteland GS is hard (#5843)
* Fix: ground jump for haunted wasteland GS is hard, & can't be done with hammer

Pulled this logic from zootr where sword is assumed, was not able to execute myself

* Pepper0ni took a stab at this, wasn't able to do it, but didn't feel it wasn't doable

* AGreenSpoon figured it out with BGS/hammer
2025-11-13 19:24:40 -07:00
Jordan Longstaff 14fb9e60d8 Autosave no longer works after save file is deleted (#5959) 2025-11-13 15:31:57 -07:00
Jordan Longstaff dadc2e5218 "Assignable Tunic and Boots" setting now allows shields also (#5953) 2025-11-13 14:29:48 -07:00
Malkierian 5629d033c2 Forgot to get CVar value with the default specified in OTRGlobals to feed into InitLogging. (#5952) 2025-11-11 10:03:14 -07:00
Philip Dubé 3eade8133e Fix GF entrance tracker & generation issues with cratesanity (#5934) 2025-11-09 19:16:33 -07:00
Malkierian 6b83070343 Logging bump (#5938)
* Bump LUS, call `InitLogging` with both values at `trace`.
Set up proper conditional default log level in Dev Tools.

* Update LUS ref.
2025-11-09 18:27:19 -07:00
Jordan Longstaff 7ff93a3960 Dampe's Hut open all night when Dampe is out (#5937) 2025-11-09 18:27:09 -07:00
Pepper0ni 3d525d7eb1 Fix excluded locations getting bombchus when bombchu bag is off (#5943)
* Fix bombchus being consistently added to excluded locations

* change to be a random junk item
2025-11-09 08:58:18 -07:00
Garrett Cox 0e162cbb7c Fix skybox being manipulated outside of appropriate scenes, other tweaks to disable 2d backgrounds impl (#5925) 2025-11-08 15:49:35 -06:00
Jordan Longstaff 3793e821c8 Autosave disabled when Ocarina of Time obtained but not Song of Time (#5936)
* Autosave disabled when Ocarina of Time obtained but not Song of Time

* Add explanation comment

Added a condition to prevent autosaving between obtaining the Ocarina of Time and the Song of Time.
2025-11-08 13:06:01 -07:00
Malkierian 4aaad850bd Bump to Bravo 9.1.1. (#5922) 2025-11-04 17:40:02 -07:00
Malkierian cf275b1a6c Implement logger changes (#5914)
* Implement logger changes, and make default log level dynamic based on debug/release.

* Bump LUS.

* typo

Co-authored-by: Eblo <7004497+Eblo@users.noreply.github.com>

---------

Co-authored-by: Eblo <7004497+Eblo@users.noreply.github.com>
2025-11-03 18:54:01 -07:00
Philip Dubé 0014f40676 rando BOTWMQ: only turn the water level crystal switch into toggle (#5902) 2025-11-03 18:49:11 -07:00
Malkierian f015a1b339 Apply expected lexicographical sort to initial and new mod addition list. (#5904) 2025-11-03 18:48:48 -07:00
Malkierian b2090dcf83 Add "CuccosToReturn": 1 to Main Randomizer preset. (#5915) 2025-11-03 18:00:31 -07:00
Malkierian ebf8a3b47f Fix Song Shuffle Off (#5918)
* Temporarily fixes Song Shuffle Off causing songs to not be given on seeds created solely from spoilers.

* Remove instead of comment.
2025-11-03 17:40:54 -07:00
Pepper0ni f3b0d8e230 quick fix ZR grass logic (#5906) 2025-11-01 11:45:23 +01:00
Jordan Longstaff 1ba6cf643d Modularize coloured ToT Medallions hook (#5877)
* Modularize colourized ToT Medallions hook

* Add overlooked reset of Forest Medallion colour

* Move asset variables to hook file

* Fix includes

* Remove forward declarations

* Use data structures to reduceboilerplate code

* Simplify data structures, reduce boilerplate even more

* Correct patchName2 checks

* Clang format

* Add brackets on if statements
2025-10-29 13:36:29 -07:00
Malkierian b30191f369 Set cutsceneIndex to 0 to bypass cutscene shenanigans when spawning in ToT as adult. (#5899)
Unify savedSceneNum at -1 regardless of starting age.
2025-10-29 09:05:14 -07:00
Jordan Longstaff a419b431a7 Modularize Pause Warp hook (#5869)
* Modularize Pause Warp hook

* Make RegisterPauseMenuHooks static

* Merge pausewarp.c into hook module

* Use brackets with if statements

* Fix timer bug with spoiling items
2025-10-28 20:52:18 -07:00
Malkierian 48f4f56bac Change disabling rules for Skip Feeding Jabu-Jabu to reflect the fact that it only works in rando based on the rando setting. (#5889) 2025-10-28 17:01:59 -07:00
Pepper0ni 1b29c0cad3 fix logic error with detu tree torch room (#5898) 2025-10-28 16:57:16 -07:00
Malkierian 74312442d5 Fix wrong CVar on Hookshot Identifier. (#5883) 2025-10-25 19:52:22 -07:00
Malkierian ad2807ce32 Fix adding Enable Mods twice as search widget. (#5880)
Fix a couple typos in search widget extra terms.
2025-10-23 18:32:00 -07:00
56 changed files with 1054 additions and 761 deletions
+4 -1
View File
@@ -6,9 +6,12 @@ set(CMAKE_C_STANDARD 23 CACHE STRING "The C standard to use")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
project(Ship VERSION 9.1.0 LANGUAGES C CXX)
project(Ship VERSION 9.1.2 LANGUAGES C CXX)
include(CMake/soh-cvars.cmake)
include(CMake/lus-cvars.cmake)
set(SPDLOG_LEVEL_TRACE 0)
set(SPDLOG_LEVEL_OFF 6)
set(SPDLOG_MIN_CUTOFF SPDLOG_LEVEL_TRACE CACHE STRING "cutoff at trace")
option(SUPPRESS_WARNINGS "Suppress warnings in LUS and src (decomp)" ON)
if(SUPPRESS_WARNINGS)
+2 -2
View File
@@ -239,7 +239,7 @@ cmake --build build-cmake --target ExtractAssetHeaders
```
## macOS
Requires Xcode (or xcode-tools) && `sdl2, libpng, glew, ninja, cmake, tinyxml2, nlohmann-json, libzip` (can be installed via [homebrew](https://brew.sh/), macports, etc)
Requires Xcode (or xcode-tools) && `sdl2, libpng, glew, ninja, cmake, tinyxml2, nlohmann-json, libzip, opusfile, libvorbis` (can be installed via [homebrew](https://brew.sh/), macports, etc)
**Important: For maximum performance make sure you have ninja build tools installed!**
@@ -254,7 +254,7 @@ cd ShipWright
git submodule update --init
# Install development dependencies (assuming homebrew)
brew install sdl2 libpng glew ninja cmake tinyxml2 nlohmann-json libzip
brew install sdl2 libpng glew ninja cmake tinyxml2 nlohmann-json libzip opusfile libvorbis
# Generate Ninja project
# Add `-DCMAKE_BUILD_TYPE:STRING=Release` if you're packaging
+19 -13
View File
@@ -337,13 +337,15 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
"ENABLE_DX11;"
">"
"$<$<CONFIG:Release>:"
"NDEBUG"
"NDEBUG;"
">"
"$<$<BOOL:${BUILD_REMOTE_CONTROL}>:ENABLE_REMOTE_CONTROL>"
"INCLUDE_GAME_PRINTF;"
"F3DEX_GBI_2"
"UNICODE;"
"_UNICODE"
SPDLOG_ACTIVE_LEVEL=${SPDLOG_MIN_CUTOFF}
LOG_LEVEL_GAME_PRINTS=${SPDLOG_LEVEL_OFF}
STORMLIB_NO_AUTO_LINK
"_CRT_SECURE_NO_WARNINGS;"
NOMINMAX
@@ -354,7 +356,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
"NOINCLUDE_GAME_PRINTF;"
"_DEBUG;"
"_CRT_SECURE_NO_WARNINGS;"
"ENABLE_OPENGL"
"ENABLE_OPENGL;"
">"
"$<$<CONFIG:Release>:"
"NDEBUG;"
@@ -363,7 +365,9 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
"F3DEX_GBI_2"
"WIN32;"
"UNICODE;"
"_UNICODE"
"_UNICODE;"
SPDLOG_ACTIVE_LEVEL=${SPDLOG_MIN_CUTOFF}
LOG_LEVEL_GAME_PRINTS=${SPDLOG_LEVEL_OFF}
STORMLIB_NO_AUTO_LINK
NOMINMAX
)
@@ -371,33 +375,35 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
elseif (CMAKE_SYSTEM_NAME STREQUAL "CafeOS")
target_compile_definitions(${PROJECT_NAME} PRIVATE
"$<$<CONFIG:Debug>:"
"_DEBUG"
"_DEBUG;"
">"
"$<$<CONFIG:Release>:"
"NDEBUG"
"NDEBUG;"
">"
"F3DEX_GBI_2"
"SPDLOG_ACTIVE_LEVEL=3;"
"F3DEX_GBI_2;"
"SPDLOG_NO_THREAD_ID;"
"SPDLOG_NO_TLS;"
"STBI_NO_THREAD_LOCALS;"
SPDLOG_ACTIVE_LEVEL=${SPDLOG_MIN_CUTOFF}
LOG_LEVEL_GAME_PRINTS=${SPDLOG_LEVEL_OFF}
)
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang")
target_compile_definitions(${PROJECT_NAME} PRIVATE
"$<$<CONFIG:Debug>:"
"_DEBUG"
"_DEBUG;"
">"
"$<$<CONFIG:Release>:"
"NDEBUG"
"NDEBUG;"
">"
"F3DEX_GBI_2"
"$<$<BOOL:${BUILD_REMOTE_CONTROL}>:ENABLE_REMOTE_CONTROL>"
"SPDLOG_ACTIVE_LEVEL=0;"
"F3DEX_GBI_2;"
"$<$<BOOL:${BUILD_REMOTE_CONTROL}>:ENABLE_REMOTE_CONTROL>;"
"_CONSOLE;"
"_CRT_SECURE_NO_WARNINGS;"
"ENABLE_OPENGL;"
"UNICODE;"
"_UNICODE"
"_UNICODE;"
SPDLOG_ACTIVE_LEVEL=${SPDLOG_MIN_CUTOFF}
LOG_LEVEL_GAME_PRINTS=${SPDLOG_LEVEL_OFF}
)
endif()
################################################################################
@@ -16,6 +16,7 @@
"ClimbSpeed": 3,
"CrawlSpeed": 2,
"CreditsFix": 1,
"CuccosToReturn": 1,
"CustomizeFishing": 1,
"CustomizeFrogsOcarinaGame": 1,
"CustomizeOcarinaGame": 1,
+2 -6
View File
@@ -13,8 +13,8 @@ extern "C"
#include <libultraship/log/luslog.h>
#include <soh/Enhancements/item-tables/ItemTableTypes.h>
#if defined(INCLUDE_GAME_PRINTF) && defined(_DEBUG)
#define osSyncPrintf(fmt, ...) lusprintf(__FILE__, __LINE__, 0, fmt, ##__VA_ARGS__)
#if (LOG_LEVEL_GAME_PRINTS >= SPDLOG_ACTIVE_LEVEL) && !(LOG_LEVEL_GAME_PRINTS >= 6)
#define osSyncPrintf(...) lusprintf(__FILE__, __LINE__, LOG_LEVEL_GAME_PRINTS , __VA_ARGS__)
#else
#define osSyncPrintf(fmt, ...) osSyncPrintfUnused(fmt, ##__VA_ARGS__)
#endif
@@ -2457,10 +2457,6 @@ void Interface_RandoRestoreSwordless(void);
s32 Ship_CalcShouldDrawAndUpdate(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projectedW, bool* shouldDraw,
bool* shouldUpdate);
//Pause Warp
void PauseWarp_HandleSelection();
void PauseWarp_Execute();
// #endregion
#ifdef __cplusplus
+3
View File
@@ -146,6 +146,9 @@ typedef enum {
/* 0x1B */ SLOT_BOOTS_KOKIRI,
/* 0x1C */ SLOT_BOOTS_IRON,
/* 0x1D */ SLOT_BOOTS_HOVER,
/* 0x1E */ SLOT_SHIELD_DEKU,
/* 0x1F */ SLOT_SHIELD_HYLIAN,
/* 0x20 */ SLOT_SHIELD_MIRROR,
/* 0xFF */ SLOT_NONE = 0xFF
} InventorySlot;
@@ -14,7 +14,7 @@ extern PlayState* gPlayState;
static u16 sItemButtons[] = { BTN_B, BTN_CLEFT, BTN_CDOWN, BTN_CRIGHT, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT };
void UseTunicBoots(Player* player, PlayState* play, Input* input) {
static void UseTunicBoots(Player* player, PlayState* play, Input* input) {
// Boots and tunics equip despite state
if (player->stateFlags1 & (PLAYER_STATE1_INPUT_DISABLED | PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE |
PLAYER_STATE1_TALKING | PLAYER_STATE1_DEAD) ||
@@ -30,7 +30,7 @@ void UseTunicBoots(Player* player, PlayState* play, Input* input) {
}
}
if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) {
if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) {
if (item >= ITEM_BOOTS_KOKIRI) {
u16 bootsValue = item - ITEM_BOOTS_KOKIRI + 1;
if (CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS) == bootsValue) {
@@ -41,7 +41,7 @@ void UseTunicBoots(Player* player, PlayState* play, Input* input) {
Player_SetEquipmentData(play, player);
func_808328EC(player, CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS) == EQUIP_VALUE_BOOTS_IRON ? NA_SE_PL_WALK_HEAVYBOOTS
: NA_SE_PL_CHANGE_ARMS);
} else {
} else if (item >= ITEM_TUNIC_KOKIRI) {
u16 tunicValue = item - ITEM_TUNIC_KOKIRI + 1;
if (CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC) == tunicValue) {
Inventory_ChangeEquipment(EQUIP_TYPE_TUNIC, EQUIP_VALUE_TUNIC_KOKIRI);
@@ -50,45 +50,93 @@ void UseTunicBoots(Player* player, PlayState* play, Input* input) {
}
Player_SetEquipmentData(play, player);
func_808328EC(player, NA_SE_PL_CHANGE_ARMS);
} else {
u16 shieldValue = item - ITEM_SHIELD_DEKU + 1;
if (CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) != shieldValue) {
Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, shieldValue);
Player_SetEquipmentData(play, player);
func_808328EC(player, NA_SE_PL_CHANGE_ARMS);
}
}
}
}
void ClearAssignedTunicsBoots(int32_t unused = 0) {
static void ClearAssignedTunicsBoots(int32_t unused = 0) {
for (int32_t buttonIndex = 0; buttonIndex < 8; buttonIndex++) {
int32_t item = gSaveContext.equips.buttonItems[buttonIndex];
if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) {
if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) {
gSaveContext.equips.buttonItems[buttonIndex] = ITEM_NONE;
}
}
}
static void ClearDeletedAssignedEquipment(int16_t equipmentType, uint16_t equipValue) {
ItemID itemToRemove = ITEM_NONE;
if (equipmentType == EQUIP_TYPE_TUNIC) {
switch (equipValue) {
case EQUIP_VALUE_TUNIC_KOKIRI:
break;
case EQUIP_VALUE_TUNIC_GORON:
itemToRemove = ITEM_TUNIC_GORON;
break;
case EQUIP_VALUE_TUNIC_ZORA:
itemToRemove = ITEM_TUNIC_ZORA;
break;
}
} else if (equipmentType == EQUIP_TYPE_SHIELD) {
switch (equipValue) {
case EQUIP_VALUE_SHIELD_DEKU:
itemToRemove = ITEM_SHIELD_DEKU;
break;
case EQUIP_VALUE_SHIELD_HYLIAN:
itemToRemove = ITEM_SHIELD_HYLIAN;
break;
case EQUIP_VALUE_SHIELD_MIRROR:
itemToRemove = ITEM_SHIELD_MIRROR;
break;
}
}
if (itemToRemove == ITEM_NONE) {
return;
}
for (int i = 1; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
if (gSaveContext.equips.buttonItems[i] == itemToRemove) {
gSaveContext.equips.buttonItems[i] = ITEM_NONE;
gSaveContext.equips.cButtonSlots[i - 1] = SLOT_NONE;
}
}
}
#define CVAR_TUNICBOOTS_NAME CVAR_ENHANCEMENT("AssignableTunicsAndBoots")
#define CVAR_TUNICBOOTS_DEFAULT 0
#define CVAR_TUNICBOOTS_VALUE CVarGetInteger(CVAR_TUNICBOOTS_NAME, CVAR_TUNICBOOTS_DEFAULT)
#define CVAR_TUNICBOOTS_SET (CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT)
void RegisterAssignableTunicsBoots() {
// make sure we don't change our held/equipped item when changing tunics/boots
COND_VB_SHOULD(VB_CHANGE_HELD_ITEM_AND_USE_ITEM, CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT, {
static void RegisterAssignableTunicsBoots() {
// make sure we don't change our held/equipped item when changing shield/tunic/boots
COND_VB_SHOULD(VB_CHANGE_HELD_ITEM_AND_USE_ITEM, CVAR_TUNICBOOTS_SET, {
int32_t item = va_arg(args, int32_t);
if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) {
if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) {
*should = false;
}
});
// make sure we don't crash because tunics/boots don't have assoicated item actions
COND_VB_SHOULD(VB_ITEM_ACTION_BE_NONE, CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT, {
COND_VB_SHOULD(VB_ITEM_ACTION_BE_NONE, CVAR_TUNICBOOTS_SET, {
int32_t item = va_arg(args, int32_t);
if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) {
if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) {
*should = true;
}
});
// don't throw items when the pressed button is a tunic or boots
COND_VB_SHOULD(VB_THROW_OR_PUT_DOWN_HELD_ITEM, CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT, {
// don't throw items when the pressed button is a shield, tunic or boots
COND_VB_SHOULD(VB_THROW_OR_PUT_DOWN_HELD_ITEM, CVAR_TUNICBOOTS_SET, {
// if the vanilla condition doesn't want us to throw/put down the item, early return
if (!*should) {
return;
@@ -104,13 +152,13 @@ void RegisterAssignableTunicsBoots() {
}
}
if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) {
if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) {
*should = false;
}
});
// do something when the player presses a button to use the tunics/boots
COND_VB_SHOULD(VB_EXECUTE_PLAYER_ACTION_FUNC, CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT, {
COND_VB_SHOULD(VB_EXECUTE_PLAYER_ACTION_FUNC, CVAR_TUNICBOOTS_SET, {
// if the vanilla condition doesn't want us to run the actionFunc, don't do any of this
if (!*should) {
return;
@@ -133,13 +181,16 @@ void RegisterAssignableTunicsBoots() {
UseTunicBoots(player, gPlayState, input);
});
// remove assigned equipment when it gets deleted
COND_HOOK(OnEquipmentDelete, CVAR_TUNICBOOTS_SET, ClearDeletedAssignedEquipment);
// clear out assigned tunics/boots when the enhancement is toggled off
if (GameInteractor::IsSaveLoaded(true) && CVAR_TUNICBOOTS_VALUE == CVAR_TUNICBOOTS_DEFAULT) {
if (GameInteractor::IsSaveLoaded(true) && !CVAR_TUNICBOOTS_SET) {
ClearAssignedTunicsBoots();
}
// clear out assigned tunics/boots when loading a save with enhancement turned off
COND_HOOK(OnLoadGame, CVAR_TUNICBOOTS_VALUE == CVAR_TUNICBOOTS_DEFAULT, ClearAssignedTunicsBoots);
COND_HOOK(OnLoadGame, !CVAR_TUNICBOOTS_SET, ClearAssignedTunicsBoots);
}
static RegisterShipInitFunc initFunc(RegisterAssignableTunicsBoots, { CVAR_TUNICBOOTS_NAME });
@@ -0,0 +1,88 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ObjectExtension/ActorMaximumHealth.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include "functions.h"
#include "src/overlays/actors/ovl_En_Fz/z_en_fz.h"
}
static constexpr int32_t CVAR_RANDO_ENEMY_SIZE_DEFAULT = 0;
#define CVAR_RANDO_ENEMY_SIZE_NAME CVAR_ENHANCEMENT("RandomizedEnemySizes")
#define CVAR_RANDO_ENEMY_SIZE_VALUE CVarGetInteger(CVAR_RANDO_ENEMY_SIZE_NAME, CVAR_RANDO_ENEMY_SIZE_DEFAULT)
static constexpr int32_t CVAR_ENEMY_SCALE_HEALTH_DEFAULT = 0;
#define CVAR_ENEMY_SCALE_HEALTH_NAME CVAR_ENHANCEMENT("EnemySizeScalesHealth")
#define CVAR_ENEMY_SCALE_HEALTH_VALUE CVarGetInteger(CVAR_ENEMY_SCALE_HEALTH_NAME, CVAR_ENEMY_SCALE_HEALTH_DEFAULT)
static void RandomizedEnemySizes(void* refActor) {
// Randomized Enemy Sizes
Actor* actor = static_cast<Actor*>(refActor);
// Exclude wobbly platforms in Jabu because they need to act like platforms.
// Exclude demo effect for Zora sapphire being re-categorized as a "boss".
// Exclude Dead Hand hands and Bongo Bongo main body because they make the fights (near) impossible.
bool excludedEnemy = actor->id == ACTOR_EN_BROB || actor->id == ACTOR_EN_DHA || actor->id == ACTOR_DEMO_EFFECT ||
(actor->id == ACTOR_BOSS_SST && actor->params == -1);
// Only apply to enemies and bosses.
if ((actor->category != ACTORCAT_ENEMY && actor->category != ACTORCAT_BOSS) || excludedEnemy) {
return;
}
float randomNumber;
float randomScale;
// Dodongo, Volvagia and Dead Hand are always smaller because they're impossible when bigger.
bool smallOnlyEnemy = actor->id == ACTOR_BOSS_DODONGO || actor->id == ACTOR_BOSS_FD ||
actor->id == ACTOR_BOSS_FD2 || actor->id == ACTOR_EN_DH;
bool bigActor = !smallOnlyEnemy && (rand() % 2);
// Big actor
if (bigActor) {
randomNumber = rand() % 200;
// Between 100% and 300% size.
randomScale = 1.0f + (randomNumber / 100);
} else {
// Small actor
randomNumber = rand() % 90;
// Between 10% and 100% size.
randomScale = 0.1f + (randomNumber / 100);
}
Actor_SetScale(actor, actor->scale.z * randomScale);
if (CVAR_ENEMY_SCALE_HEALTH_VALUE && (actor->category == ACTORCAT_ENEMY)) {
// Scale the health based on a smaller factor than randomScale
float healthScalingFactor = 0.8f; // Adjust this factor as needed
float scaledHealth = actor->colChkInfo.health * (randomScale * healthScalingFactor);
// Ensure the scaled health doesn't go below zero
actor->colChkInfo.health = fmax(scaledHealth, 1.0f);
// Ensure maximum health gets set
SetActorMaximumHealth(actor, actor->colChkInfo.health);
}
}
static void RegisterRandomizedEnemySizes() {
COND_HOOK(OnActorInit, CVAR_RANDO_ENEMY_SIZE_VALUE, RandomizedEnemySizes);
}
static void RegisterFreezardHealthScale() {
COND_VB_SHOULD(VB_FREEZARD_SCALE_HEALTH_WITH_SIZE, CVAR_RANDO_ENEMY_SIZE_VALUE && CVAR_ENEMY_SCALE_HEALTH_VALUE, {
// With enemy health scaling, the Freezard's health could cause an index out of bounds for the displayLists, so
// we need to recompute the index based on the scaled health (using the maximum health value) and clamp the
// final result for safety.
Actor* actor = va_arg(args, Actor*);
s32* index = va_arg(args, s32*);
u8 scaledHealth = (u8)(((f32)actor->colChkInfo.health / GetActorMaximumHealth(actor)) * 6);
*index = CLAMP((6 - scaledHealth) >> 1, 0, 2);
});
}
static RegisterShipInitFunc initFunc_EnemySizes(RegisterRandomizedEnemySizes, { CVAR_RANDO_ENEMY_SIZE_NAME });
static RegisterShipInitFunc initFunc_Freezard(RegisterFreezardHealthScale,
{ CVAR_RANDO_ENEMY_SIZE_NAME, CVAR_ENEMY_SCALE_HEALTH_NAME });
@@ -0,0 +1,59 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include "macros.h"
#include "functions.h"
}
#define FIDGET_SWORD_SWING 9
#define FIDGET_ADJUST_SHIELD 12
static constexpr int32_t CVAR_FIX_TWO_HANDED_IDLE_DEFAULT = 0;
#define CVAR_FIX_TWO_HANDED_IDLE_NAME CVAR_ENHANCEMENT("TwoHandedIdle")
#define CVAR_FIX_TWO_HANDED_IDLE_VALUE CVarGetInteger(CVAR_FIX_TWO_HANDED_IDLE_NAME, CVAR_FIX_TWO_HANDED_IDLE_DEFAULT)
// clang-format off
static RegisterShipInitFunc initFunc([]() {
COND_VB_SHOULD(VB_SET_IDLE_ANIM, CVAR_FIX_TWO_HANDED_IDLE_VALUE, {
Player* player = va_arg(args, Player*);
s32 commonType = va_arg(args, s32);
// Fixes a bug here where the condition for reaching two-handed idle animation was impossible. Original condition:
/*
(
(
(commonType + FIDGET_SWORD_SWING != FIDGET_SWORD_SWING) &&
(commonType + FIDGET_SWORD_SWING != FIDGET_ADJUST_SHIELD)
) ||
(
(player->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) &&
(
(commonType + FIDGET_SWORD_SWING == FIDGET_ADJUST_SHIELD) ||
// This should not have been grouped here, because two handed melee weapons do not have shield.
(Player_GetMeleeWeaponHeld2(player) != 0)
)
)
)
*/
*should = (
// Animation is not FIDGET_SWORD_SWING and FIDGET_ADJUST_SHIELD (So it's either FIDGET_ADJUST_TUNIC or FIDGET_TAP_FEET)
(
(commonType + FIDGET_SWORD_SWING != FIDGET_SWORD_SWING) &&
(commonType + FIDGET_SWORD_SWING != FIDGET_ADJUST_SHIELD)
) ||
// Animation is FIDGET_ADJUST_SHIELD and player is holding a shield in right hand
(
(player->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) &&
(commonType + FIDGET_SWORD_SWING == FIDGET_ADJUST_SHIELD)
) ||
// Animation is FIDGET_SWORD_SWING and player is holding a melee weapon
(
(commonType + FIDGET_SWORD_SWING == FIDGET_SWORD_SWING) &&
(Player_GetMeleeWeaponHeld(player) != 0)
)
);
});
}, { CVAR_FIX_TWO_HANDED_IDLE_NAME });
// clang-format on
@@ -0,0 +1,223 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include "src/overlays/actors/ovl_En_Bb/z_en_bb.h"
#include "src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.h"
#include "src/overlays/actors/ovl_En_Mb/z_en_mb.h"
#include "src/overlays/actors/ovl_En_Tite/z_en_tite.h"
#include "src/overlays/actors/ovl_En_Zf/z_en_zf.h"
#include "src/overlays/actors/ovl_En_Wf/z_en_wf.h"
#include "src/overlays/actors/ovl_En_Reeba/z_en_reeba.h"
#include "src/overlays/actors/ovl_En_Peehat/z_en_peehat.h"
#include "src/overlays/actors/ovl_En_Po_Field/z_en_po_field.h"
#include "src/overlays/actors/ovl_En_Poh/z_en_poh.h"
#include "src/overlays/actors/ovl_En_Tp/z_en_tp.h"
#include "src/overlays/actors/ovl_En_Firefly/z_en_firefly.h"
extern SaveContext gSaveContext;
}
static void IncrementEnemyDefeatCount(GameplayStatCount countType) {
gSaveContext.ship.stats.count[countType]++;
}
#define ENEMY_DEFEAT_COUNT(actorID, func) \
COND_ID_HOOK(OnEnemyDefeat, actorID, true, [](void* refActor) { func(static_cast<Actor*>(refActor)); });
#define ENEMY_DEFEAT_COUNT_UNIQUE(actorID, countType) \
COND_ID_HOOK(OnEnemyDefeat, actorID, true, [](void*) { IncrementEnemyDefeatCount(countType); });
static void EnemyDefeatCounts_EnBb(Actor* actor) {
GameplayStatCount countType;
switch (actor->params) {
case ENBB_GREEN:
case ENBB_GREEN_BIG:
countType = COUNT_ENEMIES_DEFEATED_BUBBLE_GREEN;
break;
case ENBB_BLUE:
countType = COUNT_ENEMIES_DEFEATED_BUBBLE_BLUE;
break;
case ENBB_WHITE:
countType = COUNT_ENEMIES_DEFEATED_BUBBLE_WHITE;
break;
case ENBB_RED:
countType = COUNT_ENEMIES_DEFEATED_BUBBLE_RED;
break;
default:
return;
}
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnDekubaba(Actor* actor) {
GameplayStatCount countType =
(actor->params == DEKUBABA_BIG) ? COUNT_ENEMIES_DEFEATED_DEKU_BABA_BIG : COUNT_ENEMIES_DEFEATED_DEKU_BABA;
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnZf(Actor* actor) {
GameplayStatCount countType =
(actor->params == ENZF_TYPE_DINOLFOS) ? COUNT_ENEMIES_DEFEATED_DINOLFOS : COUNT_ENEMIES_DEFEATED_LIZALFOS;
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnRd(Actor* actor) {
GameplayStatCount countType = (actor->params >= -1) ? COUNT_ENEMIES_DEFEATED_REDEAD : COUNT_ENEMIES_DEFEATED_GIBDO;
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnIk(Actor* actor) {
GameplayStatCount countType =
(actor->params == 0) ? COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE_NABOORU : COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE;
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnFirefly(Actor* actor) {
GameplayStatCount countType;
switch (actor->params) {
case KEESE_NORMAL_FLY:
case KEESE_NORMAL_PERCH:
countType = COUNT_ENEMIES_DEFEATED_KEESE;
break;
case KEESE_FIRE_FLY:
case KEESE_FIRE_PERCH:
countType = COUNT_ENEMIES_DEFEATED_KEESE_FIRE;
break;
case KEESE_ICE_FLY:
countType = COUNT_ENEMIES_DEFEATED_KEESE_ICE;
break;
default:
return;
}
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnTp(Actor* actor) {
// Only count the head, otherwise each body segment will increment
if (actor->params == TAILPASARAN_HEAD) {
IncrementEnemyDefeatCount(COUNT_ENEMIES_DEFEATED_TAILPASARAN);
}
}
static void EnemyDefeatCounts_EnReeba(Actor* actor) {
EnReeba* reeba = (EnReeba*)actor;
GameplayStatCount countType = reeba->isBig ? COUNT_ENEMIES_DEFEATED_LEEVER_BIG : COUNT_ENEMIES_DEFEATED_LEEVER;
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnMb(Actor* actor) {
GameplayStatCount countType =
(actor->params == 0) ? COUNT_ENEMIES_DEFEATED_MOBLIN_CLUB : COUNT_ENEMIES_DEFEATED_MOBLIN;
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnPeehat(Actor* actor) {
GameplayStatCount countType =
(actor->params == PEAHAT_TYPE_LARVA) ? COUNT_ENEMIES_DEFEATED_PEAHAT_LARVA : COUNT_ENEMIES_DEFEATED_PEAHAT;
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnPoh(Actor* actor) {
GameplayStatCount countType = (actor->params == EN_POH_FLAT || actor->params == EN_POH_SHARP)
? COUNT_ENEMIES_DEFEATED_POE_COMPOSER
: COUNT_ENEMIES_DEFEATED_POE;
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnPoField(Actor* actor) {
GameplayStatCount countType =
(actor->params == EN_PO_FIELD_BIG) ? COUNT_ENEMIES_DEFEATED_POE_BIG : COUNT_ENEMIES_DEFEATED_POE;
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnSt(Actor* actor) {
GameplayStatCount countType =
(actor->params == 1) ? COUNT_ENEMIES_DEFEATED_SKULLTULA_BIG : COUNT_ENEMIES_DEFEATED_SKULLTULA;
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnSw(Actor* actor) {
GameplayStatCount countType;
if (((actor->params & 0xE000) >> 0xD) != 0) {
countType = COUNT_ENEMIES_DEFEATED_SKULLTULA_GOLD;
} else {
countType = COUNT_ENEMIES_DEFEATED_SKULLWALLTULA;
}
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnTite(Actor* actor) {
GameplayStatCount countType =
(actor->params == TEKTITE_BLUE) ? COUNT_ENEMIES_DEFEATED_TEKTITE_BLUE : COUNT_ENEMIES_DEFEATED_TEKTITE_RED;
IncrementEnemyDefeatCount(countType);
}
static void EnemyDefeatCounts_EnWf(Actor* actor) {
GameplayStatCount countType =
(actor->params == WOLFOS_WHITE) ? COUNT_ENEMIES_DEFEATED_WOLFOS_WHITE : COUNT_ENEMIES_DEFEATED_WOLFOS;
IncrementEnemyDefeatCount(countType);
}
static void RegisterEnemyDefeatCounts() {
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_ANUBICE, COUNT_ENEMIES_DEFEATED_ANUBIS);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_AM, COUNT_ENEMIES_DEFEATED_ARMOS);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_CLEAR_TAG, COUNT_ENEMIES_DEFEATED_ARWING);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_VALI, COUNT_ENEMIES_DEFEATED_BARI);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_VM, COUNT_ENEMIES_DEFEATED_BEAMOS);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_BIGOKUTA, COUNT_ENEMIES_DEFEATED_BIG_OCTO);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_BILI, COUNT_ENEMIES_DEFEATED_BIRI);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_DNS, COUNT_ENEMIES_DEFEATED_BUSINESS_SCRUB);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_TORCH2, COUNT_ENEMIES_DEFEATED_DARK_LINK);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_DH, COUNT_ENEMIES_DEFEATED_DEAD_HAND);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_HINTNUTS, COUNT_ENEMIES_DEFEATED_DEKU_SCRUB);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_DODONGO, COUNT_ENEMIES_DEFEATED_DODONGO);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_DODOJR, COUNT_ENEMIES_DEFEATED_DODONGO_BABY);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_DOOR_KILLER, COUNT_ENEMIES_DEFEATED_DOOR_TRAP);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_FD, COUNT_ENEMIES_DEFEATED_FLARE_DANCER);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_FLOORMAS, COUNT_ENEMIES_DEFEATED_FLOORMASTER);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_TUBO_TRAP, COUNT_ENEMIES_DEFEATED_FLYING_POT);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_YUKABYUN, COUNT_ENEMIES_DEFEATED_FLOOR_TILE);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_FZ, COUNT_ENEMIES_DEFEATED_FREEZARD);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_GELDB, COUNT_ENEMIES_DEFEATED_GERUDO_THIEF);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_GOMA, COUNT_ENEMIES_DEFEATED_GOHMA_LARVA);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_CROW, COUNT_ENEMIES_DEFEATED_GUAY);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_RR, COUNT_ENEMIES_DEFEATED_LIKE_LIKE);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_DEKUNUTS, COUNT_ENEMIES_DEFEATED_MAD_SCRUB);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_OKUTA, COUNT_ENEMIES_DEFEATED_OCTOROK);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_BA, COUNT_ENEMIES_DEFEATED_PARASITIC_TENTACLE);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_PO_SISTERS, COUNT_ENEMIES_DEFEATED_POE_SISTERS);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_BUBBLE, COUNT_ENEMIES_DEFEATED_SHABOM);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_SB, COUNT_ENEMIES_DEFEATED_SHELLBLADE);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_SKJ, COUNT_ENEMIES_DEFEATED_SKULL_KID);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_NY, COUNT_ENEMIES_DEFEATED_SPIKE);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_SKB, COUNT_ENEMIES_DEFEATED_STALCHILD);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_TEST, COUNT_ENEMIES_DEFEATED_STALFOS);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_EIYER, COUNT_ENEMIES_DEFEATED_STINGER);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_WEIYER, COUNT_ENEMIES_DEFEATED_STINGER);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_BW, COUNT_ENEMIES_DEFEATED_TORCH_SLUG);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_WALLMAS, COUNT_ENEMIES_DEFEATED_WALLMASTER);
ENEMY_DEFEAT_COUNT_UNIQUE(ACTOR_EN_KAREBABA, COUNT_ENEMIES_DEFEATED_WITHERED_DEKU_BABA);
ENEMY_DEFEAT_COUNT(ACTOR_EN_BB, EnemyDefeatCounts_EnBb);
ENEMY_DEFEAT_COUNT(ACTOR_EN_DEKUBABA, EnemyDefeatCounts_EnDekubaba);
ENEMY_DEFEAT_COUNT(ACTOR_EN_ZF, EnemyDefeatCounts_EnZf);
ENEMY_DEFEAT_COUNT(ACTOR_EN_RD, EnemyDefeatCounts_EnRd);
ENEMY_DEFEAT_COUNT(ACTOR_EN_IK, EnemyDefeatCounts_EnIk);
ENEMY_DEFEAT_COUNT(ACTOR_EN_FIREFLY, EnemyDefeatCounts_EnFirefly);
ENEMY_DEFEAT_COUNT(ACTOR_EN_TP, EnemyDefeatCounts_EnTp);
ENEMY_DEFEAT_COUNT(ACTOR_EN_REEBA, EnemyDefeatCounts_EnReeba);
ENEMY_DEFEAT_COUNT(ACTOR_EN_MB, EnemyDefeatCounts_EnMb);
ENEMY_DEFEAT_COUNT(ACTOR_EN_PEEHAT, EnemyDefeatCounts_EnPeehat);
ENEMY_DEFEAT_COUNT(ACTOR_EN_POH, EnemyDefeatCounts_EnPoh);
ENEMY_DEFEAT_COUNT(ACTOR_EN_PO_FIELD, EnemyDefeatCounts_EnPoField);
ENEMY_DEFEAT_COUNT(ACTOR_EN_ST, EnemyDefeatCounts_EnSt);
ENEMY_DEFEAT_COUNT(ACTOR_EN_SW, EnemyDefeatCounts_EnSw);
ENEMY_DEFEAT_COUNT(ACTOR_EN_TITE, EnemyDefeatCounts_EnTite);
ENEMY_DEFEAT_COUNT(ACTOR_EN_WF, EnemyDefeatCounts_EnWf);
}
static RegisterShipInitFunc initFunc(RegisterEnemyDefeatCounts);
@@ -13,7 +13,7 @@ extern PlayState* gPlayState;
#define CVAR_NAME CVAR_ENHANCEMENT("3DSceneRender")
#define CVAR_VALUE CVarGetInteger(CVAR_NAME, 0)
std::vector<SceneID> fogControlList = {
std::set<SceneID> fogControlList = {
SCENE_MARKET_ENTRANCE_DAY,
SCENE_MARKET_ENTRANCE_NIGHT,
SCENE_MARKET_ENTRANCE_RUINS,
@@ -46,7 +46,7 @@ std::vector<SceneID> fogControlList = {
SCENE_GRAVEKEEPERS_HUT,
};
std::vector<SceneID> skyboxSceneControlList = {
std::set<SceneID> skyboxSceneControlList = {
SCENE_MARKET_ENTRANCE_DAY,
SCENE_MARKET_ENTRANCE_NIGHT,
SCENE_MARKET_ENTRANCE_RUINS,
@@ -62,7 +62,7 @@ std::vector<SceneID> skyboxSceneControlList = {
SCENE_FOREST_TEMPLE,
};
std::vector<SkyboxId> skyboxIdControlList = {
std::set<SkyboxId> skyboxIdControlList = {
SKYBOX_BAZAAR,
SKYBOX_HOUSE_LINK,
SKYBOX_MARKET_ADULT,
@@ -88,74 +88,49 @@ std::vector<SkyboxId> skyboxIdControlList = {
void Register3DPreRenderedScenes() {
COND_HOOK(AfterSceneCommands, CVAR_VALUE, [](int16_t sceneNum) {
// Check if this scene is in the skyboxControlList
bool shouldControlSkybox = false;
for (const auto& scene : skyboxSceneControlList) {
if (sceneNum == scene) {
shouldControlSkybox = true;
break;
}
if (!skyboxSceneControlList.contains(static_cast<SceneID>(sceneNum))) {
return;
}
if (shouldControlSkybox) {
// Add a skybox on scenes from skyboxSceneControlList
gPlayState->envCtx.skyboxDisabled = false;
// Add a skybox on scenes from skyboxSceneControlList
gPlayState->envCtx.skyboxDisabled = false;
// Replace skybox with normal sky
gPlayState->skyboxId = SKYBOX_NORMAL_SKY;
// Apply the always cloudy skybox as an adult for Temple of Time and the Market
if (sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS || sceneNum == SCENE_MARKET_RUINS ||
sceneNum == SCENE_MARKET_ENTRANCE_RUINS) {
gWeatherMode = 3;
}
// Replace skybox with normal sky
gPlayState->skyboxId = SKYBOX_NORMAL_SKY;
// Apply the always cloudy skybox as an adult for Temple of Time and the Market
if (sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS || sceneNum == SCENE_MARKET_RUINS ||
sceneNum == SCENE_MARKET_ENTRANCE_RUINS) {
gWeatherMode = 3;
}
});
COND_HOOK(OnPlayDrawBegin, CVAR_VALUE, []() {
if (!CVarGetInteger(CVAR_ENHANCEMENT("3DSceneRender"), 0)) {
if (!fogControlList.contains(static_cast<SceneID>(gPlayState->sceneNum))) {
return;
}
for (auto& scene : fogControlList) {
if (scene == gPlayState->sceneNum) {
if ((HREG(80) != 10) || (HREG(82) != 0)) {
// Furthest possible fog and zFar
gPlayState->view.zFar = 12800;
gPlayState->lightCtx.fogNear = 996; // Set to 1000 to complete disable fog entirely
gPlayState->lightCtx.fogFar = 12800;
// General gray fog color
gPlayState->lightCtx.fogColor[0] = 100;
gPlayState->lightCtx.fogColor[1] = 100;
gPlayState->lightCtx.fogColor[2] = 100;
}
break;
}
}
});
REGISTER_VB_SHOULD(VB_DRAW_2D_BACKGROUND, {
if (CVAR_VALUE) {
*should = false;
return;
if ((HREG(80) != 10) || (HREG(82) != 0)) {
// Furthest possible fog and zFar
gPlayState->view.zFar = 12800;
gPlayState->lightCtx.fogNear = 996; // Set to 1000 to complete disable fog entirely
gPlayState->lightCtx.fogFar = 12800;
// General gray fog color
gPlayState->lightCtx.fogColor[0] = 100;
gPlayState->lightCtx.fogColor[1] = 100;
gPlayState->lightCtx.fogColor[2] = 100;
}
});
REGISTER_VB_SHOULD(VB_LOAD_SKYBOX, {
if (!gPlayState) {
COND_VB_SHOULD(VB_DRAW_2D_BACKGROUND, CVAR_VALUE, { *should = false; });
COND_VB_SHOULD(VB_LOAD_SKYBOX, CVAR_VALUE, {
if (!gPlayState || !skyboxIdControlList.contains(static_cast<SkyboxId>(gPlayState->skyboxCtx.skyboxId))) {
return;
}
if (!CVAR_VALUE) {
return;
}
for (auto& skybox : skyboxIdControlList) {
if (gPlayState->skyboxCtx.skyboxId == skybox) {
gPlayState->skyboxCtx.unk_140 = 0;
*should = false;
return;
}
}
gPlayState->skyboxCtx.unk_140 = 0;
*should = false;
});
}
static RegisterShipInitFunc initFunc(Register3DPreRenderedScenes, { CVAR_NAME });
static RegisterShipInitFunc initFunc(Register3DPreRenderedScenes, { CVAR_NAME });
@@ -0,0 +1,129 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/Enhancements/mods.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include "align_asset_macro.h"
#include "macros.h"
#include "variables.h"
#include "soh/ResourceManagerHelpers.h"
extern PlayState* gPlayState;
}
static constexpr int32_t CVAR_TOT_MEDALLION_COLORS_DEFAULT = 0;
#define CVAR_TOT_MEDALLION_COLORS_NAME CVAR_ENHANCEMENT("ToTMedallionsColors")
#define CVAR_TOT_MEDALLION_COLORS_VALUE \
CVarGetInteger(CVAR_TOT_MEDALLION_COLORS_NAME, CVAR_TOT_MEDALLION_COLORS_DEFAULT)
// GreyScaleEndDlist
#define dgEndGrayscaleAndEndDlistDL "__OTR__helpers/cosmetics/gEndGrayscaleAndEndDlistDL"
static const ALIGN_ASSET(2) char gEndGrayscaleAndEndDlistDL[] = dgEndGrayscaleAndEndDlistDL;
// This is used for the Temple of Time Medalions' color
#define dtokinoma_room_0DL_007A70 "__OTR__scenes/shared/tokinoma_scene/tokinoma_room_0DL_007A70"
static const ALIGN_ASSET(2) char tokinoma_room_0DL_007A70[] = dtokinoma_room_0DL_007A70;
#define dtokinoma_room_0DL_007FD0 "__OTR__scenes/shared/tokinoma_scene/tokinoma_room_0DL_007FD0"
static const ALIGN_ASSET(2) char tokinoma_room_0DL_007FD0[] = dtokinoma_room_0DL_007FD0;
static Gfx grayscaleWhite = gsDPSetGrayscaleColor(255, 255, 255, 255);
class ToTPatchSetup {
public:
ToTPatchSetup(Gfx ifColored, const char* patchName, int index, const char* patchName2 = "", int index2 = 0)
: patchName(patchName), index(index), ifColored(ifColored), patchName2(patchName2), index2(index2) {
}
void ApplyPatch(bool colored = true) {
Gfx colorGfx = colored ? ifColored : grayscaleWhite;
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, patchName, index, colorGfx);
if (patchName2 && *patchName2) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, patchName2, index2, colorGfx);
}
}
void RevertPatch() {
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, patchName);
if (patchName2 && *patchName2) {
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, patchName2);
}
}
private:
const char* patchName;
const char* patchName2;
int index;
int index2;
Gfx ifColored;
};
typedef struct MedallionColorPatch {
QuestItem questItemId;
ToTPatchSetup patch;
} MedallionColorPatch;
static ToTPatchSetup startGrayscale =
ToTPatchSetup(gsSPGrayscale(true), "ToTMedallions_StartGrayscale", 7, "ToTMedallions_2_StartGrayscale", 7);
static MedallionColorPatch medallionColorPatches[] = {
{ QUEST_MEDALLION_WATER, ToTPatchSetup(gsDPSetGrayscaleColor(0, 161, 255, 255), "ToTMedallions_MakeBlue", 16) },
{ QUEST_MEDALLION_SPIRIT, ToTPatchSetup(gsDPSetGrayscaleColor(255, 135, 0, 255), "ToTMedallions_MakeOrange", 45) },
{ QUEST_MEDALLION_LIGHT, ToTPatchSetup(gsDPSetGrayscaleColor(255, 255, 0, 255), "ToTMedallions_MakeYellow", 69,
"ToTMedallions_2_MakeYellow", 16) },
{ QUEST_MEDALLION_FOREST, ToTPatchSetup(gsDPSetGrayscaleColor(0, 255, 0, 255), "ToTMedallions_MakeGreen", 94) },
{ QUEST_MEDALLION_FIRE, ToTPatchSetup(gsDPSetGrayscaleColor(255, 0, 0, 255), "ToTMedallions_MakeRed", 118) },
{ QUEST_MEDALLION_SHADOW, ToTPatchSetup(gsDPSetGrayscaleColor(212, 0, 255, 255), "ToTMedallions_MakePurple", 142,
"ToTMedallions_2_MakePurple", 27) },
};
static ToTPatchSetup endGrayscale =
ToTPatchSetup(gsSPBranchListOTRFilePath(gEndGrayscaleAndEndDlistDL), "ToTMedallions_EndGrayscaleAndEndDlist", 160,
"ToTMedallions_2_EndGrayscaleAndEndDlist", 51);
static void PatchToTMedallions() {
// TODO: Refactor the DemoEffect_UpdateJewelAdult and DemoEffect_UpdateJewelChild from z_demo_effect
// effects to take effect in there
startGrayscale.ApplyPatch();
for (auto& medallionPatch : medallionColorPatches) {
medallionPatch.patch.ApplyPatch(CHECK_QUEST_ITEM(medallionPatch.questItemId));
}
endGrayscale.ApplyPatch();
}
static void ResetToTMedallions() {
// Unpatch everything
startGrayscale.RevertPatch();
for (auto& medallionPatch : medallionColorPatches) {
medallionPatch.patch.RevertPatch();
}
endGrayscale.RevertPatch();
}
void UpdateToTMedallions() {
if (CVAR_TOT_MEDALLION_COLORS_VALUE) {
PatchToTMedallions();
} else {
ResetToTMedallions();
}
}
static void CheckTempleOfTime(int16_t sceneNum) {
if (sceneNum != SCENE_TEMPLE_OF_TIME) {
return;
}
PatchToTMedallions();
}
static void RegisterToTMedallions() {
COND_HOOK(OnItemReceive, CVAR_TOT_MEDALLION_COLORS_VALUE, [](GetItemEntry) {
if (gPlayState) {
CheckTempleOfTime(gPlayState->sceneNum);
}
});
COND_HOOK(OnSceneInit, CVAR_TOT_MEDALLION_COLORS_VALUE, CheckTempleOfTime);
}
static RegisterShipInitFunc initFunc(RegisterToTMedallions, { CVAR_TOT_MEDALLION_COLORS_NAME });
@@ -7,6 +7,7 @@
extern "C" {
extern PlayState* gPlayState;
#include "functions.h"
#include "macros.h"
#include "variables.h"
}
@@ -15,27 +16,31 @@ static uint64_t lastSaveTimestamp = GetUnixTimestamp();
#define CVAR_AUTOSAVE_NAME CVAR_ENHANCEMENT("Autosave")
#define CVAR_AUTOSAVE_DEFAULT AUTOSAVE_OFF
#define CVAR_AUTOSAVE_VALUE CVarGetInteger(CVAR_AUTOSAVE_NAME, CVAR_AUTOSAVE_DEFAULT)
#define THREE_MINUTES_IN_UNIX 3 * 60000
static constexpr uint64_t THREE_MINUTES_IN_UNIX = 3 * 60000;
typedef enum {
AUTOSAVE_OFF,
AUTOSAVE_ON,
} AutosaveOptions;
bool Autosave_CanSave() {
static bool Autosave_CanSave() {
// Don't save when in title screen or debug file
// Don't save a file that doesn't exist (e.g. it was deleted on death by user option)
// Don't save the first 60 frames to not save the magic meter when it's still in the animation of filling it.
// Don't save in Chamber of Sages and the Cutscene map because of remember save location and cutscene item gives.
if (!GameInteractor::IsSaveLoaded(false) || gPlayState->gameplayFrames < 60 ||
gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES || gPlayState->sceneNum == SCENE_CUTSCENE_MAP) {
// Don't save between obtaining Ocarina of Time and Song of Time because the latter would become unobtainable.
if (!SaveManager::Instance->SaveFile_Exist(gSaveContext.fileNum) || !GameInteractor::IsSaveLoaded(false) ||
gPlayState->gameplayFrames < 60 || gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES ||
gPlayState->sceneNum == SCENE_CUTSCENE_MAP ||
(!CHECK_QUEST_ITEM(QUEST_SONG_TIME) && (INV_CONTENT(ITEM_OCARINA_TIME) == ITEM_OCARINA_TIME))) {
return false;
}
return true;
}
void Autosave_PerformSave() {
static void Autosave_PerformSave() {
Play_PerformSave(gPlayState);
// Send notification
@@ -44,7 +49,7 @@ void Autosave_PerformSave() {
});
}
void Autosave_IntervalSave() {
static void Autosave_IntervalSave() {
// Check if the interval has passed in minutes.
uint64_t currentTimestamp = GetUnixTimestamp();
if ((currentTimestamp - lastSaveTimestamp) < THREE_MINUTES_IN_UNIX) {
@@ -64,13 +69,13 @@ void Autosave_IntervalSave() {
}
}
void Autosave_SoftResetSave() {
static void Autosave_SoftResetSave() {
if (Autosave_CanSave()) {
Autosave_PerformSave();
}
}
void RegisterAutosave() {
static void RegisterAutosave() {
COND_HOOK(GameInteractor::OnGameFrameUpdate, CVAR_AUTOSAVE_VALUE, Autosave_IntervalSave);
COND_HOOK(GameInteractor::OnExitGame, CVAR_AUTOSAVE_VALUE, [](int32_t fileNum) { Autosave_SoftResetSave(); });
}
@@ -1,8 +1,21 @@
#include "custom-message/CustomMessageTypes.h"
#include "global.h"
#include "z64.h"
#include "game-interactor/GameInteractor.h"
#include "soh/OTRGlobals.h"
#include <string>
#include "soh/Enhancements/custom-message/CustomMessageTypes.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include "functions.h"
#include "macros.h"
#include "variables.h"
extern PlayState* gPlayState;
u8 Randomizer_GetSettingValue(RandomizerSettingKey);
}
static constexpr int32_t CVAR_PAUSE_WARP_DEFAULT = 0;
#define CVAR_PAUSE_WARP_NAME CVAR_ENHANCEMENT("PauseWarp")
#define CVAR_PAUSE_WARP_VALUE CVarGetInteger(CVAR_PAUSE_WARP_NAME, CVAR_PAUSE_WARP_DEFAULT)
static const int songMessageMap[] = {
TEXT_WARP_MINUET_OF_FOREST, TEXT_WARP_BOLERO_OF_FIRE, TEXT_WARP_SERENADE_OF_WATER,
@@ -30,7 +43,7 @@ static const int songAudioMap[] = {
static bool isWarpActive = false;
void PauseWarp_Execute() {
static void PauseWarp_Execute() {
if (!isWarpActive || gPlayState->msgCtx.msgMode != MSGMODE_NONE) {
return;
}
@@ -48,13 +61,14 @@ void PauseWarp_Execute() {
for (int i = 0; i < ARRAY_COUNT(ocarinaSongMap); i++) {
if (gPlayState->msgCtx.lastPlayedSong == ocarinaSongMap[i]) {
gPlayState->nextEntranceIndex = entranceIndexMap[i];
func_80088AF0(gPlayState);
return;
}
}
gPlayState->transitionTrigger = TRANS_TRIGGER_OFF;
}
void ActivateWarp(PauseContext* pauseCtx, int song) {
static void ActivateWarp(PauseContext* pauseCtx, int song) {
Audio_OcaSetInstrument(0);
Interface_SetDoAction(gPlayState, DO_ACTION_NONE);
pauseCtx->state = 0x12;
@@ -70,7 +84,7 @@ void ActivateWarp(PauseContext* pauseCtx, int song) {
isWarpActive = true;
}
void PauseWarp_HandleSelection() {
static void PauseWarp_HandleSelection() {
if (gSaveContext.inventory.items[SLOT_OCARINA] != ITEM_NONE) {
int aButtonPressed = CHECK_BTN_ALL(gPlayState->state.input->press.button, BTN_A);
int song = gPlayState->pauseCtx.cursorPoint[PAUSE_QUEST];
@@ -122,3 +136,18 @@ void PauseWarp_HandleSelection() {
}
}
}
static void RegisterPauseMenuHooks() {
COND_HOOK(OnKaleidoUpdate, CVAR_PAUSE_WARP_VALUE, [] {
if (GameInteractor::IsSaveLoaded()) {
PauseWarp_HandleSelection();
}
});
COND_HOOK(OnGameFrameUpdate, CVAR_PAUSE_WARP_VALUE, [] {
if (GameInteractor::IsSaveLoaded()) {
PauseWarp_Execute();
}
});
}
static RegisterShipInitFunc initFunc(RegisterPauseMenuHooks, { CVAR_PAUSE_WARP_NAME });
@@ -0,0 +1,37 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/OTRGlobals.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include "src/overlays/actors/ovl_En_Door/z_en_door.h"
extern SaveContext gSaveContext;
extern PlayState* gPlayState;
}
static constexpr int32_t CVAR_DAMPE_ALL_NIGHT_DEFAULT = 0;
#define CVAR_DAMPE_ALL_NIGHT_NAME CVAR_ENHANCEMENT("DampeAllNight")
#define CVAR_DAMPE_ALL_NIGHT_VALUE CVarGetInteger(CVAR_DAMPE_ALL_NIGHT_NAME, CVAR_DAMPE_ALL_NIGHT_DEFAULT)
static constexpr s16 DAMPE_HUT_DOOR_OPEN = 447;
static constexpr s16 DAMPE_HUT_DOOR_CLOSED = 774;
static bool DampeIsResting() {
return LINK_IS_ADULT || gPlayState->sceneNum != SCENE_GRAVEYARD;
}
static void OpenDampeHutDoor(void* refActor) {
EnDoor* enDoor = static_cast<EnDoor*>(refActor);
s16* params = &enDoor->actor.params;
if (*params == DAMPE_HUT_DOOR_CLOSED && !DampeIsResting()) {
*params = DAMPE_HUT_DOOR_OPEN;
EnDoor_SetupType(enDoor, gPlayState);
}
}
static void RegisterDampeAllNight() {
COND_VB_SHOULD(VB_DAMPE_IN_GRAVEYARD_DESPAWN, CVAR_DAMPE_ALL_NIGHT_VALUE, { *should = DampeIsResting(); });
COND_ID_HOOK(OnActorInit, ACTOR_EN_DOOR, CVAR_DAMPE_ALL_NIGHT_VALUE, OpenDampeHutDoor);
}
static RegisterShipInitFunc initFunc(RegisterDampeAllNight, { CVAR_DAMPE_ALL_NIGHT_NAME });
@@ -1377,12 +1377,11 @@ void SohInputEditorWindow::DrawCameraControlPanel() {
CheckboxOptions()
.Color(THEME_COLOR)
.Tooltip("Allows for aiming with the right stick in:\n-First-Person/C-Up view\n-Weapon Aiming"));
if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) {
CVarCheckbox("Allow moving while in first-person mode", CVAR_SETTING("MoveInFirstPerson"),
CheckboxOptions()
.Color(THEME_COLOR)
.Tooltip("Changes the left stick to move the player while in first-person mode"));
}
CVarCheckbox("Allow moving while in first-person mode", CVAR_SETTING("MoveInFirstPerson"),
CheckboxOptions({ { .disabled = !CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0),
.disabledTooltip = "Forced off because Right Stick Aiming is disabled." } })
.Color(THEME_COLOR)
.Tooltip("Changes the left stick to move the player while in first-person mode"));
CVarCheckbox("Invert Aiming X Axis", CVAR_SETTING("Controls.InvertAimingXAxis"),
CheckboxOptions()
.Color(THEME_COLOR)
@@ -12,6 +12,7 @@ DEFINE_HOOK(OnExitGame, (int32_t fileNum));
DEFINE_HOOK(OnGameStateMainStart, ());
DEFINE_HOOK(OnGameFrameUpdate, ());
DEFINE_HOOK(OnItemReceive, (GetItemEntry itemEntry));
DEFINE_HOOK(OnEquipmentDelete, (int16_t equipmentType, uint16_t equipValue));
DEFINE_HOOK(OnSaleEnd, (GetItemEntry itemEntry));
DEFINE_HOOK(OnTransitionEnd, (int16_t sceneNum));
DEFINE_HOOK(OnSceneInit, (int16_t sceneNum));
@@ -34,6 +34,11 @@ void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry) {
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnItemReceive>(itemEntry);
}
void GameInteractor_ExecuteOnEquipmentDelete(int16_t equipmentType, uint16_t equipValue) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnEquipmentDelete>(equipmentType, equipValue);
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnEquipmentDelete>(equipmentType, equipValue);
}
void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSaleEnd>(itemEntry);
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnSaleEnd>(itemEntry);
@@ -15,6 +15,7 @@ void GameInteractor_ExecuteOnExitGame(int32_t fileNum);
void GameInteractor_ExecuteOnGameStateMainStart();
void GameInteractor_ExecuteOnGameFrameUpdate();
void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry);
void GameInteractor_ExecuteOnEquipmentDelete(int16_t equipmentType, uint16_t equipValue);
void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry);
void GameInteractor_ExecuteOnTransitionEndHooks(int16_t sceneNum);
void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum);
@@ -535,6 +535,15 @@ typedef enum {
// - None
VB_FIX_SAW_SOFTLOCK,
// #### `result`
// ```c
// false
// ```
// #### `args`
// - `*EnFz`
// - `*s32`
VB_FREEZARD_SCALE_HEALTH_WITH_SIZE,
// #### `result`
// ```c
// true
@@ -2318,6 +2327,27 @@ typedef enum {
// - `*Player`
VB_SET_STATIC_FLOOR_TYPE,
// #### `result`
// ```c
// (
// (
// (commonType + FIDGET_SWORD_SWING != FIDGET_SWORD_SWING) &&
// (commonType + FIDGET_SWORD_SWING != FIDGET_ADJUST_SHIELD)
// ) ||
// (
// (player->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) &&
// (
// (commonType + FIDGET_SWORD_SWING == FIDGET_ADJUST_SHIELD) ||
// (Player_GetMeleeWeaponHeld2(player) != 0)
// )
// )
// )
// ```
// #### `args`
// - `Player*`
// - `s32` commonType
VB_SET_IDLE_ANIM,
} GIVanillaBehavior;
#endif
+91 -91
View File
@@ -137,97 +137,97 @@ const char* const sceneMappings[] = {
};
const char* const countMappings[] = {
"Anubis:",
"Armos:",
"Arwing:",
"Bari:",
"Biri:",
"Beamos:",
"Big Octo:",
"Bubble (Blue):",
"Bubble (Green):",
"Bubble (Red):",
"Bubble (White):",
"Business Scrub:",
"Dark Link:",
"Dead Hand:",
"Deku Baba:",
"Deku Baba (Big):",
"Deku Scrub:",
"Dinolfos:",
"Dodongo:",
"Dodongo (Baby):",
"Door Mimic:",
"Flare Dancer:",
"Floormaster:",
"Flying Floor Tile:",
"Flying Pot:",
"Freezard:",
"Gerudo Thief:",
"Gibdo:",
"Gohma Larva:",
"Guay:",
"Iron Knuckle:",
"Iron Knuckle (Nab):",
"Keese:",
"Keese (Fire):",
"Keese (Ice):",
"Leever:",
"Leever (Big):",
"Like-Like:",
"Lizalfos:",
"Mad Scrub:",
"Moblin:",
"Moblin (Club):",
"Octorok:",
"Parasitic Tentacle:",
"Peahat:",
"Peahat Larva:",
"Poe:",
"Poe (Big):",
"Poe (Composer):",
"Poe Sisters:",
"Redead:",
"Shabom:",
"Shell Blade:",
"Skull Kid:",
"Skulltula:",
"Skulltula (Big):",
"Skulltula (Gold):",
"Skullwalltula:",
"Spike:",
"Stalchild:",
"Stalfos:",
"Stinger:",
"Tailpasaran:",
"Tektite (Blue):",
"Tektite (Red):",
"Torch Slug:",
"Wallmaster:",
"Withered Deku Baba:",
"Wolfos:",
"Wolfos (White):",
"Deku Sticks:",
"Deku Nuts:",
"Bombs:",
"Arrows:",
"Deku Seeds:",
"Bombchus:",
"Beans:",
"A:",
"B:",
"L:",
"R:",
"Z:",
"C-Up:",
"C-Right:",
"C-Down:",
"C-Left:",
"D-Up:",
"D-Right:",
"D-Down:",
"D-Left:",
"Start:",
"Anubis:", // COUNT_ENEMIES_DEFEATED_ANUBIS
"Armos:", // COUNT_ENEMIES_DEFEATED_ARMOS
"Arwing:", // COUNT_ENEMIES_DEFEATED_ARWING
"Bari:", // COUNT_ENEMIES_DEFEATED_BARI
"Beamos:", // COUNT_ENEMIES_DEFEATED_BEAMOS
"Big Octo:", // COUNT_ENEMIES_DEFEATED_BIG_OCTO
"Biri:", // COUNT_ENEMIES_DEFEATED_BIRI
"Bubble (Green):", // COUNT_ENEMIES_DEFEATED_BUBBLE_GREEN
"Bubble (Blue):", // COUNT_ENEMIES_DEFEATED_BUBBLE_BLUE
"Bubble (White):", // COUNT_ENEMIES_DEFEATED_BUBBLE_WHITE
"Bubble (Red):", // COUNT_ENEMIES_DEFEATED_BUBBLE_RED
"Business Scrub:", // COUNT_ENEMIES_DEFEATED_BUSINESS_SCRUB
"Dark Link:", // COUNT_ENEMIES_DEFEATED_DARK_LINK
"Dead Hand:", // COUNT_ENEMIES_DEFEATED_DEAD_HAND
"Deku Baba:", // COUNT_ENEMIES_DEFEATED_DEKU_BABA
"Deku Baba (Big):", // COUNT_ENEMIES_DEFEATED_DEKU_BABA_BIG
"Deku Scrub:", // COUNT_ENEMIES_DEFEATED_DEKU_SCRUB
"Dinolfos:", // COUNT_ENEMIES_DEFEATED_DINOLFOS
"Dodongo:", // COUNT_ENEMIES_DEFEATED_DODONGO
"Dodongo (Baby):", // COUNT_ENEMIES_DEFEATED_DODONGO_BABY
"Door Mimic:", // COUNT_ENEMIES_DEFEATED_DOOR_TRAP
"Flare Dancer:", // COUNT_ENEMIES_DEFEATED_FLARE_DANCER
"Floormaster:", // COUNT_ENEMIES_DEFEATED_FLOORMASTER
"Flying Pot:", // COUNT_ENEMIES_DEFEATED_FLYING_POT
"Flying Floor Tile:", // COUNT_ENEMIES_DEFEATED_FLOOR_TILE
"Freezard:", // COUNT_ENEMIES_DEFEATED_FREEZARD
"Gerudo Thief:", // COUNT_ENEMIES_DEFEATED_GERUDO_THIEF
"Gibdo:", // COUNT_ENEMIES_DEFEATED_GIBDO
"Gohma Larva:", // COUNT_ENEMIES_DEFEATED_GOHMA_LARVA
"Guay:", // COUNT_ENEMIES_DEFEATED_GUAY
"Iron Knuckle:", // COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE
"Iron Knuckle (Nab):", // COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE_NABOORU
"Keese:", // COUNT_ENEMIES_DEFEATED_KEESE
"Keese (Fire):", // COUNT_ENEMIES_DEFEATED_KEESE_FIRE
"Keese (Ice):", // COUNT_ENEMIES_DEFEATED_KEESE_ICE
"Leever:", // COUNT_ENEMIES_DEFEATED_LEEVER
"Leever (Big):", // COUNT_ENEMIES_DEFEATED_LEEVER_BIG
"Like-Like:", // COUNT_ENEMIES_DEFEATED_LIKE_LIKE
"Lizalfos:", // COUNT_ENEMIES_DEFEATED_LIZALFOS
"Mad Scrub:", // COUNT_ENEMIES_DEFEATED_MAD_SCRUB
"Moblin:", // COUNT_ENEMIES_DEFEATED_MOBLIN
"Moblin (Club):", // COUNT_ENEMIES_DEFEATED_MOBLIN_CLUB
"Octorok:", // COUNT_ENEMIES_DEFEATED_OCTOROK
"Parasitic Tentacle:", // COUNT_ENEMIES_DEFEATED_PARASITIC_TENTACLE
"Peahat:", // COUNT_ENEMIES_DEFEATED_PEAHAT
"Peahat Larva:", // COUNT_ENEMIES_DEFEATED_PEAHAT_LARVA
"Poe:", // COUNT_ENEMIES_DEFEATED_POE
"Poe (Big):", // COUNT_ENEMIES_DEFEATED_POE_BIG
"Poe (Composer):", // COUNT_ENEMIES_DEFEATED_POE_COMPOSER
"Poe Sisters:", // COUNT_ENEMIES_DEFEATED_POE_SISTERS
"Redead:", // COUNT_ENEMIES_DEFEATED_REDEAD
"Shabom:", // COUNT_ENEMIES_DEFEATED_SHABOM
"Shell Blade:", // COUNT_ENEMIES_DEFEATED_SHELLBLADE
"Skulltula:", // COUNT_ENEMIES_DEFEATED_SKULLTULA
"Skulltula (Big):", // COUNT_ENEMIES_DEFEATED_SKULLTULA_BIG
"Skulltula (Gold):", // COUNT_ENEMIES_DEFEATED_SKULLTULA_GOLD
"Skullwalltula:", // COUNT_ENEMIES_DEFEATED_SKULLWALLTULA
"Skull Kid:", // COUNT_ENEMIES_DEFEATED_SKULL_KID
"Spike:", // COUNT_ENEMIES_DEFEATED_SPIKE
"Stalchild:", // COUNT_ENEMIES_DEFEATED_STALCHILD
"Stalfos:", // COUNT_ENEMIES_DEFEATED_STALFOS
"Stinger:", // COUNT_ENEMIES_DEFEATED_STINGER
"Tailpasaran:", // COUNT_ENEMIES_DEFEATED_TAILPASARAN
"Tektite (Blue):", // COUNT_ENEMIES_DEFEATED_TEKTITE_BLUE
"Tektite (Red):", // COUNT_ENEMIES_DEFEATED_TEKTITE_RED
"Torch Slug:", // COUNT_ENEMIES_DEFEATED_TORCH_SLUG
"Wallmaster:", // COUNT_ENEMIES_DEFEATED_WALLMASTER
"Withered Deku Baba:", // COUNT_ENEMIES_DEFEATED_WITHERED_DEKU_BABA
"Wolfos:", // COUNT_ENEMIES_DEFEATED_WOLFOS
"Wolfos (White):", // COUNT_ENEMIES_DEFEATED_WOLFOS_WHITE
"Deku Sticks:", // COUNT_AMMO_USED_STICK
"Deku Nuts:", // COUNT_AMMO_USED_NUT
"Bombs:", // COUNT_AMMO_USED_BOMB
"Arrows:", // COUNT_AMMO_USED_ARROW
"Deku Seeds:", // COUNT_AMMO_USED_SEED
"Bombchus:", // COUNT_AMMO_USED_BOMBCHU
"Beans:", // COUNT_AMMO_USED_BEAN
"A:", // COUNT_BUTTON_PRESSES_A
"B:", // COUNT_BUTTON_PRESSES_B
"L:", // COUNT_BUTTON_PRESSES_L
"R:", // COUNT_BUTTON_PRESSES_R
"Z:", // COUNT_BUTTON_PRESSES_Z
"C-Up:", // COUNT_BUTTON_PRESSES_CUP
"C-Right:", // COUNT_BUTTON_PRESSES_CRIGHT
"C-Down:", // COUNT_BUTTON_PRESSES_CDOWN
"C-Left:", // COUNT_BUTTON_PRESSES_CLEFT
"D-Up:", // COUNT_BUTTON_PRESSES_DUP
"D-Right:", // COUNT_BUTTON_PRESSES_DRIGHT
"D-Down:", // COUNT_BUTTON_PRESSES_DDOWN
"D-Left:", // COUNT_BUTTON_PRESSES_DLEFT
"Start:", // COUNT_BUTTON_PRESSES_START
};
#define COLOR_WHITE ImVec4(1.00f, 1.00f, 1.00f, 1.00f)
+20 -9
View File
@@ -55,6 +55,7 @@ void SetEnabledModsCVarValue() {
}
CVarSetString(CVAR_ENABLED_MODS_NAME, s.c_str());
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
}
void AfterModChange() {
@@ -95,6 +96,8 @@ void ModsHandleDragAndDrop(std::vector<std::string>& objectList, int targetIndex
std::vector<std::string> GetEnabledModsFromCVar() {
std::string enabledModsCVarValue = CVAR_ENABLED_MODS_VALUE;
if (enabledModsCVarValue.empty())
return {};
return StringHelper::Split(enabledModsCVarValue, SEPARATOR);
}
@@ -128,8 +131,9 @@ void UpdateModFiles(bool init = false, bool reset = false) {
disabledModFiles.clear();
unsupportedFiles.clear();
filePaths.clear();
std::string modsPath = Ship::Context::LocateFileAcrossAppDirs("mods", appShortName);
bool changed = false;
std::string modsPath = Ship::Context::LocateFileAcrossAppDirs("mods", appShortName);
std::map<std::string, std::string> tempMods;
if (modsPath.length() > 0 && std::filesystem::exists(modsPath)) {
std::vector<std::filesystem::path> enabledFiles;
if (std::filesystem::is_directory(modsPath)) {
@@ -147,11 +151,17 @@ void UpdateModFiles(bool init = false, bool reset = false) {
bool enabled =
std::find(enabledModFiles.begin(), enabledModFiles.end(), filename) != enabledModFiles.end();
if (!enabled) {
enabledModFiles.push_back(filename);
changed = true;
tempMods.emplace(p.path().lexically_normal().generic_string(), filename);
}
filePaths.emplace(filename, p.path());
}
if (tempMods.size() > 0) {
changed = true;
for (auto [path, name] : tempMods) {
enabledModFiles.push_back(name);
}
tempMods.clear();
}
if (init) {
std::vector<std::string> enabledTemp(enabledModFiles);
for (std::string mod : enabledTemp) {
@@ -159,13 +169,14 @@ void UpdateModFiles(bool init = false, bool reset = false) {
GetArchiveManager()->AddArchive(filePaths.at(mod).generic_string());
} else {
enabledModFiles.erase(std::find(enabledModFiles.begin(), enabledModFiles.end(), mod));
changed = true;
}
}
}
}
}
if (changed) {
SetEnabledModsCVarValue();
if (changed) {
SetEnabledModsCVarValue();
}
}
}
@@ -205,7 +216,7 @@ void DrawMods(bool enabled) {
int switchToIndex = -1;
uint32_t index = 0;
for (int i = selectedModFiles.size() - 1; i >= 0; i--) {
for (int i = static_cast<int>(selectedModFiles.size()) - 1; i >= 0; i--) {
std::string file = selectedModFiles[i];
if (enabled) {
ImGui::BeginGroup();
@@ -365,7 +376,7 @@ void RegisterModMenuWidgets() {
auto options = std::static_pointer_cast<UIWidgets::CheckboxOptions>(info.options);
options->disabled = editing;
});
SohGui::mSohMenu->AddSearchWidget({ enableModsWidget, "Settings", "Mod Menu", "Top", "alternat assets" });
SohGui::mSohMenu->AddSearchWidget({ enableModsWidget, "Settings", "Mod Menu", "Top", "alternate assets" });
tabHotkeyWidget = { .name = "Mods Tab Hotkey", .type = WidgetType::WIDGET_CVAR_CHECKBOX };
tabHotkeyWidget.CVar(CVAR_SETTING("Mods.AlternateAssetsHotkey"))
@@ -375,7 +386,7 @@ void RegisterModMenuWidgets() {
.Tooltip("Allows pressing the Tab key to toggle mods")
.DefaultValue(true));
SohGui::mSohMenu->AddSearchWidget(
{ enableModsWidget, "Settings", "Mod Menu", "Top", "alternat assets tab hotkey" });
{ tabHotkeyWidget, "Settings", "Mod Menu", "Top", "alternate assets tab hotkey" });
}
static RegisterMenuInitFunc menuInitFunc(RegisterModMenuWidgets);
-393
View File
@@ -14,24 +14,7 @@
#include "soh/Enhancements/timesaver_hook_handlers.h"
#include "soh/Enhancements/randomizer/hook_handlers.h"
#include "src/overlays/actors/ovl_En_Bb/z_en_bb.h"
#include "src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.h"
#include "src/overlays/actors/ovl_En_Mb/z_en_mb.h"
#include "src/overlays/actors/ovl_En_Tite/z_en_tite.h"
#include "src/overlays/actors/ovl_En_Zf/z_en_zf.h"
#include "src/overlays/actors/ovl_En_Wf/z_en_wf.h"
#include "src/overlays/actors/ovl_En_Reeba/z_en_reeba.h"
#include "src/overlays/actors/ovl_En_Peehat/z_en_peehat.h"
#include "src/overlays/actors/ovl_En_Po_Field/z_en_po_field.h"
#include "src/overlays/actors/ovl_En_Poh/z_en_poh.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_Xc/z_en_xc.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_Door_Shutter/z_door_shutter.h"
#include "src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.h"
#include "src/overlays/actors/ovl_En_Elf/z_en_elf.h"
#include "objects/object_link_boy/object_link_boy.h"
#include "objects/object_link_child/object_link_child.h"
#include "soh_assets.h"
@@ -49,16 +32,6 @@ extern SaveContext gSaveContext;
extern PlayState* gPlayState;
}
// GreyScaleEndDlist
#define dgEndGrayscaleAndEndDlistDL "__OTR__helpers/cosmetics/gEndGrayscaleAndEndDlistDL"
static const ALIGN_ASSET(2) char gEndGrayscaleAndEndDlistDL[] = dgEndGrayscaleAndEndDlistDL;
// This is used for the Temple of Time Medalions' color
#define dtokinoma_room_0DL_007A70 "__OTR__scenes/shared/tokinoma_scene/tokinoma_room_0DL_007A70"
static const ALIGN_ASSET(2) char tokinoma_room_0DL_007A70[] = dtokinoma_room_0DL_007A70;
#define dtokinoma_room_0DL_007FD0 "__OTR__scenes/shared/tokinoma_scene/tokinoma_room_0DL_007FD0"
static const ALIGN_ASSET(2) char tokinoma_room_0DL_007FD0[] = dtokinoma_room_0DL_007FD0;
/// Switches Link's age and respawns him at the last entrance he entered.
void SwitchAge() {
if (gPlayState == NULL)
@@ -436,196 +409,6 @@ void RegisterResetNaviTimer() {
});
}
// this map is used for enemies that can be uniquely identified by their id
// and that are always counted
// enemies that can't be uniquely identified by their id
// or only sometimes count (like ACTOR_EN_TP)
// have to be manually handled in RegisterEnemyDefeatCounts
static std::unordered_map<u16, u16> uniqueEnemyIdToStatCount = {
{ ACTOR_EN_ANUBICE, COUNT_ENEMIES_DEFEATED_ANUBIS },
{ ACTOR_EN_AM, COUNT_ENEMIES_DEFEATED_ARMOS },
{ ACTOR_EN_CLEAR_TAG, COUNT_ENEMIES_DEFEATED_ARWING },
{ ACTOR_EN_VALI, COUNT_ENEMIES_DEFEATED_BARI },
{ ACTOR_EN_VM, COUNT_ENEMIES_DEFEATED_BEAMOS },
{ ACTOR_EN_BIGOKUTA, COUNT_ENEMIES_DEFEATED_BIG_OCTO },
{ ACTOR_EN_BILI, COUNT_ENEMIES_DEFEATED_BIRI },
{ ACTOR_EN_DNS, COUNT_ENEMIES_DEFEATED_BUSINESS_SCRUB },
{ ACTOR_EN_TORCH, COUNT_ENEMIES_DEFEATED_DARK_LINK },
{ ACTOR_EN_DH, COUNT_ENEMIES_DEFEATED_DEAD_HAND },
{ ACTOR_EN_HINTNUTS, COUNT_ENEMIES_DEFEATED_DEKU_SCRUB },
{ ACTOR_EN_DODONGO, COUNT_ENEMIES_DEFEATED_DODONGO },
{ ACTOR_EN_DODOJR, COUNT_ENEMIES_DEFEATED_DODONGO_BABY },
{ ACTOR_DOOR_KILLER, COUNT_ENEMIES_DEFEATED_DOOR_TRAP },
{ ACTOR_EN_FD, COUNT_ENEMIES_DEFEATED_FLARE_DANCER },
{ ACTOR_EN_FLOORMAS, COUNT_ENEMIES_DEFEATED_FLOORMASTER },
{ ACTOR_EN_TUBO_TRAP, COUNT_ENEMIES_DEFEATED_FLYING_POT },
{ ACTOR_EN_YUKABYUN, COUNT_ENEMIES_DEFEATED_FLOOR_TILE },
{ ACTOR_EN_FZ, COUNT_ENEMIES_DEFEATED_FREEZARD },
{ ACTOR_EN_GELDB, COUNT_ENEMIES_DEFEATED_GERUDO_THIEF },
{ ACTOR_EN_GOMA, COUNT_ENEMIES_DEFEATED_GOHMA_LARVA },
{ ACTOR_EN_CROW, COUNT_ENEMIES_DEFEATED_GUAY },
{ ACTOR_EN_RR, COUNT_ENEMIES_DEFEATED_LIKE_LIKE },
{ ACTOR_EN_DEKUNUTS, COUNT_ENEMIES_DEFEATED_MAD_SCRUB },
{ ACTOR_EN_OKUTA, COUNT_ENEMIES_DEFEATED_OCTOROK },
{ ACTOR_EN_BA, COUNT_ENEMIES_DEFEATED_PARASITIC_TENTACLE },
{ ACTOR_EN_PO_SISTERS, COUNT_ENEMIES_DEFEATED_POE_SISTERS },
{ ACTOR_EN_BUBBLE, COUNT_ENEMIES_DEFEATED_SHABOM },
{ ACTOR_EN_SB, COUNT_ENEMIES_DEFEATED_SHELLBLADE },
{ ACTOR_EN_SKJ, COUNT_ENEMIES_DEFEATED_SKULL_KID },
{ ACTOR_EN_NY, COUNT_ENEMIES_DEFEATED_SPIKE },
{ ACTOR_EN_SKB, COUNT_ENEMIES_DEFEATED_STALCHILD },
{ ACTOR_EN_TEST, COUNT_ENEMIES_DEFEATED_STALFOS },
{ ACTOR_EN_WEIYER, COUNT_ENEMIES_DEFEATED_STINGER },
{ ACTOR_EN_BW, COUNT_ENEMIES_DEFEATED_TORCH_SLUG },
{ ACTOR_EN_WALLMAS, COUNT_ENEMIES_DEFEATED_WALLMASTER },
{ ACTOR_EN_KAREBABA, COUNT_ENEMIES_DEFEATED_WITHERED_DEKU_BABA },
};
void RegisterEnemyDefeatCounts() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnEnemyDefeat>([](void* refActor) {
Actor* actor = static_cast<Actor*>(refActor);
if (uniqueEnemyIdToStatCount.contains(actor->id)) {
gSaveContext.ship.stats.count[uniqueEnemyIdToStatCount[actor->id]]++;
} else {
switch (actor->id) {
case ACTOR_EN_BB:
if (actor->params == ENBB_GREEN || actor->params == ENBB_GREEN_BIG) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_GREEN]++;
} else if (actor->params == ENBB_BLUE) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_BLUE]++;
} else if (actor->params == ENBB_WHITE) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_WHITE]++;
} else if (actor->params == ENBB_RED) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_RED]++;
}
break;
case ACTOR_EN_DEKUBABA:
if (actor->params == DEKUBABA_BIG) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_DEKU_BABA_BIG]++;
} else {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_DEKU_BABA]++;
}
break;
case ACTOR_EN_ZF:
if (actor->params == ENZF_TYPE_DINOLFOS) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_DINOLFOS]++;
} else {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_LIZALFOS]++;
}
break;
case ACTOR_EN_RD:
if (actor->params >= -1) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_REDEAD]++;
} else {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_GIBDO]++;
}
break;
case ACTOR_EN_IK:
if (actor->params == 0) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE_NABOORU]++;
} else {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE]++;
}
break;
case ACTOR_EN_FIREFLY:
if (actor->params == KEESE_NORMAL_FLY || actor->params == KEESE_NORMAL_PERCH) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_KEESE]++;
} else if (actor->params == KEESE_FIRE_FLY || actor->params == KEESE_FIRE_PERCH) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_KEESE_FIRE]++;
} else if (actor->params == KEESE_ICE_FLY) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_KEESE_ICE]++;
}
break;
case ACTOR_EN_REEBA: {
EnReeba* reeba = (EnReeba*)actor;
if (reeba->isBig) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_LEEVER_BIG]++;
} else {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_LEEVER]++;
}
} break;
case ACTOR_EN_MB:
if (actor->params == 0) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_MOBLIN_CLUB]++;
} else {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_MOBLIN]++;
}
break;
case ACTOR_EN_PEEHAT:
if (actor->params == PEAHAT_TYPE_LARVA) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_PEAHAT_LARVA]++;
} else {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_PEAHAT]++;
}
break;
case ACTOR_EN_POH:
if (actor->params == EN_POH_FLAT || actor->params == EN_POH_SHARP) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_POE_COMPOSER]++;
} else {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_POE]++;
}
break;
case ACTOR_EN_PO_FIELD:
if (actor->params == EN_PO_FIELD_BIG) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_POE_BIG]++;
} else {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_POE]++;
}
break;
case ACTOR_EN_ST:
if (actor->params == 1) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_SKULLTULA_BIG]++;
} else {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_SKULLTULA]++;
}
break;
case ACTOR_EN_SW:
if (((actor->params & 0xE000) >> 0xD) != 0) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_SKULLTULA_GOLD]++;
} else {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_SKULLWALLTULA]++;
}
break;
case ACTOR_EN_TP:
// Only count the head, otherwise each body segment will increment
if (actor->params == TAILPASARAN_HEAD) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_TAILPASARAN]++;
}
break;
case ACTOR_EN_TITE:
if (actor->params == TEKTITE_BLUE) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_TEKTITE_BLUE]++;
} else {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_TEKTITE_RED]++;
}
break;
case ACTOR_EN_WF:
if (actor->params == WOLFOS_WHITE) {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_WOLFOS_WHITE]++;
} else {
gSaveContext.ship.stats.count[COUNT_ENEMIES_DEFEATED_WOLFOS]++;
}
break;
}
}
});
}
void RegisterBossDefeatTimestamps() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnBossDefeat>([](void* refActor) {
Actor* actor = static_cast<Actor*>(refActor);
@@ -687,161 +470,6 @@ void RegisterHurtContainerModeHandler() {
[](int32_t fileNum) { UpdateHurtContainerModeState(CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)); });
}
void RegisterRandomizedEnemySizes() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>([](void* refActor) {
// Randomized Enemy Sizes
Player* player = GET_PLAYER(gPlayState);
Actor* actor = static_cast<Actor*>(refActor);
// Exclude wobbly platforms in Jabu because they need to act like platforms.
// Exclude demo effect for Zora sapphire being re-categorized as a "boss".
// Exclude Dead Hand hands and Bongo Bongo main body because they make the fights (near) impossible.
uint8_t excludedEnemy = actor->id == ACTOR_EN_BROB || actor->id == ACTOR_EN_DHA ||
actor->id == ACTOR_DEMO_EFFECT || (actor->id == ACTOR_BOSS_SST && actor->params == -1);
// Dodongo, Volvagia and Dead Hand are always smaller because they're impossible when bigger.
uint8_t smallOnlyEnemy = actor->id == ACTOR_BOSS_DODONGO || actor->id == ACTOR_BOSS_FD ||
actor->id == ACTOR_BOSS_FD2 || actor->id == ACTOR_EN_DH;
// Only apply to enemies and bosses.
if (!CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemySizes"), 0) ||
(actor->category != ACTORCAT_ENEMY && actor->category != ACTORCAT_BOSS) || excludedEnemy) {
return;
}
float randomNumber;
float randomScale;
uint8_t bigActor = rand() % 2;
// Big actor
if (bigActor && !smallOnlyEnemy) {
randomNumber = rand() % 200;
// Between 100% and 300% size.
randomScale = 1.0f + (randomNumber / 100);
} else {
// Small actor
randomNumber = rand() % 90;
// Between 10% and 100% size.
randomScale = 0.1f + (randomNumber / 100);
}
Actor_SetScale(actor, actor->scale.z * randomScale);
if (CVarGetInteger(CVAR_ENHANCEMENT("EnemySizeScalesHealth"), 0) && (actor->category == ACTORCAT_ENEMY)) {
// Scale the health based on a smaller factor than randomScale
float healthScalingFactor = 0.8f; // Adjust this factor as needed
float scaledHealth = actor->colChkInfo.health * (randomScale * healthScalingFactor);
// Ensure the scaled health doesn't go below zero
actor->colChkInfo.health = fmax(scaledHealth, 1.0f);
} else {
return;
}
});
}
void PatchToTMedallions() {
// TODO: Refactor the DemoEffect_UpdateJewelAdult and DemoEffect_UpdateJewelChild from z_demo_effect
// effects to take effect in there
if (CVarGetInteger(CVAR_ENHANCEMENT("ToTMedallionsColors"), 0)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_StartGrayscale", 7, gsSPGrayscale(true));
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_StartGrayscale", 7, gsSPGrayscale(true));
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeBlue", 16,
gsDPSetGrayscaleColor(0, 161, 255, 255));
} else {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeBlue", 16,
gsDPSetGrayscaleColor(255, 255, 255, 255));
}
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeOrange", 45,
gsDPSetGrayscaleColor(255, 135, 0, 255));
} else {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeOrange", 45,
gsDPSetGrayscaleColor(255, 255, 255, 255));
}
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_LIGHT)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeYellow", 69,
gsDPSetGrayscaleColor(255, 255, 0, 255));
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakeYellow", 16,
gsDPSetGrayscaleColor(255, 255, 0, 255));
} else {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeYellow", 69,
gsDPSetGrayscaleColor(255, 255, 255, 255));
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakeYellow", 16,
gsDPSetGrayscaleColor(255, 255, 255, 255));
}
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeGreen", 94,
gsDPSetGrayscaleColor(0, 255, 0, 255));
} else {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeGreen", 94,
gsDPSetGrayscaleColor(255, 255, 255, 255));
}
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeRed", 118,
gsDPSetGrayscaleColor(255, 0, 0, 255));
} else {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeRed", 118,
gsDPSetGrayscaleColor(255, 255, 255, 255));
}
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW)) {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakePurple", 142,
gsDPSetGrayscaleColor(212, 0, 255, 255));
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakePurple", 27,
gsDPSetGrayscaleColor(212, 0, 255, 255));
} else {
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakePurple", 142,
gsDPSetGrayscaleColor(255, 255, 255, 255));
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakePurple", 27,
gsDPSetGrayscaleColor(255, 255, 255, 255));
}
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_EndGrayscaleAndEndDlist", 160,
gsSPBranchListOTRFilePath(gEndGrayscaleAndEndDlistDL));
ResourceMgr_PatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_EndGrayscaleAndEndDlist", 51,
gsSPBranchListOTRFilePath(gEndGrayscaleAndEndDlistDL));
} else {
// Unpatch everything
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_StartGrayscale");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_StartGrayscale");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeBlue");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeOrange");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeYellow");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakeYellow");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakeRed");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_MakePurple");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_MakePurple");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007A70, "ToTMedallions_EndGrayscaleAndEndDlist");
ResourceMgr_UnpatchGfxByName(tokinoma_room_0DL_007FD0, "ToTMedallions_2_EndGrayscaleAndEndDlist");
}
}
void RegisterToTMedallions() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>([](GetItemEntry _unused) {
if (!CVarGetInteger(CVAR_ENHANCEMENT("ToTMedallionsColors"), 0) || !gPlayState ||
gPlayState->sceneNum != SCENE_TEMPLE_OF_TIME) {
return;
}
PatchToTMedallions();
});
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) {
if (!CVarGetInteger(CVAR_ENHANCEMENT("ToTMedallionsColors"), 0) || sceneNum != SCENE_TEMPLE_OF_TIME) {
return;
}
PatchToTMedallions();
});
}
void RegisterFloorSwitchesHook() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>([](void* refActor) {
Actor* actor = static_cast<Actor*>(refActor);
@@ -858,23 +486,6 @@ void RegisterFloorSwitchesHook() {
});
}
void RegisterPauseMenuHooks() {
static bool pauseWarpHooksRegistered = false;
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([&]() {
if (!GameInteractor::IsSaveLoaded() || !CVarGetInteger(CVAR_ENHANCEMENT("PauseWarp"), 0)) {
pauseWarpHooksRegistered = false;
return;
}
if (!pauseWarpHooksRegistered) {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnKaleidoUpdate>(
[]() { PauseWarp_HandleSelection(); });
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(
[]() { PauseWarp_Execute(); });
pauseWarpHooksRegistered = true;
}
});
}
void RegisterCustomSkeletons() {
static int8_t previousTunic = -1;
@@ -910,14 +521,10 @@ void InitMods() {
RegisterMenuPathFix();
RegisterMirrorModeHandler();
RegisterResetNaviTimer();
RegisterEnemyDefeatCounts();
RegisterBossDefeatTimestamps();
RegisterRandomizedEnemySizes();
RegisterToTMedallions();
RegisterFloorSwitchesHook();
RegisterPatchHandHandler();
RegisterHurtContainerModeHandler();
RegisterPauseMenuHooks();
RandoKaleido_RegisterHooks();
RegisterCustomSkeletons();
}
+1 -1
View File
@@ -10,7 +10,7 @@ extern "C" {
void UpdateDirtPathFixState(int32_t sceneNum);
void UpdateMirrorModeState(int32_t sceneNum);
void UpdateHurtContainerModeState(bool newState);
void PatchToTMedallions();
void UpdateToTMedallions();
void UpdatePermanentHeartLossState();
void UpdateHyperEnemiesState();
void UpdateHyperBossesState();
@@ -1018,7 +1018,7 @@ static void FillExcludedLocations() {
FilterFromPool(ctx->allLocations, [ctx](const auto loc) { return ctx->GetItemLocation(loc)->IsExcluded(); });
for (RandomizerCheck loc : excludedLocations) {
PlaceJunkInExcludedLocation(loc);
ctx->PlaceItemInLocation(loc, GetJunkItem());
}
}
@@ -350,19 +350,6 @@ static void ReplaceMaxItem(const RandomizerGet itemToReplace, int max) {
}
}
void PlaceJunkInExcludedLocation(const RandomizerCheck il) {
// place a non-advancement item in this location
auto ctx = Rando::Context::GetInstance();
for (size_t i = 0; i < ItemPool.size(); i++) {
if (Rando::StaticData::RetrieveItem(ItemPool[i]).GetCategory() == ITEM_CATEGORY_JUNK) {
ctx->PlaceItemInLocation(il, ItemPool[i]);
ItemPool.erase(ItemPool.begin() + i);
return;
}
}
SPDLOG_ERROR("ERROR: No Junk to Place!!!");
}
static void PlaceVanillaMapsAndCompasses() {
auto ctx = Rando::Context::GetInstance();
for (auto dungeon : ctx->GetDungeons()->GetDungeonList()) {
@@ -9,7 +9,6 @@ class ItemLocation;
void AddItemToPool(std::vector<RandomizerGet>& pool, const RandomizerGet item, size_t count = 1);
RandomizerGet GetJunkItem();
void PlaceJunkInExcludedLocation(const RandomizerCheck il);
void GenerateItemPool();
extern std::vector<RandomizerGet> ItemPool;
@@ -186,7 +186,6 @@ void Context::GenerateLocationPool() {
location.GetRandomizerCheck() == RC_LW_DEKU_SCRUB_NEAR_BRIDGE ||
location.GetRandomizerCheck() == RC_HF_DEKU_SCRUB_GROTTO)) ||
(location.GetRCType() == RCTYPE_ADULT_TRADE && mOptions[RSK_SHUFFLE_ADULT_TRADE].Is(RO_GENERIC_OFF)) ||
(location.GetRCType() == RCTYPE_SONG_LOCATION && mOptions[RSK_SHUFFLE_SONGS].Is(RO_SONG_SHUFFLE_OFF)) ||
(location.GetRCType() == RCTYPE_COW && mOptions[RSK_SHUFFLE_COWS].Is(RO_GENERIC_OFF)) ||
(location.GetRandomizerCheck() == RC_LH_HYRULE_LOACH &&
mOptions[RSK_FISHSANITY].IsNot(RO_FISHSANITY_HYRULE_LOACH)) ||
+7 -5
View File
@@ -670,8 +670,7 @@ std::vector<Entrance*> EntranceShuffler::AssumeEntrancePool(std::vector<Entrance
(ctx->GetOption(RSK_SHUFFLE_OVERWORLD_ENTRANCES) ||
ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_ALL)))) {
auto type = entrance->GetType();
if (((type == EntranceType::Dungeon || type == EntranceType::ThievesHideout ||
type == EntranceType::GrottoGrave) &&
if (((type == EntranceType::Dungeon || type == EntranceType::GrottoGrave) &&
entrance->GetReverse()->GetName() !=
"Spirit Temple Entryway -> Desert Colossus From Spirit Entryway") ||
(type == EntranceType::Interior &&
@@ -796,10 +795,11 @@ static bool ValidateWorld(Entrance* entrancePlaced) {
bool checkOtherEntranceAccess =
(ctx->GetOption(RSK_SHUFFLE_OVERWORLD_ENTRANCES) ||
ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_ALL) ||
ctx->GetOption(RSK_SHUFFLE_OVERWORLD_SPAWNS)) &&
ctx->GetOption(RSK_SHUFFLE_THIEVES_HIDEOUT_ENTRANCES) || ctx->GetOption(RSK_SHUFFLE_OVERWORLD_SPAWNS)) &&
(entrancePlaced == nullptr || ctx->GetOption(RSK_MIXED_ENTRANCE_POOLS) ||
type == EntranceType::SpecialInterior || type == EntranceType::Overworld || type == EntranceType::Spawn ||
type == EntranceType::WarpSong || type == EntranceType::OwlDrop);
type == EntranceType::SpecialInterior || type == EntranceType::Overworld ||
type == EntranceType::ThievesHideout || type == EntranceType::Spawn || type == EntranceType::WarpSong ||
type == EntranceType::OwlDrop);
// Search the world to verify that all necessary conditions are still being held
// Conditions will be checked during the search and any that fail will be figured out
@@ -1333,6 +1333,7 @@ int EntranceShuffler::ShuffleAllEntrances() {
int totalMixedPools =
(ctx->GetOption(RSK_MIX_DUNGEON_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_BOSS_ENTRANCES) ? 1 : 0) +
(ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES) ? 1 : 0) +
(ctx->GetOption(RSK_MIX_THIEVES_HIDEOUT_ENTRANCES) ? 1 : 0) +
(ctx->GetOption(RSK_MIX_GROTTO_ENTRANCES) ? 1 : 0);
if (totalMixedPools < 2) {
ctx->GetOption(RSK_MIXED_ENTRANCE_POOLS).Set(RO_GENERIC_OFF);
@@ -1340,6 +1341,7 @@ int EntranceShuffler::ShuffleAllEntrances() {
ctx->GetOption(RSK_MIX_BOSS_ENTRANCES).Set(RO_GENERIC_OFF);
ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES).Set(RO_GENERIC_OFF);
ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES).Set(RO_GENERIC_OFF);
ctx->GetOption(RSK_MIX_THIEVES_HIDEOUT_ENTRANCES).Set(RO_GENERIC_OFF);
ctx->GetOption(RSK_MIX_GROTTO_ENTRANCES).Set(RO_GENERIC_OFF);
}
if (ctx->GetOption(RSK_MIXED_ENTRANCE_POOLS)) {
@@ -2187,7 +2187,8 @@ void RandomizerOnActorInitHandler(void* actorRef) {
}
// Turn MQ switch into toggle
if (actor->id == ACTOR_OBJ_SWITCH && gPlayState->sceneNum == SCENE_BOTTOM_OF_THE_WELL && (actor->params & 7) == 3) {
if (actor->id == ACTOR_OBJ_SWITCH && gPlayState->sceneNum == SCENE_BOTTOM_OF_THE_WELL &&
(actor->params & 0x3f07) == 0x303) {
auto dungeon =
OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(SCENE_BOTTOM_OF_THE_WELL);
if (dungeon->IsMQ()) {
@@ -372,6 +372,7 @@ bool GetTimePassFromScene(SceneID scene) {
case SCENE_POTION_SHOP_GRANNY:
case SCENE_GANON_BOSS:
case SCENE_HOUSE_OF_SKULLTULA:
case SCENE_KAKARIKO_VILLAGE:
case SCENE_KOKIRI_FOREST:
case SCENE_SACRED_FOREST_MEADOW:
case SCENE_LOST_WOODS:
@@ -383,6 +384,7 @@ bool GetTimePassFromScene(SceneID scene) {
case SCENE_GERUDOS_FORTRESS:
case SCENE_HAUNTED_WASTELAND:
case SCENE_DEATH_MOUNTAIN_CRATER:
case SCENE_LON_LON_RANCH:
case SCENE_ID_MAX:
return false;
@@ -393,14 +395,12 @@ bool GetTimePassFromScene(SceneID scene) {
return false;
case SCENE_HYRULE_FIELD:
case SCENE_KAKARIKO_VILLAGE:
case SCENE_ZORAS_RIVER:
case SCENE_LAKE_HYLIA:
case SCENE_GERUDO_VALLEY:
case SCENE_DESERT_COLOSSUS:
case SCENE_HYRULE_CASTLE:
case SCENE_DEATH_MOUNTAIN_TRAIL:
case SCENE_LON_LON_RANCH:
return true;
case SCENE_TEST01:
@@ -133,7 +133,7 @@ void RegionTable_Init_DekuTree() {
LOCATION(RC_DEKU_TREE_BASEMENT_TORCHES_GRASS_2, logic->CanCutShrubs()),
}, {
//Exits
Entrance(RR_DEKU_TREE_BASEMENT_WATER_ROOM_BACK, []{return true;}),
Entrance(RR_DEKU_TREE_BASEMENT_WATER_ROOM_BACK, []{return Here(RR_DEKU_TREE_BASEMENT_TORCH_ROOM, []{return logic->HasFireSourceWithTorch() || logic->CanUse(RG_FAIRY_BOW);});}),
Entrance(RR_DEKU_TREE_BASEMENT_BACK_LOBBY, []{return Here(RR_DEKU_TREE_BASEMENT_TORCH_ROOM, []{return logic->HasFireSourceWithTorch() || logic->CanUse(RG_FAIRY_BOW);});}),
});
@@ -463,7 +463,7 @@ void RegionTable_Init_ForestTemple() {
}, {
//Locations
LOCATION(RC_FOREST_TEMPLE_MQ_WELL_CHEST, logic->CanHitEyeTargets() || (logic->CanOpenUnderwaterChest() && logic->WaterTimer() >= 8)),
LOCATION(RC_FOREST_TEMPLE_MQ_GS_RAISED_ISLAND_COURTYARD, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)),
LOCATION(RC_FOREST_TEMPLE_MQ_GS_RAISED_ISLAND_COURTYARD, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)),
//implies logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)
LOCATION(RC_FOREST_TEMPLE_MQ_GS_WELL, logic->CanHitEyeTargets() || (logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT))),
LOCATION(RC_FOREST_TEMPLE_MQ_WELL_WEST_HEART, (logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 8) || logic->CanHitEyeTargets()),
@@ -24,7 +24,7 @@ void RegionTable_Init_HauntedWasteland() {
//Locations
LOCATION(RC_WASTELAND_CHEST, logic->HasFireSource()),
LOCATION(RC_WASTELAND_BOMBCHU_SALESMAN, logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS)),
LOCATION(RC_WASTELAND_GS, logic->HookshotOrBoomerang() || (logic->IsAdult && logic->CanGroundJump() && logic->CanJumpslash())),
LOCATION(RC_WASTELAND_GS, logic->HookshotOrBoomerang() || (logic->IsAdult && ctx->GetTrickOption(RT_GROUND_JUMP_HARD) && logic->CanGroundJump() && logic->CanJumpslash())), // need to jumpslash immediately with two handed weapons
LOCATION(RC_WASTELAND_NEAR_GS_POT_1, logic->CanBreakPots()),
LOCATION(RC_WASTELAND_NEAR_GS_POT_2, logic->CanBreakPots()),
LOCATION(RC_WASTELAND_NEAR_GS_POT_3, logic->CanBreakPots()),
@@ -4,7 +4,7 @@
using namespace Rando;
// clang-format off
// When Thieve's hideout entrances are shuffled, getting caught by guards should behave like void outs to avoid logic headaches.
// When Thieves' Hideout entrances are shuffled, getting caught by guards should behave like void outs to avoid logic headaches.
void RegionTable_Init_ThievesHideout() {
areaTable[RR_TH_1_TORCH_CELL] = Region("Thieves Hideout 1 Torch Cell", SCENE_THIEVES_HIDEOUT, {
//Events
@@ -32,7 +32,7 @@ void RegionTable_Init_ZoraRiver() {
EventAccess(LOGIC_GOSSIP_STONE_FAIRY, []{return logic->CallGossipFairy();}),
EventAccess(LOGIC_BEAN_PLANT_FAIRY, []{return logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS);}),
EventAccess(LOGIC_BUTTERFLY_FAIRY, []{return logic->CanUse(RG_STICKS);}),
EventAccess(LOGIC_BUG_SHRUB, []{return logic->CanCutShrubs();}),
EventAccess(LOGIC_BUG_SHRUB, []{return logic->CanCutShrubs() && (logic->IsChild || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_ZR_LOWER));}),
}, {
//Locations
LOCATION(RC_ZR_MAGIC_BEAN_SALESMAN, logic->IsChild),
@@ -61,7 +61,7 @@ void RegionTable_Init_ZoraRiver() {
LOCATION(RC_ZR_BENEATH_WATERFALL_RIGHT_RUPEE, logic->IsAdult && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG))),
LOCATION(RC_ZR_NEAR_GROTTOS_GOSSIP_STONE, true),
LOCATION(RC_ZR_NEAR_DOMAIN_GOSSIP_STONE, true),
LOCATION(RC_ZR_NEAR_FREESTANDING_POH_GRASS, logic->CanCutShrubs()),
LOCATION(RC_ZR_NEAR_FREESTANDING_POH_GRASS, (logic->CanCutShrubs() && (logic->IsChild || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_ZR_LOWER))) || logic->CanUse(RG_BOOMERANG)),
}, {
//Exits
Entrance(RR_ZR_FRONT, []{return true;}),
+19 -2
View File
@@ -223,7 +223,8 @@ bool Logic::HasItem(RandomizerGet itemName) {
case RG_GOLDEN_SCALE:
return CurrentUpgrade(UPG_SCALE) >= 2;
case RG_POCKET_EGG:
return CheckRandoInf(RAND_INF_ADULT_TRADES_HAS_POCKET_EGG);
return CheckRandoInf(RAND_INF_ADULT_TRADES_HAS_POCKET_EGG) ||
CheckRandoInf(RAND_INF_ADULT_TRADES_HAS_POCKET_CUCCO);
case RG_COJIRO:
case RG_ODD_MUSHROOM:
case RG_ODD_POTION:
@@ -2118,6 +2119,21 @@ void Logic::SetQuestItem(uint32_t item, bool state) {
}
}
const std::vector<uint8_t>& GetThievesHideoutSmallKeyDoors() {
// Retrieved from scenes/shared/gerudoway_scene/gerudoway_room_%d
// SOH::SceneCommandID::SetActorList, actor.id == ACTOR_DOOR_GERUDO, actor.params & 0x3F
static const std::vector<uint8_t> normalSmallKeyDoors{ 1, 2, 3, 4 };
static const std::vector<uint8_t> fastSmallKeyDoors{ 1 };
static const std::vector<uint8_t> freeSmallKeyDoors{};
if (RAND_GET_OPTION(RSK_GERUDO_FORTRESS) == RO_GF_CARPENTERS_NORMAL) {
return normalSmallKeyDoors;
} else if (RAND_GET_OPTION(RSK_GERUDO_FORTRESS) == RO_GF_CARPENTERS_FAST) {
return fastSmallKeyDoors;
}
return freeSmallKeyDoors;
}
// Get the swch bit positions for the dungeon
const std::vector<uint8_t>& GetDungeonSmallKeyDoors(SceneID sceneId) {
static const std::vector<uint8_t> emptyVector;
@@ -2183,7 +2199,8 @@ const std::vector<uint8_t>& GetDungeonSmallKeyDoors(SceneID sceneId) {
}
int8_t Logic::GetUsedSmallKeyCount(SceneID sceneId) {
const auto& smallKeyDoors = GetDungeonSmallKeyDoors(sceneId);
const auto& smallKeyDoors =
(sceneId == SCENE_THIEVES_HIDEOUT) ? GetThievesHideoutSmallKeyDoors() : GetDungeonSmallKeyDoors(sceneId);
// Get the swch value for the scene
uint32_t swch;
@@ -3,7 +3,6 @@
#include "soh/cvar_prefixes.h"
#include "soh/SohGui/SohGui.hpp"
#include <map>
#include <string>
#include <vector>
#include <libultraship/libultraship.h>
@@ -68,7 +67,7 @@ static std::string spoilerEntranceGroupNames[] = {
};
static std::string groupTypeNames[] = {
"One Way", "Overworld", "Interior", "Grotto", "Dungeon",
"One Way", "Overworld", "Interior", "Fortress", "Grotto", "Dungeon",
};
// Entrance data for the tracker taken from the 3ds rando entrance tracker, and supplemented with scene/spawn info and
@@ -328,32 +327,32 @@ const EntranceData entranceData[] = {
{ ENTR_GERUDO_TRAINING_GROUND_ENTRANCE, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Outside Training Ground", "Gerudo Training Ground Entrance", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON, "gtg", 1},
{ ENTRANCE_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET), ENTRANCE_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET), {{ SCENE_FAIRYS_FOUNTAIN, 0x00 }}, "GF Fairy Grotto", "GF Storms Grotto Entry", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_GROTTO, ""},
{ ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE, SINGLE_SCENE_INFO(SCENE_GERUDO_TRAINING_GROUND), "Gerudo Training Ground Entrance", "GF Outside Training Ground", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON, "gtg"},
{ ENTR_GERUDOS_FORTRESS_1, ENTR_THIEVES_HIDEOUT_0, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Outskirts", "TH 1 Torch Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_GERUDOS_FORTRESS_2, ENTR_THIEVES_HIDEOUT_1, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Near Grotto", "TH 1 Torch Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_GERUDOS_FORTRESS_3, ENTR_THIEVES_HIDEOUT_2, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Near Grotto", "TH Kitchen Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_GERUDOS_FORTRESS_4, ENTR_THIEVES_HIDEOUT_3, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Above GTG", "TH Kitchen Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_GERUDOS_FORTRESS_5, ENTR_THIEVES_HIDEOUT_4, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Near Grotto", "TH Steep Slope Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_GERUDOS_FORTRESS_6, ENTR_THIEVES_HIDEOUT_5, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Bottom of Lower Vines", "TH Steep Slope Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_GERUDOS_FORTRESS_7, ENTR_THIEVES_HIDEOUT_6, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Above GTG", "TH Double Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_GERUDOS_FORTRESS_8, ENTR_THIEVES_HIDEOUT_7, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Top of Lower Vines", "TH Double Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_GERUDOS_FORTRESS_9, ENTR_THIEVES_HIDEOUT_8, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Top of Lower Vines", "TH Kitchen By Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_GERUDOS_FORTRESS_10, ENTR_THIEVES_HIDEOUT_9, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Near GS", "TH Kitchen Opposite Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_GERUDOS_FORTRESS_11, ENTR_THIEVES_HIDEOUT_10, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Below Chest", "TH Break Room", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_GERUDOS_FORTRESS_12, ENTR_THIEVES_HIDEOUT_11, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Above Jail", "TH Break Room Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_GERUDOS_FORTRESS_13, ENTR_THIEVES_HIDEOUT_12, SINGLE_SCENE_INFO(SCENE_THIEVES_HIDEOUT), "GF Below GS", "TH Dead End Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_THIEVES_HIDEOUT_0, ENTR_GERUDOS_FORTRESS_1, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH 1 Torch Cell", "GF Outskirts", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_THIEVES_HIDEOUT_1, ENTR_GERUDOS_FORTRESS_2, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH 1 Torch Cell", "GF Near Grotto", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_THIEVES_HIDEOUT_2, ENTR_GERUDOS_FORTRESS_3, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Kitchen Corridor", "GF Near Grotto", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_THIEVES_HIDEOUT_3, ENTR_GERUDOS_FORTRESS_4, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Kitchen Corridor", "GF Above GTG", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_THIEVES_HIDEOUT_4, ENTR_GERUDOS_FORTRESS_5, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Steep Slope Cell", "GF Near Grotto", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_THIEVES_HIDEOUT_5, ENTR_GERUDOS_FORTRESS_6, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Steep Slope Cell", "GF Bottom of Lower Vines", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_THIEVES_HIDEOUT_6, ENTR_GERUDOS_FORTRESS_7, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Double Cell", "GF Above GTG", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_THIEVES_HIDEOUT_7, ENTR_GERUDOS_FORTRESS_8, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Double Cell", "GF Top of Lower Vines", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_THIEVES_HIDEOUT_8, ENTR_GERUDOS_FORTRESS_9, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Kitchen By Corridor", "GF Top of Lower Vines", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_THIEVES_HIDEOUT_9, ENTR_GERUDOS_FORTRESS_10, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Kitchen Opposite Corridor", "GF Near GS", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_THIEVES_HIDEOUT_10, ENTR_GERUDOS_FORTRESS_11, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Break Room", "GF Below Chest", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_THIEVES_HIDEOUT_11, ENTR_GERUDOS_FORTRESS_12, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Break Room Corridor", "GF Above Jail", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_THIEVES_HIDEOUT_12, ENTR_GERUDOS_FORTRESS_13, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "TH Dead End Cell", "GF Below GS", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_DUNGEON},
{ ENTR_GERUDOS_FORTRESS_1, ENTR_THIEVES_HIDEOUT_0, {{ SCENE_THIEVES_HIDEOUT, 2 }}, "TH 1 Torch Cell Turn", "GF Outskirts", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_GERUDOS_FORTRESS_2, ENTR_THIEVES_HIDEOUT_1, {{ SCENE_THIEVES_HIDEOUT, 2 }}, "TH 1 Torch Cell", "GF Near Grotto East", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_GERUDOS_FORTRESS_3, ENTR_THIEVES_HIDEOUT_2, {{ SCENE_THIEVES_HIDEOUT, 3 }}, "TH Kitchen Corridor Lower", "GF Near Grotto North", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_GERUDOS_FORTRESS_4, ENTR_THIEVES_HIDEOUT_3, {{ SCENE_THIEVES_HIDEOUT, 3 }}, "TH Kitchen Corridor Upper", "GF Above GTG", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_GERUDOS_FORTRESS_5, ENTR_THIEVES_HIDEOUT_4, {{ SCENE_THIEVES_HIDEOUT, 4 }}, "TH Steep Slope Cell", "GF Near Grotto", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_GERUDOS_FORTRESS_6, ENTR_THIEVES_HIDEOUT_5, {{ SCENE_THIEVES_HIDEOUT, 4 }}, "TH Steep Slope Cell Two Ramps", "GF Bottom of Lower Vines", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_GERUDOS_FORTRESS_7, ENTR_THIEVES_HIDEOUT_6, {{ SCENE_THIEVES_HIDEOUT, 5 }}, "TH Double Cell Lower", "GF Above GTG Directly", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_GERUDOS_FORTRESS_8, ENTR_THIEVES_HIDEOUT_7, {{ SCENE_THIEVES_HIDEOUT, 5 }}, "TH Double Cell Upper", "GF Top of Lower Vines Across", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_GERUDOS_FORTRESS_9, ENTR_THIEVES_HIDEOUT_8, {{ SCENE_THIEVES_HIDEOUT, 3 }}, "TH Kitchen By Corridor", "GF Top of Lower Vines Near", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_GERUDOS_FORTRESS_10, ENTR_THIEVES_HIDEOUT_9, {{ SCENE_THIEVES_HIDEOUT, 3 }}, "TH Kitchen Opposite Corridor", "GF Near GS", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_GERUDOS_FORTRESS_11, ENTR_THIEVES_HIDEOUT_10, {{ SCENE_THIEVES_HIDEOUT, 0 }}, "TH Break Room", "GF Below Chest", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_GERUDOS_FORTRESS_12, ENTR_THIEVES_HIDEOUT_11, {{ SCENE_THIEVES_HIDEOUT, 0 }}, "TH Break Room Corridor", "GF Above Jail", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_GERUDOS_FORTRESS_13, ENTR_THIEVES_HIDEOUT_12, {{ SCENE_THIEVES_HIDEOUT, 1 }}, "TH Dead End Cell", "GF Below GS", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_THIEVES_HIDEOUT_0, ENTR_GERUDOS_FORTRESS_1, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Outskirts", "TH 1 Torch Cell Turn", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_THIEVES_HIDEOUT_1, ENTR_GERUDOS_FORTRESS_2, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Near Grotto East", "TH 1 Torch Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_THIEVES_HIDEOUT_2, ENTR_GERUDOS_FORTRESS_3, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Near Grotto North", "TH Kitchen Corridor Lower", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_THIEVES_HIDEOUT_3, ENTR_GERUDOS_FORTRESS_4, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Above GTG", "TH Kitchen Corridor Upper", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_THIEVES_HIDEOUT_4, ENTR_GERUDOS_FORTRESS_5, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Near Grotto", "TH Steep Slope Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_THIEVES_HIDEOUT_5, ENTR_GERUDOS_FORTRESS_6, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Bottom of Lower Vines", "TH Steep Slope Cell Two Ramps", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_THIEVES_HIDEOUT_6, ENTR_GERUDOS_FORTRESS_7, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Above GTG Directly", "TH Double Cell Lower", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_THIEVES_HIDEOUT_7, ENTR_GERUDOS_FORTRESS_8, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Top of Lower Vines Across", "TH Double Cell Upper", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_THIEVES_HIDEOUT_8, ENTR_GERUDOS_FORTRESS_9, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Top of Lower Vines Near", "TH Kitchen By Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_THIEVES_HIDEOUT_9, ENTR_GERUDOS_FORTRESS_10, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Near GS", "TH Kitchen Opposite Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_THIEVES_HIDEOUT_10, ENTR_GERUDOS_FORTRESS_11, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Below Chest", "TH Break Room", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_THIEVES_HIDEOUT_11, ENTR_GERUDOS_FORTRESS_12, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Above Jail", "TH Break Room Corridor", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
{ ENTR_THIEVES_HIDEOUT_12, ENTR_GERUDOS_FORTRESS_13, SINGLE_SCENE_INFO(SCENE_GERUDOS_FORTRESS), "GF Below GS", "TH Dead End Cell", ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_FORTRESS},
// The Wasteland
{ ENTR_GERUDOS_FORTRESS_GATE_EXIT, ENTR_HAUNTED_WASTELAND_EAST_EXIT, SINGLE_SCENE_INFO(SCENE_HAUNTED_WASTELAND), "Haunted Wasteland East Exit", "Gerudo Fortress Gate Exit", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_GERUDO_FORTRESS, ENTRANCE_TYPE_OVERWORLD, "hw,gf"},
@@ -433,11 +432,13 @@ int16_t LinkIsInArea(const EntranceData* entrance) {
// Otherwise check all scenes/spawns
// Not all areas require a spawn position to differeniate between another area
for (auto info : entrance->scenes) {
// When a spawn position is specified, check that combination
if (info.spawn != -1) {
result = Entrance_SceneAndSpawnAre(info.scene, info.spawn);
} else { // Otherwise just check the current scene
// only check current scene when spawn info missing
if (info.spawn == -1) {
result = gPlayState->sceneNum == info.scene;
} else if (gPlayState->sceneNum == SCENE_THIEVES_HIDEOUT) { // group by rooms, not spawn
result = info.scene == SCENE_THIEVES_HIDEOUT && gPlayState->roomCtx.curRoom.num == info.spawn;
} else { // Otherwise just check scene & spawn
result = Entrance_SceneAndSpawnAre(info.scene, info.spawn);
}
// Return the scene for tracking
@@ -514,7 +515,7 @@ void SortEntranceListByType(EntranceOverride* entranceList, u8 byDest) {
break;
}
size_t entranceIndex = byDest ? tempList[j].override : tempList[j].index;
int16_t entranceIndex = byDest ? tempList[j].override : tempList[j].index;
if (entranceData[i].type == k && entranceIndex == entranceData[i].index) {
entranceList[idx] = tempList[j];
@@ -838,7 +839,7 @@ void EntranceTrackerWindow::DrawElement() {
// Combine destToggle and groupToggle to get a range of 0-3
uint8_t groupType = destToggle + (groupToggle * 2);
size_t groupCount = groupToggle ? ENTRANCE_TYPE_COUNT : SPOILER_ENTRANCE_GROUP_COUNT;
size_t groupCount = groupToggle ? (size_t)ENTRANCE_TYPE_COUNT : (size_t)SPOILER_ENTRANCE_GROUP_COUNT;
auto groupNames = groupToggle ? groupTypeNames : spoilerEntranceGroupNames;
EntranceOverride* entranceList;
@@ -874,7 +875,7 @@ void EntranceTrackerWindow::DrawElement() {
uint16_t startIndex = gEntranceTrackingData.GroupOffsets[groupType][i];
bool doAreaScroll = false;
size_t undiscovered = 0;
int undiscovered = 0;
std::vector<EntranceOverride> displayEntrances = {};
// Loop over entrances first for filtering
@@ -934,10 +935,8 @@ void EntranceTrackerWindow::DrawElement() {
}
displayEntrances.push_back(entrance);
} else {
if (!isDiscovered) {
undiscovered++;
}
} else if (!isDiscovered) {
undiscovered++;
}
}
@@ -990,8 +989,7 @@ void EntranceTrackerWindow::DrawElement() {
ImGui::PushStyleColor(ImGuiCol_Text, color);
// Use a non-breaking space to keep the arrow from wrapping to a newline by itself
auto nbsp = u8"\u00A0";
ImGui::TextWrapped("%s%s-> %s", origSrcName, nbsp, rplcDstName);
ImGui::TextWrapped("%s\u00A0-> %s", origSrcName, rplcDstName);
ImGui::PopStyleColor();
}
@@ -37,6 +37,7 @@ typedef enum {
ENTRANCE_TYPE_ONE_WAY,
ENTRANCE_TYPE_OVERWORLD,
ENTRANCE_TYPE_INTERIOR,
ENTRANCE_TYPE_FORTRESS,
ENTRANCE_TYPE_GROTTO,
ENTRANCE_TYPE_DUNGEON,
ENTRANCE_TYPE_COUNT,
@@ -755,6 +755,14 @@ void DrawQuest(ItemTrackerItem item) {
Tooltip(SohUtils::GetQuestItemName(item.id).c_str());
};
bool HasBossSoul(RandomizerInf bossSoul) {
uint8_t soulSetting = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BOSS_SOULS);
bool isSoulRandomized = IS_RANDO && (soulSetting == RO_BOSS_SOULS_ON_PLUS_GANON ||
(soulSetting == RO_BOSS_SOULS_ON && bossSoul != RAND_INF_GANON_SOUL));
return isSoulRandomized ? Flags_GetRandomizerInf(bossSoul) : true;
}
void DrawItem(ItemTrackerItem item) {
uint32_t actualItemId = GameInteractor::IsSaveLoaded() ? INV_CONTENT(item.id) : ITEM_NONE;
@@ -811,50 +819,47 @@ void DrawItem(ItemTrackerItem item) {
break;
case RG_GOHMA_SOUL:
actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_GOHMA_SOUL);
hasItem = HasBossSoul(RAND_INF_GOHMA_SOUL);
itemName = "Gohma's Soul";
break;
case RG_KING_DODONGO_SOUL:
actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_KING_DODONGO_SOUL);
hasItem = HasBossSoul(RAND_INF_KING_DODONGO_SOUL);
itemName = "King Dodongo's Soul";
break;
case RG_BARINADE_SOUL:
actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_BARINADE_SOUL);
hasItem = HasBossSoul(RAND_INF_BARINADE_SOUL);
itemName = "Barinade's Soul";
break;
case RG_PHANTOM_GANON_SOUL:
actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_PHANTOM_GANON_SOUL);
hasItem = HasBossSoul(RAND_INF_PHANTOM_GANON_SOUL);
itemName = "Phantom Ganon's Soul";
break;
case RG_VOLVAGIA_SOUL:
actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_VOLVAGIA_SOUL);
hasItem = HasBossSoul(RAND_INF_VOLVAGIA_SOUL);
itemName = "Volvagia's Soul";
break;
case RG_MORPHA_SOUL:
actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_MORPHA_SOUL);
hasItem = HasBossSoul(RAND_INF_MORPHA_SOUL);
itemName = "Morpha's Soul";
break;
case RG_BONGO_BONGO_SOUL:
actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_BONGO_BONGO_SOUL);
hasItem = HasBossSoul(RAND_INF_BONGO_BONGO_SOUL);
itemName = "Bongo Bongo's Soul";
break;
case RG_TWINROVA_SOUL:
actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_TWINROVA_SOUL);
hasItem = HasBossSoul(RAND_INF_TWINROVA_SOUL);
itemName = "Twinrova's Soul";
break;
case RG_GANON_SOUL:
actualItemId = item.id;
hasItem = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BOSS_SOULS) ==
RO_BOSS_SOULS_ON_PLUS_GANON
? Flags_GetRandomizerInf(RAND_INF_GANON_SOUL)
: true;
hasItem = HasBossSoul(RAND_INF_GANON_SOUL);
itemName = "Ganon's Soul";
break;
@@ -2113,7 +2118,7 @@ void RegisterItemTrackerWidgets() {
SohGui::mSohMenu->AddSearchWidget({ personalNotesWiget, "Randomizer", "Item Tracker", "General Settings" });
hookshotIdentWidget = { .name = "Show Hookshot Identifiers", .type = WidgetType::WIDGET_CVAR_CHECKBOX };
hookshotIdentWidget.CVar(CVAR_SETTING("FreeLook.Enabled"))
hookshotIdentWidget.CVar(CVAR_TRACKER_ITEM("HookshotIdentifier"))
.Options(CheckboxOptions()
.Color(THEME_COLOR)
.Tooltip("Shows an 'H' or an 'L' to more easily distinguish between Hookshot and Longshot."));
+2 -2
View File
@@ -282,15 +282,15 @@ extern "C" void Randomizer_InitSaveFile() {
}
int startingAge = OTRGlobals::Instance->gRandoContext->GetOption(RSK_SELECTED_STARTING_AGE).Get();
gSaveContext.savedSceneNum = -1;
switch (startingAge) {
case RO_AGE_ADULT: // Adult
gSaveContext.linkAge = LINK_AGE_ADULT;
gSaveContext.entranceIndex = ENTR_TEMPLE_OF_TIME_WARP_PAD;
gSaveContext.savedSceneNum = SCENE_LON_LON_RANCH; // Set scene num manually to ToT.
gSaveContext.cutsceneIndex = 0;
break;
case RO_AGE_CHILD: // Child
gSaveContext.linkAge = LINK_AGE_CHILD;
gSaveContext.savedSceneNum = -1;
break;
default:
break;
@@ -225,7 +225,7 @@ StaticData::PopulateTranslationMap(std::unordered_map<uint32_t, CustomMessage> i
if (output.contains(string)) {
if (output[string] != key) {
// RANDOTODO should this cause an error of some kind?
SPDLOG_DEBUG("\tREPEATED STRING IN " + message.GetEnglish(MF_CLEAN) + "\n\n");
SPDLOG_DEBUG("REPEATED STRING IN " + message.GetEnglish(MF_CLEAN));
}
} else {
output[string] = key;
@@ -244,7 +244,7 @@ StaticData::PopulateTranslationMap(std::unordered_map<uint32_t, RandomizerHintTe
if (output.contains(string)) {
if (output[string] != key) {
// RANDOTODO should this cause an error of some kind?
SPDLOG_DEBUG("\tREPEATED STRING WITH " + string + "\n\n");
SPDLOG_DEBUG("REPEATED STRING WITH " + string);
}
} else {
output[string] = key;
@@ -709,11 +709,6 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
*should = false;
}
break;
case VB_DAMPE_IN_GRAVEYARD_DESPAWN:
if (CVarGetInteger(CVAR_ENHANCEMENT("DampeAllNight"), 0)) {
*should = LINK_IS_ADULT || gPlayState->sceneNum != SCENE_GRAVEYARD;
}
break;
case VB_BE_VALID_GRAVEDIGGING_SPOT:
if (CVarGetInteger(CVAR_ENHANCEMENT("DampeWin"), IS_RANDO)) {
EnTk* enTk = va_arg(args, EnTk*);
+16 -8
View File
@@ -12,6 +12,7 @@
#include <fast/resource/type/DisplayList.h>
#include <ship/window/Window.h>
#include <soh/GameVersions.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include "Enhancements/gameconsole.h"
#ifdef _WIN32
@@ -291,10 +292,19 @@ void OTRGlobals::Initialize() {
OOT_NTSC_JP_GC, OOT_NTSC_US_GC, OOT_PAL_GC, OOT_PAL_GC_DBG1, OOT_PAL_GC_DBG2,
};
context->InitLogging();
context->InitGfxDebugger();
#if (_DEBUG)
auto defaultLogLevel = spdlog::level::trace;
#else
auto defaultLogLevel = spdlog::level::info;
#endif
context->InitConfiguration();
context->InitConsoleVariables();
auto logLevel =
static_cast<spdlog::level::level_enum>(CVarGetInteger(CVAR_DEVELOPER_TOOLS("LogLevel"), defaultLogLevel));
context->InitLogging(logLevel, logLevel);
Ship::Context::GetInstance()->GetLogger()->set_pattern("[%H:%M:%S.%e] [%s:%#] [%l] %v");
context->InitGfxDebugger();
context->InitFileDropMgr();
// tell LUS to reserve 3 SoH specific threads (Game, Audio, Save)
@@ -319,10 +329,6 @@ void OTRGlobals::Initialize() {
context->InitCrashHandler();
context->InitConsole();
Ship::Context::GetInstance()->GetLogger()->set_level(
(spdlog::level::level_enum)CVarGetInteger(CVAR_DEVELOPER_TOOLS("LogLevel"), 1));
Ship::Context::GetInstance()->GetLogger()->set_pattern("[%H:%M:%S.%e] [%s:%#] [%l] %v");
auto sohInputEditorWindow =
std::make_shared<SohInputEditorWindow>(CVAR_WINDOW("ControllerConfiguration"), "Configure Controller");
auto sohFast3dWindow =
@@ -1265,6 +1271,7 @@ extern "C" void InitOTR(int argc, char* argv[]) {
conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion2Updater>());
conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion3Updater>());
conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion4Updater>());
conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion5Updater>());
conf->RunVersionUpdates();
SohGui::SetupGuiElements();
@@ -2264,7 +2271,8 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
s16 actorParams = 0;
if (IS_RANDO) {
auto ctx = Rando::Context::GetInstance();
if (ctx->GetOption(RSK_SHUFFLE_ENTRANCES)) {
if (ctx->GetOption(RSK_SHUFFLE_ENTRANCES) &&
CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("EntrancesOnSigns"), 0)) {
s16 entrance = -1;
switch (textId) {
case TEXT_WATERFALL:
@@ -2305,7 +2313,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
entrance = ENTR_GROTTOS_13;
break;
case TEXT_DMT_DC_SIGN:
entrance = ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN;
entrance = ENTR_DODONGOS_CAVERN_ENTRANCE;
break;
case TEXT_DMT_GC_SIGN:
entrance = ENTR_GORON_CITY_UPPER_EXIT;
+3 -6
View File
@@ -650,18 +650,15 @@ void Menu::DrawElement() {
ImVec2 pos = window->DC.CursorPos;
float centerX = pos.x + windowWidth / 2 - (style.ItemSpacing.x * (menuEntries.size() + 1));
std::vector<ImVec2> headerSizes;
float headerWidth = style.ItemSpacing.x + 20;
float headerWidth = 0.0f;
bool headerSearch = !CVarGetInteger(CVAR_SETTING("Menu.SidebarSearch"), 0);
if (headerSearch) {
headerWidth += 200.0f + style.ItemSpacing.x + style.FramePadding.x;
headerWidth += 200.0f;
}
for (auto& label : menuOrder) {
ImVec2 size = ImGui::CalcTextSize(label.c_str());
headerSizes.push_back(size);
headerWidth += size.x + style.FramePadding.x * 2;
if (label == headerIndex) {
headerWidth += style.ItemSpacing.x;
}
headerWidth += size.x + style.FramePadding.x * 2 + style.ItemSpacing.x;
}
// Full screen menu with widths below 1280, heights below 800.
+9 -2
View File
@@ -11,6 +11,12 @@ static const std::unordered_map<int32_t, const char*> logLevels = {
{ DEBUG_LOG_OFF, "Off" },
};
#ifdef _DEBUG
DebugLogOption defaultLogLevel = DEBUG_LOG_TRACE;
#else
DebugLogOption defaultLogLevel = DEBUG_LOG_INFO;
#endif
static const std::unordered_map<int32_t, const char*> debugSaveFileModes = {
{ 0, "Off" },
{ 1, "Vanilla" },
@@ -110,10 +116,11 @@ void SohMenu::AddMenuDevTools() {
.Options(ComboboxOptions()
.Tooltip("The log level determines which messages are printed to the console."
" This does not affect the log file output")
.ComboMap(logLevels))
.ComboMap(logLevels)
.DefaultIndex(defaultLogLevel))
.Callback([](WidgetInfo& info) {
Ship::Context::GetInstance()->GetLogger()->set_level(
(spdlog::level::level_enum)CVarGetInteger(CVAR_DEVELOPER_TOOLS("LogLevel"), DEBUG_LOG_DEBUG));
(spdlog::level::level_enum)CVarGetInteger(CVAR_DEVELOPER_TOOLS("LogLevel"), defaultLogLevel));
})
.PreFunc([](WidgetInfo& info) { info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active; });
+4 -5
View File
@@ -334,8 +334,7 @@ void SohMenu::AddMenuEnhancements() {
AddWidget(path, "Skip Feeding Jabu-Jabu", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("TimeSavers.SkipJabuJabuFish"))
.PreFunc([](WidgetInfo& info) {
info.options->disabled =
IS_RANDO && OTRGlobals::Instance->gRandoContext->GetOption(RSK_JABU_OPEN).Is(RO_JABU_OPEN);
info.options->disabled = IS_RANDO;
info.options->disabledTooltip =
"This setting is disabled because a randomizer savefile with \"Jabu-Jabu: Open\" is loaded.";
})
@@ -603,7 +602,7 @@ void SohMenu::AddMenuEnhancements() {
AddWidget(path, "Color Temple of Time's Medallions", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("ToTMedallionsColors"))
.RaceDisable(false)
.Callback([](WidgetInfo& info) { PatchToTMedallions(); })
.Callback([](WidgetInfo& info) { UpdateToTMedallions(); })
.Options(CheckboxOptions().Tooltip(
"When Medallions are collected, the Medallion imprints around the Master Sword Pedestal in the Temple "
"of Time will become colored-in."));
@@ -733,9 +732,9 @@ void SohMenu::AddMenuEnhancements() {
.Options(CheckboxOptions().Tooltip(
"Equip items and equipment on the D-pad. If used with \"D-pad on Pause Screen\", you must "
"hold C-Up to equip instead of navigate."));
AddWidget(path, "Assignable Tunics and Boots", WIDGET_CVAR_CHECKBOX)
AddWidget(path, "Assignable Shields, Tunics and Boots", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("AssignableTunicsAndBoots"))
.Options(CheckboxOptions().Tooltip("Allows equipping the Tunics and Boots to C-Buttons/D-pad."));
.Options(CheckboxOptions().Tooltip("Allows equipping Shields, Tunics and Boots to C-Buttons/D-pad."));
// TODO: Revist strength toggle, it's currently separate but should probably be locked behind the
// Equipment toggle settings or be absorbed by it completely.
AddWidget(path, "Equipment Toggle", WIDGET_CVAR_CHECKBOX)
+3
View File
@@ -97,6 +97,9 @@ void SohMenu::AddMenuRandomizer() {
})
.Options(FloatSliderOptions().Min(5.0f).Max(15.0f).Format("%.2f").DefaultValue(10.0f).Tooltip(
"The size of the item when it is picked up."));
AddWidget(path, "Signs Hint Entrances", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_RANDOMIZER_ENHANCEMENT("EntrancesOnSigns"))
.Options(CheckboxOptions().Tooltip("If enabled, signs near loading zones will tell you where they lead to."));
// Plandomizer
path.sidebarName = "Plandomizer";
+9
View File
@@ -11,6 +11,8 @@ ConfigVersion3Updater::ConfigVersion3Updater() : ConfigVersionUpdater(3) {
}
ConfigVersion4Updater::ConfigVersion4Updater() : ConfigVersionUpdater(4) {
}
ConfigVersion5Updater::ConfigVersion5Updater() : ConfigVersionUpdater(5) {
}
void ConfigVersion1Updater::Update(Ship::Config* conf) {
if (conf->GetInt("Window.Width", 640) == 640) {
@@ -122,4 +124,11 @@ void ConfigVersion4Updater::Update(Ship::Config* conf) {
CVarClear(migration.from.c_str());
}
}
void ConfigVersion5Updater::Update(Ship::Config* conf) {
// After removal of Vanilla, make sure it doesn't crash because of an out of range on the combobox
if (CVarGetInteger("gRandoSettings.LogicRules", 0) == 2) {
CVarSetInteger("gRandoSettings.LogicRules", 0);
}
}
} // namespace SOH
+6
View File
@@ -24,4 +24,10 @@ class ConfigVersion4Updater final : public Ship::ConfigVersionUpdater {
ConfigVersion4Updater();
void Update(Ship::Config* conf);
};
class ConfigVersion5Updater final : public Ship::ConfigVersionUpdater {
public:
ConfigVersion5Updater();
void Update(Ship::Config* conf);
};
} // namespace SOH
+2 -3
View File
@@ -472,13 +472,12 @@ extern "C" s32 OTRfunc_800973FC(PlayState* play, RoomContext* roomCtx) {
gSegments[3] = VIRTUAL_TO_PHYSICAL(roomCtx->unk_34);
OTRScene_ExecuteCommands(play, (SOH::Scene*)roomCtx->roomToLoad);
if (!GameInteractor_Should(VB_DRAW_2D_BACKGROUND, true)) {
play->envCtx.skyboxDisabled = false;
}
Player_SetBootData(play, GET_PLAYER(play));
Actor_SpawnTransitionActors(play, &play->actorCtx);
GameInteractor_ExecuteAfterSceneCommands(play->sceneNum);
return 1;
}
+3 -12
View File
@@ -1,4 +1,5 @@
#include "global.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "textures/icon_item_static/icon_item_static.h"
#include "textures/icon_item_24_static/icon_item_24_static.h"
#include "textures/parameter_static/parameter_static.h"
@@ -204,20 +205,10 @@ u8 Inventory_DeleteEquipment(PlayState* play, s16 equipment) {
if (equipment == EQUIP_TYPE_TUNIC) {
gSaveContext.equips.equipment |= EQUIP_VALUE_TUNIC_KOKIRI << (EQUIP_TYPE_TUNIC * 4);
// non-vanilla: remove goron and zora tunics from item buttons if assignable tunics is on
if (CVarGetInteger(CVAR_ENHANCEMENT("AssignableTunicsAndBoots"), 0) &&
equipValue != EQUIP_VALUE_TUNIC_KOKIRI) {
ItemID item = (equipValue == EQUIP_VALUE_TUNIC_GORON ? ITEM_TUNIC_GORON : ITEM_TUNIC_ZORA);
for (int i = 1; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
if (gSaveContext.equips.buttonItems[i] == item) {
gSaveContext.equips.buttonItems[i] = ITEM_NONE;
gSaveContext.equips.cButtonSlots[i - 1] = SLOT_NONE;
}
}
}
// end non-vanilla
}
GameInteractor_ExecuteOnEquipmentDelete(equipment, equipValue);
if (equipment == EQUIP_TYPE_SWORD) {
gSaveContext.equips.buttonItems[0] = ITEM_NONE;
gSaveContext.infTable[29] = 1;
+1 -5
View File
@@ -725,11 +725,7 @@ void EnFz_Draw(Actor* thisx, PlayState* play) {
// SOH [Enhancement] - With enemy health scaling, the Freezards health could cause an index out of bounds for the
// displayLists, so we need to recompute the index based on the scaled health (using the maximum health value) and
// clamp the final result for safety.
if (CVarGetInteger(CVAR_ENHANCEMENT("EnemySizeScalesHealth"), 0)) {
u8 scaledHealth = (u8)(((f32)this->actor.colChkInfo.health / GetActorMaximumHealth(this)) * 6);
index = (6 - scaledHealth) >> 1;
index = CLAMP(index, 0, 2);
}
GameInteractor_Should(VB_FREEZARD_SCALE_HEALTH_WITH_SIZE, false, this, &index);
OPEN_DISPS(play->state.gfxCtx);
@@ -8293,19 +8293,20 @@ void Player_ChooseNextIdleAnim(PlayState* play, Player* this) {
//
// Note that `FIDGET_SWORD_SWING` is the first common fidget type, which is why
// all operations are done relative to this type.
if (((commonType + FIDGET_SWORD_SWING != FIDGET_SWORD_SWING) &&
(commonType + FIDGET_SWORD_SWING != FIDGET_ADJUST_SHIELD)) ||
((this->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) &&
((commonType + FIDGET_SWORD_SWING == FIDGET_ADJUST_SHIELD) ||
(Player_GetMeleeWeaponHeld2(this) != 0)))) {
if (GameInteractor_Should(VB_SET_IDLE_ANIM,
(((commonType + FIDGET_SWORD_SWING != FIDGET_SWORD_SWING) &&
(commonType + FIDGET_SWORD_SWING != FIDGET_ADJUST_SHIELD)) ||
((this->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) &&
((commonType + FIDGET_SWORD_SWING == FIDGET_ADJUST_SHIELD) ||
(Player_GetMeleeWeaponHeld2(this) != 0)))),
this, commonType)) {
//! @bug It is possible for `FIDGET_ADJUST_SHIELD` to be used even if
//! a shield is not currently equipped. This is because of how being shieldless
//! is implemented. There is no sword-only model type, only
//! `PLAYER_MODELGROUP_SWORD_AND_SHIELD` exists. Therefore, the right hand type will be
//! `PLAYER_MODELTYPE_RH_SHIELD` if sword is in hand, even if no shield is equipped.
if ((commonType + FIDGET_SWORD_SWING == FIDGET_SWORD_SWING) &&
Player_HoldsTwoHandedWeapon(this) &&
CVarGetInteger(CVAR_ENHANCEMENT("TwoHandedIdle"), 0) == 1) {
Player_HoldsTwoHandedWeapon(this)) {
//! @bug This code is unreachable.
//! The check above groups the `Player_GetMeleeWeaponHeld2` check and
//! `PLAYER_MODELTYPE_RH_SHIELD` conditions together, meaning sword and shield must be
@@ -12745,7 +12746,8 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) {
if (!func_8002DD78(this) && !func_808334B4(this) && (arg2 == 0)) { // First person without weapon
// Y Axis
if (!CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0)) {
if (!(CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0) &&
CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0))) {
temp2 += sControlInput->rel.stick_y * 240.0f * invertYAxisMulti * yAxisMulti;
}
if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) {
@@ -12763,7 +12765,8 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) {
// X Axis
temp2 = 0;
if (!CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0)) {
if (!(CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0) &&
CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0))) {
temp2 += sControlInput->rel.stick_x * -16.0f * invertXAxisMulti * xAxisMulti;
}
if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) {
@@ -12778,7 +12781,8 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) {
// Y Axis
temp1 = (this->stateFlags1 & PLAYER_STATE1_ON_HORSE) ? 3500 : 14000;
if (!CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0)) {
if (!(CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0) &&
CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0))) {
temp3 += ((sControlInput->rel.stick_y >= 0) ? 1 : -1) *
(s32)((1.0f - Math_CosS(sControlInput->rel.stick_y * 200)) * 1500.0f) * invertYAxisMulti *
yAxisMulti;
@@ -12798,7 +12802,8 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) {
temp1 = 19114;
temp2 = this->actor.focus.rot.y - this->actor.shape.rot.y;
temp3 = 0;
if (!CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0)) {
if (!(CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0) &&
CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0))) {
temp3 = ((sControlInput->rel.stick_x >= 0) ? 1 : -1) *
(s32)((1.0f - Math_CosS(sControlInput->rel.stick_x * 200)) * -1500.0f) * invertXAxisMulti *
xAxisMulti;
@@ -12815,7 +12820,8 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) {
this->actor.focus.rot.y = CLAMP(temp2, -temp1, temp1) + this->actor.shape.rot.y;
}
if (CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0)) {
if (CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0) &&
CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) {
f32 movementSpeed = LINK_IS_ADULT ? 9.0f : 8.25f;
if (CVarGetInteger(CVAR_ENHANCEMENT("MMBunnyHood"), BUNNY_HOOD_VANILLA) != BUNNY_HOOD_VANILLA &&
this->currentMask == PLAYER_MASK_BUNNY) {
@@ -642,8 +642,8 @@ void KaleidoScope_DrawEquipment(PlayState* play) {
pauseCtx->unk_1E4 = 7;
sEquipTimer = 10;
} else if (CVarGetInteger(CVAR_ENHANCEMENT("AssignableTunicsAndBoots"), 0) != 0) {
// Only allow assigning tunic and boots to c-buttons
if (pauseCtx->cursorY[PAUSE_EQUIP] > 1) {
// Only allow assigning shield, tunic and boots to c-buttons
if (pauseCtx->cursorY[PAUSE_EQUIP] > 0) {
if (CHECK_OWNED_EQUIP(pauseCtx->cursorY[PAUSE_EQUIP], pauseCtx->cursorX[PAUSE_EQUIP] - 1)) {
u16 slot = 0;
switch (cursorItem) {
@@ -665,6 +665,15 @@ void KaleidoScope_DrawEquipment(PlayState* play) {
case ITEM_BOOTS_HOVER:
slot = SLOT_BOOTS_HOVER;
break;
case ITEM_SHIELD_DEKU:
slot = SLOT_SHIELD_DEKU;
break;
case ITEM_SHIELD_HYLIAN:
slot = SLOT_SHIELD_HYLIAN;
break;
case ITEM_SHIELD_MIRROR:
slot = SLOT_SHIELD_MIRROR;
break;
default:
break;
}