mirror of
https://github.com/HarbourMasters/Shipwright
synced 2026-10-21 10:51:32 -04:00
Merge branch 'develop-dewey' into merge-dewey
This commit is contained in:
@@ -82,11 +82,7 @@ if (WIN32)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED VCPKG_ROOT)
|
||||
if(NOT DEFINED ENV{VCPKG_ROOT})
|
||||
set(VCPKG_ROOT ${VCPKG_FALLBACK_ROOT})
|
||||
else()
|
||||
set(VCPKG_ROOT $ENV{VCPKG_ROOT})
|
||||
endif()
|
||||
set(VCPKG_ROOT ${VCPKG_FALLBACK_ROOT})
|
||||
endif()
|
||||
|
||||
# Installs a new copy of Vcpkg or updates an existing one
|
||||
|
||||
@@ -79,7 +79,8 @@ If you want to playtest a continuous integration build, you can find them at the
|
||||
|
||||
* [Windows](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-windows.zip)
|
||||
* [macOS](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-mac.zip)
|
||||
* [Linux](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-linux.zip)
|
||||
* [Linux X86_64](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-linux-x86_64.zip)
|
||||
* [Linux ARM](https://nightly.link/HarbourMasters/Shipwright/workflows/generate-builds/develop/soh-linux-aarch64.zip)
|
||||
|
||||
### Further Reading
|
||||
More detailed documentation can be found in the 'docs' directory, including the aforementioned [building instructions](docs/BUILDING.md).
|
||||
|
||||
@@ -1171,6 +1171,38 @@
|
||||
"rewards on slider does not change."
|
||||
]
|
||||
},
|
||||
"wincon": {
|
||||
"name": "Win Condition",
|
||||
"description": [
|
||||
"Defeat Ganon - Defeat Ganon to complete the game.\n",
|
||||
"\n",
|
||||
"Anywhere - Collect the Triforce to complete the game. It can appear anywhere in the world.\n",
|
||||
"\n",
|
||||
"Trigger - These settings put the completing the game on a trigger, ",
|
||||
"playing the credits when the requirements are met:\n",
|
||||
"- Stones: Obtain the specified amount of Spiritual Stones.\n",
|
||||
"- Medallions: Obtain the specified amount of medallions.\n",
|
||||
"- Dungeon rewards: Obtain the specified total sum of Spiritual Stones or medallions.\n",
|
||||
"- Dungeons: Complete the specified amount of dungeons. Dungeons are considered complete after stepping into ",
|
||||
"the blue warp after the boss.\n",
|
||||
"- Tokens: Obtain the specified amount of Skulltula tokens.\n",
|
||||
"- Triforce Pieces: Obtain the specified number of Triforce Pieces."
|
||||
]
|
||||
},
|
||||
"wincon_options": {
|
||||
"name": "Win Condition Options",
|
||||
"description": [
|
||||
"Standard Rewards - Greg does not change logic, Greg does not help obtain Ganon's Soul, max ",
|
||||
"number of rewards on slider does not change.\n",
|
||||
"\n",
|
||||
"Greg as Reward - Greg does change logic (can be part of expected path for obtaining ",
|
||||
"Ganon's Soul), Greg helps obtain Ganon's Soul, max number of rewards on slider increases by 1 to ",
|
||||
"account for Greg.\n",
|
||||
"\n",
|
||||
"Greg as Wildcard - Greg does not change logic, Greg helps obtain Ganon's Soul, max number of ",
|
||||
"rewards on slider does not change."
|
||||
]
|
||||
},
|
||||
"big_poe_count": {
|
||||
"name": "Big Poe Target Count",
|
||||
"description": "The Poe collector will give a reward for turning in this many Big Poes."
|
||||
@@ -1568,12 +1600,6 @@
|
||||
"ganons_soul_triforce_count": {
|
||||
"name": "Ganon's Soul Triforce Piece Count"
|
||||
},
|
||||
"wincon": {
|
||||
"name": "Win Condition"
|
||||
},
|
||||
"wincon_options": {
|
||||
"name": "Win Condition Reward Options"
|
||||
},
|
||||
"wincon_medallion_count": {
|
||||
"name": "Win Condition Medallion Count"
|
||||
},
|
||||
@@ -2303,7 +2329,7 @@
|
||||
},
|
||||
"pass_guards_with_nothing": {
|
||||
"name": "Sneak Past Moving Gerudo Guards with No Items",
|
||||
"description": "The logic normally guarantees Bow or Hookshot to stun them from a distance, but every moving guard can be passed with basic movement and AI manipulation"
|
||||
"description": "The logic normally guarantees Bow or Hookshot to stun them from a distance, but every moving guard can be passed with basic movement and AI manipulation. This trick also enables reading the sign outside GTG without Gerudo card, which is barely possible."
|
||||
},
|
||||
"gf_wasteland_gate_sidehop_skip": {
|
||||
"name": "Gerudo's Fortress Gate Skip with Sidehop",
|
||||
@@ -2677,7 +2703,7 @@
|
||||
},
|
||||
"water_dragon_jump_dive": {
|
||||
"name": "Water Temple Dragon Statue Jump Dive",
|
||||
"description": "If you come into the dragon statue room from the serpent river, you can sidehop down from above and get into the tunnel without needing either Iron Boots or a Scale. Alternativly, you can do this while equipping irons in midair to get into the tunnel without swim. This trick applies to both Vanilla and Master Quest. In Vanilla, you must shoot the switch from above with the Bow, and then quickly get through the tunnel before the gate closes."
|
||||
"description": "If you come into the dragon statue room from the serpent river, you can sidehop down from above and get into the tunnel without needing either Iron Boots or a Scale. Alternatively, you can do this while equipping irons in midair to get into the tunnel without swim. This trick applies to both Vanilla and Master Quest. In Vanilla, you must shoot the switch from above with the Bow, and then quickly get through the tunnel before the gate closes."
|
||||
},
|
||||
"water_adult_dragon": {
|
||||
"name": "Water Temple Dragon Statue Switch from Above the Water as Adult",
|
||||
@@ -2913,7 +2939,7 @@
|
||||
},
|
||||
"gtg_mq_with_hookshot": {
|
||||
"name": "Gerudo Training Ground MQ Left Side Silver Rupees with Hookshot",
|
||||
"description": "The highest Silver Rupee can be obtained by hookshooting the target and then immediately jumpslashing toward the Rupee."
|
||||
"description": "The highest Silver Rupee can be obtained by hookshotting the target and then immediately jumpslashing toward the Rupee."
|
||||
},
|
||||
"gtg_mq_without_hookshot": {
|
||||
"name": "Gerudo Training Ground MQ Left Side Silver Rupees without Hookshot",
|
||||
|
||||
@@ -594,8 +594,9 @@ typedef enum {
|
||||
/* 0x7A */ GID_SONG_TIME,
|
||||
/* 0x7B */ GID_SONG_STORM,
|
||||
/* 0x7C */ GID_TRIFORCE_PIECE,
|
||||
/* 0x7D */ GID_FISHING_POLE,
|
||||
/* 0x7E */ GID_MAXIMUM
|
||||
/* 0x7D */ GID_TRIFORCE,
|
||||
/* 0x7E */ GID_FISHING_POLE,
|
||||
/* 0x7F */ GID_MAXIMUM
|
||||
|
||||
} GetItemDrawID;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/ShipInit.hpp"
|
||||
#include "soh/Enhancements/randomizer/SeedContext.h"
|
||||
|
||||
extern "C" {
|
||||
#include "z64save.h"
|
||||
@@ -23,7 +24,9 @@ static void RegisterBossDefeatTimestamps() {
|
||||
BOSS_DEFEAT_TIMESTAMP(ACTOR_BOSS_GANON, TIMESTAMP_DEFEAT_GANONDORF);
|
||||
COND_ID_HOOK(OnBossDefeat, ACTOR_BOSS_GANON2, true, [](void* refActor) {
|
||||
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = static_cast<u32>(GAMEPLAYSTAT_TOTAL_TIME);
|
||||
gSaveContext.ship.stats.gameComplete = true;
|
||||
if (!IS_RANDO || RAND_GET_OPTION(RSK_WINCON).Is(RO_WINCON_DEFEAT_GANON)) {
|
||||
gSaveContext.ship.stats.gameComplete = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -88,11 +88,22 @@ static void RegisterBombchuBowlingNoBigCucco() {
|
||||
}
|
||||
|
||||
static void RegisterBombchuBowlingAmmo() {
|
||||
COND_VB_SHOULD(VB_SET_BOMBCHU_BOWLING_AMMO,
|
||||
CVAR_BOWLING_VALUE && (CVAR_BOWLING_AMMO_VALUE != CUCCO_BOWLING_AMMO_DEFAULT), {
|
||||
gPlayState->bombchuBowlingStatus = CVAR_BOWLING_AMMO_VALUE;
|
||||
*should = false;
|
||||
});
|
||||
static s32 prevAmmoValue = CUCCO_BOWLING_AMMO_DEFAULT;
|
||||
s32 nextAmmoValue = CVAR_BOWLING_VALUE ? CVAR_BOWLING_AMMO_VALUE : CUCCO_BOWLING_AMMO_DEFAULT;
|
||||
|
||||
if ((prevAmmoValue != nextAmmoValue) && (gPlayState->bombchuBowlingStatus > 0)) {
|
||||
gPlayState->bombchuBowlingStatus += nextAmmoValue - prevAmmoValue;
|
||||
if (gPlayState->bombchuBowlingStatus <= 0) {
|
||||
gPlayState->bombchuBowlingStatus = -1;
|
||||
}
|
||||
prevAmmoValue = nextAmmoValue;
|
||||
}
|
||||
|
||||
COND_VB_SHOULD(VB_SET_BOMBCHU_BOWLING_AMMO, nextAmmoValue != CUCCO_BOWLING_AMMO_DEFAULT, {
|
||||
prevAmmoValue = CVAR_BOWLING_AMMO_VALUE;
|
||||
gPlayState->bombchuBowlingStatus = prevAmmoValue;
|
||||
*should = false;
|
||||
});
|
||||
}
|
||||
|
||||
static void RegisterBombchuBowlingFirstPrize() {
|
||||
|
||||
@@ -64,6 +64,9 @@ static void RegisterSpeedModifiers() {
|
||||
COND_VB_SHOULD(VB_PLAYER_LIMIT_JUMP_SPEED, speedModifierActive || bunnyHoodActive, {
|
||||
Player* player = va_arg(args, Player*);
|
||||
if (ShouldAmplifyJump(player)) {
|
||||
f32 maxSpeed =
|
||||
(R_RUN_SPEED_LIMIT / 100.0f) * Ship_GetBunnyHoodJumpFactor(player) * GetSpeedModifierJumpFactor();
|
||||
player->linearVelocity = CLAMP(player->linearVelocity, -maxSpeed, maxSpeed);
|
||||
*should = false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#include <libultraship/bridge/consolevariablebridge.h>
|
||||
|
||||
#include "soh/Enhancements/custom-message/CustomMessageTypes.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/randomizer/SeedContext.h"
|
||||
#include "soh/ShipInit.hpp"
|
||||
|
||||
extern "C" {
|
||||
@@ -29,7 +31,13 @@ void RegisterGanonIntros() {
|
||||
}
|
||||
});
|
||||
|
||||
COND_VB_SHOULD(VB_PLAY_GANONDORF_INTRO_CS, CVAR_VALUE || IS_RANDO || IS_BOSS_RUSH, { *should = false; });
|
||||
COND_VB_SHOULD(VB_PLAY_GANONDORF_INTRO_CS, CVAR_VALUE || IS_RANDO || IS_BOSS_RUSH, {
|
||||
va_arg(args, void*);
|
||||
u16 textId = va_arg(args, int);
|
||||
if (textId != TEXT_GANONDORF || !IS_RANDO || !RAND_GET_OPTION(RSK_GANONDORF_HINT)) {
|
||||
*should = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Skip Ganon rising from the rubble, transforming and landing. Skip to knocking sword away.
|
||||
COND_VB_SHOULD(VB_PLAY_GANON_INTRO_CS, CVAR_VALUE, {
|
||||
|
||||
@@ -8,6 +8,7 @@ extern "C" {
|
||||
|
||||
s32 EnKz_FollowPath(EnKz* enKz, PlayState* play);
|
||||
s32 EnKz_SetMovedPos(EnKz* enKz, PlayState* play);
|
||||
void EnKz_Mweep(EnKz* enKz, PlayState* play);
|
||||
void EnKz_SetupMweep(EnKz* enKz, PlayState* play);
|
||||
void EnKz_Wait(EnKz* enKz, PlayState* play);
|
||||
}
|
||||
@@ -35,7 +36,11 @@ static void EnKz_MweepNoCutscene(EnKz* enKz, PlayState* play) {
|
||||
|
||||
static void EnKz_InterceptMweep(void* actorPtr) {
|
||||
EnKz* enKz = (EnKz*)actorPtr;
|
||||
if (enKz->actionFunc != EnKz_SetupMweep) {
|
||||
if (enKz->actionFunc == EnKz_Mweep) {
|
||||
Play_ChangeCameraStatus(gPlayState, enKz->gameplayCamera, CAM_STAT_ACTIVE);
|
||||
Play_ClearCamera(gPlayState, enKz->cutsceneCamera);
|
||||
Player_SetCsActionWithHaltedActors(gPlayState, &enKz->actor, 7);
|
||||
} else if (enKz->actionFunc != EnKz_SetupMweep) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -468,7 +468,7 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
|
||||
*should = false;
|
||||
|
||||
// We should still come out of a softlock if the setting was changed during forced dialogue
|
||||
if (GET_PLAYER(gPlayState)->csAction != 7) {
|
||||
if (GET_PLAYER(gPlayState)->csAction == 8) {
|
||||
Player_SetCsAction(gPlayState, NULL, 7);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,26 @@ static Gfx* LoadCustomGfx(const char* path) {
|
||||
return ResourceMgr_LoadGfxByName(path);
|
||||
}
|
||||
|
||||
// Shooting Gallery being weird giving bow and then load slingshot model when child Link
|
||||
// This function is used to determine if bow or slingshot model should be loaded
|
||||
static bool UsesBowModel(Player* player) {
|
||||
if (CVarGetInteger(CVAR_ENHANCEMENT("BowSlingshotAmmoFix"), 0) ||
|
||||
CVarGetInteger(CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), 0)) {
|
||||
return !Player_HoldsSlingshot(player);
|
||||
}
|
||||
return LINK_IS_ADULT;
|
||||
}
|
||||
|
||||
// Detects cutscenes that swap the left hand DL without updating leftHandType.
|
||||
static bool IsLeftHandSwappedByCutscene(Player* player) {
|
||||
Gfx** dLists = player->leftHandDLists;
|
||||
if (dLists == &gPlayerLeftHandOpenDLs[0] || dLists == &gPlayerLeftHandOpenDLs[1] ||
|
||||
dLists == &gPlayerLeftHandClosedDLs[0] || dLists == &gPlayerLeftHandClosedDLs[1]) {
|
||||
return true;
|
||||
}
|
||||
return dLists == &gPlayerLeftHandBgsDLs[1] && player->leftHandType != PLAYER_MODELTYPE_LH_BGS;
|
||||
}
|
||||
|
||||
static u8 sLastValidSwordEquip = EQUIP_VALUE_SWORD_NONE;
|
||||
|
||||
static u8 GetEquippedSwordValue(PlayState* play) {
|
||||
@@ -250,6 +270,8 @@ static void RegisterCustomEquipment() {
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (IsLeftHandSwappedByCutscene(player))
|
||||
break;
|
||||
switch ((u8)player->leftHandType) {
|
||||
case PLAYER_MODELTYPE_LH_SWORD: {
|
||||
if (player == GET_PLAYER(play)) {
|
||||
@@ -317,7 +339,7 @@ static void RegisterCustomEquipment() {
|
||||
case PLAYER_MODELTYPE_RH_BOW_SLINGSHOT_2:
|
||||
fpsHand = isAdult ? gCustomAdultFPSHandDL : gCustomChildFPSHandDL;
|
||||
fpsWeapon =
|
||||
Player_HoldsBow(player)
|
||||
UsesBowModel(player)
|
||||
? ResolveCustomChain({ gCustomFPSBowDL, gCustomBowDL, nullptr })
|
||||
: ResolveCustomChain({ gCustomFPSSlingshotDL, gCustomSlingshotDL, nullptr });
|
||||
break;
|
||||
@@ -391,7 +413,7 @@ static void RegisterCustomEquipment() {
|
||||
break;
|
||||
case PLAYER_MODELTYPE_RH_BOW_SLINGSHOT:
|
||||
case PLAYER_MODELTYPE_RH_BOW_SLINGSHOT_2:
|
||||
customDL = Player_HoldsBow(player) ? gCustomBowDL : gCustomSlingshotDL;
|
||||
customDL = UsesBowModel(player) ? gCustomBowDL : gCustomSlingshotDL;
|
||||
break;
|
||||
case PLAYER_MODELTYPE_RH_HOOKSHOT:
|
||||
customDL = (player->heldItemAction == PLAYER_IA_HOOKSHOT) ? gCustomHookshotDL
|
||||
|
||||
@@ -436,6 +436,34 @@ std::string decodeNTSCPlayerNameChar(int code) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
char decodePlayerNameChar(uint8_t code, uint8_t language) {
|
||||
if (language == NAME_LANGUAGE_PAL) {
|
||||
return z2ASCII(code);
|
||||
}
|
||||
const std::string decoded = decodeNTSCPlayerNameChar(code);
|
||||
return (decoded.length() == 1 && decoded[0] >= 32 && decoded[0] <= 126) ? decoded[0] : ' ';
|
||||
}
|
||||
|
||||
uint8_t encodePlayerNameChar(char c, uint8_t language) {
|
||||
const bool pal = language == NAME_LANGUAGE_PAL;
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
return (pal ? 10 : 171) + (c - 'A');
|
||||
}
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
return (pal ? 36 : 197) + (c - 'a');
|
||||
}
|
||||
if (c == '-') {
|
||||
return pal ? 0x3F : 0xE4;
|
||||
}
|
||||
if (c == '.') {
|
||||
return pal ? 0x40 : 0xEA;
|
||||
}
|
||||
return pal ? 0x3E : 0xDF;
|
||||
}
|
||||
|
||||
enum MagicLevel { MAGIC_LEVEL_NONE, MAGIC_LEVEL_SINGLE, MAGIC_LEVEL_DOUBLE };
|
||||
|
||||
std::map<int8_t, const char*> magicLevelMap = {
|
||||
@@ -528,13 +556,8 @@ void DrawGeneralTab() {
|
||||
// Convert current name to ASCII for display
|
||||
if (!nameInputInitialized || ImGui::IsWindowFocused()) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (gSaveContext.ship.filenameLanguage == NAME_LANGUAGE_PAL) {
|
||||
playerNameInput[i] = z2ASCII(gSaveContext.playerName[i]);
|
||||
} else {
|
||||
const std::string decoded = decodeNTSCPlayerNameChar(gSaveContext.playerName[i]);
|
||||
playerNameInput[i] =
|
||||
(decoded.length() == 1 && decoded[0] >= 32 && decoded[0] <= 126) ? decoded[0] : ' ';
|
||||
}
|
||||
playerNameInput[i] =
|
||||
decodePlayerNameChar(gSaveContext.playerName[i], gSaveContext.ship.filenameLanguage);
|
||||
}
|
||||
playerNameInput[8] = '\0';
|
||||
nameInputInitialized = true;
|
||||
@@ -545,12 +568,11 @@ void DrawGeneralTab() {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f));
|
||||
if (ImGui::InputText("##PlayerNameInput", playerNameInput, 9)) {
|
||||
// Convert ASCII input back to game encoding
|
||||
bool ended = false;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (playerNameInput[i] == '\0') {
|
||||
gSaveContext.playerName[i] = 223;
|
||||
} else {
|
||||
gSaveContext.playerName[i] = playerNameInput[i];
|
||||
}
|
||||
ended = ended || playerNameInput[i] == '\0';
|
||||
gSaveContext.playerName[i] =
|
||||
encodePlayerNameChar(ended ? ' ' : playerNameInput[i], gSaveContext.ship.filenameLanguage);
|
||||
}
|
||||
nameInputInitialized = false;
|
||||
}
|
||||
@@ -563,6 +585,7 @@ void DrawGeneralTab() {
|
||||
// Filename encoding
|
||||
const bool hasPAL = (sGerMessageEntryTablePtr != nullptr) && (sFraMessageEntryTablePtr != nullptr);
|
||||
const bool hasNTSC = (sJpnMessageEntryTablePtr != nullptr);
|
||||
const uint8_t oldNameLanguage = gSaveContext.ship.filenameLanguage;
|
||||
if (hasPAL && hasNTSC) {
|
||||
Combobox("Player Name Language", &gSaveContext.ship.filenameLanguage, filenameLanguageMap,
|
||||
comboboxOptionsBase.Tooltip("Encoding used for Player Name"));
|
||||
@@ -576,6 +599,13 @@ void DrawGeneralTab() {
|
||||
comboboxOptionsBase.Tooltip("Encoding used for Player Name"));
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
if (canEditName && gSaveContext.ship.filenameLanguage != oldNameLanguage) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
gSaveContext.playerName[i] = encodePlayerNameChar(
|
||||
decodePlayerNameChar(gSaveContext.playerName[i], oldNameLanguage), gSaveContext.ship.filenameLanguage);
|
||||
}
|
||||
nameInputInitialized = false;
|
||||
}
|
||||
UIWidgets::EndCard();
|
||||
|
||||
UIWidgets::BeginCard("healthCard");
|
||||
|
||||
@@ -41,6 +41,14 @@ typedef enum {
|
||||
// - None
|
||||
VB_AMY_SOLVE,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `*Color_RGB8`
|
||||
VB_APPLY_MAGIC_METER_FILL_COLOR,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
@@ -50,6 +58,14 @@ typedef enum {
|
||||
// - `s8*` sCsState
|
||||
VB_BARINADE_DEATH_SCENE,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// Flags_GetEventChkInf(EVENTCHKINF_BEGAN_BARINA_BATTLE)
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `*BossVa`
|
||||
VB_BARINADE_PLAY_REMATCH_INTRO,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// this->actor.textId == 0x401A
|
||||
@@ -1858,6 +1874,14 @@ typedef enum {
|
||||
// - `*EnMs`
|
||||
VB_MAGIC_BEAN_SALESMAN_TAKE_MONEY,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// true
|
||||
// ```
|
||||
// #### `args`
|
||||
// - None
|
||||
VB_MAGIC_METER_DEPLETE_WHILE_CONSUMING,
|
||||
|
||||
// #### `result`
|
||||
// ```c
|
||||
// CHECK_QUEST_ITEM(QUEST_SONG_EPONA)
|
||||
@@ -2265,6 +2289,7 @@ typedef enum {
|
||||
// ```
|
||||
// #### `args`
|
||||
// - `*BossGanon`
|
||||
// - `u16` text ID
|
||||
VB_PLAY_GANONDORF_INTRO_CS,
|
||||
|
||||
// #### `result`
|
||||
|
||||
@@ -293,6 +293,7 @@ void LoadStatsVersion1() {
|
||||
SaveManager::Instance->LoadData("fileCreatedAt", gSaveContext.ship.stats.fileCreatedAt);
|
||||
SaveManager::Instance->LoadData("playTimer", gSaveContext.ship.stats.playTimer);
|
||||
SaveManager::Instance->LoadData("pauseTimer", gSaveContext.ship.stats.pauseTimer);
|
||||
SaveManager::Instance->LoadData("gameComplete", gSaveContext.ship.stats.gameComplete);
|
||||
SaveManager::Instance->LoadArray(
|
||||
"itemTimestamps", ARRAY_COUNT(gSaveContext.ship.stats.itemTimestamp),
|
||||
[](size_t i) { SaveManager::Instance->LoadData("", gSaveContext.ship.stats.itemTimestamp[i]); });
|
||||
@@ -343,6 +344,7 @@ void SaveStats(const SaveContext& saveContext, int sectionID, bool fullSave) {
|
||||
SaveManager::Instance->SaveData("fileCreatedAt", saveContext.ship.stats.fileCreatedAt);
|
||||
SaveManager::Instance->SaveData("playTimer", saveContext.ship.stats.playTimer);
|
||||
SaveManager::Instance->SaveData("pauseTimer", saveContext.ship.stats.pauseTimer);
|
||||
SaveManager::Instance->SaveData("gameComplete", saveContext.ship.stats.gameComplete);
|
||||
SaveManager::Instance->SaveArray(
|
||||
"itemTimestamps", ARRAY_COUNT(saveContext.ship.stats.itemTimestamp),
|
||||
[&](size_t i) { SaveManager::Instance->SaveData("", saveContext.ship.stats.itemTimestamp[i]); });
|
||||
|
||||
@@ -25,10 +25,10 @@ char* GameplayStats_GetCurrentTime();
|
||||
? (!gSaveContext.ship.stats.firstInput \
|
||||
? 0 \
|
||||
: ((GetUnixTimestamp() - gSaveContext.ship.stats.firstInput) / 100)) \
|
||||
: (gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] \
|
||||
? gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] \
|
||||
: (gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] \
|
||||
? gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] \
|
||||
: (gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] \
|
||||
? gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] \
|
||||
: (gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] \
|
||||
? gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] \
|
||||
: gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TIMESPLITS_COMPLETED]))) \
|
||||
: (gSaveContext.ship.stats.playTimer / 2 + gSaveContext.ship.stats.pauseTimer / 3))
|
||||
#define CURRENT_MODE_TIMER \
|
||||
|
||||
@@ -2152,6 +2152,11 @@ void StaticData::HintTable_Init() {
|
||||
| WINCON HINT TEXT |
|
||||
---------------------------*/
|
||||
|
||||
hintTextTable[RHT_WINCON_DEFEAT_GANON_HINT] = HintText(CustomMessage("$wAnd the #Triforce# will be granted once #the evil one# is defeated.^",
|
||||
/*german*/ "$wUnd das #Triforce# wird gewährt, sobald #der Böse# besiegt wurde.^",
|
||||
/*french*/ "$wAussi, la #Triforce# sera accordée une fois que #le Malin# aura été vaincu.^",
|
||||
{QM_YELLOW, QM_PINK}));
|
||||
|
||||
hintTextTable[RHT_WINCON_ANYWHERE_HINT] = HintText(CustomMessage("$wAnd the #Triforce# will be hidden somewhere&#in Hyrule#.^",
|
||||
/*german*/ "$wUnd das #Triforce# wird irgendwo #in Hyrule# zu finden sein.^",
|
||||
/*french*/ "$wAussi, la #Triforce# se trouve quelque part #dans Hyrule#.^",
|
||||
|
||||
@@ -64,6 +64,7 @@ CheckIdentity ShuffleFairies_GetFairyIdentity(int32_t params, ActorID id) {
|
||||
CheckIdentity fairyIdentity;
|
||||
s16 sceneNum = gPlayState->sceneNum;
|
||||
fairyIdentity.randomizerInf = RAND_INF_MAX;
|
||||
fairyIdentity.randomizerCheck = RC_UNKNOWN_CHECK;
|
||||
|
||||
if (sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_NIGHT || sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS) {
|
||||
sceneNum = SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY;
|
||||
@@ -73,7 +74,6 @@ CheckIdentity ShuffleFairies_GetFairyIdentity(int32_t params, ActorID id) {
|
||||
|
||||
if (location->GetRandomizerCheck() == RC_UNKNOWN_CHECK) {
|
||||
SPDLOG_WARN("FairyGetIdentity did not receive a valid RC value ({}).", location->GetRandomizerCheck());
|
||||
assert(false);
|
||||
} else {
|
||||
fairyIdentity.randomizerInf = static_cast<RandomizerInf>(location->GetCollectionCheck().flag);
|
||||
fairyIdentity.randomizerCheck = location->GetRandomizerCheck();
|
||||
@@ -84,6 +84,9 @@ CheckIdentity ShuffleFairies_GetFairyIdentity(int32_t params, ActorID id) {
|
||||
|
||||
static bool SpawnFairy(f32 posX, f32 posY, f32 posZ, int32_t params, FairyType fairyType, ActorID id) {
|
||||
CheckIdentity fairyIdentity = ShuffleFairies_GetFairyIdentity(params, id);
|
||||
if (fairyIdentity.randomizerCheck == RC_UNKNOWN_CHECK) {
|
||||
return false;
|
||||
}
|
||||
if (!Flags_GetRandomizerInf(fairyIdentity.randomizerInf)) {
|
||||
Actor* fairy =
|
||||
Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_ELF, posX, posY - 30.0f, posZ, 0, 0, 0, fairyType);
|
||||
|
||||
@@ -334,8 +334,8 @@ void Rando::StaticData::RegisterSilverLocations() {
|
||||
locationTable[RC_SPIRIT_NEAR_DOOR_SUN_SILVER] = Location::Collectable(RC_SPIRIT_NEAR_DOOR_SUN_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(-984, -450), "Near Door Sun Silver", RHT_SPIRIT_TEMPLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_NEAR_DOOR_SUN_SILVER));
|
||||
locationTable[RC_SPIRIT_NW_BOULDER_SILVER] = Location::Collectable(RC_SPIRIT_NW_BOULDER_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(1284, -1355), "Northwest Boulder Silver", RHT_SPIRIT_TEMPLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_NW_BOULDER_SILVER));
|
||||
locationTable[RC_SPIRIT_SW_BOULDER_SILVER] = Location::Collectable(RC_SPIRIT_SW_BOULDER_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(1284, -813), "Southwest Boulder Silver", RHT_SPIRIT_TEMPLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_SW_BOULDER_SILVER));
|
||||
locationTable[RC_SPIRIT_SE_BOULDER_SILVER] = Location::Collectable(RC_SPIRIT_SE_BOULDER_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(1856, -944), "Southeast Boulder Super", RHT_SPIRIT_TEMPLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_SE_BOULDER_SILVER));
|
||||
locationTable[RC_SPIRIT_NE_BOULDER_SILVER] = Location::Collectable(RC_SPIRIT_NE_BOULDER_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(1856, -1219), "Notheast Boulder Silver", RHT_SPIRIT_TEMPLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_NE_BOULDER_SILVER));
|
||||
locationTable[RC_SPIRIT_SE_BOULDER_SILVER] = Location::Collectable(RC_SPIRIT_SE_BOULDER_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(1856, -944), "Southeast Boulder Silver", RHT_SPIRIT_TEMPLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_SE_BOULDER_SILVER));
|
||||
locationTable[RC_SPIRIT_NE_BOULDER_SILVER] = Location::Collectable(RC_SPIRIT_NE_BOULDER_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(1856, -1219), "Northeast Boulder Silver", RHT_SPIRIT_TEMPLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_NE_BOULDER_SILVER));
|
||||
locationTable[RC_SPIRIT_AIRBORNE_BOULDER_SILVER] = Location::Collectable(RC_SPIRIT_AIRBORNE_BOULDER_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(1573, -920), "Airborne Boulder Silver", RHT_SPIRIT_TEMPLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_AIRBORNE_BOULDER_SILVER));
|
||||
locationTable[RC_BOTW_B1_LADDER_SILVER] = Location::Collectable(RC_BOTW_B1_LADDER_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(-796, -150), "B1 Ladder Silver", RHT_BOTW_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTW_B1_LADDER_SILVER));
|
||||
locationTable[RC_BOTW_B2_LADDER_SILVER] = Location::Collectable(RC_BOTW_B2_LADDER_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(-614, -297), "B2 Ladder Silver", RHT_BOTW_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTW_B2_LADDER_SILVER));
|
||||
@@ -378,7 +378,7 @@ void Rando::StaticData::RegisterSilverLocations() {
|
||||
locationTable[RC_GANONS_CASTLE_UNDER_LEDGE_FOREST_SILVER] = Location::Collectable(RC_GANONS_CASTLE_UNDER_LEDGE_FOREST_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1651, 2021), "Under Ledge Forest Trial Silver", RHT_GANONS_CASTLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_UNDER_LEDGE_FOREST_SILVER));
|
||||
locationTable[RC_GANONS_CASTLE_ON_TARGET_FOREST_SILVER] = Location::Collectable(RC_GANONS_CASTLE_ON_TARGET_FOREST_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1634, 1550), "On Target Forest Trial Silver", RHT_GANONS_CASTLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_ON_TARGET_FOREST_SILVER));
|
||||
locationTable[RC_GANONS_CASTLE_TORCH_SLUG_FIRE_SILVER] = Location::Collectable(RC_GANONS_CASTLE_TORCH_SLUG_FIRE_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(-854, -3679), "Torch Slug Fire Trial Silver", RHT_GANONS_CASTLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_TORCH_SLUG_FIRE_SILVER));
|
||||
locationTable[RC_GANONS_CASTLE_FLAME_JETS_FIRE_SILVER] = Location::Collectable(RC_GANONS_CASTLE_FLAME_JETS_FIRE_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(-1555, -2317), "Flame Jets Fire Temple Silver", RHT_GANONS_CASTLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_FLAME_JETS_FIRE_SILVER));
|
||||
locationTable[RC_GANONS_CASTLE_FLAME_JETS_FIRE_SILVER] = Location::Collectable(RC_GANONS_CASTLE_FLAME_JETS_FIRE_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(-1555, -2317), "Flame Jets Fire Trial Silver", RHT_GANONS_CASTLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_FLAME_JETS_FIRE_SILVER));
|
||||
locationTable[RC_GANONS_CASTLE_DISTANT_PLATFORM_FIRE_SILVER] = Location::Collectable(RC_GANONS_CASTLE_DISTANT_PLATFORM_FIRE_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(-515, -3253), "Distant Platform Fire Trial Silver", RHT_GANONS_CASTLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_DISTANT_PLATFORM_FIRE_SILVER));
|
||||
locationTable[RC_GANONS_CASTLE_CLOSE_PLATFORM_FIRE_SILVER] = Location::Collectable(RC_GANONS_CASTLE_CLOSE_PLATFORM_FIRE_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(-604, -2592), "Close Platform Fire Trial Silver", RHT_GANONS_CASTLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_CLOSE_PLATFORM_FIRE_SILVER));
|
||||
locationTable[RC_GANONS_GANONS_CASTLE_UNDER_PILLAR_FIRE_SILVER] = Location::Collectable(RC_GANONS_GANONS_CASTLE_UNDER_PILLAR_FIRE_SILVER, RCQUEST_VANILLA, RCTYPE_SILVER, ACTOR_EN_G_SWITCH, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(-1867, -2754), "Under Pillar Fire Trial Silver", RHT_GANONS_CASTLE_SILVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_GANONS_CASTLE_UNDER_PILLAR_FIRE_SILVER));
|
||||
|
||||
@@ -357,6 +357,10 @@ extern "C" void Randomizer_DrawTriforcePieceGI(PlayState* play, GetItemEntry get
|
||||
if (rando->GetRandoSettingValue(RSK_GANONS_SOUL) == RO_GANONS_SOUL_TRIFORCE_PIECES) {
|
||||
fullTriforce = fullTriforce || (rando->GetRandoSettingValue(RSK_GANONS_SOUL_TRIFORCE_COUNT) == current);
|
||||
}
|
||||
if (getItemEntry.getItemId == RG_TRIFORCE) {
|
||||
fullTriforce = true;
|
||||
triforcePieceScale = 0.035f;
|
||||
}
|
||||
|
||||
Matrix_Scale(triforcePieceScale, triforcePieceScale, triforcePieceScale, MTXMODE_APPLY);
|
||||
|
||||
|
||||
@@ -177,6 +177,11 @@ int8_t GetSceneTotalSmallKeys(const SaveContext* saveContext, const SceneID scen
|
||||
return FindCurrentSmallKeys(saveContext, scene);
|
||||
}
|
||||
|
||||
int8_t GetSceneUnspentSmallKeyMax(const SaveContext* saveContext, const SceneID scene) {
|
||||
int8_t used = GetSceneTotalSmallKeys(saveContext, scene) - FindCurrentSmallKeys(saveContext, scene);
|
||||
return GetSceneSmallKeyMax(scene) - used;
|
||||
}
|
||||
|
||||
int8_t DungeonInfo::GetUsedSmallKeys(SaveContext* saveContext) const {
|
||||
return FindUsedSmallKeys(saveContext, scene, GetDoorFlags());
|
||||
}
|
||||
|
||||
@@ -83,6 +83,8 @@ int8_t FindTotalSmallKeys(const SaveContext* saveContext, const SceneID scene, s
|
||||
uint8_t GetSceneSmallKeyMax(SceneID scene);
|
||||
/// Small keys held for the scene plus the ones already spent there.
|
||||
int8_t GetSceneTotalSmallKeys(const SaveContext* saveContext, SceneID scene);
|
||||
/// The scene's small key max, minus keys already used on doors. Key rings and skeleton key set held keys to this.
|
||||
int8_t GetSceneUnspentSmallKeyMax(const SaveContext* saveContext, SceneID scene);
|
||||
|
||||
typedef enum {
|
||||
DEKU_TREE,
|
||||
|
||||
@@ -650,7 +650,9 @@ CustomMessage Hint::GetWinconText() {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
CustomMessage winconMessage;
|
||||
|
||||
if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_ANYWHERE)) {
|
||||
if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_DEFEAT_GANON)) {
|
||||
return StaticData::hintTextTable[RHT_WINCON_DEFEAT_GANON_HINT].GetHintMessage();
|
||||
} else if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_ANYWHERE)) {
|
||||
return StaticData::hintTextTable[RHT_WINCON_ANYWHERE_HINT].GetHintMessage();
|
||||
} else if (ctx->GetOption(RSK_WINCON).Is(RO_WINCON_STONES)) {
|
||||
winconMessage = StaticData::hintTextTable[RHT_WINCON_STONES_HINT].GetHintMessage();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "soh/Enhancements/randomizer/fishsanity.h"
|
||||
#include "soh/Enhancements/randomizer/static_data.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/SohGui/ImGuiUtils.h"
|
||||
#include "soh/Notification/Notification.h"
|
||||
#include "soh/SaveManager.h"
|
||||
@@ -478,6 +479,9 @@ static void PlantAllBeans() {
|
||||
gSaveContext.sceneFlags[patch.scene].swch |= 1 << patch.swchFlag;
|
||||
if (gPlayState->sceneNum == patch.scene) {
|
||||
Flags_SetSwitch(gPlayState, patch.swchFlag);
|
||||
} else {
|
||||
// Flags_SetSwitch fires this for the current scene, other scenes need it so Anchor syncs them
|
||||
GameInteractor_ExecuteOnSceneFlagSet(patch.scene, FLAG_SCENE_SWITCH, patch.swchFlag);
|
||||
}
|
||||
}
|
||||
ObjBean* bean = (ObjBean*)Actor_Find(&gPlayState->actorCtx, ACTOR_OBJ_BEAN, ACTORCAT_BG);
|
||||
@@ -1018,6 +1022,24 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
|
||||
case VB_CRAWL:
|
||||
*should = *should && Flags_GetRandomizerInf(RAND_INF_CAN_CRAWL);
|
||||
break;
|
||||
case VB_BARINADE_PLAY_REMATCH_INTRO:
|
||||
*should =
|
||||
*should && (!RAND_GET_OPTION(RSK_SHUFFLE_BOSS_SOULS) || Flags_GetRandomizerInf(RAND_INF_BARINADE_SOUL));
|
||||
break;
|
||||
case VB_MAGIC_METER_DEPLETE_WHILE_CONSUMING:
|
||||
*should = *should && !Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER);
|
||||
break;
|
||||
case VB_APPLY_MAGIC_METER_FILL_COLOR: {
|
||||
Color_RGB8* color = va_arg(args, Color_RGB8*);
|
||||
|
||||
if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER)) {
|
||||
*color = { 0, 0, 200 };
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Consumable_MagicInfinite.Changed"), 0)) {
|
||||
*color = CVarGetColor24(CVAR_COSMETIC("Consumable_MagicInfinite.Value"), *color);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VB_CAN_BUY_SHOP_SHIELD_OR_TUNIC: {
|
||||
// Gate non-randomized shop shields/tunics behind finding a non-shop copy.
|
||||
if (RAND_GET_OPTION(RSK_SHOP_SHIELDS_AND_TUNICS_ONLY_REFILL).Is(RO_GENERIC_ON)) {
|
||||
@@ -1868,7 +1890,7 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
|
||||
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_GANONS_TOWER);
|
||||
randomizerQueuedChecks.push(RC_GANON);
|
||||
CheckTriggers();
|
||||
gPlayState->nextEntranceIndex = ENTR_OUTSIDE_GANONS_CASTLE_1_2;
|
||||
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_OUTSIDE_GANONS_CASTLE_1_2);
|
||||
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
|
||||
gPlayState->transitionType = TRANS_TYPE_FADE_WHITE;
|
||||
gSaveContext.nextTransitionType = TRANS_TYPE_FADE_WHITE_SLOW;
|
||||
@@ -2742,9 +2764,6 @@ void RandomizerOnPlayerUpdateHandler() {
|
||||
// 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;
|
||||
|
||||
@@ -472,7 +472,7 @@ void Rando::StaticData::InitItemTable() {
|
||||
itemTable[RG_DEKU_NUT_BAG] = Item(RG_DEKU_NUT_BAG, Text{ "Deku Nut Bag", "Sac de Noix Mojo", "Deku-Nuß-Tasche" }, ITEMTYPE_ITEM, GI_NUT_UPGRADE_30, true, LOGIC_PROGRESSIVE_NUT_BAG, RHT_NONE, RG_DEKU_NUT_BAG, OBJECT_GI_NUTS, GID_NUTS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"a ", "un ", "eine "}).CustomIcon(gItemIconDekuNutTex);
|
||||
itemTable[RG_DEKU_NUT_BAG].SetCustomDrawFunc(Randomizer_DrawNutBag);
|
||||
|
||||
itemTable[RG_TRIFORCE] = Item(RG_TRIFORCE, Text{ "Triforce", "Triforce", "Triforce" }, ITEMTYPE_ITEM, RG_TRIFORCE, true, LOGIC_NONE, RHT_NONE, RG_TRIFORCE, OBJECT_GI_BOMB_2, GID_TRIFORCE_PIECE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"the ", "la ", "die "}).CustomIcon(gTriforcePieceTex);;
|
||||
itemTable[RG_TRIFORCE] = Item(RG_TRIFORCE, Text{ "Triforce", "Triforce", "Triforce" }, ITEMTYPE_ITEM, RG_TRIFORCE, true, LOGIC_NONE, RHT_NONE, RG_TRIFORCE, OBJECT_GI_BOMB_2, GID_TRIFORCE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"the ", "la ", "die "}).CustomIcon(gTriforcePieceTex);;
|
||||
itemTable[RG_HINT] = Item(RG_HINT, Text{ "Hint", "Indice", "Hinweis" }, ITEMTYPE_EVENT, RG_HINT, false, LOGIC_NONE, RHT_NONE, ITEM_CATEGORY_LESSER);
|
||||
// Individual stages of progressive items (only here for GetItemEntry purposes, not for use in seed gen)
|
||||
itemTable[RG_HOOKSHOT] = Item(RG_HOOKSHOT, Text{ "Hookshot", "Grappin", "Fanghaken" }, ITEMTYPE_ITEM, GI_HOOKSHOT, true, LOGIC_PROGRESSIVE_HOOKSHOT, RHT_HOOKSHOT, ITEM_HOOKSHOT, OBJECT_GI_HOOKSHOT, GID_HOOKSHOT, 0x36, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, {"the ", "le ", "den "});
|
||||
|
||||
@@ -42,7 +42,7 @@ bool LocationAccess::CheckConditionAtAgeTime(bool& age, bool& time) const {
|
||||
}
|
||||
|
||||
bool LocationAccess::ConditionsMet(Region* parentRegion, bool calculatingAvailableChecks) const {
|
||||
// WARNING enterance validation can run this after resetting the access for sphere 0 validation
|
||||
// WARNING entrance validation can run this after resetting the access for sphere 0 validation
|
||||
// When refactoring ToD access, either fix the above or do not assume that we
|
||||
// have any access at all just because this is being run
|
||||
bool conditionsMet = false;
|
||||
|
||||
@@ -644,7 +644,7 @@ void RegionTable_Init_FireTemple() {
|
||||
//Only adult is tall enough to use a bow shot without climbing to UPPER
|
||||
//Logical ways to cross are hovers, longshot or running through the lava and then climbing back up as adult (child can't reach the ledge).
|
||||
//The Damage logic here is for jumping down and running across the lava to get in dins range of the south torch
|
||||
//Fairies cannot be used for this as it is time sensetive, and NL is only useful with sticks as it disables other magic while in use, so it's tunic or raw damage taking ability.
|
||||
//Fairies cannot be used for this as it is time sensitive, and NL is only useful with sticks as it disables other magic while in use, so it's tunic or raw damage taking ability.
|
||||
//testing tells me you take 3 ticks of lava damage, which is 12 internal damage or 3/4 of a heart at x1 damage multiplier, performing this run
|
||||
LOCATION(RC_FIRE_TEMPLE_MQ_NEAR_BOSS_CHEST, logic->CanUse(RG_DINS_FIRE) && logic->HasItem(RG_OPEN_CHEST) &&
|
||||
(logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT) || logic->CanMegajump() ||
|
||||
|
||||
@@ -744,7 +744,7 @@ void RegionTable_Init_ForestTemple() {
|
||||
LOCATION(RC_FOREST_TEMPLE_MQ_RAISED_ISLAND_COURTYARD_LOWER_CHEST, logic->HasItem(RG_OPEN_CHEST)),
|
||||
}, {
|
||||
//Exits
|
||||
//Skipping swim here is non-trival, needs a roll-jump. If a swim lock is added it's probably wise to copy deku baba events here
|
||||
//Skipping swim here is non-trivial, needs a roll-jump. If a swim lock is added it's probably wise to copy deku baba events here
|
||||
ENTRANCE(RR_FOREST_TEMPLE_MQ_NE_COURTYARD, true),
|
||||
ENTRANCE(RR_FOREST_TEMPLE_MQ_NE_COURTYARD_LEDGE_ABOVE_ISLAND, logic->CanUse(RG_SONG_OF_TIME)),
|
||||
});
|
||||
|
||||
@@ -638,7 +638,7 @@ void RegionTable_Init_GanonsCastle() {
|
||||
LOCATION(RC_GANONS_CASTLE_MQ_FIRE_TRIAL_POT_2, logic->CanBreakPots()),
|
||||
}, {
|
||||
//Exits
|
||||
ENTRANCE(RR_GANONS_CASTLE_MQ_FIRE_TRIAL_BARRED_DOOR, true),
|
||||
ENTRANCE(RR_GANONS_CASTLE_MQ_FIRE_TRIAL_FROM_BARRED, true),
|
||||
});
|
||||
|
||||
areaTable[RR_GANONS_CASTLE_MQ_WATER_TRIAL_GEYSER_ROOM] = Region("Ganon's Castle MQ Water Trial Geyser Room", SCENE_INSIDE_GANONS_CASTLE, {
|
||||
@@ -939,7 +939,7 @@ void RegionTable_Init_GanonsCastle() {
|
||||
//Exits
|
||||
//I got the trick going backwards, but only while taking damage. this isn't relevant anyway though
|
||||
ENTRANCE(RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_BOULDER_ROOM_FRONT, logic->CanUse(RG_HOOKSHOT) || ctx->GetTrickOption(RT_GANON_MQ_LIGHT_TRIAL) || (logic->IsAdult && logic->CanGroundJump())),
|
||||
ENTRANCE(RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_FINAL_ROOM, logic->SmallKeys(SCENE_INSIDE_GANONS_CASTLE, 3)),
|
||||
ENTRANCE(RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_FAKE_FINAL_ROOM, logic->SmallKeys(SCENE_INSIDE_GANONS_CASTLE, 3)),
|
||||
});
|
||||
|
||||
areaTable[RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_FAKE_FINAL_ROOM] = Region("Ganon's Castle MQ Light Trial Fake Final Room", SCENE_INSIDE_GANONS_CASTLE, {}, {}, {
|
||||
@@ -955,10 +955,7 @@ void RegionTable_Init_GanonsCastle() {
|
||||
//Locations
|
||||
LOCATION(RC_GANONS_CASTLE_MQ_LIGHT_TRIAL_POT_1, logic->CanBreakPots()),
|
||||
LOCATION(RC_GANONS_CASTLE_MQ_LIGHT_TRIAL_POT_2, logic->CanBreakPots()),
|
||||
}, {
|
||||
//Exits
|
||||
ENTRANCE(RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_BOULDER_ROOM_BACK, logic->SmallKeys(SCENE_INSIDE_GANONS_CASTLE, 3)),
|
||||
});
|
||||
}, {});
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
@@ -328,7 +328,7 @@ void RegionTable_Init_IceCavern() {
|
||||
LOCATION(RC_ICE_CAVERN_MQ_MAP_RED_ICE, logic->BlueFire()),
|
||||
}, {});
|
||||
|
||||
//RANDOTODO the default state of the toggle switchs in MQ ice are wierd, investigate for doordsanity
|
||||
//RANDOTODO the default state of the toggle switches in MQ ice are weird, investigate for doorsanity
|
||||
areaTable[RR_ICE_CAVERN_MQ_SCARECROW_ROOM] = Region("Ice Cavern MQ Scarecrow Room", SCENE_ICE_CAVERN, {}, {
|
||||
//Locations
|
||||
//Implies being able to kill the skull if you hit the switch
|
||||
|
||||
@@ -736,7 +736,7 @@ void RegionTable_Init_ShadowTemple() {
|
||||
ENTRANCE(RR_SHADOW_TEMPLE_MQ_SPIKES_DOOR_PLATFORM, (((ctx->GetTrickOption(RT_LENS_SHADOW_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->CanUse(logic->IsAdult && AnyAgeTime([]{return logic->CanKillEnemy(RE_REDEAD);}) ? RG_HOOKSHOT : RG_LONGSHOT)) &&
|
||||
//regardless, we can't assume the player does not have the silvers, so we need to cross from the glass to the platform with hovers or a jumpslash jump (as adult)
|
||||
(logic->CanUse(RG_HOVER_BOOTS) || (logic->IsAdult && logic->CanJumpslash()))) ||
|
||||
//alternativly, a midair groundjump from the invisible spikes takes adult directly there
|
||||
//alternatively, a midair groundjump from the invisible spikes takes adult directly there
|
||||
((ctx->GetTrickOption(RT_LENS_SHADOW_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->IsAdult && logic->CanMidairGroundJump()))
|
||||
});
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ void RegionTable_Init_SpiritTemple() {
|
||||
RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_LONGSHOT);})),
|
||||
}, {
|
||||
//Exits
|
||||
//Adult can barely make it with just a jump, but it's a bit tight with a bad retry if it is relevent, so it would be it's own trick.
|
||||
//Adult can barely make it with just a jump, but it's a bit tight with a bad retry if it is relevant, so it would be it's own trick.
|
||||
ENTRANCE(RR_SPIRIT_TEMPLE_STATUE_ROOM_CHILD, logic->CanUse(RG_HOVER_BOOTS) || logic->BunnyHood() || logic->CanJumpslash() || logic->CanUse(RG_HOOKSHOT)),
|
||||
ENTRANCE(RR_SPIRIT_TEMPLE_STATUE_ROOM, true),
|
||||
ENTRANCE(RR_SPIRIT_TEMPLE_GS_LEDGE, logic->IsAdult && (ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP) || logic->BunnyHood())),
|
||||
@@ -582,7 +582,7 @@ void RegionTable_Init_SpiritTemple() {
|
||||
// Exits
|
||||
ENTRANCE(RR_SPIRIT_TEMPLE_STATUE_ROOM, true),
|
||||
//Bunny hood jumps with a jumpslash can reach either hand and with good timing the platform as child.
|
||||
//The latter is definitely a trick, the former may not be but I am dissalowing it because I would have to spend a week writing a new branch of spirit key logic.
|
||||
//The latter is definitely a trick, the former may not be but I am disallowing it because I would have to spend a week writing a new branch of spirit key logic.
|
||||
//If this interaction with the hands is added, SpiritSharedLogic needs updating for it
|
||||
//RANDOTODO bunny hood megajumps let child into adult side too, saving for a future PR
|
||||
ENTRANCE(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, logic->CanUse(RG_HOVER_BOOTS) || (logic->BunnyHood() && logic->CanJumpslash() && logic->IsAdult) || logic->CanMegajump()),
|
||||
@@ -673,7 +673,7 @@ void RegionTable_Init_SpiritTemple() {
|
||||
LOCATION(RC_SPIRIT_TEMPLE_MQ_GIBDO_BOULDER, logic->BlastOrSmash()),
|
||||
}, {
|
||||
//Exits
|
||||
//climbing the grate through the celing allows you to go backwards here, but that's a clip not visible collision
|
||||
//climbing the grate through the ceiling allows you to go backwards here, but that's a clip not visible collision
|
||||
ENTRANCE(RR_SPIRIT_TEMPLE_MQ_TURNTABLE, logic->Get(LOGIC_SPIRIT_MQ_GIBDOS_CLEARED)),
|
||||
});
|
||||
|
||||
@@ -1211,7 +1211,7 @@ void RegionTable_Init_SpiritTemple() {
|
||||
// Exits
|
||||
ENTRANCE(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, true),
|
||||
//Bunny hood jumps with a jumpslash can reach either hand and with good timing the platform as child.
|
||||
//The latter is definitely a trick, the former may not be but I am dissalowing it because I would have to spend a week writing a new branch of spirit key logic.
|
||||
//The latter is definitely a trick, the former may not be but I am disallowing it because I would have to spend a week writing a new branch of spirit key logic.
|
||||
//If this interaction with the hands is added, SpiritSharedLogic needs updating for it
|
||||
ENTRANCE(RR_SPIRIT_TEMPLE_MQ_INNER_RIGHT_HAND, logic->CanUse(RG_HOVER_BOOTS) || (logic->BunnyHood() && logic->CanJumpslash() && logic->IsAdult) || logic->CanMegajump()),
|
||||
ENTRANCE(RR_SPIRIT_TEMPLE_MQ_INNER_LEFT_HAND, logic->CanUse(RG_HOVER_BOOTS) || (logic->BunnyHood() && logic->CanJumpslash() && logic->IsAdult)),
|
||||
|
||||
@@ -258,11 +258,11 @@ void RegionTable_Init_WaterTemple() {
|
||||
//Implies CanAvoid(RE_STINGER)
|
||||
//the full logic for the puzzle, as it is cut down here for optimisation because of how the water rising starts requiring scale
|
||||
//EVENT_ACCESS(LOGIC_WATER_PUSHED_B1_BLOCK, logic->HasItem(RG_GORONS_BRACELET) && logic->HasExplosives() &&
|
||||
// (logic->CanUse(RG_HOOKSHOT) || logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_HOVER_BOOTS) || (logic->IsAdult && logiic->BunnyHood()))),
|
||||
// (logic->CanUse(RG_HOOKSHOT) || logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_HOVER_BOOTS) || (logic->IsAdult && logic->BunnyHood()))),
|
||||
}, {
|
||||
//Locations //we can always get the pots by shooting them from afar and diving for the item...
|
||||
LOCATION(RC_WATER_TEMPLE_BASEMENT_BLOCK_PUZZLE_POT_1, (logic->CanBreakPots(ED_LONGSHOT, true, true) && logic->HasItem(RG_BRONZE_SCALE)) ||
|
||||
//or we can use irons and hookshot the pot, hookshoting to the target door if needed to cross the gap
|
||||
//or we can use irons and hookshot the pot, hookshotting to the target door if needed to cross the gap
|
||||
(logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->WaterTimer() >= 8)),
|
||||
LOCATION(RC_WATER_TEMPLE_BASEMENT_BLOCK_PUZZLE_POT_2, (logic->CanBreakPots(ED_LONGSHOT, true, true) && logic->HasItem(RG_BRONZE_SCALE)) ||
|
||||
(logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->WaterTimer() >= 8)),
|
||||
@@ -1122,7 +1122,7 @@ void RegionTable_Init_WaterTemple() {
|
||||
LOCATION(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_3, logic->CanUse(RG_HOOKSHOT)),
|
||||
}, {
|
||||
//Exits
|
||||
//you can also make it with hover boots and good timing, using the momentom of the slope, but it's a trick
|
||||
//you can also make it with hover boots and good timing, using the momentum of the slope, but it's a trick
|
||||
ENTRANCE(RR_WATER_TEMPLE_MQ_WATERFALL, logic->CanUse(RG_LONGSHOT) && logic->CanHitSwitch(ED_FAR)),
|
||||
ENTRANCE(RR_WATER_TEMPLE_MQ_STALFOS_PIT, true),
|
||||
});
|
||||
@@ -1142,7 +1142,7 @@ void RegionTable_Init_WaterTemple() {
|
||||
//Exits
|
||||
ENTRANCE(RR_WATER_TEMPLE_MQ_STALFOS_PIT, logic->CanUse(RG_HOOKSHOT) && (logic->IsAdult || (logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 8))),
|
||||
//Child can use the second method (going via the wall target near the half raised target) with bunny hood, but while the first jump is just wall assisted, the second is inconsistent
|
||||
//It is also possible for them to jump around the statue with bunny, or even climb up sometimes. This needs more investigation before catagorisation as they also seem inconsistent without a setup
|
||||
//It is also possible for them to jump around the statue with bunny, or even climb up sometimes. This needs more investigation before categorisation as they also seem inconsistent without a setup
|
||||
ENTRANCE(RR_WATER_TEMPLE_MQ_STALFOS_PIT_POTS, (logic->IsAdult && logic->CanUse(RG_HOOKSHOT)) || (logic->CanUse(RG_HOOKSHOT) && (logic->IsAdult || logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 8) && (logic->CanUse(RG_HOVER_BOOTS) || logic->Get(LOGIC_WATER_MQ_STALFOS_PIT)))),
|
||||
ENTRANCE(RR_WATER_TEMPLE_MQ_STALFOS_PIT_UPPER, logic->Get(LOGIC_WATER_MQ_STALFOS_PIT) && (logic->IsAdult || logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 8) && logic->CanUse(RG_HOOKSHOT)),
|
||||
});
|
||||
|
||||
@@ -49,7 +49,7 @@ void RegionTable_Init_GerudoFortress() {
|
||||
EVENT_ACCESS(LOGIC_GTG_GATE_OPEN, logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) && logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_GERUDO)),
|
||||
}, {
|
||||
//Locations
|
||||
LOCATION(RC_GF_GTG_ENTRANCE_RECTANGLE_SIGN, logic->IsAdult && logic->CanRead()),
|
||||
LOCATION(RC_GF_GTG_ENTRANCE_RECTANGLE_SIGN, logic->IsAdult && logic->CanRead() && (ctx->GetTrickOption(RT_PASS_GUARDS_WITH_NOTHING) || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD))),
|
||||
}, {
|
||||
//Exits
|
||||
ENTRANCE(RR_GF_TO_GTG, logic->Get(LOGIC_GTG_GATE_OPEN) && (logic->IsAdult || ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES))),
|
||||
@@ -57,7 +57,7 @@ void RegionTable_Init_GerudoFortress() {
|
||||
ENTRANCE(RR_GF_JAIL_WINDOW, logic->CanUse(RG_HOOKSHOT)),
|
||||
ENTRANCE(RR_GF_OUTSKIRTS, true),
|
||||
ENTRANCE(RR_GF_NEAR_GROTTO, logic->IsChild || logic->CanPassEnemy(RE_GERUDO_GUARD)),
|
||||
// RANDTODO: Add tricks for getting past the gerudo guarding the hba range
|
||||
// RANDOTODO: Add tricks for getting past the gerudo guarding the hba range
|
||||
ENTRANCE(RR_GF_ABOVE_GTG, logic->IsChild || logic->CanPassEnemy(RE_GERUDO_GUARD)),
|
||||
ENTRANCE(RR_GF_TOP_OF_UPPER_VINES, logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) && logic->CanUse(RG_LONGSHOT)),
|
||||
ENTRANCE(RR_GF_HBA_RANGE, logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)),
|
||||
|
||||
@@ -121,10 +121,10 @@ void RegionTable_Init_GerudoValley() {
|
||||
LOCATION(RC_GV_CRATE_BRIDGE_2, logic->IsChild && logic->CanBreakCrates()),
|
||||
LOCATION(RC_GV_CRATE_BRIDGE_3, logic->IsChild && logic->CanBreakCrates()),
|
||||
LOCATION(RC_GV_CRATE_BRIDGE_4, logic->IsChild && logic->CanBreakCrates()),
|
||||
LOCATION(RC_GV_ROCK_ACROSS_BRIDGE_1, logic->IsAdult),
|
||||
LOCATION(RC_GV_ROCK_ACROSS_BRIDGE_2, logic->IsAdult),
|
||||
LOCATION(RC_GV_ROCK_ACROSS_BRIDGE_3, logic->IsAdult),
|
||||
LOCATION(RC_GV_ROCK_ACROSS_BRIDGE_4, logic->IsAdult),
|
||||
LOCATION(RC_GV_ROCK_ACROSS_BRIDGE_1, logic->IsAdult && logic->CanBreakRocks()),
|
||||
LOCATION(RC_GV_ROCK_ACROSS_BRIDGE_2, logic->IsAdult && logic->CanBreakRocks()),
|
||||
LOCATION(RC_GV_ROCK_ACROSS_BRIDGE_3, logic->IsAdult && logic->CanBreakRocks()),
|
||||
LOCATION(RC_GV_ROCK_ACROSS_BRIDGE_4, logic->IsAdult && logic->CanBreakRocks()),
|
||||
LOCATION(RC_GV_BOULDER_ACROSS_BRIDGE, logic->IsAdult && logic->BlastOrSmash()),
|
||||
LOCATION(RC_GV_BRONZE_BOULDER_ACROSS_BRIDGE_1, logic->IsAdult && logic->CanUse(RG_MEGATON_HAMMER)),
|
||||
LOCATION(RC_GV_BRONZE_BOULDER_ACROSS_BRIDGE_2, logic->IsAdult && logic->CanUse(RG_MEGATON_HAMMER)),
|
||||
|
||||
@@ -14,54 +14,54 @@ void RegionTable_Init_HyruleField() {
|
||||
LOCATION(RC_HF_OCARINA_OF_TIME_ITEM, logic->IsChild && logic->StoneCount() == 3 && logic->HasItem(RG_BRONZE_SCALE)),
|
||||
LOCATION(RC_SONG_FROM_OCARINA_OF_TIME, logic->IsChild && logic->StoneCount() == 3 && logic->HasItem(RG_BRONZE_SCALE)),
|
||||
LOCATION(RC_HF_POND_STORMS_FAIRY, logic->CanUse(RG_SONG_OF_STORMS)),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_1, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_2, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_3, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_4, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_5, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_6, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_7, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_8, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_9, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_10, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_11, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_12, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_1, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_2, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_3, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_4, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_5, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_6, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_7, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_8, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_9, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_10, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_11, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_12, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_1, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_2, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_3, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_4, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_5, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_6, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_7, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_8, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_9, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_10, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_11, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_12, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_1, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_2, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_3, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_4, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_5, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_6, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_7, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_8, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_9, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_10, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_11, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_12, logic->CanCutShrubs()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_1, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_2, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_3, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_4, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_5, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_6, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_7, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_8, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_9, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_10, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_11, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_CENTRAL_GRASS_12, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_1, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_2, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_3, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_4, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_5, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_6, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_7, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_8, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_9, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_10, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_11, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_SOUTH_GRASS_12, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_1, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_2, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_3, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_4, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_5, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_6, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_7, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_8, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_9, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_10, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_11, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_MARKET_GRASS_12, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_1, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_2, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_3, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_4, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_5, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_6, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_7, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_8, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_9, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_10, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_11, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_NEAR_KF_GRASS_12, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_HF_SILVER_BOULDER, logic->CanUse(RG_SILVER_GAUNTLETS)),
|
||||
LOCATION(RC_HF_ROCK_1, logic->CanBreakRocks()),
|
||||
LOCATION(RC_HF_ROCK_2, logic->CanBreakRocks()),
|
||||
|
||||
@@ -39,48 +39,48 @@ void RegionTable_Init_LakeHylia() {
|
||||
LOCATION(RC_LH_LAB_GOSSIP_STONE, true),
|
||||
LOCATION(RC_LH_SOUTHEAST_GOSSIP_STONE, true),
|
||||
LOCATION(RC_LH_SOUTHWEST_GOSSIP_STONE, true),
|
||||
LOCATION(RC_LH_GRASS_1, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_2, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_3, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_4, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_5, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_6, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_7, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_8, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_9, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_10, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_11, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_12, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_13, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_14, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_15, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_16, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_17, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_18, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_19, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_20, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_21, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_22, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_23, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_24, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_25, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_26, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_27, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_28, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_29, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_30, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_31, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_32, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_33, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_34, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_35, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_36, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_CHILD_GRASS_1, logic->IsChild && logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_CHILD_GRASS_2, logic->IsChild && logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_CHILD_GRASS_3, logic->IsChild && logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_CHILD_GRASS_4, logic->IsChild && logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_WARP_PAD_GRASS_1, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_WARP_PAD_GRASS_2, logic->CanCutShrubs()),
|
||||
LOCATION(RC_LH_GRASS_1, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_2, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_3, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_4, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_5, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_6, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_7, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_8, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_9, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_10, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_11, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_12, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_13, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_14, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_15, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_16, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_17, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_18, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_19, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_20, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_21, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_22, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_23, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_24, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_25, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_26, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_27, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_28, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_29, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_30, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_31, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_32, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_33, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_34, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_35, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_GRASS_36, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_CHILD_GRASS_1, logic->IsChild && (logic->CanCutShrubs() || logic->SummonEpona())),
|
||||
LOCATION(RC_LH_CHILD_GRASS_2, logic->IsChild && (logic->CanCutShrubs() || logic->SummonEpona())),
|
||||
LOCATION(RC_LH_CHILD_GRASS_3, logic->IsChild && (logic->CanCutShrubs() || logic->SummonEpona())),
|
||||
LOCATION(RC_LH_CHILD_GRASS_4, logic->IsChild && (logic->CanCutShrubs() || logic->SummonEpona())),
|
||||
LOCATION(RC_LH_WARP_PAD_GRASS_1, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_WARP_PAD_GRASS_2, logic->CanCutShrubs() || logic->SummonEpona()),
|
||||
LOCATION(RC_LH_SCARECROW_BUTTERFLY_FAIRY, logic->IsChild && logic->CanUse(RG_STICKS)),
|
||||
LOCATION(RC_LH_LAB_RECTANGLE_SIGN, logic->CanRead()),
|
||||
LOCATION(RC_LH_NORTH_EXIT_ARROW_SIGN, logic->CanRead()),
|
||||
|
||||
@@ -1079,7 +1079,7 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal
|
||||
// RANDOTODO make a function to track our ammo vs his HP when ammo capacity is taken into account in logic
|
||||
// all swords can at least trade blows with dark link, and even with 1 damage a slash it works out
|
||||
return CanUseSword() ||
|
||||
// Boomerang is a relaible, infinite ammo stun, so it enables any way to get enough damage with the
|
||||
// Boomerang is a reliable, infinite ammo stun, so it enables any way to get enough damage with the
|
||||
// ammo we have Max HP dark link has 40 HP, bows and bombs do 2 so 20 ammo, stick jumpslash does 4 so
|
||||
// 10 sticks
|
||||
(CanUse(RG_BOOMERANG) &&
|
||||
@@ -1209,7 +1209,7 @@ bool Logic::CanPassEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal
|
||||
}
|
||||
|
||||
// Can we avoid this enemy while climbing up a wall, or doing a difficult platforming challenge?
|
||||
// use grounded if the challenge is such that the enemy interfears even if it cannot hit link out of the air
|
||||
// use grounded if the challenge is such that the enemy interferes even if it cannot hit link out of the air
|
||||
bool Logic::CanAvoidEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool grounded, uint8_t quantity) {
|
||||
// DISTANCE AND WALL ASSUMED, add more arguments later if needed
|
||||
if (CanKillEnemy(enemy, distance, true, quantity)) {
|
||||
@@ -1505,7 +1505,7 @@ bool Logic::WaterRisingTargetTo3FCentral() {
|
||||
* changed out of logic
|
||||
*
|
||||
* Extending from these 3, LOGIC_WATER_COULD_LOW_FROM_HIGH and LOGIC_WATER_COULD_HIGH_FROM_MID tell us if we can move
|
||||
* from 1 level to the next, without us first having to confirm we can always do the preceeding level first. These allow
|
||||
* from 1 level to the next, without us first having to confirm we can always do the preceding level first. These allow
|
||||
* us to check for conditions where we can complete a water level loop and reach any level from any level before we know
|
||||
* for sure we have real access. MIDDLE_EMBLEM always requires low water, so FROM_LOW is implied in
|
||||
* LOGIC_WATER_COULD_MIDDLE.
|
||||
@@ -1838,7 +1838,7 @@ uint16_t Logic::FireTimer() {
|
||||
}
|
||||
|
||||
// Tunic is not required if you are using irons to do something that a simple gold scale dive could do, and you are not
|
||||
// in water temple. (celing swimming and long walks through water do not count)
|
||||
// in water temple. (ceiling swimming and long walks through water do not count)
|
||||
uint16_t Logic::WaterTimer() {
|
||||
if (CanUse(RG_ZORA_TUNIC)) {
|
||||
return UINT16_MAX;
|
||||
|
||||
@@ -1172,13 +1172,13 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
|
||||
// This isn't technically necessary, because keys will no longer be consumed,
|
||||
// but for the player's sanity we display that they _have_ keys.
|
||||
for (Rando::DungeonInfo* dungeon : Rando::Context::GetInstance()->GetDungeons()->GetDungeonList()) {
|
||||
uint8_t keys = Rando::GetSceneSmallKeyMax(dungeon->GetScene());
|
||||
if (keys > 0) {
|
||||
gSaveContext.inventory.dungeonKeys[dungeon->GetScene()] = keys;
|
||||
if (Rando::GetSceneSmallKeyMax(dungeon->GetScene()) > 0) {
|
||||
gSaveContext.inventory.dungeonKeys[dungeon->GetScene()] =
|
||||
Rando::GetSceneUnspentSmallKeyMax(&gSaveContext, dungeon->GetScene());
|
||||
}
|
||||
}
|
||||
gSaveContext.inventory.dungeonKeys[SCENE_THIEVES_HIDEOUT] =
|
||||
Rando::GetSceneSmallKeyMax(SCENE_THIEVES_HIDEOUT);
|
||||
Rando::GetSceneUnspentSmallKeyMax(&gSaveContext, SCENE_THIEVES_HIDEOUT);
|
||||
} else if (item >= RG_KEATON_MASK && item <= RG_MASK_OF_TRUTH) {
|
||||
if (!ChildTradeSlotOccupied()) {
|
||||
INV_CONTENT(ITEM_TRADE_CHILD) = (int)ITEM_MASK_KEATON + (item - RG_KEATON_MASK);
|
||||
@@ -1351,7 +1351,8 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
|
||||
if ((item >= RG_FOREST_TEMPLE_KEY_RING) && (item <= RG_TREASURE_GAME_KEY_RING)) {
|
||||
u8 keysOnRing = Rando::GetSceneSmallKeyMax((SceneID)mapIndex);
|
||||
gSaveContext.ship.stats.dungeonKeys[mapIndex] = keysOnRing;
|
||||
gSaveContext.inventory.dungeonKeys[mapIndex] = keysOnRing;
|
||||
gSaveContext.inventory.dungeonKeys[mapIndex] =
|
||||
Rando::GetSceneUnspentSmallKeyMax(&gSaveContext, (SceneID)mapIndex);
|
||||
return Return_Item_Entry(giEntry, RG_NONE);
|
||||
}
|
||||
|
||||
@@ -1401,6 +1402,9 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
|
||||
}
|
||||
break;
|
||||
case RG_TRIFORCE:
|
||||
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] =
|
||||
static_cast<u32>(GAMEPLAYSTAT_TOTAL_TIME);
|
||||
gSaveContext.ship.stats.gameComplete = 1;
|
||||
GameInteractor_SetTriforceHuntCreditsWarpActive(true);
|
||||
break;
|
||||
case RG_TRIFORCE_PIECE:
|
||||
|
||||
@@ -1258,6 +1258,7 @@ RANDO_ENUM_ITEM(RHT_GANONS_SOUL_DUNGEONS_HINT)
|
||||
RANDO_ENUM_ITEM(RHT_GANONS_SOUL_TOKENS_HINT)
|
||||
RANDO_ENUM_ITEM(RHT_GANONS_SOUL_TRIFORCE_PIECES_HINT)
|
||||
// Wincon
|
||||
RANDO_ENUM_ITEM(RHT_WINCON_DEFEAT_GANON_HINT)
|
||||
RANDO_ENUM_ITEM(RHT_WINCON_ANYWHERE_HINT)
|
||||
RANDO_ENUM_ITEM(RHT_WINCON_STONES_HINT)
|
||||
RANDO_ENUM_ITEM(RHT_WINCON_MEDALLIONS_HINT)
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
// Randomizer tricks and glitches (beta) based on knowledge of what is achievable in Ship of Harkinian randomizer,
|
||||
// excludes Item manipulation (e.g. RBA/GIM/SRM) and Wrong Warping and Ganon BK Skip
|
||||
// TODO Fill and alphabetize (area based tricks must have area in name and come alphetized after general tricks)
|
||||
// TODO Fill and alphabetize (area based tricks must have area in name and come alphabetized after general tricks)
|
||||
// TODO test commented out tricks to see if consistent with console on SoH
|
||||
// TODO add area specific tricks
|
||||
RANDO_ENUM_BEGIN(RandomizerTrick)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -280,65 +282,83 @@ std::vector<ItemTrackerItem> silverRupeeItems = {
|
||||
ITEM_TRACKER_RG_CUSTOM(RG_GANONS_CASTLE_MQ_SILVER_SHADOW, "ShaTri", ITEM_RUPEE_SILVER, 0, DrawItem),
|
||||
};
|
||||
|
||||
std::vector<ItemTrackerDungeon> itemTrackerDungeonsWithMapsHorizontal = {
|
||||
{ SCENE_DEKU_TREE, { ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_DODONGOS_CAVERN, { ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_JABU_JABU, { ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_FOREST_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_FIRE_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_WATER_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_SPIRIT_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_SHADOW_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_INSIDE_GANONS_CASTLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_BOTTOM_OF_THE_WELL, { ITEM_KEY_SMALL, ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_ICE_CAVERN, { ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_GERUDO_TRAINING_GROUND, { ITEM_KEY_SMALL } },
|
||||
};
|
||||
// Dungeon items stacked top to bottom in one grid column, scene in data. Unused slots have no drawFunc
|
||||
using ItemTrackerDungeonColumn = std::array<ItemTrackerItem, 4>;
|
||||
|
||||
std::vector<ItemTrackerDungeon> itemTrackerDungeonsHorizontal = {
|
||||
{ SCENE_FOREST_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_FIRE_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_WATER_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_SPIRIT_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_SHADOW_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_INSIDE_GANONS_CASTLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_BOTTOM_OF_THE_WELL, { ITEM_KEY_SMALL } },
|
||||
{ SCENE_GERUDO_TRAINING_GROUND, { ITEM_KEY_SMALL } },
|
||||
};
|
||||
static ItemTrackerItem Key(SceneID scene) {
|
||||
return ITEM_TRACKER_ITEM(ITEM_KEY_SMALL, "", static_cast<uint32_t>(scene), DrawDungeonItem);
|
||||
}
|
||||
static ItemTrackerItem Boss(SceneID scene) {
|
||||
return ITEM_TRACKER_ITEM(ITEM_KEY_BOSS, "", static_cast<uint32_t>(scene), DrawDungeonItem);
|
||||
}
|
||||
static ItemTrackerItem Map(SceneID scene) {
|
||||
return ITEM_TRACKER_ITEM(ITEM_DUNGEON_MAP, "", static_cast<uint32_t>(scene), DrawDungeonItem);
|
||||
}
|
||||
static ItemTrackerItem Compass(SceneID scene) {
|
||||
return ITEM_TRACKER_ITEM(ITEM_COMPASS, "", static_cast<uint32_t>(scene), DrawDungeonItem);
|
||||
}
|
||||
|
||||
std::vector<ItemTrackerDungeon> itemTrackerDungeonsWithMapsCompact = {
|
||||
{ SCENE_FOREST_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_FIRE_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_WATER_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_SPIRIT_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_SHADOW_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS, ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_BOTTOM_OF_THE_WELL, { ITEM_KEY_SMALL, ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_DEKU_TREE, { ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_DODONGOS_CAVERN, { ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_JABU_JABU, { ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_ICE_CAVERN, { ITEM_DUNGEON_MAP, ITEM_COMPASS } },
|
||||
{ SCENE_INSIDE_GANONS_CASTLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_GERUDO_TRAINING_GROUND, { ITEM_KEY_SMALL } },
|
||||
};
|
||||
static const auto itemTrackerDungeonsWithMapsHorizontal = std::to_array<ItemTrackerDungeonColumn>({
|
||||
{ Map(SCENE_DEKU_TREE), Compass(SCENE_DEKU_TREE) },
|
||||
{ Map(SCENE_DODONGOS_CAVERN), Compass(SCENE_DODONGOS_CAVERN) },
|
||||
{ Map(SCENE_JABU_JABU), Compass(SCENE_JABU_JABU) },
|
||||
{ Key(SCENE_FOREST_TEMPLE), Boss(SCENE_FOREST_TEMPLE), Map(SCENE_FOREST_TEMPLE), Compass(SCENE_FOREST_TEMPLE) },
|
||||
{ Key(SCENE_FIRE_TEMPLE), Boss(SCENE_FIRE_TEMPLE), Map(SCENE_FIRE_TEMPLE), Compass(SCENE_FIRE_TEMPLE) },
|
||||
{ Key(SCENE_WATER_TEMPLE), Boss(SCENE_WATER_TEMPLE), Map(SCENE_WATER_TEMPLE), Compass(SCENE_WATER_TEMPLE) },
|
||||
{ Key(SCENE_SPIRIT_TEMPLE), Boss(SCENE_SPIRIT_TEMPLE), Map(SCENE_SPIRIT_TEMPLE), Compass(SCENE_SPIRIT_TEMPLE) },
|
||||
{ Key(SCENE_SHADOW_TEMPLE), Boss(SCENE_SHADOW_TEMPLE), Map(SCENE_SHADOW_TEMPLE), Compass(SCENE_SHADOW_TEMPLE) },
|
||||
{ Key(SCENE_INSIDE_GANONS_CASTLE), Boss(SCENE_GANONS_TOWER) },
|
||||
{ Key(SCENE_BOTTOM_OF_THE_WELL), Map(SCENE_BOTTOM_OF_THE_WELL), Compass(SCENE_BOTTOM_OF_THE_WELL) },
|
||||
{ Map(SCENE_ICE_CAVERN), Compass(SCENE_ICE_CAVERN) },
|
||||
{ Key(SCENE_GERUDO_TRAINING_GROUND), Key(SCENE_THIEVES_HIDEOUT), Key(SCENE_TREASURE_BOX_SHOP) },
|
||||
});
|
||||
|
||||
std::vector<ItemTrackerDungeon> itemTrackerDungeonsCompact = {
|
||||
{ SCENE_FOREST_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_FIRE_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_WATER_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_SPIRIT_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_SHADOW_TEMPLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_INSIDE_GANONS_CASTLE, { ITEM_KEY_SMALL, ITEM_KEY_BOSS } },
|
||||
{ SCENE_BOTTOM_OF_THE_WELL, { ITEM_KEY_SMALL } },
|
||||
{ SCENE_GERUDO_TRAINING_GROUND, { ITEM_KEY_SMALL } },
|
||||
{ SCENE_THIEVES_HIDEOUT, { ITEM_KEY_SMALL } },
|
||||
};
|
||||
static const auto itemTrackerDungeonsHorizontal = std::to_array<ItemTrackerDungeonColumn>({
|
||||
{ Key(SCENE_FOREST_TEMPLE), Boss(SCENE_FOREST_TEMPLE) },
|
||||
{ Key(SCENE_FIRE_TEMPLE), Boss(SCENE_FIRE_TEMPLE) },
|
||||
{ Key(SCENE_WATER_TEMPLE), Boss(SCENE_WATER_TEMPLE) },
|
||||
{ Key(SCENE_SPIRIT_TEMPLE), Boss(SCENE_SPIRIT_TEMPLE) },
|
||||
{ Key(SCENE_SHADOW_TEMPLE), Boss(SCENE_SHADOW_TEMPLE) },
|
||||
{ Key(SCENE_INSIDE_GANONS_CASTLE), Boss(SCENE_GANONS_TOWER) },
|
||||
{ Key(SCENE_BOTTOM_OF_THE_WELL), Key(SCENE_TREASURE_BOX_SHOP) },
|
||||
{ Key(SCENE_GERUDO_TRAINING_GROUND), Key(SCENE_THIEVES_HIDEOUT) },
|
||||
});
|
||||
|
||||
static const auto itemTrackerDungeonsWithMapsCompact = std::to_array<ItemTrackerDungeonColumn>({
|
||||
{ Key(SCENE_FOREST_TEMPLE), Boss(SCENE_FOREST_TEMPLE), Map(SCENE_FOREST_TEMPLE), Compass(SCENE_FOREST_TEMPLE) },
|
||||
{ Key(SCENE_FIRE_TEMPLE), Boss(SCENE_FIRE_TEMPLE), Map(SCENE_FIRE_TEMPLE), Compass(SCENE_FIRE_TEMPLE) },
|
||||
{ Key(SCENE_WATER_TEMPLE), Boss(SCENE_WATER_TEMPLE), Map(SCENE_WATER_TEMPLE), Compass(SCENE_WATER_TEMPLE) },
|
||||
{ Key(SCENE_SPIRIT_TEMPLE), Boss(SCENE_SPIRIT_TEMPLE), Map(SCENE_SPIRIT_TEMPLE), Compass(SCENE_SPIRIT_TEMPLE) },
|
||||
{ Key(SCENE_SHADOW_TEMPLE), Boss(SCENE_SHADOW_TEMPLE), Map(SCENE_SHADOW_TEMPLE), Compass(SCENE_SHADOW_TEMPLE) },
|
||||
{ Key(SCENE_BOTTOM_OF_THE_WELL), Map(SCENE_BOTTOM_OF_THE_WELL), Compass(SCENE_BOTTOM_OF_THE_WELL),
|
||||
Key(SCENE_TREASURE_BOX_SHOP) },
|
||||
{ Map(SCENE_DEKU_TREE), Compass(SCENE_DEKU_TREE) },
|
||||
{ Map(SCENE_DODONGOS_CAVERN), Compass(SCENE_DODONGOS_CAVERN) },
|
||||
{ Map(SCENE_JABU_JABU), Compass(SCENE_JABU_JABU) },
|
||||
{ Map(SCENE_ICE_CAVERN), Compass(SCENE_ICE_CAVERN) },
|
||||
{ Key(SCENE_INSIDE_GANONS_CASTLE), Boss(SCENE_GANONS_TOWER) },
|
||||
{ Key(SCENE_GERUDO_TRAINING_GROUND), Key(SCENE_THIEVES_HIDEOUT) },
|
||||
});
|
||||
|
||||
static const auto itemTrackerDungeonsCompact = std::to_array<ItemTrackerDungeonColumn>({
|
||||
{ Key(SCENE_FOREST_TEMPLE), Boss(SCENE_FOREST_TEMPLE) },
|
||||
{ Key(SCENE_FIRE_TEMPLE), Boss(SCENE_FIRE_TEMPLE) },
|
||||
{ Key(SCENE_WATER_TEMPLE), Boss(SCENE_WATER_TEMPLE) },
|
||||
{ Key(SCENE_SPIRIT_TEMPLE), Boss(SCENE_SPIRIT_TEMPLE) },
|
||||
{ Key(SCENE_SHADOW_TEMPLE), Boss(SCENE_SHADOW_TEMPLE) },
|
||||
{ Key(SCENE_INSIDE_GANONS_CASTLE), Boss(SCENE_GANONS_TOWER) },
|
||||
{ Key(SCENE_BOTTOM_OF_THE_WELL) },
|
||||
{ Key(SCENE_GERUDO_TRAINING_GROUND) },
|
||||
{ Key(SCENE_THIEVES_HIDEOUT) },
|
||||
{ Key(SCENE_TREASURE_BOX_SHOP) },
|
||||
});
|
||||
|
||||
std::map<uint16_t, std::string> itemTrackerDungeonShortNames = {
|
||||
{ SCENE_FOREST_TEMPLE, "FRST" }, { SCENE_FIRE_TEMPLE, "FIRE" }, { SCENE_WATER_TEMPLE, "WATR" },
|
||||
{ SCENE_SPIRIT_TEMPLE, "SPRT" }, { SCENE_SHADOW_TEMPLE, "SHDW" }, { SCENE_BOTTOM_OF_THE_WELL, "BOTW" },
|
||||
{ SCENE_DEKU_TREE, "DEKU" }, { SCENE_DODONGOS_CAVERN, "DCVN" }, { SCENE_JABU_JABU, "JABU" },
|
||||
{ SCENE_ICE_CAVERN, "ICE" }, { SCENE_INSIDE_GANONS_CASTLE, "GANON" }, { SCENE_GERUDO_TRAINING_GROUND, "GTG" },
|
||||
{ SCENE_THIEVES_HIDEOUT, "HIDE" },
|
||||
{ SCENE_THIEVES_HIDEOUT, "HIDE" }, { SCENE_TREASURE_BOX_SHOP, "TCG" },
|
||||
};
|
||||
|
||||
std::vector<ItemTrackerItem> dungeonItems = {};
|
||||
@@ -1439,55 +1459,56 @@ void DrawItemsInACircle(std::vector<ItemTrackerItem> items) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool ShowDungeonItem(const ItemTrackerItem& item) {
|
||||
if (item.data == SCENE_TREASURE_BOX_SHOP) {
|
||||
return IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_CHEST_MINIGAME);
|
||||
}
|
||||
if (item.data == SCENE_THIEVES_HIDEOUT) {
|
||||
return !IS_RANDO || RAND_GET_OPTION(RSK_GERUDO_FORTRESS).IsNot(RO_GF_CARPENTERS_FREE);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetDungeonItemsVector
|
||||
* Loops over dungeons and creates vectors of items in the correct order
|
||||
* to then call DrawItemsInRows
|
||||
* Lays out columns in blocks of `columns` wide, each block as tall as its tallest column,
|
||||
* and flattens them row by row for DrawItemsInRows
|
||||
*/
|
||||
std::vector<ItemTrackerItem> GetDungeonItemsVector(std::vector<ItemTrackerDungeon> dungeons, size_t columns = 6) {
|
||||
std::vector<ItemTrackerItem> dungeonItems = {};
|
||||
|
||||
size_t rowCount = 0;
|
||||
for (size_t i = 0; i < dungeons.size(); i++) {
|
||||
if (dungeons[i].items.size() > rowCount)
|
||||
rowCount = static_cast<int32_t>(dungeons[i].items.size());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < rowCount; i++) {
|
||||
for (size_t j = 0; j < std::min(dungeons.size(), columns); j++) {
|
||||
if (dungeons[j].items.size() > i) {
|
||||
switch (dungeons[j].items[i]) {
|
||||
case ITEM_KEY_SMALL:
|
||||
dungeonItems.push_back(ITEM_TRACKER_ITEM(ITEM_KEY_SMALL, "", dungeons[j].id, DrawDungeonItem));
|
||||
break;
|
||||
case ITEM_KEY_BOSS:
|
||||
// Swap Ganon's Castle boss key to the right scene ID manually
|
||||
if (dungeons[j].id == SCENE_INSIDE_GANONS_CASTLE) {
|
||||
dungeonItems.push_back(
|
||||
ITEM_TRACKER_ITEM(ITEM_KEY_BOSS, "", SCENE_GANONS_TOWER, DrawDungeonItem));
|
||||
} else {
|
||||
dungeonItems.push_back(
|
||||
ITEM_TRACKER_ITEM(ITEM_KEY_BOSS, "", dungeons[j].id, DrawDungeonItem));
|
||||
}
|
||||
break;
|
||||
case ITEM_DUNGEON_MAP:
|
||||
dungeonItems.push_back(
|
||||
ITEM_TRACKER_ITEM(ITEM_DUNGEON_MAP, "", dungeons[j].id, DrawDungeonItem));
|
||||
break;
|
||||
case ITEM_COMPASS:
|
||||
dungeonItems.push_back(ITEM_TRACKER_ITEM(ITEM_COMPASS, "", dungeons[j].id, DrawDungeonItem));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
dungeonItems.push_back(ITEM_TRACKER_ITEM(ITEM_NONE, "", 0, DrawItem));
|
||||
std::vector<ItemTrackerItem> GetDungeonItemsVector(std::span<const ItemTrackerDungeonColumn> layout,
|
||||
size_t columns = 6) {
|
||||
struct ShownColumn {
|
||||
ItemTrackerDungeonColumn items;
|
||||
size_t size = 0;
|
||||
};
|
||||
std::vector<ShownColumn> shown;
|
||||
for (const auto& column : layout) {
|
||||
ShownColumn kept;
|
||||
for (const auto& item : column) {
|
||||
if (item.drawFunc != nullptr && ShowDungeonItem(item)) {
|
||||
kept.items[kept.size++] = item;
|
||||
}
|
||||
}
|
||||
if (kept.size > 0) {
|
||||
shown.push_back(kept);
|
||||
}
|
||||
}
|
||||
|
||||
if (dungeons.size() > columns) {
|
||||
std::vector<ItemTrackerItem> nextDungeonItems =
|
||||
GetDungeonItemsVector(std::vector<ItemTrackerDungeon>(dungeons.begin() + columns, dungeons.end()), columns);
|
||||
dungeonItems.insert(dungeonItems.end(), nextDungeonItems.begin(), nextDungeonItems.end());
|
||||
std::vector<ItemTrackerItem> dungeonItems = {};
|
||||
for (size_t start = 0; start < shown.size(); start += columns) {
|
||||
size_t end = std::min(start + columns, shown.size());
|
||||
size_t rowCount = 0;
|
||||
for (size_t j = start; j < end; j++) {
|
||||
rowCount = std::max(rowCount, shown[j].size);
|
||||
}
|
||||
for (size_t i = 0; i < rowCount; i++) {
|
||||
for (size_t j = start; j < end; j++) {
|
||||
if (i < shown[j].size) {
|
||||
dungeonItems.push_back(shown[j].items[i]);
|
||||
} else {
|
||||
dungeonItems.push_back(ITEM_TRACKER_ITEM(ITEM_NONE, "", 0, DrawItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dungeonItems;
|
||||
@@ -1563,18 +1584,12 @@ void UpdateVectors() {
|
||||
SECTION_DISPLAY_SEPARATE) {
|
||||
if (CVarGetInteger(CVAR_TRACKER_ITEM("DungeonItems.DisplayMaps"), 1)) {
|
||||
dungeonItems = GetDungeonItemsVector(itemTrackerDungeonsWithMapsHorizontal, 12);
|
||||
// Manually adding Thieves Hideout to an open spot so we don't get an additional row for one item
|
||||
dungeonItems[23] = ITEM_TRACKER_ITEM(ITEM_KEY_SMALL, "", SCENE_THIEVES_HIDEOUT, DrawDungeonItem);
|
||||
} else {
|
||||
// Manually adding Thieves Hideout to an open spot so we don't get an additional row for one item
|
||||
dungeonItems = GetDungeonItemsVector(itemTrackerDungeonsHorizontal, 8);
|
||||
dungeonItems[15] = ITEM_TRACKER_ITEM(ITEM_KEY_SMALL, "", SCENE_THIEVES_HIDEOUT, DrawDungeonItem);
|
||||
}
|
||||
} else {
|
||||
if (CVarGetInteger(CVAR_TRACKER_ITEM("DungeonItems.DisplayMaps"), 1)) {
|
||||
dungeonItems = GetDungeonItemsVector(itemTrackerDungeonsWithMapsCompact);
|
||||
// Manually adding Thieves Hideout to an open spot so we don't get an additional row for one item
|
||||
dungeonItems[35] = ITEM_TRACKER_ITEM(ITEM_KEY_SMALL, "", SCENE_THIEVES_HIDEOUT, DrawDungeonItem);
|
||||
} else {
|
||||
dungeonItems = GetDungeonItemsVector(itemTrackerDungeonsCompact);
|
||||
}
|
||||
|
||||
@@ -72,11 +72,6 @@ static std::array<const char*, 16> itemTrackerWindowIDs = { "Item Tracker",
|
||||
"Total Checks" };
|
||||
void ItemTracker_LoadFromPreset(const nlohmann::json& trackerInfo);
|
||||
|
||||
typedef struct ItemTrackerDungeon {
|
||||
uint32_t id;
|
||||
std::vector<uint32_t> items;
|
||||
} ItemTrackerDungeon;
|
||||
|
||||
class ItemTrackerSettingsWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
@@ -447,7 +447,7 @@ void Settings::CreateOptions() {
|
||||
OPT_U8(RSK_MQ_DUNGEON_RANDOM, {"None", "Set Number", "Random", "Selection Only"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeons"), WIDGET_CVAR_COMBOBOX, RO_MQ_DUNGEONS_NONE, false, nullptr, IMFLAG_NONE);
|
||||
OPT_CALLBACK(RSK_MQ_DUNGEON_RANDOM, {
|
||||
switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MQDungeons"), RO_MQ_DUNGEONS_NONE)) {
|
||||
// If No MQ Dungeons, add a separator after the combobx and hide
|
||||
// If No MQ Dungeons, add a separator after the combobox and hide
|
||||
// the count slider and the toggle for individual dungeon selections.
|
||||
case RO_MQ_DUNGEONS_NONE:
|
||||
mOptions[RSK_MQ_DUNGEON_COUNT].Hide();
|
||||
@@ -1099,7 +1099,7 @@ void Settings::CreateOptions() {
|
||||
mOptions[RSK_LOACH_HINT].Enable();
|
||||
} else {
|
||||
mOptions[RSK_LOACH_HINT].Disable(
|
||||
"Loach hint is only avaliable with \"Fishsanity\" set to \"Shuffle only Hyrule Loach\"\nas that's the only "
|
||||
"Loach hint is only available with \"Fishsanity\" set to \"Shuffle only Hyrule Loach\"\nas that's the only "
|
||||
"setting where you present the loach to the fishing pond owner.");
|
||||
}
|
||||
});
|
||||
@@ -2771,7 +2771,7 @@ void Context::FinalizeSettings(const std::set<RandomizerCheck>& excludedLocation
|
||||
mqSet += 1;
|
||||
break;
|
||||
case RO_MQ_SET_RANDOM:
|
||||
// 50% per dungeon, rolled separatly so people can either have a linear distribtuion
|
||||
// 50% per dungeon, rolled separately so people can either have a linear distribution
|
||||
// or a bell curve for the number of MQ dungeons per seed.
|
||||
if (Random(0, 2)) {
|
||||
dungeon->SetMQ();
|
||||
@@ -2815,7 +2815,7 @@ void Context::FinalizeSettings(const std::set<RandomizerCheck>& excludedLocation
|
||||
}
|
||||
// otherwise, every dungeon is possible
|
||||
} else {
|
||||
// if count is MAX_MQ_DUNGEON_COUNT, we know everything is MQ, so can skip some setps and not set Known
|
||||
// if count is MAX_MQ_DUNGEON_COUNT, we know everything is MQ, so can skip some steps and not set Known
|
||||
if (mOptions[RSK_MQ_DUNGEON_RANDOM].Is(RO_MQ_DUNGEONS_SET_NUMBER) && mqCount == MAX_MQ_DUNGEON_COUNT) {
|
||||
randMQOption.resize(MAX_MQ_DUNGEON_COUNT);
|
||||
for (int i = 0; i < MAX_MQ_DUNGEON_COUNT; i++) {
|
||||
|
||||
@@ -318,6 +318,18 @@ bool Extractor::GetRomPathFromBox() {
|
||||
}
|
||||
mCurrentRomPath = nameBuffer;
|
||||
#else
|
||||
if (!pfd::settings::available()) {
|
||||
std::string msg =
|
||||
"No file picker was found on your system. Ship of Harkinian needs one of these to select a ROM:\n\n"
|
||||
" - GNOME, Xfce, Cinnamon and other GTK desktops: install \"zenity\"\n"
|
||||
" - KDE Plasma: install \"kdialog\"\n\n"
|
||||
"On other distributions or window managers, search your package manager.\n\n"
|
||||
"Alternatively, place your ROM in:\n" +
|
||||
mSearchPath + "\nand restart.";
|
||||
ShowErrorBox("File Picker Unavailable", msg.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto selection = pfd::open_file("Select a file", mSearchPath, { "N64 Roms", "*.z64 *.n64 *.v64" }).result();
|
||||
|
||||
if (selection.empty()) {
|
||||
@@ -354,7 +366,7 @@ size_t Extractor::GetCurRomSize() const {
|
||||
|
||||
bool Extractor::ValidateAndFixRom() {
|
||||
// The MQ debug rom sometimes has the header patched to look like a US rom.
|
||||
// Check the crc as if it were changed back, but leave mRomData as the original dump for torch.
|
||||
// Check the crc as if it were changed back. CallTorch changes it back for real.
|
||||
const uint8_t region = mRomData[0x3E];
|
||||
if (GetRomVerCrc() == OOT_PAL_GC_MQ_DBG) {
|
||||
mRomData[0x3E] = 'P';
|
||||
@@ -654,6 +666,13 @@ bool Extractor::CallTorch(std::string installPath, std::string exportdir, std::a
|
||||
*totalExtract = SohTorch::CountAssetFiles(srcDir + "/" + GetTorchVersionDir());
|
||||
*extractCount = 0;
|
||||
|
||||
// MQ debug dumps only hold 54MB of data, the rest is dump-specific padding, and some have
|
||||
// the region byte changed. Trim and restore it so every dump matches the one config.yml hash.
|
||||
if (GetRomVerCrc() == OOT_PAL_GC_MQ_DBG && mRomData.size() == MB64) {
|
||||
mRomData.resize(MB54);
|
||||
mRomData[0x3E] = 'P';
|
||||
}
|
||||
|
||||
// config.yml decides whether this is oot.o2r or oot-mq.o2r.
|
||||
std::string archiveName = SohTorch::Extract(std::move(mRomData), srcDir, tempdir, portVersion, extractCount);
|
||||
bool success = !archiveName.empty();
|
||||
|
||||
@@ -254,4 +254,10 @@ void DummyPlayer_Destroy(Actor* actor, PlayState* play) {
|
||||
// asserts. Set the id back to ACTOR_PLAYER so that `numLoaded` will be decremented
|
||||
// correctly.
|
||||
actor->id = ACTOR_PLAYER;
|
||||
|
||||
// Drop client's pointer so packet handlers don't use after free
|
||||
uint32_t clientId = Anchor::Instance->GetDummyPlayerClientId(actor);
|
||||
if (Anchor::Instance->clients.contains(clientId) && Anchor::Instance->clients[clientId].player == (Player*)actor) {
|
||||
Anchor::Instance->clients[clientId].player = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,18 +104,50 @@ inline void from_json(const json& j, SohStats& sohStats) {
|
||||
j.at("fileCreatedAt").get_to(sohStats.fileCreatedAt);
|
||||
}
|
||||
|
||||
#define SILVER_RUPEE_FIELDS(X) \
|
||||
X(silverShadowBlades) \
|
||||
X(silverShadowPit) \
|
||||
X(silverShadowSpikes) \
|
||||
X(silverSpiritChild) \
|
||||
X(silverSpiritSun) \
|
||||
X(silverSpiritBoulders) \
|
||||
X(silverBotw) \
|
||||
X(silverIceCavernBlades) \
|
||||
X(silverIceCavernBlock) \
|
||||
X(silverGtgSlope) \
|
||||
X(silverGtgLava) \
|
||||
X(silverGtgWater) \
|
||||
X(silverGanonLight) \
|
||||
X(silverGanonForest) \
|
||||
X(silverGanonFire) \
|
||||
X(silverGanonSpirit) \
|
||||
X(silverMqDodongosCavern) \
|
||||
X(silverMqShadowInvisibleBlades) \
|
||||
X(silverMqSpiritLobby) \
|
||||
X(silverMqSpiritBigWall) \
|
||||
X(silverMqGanonWater) \
|
||||
X(silverMqGanonShadow)
|
||||
|
||||
inline void to_json(json& j, const ShipRandomizerSaveContextData& shipRandomizerSaveContextData) {
|
||||
j = json{
|
||||
{ "triforcePiecesCollected", shipRandomizerSaveContextData.triforcePiecesCollected },
|
||||
{ "bombchuUpgradeLevel", shipRandomizerSaveContextData.bombchuUpgradeLevel },
|
||||
};
|
||||
#define SILVER_TO_JSON(field) j[#field] = shipRandomizerSaveContextData.field;
|
||||
SILVER_RUPEE_FIELDS(SILVER_TO_JSON)
|
||||
#undef SILVER_TO_JSON
|
||||
}
|
||||
|
||||
inline void from_json(const json& j, ShipRandomizerSaveContextData& shipRandomizerSaveContextData) {
|
||||
j.at("triforcePiecesCollected").get_to(shipRandomizerSaveContextData.triforcePiecesCollected);
|
||||
j.at("bombchuUpgradeLevel").get_to(shipRandomizerSaveContextData.bombchuUpgradeLevel);
|
||||
#define SILVER_FROM_JSON(field) shipRandomizerSaveContextData.field = j.value(#field, (s8)0);
|
||||
SILVER_RUPEE_FIELDS(SILVER_FROM_JSON)
|
||||
#undef SILVER_FROM_JSON
|
||||
}
|
||||
|
||||
#undef SILVER_RUPEE_FIELDS
|
||||
|
||||
inline void to_json(json& j, const ShipQuestSpecificSaveContextData& shipQuestSpecificSaveContextData) {
|
||||
j = json{
|
||||
{ "randomizer", shipQuestSpecificSaveContextData.randomizer },
|
||||
|
||||
+43
-15
@@ -430,7 +430,12 @@ void OTRGlobals::RunExtract(int argc, char* argv[]) {
|
||||
Extractor extract;
|
||||
PromptSteps promptStep = PS_FILE_CHECK;
|
||||
bool generatedIsMQ = false;
|
||||
// Whether generatedIsMQ is set, i.e. the first prompted rom extracted fine
|
||||
bool generatedFirst = false;
|
||||
bool lastExtractFailed = false;
|
||||
std::atomic<size_t> extractCount = 0, totalExtract = 0;
|
||||
// Set by the extraction thread, shown as a popup by the main thread once the task finishes
|
||||
std::atomic<bool> extractFailed = false;
|
||||
|
||||
std::string installPath = std::filesystem::absolute(Ship::Context::GetAppBundlePath()).string();
|
||||
std::string dataPath = std::filesystem::absolute(Ship::Context::GetAppDirectoryPath(appShortName)).string();
|
||||
@@ -613,15 +618,17 @@ void OTRGlobals::RunExtract(int argc, char* argv[]) {
|
||||
std::string msg = "Archive for current ROM, " + archive + ", already exists.\nExtract again?";
|
||||
SohGui::RegisterPopup("Confirm Re-extract", msg.c_str(), "Yes", "No", [&]() {
|
||||
extractionTask = threadPool->submit_task([&]() -> void {
|
||||
extract.CallTorch(installPath, Ship::Context::GetAppDirectoryPath(appShortName),
|
||||
&extractCount, &totalExtract);
|
||||
extractFailed =
|
||||
!extract.CallTorch(installPath, Ship::Context::GetAppDirectoryPath(appShortName),
|
||||
&extractCount, &totalExtract);
|
||||
extractCount = totalExtract = 0;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
extractionTask = threadPool->submit_task([&]() -> void {
|
||||
extract.CallTorch(installPath, Ship::Context::GetAppDirectoryPath(appShortName),
|
||||
&extractCount, &totalExtract);
|
||||
extractFailed =
|
||||
!extract.CallTorch(installPath, Ship::Context::GetAppDirectoryPath(appShortName),
|
||||
&extractCount, &totalExtract);
|
||||
extractCount = totalExtract = 0;
|
||||
});
|
||||
}
|
||||
@@ -677,9 +684,13 @@ void OTRGlobals::RunExtract(int argc, char* argv[]) {
|
||||
continue;
|
||||
}
|
||||
extractionTask = threadPool->submit_task([&]() -> void {
|
||||
extract.CallTorch(installPath, Ship::Context::GetAppDirectoryPath(appShortName),
|
||||
&extractCount, &totalExtract);
|
||||
generatedIsMQ = extract.IsMasterQuest();
|
||||
extractFailed =
|
||||
!extract.CallTorch(installPath, Ship::Context::GetAppDirectoryPath(appShortName),
|
||||
&extractCount, &totalExtract);
|
||||
if (!extractFailed) {
|
||||
generatedIsMQ = extract.IsMasterQuest();
|
||||
generatedFirst = true;
|
||||
}
|
||||
promptStep = PS_SECOND;
|
||||
extractCount = 0;
|
||||
totalExtract = 0;
|
||||
@@ -687,17 +698,28 @@ void OTRGlobals::RunExtract(int argc, char* argv[]) {
|
||||
continue;
|
||||
}
|
||||
case PS_SECOND: {
|
||||
// Keep asking until the user says no, so a failed or cancelled rom can be retried
|
||||
SohGui::RegisterPopup(
|
||||
"Extraction Complete", "ROM Extracted. Extract another?", "Yes", "No",
|
||||
lastExtractFailed ? "Extract Another" : "Extraction Complete",
|
||||
lastExtractFailed ? "Try extracting another ROM?" : "ROM Extracted. Extract another?",
|
||||
"Yes", "No",
|
||||
[&]() {
|
||||
if (!extract.ManuallySearchForRomMatchingType(generatedIsMQ ? RomSearchMode::Vanilla
|
||||
: RomSearchMode::MQ)) {
|
||||
extractStep = ES_VERIFY;
|
||||
} else {
|
||||
RomSearchMode mode = RomSearchMode::Both;
|
||||
if (generatedFirst) {
|
||||
mode = generatedIsMQ ? RomSearchMode::Vanilla : RomSearchMode::MQ;
|
||||
}
|
||||
if (extract.ManuallySearchForRomMatchingType(mode)) {
|
||||
extractionTask = threadPool->submit_task([&]() -> void {
|
||||
extract.CallTorch(installPath, Ship::Context::GetAppDirectoryPath(appShortName),
|
||||
&extractCount, &totalExtract);
|
||||
extractStep = ES_VERIFY;
|
||||
extractFailed = !extract.CallTorch(
|
||||
installPath, Ship::Context::GetAppDirectoryPath(appShortName),
|
||||
&extractCount, &totalExtract);
|
||||
if (!extractFailed && generatedFirst) {
|
||||
extractStep = ES_VERIFY;
|
||||
} else if (!extractFailed) {
|
||||
// A retry of the first rom worked, so still offer the other type
|
||||
generatedIsMQ = extract.IsMasterQuest();
|
||||
generatedFirst = true;
|
||||
}
|
||||
extractCount = 0;
|
||||
totalExtract = 0;
|
||||
});
|
||||
@@ -754,6 +776,12 @@ void OTRGlobals::RunExtract(int argc, char* argv[]) {
|
||||
} catch (const std::exception& e) {
|
||||
SohGui::RegisterPopup("Extraction Crashed", e.what(), "Close", "", []() { exit(1); });
|
||||
}
|
||||
lastExtractFailed = extractFailed;
|
||||
if (extractFailed) {
|
||||
SohGui::RegisterPopup("Extraction Failed",
|
||||
"The ROM could not be extracted. Check the log for details.", "OK", "");
|
||||
extractFailed = false;
|
||||
}
|
||||
extractionTask.reset();
|
||||
} else {
|
||||
if (!ImGui::IsPopupOpen("ROM Extraction")) {
|
||||
|
||||
@@ -1186,12 +1186,12 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) {
|
||||
u16 seqId = AudioEditor_GetReplacementSeq(seqPlayer->seqId);
|
||||
SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqId]);
|
||||
|
||||
// The game apparantely would sometimes do negative array lookups, the result of which would
|
||||
// The game apparently would sometimes do negative array lookups, the result of which would
|
||||
// get rejected by AudioHeap_SearchCaches, never changing the actual fontid.
|
||||
if (result > sDat.numFonts)
|
||||
if (result >= sDat.numFonts)
|
||||
break;
|
||||
|
||||
fontId = sDat.fonts[(sDat.numFonts - result - 1)];
|
||||
fontId = sDat.fonts[sDat.numFonts - result - 1];
|
||||
}
|
||||
|
||||
if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, fontId)) {
|
||||
|
||||
@@ -54,6 +54,8 @@ int SDL_main(int argc, char* argv[]) {
|
||||
#endif
|
||||
// Allow non-ascii characters for Windows
|
||||
setlocale(LC_ALL, ".UTF8");
|
||||
// Keep '.' as the decimal point, user locale may use ','
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
|
||||
#else //_WIN32
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
+28
-8
@@ -114,6 +114,7 @@ void GetItem_DrawJewelGoron(PlayState* play, s16 drawId);
|
||||
void GetItem_DrawJewelZora(PlayState* play, s16 drawId);
|
||||
void GetItem_DrawGenericMusicNote(PlayState* play, s16 drawId);
|
||||
void GetItem_DrawTriforcePiece(PlayState* play, s16 drawId);
|
||||
void GetItem_DrawTriforce(PlayState* play, s16 drawId);
|
||||
void GetItem_DrawFishingPole(PlayState* play, s16 drawId);
|
||||
|
||||
typedef struct {
|
||||
@@ -384,14 +385,15 @@ DrawItemTableEntry sDrawItemTable[] = {
|
||||
|
||||
{ GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, // Generic
|
||||
|
||||
{ GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, // Zelda's Lullaby
|
||||
{ GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, // Epona's song
|
||||
{ GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, // Saria's song
|
||||
{ GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, // Sun's song
|
||||
{ GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, // Song of time
|
||||
{ GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, // Song of storms
|
||||
{ GetItem_DrawTriforcePiece, { gTriforcePiece0DL } }, // Triforce Piece
|
||||
{ GetItem_DrawFishingPole, { gGiFishingPoleDL } }, // Fishing Pole
|
||||
{ GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, // Zelda's Lullaby
|
||||
{ GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, // Epona's song
|
||||
{ GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, // Saria's song
|
||||
{ GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, // Sun's song
|
||||
{ GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, // Song of time
|
||||
{ GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, // Song of storms
|
||||
{ GetItem_DrawTriforcePiece, { gTriforcePiece0DL } }, // Triforce Piece
|
||||
{ GetItem_DrawTriforce, { gTriforcePieceCompletedDL } }, // Triforce
|
||||
{ GetItem_DrawFishingPole, { gGiFishingPoleDL } }, // Fishing Pole
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1027,6 +1029,24 @@ void GetItem_DrawTriforcePiece(PlayState* play, s16 drawId) {
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
}
|
||||
|
||||
void GetItem_DrawTriforce(PlayState* play, s16 drawId) {
|
||||
OPEN_DISPS(play->state.gfxCtx);
|
||||
|
||||
Gfx_SetupDL_25Opa(play->state.gfxCtx);
|
||||
|
||||
Matrix_Scale(0.035f, 0.035f, 0.035f, MTXMODE_APPLY);
|
||||
|
||||
uint8_t index = gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected % 3;
|
||||
Gfx* triforceDL = (Gfx*)gTriforcePieceCompletedDL;
|
||||
|
||||
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__),
|
||||
G_MTX_MODELVIEW | G_MTX_LOAD);
|
||||
|
||||
gSPDisplayList(POLY_OPA_DISP++, triforceDL);
|
||||
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
}
|
||||
|
||||
void GetItem_DrawFishingPole(PlayState* play, s16 drawId) {
|
||||
Vec3f pos;
|
||||
OPEN_DISPS(play->state.gfxCtx);
|
||||
|
||||
@@ -3263,7 +3263,7 @@ void Interface_UpdateMagicBar(PlayState* play) {
|
||||
break;
|
||||
|
||||
case MAGIC_STATE_CONSUME:
|
||||
if (!Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER)) {
|
||||
if (GameInteractor_Should(VB_MAGIC_METER_DEPLETE_WHILE_CONSUMING, true)) {
|
||||
gSaveContext.magic -= 2;
|
||||
if (gSaveContext.magic <= 0) {
|
||||
gSaveContext.magic = 0;
|
||||
@@ -3454,7 +3454,6 @@ void Interface_DrawMagicBar(PlayState* play) {
|
||||
Color_RGB8 magicbar_yellow = { 250, 250, 0 }; // Magic bar being used
|
||||
Color_RGB8 magicbar_green = { R_MAGIC_FILL_COLOR(0), R_MAGIC_FILL_COLOR(1),
|
||||
R_MAGIC_FILL_COLOR(2) }; // Magic bar fill
|
||||
Color_RGB8 magicbar_blue = { 0, 0, 200 }; // Infinite magic bar
|
||||
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Consumable.MagicActive.Changed"), 0)) {
|
||||
magicbar_yellow = CVarGetColor24(CVAR_COSMETIC("Consumable.MagicActive.Value"), magicbar_yellow);
|
||||
@@ -3462,9 +3461,7 @@ void Interface_DrawMagicBar(PlayState* play) {
|
||||
if (CVarGetInteger(CVAR_COSMETIC("Consumable.Magic.Changed"), 0)) {
|
||||
magicbar_green = CVarGetColor24(CVAR_COSMETIC("Consumable.Magic.Value"), magicbar_green);
|
||||
}
|
||||
if (CVarGetInteger("gCosmetics.Consumable_MagicInfinite.Changed", 0)) {
|
||||
magicbar_blue = CVarGetColor24("gCosmetics.Consumable_MagicInfinite.Value", magicbar_blue);
|
||||
}
|
||||
GameInteractor_Should(VB_APPLY_MAGIC_METER_FILL_COLOR, true, &magicbar_green);
|
||||
|
||||
OPEN_DISPS(play->state.gfxCtx);
|
||||
|
||||
@@ -3591,30 +3588,16 @@ void Interface_DrawMagicBar(PlayState* play) {
|
||||
|
||||
// Fill the rest of the bar with the normal magic color
|
||||
gDPPipeSync(OVERLAY_DISP++);
|
||||
if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER)) {
|
||||
// Blue magic
|
||||
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_blue.r, magicbar_blue.g, magicbar_blue.b,
|
||||
interfaceCtx->magicAlpha);
|
||||
} else {
|
||||
// Green magic (default)
|
||||
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_green.r, magicbar_green.g, magicbar_green.b,
|
||||
interfaceCtx->magicAlpha);
|
||||
}
|
||||
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_green.r, magicbar_green.g, magicbar_green.b,
|
||||
interfaceCtx->magicAlpha);
|
||||
|
||||
gSPWideTextureRectangle(OVERLAY_DISP++, rMagicFillX << 2, (magicBarY + 3) << 2,
|
||||
(rMagicFillX + gSaveContext.magicTarget) << 2, (magicBarY + 10) << 2,
|
||||
G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10);
|
||||
} else {
|
||||
// Fill the whole bar with the normal magic color
|
||||
if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER)) {
|
||||
// Blue magic
|
||||
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_blue.r, magicbar_blue.g, magicbar_blue.b,
|
||||
interfaceCtx->magicAlpha);
|
||||
} else {
|
||||
// Green magic (default)
|
||||
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_green.r, magicbar_green.g, magicbar_green.b,
|
||||
interfaceCtx->magicAlpha);
|
||||
}
|
||||
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_green.r, magicbar_green.g, magicbar_green.b,
|
||||
interfaceCtx->magicAlpha);
|
||||
|
||||
gDPLoadMultiBlock_4b(OVERLAY_DISP++, gMagicMeterFillTex, 0, G_TX_RENDERTILE, G_IM_FMT_I, 16, 16, 0,
|
||||
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK,
|
||||
|
||||
@@ -918,7 +918,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) {
|
||||
this->csTimer = 0;
|
||||
this->csCamFov = 60.0f;
|
||||
BossGanon_SetIntroCsCamera(this, 12);
|
||||
if (GameInteractor_Should(VB_PLAY_GANONDORF_INTRO_CS, true, this)) {
|
||||
if (GameInteractor_Should(VB_PLAY_GANONDORF_INTRO_CS, true, this, 0x70CB)) {
|
||||
Message_StartTextbox(play, 0x70CB, NULL);
|
||||
}
|
||||
}
|
||||
@@ -942,7 +942,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) {
|
||||
|
||||
this->csState = 19;
|
||||
this->csTimer = 0;
|
||||
if (GameInteractor_Should(VB_PLAY_GANONDORF_INTRO_CS, true, this)) {
|
||||
if (GameInteractor_Should(VB_PLAY_GANONDORF_INTRO_CS, true, this, 0x70CC)) {
|
||||
Message_StartTextbox(play, 0x70CC, NULL);
|
||||
}
|
||||
Animation_MorphToPlayOnce(&this->skelAnime, &gGanondorfRaiseHandStartAnim, -5.0f);
|
||||
@@ -985,7 +985,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) {
|
||||
}
|
||||
|
||||
if ((this->csTimer > 80) && (Message_GetState(&play->msgCtx) == TEXT_STATE_NONE)) {
|
||||
if (!GameInteractor_Should(VB_PLAY_GANONDORF_INTRO_CS, true, this)) {
|
||||
if (!GameInteractor_Should(VB_PLAY_GANONDORF_INTRO_CS, true, this, 0)) {
|
||||
this->timers[2] = 30;
|
||||
this->csCamAt.x = this->unk_1FC.x - 10.0f;
|
||||
this->csCamAt.y = this->unk_1FC.y + 30.0f;
|
||||
|
||||
@@ -672,43 +672,39 @@ void BossVa_Init(Actor* thisx, PlayState* play2) {
|
||||
} else {
|
||||
this->actor.colChkInfo.damageTable = sDamageTable;
|
||||
sPhase2Timer = 0xFFFF;
|
||||
if (Flags_GetEventChkInf(EVENTCHKINF_BEGAN_BARINA_BATTLE)) {
|
||||
if (Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_SOULS) &&
|
||||
!Flags_GetRandomizerInf(RAND_INF_BARINADE_SOUL)) {
|
||||
sCsState = BOSSVA_BATTLE;
|
||||
} else {
|
||||
sCsState = INTRO_CALL_BARI;
|
||||
sDoorState = 100;
|
||||
Player_SetCsActionWithHaltedActors(play, &this->actor, 1);
|
||||
play->envCtx.screenFillColor[0] = 0xDC;
|
||||
play->envCtx.screenFillColor[1] = 0xDC;
|
||||
play->envCtx.screenFillColor[2] = 0xBE;
|
||||
play->envCtx.screenFillColor[3] = 0xD2;
|
||||
func_80064520(play, &play->csCtx);
|
||||
sSubCamId = Play_CreateSubCamera(play);
|
||||
Play_ChangeCameraStatus(play, CAM_ID_MAIN, CAM_STAT_WAIT);
|
||||
Play_ChangeCameraStatus(play, sSubCamId, CAM_STAT_ACTIVE);
|
||||
sSubCamEyeNext.x = sSubCamEye.x = 140.0f;
|
||||
sSubCamEyeNext.y = sSubCamEye.y = 205.0f;
|
||||
sSubCamEyeNext.z = sSubCamEye.z = -20.0f;
|
||||
sSubCamAtNext.x = sSubCamAt.x = 10.0f;
|
||||
sSubCamAtNext.y = sSubCamAt.y = 50.0f;
|
||||
sSubCamAtNext.z = sSubCamAt.z = -220.0f;
|
||||
Play_CameraSetAtEye(play, sSubCamId, &sSubCamAt, &sSubCamEye);
|
||||
this->timer = 20;
|
||||
if (GameInteractor_Should(VB_BARINADE_PLAY_REMATCH_INTRO,
|
||||
Flags_GetEventChkInf(EVENTCHKINF_BEGAN_BARINA_BATTLE), this)) {
|
||||
sCsState = INTRO_CALL_BARI;
|
||||
sDoorState = 100;
|
||||
Player_SetCsActionWithHaltedActors(play, &this->actor, 1);
|
||||
play->envCtx.screenFillColor[0] = 0xDC;
|
||||
play->envCtx.screenFillColor[1] = 0xDC;
|
||||
play->envCtx.screenFillColor[2] = 0xBE;
|
||||
play->envCtx.screenFillColor[3] = 0xD2;
|
||||
func_80064520(play, &play->csCtx);
|
||||
sSubCamId = Play_CreateSubCamera(play);
|
||||
Play_ChangeCameraStatus(play, CAM_ID_MAIN, CAM_STAT_WAIT);
|
||||
Play_ChangeCameraStatus(play, sSubCamId, CAM_STAT_ACTIVE);
|
||||
sSubCamEyeNext.x = sSubCamEye.x = 140.0f;
|
||||
sSubCamEyeNext.y = sSubCamEye.y = 205.0f;
|
||||
sSubCamEyeNext.z = sSubCamEye.z = -20.0f;
|
||||
sSubCamAtNext.x = sSubCamAt.x = 10.0f;
|
||||
sSubCamAtNext.y = sSubCamAt.y = 50.0f;
|
||||
sSubCamAtNext.z = sSubCamAt.z = -220.0f;
|
||||
Play_CameraSetAtEye(play, sSubCamId, &sSubCamAt, &sSubCamEye);
|
||||
this->timer = 20;
|
||||
|
||||
for (i = BOSSVA_BARI_LOWER_5; i >= BOSSVA_BARI_UPPER_1; i--) {
|
||||
Actor_SpawnAsChild(
|
||||
&play->actorCtx, &this->actor, play, ACTOR_BOSS_VA,
|
||||
sInitPosOffsets[i].x + this->actor.world.pos.x,
|
||||
sInitPosOffsets[i].y + this->actor.world.pos.y,
|
||||
sInitPosOffsets[i].z + this->actor.world.pos.z, sInitRot[i].x + this->actor.world.rot.x,
|
||||
sInitRot[i].y + this->actor.world.rot.y, sInitRot[i].z + this->actor.world.rot.z, i);
|
||||
}
|
||||
|
||||
sSubCamAtMaxVelFrac = sSubCamEyeMaxVelFrac = sZeroVec;
|
||||
for (i = BOSSVA_BARI_LOWER_5; i >= BOSSVA_BARI_UPPER_1; i--) {
|
||||
Actor_SpawnAsChild(
|
||||
&play->actorCtx, &this->actor, play, ACTOR_BOSS_VA,
|
||||
sInitPosOffsets[i].x + this->actor.world.pos.x,
|
||||
sInitPosOffsets[i].y + this->actor.world.pos.y,
|
||||
sInitPosOffsets[i].z + this->actor.world.pos.z, sInitRot[i].x + this->actor.world.rot.x,
|
||||
sInitRot[i].y + this->actor.world.rot.y, sInitRot[i].z + this->actor.world.rot.z, i);
|
||||
}
|
||||
|
||||
sSubCamAtMaxVelFrac = sSubCamEyeMaxVelFrac = sZeroVec;
|
||||
|
||||
} else {
|
||||
sCsState = INTRO_START;
|
||||
sDoorState = 5;
|
||||
|
||||
Reference in New Issue
Block a user