Compare commits

..

66 Commits

Author SHA1 Message Date
Philip Dubé 0dcd52e5c7 Fix warp shuffle without warp hint text (#6551)
Issue was needing to hook on RSK_SHUFFLE_WARP_SONGS, but cleaned up code
2026-05-27 18:28:43 +00:00
Chris e5ad4e6f11 Fix ending audio shuffle (#6608) 2026-05-27 15:58:17 +00:00
Unreference e3ee258a92 fix(actors): Restore vanilla default for Kokiri Forest quest state hook (#6614) 2026-05-27 14:02:32 +00:00
Chris b728e671bf Fix blank text on carpet salesman (#6605) 2026-05-25 03:30:31 +00:00
Chris 37db034815 Use singular on message for 1 token (#6567) 2026-05-09 22:25:09 +00:00
Pepper0ni 5b4d8edf51 fix bad merge with the suns song fairy in spirit (#6590) 2026-05-04 02:02:21 +00:00
Jameriquiah 1a46d2ec96 hyrule field typo fix (#6574) 2026-04-29 09:37:20 -07:00
Pepper0ni ea76550fc7 Fix owl talk logic to include kokiri (#6564) 2026-04-25 15:30:27 +00:00
Philip Dubé ccfa31a245 Fix drag&drop not updating excluded locations (#6559) 2026-04-23 22:48:12 +00:00
Philip Dubé 806398a65b Fix OGC great fairy reward in vanilla with skip misc interactions when fish is not obtainable (#6556)
Item_CheckObtainability should only be called with MOD_NONE GI

For RG_DOUBLE_DEFENSE that became ITEM_FISH. Nonsense ensued

To reproduce issue, create debug save & go straight to OGC great fairy with only magic/ocarina/lullaby
2026-04-23 12:34:26 +00:00
Pepper0ni 3221d8a988 Fix bean fairies + start with beans generation (#6548) 2026-04-21 23:04:37 +00:00
Philip Dubé 69681e608f Fix swimvoid in grottos to just respawn in grotto (#6529) 2026-04-21 16:02:33 +00:00
Philip Dubé 719bb87eda add hint text for sun song fairies in spirit temple (#6544) 2026-04-21 15:30:33 +00:00
Reppan bf37645d72 Fix mirror shield color editor (#6542)
Oversight on my end that customequipment.cpp unloaded and reloaded assets abit to aggressive.

Adds a guard to make sure to only unload if custom asset is used and to not unload then reload vanilla asset.

Have tested it with fados customequipment and confirmed that mirrorshield is working as intended.
2026-04-20 17:18:08 +00:00
Philip Dubé 3b65eaa4ef Fix cutscene skips causing credits to spawn player in Lake Hylia (#6534)
SkipBlueWarp was intercepting credits. Disable during GAMEMODE_END_CREDITS
2026-04-19 20:50:11 +00:00
Chris 3be7eff02c Arrow cycle should check for sufficient magic (#6532) 2026-04-19 15:04:13 +00:00
Philip Dubé 39dcc0a73c Triforce Hunt: drain queue before credits (#6519) 2026-04-18 21:21:29 +00:00
Philip Dubé 256ab01630 Fishing: don't say default 6/7 when minimum reduced by enhancement (#6523) 2026-04-17 04:31:02 +00:00
Philip Dubé 072838613a Fix kak bazaar items having articles (#6522)
IsShop didn't include SCENE_TEST01, but that's used as placeholder for kak bazaar vs market bazaar
2026-04-17 03:53:24 +00:00
Chris 9c321862ca Fix Rate Limited Success Chime (#6512) 2026-04-17 01:30:28 +00:00
A Green Spoon 412b60a02f Fix Duplicate Reticles in Anchor (#6520) 2026-04-16 23:50:55 +00:00
Philip Dubé 1876435e98 Fix skulltula hints ending text too soon (#6516) 2026-04-16 15:59:14 +00:00
Philip Dubé cb71e22a79 9.2.3 Ackbar Delta (#6503) 2026-04-13 23:48:22 -07:00
Philip Dubé feb489cab0 Fix shopsanity soft lock (#6502)
GetForCurrentLanguage() defaults to MF_FORMATTED, appending MESSAGE_END (\x02) to output string

Revert using hint name for items in shops. Other merchants return GetHintMessage directly
2026-04-14 06:41:37 +00:00
Philip Dubé adb1e46ba9 fix bad args handling in extractor (#6501) 2026-04-14 01:00:16 +00:00
Philip Dubé 13ebc31ec7 9.2.2 Ackbar Charlie (#6494) 2026-04-13 14:12:37 +00:00
Jerom Venneker ca38dba192 Add missing Hint clarities back (#6477) 2026-04-13 03:39:30 +00:00
Philip Dubé 25eb09180d Revise Arrow Cycling (#6490)
1. simplify UI, flashing buttons are unnecessary
2. change arrow without drawing a new arrow
2026-04-12 23:03:46 +00:00
Philip Dubé d855742c2f Avoid over-allocating with texture interpolation (#6489)
Also remove interpolation with all zeroes & fix one interpolation direction
2026-04-12 18:51:04 +00:00
Philip Dubé 8513fd8800 fix cosmetic/audio randomizing options being seeded when they shouldn't be (#6481) 2026-04-12 18:25:05 +00:00
Philip Dubé 568813a2a4 Remove tooltip text referencing Let It Snow chests (#6488)
Was not preserved in chest revamp
2026-04-12 18:24:53 +00:00
Philip Dubé 7b7b799fad Make Lake Hylia water level switch in rando 10x faster (#6492)
35 seconds was an eternity
2026-04-12 18:24:14 +00:00
Philip Dubé 88099e6ebe Fix generation failure for Ganon's Castle MQ Fire Trial (#6487) 2026-04-12 13:27:06 +00:00
Pepper0ni 69ef35d258 Fix malon text and make the DMC jump unintuitive (#6479) 2026-04-10 21:45:23 +00:00
Jordan Longstaff 4122d8079e Faster bean planting (#6473) 2026-04-08 22:54:04 +00:00
Philip Dubé 40425c8c28 Fix seeded warp traps to be deterministic (#6469) 2026-04-07 16:31:41 +00:00
Chris 695c05d339 Fix cooldown behavior for crystal and light switches, water gate (#6461) 2026-04-07 01:09:07 +00:00
A Green Spoon 77f2883510 use category adj for gi (#6466) 2026-04-07 01:08:32 +00:00
Malkierian 876d512a4b Fix Skip Planting Beans crash (#6463)
Move code to set beans as planted after getting the bean pack to rando item give handler.
2026-04-07 01:07:20 +00:00
OtherBlue 978a219213 add check for market balcony camera (#6464) 2026-04-06 04:04:58 +00:00
Malkierian b6f227961b Restore sub-100% values for speed modifier slider. (#6465) 2026-04-06 03:47:17 +00:00
Chris 14b464bab2 Bean guy text formatting and rando fixes (#6444) 2026-03-30 01:33:16 +00:00
Pepper0ni efc4086c6a Some Logic fixes (#6445)
- Visible Collision added to more Skulls in Crates for consistency with similar situations
- Visible Collision added to chests in crates in MQ fire and for more ways to hit the switch in MQ fire Lizalfos maze
- Allow any jumpslash to break the pot in red ice in ice cavern with visible collision.
- Add Visible Collision as a way to get the gold skull token in the rubble in 4F Gibdo room in MQ shadow
- Turns out, while you take fall damage if you backflip onto a building from GF above jail, the floor is perfectly fine.
- Granny's shop forgot to check the price of the item being sold for wallets.
2026-03-30 01:32:31 +00:00
Pepper0ni 972ed22167 reset possible ice trap models before populating item pool (#6442) 2026-03-29 18:05:56 +00:00
Chris 5494a81eb1 Fix market sneak hook (#6440) 2026-03-29 16:46:47 +00:00
Chris ebea14f297 Fix displayed token count (#6441) 2026-03-29 16:45:48 +00:00
Chris 4729eef7c8 Make item category adjustments more consistent (#6434) 2026-03-29 07:34:59 +00:00
chartergirl64 0b3ebd584d [bug fix] Changes item tracker capacity to recognize progressive chu bag limits (#6436) 2026-03-29 07:32:40 +00:00
Chris 1be7533675 Conslidate ganon2 hooks (#6432) 2026-03-28 17:52:55 +00:00
Philip Dubé d1b7edfa7c 9.2.1 Ackbar Bravo (#6424) 2026-03-28 04:15:06 +00:00
Malkierian 6fea1821a1 Changed name of scarecrow's song enhancement and added to the description to clarify that it only skips song playback without the rando setting. (#6423) 2026-03-28 03:43:55 +00:00
Christopher Leggett 42ba25449e Make Infinite Ammo Cheat respect Progressive Bombchu Bag Capacities (#6421)
Cap infinite ammo for chus according to rando settings
Add bombchuUpgradeLevel to save editor
2026-03-28 02:58:41 +00:00
xxAtrain223 d1643aa196 LACS-Dungeons Check Availability (#6202)
* Fixes #5433, LACS-Dungeons beatable vs beat.

* Use CalculatingAvailableChecks in Logic::DungeonCount to check flags.
2026-03-27 19:40:34 -07:00
Philip Dubé 7edf44e170 add tts for Roc's Feather (#6418) 2026-03-27 02:07:11 +00:00
Pepper0ni 53dc7f43ba Fix DMC oversight (#6419) 2026-03-27 01:43:36 +00:00
Pepper0ni 5f139ef311 Hookshot ladder fixes (#6417) 2026-03-26 22:39:17 +00:00
Ali 99c1f23d5b fix: Fix macOS crashing on first install use (#6412)
With the new imgui OTR generation flow, macOS would crash when Ship
was run for the first time unless com.shipofharkinian.soh had already been created.
Move the call to CheckAndCreateModFolder() earlier in execution to prevent crashing.
2026-03-26 15:12:51 +00:00
Christopher Leggett 69e03dcc52 LUS bump to fix texture pack performance (#6416) 2026-03-26 15:11:22 +00:00
Christopher Leggett 4aa6e2ec28 Move retrieval of dbEntry after Actor_Destroy (#6410)
Before I had the change back to the placeholder actor id the Dummy
Player actors were spawned with, but since we grabbed the actorDB
entry before Actor_Destroy was called it didn't matter. Move it
and the requisite log statement to after Actor_Destroy.
2026-03-26 14:18:01 +00:00
Pepper0ni 94b650ec67 Fix several logic errors, create Hooskshot Bridge trick. (#6414) 2026-03-26 03:02:54 +00:00
Pepper0ni 57269c8e46 fix volv FTR logic (#6403) 2026-03-24 20:23:20 +00:00
Christopher Leggett 5e13570b83 Fix leak of shuffled fairy bottle-swipe behavior to other actors (#6405) 2026-03-24 19:37:21 +00:00
Christopher Leggett 5fe4680a20 Adds a more compact Clear button to Check Tracker search (#6401) 2026-03-23 20:07:45 +00:00
Philip Dubé c439d62137 Enemy rando: don't mutate ActorEntry (#6400) 2026-03-23 16:27:20 +00:00
A Green Spoon 2cfb0f73dc make non-cmc pot model major for shuffle clarity (#6398) 2026-03-23 15:46:03 +00:00
Pepper0ni c61c1c0fec fix oversight in well logic (#6397) 2026-03-23 15:45:48 +00:00
88 changed files with 713 additions and 688 deletions
+1 -1
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")
project(Ship VERSION 9.2.0 LANGUAGES C CXX)
project(Ship VERSION 9.2.3 LANGUAGES C CXX)
include(CMake/soh-cvars.cmake)
include(CMake/lus-cvars.cmake)
set(SPDLOG_LEVEL_TRACE 0)
@@ -172,6 +172,7 @@
"153": "STICK UPGRADE 30",
"154": "NUT UPGRADE 30",
"155": "NUT UPGRADE 40",
"157": "Roc's Feather",
"255": "",
"256": "Haunted Wasteland",
"257": "Gerudos Fortress",
@@ -172,6 +172,7 @@
"153": "AMÉLIORATION BÂTON MOJO 30",
"154": "AMÉLIORATION NOIX MOJO 30",
"155": "AMÉLIORATION NOIX MOJO 40",
"157": "Plume de Roc",
"255": "",
"256": "Désert Hanté",
"257": "Forteresse Gerudo",
@@ -172,6 +172,7 @@
"153": "DEKU-STAB-KAPAZITÄT 30",
"154": "DEKU-NUẞ-KAPAZITÄT 30",
"155": "DEKU-NUẞ-KAPAZITÄT 40",
"157": "Greifenfeder",
"255": "",
"256": "Gespensterwüste",
"257": "Gerudo-Festung",
+44 -125
View File
@@ -8,8 +8,7 @@ extern "C" {
#include "overlays/actors/ovl_En_Arrow/z_en_arrow.h"
s32 func_808351D4(Player* thisx, PlayState* play); // Arrow nocked
s32 func_808353D8(Player* thisx, PlayState* play); // Aiming in first person
void Player_InitItemAction(PlayState* play, Player* thisx, PlayerItemAction itemAction);
void EnArrow_Init(Actor* thisx, PlayState* play);
extern PlayState* gPlayState;
}
@@ -20,16 +19,6 @@ extern PlayState* gPlayState;
static const s16 sMagicArrowCosts[] = { 4, 4, 8 };
#define MINIGAME_STATUS_ACTIVE 1
static const s16 BUTTON_FLASH_DURATION = 3;
static const s16 BUTTON_FLASH_COUNT = 3;
static const s16 BUTTON_HIGHLIGHT_ALPHA = 128;
static s16 sButtonFlashTimer = 0;
static s16 sButtonFlashCount = 0;
static s16 sJustCycledFrames = 0;
static const PlayerItemAction sArrowCycleOrder[] = {
PLAYER_IA_BOW,
PLAYER_IA_BOW_FIRE,
@@ -54,11 +43,11 @@ static bool HasArrowType(PlayerItemAction itemAction) {
case PLAYER_IA_BOW:
return true;
case PLAYER_IA_BOW_FIRE:
return (INV_CONTENT(ITEM_ARROW_FIRE) == ITEM_ARROW_FIRE);
return INV_CONTENT(ITEM_ARROW_FIRE) == ITEM_ARROW_FIRE && gSaveContext.magic >= sMagicArrowCosts[0];
case PLAYER_IA_BOW_ICE:
return (INV_CONTENT(ITEM_ARROW_ICE) == ITEM_ARROW_ICE);
return INV_CONTENT(ITEM_ARROW_ICE) == ITEM_ARROW_ICE && gSaveContext.magic >= sMagicArrowCosts[1];
case PLAYER_IA_BOW_LIGHT:
return (INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT);
return INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT && gSaveContext.magic >= sMagicArrowCosts[2];
default:
return false;
}
@@ -77,15 +66,24 @@ static s32 GetBowItemForArrow(PlayerItemAction itemAction) {
}
}
static ArrowType GetArrowTypeForArrow(s8 itemAction) {
switch (itemAction) {
case PLAYER_IA_BOW_FIRE:
return ARROW_FIRE;
case PLAYER_IA_BOW_ICE:
return ARROW_ICE;
case PLAYER_IA_BOW_LIGHT:
return ARROW_LIGHT;
default:
return ARROW_NORMAL;
}
}
static bool CanCycleArrows() {
Player* player = GET_PLAYER(gPlayState);
// don't allow cycling during minigames
if (gSaveContext.minigameState == MINIGAME_STATUS_ACTIVE) {
return false;
}
return !(player->stateFlags1 & PLAYER_STATE1_ON_HORSE) && player->rideActor == NULL &&
return LINK_IS_ADULT && !gSaveContext.minigameState && gPlayState->sceneNum != SCENE_SHOOTING_GALLERY &&
!(player->stateFlags1 & PLAYER_STATE1_ON_HORSE) && player->rideActor == NULL &&
INV_CONTENT(SLOT_BOW) == ITEM_BOW &&
(INV_CONTENT(ITEM_ARROW_FIRE) == ITEM_ARROW_FIRE || INV_CONTENT(ITEM_ARROW_ICE) == ITEM_ARROW_ICE ||
INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT);
@@ -113,66 +111,6 @@ static s8 GetNextArrowType(s8 currentArrowType) {
static void UpdateButtonAlpha(s16 flashAlpha, bool isButtonBow, u16* buttonAlpha) {
if (isButtonBow) {
*buttonAlpha = flashAlpha;
if (sButtonFlashTimer == 0) {
*buttonAlpha = 255;
}
}
}
static void UpdateFlashEffect(PlayState* play) {
if (sButtonFlashTimer <= 0) {
return;
}
sButtonFlashTimer--;
s16 flashAlpha = (sButtonFlashTimer % 3) ? BUTTON_HIGHLIGHT_ALPHA : 255;
if (sButtonFlashTimer == 0 && sButtonFlashCount < BUTTON_FLASH_COUNT - 1) {
sButtonFlashTimer = BUTTON_FLASH_DURATION;
sButtonFlashCount++;
}
UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[1] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[1] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[1] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.cLeftAlpha);
UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[2] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[2] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[2] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.cDownAlpha);
UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[3] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[3] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[3] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.cRightAlpha);
if (CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0)) {
UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[4] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[4] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[4] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.dpadRightAlpha);
UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[5] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[5] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[5] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.dpadLeftAlpha);
UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[6] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[6] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[6] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.dpadDownAlpha);
UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[7] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[7] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[7] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.dpadUpAlpha);
}
}
@@ -193,72 +131,53 @@ static void UpdateEquippedBow(PlayState* play, s8 arrowType) {
}
gSaveContext.buttonStatus[i] = BTN_ENABLED;
sButtonFlashTimer = BUTTON_FLASH_DURATION;
sButtonFlashCount = 0;
}
}
UpdateFlashEffect(play);
}
static void CycleToNextArrow(PlayState* play, Player* player) {
s8 nextArrow = GetNextArrowType(player->heldItemAction);
if (player->heldActor != NULL && player->heldActor->id == ACTOR_EN_ARROW) {
EnArrow* arrow = (EnArrow*)player->heldActor;
if (arrow->actor.child != NULL) {
Actor_Kill(arrow->actor.child);
}
Actor_Kill(&arrow->actor);
}
Player_InitItemAction(play, player, (PlayerItemAction)nextArrow);
UpdateEquippedBow(play, nextArrow);
Audio_PlaySoundGeneral(NA_SE_PL_CHANGE_ARMS, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
sJustCycledFrames = 2;
}
void ArrowCycleMain() {
bool ArrowCycleMain() {
if (gPlayState == nullptr || !CanCycleArrows()) {
return;
return false;
}
if (sJustCycledFrames > 0) {
sJustCycledFrames--;
}
UpdateFlashEffect(gPlayState);
Player* player = GET_PLAYER(gPlayState);
Input* input = &gPlayState->state.input[0];
if (IsAimingBow(player) && CHECK_BTN_ANY(input->press.button, BTN_R)) {
if (player->heldActor != NULL && player->heldActor->id == ACTOR_EN_ARROW) {
if (IsHoldingMagicBow(player) && gSaveContext.magicState != MAGIC_STATE_IDLE && player->heldActor == NULL) {
Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
return;
return true;
}
// reset magic state to IDLE before cycling to prevent error sound
gSaveContext.magicState = MAGIC_STATE_IDLE;
CycleToNextArrow(gPlayState, player);
s8 nextArrow = GetNextArrowType(player->heldItemAction);
player->heldItemAction = nextArrow;
player->itemAction = nextArrow;
Actor* arrow = player->heldActor;
if (arrow->child != NULL) {
Actor_Kill(arrow->child);
arrow->child = NULL;
}
arrow->params = GetArrowTypeForArrow(nextArrow);
EnArrow_Init(arrow, gPlayState);
UpdateEquippedBow(gPlayState, nextArrow);
return true;
}
return false;
}
void RegisterArrowCycle() {
COND_ID_HOOK(OnActorUpdate, ACTOR_PLAYER, CVAR_ARROW_CYCLE_VALUE, [](void* actor) { ArrowCycleMain(); });
// suppress shield input when R is held while aiming to allow arrow cycling
COND_VB_SHOULD(VB_EXECUTE_PLAYER_ACTION_FUNC, CVAR_ARROW_CYCLE_VALUE, {
Player* player = (Player*)va_arg(args, void*);
Input* input = (Input*)va_arg(args, void*);
if ((IsAimingBow(player) || sJustCycledFrames > 0) && CHECK_BTN_ANY(input->cur.button, BTN_R)) {
input->cur.button &= ~BTN_R;
input->press.button &= ~BTN_R;
if (IsAimingBow(player) && CHECK_BTN_ANY(input->press.button, BTN_R)) {
if (ArrowCycleMain()) {
input->cur.button &= ~BTN_R;
input->press.button &= ~BTN_R;
}
}
});
@@ -270,9 +189,9 @@ void RegisterArrowCycle() {
if (gSaveContext.magic < sMagicArrowCosts[magicArrowType]) {
*arrowType = ARROW_NORMAL;
} else {
*should = false;
}
*should = false;
});
COND_VB_SHOULD(VB_EN_ARROW_MAGIC_CONSUMPTION, CVAR_ARROW_CYCLE_VALUE, {
@@ -1,5 +1,6 @@
#include <libultraship/bridge.h>
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/OTRGlobals.h"
#include "soh/ShipInit.hpp"
#include "z64save.h"
@@ -24,7 +25,11 @@ void OnGameFrameUpdateInfiniteAmmo() {
AMMO(ITEM_BOW) = CUR_CAPACITY(UPG_QUIVER);
AMMO(ITEM_SLINGSHOT) = CUR_CAPACITY(UPG_BULLET_BAG);
if (INV_CONTENT(ITEM_BOMBCHU) != ITEM_NONE) {
AMMO(ITEM_BOMBCHU) = 50;
int chuCapacity = 50;
if (IS_RANDO && RAND_GET_OPTION(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_PROGRESSIVE)) {
chuCapacity = OTRGlobals::Instance->gRandoContext->GetBombchuCapacity();
}
AMMO(ITEM_BOMBCHU) = chuCapacity;
}
}
@@ -5,12 +5,10 @@
#include "soh/Enhancements/enhancementTypes.h"
#include "soh/ObjectExtension/ObjectExtension.h"
#include "variables.h"
#include "soh/OTRGlobals.h"
#include "soh/cvar_prefixes.h"
#include "soh/ResourceManagerHelpers.h"
#include "soh/SohGui/MenuTypes.h"
#include "soh/SohGui/SohMenu.h"
#include "soh/SohGui/SohGui.hpp"
extern "C" {
#include <z64.h>
@@ -627,11 +625,20 @@ void RegisterEnemyRandomizer() {
ActorContext* actorCtx = va_arg(args, ActorContext*);
ActorEntry* actorEntry = va_arg(args, ActorEntry*);
PlayState* play = va_arg(args, PlayState*);
Actor* actor = va_arg(args, Actor*);
Actor** actor = va_arg(args, Actor**);
if (!GetRandomizedEnemy(play, &actorEntry->id, &actorEntry->pos.x, &actorEntry->pos.y, &actorEntry->pos.z,
&actorEntry->rot.x, &actorEntry->rot.y, &actorEntry->rot.z, &actorEntry->params)) {
*should = false;
s16 actorId = actorEntry->id;
s16 posX = actorEntry->pos.x;
s16 posY = actorEntry->pos.y;
s16 posZ = actorEntry->pos.z;
s16 rotX = actorEntry->rot.x;
s16 rotY = actorEntry->rot.y;
s16 rotZ = actorEntry->rot.z;
s16 params = actorEntry->params;
*should = false;
if (GetRandomizedEnemy(play, &actorId, &posX, &posY, &posZ, &rotX, &rotY, &rotZ, &params)) {
*actor = Actor_Spawn(actorCtx, play, actorId, posX, posY, posZ, rotX, rotY, rotZ, params);
}
});
+9 -26
View File
@@ -32,6 +32,7 @@ typedef enum {
static AltTrapType roll = ADD_TRAP_MAX;
static int statusTimer = -1;
static int eventTimer = -1;
static EntranceIndex teleportRoll = ENTR_MAX;
const char* altTrapTypeCvars[] = {
CVAR_ENHANCEMENT("ExtraTraps.Ice"), CVAR_ENHANCEMENT("ExtraTraps.Burn"),
@@ -41,6 +42,12 @@ const char* altTrapTypeCvars[] = {
CVAR_ENHANCEMENT("ExtraTraps.Kill"), CVAR_ENHANCEMENT("ExtraTraps.Teleport"),
};
const std::array<EntranceIndex, 7> teleportDestinations = {
ENTR_LINKS_HOUSE_CHILD_SPAWN, ENTR_SACRED_FOREST_MEADOW_WARP_PAD, ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD,
ENTR_LAKE_HYLIA_WARP_PAD, ENTR_DESERT_COLOSSUS_WARP_PAD, ENTR_GRAVEYARD_WARP_PAD,
ENTR_TEMPLE_OF_TIME_WARP_PAD,
};
std::vector<AltTrapType> getEnabledAddTraps() {
std::vector<AltTrapType> enabledAddTraps;
for (int i = 0; i < ADD_TRAP_MAX; i++) {
@@ -102,6 +109,7 @@ static void RollRandomTrap(uint64_t seed) {
break;
case ADD_TELEPORT_TRAP:
eventTimer = 3;
teleportRoll = ShipUtils::RandomElement(teleportDestinations, &state);
break;
default:
break;
@@ -135,32 +143,7 @@ static void OnPlayerUpdate() {
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
break;
case ADD_TELEPORT_TRAP: {
int entrance;
int index = Random(0, 7);
switch (index) {
case 0:
entrance = GI_TP_DEST_SERENADE;
break;
case 1:
entrance = GI_TP_DEST_REQUIEM;
break;
case 2:
entrance = GI_TP_DEST_BOLERO;
break;
case 3:
entrance = GI_TP_DEST_MINUET;
break;
case 4:
entrance = GI_TP_DEST_NOCTURNE;
break;
case 5:
entrance = GI_TP_DEST_PRELUDE;
break;
default:
entrance = GI_TP_DEST_LINKSHOUSE;
break;
}
GameInteractor::RawAction::TeleportPlayer(entrance);
GameInteractor::RawAction::TeleportPlayer(teleportRoll);
break;
}
default:
+21
View File
@@ -0,0 +1,21 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include <variables.h>
f32 Fishing_GetMinimumRequiredScore();
}
void BuildFishingMessage(uint16_t* textId, bool* loadFromMessageTable) {
if (gSaveContext.minigameScore == 0) {
gSaveContext.minigameScore = Fishing_GetMinimumRequiredScore();
}
}
void RegisterFishingMessages() {
COND_ID_HOOK(OnOpenText, 0x40AE, CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0), BuildFishingMessage);
COND_ID_HOOK(OnOpenText, 0x4080, CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0), BuildFishingMessage);
}
static RegisterShipInitFunc initFunc(RegisterFishingMessages, { CVAR_ENHANCEMENT("CustomizeFishing") });
@@ -17,10 +17,10 @@ static void RegisterBossDefeatTimestamps() {
BOSS_DEFEAT_TIMESTAMP(ACTOR_BOSS_SST, TIMESTAMP_DEFEAT_BONGO_BONGO);
BOSS_DEFEAT_TIMESTAMP(ACTOR_BOSS_TW, TIMESTAMP_DEFEAT_TWINROVA);
BOSS_DEFEAT_TIMESTAMP(ACTOR_BOSS_GANON, TIMESTAMP_DEFEAT_GANONDORF);
BOSS_DEFEAT_TIMESTAMP(ACTOR_BOSS_GANON2, TIMESTAMP_DEFEAT_GANON);
COND_ID_HOOK(OnBossDefeat, ACTOR_BOSS_GANON2, true,
[](void* refActor) { gSaveContext.ship.stats.gameComplete = true; });
COND_ID_HOOK(OnBossDefeat, ACTOR_BOSS_GANON2, true, [](void* refActor) {
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
gSaveContext.ship.stats.gameComplete = true;
});
}
static RegisterShipInitFunc initFunc(RegisterBossDefeatTimestamps);
@@ -84,7 +84,8 @@ static void DisableFixedCamera_RestoreAllCameraData() {
// Helper to check if a camera type is a fixed camera
static bool IsFixedCameraType(s16 type) {
return type == CAM_SET_PREREND_FIXED || type == CAM_SET_PREREND_PIVOT || type == CAM_SET_PIVOT_FROM_SIDE;
return type == CAM_SET_PREREND_FIXED || type == CAM_SET_PREREND_PIVOT || type == CAM_SET_PIVOT_FROM_SIDE ||
type == CAM_SET_MARKET_BALCONY;
}
static void RegisterDisableFixedCamera() {
+7 -7
View File
@@ -6,11 +6,11 @@ extern "C" {
void BuildSkulltulaMessage(uint16_t* textId, bool* loadFromMessageTable) {
CustomMessage msg =
CustomMessage("You got a %rGold Skulltula Token%w!&You've collected %r[[gsCount]]%w tokens&in total!",
"Ein %rGoldenes Skulltula-Symbol%w!&Du hast nun insgesamt %r[[gsCount]]&%wGoldene "
"Skulltula-Symbole&gesammelt!",
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r[[gsCount]]%w symboles en "
"tout!",
CustomMessage("You got a %rGold Skulltula Token%w!&You've collected %r[[d]]%w |token|tokens|&in total!",
"Ein %rGoldenes Skulltula-Symbol%w!&Du hast nun insgesamt %r[[d]]&%w|Goldenes "
"Skulltula-Symbol|Goldene Skulltula-Symbole|&gesammelt!",
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r[[d]]%w |symbole|symboles| "
"en tout!",
TEXTBOX_TYPE_BLUE);
// The freeze text cannot be manually dismissed and must be auto-dismissed.
// This is fine and even wanted when skull tokens are not shuffled, but when
@@ -24,8 +24,8 @@ void BuildSkulltulaMessage(uint16_t* textId, bool* loadFromMessageTable) {
// Auto dismiss textbox after 0x3C (60) frames (about 3 seconds for OoT)
msg = msg + "\x0E\x3C";
}
int16_t gsCount = gSaveContext.inventory.gsTokens + (IS_RANDO ? 1 : 0);
msg.Replace("[[gsCount]]", std::to_string(gsCount));
int16_t gsCount = gSaveContext.inventory.gsTokens;
msg.InsertNumber(gsCount);
msg.AutoFormat(ITEM_SKULL_TOKEN);
msg.LoadIntoFont();
*loadFromMessageTable = false;
@@ -36,7 +36,7 @@ void RegisterAdditionalReticles() {
bool shouldRegister = CVAR_BOW_RETICLE_VALUE || CVAR_BOOMERANG_RETICLE_VALUE;
COND_VB_SHOULD(VB_DRAW_ADDITIONAL_RETICLES, shouldRegister, {
Player* player = GET_PLAYER(gPlayState);
Player* player = va_arg(args, Player*);
Actor* heldActor = player->heldActor;
if (CVAR_BOW_RETICLE_VALUE &&
((player->heldItemAction >= PLAYER_IA_BOW && player->heldItemAction <= PLAYER_IA_BOW_LIGHT) ||
@@ -2,11 +2,17 @@
extern "C" {
#include <variables.h>
extern PlayState* gPlayState;
}
// RANDOTODO: Port the rest of the behavior for this enhancement here.
void BuildNightGuardMessage(uint16_t* textId, bool* loadFromMessageTable) {
// Other guards should not have their text overridden
if (gPlayState->sceneNum != SCENE_MARKET_ENTRANCE_NIGHT) {
return;
}
CustomMessage msg = CustomMessage("You look bored. Wanna go out for a walk?\x1B%gYes&No%w",
"Du siehst gelangweilt aus. Willst Du einen Spaziergang machen?\x1B%gJa&Nein%w",
"Tu as l'air de t'ennuyer. Tu veux aller faire un tour?\x1B%gOui&Non%w");
@@ -86,8 +86,7 @@ void RegisterShouldPlayBlueWarp() {
* should also account for the difference between your first and following visits to the blue warp.
*/
REGISTER_VB_SHOULD(VB_PLAY_TRANSITION_CS, {
// Do nothing when in a boss rush
if (IS_BOSS_RUSH) {
if (IS_BOSS_RUSH || gSaveContext.gameMode == GAMEMODE_END_CREDITS) {
return;
}
@@ -1,42 +0,0 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include "src/overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.h"
#include "src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.h"
#include "src/overlays/actors/ovl_Bg_Jya_Bombchuiwa/z_bg_jya_bombchuiwa.h"
extern PlayState* gPlayState;
}
#define SKIP_MISC_INTERACTIONS_NAME CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions")
#define SKIP_MISC_INTERACTIONS_VALUE CVarGetInteger(SKIP_MISC_INTERACTIONS_NAME, IS_RANDO)
static void RegisterSkipTimerDelay() {
// Skip Water Temple gate delay
COND_ID_HOOK(OnActorUpdate, ACTOR_BG_SPOT06_OBJECTS, SKIP_MISC_INTERACTIONS_VALUE, [](void* actor) {
auto spot06 = static_cast<BgSpot06Objects*>(actor);
if (spot06->dyna.actor.params == 0) {
spot06->timer = 0;
}
});
// Skip Spirit Sun on Floor activation delay
COND_ID_HOOK(OnActorUpdate, ACTOR_BG_JYA_BOMBCHUIWA, SKIP_MISC_INTERACTIONS_VALUE, [](void* actor) {
auto jya = static_cast<BgJyaBombchuiwa*>(actor);
if (!(jya->drawFlags & 4) && jya->timer > 0 && jya->timer < 9) {
jya->timer = 9;
}
});
// Skip Spirit Sun on Floor & Sun on Block activation delay
COND_ID_HOOK(OnActorUpdate, ACTOR_OBJ_LIGHTSWITCH, SKIP_MISC_INTERACTIONS_VALUE, [](void* actor) {
if (gPlayState->sceneNum == SCENE_SPIRIT_TEMPLE &&
(gPlayState->roomCtx.curRoom.num == 4 || gPlayState->roomCtx.curRoom.num == 8)) {
auto sun = static_cast<ObjLightswitch*>(actor);
sun->toggleDelay = 0;
}
});
}
static RegisterShipInitFunc initFunc_SkipTimerDelay(RegisterSkipTimerDelay,
{ SKIP_MISC_INTERACTIONS_NAME, "IS_RANDO" });
@@ -0,0 +1,18 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include "src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.h"
extern SaveContext gSaveContext;
}
static void RegisterSpot06GateSkip() {
COND_VB_SHOULD(VB_BG_SPOT06_OBJECTS_GATE_SKIP,
CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO), {
BgSpot06Objects* actor = va_arg(args, BgSpot06Objects*);
actor->timer = 0;
*should = false;
});
}
static RegisterShipInitFunc initFunc(RegisterSpot06GateSkip, { CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint") });
+14 -7
View File
@@ -53,6 +53,7 @@ extern std::shared_ptr<SohMenu> mSohMenu;
#define SEQ_COUNT_INSTRUMENT 6
#define SEQ_COUNT_SFX 57
#define SEQ_COUNT_VOICE 108
#define SEQ_COUNT_ENDING 5
size_t AuthenticCountBySequenceType(SeqType type) {
switch (type) {
@@ -74,6 +75,8 @@ size_t AuthenticCountBySequenceType(SeqType type) {
return SEQ_COUNT_INSTRUMENT;
case SEQ_VOICE:
return SEQ_COUNT_VOICE;
case SEQ_ENDING:
return SEQ_COUNT_ENDING;
default:
return 0;
}
@@ -109,19 +112,22 @@ void UpdateCurrentBGM(u16 seqKey, SeqType seqType) {
}
}
static uint64_t seeded_audio_state = 0;
void RandomizeGroup(SeqType type, bool manual = true) {
std::vector<u16> values;
uint64_t localRngState = 0;
uint64_t* shuffleState = nullptr;
if (!manual) {
if (CVarGetInteger(CVAR_AUDIO("RandomizeAudioGenModes"), 0) == RANDOMIZE_ON_FILE_LOAD_SEEDED ||
CVarGetInteger(CVAR_AUDIO("RandomizeAudioGenModes"), 0) == RANDOMIZE_ON_RANDO_GEN_ONLY) {
int randomizeMode = CVarGetInteger(CVAR_AUDIO("RandomizeAudioGenModes"), 0);
if (randomizeMode == RANDOMIZE_ON_FILE_LOAD_SEEDED || randomizeMode == RANDOMIZE_ON_RANDO_GEN_ONLY) {
uint32_t finalSeed = type + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed()
: static_cast<uint32_t>(gSaveContext.ship.stats.fileCreatedAt));
ShipUtils::RandInit(finalSeed, &seeded_audio_state);
ShipUtils::RandInit(finalSeed, &localRngState);
shuffleState = &localRngState;
}
// For RANDOMIZE_ON_NEW_SCENE, shuffleState remains nullptr, which uses the global RNG
}
// An empty IncludedSequences set means that the AudioEditor window has never been drawn
@@ -141,7 +147,7 @@ void RandomizeGroup(SeqType type, bool manual = true) {
if (!values.size())
return;
}
ShipUtils::Shuffle(values, &seeded_audio_state);
ShipUtils::Shuffle(values, shuffleState);
for (const auto& [seqId, seqData] : AudioCollection::Instance->GetAllSequences()) {
const std::string cvarKey = AudioCollection::Instance->GetCvarKey(seqData.sfxKey);
const std::string cvarLockKey = AudioCollection::Instance->GetCvarLockKey(seqData.sfxKey);
@@ -803,7 +809,8 @@ void AudioEditor::DrawElement() {
}
std::vector<SeqType> allTypes = {
SEQ_BGM_WORLD, SEQ_BGM_EVENT, SEQ_BGM_BATTLE, SEQ_OCARINA, SEQ_FANFARE, SEQ_INSTRUMENT, SEQ_SFX, SEQ_VOICE,
SEQ_BGM_WORLD, SEQ_BGM_EVENT, SEQ_BGM_BATTLE, SEQ_OCARINA, SEQ_FANFARE,
SEQ_INSTRUMENT, SEQ_SFX, SEQ_VOICE, SEQ_ENDING,
};
void AudioEditor_RandomizeAll() {
@@ -1889,11 +1889,11 @@ void DrawSillyTab() {
UIWidgets::Separator(true, true, 2.0f, 2.0f);
UIWidgets::CVarCheckbox("Let It Snow", CVAR_GENERAL("LetItSnow"),
UIWidgets::CheckboxOptions()
.Color(THEME_COLOR)
.Tooltip("Makes snow fall, changes chest texture colors to red and green, etc, for "
"December holidays.\nWill reset on restart outside of December 23-25."));
UIWidgets::CVarCheckbox(
"Let It Snow", CVAR_GENERAL("LetItSnow"),
UIWidgets::CheckboxOptions()
.Color(THEME_COLOR)
.Tooltip("Makes snow fall for December holidays.\nWill reset on restart outside of December 23-25."));
UIWidgets::Separator(true, true, 2.0f, 2.0f);
@@ -2104,24 +2104,28 @@ void ApplySideEffects(CosmeticOption& cosmeticOption) {
}
}
static uint64_t seeded_cosmetics_state = 0;
void RandomizeColor(CosmeticOption& cosmeticOption, bool manual = true) {
ImVec4 randomColor;
if (!manual && CVarGetInteger(CVAR_COSMETIC("RandomizeCosmeticsGenModes"), 0) == RANDOMIZE_ON_FILE_LOAD_SEEDED ||
!manual && CVarGetInteger(CVAR_COSMETIC("RandomizeCosmeticsGenModes"), 0) == RANDOMIZE_ON_RANDO_GEN_ONLY) {
uint64_t local_seed_state = 0;
uint64_t* randomState = nullptr;
uint32_t finalSeed = cosmeticOption.defaultColor.r + cosmeticOption.defaultColor.g +
cosmeticOption.defaultColor.b + cosmeticOption.defaultColor.a +
(IS_RANDO ? Rando::Context::GetInstance()->GetSeed()
: static_cast<uint32_t>(gSaveContext.ship.stats.fileCreatedAt));
if (!manual) {
int randomizeMode = CVarGetInteger(CVAR_COSMETIC("RandomizeCosmeticsGenModes"), 0);
if (randomizeMode == RANDOMIZE_ON_FILE_LOAD_SEEDED || randomizeMode == RANDOMIZE_ON_RANDO_GEN_ONLY) {
randomColor = GetRandomValue(finalSeed, &seeded_cosmetics_state);
} else {
randomColor = GetRandomValue();
uint32_t finalSeed = cosmeticOption.defaultColor.r + cosmeticOption.defaultColor.g +
cosmeticOption.defaultColor.b + cosmeticOption.defaultColor.a +
(IS_RANDO ? Rando::Context::GetInstance()->GetSeed()
: static_cast<uint32_t>(gSaveContext.ship.stats.fileCreatedAt));
randomState = &local_seed_state;
ShipUtils::RandInit(finalSeed, randomState);
}
// For RANDOMIZE_ON_NEW_SCENE, randomState remains nullptr, which uses the global RNG
}
randomColor = GetRandomValue(randomState);
Color_RGBA8 newColor;
newColor.r = static_cast<uint8_t>(randomColor.x * 255.0f);
newColor.g = static_cast<uint8_t>(randomColor.y * 255.0f);
@@ -2703,6 +2707,7 @@ void RegisterCosmeticHooks() {
[](s16 sceneNum) { CosmeticsEditor_AutoRandomizeAll(); });
COND_HOOK(OnGameFrameUpdate, true, CosmeticsUpdateTick);
COND_HOOK(OnAssetAltChange, true, []() { ApplyOrResetCustomGfxPatches(true); });
}
void RegisterCosmeticWidgets() {
+12 -2
View File
@@ -121,6 +121,8 @@ static bool IsDummyPlayer(const Player* player) {
return player != nullptr && player->actor.update == DummyPlayer_Update;
}
static bool sPrevAltAssetsEnabled = false;
void PatchOrUnpatch(const char* resource, const char* gfx, const char* dlist1, const char* dlist2, const char* dlist3,
const char* alternateDL) {
if (resource == NULL || gfx == NULL || dlist1 == NULL || dlist2 == NULL) {
@@ -128,6 +130,7 @@ void PatchOrUnpatch(const char* resource, const char* gfx, const char* dlist1, c
}
const bool altAssetsRuntime = ResourceMgr_IsAltAssetsEnabled();
const bool altAssetsChanged = (altAssetsRuntime != sPrevAltAssetsEnabled);
if (!altAssetsRuntime) {
// Alt assets are off; ensure any prior patches using these names are reverted.
@@ -136,11 +139,16 @@ void PatchOrUnpatch(const char* resource, const char* gfx, const char* dlist1, c
if (dlist3 != NULL) {
ResourceMgr_UnpatchGfxByName(resource, dlist3);
}
// Drop any cached version of the resource so it reloads clean (unpatched) next use.
ResourceMgr_UnloadResource(resource);
if (altAssetsChanged) {
ResourceMgr_UnloadResource(resource);
}
return;
}
if (altAssetsChanged) {
ResourceMgr_UnloadResource(resource);
}
if (!ResourceGetIsCustomByName(gfx)) {
return;
}
@@ -510,4 +518,6 @@ void UpdatePatchCustomEquipmentDlists() {
}
ApplyCommonEquipmentPatches();
sPrevAltAssetsEnabled = ResourceMgr_IsAltAssetsEnabled();
}
@@ -1,4 +1,5 @@
#include "debugSaveEditor.h"
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "soh/util.h"
#include "soh/SohGui/ImGuiUtils.h"
#include "soh/OTRGlobals.h"
@@ -1392,6 +1393,39 @@ void DrawEquipmentTab() {
"40",
};
DrawUpgrade("Deku Nut Capacity", UPG_NUTS, nutNames);
if (IS_RANDO &&
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_BOMBCHU_BAG) == RO_BOMBCHU_BAG_PROGRESSIVE) {
const std::vector<std::string> bombchuNames = {
"None",
"20",
"30",
"50",
};
ImGui::Text("%s", "Bombchu Bag Capacity");
ImGui::SameLine();
ImGui::PushID("Bombchu Bag Capacity");
PushStyleCombobox(THEME_COLOR);
ImGui::AlignTextToFramePadding();
auto value = gSaveContext.ship.quest.data.randomizer.bombchuUpgradeLevel;
auto name = value < bombchuNames.size() ? bombchuNames[value].c_str() : "Glitched";
if (ImGui::BeginCombo("##upgrade", name)) {
for (size_t i = 0; i < bombchuNames.size(); i++) {
if (ImGui::Selectable(bombchuNames[i].c_str())) {
gSaveContext.ship.quest.data.randomizer.bombchuUpgradeLevel = i;
if (i > 0) {
INV_CONTENT(ITEM_BOMBCHU) = ITEM_BOMBCHU;
} else {
INV_CONTENT(ITEM_BOMBCHU) = ITEM_NONE;
}
}
}
ImGui::EndCombo();
}
PopStyleCombobox();
ImGui::PopID();
UIWidgets::Tooltip("Bombchu Bag Capapcity");
}
}
// Draws a toggleable icon for a quest item that is faded when disabled
@@ -52,16 +52,6 @@ typedef enum {
/* 0x08 */ GI_COLOR_BLACK,
} GIColors;
typedef enum {
/* */ GI_TP_DEST_LINKSHOUSE = ENTR_LINKS_HOUSE_CHILD_SPAWN,
/* */ GI_TP_DEST_MINUET = ENTR_SACRED_FOREST_MEADOW_WARP_PAD,
/* */ GI_TP_DEST_BOLERO = ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD,
/* */ GI_TP_DEST_SERENADE = ENTR_LAKE_HYLIA_WARP_PAD,
/* */ GI_TP_DEST_REQUIEM = ENTR_DESERT_COLOSSUS_WARP_PAD,
/* */ GI_TP_DEST_NOCTURNE = ENTR_GRAVEYARD_WARP_PAD,
/* */ GI_TP_DEST_PRELUDE = ENTR_TEMPLE_OF_TIME_WARP_PAD,
} GITeleportDestinations;
#ifdef __cplusplus
extern "C" {
#endif
@@ -225,6 +215,9 @@ class GameInteractor {
static GameInteractionEffectQueryResult RemoveEffect(RemovableGameInteractionEffect& effect);
// Game Hooks
//
// Hooks should be idempotent and execution order is not guaranteed.
// If two operations must happen in a specific order, they should be placed in the same hook.
HOOK_ID nextHookId = 1;
template <typename H> struct RegisteredGameHooks {
@@ -203,6 +203,14 @@ typedef enum {
// - None
VB_BG_BREAKWALL_BREAK,
// #### `result`
// ```c
// this->timer > 0 && this->timer <= 100
// ```
// #### `args`
// - `*BgSpot06Objects`
VB_BG_SPOT06_OBJECTS_GATE_SKIP,
// #### `result`
// ```c
// gSaveContext.bgsFlag
@@ -532,10 +540,11 @@ typedef enum {
// #### `result`
// ```c
// true
// (this->heldItemAction == PLAYER_IA_HOOKSHOT) ||
// (this->heldItemAction == PLAYER_IA_LONGSHOT)
// ```
// #### `args`
// - None
// - '*Player'
VB_DRAW_ADDITIONAL_RETICLES,
// #### `result`
@@ -1355,6 +1364,14 @@ typedef enum {
// - `*DoorShutter`
VB_LOCK_BOSS_DOOR,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - `*EnMs`
VB_MAGIC_BEAN_SALESMAN_TAKE_MONEY,
// #### `result`
// ```c
// CHECK_QUEST_ITEM(QUEST_SONG_EPONA)
@@ -1518,6 +1535,14 @@ typedef enum {
// - `*BossGanondrof`
VB_PHANTOM_GANON_DEATH_SCENE,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - None
VB_PLAY_BEAN_PLANTING_CS,
// #### `result`
// ##### In `DoorWarp1_ChildWarpOut` - `SCENE_DODONGOS_CAVERN_BOSS`
// ```c
@@ -2663,7 +2688,7 @@ typedef enum {
// - `*ActorContext`
// - `*ActorEntry`
// - `*PlayState`
// - `*Actor`
// - `**Actor`
VB_SPAWN_ACTOR_ENTRY,
// #### `result`
@@ -2328,12 +2328,12 @@ void StaticData::HintTable_Init() {
{QM_RED, QM_BLUE, QM_GREEN}));
hintTextTable[RHT_MALON_HINT_OBSTICLE_COURSE] = HintText(CustomMessage("How about trying the #Obstacle Course?# If you beat my time I'll let you keep my favourite #cow# Elsie and her toy #[[1]]#!^"
"Challenge the #Obstacle Course?#&\x1B&#Let's go&No thanks#",
"Challenge the #Obstacle Course?#\x1B#Let's go&No thanks#",
/*german*/ "Warum versuchst Du Dich nicht mit Epona an dem #Hindernisparcours#?^"
"Gelingt es Dir den Rekord zu brechen, bekommst Du meine #Lieblingskuh# Elsie^und ihr Lieblingsspielzeug, #[[1]]#!^"
"Wie sieht's aus?&Möchtest Du es versuchen?\x1B&#Ja!&Nein!#",
"Wie sieht's aus?&Möchtest Du es versuchen?\x1B#Ja!&Nein!#",
/*french*/ "Que dirais-tu d'essayer le #Parcours d'Obstacles#? Si tu bats mon temps, je te donnerai ma vache préférée, Elsie, et son jouet #[[1]]#!^"
"Tenter le #Parcours d'Obstacles#?&\x1B&#Allons-y&Non merci#",
"Tenter le #Parcours d'Obstacles#?\x1B#Allons-y&Non merci#",
{QM_RED, QM_BLUE, QM_GREEN, QM_RED, QM_GREEN}));
hintTextTable[RHT_MALON_HINT_TURNING_EVIL] = HintText(CustomMessage("@? Is that you? ^If I ran the ranch, I'd build an #Obstacle Course#, and whoever gets the best time would win a #cow#!^"
@@ -1318,6 +1318,14 @@ void StaticData::HintTable_Init_Exclude_Dungeon() {
/*german*/ "Man erzählt sich, daß sich bewacht von einem #Ring der Flammen#, im Geistertempel #[[1]]# |befände|befänden|.",
/*french*/ "Selon moi, protégé par un #cercle de flammes# dans le Temple de l'Esprit se trouve #[[1]]#.", {QM_RED, QM_GREEN}));
hintTextTable[RHT_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun past rolling boulders in Spirit Temple# reveals #[[1]]#.",
/*german*/ TODO_TRANSLATE,
/*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN}));
hintTextTable[RHT_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in the spotlight by statues# reveals #[[1]]#.",
/*german*/ TODO_TRANSLATE,
/*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN}));
hintTextTable[RHT_CRATE_SPIRIT_TEMPLE] = HintText(CustomMessage("They say that a #crate in Spirit Temple# contains #[[1]]#.",
/*german*/ "Man erzählt sich, daß eine #Kiste im Geistertempel# #[[1]]# enthielte.",
/*french*/ "Selon moi, une #caisse dans le Temple de l'Esprit# contient #[[1]]#.", {QM_RED, QM_GREEN}));
@@ -2128,7 +2128,7 @@ void StaticData::HintTable_Init_Exclude_Overworld() {
/*french*/ "Selon moi, un #arbre au Ranch Lon Lon# cache #[[1]]#.", { QM_RED, QM_GREEN }));
hintTextTable[RHT_BUSH_HYRULE_FIELD] =
HintText(CustomMessage("They say that a #bush in Hyrle Field# contains #[[1]]#.",
HintText(CustomMessage("They say that a #bush in Hyrule Field# contains #[[1]]#.",
/*german*/ "",
/*french*/ "Selon moi, un #buisson dans la Plaine d'Hyrule# cache #[[1]]#.", { QM_RED, QM_GREEN }));
hintTextTable[RHT_BUSH_ZORAS_FOUNTAIN] =
@@ -149,6 +149,7 @@ static void PlaceItemsForType(RandomizerCheckType rctype, bool overworldActive =
void GenerateItemPool() {
// RANDOTODO proper removal of items not in pool or logically relevant instead of dummy checks.
auto ctx = Rando::Context::GetInstance();
ctx->possibleIceTrapModels.clear();
itemPool.clear();
junkPool.clear();
plentifulPool.clear();
@@ -126,6 +126,7 @@ void GenerateStartingInventory() {
AddItemToInventory(RG_NOCTURNE_OF_SHADOW, ctx->GetOption(RSK_STARTING_NOCTURNE_OF_SHADOW) ? 1 : 0);
AddItemToInventory(RG_PRELUDE_OF_LIGHT, ctx->GetOption(RSK_STARTING_PRELUDE_OF_LIGHT) ? 1 : 0);
AddItemToInventory(RG_KOKIRI_SWORD, ctx->GetOption(RSK_STARTING_KOKIRI_SWORD) ? 1 : 0);
AddItemToInventory(RG_MAGIC_BEAN_PACK, ctx->GetOption(RSK_STARTING_BEANS) ? 1 : 0);
// if (ProgressiveGoronSword) {
// AddItemToInventory(RG_PROGRESSIVE_GORONSWORD, StartingBiggoronSword.Value<uint8_t>());
// } else {
@@ -24,8 +24,8 @@ extern PlayState* gPlayState;
RAND_GET_OPTION(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL))
void BuildMerchantMessage(CustomMessage& msg, RandomizerCheck rc, bool mysterious = true) {
RandomizerGet rgid = RAND_GET_ITEM(rc)->GetPlacedRandomizerGet();
uint16_t price = RAND_GET_ITEM(rc)->GetPrice();
auto location = RAND_GET_ITEM(rc);
RandomizerGet rgid = location->GetPlacedRandomizerGet();
CustomMessage itemName;
std::string color = Rando::StaticData::RetrieveItem(static_cast<RandomizerGet>(rgid)).GetColor();
if (mysterious) {
@@ -36,10 +36,15 @@ void BuildMerchantMessage(CustomMessage& msg, RandomizerCheck rc, bool mysteriou
itemName = CustomMessage(RAND_GET_OVERRIDE(rc).GetTrickName());
color = "%g";
} else {
itemName = CustomMessage(Rando::StaticData::RetrieveItem(rgid).GetName());
const Rando::Item& item = Rando::StaticData::RetrieveItem(rgid);
if (Rando::StaticData::GetLocation(rc)->IsShop()) {
itemName = CustomMessage(Rando::StaticData::RetrieveItem(rgid).GetName());
} else {
itemName = item.GetHint().GetHintMessage();
}
}
msg.Replace("[[color]]", color);
msg.InsertNames({ itemName, CustomMessage(std::to_string(price)) });
msg.InsertNames({ itemName, CustomMessage(std::to_string(location->GetPrice())) });
}
void BuildBeanGuyMessage(uint16_t* textId, bool* loadFromMessageTable) {
@@ -49,9 +54,9 @@ void BuildBeanGuyMessage(uint16_t* textId, bool* loadFromMessageTable) {
"I never thought I'd say this, but I'm selling the last %rMagic Bean%w.^%y99 Rupees%w, no "
"less.\x1B%gYes&No%w",
"Ich hätte nie gedacht, daß ich das sage, aber ich verkaufe die letzte^%rWundererbse%w für %y99 "
"Rubine%w.\x1B&%gJa&Nein%w",
"Je te vends mon dernier %rHaricot&magique%g pour %y99 Rubis%w.\x1B&%gAcheterNe pas acheter%w");
msg.Format();
"Rubine%w.\x1B%gJa&Nein%w",
"Je te vends mon dernier %rHaricot&magique%w pour %y99 Rubis%w.\x1B%gAcheter&Ne pas acheter%w");
msg.AutoFormat();
} else if (*textId == TEXT_BEAN_SALESMAN_BUY_FOR_10) {
msg = CustomMessage("Want to buy [[color]][[1]]%w for %y[[2]] Rupees%w?\x1B%gYes&No%w",
"Möchten Sie [[color]][[1]]%w für %y[[2]] Rubin%w kaufen?\x1B%gJa&Nein%w",
@@ -105,6 +110,8 @@ void BuildCarpetGuyMessage(uint16_t* textId, bool* loadFromMessageTable) {
BuildMerchantMessage(msg, RC_WASTELAND_BOMBCHU_SALESMAN,
!RAND_GET_OPTION(RSK_MERCHANT_TEXT_HINT) ||
CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("MysteriousShuffle"), 0));
} else {
return;
}
msg.AutoFormat();
msg.LoadIntoFont();
@@ -140,7 +140,7 @@ void BuildSkulltulaPeopleMessage(uint16_t* textId, bool* loadFromMessageTable) {
"et j'aurai quelque chose à te donner! [[color]]([[1]])%w");
msg.InsertNumber(count);
msg.Replace("[[color]]", item.GetColor());
msg.InsertNames({ item.GetName() });
msg.InsertNames({ item.GetHint().GetHintMessage() });
msg.AutoFormat();
msg.LoadIntoFont();
*loadFromMessageTable = false;
@@ -155,12 +155,10 @@ void Build100SkullsHintMessage(uint16_t* textId, bool* loadFromMessageTable) {
/*french*/
"Yeaaarrgh! Je suis maudit!^Détruit encore %y100 Araignées de la Malédiction%w "
"et j'aurai quelque chose à te donner! [[color]]([[1]])%w");
msg.Replace("[[color]]", Rando::StaticData::RetrieveItem(
RAND_GET_ITEM_LOC(RC_KAK_100_GOLD_SKULLTULA_REWARD)->GetPlacedRandomizerGet())
.GetColor());
msg.InsertNames(
{ Rando::StaticData::RetrieveItem(RAND_GET_ITEM_LOC(RC_KAK_100_GOLD_SKULLTULA_REWARD)->GetPlacedRandomizerGet())
.GetName() });
Rando::Item& item =
Rando::StaticData::RetrieveItem(RAND_GET_ITEM_LOC(RC_KAK_100_GOLD_SKULLTULA_REWARD)->GetPlacedRandomizerGet());
msg.Replace("[[color]]", item.GetColor());
msg.InsertNames({ item.GetHint().GetHintMessage() });
msg.AutoFormat();
msg.LoadIntoFont();
*loadFromMessageTable = false;
@@ -180,78 +178,43 @@ void BuildGregHintMessage(uint16_t* textId, bool* loadFromMessageTable) {
}
}
void BuildMysteriousWarpMessage() {
CustomMessage msg = CustomMessage(
"Warp to&%ra mysterious place?%w&" + CustomMessage::TWO_WAY_CHOICE() + "%gOK&No%w",
"Zu&%reinem mysteriösen Ort%w?&" + CustomMessage::TWO_WAY_CHOICE() + "%gOK&No%w",
"Se téléporter vers&%run endroit mystérieux%w?&" + CustomMessage::TWO_WAY_CHOICE() + "%rOK!&Non%w");
msg.LoadIntoFont();
static void BuildWarpMessage(RandomizerHint rh, bool* loadFromMessageTable) {
if (!RAND_GET_OPTION(RSK_WARP_SONG_HINTS)) {
CustomMessage msg = CustomMessage(
"Warp to&%ra mysterious place?%w&" + CustomMessage::TWO_WAY_CHOICE() + "%gOK&No%w",
"Zu&%reinem mysteriösen Ort%w?&" + CustomMessage::TWO_WAY_CHOICE() + "%gOK&No%w",
"Se téléporter vers&%run endroit mystérieux%w?&" + CustomMessage::TWO_WAY_CHOICE() + "%rOK!&Non%w");
msg.AutoFormat();
msg.LoadIntoFont();
} else {
CustomMessage msg = RAND_GET_HINT(rh)->GetHintMessage(MF_AUTO_FORMAT);
msg.LoadIntoFont();
}
*loadFromMessageTable = false;
}
void BuildMinuetWarpMessage(uint16_t* textId, bool* loadFromMessageTable) {
if (!RAND_GET_OPTION(RSK_WARP_SONG_HINTS)) {
BuildMysteriousWarpMessage();
*loadFromMessageTable = false;
return;
}
CustomMessage msg = RAND_GET_HINT(RH_MINUET_WARP_LOC)->GetHintMessage(MF_AUTO_FORMAT);
msg.LoadIntoFont();
*loadFromMessageTable = false;
BuildWarpMessage(RH_MINUET_WARP_LOC, loadFromMessageTable);
}
void BuildBoleroWarpMessage(uint16_t* textId, bool* loadFromMessageTable) {
if (!RAND_GET_OPTION(RSK_WARP_SONG_HINTS)) {
BuildMysteriousWarpMessage();
*loadFromMessageTable = false;
return;
}
CustomMessage msg = RAND_GET_HINT(RH_BOLERO_WARP_LOC)->GetHintMessage(MF_AUTO_FORMAT);
msg.LoadIntoFont();
*loadFromMessageTable = false;
BuildWarpMessage(RH_BOLERO_WARP_LOC, loadFromMessageTable);
}
void BuildSerenadeWarpMessage(uint16_t* textId, bool* loadFromMessageTable) {
if (!RAND_GET_OPTION(RSK_WARP_SONG_HINTS)) {
BuildMysteriousWarpMessage();
*loadFromMessageTable = false;
return;
}
CustomMessage msg = RAND_GET_HINT(RH_SERENADE_WARP_LOC)->GetHintMessage(MF_AUTO_FORMAT);
msg.LoadIntoFont();
*loadFromMessageTable = false;
BuildWarpMessage(RH_SERENADE_WARP_LOC, loadFromMessageTable);
}
void BuildRequiemWarpMessage(uint16_t* textId, bool* loadFromMessageTable) {
if (!RAND_GET_OPTION(RSK_WARP_SONG_HINTS)) {
BuildMysteriousWarpMessage();
*loadFromMessageTable = false;
return;
}
CustomMessage msg = RAND_GET_HINT(RH_REQUIEM_WARP_LOC)->GetHintMessage(MF_AUTO_FORMAT);
msg.LoadIntoFont();
*loadFromMessageTable = false;
BuildWarpMessage(RH_REQUIEM_WARP_LOC, loadFromMessageTable);
}
void BuildNocturneWarpMessage(uint16_t* textId, bool* loadFromMessageTable) {
if (!RAND_GET_OPTION(RSK_WARP_SONG_HINTS)) {
BuildMysteriousWarpMessage();
*loadFromMessageTable = false;
return;
}
CustomMessage msg = RAND_GET_HINT(RH_NOCTURNE_WARP_LOC)->GetHintMessage(MF_AUTO_FORMAT);
msg.LoadIntoFont();
*loadFromMessageTable = false;
BuildWarpMessage(RH_NOCTURNE_WARP_LOC, loadFromMessageTable);
}
void BuildPreludeWarpMessage(uint16_t* textId, bool* loadFromMessageTable) {
if (!RAND_GET_OPTION(RSK_WARP_SONG_HINTS)) {
BuildMysteriousWarpMessage();
*loadFromMessageTable = false;
return;
}
CustomMessage msg = RAND_GET_HINT(RH_PRELUDE_WARP_LOC)->GetHintMessage(MF_AUTO_FORMAT);
msg.LoadIntoFont();
*loadFromMessageTable = false;
BuildWarpMessage(RH_PRELUDE_WARP_LOC, loadFromMessageTable);
}
void BuildFrogsHintMessage(uint16_t* textId, bool* loadFromMessageTable) {
@@ -421,15 +384,17 @@ void RegisterStaticHints() {
COND_ID_HOOK(OnOpenText, TEXT_CHEST_GAME_REAL_GAMBLER, RAND_GET_OPTION(RSK_GREG_HINT), BuildGregHintMessage);
COND_ID_HOOK(OnOpenText, TEXT_CHEST_GAME_THANKS_A_LOT, RAND_GET_OPTION(RSK_GREG_HINT), BuildGregHintMessage);
// Warp
COND_ID_HOOK(OnOpenText, TEXT_WARP_MINUET_OF_FOREST, RAND_GET_OPTION(RSK_WARP_SONG_HINTS), BuildMinuetWarpMessage);
COND_ID_HOOK(OnOpenText, TEXT_WARP_BOLERO_OF_FIRE, RAND_GET_OPTION(RSK_WARP_SONG_HINTS), BuildBoleroWarpMessage);
COND_ID_HOOK(OnOpenText, TEXT_WARP_SERENADE_OF_WATER, RAND_GET_OPTION(RSK_WARP_SONG_HINTS),
COND_ID_HOOK(OnOpenText, TEXT_WARP_MINUET_OF_FOREST, RAND_GET_OPTION(RSK_SHUFFLE_WARP_SONGS),
BuildMinuetWarpMessage);
COND_ID_HOOK(OnOpenText, TEXT_WARP_BOLERO_OF_FIRE, RAND_GET_OPTION(RSK_SHUFFLE_WARP_SONGS), BuildBoleroWarpMessage);
COND_ID_HOOK(OnOpenText, TEXT_WARP_SERENADE_OF_WATER, RAND_GET_OPTION(RSK_SHUFFLE_WARP_SONGS),
BuildSerenadeWarpMessage);
COND_ID_HOOK(OnOpenText, TEXT_WARP_REQUIEM_OF_SPIRIT, RAND_GET_OPTION(RSK_WARP_SONG_HINTS),
COND_ID_HOOK(OnOpenText, TEXT_WARP_REQUIEM_OF_SPIRIT, RAND_GET_OPTION(RSK_SHUFFLE_WARP_SONGS),
BuildRequiemWarpMessage);
COND_ID_HOOK(OnOpenText, TEXT_WARP_NOCTURNE_OF_SHADOW, RAND_GET_OPTION(RSK_WARP_SONG_HINTS),
COND_ID_HOOK(OnOpenText, TEXT_WARP_NOCTURNE_OF_SHADOW, RAND_GET_OPTION(RSK_SHUFFLE_WARP_SONGS),
BuildNocturneWarpMessage);
COND_ID_HOOK(OnOpenText, TEXT_WARP_PRELUDE_OF_LIGHT, RAND_GET_OPTION(RSK_WARP_SONG_HINTS), BuildPreludeWarpMessage);
COND_ID_HOOK(OnOpenText, TEXT_WARP_PRELUDE_OF_LIGHT, RAND_GET_OPTION(RSK_SHUFFLE_WARP_SONGS),
BuildPreludeWarpMessage);
// Frogs
COND_ID_HOOK(OnOpenText, TEXT_FROGS_UNDERWATER, RAND_GET_OPTION(RSK_FROGS_HINT), BuildFrogsHintMessage);
// Loach
@@ -4,6 +4,7 @@
#include <libultraship/libultra.h>
#include "global.h"
#include "soh/ObjectExtension/ObjectExtension.h"
#include "item_category_adj.h"
extern "C" {
#include "variables.h"
@@ -35,24 +36,7 @@ extern "C" void ObjKibako2_RandomizerDraw(Actor* thisx, PlayState* play) {
GetItemEntry crateItem =
Rando::Context::GetInstance()->GetFinalGIEntry(crateIdentity->randomizerCheck, true, GI_NONE);
getItemCategory = crateItem.getItemCategory;
// If they have bombchus, don't consider the bombchu item major
if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU &&
((crateItem.modIndex == MOD_RANDOMIZER && crateItem.getItemId == RG_PROGRESSIVE_BOMBCHU_BAG) ||
(crateItem.modIndex == MOD_NONE &&
(crateItem.getItemId == GI_BOMBCHUS_5 || crateItem.getItemId == GI_BOMBCHUS_10 ||
crateItem.getItemId == GI_BOMBCHUS_20)))) {
getItemCategory = ITEM_CATEGORY_JUNK;
// If it's a bottle and they already have one, consider the item lesser
} else if ((crateItem.modIndex == MOD_RANDOMIZER && crateItem.getItemId >= RG_BOTTLE_WITH_RED_POTION &&
crateItem.getItemId <= RG_BOTTLE_WITH_POE) ||
(crateItem.modIndex == MOD_NONE &&
(crateItem.getItemId == GI_BOTTLE || crateItem.getItemId == GI_MILK_BOTTLE))) {
if (gSaveContext.inventory.items[SLOT_BOTTLE_1] != ITEM_NONE) {
getItemCategory = ITEM_CATEGORY_LESSER;
}
}
getItemCategory = Randomizer_AdjustItemCategory(crateItem);
// Change texture
switch (getItemCategory) {
@@ -102,24 +86,7 @@ extern "C" void ObjKibako_RandomizerDraw(Actor* thisx, PlayState* play) {
GetItemEntry smallCrateItem =
Rando::Context::GetInstance()->GetFinalGIEntry(crateIdentity->randomizerCheck, true, GI_NONE);
getItemCategory = smallCrateItem.getItemCategory;
// If they have bombchus, don't consider the bombchu item major
if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU &&
((smallCrateItem.modIndex == MOD_RANDOMIZER && smallCrateItem.getItemId == RG_PROGRESSIVE_BOMBCHU_BAG) ||
(smallCrateItem.modIndex == MOD_NONE &&
(smallCrateItem.getItemId == GI_BOMBCHUS_5 || smallCrateItem.getItemId == GI_BOMBCHUS_10 ||
smallCrateItem.getItemId == GI_BOMBCHUS_20)))) {
getItemCategory = ITEM_CATEGORY_JUNK;
// If it's a bottle and they already have one, consider the item lesser
} else if ((smallCrateItem.modIndex == MOD_RANDOMIZER && smallCrateItem.getItemId >= RG_BOTTLE_WITH_RED_POTION &&
smallCrateItem.getItemId <= RG_BOTTLE_WITH_POE) ||
(smallCrateItem.modIndex == MOD_NONE &&
(smallCrateItem.getItemId == GI_BOTTLE || smallCrateItem.getItemId == GI_MILK_BOTTLE))) {
if (gSaveContext.inventory.items[SLOT_BOTTLE_1] != ITEM_NONE) {
getItemCategory = ITEM_CATEGORY_LESSER;
}
}
getItemCategory = Randomizer_AdjustItemCategory(smallCrateItem);
// Change texture
switch (getItemCategory) {
@@ -106,11 +106,13 @@ void RegisterShuffleFairies() {
COND_VB_SHOULD(VB_BOTTLE_ACTOR, shouldRegister, {
Actor* actor = va_arg(args, Actor*);
const auto fairyIdentity = ObjectExtension::GetInstance().Get<CheckIdentity>(actor);
if (fairyIdentity != nullptr && fairyIdentity->randomizerInf != RAND_INF_MAX) {
Flags_SetRandomizerInf(fairyIdentity->randomizerInf);
actor->parent = &GET_PLAYER(gPlayState)->actor;
*should = false;
if (actor->id == ACTOR_EN_ELF) {
const auto fairyIdentity = ObjectExtension::GetInstance().Get<CheckIdentity>(actor);
if (fairyIdentity != nullptr && fairyIdentity->randomizerInf != RAND_INF_MAX) {
Flags_SetRandomizerInf(fairyIdentity->randomizerInf);
actor->parent = &GET_PLAYER(gPlayState)->actor;
*should = false;
}
}
});
@@ -1,6 +1,7 @@
#include <soh/OTRGlobals.h>
#include "soh_assets.h"
#include "static_data.h"
#include "item_category_adj.h"
#include "soh/ObjectExtension/ObjectExtension.h"
extern "C" {
@@ -38,7 +39,7 @@ extern "C" void EnKusa_RandomizerDraw(Actor* thisx, PlayState* play) {
if (csmc && (!requiresStoneAgony || (requiresStoneAgony && CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)))) {
auto itemEntry =
Rando::Context::GetInstance()->GetFinalGIEntry(grassIdentity->randomizerCheck, true, GI_NONE);
GetItemCategory getItemCategory = itemEntry.getItemCategory;
GetItemCategory getItemCategory = Randomizer_AdjustItemCategory(itemEntry);
switch (getItemCategory) {
case ITEM_CATEGORY_JUNK:
@@ -1,6 +1,7 @@
#include "soh/OTRGlobals.h"
#include "soh_assets.h"
#include "static_data.h"
#include "item_category_adj.h"
#include "soh/ObjectExtension/ObjectExtension.h"
extern "C" {
@@ -28,7 +29,7 @@ extern "C" void ObjTsubo_RandomizerDraw(Actor* thisx, PlayState* play) {
if (csmc && (!requiresStoneAgony || (requiresStoneAgony && CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)))) {
auto itemEntry =
Rando::Context::GetInstance()->GetFinalGIEntry(potIdentity->randomizerCheck, true, GI_NONE);
GetItemCategory getItemCategory = itemEntry.getItemCategory;
GetItemCategory getItemCategory = Randomizer_AdjustItemCategory(itemEntry);
switch (getItemCategory) {
case ITEM_CATEGORY_LESSER:
@@ -54,7 +55,7 @@ extern "C" void ObjTsubo_RandomizerDraw(Actor* thisx, PlayState* play) {
break;
}
} else {
gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gPotStandardDL);
gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gPotMajorDL);
}
} else {
gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gPotDL);
@@ -2,6 +2,7 @@
#include "soh_assets.h"
#include "static_data.h"
#include "soh/ObjectExtension/ObjectExtension.h"
#include "item_category_adj.h"
extern "C" {
#include "variables.h"
@@ -60,24 +61,7 @@ extern "C" void EnWood02_RandomizerDraw(Actor* thisx, PlayState* play) {
getItemCategory = ITEM_CATEGORY_JUNK;
} else {
treeItem = Rando::Context::GetInstance()->GetFinalGIEntry(treeIdentity->randomizerCheck, true, GI_NONE);
getItemCategory = treeItem.getItemCategory;
// If they have bombchus, don't consider the bombchu item major
if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU &&
((treeItem.modIndex == MOD_RANDOMIZER && treeItem.getItemId == RG_PROGRESSIVE_BOMBCHU_BAG) ||
(treeItem.modIndex == MOD_NONE &&
(treeItem.getItemId == GI_BOMBCHUS_5 || treeItem.getItemId == GI_BOMBCHUS_10 ||
treeItem.getItemId == GI_BOMBCHUS_20)))) {
getItemCategory = ITEM_CATEGORY_JUNK;
// If it's a bottle and they already have one, consider the item lesser
} else if ((treeItem.modIndex == MOD_RANDOMIZER && treeItem.getItemId >= RG_BOTTLE_WITH_RED_POTION &&
treeItem.getItemId <= RG_BOTTLE_WITH_POE) ||
(treeItem.modIndex == MOD_NONE &&
(treeItem.getItemId == GI_BOTTLE || treeItem.getItemId == GI_MILK_BOTTLE))) {
if (gSaveContext.inventory.items[SLOT_BOTTLE_1] != ITEM_NONE) {
getItemCategory = ITEM_CATEGORY_LESSER;
}
}
getItemCategory = Randomizer_AdjustItemCategory(treeItem);
}
GraphicsContext* gfxCtx = play->state.gfxCtx;
+3 -7
View File
@@ -513,17 +513,13 @@ const HintText Hint::GetItemHintText(uint8_t slot, bool mysterious) const {
auto ctx = Rando::Context::GetInstance();
RandomizerCheck hintedCheck = locations[slot];
RandomizerGet targetRG = ctx->GetItemLocation(hintedCheck)->GetPlacedRandomizerGet();
CustomMessage msg;
if (mysterious) {
return StaticData::hintTextTable[RHT_MYSTERIOUS_ITEM];
} else if (!ctx->GetOption(RSK_HINT_CLARITY).Is(RO_HINT_CLARITY_AMBIGUOUS) &&
targetRG == RG_ICE_TRAP) { // RANDOTODO store in item hint instead of item
msg = CustomMessage({ ctx->overrides[hintedCheck].GetTrickName() });
} else if (targetRG == RG_ICE_TRAP) { // RANDOTODO store in item hint instead of item
return HintText(CustomMessage({ ctx->overrides[hintedCheck].GetTrickName() }));
} else {
msg = ctx->GetItemLocation(hintedCheck)->GetPlacedItem().GetName();
return ctx->GetItemLocation(hintedCheck)->GetPlacedItem().GetHint();
}
msg = CustomMessage(ctx->GetItemLocation(hintedCheck)->GetPlacedItem().GetArticle()) + msg;
return HintText(msg);
}
const HintText Hint::GetAreaHintText(uint8_t slot) const {
@@ -13,6 +13,7 @@
#include "soh/SaveManager.h"
#include "soh/ShipInit.hpp"
#include "soh/ObjectExtension/ObjectExtension.h"
#include "item_category_adj.h"
extern "C" {
#include "macros.h"
@@ -370,6 +371,7 @@ void RandomizerOnPlayerUpdateForRCQueueHandler() {
GetItemID vanillaItem = (GetItemID)Rando::StaticData::RetrieveItem(vanillaRandomizerGet).GetItemID();
GetItemEntry getItemEntry =
Rando::Context::GetInstance()->GetFinalGIEntry(rc, true, (GetItemID)vanillaRandomizerGet);
GetItemCategory getItemCategory = Randomizer_AdjustItemCategory(getItemEntry);
if (loc->HasObtained()) {
SPDLOG_INFO("RC {} already obtained, skipping", static_cast<uint32_t>(rc));
@@ -393,13 +395,8 @@ void RandomizerOnPlayerUpdateForRCQueueHandler() {
// crude fix to ensure map hints are readable. Ideally replace with better hint tracking.
!(getItemEntry.getItemId >= RG_DEKU_TREE_MAP && getItemEntry.getItemId <= RG_ICE_CAVERN_MAP &&
getItemEntry.modIndex == MOD_RANDOMIZER) &&
(getItemEntry.getItemCategory == ITEM_CATEGORY_JUNK ||
getItemEntry.getItemCategory == ITEM_CATEGORY_SKULLTULA_TOKEN ||
getItemEntry.getItemCategory == ITEM_CATEGORY_HEALTH ||
getItemEntry.getItemCategory == ITEM_CATEGORY_LESSER ||
// Treat small keys as junk if Skeleton Key is obtained.
(getItemEntry.getItemCategory == ITEM_CATEGORY_SMALL_KEY &&
Flags_GetRandomizerInf(RAND_INF_HAS_SKELETON_KEY))))))) {
(getItemCategory == ITEM_CATEGORY_JUNK || getItemCategory == ITEM_CATEGORY_SKULLTULA_TOKEN ||
getItemCategory == ITEM_CATEGORY_HEALTH || getItemCategory == ITEM_CATEGORY_LESSER))))) {
Item_DropCollectible(gPlayState, &spawnPos, static_cast<int16_t>(ITEM00_SOH_GIVE_ITEM_ENTRY | 0x8000));
}
}
@@ -446,6 +443,54 @@ void RandomizerOnItemReceiveHandler(GetItemEntry receivedItemEntry) {
randomizerQueuedItemEntry = GET_ITEM_NONE;
}
if (receivedItemEntry.modIndex == MOD_RANDOMIZER && receivedItemEntry.getItemId == RG_MAGIC_BEAN_PACK) {
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SKIP_PLANTING_BEANS)) {
gSaveContext.sceneFlags[SCENE_DEATH_MOUNTAIN_CRATER].swch |= (1 << 3);
if (gPlayState->sceneNum == SCENE_DEATH_MOUNTAIN_CRATER) {
Flags_SetSwitch(gPlayState, 3);
}
gSaveContext.sceneFlags[SCENE_DEATH_MOUNTAIN_TRAIL].swch |= (1 << 6);
if (gPlayState->sceneNum == SCENE_DEATH_MOUNTAIN_TRAIL) {
Flags_SetSwitch(gPlayState, 6);
}
gSaveContext.sceneFlags[SCENE_DESERT_COLOSSUS].swch |= (1 << 24);
if (gPlayState->sceneNum == SCENE_DESERT_COLOSSUS) {
Flags_SetSwitch(gPlayState, 24);
}
gSaveContext.sceneFlags[SCENE_GERUDO_VALLEY].swch |= (1 << 3);
if (gPlayState->sceneNum == SCENE_GERUDO_VALLEY) {
Flags_SetSwitch(gPlayState, 3);
}
gSaveContext.sceneFlags[SCENE_GRAVEYARD].swch |= (1 << 3);
if (gPlayState->sceneNum == SCENE_GRAVEYARD) {
Flags_SetSwitch(gPlayState, 3);
}
gSaveContext.sceneFlags[SCENE_KOKIRI_FOREST].swch |= (1 << 9);
if (gPlayState->sceneNum == SCENE_KOKIRI_FOREST) {
Flags_SetSwitch(gPlayState, 9);
}
gSaveContext.sceneFlags[SCENE_LAKE_HYLIA].swch |= (1 << 1);
if (gPlayState->sceneNum == SCENE_LAKE_HYLIA) {
Flags_SetSwitch(gPlayState, 1);
}
gSaveContext.sceneFlags[SCENE_LOST_WOODS].swch |= (1 << 4) | (1 << 18);
if (gPlayState->sceneNum == SCENE_LOST_WOODS) {
Flags_SetSwitch(gPlayState, 4);
Flags_SetSwitch(gPlayState, 18);
}
gSaveContext.sceneFlags[SCENE_ZORAS_RIVER].swch |= (1 << 3);
if (gPlayState->sceneNum == SCENE_ZORAS_RIVER) {
Flags_SetSwitch(gPlayState, 3);
}
ObjBean* bean = (ObjBean*)Actor_Find(&gPlayState->actorCtx, ACTOR_OBJ_BEAN, ACTORCAT_BG);
if (bean != nullptr) {
Flags_SetSwitch(gPlayState, bean->dyna.actor.params & 0x3F);
func_80B8FE00(bean);
}
AMMO(ITEM_BEAN) = 0;
}
}
if (receivedItemEntry.modIndex == MOD_NONE &&
(receivedItemEntry.itemId == ITEM_HEART_PIECE || receivedItemEntry.itemId == ITEM_HEART_PIECE_2 ||
receivedItemEntry.itemId == ITEM_HEART_CONTAINER)) {
@@ -980,6 +1025,15 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
OTRGlobals::Instance->gRandoContext->GetItemLocation(RC_ZR_MAGIC_BEAN_SALESMAN)->GetPrice();
} else if (RAND_GET_OPTION(RSK_SKIP_PLANTING_BEANS)) {
*should = gSaveContext.rupees >= 60;
} else if (BEANS_BOUGHT == 9) {
*should = gSaveContext.rupees >= 99;
}
break;
}
case VB_MAGIC_BEAN_SALESMAN_TAKE_MONEY: {
if (BEANS_BOUGHT == 9) {
Rupees_ChangeBy(-99);
*should = false;
}
break;
}
@@ -2562,7 +2616,16 @@ void RandomizerOnPlayerUpdateHandler() {
gSaveContext.respawn[RESPAWN_MODE_DOWN].yaw = respawn->second.yaw;
}
Play_TriggerVoidOut(gPlayState);
if (gPlayState->sceneNum == SCENE_GROTTOS) {
// RESPAWN_MODE_DOWN isn't refreshed on grotto entry, reload grotto instead
gPlayState->nextEntranceIndex = gSaveContext.entranceIndex;
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->transitionType = TRANS_TYPE_FADE_BLACK;
gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK;
gSaveContext.respawnFlag = 0;
} else {
Play_TriggerVoidOut(gPlayState);
}
}
}
@@ -2575,8 +2638,14 @@ void RandomizerOnPlayerUpdateHandler() {
}
if (!GameInteractor::IsGameplayPaused() && RAND_GET_OPTION(RSK_TRIFORCE_HUNT).IsNot(RO_TRIFORCE_HUNT_OFF)) {
// Warp to credits
if (GameInteractor::State::TriforceHuntCreditsWarpActive) {
// Warp to credits once item queue has drained to avoid losing queued items
if (GameInteractor::State::TriforceHuntCreditsWarpActive && randomizerQueuedChecks.empty() &&
randomizerQueuedCheck == RC_UNKNOWN_CHECK) {
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] =
static_cast<u32>(GAMEPLAYSTAT_TOTAL_TIME);
gSaveContext.ship.stats.gameComplete = 1;
Play_PerformSave(gPlayState);
Notification::Emit({ .message = "Game autosaved" });
gPlayState->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.nextCutsceneIndex = 0xFFF2;
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
-1
View File
@@ -1,7 +1,6 @@
#pragma once
#include <string>
#include <variant>
#include <memory>
#include "3drando/text.hpp"
@@ -0,0 +1,28 @@
#include <stdint.h>
#include "item_category_adj.h"
#include "z64item.h"
#include "variables.h"
#include "macros.h"
GetItemCategory Randomizer_AdjustItemCategory(GetItemEntry item) {
GetItemCategory category = item.getItemCategory;
// Downgrade bombchus to lesser if the player already has bombchus
if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU &&
((item.modIndex == MOD_RANDOMIZER && item.getItemId == RG_PROGRESSIVE_BOMBCHU_BAG) ||
(item.modIndex == MOD_NONE &&
(item.getItemId == GI_BOMBCHUS_5 || item.getItemId == GI_BOMBCHUS_10 || item.getItemId == GI_BOMBCHUS_20)))) {
category = ITEM_CATEGORY_LESSER;
}
// Downgrade bottles to lesser if the player already has a bottle
if ((item.modIndex == MOD_RANDOMIZER && item.getItemId >= RG_BOTTLE_WITH_RED_POTION &&
item.getItemId <= RG_BOTTLE_WITH_POE) ||
(item.modIndex == MOD_NONE && (item.getItemId == GI_BOTTLE || item.getItemId == GI_MILK_BOTTLE))) {
if (gSaveContext.inventory.items[SLOT_BOTTLE_1] != ITEM_NONE) {
category = ITEM_CATEGORY_LESSER;
}
}
return category;
}
@@ -0,0 +1,18 @@
#pragma once
#ifndef ITEM_CATEGORY_ADJ_H
#define ITEM_CATEGORY_ADJ_H
#include "../item-tables/ItemTableTypes.h"
#ifdef __cplusplus
extern "C" {
#endif
GetItemCategory Randomizer_AdjustItemCategory(GetItemEntry item);
#ifdef __cplusplus
}
#endif
#endif
@@ -1,7 +1,6 @@
#include "soh_assets.h"
#include "static_data.h"
#include "SeedContext.h"
#include "logic.h"
#include "textures/icon_item_24_static/icon_item_24_static.h"
#include "textures/icon_item_static/icon_item_static.h"
#include "z64object.h"
+1 -2
View File
@@ -1,6 +1,5 @@
#include "location.h"
#include "static_data.h"
#include <algorithm>
#include <assert.h>
#include "option.h"
@@ -65,7 +64,7 @@ bool Rando::Location::IsOverworld() const {
}
bool Rando::Location::IsShop() const {
return scene >= SCENE_BAZAAR && scene <= SCENE_BOMBCHU_SHOP;
return (scene >= SCENE_BAZAAR && scene <= SCENE_BOMBCHU_SHOP) || scene == SCENE_TEST01;
}
bool Rando::Location::IsVanillaCompletion() const {
@@ -31,8 +31,8 @@ void RegionTable_Init_BottomOfTheWell() {
EVENT_ACCESS(LOGIC_BOTW_LOWERED_WATER, logic->CanUse(RG_ZELDAS_LULLABY)),
}, {
//Locations
LOCATION(RC_BOTTOM_OF_THE_WELL_FRONT_LEFT_FAKE_WALL_CHEST, ctx->GetTrickOption(RT_LENS_BOTW) || logic->CanUse(RG_LENS_OF_TRUTH) && logic->HasItem(RG_OPEN_CHEST)),
LOCATION(RC_BOTTOM_OF_THE_WELL_RIGHT_BOTTOM_FAKE_WALL_CHEST, ctx->GetTrickOption(RT_LENS_BOTW) || logic->CanUse(RG_LENS_OF_TRUTH) && logic->HasItem(RG_OPEN_CHEST)),
LOCATION(RC_BOTTOM_OF_THE_WELL_FRONT_LEFT_FAKE_WALL_CHEST, (ctx->GetTrickOption(RT_LENS_BOTW) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->HasItem(RG_OPEN_CHEST)),
LOCATION(RC_BOTTOM_OF_THE_WELL_RIGHT_BOTTOM_FAKE_WALL_CHEST, (ctx->GetTrickOption(RT_LENS_BOTW) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->HasItem(RG_OPEN_CHEST)),
LOCATION(RC_BOTTOM_OF_THE_WELL_FRONT_CENTER_BOMBABLE_CHEST, logic->HasExplosives() && logic->HasItem(RG_OPEN_CHEST)),
LOCATION(RC_BOTTOM_OF_THE_WELL_BACK_LEFT_BOMBABLE_CHEST, logic->HasExplosives() && (ctx->GetTrickOption(RT_LENS_BOTW) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->HasItem(RG_OPEN_CHEST)),
LOCATION(RC_BOTTOM_OF_THE_WELL_UNDERWATER_FRONT_CHEST, (logic->Get(LOGIC_BOTW_LOWERED_WATER) && logic->HasItem(RG_OPEN_CHEST)) || logic->CanOpenUnderwaterChest()),
@@ -265,11 +265,10 @@ void RegionTable_Init_BottomOfTheWell() {
// Fairies are in slingshot wonder item, & pot behind grate. Pot can also be broken with boomerang trick
EVENT_ACCESS(LOGIC_FAIRY_ACCESS, (logic->IsChild && logic->CanUse(RG_FAIRY_SLINGSHOT)) ||
(AnyAgeTime([]{return logic->BlastOrSmash();}) && logic->CanHitEyeTargets()) ||
//Item extension can get a fairy in 1 of 2 ways: we can either shoot the pot through the grate and let the fairy fly through the wall
//or we can shoot the eye target through the boulder, but not as adult with bow.
//The former cannot be done if the pot has an item in it, as it cannot be collected this way.
(ctx->GetTrickOption(RT_ITEM_EXTENSION) &&
(logic->IsChild || ctx->GetOption(RSK_SHUFFLE_POTS).Is(RO_SHUFFLE_POTS_OFF) || ctx->GetOption(RSK_SHUFFLE_POTS).Is(RO_SHUFFLE_POTS_OVERWORLD)) ? logic->CanHitEyeTargets() : logic->CanUse(RG_FAIRY_SLINGSHOT))),
//Item extension can get a fairy by either shooting the pot through the grate and letting the fairy fly through the wall
//This cannot be done if the pot has an item in it, as it cannot be collected this way.
(ctx->GetTrickOption(RT_ITEM_EXTENSION) && (ctx->GetOption(RSK_SHUFFLE_POTS).Is(RO_SHUFFLE_POTS_OFF) || ctx->GetOption(RSK_SHUFFLE_POTS).Is(RO_SHUFFLE_POTS_OVERWORLD)) && logic->CanHitEyeTargets()) ||
(ctx->GetTrickOption(RT_VISIBLE_COLLISION) && logic->IsChild ? logic->CanHitEyeTargets() : logic->CanUse(RG_FAIRY_SLINGSHOT))),
//It is possible to hit the water switch with a pot from RR_BOTW_MQ_MIDDLE, however the hitbox for making it activate is very unintuitive
//You have to throw the pot from further back to hit the switch from the front instead of the top, trying to hit the "fingers" directly
//This unintuitiveness means it should be a trick. ZL is needed to get a clear path to carry the pot
@@ -282,7 +281,7 @@ void RegionTable_Init_BottomOfTheWell() {
//Not even bow extension seems to get adult's bow to work
//this would be a trick
LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_OUTER_LOBBY_POT, (AnyAgeTime([]{return logic->BlastOrSmash();}) && logic->CanHitEyeTargets()) ||
(ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->IsChild ? logic->CanHitEyeTargets() : logic->CanUse(RG_FAIRY_SLINGSHOT))),
(ctx->GetTrickOption(RT_VISIBLE_COLLISION) && logic->IsChild ? logic->CanHitEyeTargets() : logic->CanUse(RG_FAIRY_SLINGSHOT))),
LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_BOMB_LEFT_HEART, logic->HasExplosives()),
LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_BOMB_RIGHT_HEART, logic->HasExplosives()),
}, {
@@ -244,7 +244,7 @@ void RegionTable_Init_DekuTree() {
}, {
//Locations
LOCATION(RC_DEKU_TREE_MQ_MAP_CHEST, logic->HasItem(RG_OPEN_CHEST)),
LOCATION(RC_DEKU_TREE_MQ_GS_LOBBY, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)),
LOCATION(RC_DEKU_TREE_MQ_GS_LOBBY, (logic->CanBreakCrates() || ctx->GetTrickOption(RT_VISIBLE_COLLISION)) && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)),
LOCATION(RC_DEKU_TREE_MQ_LOBBY_HEART, true),
LOCATION(RC_DEKU_TREE_MQ_2F_GRASS_1, logic->CanCutShrubs()),
LOCATION(RC_DEKU_TREE_MQ_2F_GRASS_2, logic->CanCutShrubs()),
@@ -725,7 +725,7 @@ void RegionTable_Init_FireTemple() {
areaTable[RR_FIRE_TEMPLE_MQ_MAZE_CRATE_CAGE] = Region("Fire Temple MQ Maze Crate Cage", SCENE_FIRE_TEMPLE, {}, {
//Locations
LOCATION(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_LOWER_CHEST, logic->HasItem(RG_OPEN_CHEST)),
LOCATION(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_LOWER_CHEST, logic->HasItem(RG_OPEN_CHEST) && (ctx->GetTrickOption(RT_VISIBLE_COLLISION) || logic->CanBreakCrates())),
LOCATION(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_LOWER_CRATE_1, logic->CanBreakCrates()),
LOCATION(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_LOWER_CRATE_2, logic->CanBreakCrates()),
LOCATION(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_LOWER_CRATE_3, logic->CanBreakCrates()),
@@ -739,9 +739,8 @@ void RegionTable_Init_FireTemple() {
areaTable[RR_FIRE_TEMPLE_MQ_UPPER_LIZALFOS_MAZE] = Region("Fire Temple MQ Upper Lizalfos Maze", SCENE_FIRE_TEMPLE, {}, {}, {
//Exits
ENTRANCE(RR_FIRE_TEMPLE_MQ_LOWER_LIZALFOS_MAZE, true),
//this cage is much more lenient than the lower cage as the switch is close to the front. sling, rang and bow all hit the switch easily, though might be too unintuitive for default logic
//This shouldn't come up in most cases anyway as most methods to get here need either a melee weapon or explosives
ENTRANCE(RR_FIRE_TEMPLE_MQ_MAZE_BOX_CAGE, AnyAgeTime([]{return logic->CanJumpslash() || logic->HasExplosives();})),
ENTRANCE(RR_FIRE_TEMPLE_MQ_MAZE_BOX_CAGE, AnyAgeTime([]{return logic->CanJumpslash() || logic->HasExplosives() ||
(ctx->GetTrickOption(RT_VISIBLE_COLLISION) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG)));})),
ENTRANCE(RR_FIRE_TEMPLE_MQ_SHORTCUT_CLIMB, logic->HasExplosives()),
//Implies RR_FIRE_TEMPLE_MQ_LOWER_LIZALFOS_MAZE access
ENTRANCE(RR_FIRE_TEMPLE_MQ_ABOVE_MAZE, logic->HasExplosives() && logic->CanUse(RG_MEGATON_HAMMER) && (logic->CanUse(RG_LONGSHOT) || (logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_SONG_OF_TIME)))),
@@ -750,7 +749,7 @@ void RegionTable_Init_FireTemple() {
areaTable[RR_FIRE_TEMPLE_MQ_MAZE_BOX_CAGE] = Region("Fire Temple MQ Maze Box Cage", SCENE_FIRE_TEMPLE, {}, {
//Locations
LOCATION(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_UPPER_CHEST, logic->HasItem(RG_OPEN_CHEST)),
LOCATION(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_UPPER_CHEST, logic->HasItem(RG_OPEN_CHEST) && (ctx->GetTrickOption(RT_VISIBLE_COLLISION) || logic->CanBreakCrates())),
LOCATION(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_UPPER_CRATE_1, logic->CanBreakCrates()),
LOCATION(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_UPPER_CRATE_2, logic->CanBreakCrates()),
LOCATION(RC_FIRE_TEMPLE_MQ_LIZALFOS_MAZE_UPPER_CRATE_3, logic->CanBreakCrates()),
@@ -1062,7 +1061,7 @@ void RegionTable_Init_FireTemple() {
areaTable[RR_FIRE_TEMPLE_BOSS_ROOM] = Region("Fire Temple Boss Room", SCENE_FIRE_TEMPLE_BOSS, {
// Events
EVENT_ACCESS(LOGIC_FIRE_TEMPLE_CLEAR, logic->FireTimer() >= 64 && logic->CanKillEnemy(RE_VOLVAGIA)),
EVENT_ACCESS(LOGIC_FIRE_TEMPLE_CLEAR, logic->CanUse(RG_GORON_TUNIC) && logic->CanKillEnemy(RE_VOLVAGIA)),
}, {
// Locations
LOCATION(RC_FIRE_TEMPLE_VOLVAGIA_HEART, logic->Get(LOGIC_FIRE_TEMPLE_CLEAR)),
@@ -461,7 +461,8 @@ void RegionTable_Init_GanonsCastle() {
areaTable[RR_GANONS_CASTLE_MQ_FIRE_TRIAL_OPEN_DOOR] = Region("Ganon's Castle MQ Fire Trial Open Door", SCENE_INSIDE_GANONS_CASTLE, {}, {}, {
//Exits
ENTRANCE(RR_GANONS_CASTLE_MQ_MAIN, true)
ENTRANCE(RR_GANONS_CASTLE_MQ_MAIN, true),
ENTRANCE(RR_GANONS_CASTLE_MQ_FIRE_TRIAL_FROM_OPEN, true),
});
areaTable[RR_GANONS_CASTLE_MQ_FIRE_TRIAL_FROM_OPEN] = Region("Ganon's Castle MQ Fire Trial From Open Door", SCENE_INSIDE_GANONS_CASTLE, {
@@ -476,7 +477,7 @@ void RegionTable_Init_GanonsCastle() {
areaTable[RR_GANONS_CASTLE_MQ_FIRE_TRIAL_FROM_BARRED] = Region("Ganon's Castle MQ Fire Trial From Barred Door", SCENE_INSIDE_GANONS_CASTLE, {}, {}, {
//Exits
ENTRANCE(RR_GANONS_CASTLE_MQ_FIRE_TRIAL_BARRED_DOOR, true)
ENTRANCE(RR_GANONS_CASTLE_MQ_FIRE_TRIAL_BARRED_DOOR, true),
});
areaTable[RR_GANONS_CASTLE_MQ_FIRE_TRIAL_BARRED_DOOR] = Region("Ganon's Castle MQ Fire Trial Barred Door", SCENE_INSIDE_GANONS_CASTLE, {}, {}, {
@@ -50,9 +50,9 @@ void RegionTable_Init_IceCavern() {
}, {
//Locations
LOCATION(RC_ICE_CAVERN_MAP_CHEST, logic->BlueFire() && logic->HasItem(RG_OPEN_CHEST)),
// very easy to break pot through ice
// Bow extension is possible, but very precise: X = 403, Z = 2062-3, Rot = -11475, needs a setup and is its own trick
LOCATION(RC_ICE_CAVERN_FROZEN_POT_1, (logic->CanBreakPots() && logic->BlueFire()) || logic->HasExplosives() ||
(ctx->GetTrickOption(RT_VISIBLE_COLLISION) && ((logic->CanStandingShield() && logic->CanUse(RG_KOKIRI_SWORD)) || logic->CanUse(RG_MASTER_SWORD) || logic->CanUse(RG_BIGGORON_SWORD) || logic->CanUse(RG_MEGATON_HAMMER))) ||
(ctx->GetTrickOption(RT_VISIBLE_COLLISION) && logic->CanJumpslash()) ||
(ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_HOOKSHOT))),
LOCATION(RC_ICE_CAVERN_MAP_ROOM_LEFT_HEART, true),
LOCATION(RC_ICE_CAVERN_MAP_ROOM_MIDDLE_HEART, true),
@@ -155,7 +155,7 @@ void RegionTable_Init_ShadowTemple() {
LOCATION(RC_SHADOW_TEMPLE_FALLING_SPIKES_POT_3, logic->CanUse(RG_BOOMERANG)),
}, {
//Exits
ENTRANCE(RR_SHADOW_TEMPLE_LOWER_HUGE_PIT, true),
ENTRANCE(RR_SHADOW_TEMPLE_LOWER_HUGE_PIT, !!ctx->GetTrickOption(RT_VISIBLE_COLLISION)),
ENTRANCE(RR_SHADOW_TEMPLE_STONE_UMBRELLA_UPPER, ctx->GetTrickOption(RT_SHADOW_UMBRELLA_CLIP) || (ctx->GetTrickOption(RT_DAMAGE_BOOST_SIMPLE) && logic->TakeDamage()) || (logic->IsAdult && ((ctx->GetTrickOption(RT_SHADOW_UMBRELLA_HOVER) && logic->CanUse(RG_HOVER_BOOTS)) || logic->HasItem(RG_GORONS_BRACELET)))),
});
@@ -638,7 +638,8 @@ void RegionTable_Init_ShadowTemple() {
//Locations
LOCATION(RC_SHADOW_TEMPLE_MQ_AFTER_WIND_ENEMY_CHEST, logic->CanKillEnemy(RE_GIBDO) && logic->HasItem(RG_OPEN_CHEST)),
LOCATION(RC_SHADOW_TEMPLE_MQ_AFTER_WIND_HIDDEN_CHEST, logic->HasExplosives() && (ctx->GetTrickOption(RT_LENS_SHADOW_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->HasItem(RG_OPEN_CHEST)),
LOCATION(RC_SHADOW_TEMPLE_MQ_GS_AFTER_WIND, logic->HasExplosives()),
//The various methods for this can be a bit specific, might be worthy of it's own trick when it becomes relevant with dungeon shortcut settings.
LOCATION(RC_SHADOW_TEMPLE_MQ_GS_AFTER_WIND, logic->HasExplosives() || (ctx->GetTrickOption(RT_VISIBLE_COLLISION) && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA))),
LOCATION(RC_SHADOW_TEMPLE_MQ_BEFORE_BOAT_POT_1, logic->CanBreakPots()),
LOCATION(RC_SHADOW_TEMPLE_MQ_BEFORE_BOAT_POT_2, logic->CanBreakPots()),
}, {
@@ -134,7 +134,6 @@ void RegionTable_Init_SpiritTemple() {
LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, []{return logic->CanHitSwitch(ED_BOMB_THROW) && logic->HasItem(RG_OPEN_CHEST);})),
LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA, logic->TakeDamage() ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW);}, false,
RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_1F, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG);})),
LOCATION(RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH))) && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash())),
}, {
//Exits
ENTRANCE(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_1F, true),
@@ -1358,7 +1358,7 @@ void RegionTable_Init_WaterTemple() {
areaTable[RR_WATER_TEMPLE_MQ_CRATE_VORTEX_CAGE] = Region("Water Temple MQ Crate Vortex Cage", SCENE_WATER_TEMPLE, {}, {
//Locations
LOCATION(RC_WATER_TEMPLE_MQ_GS_FREESTANDING_KEY_AREA, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA) && logic->CanBreakCrates()),
LOCATION(RC_WATER_TEMPLE_MQ_GS_FREESTANDING_KEY_AREA, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA) && (logic->CanBreakCrates() || ctx->GetTrickOption(RT_VISIBLE_COLLISION))),
LOCATION(RC_WATER_TEMPLE_MQ_WHIRLPOOL_BEHIND_GATE_CRATE_1, logic->CanBreakCrates()),
LOCATION(RC_WATER_TEMPLE_MQ_WHIRLPOOL_BEHIND_GATE_CRATE_2, logic->CanBreakCrates()),
LOCATION(RC_WATER_TEMPLE_MQ_WHIRLPOOL_BEHIND_GATE_CRATE_3, logic->CanBreakCrates()),
@@ -95,7 +95,7 @@ void RegionTable_Init_CastleGrounds() {
//Exits
ENTRANCE(RR_HC_GATE, true),
ENTRANCE(RR_HC_STORMS_GROTTO, logic->CanOpenStormsGrotto()),
ENTRANCE(RR_HC_GARDEN, (logic->CanUse(RG_WEIRD_EGG) && logic->HasItem(RG_POWER_BRACELET) && logic->HasItem(RG_SPEAK_HYLIAN)) ||
ENTRANCE(RR_HC_DRAIN_LEDGE, (logic->CanUse(RG_WEIRD_EGG) && logic->HasItem(RG_POWER_BRACELET) && logic->HasItem(RG_SPEAK_HYLIAN)) ||
(ctx->GetTrickOption(RT_DAMAGE_BOOST_SIMPLE) && logic->TakeDamage() && logic->HasExplosives() && logic->CanJumpslash())),
});
@@ -30,17 +30,17 @@ void RegionTable_Init_DeathMountainCrater() {
ENTRANCE(RR_DMC_CRACKED_WALL, (logic->FireTimer() >= 16 || logic->Hearts() >= 3)),
ENTRANCE(RR_DMC_SCRUB, logic->FireTimer() >= 16 || logic->Hearts() >= 3),
ENTRANCE(RR_DMC_BLOCKED_EXIT, ((logic->FireTimer() >= 24 || logic->Hearts() >= 5) && logic->DMCUpperToPots()) ||
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->ReachDistantScarecrow() && logic->TakeDamage())),
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->ReachDistantScarecrow() && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS))),
ENTRANCE(RR_DMC_POTS, ((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->DMCUpperToPots()) ||
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage())),
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS))),
ENTRANCE(RR_DMC_POT_GROTTO_EXIT, ((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->DMCUpperToPots()) ||
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage())),
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS))),
ENTRANCE(RR_DMC_CENTRAL, ((logic->FireTimer() >= 64 || logic->Hearts() >= 12) && logic->DMCUpperToPots() && logic->DMCPotsToPad()) ||
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage())),
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS))),
ENTRANCE(RR_DMC_FAR_PLATFORM, (logic->IsAdult && (logic->FireTimer() >= 72 || logic->Hearts() >= 14) && logic->DMCUpperToPots() && logic->DMCPotsToPad() && logic->ReachDistantScarecrow()) ||
(logic->FireTimer() >= 24 || logic->Hearts() >= 5) && logic->TakeDamage()),
(logic->FireTimer() >= 24 || logic->Hearts() >= 5) && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS)),
ENTRANCE(RR_DMC_TEMPLE_EXIT, ((logic->FireTimer() >= 72 || logic->Hearts() >= 14) && logic->DMCUpperToPots() && logic->DMCPotsToPad()) ||
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage())),
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS))),
});
areaTable[RR_DMC_ROCKS_GROTTO_ENTRY] = Region("DMC Rocks Grotto Entry", SCENE_DEATH_MOUNTAIN_CRATER, {
@@ -56,17 +56,17 @@ void RegionTable_Init_DeathMountainCrater() {
ENTRANCE(RR_DMC_CRACKED_WALL, (logic->FireTimer() >= 16 || logic->Hearts() >= 3)),
ENTRANCE(RR_DMC_SCRUB, logic->FireTimer() >= 16 || logic->Hearts() >= 3),
ENTRANCE(RR_DMC_BLOCKED_EXIT, ((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->DMCUpperToPots()) ||
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage())),
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS))),
ENTRANCE(RR_DMC_POTS, ((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->DMCUpperToPots()) ||
(logic->IsAdult && (logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->ReachDistantScarecrow() && logic->TakeDamage())),
(logic->IsAdult && (logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->ReachDistantScarecrow() && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS))),
ENTRANCE(RR_DMC_POT_GROTTO_EXIT, ((logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->DMCUpperToPots()) ||
(logic->IsAdult && (logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->ReachDistantScarecrow() && logic->TakeDamage())),
(logic->IsAdult && (logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->ReachDistantScarecrow() && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS))),
ENTRANCE(RR_DMC_CENTRAL, ((logic->FireTimer() >= 64 || logic->Hearts() >= 12) && logic->DMCUpperToPots() && logic->DMCPotsToPad()) ||
(logic->IsAdult && (logic->FireTimer() >= 40 || logic->Hearts() >= 3) && logic->ReachDistantScarecrow() && logic->TakeDamage())),
(logic->IsAdult && (logic->FireTimer() >= 40 || logic->Hearts() >= 3) && logic->ReachDistantScarecrow() && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS))),
ENTRANCE(RR_DMC_FAR_PLATFORM, (logic->IsAdult && (logic->FireTimer() >= 72 || logic->Hearts() >= 14) && logic->DMCUpperToPots() && logic->DMCPotsToPad() && logic->ReachDistantScarecrow()) ||
(logic->FireTimer() >= 16 || logic->Hearts() >= 3) && logic->TakeDamage()),
(logic->FireTimer() >= 16 || logic->Hearts() >= 3) && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS)),
ENTRANCE(RR_DMC_TEMPLE_EXIT, ((logic->FireTimer() >= 64 || logic->Hearts() >= 12) && logic->DMCUpperToPots() && logic->DMCPotsToPad()) ||
(logic->IsAdult && (logic->FireTimer() >= 40 || logic->Hearts() >= 3) && logic->ReachDistantScarecrow() && logic->TakeDamage())),
(logic->IsAdult && (logic->FireTimer() >= 40 || logic->Hearts() >= 3) && logic->ReachDistantScarecrow() && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS))),
});
areaTable[RR_DMC_BLOCKED_ENTRY] = Region("DMC Blocked Entry", SCENE_DEATH_MOUNTAIN_CRATER, {}, {
@@ -92,13 +92,13 @@ void RegionTable_Init_DeathMountainCrater() {
ENTRANCE(RR_DMC_POTS, logic->FireTimer() >= 8 || logic->Hearts() >= 2),
ENTRANCE(RR_DMC_POT_GROTTO_EXIT, logic->FireTimer() >= 16 || logic->Hearts() >= 3),
ENTRANCE(RR_DMC_CENTRAL, ((logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->DMCPotsToPad()) ||
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))),
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow() && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && logic->CanClimbLadder())),
ENTRANCE(RR_DMC_FAR_PLATFORM, (logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->DMCPotsToPad() && logic->ReachDistantScarecrow()) ||
((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->TakeDamage() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT))) ||
((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && logic->CanClimbLadder()) ||
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->DMCPotsToPad() && CanPlantBean(RR_DMC_CENTRAL, RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL)) ||
((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->TakeDamage() && ctx->GetTrickOption(RT_DMC_HOVER_BEAN_POH) && logic->CanUse(RG_HOVER_BOOTS))),
((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && ctx->GetTrickOption(RT_DMC_HOVER_BEAN_POH) && logic->CanUse(RG_HOVER_BOOTS))),
ENTRANCE(RR_DMC_TEMPLE_EXIT, ((logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->DMCPotsToPad()) ||
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))),
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && logic->ReachDistantScarecrow() && logic->CanClimbLadder())),
});
areaTable[RR_DMC_POTS_ENTRY] = Region("DMC Pots Entry", SCENE_DEATH_MOUNTAIN_CRATER, {}, {
@@ -124,11 +124,11 @@ void RegionTable_Init_DeathMountainCrater() {
ENTRANCE(RR_DMC_POTS, true),
ENTRANCE(RR_DMC_POT_GROTTO_EXIT, logic->FireTimer() >= 8 || logic->Hearts() >= 2),
ENTRANCE(RR_DMC_CENTRAL, ((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->DMCPotsToPad()) ||
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))),
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && logic->ReachDistantScarecrow() && logic->CanClimbLadder())),
ENTRANCE(RR_DMC_FAR_PLATFORM, (logic->IsAdult && (logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->DMCPotsToPad() && logic->ReachDistantScarecrow()) ||
((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->TakeDamage() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))),
((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && logic->CanClimbLadder())),
ENTRANCE(RR_DMC_TEMPLE_EXIT, ((logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->DMCPotsToPad()) ||
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))),
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && logic->ReachDistantScarecrow() && logic->CanClimbLadder())),
});
areaTable[RR_DMC_POT_GROTTO_ENTRY] = Region("DMC Pot Grotto Entry", SCENE_DEATH_MOUNTAIN_CRATER, {}, {
@@ -154,11 +154,11 @@ void RegionTable_Init_DeathMountainCrater() {
ENTRANCE(RR_DMC_POTS, logic->FireTimer() >= 8 || logic->Hearts() >= 2),
ENTRANCE(RR_DMC_POT_GROTTO_EXIT, true),
ENTRANCE(RR_DMC_CENTRAL, ((logic->FireTimer() >= 32 || logic->Hearts() >= 6) && logic->DMCPotsToPad()) ||
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))),
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && logic->ReachDistantScarecrow() && logic->CanClimbLadder())),
ENTRANCE(RR_DMC_FAR_PLATFORM, (logic->IsAdult && (logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->DMCPotsToPad() && logic->ReachDistantScarecrow()) ||
((logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->TakeDamage() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))),
((logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && logic->CanClimbLadder())),
ENTRANCE(RR_DMC_TEMPLE_EXIT, ((logic->FireTimer() >= 40 || logic->Hearts() >= 8) && logic->DMCPotsToPad()) ||
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->ReachDistantScarecrow() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT)))),
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && logic->ReachDistantScarecrow() && logic->CanClimbLadder())),
});
areaTable[RR_DMC_PAD_ENTRY] = Region("DMC Pad Entry", SCENE_DEATH_MOUNTAIN_CRATER, {}, {
@@ -188,7 +188,7 @@ void RegionTable_Init_DeathMountainCrater() {
ENTRANCE(RR_DMC_POT_GROTTO_EXIT, (logic->FireTimer() >= 16 || logic->Hearts() >= 3) && logic->DMCPadToPots() ||
((logic->IsAdult && logic->FireTimer() >= 24 || logic->Hearts() >= 5) && CanPlantBean(RR_DMC_CENTRAL, RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL))),
ENTRANCE(RR_DMC_CENTRAL, (logic->FireTimer() >= 16 || logic->Hearts() >= 3)),
ENTRANCE(RR_DMC_FAR_PLATFORM, ((logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && logic->CanClimbLadder() && logic->DMCPadToPots()) ||
ENTRANCE(RR_DMC_FAR_PLATFORM, ((logic->FireTimer() >= 56 || logic->Hearts() >= 11) && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && logic->CanClimbLadder() && logic->DMCPadToPots()) ||
((logic->FireTimer() >= 40 || logic->Hearts() >= 8) && ctx->GetTrickOption(RT_DMC_HOVER_BEAN_POH) && logic->CanUse(RG_HOVER_BOOTS) && logic->CanUse(RG_LONGSHOT)) ||
(logic->IsAdult && (logic->FireTimer() >= 24 || logic->Hearts() >= 5) && CanPlantBean(RR_DMC_CENTRAL, RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL))||
(logic->IsAdult && (logic->FireTimer() >= 16 || logic->Hearts() >= 3) && logic->ReachDistantScarecrow())),
@@ -232,7 +232,7 @@ void RegionTable_Init_DeathMountainCrater() {
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && CanPlantBean(RR_DMC_CENTRAL, RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL)))),
ENTRANCE(RR_DMC_CENTRAL, logic->HasItem(RG_CLIMB) && (logic->FireTimer() >= 48 || logic->Hearts() >= 9)),
ENTRANCE(RR_DMC_FAR_PLATFORM, logic->HasItem(RG_CLIMB) &&
(((logic->FireTimer() >= 88 || logic->Hearts() >= 3) && logic->TakeDamage() && logic->CanClimbLadder() && logic->DMCPadToPots()) ||
(((logic->FireTimer() >= 88 || logic->Hearts() >= 3) && logic->TakeDamage() && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && logic->CanClimbLadder() && logic->DMCPadToPots()) ||
((logic->FireTimer() >= 72 || logic->Hearts() >= 14) && ctx->GetTrickOption(RT_DMC_HOVER_BEAN_POH) && logic->CanUse(RG_HOVER_BOOTS) && logic->CanUse(RG_LONGSHOT)) ||
(logic->IsAdult && (logic->FireTimer() >= 56 || logic->Hearts() >= 11) && CanPlantBean(RR_DMC_CENTRAL, RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL))||
(logic->IsAdult && (logic->FireTimer() >= 48 || logic->Hearts() >= 9) && logic->ReachDistantScarecrow()))),
@@ -37,7 +37,8 @@ void RegionTable_Init_DeathMountainTrail() {
//Locations
LOCATION(RC_DMT_GS_FALLING_ROCKS_PATH, logic->IsAdult && logic->CanGetNightTimeGS() &&
(logic->CanUse(RG_MEGATON_HAMMER) || (ctx->GetTrickOption(RT_DISTANT_BOULDER_COLLISION) && logic->CanUse(RG_LONGSHOT)) || (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_HOOKSHOT)) ||
(ctx->GetTrickOption(RT_DMT_UPPER_GS) && (logic->CanJumpslash() || logic->CanUse(RG_DINS_FIRE) || logic->HasExplosives() || ((ctx->GetTrickOption(RT_DISTANT_BOULDER_COLLISION) || ctx->GetTrickOption(RT_ITEM_EXTENSION)) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT))))))),
(ctx->GetTrickOption(RT_DMT_UPPER_GS) && (logic->CanJumpslash() || logic->CanUse(RG_DINS_FIRE) || logic->HasExplosives() || (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_FAIRY_SLINGSHOT)) ||
(ctx->GetTrickOption(RT_DISTANT_BOULDER_COLLISION) && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_LONGSHOT)))))),
}, {
//Exits
ENTRANCE(RR_DEATH_MOUNTAIN_TRAIL, true),
@@ -63,7 +64,7 @@ void RegionTable_Init_DeathMountainTrail() {
//Exits
ENTRANCE(RR_DEATH_MOUNTAIN_ROCKFALL, true),
ENTRANCE(RR_DMC_UPPER_ENTRY, true),
ENTRANCE(RR_DMT_OWL_FLIGHT, logic->IsChild && (logic->HasItem(RG_SPEAK_DEKU) || logic->HasItem(RG_SPEAK_GERUDO) || logic->HasItem(RG_SPEAK_GORON) || logic->HasItem(RG_SPEAK_HYLIAN) || logic->HasItem(RG_SPEAK_ZORA))),
ENTRANCE(RR_DMT_OWL_FLIGHT, logic->IsChild && (logic->HasItem(RG_SPEAK_DEKU) || logic->HasItem(RG_SPEAK_GERUDO) || logic->HasItem(RG_SPEAK_GORON) || logic->HasItem(RG_SPEAK_KOKIRI) || logic->HasItem(RG_SPEAK_HYLIAN) || logic->HasItem(RG_SPEAK_ZORA))),
ENTRANCE(RR_DMT_GREAT_FAIRY_FOUNTAIN, AnyAgeTime([]{return logic->BlastOrSmash();})),
});
@@ -200,9 +200,10 @@ void RegionTable_Init_GerudoFortress() {
LOCATION(RC_GF_ABOVE_JAIL_CRATE, true),
}, {
//Exits
//you don't take fall damage if you land on the rock with the flag on for some reason
//there's a trick to reach RR_GF_LONG_ROOF
ENTRANCE(RR_GF_OUTSKIRTS, ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) || logic->TakeDamage()),
//For some reason, you take fall damage if you backflip onto the fortress but not onto the sand
//It's unintuitive to avoid being caught on landing, but that sends you to the same place anyway...
ENTRANCE(RR_GF_OUTSKIRTS, true),
ENTRANCE(RR_GF_NEAR_CHEST, logic->CanUse(RG_LONGSHOT)),
ENTRANCE(RR_GF_BELOW_CHEST, logic->TakeDamage()),
ENTRANCE(RR_GF_JAIL_WINDOW, logic->CanUse(RG_HOOKSHOT)),
@@ -19,7 +19,7 @@ void RegionTable_Init_GerudoValley() {
ENTRANCE(RR_GV_CRATE_LEDGE, (logic->IsChild && logic->HasItem(RG_POWER_BRACELET)) || logic->CanUse(RG_LONGSHOT)),
ENTRANCE(RR_GV_GROTTO_LEDGE, true),
ENTRANCE(RR_GV_FORTRESS_SIDE, (logic->IsAdult && (logic->SummonEpona() || logic->CanUse(RG_LONGSHOT) || ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_FREE) || logic->Get(LOGIC_TH_RESCUED_ALL_CARPENTERS))) || (ctx->GetTrickOption(RT_HOVER_BOOST_SIMPLE) && logic->CanUse(RG_MEGATON_HAMMER) && logic->CanUse(RG_HOVER_BOOTS)) ||
((logic->IsChild || ctx->GetTrickOption(RT_ITEM_EXTENSION)) && logic->CanUse(RG_HOOKSHOT)) || (logic->IsChild && ctx->GetTrickOption(RT_GV_CHILD_CUCCO_JUMP) && logic->HasItem(RG_POWER_BRACELET) && logic->CanJumpslash())),
((logic->IsChild || ctx->GetTrickOption(RT_GV_HOOKSHOT_BRIDGE)) && logic->CanUse(RG_HOOKSHOT)) || (logic->IsChild && ctx->GetTrickOption(RT_GV_CHILD_CUCCO_JUMP) && logic->HasItem(RG_POWER_BRACELET) && logic->CanJumpslash())),
ENTRANCE(RR_GV_WATERFALL_ALCOVE, logic->IsChild && logic->HasItem(RG_POWER_BRACELET)),
ENTRANCE(RR_GV_LOWER_STREAM, logic->IsChild && logic->HasItem(RG_POWER_BRACELET)),
});
@@ -243,7 +243,8 @@ void RegionTable_Init_Kakariko() {
}, {
//Locations
LOCATION(RC_KAK_TRADE_ODD_MUSHROOM, logic->IsAdult && logic->CanUse(RG_ODD_MUSHROOM)),
LOCATION(RC_KAK_GRANNYS_SHOP, logic->IsAdult && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->CanUse(RG_ODD_MUSHROOM) || logic->TradeQuestStep(RG_ODD_MUSHROOM))),
LOCATION(RC_KAK_GRANNYS_SHOP, logic->IsAdult && logic->HasItem(RG_SPEAK_HYLIAN) &&
(logic->CanUse(RG_ODD_MUSHROOM) || logic->TradeQuestStep(RG_ODD_MUSHROOM)) && GetCheckPrice() <= GetWalletCapacity()),
}, {
// Exits
ENTRANCE(RR_KAK_BACKYARD, true),
@@ -70,7 +70,9 @@ void RegionTable_Init_KokiriForest() {
}, {
//Exits
ENTRANCE(RR_KF_BOULDER_LOOP, logic->CanUse(RG_CRAWL)),
ENTRANCE(RR_KF_LINKS_PORCH, logic->IsChild ? logic->CanClimbLadder() : logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOVER_BOOTS)),
//The Deku Baba blocks the setup as Adult, and stunning doesn't last long enough to perform it.
ENTRANCE(RR_KF_LINKS_PORCH, logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOVER_BOOTS) ||
((logic->IsChild || logic->CanKillEnemy(RE_DEKU_BABA) || logic->Get(LOGIC_FOREST_TEMPLE_CLEAR)) && logic->CanClimbLadder())),
ENTRANCE(RR_KF_MIDOS_HOUSE, true),
ENTRANCE(RR_KF_SARIAS_HOUSE, true),
ENTRANCE(RR_KF_HOUSE_OF_TWINS, true),
@@ -83,11 +83,11 @@ void RegionTable_Init_LakeHylia() {
//Exits
ENTRANCE(RR_HF_TO_LAKE_HYLIA, true),
ENTRANCE(RR_LH_FROM_SHORTCUT, true),
ENTRANCE(RR_LH_OWL_FLIGHT, logic->IsChild && (logic->HasItem(RG_SPEAK_DEKU) || logic->HasItem(RG_SPEAK_GERUDO) || logic->HasItem(RG_SPEAK_GORON) || logic->HasItem(RG_SPEAK_HYLIAN) || logic->HasItem(RG_SPEAK_ZORA))),
ENTRANCE(RR_LH_OWL_FLIGHT, logic->IsChild && (logic->HasItem(RG_SPEAK_DEKU) || logic->HasItem(RG_SPEAK_GERUDO) || logic->HasItem(RG_SPEAK_GORON) || logic->HasItem(RG_SPEAK_KOKIRI) || logic->HasItem(RG_SPEAK_HYLIAN) || logic->HasItem(RG_SPEAK_ZORA))),
ENTRANCE(RR_LH_FISHING_ISLAND, ((logic->IsChild || logic->Get(LOGIC_WATER_TEMPLE_CLEAR)) && logic->HasItem(RG_BRONZE_SCALE)) || (logic->IsAdult && (logic->ReachScarecrow() || CanPlantBean(RR_LAKE_HYLIA, RG_LAKE_HYLIA_BEAN_SOUL)))),
ENTRANCE(RR_LH_LAB, logic->CanOpenOverworldDoor(RG_HYLIA_LAB_KEY)),
ENTRANCE(RR_LH_FROM_WATER_TEMPLE, true),
ENTRANCE(RR_LH_GROTTO, logic->HasItem(RG_POWER_BRACELET) && (logic->IsAdult || logic->HasItem(RG_SPEAK_DEKU) || logic->HasItem(RG_SPEAK_GERUDO) || logic->HasItem(RG_SPEAK_GORON) || logic->HasItem(RG_SPEAK_HYLIAN) || logic->HasItem(RG_SPEAK_ZORA))),
ENTRANCE(RR_LH_GROTTO, logic->HasItem(RG_POWER_BRACELET) && (logic->IsAdult || logic->HasItem(RG_SPEAK_DEKU) || logic->HasItem(RG_SPEAK_GERUDO) || logic->HasItem(RG_SPEAK_GORON) || logic->HasItem(RG_SPEAK_KOKIRI) || logic->HasItem(RG_SPEAK_HYLIAN) || logic->HasItem(RG_SPEAK_ZORA))),
});
areaTable[RR_LH_FROM_SHORTCUT] = Region("LH From Shortcut", SCENE_LAKE_HYLIA, TIME_DOESNT_PASS, {RA_LAKE_HYLIA}, {}, {}, {
@@ -59,7 +59,7 @@ void RegionTable_Init_ZoraRiver() {
}, {
//Exits
ENTRANCE(RR_ZR_FRONT, logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE) || logic->HasItem(RG_POWER_BRACELET) || logic->BlastOrSmash() || logic->HasItem(RG_HOVER_BOOTS)),
ENTRANCE(RR_ZR_ATOP_LADDER, (logic->IsAdult || logic->HasItem(RG_POWER_BRACELET)) && (logic->CanClimbLadder() || CanPlantBean(RR_ZORAS_RIVER, RG_ZORAS_RIVER_BEAN_SOUL))),
ENTRANCE(RR_ZR_ATOP_LADDER, ((logic->IsAdult || logic->HasItem(RG_POWER_BRACELET)) && logic->CanClimbLadder()) || (logic->IsAdult && CanPlantBean(RR_ZORAS_RIVER, RG_ZORAS_RIVER_BEAN_SOUL))),
ENTRANCE(RR_ZR_PILLAR, (logic->IsChild && logic->HasItem(RG_POWER_BRACELET)) || logic->CanUse(RG_HOVER_BOOTS) || (logic->IsAdult && ctx->GetTrickOption(RT_ZR_LOWER))),
ENTRANCE(RR_ZR_FROM_SHORTCUT, logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS)),
ENTRANCE(RR_ZR_STORMS_GROTTO, logic->CanOpenStormsGrotto()),
+14 -3
View File
@@ -1396,9 +1396,20 @@ uint8_t Logic::Hearts() {
}
uint8_t Logic::DungeonCount() {
return Get(LOGIC_DEKU_TREE_CLEAR) + Get(LOGIC_DODONGOS_CAVERN_CLEAR) + Get(LOGIC_JABU_JABUS_BELLY_CLEAR) +
Get(LOGIC_FOREST_TEMPLE_CLEAR) + Get(LOGIC_FIRE_TEMPLE_CLEAR) + Get(LOGIC_WATER_TEMPLE_CLEAR) +
Get(LOGIC_SPIRIT_TEMPLE_CLEAR) + Get(LOGIC_SHADOW_TEMPLE_CLEAR);
if (CalculatingAvailableChecks) {
return CheckEventChkInf(EVENTCHKINF_USED_DEKU_TREE_BLUE_WARP) +
CheckEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP) +
CheckEventChkInf(EVENTCHKINF_USED_JABU_JABUS_BELLY_BLUE_WARP) +
CheckEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP) +
CheckEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP) +
CheckEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP) +
CheckRandoInf(RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE) +
CheckRandoInf(RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE);
} else {
return Get(LOGIC_DEKU_TREE_CLEAR) + Get(LOGIC_DODONGOS_CAVERN_CLEAR) + Get(LOGIC_JABU_JABUS_BELLY_CLEAR) +
Get(LOGIC_FOREST_TEMPLE_CLEAR) + Get(LOGIC_FIRE_TEMPLE_CLEAR) + Get(LOGIC_WATER_TEMPLE_CLEAR) +
Get(LOGIC_SPIRIT_TEMPLE_CLEAR) + Get(LOGIC_SHADOW_TEMPLE_CLEAR);
}
}
uint8_t Logic::StoneCount() {
+1 -52
View File
@@ -3867,51 +3867,6 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
if (INV_CONTENT(ITEM_BEAN) == ITEM_NONE) {
INV_CONTENT(ITEM_BEAN) = ITEM_BEAN;
AMMO(ITEM_BEAN) = 10;
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SKIP_PLANTING_BEANS)) {
gSaveContext.sceneFlags[SCENE_DEATH_MOUNTAIN_CRATER].swch |= (1 << 3);
if (gPlayState->sceneNum == SCENE_DEATH_MOUNTAIN_CRATER) {
Flags_SetSwitch(gPlayState, 3);
}
gSaveContext.sceneFlags[SCENE_DEATH_MOUNTAIN_TRAIL].swch |= (1 << 6);
if (gPlayState->sceneNum == SCENE_DEATH_MOUNTAIN_TRAIL) {
Flags_SetSwitch(gPlayState, 6);
}
gSaveContext.sceneFlags[SCENE_DESERT_COLOSSUS].swch |= (1 << 24);
if (gPlayState->sceneNum == SCENE_DESERT_COLOSSUS) {
Flags_SetSwitch(gPlayState, 24);
}
gSaveContext.sceneFlags[SCENE_GERUDO_VALLEY].swch |= (1 << 3);
if (gPlayState->sceneNum == SCENE_GERUDO_VALLEY) {
Flags_SetSwitch(gPlayState, 3);
}
gSaveContext.sceneFlags[SCENE_GRAVEYARD].swch |= (1 << 3);
if (gPlayState->sceneNum == SCENE_GRAVEYARD) {
Flags_SetSwitch(gPlayState, 3);
}
gSaveContext.sceneFlags[SCENE_KOKIRI_FOREST].swch |= (1 << 9);
if (gPlayState->sceneNum == SCENE_KOKIRI_FOREST) {
Flags_SetSwitch(gPlayState, 9);
}
gSaveContext.sceneFlags[SCENE_LAKE_HYLIA].swch |= (1 << 1);
if (gPlayState->sceneNum == SCENE_LAKE_HYLIA) {
Flags_SetSwitch(gPlayState, 1);
}
gSaveContext.sceneFlags[SCENE_LOST_WOODS].swch |= (1 << 4) | (1 << 18);
if (gPlayState->sceneNum == SCENE_LOST_WOODS) {
Flags_SetSwitch(gPlayState, 4);
Flags_SetSwitch(gPlayState, 18);
}
gSaveContext.sceneFlags[SCENE_ZORAS_RIVER].swch |= (1 << 3);
if (gPlayState->sceneNum == SCENE_ZORAS_RIVER) {
Flags_SetSwitch(gPlayState, 3);
}
ObjBean* bean = (ObjBean*)Actor_Find(&gPlayState->actorCtx, ACTOR_OBJ_BEAN, ACTORCAT_BG);
if (bean != nullptr) {
Flags_SetSwitch(gPlayState, bean->dyna.actor.params & 0x3F);
func_80B8FE00(bean);
}
AMMO(ITEM_BEAN) = 0;
}
}
break;
case RG_DOUBLE_DEFENSE:
@@ -3947,13 +3902,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) ==
RO_TRIFORCE_HUNT_WIN) {
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] =
static_cast<u32>(GAMEPLAYSTAT_TOTAL_TIME);
gSaveContext.ship.stats.gameComplete = 1;
Play_PerformSave(play);
Notification::Emit({
.message = "Game autosaved",
});
// Save and warp are deferred until item queue drains
GameInteractor_SetTriforceHuntCreditsWarpActive(true);
}
}
@@ -76,6 +76,7 @@ RANDO_ENUM_ITEM(RT_LH_WATER_HOOKSHOT)
RANDO_ENUM_ITEM(RT_GV_CRATE_HOVERS)
RANDO_ENUM_ITEM(RT_GV_CHILD_TENT)
RANDO_ENUM_ITEM(RT_GV_CHILD_CUCCO_JUMP)
RANDO_ENUM_ITEM(RT_GV_HOOKSHOT_BRIDGE)
RANDO_ENUM_ITEM(RT_PASS_GUARDS_WITH_NOTHING)
RANDO_ENUM_ITEM(RT_GF_WASTELAND_GATE_SIDEHOP_SKIP)
RANDO_ENUM_ITEM(RT_GF_ADULT_SKIP_WASTELAND_GATE)
@@ -1082,13 +1082,21 @@ void CheckTrackerWindow::DrawElement() {
}
UIWidgets::PushStyleCombobox(THEME_COLOR);
if (CVarGetInteger(CVAR_TRACKER_CHECK("SearchInputVisible"), 1)) {
if (checkSearch.Draw("", ImGui::GetContentRegionAvail().x - 6)) {
if (checkSearch.Draw("", ImGui::GetContentRegionAvail().x - 42)) {
UpdateFilters();
}
std::string checkSearchText = "";
checkSearchText = checkSearch.InputBuf;
std::string checkSearchText = checkSearch.InputBuf;
checkSearchText.erase(std::remove(checkSearchText.begin(), checkSearchText.end(), ' '),
checkSearchText.end());
ImGui::SameLine();
if (UIWidgets::Button(ICON_FA_ERASER, UIWidgets::ButtonOptions()
.Size(UIWidgets::Sizes::Inline)
.Color(THEME_COLOR)
.Padding(ImVec2(10.f, 6.f)))) {
checkSearch.Clear();
UpdateFilters();
doAreaScroll = true;
}
if (checkSearchText.length() < 1) {
ImGui::SameLine(20.0f);
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "Search...");
@@ -537,11 +537,24 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) {
: 500;
result.currentAmmo = gSaveContext.rupees;
break;
case ITEM_BOMBCHU:
result.currentCapacity = INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU ? 50 : 0;
case ITEM_BOMBCHU: {
auto bombchuBag = RAND_GET_OPTION(RSK_BOMBCHU_BAG);
uint8_t capacity = 0;
if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU) {
if (bombchuBag.Is(RO_BOMBCHU_BAG_PROGRESSIVE)) {
capacity = OTRGlobals::Instance->gRandoContext->GetBombchuCapacity();
} else {
capacity = 50;
}
}
result.currentCapacity = capacity;
result.maxCapacity = 50;
result.currentAmmo = AMMO(ITEM_BOMBCHU);
break;
}
case ITEM_BEAN:
result.currentCapacity = INV_CONTENT(ITEM_BEAN) == ITEM_BEAN ? 10 : 0;
result.maxCapacity = 10;
@@ -702,7 +715,7 @@ void DrawItemCount(ItemTrackerItem item, bool hideMax) {
bool shouldDisplayAmmo = trackerNumberDisplayMode == ITEM_TRACKER_NUMBER_AMMO ||
trackerNumberDisplayMode == ITEM_TRACKER_NUMBER_CURRENT_AMMO_ONLY ||
// These items have a static capacity, so display ammo instead
item.id == ITEM_BOMBCHU || item.id == ITEM_BEAN || item.id == QUEST_SKULL_TOKEN ||
item.id == ITEM_BEAN || item.id == QUEST_SKULL_TOKEN ||
item.id == ITEM_HEART_CONTAINER || item.id == ITEM_HEART_PIECE;
bool shouldDisplayMax = !(trackerNumberDisplayMode == ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY ||
@@ -1677,6 +1677,10 @@ void Settings::CreateOptions() {
OPT_TRICK(RT_GV_CHILD_CUCCO_JUMP, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::INTERMEDIATE },
"Gerudo Valley Jump Fence with Cucco", "GVCUC",
"Using cucco as child, it's possible to jumpslash over the gate.");
OPT_TRICK(RT_GV_HOOKSHOT_BRIDGE, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::ADVANCED },
"Gerudo Valley Bridge with only Hookshot", "GVHSBrg",
"Using Hookshot Extension and a precise setup, you can cross the broken bridge in Gerudo Valley with "
"only a Hookshot.");
OPT_TRICK(RT_PASS_GUARDS_WITH_NOTHING, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::NOVICE },
"Sneak Past Moving Gerudo Guards with No Items", "Guards",
"The logic normally guarantees Bow or Hookshot to stun them from a distance,"
@@ -29,6 +29,8 @@ extern "C" {
#include "src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h"
#include "src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.h"
#include "src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.h"
#include "src/overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.h"
#include "src/overlays/actors/ovl_Bg_Jya_Bombchuiwa/z_bg_jya_bombchuiwa.h"
#include <overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.h>
#include <overlays/actors/ovl_En_Ik/z_en_ik.h>
#include <objects/object_gnd/object_gnd.h>
@@ -328,6 +330,15 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
RateLimitedSuccessChime();
break;
}
case ACTOR_BG_JYA_BOMBCHUIWA: {
BgJyaBombchuiwa* bombchuiwa = (BgJyaBombchuiwa*)actor;
if (!(bombchuiwa->drawFlags & 4) && bombchuiwa->timer >= 0 && bombchuiwa->timer < 9) {
bombchuiwa->timer = 9;
}
*should = false;
RateLimitedSuccessChime();
break;
}
case ACTOR_EN_GO2: {
EnGo2* biggoron = (EnGo2*)actor;
biggoron->isAwake = true;
@@ -376,8 +387,14 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
RateLimitedSuccessChime();
break;
}
case ACTOR_OBJ_LIGHTSWITCH: {
ObjLightswitch* lightswitch = (ObjLightswitch*)actor;
lightswitch->toggleDelay = 0;
*should = false;
RateLimitedSuccessChime();
break;
}
case ACTOR_BG_ICE_SHUTTER:
case ACTOR_OBJ_LIGHTSWITCH:
case ACTOR_OBJ_SYOKUDAI:
case ACTOR_OBJ_TIMEBLOCK:
case ACTOR_EN_PO_SISTERS:
@@ -554,6 +571,7 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
}
break;
}
case VB_PLAY_BEAN_PLANTING_CS:
case VB_PLAY_EYEDROP_CREATION_ANIM:
case VB_PLAY_EYEDROPS_CS:
case VB_PLAY_DROP_FISH_FOR_JABU_CS:
@@ -1222,41 +1240,41 @@ void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) {
case FLAG_EVENT_CHECK_INF:
switch (flag) {
case EVENTCHKINF_SPOKE_TO_SARIA_ON_BRIDGE:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FAIRY_OCARINA).GetGIEntry_Copy();
TimeSaverQueueItem(RG_FAIRY_OCARINA);
break;
case EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_KOKIRI_EMERALD).GetGIEntry_Copy();
TimeSaverQueueItem(RG_KOKIRI_EMERALD);
break;
case EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_GORON_RUBY).GetGIEntry_Copy();
TimeSaverQueueItem(RG_GORON_RUBY);
break;
case EVENTCHKINF_USED_JABU_JABUS_BELLY_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_ZORA_SAPPHIRE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_ZORA_SAPPHIRE);
break;
case EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FOREST_MEDALLION).GetGIEntry_Copy();
TimeSaverQueueItem(RG_FOREST_MEDALLION);
break;
case EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FIRE_MEDALLION).GetGIEntry_Copy();
TimeSaverQueueItem(RG_FIRE_MEDALLION);
break;
case EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_WATER_MEDALLION).GetGIEntry_Copy();
TimeSaverQueueItem(RG_WATER_MEDALLION);
break;
case EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_LIGHT_ARROWS).GetGIEntry_Copy();
TimeSaverQueueItem(RG_LIGHT_ARROWS);
break;
case EVENTCHKINF_TIME_TRAVELED_TO_ADULT:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_LIGHT_MEDALLION).GetGIEntry_Copy();
TimeSaverQueueItem(RG_LIGHT_MEDALLION);
break;
}
break;
case FLAG_RANDOMIZER_INF:
switch (flag) {
case RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SHADOW_MEDALLION).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SHADOW_MEDALLION);
break;
case RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SPIRIT_MEDALLION).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SPIRIT_MEDALLION);
break;
}
break;
@@ -1268,22 +1286,22 @@ void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) {
case FLAG_RANDOMIZER_INF:
switch (flag) {
case RAND_INF_ZF_GREAT_FAIRY_REWARD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FARORES_WIND).GetGIEntry_Copy();
TimeSaverQueueItem(RG_FARORES_WIND);
break;
case RAND_INF_HC_GREAT_FAIRY_REWARD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_DINS_FIRE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_DINS_FIRE);
break;
case RAND_INF_COLOSSUS_GREAT_FAIRY_REWARD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_NAYRUS_LOVE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_NAYRUS_LOVE);
break;
case RAND_INF_DMT_GREAT_FAIRY_REWARD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_MAGIC_SINGLE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_MAGIC_SINGLE);
break;
case RAND_INF_DMC_GREAT_FAIRY_REWARD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_MAGIC_DOUBLE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_MAGIC_DOUBLE);
break;
case RAND_INF_OGC_GREAT_FAIRY_REWARD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_DOUBLE_DEFENSE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_DOUBLE_DEFENSE);
break;
}
break;
@@ -1309,47 +1327,44 @@ void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) {
case FLAG_EVENT_CHECK_INF:
switch (flag) {
case EVENTCHKINF_LEARNED_ZELDAS_LULLABY:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_ZELDAS_LULLABY).GetGIEntry_Copy();
TimeSaverQueueItem(RG_ZELDAS_LULLABY);
break;
case EVENTCHKINF_LEARNED_MINUET_OF_FOREST:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_MINUET_OF_FOREST).GetGIEntry_Copy();
TimeSaverQueueItem(RG_MINUET_OF_FOREST);
break;
case EVENTCHKINF_LEARNED_BOLERO_OF_FIRE:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_BOLERO_OF_FIRE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_BOLERO_OF_FIRE);
break;
case EVENTCHKINF_LEARNED_SERENADE_OF_WATER:
vanillaQueuedItemEntry =
Rando::StaticData::RetrieveItem(RG_SERENADE_OF_WATER).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SERENADE_OF_WATER);
break;
case EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT:
vanillaQueuedItemEntry =
Rando::StaticData::RetrieveItem(RG_REQUIEM_OF_SPIRIT).GetGIEntry_Copy();
TimeSaverQueueItem(RG_REQUIEM_OF_SPIRIT);
break;
case EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL:
vanillaQueuedItemEntry =
Rando::StaticData::RetrieveItem(RG_NOCTURNE_OF_SHADOW).GetGIEntry_Copy();
TimeSaverQueueItem(RG_NOCTURNE_OF_SHADOW);
break;
case EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_PRELUDE_OF_LIGHT).GetGIEntry_Copy();
TimeSaverQueueItem(RG_PRELUDE_OF_LIGHT);
break;
case EVENTCHKINF_LEARNED_SARIAS_SONG:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SARIAS_SONG).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SARIAS_SONG);
break;
case EVENTCHKINF_LEARNED_SONG_OF_TIME:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SONG_OF_TIME).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SONG_OF_TIME);
break;
case EVENTCHKINF_LEARNED_SONG_OF_STORMS:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SONG_OF_STORMS).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SONG_OF_STORMS);
break;
case EVENTCHKINF_LEARNED_SUNS_SONG:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SUNS_SONG).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SUNS_SONG);
break;
}
break;
case FLAG_RANDOMIZER_INF:
switch (flag) {
case RAND_INF_LEARNED_EPONA_SONG:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_EPONAS_SONG).GetGIEntry_Copy();
TimeSaverQueueItem(RG_EPONAS_SONG);
break;
}
break;
@@ -1396,12 +1411,10 @@ static void TimeSaverRegisterHooks() {
TimeSaverOnSceneInitHandler);
COND_HOOK(OnVanillaBehavior, true, TimeSaverOnVanillaBehaviorHandler);
COND_HOOK(OnActorInit, true, TimeSaverOnActorInitHandler);
COND_HOOK(OnSceneInit, true, [](int16_t sceneNum) { successChimeCooldown = 0; });
// item queue for use outside rando, rando has its own queue
COND_HOOK(OnLoadGame, !IS_RANDO, [](int32_t fileNum) {
vanillaQueuedItemEntry = GET_ITEM_NONE;
successChimeCooldown = 0;
});
COND_HOOK(OnLoadGame, !IS_RANDO, [](int32_t fileNum) { vanillaQueuedItemEntry = GET_ITEM_NONE; });
COND_HOOK(OnItemReceive, !IS_RANDO, TimeSaverOnItemReceiveHandler);
COND_HOOK(OnPlayerUpdate, !IS_RANDO, TimeSaverOnPlayerUpdateHandler);
COND_HOOK(OnFlagSet,
+8 -21
View File
@@ -6,7 +6,6 @@
#include <ship/resource/type/Json.h>
#include <libultraship/classes.h>
#include <nlohmann/json.hpp>
#include <spdlog/fmt/fmt.h>
#include "soh/ShipInit.hpp"
#include "message_data_static.h"
@@ -37,11 +36,10 @@ nlohmann::json fileChooseMap = nullptr;
std::string GetParameritizedText(std::string key, TextBank bank, const char* arg) {
switch (bank) {
case TEXT_BANK_SCENES: {
return sceneMap[key].get<std::string>();
break;
return sceneMap.value(key, "unknown");
}
case TEXT_BANK_MISC: {
auto value = miscMap[key].get<std::string>();
auto value = miscMap.value(key, "unknown");
std::string searchString = "$0";
size_t index = value.find(searchString);
@@ -49,15 +47,11 @@ std::string GetParameritizedText(std::string key, TextBank bank, const char* arg
if (index != std::string::npos) {
assert(arg != nullptr);
value.replace(index, searchString.size(), std::string(arg));
return value;
} else {
return value;
}
break;
return value;
}
case TEXT_BANK_KALEIDO: {
auto value = kaleidoMap[key].get<std::string>();
auto value = kaleidoMap.value(key, "unknown");
std::string searchString = "$0";
size_t index = value.find(searchString);
@@ -65,15 +59,11 @@ std::string GetParameritizedText(std::string key, TextBank bank, const char* arg
if (index != std::string::npos) {
assert(arg != nullptr);
value.replace(index, searchString.size(), std::string(arg));
return value;
} else {
return value;
}
break;
return value;
}
case TEXT_BANK_FILECHOOSE: {
auto value = fileChooseMap[key].get<std::string>();
auto value = fileChooseMap.value(key, "unknown");
std::string searchString = "$0";
size_t index = value.find(searchString);
@@ -81,14 +71,11 @@ std::string GetParameritizedText(std::string key, TextBank bank, const char* arg
if (index != std::string::npos) {
assert(arg != nullptr);
value.replace(index, searchString.size(), std::string(arg));
return value;
} else {
return value;
}
break;
return value;
}
}
return "unknown";
}
const char* GetLanguageCode() {
@@ -5,8 +5,7 @@
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>
#include <spdlog/fmt/fmt.h>
#include <regex>
#include "soh/OTRGlobals.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include <z64.h>
@@ -625,37 +624,37 @@ CrowdControl::Effect* CrowdControl::ParseMessage(nlohmann::json dataReceived) {
case kEffectTpLinksHouse:
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
GI_TP_DEST_LINKSHOUSE;
ENTR_LINKS_HOUSE_CHILD_SPAWN;
break;
case kEffectTpMinuet:
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
GI_TP_DEST_MINUET;
ENTR_SACRED_FOREST_MEADOW_WARP_PAD;
break;
case kEffectTpBolero:
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
GI_TP_DEST_BOLERO;
ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD;
break;
case kEffectTpSerenade:
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
GI_TP_DEST_SERENADE;
ENTR_LAKE_HYLIA_WARP_PAD;
break;
case kEffectTpRequiem:
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
GI_TP_DEST_REQUIEM;
ENTR_DESERT_COLOSSUS_WARP_PAD;
break;
case kEffectTpNocturne:
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
GI_TP_DEST_NOCTURNE;
ENTR_GRAVEYARD_WARP_PAD;
break;
case kEffectTpPrelude:
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
GI_TP_DEST_PRELUDE;
ENTR_TEMPLE_OF_TIME_WARP_PAD;
break;
default:
+10 -12
View File
@@ -403,7 +403,7 @@ void OTRGlobals::RunExtract(int argc, char* argv[]) {
std::vector<std::string> args;
if (argc > 1) {
for (int i = 1; i < argc; i++) {
args.push_back(argv[argc]);
args.push_back(argv[i]);
}
}
Extractor extract;
@@ -445,6 +445,11 @@ void OTRGlobals::RunExtract(int argc, char* argv[]) {
std::shared_ptr<BS::thread_pool> threadPool = std::make_shared<BS::thread_pool>(1);
std::optional<std::future<void>> extractionTask;
#if not defined(__SWITCH__) && not defined(__WIIU__)
CheckAndCreateModFolder();
#endif
while (!extractDone) {
if (SohGui::PopupsQueued() > 0 || extractionTask.has_value()) {
goto render;
@@ -457,7 +462,7 @@ void OTRGlobals::RunExtract(int argc, char* argv[]) {
#elif (defined(__WIIU__) || defined(__SWITCH__))
extractStep = ES_VERIFY;
#else
extractStep = ES_EXTRACT;
extractStep = args.empty() ? ES_EXTRACT : ES_EXTRACT_ARGS;
#endif
} else {
std::string msg;
@@ -542,11 +547,7 @@ void OTRGlobals::RunExtract(int argc, char* argv[]) {
"OK", "", [&]() { exit(0); });
} else {
windowsStep = WS_DONE;
if (args.size() > 0) {
extractStep = ES_EXTRACT_ARGS;
} else {
extractStep = ES_EXTRACT;
}
extractStep = args.empty() ? ES_EXTRACT : ES_EXTRACT_ARGS;
}
continue;
}
@@ -557,7 +558,7 @@ void OTRGlobals::RunExtract(int argc, char* argv[]) {
}
case ES_EXTRACT_ARGS: {
#if !defined(__SWITCH__) && !defined(__WIIU__)
if (args.size() == 0) {
if (args.empty()) {
SohGui::RegisterPopup(
"Run Ship of Harkinian", "All files have been processed. Run SoH?", "Yes", "No",
[&]() {
@@ -760,10 +761,6 @@ void OTRGlobals::RunExtract(int argc, char* argv[]) {
#elif defined(__WIIU__)
Ship::WiiU::Init(appShortName);
#endif
#if not defined(__SWITCH__) && not defined(__WIIU__)
CheckAndCreateModFolder();
#endif
}
void OTRGlobals::Initialize() {
@@ -2539,6 +2536,7 @@ bool SoH_HandleConfigDrop(char* filePath) {
->ClearBindings();
Rando::Settings::GetInstance()->UpdateAllOptions();
SohGui::MarkRandomizerMenusDirty();
gui->SaveConsoleVariablesNextFrame();
ShipInit::Init("*");
+1
View File
@@ -28,6 +28,7 @@ static std::map<int32_t, const char*> languages = {
};
void UpdateMenuTricks();
void UpdateMenuLocations();
void MarkRandomizerMenusDirty();
class SohMenu : public Ship::Menu {
public:
+7 -5
View File
@@ -491,16 +491,18 @@ void SohMenu::AddMenuEnhancements() {
AddWidget(path, "Skip Tower Escape", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("TimeSavers.SkipTowerEscape"))
.Options(CheckboxOptions().Tooltip("Skip the tower escape sequence between Ganondorf and Ganon."));
AddWidget(path, "Skip Scarecrow's Song", WIDGET_CVAR_CHECKBOX)
AddWidget(path, "Skip Playing Scarecrow's Song", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("InstantScarecrow"))
.PreFunc([](WidgetInfo& info) {
info.options->disabled =
IS_RANDO && OTRGlobals::Instance->gRandoContext->GetOption(RSK_SKIP_SCARECROWS_SONG);
info.options->disabledTooltip = "This setting is forcefully enabled because a randomized "
"save file with the option \"Skip Scarecrow Song\" is currently loaded.";
info.options->disabledTooltip = "This setting is forcefully enabled because a randomized save "
"file with the option \"Skip Scarecrow's Song\" is currently loaded.";
})
.Options(CheckboxOptions().Tooltip(
"Pierre appears when an Ocarina is pulled out. Requires learning the Scarecrow's Song first."));
"Pierre appears when an Ocarina is pulled out. Requires learning the Scarecrow's Song first.\n"
"Without the randomizer option \"Skip Scarecrow's Song\" enabled for a seed, this still requires you "
"to teach the scarecrow the song as both ages before summoning."));
AddWidget(path, "Faster Rupee Accumulator", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("FasterRupeeAccumulator"))
.Options(CheckboxOptions().Tooltip("Causes your Wallet to fill and empty faster when you gain or lose money."));
@@ -1799,7 +1801,7 @@ void SohMenu::AddMenuEnhancements() {
.CVar(CVAR_CHEAT("SpeedModifier.DoesntChangeJump"));
AddWidget(path, "Multiplier:", WIDGET_CVAR_SLIDER_FLOAT)
.CVar(CVAR_CHEAT("SpeedModifier.Value"))
.Options(FloatSliderOptions().IsPercentage().Min(1.0f).Max(5.0f).DefaultValue(1.0f).ShowButtons(true).Format(
.Options(FloatSliderOptions().IsPercentage().Min(0.01f).Max(5.0f).DefaultValue(1.0f).ShowButtons(true).Format(
"%.0f%%"));
AddWidget(path, "Button Combination:", WIDGET_CVAR_BTN_SELECTOR)
.CVar(CVAR_CHEAT("SpeedModifier.Btn"))
+5
View File
@@ -181,6 +181,11 @@ void DrawLocationsMenu(WidgetInfo& info) {
ImGui::EndDisabled();
}
void MarkRandomizerMenusDirty() {
locationsDirty = true;
tricksDirty = true;
}
void UpdateMenuLocations() {
RandomizerCheckObjects::UpdateImGuiVisibility();
// todo: this efficiently when we build out cvar array support
+1 -10
View File
@@ -1250,16 +1250,7 @@ bool CVarBtnSelector(const char* label, const char* cvarName, const BtnSelectorO
}
} // namespace UIWidgets
ImVec4 GetRandomValue() {
ImVec4 NewColor;
NewColor.x = (float)ShipUtils::RandomDouble();
NewColor.y = (float)ShipUtils::RandomDouble();
NewColor.z = (float)ShipUtils::RandomDouble();
return NewColor;
}
ImVec4 GetRandomValue(uint32_t seed, uint64_t* state) {
ShipUtils::RandInit(seed, state);
ImVec4 GetRandomValue(uint64_t* state) {
ImVec4 NewColor;
NewColor.x = (float)ShipUtils::RandomDouble(state);
NewColor.y = (float)ShipUtils::RandomDouble(state);
+1 -2
View File
@@ -1089,8 +1089,7 @@ void InsertHelpHoverText(const std::string& text);
void InsertHelpHoverText(const char* text);
} // namespace UIWidgets
ImVec4 GetRandomValue();
ImVec4 GetRandomValue(uint32_t seed, uint64_t* state = nullptr);
ImVec4 GetRandomValue(uint64_t* state = nullptr);
Color_RGBA8 RGBA8FromVec(ImVec4 vec);
ImVec4 VecFromRGBA8(Color_RGBA8 color);
+16 -13
View File
@@ -2032,8 +2032,10 @@ s32 GiveItemEntryWithoutActor(PlayState* play, GetItemEntry getItemEntry) {
PLAYER_STATE1_CLIMBING_LEDGE | PLAYER_STATE1_JUMPING | PLAYER_STATE1_FREEFALL | PLAYER_STATE1_FIRST_PERSON |
PLAYER_STATE1_CLIMBING_LADDER)) &&
Player_GetExplosiveHeld(player) < 0) {
if (((player->heldActor != NULL) && ((getItemEntry.getItemId > GI_NONE) && (getItemEntry.getItemId < GI_MAX)) ||
(IS_RANDO && (getItemEntry.getItemId > RG_NONE) && (getItemEntry.getItemId < RG_MAX))) ||
if (((player->heldActor != NULL && (getItemEntry.modIndex == MOD_NONE && getItemEntry.getItemId > GI_NONE &&
getItemEntry.getItemId < GI_MAX)) ||
(getItemEntry.modIndex == MOD_RANDOMIZER && getItemEntry.getItemId > RG_NONE &&
getItemEntry.getItemId < RG_MAX)) ||
(!(player->stateFlags1 & (PLAYER_STATE1_CARRYING_ACTOR | PLAYER_STATE1_IN_CUTSCENE)))) {
if ((getItemEntry.getItemId != GI_NONE)) {
player->getItemEntry = getItemEntry;
@@ -2073,8 +2075,10 @@ s32 GiveItemEntryFromActor(Actor* actor, PlayState* play, GetItemEntry getItemEn
PLAYER_STATE1_CLIMBING_LADDER)) &&
Player_GetExplosiveHeld(player) < 0) {
if ((((player->heldActor != NULL) || (actor == player->talkActor)) &&
((!IS_RANDO && ((getItemEntry.getItemId > GI_NONE) && (getItemEntry.getItemId < GI_MAX))) ||
(IS_RANDO && ((getItemEntry.getItemId > RG_NONE) && (getItemEntry.getItemId < RG_MAX))))) ||
((getItemEntry.getItemId == MOD_NONE &&
((getItemEntry.getItemId > GI_NONE) && (getItemEntry.getItemId < GI_MAX))) ||
(getItemEntry.getItemId == MOD_RANDOMIZER &&
((getItemEntry.getItemId > RG_NONE) && (getItemEntry.getItemId < RG_MAX))))) ||
(!(player->stateFlags1 & (PLAYER_STATE1_CARRYING_ACTOR | PLAYER_STATE1_IN_CUTSCENE)))) {
if ((actor->xzDistToPlayer < xzRange) && (fabsf(actor->yDistToPlayer) < yRange)) {
s16 yawDiff = actor->yawTowardsPlayer - player->actor.shape.rot.y;
@@ -2118,8 +2122,7 @@ s32 Actor_OfferGetItem(Actor* actor, PlayState* play, s32 getItemId, f32 xzRange
PLAYER_STATE1_CLIMBING_LADDER)) &&
Player_GetExplosiveHeld(player) < 0) {
if ((((player->heldActor != NULL) || (actor == player->talkActor)) &&
((!IS_RANDO && ((getItemId > GI_NONE) && (getItemId < GI_MAX))) ||
(IS_RANDO && ((getItemId > RG_NONE) && (getItemId < RG_MAX))))) ||
((getItemId > GI_NONE) && (getItemId < GI_MAX))) ||
(!(player->stateFlags1 & (PLAYER_STATE1_CARRYING_ACTOR | PLAYER_STATE1_IN_CUTSCENE)))) {
if ((actor->xzDistToPlayer < xzRange) && (fabsf(actor->yDistToPlayer) < yRange)) {
s16 yawDiff = actor->yawTowardsPlayer - player->actor.shape.rot.y;
@@ -3471,7 +3474,7 @@ Actor* Actor_SpawnEntry(ActorContext* actorCtx, ActorEntry* actorEntry, PlayStat
gMapLoading = 1;
Actor* ret;
if (GameInteractor_Should(VB_SPAWN_ACTOR_ENTRY, true, actorCtx, actorEntry, play, ret)) {
if (GameInteractor_Should(VB_SPAWN_ACTOR_ENTRY, true, actorCtx, actorEntry, play, &ret)) {
ret = Actor_Spawn(actorCtx, play, actorEntry->id, actorEntry->pos.x, actorEntry->pos.y, actorEntry->pos.z,
actorEntry->rot.x, actorEntry->rot.y, actorEntry->rot.z, actorEntry->params);
}
@@ -3491,12 +3494,6 @@ Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play) {
// Execute before actor memory is freed
GameInteractor_ExecuteOnActorDestroy(actor);
dbEntry = ActorDB_Retrieve(actor->id);
if (HREG(20) != 0) {
osSyncPrintf("アクタークラス削除 [%s]\n", dbEntry->name); // "Actor class deleted [%s]"
}
if ((player != NULL) && (actor == player->focusActor)) {
Player_ReleaseLockOn(player);
Camera_ChangeMode(Play_GetCamera(play, Play_GetActiveCamId(play)), 0);
@@ -3517,6 +3514,12 @@ Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play) {
Audio_StopSfxByPos(&actor->projectedPos);
Actor_Destroy(actor, play);
dbEntry = ActorDB_Retrieve(actor->id);
if (HREG(20) != 0) {
osSyncPrintf("アクタークラス削除 [%s]\n", dbEntry->name); // "Actor class deleted [%s]"
}
newHead = Actor_RemoveFromCategory(play, actorCtx, actor);
// #region SOH [ObjectExtension]
+4 -3
View File
@@ -3,7 +3,6 @@
#include "objects/gameplay_field_keep/gameplay_field_keep.h"
#include "objects/object_link_boy/object_link_boy.h"
#include "objects/object_link_child/object_link_child.h"
#include "objects/object_triforce_spot/object_triforce_spot.h"
#include "overlays/actors/ovl_Demo_Effect/z_demo_effect.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
@@ -1906,8 +1905,10 @@ void Player_PostLimbDrawGameplay(PlayState* play, s32 limbIndex, Gfx** dList, Ve
}
if (this->actor.scale.y >= 0.0f) {
if (GameInteractor_Should(VB_DRAW_ADDITIONAL_RETICLES, (this->heldItemAction == PLAYER_IA_HOOKSHOT) ||
(this->heldItemAction == PLAYER_IA_LONGSHOT))) {
if (GameInteractor_Should(VB_DRAW_ADDITIONAL_RETICLES,
(this->heldItemAction == PLAYER_IA_HOOKSHOT) ||
(this->heldItemAction == PLAYER_IA_LONGSHOT),
this)) {
Matrix_MultVec3f(&D_80126184, &this->unk_3C8);
if (heldActor != NULL) {
@@ -7,6 +7,7 @@
#include "z_bg_spot06_objects.h"
#include "objects/object_spot06_objects/object_spot06_objects.h"
#include "soh/Enhancements/custom-message/CustomMessageTypes.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_HOOKSHOT_PULLS_ACTOR
@@ -266,7 +267,7 @@ void BgSpot06Objects_GateWaitForSwitch(BgSpot06Objects* this, PlayState* play) {
* This is where the gate waits a few frames before rising after the switch is set.
*/
void BgSpot06Objects_GateWaitToOpen(BgSpot06Objects* this, PlayState* play) {
if (this->timer != 0) {
if (GameInteractor_Should(VB_BG_SPOT06_OBJECTS_GATE_SKIP, this->timer != 0, this)) {
this->timer--;
}
@@ -604,7 +605,7 @@ void BgSpot06Objects_WaterPlaneCutsceneRise(BgSpot06Objects* this, PlayState* pl
play->roomCtx.unk_74[0] = 0; // Apply the moving under water texture to lake hylia ground
}
} else {
Math_SmoothStepToF(&this->lakeHyliaWaterLevel, 1.0f, 0.1f, 1.0f, 0.001f);
Math_SmoothStepToF(&this->lakeHyliaWaterLevel, 1.0f, 0.1f, IS_RANDO ? 10.0f : 1.0f, 0.001f);
play->colCtx.colHeader->waterBoxes[LHWB_GERUDO_VALLEY_RIVER_LOWER].ySurface = WATER_LEVEL_RIVER_LOWERED;
play->colCtx.colHeader->waterBoxes[LHWB_MAIN_1].ySurface = this->dyna.actor.world.pos.y;
play->colCtx.colHeader->waterBoxes[LHWB_MAIN_2].ySurface = this->dyna.actor.world.pos.y;
@@ -634,7 +635,7 @@ void BgSpot06Objects_WaterPlaneCutsceneLower(BgSpot06Objects* this, PlayState* p
this->actionFunc = BgSpot06Objects_DoNothing;
} else {
// Go slightly beyond -681 so the smoothing doesn't slow down too much (matches the reverse of water rise func)
Math_SmoothStepToF(&this->lakeHyliaWaterLevel, -682.0f, 0.1f, 1.0f, 0.001f);
Math_SmoothStepToF(&this->lakeHyliaWaterLevel, -682.0f, 0.1f, 10.0f, 0.01f);
play->colCtx.colHeader->waterBoxes[LHWB_GERUDO_VALLEY_RIVER_LOWER].ySurface = WATER_LEVEL_RIVER_LOWERED;
play->colCtx.colHeader->waterBoxes[LHWB_GERUDO_VALLEY_RIVER_LOWER].zMin = WATER_LEVEL_RIVER_LOWER_Z - 50;
play->colCtx.colHeader->waterBoxes[LHWB_MAIN_1].ySurface = yPos;
@@ -4246,7 +4246,7 @@ void BossTw_BlastIce(BossTw* this, PlayState* play) {
Math_ApproachF(&sKotakePtr->workf[UNK_F9], 80.0f, 1.0f, 3.0f);
Math_ApproachF(&sKotakePtr->workf[UNK_F11], 255.0f, 1.0f, 10.0f);
Math_ApproachF(&sKotakePtr->workf[UNK_F12], 0.04f, 0.1f, 0.002f);
Math_ApproachF(&sKotakePtr->workf[UNK_F16], 70.0f, 1.0f, 5.0f);
Math_ApproachF(&sKotakePtr->workf[UNK_F16], 70.0f, 1.0f, -5.0f);
if ((this->timers[0] == 70) || (this->timers[0] == 30)) {
sKotakePtr->workf[UNK_F16] = 10.0f;
@@ -5037,9 +5037,8 @@ void BossTw_DrawEffects(PlayState* play) {
if (sp18F == 0) {
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gTwinrovaIceSurroundingPlayerMaterialDL));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, 255);
gSPSegment(
POLY_XLU_DISP++, 8,
Gfx_TwoTexScrollEx(play->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, 0, 0x20, 0x20, 0, 0, 0, 0));
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScroll(play->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, 0, 0x20, 0x20));
sp18F++;
BossTw_InitRand(1, 0x71AC, 0x263A);
}
@@ -5091,9 +5090,8 @@ void BossTw_DrawEffects(PlayState* play) {
}
gSPSegment(POLY_XLU_DISP++, 8,
Gfx_TwoTexScrollEx(play->state.gfxCtx, 0, (currentEffect->frame * 3) & 0x7F,
(-currentEffect->frame * 15) & 0xFF, 0x20, 0x40, 1, 0, 0, 0x20, 0x20, 3, -15,
0, 0));
Gfx_TwoTexScroll(play->state.gfxCtx, 0, (currentEffect->frame * 3) & 0x7F,
(-currentEffect->frame * 15) & 0xFF, 0x20, 0x40, 1, 0, 0, 0x20, 0x20));
Matrix_Translate(currentEffect->pos.x, currentEffect->pos.y, currentEffect->pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&play->billboardMtxF);
Matrix_Scale(currentEffect->workf[EFF_SCALE], currentEffect->workf[EFF_SCALE], 1.0f, MTXMODE_APPLY);
+2 -15
View File
@@ -5,6 +5,7 @@
#include "soh/OTRGlobals.h"
#include "soh/ResourceManagerHelpers.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/Enhancements/randomizer/item_category_adj.h"
#define FLAGS 0
@@ -580,21 +581,7 @@ void EnBox_UpdateTexture(EnBox* this, PlayState* play) {
this->dyna.actor.room != 6); // Exclude treasure game chests except for the final room
if (!isVanilla) {
getItemCategory = chestItem.getItemCategory;
// If they have bombchus, don't consider the bombchu item major
if ((INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU &&
((chestItem.modIndex == MOD_RANDOMIZER && chestItem.getItemId == RG_PROGRESSIVE_BOMBCHU_BAG) ||
(chestItem.modIndex == MOD_NONE &&
(chestItem.getItemId == GI_BOMBCHUS_5 || chestItem.getItemId == GI_BOMBCHUS_10 ||
chestItem.getItemId == GI_BOMBCHUS_20)))) ||
// If it's a bottle and they already have one, consider the item lesser
((chestItem.modIndex == MOD_RANDOMIZER && chestItem.getItemId >= RG_BOTTLE_WITH_RED_POTION &&
chestItem.getItemId <= RG_BOTTLE_WITH_POE) ||
(chestItem.modIndex == MOD_NONE &&
(chestItem.getItemId == GI_BOTTLE || chestItem.getItemId == GI_MILK_BOTTLE)) &&
gSaveContext.inventory.items[SLOT_BOTTLE_1] != ITEM_NONE)) {
getItemCategory = ITEM_CATEGORY_LESSER;
}
getItemCategory = Randomizer_AdjustItemCategory(chestItem);
}
switch (this->type) {
+2 -3
View File
@@ -2,7 +2,6 @@
#include "objects/object_fz/object_fz.h"
#include "soh/frame_interpolation.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ObjectExtension/ActorMaximumHealth.h"
#define FLAGS \
(ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_CULLING_DISABLED | \
@@ -887,8 +886,8 @@ void EnFz_DrawIceSmoke(EnFz* this, PlayState* play) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, iceSmoke->primAlpha);
gSPSegment(POLY_XLU_DISP++, 0x08,
Gfx_TwoTexScrollEx(play->state.gfxCtx, 0, 3 * (iceSmoke->timer + (3 * i)),
15 * (iceSmoke->timer + (3 * i)), 32, 64, 1, 0, 0, 32, 32, 3, 15, 0, 0));
Gfx_TwoTexScroll(play->state.gfxCtx, 0, 3 * (iceSmoke->timer + (3 * i)),
15 * (iceSmoke->timer + (3 * i)), 32, 64, 1, 0, 0, 32, 32));
Matrix_Translate(iceSmoke->pos.x, iceSmoke->pos.y, iceSmoke->pos.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&play->billboardMtxF);
Matrix_Scale(iceSmoke->xyScale, iceSmoke->xyScale, 1.0f, MTXMODE_APPLY);
+1 -1
View File
@@ -1183,7 +1183,7 @@ void func_80A99048(EnKo* this, PlayState* play) {
if (ENKO_TYPE == ENKO_TYPE_CHILD_5) {
this->collider.base.ocFlags1 |= 0x40;
}
if (GameInteractor_Should(VB_KOKIRI_GET_FOREST_QUEST_STATE2, false, this)) {
if (GameInteractor_Should(VB_KOKIRI_GET_FOREST_QUEST_STATE2, true, this)) {
this->forestQuestState = EnKo_GetForestQuestState2(this);
}
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, sOsAnimeLookup[ENKO_TYPE][this->forestQuestState]);
+3 -1
View File
@@ -150,7 +150,9 @@ void EnMs_Talk(EnMs* this, PlayState* play) {
void EnMs_Sell(EnMs* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) {
Rupees_ChangeBy(-sPrices[BEANS_BOUGHT]);
if (GameInteractor_Should(VB_MAGIC_BEAN_SALESMAN_TAKE_MONEY, true, this)) {
Rupees_ChangeBy(-sPrices[BEANS_BOUGHT]);
}
this->actor.parent = NULL;
this->actionFunc = EnMs_TalkAfterPurchase;
} else {
@@ -432,6 +432,8 @@ static FishingEffect sFishingEffects[FISHING_EFFECT_COUNT];
static Vec3f sStreamSoundProjectedPos;
static s16 sFishOnHandParams;
f32 Fishing_GetMinimumRequiredScore();
u8 AllHyruleLoaches() {
return CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0) &&
CVarGetInteger(CVAR_ENHANCEMENT("AllHyruleLoaches"), 0);
@@ -904,12 +906,16 @@ void Fishing_Init(Actor* thisx, PlayState* play2) {
if (sLinkAge == LINK_AGE_CHILD) {
if ((HIGH_SCORE(HS_FISHING) & HS_FISH_LENGTH_CHILD) != 0) {
sFishingRecordLength = HIGH_SCORE(HS_FISHING) & HS_FISH_LENGTH_CHILD;
} else if (CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0)) {
sFishingRecordLength = Fishing_GetMinimumRequiredScore();
} else {
sFishingRecordLength = 40.0f; // 6 lbs
}
} else {
if ((HIGH_SCORE(HS_FISHING) & HS_FISH_LENGTH_ADULT) != 0) {
sFishingRecordLength = (HIGH_SCORE(HS_FISHING) & HS_FISH_LENGTH_ADULT) >> 0x18;
} else if (CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0)) {
sFishingRecordLength = Fishing_GetMinimumRequiredScore();
} else {
sFishingRecordLength = 45.0f; // 7 lbs
}
@@ -551,7 +551,9 @@ void ObjBean_WaitForBean(ObjBean* this, PlayState* play) {
void func_80B8FE00(ObjBean* this) {
this->actionFunc = func_80B8FE3C;
ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_LEAVES);
this->timer = 60;
if (GameInteractor_Should(VB_PLAY_BEAN_PLANTING_CS, true)) {
this->timer = 60;
}
}
// Link is looking at the soft soil
@@ -256,9 +256,8 @@ void ObjSwitch_SetOn(ObjSwitch* this, PlayState* play) {
} else {
OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR);
}
this->cooldownOn = true;
}
this->cooldownOn = true;
}
}
@@ -271,8 +270,8 @@ void ObjSwitch_SetOff(ObjSwitch* this, PlayState* play) {
if ((this->dyna.actor.params >> 4 & 7) == 1) {
if (GameInteractor_Should(VB_PLAY_ONEPOINT_ACTOR_CS, true, this)) {
OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR);
this->cooldownOn = true;
}
this->cooldownOn = true;
}
}
}
@@ -6063,7 +6063,9 @@ s32 Player_ActionHandler_13(Player* this, PlayState* play) {
Inventory_ChangeAmmo(ITEM_BEAN, -1);
Player_SetupActionPreserveItemAction(play, this, Player_Action_8084279C, 0);
this->stateFlags1 |= PLAYER_STATE1_IN_CUTSCENE;
this->av2.actionVar2 = 0x50;
if (GameInteractor_Should(VB_PLAY_BEAN_PLANTING_CS, true)) {
this->av2.actionVar2 = 0x50;
}
this->av1.actionVar1 = -1;
}
talkActor->flags |= ACTOR_FLAG_TALK;
@@ -7337,8 +7339,9 @@ s32 Player_ActionHandler_2(Player* this, PlayState* play) {
// getting bombchus need to show the cutscene) and whenever the player doesn't have the item yet. In
// rando, we're overruling this because we need to keep showing the cutscene because those items can be
// randomized and thus it's important to keep showing the cutscene.
uint8_t showItemCutscene = play->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY ||
Item_CheckObtainability(giEntry.itemId) == ITEM_NONE || IS_RANDO;
uint8_t showItemCutscene = play->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY || IS_RANDO ||
giEntry.modIndex == MOD_RANDOMIZER ||
Item_CheckObtainability(giEntry.itemId) == ITEM_NONE;
// Only skip cutscenes for drops when they're items/consumables from bushes/rocks/enemies.
uint8_t isDropToSkip =
@@ -7357,8 +7360,8 @@ s32 Player_ActionHandler_2(Player* this, PlayState* play) {
// the player already has because those items could be a randomized item coming from scrubs,
// freestanding PoH's and keys. So we need to once again overrule this specifically for items coming
// from bushes/rocks/enemies when the player has already picked that item up.
uint8_t skipItemCutsceneRando =
IS_RANDO && Item_CheckObtainability(giEntry.itemId) != ITEM_NONE && isDropToSkip;
uint8_t skipItemCutsceneRando = IS_RANDO && giEntry.modIndex == MOD_NONE &&
Item_CheckObtainability(giEntry.itemId) != ITEM_NONE && isDropToSkip;
// Show cutscene when picking up a item.
if (showItemCutscene && !skipItemCutscene && !skipItemCutsceneRando) {
@@ -122,8 +122,8 @@ void EffectSsEnIce_Draw(PlayState* play, u32 index, EffectSs* this) {
Gfx_SetupDL_25Xlu(play->state.gfxCtx);
func_8002EB44(&this->pos, &play->view.eye, &hiliteLightDir, play->state.gfxCtx);
gSPSegment(POLY_XLU_DISP++, 0x08,
Gfx_TwoTexScrollEx(play->state.gfxCtx, 0, 0, gameplayFrames & 0xFF, 0x20, 0x10, 1, 0,
(gameplayFrames * 2) & 0xFF, 0x40, 0x20, 0, 1, 0, 2));
Gfx_TwoTexScroll(play->state.gfxCtx, 0, 0, gameplayFrames & 0xFF, 0x20, 0x10, 1, 0,
(gameplayFrames * 2) & 0xFF, 0x40, 0x20));
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB,
this->rPrimColorA);
gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, (u32)alpha);