18 Commits

Author SHA1 Message Date
Shishu the Dragon
97d4cc2881 Dev: Mac/Linux compat for VS Code tasks (#6211) 2026-02-26 01:22:57 +00:00
Shishu the Dragon
2d74552d05 Ivan: Fix intermittent crash with certain items (#6253) 2026-02-15 00:38:32 +00:00
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
23 changed files with 622 additions and 420 deletions

42
.vscode/tasks.json vendored
View File

@@ -9,14 +9,24 @@
"-S", "-S",
".", ".",
"-B", "-B",
"build/x64", "build-cmake",
"-G", "-G",
"Visual Studio 17 2022", "Ninja"
"-T",
"v143",
"-A",
"x64"
], ],
"windows": {
"args": [
"-S",
".",
"-B",
"build/x64",
"-G",
"Visual Studio 17 2022",
"-T",
"v143",
"-A",
"x64"
]
},
"group": "build", "group": "build",
"problemMatcher": [] "problemMatcher": []
}, },
@@ -26,10 +36,18 @@
"command": "cmake", "command": "cmake",
"args": [ "args": [
"--build", "--build",
"./build/x64", "build-cmake",
"--target", "--target",
"GenerateSohOtr" "GenerateSohOtr"
], ],
"windows": {
"args": [
"--build",
"./build/x64",
"--target",
"GenerateSohOtr"
]
},
"group": "build", "group": "build",
"problemMatcher": [] "problemMatcher": []
}, },
@@ -39,8 +57,14 @@
"command": "cmake", "command": "cmake",
"args": [ "args": [
"--build", "--build",
"./build/x64" "build-cmake"
], ],
"windows": {
"args": [
"--build",
"./build/x64"
]
},
"group": { "group": {
"kind": "build", "kind": "build",
"isDefault": true "isDefault": true
@@ -58,4 +82,4 @@
] ]
} }
] ]
} }

View File

@@ -6,7 +6,7 @@ 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") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
project(Ship VERSION 9.1.1 LANGUAGES C CXX) project(Ship VERSION 9.1.2 LANGUAGES C CXX)
include(CMake/soh-cvars.cmake) include(CMake/soh-cvars.cmake)
include(CMake/lus-cvars.cmake) include(CMake/lus-cvars.cmake)
set(SPDLOG_LEVEL_TRACE 0) set(SPDLOG_LEVEL_TRACE 0)

View File

@@ -239,7 +239,7 @@ cmake --build build-cmake --target ExtractAssetHeaders
``` ```
## macOS ## 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!** **Important: For maximum performance make sure you have ninja build tools installed!**
@@ -254,7 +254,7 @@ cd ShipWright
git submodule update --init git submodule update --init
# Install development dependencies (assuming homebrew) # 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 # Generate Ninja project
# Add `-DCMAKE_BUILD_TYPE:STRING=Release` if you're packaging # Add `-DCMAKE_BUILD_TYPE:STRING=Release` if you're packaging

View File

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

View File

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

View File

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

View File

@@ -1377,12 +1377,11 @@ void SohInputEditorWindow::DrawCameraControlPanel() {
CheckboxOptions() CheckboxOptions()
.Color(THEME_COLOR) .Color(THEME_COLOR)
.Tooltip("Allows for aiming with the right stick in:\n-First-Person/C-Up view\n-Weapon Aiming")); .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"),
CVarCheckbox("Allow moving while in first-person mode", CVAR_SETTING("MoveInFirstPerson"), CheckboxOptions({ { .disabled = !CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0),
CheckboxOptions() .disabledTooltip = "Forced off because Right Stick Aiming is disabled." } })
.Color(THEME_COLOR) .Color(THEME_COLOR)
.Tooltip("Changes the left stick to move the player while in first-person mode")); .Tooltip("Changes the left stick to move the player while in first-person mode"));
}
CVarCheckbox("Invert Aiming X Axis", CVAR_SETTING("Controls.InvertAimingXAxis"), CVarCheckbox("Invert Aiming X Axis", CVAR_SETTING("Controls.InvertAimingXAxis"),
CheckboxOptions() CheckboxOptions()
.Color(THEME_COLOR) .Color(THEME_COLOR)

View File

@@ -535,6 +535,15 @@ typedef enum {
// - None // - None
VB_FIX_SAW_SOFTLOCK, VB_FIX_SAW_SOFTLOCK,
// #### `result`
// ```c
// false
// ```
// #### `args`
// - `*EnFz`
// - `*s32`
VB_FREEZARD_SCALE_HEALTH_WITH_SIZE,
// #### `result` // #### `result`
// ```c // ```c
// true // true
@@ -2318,6 +2327,27 @@ typedef enum {
// - `*Player` // - `*Player`
VB_SET_STATIC_FLOOR_TYPE, 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; } GIVanillaBehavior;
#endif #endif

View File

@@ -137,97 +137,97 @@ const char* const sceneMappings[] = {
}; };
const char* const countMappings[] = { const char* const countMappings[] = {
"Anubis:", "Anubis:", // COUNT_ENEMIES_DEFEATED_ANUBIS
"Armos:", "Armos:", // COUNT_ENEMIES_DEFEATED_ARMOS
"Arwing:", "Arwing:", // COUNT_ENEMIES_DEFEATED_ARWING
"Bari:", "Bari:", // COUNT_ENEMIES_DEFEATED_BARI
"Biri:", "Beamos:", // COUNT_ENEMIES_DEFEATED_BEAMOS
"Beamos:", "Big Octo:", // COUNT_ENEMIES_DEFEATED_BIG_OCTO
"Big Octo:", "Biri:", // COUNT_ENEMIES_DEFEATED_BIRI
"Bubble (Blue):", "Bubble (Green):", // COUNT_ENEMIES_DEFEATED_BUBBLE_GREEN
"Bubble (Green):", "Bubble (Blue):", // COUNT_ENEMIES_DEFEATED_BUBBLE_BLUE
"Bubble (Red):", "Bubble (White):", // COUNT_ENEMIES_DEFEATED_BUBBLE_WHITE
"Bubble (White):", "Bubble (Red):", // COUNT_ENEMIES_DEFEATED_BUBBLE_RED
"Business Scrub:", "Business Scrub:", // COUNT_ENEMIES_DEFEATED_BUSINESS_SCRUB
"Dark Link:", "Dark Link:", // COUNT_ENEMIES_DEFEATED_DARK_LINK
"Dead Hand:", "Dead Hand:", // COUNT_ENEMIES_DEFEATED_DEAD_HAND
"Deku Baba:", "Deku Baba:", // COUNT_ENEMIES_DEFEATED_DEKU_BABA
"Deku Baba (Big):", "Deku Baba (Big):", // COUNT_ENEMIES_DEFEATED_DEKU_BABA_BIG
"Deku Scrub:", "Deku Scrub:", // COUNT_ENEMIES_DEFEATED_DEKU_SCRUB
"Dinolfos:", "Dinolfos:", // COUNT_ENEMIES_DEFEATED_DINOLFOS
"Dodongo:", "Dodongo:", // COUNT_ENEMIES_DEFEATED_DODONGO
"Dodongo (Baby):", "Dodongo (Baby):", // COUNT_ENEMIES_DEFEATED_DODONGO_BABY
"Door Mimic:", "Door Mimic:", // COUNT_ENEMIES_DEFEATED_DOOR_TRAP
"Flare Dancer:", "Flare Dancer:", // COUNT_ENEMIES_DEFEATED_FLARE_DANCER
"Floormaster:", "Floormaster:", // COUNT_ENEMIES_DEFEATED_FLOORMASTER
"Flying Floor Tile:", "Flying Pot:", // COUNT_ENEMIES_DEFEATED_FLYING_POT
"Flying Pot:", "Flying Floor Tile:", // COUNT_ENEMIES_DEFEATED_FLOOR_TILE
"Freezard:", "Freezard:", // COUNT_ENEMIES_DEFEATED_FREEZARD
"Gerudo Thief:", "Gerudo Thief:", // COUNT_ENEMIES_DEFEATED_GERUDO_THIEF
"Gibdo:", "Gibdo:", // COUNT_ENEMIES_DEFEATED_GIBDO
"Gohma Larva:", "Gohma Larva:", // COUNT_ENEMIES_DEFEATED_GOHMA_LARVA
"Guay:", "Guay:", // COUNT_ENEMIES_DEFEATED_GUAY
"Iron Knuckle:", "Iron Knuckle:", // COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE
"Iron Knuckle (Nab):", "Iron Knuckle (Nab):", // COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE_NABOORU
"Keese:", "Keese:", // COUNT_ENEMIES_DEFEATED_KEESE
"Keese (Fire):", "Keese (Fire):", // COUNT_ENEMIES_DEFEATED_KEESE_FIRE
"Keese (Ice):", "Keese (Ice):", // COUNT_ENEMIES_DEFEATED_KEESE_ICE
"Leever:", "Leever:", // COUNT_ENEMIES_DEFEATED_LEEVER
"Leever (Big):", "Leever (Big):", // COUNT_ENEMIES_DEFEATED_LEEVER_BIG
"Like-Like:", "Like-Like:", // COUNT_ENEMIES_DEFEATED_LIKE_LIKE
"Lizalfos:", "Lizalfos:", // COUNT_ENEMIES_DEFEATED_LIZALFOS
"Mad Scrub:", "Mad Scrub:", // COUNT_ENEMIES_DEFEATED_MAD_SCRUB
"Moblin:", "Moblin:", // COUNT_ENEMIES_DEFEATED_MOBLIN
"Moblin (Club):", "Moblin (Club):", // COUNT_ENEMIES_DEFEATED_MOBLIN_CLUB
"Octorok:", "Octorok:", // COUNT_ENEMIES_DEFEATED_OCTOROK
"Parasitic Tentacle:", "Parasitic Tentacle:", // COUNT_ENEMIES_DEFEATED_PARASITIC_TENTACLE
"Peahat:", "Peahat:", // COUNT_ENEMIES_DEFEATED_PEAHAT
"Peahat Larva:", "Peahat Larva:", // COUNT_ENEMIES_DEFEATED_PEAHAT_LARVA
"Poe:", "Poe:", // COUNT_ENEMIES_DEFEATED_POE
"Poe (Big):", "Poe (Big):", // COUNT_ENEMIES_DEFEATED_POE_BIG
"Poe (Composer):", "Poe (Composer):", // COUNT_ENEMIES_DEFEATED_POE_COMPOSER
"Poe Sisters:", "Poe Sisters:", // COUNT_ENEMIES_DEFEATED_POE_SISTERS
"Redead:", "Redead:", // COUNT_ENEMIES_DEFEATED_REDEAD
"Shabom:", "Shabom:", // COUNT_ENEMIES_DEFEATED_SHABOM
"Shell Blade:", "Shell Blade:", // COUNT_ENEMIES_DEFEATED_SHELLBLADE
"Skull Kid:", "Skulltula:", // COUNT_ENEMIES_DEFEATED_SKULLTULA
"Skulltula:", "Skulltula (Big):", // COUNT_ENEMIES_DEFEATED_SKULLTULA_BIG
"Skulltula (Big):", "Skulltula (Gold):", // COUNT_ENEMIES_DEFEATED_SKULLTULA_GOLD
"Skulltula (Gold):", "Skullwalltula:", // COUNT_ENEMIES_DEFEATED_SKULLWALLTULA
"Skullwalltula:", "Skull Kid:", // COUNT_ENEMIES_DEFEATED_SKULL_KID
"Spike:", "Spike:", // COUNT_ENEMIES_DEFEATED_SPIKE
"Stalchild:", "Stalchild:", // COUNT_ENEMIES_DEFEATED_STALCHILD
"Stalfos:", "Stalfos:", // COUNT_ENEMIES_DEFEATED_STALFOS
"Stinger:", "Stinger:", // COUNT_ENEMIES_DEFEATED_STINGER
"Tailpasaran:", "Tailpasaran:", // COUNT_ENEMIES_DEFEATED_TAILPASARAN
"Tektite (Blue):", "Tektite (Blue):", // COUNT_ENEMIES_DEFEATED_TEKTITE_BLUE
"Tektite (Red):", "Tektite (Red):", // COUNT_ENEMIES_DEFEATED_TEKTITE_RED
"Torch Slug:", "Torch Slug:", // COUNT_ENEMIES_DEFEATED_TORCH_SLUG
"Wallmaster:", "Wallmaster:", // COUNT_ENEMIES_DEFEATED_WALLMASTER
"Withered Deku Baba:", "Withered Deku Baba:", // COUNT_ENEMIES_DEFEATED_WITHERED_DEKU_BABA
"Wolfos:", "Wolfos:", // COUNT_ENEMIES_DEFEATED_WOLFOS
"Wolfos (White):", "Wolfos (White):", // COUNT_ENEMIES_DEFEATED_WOLFOS_WHITE
"Deku Sticks:", "Deku Sticks:", // COUNT_AMMO_USED_STICK
"Deku Nuts:", "Deku Nuts:", // COUNT_AMMO_USED_NUT
"Bombs:", "Bombs:", // COUNT_AMMO_USED_BOMB
"Arrows:", "Arrows:", // COUNT_AMMO_USED_ARROW
"Deku Seeds:", "Deku Seeds:", // COUNT_AMMO_USED_SEED
"Bombchus:", "Bombchus:", // COUNT_AMMO_USED_BOMBCHU
"Beans:", "Beans:", // COUNT_AMMO_USED_BEAN
"A:", "A:", // COUNT_BUTTON_PRESSES_A
"B:", "B:", // COUNT_BUTTON_PRESSES_B
"L:", "L:", // COUNT_BUTTON_PRESSES_L
"R:", "R:", // COUNT_BUTTON_PRESSES_R
"Z:", "Z:", // COUNT_BUTTON_PRESSES_Z
"C-Up:", "C-Up:", // COUNT_BUTTON_PRESSES_CUP
"C-Right:", "C-Right:", // COUNT_BUTTON_PRESSES_CRIGHT
"C-Down:", "C-Down:", // COUNT_BUTTON_PRESSES_CDOWN
"C-Left:", "C-Left:", // COUNT_BUTTON_PRESSES_CLEFT
"D-Up:", "D-Up:", // COUNT_BUTTON_PRESSES_DUP
"D-Right:", "D-Right:", // COUNT_BUTTON_PRESSES_DRIGHT
"D-Down:", "D-Down:", // COUNT_BUTTON_PRESSES_DDOWN
"D-Left:", "D-Left:", // COUNT_BUTTON_PRESSES_DLEFT
"Start:", "Start:", // COUNT_BUTTON_PRESSES_START
}; };
#define COLOR_WHITE ImVec4(1.00f, 1.00f, 1.00f, 1.00f) #define COLOR_WHITE ImVec4(1.00f, 1.00f, 1.00f, 1.00f)

View File

@@ -96,6 +96,8 @@ void ModsHandleDragAndDrop(std::vector<std::string>& objectList, int targetIndex
std::vector<std::string> GetEnabledModsFromCVar() { std::vector<std::string> GetEnabledModsFromCVar() {
std::string enabledModsCVarValue = CVAR_ENABLED_MODS_VALUE; std::string enabledModsCVarValue = CVAR_ENABLED_MODS_VALUE;
if (enabledModsCVarValue.empty())
return {};
return StringHelper::Split(enabledModsCVarValue, SEPARATOR); return StringHelper::Split(enabledModsCVarValue, SEPARATOR);
} }

View File

@@ -14,24 +14,7 @@
#include "soh/Enhancements/timesaver_hook_handlers.h" #include "soh/Enhancements/timesaver_hook_handlers.h"
#include "soh/Enhancements/randomizer/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_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_boy/object_link_boy.h"
#include "objects/object_link_child/object_link_child.h" #include "objects/object_link_child/object_link_child.h"
#include "soh_assets.h" #include "soh_assets.h"
@@ -426,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() { void RegisterBossDefeatTimestamps() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnBossDefeat>([](void* refActor) { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnBossDefeat>([](void* refActor) {
Actor* actor = static_cast<Actor*>(refActor); Actor* actor = static_cast<Actor*>(refActor);
@@ -677,60 +470,6 @@ void RegisterHurtContainerModeHandler() {
[](int32_t fileNum) { UpdateHurtContainerModeState(CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)); }); [](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 RegisterFloorSwitchesHook() { void RegisterFloorSwitchesHook() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>([](void* refActor) { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>([](void* refActor) {
Actor* actor = static_cast<Actor*>(refActor); Actor* actor = static_cast<Actor*>(refActor);
@@ -782,9 +521,7 @@ void InitMods() {
RegisterMenuPathFix(); RegisterMenuPathFix();
RegisterMirrorModeHandler(); RegisterMirrorModeHandler();
RegisterResetNaviTimer(); RegisterResetNaviTimer();
RegisterEnemyDefeatCounts();
RegisterBossDefeatTimestamps(); RegisterBossDefeatTimestamps();
RegisterRandomizedEnemySizes();
RegisterFloorSwitchesHook(); RegisterFloorSwitchesHook();
RegisterPatchHandHandler(); RegisterPatchHandHandler();
RegisterHurtContainerModeHandler(); RegisterHurtContainerModeHandler();

View File

@@ -372,6 +372,7 @@ bool GetTimePassFromScene(SceneID scene) {
case SCENE_POTION_SHOP_GRANNY: case SCENE_POTION_SHOP_GRANNY:
case SCENE_GANON_BOSS: case SCENE_GANON_BOSS:
case SCENE_HOUSE_OF_SKULLTULA: case SCENE_HOUSE_OF_SKULLTULA:
case SCENE_KAKARIKO_VILLAGE:
case SCENE_KOKIRI_FOREST: case SCENE_KOKIRI_FOREST:
case SCENE_SACRED_FOREST_MEADOW: case SCENE_SACRED_FOREST_MEADOW:
case SCENE_LOST_WOODS: case SCENE_LOST_WOODS:
@@ -383,6 +384,7 @@ bool GetTimePassFromScene(SceneID scene) {
case SCENE_GERUDOS_FORTRESS: case SCENE_GERUDOS_FORTRESS:
case SCENE_HAUNTED_WASTELAND: case SCENE_HAUNTED_WASTELAND:
case SCENE_DEATH_MOUNTAIN_CRATER: case SCENE_DEATH_MOUNTAIN_CRATER:
case SCENE_LON_LON_RANCH:
case SCENE_ID_MAX: case SCENE_ID_MAX:
return false; return false;
@@ -393,14 +395,12 @@ bool GetTimePassFromScene(SceneID scene) {
return false; return false;
case SCENE_HYRULE_FIELD: case SCENE_HYRULE_FIELD:
case SCENE_KAKARIKO_VILLAGE:
case SCENE_ZORAS_RIVER: case SCENE_ZORAS_RIVER:
case SCENE_LAKE_HYLIA: case SCENE_LAKE_HYLIA:
case SCENE_GERUDO_VALLEY: case SCENE_GERUDO_VALLEY:
case SCENE_DESERT_COLOSSUS: case SCENE_DESERT_COLOSSUS:
case SCENE_HYRULE_CASTLE: case SCENE_HYRULE_CASTLE:
case SCENE_DEATH_MOUNTAIN_TRAIL: case SCENE_DEATH_MOUNTAIN_TRAIL:
case SCENE_LON_LON_RANCH:
return true; return true;
case SCENE_TEST01: case SCENE_TEST01:

View File

@@ -463,7 +463,7 @@ void RegionTable_Init_ForestTemple() {
}, { }, {
//Locations //Locations
LOCATION(RC_FOREST_TEMPLE_MQ_WELL_CHEST, logic->CanHitEyeTargets() || (logic->CanOpenUnderwaterChest() && logic->WaterTimer() >= 8)), 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) //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_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()), LOCATION(RC_FOREST_TEMPLE_MQ_WELL_WEST_HEART, (logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 8) || logic->CanHitEyeTargets()),

View File

@@ -223,7 +223,8 @@ bool Logic::HasItem(RandomizerGet itemName) {
case RG_GOLDEN_SCALE: case RG_GOLDEN_SCALE:
return CurrentUpgrade(UPG_SCALE) >= 2; return CurrentUpgrade(UPG_SCALE) >= 2;
case RG_POCKET_EGG: 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_COJIRO:
case RG_ODD_MUSHROOM: case RG_ODD_MUSHROOM:
case RG_ODD_POTION: 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 // Get the swch bit positions for the dungeon
const std::vector<uint8_t>& GetDungeonSmallKeyDoors(SceneID sceneId) { const std::vector<uint8_t>& GetDungeonSmallKeyDoors(SceneID sceneId) {
static const std::vector<uint8_t> emptyVector; 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) { 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 // Get the swch value for the scene
uint32_t swch; uint32_t swch;

View File

@@ -755,6 +755,14 @@ void DrawQuest(ItemTrackerItem item) {
Tooltip(SohUtils::GetQuestItemName(item.id).c_str()); 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) { void DrawItem(ItemTrackerItem item) {
uint32_t actualItemId = GameInteractor::IsSaveLoaded() ? INV_CONTENT(item.id) : ITEM_NONE; uint32_t actualItemId = GameInteractor::IsSaveLoaded() ? INV_CONTENT(item.id) : ITEM_NONE;
@@ -811,50 +819,47 @@ void DrawItem(ItemTrackerItem item) {
break; break;
case RG_GOHMA_SOUL: case RG_GOHMA_SOUL:
actualItemId = item.id; actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_GOHMA_SOUL); hasItem = HasBossSoul(RAND_INF_GOHMA_SOUL);
itemName = "Gohma's Soul"; itemName = "Gohma's Soul";
break; break;
case RG_KING_DODONGO_SOUL: case RG_KING_DODONGO_SOUL:
actualItemId = item.id; actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_KING_DODONGO_SOUL); hasItem = HasBossSoul(RAND_INF_KING_DODONGO_SOUL);
itemName = "King Dodongo's Soul"; itemName = "King Dodongo's Soul";
break; break;
case RG_BARINADE_SOUL: case RG_BARINADE_SOUL:
actualItemId = item.id; actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_BARINADE_SOUL); hasItem = HasBossSoul(RAND_INF_BARINADE_SOUL);
itemName = "Barinade's Soul"; itemName = "Barinade's Soul";
break; break;
case RG_PHANTOM_GANON_SOUL: case RG_PHANTOM_GANON_SOUL:
actualItemId = item.id; actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_PHANTOM_GANON_SOUL); hasItem = HasBossSoul(RAND_INF_PHANTOM_GANON_SOUL);
itemName = "Phantom Ganon's Soul"; itemName = "Phantom Ganon's Soul";
break; break;
case RG_VOLVAGIA_SOUL: case RG_VOLVAGIA_SOUL:
actualItemId = item.id; actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_VOLVAGIA_SOUL); hasItem = HasBossSoul(RAND_INF_VOLVAGIA_SOUL);
itemName = "Volvagia's Soul"; itemName = "Volvagia's Soul";
break; break;
case RG_MORPHA_SOUL: case RG_MORPHA_SOUL:
actualItemId = item.id; actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_MORPHA_SOUL); hasItem = HasBossSoul(RAND_INF_MORPHA_SOUL);
itemName = "Morpha's Soul"; itemName = "Morpha's Soul";
break; break;
case RG_BONGO_BONGO_SOUL: case RG_BONGO_BONGO_SOUL:
actualItemId = item.id; actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_BONGO_BONGO_SOUL); hasItem = HasBossSoul(RAND_INF_BONGO_BONGO_SOUL);
itemName = "Bongo Bongo's Soul"; itemName = "Bongo Bongo's Soul";
break; break;
case RG_TWINROVA_SOUL: case RG_TWINROVA_SOUL:
actualItemId = item.id; actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_TWINROVA_SOUL); hasItem = HasBossSoul(RAND_INF_TWINROVA_SOUL);
itemName = "Twinrova's Soul"; itemName = "Twinrova's Soul";
break; break;
case RG_GANON_SOUL: case RG_GANON_SOUL:
actualItemId = item.id; actualItemId = item.id;
hasItem = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BOSS_SOULS) == hasItem = HasBossSoul(RAND_INF_GANON_SOUL);
RO_BOSS_SOULS_ON_PLUS_GANON
? Flags_GetRandomizerInf(RAND_INF_GANON_SOUL)
: true;
itemName = "Ganon's Soul"; itemName = "Ganon's Soul";
break; break;

View File

@@ -1271,6 +1271,7 @@ extern "C" void InitOTR(int argc, char* argv[]) {
conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion2Updater>()); conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion2Updater>());
conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion3Updater>()); conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion3Updater>());
conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion4Updater>()); conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion4Updater>());
conf->RegisterVersionUpdater(std::make_shared<SOH::ConfigVersion5Updater>());
conf->RunVersionUpdates(); conf->RunVersionUpdates();
SohGui::SetupGuiElements(); SohGui::SetupGuiElements();
@@ -2270,7 +2271,8 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
s16 actorParams = 0; s16 actorParams = 0;
if (IS_RANDO) { if (IS_RANDO) {
auto ctx = Rando::Context::GetInstance(); 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; s16 entrance = -1;
switch (textId) { switch (textId) {
case TEXT_WATERFALL: case TEXT_WATERFALL:
@@ -2311,7 +2313,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
entrance = ENTR_GROTTOS_13; entrance = ENTR_GROTTOS_13;
break; break;
case TEXT_DMT_DC_SIGN: case TEXT_DMT_DC_SIGN:
entrance = ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN; entrance = ENTR_DODONGOS_CAVERN_ENTRANCE;
break; break;
case TEXT_DMT_GC_SIGN: case TEXT_DMT_GC_SIGN:
entrance = ENTR_GORON_CITY_UPPER_EXIT; entrance = ENTR_GORON_CITY_UPPER_EXIT;

View File

@@ -650,18 +650,15 @@ void Menu::DrawElement() {
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
float centerX = pos.x + windowWidth / 2 - (style.ItemSpacing.x * (menuEntries.size() + 1)); float centerX = pos.x + windowWidth / 2 - (style.ItemSpacing.x * (menuEntries.size() + 1));
std::vector<ImVec2> headerSizes; std::vector<ImVec2> headerSizes;
float headerWidth = style.ItemSpacing.x + 20; float headerWidth = 0.0f;
bool headerSearch = !CVarGetInteger(CVAR_SETTING("Menu.SidebarSearch"), 0); bool headerSearch = !CVarGetInteger(CVAR_SETTING("Menu.SidebarSearch"), 0);
if (headerSearch) { if (headerSearch) {
headerWidth += 200.0f + style.ItemSpacing.x + style.FramePadding.x; headerWidth += 200.0f;
} }
for (auto& label : menuOrder) { for (auto& label : menuOrder) {
ImVec2 size = ImGui::CalcTextSize(label.c_str()); ImVec2 size = ImGui::CalcTextSize(label.c_str());
headerSizes.push_back(size); headerSizes.push_back(size);
headerWidth += size.x + style.FramePadding.x * 2; headerWidth += size.x + style.FramePadding.x * 2 + style.ItemSpacing.x;
if (label == headerIndex) {
headerWidth += style.ItemSpacing.x;
}
} }
// Full screen menu with widths below 1280, heights below 800. // Full screen menu with widths below 1280, heights below 800.

View File

@@ -97,6 +97,9 @@ void SohMenu::AddMenuRandomizer() {
}) })
.Options(FloatSliderOptions().Min(5.0f).Max(15.0f).Format("%.2f").DefaultValue(10.0f).Tooltip( .Options(FloatSliderOptions().Min(5.0f).Max(15.0f).Format("%.2f").DefaultValue(10.0f).Tooltip(
"The size of the item when it is picked up.")); "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 // Plandomizer
path.sidebarName = "Plandomizer"; path.sidebarName = "Plandomizer";

View File

@@ -11,6 +11,8 @@ ConfigVersion3Updater::ConfigVersion3Updater() : ConfigVersionUpdater(3) {
} }
ConfigVersion4Updater::ConfigVersion4Updater() : ConfigVersionUpdater(4) { ConfigVersion4Updater::ConfigVersion4Updater() : ConfigVersionUpdater(4) {
} }
ConfigVersion5Updater::ConfigVersion5Updater() : ConfigVersionUpdater(5) {
}
void ConfigVersion1Updater::Update(Ship::Config* conf) { void ConfigVersion1Updater::Update(Ship::Config* conf) {
if (conf->GetInt("Window.Width", 640) == 640) { if (conf->GetInt("Window.Width", 640) == 640) {
@@ -122,4 +124,11 @@ void ConfigVersion4Updater::Update(Ship::Config* conf) {
CVarClear(migration.from.c_str()); 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 } // namespace SOH

View File

@@ -24,4 +24,10 @@ class ConfigVersion4Updater final : public Ship::ConfigVersionUpdater {
ConfigVersion4Updater(); ConfigVersion4Updater();
void Update(Ship::Config* conf); void Update(Ship::Config* conf);
}; };
class ConfigVersion5Updater final : public Ship::ConfigVersionUpdater {
public:
ConfigVersion5Updater();
void Update(Ship::Config* conf);
};
} // namespace SOH } // namespace SOH

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 // 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 // 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. // clamp the final result for safety.
if (CVarGetInteger(CVAR_ENHANCEMENT("EnemySizeScalesHealth"), 0)) { GameInteractor_Should(VB_FREEZARD_SCALE_HEALTH_WITH_SIZE, false, this, &index);
u8 scaledHealth = (u8)(((f32)this->actor.colChkInfo.health / GetActorMaximumHealth(this)) * 6);
index = (6 - scaledHealth) >> 1;
index = CLAMP(index, 0, 2);
}
OPEN_DISPS(play->state.gfxCtx); OPEN_DISPS(play->state.gfxCtx);

View File

@@ -24,7 +24,6 @@ void EnPartner_Update(Actor* thisx, PlayState* play);
void EnPartner_Draw(Actor* thisx, PlayState* play); void EnPartner_Draw(Actor* thisx, PlayState* play);
void EnPartner_SpawnSparkles(EnPartner* this, PlayState* play, s32 sparkleLife); void EnPartner_SpawnSparkles(EnPartner* this, PlayState* play, s32 sparkleLife);
void func_808328EC(Player* this, u16 sfxId);
void Player_RequestQuake(PlayState* play, s32 speed, s32 y, s32 countdown); void Player_RequestQuake(PlayState* play, s32 speed, s32 y, s32 countdown);
s32 spawn_boomerang_ivan(EnPartner* this, PlayState* play); s32 spawn_boomerang_ivan(EnPartner* this, PlayState* play);
@@ -194,7 +193,7 @@ void UseBow(Actor* thisx, PlayState* play, u8 started, u8 arrowType) {
EnPartner* this = (EnPartner*)thisx; EnPartner* this = (EnPartner*)thisx;
if (started == 1) { if (started == 1) {
func_808328EC(this, NA_SE_PL_CHANGE_ARMS); Player_PlaySfx(this, NA_SE_PL_CHANGE_ARMS);
this->canMove = 0; this->canMove = 0;
} else if (started == 0) { } else if (started == 0) {
if (this->itemTimer <= 0) { if (this->itemTimer <= 0) {
@@ -235,7 +234,7 @@ void UseSlingshot(Actor* thisx, PlayState* play, u8 started) {
EnPartner* this = (EnPartner*)thisx; EnPartner* this = (EnPartner*)thisx;
if (started == 1) { if (started == 1) {
func_808328EC(this, NA_SE_PL_CHANGE_ARMS); Player_PlaySfx(this, NA_SE_PL_CHANGE_ARMS);
this->canMove = 0; this->canMove = 0;
} else if (started == 0) { } else if (started == 0) {
if (this->itemTimer <= 0) { if (this->itemTimer <= 0) {
@@ -324,7 +323,7 @@ void UseDekuStick(Actor* thisx, PlayState* play, u8 started) {
if (this->itemTimer <= 0) { if (this->itemTimer <= 0) {
if (started == 1) { if (started == 1) {
if (AMMO(ITEM_STICK) > 0) { if (AMMO(ITEM_STICK) > 0) {
func_808328EC(this, NA_SE_EV_FLAME_IGNITION); Player_PlaySfx(this, NA_SE_EV_FLAME_IGNITION);
} else { } else {
Sfx_PlaySfxCentered(NA_SE_SY_ERROR); Sfx_PlaySfxCentered(NA_SE_SY_ERROR);
} }
@@ -373,7 +372,7 @@ void UseHookshot(Actor* thisx, PlayState* play, u8 started) {
if (this->itemTimer <= 0) { if (this->itemTimer <= 0) {
if (started == 1) { if (started == 1) {
func_808328EC(this, NA_SE_PL_CHANGE_ARMS); Player_PlaySfx(this, NA_SE_PL_CHANGE_ARMS);
this->canMove = 0; this->canMove = 0;
this->hookshotTarget = this->hookshotTarget =
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_OBJ_HSBLOCK, this->actor.world.pos.x, Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_OBJ_HSBLOCK, this->actor.world.pos.x,
@@ -385,7 +384,7 @@ void UseHookshot(Actor* thisx, PlayState* play, u8 started) {
} else if (started == 0) { } else if (started == 0) {
Actor_Kill(this->hookshotTarget); Actor_Kill(this->hookshotTarget);
this->hookshotTarget = NULL; this->hookshotTarget = NULL;
func_808328EC(this, NA_SE_PL_CHANGE_ARMS); Player_PlaySfx(this, NA_SE_PL_CHANGE_ARMS);
this->canMove = 1; this->canMove = 1;
} else if (started == 2) { } else if (started == 2) {
this->hookshotTarget->shape.rot.y = this->actor.world.rot.y; this->hookshotTarget->shape.rot.y = this->actor.world.rot.y;

View File

@@ -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 // Note that `FIDGET_SWORD_SWING` is the first common fidget type, which is why
// all operations are done relative to this type. // all operations are done relative to this type.
if (((commonType + FIDGET_SWORD_SWING != FIDGET_SWORD_SWING) && if (GameInteractor_Should(VB_SET_IDLE_ANIM,
(commonType + FIDGET_SWORD_SWING != FIDGET_ADJUST_SHIELD)) || (((commonType + FIDGET_SWORD_SWING != FIDGET_SWORD_SWING) &&
((this->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) && (commonType + FIDGET_SWORD_SWING != FIDGET_ADJUST_SHIELD)) ||
((commonType + FIDGET_SWORD_SWING == FIDGET_ADJUST_SHIELD) || ((this->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) &&
(Player_GetMeleeWeaponHeld2(this) != 0)))) { ((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 //! @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 //! a shield is not currently equipped. This is because of how being shieldless
//! is implemented. There is no sword-only model type, only //! is implemented. There is no sword-only model type, only
//! `PLAYER_MODELGROUP_SWORD_AND_SHIELD` exists. Therefore, the right hand type will be //! `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. //! `PLAYER_MODELTYPE_RH_SHIELD` if sword is in hand, even if no shield is equipped.
if ((commonType + FIDGET_SWORD_SWING == FIDGET_SWORD_SWING) && if ((commonType + FIDGET_SWORD_SWING == FIDGET_SWORD_SWING) &&
Player_HoldsTwoHandedWeapon(this) && Player_HoldsTwoHandedWeapon(this)) {
CVarGetInteger(CVAR_ENHANCEMENT("TwoHandedIdle"), 0) == 1) {
//! @bug This code is unreachable. //! @bug This code is unreachable.
//! The check above groups the `Player_GetMeleeWeaponHeld2` check and //! The check above groups the `Player_GetMeleeWeaponHeld2` check and
//! `PLAYER_MODELTYPE_RH_SHIELD` conditions together, meaning sword and shield must be //! `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 if (!func_8002DD78(this) && !func_808334B4(this) && (arg2 == 0)) { // First person without weapon
// Y Axis // 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; temp2 += sControlInput->rel.stick_y * 240.0f * invertYAxisMulti * yAxisMulti;
} }
if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) { if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) {
@@ -12763,7 +12765,8 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) {
// X Axis // X Axis
temp2 = 0; 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; temp2 += sControlInput->rel.stick_x * -16.0f * invertXAxisMulti * xAxisMulti;
} }
if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) { if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) {
@@ -12778,7 +12781,8 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) {
// Y Axis // Y Axis
temp1 = (this->stateFlags1 & PLAYER_STATE1_ON_HORSE) ? 3500 : 14000; 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) * temp3 += ((sControlInput->rel.stick_y >= 0) ? 1 : -1) *
(s32)((1.0f - Math_CosS(sControlInput->rel.stick_y * 200)) * 1500.0f) * invertYAxisMulti * (s32)((1.0f - Math_CosS(sControlInput->rel.stick_y * 200)) * 1500.0f) * invertYAxisMulti *
yAxisMulti; yAxisMulti;
@@ -12798,7 +12802,8 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) {
temp1 = 19114; temp1 = 19114;
temp2 = this->actor.focus.rot.y - this->actor.shape.rot.y; temp2 = this->actor.focus.rot.y - this->actor.shape.rot.y;
temp3 = 0; 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) * temp3 = ((sControlInput->rel.stick_x >= 0) ? 1 : -1) *
(s32)((1.0f - Math_CosS(sControlInput->rel.stick_x * 200)) * -1500.0f) * invertXAxisMulti * (s32)((1.0f - Math_CosS(sControlInput->rel.stick_x * 200)) * -1500.0f) * invertXAxisMulti *
xAxisMulti; 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; 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; f32 movementSpeed = LINK_IS_ADULT ? 9.0f : 8.25f;
if (CVarGetInteger(CVAR_ENHANCEMENT("MMBunnyHood"), BUNNY_HOOD_VANILLA) != BUNNY_HOOD_VANILLA && if (CVarGetInteger(CVAR_ENHANCEMENT("MMBunnyHood"), BUNNY_HOOD_VANILLA) != BUNNY_HOOD_VANILLA &&
this->currentMask == PLAYER_MASK_BUNNY) { this->currentMask == PLAYER_MASK_BUNNY) {