From fd96bf6b14c8d37eac68d0941db692fa378f6d68 Mon Sep 17 00:00:00 2001 From: inspectredc <78732756+inspectredc@users.noreply.github.com> Date: Thu, 3 Apr 2025 05:06:16 +0100 Subject: [PATCH 001/157] Fix Disable Bomb Billboarding (#5290) --- soh/soh/SohGui/SohMenuEnhancements.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 10975589f7c..dad94c8b480 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -434,7 +434,7 @@ void SohMenu::AddMenuEnhancements() { "Toggle between standard assets and alternate assets. Usually mods will indicate if " "this setting has to be used or not.")); AddWidget(path, "Disable Bomb Billboarding", WIDGET_CVAR_CHECKBOX) - .CVar("DisableBombBillboarding") + .CVar(CVAR_ENHANCEMENT("DisableBombBillboarding")) .Options(CheckboxOptions().Tooltip( "Disables bombs always rotating to face the camera. To be used in conjunction with mods that want to " "replace bombs with 3D objects.")); From 4f783d5945b3c948fa44dbf60f06ec2d2efcc661 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Thu, 3 Apr 2025 21:47:56 +0100 Subject: [PATCH 002/157] fix oversight causing big poes check not to run (#5303) --- soh/soh/Enhancements/randomizer/3drando/fill.cpp | 6 +++--- soh/soh/Enhancements/randomizer/location_access.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index e5e22ad1d63..dcae4842c12 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -161,7 +161,7 @@ static void ValidateOtherEntrance(GetAccessibleLocationsStruct& gals) { } // If we are not shuffling the guard house, add the key so we can properly check for poe merchant access if (gals.validatedStartingRegion && gals.foundTempleOfTime && - !ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_ALL)) { + ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF)) { Rando::StaticData::RetrieveItem(RG_GUARD_HOUSE_KEY).ApplyEffect(); } } @@ -213,7 +213,7 @@ void ProcessExits(Region* region, GetAccessibleLocationsStruct& gals, Randomizer // Update Time of Day Access for the exit if (UpdateToDAccess(&exit, exitRegion)) { gals.logicUpdated = true; - if (!gals.sphereZeroComplete) { + if (!gals.sphereZeroComplete || logic->AreCheckingBigPoes) { if (!gals.foundTempleOfTime || !gals.validatedStartingRegion) { ValidateOtherEntrance(gals); } @@ -622,7 +622,7 @@ void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAcce RegionTable(RR_ROOT)->adultDay = true; } else if (checkPoeCollectorAccess) { // If we are not shuffling the guard house, add the key so we can properly check for poe merchant access - if (!ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_ALL)) { + if (ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF)) { Rando::StaticData::RetrieveItem(RG_GUARD_HOUSE_KEY).ApplyEffect(); } } else { diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index c06ca5d70b2..6cf2aa2ab3b 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -326,7 +326,7 @@ void RegionTable_Init() { EventAccess(&logic->KakarikoVillageGateOpen, []{return ctx->GetOption(RSK_KAK_GATE).Is(RO_KAK_GATE_OPEN);}), //The big poes bottle softlock safety check does not account for the guard house lock if the guard house is not shuffled, so the key is needed before we can safely allow bottle use in logic //RANDOTODO a setting that lets you drink/dump big poes so we don't need this logic - EventAccess(&logic->CouldEmptyBigPoes, []{return ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_ALL) || logic->CanOpenOverworldDoor(RG_GUARD_HOUSE_KEY);}), + EventAccess(&logic->CouldEmptyBigPoes, []{return !ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF) || logic->CanOpenOverworldDoor(RG_GUARD_HOUSE_KEY);}), }, { //Locations LOCATION(RC_LINKS_POCKET, true), From 9a78b04e6810afb6f05f9d10b6d5e638cbbcdf4c Mon Sep 17 00:00:00 2001 From: PBillodeau <3998670+PBillodeau@users.noreply.github.com> Date: Thu, 3 Apr 2025 19:33:02 -0500 Subject: [PATCH 003/157] ESC now toggles menubar (#5293) * ESC now toggles menubar * Update "menubar" to "menu" in README * Add mouse capture toggle to README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e7f929bad5c..ea1ad76a230 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,8 @@ Congratulations, you are now sailing with the Ship of Harkinian! Have fun! ### Other shortcuts | Keys | Action | | - | - | -| F1 | Toggle menubar | +| ESC | Toggle menu | +| F2 | Toggle capture mouse input | | F5 | Save state | | F6 | Change state | | F7 | Load state | From 8b8403f59566d864e6d05c9877849b1c9fced1cc Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 4 Apr 2025 03:21:08 -0600 Subject: [PATCH 004/157] Implemented Leveled in to Blair Alfa. Changed to new UI for configuration. --- soh/include/functions.h | 8 +- soh/include/global.h | 3 + soh/include/leveled_actor_level_table.h | 6 + soh/include/leveled_overlays.h | 15 + soh/include/leveled_stat_math.h | 34 + soh/include/z64actor.h | 19 +- soh/include/z64player.h | 2 +- soh/include/z64save.h | 7 +- soh/soh/Enhancements/debugger/actorViewer.cpp | 6 +- .../Enhancements/debugger/debugSaveEditor.cpp | 17 +- .../game-interactor/GameInteractionEffect.cpp | 2 +- .../GameInteractor_RawAction.cpp | 8 +- soh/soh/Enhancements/gameplaystats.cpp | 2 + soh/soh/Enhancements/mods.cpp | 10 +- .../Enhancements/randomizer/randomizer.cpp | 6 +- .../Enhancements/timesaver_hook_handlers.cpp | 4 +- soh/soh/OTRGlobals.cpp | 44 + soh/soh/OTRGlobals.h | 1 + soh/soh/SaveManager.cpp | 22 +- soh/soh/SaveManager.h | 1 + soh/soh/SohGui/SohMenu.cpp | 1 + soh/soh/SohGui/SohMenu.h | 1 + soh/soh/SohGui/SohMenuLeveled.cpp | 159 +++ soh/src/code/leveled_actor_level_table.c | 970 ++++++++++++++++++ soh/src/code/leveled_map_levels.c | 181 ++++ soh/src/code/leveled_overlays.c | 776 ++++++++++++++ soh/src/code/leveled_stat_math.c | 267 +++++ soh/src/code/z_actor.c | 57 +- soh/src/code/z_collision_check.c | 13 + soh/src/code/z_en_item00.c | 2 +- soh/src/code/z_lifemeter.c | 25 +- soh/src/code/z_message_PAL.c | 17 +- soh/src/code/z_parameter.c | 101 +- soh/src/code/z_player_lib.c | 52 + soh/src/code/z_sram.c | 5 +- .../ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c | 10 +- .../actors/ovl_Boss_Dodongo/z_boss_dodongo.c | 11 +- .../overlays/actors/ovl_Boss_Fd/z_boss_fd.c | 15 +- .../overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c | 54 +- .../actors/ovl_Boss_Ganon/z_boss_ganon.c | 29 +- .../actors/ovl_Boss_Ganon2/z_boss_ganon2.c | 33 +- .../ovl_Boss_Ganondrof/z_boss_ganondrof.c | 37 +- .../actors/ovl_Boss_Goma/z_boss_goma.c | 11 +- .../overlays/actors/ovl_Boss_Mo/z_boss_mo.c | 13 +- .../overlays/actors/ovl_Boss_Sst/z_boss_sst.c | 5 +- .../overlays/actors/ovl_Boss_Tw/z_boss_tw.c | 19 +- .../overlays/actors/ovl_Boss_Va/z_boss_va.c | 18 +- soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c | 10 +- soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c | 10 +- .../actors/ovl_En_Bdfire/z_en_bdfire.c | 3 +- .../actors/ovl_En_Bigokuta/z_en_bigokuta.c | 1 + .../overlays/actors/ovl_En_Bili/z_en_bili.c | 14 +- .../ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c | 2 +- .../actors/ovl_En_Bubble/z_en_bubble.c | 2 +- soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c | 2 + .../actors/ovl_En_Clear_Tag/z_en_clear_tag.c | 13 +- .../overlays/actors/ovl_En_Crow/z_en_crow.c | 17 +- .../actors/ovl_En_Dekubaba/z_en_dekubaba.c | 1 + .../actors/ovl_En_Dekunuts/z_en_dekunuts.c | 6 +- soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c | 7 +- .../actors/ovl_En_Dodongo/z_en_dodongo.c | 1 + soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c | 2 +- .../actors/ovl_En_Firefly/z_en_firefly.c | 45 +- .../actors/ovl_En_Floormas/z_en_floormas.c | 8 +- soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c | 9 +- soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c | 11 +- .../overlays/actors/ovl_En_GirlA/z_en_girla.c | 2 +- .../overlays/actors/ovl_En_Goma/z_en_goma.c | 17 +- soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c | 15 +- soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c | 4 + .../actors/ovl_En_Peehat/z_en_peehat.c | 1 + soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c | 8 +- soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c | 3 + soh/src/overlays/actors/ovl_En_Si/z_en_si.c | 2 + soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c | 1 + soh/src/overlays/actors/ovl_En_St/z_en_st.c | 3 +- .../overlays/actors/ovl_En_Test/z_en_test.c | 4 +- .../overlays/actors/ovl_En_Tite/z_en_tite.c | 2 + soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c | 1 + soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c | 6 +- soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c | 1 + soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c | 4 +- soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c | 3 +- .../actors/ovl_player_actor/z_player.c | 28 +- .../ovl_file_choose/z_file_choose.c | 58 +- .../misc/ovl_kaleido_scope/z_kaleido_debug.c | 2 + .../ovl_kaleido_scope/z_kaleido_equipment.c | 6 + .../ovl_kaleido_scope/z_kaleido_scope_PAL.c | 2 +- 88 files changed, 3211 insertions(+), 225 deletions(-) create mode 100644 soh/include/leveled_actor_level_table.h create mode 100644 soh/include/leveled_overlays.h create mode 100644 soh/include/leveled_stat_math.h create mode 100644 soh/soh/SohGui/SohMenuLeveled.cpp create mode 100644 soh/src/code/leveled_actor_level_table.c create mode 100644 soh/src/code/leveled_map_levels.c create mode 100644 soh/src/code/leveled_overlays.c create mode 100644 soh/src/code/leveled_stat_math.c diff --git a/soh/include/functions.h b/soh/include/functions.h index 86e3bf77f6e..91cc5eaace8 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -27,6 +27,12 @@ void gSPDisplayListOffset(Gfx* pkt, Gfx* dl, int offset); void gSPVertex(Gfx* pkt, uintptr_t v, int n, int v0); void gSPInvalidateTexCache(Gfx* pkt, uintptr_t texAddr); +void Actor_RefreshLeveledStats(Actor* actor, Player* player); +Gfx* Gfx_TextureIA8(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy); +Gfx* Gfx_TextureI8(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy); +void Player_GainExperience(PlayState* play, u16 experience); void cleararena(void); void bootproc(void); @@ -548,7 +554,7 @@ s32 func_800354B4(PlayState* play, Actor* actor, f32 range, s16 arg3, s16 arg4, void func_8003555C(PlayState* play, Vec3f* pos, Vec3f* velocity, Vec3f* accel); void func_800355B8(PlayState* play, Vec3f* pos); u8 func_800355E4(PlayState* play, Collider* collider); -u8 Actor_ApplyDamage(Actor* actor); +u16 Actor_ApplyDamage(Actor* actor); void Actor_SetDropFlag(Actor* actor, ColliderInfo* colBody, s32 freezeFlag); void Actor_SetDropFlagJntSph(Actor* actor, ColliderJntSph* colBody, s32 freezeFlag); void func_80035844(Vec3f* arg0, Vec3f* arg1, Vec3s* arg2, s32 arg3); diff --git a/soh/include/global.h b/soh/include/global.h index b453439fd3f..8a7e92529ad 100644 --- a/soh/include/global.h +++ b/soh/include/global.h @@ -8,6 +8,9 @@ #include "functions.h" #include "variables.h" #include "macros.h" +#include "leveled_overlays.h" +#include "leveled_stat_math.h" +#include "leveled_actor_level_table.h" #include "soh/cvar_prefixes.h" #include "soh/Enhancements/gameconsole.h" #include "soh/Enhancements/gameplaystats.h" diff --git a/soh/include/leveled_actor_level_table.h b/soh/include/leveled_actor_level_table.h new file mode 100644 index 00000000000..7472b50c06c --- /dev/null +++ b/soh/include/leveled_actor_level_table.h @@ -0,0 +1,6 @@ +#ifndef LEVELED_ACTOR_LEVEL_TABLE_H +#define LEVELED_ACTOR_LEVEL_TABLE_H +#include "z64.h" +#endif + +void Actor_GetLevelAndExperience(PlayState* play, Actor* actor, u16 actorIdOverride); \ No newline at end of file diff --git a/soh/include/leveled_overlays.h b/soh/include/leveled_overlays.h new file mode 100644 index 00000000000..26f01abe4fc --- /dev/null +++ b/soh/include/leveled_overlays.h @@ -0,0 +1,15 @@ +#ifndef LEVELED_OVERLAYS_H +#define LEVELED_OVERLAYS_H +#include "z64.h" +#endif + +void ActorDamageNumber_New(Actor* actor, u16 damage); +void ActorExperienceNumber_New(Actor* actor, u16 experience); +void ActorLevelUp_New(Actor* actor, u8 powerDiff, u8 courageDiff, u16 healthDiff, u8 magicDiff); +void ActorDamageNumber_Draw(PlayState* play, Actor* actor); +void ActorExperienceNumber_Draw(PlayState* play, Actor* actor); +void Actor_LevelUpDraw(PlayState* play, Actor* actor); +void Leveled_ValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b); +void Leveled_BigValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b, u8 a); +void Leveled_KaleidoEquip_Stats(PlayState* play); +void Leveled_Interface_DrawNextLevel(PlayState* play); \ No newline at end of file diff --git a/soh/include/leveled_stat_math.h b/soh/include/leveled_stat_math.h new file mode 100644 index 00000000000..40dfa0c0326 --- /dev/null +++ b/soh/include/leveled_stat_math.h @@ -0,0 +1,34 @@ +#ifndef LEVELED_STAT_MATH_H +#define LEVELED_STAT_MATH_H +#include "z64.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +u16 GetActorStat_DisplayAttack(u16 attack, u8 power); +u16 GetActorStat_Attack(u16 attack, u8 power); +u8 GetActorStat_Power(u8 level); +u8 GetActorStat_Courage(u8 level); +u8 GetActorStat_PlayerPower(u8 level); +u8 GetActorStat_PlayerCourage(u8 level); +u16 GetActorStat_EnemyMaxHealth(u16 baseHealth, u8 level); +u8 GetPlayerStat_BonusHearts(u8 level); +u8 GetPlayerStat_MagicUnits(u8 level); +u16 GetPlayerStat_GetModifiedHealthCapacity(u16 baseHealth, u8 level); +u16 GetPlayerStat_NextLevelExpAtLevel(u8 level); +u16 GetActorStat_NextLevelExp(u8 level, u32 currentExp); +u16 GetEnemyExperienceReward(u8 level, u16 expRate); +f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage); +f32 Leveled_DamageModify(Actor * actor, Actor * attackingActor, f32 attack); +u16 Leveled_GoldSkulltulaExperience(u8 tokens); +void Leveled_SetPlayerModifiedStats(Player * player); +u8 Leveled_GetHealthAttackMultiplier(); +s8 Leveled_GetSceneLevel(s16 sceneId); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/soh/include/z64actor.h b/soh/include/z64actor.h index 6d8fd3ca07f..2c7922ee674 100644 --- a/soh/include/z64actor.h +++ b/soh/include/z64actor.h @@ -55,14 +55,14 @@ typedef struct { } DamageTable; typedef struct { - /* 0x00 */ u8 health; + /* 0x00 */ u16 health; /* 0x02 */ s16 cylRadius; /* 0x04 */ s16 cylHeight; /* 0x06 */ u8 mass; } CollisionCheckInfoInit; typedef struct { - /* 0x00 */ u8 health; + /* 0x00 */ u16 health; /* 0x02 */ s16 cylRadius; /* 0x04 */ s16 cylHeight; /* 0x06 */ s16 cylYShift; @@ -76,8 +76,8 @@ typedef struct { /* 0x12 */ s16 cylHeight; // Used for various purposes /* 0x14 */ s16 cylYShift; // Unused. Purpose inferred from Cylinder16 and CollisionCheck_CylSideVsLineSeg /* 0x16 */ u8 mass; // Used to compute displacement for OC collisions - /* 0x17 */ u8 health; // Note: some actors may use their own health variable instead of this one - /* 0x18 */ u8 damage; // Amount to decrement health by + /* 0x17 */ u16 health; // Note: some actors may use their own health variable instead of this one + /* 0x18 */ u16 damage; // Amount to decrement health by /* 0x19 */ u8 damageEffect; // Stores what effect should occur when hit by a weapon /* 0x1A */ u8 atHitEffect; // Stores what effect should occur when AT connects with an AC /* 0x1B */ u8 acHitEffect; // Stores what effect should occur when AC is touched by an AT @@ -269,6 +269,17 @@ typedef struct Actor { // #region SOH [General] /* */ u8 maximumHealth; // Max health value for use with health bars, set on actor init // #endregion + u16 exp; // Experience + u8 level; // Actor Level + u8 power; // e.g. Strength + u8 courage; // e.g. Defense + s8 powerModifier; // Modifies Power + s8 courageModifier; // Modifies Courage + u16 floatingNumber[7]; + u8 floatingNumberLife[7]; + Vec2f floatingNumberPosition[7]; + Vec2f floatingNumberVelocity[7]; + bool ignoreExpReward; // Actor handles exp reward differently } Actor; // size = 0x14C typedef enum { diff --git a/soh/include/z64player.h b/soh/include/z64player.h index a00d86557c7..992fe5a9611 100644 --- a/soh/include/z64player.h +++ b/soh/include/z64player.h @@ -909,7 +909,7 @@ typedef struct Player { /* 0x089A */ s16 floorPitchAlt; // the calculation for this value is bugged and doesn't represent anything meaningful /* 0x089C */ s16 unk_89C; /* 0x089E */ u16 floorSfxOffset; - /* 0x08A0 */ u8 knockbackDamage; + /* 0x08A0 */ u16 knockbackDamage; /* 0x08A1 */ u8 knockbackType; /* 0x08A2 */ s16 knockbackRot; /* 0x08A4 */ f32 knockbackSpeed; diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 751ad0b0f98..a450b0cb23e 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -208,7 +208,7 @@ typedef struct { /* 0x002E */ s16 healthCapacity; // "max_life" /* 0x0030 */ s16 health; // "now_life" /* 0x0032 */ s8 magicLevel; // 0 for no magic/new load, 1 for magic, 2 for double magic - /* 0x0033 */ s8 magic; // current magic available for use + /* 0x0033 */ u8 magic; // current magic available for use /* 0x0034 */ s16 rupees; /* 0x0036 */ u16 swordHealth; /* 0x0038 */ u16 naviTimer; @@ -307,6 +307,11 @@ typedef struct { /* 0x1422 */ s16 sunsSongState; // controls the effects of suns song /* 0x1424 */ s16 healthAccumulator; /* */ ShipSaveContextData ship; + u32 experience; + s16 heartContainers; + s16 healthCapacity2; // Modified max health + u8 magicUnits; // Modified magic units per magic level + s16 showNeededExpTimer; // Shows EXP required in the hud when greater than 0 } SaveContext; // size = 0x1428 typedef enum { diff --git a/soh/soh/Enhancements/debugger/actorViewer.cpp b/soh/soh/Enhancements/debugger/actorViewer.cpp index 8ae940ffb99..72bab7617ac 100644 --- a/soh/soh/Enhancements/debugger/actorViewer.cpp +++ b/soh/soh/Enhancements/debugger/actorViewer.cpp @@ -922,6 +922,10 @@ void ActorViewerWindow::DrawElement() { ImGui::Text("Name: %s", ActorDB::Instance->RetrieveEntry(display->id).name.c_str()); ImGui::Text("Description: %s", GetActorDescription(display->id).c_str()); ImGui::Text("Category: %s", acMapping[display->category]); + if (display->category == ACTORCAT_BOSS || display->category == ACTORCAT_ENEMY) { + ImGui::Text("Level: %d", display->level); + ImGui::Text("EXP: %d", display->exp); + } ImGui::Text("ID: %d", display->id); ImGui::Text("Parameters: %d", display->params); }, @@ -957,7 +961,7 @@ void ActorViewerWindow::DrawElement() { if (display->category == ACTORCAT_BOSS || display->category == ACTORCAT_ENEMY) { PushStyleInput(THEME_COLOR); - ImGui::InputScalar("Enemy Health", ImGuiDataType_U8, &display->colChkInfo.health); + ImGui::InputScalar("Enemy Health", ImGuiDataType_U16, &display->colChkInfo.health); PopStyleInput(); UIWidgets::InsertHelpHoverText("Some actors might not use this!"); } diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index 534201f8e7a..cc495aa1370 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -20,6 +20,7 @@ extern "C" { #include "functions.h" #include "macros.h" #include "soh/cvar_prefixes.h" +#include "leveled_stat_math.h" extern PlayState* gPlayState; #include "textures/icon_item_static/icon_item_static.h" @@ -171,16 +172,22 @@ void DrawInfoTab() { PopStyleInput(); if (ImGui::IsItemDeactivated()) { gSaveContext.healthCapacity = healthIntermediary; + + Player* player = GET_PLAYER(gPlayState); + gSaveContext.healthCapacity2 = + GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, player->actor.level); + if (gSaveContext.health > gSaveContext.healthCapacity2) + gSaveContext.health = gSaveContext.healthCapacity2; } Tooltip("Maximum health. 16 units per full heart"); - if (gSaveContext.health > gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; // Clamp health to new max + if (gSaveContext.health > gSaveContext.healthCapacity2) { + gSaveContext.health = gSaveContext.healthCapacity2; // Clamp health to new max } int32_t health = (int32_t)gSaveContext.health; if (SliderInt("Health", &health, intSliderOptionsBase.Tooltip("Current health. 16 units per full heart") .Min(0) - .Max(gSaveContext.healthCapacity))) { + .Max(gSaveContext.healthCapacity2))) { gSaveContext.health = (int16_t)health; } @@ -196,7 +203,7 @@ void DrawInfoTab() { gSaveContext.isMagicAcquired = gSaveContext.magicLevel > 0; gSaveContext.isDoubleMagicAcquired = gSaveContext.magicLevel == 2; } - gSaveContext.magicCapacity = gSaveContext.magicLevel * 0x30; // Set to get the bar drawn in the UI + gSaveContext.magicCapacity = gSaveContext.magicLevel * gSaveContext.magicUnits; // Set to get the bar drawn in the UI if (gSaveContext.magic > gSaveContext.magicCapacity) { gSaveContext.magic = gSaveContext.magicCapacity; // Clamp magic to new max } @@ -206,7 +213,7 @@ void DrawInfoTab() { intSliderOptionsBase.Min(0) .Max(gSaveContext.magicCapacity) .Tooltip("Current magic. 48 units per magic level"))) { - gSaveContext.magic = (int8_t)magic; + gSaveContext.magic = (uint8_t)magic; } PushStyleInput(THEME_COLOR); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp index 95455926b34..0abb8e803a3 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp @@ -189,7 +189,7 @@ void ModifyGravity::_Remove() { GameInteractionEffectQueryResult ModifyHealth::CanBeApplied() { if (!GameInteractor::IsSaveLoaded()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; - } else if ((parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity) || + } else if ((parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity2) || (parameters[0] < 0 && (gSaveContext.health + (16 * parameters[0]) <= 0))) { return GameInteractionEffectQueryResult::NotPossible; } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp index ac99859b54c..2798e374b14 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -23,7 +23,7 @@ void GameInteractor::RawAction::AddOrRemoveHealthContainers(int16_t amount) { void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { // Full single magic = 48 // Full double magic = 96 - int8_t currentMagicCapacity = (gSaveContext.isDoubleMagicAcquired + 1) * 48; + int8_t currentMagicCapacity = (gSaveContext.isDoubleMagicAcquired + 1) * gSaveContext.magicUnits; if (gSaveContext.isMagicAcquired) { gSaveContext.prevMagicState = gSaveContext.magicState; @@ -46,17 +46,17 @@ void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { void GameInteractor::RawAction::HealOrDamagePlayer(int16_t hearts) { if (hearts > 0) { - Health_ChangeBy(gPlayState, hearts * 0x10); + Health_ChangeBy(gPlayState, hearts * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2); } else if (hearts < 0) { Player* player = GET_PLAYER(gPlayState); - Health_ChangeBy(gPlayState, hearts * 0x10); + Health_ChangeBy(gPlayState, hearts * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2); func_80837C0C(gPlayState, player, 0, 0, 0, 0, 0); player->invincibilityTimer = 28; } } void GameInteractor::RawAction::SetPlayerHealth(int16_t hearts) { - gSaveContext.health = hearts * 0x10; + gSaveContext.health = hearts * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; } void GameInteractor::RawAction::SetLinkInvisibility(bool active) { diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp index ca360543fa4..c7cf007e357 100644 --- a/soh/soh/Enhancements/gameplaystats.cpp +++ b/soh/soh/Enhancements/gameplaystats.cpp @@ -445,6 +445,8 @@ void DrawGameplayStatsHeader() { ImGui::TableSetupColumn("stat", ImGuiTableColumnFlags_WidthStretch); // if tag is empty (not a release build) if (gGitCommitTag[0] == 0) { + GameplayStatsRow("Leveled Mod Version:", "1.1.3"); + GameplayStatsRow("A Mod By Gotest, AKA Arrenton", ""); GameplayStatsRow("Git Branch:", (char*)gGitBranch); GameplayStatsRow("Git Commit Hash:", (char*)gGitCommitHash); } else { diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 2c357fd34f6..07f15d4e785 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -204,8 +204,8 @@ void UpdatePermanentHeartLossState() { 16 * (IS_RANDO ? (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_HEARTS) + 1) : 3); uint8_t newCapacity = startingHealth + (heartContainers * 16) + ((heartPieces - (heartPieces % 4)) * 4); - gSaveContext.healthCapacity = MAX(newCapacity, gSaveContext.healthCapacity); - gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity); + gSaveContext.healthCapacity = MAX(newCapacity, gSaveContext.healthCapacity2); + gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity2); hasAffectedHealth = false; } } @@ -222,7 +222,8 @@ void RegisterPermanentHeartLoss() { if (gSaveContext.healthCapacity > 16 && gSaveContext.healthCapacity - gSaveContext.health >= 16) { gSaveContext.healthCapacity -= 16; - gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity); + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity2); hasAffectedHealth = true; } }); @@ -820,6 +821,7 @@ void UpdateHurtContainerModeState(bool newState) { } else { gSaveContext.healthCapacity = 48 + ((getHeartPieces + getHeartContainers) * 16); } + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); } void RegisterHurtContainerModeHandler() { @@ -872,9 +874,11 @@ void RegisterRandomizedEnemySizes() { // Scale the health based on a smaller factor than randomScale float healthScalingFactor = 0.8f; // Adjust this factor as needed float scaledHealth = actor->colChkInfo.health * (randomScale * healthScalingFactor); + float scaledExp = actor->exp * (randomScale * healthScalingFactor); // Ensure the scaled health doesn't go below zero actor->colChkInfo.health = fmax(scaledHealth, 1.0f); + actor->exp = fmax(scaledExp, 1.0f); } else { return; } diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index d567a3d8e24..01ae166e91a 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -5813,7 +5813,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { switch (item) { case RG_MAGIC_SINGLE: gSaveContext.isMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_NORMAL_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits; Magic_Fill(play); break; case RG_MAGIC_DOUBLE: @@ -5821,7 +5821,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { gSaveContext.isMagicAcquired = true; } gSaveContext.isDoubleMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_DOUBLE_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits * 2; gSaveContext.magicLevel = 0; Magic_Fill(play); break; @@ -5834,7 +5834,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { case RG_DOUBLE_DEFENSE: gSaveContext.isDoubleDefenseAcquired = true; gSaveContext.inventory.defenseHearts = 20; - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; break; case RG_TYCOON_WALLET: Inventory_ChangeUpgrade(UPG_WALLET, 3); diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index b738e2fc16c..b116c2d704f 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -760,7 +760,8 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li EnIk* ik = va_arg(args, EnIk*); if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), IS_RANDO)) { // Because no CS in rando, we hide the death of the knuckle by spawning flames and kill the actor - if ((ik->actor.colChkInfo.health <= 10)) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(10, ik->actor.level); + if ((ik->actor.colChkInfo.health <= healthCheck)) { s32 i; Vec3f pos; Vec3f sp7C = { 0.0f, 0.5f, 0.0f }; @@ -773,6 +774,7 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li EffectSsDeadDb_Spawn(gPlayState, &pos, &sp7C, &sp7C, 100, 0, 255, 255, 255, 255, 0, 0, 255, 1, 9, true); } + Player_GainExperience(gPlayState, ik->actor.exp); Actor_Kill(&ik->actor); } *should = false; diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 0151773a902..dfb24227c3d 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2034,6 +2034,50 @@ extern "C" int GetEquipNowMessage(char* buffer, char* src, const int maxBufferSi return 0; } +extern "C" int GetLeveledNaviEnemyInfo(char* buffer, char* src, const int maxBufferSize, Actor* actor) { + std::string postfix; + + if (!actor) + return 0; + + if (gSaveContext.language == LANGUAGE_FRA) { + postfix = ""; + } else if (gSaveContext.language == LANGUAGE_GER) { + postfix = ""; + } else { + postfix = ""; + if (CVarGetInteger("gLeveled.Navi.TellEnemyLevel", 1)) { + postfix += " \x05" + "F" + "Lv" + + std::to_string(actor->level); + } + if (CVarGetInteger("gLeveled.Navi.TellEnemyMaxHP", 1) && actor->maximumHealth > 0) { + postfix += " \x05" + "A" + "MaxHP " + + std::to_string(actor->maximumHealth); + } + } + std::string str; + std::string FixedBaseStr(src); + int FoundControlChar = FixedBaseStr.find_first_of("\x01"); + + if (FoundControlChar != std::string::npos) { + FixedBaseStr = FixedBaseStr.insert(FoundControlChar, postfix); + } + + str = FixedBaseStr; + + if (!str.empty()) { + memset(buffer, 0, maxBufferSize); + const int copiedCharLen = std::min(maxBufferSize - 1, str.length()); + memcpy(buffer, str.c_str(), copiedCharLen); + return copiedCharLen; + } + return 0; +} + extern "C" void Randomizer_ParseSpoiler(const char* fileLoc) { OTRGlobals::Instance->gRandoContext->ParseSpoiler(fileLoc); } diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index b14d322d281..0fad3ce0a4f 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -163,6 +163,7 @@ void Gfx_UnregisterBlendedTexture(const char* name); void Gfx_TextureCacheDelete(const uint8_t* addr); void SaveManager_ThreadPoolWait(); void CheckTracker_OnMessageClose(); +int GetLeveledNaviEnemyInfo(char* buffer, char* src, const int maxBufferSize, Actor* actor); GetItemID RetrieveGetItemIDFromItemID(ItemID itemID); RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID); diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 36640c72896..5b84db3261c 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -15,6 +15,7 @@ #include "functions.h" #include "macros.h" #include +#include "leveled_stat_math.h" #include #include "soh/SohGui/SohGui.hpp" @@ -706,6 +707,12 @@ void SaveManager::InitMeta(int fileNum) { fileMetaInfo[fileNum].hasWallet = Flags_GetRandomizerInf(RAND_INF_HAS_WALLET) || !IS_RANDO; fileMetaInfo[fileNum].defense = gSaveContext.inventory.defenseHearts; fileMetaInfo[fileNum].health = gSaveContext.health; + fileMetaInfo[fileNum].level = 0; + + while (GetActorStat_NextLevelExp(fileMetaInfo[fileNum].level, gSaveContext.experience) <= 0 && + fileMetaInfo[fileNum].level < 99) { + fileMetaInfo[fileNum].level += 1; + } auto randoContext = Rando::Context::GetInstance(); for (int i = 0; i < ARRAY_COUNT(fileMetaInfo[fileNum].seedHash); i++) { @@ -760,9 +767,13 @@ void SaveManager::InitFileNormal() { gSaveContext.ship.filenameLanguage = (gSaveContext.language == LANGUAGE_JPN) ? NAME_LANGUAGE_NTSC_JPN : NAME_LANGUAGE_NTSC_ENG; } + gSaveContext.healthCapacity2 = 9999; + gSaveContext.magicUnits = 9999; + gSaveContext.experience = 0; + gSaveContext.showNeededExpTimer = 0; gSaveContext.n64ddFlag = 0; gSaveContext.healthCapacity = 0x30; - gSaveContext.health = 0x30; + gSaveContext.health = 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.magicLevel = 0; gSaveContext.magic = 0x30; gSaveContext.rupees = 0; @@ -930,9 +941,11 @@ void SaveManager::InitFileDebug() { gSaveContext.ship.filenameLanguage = (gSaveContext.language == LANGUAGE_JPN) ? NAME_LANGUAGE_NTSC_JPN : NAME_LANGUAGE_NTSC_ENG; } + gSaveContext.experience = 80000; gSaveContext.n64ddFlag = 0; gSaveContext.healthCapacity = 0xE0; - gSaveContext.health = 0xE0; + gSaveContext.healthCapacity2 = 0x270F; + gSaveContext.health = 0x270F; gSaveContext.magicLevel = 0; gSaveContext.magic = 0x30; gSaveContext.rupees = 150; @@ -1482,6 +1495,7 @@ void SaveManager::CreateDefaultGlobal() { } void SaveManager::LoadBaseVersion1() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -1626,6 +1640,7 @@ void SaveManager::LoadBaseVersion1() { } void SaveManager::LoadBaseVersion2() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -1842,6 +1857,7 @@ void SaveManager::LoadBaseVersion2() { } void SaveManager::LoadBaseVersion3() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -2062,6 +2078,7 @@ void SaveManager::LoadBaseVersion3() { } void SaveManager::LoadBaseVersion4() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -2244,6 +2261,7 @@ void SaveManager::LoadBaseVersion4() { } void SaveManager::SaveBase(SaveContext* saveContext, int sectionID, bool fullSave) { + SaveManager::Instance->SaveData("experience", saveContext->experience); SaveManager::Instance->SaveData("entranceIndex", saveContext->entranceIndex); SaveManager::Instance->SaveData("linkAge", saveContext->linkAge); SaveManager::Instance->SaveData("cutsceneIndex", saveContext->cutsceneIndex); diff --git a/soh/soh/SaveManager.h b/soh/soh/SaveManager.h index 3c4a3124323..5d8e51c5aed 100644 --- a/soh/soh/SaveManager.h +++ b/soh/soh/SaveManager.h @@ -32,6 +32,7 @@ typedef struct { s32 filenameLanguage; s32 gregFound; s32 hasWallet; + u8 level; } SaveFileMetaInfo; typedef enum { diff --git a/soh/soh/SohGui/SohMenu.cpp b/soh/soh/SohGui/SohMenu.cpp index 61967087bfb..a7d6a82f5ba 100644 --- a/soh/soh/SohGui/SohMenu.cpp +++ b/soh/soh/SohGui/SohMenu.cpp @@ -85,6 +85,7 @@ void SohMenu::InitElement() { AddMenuSettings(); AddMenuEnhancements(); AddMenuRandomizer(); + AddMenuLeveled(); #ifdef ENABLE_REMOTE_CONTROL AddMenuNetwork(); #endif diff --git a/soh/soh/SohGui/SohMenu.h b/soh/soh/SohGui/SohMenu.h index 4a72938bb31..c1922c40b8d 100644 --- a/soh/soh/SohGui/SohMenu.h +++ b/soh/soh/SohGui/SohMenu.h @@ -198,6 +198,7 @@ class SohMenu : public Ship::Menu { void AddMenuEnhancements(); void AddMenuDevTools(); void AddMenuRandomizer(); + void AddMenuLeveled(); void AddMenuNetwork(); static void UpdateLanguageMap(std::unordered_map& languageMap); diff --git a/soh/soh/SohGui/SohMenuLeveled.cpp b/soh/soh/SohGui/SohMenuLeveled.cpp new file mode 100644 index 00000000000..08f7d3ac822 --- /dev/null +++ b/soh/soh/SohGui/SohMenuLeveled.cpp @@ -0,0 +1,159 @@ +#include "SohMenu.h" +#include "SohGui.hpp" +#include + +namespace SohGui { + + extern std::shared_ptr mSohMenu; + using namespace UIWidgets; + + void SohMenu::AddMenuLeveled() { + // Add Leveled Menu + AddMenuEntry("Leveled", CVAR_SETTING("Menu.LeveledSidebarSection")); + + // UI Settings + WidgetPath path = { "Leveled", "UI Settings", SECTION_COLUMN_1 }; + AddSidebarEntry("Leveled", path.sidebarName, 1); + + // HUD + AddWidget(path, "HUD", WIDGET_SEPARATOR_TEXT); + // EXP Next Level + AddWidget(path, "EXP to NEXT Level", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.ExperienceNextLevel") + .Options(CheckboxOptions() + .Tooltip("Show experience required to level up popup in the HUD when gaining EXP.") + .DefaultValue(true)); + // Level Up + AddWidget(path, "Level Up", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.LevelUp") + .Options(CheckboxOptions().Tooltip("Show Level Up popup when leveling up.").DefaultValue(true)); + // Level Up Sound + AddWidget(path, "Level Up Sound", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.LevelUpSound") + .Options(CheckboxOptions().Tooltip("Plays sound when leveling up.").DefaultValue(true)); + + // Floating Numbers + AddWidget(path, "Floating Numbers", WIDGET_SEPARATOR_TEXT); + // Enemy Damage + AddWidget(path, "Enemy Damage", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.FloatingNumbers.EnemyDamage") + .Options(CheckboxOptions().Tooltip("Show floating damage numbers on enemies.").DefaultValue(true)); + // Player Damage + AddWidget(path, "Player Damage", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.FloatingNumbers.PlayerDamage") + .Options(CheckboxOptions().Tooltip("Show floating damage numbers on link.").DefaultValue(true)); + // EXP Gain + AddWidget(path, "EXP Gain", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.FloatingNumbers.ExpGain") + .Options(CheckboxOptions().Tooltip("Show floating EXP gain numbers.").DefaultValue(true)); + + // Navi Info + AddWidget(path, "Navi Enemy Info", WIDGET_SEPARATOR_TEXT); + // Navi Level + AddWidget(path, "Navi tells enemy level", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Navi.TellEnemyLevel") + .Options(CheckboxOptions().Tooltip("Navi will tell the enemy level.").DefaultValue(true)); + // Navi HP + AddWidget(path, "Navi tells enemy max HP", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Navi.TellEnemyMaxHP") + .Options(CheckboxOptions().Tooltip("Navi will tell the enemy's maximum HP.").DefaultValue(true)); + + // Entity Modifications + path.sidebarName = "Entity Modifications"; + AddSidebarEntry("Leveled", path.sidebarName, 1); + // Enemy / Link HP Mult + AddWidget(path, "Enemy HP and Link's Attack Multiplier: %dx", WIDGET_CVAR_SLIDER_INT) + .CVar("gLeveled.Enhancements.AttackAndHPMultiplier") + .Options(IntSliderOptions().Min(1).Max(32).Format("%d").DefaultValue(9).Tooltip( + "Changes Link's Attack and enemy HP multiplier.\nThis doesn't change balance, but rather the size of " + "numbers.")); + AddWidget(path, "Player Modifications", WIDGET_SEPARATOR_TEXT); + // Level Increases Life + AddWidget(path, "Level Gives Bonus Hearts", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Player.Enhancements.HeartsWithLevelUp") + .Options( + CheckboxOptions().Tooltip("Levels give health upgrades, up to 10 extra hearts.").DefaultValue(true)); + // Level Modifies Magic + AddWidget(path, "Level Affects Magic Capacity", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Player.Enhancements.MagicWithLevelUp") + .Options(CheckboxOptions() + .Tooltip("Levels alter magic capacity. Starts at 25% of the vanilla value, but will go up to " + "150% of vanilla. Double magic works the same as the original, doubling capacity.") + .DefaultValue(true)); + // Equipment Stats + AddWidget(path, "Equipment Affects Stats", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Player.Enhancements.EquipmentStats") + .Options(CheckboxOptions() + .Tooltip("Equipment will alter stats. Such as Goron Tunic providing +3 STR, but gives -3 " + "Defense, Bracelets give +1 STR, Shields, etc.") + .DefaultValue(true)); + AddWidget(path, "Enemy Modifications", WIDGET_SEPARATOR_TEXT); + // Enemy Level Affects Attack + AddWidget(path, "Enemy Level Affects Base Attack", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Enemy.Enhancements.AttackScalesWithLevel") + .Options( + CheckboxOptions() + .Tooltip("Enemies have a fixed attack value. This option scales this up the higher the enemy's " + "Strength stat. \nThis will increase difficulty a bit.") + .DefaultValue(true)); + + // Difficulty Options + path.sidebarName = "Difficulty Options"; + AddSidebarEntry("Leveled", path.sidebarName, 1); + + // Player Options + AddWidget(path, "Player Options", WIDGET_SEPARATOR_TEXT); + // Heart Value + AddWidget(path, ("Heart Container Value in Units: "), WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { + ImGui::Text("Heart Container Value in Units: %d (%.2fx)", CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2, (float)CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) / 4.0f); + UIWidgets::CVarSliderInt("Heart Container Value in Units", "gLeveled.Difficulty.HeartUnits", + IntSliderOptions().LabelPosition(Within).Min(1).Max(32).Format("").DefaultValue(4).Color(THEME_COLOR) + .Tooltip( + "Sets how many health units each completed heart container is worth.\n" + "One heart on the health meter is equal to 16 health units.\n" + "A lower setting will result in lower total health.\n" + "Change areas to update health capacity.")); + }); + // Damage Multiplier + AddWidget(path, ("Damage Multiplier: "), WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { + ImGui::Text("Damage Multiplier: %.2fx", (float)CVarGetInteger("gLeveled.Difficulty.Player.DamageMultiplier", 4) / 4.0f); + UIWidgets::CVarSliderInt("Leveled Damage Multiplier", "gLeveled.Difficulty.Player.DamageMultiplier", + IntSliderOptions().LabelPosition(Within).Min(1).Max(32).Format("").DefaultValue(4).Color(THEME_COLOR) + .Tooltip("Sets a multiplier for the damage the player takes. Includes ALL sources, even damage while being frozen or burned.\nDamage cannot be reduced below 1.")); + }); + + // EXP Options + AddWidget(path, "EXP Options", WIDGET_SEPARATOR_TEXT); + // EXP Multiplier + AddWidget(path, "EXP Rate: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gLeveled.Difficulty.EXP.Rate") + .Options(FloatSliderOptions().Min(0.0f).Max(10.0f).Format("%.2fx").DefaultValue(1.0f).Tooltip( + "Sets the EXP multiplier link gains, with the exception of gold skulltula tokens.")); + // Token EXP + AddWidget(path, "Skulltula Token EXP Rate: %.2f x", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gLeveled.Difficulty.EXP.TokenRate") + .Options(FloatSliderOptions().Min(0.0f).Max(10.0f).Format("%.2fx").DefaultValue(1.0f).Tooltip( + "Sets the EXP multiplier link gains from gold skulltula tokens.")); + + // Enemy Options + AddWidget(path, "Enemy Options", WIDGET_SEPARATOR_TEXT); + // Max GS Tokens make Ganon's Castle Enemies Lv99 + AddWidget(path, "Collecting all 100 tokens makes all enemies in Ganon's Castle level 99", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Difficulty.Enemy.MaxLevelInGanonCastle") + .Options(CheckboxOptions() + .Tooltip("If all tokens are collected, all enemies in Ganon's Casle are level 99.") + .DefaultValue(true)); + // Enemy HP + AddWidget(path, "Enemy HP: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gLeveled.Difficulty.Enemy.HPPercent") + .Options( + FloatSliderOptions().Min(0.0f).Max(10.0f).Format("%.2fx").DefaultValue(1.0f).Tooltip( + "Sets the HP multiplier for enemies.")); + // Level Scale + AddWidget(path, "Level Scale: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gLeveled.Difficulty.Enemy.LevelScale") + .Options(FloatSliderOptions().Min(0.0f).Max(10.0f).Format("%.2fx").DefaultValue(1.0f).Tooltip( + "Sets the multiplier for enemy levels.\nFor example, if set to 150%, a level 20 enemy will be level 30.\nCaps at level 99.")); + } + +} // namespace SohGui diff --git a/soh/src/code/leveled_actor_level_table.c b/soh/src/code/leveled_actor_level_table.c new file mode 100644 index 00000000000..e059bab5371 --- /dev/null +++ b/soh/src/code/leveled_actor_level_table.c @@ -0,0 +1,970 @@ +#include "global.h" + +typedef struct { + s8 levelModifier; + u16 experienceRate; + u8 ignoreEntry; +} SceneLevelEntry; + +///////////////////////////////////////////////// +////////////////// BOSSES ///////////////////// +///////////////////////////////////////////////// +void Leveled_Gohma(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 7; + actor->exp = 120; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 3000; + + if (actor->category == ACTORCAT_ENEMY) { // Larva + actor->level = 5; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 0; + if (actor->params < 10) { + actor->exp = 5; + levelEntry->experienceRate = 100; + } + } +} + +void Leveled_Dodongo(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->category == ACTORCAT_BOSS) { // King Dodongo + actor->level = 15; + actor->exp = 500; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 4000; + } else { + actor->level = 14; + actor->exp = 18; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 160; + } +} + +void Leveled_Barinade(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 21; + levelEntry->levelModifier = 5; + if (actor->params == -1) { // Body + actor->exp = 850; + levelEntry->experienceRate = 5000; + } +} + +void Leveled_PhantomGanon(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 28; + levelEntry->levelModifier = 5; + if (actor->params == 1) + actor->exp = 1400; + levelEntry->experienceRate = 6000; +} + +void Leveled_Volvagia(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 35; + levelEntry->levelModifier = 5; + if (actor->id == ACTOR_BOSS_FD2) + actor->exp = 2475; + levelEntry->experienceRate = 6300; +} + +void Leveled_Morpha(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 40; + actor->exp = 3500; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 6600; +} + +void Leveled_BongoBongo(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 45; + actor->exp = 4666; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 7000; +} + +void Leveled_Twinrova(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 52; + levelEntry->levelModifier = 6; + if (actor->params == 2) + actor->exp = 6000; + levelEntry->experienceRate = 7000; +} + +void Leveled_Ganondorf(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 56; + actor->exp = 9999; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 40000; +} + +void Leveled_Ganon(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 60; + levelEntry->levelModifier = 8; +} + +///////////////////////////////////////////////// +/////////////// MINI-BOSSES /////////////////// +///////////////////////////////////////////////// +void Leveled_BigOctorock(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 20; + actor->exp = 320; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 2100; +} + +void Leveled_FlareDancer(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 34; + levelEntry->levelModifier = 4; + if (actor->id == ACTOR_EN_FW) { + actor->exp = 1000; + levelEntry->experienceRate = 3000; + } +} + +void Leveled_DarkLink(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = GET_PLAYER(play)->actor.level; + actor->exp = (u16)((f32)GetPlayerStat_NextLevelExpAtLevel(GET_PLAYER(play)->actor.level) * 0.50f); + levelEntry->ignoreEntry = true; +} + +void Leveled_DeadHand(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 44; + levelEntry->levelModifier = 4; + + if (actor->id == ACTOR_EN_DH) { + actor->exp = 2465; + levelEntry->experienceRate = 3666; + } +} + +void Leveled_IronKnuckle(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 50; + actor->exp = 2733; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 5000; + + if (actor->params == 0) { + actor->level = 52; + actor->exp = 3100; + levelEntry->levelModifier = 6; + } + if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 55; + actor->exp = 2850; + } +} +///////////////////////////////////////////////// +////////////// NORMAL ENEMIES ///////////////// +///////////////////////////////////////////////// +void Leveled_Dekubaba(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == 1) { // Big baba + actor->level = 24; + actor->exp = 32; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 116; + if (LINK_IS_CHILD) { + actor->level = 5; + actor->exp = 8; + } + } else { + actor->level = 2; + actor->exp = 3; + levelEntry->levelModifier = -2; + levelEntry->experienceRate = 60; + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 23; + actor->exp = 19; + } + } +} + +void Leveled_StickDekuBaba(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 1; + actor->exp = 1; + levelEntry->levelModifier = -99; + levelEntry->experienceRate = 1; +} + +void Leveled_Skulltula(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 3; + actor->exp = 3; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 80; + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 21; + actor->exp = 19; + } + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 38; + actor->exp = 30; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 44; + actor->exp = 34; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 50; + } + if (actor->params == 1) { // Big + actor->level += 3; + actor->exp += 2; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 90; + } +} + +void Leveled_Wall_Skulltula(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (((play, actor->params & 0xE000) >> 0xD) != 0) { // Gold Skulltula + actor->level = gSaveContext.inventory.gsTokens + 1; + actor->exp = 0; + levelEntry->ignoreEntry = true; + } else { + actor->level = 3; + actor->exp = 2; + levelEntry->levelModifier = -2; + levelEntry->experienceRate = 40; + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 24; + actor->exp = 18; + } + } +} + +void Leveled_DekuScrub(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 8; + actor->exp = 7; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 75; +} + +void Leveled_Tektite(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == -2) { // Blue + actor->level = 12; + actor->exp = 12; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 100; + if (LINK_IS_ADULT) { + actor->level = 25; + actor->exp = 21; + } + if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 36; + actor->exp = 44; + } + } else { + actor->level = 9; + actor->exp = 9; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 90; + if (LINK_IS_ADULT) { + actor->level = 27; + actor->exp = 24; + } + } +} + +void Leveled_Guay(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 8; + actor->exp = 4; + levelEntry->levelModifier = -1; + levelEntry->experienceRate = 50; + if (LINK_IS_ADULT) { + actor->level = 21; + actor->exp = 13; + } +} + +void Leveled_Octorock(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 13; + actor->exp = 12; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 70; + if (actor->params != 0) { + actor->level = 0; + actor->exp = 0; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 0; + } +} + +void Leveled_Armos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 12; + actor->exp = 17; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 135; + if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 45; + actor->exp = 60; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { // Ganon's Castle + actor->level = 50; + actor->exp = 70; + } +} + +void Leveled_BabyDodongo(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 11; + actor->exp = 7; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 75; +} + +void Leveled_Keese(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == 4) { // Ice Keese + actor->level = 34; + actor->exp = 33; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 60; + } else { + actor->level = 9; + actor->exp = 5; + levelEntry->levelModifier = -1; + levelEntry->experienceRate = 50; + + if (play->sceneNum == SCENE_FIRE_TEMPLE) { + actor->level = 28; + actor->exp = 22; + } else if (play->sceneNum == SCENE_ICE_CAVERN) { + actor->level = 31; + actor->exp = 26; + } else if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 34; + actor->exp = 29; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 42; + actor->exp = 36; + } else if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 38; + actor->exp = 33; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 38; + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 80; + } + } +} + +void Leveled_Peahat(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 18; + actor->exp = 47; + levelEntry->levelModifier = 10; + levelEntry->experienceRate = 275; + if (actor->params == 1) { // Larva + actor->level = 13; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 0; + } +} +void Leveled_Poh(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 10; + actor->exp = 14; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 130; + if (actor->params == 2 || actor->params == 3) { // Composer + actor->level = 15; + actor->exp = 25; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 130; + } +} + +void Leveled_Poh_Field(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 24; + actor->exp = 25; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 130; +} + +void Leveled_ReDead(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params >= -1) { + actor->level = 25; + actor->exp = 45; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 280; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 42; + actor->exp = 171; + } else if (play->sceneNum == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || play->sceneNum == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE) { + actor->level = 50; + actor->exp = 230; + } + } else { // Gibdo + actor->level = 43; + actor->exp = 183; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 305; + } +} + +void Leveled_Shabom(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 15; + levelEntry->levelModifier = -1; +} + +void Leveled_Bari(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 18; + actor->exp = 25; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 85; +} + +void Leveled_Biri(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 16; + actor->exp = 17; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 100; +} + +void Leveled_Stinger(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 17; + actor->exp = 19; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 125; + if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 37; + actor->exp = 85; + } +} + +void Leveled_JabuTentacle(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 19; + levelEntry->levelModifier = 3; + if (actor->params < 3) { + actor->exp = 61; + levelEntry->experienceRate = 400; + } +} + +void Leveled_Tailpasaran(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 19; + actor->exp = 22; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 125; +} + +void Leveled_Stalchild(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 6 + (u16)(play, actor->params / 2); + actor->exp = 3 + (u16)(play, actor->params / 2.5); + levelEntry->levelModifier = 1 + (u16)(play, actor->params / 2); + levelEntry->experienceRate = 45 + (u16)(play, actor->params * 5); +} + +void Leveled_Beamos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == 0) { // Big + actor->level = 48; + actor->exp = 90; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 150; + } else { + actor->level = 11; + actor->exp = 17; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 150; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 43; + actor->exp = 80; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 87; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 90; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 300; + } + } +} + +void Leveled_Wolfos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == 0) { + actor->level = 10; + actor->exp = 24; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 180; + if (LINK_IS_ADULT) { + actor->level = 20; + actor->exp = 29; + } + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 26; + actor->exp = 43; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 158; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 165; + } + } else { // White + actor->level = 37; + actor->exp = 126; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 180; + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 420; + } +} + +void Leveled_Lizalfos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == -2) { // Dinolfos + actor->level = 52; + actor->exp = 250; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 320; + } else if (actor->params == -1) { // Lizalfos + actor->level = 47; + actor->exp = 210; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 290; + } else { // Miniboss Lizalfos + actor->level = 13; + actor->exp = 140; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 1150; + actor->ignoreExpReward = true; + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 570; + } +} + +void Leveled_Moblins(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == -1) { // Spear + actor->level = 24; + actor->exp = 32; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 140; + } else if (actor->params == 0) { // Club + actor->level = 25; + actor->exp = 47; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 240; + } else { // Spear patrol + actor->level = 24; + actor->exp = 24; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 140; + } +} + +void Leveled_Bubble(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == -1) { // Blue + actor->level = 25; + actor->exp = 29; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 105; + } else if (actor->params == -2) { // Red + actor->level = 32; + actor->exp = 47; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 110; + if ( play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 40; + actor->exp = 75; + } + } else if (actor->params == -3) { // White + actor->level = 45; + actor->exp = 76; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 107; + } else if (actor->params == -4) { // Big Green + actor->level = 41; + actor->exp = 82; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 100; + } else { // Green + actor->level = 26; + actor->exp = 31; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 93; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 41; + actor->exp = 70; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 45; + actor->exp = 77; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 85; + } + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 220; + } +} + +void Leveled_Stalfos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 27; + actor->exp = 64; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 345; + + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->ignoreExpReward = true; + actor->exp = 250; + levelEntry->experienceRate = 800; + }else if (play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 43; + actor->exp = 214; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 48; + actor->exp = 236; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER || + play->sceneNum == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || + play->sceneNum == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE) { + actor->level = 50; + actor->exp = 260; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 700; + } +} + +void Leveled_Floormaster(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 26; + actor->exp = 41; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 170; + actor->ignoreExpReward = true; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 42; + actor->exp = 140; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 160; + } +} + +void Leveled_Wallmaster(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 27; + actor->exp = 44; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 175; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 41; + actor->exp = 129; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 138; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 150; + } +} + +void Leveled_Poh_Sisters(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 28; + actor->exp = 127; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 450; +} + +void Leveled_LikeLike(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 32; + actor->exp = 99; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 245; + if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 38; + actor->exp = 142; + } else if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 43; + actor->exp = 161; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 48; + actor->exp = 182; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 190; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 375; + } +} + +void Leveled_TorchSlug(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 33; + actor->exp = 67; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 135; + if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 48; + actor->exp = 163; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 170; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 350; + } +} +void Leveled_Freezer(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 35; + actor->exp = 101; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 205; + if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 160; + } +} + +void Leveled_ShellBlade(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 38; + actor->exp = 129; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 200; + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 390; + } +} + +void Leveled_RollingSpike(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 37; + actor->exp = 100; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 180; + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 325; + } +} + +void Leveled_GerudoFighter(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 45; + actor->exp = 455; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 1250; +} + +void Leveled_Leever(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 45; + actor->exp = 15; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 20; +} + +void Leveled_Anubis(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 48; + levelEntry->levelModifier = 3; + if (actor->id == ACTOR_EN_ANUBICE) { + actor->exp = 120; + levelEntry->experienceRate = 155; + } +} + +void Leveled_Arwing(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 20; + actor->exp = 50; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 215; +} + + +void Actor_GetLevelAndExperience(PlayState* play, Actor* actor, u16 actorIdOverride) { + SceneLevelEntry levelEntry; + levelEntry.levelModifier = 0; + levelEntry.experienceRate = 0; + levelEntry.ignoreEntry = 0; + + u16 actorId = actor->id; + if (actorIdOverride != 0) { + actorId = actorIdOverride; + } + + switch (actorId) { + case ACTOR_BOSS_DODONGO: + case ACTOR_EN_DODONGO: + Leveled_Dodongo(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GOMA: + case ACTOR_EN_GOMA: + Leveled_Gohma(play, actor, &levelEntry); + break; + case ACTOR_BOSS_VA: + Leveled_Barinade(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GANONDROF: + Leveled_PhantomGanon(play, actor, &levelEntry); + break; + case ACTOR_BOSS_FD: + case ACTOR_BOSS_FD2: + Leveled_Volvagia(play, actor, &levelEntry); + break; + case ACTOR_BOSS_MO: + Leveled_Morpha(play, actor, &levelEntry); + break; + case ACTOR_BOSS_SST: + Leveled_BongoBongo(play, actor, &levelEntry); + break; + case ACTOR_BOSS_TW: + Leveled_Twinrova(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GANON: + Leveled_Ganondorf(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GANON2: + Leveled_Ganon(play, actor, &levelEntry); + break; + case ACTOR_EN_DEKUBABA: + Leveled_Dekubaba(play, actor, &levelEntry); + break; + case ACTOR_EN_KAREBABA: + Leveled_StickDekuBaba(play, actor, &levelEntry); + break; + case ACTOR_EN_ST: + Leveled_Skulltula(play, actor, &levelEntry); + break; + case ACTOR_EN_DEKUNUTS: + Leveled_DekuScrub(play, actor, &levelEntry); + break; + case ACTOR_EN_TITE: + Leveled_Tektite(play, actor, &levelEntry); + break; + case ACTOR_EN_CROW: + Leveled_Guay(play, actor, &levelEntry); + break; + case ACTOR_EN_OKUTA: + Leveled_Octorock(play, actor, &levelEntry); + break; + case ACTOR_EN_AM: + Leveled_Armos(play, actor, &levelEntry); + break; + case ACTOR_EN_DODOJR: + Leveled_BabyDodongo(play, actor, &levelEntry); + break; + case ACTOR_EN_FIREFLY: + Leveled_Keese(play, actor, &levelEntry); + break; + case ACTOR_EN_PEEHAT: + Leveled_Peahat(play, actor, &levelEntry); + break; + case ACTOR_EN_POH: + Leveled_Poh(play, actor, &levelEntry); + case ACTOR_EN_PO_FIELD: + Leveled_Poh_Field(play, actor, &levelEntry); + break; + case ACTOR_EN_RD: + Leveled_ReDead(play, actor, &levelEntry); + break; + case ACTOR_EN_SKB: + Leveled_Stalchild(play, actor, &levelEntry); + break; + case ACTOR_EN_SW: + Leveled_Wall_Skulltula(play, actor, &levelEntry); + break; + case ACTOR_EN_VM: + Leveled_Beamos(play, actor, &levelEntry); + break; + case ACTOR_EN_WF: + Leveled_Wolfos(play, actor, &levelEntry); + break; + case ACTOR_EN_ZF: + Leveled_Lizalfos(play, actor, &levelEntry); + break; + case ACTOR_EN_BUBBLE: + Leveled_Shabom(play, actor, &levelEntry); + break; + case ACTOR_EN_BILI: + Leveled_Biri(play, actor, &levelEntry); + break; + case ACTOR_EN_VALI: + Leveled_Bari(play, actor, &levelEntry); + break; + case ACTOR_EN_TP: + Leveled_Tailpasaran(play, actor, &levelEntry); + break; + case ACTOR_EN_BA: + case ACTOR_EN_BX: + Leveled_JabuTentacle(play, actor, &levelEntry); + break; + case ACTOR_EN_EIYER: + case ACTOR_EN_WEIYER: + Leveled_Stinger(play, actor, &levelEntry); + break; + case ACTOR_EN_BIGOKUTA: + Leveled_BigOctorock(play, actor, &levelEntry); + break; + case ACTOR_EN_MB: + Leveled_Moblins(play, actor, &levelEntry); + break; + case ACTOR_EN_BB: + Leveled_Bubble(play, actor, &levelEntry); + break; + case ACTOR_EN_TEST: + Leveled_Stalfos(play, actor, &levelEntry); + break; + case ACTOR_EN_FLOORMAS: + Leveled_Floormaster(play, actor, &levelEntry); + break; + case ACTOR_EN_WALLMAS: + Leveled_Wallmaster(play, actor, &levelEntry); + break; + case ACTOR_EN_PO_SISTERS: + Leveled_Poh_Sisters(play, actor, &levelEntry); + break; + case ACTOR_EN_RR: + Leveled_LikeLike(play, actor, &levelEntry); + break; + case ACTOR_EN_BW: + Leveled_TorchSlug(play, actor, &levelEntry); + break; + case ACTOR_EN_FZ: + Leveled_Freezer(play, actor, &levelEntry); + break; + case ACTOR_EN_SB: + Leveled_ShellBlade(play, actor, &levelEntry); + break; + case ACTOR_EN_NY: + Leveled_RollingSpike(play, actor, &levelEntry); + break; + case ACTOR_EN_TORCH2: + Leveled_DarkLink(play, actor, &levelEntry); + break; + case ACTOR_EN_DH: + case ACTOR_EN_DHA: + Leveled_DeadHand(play, actor, &levelEntry); + break; + case ACTOR_EN_FD: + case ACTOR_EN_FW: + case ACTOR_EN_FD_FIRE: + Leveled_FlareDancer(play, actor, &levelEntry); + break; + case ACTOR_EN_IK: + Leveled_IronKnuckle(play, actor, &levelEntry); + break; + case ACTOR_EN_GELDB: + Leveled_GerudoFighter(play, actor, &levelEntry); + break; + case ACTOR_EN_REEBA: + Leveled_Leever(play, actor, &levelEntry); + break; + case ACTOR_EN_ANUBICE: + case ACTOR_EN_ANUBICE_FIRE: + Leveled_Anubis(play, actor, &levelEntry); + break; + case ACTOR_EN_CLEAR_TAG: + Leveled_Arwing(play, actor, &levelEntry); + break; + default: + actor->level = GET_PLAYER(play)->actor.level; + break; + } + + s8 sceneLevel = Leveled_GetSceneLevel(play->sceneNum); + if (!levelEntry.ignoreEntry && sceneLevel >= 0) { + actor->level = (u8)CLAMP((f32)(sceneLevel + levelEntry.levelModifier) * (f32)CVarGetFloat("gLeveled.Difficulty.Enemy.LevelScale", 1.0f), 1, 99); + actor->exp = GetEnemyExperienceReward(actor->level, levelEntry.experienceRate); + } +} \ No newline at end of file diff --git a/soh/src/code/leveled_map_levels.c b/soh/src/code/leveled_map_levels.c new file mode 100644 index 00000000000..ea1616b8043 --- /dev/null +++ b/soh/src/code/leveled_map_levels.c @@ -0,0 +1,181 @@ +#include "global.h" + +s8 Leveled_GetSceneLevel(s16 sceneId) { + switch (sceneId) { + case SCENE_DEKU_TREE: + case SCENE_DEKU_TREE_BOSS: + return 2; // Deku Tree + + case SCENE_DODONGOS_CAVERN: + case SCENE_DODONGOS_CAVERN_BOSS: + return 10; // Dodongo's + + case SCENE_JABU_JABU: + case SCENE_JABU_JABU_BOSS: + return 16; // Jabu Jabu + + case SCENE_FOREST_TEMPLE: + case SCENE_FOREST_TEMPLE_BOSS: + return 24; // Forest Temple + + case SCENE_FIRE_TEMPLE: + case SCENE_FIRE_TEMPLE_BOSS: + return 30; // Fire Temple + + case SCENE_WATER_TEMPLE: + case SCENE_WATER_TEMPLE_BOSS: + return 35; // Water Temple + + case SCENE_SPIRIT_TEMPLE: + case SCENE_SPIRIT_TEMPLE_BOSS: + return 45; // Spirit Temple + + case SCENE_SHADOW_TEMPLE: + case SCENE_SHADOW_TEMPLE_BOSS: + return 40; // Shadow Temple + + case SCENE_BOTTOM_OF_THE_WELL: + return 38; // Bottom of the Well + + case SCENE_ICE_CAVERN: + return 32; // Ice Cavern + + case SCENE_GANONS_TOWER: + case SCENE_GANONDORF_BOSS: + case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR: + case SCENE_GANON_BOSS: + if (CVarGetInteger("gLeveled.Difficulty.Enemy.MaxLevelInGanonCastle", 0) == 1 && gSaveContext.inventory.gsTokens == 100) + return 120; + else + return 50; + + case SCENE_GERUDO_TRAINING_GROUND: + return 43; + + case SCENE_THIEVES_HIDEOUT: + return 42; + + case SCENE_INSIDE_GANONS_CASTLE: + return 52; + + case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: + case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE: + return 53; + + case SCENE_MARKET_RUINS: + return 23; + + case SCENE_REDEAD_GRAVE: + return 10; + + case SCENE_ROYAL_FAMILYS_TOMB: + return 8; + + case SCENE_WINDMILL_AND_DAMPES_GRAVE: + return 22; + + case SCENE_HOUSE_OF_SKULLTULA: + return 99; + + case SCENE_HYRULE_FIELD: + if (LINK_IS_CHILD) + return 5; + else + return 20; + + case SCENE_GRAVEYARD: + if (LINK_IS_CHILD) + return 7; + else + return 20; + + case SCENE_ZORAS_RIVER: + if (LINK_IS_CHILD) + return 8; + else + return 24; + + case SCENE_KOKIRI_FOREST: + if (LINK_IS_CHILD) + return 2; + else + return 21; + + case SCENE_SACRED_FOREST_MEADOW: + if (LINK_IS_CHILD) + return 9; + else + return 23; + + case SCENE_LAKE_HYLIA: + if (LINK_IS_CHILD) + return 7; + else + return 22; + + case SCENE_ZORAS_FOUNTAIN: + if (LINK_IS_CHILD) + return 13; + else + return 30; + + case SCENE_GERUDO_VALLEY: + if (LINK_IS_CHILD) + return 10; + else + return 28; + + case SCENE_LOST_WOODS: + if (LINK_IS_CHILD) + return 7; + else + return 22; + + case SCENE_DESERT_COLOSSUS: + return 42; + + case SCENE_GERUDOS_FORTRESS: + return 40; + + case SCENE_DEATH_MOUNTAIN_TRAIL: + if (LINK_IS_CHILD) + return 8; + else + return 24; + + case SCENE_DEATH_MOUNTAIN_CRATER: + return 11; + + case SCENE_LON_LON_RANCH: + return 6; + + case SCENE_OUTSIDE_GANONS_CASTLE: + return 25; + + + // Debug only scenes + case SCENE_TEST01: + // Test Map + case SCENE_BESITU: + // Test Room + case SCENE_DEPTH_TEST: + // Depth Test + case SCENE_SYOTES: + // Stalfos Mini-Boss + case SCENE_SYOTES2: + // Stalfos Boss + case SCENE_SUTARU: + // Dark Link + case SCENE_HAIRAL_NIWA2: + // Castle Maze (Broken) + case SCENE_SASATEST: + // SRD Room + case SCENE_TESTROOM: + // Chest Room + return 45; + + default: + return -1; + } + return -1; +} \ No newline at end of file diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c new file mode 100644 index 00000000000..1003e49327e --- /dev/null +++ b/soh/src/code/leveled_overlays.c @@ -0,0 +1,776 @@ +#include "leveled_overlays.h" +#include "global.h" +#include "textures/icon_item_static/icon_item_static.h" +#include "textures/parameter_static/parameter_static.h" +#include "textures/do_action_static/do_action_static.h" +#include "textures/icon_item_24_static/icon_item_24_static.h" +#include "textures/message_static/message_static.h" +#include "textures/nes_font_static/nes_font_static.h" + + + +Gfx* Gfx_Texture32(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { + gDPLoadTextureBlock(displayListHead++, texture, G_IM_FMT_RGBA, G_IM_SIZ_32b, textureWidth, textureHeight, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPWideTextureRectangle(displayListHead++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, + (rectTop + rectHeight) << 2, G_TX_RENDERTILE, 0, 0, dsdx, (s32)((dtdy))); + + return displayListHead; +} + +Gfx* Gfx_Texture4b(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { + gDPLoadTextureBlock_4b(displayListHead++, texture, G_IM_FMT_IA, textureWidth, textureHeight, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPWideTextureRectangle(displayListHead++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, + (rectTop + rectHeight) << 2, G_TX_RENDERTILE, 0, 0, dsdx, dtdy); + + return displayListHead; +} + +void Leveled_DrawTexI8(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight, u8 r, u8 g, u8 b) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_TextureI8(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, + textureHeight, (s32)(1024 * (f32)textureWidth / rectWidth), + (s32)(1024 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_DrawTexIA8(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight, u8 r, u8 g, u8 b) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_TextureIA8(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, textureHeight, (s32)(1024 * (f32)textureWidth / rectWidth), + (s32)(1024 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_DrawTex32(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_Texture32(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, textureHeight, (s32)(2048 * (f32)textureWidth / rectWidth), + (s32)(2048 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_DrawTex4b(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_Texture4b(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, textureHeight, (s32)(2048 * (f32)textureWidth / rectWidth), + (s32)(2048 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_OverlayDrawTex4b(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight, u16 alpha) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(OVERLAY_DISP++); + gDPSetTextureFilter(OVERLAY_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, alpha); + + OVERLAY_DISP = Gfx_Texture4b(OVERLAY_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, rectWidth, + rectHeight, 1 << 10, 1 << 10); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void ActorDamageNumber_New(Actor* actor, u16 damage) { + if (damage == 0 || (actor->category == ACTORCAT_PLAYER && !CVarGetInteger("gLeveled.HUD.FloatingNumbers.PlayerDamage", 1)) || (actor->category != ACTORCAT_PLAYER && !CVarGetInteger("gLeveled.HUD.FloatingNumbers.EnemyDamage", 1))) + return; + + Vec2f position = { 0, 0 }; + Vec2f velocity = { 0, -8 }; + + actor->floatingNumber[0] = damage; + actor->floatingNumberLife[0] = 30; + actor->floatingNumberPosition[0] = position; + actor->floatingNumberVelocity[0] = velocity; +} + +void ActorExperienceNumber_New(Actor* actor, u16 experience) { + if (experience == 0 || !CVarGetInteger("gLeveled.HUD.FloatingNumbers.ExpGain", 1)) + return; + + + Vec2f position = { 0, 0 }; + Vec2f velocity = { 0, -6 }; + + actor->floatingNumber[1] = experience; + actor->floatingNumberLife[1] = 40; + actor->floatingNumberPosition[1] = position; + actor->floatingNumberVelocity[1] = velocity; +} + +void ActorLevelUp_New(Actor* actor, u8 powerDiff, u8 courageDiff, u16 healthDiff, u8 magicDiff) { + + Vec2f position = { 0, 0 }; + Vec2f velocity = { 0, -6 }; + + actor->floatingNumberLife[2] = 100; + if (powerDiff > 0) + actor->floatingNumberLife[3] = 100; + if (courageDiff > 0) + actor->floatingNumberLife[4] = 100; + if (healthDiff > 0) + actor->floatingNumberLife[5] = 100; + if (magicDiff > 0) + actor->floatingNumberLife[6] = 100; + + actor->floatingNumberPosition[2] = position; + actor->floatingNumberVelocity[2] = velocity; + position.x = 16; + position.y = 0; + velocity.x = -4; + velocity.y = -5; + actor->floatingNumberPosition[3] = position; + actor->floatingNumberVelocity[3] = velocity; + position.x = 16; + position.y = 0; + velocity.x = 4; + velocity.y = -5; + actor->floatingNumberPosition[4] = position; + actor->floatingNumberVelocity[4] = velocity; + position.x = 16; + position.y = 0; + velocity.x = -2; + velocity.y = -5; + actor->floatingNumberPosition[5] = position; + actor->floatingNumberVelocity[5] = velocity; + position.x = 16; + position.y = 0; + velocity.x = 2; + velocity.y = -5; + actor->floatingNumberPosition[6] = position; + actor->floatingNumberVelocity[6] = velocity; +} + +void ActorExperienceNumber_Draw(PlayState* play, Actor* actor) { + + extern const char* digitTextures[]; + s16 val = actor->floatingNumber[1]; + u8 digit[] = { 0, 0, 0, 0, 0 }; + u8 digits = 1; + u8 width = 8; + Vec3f spBC; + f32 spB4; + s32 j; + + if (actor->floatingNumberLife[1] <= 0) + return; + + OPEN_DISPS(play->state.gfxCtx); + + // Digits + if (val < 0) + val = 0; + + if (val > 32000) + val = 32000; + + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = (u8)val; + + // Velocity + actor->floatingNumberPosition[1].x += actor->floatingNumberVelocity[1].x; + actor->floatingNumberPosition[1].y += actor->floatingNumberVelocity[1].y; + + if (actor->floatingNumberVelocity[1].y < 0) { + actor->floatingNumberVelocity[1].y += 0.4f; + } + + // Life + actor->floatingNumberLife[1]--; + + // Position + func_8002BE04(play, &actor->world.pos, &spBC, &spB4); + + spBC.x = (160 * (spBC.x * spB4)) * 1.0f + 157 + actor->floatingNumberPosition[1].x + (digits - 1) * width / 2; + spBC.x = CLAMP(spBC.x, -320.0f, 320.0f); + + spBC.y = (120 * (spBC.y * spB4)) * -1.0f + 90 + actor->floatingNumberPosition[1].y; + spBC.y = CLAMP(spBC.y, -240.0f, 240.0f); + + if (spBC.z <= -0) { + spBC.x = 160 + actor->floatingNumberPosition[1].x + (digits - 1) * width / 2; + spBC.y = 200 + actor->floatingNumberPosition[1].y; + } + + spBC.z = spBC.z * 1.0f; + + // Color + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 190, 190, 0, 255); + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + // Draw + for (u8 i = 0; i < digits; i++) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, (s16)spBC.x - i * width, (s16)spBC.y, + 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void ActorDamageNumber_Draw(PlayState* play, Actor* actor) { + + extern const char* digitTextures[]; + s16 val = actor->floatingNumber[0]; + u8 digit[] = { 0, 0, 0, 0 }; + u8 digits = 1; + u8 width = 8; + Vec3f spBC; + f32 spB4; + s32 j; + + if (actor->floatingNumberLife[0] <= 0) + return; + + OPEN_DISPS(play->state.gfxCtx); + + // Digits + if (val < 0) + val = 0; + + if (val > 9999) + val = 9999; + + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = (u8)val; + + // Velocity + actor->floatingNumberPosition[0].x += actor->floatingNumberVelocity[0].x; + actor->floatingNumberPosition[0].y += actor->floatingNumberVelocity[0].y; + + actor->floatingNumberVelocity[0].y += 2.0f; + if (actor->floatingNumberPosition[0].y >= 0) { + actor->floatingNumberPosition[0].y = 0; + actor->floatingNumberVelocity[0].y = -actor->floatingNumberVelocity[0].y * 0.5f; + if (actor->floatingNumberVelocity[0].y >= -4.0f) + actor->floatingNumberVelocity[0].y = 0; + } + + // Life + actor->floatingNumberLife[0]--; + + // Position + func_8002BE04(play, &actor->world.pos, &spBC, &spB4); + + spBC.x = (160 * (spBC.x * spB4)) * 1.0f + 157 + actor->floatingNumberPosition[0].x + (digits - 1) * width / 2; + spBC.x = CLAMP(spBC.x, -320.0f, 320.0f); + + spBC.y = (120 * (spBC.y * spB4)) * -1.0f + 90 + actor->floatingNumberPosition[0].y; + spBC.y = CLAMP(spBC.y, -240.0f, 240.0f); + + spBC.z = spBC.z * 1.0f; + + // Color + if (actor->category == ACTORCAT_PLAYER) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 100, 0, 255); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255); + } + + // Draw + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + for (u8 i = 0; i < digits; i++) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, (s16)spBC.x - i * width - (digits - 1) * width * 0.5f, (s16)spBC.y, + 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Actor_LevelUpDraw(PlayState* play, Actor* actor) { + u8 width = 8; + Vec3f spBC; + f32 spB4; + for (s8 i = 2; i < 7; i++) { + if (actor->floatingNumberLife[i] <= 0) + continue; + + OPEN_DISPS(play->state.gfxCtx); + + // Velocity + actor->floatingNumberPosition[i].x += actor->floatingNumberVelocity[i].x; + actor->floatingNumberPosition[i].y += actor->floatingNumberVelocity[i].y; + + if (actor->floatingNumberVelocity[i].y < 0) { + actor->floatingNumberVelocity[i].y += 0.75f; + if (actor->floatingNumberVelocity[i].y > 0) + actor->floatingNumberVelocity[i].y = 0; + } + if (actor->floatingNumberVelocity[i].x < 0) { + actor->floatingNumberVelocity[i].x += 0.5f; + if (actor->floatingNumberVelocity[i].x > 0) + actor->floatingNumberVelocity[i].x = 0; + } + if (actor->floatingNumberVelocity[i].x > 0) { + actor->floatingNumberVelocity[i].x -= 0.5f; + if (actor->floatingNumberVelocity[i].x < 0) + actor->floatingNumberVelocity[i].x = 0; + } + + // Life + actor->floatingNumberLife[i]--; + + // Position + func_8002BE04(play, &actor->world.pos, &spBC, &spB4); + + spBC.x = (160 * (spBC.x * spB4)) * 1.0f + 165 + actor->floatingNumberPosition[i].x - 24; + spBC.x = CLAMP(spBC.x, -320.0f, 320.0f); + + spBC.y = (120 * (spBC.y * spB4)) * -1.0f + 90 + actor->floatingNumberPosition[i].y; + spBC.y = CLAMP(spBC.y, -240.0f, 240.0f); + + spBC.z = spBC.z * 1.0f; + + if (spBC.z < 0) { + spBC.x = 160 + actor->floatingNumberPosition[i].x - 24; + spBC.y = 200 + actor->floatingNumberPosition[i].y; + } + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + if (i == 2) { + + for (u8 j = 0; j < 3; j++) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar4CLatinCapitalLetterLTex, 8, 16, (s16)spBC.x, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar45LatinCapitalLetterETex, 8, 16, (s16)spBC.x + 5, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar56LatinCapitalLetterVTex, 8, 16, (s16)spBC.x + 10, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar45LatinCapitalLetterETex, 8, 16, (s16)spBC.x + 15, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar4CLatinCapitalLetterLTex, 8, 16, (s16)spBC.x + 20, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar55LatinCapitalLetterUTex, 8, 16, (s16)spBC.x + 30, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar50LatinCapitalLetterPTex, 8, 16, (s16)spBC.x + 35, (s16)spBC.y, 8, 16, 2048, 2048); + } + } + + if (i == 3) { + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgItemIconSilverGauntletsTex, 32, 32, (s16)spBC.x, (s16)spBC.y, 32, 32, 4098, 4098); + } + if (i == 4) { + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgItemIconShieldHylianTex, 32, 32, (s16)spBC.x, (s16)spBC.y, 32, 32, 4098, 4098); + } + if (i == 5) { + + gDPPipeSync(OVERLAY_DISP++); + gDPSetTextureFilter(OVERLAY_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 70, 0, 255); + + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, dgHeartFullTex, 16, 16, (s16)spBC.x, (s16)spBC.y, 16, 16, 2048, 2048); + } + + if (i == 6) { + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgQuestIconMagicJarBigTex, 24, 24, (s16)spBC.x, (s16)spBC.y, 24, 24, 3072, 3072); + } + + CLOSE_DISPS(play->state.gfxCtx); + } +} + +extern const char* _gAmmoDigit0Tex[]; +void Leveled_ValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b) { + s32 val; + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + s16 separation = 5; + + u8 digits; + + OPEN_DISPS(play->state.gfxCtx); + + val = value; + + if (val > 999999) + val = 999999; + + if (val < 0) + val = 0; + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + digits = 1; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + s8 j; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = val; + + gDPPipeSync(POLY_OPA_DISP++); + + for (s8 i = 0; i < digits; i++) { + POLY_OPA_DISP = + Gfx_TextureIA8(POLY_OPA_DISP, (u8*)_gAmmoDigit0Tex[digit[i]], 8, 8, x - i * 6 + 6 * (digits - 1), y, 8, + 8, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_BigValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b, u8 a) { + s32 val; + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + s16 separation = 5; + u8 width = 7; + + extern const char* digitTextures[]; + + u8 digits; + + OPEN_DISPS(play->state.gfxCtx); + + val = value; + + if (val > 999999) + val = 999999; + + if (val < 0) + val = 0; + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, a); + + digits = 1; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + s8 j; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = val; + + gDPPipeSync(POLY_OPA_DISP++); + + for (u8 i = 0; i < digits; i++) { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 0, 0, 255); + + for (j = 0; j < 4; j++) { + POLY_OPA_DISP = Gfx_TextureI8(POLY_OPA_DISP, (u8*)digitTextures[digit[i]], 8, 16, x - i * width + width * (digits - 1) + (j % 2) * 2 - 1, y + (j / 2) * 2 - 1, 8, 16, 1 << 10, 1 << 10); + } + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + POLY_OPA_DISP = Gfx_TextureI8(POLY_OPA_DISP, (u8*)digitTextures[digit[i]], 8, 16, x - i * width + width * (digits - 1), y, 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_KaleidoEquip_Stats(PlayState* play) { + extern const char* digitTextures[]; + Player* player = GET_PLAYER(play); + u16 statX = 92; + u16 statY = 76; + u8 attack = 1; + Color_RGBA8 textColor = { 255, 255, 255, 255 }; + + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 1) + attack = Leveled_GetHealthAttackMultiplier(); + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 2) + attack = Leveled_GetHealthAttackMultiplier() << 1; + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 3) { + attack = Leveled_GetHealthAttackMultiplier() << 2; + if (gBitFlags[3] & gSaveContext.inventory.equipment) + attack = Leveled_GetHealthAttackMultiplier(); + } + + // Values and Icons + // Level + Leveled_DrawTexI8(play, dgMsgChar4CLatinCapitalLetterLTex, 8, 16, statX + 2, statY - 2, 10, 11, 255, 255, 255); + Leveled_DrawTexI8(play, dgMsgChar76LatinSmallLetterVTex, 8, 16, statX + 5, statY - 2, 10, 11, 255, 255, 255); + + Leveled_BigValueNumberDraw(play, statX + 10, statY - 6, player->actor.level, 255, 255, 255, 255); + statY += 10; + // Health + Leveled_DrawTexIA8(play, dgHeartFullTex, 16, 16, statX + 2, statY, 8, 8, 255, 70, 0); + u8 healthValX = gSaveContext.healthCapacity2 >= 1000 ? 12 : gSaveContext.healthCapacity2 >= 100 ? 6 : 0; + if (HealthMeter_IsCritical()) { + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.health, 255, 127, 0); + } else { + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.health, 255, 255, 255); + } + Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 23 + healthValX, statY - 1, 8, 9, 255, 255, 255); + Leveled_ValueNumberDraw(play, statX + 28 + healthValX, statY, gSaveContext.healthCapacity2, 120, 255, 0); + statY += 8; + // Magic + if (gSaveContext.magicCapacity > 0) { + healthValX = gSaveContext.magicCapacity >= 100 ? 6 : 0; + Leveled_DrawTex32(play, dgQuestIconMagicJarBigTex, 24, 24, statX + 2, statY, 14, 14); + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.magic, 255, 255, 255); + Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 23 + healthValX, statY - 1, 8, 9, 255, 255, 255); + Leveled_ValueNumberDraw(play, statX + 28 + healthValX, statY, gSaveContext.magicCapacity, 120, 255, 0); + statY += 8; + } + // Attack + Leveled_DrawTex32(play, dgItemIconSwordKokiriTex, 32, 32, statX, statY - 1, 22, 20); + Leveled_ValueNumberDraw(play, statX + 10, statY, GetActorStat_Attack(attack, CLAMP(player->actor.power + player->actor.powerModifier, 0, 255)), 255, 255, 255); + statY += 8; + // Power + if (player->actor.powerModifier > 0) { + textColor.r = 120; + textColor.g = 255; + textColor.b = 0; + } else if (player->actor.powerModifier < 0) { + textColor.r = 255; + textColor.g = 0; + textColor.b = 0; + } + Leveled_DrawTex32(play, dgItemIconSilverGauntletsTex, 32, 32, statX + 2, statY, 16, 16); + Leveled_ValueNumberDraw(play, statX + 10, statY, CLAMP(player->actor.power + player->actor.powerModifier, 0, 255), textColor.r, textColor.g, textColor.b); + statY += 8; + // Courage + if (player->actor.courageModifier > 0) { + textColor.r = 120; + textColor.g = 255; + textColor.b = 0; + } else if (player->actor.courageModifier < 0) { + textColor.r = 255; + textColor.g = 0; + textColor.b = 0; + } else { + textColor.r = 255; + textColor.g = 255; + textColor.b = 255; + } + Leveled_DrawTex32(play, dgItemIconShieldHylianTex, 32, 32, statX + 2, statY, 16, 16); + Leveled_ValueNumberDraw(play, statX + 10, statY, CLAMP(player->actor.courage + player->actor.courageModifier, 0, 255), textColor.r, textColor.g, textColor.b); + statX = 90; + statY += 60; + if (gSaveContext.magicCapacity > 0) { + statY -= 8; + } + // EXP + Leveled_DrawTex32(play, dgItemIconGoronsBraceletTex, 32, 32, statX + 4, statY, 13, 16); + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.experience, 255, 255, 255); + statY += 8; + // Next LV + Leveled_DrawTex4b(play, dgNextDoActionENGTex, 48, 16, statX - 4, statY, 68, 15); + Leveled_ValueNumberDraw(play, statX + 22, statY, + GetActorStat_NextLevelExp(player->actor.level, gSaveContext.experience), 255, 255, 255); +} + +void Leveled_Interface_DrawNextLevel(PlayState* play) { + if (gSaveContext.showNeededExpTimer > 0){ + gSaveContext.showNeededExpTimer--; + } else { + return; + } + if (play->pauseCtx.state != 0 || !CVarGetInteger("gLeveled.HUD.ExperienceNextLevel", 1)) + return; + + Player* player = GET_PLAYER(play); + u16 posX = OTRGetRectDimensionFromLeftEdge(10); + u16 posY = 58; + extern const char* digitTextures[]; + s32 val = GetActorStat_NextLevelExp(player->actor.level, gSaveContext.experience); + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + u8 digits = 1; + u8 width = 8; + s32 j; + + Leveled_OverlayDrawTex4b(play, dgNextDoActionENGTex, 48, 16, -192, -268, 48, 16, 255 - play->pauseCtx.alpha); // Load texture + Leveled_OverlayDrawTex4b(play, dgNextDoActionENGTex, 48, 16, posX, posY, 48, 16, 255 - play->pauseCtx.alpha); // Draw texture + + OPEN_DISPS(play->state.gfxCtx); + + // Digits + if (val < 0) + val = 0; + + if (val > 999999) + val = 999999; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = (u8)val; + + // Color + + // Draw + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + s16 numbersPosX = 38; + + for (u8 i = 0; i < digits; i++) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, 255 - play->pauseCtx.alpha); + + for (j = 0; j < 3; j++) { + for (u8 k = 0; k < 3; k++) { + if (j == 1 && k == 1) + continue; + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, + posX - i * width + width * (digits - 1) + numbersPosX - 1 + k, posY - 1 + j, 8, 16, 1024, 1024); + } + } + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255 - play->pauseCtx.alpha); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, posX - i * width + width * (digits - 1) + numbersPosX, posY, 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} \ No newline at end of file diff --git a/soh/src/code/leveled_stat_math.c b/soh/src/code/leveled_stat_math.c new file mode 100644 index 00000000000..e1ae153d9bc --- /dev/null +++ b/soh/src/code/leveled_stat_math.c @@ -0,0 +1,267 @@ +#include "global.h" + +static u32 sExpTable[] = { 0, 30, 64, 105, 155, 217, 292, 385, 497, 632, 792, + 981, 1202, 1458, 1753, 2089, 2471, 2903, 3388, 3930, 4534, 5203, + 5942, 6755, 7648, 8625, 9691, 10851, 12110, 13474, 14949, 16540, 18253, + 20094, 22070, 24188, 26453, 28874, 31456, 34209, 37139, 40254, 43562, 47073, + 50794, 54734, 58904, 63312, 67968, 72883, 78067, 83531, 89286, 95345, 101719, + 108421, 115463, 122859, 130623, 138770, 147314, 156270, 165655, 175485, 185778, 196552, + 207824, 219614, 231943, 244831, 258300, 272372, 287071, 302422, 318450, 335182, 352645, + 370870, 389885, 409724, 430419, 452005, 474520, 498001, 522489, 548027, 574659, 602434, + 631400, 661611, 693122, 725993, 760287, 796070, 833414, 872395, 913094, 955597, 999999 }; +/* Old table +{ 0, 30, 65, 110, 167, 240, 334, 452, 598, 777, 993, + 1251, 1555, 1910, 2320, 2791, 3329, 3937, 4621, 5388, 6242, 7189, + 8235, 9386, 10647, 12026, 13528, 15160, 16929, 18840, 20902, 23120, 25501, + 28054, 30784, 33700, 36809, 40119, 43637, 47371, 51329, 55520, 59951, 64630, + 69567, 74770, 80248, 86008, 92061, 98416, 105080, 112065, 119379, 127032, 135033, + 143392, 152119, 161225, 170719, 180611, 190912, 201632, 212783, 224374, 236417, 248922, + 261901, 275365, 289325, 303792, 318779, 334297, 350358, 366973, 384155, 401916, 420268, + 439224, 458796, 478997, 499839, 521336, 543501, 566346, 589885, 614131, 639098, 664799, + 691249, 718461, 746448, 775226, 804807, 835208, 866441, 898523, 931466, 965287, 999999 };*/ + +u8 Leveled_GetHealthAttackMultiplier() { + return CLAMP(CVarGetInteger("gLeveled.Enhancements.AttackAndHPMultiplier", 9), 1, 32); +} + +u16 GetActorStat_DisplayAttack(u16 attack, u8 power) { + return GetActorStat_Attack(attack, power) / (1 + (float)power / 30.0f); +} + +u16 GetActorStat_Attack(u16 attack, u8 power) { + return (float)attack * (1 + (power - 2) * (0.14f + (power - 2) * 0.0006f)); +} + +f32 GetActorStat_EnemyAttack(u16 attack, u8 power) { + return (float)attack * (1 + (power - 2) * (0.007f + (power - 2) * 0.0002f)); +} + +u8 GetActorStat_Power(u8 level) { + return 3 + (u8)(84 * level / 99.0f); +} + +u8 GetActorStat_Courage(u8 level) { + return 2 + (u8)(75 * level / 99.0f); +} + +u8 GetActorStat_PlayerPower(u8 level) { + return 2 + (u8)(75 * level / 99.0f); +} + +u8 GetActorStat_PlayerCourage(u8 level) { + return 3 + (u8)(84 * level / 99.0f); +} + +u16 GetActorStat_EnemyMaxHealth(u16 baseHealth, u8 level){ + return (u16)(CLAMP((f32)GetActorStat_Attack(baseHealth * Leveled_GetHealthAttackMultiplier(), GetActorStat_PlayerPower(level)) * CVarGetFloat("gLeveled.Difficulty.Enemy.HPPercent", 1.0f), 1, 0xffff)); +} + +u8 GetPlayerStat_BonusHearts(u8 level){ + if (CVarGetInteger("gLeveled.Player.Enhancements.HeartsWithLevelUp", 1) == 0){ + return 0; + } + + u8 bonusHearts = (level + 1) / 8; + if (bonusHearts > 10){ + bonusHearts = 10; + } + return bonusHearts; +} + +u8 GetPlayerStat_MagicUnits(u8 level){ + if (CVarGetInteger("gLeveled.Player.Enhancements.MagicWithLevelUp", 1) == 0){ + return 48; + } + + u8 maximumMagic = 12 + (u8)((f32)level / 2.8f) * 2; + if (maximumMagic > 72){ + maximumMagic = 72; + } + return maximumMagic; +} + +u16 GetPlayerStat_GetModifiedHealthCapacity(u16 baseHealth, u8 level){ + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + u16 baseHearts = baseHealth / 16; + return (baseHearts + GetPlayerStat_BonusHearts(level)) * heartUnits; +} + +u16 GetPlayerStat_NextLevelExpAtLevel(u8 level) { + if (level == 99) + return 0; + + s32 nextLv = sExpTable[level] - sExpTable[level - 1]; +} + +u16 GetActorStat_NextLevelExp(u8 level, u32 currentExp) { + if (level == 99) + return 0; + + s32 nextLv = sExpTable[level] - currentExp; + + if (nextLv < 0) + nextLv = 0; + return nextLv; +} + +u16 GetEnemyExperienceReward(u8 level, u16 expRate) { + if (expRate == 0) + return 0; + + return CLAMP(round((3 + CLAMP_MAX(floor(level / 6) * 4, 4) + (level - 1) * ((0.1 + (level / 95.0)) + pow(CLAMP_MIN(level - 8, 0), 1.25) / 80.0)) * expRate / 100.0), 1, 9999); +} + +f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage) { + f32 damage = GetActorStat_Attack(attack, power); + if (power >= courage) { + for (u8 i = 0; i < power - courage; i++) { + damage *= 1.01f; + } + } else { + for (u8 i = 0; i < courage - power; i++) { + damage *= 0.97f; + } + } + return damage; +} + +f32 Leveled_DamageFormulaOnPlayer(f32 attack, u8 power, u8 courage) { + f32 damage = attack; + + if (CVarGetInteger("gLeveled.Enemy.Enhancements.AttackScalesWithLevel", 1) == 1){ + damage = GetActorStat_EnemyAttack(attack, power); + + if (power >= courage) { + for (u8 i = 0; i < power - courage; i++) { + f32 multAddition = CLAMP_MIN((0.07f - (power * 0.0005f)) - (f32)i / (100.0f - (power * 0.33f)), 0); + damage *= 1.04f + multAddition; + } + } else { + for (u8 i = 0; i < courage - power; i++) { + damage *= 0.96f; + } + } + } else { + if (power >= courage) { + for (u8 i = 0; i < power - courage; i++) { + damage *= 1.05f + CLAMP_MIN(0.05f - (f32)i / 100.0f, 0); + } + } else { + for (u8 i = 0; i < courage - power; i++) { + damage *= 0.96f; + } + } + } + return damage; +} + +f32 Leveled_DamageModify(Actor* actor, Actor* attackingActor, f32 attack) { + f32 damage; + if (actor->category == ACTORCAT_PLAYER) { + damage = Leveled_DamageFormulaOnPlayer(attack, CLAMP(attackingActor->power + attackingActor->powerModifier, 0, 255), CLAMP(actor->courage + actor->courageModifier, 0, 255)); + } else { + damage = Leveled_DamageFormula(attack, CLAMP(attackingActor->power + attackingActor->powerModifier, 0, 255), CLAMP(actor->courage + actor->courageModifier, 0, 255)); + } + + + if (damage >= 1.25f) + damage += Rand_ZeroOne() - 0.2f; + + if (damage >= 6) + damage += Rand_ZeroFloat(damage * 0.12f) - damage * 0.06f; + + if (damage > 9999) + damage = 9999; + + damage = (u16)(damage + 0.5f); + + return CLAMP_MIN(damage, attack > 0 ? 1 : 0); +} + +u16 Leveled_GoldSkulltulaExperience(u8 tokens) { + u16 experience = 5; + u8 i; + + for (i = 0; i < tokens; i++) { + experience += 5 + 5 * i / (f32)10.0; + } + return (u16)((f32)experience * CVarGetFloat("gLeveled.Difficulty.EXP.TokenRate", 1.0f)); +} + +void Leveled_SetPlayerModifiedStats(Player* player) { + s8 powerModifier = 0; + s8 courageModifier = 0; + + if (CVarGetInteger("gLeveled.Player.Enhancements.EquipmentStats", 1) == 1){ + switch (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD)){ + case PLAYER_SWORD_MASTER: + courageModifier += 1; + break; + + case PLAYER_SWORD_BIGGORON: + if (gBitFlags[3] & gSaveContext.inventory.equipment){ + powerModifier -= 7; + courageModifier -= 12; + } else { + powerModifier += 2; + courageModifier -= 8; + } + break; + + default: + break; + } + + switch (CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC) - 1){ + case PLAYER_TUNIC_GORON: + powerModifier += 3; + courageModifier -= 3; + break; + + case PLAYER_TUNIC_ZORA: + powerModifier -= 3; + courageModifier += 3; + break; + + default: + break; + } + + switch (CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD)){ + case PLAYER_SHIELD_DEKU: + courageModifier += 1; + break; + + case PLAYER_SHIELD_HYLIAN: + courageModifier += 2; + break; + + case PLAYER_SHIELD_MIRROR: + powerModifier -= 2; + courageModifier += 3; + break; + + default: + break; + } + + switch (CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS) - 1){ + case PLAYER_BOOTS_IRON: + powerModifier += 2; + courageModifier += 1; + break; + + case PLAYER_BOOTS_HOVER: + courageModifier -= 1; + break; + + default: + break; + } + + powerModifier += Player_GetStrength(); + } + + player->actor.powerModifier = powerModifier; + player->actor.courageModifier = courageModifier; +} \ No newline at end of file diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index 3ca67ad3b53..e9c703fe1cc 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -1236,12 +1236,37 @@ void Actor_SetObjectDependency(PlayState* play, Actor* actor) { gSegments[6] = VIRTUAL_TO_PHYSICAL(play->objectCtx.status[actor->objBankIndex].segment); } +void Actor_RefreshLeveledStats(Actor* actor, Player* player) { + if (actor->category == ACTORCAT_PLAYER) { + actor->power = GetActorStat_PlayerPower(actor->level); + actor->courage = GetActorStat_PlayerCourage(actor->level); + gSaveContext.healthCapacity2 = + GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, actor->level); + if (gSaveContext.health > gSaveContext.healthCapacity2) + gSaveContext.health = gSaveContext.healthCapacity2; + gSaveContext.magicUnits = GetPlayerStat_MagicUnits(actor->level); + Leveled_SetPlayerModifiedStats(player); + } else { + actor->power = GetActorStat_Power(actor->level); + actor->powerModifier = 1; + actor->courage = GetActorStat_Courage(actor->level); + actor->courageModifier = 0; + } +} + void Actor_Init(Actor* actor, PlayState* play) { Actor_SetWorldToHome(actor); Actor_SetShapeRotToWorld(actor); Actor_SetFocus(actor, 0.0f); Math_Vec3f_Copy(&actor->prevPos, &actor->world.pos); Actor_SetScale(actor, 0.01f); + actor->level = 0; + actor->exp = 0; + actor->ignoreExpReward = false; + for (u8 i = 0; i < 5; i++) { + actor->floatingNumber[i] = 0; + actor->floatingNumberLife[i] = 0; + } actor->targetMode = 3; actor->minVelocityY = -20.0f; actor->xyzDistToPlayerSq = FLT_MAX; @@ -1257,12 +1282,19 @@ void Actor_Init(Actor* actor, PlayState* play) { actor->init(actor, play); actor->init = NULL; + if (actor->category != ACTORCAT_PLAYER) { + Actor_GetLevelAndExperience(play, actor, 0); + actor->colChkInfo.health = GetActorStat_EnemyMaxHealth(actor->colChkInfo.health, actor->level); + } + GameInteractor_ExecuteOnActorInit(actor); // For enemy health bar we need to know the max health during init - if (actor->category == ACTORCAT_ENEMY) { + if (actor->category != ACTORCAT_PLAYER) { actor->maximumHealth = actor->colChkInfo.health; } + + Actor_RefreshLeveledStats(actor, GET_PLAYER(play)); } } @@ -2201,8 +2233,12 @@ s32 Actor_NotMounted(PlayState* play, Actor* horse) { void func_8002F698(PlayState* play, Actor* actor, f32 arg2, s16 arg3, f32 arg4, u32 arg5, u32 arg6) { Player* player = GET_PLAYER(play); + u16 damage = arg6; + if (actor != NULL && damage != 0) { + damage = Leveled_DamageModify(&player->actor, actor, arg6); + } - player->knockbackDamage = arg6; + player->knockbackDamage = damage; player->knockbackType = arg5; player->knockbackRot = arg3; player->knockbackSpeed = arg2; @@ -2618,12 +2654,19 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { actor->init(actor, play); actor->init = NULL; + if (actor->category != ACTORCAT_PLAYER) { + Actor_GetLevelAndExperience(play, actor, 0); + actor->colChkInfo.health = GetActorStat_EnemyMaxHealth(actor->colChkInfo.health, actor->level); + } + GameInteractor_ExecuteOnActorInit(actor); // For enemy health bar we need to know the max health during init - if (actor->category == ACTORCAT_ENEMY) { + if (actor->category != ACTORCAT_PLAYER) { actor->maximumHealth = actor->colChkInfo.health; } + + Actor_RefreshLeveledStats(actor, GET_PLAYER(play)); } actor = actor->next; } else if (!Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) { @@ -3628,6 +3671,9 @@ Actor* Actor_Find(ActorContext* actorCtx, s32 actorId, s32 actorCategory) { */ void Enemy_StartFinishingBlow(PlayState* play, Actor* actor) { play->actorCtx.freezeFlashTimer = 5; + if (!actor->ignoreExpReward) { + Player_GainExperience(play, actor->exp); + } SoundSource_PlaySfxAtFixedWorldPos(play, &actor->world.pos, 20, NA_SE_EN_LAST_DAMAGE); } @@ -4740,12 +4786,15 @@ u8 func_800355E4(PlayState* play, Collider* collider) { } } -u8 Actor_ApplyDamage(Actor* actor) { +u16 Actor_ApplyDamage(Actor* actor) { if (actor->colChkInfo.damage >= actor->colChkInfo.health) { actor->colChkInfo.health = 0; } else { actor->colChkInfo.health -= actor->colChkInfo.damage; } + if (actor->colChkInfo.damage > 0) { + ActorDamageNumber_New(actor, actor->colChkInfo.damage); + } return actor->colChkInfo.health; } diff --git a/soh/src/code/z_collision_check.c b/soh/src/code/z_collision_check.c index aa466e412eb..0a63113ed04 100644 --- a/soh/src/code/z_collision_check.c +++ b/soh/src/code/z_collision_check.c @@ -3024,12 +3024,25 @@ void CollisionCheck_ApplyDamage(PlayState* play, CollisionCheckContext* colChkCt collider->actor->colChkInfo.damageEffect = tbl->table[i] >> 4 & 0xF; } if (!(collider->acFlags & AC_HARD)) { + Actor* attacker = collider->ac; + + if (collider->actor->category != ACTORCAT_PLAYER) { + damage *= Leveled_GetHealthAttackMultiplier(); + } else { + damage *= (1 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0)); + } + + if (info->acHit->atFlags & AT_TYPE_PLAYER) + attacker = &GET_PLAYER(play)->actor; + + damage = (u16)Leveled_DamageModify(collider->actor, attacker, damage); collider->actor->colChkInfo.damage += damage; } if (CVarGetInteger(CVAR_ENHANCEMENT("IvanCoopModeEnabled"), 0)) { collider->actor->colChkInfo.damage *= GET_PLAYER(play)->ivanDamageMultiplier; } + } /** diff --git a/soh/src/code/z_en_item00.c b/soh/src/code/z_en_item00.c index 0405494666f..0aa276a2d3e 100644 --- a/soh/src/code/z_en_item00.c +++ b/soh/src/code/z_en_item00.c @@ -1564,7 +1564,7 @@ s16 func_8001F404(s16 dropId) { } // clang-format on - if (dropId == ITEM00_HEART && gSaveContext.healthCapacity == gSaveContext.health) { + if (dropId == ITEM00_HEART && gSaveContext.healthCapacity2 == gSaveContext.health) { return ITEM00_RUPEE_GREEN; } diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index 451bdf9ab53..31f01650a39 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -390,12 +390,17 @@ void HealthMeter_Draw(PlayState* play) { f32 temp2; f32 temp3; f32 temp4; + u8 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + if (heartUnits < 4) { + heartUnits = 4; + CVarSetInteger("gLeveled.Difficulty.HeartUnits", 1); + } InterfaceContext* interfaceCtx = &play->interfaceCtx; GraphicsContext* gfxCtx = play->state.gfxCtx; Vtx* sp154 = interfaceCtx->beatingHeartVtx; - s32 curHeartFraction = gSaveContext.health % 0x10; - s16 totalHeartCount = gSaveContext.healthCapacity / 0x10; - s16 fullHeartCount = gSaveContext.health / 0x10; + s32 curHeartFraction = (s32)((f32)gSaveContext.health / heartUnits * 16) % 0x10; + s16 totalHeartCount = gSaveContext.healthCapacity2 / heartUnits; + s16 fullHeartCount = gSaveContext.health / heartUnits; s32 pad2; f32 sp144 = interfaceCtx->unk_22A * 0.1f; s32 curCombineModeSet = 0; @@ -410,7 +415,7 @@ void HealthMeter_Draw(PlayState* play) { OPEN_DISPS(gfxCtx); - if (!(gSaveContext.health % 0x10)) { + if (!(gSaveContext.health % heartUnits)) { fullHeartCount--; } @@ -669,15 +674,9 @@ void HealthMeter_HandleCriticalAlarm(PlayState* play) { u32 HealthMeter_IsCritical(void) { s32 var; - if (gSaveContext.healthCapacity <= 0x50) { - var = 0x10; - } else if (gSaveContext.healthCapacity <= 0xA0) { - var = 0x18; - } else if (gSaveContext.healthCapacity <= 0xF0) { - var = 0x20; - } else { - var = 0x2C; - } + s32 heartValue = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + + var = (s32)(heartValue + (f32)(((gSaveContext.healthCapacity2 / heartValue) - 3) * heartValue) * 0.103f); if (GameInteractor_Should(VB_HEALTH_METER_BE_CRITICAL, var >= gSaveContext.health && gSaveContext.health > 0)) { return true; diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index fc63f96c8a5..6684aa30b95 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -2823,6 +2823,12 @@ void Message_OpenText(PlayState* play, u16 textId) { } } msgCtx->msgLength = font->msgLength = GetEquipNowMessage(font->msgBuf, font->msgOffset, sizeof(font->msgBuf)); + } else if ((CVarGetInteger("gLeveled.Navi.TellEnemyLevel", 1) || + CVarGetInteger("gLeveled.Navi.TellEnemyMaxHP", 1)) && + (textId > 0x0600 && textId < 0x06FF) && play->actorCtx.targetCtx.targetedActor != NULL) { + Message_FindMessage(play, textId); + msgCtx->msgLength = font->msgLength = GetLeveledNaviEnemyInfo( + font->msgBuf, font->msgOffset, sizeof(font->msgBuf), play->actorCtx.targetCtx.targetedActor); } else { if (gSaveContext.language == LANGUAGE_JPN) { Message_FindMessageJPN(play, textId); @@ -4692,12 +4698,19 @@ void Message_Update(PlayState* play) { } if ((s32)(gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000) { gSaveContext.inventory.questItems ^= 0x40000000; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.health += heartUnits; + if (play != NULL) { + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); + } } else { gSaveContext.healthCapacity -= 0x10; - gSaveContext.health -= 0x10; + gSaveContext.health += heartUnits; + if (play != NULL) { + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); + } } } if (msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP_DONE) { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index facbbfd818f..875f4c6503b 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2318,19 +2318,21 @@ u8 Item_Give(PlayState* play, u8 item) { gSaveContext.ship.stats.heartPieces++; return Return_Item(item, MOD_NONE, ITEM_NONE); } else if (item == ITEM_HEART_CONTAINER) { + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.health += heartUnits; } else { gSaveContext.healthCapacity -= 0x10; - gSaveContext.health -= 0x10; + gSaveContext.health -= heartUnits; } gSaveContext.ship.stats.heartContainers++; return Return_Item(item, MOD_NONE, ITEM_NONE); } else if (item == ITEM_HEART) { osSyncPrintf("回復ハート回復ハート回復ハート\n"); // "Recovery Heart" if (play != NULL) { - Health_ChangeBy(play, 0x10); + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + Health_ChangeBy(play, heartUnits); } return Return_Item(item, MOD_NONE, item); } else if (item == ITEM_MAGIC_SMALL) { @@ -2891,14 +2893,21 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { healthChange *= abs(giDefenseModifier); } } + + if (healthChange < 0) { + healthChange = (f32)healthChange * (f32)CVarGetInteger("gLeveled.Difficulty.Player.DamageMultiplier", 4) / 4.0f; + if (healthChange >= 0) + healthChange = -1; + ActorDamageNumber_New(GET_PLAYER(play), -healthChange); + } gSaveContext.health += healthChange; - if (gSaveContext.health > gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; + if (gSaveContext.health > gSaveContext.healthCapacity2) { + gSaveContext.health = gSaveContext.healthCapacity2; } - heartCount = gSaveContext.health % 0x10; + heartCount = gSaveContext.health / CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; healthLevel = heartCount; if (heartCount != 0) { @@ -3035,7 +3044,7 @@ void Inventory_ChangeAmmo(s16 item, s16 ammoChange) { void Magic_Fill(PlayState* play) { if (gSaveContext.isMagicAcquired) { gSaveContext.prevMagicState = gSaveContext.magicState; - gSaveContext.magicFillTarget = (gSaveContext.isDoubleMagicAcquired + 1) * MAGIC_NORMAL_METER; + gSaveContext.magicFillTarget = (gSaveContext.isDoubleMagicAcquired + 1) * gSaveContext.magicUnits; gSaveContext.magicState = MAGIC_STATE_FILL; } } @@ -3191,7 +3200,7 @@ void Interface_UpdateMagicBar(PlayState* play) { switch (gSaveContext.magicState) { case MAGIC_STATE_STEP_CAPACITY: - temp = gSaveContext.magicLevel * MAGIC_NORMAL_METER; + temp = gSaveContext.magicLevel * gSaveContext.magicUnits; if (gSaveContext.magicCapacity != temp) { if (gSaveContext.magicCapacity < temp) { gSaveContext.magicCapacity += 8; @@ -3219,6 +3228,9 @@ void Interface_UpdateMagicBar(PlayState* play) { // "Storage MAGIC_NOW=%d (%d)" osSyncPrintf("蓄電 MAGIC_NOW=%d (%d)\n", gSaveContext.magic, gSaveContext.magicFillTarget); + if (gSaveContext.magicFillTarget > gSaveContext.magicCapacity) { + gSaveContext.magicFillTarget = gSaveContext.magicCapacity; + } if (gSaveContext.magic >= gSaveContext.magicFillTarget) { gSaveContext.magic = gSaveContext.magicFillTarget; gSaveContext.magicState = gSaveContext.prevMagicState; @@ -3458,7 +3470,7 @@ void Interface_DrawMagicBar(PlayState* play) { s16 rMagicBarX; s16 PosX_MidEnd; s16 rMagicFillX; - s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 10); + s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 15); if (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) != ORIGINAL_LOCATION) { magicBarY = CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosY"), 0) + Y_Margins; if (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) == ANCHOR_LEFT) { @@ -3498,7 +3510,8 @@ void Interface_DrawMagicBar(PlayState* play) { } else if (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) == ANCHOR_TO_LIFE_METER) { magicBarY = R_MAGIC_BAR_SMALL_Y - 2 + - magicDrop * (lineLength == 0 ? 0 : (gSaveContext.healthCapacity - 1) / (0x10 * lineLength)) + + magicDrop * (lineLength == 0 ? 0 : (gSaveContext.healthCapacity2 - 1) / + ((CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) * lineLength)) + CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosY"), 0) + getHealthMeterYOffset(); s16 xPushover = CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosX"), 0) + getHealthMeterXOffset() + R_MAGIC_BAR_X - 1; @@ -3509,10 +3522,10 @@ void Interface_DrawMagicBar(PlayState* play) { R_MAGIC_FILL_X - 1; } } else { - if ((gSaveContext.healthCapacity - 1) / 0x10 >= lineLength && lineLength != 0) { + if ((gSaveContext.healthCapacity2 - 1) / (CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) >= lineLength && lineLength != 0) { magicBarY = magicBarY_original_l + - magicDrop * (lineLength == 0 ? 0 : ((gSaveContext.healthCapacity - 1) / (0x10 * lineLength) - 1)); + magicDrop * (lineLength == 0 ? 0 : ((gSaveContext.healthCapacity2 - 1) / ((CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) * lineLength) - 1)); } else { magicBarY = magicBarY_original_s; } @@ -5147,7 +5160,7 @@ void Interface_Draw(PlayState* play) { if (pauseCtx->debugState == 0) { Interface_InitVertices(play); func_8008A994(interfaceCtx); - if (fullUi || gSaveContext.health != gSaveContext.healthCapacity) { + if (fullUi || gSaveContext.health != gSaveContext.healthCapacity2) { HealthMeter_Draw(play); } @@ -5405,6 +5418,49 @@ void Interface_Draw(PlayState* play) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); } + // Draw Damage + Actor* currAct = play->actorCtx.actorLists[ACTORCAT_ENEMY].head; + if (currAct != NULL) { + while (currAct != NULL) { + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + currAct = play->actorCtx.actorLists[ACTORCAT_MISC].head; + if (currAct != NULL) { + while (currAct != NULL) { + if (currAct->id == ACTOR_EN_REEBA) + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + currAct = play->actorCtx.actorLists[ACTORCAT_NPC].head; + if (currAct != NULL) { + while (currAct != NULL) { + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + currAct = play->actorCtx.actorLists[ACTORCAT_BOSS].head; + if (currAct != NULL) { + while (currAct != NULL) { + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + ActorDamageNumber_Draw(play, GET_PLAYER(play)); + + // Draw Experience Gain + + ActorExperienceNumber_Draw(play, GET_PLAYER(play)); + + // Draw Level Up + Actor_LevelUpDraw(play, GET_PLAYER(play)); + // Render enemy health bar after Z-target to leverage set variables if (CVarGetInteger(CVAR_ENHANCEMENT("EnemyHealthBar"), 0)) { Interface_DrawEnemyHealthBar(&play->actorCtx.targetCtx, play); @@ -5509,6 +5565,8 @@ void Interface_Draw(PlayState* play) { gDPPipeSync(OVERLAY_DISP++); + Leveled_Interface_DrawNextLevel(play); // Draw next level + // C-Left Button Icon & Ammo Count if (gSaveContext.equips.buttonItems[1] < 0xF0) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cLeftAlpha); @@ -5941,7 +5999,8 @@ void Interface_Draw(PlayState* play) { case 1: D_8015FFE2 = 20; D_8015FFE0 = 20; - gSaveContext.timerSeconds = gSaveContext.health >> 1; + u8 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + gSaveContext.timerSeconds = (s32)((f32)gSaveContext.health / heartUnits * 8); gSaveContext.timerState = 2; break; case 2: @@ -6621,18 +6680,19 @@ void Interface_Update(PlayState* play) { Map_Update(play); if (gSaveContext.healthAccumulator != 0) { - gSaveContext.healthAccumulator -= 4; - gSaveContext.health += 4; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + gSaveContext.healthAccumulator -= heartUnits >> 2; + gSaveContext.health += heartUnits >> 2; - if ((gSaveContext.health & 0xF) < 4) { + if ((gSaveContext.health % (heartUnits)) < heartUnits >> 2) { Audio_PlaySoundGeneral(NA_SE_SY_HP_RECOVER, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); } osSyncPrintf("now_life=%d max_life=%d\n", gSaveContext.health, gSaveContext.healthCapacity); - if (gSaveContext.health >= gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; + if (gSaveContext.health >= gSaveContext.healthCapacity2) { + gSaveContext.health = gSaveContext.healthCapacity2; osSyncPrintf("S_Private.now_life=%d S_Private.max_life=%d\n", gSaveContext.health, gSaveContext.healthCapacity); gSaveContext.healthAccumulator = 0; @@ -6779,7 +6839,8 @@ void Interface_Update(PlayState* play) { } if (gSaveContext.timerState == 0) { - if (((D_80125A58 == 1) || (D_80125A58 == 2) || (D_80125A58 == 4)) && ((gSaveContext.health >> 1) != 0)) { + u8 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + if (((D_80125A58 == 1) || (D_80125A58 == 2) || (D_80125A58 == 4)) && (((s32)((f32)gSaveContext.health / heartUnits * 8)) != 0)) { gSaveContext.timerState = 1; gSaveContext.timerX[0] = 140; gSaveContext.timerY[0] = 80; diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index c5c600459e7..9d36e7a33cb 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -681,6 +681,7 @@ void Player_SetEquipmentData(PlayState* play, Player* this) { this->currentSwordItemId = B_BTN_ITEM; Player_SetModelGroup(this, Player_ActionToModelGroup(this, this->heldItemAction)); Player_SetBootData(play, this); + Leveled_SetPlayerModifiedStats(this); } } @@ -787,6 +788,57 @@ s32 Player_GetStrength(void) { } } +void Player_GainExperience(PlayState* play, u16 experience) { + Player* player = GET_PLAYER(play); + + if (player == NULL) + return; + + experience = (u16)(CLAMP(round((f32)experience * CVarGetFloat("gLeveled.Difficulty.EXP.Rate", (f32)1.0)), 0, 0xffff)); + + bool levelUp = false; + u8 prevPower = player->actor.power; + u8 prevCourage = player->actor.courage; + u16 prevHealthCapacity = gSaveContext.healthCapacity2; + u16 prevMagicUnits = gSaveContext.magicUnits; + + if (player->actor.level == 99) + return; + + if (gSaveContext.experience < 999999) { + if (experience > 0) + gSaveContext.showNeededExpTimer = 60; + + gSaveContext.experience += experience; + ActorExperienceNumber_New(&player->actor, experience); + if (gSaveContext.experience > 999999) + gSaveContext.experience = 999999; + } + + while (GetActorStat_NextLevelExp(player->actor.level, gSaveContext.experience) <= 0 && player->actor.level < 99) { + player->actor.level += 1; + if (experience > 0) { + levelUp = true; + } + } + + Actor_RefreshLeveledStats(&player->actor, player); + + if (gSaveContext.magicLevel == 0) { + prevMagicUnits = gSaveContext.magicUnits; + } + + if (levelUp) { + gSaveContext.magicCapacity = gSaveContext.magicLevel * gSaveContext.magicUnits; + if (CVarGetInteger("gLeveled.HUD.LevelUp", 1) == 1) { + ActorLevelUp_New(&player->actor, player->actor.power - prevPower, player->actor.courage - prevCourage, gSaveContext.healthCapacity2 - prevHealthCapacity, gSaveContext.magicUnits - prevMagicUnits); + } + if (CVarGetInteger("gLeveled.HUD.LevelUpSound", 1) == 1) { + Audio_PlayFanfare(NA_BGM_ITEM_GET); + } + } +} + u8 Player_GetMask(PlayState* play) { Player* this = GET_PLAYER(play); diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c index 8fc6d1dde00..69928e11044 100644 --- a/soh/src/code/z_sram.c +++ b/soh/src/code/z_sram.c @@ -150,9 +150,8 @@ void Sram_OpenSave() { osSyncPrintf("scene_no = %d\n", gSaveContext.entranceIndex); osSyncPrintf(VT_RST); - if (gSaveContext.health < 0x30) { - gSaveContext.health = - CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity : 0x30; + if (gSaveContext.health < 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) { + gSaveContext.health = CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity2 : 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; } if (gSaveContext.scarecrowLongSongSet) { diff --git a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c index 2e3ad788843..86690fd6a3a 100644 --- a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c +++ b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c @@ -475,7 +475,7 @@ void BgDyYoseizo_HealPlayer_NoReward(BgDyYoseizo* this, PlayState* play) { } if (this->healingTimer == 110) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Magic_Fill(play); this->refillTimer = 200; } @@ -724,7 +724,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { switch (actionIndex) { case FAIRY_UPGRADE_MAGIC: gSaveContext.isMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_NORMAL_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits; Interface_ChangeAlpha(9); break; case FAIRY_UPGRADE_DOUBLE_MAGIC: @@ -732,7 +732,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { gSaveContext.isMagicAcquired = true; } gSaveContext.isDoubleMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_DOUBLE_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits * 2; gSaveContext.magicLevel = 0; Interface_ChangeAlpha(9); break; @@ -743,7 +743,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { } if (!this->healing) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; this->healing = true; if (actionIndex == 2) { Magic_Fill(play); @@ -771,7 +771,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { } this->itemSpawned = true; - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Interface_ChangeAlpha(9); gSaveContext.itemGetInf[1] |= sItemGetFlags[actionIndex]; Item_Give(play, sItemIds[actionIndex]); diff --git a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c index acba457abfe..d491cc0a990 100644 --- a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c +++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c @@ -329,7 +329,8 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { Animation_PlayLoop(&this->skelAnime, &object_kingdodongo_Anim_00F0D8); this->unk_1F8 = 1.0f; BossDodongo_SetupIntroCutscene(this, play); - this->health = 12; + Actor_GetLevelAndExperience(play, &this->actor, 0); + this->health = GetActorStat_EnemyMaxHealth(12, this->actor.level); this->colorFilterMin = 995.0f; this->actor.colChkInfo.mass = MASS_IMMOVABLE; this->colorFilterMax = 1000.0f; @@ -337,6 +338,8 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { this->unk_228 = 9200.0f; Collider_InitJntSph(play, &this->collider); Collider_SetJntSph(play, &this->collider, &this->actor, &sJntSphInit, this->items); + for (u8 i = 0; i < this->collider.count; i++) + this->collider.elements[i].info.toucher.damage <<= 1; // Double fire damage (to 1 heart) if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { // KD is dead // SOH [General] @@ -733,7 +736,9 @@ void BossDodongo_Explode(BossDodongo* this, PlayState* play) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_BOMB_EXPLOSION); Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DAMAGE); func_80033E88(&this->actor, play, 4, 10); - this->health -= 2; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + this->health -= damage; + ActorDamageNumber_New(&this->actor, damage); // make sure not to die from the bomb explosion if (this->health <= 0) { @@ -1509,12 +1514,14 @@ void BossDodongo_UpdateDamage(BossDodongo* this, PlayState* play) { item1 = this->collider.elements[0].info.acHitInfo; if ((this->actionFunc == BossDodongo_Vulnerable) || (this->actionFunc == BossDodongo_LayDown)) { swordDamage = damage = CollisionCheck_GetSwordDamage(item1->toucher.dmgFlags, play); + swordDamage = Leveled_DamageModify(&this->actor, this->collider.base.actor, swordDamage * Leveled_GetHealthAttackMultiplier()); if (damage != 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DAMAGE); BossDodongo_SetupDamaged(this); this->unk_1C0 = 5; this->health -= swordDamage; + ActorDamageNumber_New(&this->actor, swordDamage); } } } diff --git a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c index 249b3afe965..807c4a05018 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c @@ -1295,12 +1295,14 @@ void BossFd_CollisionCheck(BossFd* this, PlayState* play) { if (headCollider->info.bumperFlags & BUMP_HIT) { headCollider->info.bumperFlags &= ~BUMP_HIT; hurtbox = headCollider->info.acHitInfo; - this->actor.colChkInfo.health -= 2; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + if (hurtbox->toucher.dmgFlags & 0x1000) { - this->actor.colChkInfo.health -= 2; - } - if ((s8)this->actor.colChkInfo.health <= 2) { - this->actor.colChkInfo.health = 2; + if (this->actor.colChkInfo.health + 1 >= damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 2; + } } this->work[BFD_DAMAGE_FLASH_TIMER] = 10; this->work[BFD_INVINC_TIMER] = 20; @@ -1486,7 +1488,8 @@ void BossFd_UpdateEffects(BossFd* this, PlayState* play) { diff.z = player->actor.world.pos.z - effect->pos.z; if ((this->timers[3] == 0) && (sqrtf(SQ(diff.x) + SQ(diff.y) + SQ(diff.z)) < 20.0f)) { this->timers[3] = 50; - func_8002F6D4(play, NULL, 5.0f, effect->kbAngle, 0.0f, 0x30); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 0x30); + func_8002F6D4(play, NULL, 5.0f, effect->kbAngle, 0.0f, damage); if (player->bodyIsBurning == false) { for (i2 = 0; i2 < ARRAY_COUNT(player->bodyFlameTimers); i2++) { player->bodyFlameTimers[i2] = Rand_S16Offset(0, 200); diff --git a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c index 0299016b9c5..0097b74927f 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c @@ -207,7 +207,7 @@ void BossFd2_Destroy(Actor* thisx, PlayState* play) { void BossFd2_SetupEmerge(BossFd2* this, PlayState* play) { BossFd* bossFd = (BossFd*)this->actor.parent; s16 temp_rand; - s8 health; + u16 health; osSyncPrintf("UP INIT 1\n"); Animation_PlayOnce(&this->skelAnime, &gHoleVolvagiaEmergeAnim); @@ -221,11 +221,11 @@ void BossFd2_SetupEmerge(BossFd2* this, PlayState* play) { this->timers[0] = 10; if (bossFd != NULL) { health = bossFd->actor.colChkInfo.health; - if (health >= 18) { + if (health >= GetActorStat_EnemyMaxHealth(18, this->actor.level)) { this->work[FD2_FAKEOUT_COUNT] = 0; - } else if (health >= 12) { + } else if (health >= GetActorStat_EnemyMaxHealth(12, this->actor.level)) { this->work[FD2_FAKEOUT_COUNT] = 1; - } else if (health >= 6) { + } else if (health >= GetActorStat_EnemyMaxHealth(6, this->actor.level)) { this->work[FD2_FAKEOUT_COUNT] = 2; } else { this->work[FD2_FAKEOUT_COUNT] = 3; @@ -234,7 +234,7 @@ void BossFd2_SetupEmerge(BossFd2* this, PlayState* play) { } void BossFd2_Emerge(BossFd2* this, PlayState* play) { - s8 health; + u16 health; BossFd* bossFd = (BossFd*)this->actor.parent; Player* player = GET_PLAYER(play); s16 i; @@ -258,13 +258,13 @@ void BossFd2_Emerge(BossFd2* this, PlayState* play) { this->work[FD2_HOLE_COUNTER]++; this->actor.world.pos.y = -200.0f; health = bossFd->actor.colChkInfo.health; - if (health == 24) { + if (health == GetActorStat_EnemyMaxHealth(24, this->actor.level)) { holeTime = 30; - } else if (health >= 18) { + } else if (health >= GetActorStat_EnemyMaxHealth(18, this->actor.level)) { holeTime = 25; - } else if (health >= 12) { + } else if (health >= GetActorStat_EnemyMaxHealth(12, this->actor.level)) { holeTime = 20; - } else if (health >= 6) { + } else if (health >= GetActorStat_EnemyMaxHealth(6, this->actor.level)) { holeTime = 10; } else { holeTime = 5; @@ -317,20 +317,20 @@ void BossFd2_Emerge(BossFd2* this, PlayState* play) { void BossFd2_SetupIdle(BossFd2* this, PlayState* play) { BossFd* bossFd = (BossFd*)this->actor.parent; - s8 health; + u16 health; s16 idleTime; osSyncPrintf("UP INIT 1\n"); Animation_PlayLoop(&this->skelAnime, &gHoleVolvagiaTurnAnim); this->actionFunc = BossFd2_Idle; health = bossFd->actor.colChkInfo.health; - if (health == 24) { + if (health == GetActorStat_EnemyMaxHealth(24, this->actor.level)) { idleTime = 50; - } else if (health >= 18) { + } else if (health >= GetActorStat_EnemyMaxHealth(18, this->actor.level)) { idleTime = 40; - } else if (health >= 12) { + } else if (health >= GetActorStat_EnemyMaxHealth(12, this->actor.level)) { idleTime = 40; - } else if (health >= 6) { + } else if (health >= GetActorStat_EnemyMaxHealth(6, this->actor.level)) { idleTime = 30; } else { idleTime = 20; @@ -384,7 +384,8 @@ void BossFd2_Burrow(BossFd2* this, PlayState* play) { } else { Math_ApproachF(&this->actor.world.pos.y, -100.0f, 1.0f, 10.0f); if (this->timers[0] == 0) { - if ((this->work[FD2_HOLE_COUNTER] >= 3) && ((s8)bossFd->actor.colChkInfo.health < 24)) { + if ((this->work[FD2_HOLE_COUNTER] >= 3) && + (bossFd->actor.colChkInfo.health < GetActorStat_EnemyMaxHealth(24, this->actor.level))) { this->work[FD2_HOLE_COUNTER] = 0; this->actionFunc = BossFd2_Wait; bossFd->handoffSignal = FD2_SIGNAL_FLY; @@ -844,8 +845,12 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { hurtbox = this->collider.elements[0].info.acHitInfo; if (!bossFd->faceExposed) { if (hurtbox->toucher.dmgFlags & 0x40000040) { - bossFd->actor.colChkInfo.health -= 2; - if ((s8)bossFd->actor.colChkInfo.health <= 2) { + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + + if (bossFd->actor.colChkInfo.health + 1 > damage) { + bossFd->actor.colChkInfo.health -= damage; + } else { bossFd->actor.colChkInfo.health = 1; } bossFd->faceExposed = true; @@ -872,7 +877,7 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { } } else { u8 canKill = false; - u8 damage; + u16 damage; if ((damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags, play)) == 0) { damage = (hurtbox->toucher.dmgFlags & 0x00001000) ? 4 : 2; @@ -882,15 +887,22 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { if (hurtbox->toucher.dmgFlags & 0x80) { damage = 0; } - if (((s8)bossFd->actor.colChkInfo.health > 2) || canKill) { - bossFd->actor.colChkInfo.health -= damage; + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); + + if ((bossFd->actor.colChkInfo.health > 2) || canKill) { + if (bossFd->actor.colChkInfo.health >= damage) { + ActorDamageNumber_New(&this->actor, damage); + bossFd->actor.colChkInfo.health -= damage; + } else { + bossFd->actor.colChkInfo.health = 0; + } osSyncPrintf(VT_FGCOL(GREEN)); osSyncPrintf("damage %d\n", damage); } osSyncPrintf(VT_RST); osSyncPrintf("hp %d\n", bossFd->actor.colChkInfo.health); - if ((s8)bossFd->actor.colChkInfo.health <= 0) { + if (bossFd->actor.colChkInfo.health <= 0) { bossFd->actor.colChkInfo.health = 0; BossFd2_SetupDeath(this, play); this->work[FD2_DAMAGE_FLASH_TIMER] = 10; diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c index f845f323a20..4cfc1ec2d3a 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c @@ -584,7 +584,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) { this->unk_198 = 2; this->timers[2] = 110; if (GameInteractor_Should(VB_GANON_HEAL_BEFORE_FIGHT, true)) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } Audio_QueueSeqCmd(NA_BGM_STOP); } else { @@ -800,7 +800,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) { } if (this->csTimer == 25) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } if (this->csTimer == 100) { @@ -1218,7 +1218,7 @@ void BossGanon_SetupTowerCutscene(BossGanon* this, PlayState* play) { this->csState = 100; this->unk_198 = 1; gSaveContext.magic = gSaveContext.magicCapacity; - gSaveContext.health = gSaveContext.healthCapacity; + gSaveContext.health = gSaveContext.healthCapacity2; } else { this->actionFunc = BossGanon_SetupTowerCutscene; } @@ -2277,7 +2277,7 @@ void BossGanon_Wait(BossGanon* this, PlayState* play) { } else if ((this->timers[0] == 0) && !(player->stateFlags1 & PLAYER_STATE1_HANGING_OFF_LEDGE)) { this->timers[0] = (s16)Rand_ZeroFloat(30.0f) + 30; - if ((s8)this->actor.colChkInfo.health >= 20) { + if (this->actor.colChkInfo.health >= GetActorStat_EnemyMaxHealth(20, this->actor.level)) { BossGanon_SetupChargeLightBall(this, play); } else if (Rand_ZeroOne() >= 0.5f) { if ((Rand_ZeroOne() >= 0.5f) || (this->actor.xzDistToPlayer > 350.0f)) { @@ -2769,7 +2769,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { } else if ((this->actionFunc == BossGanon_Vulnerable) && (this->unk_1C2 >= 3)) { if (!(acHitInfo->toucher.dmgFlags & 0x80)) { u8 hitWithSword = false; - u8 damage; + u16 damage; Vec3f sp50; u32 flags; @@ -2789,8 +2789,21 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { hitWithSword = true; } - if (((s8)this->actor.colChkInfo.health >= 3) || hitWithSword) { - this->actor.colChkInfo.health -= damage; + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + + if (hitWithSword) { + if (this->actor.colChkInfo.health >= damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } + } else { + if (this->actor.colChkInfo.health > damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 1; + } } for (i = 0; i < ARRAY_COUNT(sBossGanonCape->strands); i++) { @@ -2800,7 +2813,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { } } - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { BossGanon_SetupDeathCutscene(this, play); Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DEAD); Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DD_THUNDER); diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c index 1799dafd0c4..a32ec8d7292 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c @@ -170,7 +170,7 @@ void BossGanon2_Init(Actor* thisx, PlayState* play) { } this->actor.colChkInfo.mass = MASS_IMMOVABLE; - this->actor.colChkInfo.health = 30; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(30, this->actor.level); Collider_InitJntSph(play, &this->unk_424); Collider_SetJntSph(play, &this->unk_424, &this->actor, &sJntSphInit1, this->unk_464); Collider_InitJntSph(play, &this->unk_444); @@ -1464,7 +1464,7 @@ void func_80900890(BossGanon2* this, PlayState* play) { if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { func_808FFDB0(this, play); if (this->unk_334 == 0) { - this->actor.colChkInfo.health = 25; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(25, this->actor.level); } this->unk_336 = 1; } @@ -1931,10 +1931,10 @@ void func_80902348(BossGanon2* this, PlayState* play) { } void func_80902524(BossGanon2* this, PlayState* play) { - s8 temp_v0_4; + u16 temp_v0_4; ColliderInfo* acHitInfo; s16 i; - u8 phi_v1_2; + u16 phi_v1_2; osSyncPrintf("this->no_hit_time %d\n", this->unk_316); if (this->unk_316 != 0 || ((this->unk_334 == 0) && (this->actionFunc == func_80900890))) { @@ -1959,9 +1959,15 @@ void func_80902524(BossGanon2* this, PlayState* play) { this->unk_342 = 5; Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_DAMAGE); Audio_StopSfxById(NA_SE_EN_MGANON_UNARI); - this->actor.colChkInfo.health -= 2; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + if (this->actor.colChkInfo.health >= damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 1; + } + ActorDamageNumber_New(&this->actor, damage); temp_v0_4 = this->actor.colChkInfo.health; - if (temp_v0_4 < 0x15 && this->unk_334 == 0) { + if (temp_v0_4 < GetActorStat_EnemyMaxHealth(21, this->actor.level) && this->unk_334 == 0) { func_80900818(this, play); } else { if (temp_v0_4 <= 0) { @@ -1993,11 +1999,20 @@ void func_80902524(BossGanon2* this, PlayState* play) { phi_v1_2 = 2; } } - this->actor.colChkInfo.health -= phi_v1_2; + u8 baseDamage = phi_v1_2; + + phi_v1_2 = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, phi_v1_2 * Leveled_GetHealthAttackMultiplier()); + if (phi_v1_2 <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= phi_v1_2; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, phi_v1_2); + temp_v0_4 = this->actor.colChkInfo.health; - if ((temp_v0_4 < 0x15) && (this->unk_334 == 0)) { + if ((temp_v0_4 < GetActorStat_EnemyMaxHealth(21, this->actor.level)) && (this->unk_334 == 0)) { func_80900818(this, play); - } else if ((temp_v0_4 <= 0) && (phi_v1_2 >= 2)) { + } else if ((temp_v0_4 <= 0) && (baseDamage >= 2)) { func_80901020(this, play); } else { if (temp_v0_4 <= 0) { diff --git a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c index 8f74d05a851..a18ef5409cc 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c @@ -458,7 +458,8 @@ void BossGanondrof_Neutral(BossGanondrof* this, PlayState* play) { if (this->timers[0] == 0) { this->timers[0] = (s16)(Rand_ZeroOne() * 64.0f) + 30; rand01 = Rand_ZeroOne(); - if (thisx->colChkInfo.health < 5) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(5, this->actor.level); + if (thisx->colChkInfo.health < healthCheck) { if (rand01 < 0.25f) { BossGanondrof_SetupThrow(this, play); } else if (rand01 >= 0.8f) { @@ -629,8 +630,10 @@ void BossGanondrof_Throw(BossGanondrof* this, PlayState* play) { if (Animation_OnFrame(&this->skelAnime, this->work[GND_THROW_FRAME])) { EnfHG* horseTemp = (EnfHG*)this->actor.child; - Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG_FIRE, this->spearTip.x, this->spearTip.y, + Actor* fireTemp = Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG_FIRE, this->spearTip.x, this->spearTip.y, this->spearTip.z, this->work[GND_ACTION_STATE], 0, 0, FHGFIRE_ENERGY_BALL); + fireTemp->level = this->actor.level; + Actor_RefreshLeveledStats(fireTemp, GET_PLAYER(play)); this->actor.child = &horseTemp->actor; } @@ -1199,7 +1202,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { this->colliderBody.base.acFlags &= ~AC_HIT; } else { acHit = this->colliderBody.base.acFlags & AC_HIT; - if ((acHit && ((s8)this->actor.colChkInfo.health > 0)) || (this->returnCount != 0)) { + if ((acHit && (this->actor.colChkInfo.health > 0)) || (this->returnCount != 0)) { if (acHit) { this->colliderBody.base.acFlags &= ~AC_HIT; hurtbox = this->colliderBody.info.acHitInfo; @@ -1210,7 +1213,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { osSyncPrintf("hit != 0 \n"); } else if (this->actionFunc != BossGanondrof_Charge) { if (this->returnCount == 0) { - u8 dmg; + u16 dmg; u8 canKill = false; s32 dmgFlags = hurtbox->toucher.dmgFlags; @@ -1219,11 +1222,21 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { } dmg = CollisionCheck_GetSwordDamage(dmgFlags, play); (dmg == 0) ? (dmg = 2) : (canKill = true); - if (((s8)this->actor.colChkInfo.health > 2) || canKill) { - this->actor.colChkInfo.health -= dmg; + + dmg = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, dmg * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, dmg); + + if ((this->actor.colChkInfo.health > 2) || canKill) { + if (dmg < this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= dmg; + } else if (this->actor.colChkInfo.health > 2) { + this->actor.colChkInfo.health = 1; + } else { + this->actor.colChkInfo.health = 0; + } } - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { BossGanondrof_SetupDeath(this, play); Enemy_StartFinishingBlow(play, &this->actor); GameInteractor_ExecuteOnBossDefeat(&this->actor); @@ -1242,7 +1255,15 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { } } else if (acHit && (hurtbox->toucher.dmgFlags & 0x0001F8A4)) { this->work[GND_INVINC_TIMER] = 10; - this->actor.colChkInfo.health -= 2; + + u16 dmg = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, dmg); + if (dmg < this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= dmg; + } else { + this->actor.colChkInfo.health = 0; + } + horse->hitTimer = 20; Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_DAMAGE); } diff --git a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c index 503e4147247..3caa68497a4 100644 --- a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c +++ b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c @@ -1837,9 +1837,16 @@ void BossGoma_UpdateHit(BossGoma* this, PlayState* play) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DAM2); } else if (this->actionFunc == BossGoma_FloorStunned && (damage = CollisionCheck_GetSwordDamage(acHitInfo->toucher.dmgFlags, play)) != 0) { - this->actor.colChkInfo.health -= damage; - if ((s8)this->actor.colChkInfo.health > 0) { + damage = Leveled_DamageModify(&this->actor, this->collider.elements[0].info.acHit->actor, damage * Leveled_GetHealthAttackMultiplier()); + if (damage <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, damage); + + if (this->actor.colChkInfo.health > 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DAM1); BossGoma_SetupFloorDamaged(this); EffectSsSibuki_SpawnBurst(play, &this->actor.focus.pos); diff --git a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c index b80c39f3b27..89207705990 100644 --- a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c +++ b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c @@ -1783,7 +1783,7 @@ void BossMo_CoreCollisionCheck(BossMo* this, PlayState* play) { // "hit 2 !!" osSyncPrintf("Core_Damage_check 当り 2 !!\n"); if ((this->work[MO_TENT_ACTION_STATE] != MO_CORE_UNDERWATER) && (this->work[MO_TENT_INVINC_TIMER] == 0)) { - u8 damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags, play); + u16 damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags, play); if ((damage != 0) && (this->work[MO_TENT_ACTION_STATE] < MO_CORE_ATTACK)) { // "sword hit !!" @@ -1791,14 +1791,21 @@ void BossMo_CoreCollisionCheck(BossMo* this, PlayState* play) { this->work[MO_TENT_ACTION_STATE] = MO_CORE_STUNNED; this->timers[0] = 25; + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + this->actor.speedXZ = 15.0f; this->actor.world.rot.y = this->actor.yawTowardsPlayer + 0x8000; this->work[MO_CORE_DMG_FLASH_TIMER] = 15; Audio_PlayActorSound2(&this->actor, NA_SE_EN_MOFER_CORE_DAMAGE); - this->actor.colChkInfo.health -= damage; + if (damage < this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } this->hitCount++; - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { if (((sMorphaTent1->csCamera == 0) && (sMorphaTent2 == NULL)) || ((sMorphaTent1->csCamera == 0) && (sMorphaTent2 != NULL) && (sMorphaTent2->csCamera == 0))) { Enemy_StartFinishingBlow(play, &this->actor); diff --git a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c index 6e1db9049a4..07da8364bd1 100644 --- a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c +++ b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c @@ -1895,8 +1895,9 @@ void BossSst_HandCrush(BossSst* this, PlayState* play) { } else { Player_PlaySfx(&player->actor, NA_SE_VO_LI_DAMAGE_S); } - - play->damagePlayer(play, -8); + + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); } if (Animation_OnFrame(&this->skelAnime, 0.0f)) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_CATCH); diff --git a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c index 1fee8de966f..cb7fb5af9d1 100644 --- a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c +++ b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c @@ -31,7 +31,7 @@ void BossTw_Update(Actor* thisx, PlayState* play); void BossTw_Draw(Actor* thisx, PlayState* play); void BossTw_Reset(void); -void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 arg2); +void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u16 arg2); void BossTw_TwinrovaSetupFly(BossTw* this, PlayState* play); void BossTw_DrawEffects(PlayState* play); void BossTw_TwinrovaLaugh(BossTw* this, PlayState* play); @@ -3091,7 +3091,7 @@ void BossTw_TwinrovaUpdate(Actor* thisx, PlayState* play2) { if (info->toucher.dmgFlags & (DMG_SLINGSHOT | DMG_ARROW)) {} } } else if (this->collider.base.acFlags & AC_HIT) { - u8 damage; + u16 damage; u8 swordDamage; ColliderInfo* info = this->collider.info.acHitInfo; @@ -3105,8 +3105,11 @@ void BossTw_TwinrovaUpdate(Actor* thisx, PlayState* play2) { swordDamage = true; } + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + if (!(info->toucher.dmgFlags & DMG_HOOKSHOT)) { - if (((s8)this->actor.colChkInfo.health < 3) && !swordDamage) { + if ((this->actor.colChkInfo.health < GetActorStat_EnemyMaxHealth(3, this->actor.level)) && !swordDamage) { damage = 0; } @@ -5141,7 +5144,7 @@ void BossTw_TwinrovaChargeBlast(BossTw* this, PlayState* play) { Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000); if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { - if ((s8)this->actor.colChkInfo.health < 10) { + if (this->actor.colChkInfo.health < GetActorStat_EnemyMaxHealth(10, this->actor.level)) { sTwinrovaBlastType = Rand_ZeroFloat(1.99f); } else { if (++sFixedBlatSeq >= 4) { @@ -5241,7 +5244,7 @@ void BossTw_TwinrovaDoneBlastShoot(BossTw* this, PlayState* play) { Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000); } -void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) { +void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u16 damage) { if (this->actionFunc != BossTw_TwinrovaStun) { Animation_MorphToPlayOnce(&this->skelAnime, &gTwinrovaChargedAttackHitAnim, -15.0f); this->timers[0] = 150; @@ -5255,11 +5258,13 @@ void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) { this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&gTwinrovaDamageAnim); this->csState1 = 1; - if ((s8)(this->actor.colChkInfo.health -= damage) < 0) { + if (damage >= this->actor.colChkInfo.health) { this->actor.colChkInfo.health = 0; + } else { + this->actor.colChkInfo.health -= damage; } - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { BossTw_TwinrovaSetupDeathCS(this, play); Enemy_StartFinishingBlow(play, &this->actor); Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DEAD); diff --git a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c index 3e8f05776e6..7bde37a38ce 100644 --- a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c +++ b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c @@ -403,7 +403,7 @@ static s16 sDoorState; static u8 sPhase3StopMoving; static Vec3s sZapperRot; static u16 sPhase2Timer; -static s8 sPhase4HP; +static u16 sPhase4HP; void BossVa_SetupAction(BossVa* this, BossVaActionFunc func) { this->actionFunc = func; @@ -1362,7 +1362,7 @@ void BossVa_SetupBodyPhase4(BossVa* this, PlayState* play) { this->actor.world.rot.y = this->actor.yawTowardsPlayer; this->timer2 = (s16)(Rand_ZeroOne() * 150.0f) + 300; sBodyState = 1; - sPhase4HP = 4; + sPhase4HP = GetActorStat_EnemyMaxHealth(4, this->actor.level); if (this->actor.shape.yOffset != 0.0f) { this->timer = -30; } @@ -1400,11 +1400,21 @@ void BossVa_BodyPhase4(BossVa* this, PlayState* play) { this->actor.world.rot.y = this->actor.yawTowardsPlayer; Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_DAMAGE); Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 12); - sPhase4HP -= this->actor.colChkInfo.damage; + + u16 dmg = this->actor.colChkInfo.damage; + + if (dmg > sPhase4HP) { + sPhase4HP = 0; + } else { + sPhase4HP -= dmg; + } + + ActorDamageNumber_New(&this->actor, dmg); + if (sPhase4HP <= 0) { this->timer = 0; sFightPhase++; - sPhase4HP += 3; + sPhase4HP += GetActorStat_EnemyMaxHealth(3, this->actor.level); if (sFightPhase >= PHASE_DEATH) { BossVa_SetupBodyDeath(this, play); Enemy_StartFinishingBlow(play, &this->actor); diff --git a/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c index 4f16a06083d..2763cd168bd 100644 --- a/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c +++ b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c @@ -408,6 +408,7 @@ void func_809B75A0(EnBa* this, PlayState* play2) { Matrix_MultVec3f(&sp74, &this->unk_158[i + 1]); } this->unk_31A = 15; + Player_GainExperience(play, this->actor.exp); EnBa_SetupAction(this, EnBa_Die); } @@ -453,7 +454,14 @@ void EnBa_Update(Actor* thisx, PlayState* play) { if ((this->actor.params < EN_BA_DEAD_BLOB) && (this->collider.base.acFlags & 2)) { this->collider.base.acFlags &= ~2; - this->actor.colChkInfo.health--; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + + if (damage > this->actor.colChkInfo.health) { + this->actor.colChkInfo.health = 0; + } else { + this->actor.colChkInfo.health -= damage; + } if (this->actor.colChkInfo.health == 0) { func_809B75A0(this, play); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); diff --git a/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c index 424f94a1c2d..c75d6061445 100644 --- a/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c +++ b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c @@ -533,7 +533,9 @@ void EnBb_Damage(EnBb* this, PlayState* play) { Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); if (this->actor.speedXZ == 0.0f) { this->actor.shape.yOffset = 200.0f; - EnBb_SetupDown(this); + if (this->actor.params > ENBB_GREEN) { + EnBb_SetupDown(this); + } } } @@ -1187,7 +1189,7 @@ void EnBb_CollisionCheck(EnBb* this, PlayState* play) { if ((this->action != BB_DOWN) || (this->timer < 190)) { Actor_ApplyDamage(&this->actor); } - if ((this->action != BB_DOWN) && (this->actor.params != ENBB_WHITE)) { + if ((this->action != BB_DOWN) && (this->actor.params != ENBB_WHITE) && (this->actor.params > ENBB_GREEN)) { EnBb_SetupDown(this); } } else { @@ -1204,13 +1206,13 @@ void EnBb_CollisionCheck(EnBb* this, PlayState* play) { EnBb_KillFlameTrail(this); } EnBb_SetupDeath(this, play); + Player_GainExperience(play, this->actor.exp); //! @bug //! Because Din's Fire kills the bubble in a single hit, Actor_SetColorFilter is never called and //! colorFilterParams is never set. And because Din's Fire halts updating during its cutscene, //! EnBb_Death doesn't kill the bubble on the next frame like it should. This combines with //! the bug in EnBb_Draw below to crash the game. - } else if ((this->actor.params == ENBB_WHITE) && - ((this->action == BB_WHITE) || (this->action == BB_STUNNED))) { + } else if ((this->actor.params == ENBB_WHITE) && ((this->action == BB_WHITE) || (this->action == BB_STUNNED)) || (this->actor.params <= ENBB_GREEN)) { Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0xC); this->actor.speedXZ = -8.0f; this->maxSpeed = 0.0f; diff --git a/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c b/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c index d06a234baa1..e85e2dbbfeb 100644 --- a/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c +++ b/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c @@ -181,7 +181,8 @@ void func_809BC598(EnBdfire* this, PlayState* play) { player->bodyFlameTimers[i] = Rand_S16Offset(0, 200); } player->bodyIsBurning = true; - func_8002F6D4(play, &this->actor, 20.0f, this->actor.world.rot.y, 0.0f, 8); + // Buffed damage from 8 to 16. + func_8002F6D4(play, &this->actor, 20.0f, this->actor.world.rot.y, 0.0f, 16); osSyncPrintf("POWER\n"); } } diff --git a/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c index 368bf865b25..d5f947971b2 100644 --- a/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c +++ b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c @@ -178,6 +178,7 @@ void EnBigokuta_Init(Actor* thisx, PlayState* play) { } CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, sColChkInfoInit); + Actor_GetLevelAndExperience(play, &this->actor, ACTOR_EN_BIGOKUTA); this->unk_194 = 1; diff --git a/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c index d8ba3877386..01756d1c7f3 100644 --- a/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c +++ b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c @@ -565,6 +565,10 @@ void EnBili_UpdateDamage(EnBili* this, PlayState* play) { this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; } + if (this->actor.colChkInfo.health > 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BARI_DAMAGE); + } + damageEffect = this->actor.colChkInfo.damageEffect; if (damageEffect == BIRI_DMGEFF_DEKUNUT) { @@ -580,7 +584,10 @@ void EnBili_UpdateDamage(EnBili* this, PlayState* play) { } EnBili_SetupDischargeLightning(this); } else { - EnBili_SetupBurnt(this); + if (this->actor.colChkInfo.health == 0) { + this->actor.params = EN_BILI_TYPE_DYING; + EnBili_SetupBurnt(this); + } } } else if (damageEffect == BIRI_DMGEFF_FIRE) { EnBili_SetupBurnt(this); @@ -590,7 +597,10 @@ void EnBili_UpdateDamage(EnBili* this, PlayState* play) { } else if (damageEffect == BIRI_DMGEFF_SLINGSHOT) { EnBili_SetupRecoil(this); } else { - EnBili_SetupBurnt(this); + if (this->actor.colChkInfo.health == 0) { + this->actor.params = EN_BILI_TYPE_DYING; + EnBili_SetupBurnt(this); + } } if (this->collider.info.acHitInfo->toucher.dmgFlags & 0x1F820) { // DMG_ARROW diff --git a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c index 712a9008d89..561c8f97dc2 100644 --- a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c +++ b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c @@ -206,7 +206,7 @@ void EnBomBowlPit_Reset(EnBomBowlPit* this, PlayState* play) { // "Normal termination"/"completion" osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST); if (this->getItemId == GI_HEART_PIECE) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; // "Ah recovery!" (?) osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ あぁ回復! ☆☆☆☆☆ \n" VT_RST); } diff --git a/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c index 6717b9788f8..4029ae466e6 100644 --- a/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c +++ b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c @@ -116,7 +116,7 @@ u32 func_809CBCEC(EnBubble* this) { } void EnBubble_DamagePlayer(EnBubble* this, PlayState* play) { - s32 damage = -this->colliderSphere.elements[0].info.toucher.damage; + s32 damage = -Leveled_DamageModify(&GET_PLAYER(play)->actor, &this->actor, this->colliderSphere.elements[0].info.toucher.damage); play->damagePlayer(play, damage); func_8002F7A0(play, &this->actor, 6.0f, this->actor.yawTowardsPlayer, 6.0f); diff --git a/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c index 38d3a9bf11c..1feb1aadd8f 100644 --- a/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c +++ b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c @@ -675,6 +675,7 @@ void func_809D0424(EnBw* this, PlayState* play) { this->unk_230 = 1; } Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); + Player_GainExperience(play, this->actor.exp); func_809D00F4(this); } } @@ -718,6 +719,7 @@ void func_809D0584(EnBw* this, PlayState* play) { this->unk_230 = 1; } Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); + Player_GainExperience(play, this->actor.exp); func_809D00F4(this); } } else if ((this->unk_220 != 1) && (this->unk_220 != 6)) { diff --git a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c index 0b4f3bf0a55..3bc12bddfb1 100644 --- a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c +++ b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c @@ -372,10 +372,19 @@ void EnClearTag_Update(Actor* thisx, PlayState* play2) { this->acceleration.z = Rand_CenteredFloat(15.0f); Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_THUNDER_GND); - this->actor.colChkInfo.health--; - if ((s8)this->actor.colChkInfo.health <= 0) { + + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, Leveled_GetHealthAttackMultiplier()); + if (damage <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, damage); + + if (this->actor.colChkInfo.health <= 0) { this->state = CLEAR_TAG_STATE_CRASHING; this->actor.velocity.y = 0.0f; + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); goto state_crashing; } diff --git a/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c index 7ab5fbd8410..99008b32edd 100644 --- a/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c +++ b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c @@ -413,7 +413,7 @@ void EnCrow_Respawn(EnCrow* this, PlayState* play) { if (Math_StepToF(&this->actor.scale.x, target, target * 0.1f)) { this->actor.flags |= ACTOR_FLAG_ATTENTION_ENABLED; this->actor.flags &= ~ACTOR_FLAG_UPDATE_CULLING_DISABLED; - this->actor.colChkInfo.health = 1; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(1, this->actor.level); EnCrow_SetupFlyIdle(this); } this->actor.scale.z = this->actor.scale.y = this->actor.scale.x; @@ -428,10 +428,17 @@ void EnCrow_UpdateDamage(EnCrow* this, PlayState* play) { if (this->actor.colChkInfo.damageEffect == 1) { // Deku Nuts EnCrow_SetupTurnAway(this); } else { - Actor_ApplyDamage(&this->actor); - this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; - Enemy_StartFinishingBlow(play, &this->actor); - EnCrow_SetupDamaged(this, play); + if (Actor_ApplyDamage(&this->actor) == 0) { + this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; + Enemy_StartFinishingBlow(play, &this->actor); + EnCrow_SetupDamaged(this, play); + } else { + EnCrow_SetupFlyIdle(this); + this->timer = (s16)Rand_ZeroFloat(25) + 30; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_KAICHO_DAMAGE); + this->actor.flags |= ACTOR_FLAG_UPDATE_CULLING_DISABLED; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 6); + } } } } diff --git a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c index b84103a4a0b..f42176016ec 100644 --- a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c +++ b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c @@ -1047,6 +1047,7 @@ void EnDekubaba_UpdateDamage(EnDekubaba* this, PlayState* play) { ((this->actor.colChkInfo.damageEffect != DEKUBABA_DMGEFF_NONE) || (this->actor.colChkInfo.damage != 0))) { phi_s0 = this->actor.colChkInfo.health - this->actor.colChkInfo.damage; + ActorDamageNumber_New(&this->actor, this->actor.colChkInfo.damage); if (this->actionFunc != EnDekubaba_StunnedVertical) { if ((this->actor.colChkInfo.damageEffect == DEKUBABA_DMGEFF_BOOMERANG) || diff --git a/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c index ac2b5f7ee11..9acf20c5383 100644 --- a/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c +++ b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c @@ -407,7 +407,11 @@ void EnDekunuts_Gasp(EnDekunuts* this, PlayState* play) { void EnDekunuts_BeDamaged(EnDekunuts* this, PlayState* play) { Math_StepToF(&this->actor.speedXZ, 0.0f, 1.0f); if (SkelAnime_Update(&this->skelAnime)) { - EnDekunuts_SetupDie(this); + if (this->actor.colChkInfo.health == 0) { + EnDekunuts_SetupDie(this); + } else { + EnDekunuts_SetupRun(this); + } } } diff --git a/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c index 5f09f29875e..685fe347998 100644 --- a/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c +++ b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c @@ -493,9 +493,10 @@ void EnDh_CollisionCheck(EnDh* this, PlayState* play) { EnDh_SetupDeath(this); Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); } else { - if (((lastHealth >= 15) && (this->actor.colChkInfo.health < 15)) || - ((lastHealth >= 9) && (this->actor.colChkInfo.health < 9)) || - ((lastHealth >= 3) && (this->actor.colChkInfo.health < 3))) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(3, this->actor.level); + if (((lastHealth >= healthCheck * 5) && (this->actor.colChkInfo.health < healthCheck * 5)) || + ((lastHealth >= healthCheck * 3) && (this->actor.colChkInfo.health < healthCheck * 3)) || + ((lastHealth >= healthCheck) && (this->actor.colChkInfo.health < healthCheck))) { this->retreat++; } diff --git a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c index 07a244cd3cc..c336c8b8f06 100644 --- a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c +++ b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c @@ -665,6 +665,7 @@ void EnDodongo_SetupDeath(EnDodongo* this, PlayState* play) { Animation_MorphToPlayOnce(&this->skelAnime, &gDodongoDieAnim, -8.0f); this->timer = 0; Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_J_DEAD); + Player_GainExperience(play, this->actor.exp); this->actionState = DODONGO_DEATH; this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; this->actor.speedXZ = 0.0f; diff --git a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c index 96a9297b969..1632ffd3b72 100644 --- a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c +++ b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c @@ -636,7 +636,7 @@ void func_80A0329C(EnElf* this, PlayState* play) { if ((heightDiff > 0.0f) && (heightDiff < 60.0f)) { if (!func_80A01F90(&this->actor.world.pos, &refActor->actor.world.pos, 10.0f)) { if (GameInteractor_Should(VB_FAIRY_HEAL, true, this)) { - Health_ChangeBy(play, 128); + Health_ChangeBy(play, (CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) << 3); } if (this->fairyFlags & FAIRY_FLAG_BIG) { Magic_Fill(play); diff --git a/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c index ced99da23e2..9333bb5dbd0 100644 --- a/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c +++ b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c @@ -630,20 +630,18 @@ void EnFirefly_UpdateDamage(EnFirefly* this, PlayState* play) { this->collider.base.acFlags &= ~AC_HIT; Actor_SetDropFlag(&this->actor, &this->collider.elements[0].info, 1); - if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { - if (Actor_ApplyDamage(&this->actor) == 0) { - Enemy_StartFinishingBlow(play, &this->actor); - this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; - } + if ((this->actor.colChkInfo.damageEffect != 0 || this->actor.colChkInfo.damage != 0) && + ((this->actor.colorFilterTimer == 0) || ((this->actor.colorFilterParams & 0x4000) == 0))) { damageEffect = this->actor.colChkInfo.damageEffect; if (damageEffect == 2) { // Din's Fire if (this->actor.params == KEESE_ICE_FLY) { - this->actor.colChkInfo.health = 0; - Enemy_StartFinishingBlow(play, &this->actor); - EnFirefly_Combust(this, play); - EnFirefly_SetupFall(this); + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); + EnFirefly_Combust(this, play); + EnFirefly_SetupFall(this); + } } else if (!this->onFire) { EnFirefly_Ignite(this); if (this->actionFunc == EnFirefly_Perch) { @@ -654,17 +652,38 @@ void EnFirefly_UpdateDamage(EnFirefly* this, PlayState* play) { if (this->actor.params == KEESE_ICE_FLY) { EnFirefly_SetupFall(this); } else { - EnFirefly_SetupFrozenFall(this, play); + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); + this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; + EnFirefly_SetupFrozenFall(this, play); + } } } else if (damageEffect == 1) { // Deku Nuts if (this->actionFunc != EnFirefly_Stunned) { EnFirefly_SetupStunned(this); } - } else { // Fire Arrows - if ((damageEffect == 0xF) && (this->actor.params == KEESE_ICE_FLY)) { + } else if ((damageEffect == 0xF) && (this->actor.params == KEESE_ICE_FLY)) { // Fire Arrows + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); EnFirefly_Combust(this, play); + } else { + EnFirefly_SetupFall(this); + } + } else { + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); + this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; + EnFirefly_SetupFall(this); + } + } + + if (this->actor.colChkInfo.health > 0 && damageEffect != 1) { + if (this->actionFunc == EnFirefly_Perch) { + EnFirefly_SetupFlyIdle(this); } - EnFirefly_SetupFall(this); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FFLY_DEAD); + this->actor.flags |= ACTOR_FLAG_UPDATE_CULLING_DISABLED; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 6); } } } diff --git a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c index b76f1fd9f09..5a667b05b97 100644 --- a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c +++ b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c @@ -740,6 +740,11 @@ void EnFloormas_SmDecideAction(EnFloormas* this, PlayState* play) { void EnFloormas_SmShrink(EnFloormas* this, PlayState* play) { if (Math_StepToF(&this->actor.scale.x, 0.0f, 0.0015f)) { EnFloormas_SetupSmWait(this); + EnFloormas* parent = (EnFloormas*)this->actor.parent; + EnFloormas* child = (EnFloormas*)this->actor.child; + if ((parent->actionFunc == EnFloormas_SmWait) && (child->actionFunc == EnFloormas_SmWait)) { + Player_GainExperience(play, this->actor.exp); + } } this->actor.scale.z = this->actor.scale.x; this->actor.scale.y = this->actor.scale.x; @@ -823,7 +828,8 @@ void EnFloormas_GrabLink(EnFloormas* this, PlayState* play) { } else { Player_PlaySfx(&player->actor, NA_SE_VO_LI_DAMAGE_S); } - play->damagePlayer(play, -8); + u16 damage = (u16)Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); } } diff --git a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c index 1cbe3bed2e7..d1f7e3b929e 100644 --- a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c +++ b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c @@ -153,7 +153,14 @@ s32 EnFw_CheckCollider(EnFw* this, PlayState* play) { this->lastDmgHook = false; } this->collider.base.acFlags &= ~AC_HIT; - if (Actor_ApplyDamage(&this->actor) <= 0) { + + if (this->actor.colChkInfo.damage >= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health = 0; + } else { + this->actor.colChkInfo.health -= this->actor.colChkInfo.damage; + } + + if (this->actor.colChkInfo.health <= 0) { if (this->actor.parent->colChkInfo.health <= 8) { Enemy_StartFinishingBlow(play, &this->actor); this->actor.parent->colChkInfo.health = 0; diff --git a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c index 217afe31f29..172b9e72676 100644 --- a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c +++ b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c @@ -365,6 +365,7 @@ void EnFz_ApplyDamage(EnFz* this, PlayState* play) { vec.z = this->actor.world.pos.z; EnFz_Damaged(this, play, &vec, 30, 10.0f); EnFz_SetupDespawn(this, play); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } } @@ -373,6 +374,7 @@ void EnFz_ApplyDamage(EnFz* this, PlayState* play) { Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 8); if (this->actor.colChkInfo.health == 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DEAD); + Player_GainExperience(play, this->actor.exp); EnFz_SetupMelt(this); } else { Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DAMAGE); @@ -718,17 +720,18 @@ void EnFz_Draw(Actor* thisx, PlayState* play) { EnFz* this = (EnFz*)thisx; s32 pad; s32 index; + + index = (6 - (u8)CLAMP(((f32)this->actor.colChkInfo.health / GetActorStat_EnemyMaxHealth(6, this->actor.level) * 5 + 0.99999f), 0, 6)) >> 1; - index = (6 - this->actor.colChkInfo.health) >> 1; - + // Leveled Mod - Already does this. // SOH [Enhancement] - With enemy health scaling, the Freezards health could cause an index out of bounds for the // displayLists, so we need to recompute the index based on the scaled health (using the maximum health value) and // clamp the final result for safety. - if (CVarGetInteger(CVAR_ENHANCEMENT("EnemySizeScalesHealth"), 0)) { + /* if (CVarGetInteger(CVAR_ENHANCEMENT("EnemySizeScalesHealth"), 0)) { u8 scaledHealth = (u8)(((f32)this->actor.colChkInfo.health / this->actor.maximumHealth) * 6); index = (6 - scaledHealth) >> 1; index = CLAMP(index, 0, 2); - } + }*/ OPEN_DISPS(play->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c index 277677360b4..87e22131507 100644 --- a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c +++ b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c @@ -724,7 +724,7 @@ s32 EnGirlA_CanBuy_ZoraTunic(PlayState* play, EnGirlA* this) { } s32 EnGirlA_CanBuy_Health(PlayState* play, EnGirlA* this) { - if (gSaveContext.healthCapacity == gSaveContext.health) { + if (gSaveContext.healthCapacity2 == gSaveContext.health) { return CANBUY_RESULT_CANT_GET_NOW; } if (gSaveContext.rupees < this->basePrice) { diff --git a/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c index fa95087bf71..b57666f3726 100644 --- a/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c +++ b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c @@ -329,7 +329,7 @@ void EnGoma_SetupHurt(EnGoma* this, PlayState* play) { Animation_GetLastFrame(&gObjectGolDamagedAnim), ANIMMODE_ONCE, -2.0f); this->actionFunc = EnGoma_Hurt; - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { this->actionTimer = 5; Enemy_StartFinishingBlow(play, &this->actor); } else { @@ -353,7 +353,7 @@ void EnGoma_Hurt(EnGoma* this, PlayState* play) { } if (this->actionTimer == 0) { - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { EnGoma_SetupDie(this); } else { EnGoma_SetupFlee(this); @@ -620,7 +620,7 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { this->hurtTimer--; } else { ColliderInfo* acHitInfo; - u8 swordDamage; + u16 swordDamage; if ((this->colCyl1.base.atFlags & 2) && this->actionFunc == EnGoma_Jump) { EnGoma_SetupLand(this); @@ -628,7 +628,7 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { this->actor.velocity.y = 0.0f; } - if ((this->colCyl2.base.acFlags & AC_HIT) && (s8)this->actor.colChkInfo.health > 0) { + if ((this->colCyl2.base.acFlags & AC_HIT) && this->actor.colChkInfo.health > 0) { acHitInfo = this->colCyl2.info.acHitInfo; this->colCyl2.base.acFlags &= ~AC_HIT; @@ -659,7 +659,14 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { swordDamage = 1; } - this->actor.colChkInfo.health -= swordDamage; + swordDamage = Leveled_DamageModify(&this->actor, &player->actor, swordDamage * Leveled_GetHealthAttackMultiplier()); + if (swordDamage <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= swordDamage; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, swordDamage); + EnGoma_SetupHurt(this, play); Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 5); this->hurtTimer = 13; diff --git a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c index c4dbe3f3af0..0b1b2b824d4 100644 --- a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c +++ b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c @@ -686,7 +686,7 @@ void func_80A75C38(EnIk* this, PlayState* play) { f32 temp_f0; u8 pad; u8 pad2; - u8 prevHealth; + u16 prevHealth; s32 temp_v0_3; Vec3f sp38; @@ -720,20 +720,21 @@ void func_80A75C38(EnIk* this, PlayState* play) { } Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0xC); prevHealth = this->actor.colChkInfo.health; + u16 healthCheck = GetActorStat_EnemyMaxHealth(10, this->actor.level); Actor_ApplyDamage(&this->actor); if (this->actor.params != 0) { - if ((prevHealth > 10) && (this->actor.colChkInfo.health <= 10)) { + if ((prevHealth > healthCheck) && (this->actor.colChkInfo.health <= healthCheck)) { this->unk_2FB = 1; BodyBreak_Alloc(&this->bodyBreak, 3, play); } - } else if (this->actor.colChkInfo.health <= 10) { + } else if (this->actor.colChkInfo.health <= healthCheck) { Actor_ChangeCategory(play, &play->actorCtx, &this->actor, ACTORCAT_BOSS); SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 20, NA_SE_EN_LAST_DAMAGE); if (this->switchFlags != 0xFF) { Flags_SetSwitch(play, this->switchFlags); } return; - } else if (prevHealth == 50) { + } else if (prevHealth == GetActorStat_EnemyMaxHealth(50, this->actor.level)) { Actor_ChangeCategory(play, &play->actorCtx, &this->actor, ACTORCAT_ENEMY); } @@ -771,7 +772,8 @@ void func_80A75FA0(Actor* thisx, PlayState* play) { this->unk_2FA = this->unk_2FB; func_80A75C38(this, play); - if ((this->actor.params == 0) && (this->actor.colChkInfo.health <= 10)) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(10, this->actor.level); + if ((this->actor.params == 0) && (this->actor.colChkInfo.health <= healthCheck)) { func_80A781CC(&this->actor, play); return; } @@ -785,7 +787,8 @@ void func_80A75FA0(Actor* thisx, PlayState* play) { player->invincibilityTimer = 0; } else { player->invincibilityTimer = 0; - play->damagePlayer(play, -64); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 64); + play->damagePlayer(play, -damage); this->unk_2FE = 0; } } diff --git a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c index 50ac7e11ba6..46be3ed8ff1 100644 --- a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c +++ b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c @@ -625,6 +625,7 @@ void EnMb_Stunned(EnMb* this, PlayState* play) { if (this->actor.params == ENMB_TYPE_CLUB) { if (this->actor.colChkInfo.health == 0) { EnMb_SetupClubDead(this); + Player_GainExperience(play, this->actor.exp); } else if (this->state == ENMB_STATE_CLUB_KNEELING) { /* dead code: the setup for this action sets state to something else */ EnMb_SetupClubDamagedWhileKneeling(this); @@ -634,6 +635,7 @@ void EnMb_Stunned(EnMb* this, PlayState* play) { } else { if (this->actor.colChkInfo.health == 0) { EnMb_SetupSpearDead(this); + Player_GainExperience(play, this->actor.exp); } else { EnMb_SetupSpearDamaged(this); } @@ -1446,6 +1448,7 @@ void EnMb_CheckColliding(EnMb* this, PlayState* play) { if (this->actor.params == ENMB_TYPE_CLUB) { if (this->actor.colChkInfo.health == 0) { EnMb_SetupClubDead(this); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } else if (this->state != ENMB_STATE_CLUB_KNEELING) { EnMb_SetupClubDamaged(this); @@ -1453,6 +1456,7 @@ void EnMb_CheckColliding(EnMb* this, PlayState* play) { } else { if (this->actor.colChkInfo.health == 0) { EnMb_SetupSpearDead(this); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } else { EnMb_SetupSpearDamaged(this); diff --git a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c index 33133be7ba7..1a25238108c 100644 --- a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c +++ b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c @@ -871,6 +871,7 @@ void EnPeehat_StateExplode(EnPeehat* this, PlayState* play) { if (this->animTimer == 5) { bomb = (EnBom*)Actor_Spawn(&play->actorCtx, play, ACTOR_EN_BOM, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0x602, 0, true); + Player_GainExperience(play, this->actor.exp); if (bomb != NULL) { bomb->timer = 0; } diff --git a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c index e9b4ee2b6f9..7b6d6babbed 100644 --- a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c +++ b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c @@ -502,7 +502,8 @@ void func_80AE3454(EnRd* this, PlayState* play) { case 1: Animation_PlayLoop(&this->skelAnime, &gGibdoRedeadGrabAttackAnim); this->unk_304++; - play->damagePlayer(play, -8); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); func_800AA000(this->actor.xzDistToPlayer, 0xFF, 1, 0xC); this->unk_319 = 20; case 0: @@ -536,7 +537,8 @@ void func_80AE3454(EnRd* this, PlayState* play) { this->unk_319--; if (this->unk_319 == 0) { - play->damagePlayer(play, -8); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); func_800AA000(this->actor.xzDistToPlayer, 0xF0, 1, 0xC); this->unk_319 = 20; Player_PlaySfx(&player->actor, NA_SE_VO_LI_DAMAGE_S + player->ageProperties->unk_92); @@ -730,6 +732,7 @@ void func_80AE3ECC(EnRd* this, PlayState* play) { if (this->actor.colChkInfo.health == 0) { func_80AE2630(play, &this->actor, 1); func_80AE3C20(this); + Player_GainExperience(play, this->actor.exp); Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); } else { func_80AE3A8C(this); @@ -801,6 +804,7 @@ void func_80AE4114(EnRd* this, PlayState* play) { if (this->actor.colChkInfo.health == 0) { func_80AE2630(play, &this->actor, 1); func_80AE3C20(this); + Player_GainExperience(play, this->actor.exp); Item_DropCollectibleRandom(play, 0, &this->actor.world.pos, 0x90); } else { func_80AE3A8C(this); diff --git a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c index 3797adafe55..726402a8b54 100644 --- a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c +++ b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c @@ -335,6 +335,7 @@ void EnRr_SetupReleasePlayer(EnRr* this, PlayState* play) { EnRr_SetupDamage(this); } else { EnRr_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); } } @@ -463,6 +464,7 @@ void EnRr_CollisionCheck(EnRr* this, PlayState* play) { } else { this->dropType = dropType; EnRr_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); } return; case RR_DMG_FIRE: // Fire Arrow and Din's Fire @@ -756,6 +758,7 @@ void EnRr_Stunned(EnRr* this, PlayState* play) { this->actionFunc = EnRr_Approach; } else { EnRr_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); } } } diff --git a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c index bd926454504..90b766f714f 100644 --- a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c +++ b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c @@ -142,6 +142,8 @@ void func_80AFB950(EnSi* this, PlayState* play) { player->actor.freezeTimer = 10; } else { SET_GS_FLAGS((this->actor.params & 0x1F00) >> 8, this->actor.params & 0xFF); + u16 experience = Leveled_GoldSkulltulaExperience(gSaveContext.inventory.gsTokens); + Player_GainExperience(play, experience); GameInteractor_ExecuteOnFlagSet(FLAG_GS_TOKEN, this->actor.params); Actor_Kill(&this->actor); } diff --git a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c index b7612dd9179..0520e1ffdb4 100644 --- a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c +++ b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c @@ -420,6 +420,7 @@ void func_80AFD7B4(EnSkb* this, PlayState* play) { this->unk_283 |= 4; EffectSsDeadSound_SpawnStationary(play, &this->actor.projectedPos, NA_SE_EN_STALKID_DEAD, 1, 1, 0x28); EnSkb_SetupAction(this, func_80AFD880); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } diff --git a/soh/src/overlays/actors/ovl_En_St/z_en_st.c b/soh/src/overlays/actors/ovl_En_St/z_en_st.c index 99f4617bb9a..456e83b67fd 100644 --- a/soh/src/overlays/actors/ovl_En_St/z_en_st.c +++ b/soh/src/overlays/actors/ovl_En_St/z_en_st.c @@ -401,7 +401,8 @@ s32 EnSt_CheckHitLink(EnSt* this, PlayState* play) { } this->gaveDamageSpinTimer = 30; - play->damagePlayer(play, -8); + u16 damage = (u16)Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); func_8002F71C(play, &this->actor, 4.0f, this->actor.yawTowardsPlayer, 6.0f); return true; diff --git a/soh/src/overlays/actors/ovl_En_Test/z_en_test.c b/soh/src/overlays/actors/ovl_En_Test/z_en_test.c index 72969cc2e83..130c3329f93 100644 --- a/soh/src/overlays/actors/ovl_En_Test/z_en_test.c +++ b/soh/src/overlays/actors/ovl_En_Test/z_en_test.c @@ -317,6 +317,8 @@ void EnTest_Destroy(Actor* thisx, PlayState* play) { if ((this->actor.params != STALFOS_TYPE_2) && !Actor_FindNearby(play, &this->actor, ACTOR_EN_TEST, ACTORCAT_ENEMY, 8000.0f)) { func_800F5B58(); + if (this->actor.ignoreExpReward) + Player_GainExperience(play, this->actor.exp); } Effect_Delete(play, this->effectIndex); @@ -1510,7 +1512,7 @@ void func_80862E6C(EnTest* this, PlayState* play) { } } else { if (this->actor.home.rot.x == 0) { - this->actor.colChkInfo.health = 10; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(10, this->actor.level); if (this->actor.params == STALFOS_TYPE_4) { this->actor.params = -1; diff --git a/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c index a7574e17919..1cfc528f669 100644 --- a/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c +++ b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c @@ -726,6 +726,7 @@ void EnTite_Stunned(EnTite* this, PlayState* play) { this->actor.world.rot.y = this->actor.shape.rot.y; if (this->actor.colChkInfo.health == 0) { EnTite_SetupDeathCry(this); + Player_GainExperience(play, this->actor.exp); } else if (this->flipState == TEKTITE_FLIPPED) { EnTite_SetupFlipUpright(this); } else if (((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f) && @@ -867,6 +868,7 @@ void EnTite_CheckDamage(Actor* thisx, PlayState* play) { } if (thisx->colChkInfo.health == 0) { EnTite_SetupDeathCry(this); + Player_GainExperience(play, this->actor.exp); } else { // Flip tektite back up if it's on its back Audio_PlayActorSound2(thisx, NA_SE_EN_TEKU_DAMAGE); diff --git a/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c b/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c index 7d9861dae62..d28355d6978 100644 --- a/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c +++ b/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c @@ -601,6 +601,7 @@ void EnTp_UpdateDamage(EnTp* this, PlayState* play) { if (head->actor.params <= TAILPASARAN_HEAD) { EnTp_SetupDie(head); + Player_GainExperience(play, this->actor.exp); head->damageEffect = this->actor.colChkInfo.damageEffect; head->actor.params = TAILPASARAN_HEAD_DYING; } diff --git a/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c index a26e3eab97e..935f458dc7e 100644 --- a/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c +++ b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c @@ -388,6 +388,7 @@ void EnVm_Die(EnVm* this, PlayState* play) { bomb->timer = 0; } + Player_GainExperience(play, this->actor.exp); Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0xA0); Actor_Kill(&this->actor); } @@ -397,7 +398,10 @@ void EnVm_CheckHealth(EnVm* this, PlayState* play) { EnBom* bomb; if (Actor_GetCollidedExplosive(play, &this->colliderCylinder.base) != NULL) { - this->actor.colChkInfo.health--; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, Leveled_GetHealthAttackMultiplier()); + this->actor.colChkInfo.damage += damage; + ActorDamageNumber_New(&this->actor, damage); + Actor_ApplyDamage(&this->actor); osSyncPrintf("hp down %d\n", this->actor.colChkInfo.health); } else { if (!(this->colliderQuad2.base.acFlags & AC_HIT) || this->unk_21C == 2) { diff --git a/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c index 99d9fba48a6..942285d8461 100644 --- a/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c +++ b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c @@ -1945,6 +1945,7 @@ void EnZf_Die(EnZf* this, PlayState* play) { if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* miniboss */ && (D_80B4A1B4 == -1)) { Flags_SetSwitch(play, this->clearFlag); func_800F5B58(); + Player_GainExperience(play, this->actor.exp); } else { D_80B4A1B4 = -1; } diff --git a/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c index 6a38b626428..99de9ae436b 100644 --- a/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c +++ b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c @@ -2537,7 +2537,7 @@ void func_80B59828(EnZl3* this, PlayState* play) { if (func_80B59698(this, play) != 0) { func_80088AA0(180); func_80B53468(); - gSaveContext.healthAccumulator = 320; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Magic_Fill(play); if (Flags_GetSwitch(play, 0x20)) { Flags_UnsetSwitch(play, 0x20); @@ -2586,7 +2586,7 @@ void func_80B59AD0(EnZl3* this, PlayState* play) { func_80B53614(this, play); Flags_UnsetEventChkInf(EVENTCHKINF_WATCHED_GANONS_CASTLE_COLLAPSE_CAUGHT_BY_GERUDO); func_80B56F10(this, play); - gSaveContext.healthAccumulator = 320; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Magic_Fill(play); this->action = 27; this->drawConfig = 1; diff --git a/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c b/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c index 3c83a1d51e0..4143a1cd755 100644 --- a/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c +++ b/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c @@ -617,7 +617,8 @@ void EnfHG_Damage(EnfHG* this, PlayState* play) { this->timers[0] = 140; this->actionFunc = EnfHG_Retreat; Animation_MorphToLoop(&this->skin.skelAnime, &gPhantomHorseRunningAnim, 0.0f); - if (bossGnd->actor.colChkInfo.health > 24) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(24, bossGnd->actor.level); + if (bossGnd->actor.colChkInfo.health > healthCheck) { this->bossGndSignal = FHG_RIDE; } else { bossGnd->flyMode = GND_FLY_NEUTRAL; diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index ddee9d0af37..5e49d95da36 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -4545,7 +4545,7 @@ void func_80837C0C(PlayState* play, Player* this, s32 damageResponseType, f32 sp Player_PlaySfx(this, NA_SE_PL_DAMAGE); - if (!func_80837B18(play, this, 0 - this->actor.colChkInfo.damage)) { + if (!func_80837B18_modified(play, this, 0 - this->actor.colChkInfo.damage, false)) { this->stateFlags2 &= ~PLAYER_STATE2_GRABBED_BY_ENEMY; if (!(this->actor.bgCheckFlags & 1) && !(this->stateFlags1 & PLAYER_STATE1_IN_WATER)) { func_80837B9C(this, play); @@ -4784,7 +4784,7 @@ s32 func_808382DC(Player* this, PlayState* play) { this->bodyShockTimer = 40; } - this->actor.colChkInfo.damage += this->knockbackDamage; + this->actor.colChkInfo.damage += this->knockbackDamage << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, knockbackResponse[this->knockbackType - 1], this->knockbackSpeed, this->knockbackYVelocity, this->knockbackRot, 20); } else { @@ -4886,7 +4886,7 @@ s32 func_808382DC(Player* this, PlayState* play) { ((this->currentTunic != PLAYER_TUNIC_GORON && CVarGetInteger(CVAR_CHEAT("SuperTunic"), 0) == 0) || (this->floorTypeTimer >= D_808544F4[sp48])))) { this->floorTypeTimer = 0; - this->actor.colChkInfo.damage = 4; + this->actor.colChkInfo.damage = 4 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, PLAYER_HIT_RESPONSE_NONE, 4.0f, 5.0f, this->actor.shape.rot.y, 20); } else { return 0; @@ -9279,7 +9279,7 @@ s32 func_80842DF4(PlayState* play, Player* this) { func_80842B7C(play, this); if (this->actor.colChkInfo.atHitEffect == 1) { - this->actor.colChkInfo.damage = 8; + this->actor.colChkInfo.damage = 8 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, PLAYER_HIT_RESPONSE_ELECTRIC_SHOCK, 0.0f, 0.0f, this->actor.shape.rot.y, 20); return 1; @@ -9536,7 +9536,7 @@ void func_80843AE8(PlayState* play, Player* this) { LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_derth_rebirth, 1.0f, 99.0f, Animation_GetLastFrame(&gPlayerAnim_link_derth_rebirth), ANIMMODE_ONCE, 0.0f); } - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; this->av2.actionVar2 = -1; } } else if (gSaveContext.healthAccumulator == 0) { @@ -9914,7 +9914,7 @@ void Player_Action_80844A44(Player* this, PlayState* play) { Math_StepToF(&this->linearVelocity, 0.0f, 0.05f); if (this->actor.bgCheckFlags & 1) { - this->actor.colChkInfo.damage = 0x10; + this->actor.colChkInfo.damage = 0x10 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, PLAYER_HIT_RESPONSE_KNOCKBACK_LARGE, 4.0f, 5.0f, this->actor.shape.rot.y, 20); } } @@ -10800,6 +10800,7 @@ void Player_InitCommon(Player* this, PlayState* play, FlexSkeletonHeader* skelHe Collider_SetQuad(play, &this->meleeWeaponQuads[1], &this->actor, &D_80854650); Collider_InitQuad(play, &this->shieldQuad); Collider_SetQuad(play, &this->shieldQuad, &this->actor, &D_808546A0); + Player_GainExperience(play, 0); this->ivanDamageMultiplier = 1; } @@ -14610,25 +14611,27 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { if (this->itemAction == PLAYER_IA_BOTTLE_POE) { s32 rand = Rand_S16Offset(-1, 3); + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (rand == 0) { rand = 3; } - if ((rand < 0) && (gSaveContext.health <= 0x10)) { + if ((rand < 0) && (gSaveContext.health <= heartUnits)) { rand = 3; } if (rand < 0) { - Health_ChangeBy(play, -0x10); + Health_ChangeBy(play, -heartUnits); } else { - gSaveContext.healthAccumulator = rand * 0x10; + gSaveContext.healthAccumulator = rand * heartUnits; } } else { s32 sp28 = D_808549FC[this->itemAction - PLAYER_IA_BOTTLE_POTION_RED]; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (sp28 & 1) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = heartUnits * 20; } if (sp28 & 2) { @@ -14636,7 +14639,7 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { } if (sp28 & 4) { - gSaveContext.healthAccumulator = 0x50; + gSaveContext.healthAccumulator = heartUnits * 5; } } @@ -14772,7 +14775,8 @@ void Player_Action_8084EED8(Player* this, PlayState* play) { Player_PlaySfx(this, NA_SE_EV_BOTTLE_CAP_OPEN); Player_PlaySfx(this, NA_SE_EV_FIATY_HEAL - SFX_FLAG); } else if (LinkAnimation_OnFrame(&this->skelAnime, 47.0f)) { - gSaveContext.healthAccumulator = 0x140; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + gSaveContext.healthAccumulator = heartUnits * 20; } } diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 6dae312ac4a..b81c2c7451b 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -3,6 +3,7 @@ #include #include +#include "textures/nes_font_static/nes_font_static.h" #include "textures/title_static/title_static.h" #include "textures/parameter_static/parameter_static.h" #include @@ -1891,9 +1892,9 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { s16 phi_ra; s16 temp_t1; - this->windowContentVtx = Graph_Alloc(this->state.gfxCtx, 0x288 * sizeof(Vtx)); + this->windowContentVtx = Graph_Alloc(this->state.gfxCtx, 0x298 * sizeof(Vtx)); - for (phi_t2 = 0; phi_t2 < 0x288; phi_t2 += 4) { + for (phi_t2 = 0; phi_t2 < 0x298; phi_t2 += 4) { this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = 0x12C; this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = this->windowContentVtx[phi_t2].v.ob[0] + 0x10; @@ -2051,7 +2052,7 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { this->windowContentVtx[phi_t2].v.ob[1] - WREG(43); } - phi_t0 = this->windowPosX - 14; + phi_t0 = this->windowPosX - 26; temp_t1 -= 0x16; for (phi_a1 = 0; phi_a1 < 4; phi_a1++, phi_t2 += 4) { @@ -2061,7 +2062,7 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = this->windowContentVtx[phi_t2].v.ob[1] - D_80812828[phi_a1]; - phi_t0 += D_80812818[phi_a1]; + phi_t0 += D_80812818[phi_a1] - 1; } this->windowContentVtx[phi_t2 - 15].v.tc[0] = this->windowContentVtx[phi_t2 - 13].v.tc[0] = 0x400; @@ -2158,6 +2159,30 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { this->windowContentVtx[phi_t2 + 6].v.ob[1] = this->windowContentVtx[phi_t2 + 7].v.ob[1] = this->windowContentVtx[phi_t2 + 4].v.ob[1] - 0x10; this->windowContentVtx[phi_t2 + 5].v.tc[0] = this->windowContentVtx[phi_t2 + 7].v.tc[0] = 0x1000; + + phi_t2 += 8; + // Level count + phi_t0 = this->windowPosX + 30; + temp_t1 = 24; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 5; + } + phi_t0 += 3; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 9; + } } static u16 D_8081284C[] = { 0x007C, 0x0124, 0x01CC }; @@ -2270,6 +2295,31 @@ void FileChoose_DrawFileInfo(GameState* thisx, s16 fileIndex, s16 isActive) { } } + // draw level + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[648], 32, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, 255, 255, 100, this->fileInfoAlpha[fileIndex]); + + char lvText[] = { 21, 57 }; + + for (i = 0, vtxOffset = 0; i < 2; i++, vtxOffset += 4) { + FileChoose_DrawCharacter(this->state.gfxCtx, sp54->fontBuf + lvText[i] * FONT_CHAR_TEX_SIZE, vtxOffset); + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, TEXEL0, 0, + PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, 255, 255, 100, this->fileInfoAlpha[fileIndex]); + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[656], 12, 0); + + FileChoose_SplitNumber(Save_GetSaveMetaInfo(fileIndex)->level, &deathCountSplit[0], &deathCountSplit[1], + &deathCountSplit[2]); + + for (i = 1, vtxOffset = 0; i < 3; i++, vtxOffset += 4) { + FileChoose_DrawCharacter(this->state.gfxCtx, sp54->fontBuf + deathCountSplit[i] * FONT_CHAR_TEX_SIZE, + vtxOffset); + } + // end draw level + gDPPipeSync(POLY_OPA_DISP++); heartType = (Save_GetSaveMetaInfo(fileIndex)->defense == 0) ? 0 : 1; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c index 2eb5e15bbd3..1e636d7f2e5 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c @@ -372,12 +372,14 @@ void KaleidoScope_DrawDebugEditor(PlayState* play) { if (gSaveContext.healthCapacity < 0x30) { gSaveContext.healthCapacity = 0x30; } + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN) || CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { gSaveContext.healthCapacity += 0x10; if (gSaveContext.healthCapacity >= 0x140) { gSaveContext.healthCapacity = 0x140; } + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); } break; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c index aa18912eab1..b6235c8475b 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c @@ -527,6 +527,7 @@ void KaleidoScope_DrawEquipment(PlayState* play) { // Equip success sound Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + Leveled_SetPlayerModifiedStats(GET_PLAYER(play)); // Wait 10 frames before accepting input again pauseCtx->unk_1E4 = 7; sEquipTimer = 10; @@ -638,6 +639,7 @@ void KaleidoScope_DrawEquipment(PlayState* play) { Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + Leveled_SetPlayerModifiedStats(GET_PLAYER(play)); pauseCtx->unk_1E4 = 7; sEquipTimer = 10; } else if (CVarGetInteger(CVAR_ENHANCEMENT("AssignableTunicsAndBoots"), 0) != 0) { @@ -868,6 +870,10 @@ void KaleidoScope_DrawEquipment(PlayState* play) { Gfx_SetupDL_42Opa(play->state.gfxCtx); KaleidoScope_DrawEquipmentImage(play, pauseCtx->playerSegment, PAUSE_EQUIP_PLAYER_WIDTH, PAUSE_EQUIP_PLAYER_HEIGHT); + if (pauseCtx->pageIndex == PAUSE_EQUIP && (pauseCtx->unk_1E4 == 0 || sEquipTimer > 0) && pauseCtx->alpha == 255) { + Leveled_KaleidoEquip_Stats(play); + } + if (gUpgradeMasks[0]) {} CLOSE_DISPS(play->state.gfxCtx); diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index 14f749d43da..46ecd847def 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -4774,7 +4774,7 @@ void KaleidoScope_Update(PlayState* play) { play->gameplayFrames = 0; gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK; gSaveContext.health = - CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity : 0x30; + CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity2 : 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA); gSaveContext.healthAccumulator = 0; gSaveContext.magicState = MAGIC_STATE_IDLE; From e2bb2edb8ab0ea9d2077e4b2236bf605c98de324 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Fri, 4 Apr 2025 10:14:23 -0400 Subject: [PATCH 005/157] Fix the start with Master Sword option (#5304) --- soh/soh/Enhancements/randomizer/3drando/hints.cpp | 2 +- soh/soh/Enhancements/randomizer/savefile.cpp | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/hints.cpp b/soh/soh/Enhancements/randomizer/3drando/hints.cpp index c55145fcd7d..8e928185fbb 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hints.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hints.cpp @@ -835,7 +835,7 @@ void CreateGanondorfJoke() { void CreateGanondorfHint() { auto ctx = Rando::Context::GetInstance(); if (ctx->GetOption(RSK_GANONDORF_HINT) && !ctx->GetHint(RH_GANONDORF_HINT)->IsEnabled()) { - if (ctx->GetOption(RSK_SHUFFLE_MASTER_SWORD)) { + if (ctx->GetOption(RSK_SHUFFLE_MASTER_SWORD) && ctx->GetOption(RSK_STARTING_MASTER_SWORD).Is(RO_GENERIC_OFF)) { CreateStaticItemHint( RH_GANONDORF_HINT, { RHT_GANONDORF_HINT_LA_ONLY, RHT_GANONDORF_HINT_MS_ONLY, RHT_GANONDORF_HINT_LA_AND_MS }, diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index 4753bb4b4e5..cb8c7eaef70 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -107,6 +107,7 @@ void GiveLinksPocketItem() { } void SetStartingItems() { + int startingAge = OTRGlobals::Instance->gRandoContext->GetOption(RSK_SELECTED_STARTING_AGE).Get(); if (Randomizer_GetSettingValue(RSK_STARTING_KOKIRI_SWORD)) Item_Give(NULL, ITEM_SWORD_KOKIRI); if (Randomizer_GetSettingValue(RSK_STARTING_DEKU_SHIELD)) @@ -158,6 +159,13 @@ void SetStartingItems() { if (Randomizer_GetSettingValue(RSK_STARTING_NUTS) && !Randomizer_GetSettingValue(RSK_SHUFFLE_DEKU_NUT_BAG)) { GiveLinkDekuNuts(20); } + if (Randomizer_GetSettingValue(RSK_STARTING_MASTER_SWORD)) { + if (startingAge == RO_AGE_ADULT) { + Item_Give(NULL, ITEM_SWORD_MASTER); + } else { + gSaveContext.inventory.equipment |= 1 << 1; + } + } if (Randomizer_GetSettingValue(RSK_FULL_WALLETS)) { GiveLinkRupees(9001); From 38574bbb9e554943489a72b4f33faf65739b3b4e Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Fri, 4 Apr 2025 10:15:04 -0400 Subject: [PATCH 006/157] Fix excluded locations on subsequent seed generations (#5302) --- soh/soh/SaveManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 36640c72896..97ab6a52a16 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -2908,6 +2908,7 @@ extern "C" void Save_LoadFile(void) { OTRGlobals::Instance->gRandoContext = Rando::Context::CreateInstance(); OTRGlobals::Instance->gRandoContext->GetLogic()->SetSaveContext(&gSaveContext); Rando::Settings::GetInstance()->AssignContext(OTRGlobals::Instance->gRandoContext); + OTRGlobals::Instance->gRandoContext->AddExcludedOptions(); SaveManager::Instance->LoadFile(gSaveContext.fileNum); } From cb7d9279e40781000b676578aad63ca7efee93be Mon Sep 17 00:00:00 2001 From: Malkierian Date: Fri, 4 Apr 2025 08:00:05 -0700 Subject: [PATCH 007/157] Fix the GS Map combobox in the Flags tab of the save editor not remembering the selected index, and also rework it to remove the label. (#5317) --- soh/soh/Enhancements/debugger/debugSaveEditor.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index 534201f8e7a..a97a41d16f5 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -849,9 +849,19 @@ void DrawFlagsTab() { DrawGroupWithBorder( [&]() { - size_t selectedGsMap = 0; + PushStyleCombobox(THEME_COLOR); + static size_t selectedGsMap = 0; ImGui::Text("Gold Skulltulas"); - Combobox("Map##Gold Skulltulas", &selectedGsMap, gsMapping, comboboxOptionsBase.Tooltip("")); + if (ImGui::BeginCombo("##GSMap", gsMapping[selectedGsMap])) { + for (size_t index = 0; index < gsMapping.size(); index++) { + if (ImGui::Selectable(gsMapping[index])) { + selectedGsMap = index; + } + } + + ImGui::EndCombo(); + } + PopStyleCombobox(); // TODO We should write out descriptions for each one... ugh ImGui::AlignTextToFramePadding(); From 2589448057645815b3f569e38fb585342dae8271 Mon Sep 17 00:00:00 2001 From: Archez Date: Fri, 4 Apr 2025 11:57:37 -0400 Subject: [PATCH 008/157] Fix boss title card extraction for NTSC; Fix Morpha water (#5320) --- soh/assets/xml/GC_MQ_NTSC_J/objects/object_bv.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_J/objects/object_fd.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_J/objects/object_fhg.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_J/objects/object_ganon.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_J/objects/object_goma.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_J/objects/object_kingdodongo.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_J/objects/object_mo.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_J/objects/object_sst.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_J/objects/object_tw.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_U/objects/object_bv.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_U/objects/object_fd.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_U/objects/object_fhg.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_U/objects/object_ganon.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_U/objects/object_goma.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_U/objects/object_kingdodongo.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_U/objects/object_mo.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_U/objects/object_sst.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_U/objects/object_tw.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J/objects/object_bv.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fd.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fhg.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J/objects/object_ganon.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J/objects/object_goma.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J/objects/object_kingdodongo.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mo.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J/objects/object_sst.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J/objects/object_tw.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_bv.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fd.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fhg.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_ganon.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_goma.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_kingdodongo.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mo.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_sst.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_tw.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_U/objects/object_bv.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fd.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fhg.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_U/objects/object_ganon.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_U/objects/object_goma.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_U/objects/object_kingdodongo.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_U/objects/object_mo.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_U/objects/object_sst.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_U/objects/object_tw.xml | 2 +- soh/assets/xml/N64_NTSC_10/objects/object_bv.xml | 2 +- soh/assets/xml/N64_NTSC_10/objects/object_fd.xml | 2 +- soh/assets/xml/N64_NTSC_10/objects/object_fhg.xml | 2 +- soh/assets/xml/N64_NTSC_10/objects/object_ganon.xml | 2 +- soh/assets/xml/N64_NTSC_10/objects/object_goma.xml | 2 +- soh/assets/xml/N64_NTSC_10/objects/object_kingdodongo.xml | 2 +- soh/assets/xml/N64_NTSC_10/objects/object_mo.xml | 2 +- soh/assets/xml/N64_NTSC_10/objects/object_sst.xml | 2 +- soh/assets/xml/N64_NTSC_10/objects/object_tw.xml | 2 +- soh/assets/xml/N64_NTSC_11/objects/object_bv.xml | 2 +- soh/assets/xml/N64_NTSC_11/objects/object_fd.xml | 2 +- soh/assets/xml/N64_NTSC_11/objects/object_fhg.xml | 2 +- soh/assets/xml/N64_NTSC_11/objects/object_ganon.xml | 2 +- soh/assets/xml/N64_NTSC_11/objects/object_goma.xml | 2 +- soh/assets/xml/N64_NTSC_11/objects/object_kingdodongo.xml | 2 +- soh/assets/xml/N64_NTSC_11/objects/object_mo.xml | 2 +- soh/assets/xml/N64_NTSC_11/objects/object_sst.xml | 2 +- soh/assets/xml/N64_NTSC_11/objects/object_tw.xml | 2 +- soh/assets/xml/N64_NTSC_12/objects/object_bv.xml | 2 +- soh/assets/xml/N64_NTSC_12/objects/object_fd.xml | 2 +- soh/assets/xml/N64_NTSC_12/objects/object_fhg.xml | 2 +- soh/assets/xml/N64_NTSC_12/objects/object_ganon.xml | 2 +- soh/assets/xml/N64_NTSC_12/objects/object_goma.xml | 2 +- soh/assets/xml/N64_NTSC_12/objects/object_kingdodongo.xml | 2 +- soh/assets/xml/N64_NTSC_12/objects/object_mo.xml | 2 +- soh/assets/xml/N64_NTSC_12/objects/object_sst.xml | 2 +- soh/assets/xml/N64_NTSC_12/objects/object_tw.xml | 2 +- 72 files changed, 72 insertions(+), 72 deletions(-) diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_bv.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_bv.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fd.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fd.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fhg.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fhg.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_ganon.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_ganon.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_goma.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_goma.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_kingdodongo.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_kingdodongo.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mo.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mo.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_sst.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_sst.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_tw.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_tw.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_bv.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_bv.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fd.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fd.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fhg.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fhg.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_ganon.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_ganon.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_goma.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_goma.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_kingdodongo.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_kingdodongo.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_mo.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_mo.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_sst.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_sst.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_tw.xml b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/objects/object_tw.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_bv.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_bv.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fd.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fd.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fhg.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fhg.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_ganon.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_ganon.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_goma.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_goma.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_kingdodongo.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_kingdodongo.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mo.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mo.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_sst.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_sst.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_tw.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_tw.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_bv.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_bv.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fd.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fd.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fhg.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fhg.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_ganon.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_ganon.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_goma.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_goma.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_kingdodongo.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_kingdodongo.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mo.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mo.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_sst.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_sst.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_tw.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_tw.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_bv.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_bv.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fd.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fd.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fhg.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fhg.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_ganon.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_ganon.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_goma.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_goma.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_kingdodongo.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_kingdodongo.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_mo.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_mo.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_sst.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_sst.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_tw.xml b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_tw.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_bv.xml b/soh/assets/xml/N64_NTSC_10/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_bv.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_fd.xml b/soh/assets/xml/N64_NTSC_10/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_fd.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_fhg.xml b/soh/assets/xml/N64_NTSC_10/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_fhg.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_ganon.xml b/soh/assets/xml/N64_NTSC_10/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_ganon.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_goma.xml b/soh/assets/xml/N64_NTSC_10/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_goma.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_kingdodongo.xml b/soh/assets/xml/N64_NTSC_10/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_kingdodongo.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_mo.xml b/soh/assets/xml/N64_NTSC_10/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_mo.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_sst.xml b/soh/assets/xml/N64_NTSC_10/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_sst.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/objects/object_tw.xml b/soh/assets/xml/N64_NTSC_10/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/N64_NTSC_10/objects/object_tw.xml +++ b/soh/assets/xml/N64_NTSC_10/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_bv.xml b/soh/assets/xml/N64_NTSC_11/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_bv.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_fd.xml b/soh/assets/xml/N64_NTSC_11/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_fd.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_fhg.xml b/soh/assets/xml/N64_NTSC_11/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_fhg.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_ganon.xml b/soh/assets/xml/N64_NTSC_11/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_ganon.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_goma.xml b/soh/assets/xml/N64_NTSC_11/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_goma.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_kingdodongo.xml b/soh/assets/xml/N64_NTSC_11/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_kingdodongo.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_mo.xml b/soh/assets/xml/N64_NTSC_11/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_mo.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_sst.xml b/soh/assets/xml/N64_NTSC_11/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_sst.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/objects/object_tw.xml b/soh/assets/xml/N64_NTSC_11/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/N64_NTSC_11/objects/object_tw.xml +++ b/soh/assets/xml/N64_NTSC_11/objects/object_tw.xml @@ -273,7 +273,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_bv.xml b/soh/assets/xml/N64_NTSC_12/objects/object_bv.xml index 58db6e1d448..d2ebf275ba1 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_bv.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_bv.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_fd.xml b/soh/assets/xml/N64_NTSC_12/objects/object_fd.xml index 9bc25d3a618..26095796704 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_fd.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_fd.xml @@ -28,7 +28,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_fhg.xml b/soh/assets/xml/N64_NTSC_12/objects/object_fhg.xml index 6a275daeba9..669875aaf89 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_fhg.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_fhg.xml @@ -38,7 +38,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_ganon.xml b/soh/assets/xml/N64_NTSC_12/objects/object_ganon.xml index 5b68602bac9..c654c7fec9d 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_ganon.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_ganon.xml @@ -68,7 +68,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_goma.xml b/soh/assets/xml/N64_NTSC_12/objects/object_goma.xml index ec3e4211459..922678a3cf6 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_goma.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_goma.xml @@ -159,7 +159,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_kingdodongo.xml b/soh/assets/xml/N64_NTSC_12/objects/object_kingdodongo.xml index a865c15de48..5ef07a26f71 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_kingdodongo.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_kingdodongo.xml @@ -48,7 +48,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_mo.xml b/soh/assets/xml/N64_NTSC_12/objects/object_mo.xml index f450368f3be..4c4be74c834 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_mo.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_mo.xml @@ -6,7 +6,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_sst.xml b/soh/assets/xml/N64_NTSC_12/objects/object_sst.xml index d64787fa15e..b874b9a418a 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_sst.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_sst.xml @@ -17,7 +17,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/objects/object_tw.xml b/soh/assets/xml/N64_NTSC_12/objects/object_tw.xml index d238e10ab74..c7fd2a60be1 100644 --- a/soh/assets/xml/N64_NTSC_12/objects/object_tw.xml +++ b/soh/assets/xml/N64_NTSC_12/objects/object_tw.xml @@ -273,7 +273,7 @@ - + From ee23d02921dfd383fa481f5d7d70ed2ee2fa30fc Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Fri, 4 Apr 2025 15:25:30 -0400 Subject: [PATCH 009/157] move AltAssets to gSettings from gEnhancements (#5323) --- soh/soh/OTRGlobals.cpp | 6 +++--- soh/soh/SohGui/SohMenuEnhancements.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 0151773a902..72030657e14 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -310,7 +310,7 @@ OTRGlobals::OTRGlobals() { // tell LUS to reserve 3 SoH specific threads (Game, Audio, Save) context->InitResourceManager(OTRFiles, {}, 3); - prevAltAssets = CVarGetInteger(CVAR_ENHANCEMENT("AltAssets"), 0); + prevAltAssets = CVarGetInteger(CVAR_SETTING("AltAssets"), 0); context->GetResourceManager()->SetAltAssetsEnabled(prevAltAssets); auto controlDeck = std::make_shared(std::vector({ @@ -1428,7 +1428,7 @@ extern "C" void Graph_StartFrame() { } #endif case KbScancode::LUS_KB_TAB: { - CVarSetInteger(CVAR_ENHANCEMENT("AltAssets"), !CVarGetInteger(CVAR_ENHANCEMENT("AltAssets"), 0)); + CVarSetInteger(CVAR_SETTING("AltAssets"), !CVarGetInteger(CVAR_SETTING("AltAssets"), 0)); break; } } @@ -1524,7 +1524,7 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) { } } - bool curAltAssets = CVarGetInteger(CVAR_ENHANCEMENT("AltAssets"), 0); + bool curAltAssets = CVarGetInteger(CVAR_SETTING("AltAssets"), 0); if (prevAltAssets != curAltAssets) { prevAltAssets = curAltAssets; Ship::Context::GetInstance()->GetResourceManager()->SetAltAssetsEnabled(curAltAssets); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index dad94c8b480..c2f7d676418 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -429,7 +429,7 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Mods", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Use Alternate Assets", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("AltAssets")) + .CVar(CVAR_SETTING("AltAssets")) .Options(CheckboxOptions().Tooltip( "Toggle between standard assets and alternate assets. Usually mods will indicate if " "this setting has to be used or not.")); From a247c06be81551f7ad1608594cb3ee2c5b7d463d Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Fri, 4 Apr 2025 18:49:03 -0400 Subject: [PATCH 010/157] Various menu fixes (#5321) * input viewer + rang + draw + circledisp * clang --- soh/soh/Enhancements/controls/InputViewer.cpp | 10 +++++----- .../randomizer/randomizer_item_tracker.cpp | 3 ++- soh/soh/SohGui/SohMenuEnhancements.cpp | 7 +++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/soh/soh/Enhancements/controls/InputViewer.cpp b/soh/soh/Enhancements/controls/InputViewer.cpp index a48b1811fe2..3ae3bc1baa2 100644 --- a/soh/soh/Enhancements/controls/InputViewer.cpp +++ b/soh/soh/Enhancements/controls/InputViewer.cpp @@ -584,7 +584,7 @@ void InputViewerSettingsWindow::DrawElement() { } // gInputViewer.Dpad CVarCheckbox("Show D-Pad Layers", CVAR_INPUT_VIEWER("Dpad"), - CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); + CheckboxOptions().Color(THEME_COLOR).DefaultValue(false)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("Dpad"), 0)) { ImGui::Indent(); CVarCombobox("##DpadOutline", CVAR_INPUT_VIEWER("DpadOutlineMode"), buttonOutlineOptionsVerbose, @@ -593,7 +593,7 @@ void InputViewerSettingsWindow::DrawElement() { } // gInputViewer.Mod1 CVarCheckbox("Show Modifier Button 1 Layers", CVAR_INPUT_VIEWER("Mod1"), - CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); + CheckboxOptions().Color(THEME_COLOR).DefaultValue(false)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("Mod1"), 0)) { ImGui::Indent(); CVarCombobox("##Mmod1Outline", CVAR_INPUT_VIEWER("Mod1OutlineMode"), buttonOutlineOptionsVerbose, @@ -602,7 +602,7 @@ void InputViewerSettingsWindow::DrawElement() { } // gInputViewer.Mod2 CVarCheckbox("Show Modifier Button 2 Layers", CVAR_INPUT_VIEWER("Mod2"), - CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); + CheckboxOptions().Color(THEME_COLOR).DefaultValue(false)); if (useIndividualOutlines && CVarGetInteger(CVAR_INPUT_VIEWER("Mod2"), 0)) { ImGui::Indent(); CVarCombobox("##Mod2Outline", CVAR_INPUT_VIEWER("Mod2OutlineMode"), buttonOutlineOptionsVerbose, @@ -652,7 +652,7 @@ void InputViewerSettingsWindow::DrawElement() { "Right Stick Visibility", CVAR_INPUT_VIEWER("RightStick.VisibilityMode"), stickModeOptions, ComboboxOptions() .Color(THEME_COLOR) - .DefaultIndex(STICK_MODE_ALWAYS_SHOWN) + .DefaultIndex(STICK_MODE_ALWAYS_HIDDEN) .Tooltip( "Determines the conditions under which the moving layer of the right stick texture is visible.")); @@ -661,7 +661,7 @@ void InputViewerSettingsWindow::DrawElement() { "Right Stick Outline/Background Visibility", CVAR_INPUT_VIEWER("RightStick.OutlineMode"), stickModeOptions, ComboboxOptions() .Color(THEME_COLOR) - .DefaultIndex(STICK_MODE_ALWAYS_SHOWN) + .DefaultIndex(STICK_MODE_ALWAYS_HIDDEN) .Tooltip( "Determines the conditions under which the right stick outline/background texture is visible.")); diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index 8d9d2559a89..36b24aab71a 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -1839,7 +1839,8 @@ void ItemTrackerSettingsWindow::DrawElement() { } if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonRewards"), SECTION_DISPLAY_MAIN_WINDOW) == SECTION_DISPLAY_SEPARATE) { - if (CVarCheckbox("Circle display", CVAR_TRACKER_ITEM("DungeonRewardsLayout"))) { + if (CVarCheckbox("Circle display", CVAR_TRACKER_ITEM("DungeonRewardsLayout"), + CheckboxOptions().DefaultValue(false).Color(THEME_COLOR))) { shouldUpdateVectors = true; } } diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index c2f7d676418..7f5f28fcab9 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -562,7 +562,7 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Kokiri Draw Distance", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableKokiriDrawDistance")) .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("DisableDrawDistance"), 1) > 1; }) + [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("DisableDrawDistance"), 1) <= 1; }) .Options(CheckboxOptions().Tooltip( "The Kokiri are mystical beings that fade into view when approached. Enabling this will remove their " "draw distance.")); @@ -777,9 +777,8 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().Tooltip( "Change aiming for the Boomerang from Third-Person to First-Person to see past Link's head.")); AddWidget(path, "Aiming Reticle for Boomerang", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("BoomerangFirstPerson")) - .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0) != 0; }) + .CVar(CVAR_ENHANCEMENT("BoomerangReticle")) + .PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0); }) .Options(CheckboxOptions().Tooltip("Aiming with the Boomerang will display a reticle as with the Hookshot.")); AddWidget(path, "Magic Spells", WIDGET_SEPARATOR_TEXT); From 64cc4fe1c0ada906785e2d3aaa358ed490f87cae Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 7 Apr 2025 03:42:12 -0600 Subject: [PATCH 011/157] Some various fixes. Flare dancer core now takes a minimum of 9 hits to phase. Gold Skulltulas have their health depleted by half for each hit, after damage is dealt. Life meter consistancy fix when changing heart units. --- soh/include/z64actor.h | 2 +- .../game-interactor/GameInteractor_RawAction.cpp | 2 ++ soh/soh/Enhancements/mods.cpp | 6 ++++-- soh/soh/Enhancements/randomizer/hook_handlers.cpp | 7 +++++-- soh/soh/Enhancements/randomizer/logic.cpp | 2 ++ soh/src/code/z_lifemeter.c | 6 ++++-- soh/src/code/z_message_PAL.c | 8 ++------ soh/src/code/z_parameter.c | 2 ++ soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c | 13 ++++++++++--- soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c | 12 ++++++++---- soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c | 2 +- soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c | 5 +++++ 12 files changed, 46 insertions(+), 21 deletions(-) diff --git a/soh/include/z64actor.h b/soh/include/z64actor.h index 2c7922ee674..99a91a89ebf 100644 --- a/soh/include/z64actor.h +++ b/soh/include/z64actor.h @@ -267,7 +267,7 @@ typedef struct Actor { /* 0x138 */ ActorResetFunc reset; /* 0x13C */ char dbgPad[0x10]; // Padding that only exists in the debug rom // #region SOH [General] - /* */ u8 maximumHealth; // Max health value for use with health bars, set on actor init + /* */ u16 maximumHealth; // Max health value for use with health bars, set on actor init // #endregion u16 exp; // Experience u8 level; // Actor Level diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp index 2798e374b14..a090b96d7db 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -18,6 +18,8 @@ extern PlayState* gPlayState; void GameInteractor::RawAction::AddOrRemoveHealthContainers(int16_t amount) { gSaveContext.healthCapacity += amount * 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + } void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 07f15d4e785..f6e38cdda26 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -204,7 +204,8 @@ void UpdatePermanentHeartLossState() { 16 * (IS_RANDO ? (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_HEARTS) + 1) : 3); uint8_t newCapacity = startingHealth + (heartContainers * 16) + ((heartPieces - (heartPieces % 4)) * 4); - gSaveContext.healthCapacity = MAX(newCapacity, gSaveContext.healthCapacity2); + gSaveContext.healthCapacity = MAX(newCapacity, gSaveContext.healthCapacity); + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity2); hasAffectedHealth = false; } @@ -220,7 +221,8 @@ void RegisterPermanentHeartLoss() { if (!CVarGetInteger(CVAR_ENHANCEMENT("PermanentHeartLoss"), 0) || !GameInteractor::IsSaveLoaded()) return; - if (gSaveContext.healthCapacity > 16 && gSaveContext.healthCapacity - gSaveContext.health >= 16) { + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + if (gSaveContext.healthCapacity > 16 && gSaveContext.healthCapacity2 - gSaveContext.health >= heartUnits) { gSaveContext.healthCapacity -= 16; gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity2); diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 5a803f39bd4..2c67ea8da21 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -378,14 +378,17 @@ void RandomizerOnItemReceiveHandler(GetItemEntry receivedItemEntry) { randomizerQueuedItemEntry = GET_ITEM_NONE; } + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + if (receivedItemEntry.modIndex == MOD_NONE && (receivedItemEntry.itemId == ITEM_HEART_PIECE || receivedItemEntry.itemId == ITEM_HEART_PIECE_2 || receivedItemEntry.itemId == ITEM_HEART_CONTAINER)) { - gSaveContext.healthAccumulator = 0x140; // Refill 20 hearts + gSaveContext.healthAccumulator = heartUnits * 20; // Refill 20 hearts if ((s32)(gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000) { gSaveContext.inventory.questItems ^= 0x40000000; gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + gSaveContext.health += heartUnits; } } diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 4a1ede8e72a..d0f42490c57 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -1778,9 +1778,11 @@ void Logic::ApplyItemEffect(Item& item, bool state) { } break; case RG_HEART_CONTAINER: mSaveContext->healthCapacity += (!state ? -16 : 16); + mSaveContext->healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); break; case RG_PIECE_OF_HEART: mSaveContext->healthCapacity += (!state ? -4 : 4); + mSaveContext->healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); break; case RG_BOOMERANG: case RG_LENS_OF_TRUTH: diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index 31f01650a39..c293ba0db5d 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -398,9 +398,11 @@ void HealthMeter_Draw(PlayState* play) { InterfaceContext* interfaceCtx = &play->interfaceCtx; GraphicsContext* gfxCtx = play->state.gfxCtx; Vtx* sp154 = interfaceCtx->beatingHeartVtx; - s32 curHeartFraction = (s32)((f32)gSaveContext.health / heartUnits * 16) % 0x10; s16 totalHeartCount = gSaveContext.healthCapacity2 / heartUnits; s16 fullHeartCount = gSaveContext.health / heartUnits; + f32 heartUnit = (f32)gSaveContext.health / heartUnits * 16 - fullHeartCount * 16; + heartUnit = heartUnit > 0.0f && heartUnit < 1.0f ? 1.0f : heartUnit; + s32 curHeartFraction = (s32)heartUnit % 0x10; s32 pad2; f32 sp144 = interfaceCtx->unk_22A * 0.1f; s32 curCombineModeSet = 0; @@ -636,7 +638,7 @@ void HealthMeter_Draw(PlayState* play) { } offsetX += 10.0f; - s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 10); + s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 15); if (lineLength != 0 && (i + 1) % lineLength == 0) { offsetX = PosX_anchor; offsetY += 10.0f; diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index 6684aa30b95..c4fbf5c99b5 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -4701,16 +4701,12 @@ void Message_Update(PlayState* play) { s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); gSaveContext.health += heartUnits; - if (play != NULL) { - Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); - } } else { gSaveContext.healthCapacity -= 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); gSaveContext.health += heartUnits; - if (play != NULL) { - Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); - } } } if (msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP_DONE) { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 875f4c6503b..810a64e5918 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2321,9 +2321,11 @@ u8 Item_Give(PlayState* play, u8 item) { s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); gSaveContext.health += heartUnits; } else { gSaveContext.healthCapacity -= 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); gSaveContext.health -= heartUnits; } gSaveContext.ship.stats.heartContainers++; diff --git a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c index a59c736afe2..41b5462e15f 100644 --- a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c +++ b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c @@ -224,10 +224,17 @@ s32 EnFd_SpawnCore(EnFd* this, PlayState* play) { return false; } - this->actor.child->colChkInfo.health = this->actor.colChkInfo.health % 8; + u16 coreMaximumHealth = this->actor.maximumHealth / 3; + if (coreMaximumHealth * 3 != this->actor.maximumHealth) { + this->actor.colChkInfo.health = coreMaximumHealth * 3; + this->actor.maximumHealth = this->actor.colChkInfo.health; + } + + this->actor.child->colChkInfo.health = this->actor.colChkInfo.health % coreMaximumHealth; + this->actor.child->maximumHealth = coreMaximumHealth; if (this->actor.child->colChkInfo.health == 0) { - this->actor.child->colChkInfo.health = 8; + this->actor.child->colChkInfo.health = coreMaximumHealth; } if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) { @@ -781,7 +788,7 @@ void EnFd_Draw(Actor* thisx, PlayState* play) { Matrix_Pop(); if (this->actionFunc != EnFd_Reappear && !(this->fadeAlpha < 0.9f)) { Gfx_SetupDL_25Xlu(play->state.gfxCtx); - clampedHealth = CLAMP(thisx->colChkInfo.health - 1, 0, 23); + clampedHealth = CLAMP((s32)((f32)thisx->colChkInfo.health / thisx->maximumHealth * 23), 0, 23); gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, primColors[clampedHealth / 8].r, primColors[clampedHealth / 8].g, primColors[clampedHealth / 8].b, (u8)this->fadeAlpha); gDPSetEnvColor(POLY_XLU_DISP++, envColors[clampedHealth / 8].r, envColors[clampedHealth / 8].g, diff --git a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c index d1f7e3b929e..63b6b6437a4 100644 --- a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c +++ b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c @@ -154,18 +154,22 @@ s32 EnFw_CheckCollider(EnFw* this, PlayState* play) { } this->collider.base.acFlags &= ~AC_HIT; - if (this->actor.colChkInfo.damage >= this->actor.colChkInfo.health) { + u16 extraDamage = this->actor.maximumHealth / 9; + + if (this->actor.colChkInfo.damage + extraDamage >= this->actor.colChkInfo.health) { this->actor.colChkInfo.health = 0; } else { - this->actor.colChkInfo.health -= this->actor.colChkInfo.damage; + this->actor.colChkInfo.health -= this->actor.colChkInfo.damage + extraDamage; } + ActorDamageNumber_New(&this->actor, this->actor.colChkInfo.damage + extraDamage); + if (this->actor.colChkInfo.health <= 0) { - if (this->actor.parent->colChkInfo.health <= 8) { + if (this->actor.parent->colChkInfo.health <= this->actor.maximumHealth) { Enemy_StartFinishingBlow(play, &this->actor); this->actor.parent->colChkInfo.health = 0; } else { - this->actor.parent->colChkInfo.health -= 8; + this->actor.parent->colChkInfo.health -= this->actor.maximumHealth; } this->returnToParentTimer = 0; } diff --git a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c index 172b9e72676..1ebeb7999bb 100644 --- a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c +++ b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c @@ -721,7 +721,7 @@ void EnFz_Draw(Actor* thisx, PlayState* play) { s32 pad; s32 index; - index = (6 - (u8)CLAMP(((f32)this->actor.colChkInfo.health / GetActorStat_EnemyMaxHealth(6, this->actor.level) * 5 + 0.99999f), 0, 6)) >> 1; + index = (6 - (u8)CLAMP(((f32)this->actor.colChkInfo.health / this->actor.maximumHealth * 5 + 0.99999f), 0, 6)) >> 1; // Leveled Mod - Already does this. // SOH [Enhancement] - With enemy health scaling, the Freezards health could cause an index out of bounds for the diff --git a/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c index c5c8e6167fa..13d960365d8 100644 --- a/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c +++ b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c @@ -334,6 +334,11 @@ s32 func_80B0C9F0(EnSw* this, PlayState* play) { Actor_SetColorFilter(&this->actor, 0x4000, 0xC8, 0, this->unk_392); if (Actor_ApplyDamage(&this->actor) != 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_DAMAGE); + if (((this->actor.params & 0xE000) >> 0xD) != 0) { + if (this->actor.colChkInfo.health > 1) { + this->actor.colChkInfo.health >>= 1; + } + } return true; } Enemy_StartFinishingBlow(play, &this->actor); From 497f90a3928eccb5c6809187626403cd5192fa25 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Mon, 7 Apr 2025 13:27:47 -0400 Subject: [PATCH 012/157] Set Savewarp in Ganondorf to Ganon's Tower in Entrance Rando (#5339) --- soh/soh/Enhancements/randomizer/randomizer_entrance.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance.c b/soh/soh/Enhancements/randomizer/randomizer_entrance.c index 3c7b433afaa..11d17e6218e 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance.c +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance.c @@ -404,9 +404,9 @@ void Entrance_SetSavewarpEntrance(void) { gSaveContext.entranceIndex = ENTR_ICE_CAVERN_ENTRANCE; } else if (scene == SCENE_INSIDE_GANONS_CASTLE) { gSaveContext.entranceIndex = ENTR_INSIDE_GANONS_CASTLE_ENTRANCE; - } else if (scene == SCENE_GANONS_TOWER || scene == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE || - scene == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || scene == SCENE_GANON_BOSS || - scene == SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR) { + } else if (scene == SCENE_GANONS_TOWER || scene == SCENE_GANONDORF_BOSS || + scene == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE || scene == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || + scene == SCENE_GANON_BOSS || scene == SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR) { gSaveContext.entranceIndex = ENTR_GANONS_TOWER_0; // Inside Ganon's Castle -> Ganon's Tower Climb } else if (scene == SCENE_THIEVES_HIDEOUT) { // Theives hideout gSaveContext.entranceIndex = ENTR_THIEVES_HIDEOUT_0; // Gerudo Fortress -> Thieve's Hideout spawn 0 From c351a2cf7516a6fb04d088d247b2794e4b0c3333 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Mon, 7 Apr 2025 22:22:23 +0100 Subject: [PATCH 013/157] Fix shop translations and unify untranslated message alerts. (#5312) * try to fix messages, crashing on vanilla text * oops * adjust shop flavor translations to better fit on 2 lines * run clang format --- soh/include/z64.h | 2 + .../custom-message/CustomMessageManager.cpp | 11 +- .../custom-message/CustomMessageManager.h | 3 +- .../randomizer/3drando/hint_list.cpp | 181 ++++++++------- .../hint_list/hint_list_exclude_dungeon.cpp | 54 ++--- .../hint_list/hint_list_exclude_overworld.cpp | 210 +++++++++--------- .../3drando/hint_list/hint_list_item.cpp | 121 ++++------ .../Enhancements/randomizer/3drando/shops.cpp | 1 + soh/soh/Enhancements/randomizer/item_list.cpp | 48 ++-- .../Enhancements/randomizer/randomizer.cpp | 9 +- 10 files changed, 303 insertions(+), 337 deletions(-) diff --git a/soh/include/z64.h b/soh/include/z64.h index 99835304fea..83e1498dba5 100644 --- a/soh/include/z64.h +++ b/soh/include/z64.h @@ -544,6 +544,8 @@ typedef enum { LANGUAGE_MAX } Language; +#define TODO_TRANSLATE "__Translate_This__" + // TODO get these properties from the textures themselves #define FONT_CHAR_TEX_WIDTH 16 #define FONT_CHAR_TEX_HEIGHT 16 diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp index d4c47865730..278b8db0c60 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp @@ -155,11 +155,12 @@ const std::string CustomMessage::GetFrench(MessageFormat format) const { } const std::string CustomMessage::GetForCurrentLanguage(MessageFormat format) const { - return GetForLanguage((gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language, format); + return GetForLanguage(((Language)gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language, + format); } const std::string CustomMessage::GetForLanguage(uint8_t language, MessageFormat format) const { - std::string output = messages[language].length() > 0 ? messages[language] : messages[LANGUAGE_ENG]; + std::string output = messages[language] != TODO_TRANSLATE ? messages[language] : messages[LANGUAGE_ENG]; ProcessMessageFormat(output, format); return output; } @@ -273,8 +274,12 @@ void CustomMessage::Replace(std::string&& oldStr, std::string&& newStr) { void CustomMessage::Replace(std::string&& oldStr, CustomMessage newMessage) { for (uint8_t language = 0; language < LANGUAGE_MAX - 1; language++) { size_t position = messages[language].find(oldStr); + std::string newMsg = newMessage.messages[language]; + if (language != LANGUAGE_ENG && newMsg == TODO_TRANSLATE) { + newMsg = newMessage.messages[LANGUAGE_ENG]; + } while (position != std::string::npos) { - messages[language].replace(position, oldStr.length(), newMessage.messages[language]); + messages[language].replace(position, oldStr.length(), newMsg); position = messages[language].find(oldStr); } } diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.h b/soh/soh/Enhancements/custom-message/CustomMessageManager.h index 71ae423b549..f312d9f8513 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageManager.h +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.h @@ -6,6 +6,7 @@ #include #include "../../../include/z64item.h" +#include "../../../include/z64.h" #include "../../../include/message_data_textbox_types.h" #include "../randomizer/3drando/text.hpp" @@ -203,7 +204,7 @@ class CustomMessage { void CleanString(std::string& str) const; private: - std::vector messages = { "", "", "" }; + std::vector messages = { "", TODO_TRANSLATE, TODO_TRANSLATE }; TextBoxType type = TEXTBOX_TYPE_BLACK; TextBoxPosition position = TEXTBOX_POS_BOTTOM; std::vector colors = {}; diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp index 5d880f04777..bdfaaef7779 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp @@ -1458,257 +1458,257 @@ void StaticData::HintTable_Init() { ---------------------------*/ hintTextTable[RHT_JUNK01] = HintText(CustomMessage("They say you must read the names of \"Special Deal\" shop items carefully.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, les \"Offres spéciales\" sont parfois trompeuses... Lisez les attentivement!")); hintTextTable[RHT_JUNK02] = HintText(CustomMessage("They say that Zelda is a poor leader.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Zelda ne ferait pas un bon monarque.")); hintTextTable[RHT_JUNK03] = HintText(CustomMessage("These hints can be quite useful. This is an exception.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ces indices sont très utiles, à l'exception de celui-ci.")); hintTextTable[RHT_JUNK04] = HintText(CustomMessage("They say that the Lizalfos in Dodongo's Cavern like to play in lava.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, les Lézalfos de la Caverne Dodongo aiment patauger dans la lave.")); hintTextTable[RHT_JUNK05] = HintText(CustomMessage("They say that all the Zora drowned in Wind Waker.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, les Zoras se sont noyés dans Wind Waker.")); hintTextTable[RHT_JUNK06] = HintText(CustomMessage("If Gorons eat rocks, does that mean I'm in danger?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ne dis pas au Gorons que je suis ici. Ils mangent des roches, tu sais!")); hintTextTable[RHT_JUNK07] = HintText(CustomMessage("'Member when Ganon was a blue pig?^I 'member.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Dans mon temps, Ganon était un cochon bleu...^Pff! Les jeunes de nos jours, et leur Ganondorf!")); hintTextTable[RHT_JUNK08] = HintText(CustomMessage("One who does not have Triforce can't go in.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ceux sans Triforce doivent rebrousser chemin.")); hintTextTable[RHT_JUNK09] = HintText(CustomMessage("Save your future, end the Happy Mask Salesman.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, tu t'éviteras des jours de malheur si tu vaincs le vendeur de masques...")); hintTextTable[RHT_JUNK10] = HintText(CustomMessage("Glitches are a pathway to many abilities some consider to be... Unnatural.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Les glitchs sont un moyen d'acquérir de nombreuses facultés considérées par certains comme... contraire ")); hintTextTable[RHT_JUNK11] = HintText(CustomMessage("I'm stoned. Get it?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Allez, roche, papier, ciseau...&Roche.")); hintTextTable[RHT_JUNK12] = HintText(CustomMessage("Hoot! Hoot! Would you like me to repeat that?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Hou hou! Veux-tu que je répète tout ça?")); hintTextTable[RHT_JUNK13] = HintText(CustomMessage("Gorons are stupid. They eat rocks.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Les Gorons sont des vraies têtes dures.")); hintTextTable[RHT_JUNK14] = HintText(CustomMessage("They say that Lon Lon Ranch prospered under Ingo.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, le Ranch Lon Lon était plus prospère sous Ingo.")); hintTextTable[RHT_JUNK15] = HintText(CustomMessage("They say without the Lens of Truth, the Treasure Chest Mini-Game is a 1 out of 32 chance.^Good luck!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Gagner la Chasse-aux-Trésors est 1 chance sur 32.^Bonne chance!")); hintTextTable[RHT_JUNK16] = HintText(CustomMessage("Use bombs wisely.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Utilise les bombes avec précaution.")); hintTextTable[RHT_JUNK17] = HintText(CustomMessage("They say that players who select the \"ON\" option for \"MOTION CONTROL\" are the real \"Zelda players!\"", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, ceux qui utilisent les contrôles gyroscopiques sont les VRAIS joueurs.")); hintTextTable[RHT_JUNK18] = HintText(CustomMessage("L2P @.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Arrête de lire les indices et joue comme un grand, @.")); hintTextTable[RHT_JUNK19] = HintText(CustomMessage("I bet you'd like to have more bombs.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Je parie que tu veux plus de bombes.")); hintTextTable[RHT_JUNK20] = HintText(CustomMessage("When all else fails, use Fire.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Quand rien ne marche, utilise le feu.")); hintTextTable[RHT_JUNK21] = HintText(CustomMessage("Here's a hint, @. Don't be bad.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, la #Triforce# n'est pas dans le jeu... Duh!")); hintTextTable[RHT_JUNK22] = HintText(CustomMessage("Game Over. Return of Ganon.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Partie terminée. RETour de Ganon.")); hintTextTable[RHT_JUNK23] = HintText(CustomMessage("May the way of the Hero lead to the Triforce.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Que le chemin du héros te mène à la Triforce.")); hintTextTable[RHT_JUNK24] = HintText(CustomMessage("Can't find an item? Scan an Amiibo.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Tu cherches de quoi? Utilise un Amiibo!")); hintTextTable[RHT_JUNK25] = HintText(CustomMessage("They say this game has just a few glitches.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, ce jeu est complètement exempt de glitchs.")); hintTextTable[RHT_JUNK26] = HintText(CustomMessage("BRRING BRRING This is Ulrira. Wrong number?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "DRING DRING!! Pépé le Ramollo à l'appareil... Quoi? Faux numéro?")); hintTextTable[RHT_JUNK27] = HintText(CustomMessage("Tingle Tingle Kooloo Limpah!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Tingle! Tingle! Kooloolin... Pah!")); hintTextTable[RHT_JUNK28] = HintText(CustomMessage("L is real 2401", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "L is real 2401")); hintTextTable[RHT_JUNK29] = HintText(CustomMessage("They say that Ganondorf will appear in the next Mario Tennis.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Ganondorf sera la nouvelle recrue dans Mario Tennis.")); hintTextTable[RHT_JUNK30] = HintText(CustomMessage("They say Medigoron sells the earliest Breath of the Wild demo.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Medigoron vend une démo de #Breath of the Wild#.")); hintTextTable[RHT_JUNK31] = HintText(CustomMessage("Can you move me? I don't get great service here.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Peux-tu me déplacer? J'ai pas une bonne réception ici.")); hintTextTable[RHT_JUNK32] = HintText(CustomMessage("They say if you use Strength on the truck, you can find Mew.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #Mew# se trouve dessous le camion... Duh!")); hintTextTable[RHT_JUNK33] = HintText(CustomMessage("I'm a helpful hint Gossip Stone!^See, I'm helping.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Salut! Je suis une pierre de bons conseils!^Tiens, tu vois? J'aide bien, hein?")); hintTextTable[RHT_JUNK34] = HintText(CustomMessage("Dear @, please come to the castle. I've baked a cake for you.&Yours truly, Princess Zelda.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Mon très cher @:&Viens vite au château, je t'ai préparé&un délicieux gâteau...^À bientôt, Princesse Zelda")); hintTextTable[RHT_JUNK35] = HintText(CustomMessage("They say all toasters toast toast.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, les grille-pains grillent du pain.")); hintTextTable[RHT_JUNK36] = HintText(CustomMessage("You thought it would be a useful hint, but it was me, junk hint!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Tu t'attendais à un bon indice... Mais c'était moi, un mauvais indice!")); hintTextTable[RHT_JUNK37] = HintText(CustomMessage("They say that quest guidance can be found at a talking rock.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, des #indices# se trouvent auprès d'une pierre parlante... Duh!")); hintTextTable[RHT_JUNK38] = HintText(CustomMessage("They say that the final item you're looking for can be found somewhere in Hyrule.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, le #dernier objet# se trouve quelque part dans Hyrule... Duh!")); hintTextTable[RHT_JUNK39] = HintText(CustomMessage("Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.^Mweep.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.^Mwip.")); hintTextTable[RHT_JUNK40] = HintText(CustomMessage("They say that Barinade fears Deku Nuts.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Barinade a la frousse des noix Mojo.")); hintTextTable[RHT_JUNK41] = HintText(CustomMessage("They say that Flare Dancers do not fear Goron-crafted blades.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, le danse-flamme n'a pas peur des armes de Goron.")); hintTextTable[RHT_JUNK42] = HintText(CustomMessage("They say that Morpha is easily trapped in a corner.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Morpha est facilement coincé.")); hintTextTable[RHT_JUNK43] = HintText(CustomMessage("They say that Bongo Bongo really hates the cold.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Bongo Bongo a facilement froid aux doigts.")); hintTextTable[RHT_JUNK44] = HintText(CustomMessage("They say that your sword is most powerful when you put it away.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, ton épée est à pleine puissance quand tu la rengaines.")); hintTextTable[RHT_JUNK45] = HintText(CustomMessage("They say that bombing the hole Volvagia last flew into can be rewarding.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, le trou où se creuse Volvagia est vulnérable aux bombes.")); hintTextTable[RHT_JUNK46] = HintText(CustomMessage("They say that invisible ghosts can be exposed with Deku Nuts.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, des fantômes invisibles apparaissent avec des noix Mojo.")); hintTextTable[RHT_JUNK47] = HintText(CustomMessage("They say that the real Phantom Ganon is bright and loud.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, le vrai spectre de Ganon est clair et bruyant.")); hintTextTable[RHT_JUNK48] = HintText(CustomMessage("They say that walking backwards is very fast.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, tu fais marche arrière très rapidement pour un héros.")); hintTextTable[RHT_JUNK49] = HintText(CustomMessage("They say Ingo is not very good at planning ahead.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Ingo ne fait pas un très bon geôlier.")); hintTextTable[RHT_JUNK50] = HintText(CustomMessage("You found a spiritual Stone! By which I mean, I worship Nayru.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Vous avez trouvé une Pierre Ancestrale! En effet, je vénère la déesse Hylia.")); hintTextTable[RHT_JUNK51] = HintText(CustomMessage("Open your eyes.^Open your eyes.^Wake up, @.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Réveille-toi...^Réveille-toi.^Ouvre les yeux, @.")); hintTextTable[RHT_JUNK52] = HintText(CustomMessage("They say that the Nocturne of Shadow can bring you very close to Ganon.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, le nocturne de l'ombre peut t'amener très près de Ganon.")); hintTextTable[RHT_JUNK53] = HintText(CustomMessage("They say that Twinrova always casts the same spell the first three times.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Twinrova lance toujours les mêmes trois premiers sorts.")); hintTextTable[RHT_JUNK54] = HintText(CustomMessage("They say that the nightly builds may be unstable.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, les \"nightly builds\" peuvent être instables.")); hintTextTable[RHT_JUNK55] = HintText(CustomMessage("You're playing a Randomizer. I'm randomized!^Here's a random number: #4#.&Enjoy your Randomizer!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Tu joues à un randomizer. Je suis aléatoire!^Voici un nombre aléatoire: #4#.&Bonne partie!")); hintTextTable[RHT_JUNK56] = HintText(CustomMessage("They say Ganondorf's bolts can be reflected with glass or steel.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, les éclairs de Ganon se reflètent sur l'acier et le verre.")); hintTextTable[RHT_JUNK57] = HintText(CustomMessage("They say Ganon's tail is vulnerable to nuts, arrows, swords, explosives, hammers...^...sticks, seeds, " "boomerangs...^...rods, shovels, iron balls, angry bees...", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, la queue de Ganon est vulnérable aux noix, flèches, épées, bombes, marteaux...^...bâtons, " "graines, boomerangs...^...baguettes, pelles, boulets de fer, abeilles enragées...")); hintTextTable[RHT_JUNK58] = HintText(CustomMessage("They say that you're wasting time reading this hint, but I disagree. Talk to me again!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi... tu sais quoi? Parle-moi encore, et je te le dirai!")); hintTextTable[RHT_JUNK59] = HintText(CustomMessage("They say Ganondorf knows where to find the instrument of his doom.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, Ganondorf sait où il a caché son point faible.")); hintTextTable[RHT_JUNK60] = HintText(CustomMessage("I heard @ is pretty good at Zelda.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Apparemment, @ est super bon à Zelda.")); hintTextTable[RHT_JUNK61] = HintText(CustomMessage("Hi @, we've been trying to reach you about your car's extended warranty. ", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Bonjour, @. Vous avez une voiture? Vous savez, nous offrons des assurances abordables...")); hintTextTable[RHT_JUNK62] = HintText(CustomMessage("They say that it's actually possible to beat the running man.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, il est possible de battre le coureur.&Donc, tu prends ton arc, et...")); hintTextTable[RHT_JUNK63] = HintText(CustomMessage("They say this hint makes more sense in other languages.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, ces indices auraient pu être mieux traduits... Duh!")); // ^ Junk hints above are from 3drando @@ -1721,35 +1721,35 @@ void StaticData::HintTable_Init() { "Erreur 0x69a504:&Traduction manquante^C'est de la faute à Purple Hato!&J'vous jure!" hintTextTable[RHT_JUNK64] = HintText(CustomMessage("They say Greg is special.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); hintTextTable[RHT_JUNK65] = HintText(CustomMessage("They say the longer the Goron's neck, the wiser they are.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); hintTextTable[RHT_JUNK66] = HintText(CustomMessage("They say this ship is what all true gamers strive for.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, cette version du port est ce pour quoi luttent tous les vrais gamers.")); hintTextTable[RHT_JUNK67] = HintText(CustomMessage("They say that Glowsticks can be found in the Raveyard.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "On peut trouver des Bâtons Lumineux sur le dancefloor du cimetière.")); hintTextTable[RHT_JUNK68] = HintText(CustomMessage("They say @'s uncle works for Nintendo.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); hintTextTable[RHT_JUNK69] = HintText(CustomMessage("They say pulling all gravestones in the graveyard leads to something magical.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); hintTextTable[RHT_JUNK70] = HintText(CustomMessage("They say holding L while pausing makes you win the game.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); hintTextTable[RHT_JUNK71] = HintText(CustomMessage("They say @'s body is ready.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); /*-------------------------- @@ -2214,19 +2214,16 @@ void StaticData::HintTable_Init() { ---------------------------*/ hintTextTable[RHT_GANONDORF_HINT_LA_ONLY] = HintText(CustomMessage("Ha ha ha... You'll never beat me by reflecting my lightning bolts and unleashing the arrows from #[[1]]#!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ha ha ha... Pauvre fou! Tu ne pourras jamais me vaincre sans les flèches que j'ai cachées dans #[[1]]#!", {QM_RED})); // /*spanish*/Ja, ja, ja... Nunca me derrotarás reflejando mis esferas de energía y desplegando la flecha de luz de #[[1]]#! - hintTextTable[RHT_GANONDORF_HINT_MS_ONLY] = HintText(CustomMessage("Ha ha ha... You'll never defeat me, drop a castle on me and finish me off with the sacred blade from #[[2]]#!", - /*german*/ "", - /*french*/ "", - {QM_RED})); + hintTextTable[RHT_GANONDORF_HINT_MS_ONLY] = HintText(CustomMessage("Ha ha ha... You'll never defeat me, drop a castle on me and finish me off with the sacred blade from #[[2]]#!", {QM_RED})); // TODO_TRANSLATE hintTextTable[RHT_GANONDORF_HINT_LA_AND_MS] = HintText(CustomMessage("Ha ha ha... You'll never beat me by reflecting my lightning bolts and unleashing the arrows from #[[1]]#!" "^And even if you do, you'll never find the legendary blade hidden in #[[2]]#!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ha ha ha... Pauvre fou! Tu ne pourras jamais me vaincre sans les flèches que j'ai cachées dans #[[1]]#!" "^Et même si tu les trouves, tu ne touveras jamais l'épée de légende cachée dans #[[2]]#!", {QM_RED, QM_RED})); @@ -2234,7 +2231,7 @@ void StaticData::HintTable_Init() { // ^E incluso si lo haces, nunca encontrarás la espada legendaria escondida en #[[2]]#! hintTextTable[RHT_SHEIK_HINT_LA_ONLY] = HintText(CustomMessage("I overheard Ganondorf say that he misplaced the #Light Arrows# in #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "J'ai entendu dire que Ganondorf aurait caché les #Flèches de Lumière# dans #[[1]]#.", {QM_YELLOW, QM_RED})); @@ -2244,17 +2241,17 @@ void StaticData::HintTable_Init() { {QM_RED, QM_RED})); hintTextTable[RHT_GREG_HINT] = HintText(CustomMessage("By the way, if you're interested, I saw the shiniest #Green Rupee# somewhere in #[[1]]#.^It's said to have #mysterious powers#...^But then, it could just be another regular rupee.&Oh well.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Au fait, si ça t'intéresse, j'ai aperçu le plus éclatant des #Rubis Verts# quelque part à #[[1]]#. On dit qu'il possède des pouvoirs mystérieux... Mais bon, ça pourrait juste être un autre rubis ordinaire.",//RANDOTODO color in mysterious powers {QM_GREEN, QM_RED, QM_RED})); hintTextTable[RHT_SARIA_TALK_HINT] = HintText(CustomMessage("Did you feel the #surge of magic# recently? A mysterious bird told me it came from #[[1]]#.^You should check that place out, @!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "As-tu récemment ressenti une vague de #puissance magique#? Un mystérieux hibou m'a dit qu'elle provenait du #[[1]]#. Tu devrais aller y jeter un coup d'oeil, @!", {QM_GREEN, QM_RED})); hintTextTable[RHT_SARIA_SONG_HINT] = HintText(CustomMessage("Did you feel the #surge of magic# recently? A mysterious bird told me it came from #[[1]]#.^You should check that place out, @!\x0B", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "As-tu récemment ressenti une vague de #puissance magique#? Un mystérieux hibou m'a dit qu'elle provenait du #[[1]]#. Tu devrais aller y jeter un coup d'oeil, @!\x0B", {QM_GREEN, QM_RED}, {}, TEXTBOX_TYPE_BLUE)); @@ -2339,33 +2336,33 @@ void StaticData::HintTable_Init() { ---------------------------*/ hintTextTable[RHT_GANON_JOKE01] = HintText(CustomMessage("Oh! It's @.&I was expecting someone called Sheik.&Do you know what happened to them?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ah, c'est @.&J'attendais un certain Sheik.&Tu sais ce qui lui est arrivé?")); // /*spanish*/¡Oh! Pero si es @.&Estaba esperando a alguien llamado Sheik. ¿Sabes qué puede haberle pasado? hintTextTable[RHT_GANON_JOKE02] = HintText(CustomMessage("I knew I shouldn't have put the key on the other side of my door.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "J'aurais dû garder la clé ici. Hélas...")); // /*spanish*/Sabía que no tendría que haber dejado la llave al otro lado de la puerta. hintTextTable[RHT_GANON_JOKE03] = HintText(CustomMessage("Looks like it's time for a round of tennis.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "C'est l'heure de jouer au tennis.")); // /*spanish*/Parece que es hora de una pachanga de tenis. hintTextTable[RHT_GANON_JOKE04] = HintText(CustomMessage("You'll never deflect my bolts of energy with your sword, then shoot me with those Light Arrows you happen to have.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Ne perds pas ton temps à frapper mes éclairs d'énergie avec ton épée et me tirer avec tes flèches de Lumière!")); // /*spanish*/Nunca reflejarás mis esferas de energía con tu espada, para después dispararme con las flechas de luz que tendrás. hintTextTable[RHT_GANON_JOKE05] = HintText(CustomMessage("Why did I leave my trident back in the desert?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Sale bêtise... Et j'ai oublié mon trident dans le désert!")); // /*spanish*/Santa Hylia... ¿Por qué me habré dejado el tridente en el desierto? hintTextTable[RHT_GANON_JOKE06] = HintText(CustomMessage("Zelda is probably going to do something stupid, like send you back to your own timeline.^So this is " "quite meaningless. Do you really want to save this moron?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Même si je suis vaincu... Zelda te renverra dans ton ère, et je reviendrai conquérir!^Telle est la " "prophécie d'Hyrule Historia!")); // /*spanish*/Seguro que Zelda trata de hacer alguna tontería, como enviarte de vuelta a tu línea temporal.^No tiene @@ -2373,30 +2370,30 @@ void StaticData::HintTable_Init() { hintTextTable[RHT_GANON_JOKE07] = HintText(CustomMessage("What about Zelda makes you think&she'd be a better ruler than I?^I saved Lon Lon Ranch,&fed the " "hungry,&and my castle floats.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Zelda ne sera jamais un meilleur monarque que moi!^J'ai un château volant, mes sujets sont des belles " "amazones... et mes Moblins sont clairement plus puissants que jamais!")); // /*spanish*/¿Qué te hace pensar que Zelda gobierna mejor que yo?^Yo he salvado el Rancho Lon Lon,&he alimentado a // los hambrientos&y hasta hago que mi castillo flote. hintTextTable[RHT_GANON_JOKE08] = HintText(CustomMessage("I've learned this spell,&it's really neat,&I'll keep it later&for your treat!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Gamin, ton destin achève,&sous mon sort tu périras!&Cette partie ne fut pas brève,&et cette mort, tu subiras!")); // /*spanish*/Veamos ahora que harás,&la batalla ha de comenzar,&te enviaré de una vez al más allá,&¿listo para // afrontar la verdad? hintTextTable[RHT_GANON_JOKE09] = HintText(CustomMessage("Many tricks are up my sleeve,&to save yourself&you'd better leave!", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Sale petit garnement,&tu fais erreur!&C'est maintenant que marque&ta dernière heure!")); // /*spanish*/¿No osarás a mí enfrentarte?&Rimas aparte,&¡voy a matarte! hintTextTable[RHT_GANON_JOKE10] = HintText(CustomMessage("After what you did to Koholint Island, how can you call me the bad guy?", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "J'admire ce que tu as fait à l'Île Koholint... Toi et moi, nous devrions faire équipe!")); // /*spanish*/Después de lo que le hiciste a la Isla Koholint, ¿cómo te atreves a llamarme malvado? hintTextTable[RHT_GANON_JOKE11] = HintText(CustomMessage("Today, let's begin down&'The Hero is Defeated' timeline.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Si tu me vaincs, Hyrule sera englouti... mais si tu meurs, on aura A Link to the Past, le meilleur opus " "de la série!")); // /*spanish*/Hoy daremos lugar a la línea temporal del Héroe Derrotado.&¡Prepárate para el culmen de esta saga! diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp index 1347945ec3a..7657776e748 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp @@ -123,12 +123,10 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß ein #Herz im Deku-Baum# #[[1]]# verstecke.", /*french*/ "Selon moi, un #coeur dans l'Arbre Mojo# cache #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_DEKU_TREE_GRASS] = HintText(CustomMessage("They say that some #grass in the Deku Tree# hides #[[1]]#.", - /*german*/ "", - /*french*/ "", {QM_RED, QM_GREEN})); + hintTextTable[RHT_DEKU_TREE_GRASS] = HintText(CustomMessage("They say that some #grass in the Deku Tree# hides #[[1]]#.", {QM_RED, QM_GREEN})); //TODO_TRANSLATE hintTextTable[RHT_CRATE_DEKU_TREE] = HintText(CustomMessage("They say that a #crate in the Deku Tree# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans l'Arbre Mojo# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -305,12 +303,10 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein in Dodongos Höhle# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer un air orageux pour une pierre étrange dans la Caverne Dodongo# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_DODONGOS_CAVERN_GRASS] = HintText(CustomMessage("They say that some #grass in Dodongo's Cavern# hides #[[1]]#.", - /*german*/ "!!!", - /*french*/ "", {QM_RED, QM_GREEN})); + hintTextTable[RHT_DODONGOS_CAVERN_GRASS] = HintText(CustomMessage("They say that some #grass in Dodongo's Cavern# hides #[[1]]#.", {QM_RED, QM_GREEN})); //TODO_TRANSLATE hintTextTable[RHT_CRATE_DODONGOS_CAVERN] = HintText(CustomMessage("They say that a #crate in Dodongo's Cavern# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans la Caverne Dodongo# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -472,12 +468,10 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß sich #nahe des zentralen Lifts in Jabu-Jabus Bauch# #[[1]]# befände.", /*french*/ "Selon moi, près d'un #ascenceur central dans le Ventre de Jabu-Jabu# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_JABU_JABUS_BELLY_GRASS] = HintText(CustomMessage("They say that some #grass in Jabu Jabu's Belly# hides #[[1]]#.", - /*german*/ "", - /*french*/ "", {QM_RED, QM_GREEN})); + hintTextTable[RHT_JABU_JABUS_BELLY_GRASS] = HintText(CustomMessage("They say that some #grass in Jabu Jabu's Belly# hides #[[1]]#.", {QM_RED, QM_GREEN})); //TODO_TRANSLATE hintTextTable[RHT_CRATE_JABU_JABU] = HintText(CustomMessage("They say that a #crate in Jabu Jabu's Belly# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans le Ventre de Jabu-Jabu# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -711,7 +705,7 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, un #coeur dans le Temple de la Fôret# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_FOREST_TEMPLE] = HintText(CustomMessage("They say that a #crate in Forest Temple# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans le Temple de la Fôret# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -911,15 +905,15 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, un #coeur dans le Temple du Feu# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_FIRE_TEMPLE_MQ_LOOP_STALFOS_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in a hot arena# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #appeler le soleil dans une arène chaude# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_FIRE_TEMPLE_MQ_LOOP_KNUCKLE_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun behind a knight's throne in a volcano# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #appeler le soleil derrière le trône d’un chevalier dans un volcan# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_FIRE_TEMPLE] = HintText(CustomMessage("They say that a #crate in Fire Temple# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans le Temple du Feu# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -1058,19 +1052,19 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, une #rivière dans le Temple de l'Eau# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_WATER_TEMPLE_MQ_DARK_LINK_PILAR_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun atop a small pillar before a duel with one's shadow# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #appeler le soleil au sommet d’un petit pilier avant un duel avec son ombre# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_WATER_TEMPLE_MQ_DARK_LINK_LEFT_STORM_FAIRY] = HintText(CustomMessage("They say that #calling the rain before a duel with one's shadow# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #appeler la pluie avant un duel avec son ombre# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_WATER_TEMPLE_MQ_DARK_LINK_RIGHT_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun before a duel with one's shadow# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #appeler le soleil avant un duel avec son ombre# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_WATER_TEMPLE] = HintText(CustomMessage("They say that a #crate in Water Temple# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans le Temple de l'Eau# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -1311,15 +1305,15 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, une #jarre dans le Temple de l'Esprit# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SPIRIT_TEMPLE_HEART] = HintText(CustomMessage("They say that on a #small platform# in the Spirit Temple lies #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, sur une #petite plateforme# dans le Temple de l'Esprit repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SPIRIT_TEMPLE_MQ_HEART] = HintText(CustomMessage("They say that guarded by a #ring of flame# in the Spirit Temple is #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, protégé par un #cercle de flammes# dans le Temple de l'Esprit se trouve #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_SPIRIT_TEMPLE] = HintText(CustomMessage("They say that a #crate in Spirit Temple# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans le Temple de l'Esprit# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -1574,19 +1568,19 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, un #coeur dans le Temple de l'Ombre# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY] = HintText(CustomMessage("They say that an #calling the rain for a sentry guarding a house of the dead# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #appeler la pluie pour une sentinelle gardant une maison des morts# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SHADOW_TEMPLE_PIT_STORM_FAIRY] = HintText(CustomMessage("They say that an #calling the rain on a platform suspended above a bottomless pit# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #appeler la pluie sur une plateforme suspendue au-dessus d’un gouffre sans fond# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SHADOW_TEMPLE_WIND_HINT_SUN_FAIRY] = HintText(CustomMessage("They say that an #calling the sun near an invisible chest guarded by the dead# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #appeler le soleil près d’un coffre invisible gardé par les morts# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_SHADOW_TEMPLE] = HintText(CustomMessage("They say that a #crate in Shadow Temple# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans le Temple de l'Ombre# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -1731,9 +1725,7 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne in einer Sackgasse# innerhalb des Brunnens #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler le soleil dans une impasse# à l’intérieur du puits révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_BOTTOM_OF_THE_WELL_GRASS] = HintText(CustomMessage("They say that some #grass in the Bottom of the Well# hides #[[1]]#.", - /*german*/ "", - /*french*/ "", {QM_RED, QM_GREEN})); + hintTextTable[RHT_BOTTOM_OF_THE_WELL_GRASS] = HintText(CustomMessage("They say that some #grass in the Bottom of the Well# hides #[[1]]#.", {QM_RED, QM_GREEN})); // TODO_TRANSLATE /*-------------------------- @@ -2013,7 +2005,7 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, dans #une épreuve avec un Dinolfos# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_GERUDO_TRAINING_GROUND] = HintText(CustomMessage("They say that a #crate in Gerudo Training Ground# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans le Gymnase Gerudo# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp index 11d89d8377f..0c563fd4f35 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp @@ -1557,384 +1557,384 @@ void StaticData::HintTable_Init_Exclude_Overworld() { /*french*/ "Selon moi, un cœur dans la #maison d'une amie chère# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_LOST_WOODS_RUPEE] = HintText(CustomMessage("They say that under a #boulder in the woods# lies #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, sous un #rocher dans les bois# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_LOST_WOODS_SHORTCUT_RUPEE] = HintText(CustomMessage("They say that in a #pool of water in the woods# lies #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, dans une #mare d'eau dans les bois# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_LAKE_HYLIA_RUPEE] = HintText(CustomMessage("They say that just off the #coast of a lake# lies #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, juste au large de la #côte d'un lac# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_LABORATORY_RUPEE] = HintText(CustomMessage("They say that at the #bottom of a tank# lies #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, au #fond d'un réservoir# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DAMPES_GRAVE_RUPEE] = HintText(CustomMessage("They say that within a #quick-footed spirit's grave# lies #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, dans la #tombe d'un esprit à pieds rapides# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_GERUDO_VALLEY_GROTTO_RUPEE] = HintText(CustomMessage("They say that an Octarok in an #underground spring# guards #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, un Octrocher dans une #source souterraine# garde #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DEATH_MOUNTAIN_TRAIL_RUPEE] = HintText(CustomMessage("They say that beneath a boulder on a #mountain's cliffside# lies #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, sous un rocher sur la #falaise d'une montagne# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DEATH_MOUNTAIN_TRAIL_GROTTO_HEART] = HintText(CustomMessage("They say that accompanying a #cow in a small cave# is #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, accompagner une #vache dans une petite grotte# est #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DEATH_MOUNTAIN_TRAIL_GROTTO_RUPEE] = HintText(CustomMessage("They say that accompanying a #cow in a small cave# is #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, accompagner une #vache dans une petite grotte# est #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DEATH_MOUNTAIN_CRATER_RUPEE] = HintText(CustomMessage("They say that on a #small platform suspended above lava# lies #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, sur une #petite plateforme suspendue au-dessus de la lave# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_ZORAS_RIVER_WATERFALL_RUPEE] = HintText(CustomMessage("They say that beneath a #waterfall feeding a narrow river# lies #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, sous une #cascade alimentant une rivière étroite# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_ZORAS_FOUNTAIN_RUPEE] = HintText(CustomMessage("They say that at the bottom of a #partially-frozen spring# lies #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, au fond d'une #source partiellement gelée# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_SFM_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath a forest meadow# rests #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, dans #une fontaine sous un bosquet forestier# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath a river# rests #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, dans #une fontaine sous une rivière# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath a a few trees bordering a wide field# rests #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, dans #une fontaine sous quelques arbres bordant un vaste champ# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZD_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath the home of the Zoras# rests #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, dans #une fontaine sous la maison des Zora# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GF_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath the home of thieves# rests #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, dans #une fontaine sous la maison des voleurs# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GRAVEYARD_SHIELD_GRAVE_FAIRY] = HintText(CustomMessage("They say that within #a fountain behind a wall within a grave# rests #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, dans #une fontaine derrière un mur dans une tombe# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_COLOSSUS_OASIS_FAIRY] = HintText(CustomMessage("They say that #restoring water to a dried oasis# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #restaurer l'eau dans un oasis asséché# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the riverside# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #arroser une jeune pousse au bord de la rivière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout near a forest shop# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #arroser une jeune pousse près d'un magasin forestier# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_BEAN_SPROUT_NEAR_BRIDGE_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout near the entrance to the forest# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #arroser une jeune pousse près de l'entrée de la forêt# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_BEAN_SPROUT_NEAR_THEATER_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout above a sylvan theatre# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #arroser une jeune pousse au-dessus d'un théâtre sylvestre# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the lakeside# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #arroser une jeune pousse au bord du lac# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GV_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the side of a canyon# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #arroser une jeune pousse sur le côté d'un canyon# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_COLOSSUS_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout near a temple of the sand# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #arroser une jeune pousse près d'un temple du sable# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GRAVEYARD_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout within a graveyard# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #arroser une jeune pousse dans un cimetière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout within a volcano# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #arroser une jeune pousse dans un volcan# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the moutainside# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #arroser une jeune pousse sur le flanc d'une montagne# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_TOT_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone in the ouskirts of the market# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange en périphérie du marché# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_TOT_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone in the ouskirts of the market# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange en périphérie du marché# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within a volcano# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange dans un volcan# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within a volcano# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange dans un volcan# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone on a mountain cliff face# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sur une falaise de montagne# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone on a mountain cliff face# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sur une falaise de montagne# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_COLOSSUS_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near the temple of the sane# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près du temple du sable# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_COLOSSUS_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near the temple of the sane# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près du temple du sable# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GV_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near a canyon waterfall# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près d'une cascade de canyon# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GV_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near a canyon waterfall# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près d'une cascade de canyon# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GC_MAZE_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone behind a maze of rock# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange derrière un labyrinthe de roches# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GC_MAZE_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone behind a maze of rock# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange derrière un labyrinthe de roches# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GC_MEDIGORON_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near a blacksmith# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près d'un forgeron# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GC_MEDIGORON_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near a blacksmith# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près d'un forgeron# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GRAVEYARD_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking the graveyard# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant le cimetière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GRAVEYARD_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking the graveyard# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant le cimetière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_MALON_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking the path to the castle# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant le chemin du château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_MALON_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking the path to the castle# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant le chemin du château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_ROCK_WALL_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near a secret path to the castle# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près d'un chemin secret vers le château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_ROCK_WALL_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near a secret path to the castle# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près d'un chemin secret vers le château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_STORMS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath the castle# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous le château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_STORMS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath the castle# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous le château révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_DEKU_TREE_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near an ancient tree# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange près d'un arbre ancien révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_DEKU_TREE_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near an ancient tree# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange près d'un arbre ancien révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking a forest village# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking a forest village# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_STORMS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath a forest village# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_STORMS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath a forest village# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_LAB_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking the river feeding a lake# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant la rivière qui alimente un lac révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_LAB_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking the river feeding a lake# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant la rivière qui alimente un lac révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHEAST_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone at the back of a lake# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHEAST_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone at the back of a lake# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHWEST_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone at the back of a lake# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHWEST_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone at the back of a lake# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within a perplexing wood# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange dans un bois déroutant révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within a perplexing wood# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange dans un bois déroutant révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_MAZE_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking a forest maze# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant un labyrinthe forestier révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_MAZE_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking a forest maze# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant un labyrinthe forestier révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_SARIA_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone watching a hiding place in the woods# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surveillant une cachette dans les bois révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_SARIA_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone watching a hiding place in the woods# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surveillant une cachette dans les bois révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZD_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone listening to an aquatic king# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange écoutant un roi aquatique révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZD_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone listening to an aquatic king# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange écoutant un roi aquatique révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZF_FAIRY_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone in the ouskirts of a deap fountain# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange en périphérie d'une profonde fontaine révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZF_FAIRY_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone in the ouskirts of a deap fountain# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange en périphérie d'une profonde fontaine révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZF_JABU_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone watching a guardian of the sea# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surveillant un gardien de la mer révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZF_JABU_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone watching a guardian of the sea# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surveillant un gardien de la mer révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_GROTTOS_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overwatching a river# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant une rivière révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_GROTTOS_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overwatching a river# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant une rivière révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_DOMAIN_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath a waterfall# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous une cascade révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_DOMAIN_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath a waterfall# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous une cascade révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_COW_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone hiding near a cow# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange cachée près d'une vache révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_COW_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone hiding near a cow# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange cachée près d'une vache révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath the entrance to the market# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous l'entrée du marché révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath the entrance to the market# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous l'entrée du marché révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_SOUTHEAST_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath trees guarded by a Peahat# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous des arbres gardés par un Peahat révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_SOUTHEAST_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath trees guarded by a Peahat# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous des arbres gardés par un Peahat révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KAK_OPEN_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath a village at the base of a mountain# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous un village au pied d'une montagne révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KAK_OPEN_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath a village at the base of a mountain# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous un village au pied d'une montagne révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_OPEN_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within a plateau by a river# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange dans un plateau près d'une rivière révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_OPEN_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within a plateau by a river# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange dans un plateau près d'une rivière révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath an escape from the forest# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous une issue de la forêt révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath an escape from the forest# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous une issue de la forêt révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_STORMS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath the entrance to a village within a mountain# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous l'entrée d'un village dans une montagne révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_STORMS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath the entrance to a village within a mountain# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous l'entrée d'un village dans une montagne révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_UPPER_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within the side of a crater# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange dans le côté d'un cratère révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_UPPER_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within the side of a crater# reveals #[[1]]#.", - /*german*/ "!!!", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange dans le côté d'un cratère révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_ISLAND_SUN_FAIRY] = HintText(CustomMessage("They say that #summoning the sun on the lake's island# calls #[[1]]#.", {QM_RED, QM_GREEN})); @@ -1976,43 +1976,43 @@ void StaticData::HintTable_Init_Exclude_Overworld() { hintTextTable[RHT_GROTTO_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a grotto# reveals #[[1]]#.", { QM_RED, QM_GREEN })); hintTextTable[RHT_CRATE_GERUDO_VALLEY] = HintText(CustomMessage("They say that a #crate in Gerudo Valley# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans une vallée# a #[[1]]#.", { QM_RED, QM_GREEN })); hintTextTable[RHT_CRATE_GERUDOS_FORTRESS] = HintText(CustomMessage("They say that a #crate in Gerudo Fortress# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans la Forteresse Gerudo# contient #[[1]]#.", { QM_RED, QM_GREEN })); hintTextTable[RHT_CRATE_WASTELAND] = HintText(CustomMessage("They say that a #crate in Haunted Wasteland# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans le Désert Hanté# contient #[[1]]#.", { QM_RED, QM_GREEN })); hintTextTable[RHT_CRATE_MARKET] = HintText(CustomMessage("They say that a #crate in the Market# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans la Place du Marché# contient #[[1]]#.", { QM_RED, QM_GREEN })); hintTextTable[RHT_CRATE_KAKARIKO_VILLAGE] = HintText(CustomMessage("They say that a #crate in Kakariko Village# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans le Village de Cocorico# contient #[[1]]#.", { QM_RED, QM_GREEN })); hintTextTable[RHT_CRATE_GRAVEYARD] = HintText(CustomMessage("They say that a #crate in the Graveyard# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans le Cimetière# contient #[[1]]#.", { QM_RED, QM_GREEN })); hintTextTable[RHT_CRATE_GORON_CITY] = HintText(CustomMessage("They say that a #crate in Goron City# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans le Village Goron# contient #[[1]]#.", { QM_RED, QM_GREEN })); hintTextTable[RHT_CRATE_DEATH_MOUNTAIN_CRATER] = HintText(CustomMessage("They say that a #crate in Death Mountain Crater# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse sur le Mont du Péril# contient #[[1]]#.", { QM_RED, QM_GREEN })); hintTextTable[RHT_CRATE_LON_LON_RANCH] = HintText(CustomMessage("They say that a #crate in Lon Lon Ranch# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans le Ranch Lon Lon# contient #[[1]]#.", { QM_RED, QM_GREEN })); hintTextTable[RHT_CRATE_LAKESIDE_LABORATORY] = HintText(CustomMessage("They say that a #crate in the Laboratory# contains #[[1]]#.", - /*german*/ "", + /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans un laboratoire# contient #[[1]]#.", { QM_RED, QM_GREEN })); // clang-format on diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp index ba046884964..7bea4d517c1 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp @@ -1962,69 +1962,69 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a gold fragment", /*german*/"ein goldenes Fragment", /*french*/"un fragment d'or")}); // /*spanish*/un fragmento dorado - hintTextTable[RHT_GOHMA_SOUL] = HintText(CustomMessage("the soul of Gohma", /*german*/"die Seele Gohmas", /*french*/""), + hintTextTable[RHT_GOHMA_SOUL] = HintText(CustomMessage("the soul of Gohma", /*german*/"die Seele Gohmas", /*french*/TODO_TRANSLATE), { - CustomMessage("something webbed", /*german*/"etwas Verwobenes", /*french*/"") + CustomMessage("something webbed", /*german*/"etwas Verwobenes", /*french*/TODO_TRANSLATE) }, { - CustomMessage("an invasive soul", /*german*/"eine invasive Seele", /*french*/""), - CustomMessage("some spider essence", /*german*/"etwas spinnenartige Essenz", /*french*/"")}); + CustomMessage("an invasive soul", /*german*/"eine invasive Seele", /*french*/TODO_TRANSLATE), + CustomMessage("some spider essence", /*german*/"etwas spinnenartige Essenz", /*french*/TODO_TRANSLATE)}); - hintTextTable[RHT_KING_DODONGO_SOUL] = HintText(CustomMessage("the soul of King Dodongo", /*german*/"die Seele König Dodongos", /*french*/""), + hintTextTable[RHT_KING_DODONGO_SOUL] = HintText(CustomMessage("the soul of King Dodongo", /*german*/"die Seele König Dodongos", /*french*/TODO_TRANSLATE), { - CustomMessage("something explosive", /*german*/"etwas Explosives", /*french*/"") + CustomMessage("something explosive", /*german*/"etwas Explosives", /*french*/TODO_TRANSLATE) }, { - CustomMessage("a royal soul", /*german*/"eine royale Seele", /*french*/""), - CustomMessage("some reptile essence", /*german*/"etwas reptilienartige Essenz", /*french*/"")}); + CustomMessage("a royal soul", /*german*/"eine royale Seele", /*french*/TODO_TRANSLATE), + CustomMessage("some reptile essence", /*german*/"etwas reptilienartige Essenz", /*french*/TODO_TRANSLATE)}); - hintTextTable[RHT_BARINADE_SOUL] = HintText(CustomMessage("the soul of Barinade", /*german*/"die Seele Barinades", /*french*/""), + hintTextTable[RHT_BARINADE_SOUL] = HintText(CustomMessage("the soul of Barinade", /*german*/"die Seele Barinades", /*french*/TODO_TRANSLATE), { - CustomMessage("something fishy", /*german*/"etwas Fischiges", /*french*/"") + CustomMessage("something fishy", /*german*/"etwas Fischiges", /*french*/TODO_TRANSLATE) }, { - CustomMessage("an infectuous soul", /*german*/"eine infektiöse Seele", /*french*/""), - CustomMessage("some parasitic essence", /*german*/"etwas parasitäre Essenz", /*french*/"")}); + CustomMessage("an infectuous soul", /*german*/"eine infektiöse Seele", /*french*/TODO_TRANSLATE), + CustomMessage("some parasitic essence", /*german*/"etwas parasitäre Essenz", /*french*/TODO_TRANSLATE)}); - hintTextTable[RHT_PHANTOM_GANON_SOUL] = HintText(CustomMessage("the soul of Phantom Ganon", /*german*/"die Seele Phantom-Ganons", /*french*/""), + hintTextTable[RHT_PHANTOM_GANON_SOUL] = HintText(CustomMessage("the soul of Phantom Ganon", /*german*/"die Seele Phantom-Ganons", /*french*/TODO_TRANSLATE), { - CustomMessage("something spectral", /*german*/"etwas Spektrales", /*french*/"") + CustomMessage("something spectral", /*german*/"etwas Spektrales", /*french*/TODO_TRANSLATE) }, { - CustomMessage("a duplicate soul", /*german*/"eine duplizierte Seele", /*french*/""), + CustomMessage("a duplicate soul", /*german*/"eine duplizierte Seele", /*french*/TODO_TRANSLATE), - CustomMessage("some illusionary essence", /*german*/"etwas illusionäre Essenz", /*french*/"")}); + CustomMessage("some illusionary essence", /*german*/"etwas illusionäre Essenz", /*french*/TODO_TRANSLATE)}); - hintTextTable[RHT_VOLVAGIA_SOUL] = HintText(CustomMessage("the soul of Volvagia", /*german*/"die Seele Volvagias", /*french*/""), + hintTextTable[RHT_VOLVAGIA_SOUL] = HintText(CustomMessage("the soul of Volvagia", /*german*/"die Seele Volvagias", /*french*/TODO_TRANSLATE), { - CustomMessage("something hot", /*german*/"etwas Heißes", /*french*/"") + CustomMessage("something hot", /*german*/"etwas Heißes", /*french*/TODO_TRANSLATE) }, { - CustomMessage("a draconic soul", /*german*/"eine drakonische Seele", /*french*/""), - CustomMessage("some magmatic essence", /*german*/"etwas magmatische Essenz", /*french*/"")}); + CustomMessage("a draconic soul", /*german*/"eine drakonische Seele", /*french*/TODO_TRANSLATE), + CustomMessage("some magmatic essence", /*german*/"etwas magmatische Essenz", /*french*/TODO_TRANSLATE)}); - hintTextTable[RHT_MORPHA_SOUL] = HintText(CustomMessage("the soul of Morpha", /*german*/"die Seele Morphas", /*french*/""), + hintTextTable[RHT_MORPHA_SOUL] = HintText(CustomMessage("the soul of Morpha", /*german*/"die Seele Morphas", /*french*/TODO_TRANSLATE), { - CustomMessage("something wet", /*german*/"etwas Nasses", /*french*/"") + CustomMessage("something wet", /*german*/"etwas Nasses", /*french*/TODO_TRANSLATE) }, { - CustomMessage("an aquatic soul", /*german*/"eine aquatische Seele", /*french*/""), - CustomMessage("some liquid essence", /*german*/"etwas flüssige Essenz", /*french*/"")}); + CustomMessage("an aquatic soul", /*german*/"eine aquatische Seele", /*french*/TODO_TRANSLATE), + CustomMessage("some liquid essence", /*german*/"etwas flüssige Essenz", /*french*/TODO_TRANSLATE)}); - hintTextTable[RHT_BONGO_BONGO_SOUL] = HintText(CustomMessage("the soul of Bongo Bongo", /*german*/"die Seele Bongo Bongos", /*french*/""), + hintTextTable[RHT_BONGO_BONGO_SOUL] = HintText(CustomMessage("the soul of Bongo Bongo", /*german*/"die Seele Bongo Bongos", /*french*/TODO_TRANSLATE), { - CustomMessage("something dark", /*german*/"etwas Dunkles", /*french*/"") + CustomMessage("something dark", /*german*/"etwas Dunkles", /*french*/TODO_TRANSLATE) }, { - CustomMessage("a shadowy soul", /*german*/"eine schattige Seele", /*french*/""), - CustomMessage("some handy essence", /*german*/"etwas praktische Essenz", /*french*/"")}); + CustomMessage("a shadowy soul", /*german*/"eine schattige Seele", /*french*/TODO_TRANSLATE), + CustomMessage("some handy essence", /*german*/"etwas praktische Essenz", /*french*/TODO_TRANSLATE)}); - hintTextTable[RHT_TWINROVA_SOUL] = HintText(CustomMessage("the soul of Twinrova", /*german*/"die Seele Twinrovas", /*french*/""), + hintTextTable[RHT_TWINROVA_SOUL] = HintText(CustomMessage("the soul of Twinrova", /*german*/"die Seele Twinrovas", /*french*/TODO_TRANSLATE), { - CustomMessage("something spiritual", /*german*/"etwas Spirituelles", /*french*/"") + CustomMessage("something spiritual", /*german*/"etwas Spirituelles", /*french*/TODO_TRANSLATE) }, { - CustomMessage("old souls", /*german*/"alte Seelen", /*french*/""), - CustomMessage("twin essences", /*german*/"Zwillingsessenzen", /*french*/"")}); + CustomMessage("old souls", /*german*/"alte Seelen", /*french*/TODO_TRANSLATE), + CustomMessage("twin essences", /*german*/"Zwillingsessenzen", /*french*/TODO_TRANSLATE)}); - hintTextTable[RHT_GANON_SOUL] = HintText(CustomMessage("the soul of Ganon", /*german*/"die Seele Ganons", /*french*/""), + hintTextTable[RHT_GANON_SOUL] = HintText(CustomMessage("the soul of Ganon", /*german*/"die Seele Ganons", /*french*/TODO_TRANSLATE), { - CustomMessage("something strong", /*german*/"etwas Starkes", /*french*/"") + CustomMessage("something strong", /*german*/"etwas Starkes", /*french*/TODO_TRANSLATE) }, { - CustomMessage("an evil soul", /*german*/"eine böse Seele", /*french*/""), - CustomMessage("some powerful essence", /*german*/"etwas mächtige Essenz", /*french*/"")}); + CustomMessage("an evil soul", /*german*/"eine böse Seele", /*french*/TODO_TRANSLATE), + CustomMessage("some powerful essence", /*german*/"etwas mächtige Essenz", /*french*/TODO_TRANSLATE)}); hintTextTable[RHT_OCARINA_A_BUTTON] = HintText(CustomMessage("an Ocarina A Button", /*german*/"eine Okarina A Taste", /*french*/"la Touche A de l'Ocarina"), // /*spanish*/un botón A de Ocarina @@ -2098,53 +2098,22 @@ void StaticData::HintTable_Init_Item() { { CustomMessage("a master unlocker", /*german*/ "ein Meisterentsperrer", /*french*/ "un Kit de Déverrouillage") }); // /*spanish*/un desbloqueador maestro - hintTextTable[RHT_QUIVER_INF] = HintText(CustomMessage("an infinite Quiver", /*german*/"ein unendlicher Köcher", /*french*/"un Carquois Infini"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + //RANDOTODO if these are ever used for anything other than name, they want abscure and ambiguous hints + hintTextTable[RHT_QUIVER_INF] = HintText(CustomMessage("an infinite Quiver", /*german*/"ein unendlicher Köcher", /*french*/"un Carquois Infini")); - hintTextTable[RHT_BOMB_BAG_INF] = HintText(CustomMessage("an infinite Bomb Bag", /*german*/"eine unendliche Bombentasche", /*french*/"un Sac de Bombe sans fond"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_BOMB_BAG_INF] = HintText(CustomMessage("an infinite Bomb Bag", /*german*/"eine unendliche Bombentasche", /*french*/"un Sac de Bombe sans fond")); - hintTextTable[RHT_BULLET_BAG_INF] = HintText(CustomMessage("an infinite Bullet Bag", /*german*/"eine unendliche Samentasche", /*french*/"un Sac de Graine sans fond"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_BULLET_BAG_INF] = HintText(CustomMessage("an infinite Bullet Bag", /*german*/"eine unendliche Samentasche", /*french*/"un Sac de Graine sans fond")); - hintTextTable[RHT_STICK_UPGRADE_INF] = HintText(CustomMessage("infinite Deku Sticks", /*german*/"unendliche Deku-Stäbe", /*french*/"des Bâtons Mojo illimités"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_STICK_UPGRADE_INF] = HintText(CustomMessage("infinite Deku Sticks", /*german*/"unendliche Deku-Stäbe", /*french*/"des Bâtons Mojo illimités")); - hintTextTable[RHT_NUT_UPGRADE_INF] = HintText(CustomMessage("infinite Deku Nuts", /*german*/"unendliche Deku-Nüsse", /*french*/"des Noix Mojo illimitées"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_NUT_UPGRADE_INF] = HintText(CustomMessage("infinite Deku Nuts", /*german*/"unendliche Deku-Nüsse", /*french*/"des Noix Mojo illimitées")); - hintTextTable[RHT_MAGIC_INF] = HintText(CustomMessage("unlimited Magic", /*german*/"unendliche Magie", /*french*/"de la Magie infinie"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_MAGIC_INF] = HintText(CustomMessage("unlimited Magic", /*german*/"unendliche Magie", /*french*/"de la Magie infinie")); - hintTextTable[RHT_BOMBCHU_INF] = HintText(CustomMessage("infinite Bombchus", /*german*/"unendliche Krabbelminen", /*french*/"des Missiles Teigneux illimités"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_BOMBCHU_INF] = HintText(CustomMessage("infinite Bombchus", /*german*/"unendliche Krabbelminen", /*french*/"des Missiles Teigneux illimités")); - hintTextTable[RHT_WALLET_INF] = HintText(CustomMessage("an infinite Wallet", /*german*/"eine unendliche Geldbörse", /*french*/"une Bourse sans fond"), - { - CustomMessage("", /*german*/"!!!", /*french*/"!!!"), - }, { - CustomMessage("", /*german*/"!!!", /*french*/"!!!")}); + hintTextTable[RHT_WALLET_INF] = HintText(CustomMessage("an infinite Wallet", /*german*/"eine unendliche Geldbörse", /*french*/"une Bourse sans fond")); hintTextTable[RHT_EPONA] = HintText(CustomMessage("Epona", /*german*/"Epona", /*french*/"Epona"), // /*spanish*/a Epona diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp index 8224865ac6e..929314e25a1 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp @@ -809,6 +809,7 @@ void InitTrickNames() { Text{ "Shiny Rock", "Caiiloux Brillant", "Shiny Rock" }, }; + // TODO_TRANSLATE trickNameTable[RG_GOHMA_SOUL] = { Text{ "Spider Sense", "", "" }, Text{ "Deku Spirit", "", "" }, diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index d8113edd3ad..54a84f603ff 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -172,53 +172,53 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_GANONS_CASTLE_SMALL_KEY].SetCustomDrawFunc(Randomizer_DrawSmallKey); itemTable[RG_TREASURE_GAME_SMALL_KEY] = Item(RG_TREASURE_GAME_SMALL_KEY, Text{ "Chest Game Small Key", "Petite Clé du jeu la Chasse-aux-Trésors", "Kleiner Schlüssel für das Truhenspiel" }, ITEMTYPE_SMALLKEY, GI_DOOR_KEY, true, LOGIC_TREASURE_GAME_KEYS, RHT_TREASURE_GAME_SMALL_KEY, ITEM_KEY_SMALL, OBJECT_GI_KEY, GID_KEY_SMALL, 0xF3, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_NONE); itemTable[RG_TREASURE_GAME_SMALL_KEY].SetCustomDrawFunc(Randomizer_DrawSmallKey); - itemTable[RG_GUARD_HOUSE_KEY] = Item(RG_GUARD_HOUSE_KEY, Text{ "Guard House Key", "", "Schlüssel für das Haus der Wachen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_GUARD_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_GUARD_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_GUARD_HOUSE_KEY] = Item(RG_GUARD_HOUSE_KEY, Text{ "Guard House Key", TODO_TRANSLATE, "Schlüssel für das Haus der Wachen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_GUARD_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_GUARD_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_GUARD_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_MARKET_BAZAAR_KEY] = Item(RG_MARKET_BAZAAR_KEY, Text{ "Market Bazaar Key", "", "Schlüssel für den Basar des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_BAZAAR_KEY, RHT_OVERWORLD_KEY, RG_MARKET_BAZAAR_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_MARKET_BAZAAR_KEY] = Item(RG_MARKET_BAZAAR_KEY, Text{ "Market Bazaar Key", TODO_TRANSLATE, "Schlüssel für den Basar des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_BAZAAR_KEY, RHT_OVERWORLD_KEY, RG_MARKET_BAZAAR_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_MARKET_BAZAAR_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_MARKET_POTION_SHOP_KEY] = Item(RG_MARKET_POTION_SHOP_KEY, Text{ "Market Potion Shop Key", "", "Schlüssel für den Magie-Laden des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_MARKET_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_MARKET_POTION_SHOP_KEY] = Item(RG_MARKET_POTION_SHOP_KEY, Text{ "Market Potion Shop Key", TODO_TRANSLATE, "Schlüssel für den Magie-Laden des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_MARKET_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_MARKET_POTION_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_MASK_SHOP_KEY] = Item(RG_MASK_SHOP_KEY, Text{ "Mask Shop Key", "", "Schlüssel für den Maskenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MASK_SHOP_KEY, RHT_OVERWORLD_KEY, RG_MASK_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_MASK_SHOP_KEY] = Item(RG_MASK_SHOP_KEY, Text{ "Mask Shop Key", TODO_TRANSLATE, "Schlüssel für den Maskenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MASK_SHOP_KEY, RHT_OVERWORLD_KEY, RG_MASK_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_MASK_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_MARKET_SHOOTING_GALLERY_KEY] = Item(RG_MARKET_SHOOTING_GALLERY_KEY, Text{ "Market Shooting Gallery Key", "", "Schlüssel für die Schießbude des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_SHOOTING_GALLERY_KEY, RHT_OVERWORLD_KEY, RG_MARKET_SHOOTING_GALLERY_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_MARKET_SHOOTING_GALLERY_KEY] = Item(RG_MARKET_SHOOTING_GALLERY_KEY, Text{ "Market Shooting Gallery Key", TODO_TRANSLATE, "Schlüssel für die Schießbude des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_SHOOTING_GALLERY_KEY, RHT_OVERWORLD_KEY, RG_MARKET_SHOOTING_GALLERY_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_MARKET_SHOOTING_GALLERY_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_BOMBCHU_BOWLING_KEY] = Item(RG_BOMBCHU_BOWLING_KEY, Text{ "Bombchu Bowling Alley Key", "", "Schlüssel für die Minenbowlingbahn" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOMBCHU_BOWLING_KEY, RHT_OVERWORLD_KEY, RG_BOMBCHU_BOWLING_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_BOMBCHU_BOWLING_KEY] = Item(RG_BOMBCHU_BOWLING_KEY, Text{ "Bombchu Bowling Alley Key", TODO_TRANSLATE, "Schlüssel für die Minenbowlingbahn" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOMBCHU_BOWLING_KEY, RHT_OVERWORLD_KEY, RG_BOMBCHU_BOWLING_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_BOMBCHU_BOWLING_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_TREASURE_CHEST_GAME_BUILDING_KEY] = Item(RG_TREASURE_CHEST_GAME_BUILDING_KEY, Text{ "Treasure Chest Game Building Key", "", "Schlüssel für das Haus des Schatzkisten-Pokers" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_TREASURE_CHEST_GAME_BUILDING_KEY,RHT_OVERWORLD_KEY, RG_TREASURE_CHEST_GAME_BUILDING_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_TREASURE_CHEST_GAME_BUILDING_KEY] = Item(RG_TREASURE_CHEST_GAME_BUILDING_KEY, Text{ "Treasure Chest Game Building Key", TODO_TRANSLATE, "Schlüssel für das Haus des Schatzkisten-Pokers" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_TREASURE_CHEST_GAME_BUILDING_KEY,RHT_OVERWORLD_KEY, RG_TREASURE_CHEST_GAME_BUILDING_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_TREASURE_CHEST_GAME_BUILDING_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_BOMBCHU_SHOP_KEY] = Item(RG_BOMBCHU_SHOP_KEY, Text{ "Bombchu Shop Key", "", "Schlüssel für den Krabbelminenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOMBCHU_SHOP_KEY, RHT_OVERWORLD_KEY, RG_BOMBCHU_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_BOMBCHU_SHOP_KEY] = Item(RG_BOMBCHU_SHOP_KEY, Text{ "Bombchu Shop Key", TODO_TRANSLATE, "Schlüssel für den Krabbelminenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOMBCHU_SHOP_KEY, RHT_OVERWORLD_KEY, RG_BOMBCHU_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_BOMBCHU_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_RICHARDS_HOUSE_KEY] = Item(RG_RICHARDS_HOUSE_KEY, Text{ "Richard's House Key", "", "Schlüssel für das Haus von Richard" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_RICHARDS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_RICHARDS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_RICHARDS_HOUSE_KEY] = Item(RG_RICHARDS_HOUSE_KEY, Text{ "Richard's House Key", TODO_TRANSLATE, "Schlüssel für das Haus von Richard" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_RICHARDS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_RICHARDS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_RICHARDS_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_ALLEY_HOUSE_KEY] = Item(RG_ALLEY_HOUSE_KEY, Text{ "Alley House Key", "", "Schlüssel für das Gassenhaus" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_ALLEY_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_ALLEY_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_ALLEY_HOUSE_KEY] = Item(RG_ALLEY_HOUSE_KEY, Text{ "Alley House Key", TODO_TRANSLATE, "Schlüssel für das Gassenhaus" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_ALLEY_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_ALLEY_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_ALLEY_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_KAK_BAZAAR_KEY] = Item(RG_KAK_BAZAAR_KEY, Text{ "Kakariko Bazaar Key", "", "Schlüssel für den Basar von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_BAZAAR_KEY, RHT_OVERWORLD_KEY, RG_KAK_BAZAAR_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_KAK_BAZAAR_KEY] = Item(RG_KAK_BAZAAR_KEY, Text{ "Kakariko Bazaar Key", TODO_TRANSLATE, "Schlüssel für den Basar von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_BAZAAR_KEY, RHT_OVERWORLD_KEY, RG_KAK_BAZAAR_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_KAK_BAZAAR_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_KAK_POTION_SHOP_KEY] = Item(RG_KAK_POTION_SHOP_KEY, Text{ "Kakariko Potion Shop Key", "", "Schlüssel für den Magie-Laden von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_KAK_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_KAK_POTION_SHOP_KEY] = Item(RG_KAK_POTION_SHOP_KEY, Text{ "Kakariko Potion Shop Key", TODO_TRANSLATE, "Schlüssel für den Magie-Laden von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_KAK_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_KAK_POTION_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_BOSS_HOUSE_KEY] = Item(RG_BOSS_HOUSE_KEY, Text{ "Boss's House Key", "", "Schlüssel für das Haus des Chefs" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOSS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_BOSS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_BOSS_HOUSE_KEY] = Item(RG_BOSS_HOUSE_KEY, Text{ "Boss's House Key", TODO_TRANSLATE, "Schlüssel für das Haus des Chefs" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOSS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_BOSS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_BOSS_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_GRANNYS_POTION_SHOP_KEY] = Item(RG_GRANNYS_POTION_SHOP_KEY, Text{ "Granny's Potion Shop Key", "", "Schlüssel für Asas Hexenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_GRANNYS_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_GRANNYS_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_GRANNYS_POTION_SHOP_KEY] = Item(RG_GRANNYS_POTION_SHOP_KEY, Text{ "Granny's Potion Shop Key", TODO_TRANSLATE, "Schlüssel für Asas Hexenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_GRANNYS_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_GRANNYS_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_GRANNYS_POTION_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_SKULLTULA_HOUSE_KEY] = Item(RG_SKULLTULA_HOUSE_KEY, Text{ "Skulltula House Key", "", "Schlüssel für das Skulltula-Haus" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_SKULLTULA_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_SKULLTULA_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_SKULLTULA_HOUSE_KEY] = Item(RG_SKULLTULA_HOUSE_KEY, Text{ "Skulltula House Key", TODO_TRANSLATE, "Schlüssel für das Skulltula-Haus" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_SKULLTULA_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_SKULLTULA_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_SKULLTULA_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_IMPAS_HOUSE_KEY] = Item(RG_IMPAS_HOUSE_KEY, Text{ "Impa's House Key", "", "Schlüssel für das Haus von Impa" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_IMPAS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_IMPAS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_IMPAS_HOUSE_KEY] = Item(RG_IMPAS_HOUSE_KEY, Text{ "Impa's House Key", TODO_TRANSLATE, "Schlüssel für das Haus von Impa" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_IMPAS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_IMPAS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_IMPAS_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_WINDMILL_KEY] = Item(RG_WINDMILL_KEY, Text{ "Windmill Key", "", "Schlüssel für die Windmühle" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_WINDMILL_KEY, RHT_OVERWORLD_KEY, RG_WINDMILL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_WINDMILL_KEY] = Item(RG_WINDMILL_KEY, Text{ "Windmill Key", TODO_TRANSLATE, "Schlüssel für die Windmühle" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_WINDMILL_KEY, RHT_OVERWORLD_KEY, RG_WINDMILL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_WINDMILL_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_KAK_SHOOTING_GALLERY_KEY] = Item(RG_KAK_SHOOTING_GALLERY_KEY, Text{ "Kakariko Shooting Gallery Key", "", "Schlüssel für die Schießbude von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_SHOOTING_GALLERY_KEY, RHT_OVERWORLD_KEY, RG_KAK_SHOOTING_GALLERY_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_KAK_SHOOTING_GALLERY_KEY] = Item(RG_KAK_SHOOTING_GALLERY_KEY, Text{ "Kakariko Shooting Gallery Key", TODO_TRANSLATE, "Schlüssel für die Schießbude von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_SHOOTING_GALLERY_KEY, RHT_OVERWORLD_KEY, RG_KAK_SHOOTING_GALLERY_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_KAK_SHOOTING_GALLERY_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_DAMPES_HUT_KEY] = Item(RG_DAMPES_HUT_KEY, Text{ "Dampe's Hut Key", "", "Schlüssel für die Hütte von Boris" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_DAMPES_HUT_KEY, RHT_OVERWORLD_KEY, RG_DAMPES_HUT_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_DAMPES_HUT_KEY] = Item(RG_DAMPES_HUT_KEY, Text{ "Dampe's Hut Key", TODO_TRANSLATE, "Schlüssel für die Hütte von Boris" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_DAMPES_HUT_KEY, RHT_OVERWORLD_KEY, RG_DAMPES_HUT_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_DAMPES_HUT_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_TALONS_HOUSE_KEY] = Item(RG_TALONS_HOUSE_KEY, Text{ "Talon's House Key", "", "Schlüssel für das Haus von Talon" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_TALONS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_TALONS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_TALONS_HOUSE_KEY] = Item(RG_TALONS_HOUSE_KEY, Text{ "Talon's House Key", TODO_TRANSLATE, "Schlüssel für das Haus von Talon" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_TALONS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_TALONS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_TALONS_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_STABLES_KEY] = Item(RG_STABLES_KEY, Text{ "Stables Key", "", "Schlüssel für die Ställe" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_STABLES_KEY, RHT_OVERWORLD_KEY, RG_STABLES_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_STABLES_KEY] = Item(RG_STABLES_KEY, Text{ "Stables Key", TODO_TRANSLATE, "Schlüssel für die Ställe" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_STABLES_KEY, RHT_OVERWORLD_KEY, RG_STABLES_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_STABLES_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_BACK_TOWER_KEY] = Item(RG_BACK_TOWER_KEY, Text{ "Back Tower Key", "", "Schlüssel für den hinteren Turm" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BACK_TOWER_KEY, RHT_OVERWORLD_KEY, RG_BACK_TOWER_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_BACK_TOWER_KEY] = Item(RG_BACK_TOWER_KEY, Text{ "Back Tower Key", TODO_TRANSLATE, "Schlüssel für den hinteren Turm" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BACK_TOWER_KEY, RHT_OVERWORLD_KEY, RG_BACK_TOWER_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_BACK_TOWER_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_HYLIA_LAB_KEY] = Item(RG_HYLIA_LAB_KEY, Text{ "Hylia Laboratory Key", "", "Schlüssel für das Hylia-Labor" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_HYLIA_LAB_KEY, RHT_OVERWORLD_KEY, RG_HYLIA_LAB_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_HYLIA_LAB_KEY] = Item(RG_HYLIA_LAB_KEY, Text{ "Hylia Laboratory Key", TODO_TRANSLATE, "Schlüssel für das Hylia-Labor" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_HYLIA_LAB_KEY, RHT_OVERWORLD_KEY, RG_HYLIA_LAB_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_HYLIA_LAB_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_FISHING_HOLE_KEY] = Item(RG_FISHING_HOLE_KEY, Text{ "Fishing Hole Key", "", "Schlüssel für den Fischweiher" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_FISHING_HOLE_KEY, RHT_OVERWORLD_KEY, RG_FISHING_HOLE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_FISHING_HOLE_KEY] = Item(RG_FISHING_HOLE_KEY, Text{ "Fishing Hole Key", TODO_TRANSLATE, "Schlüssel für den Fischweiher" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_FISHING_HOLE_KEY, RHT_OVERWORLD_KEY, RG_FISHING_HOLE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_FISHING_HOLE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); // Key Rings itemTable[RG_FOREST_TEMPLE_KEY_RING] = Item(RG_FOREST_TEMPLE_KEY_RING, Text{ "Forest Temple Key Ring", "Trousseau du Temple de la Forêt", "Schlüsselbund für den Waldtempel" }, ITEMTYPE_SMALLKEY, 0xD5, true, LOGIC_FOREST_TEMPLE_KEYS, RHT_FOREST_TEMPLE_KEY_RING, RG_FOREST_TEMPLE_KEY_RING, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index d567a3d8e24..d3ed83a26aa 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -472,11 +472,10 @@ void Randomizer::LoadMerchantMessages() { // prompted buy/don't buy CustomMessageManager::Instance->CreateMessage( Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM, - CustomMessage( - "\x08#[[1]]# #[[2]]_Rupees#&Special deal! #ONE LEFT#!&Get it while it lasts!\x0A\x02", - "\x08#[[1]]# #[[2]]_Rubine#&Sonderangebot! #NUR NOCH EINES VERFÜGBAR#!&Beeilen Sie sich!\x0A\x02", - "\x08#[[1]]# #[[2]]_Rubis#&Offre spéciale! #DERNIER EN STOCK#!&Faites vite!\x0A\x02", - { QM_GREEN, QM_YELLOW, QM_RED })); + CustomMessage("\x08#[[1]]# #[[2]]_Rupees#&Special deal! #ONE LEFT#!&Get it while it lasts!\x0A\x02", + "\x08#[[1]]# #[[2]]_Rubine#&#NUR NOCH EINES VERFÜGBAR#!&Beeilen Sie sich!\x0A\x02", + "\x08#[[1]]# #[[2]]_Rubis#&#DERNIER EN STOCK#!&Faites vite!\x0A\x02", + { QM_GREEN, QM_YELLOW, QM_RED })); CustomMessageManager::Instance->CreateMessage( Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM_CONFIRM, From 98146c29f9a4acc341bd020325c598d4364011ee Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Mon, 7 Apr 2025 17:24:24 -0400 Subject: [PATCH 014/157] fix blank names in spoiler (#5329) * fix blank names in spoiler * replace a couple more `.english`/`.french` with `.GetEnglish()`/`.GetFrench()` --- .../Enhancements/randomizer/3drando/spoiler_log.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp index 7938d86654f..a1c91277397 100644 --- a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp @@ -267,10 +267,10 @@ static void WriteAllLocations() { switch (gSaveContext.language) { case 0: default: - placedItemName = location->GetPlacedItemName().english; + placedItemName = location->GetPlacedItemName().GetEnglish(); break; case 2: - placedItemName = location->GetPlacedItemName().french; + placedItemName = location->GetPlacedItemName().GetFrench(); break; } @@ -303,18 +303,18 @@ static void WriteAllLocations() { ["model"] = Rando::StaticData::RetrieveItem( ctx->overrides[location->GetRandomizerCheck()].LooksLike()) .GetName() - .english; + .GetEnglish(); jsonData["locations"][Rando::StaticData::GetLocation(location->GetRandomizerCheck())->GetName()] - ["trickName"] = ctx->overrides[location->GetRandomizerCheck()].GetTrickName().english; + ["trickName"] = ctx->overrides[location->GetRandomizerCheck()].GetTrickName().GetEnglish(); break; case 2: jsonData["locations"][Rando::StaticData::GetLocation(location->GetRandomizerCheck())->GetName()] ["model"] = Rando::StaticData::RetrieveItem( ctx->overrides[location->GetRandomizerCheck()].LooksLike()) .GetName() - .french; + .GetFrench(); jsonData["locations"][Rando::StaticData::GetLocation(location->GetRandomizerCheck())->GetName()] - ["trickName"] = ctx->overrides[location->GetRandomizerCheck()].GetTrickName().french; + ["trickName"] = ctx->overrides[location->GetRandomizerCheck()].GetTrickName().GetFrench(); break; } } From 4acbf799cc8bf1f22135e70ecb3589034ebd02b1 Mon Sep 17 00:00:00 2001 From: PurpleHato Date: Tue, 8 Apr 2025 00:34:56 +0200 Subject: [PATCH 015/157] French Rando : Add missing text + some tweaks (#5327) * Should cover everything, I hope * One last * final tweaks for real this time * Update soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp Co-authored-by: Extloga <141232749+Extloga@users.noreply.github.com> * Update soh/soh/Enhancements/randomizer/3drando/shops.cpp Co-authored-by: Extloga <141232749+Extloga@users.noreply.github.com> * Update soh/soh/Enhancements/randomizer/3drando/shops.cpp Co-authored-by: Extloga <141232749+Extloga@users.noreply.github.com> * TODO_TRANSLATE is a macro no a string Hato --------- Co-authored-by: Extloga <141232749+Extloga@users.noreply.github.com> --- .../randomizer/3drando/hint_list.cpp | 25 ++--- .../hint_list/hint_list_exclude_dungeon.cpp | 20 ++-- .../hint_list/hint_list_exclude_overworld.cpp | 18 ++-- .../3drando/hint_list/hint_list_item.cpp | 72 +++++++------- .../Enhancements/randomizer/3drando/shops.cpp | 98 +++++++++---------- soh/soh/Enhancements/randomizer/item_list.cpp | 48 ++++----- .../Enhancements/randomizer/randomizer.cpp | 4 +- 7 files changed, 148 insertions(+), 137 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp index bdfaaef7779..90ddcef0650 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp @@ -1663,11 +1663,11 @@ void StaticData::HintTable_Init() { hintTextTable[RHT_JUNK52] = HintText(CustomMessage("They say that the Nocturne of Shadow can bring you very close to Ganon.", /*german*/ TODO_TRANSLATE, - /*french*/ "Selon moi, le nocturne de l'ombre peut t'amener très près de Ganon.")); + /*french*/ "Selon moi, le Nocturne de l'Ombre peut t'amener très près de Ganon.")); hintTextTable[RHT_JUNK53] = HintText(CustomMessage("They say that Twinrova always casts the same spell the first three times.", /*german*/ TODO_TRANSLATE, - /*french*/ "Selon moi, Twinrova lance toujours les mêmes trois premiers sorts.")); + /*french*/ "Selon moi, le Duo Maléfique lance toujours les mêmes trois premiers sorts.")); hintTextTable[RHT_JUNK54] = HintText(CustomMessage("They say that the nightly builds may be unstable.", /*german*/ TODO_TRANSLATE, @@ -1722,11 +1722,11 @@ void StaticData::HintTable_Init() { hintTextTable[RHT_JUNK64] = HintText(CustomMessage("They say Greg is special.", /*german*/ TODO_TRANSLATE, - /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + /*french*/ "Selon moi, Greg est spécial.")); hintTextTable[RHT_JUNK65] = HintText(CustomMessage("They say the longer the Goron's neck, the wiser they are.", /*german*/ TODO_TRANSLATE, - /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + /*french*/ "Selon moi, plus le cou des Gorons est long, plus ils sont sage." )); hintTextTable[RHT_JUNK66] = HintText(CustomMessage("They say this ship is what all true gamers strive for.", /*german*/ TODO_TRANSLATE, @@ -1734,23 +1734,23 @@ void StaticData::HintTable_Init() { hintTextTable[RHT_JUNK67] = HintText(CustomMessage("They say that Glowsticks can be found in the Raveyard.", /*german*/ TODO_TRANSLATE, - /*french*/ "On peut trouver des Bâtons Lumineux sur le dancefloor du cimetière.")); + /*french*/ "Selon moi, on peut trouver des Bâtons Lumineux sur le dancefloor du cimetière.")); hintTextTable[RHT_JUNK68] = HintText(CustomMessage("They say @'s uncle works for Nintendo.", /*german*/ TODO_TRANSLATE, - /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + /*french*/ "Selon moi, l'oncle de @ travaille chez Nintendo.")); hintTextTable[RHT_JUNK69] = HintText(CustomMessage("They say pulling all gravestones in the graveyard leads to something magical.", /*german*/ TODO_TRANSLATE, - /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + /*french*/ "Selon moi, tirer toutes les tombes du Cimetière déclanche un truc magique.")); hintTextTable[RHT_JUNK70] = HintText(CustomMessage("They say holding L while pausing makes you win the game.", /*german*/ TODO_TRANSLATE, - /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + /*french*/ "Selon moi, maintenir L pendant que vous appuyez sur START vous permet de terminer le jeu.")); hintTextTable[RHT_JUNK71] = HintText(CustomMessage("They say @'s body is ready.", /*german*/ TODO_TRANSLATE, - /*french*/ HINT_TEXT_NEEDS_TRANSLATION_FR)); + /*french*/ "Selon moi, ce junk hint ne se traduirait pas bien en français.")); /*-------------------------- | DUNGEON HINT TEXT | @@ -2219,13 +2219,16 @@ void StaticData::HintTable_Init() { {QM_RED})); // /*spanish*/Ja, ja, ja... Nunca me derrotarás reflejando mis esferas de energía y desplegando la flecha de luz de #[[1]]#! - hintTextTable[RHT_GANONDORF_HINT_MS_ONLY] = HintText(CustomMessage("Ha ha ha... You'll never defeat me, drop a castle on me and finish me off with the sacred blade from #[[2]]#!", {QM_RED})); // TODO_TRANSLATE + hintTextTable[RHT_GANONDORF_HINT_MS_ONLY] = HintText(CustomMessage("Ha ha ha... You'll never defeat me, drop a castle on me and finish me off with the sacred blade from #[[2]]#!", + /*german*/ TODO_TRANSLATE, + /*french*/ "Ha ha ha... Pauvre fou! Tu ne pourras jamais me vaincre sans l'Épée de Légende cachée dans #[[2]]#!", + {QM_RED})); hintTextTable[RHT_GANONDORF_HINT_LA_AND_MS] = HintText(CustomMessage("Ha ha ha... You'll never beat me by reflecting my lightning bolts and unleashing the arrows from #[[1]]#!" "^And even if you do, you'll never find the legendary blade hidden in #[[2]]#!", /*german*/ TODO_TRANSLATE, /*french*/ "Ha ha ha... Pauvre fou! Tu ne pourras jamais me vaincre sans les flèches que j'ai cachées dans #[[1]]#!" - "^Et même si tu les trouves, tu ne touveras jamais l'épée de légende cachée dans #[[2]]#!", + "^Et même si tu les trouves, tu ne touveras jamais l'Épée de Légende cachée dans #[[2]]#!", {QM_RED, QM_RED})); // /*spanish*/Ja, ja, ja... Nunca me derrotarás reflejando mis esferas de energía y desplegando la flecha de luz de #[[1]]#! // ^E incluso si lo haces, nunca encontrarás la espada legendaria escondida en #[[2]]#! diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp index 7657776e748..73149f9096f 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp @@ -123,7 +123,9 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß ein #Herz im Deku-Baum# #[[1]]# verstecke.", /*french*/ "Selon moi, un #coeur dans l'Arbre Mojo# cache #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_DEKU_TREE_GRASS] = HintText(CustomMessage("They say that some #grass in the Deku Tree# hides #[[1]]#.", {QM_RED, QM_GREEN})); //TODO_TRANSLATE + hintTextTable[RHT_DEKU_TREE_GRASS] = HintText(CustomMessage("They say that some #grass in the Deku Tree# hides #[[1]]#.", + /*german*/ TODO_TRANSLATE, + /*french*/ "Selon moi, de l'#herbe dans l'Arbre Mojo# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_DEKU_TREE] = HintText(CustomMessage("They say that a #crate in the Deku Tree# contains #[[1]]#.", /*german*/ TODO_TRANSLATE, @@ -303,8 +305,10 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein in Dodongos Höhle# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer un air orageux pour une pierre étrange dans la Caverne Dodongo# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_DODONGOS_CAVERN_GRASS] = HintText(CustomMessage("They say that some #grass in Dodongo's Cavern# hides #[[1]]#.", {QM_RED, QM_GREEN})); //TODO_TRANSLATE - + hintTextTable[RHT_DODONGOS_CAVERN_GRASS] = HintText(CustomMessage("They say that some #grass in Dodongo's Cavern# hides #[[1]]#.", + /*german*/ TODO_TRANSLATE, + /*french*/ "Selon moi, de l'#herbe dans la Caverne Dodongo# cache #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_CRATE_DODONGOS_CAVERN] = HintText(CustomMessage("They say that a #crate in Dodongo's Cavern# contains #[[1]]#.", /*german*/ TODO_TRANSLATE, /*french*/ "Selon moi, une #caisse dans la Caverne Dodongo# contient #[[1]]#.", {QM_RED, QM_GREEN})); @@ -468,7 +472,9 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß sich #nahe des zentralen Lifts in Jabu-Jabus Bauch# #[[1]]# befände.", /*french*/ "Selon moi, près d'un #ascenceur central dans le Ventre de Jabu-Jabu# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_JABU_JABUS_BELLY_GRASS] = HintText(CustomMessage("They say that some #grass in Jabu Jabu's Belly# hides #[[1]]#.", {QM_RED, QM_GREEN})); //TODO_TRANSLATE + hintTextTable[RHT_JABU_JABUS_BELLY_GRASS] = HintText(CustomMessage("They say that some #grass in Jabu Jabu's Belly# hides #[[1]]#.", + /*german*/ TODO_TRANSLATE, + /*french*/ "Selon moi, de l'#herbe dans le Ventre de Jabu-Jabu# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_JABU_JABU] = HintText(CustomMessage("They say that a #crate in Jabu Jabu's Belly# contains #[[1]]#.", /*german*/ TODO_TRANSLATE, @@ -1725,7 +1731,9 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne in einer Sackgasse# innerhalb des Brunnens #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler le soleil dans une impasse# à l’intérieur du puits révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_BOTTOM_OF_THE_WELL_GRASS] = HintText(CustomMessage("They say that some #grass in the Bottom of the Well# hides #[[1]]#.", {QM_RED, QM_GREEN})); // TODO_TRANSLATE + hintTextTable[RHT_BOTTOM_OF_THE_WELL_GRASS] = HintText(CustomMessage("They say that some #grass in the Bottom of the Well# hides #[[1]]#.", + /*german*/ TODO_TRANSLATE, + /*french*/ "Selon moi, de l'#herbe dans le Puits# cache #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -1807,7 +1815,7 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { hintTextTable[RHT_ICE_CAVERN_HEART] = HintText(CustomMessage("They say that atop on a #frozen pillar# lies #[[1]]#.", /*german*/ "Man erzählt sich, daß auf einer #gefrorenen Säule# #[[1]]# läge.", - /*french*/ "Selon moi, ", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, le dessus d'un #pilier de glace# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_ICE_CAVERN_RUPEE] = HintText(CustomMessage("They say that a #rupee in a frozen cavern# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Rubin in einer gefrorenen Kaverne# #[[1]]# verstecke.", diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp index 0c563fd4f35..685934dfa8d 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp @@ -1344,12 +1344,12 @@ void StaticData::HintTable_Init_Exclude_Overworld() { hintTextTable[RHT_SFM_DEKU_SCRUB_GROTTO_REAR] = HintText(CustomMessage("They say that a #scrub underground duo# sells #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Deku-Paar im Untergrund# #[[1]]# verkaufe.", - /*french*/ "Selon moi, le #duo de peste Mojo au cœur du sanctuaire sylvestre# vend #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, le #duo de peste Mojo au coeur du sanctuaire sylvestre# vend #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, un #par de dekus subterráneos# de la pradera sagrada venden #[[1]]#. hintTextTable[RHT_SFM_DEKU_SCRUB_GROTTO_FRONT] = HintText(CustomMessage("They say that a #scrub underground duo# sells #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Deku-Paar im Untergrund# #[[1]]# verkaufe.", - /*french*/ "Selon moi, le #duo de peste Mojo au cœur du sanctuaire sylvestre# vend #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, le #duo de peste Mojo au coeur du sanctuaire sylvestre# vend #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, un #par de dekus subterráneos# de la pradera sagrada venden #[[1]]#. hintTextTable[RHT_GC_DEKU_SCRUB_GROTTO_LEFT] = HintText(CustomMessage("They say that a #trio of scrubs# sells #[[1]]#.", @@ -1459,37 +1459,37 @@ void StaticData::HintTable_Init_Exclude_Overworld() { hintTextTable[RHT_BEEHIVE_CHEST_GROTTO] = HintText(CustomMessage("They say that a #beehive above a chest# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock oberhalb einer Truhe# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #ruche au dessus d'un coffre# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena sobre un cofre# esconde #[[1]]#. hintTextTable[RHT_BEEHIVE_LONELY_SCRUB_GROTTO] = HintText(CustomMessage("They say that a #beehive above a lonely scrub# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock oberhalb eines einsamen Deku# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #ruche au dessus d'une Peste Mojo solitaire# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena sobre un deku solitario# esconde #[[1]]#. hintTextTable[RHT_BEEHIVE_SCRUB_PAIR_GROTTO] = HintText(CustomMessage("They say that a #beehive above a pair of scrubs# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock oberhalb eines Deku-Paars# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #ruche au dessus d'un duo de Pestes Mojo# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena sobre un par de dekus# esconde #[[1]]#. hintTextTable[RHT_BEEHIVE_SCRUB_TRIO_GROTTO] = HintText(CustomMessage("They say that a #beehive above a trio of scrubs# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock oberhalb eines Deku-Trios# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #ruche au dessus d'un trio de Pestes Mojo# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena sobre un trío de dekus# esconde #[[1]]#. hintTextTable[RHT_BEEHIVE_COW_GROTTO] = HintText(CustomMessage("They say that a #beehive above a cow# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock oberhalb einer Kuh# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #ruche au dessus d'une vache# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena sobre una vaca# esconde #[[1]]#. hintTextTable[RHT_BEEHIVE_IN_FRONT_OF_KING_ZORA] = HintText(CustomMessage("They say that a #beehive in front of the king of the zoras# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock vor dem Zora-König# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #ruche devant le Roi des Zoras# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena delante del rey de los zoras# esconde #[[1]]#. hintTextTable[RHT_BEEHIVE_BEHIND_KING_ZORA] = HintText(CustomMessage("They say that a #beehive behind the king of the zoras# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Bienenstock hinter dem Zora-König# #[[1]]# verberge.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, une #rucheau derrière le Roi des Zoras# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena detrás del rey de los zoras# esconde #[[1]]#. hintTextTable[RHT_POT_KOKIRI_FOREST] = HintText(CustomMessage("They say that a #pot in Kokiri Forest# contains #[[1]]#.", diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp index 7bea4d517c1..fa5155fef52 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp @@ -1962,69 +1962,69 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a gold fragment", /*german*/"ein goldenes Fragment", /*french*/"un fragment d'or")}); // /*spanish*/un fragmento dorado - hintTextTable[RHT_GOHMA_SOUL] = HintText(CustomMessage("the soul of Gohma", /*german*/"die Seele Gohmas", /*french*/TODO_TRANSLATE), + hintTextTable[RHT_GOHMA_SOUL] = HintText(CustomMessage("the soul of Gohma", /*german*/"die Seele Gohmas", /*french*/"l'Âme de Gohma"), { - CustomMessage("something webbed", /*german*/"etwas Verwobenes", /*french*/TODO_TRANSLATE) + CustomMessage("something webbed", /*german*/"etwas Verwobenes", /*french*/"un truc entoilé") }, { - CustomMessage("an invasive soul", /*german*/"eine invasive Seele", /*french*/TODO_TRANSLATE), - CustomMessage("some spider essence", /*german*/"etwas spinnenartige Essenz", /*french*/TODO_TRANSLATE)}); + CustomMessage("an invasive soul", /*german*/"eine invasive Seele", /*french*/"une âme invasive"), + CustomMessage("some spider essence", /*german*/"etwas spinnenartige Essenz", /*french*/"une essence d'araignée")}); - hintTextTable[RHT_KING_DODONGO_SOUL] = HintText(CustomMessage("the soul of King Dodongo", /*german*/"die Seele König Dodongos", /*french*/TODO_TRANSLATE), + hintTextTable[RHT_KING_DODONGO_SOUL] = HintText(CustomMessage("the soul of King Dodongo", /*german*/"die Seele König Dodongos", /*french*/"l'Âme du Roi Dodongo"), { - CustomMessage("something explosive", /*german*/"etwas Explosives", /*french*/TODO_TRANSLATE) + CustomMessage("something explosive", /*german*/"etwas Explosives", /*french*/"un truc détonnant") }, { - CustomMessage("a royal soul", /*german*/"eine royale Seele", /*french*/TODO_TRANSLATE), - CustomMessage("some reptile essence", /*german*/"etwas reptilienartige Essenz", /*french*/TODO_TRANSLATE)}); + CustomMessage("a royal soul", /*german*/"eine royale Seele", /*french*/"une âme royale"), + CustomMessage("some reptile essence", /*german*/"etwas reptilienartige Essenz", /*french*/"une essence de reptile")}); - hintTextTable[RHT_BARINADE_SOUL] = HintText(CustomMessage("the soul of Barinade", /*german*/"die Seele Barinades", /*french*/TODO_TRANSLATE), + hintTextTable[RHT_BARINADE_SOUL] = HintText(CustomMessage("the soul of Barinade", /*german*/"die Seele Barinades", /*french*/"l'Âme de Barinade"), { - CustomMessage("something fishy", /*german*/"etwas Fischiges", /*french*/TODO_TRANSLATE) + CustomMessage("something fishy", /*german*/"etwas Fischiges", /*french*/"un truc gluant") }, { - CustomMessage("an infectuous soul", /*german*/"eine infektiöse Seele", /*french*/TODO_TRANSLATE), - CustomMessage("some parasitic essence", /*german*/"etwas parasitäre Essenz", /*french*/TODO_TRANSLATE)}); + CustomMessage("an infectuous soul", /*german*/"eine infektiöse Seele", /*french*/"une âme infectieuse"), + CustomMessage("some parasitic essence", /*german*/"etwas parasitäre Essenz", /*french*/"une essence parasitique")}); - hintTextTable[RHT_PHANTOM_GANON_SOUL] = HintText(CustomMessage("the soul of Phantom Ganon", /*german*/"die Seele Phantom-Ganons", /*french*/TODO_TRANSLATE), + hintTextTable[RHT_PHANTOM_GANON_SOUL] = HintText(CustomMessage("the soul of Phantom Ganon", /*german*/"die Seele Phantom-Ganons", /*french*/"l'Âme de Ganon Spectral"), { - CustomMessage("something spectral", /*german*/"etwas Spektrales", /*french*/TODO_TRANSLATE) + CustomMessage("something spectral", /*german*/"etwas Spektrales", /*french*/"un truc spectral") }, { - CustomMessage("a duplicate soul", /*german*/"eine duplizierte Seele", /*french*/TODO_TRANSLATE), + CustomMessage("a duplicate soul", /*german*/"eine duplizierte Seele", /*french*/"une âme clonée"), - CustomMessage("some illusionary essence", /*german*/"etwas illusionäre Essenz", /*french*/TODO_TRANSLATE)}); + CustomMessage("some illusionary essence", /*german*/"etwas illusionäre Essenz", /*french*/"une essence illusoire")}); - hintTextTable[RHT_VOLVAGIA_SOUL] = HintText(CustomMessage("the soul of Volvagia", /*german*/"die Seele Volvagias", /*french*/TODO_TRANSLATE), + hintTextTable[RHT_VOLVAGIA_SOUL] = HintText(CustomMessage("the soul of Volvagia", /*german*/"die Seele Volvagias", /*french*/"l'Âme de Volcania"), { - CustomMessage("something hot", /*german*/"etwas Heißes", /*french*/TODO_TRANSLATE) + CustomMessage("something hot", /*german*/"etwas Heißes", /*french*/"quelque chose de très chaud") }, { - CustomMessage("a draconic soul", /*german*/"eine drakonische Seele", /*french*/TODO_TRANSLATE), - CustomMessage("some magmatic essence", /*german*/"etwas magmatische Essenz", /*french*/TODO_TRANSLATE)}); + CustomMessage("a draconic soul", /*german*/"eine drakonische Seele", /*french*/"une essence draconique"), + CustomMessage("some magmatic essence", /*german*/"etwas magmatische Essenz", /*french*/"une essence magmatique")}); - hintTextTable[RHT_MORPHA_SOUL] = HintText(CustomMessage("the soul of Morpha", /*german*/"die Seele Morphas", /*french*/TODO_TRANSLATE), + hintTextTable[RHT_MORPHA_SOUL] = HintText(CustomMessage("the soul of Morpha", /*german*/"die Seele Morphas", /*french*/"l'Âme de Morpha"), { - CustomMessage("something wet", /*german*/"etwas Nasses", /*french*/TODO_TRANSLATE) + CustomMessage("something wet", /*german*/"etwas Nasses", /*french*/"un truc mouillé") }, { - CustomMessage("an aquatic soul", /*german*/"eine aquatische Seele", /*french*/TODO_TRANSLATE), - CustomMessage("some liquid essence", /*german*/"etwas flüssige Essenz", /*french*/TODO_TRANSLATE)}); + CustomMessage("an aquatic soul", /*german*/"eine aquatische Seele", /*french*/"une âme aquatique"), + CustomMessage("some liquid essence", /*german*/"etwas flüssige Essenz", /*french*/"une essence liquide")}); - hintTextTable[RHT_BONGO_BONGO_SOUL] = HintText(CustomMessage("the soul of Bongo Bongo", /*german*/"die Seele Bongo Bongos", /*french*/TODO_TRANSLATE), + hintTextTable[RHT_BONGO_BONGO_SOUL] = HintText(CustomMessage("the soul of Bongo Bongo", /*german*/"die Seele Bongo Bongos", /*french*/"l'Âme de Bongo Bongo"), { - CustomMessage("something dark", /*german*/"etwas Dunkles", /*french*/TODO_TRANSLATE) + CustomMessage("something dark", /*german*/"etwas Dunkles", /*french*/"quelque chose de sombre") }, { - CustomMessage("a shadowy soul", /*german*/"eine schattige Seele", /*french*/TODO_TRANSLATE), - CustomMessage("some handy essence", /*german*/"etwas praktische Essenz", /*french*/TODO_TRANSLATE)}); + CustomMessage("a shadowy soul", /*german*/"eine schattige Seele", /*french*/"une âme ténébreuse"), + CustomMessage("some handy essence", /*german*/"etwas praktische Essenz", /*french*/"l'âme du tape m'en cinq")}); - hintTextTable[RHT_TWINROVA_SOUL] = HintText(CustomMessage("the soul of Twinrova", /*german*/"die Seele Twinrovas", /*french*/TODO_TRANSLATE), + hintTextTable[RHT_TWINROVA_SOUL] = HintText(CustomMessage("the soul of Twinrova", /*german*/"die Seele Twinrovas", /*french*/"l'Âme du Duo Maléfique"), { - CustomMessage("something spiritual", /*german*/"etwas Spirituelles", /*french*/TODO_TRANSLATE) + CustomMessage("something spiritual", /*german*/"etwas Spirituelles", /*french*/"un truc spirituel") }, { - CustomMessage("old souls", /*german*/"alte Seelen", /*french*/TODO_TRANSLATE), - CustomMessage("twin essences", /*german*/"Zwillingsessenzen", /*french*/TODO_TRANSLATE)}); + CustomMessage("old souls", /*german*/"alte Seelen", /*french*/"deux vieilles âmes"), + CustomMessage("twin essences", /*german*/"Zwillingsessenzen", /*french*/"des essences jumelles")}); - hintTextTable[RHT_GANON_SOUL] = HintText(CustomMessage("the soul of Ganon", /*german*/"die Seele Ganons", /*french*/TODO_TRANSLATE), + hintTextTable[RHT_GANON_SOUL] = HintText(CustomMessage("the soul of Ganon", /*german*/"die Seele Ganons", /*french*/"l'Âme de Ganon"), { - CustomMessage("something strong", /*german*/"etwas Starkes", /*french*/TODO_TRANSLATE) + CustomMessage("something strong", /*german*/"etwas Starkes", /*french*/"un truc de fort") }, { - CustomMessage("an evil soul", /*german*/"eine böse Seele", /*french*/TODO_TRANSLATE), - CustomMessage("some powerful essence", /*german*/"etwas mächtige Essenz", /*french*/TODO_TRANSLATE)}); + CustomMessage("an evil soul", /*german*/"eine böse Seele", /*french*/"une âme maléfique"), + CustomMessage("some powerful essence", /*german*/"etwas mächtige Essenz", /*french*/"une essence incroyablement puissante")}); hintTextTable[RHT_OCARINA_A_BUTTON] = HintText(CustomMessage("an Ocarina A Button", /*german*/"eine Okarina A Taste", /*french*/"la Touche A de l'Ocarina"), // /*spanish*/un botón A de Ocarina diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp index 929314e25a1..110ab8a7673 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp @@ -759,7 +759,7 @@ void InitTrickNames() { trickNameTable[RG_RECOVERY_HEART] = { Text{ "Love", "Bisou", "Te amo" }, Text{ "Life", "Vie", "vida" }, - Text{ "HP", "VP", "VP" }, + Text{ "HP", "PV", "VP" }, }; trickNameTable[RG_GREEN_RUPEE] = { Text{ "False Greg", "Faux Greg", "Falso Greg" }, Text{ "One Ruby", "Un rubis", "Un rubí" }, @@ -779,15 +779,15 @@ void InitTrickNames() { Text{ "Rupee (20)", "Rubis (20)", "Peso hyliano" }, }; trickNameTable[RG_PURPLE_RUPEE] = { - Text{ "Purpee", "pourbi", "morupiua" }, - Text{ "Fifty Rubies", "cinquante rubis", "Cincuenta rubíes" }, + Text{ "Purpee", "Pourbi", "morupiua" }, + Text{ "Fifty Rubies", "Cinquante rubis", "Cincuenta rubíes" }, Text{ "Rupoor (50)", "Roupir (50)", "Rupobre (50)" }, Text{ "Fifty Rupees", "Cinquante rubis", "Balboa hyliano" }, Text{ "Rupee (50)", "Rubis (50)", "Peso hyliano" }, }; trickNameTable[RG_HUGE_RUPEE] = { Text{ "Hugo", "Or Rubi", "Oro Rubi" }, - Text{ "Two Hundred Rubies", "deux cents rubis", "Doscientos rubíes" }, + Text{ "Two Hundred Rubies", "Deux cents rubis", "Doscientos rubíes" }, Text{ "Diamond", "Diamant", "Diamante" }, Text{ "Huge Ruby", "Énorme rubis", "Rubi gigante" }, Text{ "Two Hundred Rupees", "Deux cent rubis", "Euro hyliano" }, @@ -806,54 +806,54 @@ void InitTrickNames() { trickNameTable[RG_TRIFORCE_PIECE] = { Text{ "Piece of Cheese", "Morceau de Fromage", "Piece of Cheese" }, Text{ "Triforce Shard", "Éclat de Triforce", "Triforce Shard" }, - Text{ "Shiny Rock", "Caiiloux Brillant", "Shiny Rock" }, + Text{ "Shiny Rock", "Caillou Brillant", "Shiny Rock" }, }; // TODO_TRANSLATE trickNameTable[RG_GOHMA_SOUL] = { - Text{ "Spider Sense", "", "" }, - Text{ "Deku Spirit", "", "" }, - Text{ "Ghost of Ghoma", "", "" }, + Text{ "Spider Sense", "Sens de l'Araignée", "" }, + Text{ "Deku Spirit", "Parasite Mojo", "" }, + Text{ "Ghost of Ghoma", "Fantôme de Gohma", "" }, }; trickNameTable[RG_KING_DODONGO_SOUL] = { - Text{ "Lizard Soul", "", "" }, - Text{ "Regal Remains", "", "" }, - Text{ "Dodongo's Core", "", "" }, + Text{ "Lizard Soul", "Âme d'un Lézard", "" }, + Text{ "Regal Remains", "Restes Délicieux", "" }, + Text{ "Dodongo's Core", "Coeur de Dodongo", "" }, }; trickNameTable[RG_BARINADE_SOUL] = { - Text{ "Parasitic Poltergeist", "", "" }, - Text{ "Jabu Insides", "", "" }, - Text{ "Barinade Bacteria", "", "" }, + Text{ "Parasitic Poltergeist", "Poltergeist Parasite", "" }, + Text{ "Jabu Insides", "Entrailles de Jabu-Jabu", "" }, + Text{ "Barinade Bacteria", "Bactérie de Barinade", "" }, }; trickNameTable[RG_PHANTOM_GANON_SOUL] = { - Text{ "Bigger Poe", "", "" }, - Text{ "Sacred Forest Pine Tree", "", "" }, - Text{ "Ganon's Phantom", "", "" }, + Text{ "Bigger Poe", "Âme Gigantesque", "" }, + Text{ "Sacred Forest Pine Tree", "Grande Perche du Bosquet Sacré", "" }, + Text{ "Ganon's Phantom", "Fantôme de Ganon", "" }, }; trickNameTable[RG_VOLVAGIA_SOUL] = { - Text{ "Dragon Roast", "", "" }, - Text{ "Hot n' Ready", "", "" }, - Text{ "Volvagia's Vitality", "", "" }, + Text{ "Dragon Roast", "Friture du Dragon", "" }, + Text{ "Hot n' Ready", "Sauce Barbecue", "" }, + Text{ "Volvagia's Vitality", "Vitalité de Volcania", "" }, }; trickNameTable[RG_MORPHA_SOUL] = { - Text{ "Dihydrogen Monoxide", "", "" }, - Text{ "Morpha Molecules", "", "" }, - Text{ "Wet Stuff", "", "" }, + Text{ "Dihydrogen Monoxide", "Monoxyde de Dihydrogène", "" }, + Text{ "Morpha Molecules", "Molécule de Morpha", "" }, + Text{ "Wet Stuff", "Truc Mouillé", "" }, }; trickNameTable[RG_BONGO_BONGO_SOUL] = { - Text{ "Shadow Soul", "", "" }, - Text{ "Dark Essence", "", "" }, - Text{ "Bongo Bongo's Bongo", "", "" }, + Text{ "Shadow Soul", "Âme de l'Ombre", "" }, + Text{ "Dark Essence", "Essence Sombre", "" }, + Text{ "Bongo Bongo's Bongo", "Bongo de Bongo Bongo", "" }, }; trickNameTable[RG_TWINROVA_SOUL] = { - Text{ "Sandy Ashes", "", "" }, - Text{ "Spiritual Spirit", "", "" }, - Text{ "Twin Rovers", "", "" }, + Text{ "Sandy Ashes", "Cendres des Vieilles", "" }, + Text{ "Spiritual Spirit", "Esprit Spirituel", "" }, + Text{ "Twin Rovers", "Duo Angélique", "" }, }; trickNameTable[RG_GANON_SOUL] = { - Text{ "Pure Evil", "", "" }, - Text{ "Ganon's Ghost", "", "" }, - Text{ "Pork", "", "" }, + Text{ "Pure Evil", "Mal Incarné", "" }, + Text{ "Ganon's Ghost", "Le Malin", "" }, + Text{ "Pork", "Porc", "" }, }; trickNameTable[RG_FISHING_POLE] = { @@ -863,31 +863,31 @@ void InitTrickNames() { }; trickNameTable[RG_OCARINA_A_BUTTON] = { - Text{ "Ocarina J Button", "", "" }, - Text{ "Ocarina Ayy Button", "", "" }, - Text{ "Ocarina A Trigger", "", "" }, + Text{ "Ocarina J Button", "Touche Ha de l'Ocarina", "" }, + Text{ "Ocarina Ayy Button", "Touche Ah de l'Ocarina", "" }, + Text{ "Ocarina A Trigger", "Bumper A de l'Ocarina", "" }, }; trickNameTable[RG_OCARINA_C_UP_BUTTON] = { - Text{ "Ocarina C North Button", "", "" }, - Text{ "Ocarina C App Button", "", "" }, - Text{ "Ocarina Sup Button", "", "" }, + Text{ "Ocarina C North Button", "Touche C Nord de l'Ocarina", "" }, + Text{ "Ocarina C App Button", "Touche C'est Haut de l'Ocarina", "" }, + Text{ "Ocarina Sup Button", "Touche O de l'Ocarina", "" }, }; trickNameTable[RG_OCARINA_C_DOWN_BUTTON] = { - Text{ "Ocarina C South Button", "", "" }, - Text{ "Ocarina Z Down Button", "", "" }, - Text{ "Ocarina See Down Button", "", "" }, - Text{ "Ocarina C Dawn Button", "", "" }, + Text{ "Ocarina C South Button", "Touche C Sud de l'Ocarina", "" }, + Text{ "Ocarina Z Down Button", "Touche Z Bas de l'Ocarina", "" }, + Text{ "Ocarina See Down Button", "Touche C'est Bas de l'Ocarina", "" }, + Text{ "Ocarina C Dawn Button", "Touche Séba de l'Ocarina", "" }, }; trickNameTable[RG_OCARINA_C_LEFT_BUTTON] = { - Text{ "Ocarina C West Button", "", "" }, - Text{ "Ocarina Sea Left Button", "", "" }, - Text{ "Ocarina C Lift Button", "", "" }, - Text{ "Ocarina Rewind Button", "", "" }, + Text{ "Ocarina C West Button", "Touche C Ouest de l'Ocarina", "" }, + Text{ "Ocarina Sea Left Button", "Touche Cégoche de L'Ocarina", "" }, + Text{ "Ocarina C Lift Button", "Touche C'est Gauche de l'Ocarina", "" }, + Text{ "Ocarina Rewind Button", "Touche Rembobiner de l'Ocarina", "" }, }; trickNameTable[RG_OCARINA_C_RIGHT_BUTTON] = { - Text{ "Ocarina C East Button", "", "" }, - Text{ "Ocarina C Wright Button", "", "" }, - Text{ "Overworld C Right Button", "", "" }, + Text{ "Ocarina C East Button", "Touche C Est de l'Ocarina", "" }, + Text{ "Ocarina C Wright Button", "Touche C'est Droite de l'Ocarina", "" }, + Text{ "Overworld C Right Button", "Trou Droit de l'Ocarina", "" }, }; /* diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index 54a84f603ff..e8198737c4f 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -172,53 +172,53 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_GANONS_CASTLE_SMALL_KEY].SetCustomDrawFunc(Randomizer_DrawSmallKey); itemTable[RG_TREASURE_GAME_SMALL_KEY] = Item(RG_TREASURE_GAME_SMALL_KEY, Text{ "Chest Game Small Key", "Petite Clé du jeu la Chasse-aux-Trésors", "Kleiner Schlüssel für das Truhenspiel" }, ITEMTYPE_SMALLKEY, GI_DOOR_KEY, true, LOGIC_TREASURE_GAME_KEYS, RHT_TREASURE_GAME_SMALL_KEY, ITEM_KEY_SMALL, OBJECT_GI_KEY, GID_KEY_SMALL, 0xF3, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_NONE); itemTable[RG_TREASURE_GAME_SMALL_KEY].SetCustomDrawFunc(Randomizer_DrawSmallKey); - itemTable[RG_GUARD_HOUSE_KEY] = Item(RG_GUARD_HOUSE_KEY, Text{ "Guard House Key", TODO_TRANSLATE, "Schlüssel für das Haus der Wachen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_GUARD_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_GUARD_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_GUARD_HOUSE_KEY] = Item(RG_GUARD_HOUSE_KEY, Text{ "Guard House Key", "Clé de la Maison des Gardes", "Schlüssel für das Haus der Wachen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_GUARD_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_GUARD_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_GUARD_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_MARKET_BAZAAR_KEY] = Item(RG_MARKET_BAZAAR_KEY, Text{ "Market Bazaar Key", TODO_TRANSLATE, "Schlüssel für den Basar des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_BAZAAR_KEY, RHT_OVERWORLD_KEY, RG_MARKET_BAZAAR_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_MARKET_BAZAAR_KEY] = Item(RG_MARKET_BAZAAR_KEY, Text{ "Market Bazaar Key", "Clé du Bazar de la Place du Marché", "Schlüssel für den Basar des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_BAZAAR_KEY, RHT_OVERWORLD_KEY, RG_MARKET_BAZAAR_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_MARKET_BAZAAR_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_MARKET_POTION_SHOP_KEY] = Item(RG_MARKET_POTION_SHOP_KEY, Text{ "Market Potion Shop Key", TODO_TRANSLATE, "Schlüssel für den Magie-Laden des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_MARKET_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_MARKET_POTION_SHOP_KEY] = Item(RG_MARKET_POTION_SHOP_KEY, Text{ "Market Potion Shop Key", "Clé du Magasin de Potions de la Place du Marché", "Schlüssel für den Magie-Laden des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_MARKET_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_MARKET_POTION_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_MASK_SHOP_KEY] = Item(RG_MASK_SHOP_KEY, Text{ "Mask Shop Key", TODO_TRANSLATE, "Schlüssel für den Maskenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MASK_SHOP_KEY, RHT_OVERWORLD_KEY, RG_MASK_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_MASK_SHOP_KEY] = Item(RG_MASK_SHOP_KEY, Text{ "Mask Shop Key", "Clé de la Foire aux Masques", "Schlüssel für den Maskenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MASK_SHOP_KEY, RHT_OVERWORLD_KEY, RG_MASK_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_MASK_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_MARKET_SHOOTING_GALLERY_KEY] = Item(RG_MARKET_SHOOTING_GALLERY_KEY, Text{ "Market Shooting Gallery Key", TODO_TRANSLATE, "Schlüssel für die Schießbude des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_SHOOTING_GALLERY_KEY, RHT_OVERWORLD_KEY, RG_MARKET_SHOOTING_GALLERY_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_MARKET_SHOOTING_GALLERY_KEY] = Item(RG_MARKET_SHOOTING_GALLERY_KEY, Text{ "Market Shooting Gallery Key", "Clé du Stand de Tir de la Place du Marché", "Schlüssel für die Schießbude des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_SHOOTING_GALLERY_KEY, RHT_OVERWORLD_KEY, RG_MARKET_SHOOTING_GALLERY_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_MARKET_SHOOTING_GALLERY_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_BOMBCHU_BOWLING_KEY] = Item(RG_BOMBCHU_BOWLING_KEY, Text{ "Bombchu Bowling Alley Key", TODO_TRANSLATE, "Schlüssel für die Minenbowlingbahn" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOMBCHU_BOWLING_KEY, RHT_OVERWORLD_KEY, RG_BOMBCHU_BOWLING_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_BOMBCHU_BOWLING_KEY] = Item(RG_BOMBCHU_BOWLING_KEY, Text{ "Bombchu Bowling Alley Key", "Clé du Bowling Teigneux", "Schlüssel für die Minenbowlingbahn" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOMBCHU_BOWLING_KEY, RHT_OVERWORLD_KEY, RG_BOMBCHU_BOWLING_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_BOMBCHU_BOWLING_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_TREASURE_CHEST_GAME_BUILDING_KEY] = Item(RG_TREASURE_CHEST_GAME_BUILDING_KEY, Text{ "Treasure Chest Game Building Key", TODO_TRANSLATE, "Schlüssel für das Haus des Schatzkisten-Pokers" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_TREASURE_CHEST_GAME_BUILDING_KEY,RHT_OVERWORLD_KEY, RG_TREASURE_CHEST_GAME_BUILDING_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_TREASURE_CHEST_GAME_BUILDING_KEY] = Item(RG_TREASURE_CHEST_GAME_BUILDING_KEY, Text{ "Treasure Chest Game Building Key", "Clé de la Chasse au Trésor", "Schlüssel für das Haus des Schatzkisten-Pokers" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_TREASURE_CHEST_GAME_BUILDING_KEY,RHT_OVERWORLD_KEY, RG_TREASURE_CHEST_GAME_BUILDING_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_TREASURE_CHEST_GAME_BUILDING_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_BOMBCHU_SHOP_KEY] = Item(RG_BOMBCHU_SHOP_KEY, Text{ "Bombchu Shop Key", TODO_TRANSLATE, "Schlüssel für den Krabbelminenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOMBCHU_SHOP_KEY, RHT_OVERWORLD_KEY, RG_BOMBCHU_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_BOMBCHU_SHOP_KEY] = Item(RG_BOMBCHU_SHOP_KEY, Text{ "Bombchu Shop Key", "Clé du Magasin de Missiles", "Schlüssel für den Krabbelminenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOMBCHU_SHOP_KEY, RHT_OVERWORLD_KEY, RG_BOMBCHU_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_BOMBCHU_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_RICHARDS_HOUSE_KEY] = Item(RG_RICHARDS_HOUSE_KEY, Text{ "Richard's House Key", TODO_TRANSLATE, "Schlüssel für das Haus von Richard" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_RICHARDS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_RICHARDS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_RICHARDS_HOUSE_KEY] = Item(RG_RICHARDS_HOUSE_KEY, Text{ "Richard's House Key", "Clé de la Maison de Kiki", "Schlüssel für das Haus von Richard" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_RICHARDS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_RICHARDS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_RICHARDS_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_ALLEY_HOUSE_KEY] = Item(RG_ALLEY_HOUSE_KEY, Text{ "Alley House Key", TODO_TRANSLATE, "Schlüssel für das Gassenhaus" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_ALLEY_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_ALLEY_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_ALLEY_HOUSE_KEY] = Item(RG_ALLEY_HOUSE_KEY, Text{ "Alley House Key", "Clé de la Maison de la Ruelle", "Schlüssel für das Gassenhaus" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_ALLEY_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_ALLEY_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_ALLEY_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_KAK_BAZAAR_KEY] = Item(RG_KAK_BAZAAR_KEY, Text{ "Kakariko Bazaar Key", TODO_TRANSLATE, "Schlüssel für den Basar von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_BAZAAR_KEY, RHT_OVERWORLD_KEY, RG_KAK_BAZAAR_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_KAK_BAZAAR_KEY] = Item(RG_KAK_BAZAAR_KEY, Text{ "Kakariko Bazaar Key", "Clé du Bazar de Cocorico", "Schlüssel für den Basar von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_BAZAAR_KEY, RHT_OVERWORLD_KEY, RG_KAK_BAZAAR_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_KAK_BAZAAR_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_KAK_POTION_SHOP_KEY] = Item(RG_KAK_POTION_SHOP_KEY, Text{ "Kakariko Potion Shop Key", TODO_TRANSLATE, "Schlüssel für den Magie-Laden von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_KAK_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_KAK_POTION_SHOP_KEY] = Item(RG_KAK_POTION_SHOP_KEY, Text{ "Kakariko Potion Shop Key", "Clé du Magasin de Potions de Cocorico", "Schlüssel für den Magie-Laden von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_KAK_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_KAK_POTION_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_BOSS_HOUSE_KEY] = Item(RG_BOSS_HOUSE_KEY, Text{ "Boss's House Key", TODO_TRANSLATE, "Schlüssel für das Haus des Chefs" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOSS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_BOSS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_BOSS_HOUSE_KEY] = Item(RG_BOSS_HOUSE_KEY, Text{ "Boss's House Key", "Clé de la Maison du Chef des Ouvriers", "Schlüssel für das Haus des Chefs" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BOSS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_BOSS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_BOSS_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_GRANNYS_POTION_SHOP_KEY] = Item(RG_GRANNYS_POTION_SHOP_KEY, Text{ "Granny's Potion Shop Key", TODO_TRANSLATE, "Schlüssel für Asas Hexenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_GRANNYS_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_GRANNYS_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_GRANNYS_POTION_SHOP_KEY] = Item(RG_GRANNYS_POTION_SHOP_KEY, Text{ "Granny's Potion Shop Key", "Clé de l'Apothicaire", "Schlüssel für Asas Hexenladen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_GRANNYS_POTION_SHOP_KEY, RHT_OVERWORLD_KEY, RG_GRANNYS_POTION_SHOP_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_GRANNYS_POTION_SHOP_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_SKULLTULA_HOUSE_KEY] = Item(RG_SKULLTULA_HOUSE_KEY, Text{ "Skulltula House Key", TODO_TRANSLATE, "Schlüssel für das Skulltula-Haus" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_SKULLTULA_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_SKULLTULA_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_SKULLTULA_HOUSE_KEY] = Item(RG_SKULLTULA_HOUSE_KEY, Text{ "Skulltula House Key", "Clé de la Maison des Araignées", "Schlüssel für das Skulltula-Haus" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_SKULLTULA_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_SKULLTULA_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_SKULLTULA_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_IMPAS_HOUSE_KEY] = Item(RG_IMPAS_HOUSE_KEY, Text{ "Impa's House Key", TODO_TRANSLATE, "Schlüssel für das Haus von Impa" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_IMPAS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_IMPAS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_IMPAS_HOUSE_KEY] = Item(RG_IMPAS_HOUSE_KEY, Text{ "Impa's House Key", "Clé de la Maison d'Impa", "Schlüssel für das Haus von Impa" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_IMPAS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_IMPAS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_IMPAS_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_WINDMILL_KEY] = Item(RG_WINDMILL_KEY, Text{ "Windmill Key", TODO_TRANSLATE, "Schlüssel für die Windmühle" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_WINDMILL_KEY, RHT_OVERWORLD_KEY, RG_WINDMILL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_WINDMILL_KEY] = Item(RG_WINDMILL_KEY, Text{ "Windmill Key", "Clé du Moulin", "Schlüssel für die Windmühle" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_WINDMILL_KEY, RHT_OVERWORLD_KEY, RG_WINDMILL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_WINDMILL_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_KAK_SHOOTING_GALLERY_KEY] = Item(RG_KAK_SHOOTING_GALLERY_KEY, Text{ "Kakariko Shooting Gallery Key", TODO_TRANSLATE, "Schlüssel für die Schießbude von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_SHOOTING_GALLERY_KEY, RHT_OVERWORLD_KEY, RG_KAK_SHOOTING_GALLERY_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_KAK_SHOOTING_GALLERY_KEY] = Item(RG_KAK_SHOOTING_GALLERY_KEY, Text{ "Kakariko Shooting Gallery Key", "Clé du Stand de Tir de Cocorico", "Schlüssel für die Schießbude von Kakariko" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_KAK_SHOOTING_GALLERY_KEY, RHT_OVERWORLD_KEY, RG_KAK_SHOOTING_GALLERY_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_KAK_SHOOTING_GALLERY_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_DAMPES_HUT_KEY] = Item(RG_DAMPES_HUT_KEY, Text{ "Dampe's Hut Key", TODO_TRANSLATE, "Schlüssel für die Hütte von Boris" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_DAMPES_HUT_KEY, RHT_OVERWORLD_KEY, RG_DAMPES_HUT_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_DAMPES_HUT_KEY] = Item(RG_DAMPES_HUT_KEY, Text{ "Dampe's Hut Key", "Clé de la Cabane d'Igor", "Schlüssel für die Hütte von Boris" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_DAMPES_HUT_KEY, RHT_OVERWORLD_KEY, RG_DAMPES_HUT_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_DAMPES_HUT_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_TALONS_HOUSE_KEY] = Item(RG_TALONS_HOUSE_KEY, Text{ "Talon's House Key", TODO_TRANSLATE, "Schlüssel für das Haus von Talon" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_TALONS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_TALONS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_TALONS_HOUSE_KEY] = Item(RG_TALONS_HOUSE_KEY, Text{ "Talon's House Key", "Clé de la Maison de Talon", "Schlüssel für das Haus von Talon" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_TALONS_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_TALONS_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_TALONS_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_STABLES_KEY] = Item(RG_STABLES_KEY, Text{ "Stables Key", TODO_TRANSLATE, "Schlüssel für die Ställe" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_STABLES_KEY, RHT_OVERWORLD_KEY, RG_STABLES_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_STABLES_KEY] = Item(RG_STABLES_KEY, Text{ "Stables Key", "Clé des Écuries", "Schlüssel für die Ställe" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_STABLES_KEY, RHT_OVERWORLD_KEY, RG_STABLES_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_STABLES_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_BACK_TOWER_KEY] = Item(RG_BACK_TOWER_KEY, Text{ "Back Tower Key", TODO_TRANSLATE, "Schlüssel für den hinteren Turm" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BACK_TOWER_KEY, RHT_OVERWORLD_KEY, RG_BACK_TOWER_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_BACK_TOWER_KEY] = Item(RG_BACK_TOWER_KEY, Text{ "Back Tower Key", "Clé du Silo", "Schlüssel für den hinteren Turm" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_BACK_TOWER_KEY, RHT_OVERWORLD_KEY, RG_BACK_TOWER_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_BACK_TOWER_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_HYLIA_LAB_KEY] = Item(RG_HYLIA_LAB_KEY, Text{ "Hylia Laboratory Key", TODO_TRANSLATE, "Schlüssel für das Hylia-Labor" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_HYLIA_LAB_KEY, RHT_OVERWORLD_KEY, RG_HYLIA_LAB_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_HYLIA_LAB_KEY] = Item(RG_HYLIA_LAB_KEY, Text{ "Hylia Laboratory Key", "Clé du Laboratoire du Lac Hylia", "Schlüssel für das Hylia-Labor" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_HYLIA_LAB_KEY, RHT_OVERWORLD_KEY, RG_HYLIA_LAB_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_HYLIA_LAB_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); - itemTable[RG_FISHING_HOLE_KEY] = Item(RG_FISHING_HOLE_KEY, Text{ "Fishing Hole Key", TODO_TRANSLATE, "Schlüssel für den Fischweiher" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_FISHING_HOLE_KEY, RHT_OVERWORLD_KEY, RG_FISHING_HOLE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); + itemTable[RG_FISHING_HOLE_KEY] = Item(RG_FISHING_HOLE_KEY, Text{ "Fishing Hole Key", "Clé de l'Étang", "Schlüssel für den Fischweiher" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_FISHING_HOLE_KEY, RHT_OVERWORLD_KEY, RG_FISHING_HOLE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); itemTable[RG_FISHING_HOLE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); // Key Rings itemTable[RG_FOREST_TEMPLE_KEY_RING] = Item(RG_FOREST_TEMPLE_KEY_RING, Text{ "Forest Temple Key Ring", "Trousseau du Temple de la Forêt", "Schlüsselbund für den Waldtempel" }, ITEMTYPE_SMALLKEY, 0xD5, true, LOGIC_FOREST_TEMPLE_KEYS, RHT_FOREST_TEMPLE_KEY_RING, RG_FOREST_TEMPLE_KEY_RING, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER); diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index d3ed83a26aa..9eaba3365cf 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -5238,7 +5238,7 @@ void Randomizer::CreateCustomMessages() { "Vous obtenez la %rClé %wdu %gBazar&de la Place du Marché%w!"), GIMESSAGE(RG_MARKET_POTION_SHOP_KEY, ITEM_KEY_SMALL, "You found the key to the&%gMarket Potion Shop%w!", "Du erhältst einen %rkleinen&Schlüssel%w für den %gMagie-Laden des Marktes%w!", - "Vous obtenez la %rClé %wde la&%gPlace du Marché%w!"), + "Vous obtenez la %rClé %wdu&%gMagasin de Potions de la&Place du Marché%w!"), GIMESSAGE(RG_MASK_SHOP_KEY, ITEM_KEY_SMALL, "You found the key to the&%gMask Shop%w!", "Du erhältst einen %rkleinen&Schlüssel%w für den %gMaskenladen%w!", "Vous obtenez la %rClé %wde la&%gFoire aux Masques%w!"), @@ -5270,7 +5270,7 @@ void Randomizer::CreateCustomMessages() { "Vous obtenez la %rClé %wdu %gMagasin de&Potions de Cocorico%w!"), GIMESSAGE(RG_BOSS_HOUSE_KEY, ITEM_KEY_SMALL, "You found the key to the&%gBoss's House%w!", "Du erhältst einen %rkleinen&Schlüssel%w für das %gHaus des Chefs%w!", - "Vous obtenez la %rClé %wde la %gMaison&du chef des ouvriers%w!"), + "Vous obtenez la %rClé %wde la %gMaison&du Chef des Ouvriers%w!"), GIMESSAGE(RG_GRANNYS_POTION_SHOP_KEY, ITEM_KEY_SMALL, "You found the key to&%gGranny's Potion Shop%w!", "Du erhältst einen %rkleinen&Schlüssel%w für %gAsas Hexenladen%w!", "Vous obtenez la %rClé %wde&l'%gApothicaire%w!"), From b71a0c5803300cf70d181f9df9bc3fa0a93f3fc5 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Mon, 7 Apr 2025 19:07:07 -0400 Subject: [PATCH 016/157] Fix checking gSaveContext for used small keys (#5346) --- soh/soh/Enhancements/randomizer/logic.cpp | 4 ++-- soh/soh/Enhancements/randomizer/logic.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 4a1ede8e72a..b2856391fd3 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2244,7 +2244,7 @@ const std::vector& GetDungeonSmallKeyDoors(SceneID sceneId) { return dungeonSmallKeyDoors[key]; } -int8_t GetUsedSmallKeyCount(SceneID sceneId) { +int8_t Logic::GetUsedSmallKeyCount(SceneID sceneId) { const auto& smallKeyDoors = GetDungeonSmallKeyDoors(sceneId); // Get the swch value for the scene @@ -2252,7 +2252,7 @@ int8_t GetUsedSmallKeyCount(SceneID sceneId) { if (gPlayState != nullptr && gPlayState->sceneNum == sceneId) { swch = gPlayState->actorCtx.flags.swch; } else { - swch = gSaveContext.sceneFlags[sceneId].swch; + swch = mSaveContext->sceneFlags[sceneId].swch; } // Count the number of small keys doors unlocked diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 4549c995258..7e0daac904d 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -267,6 +267,7 @@ class Logic { bool CheckEquipment(uint32_t item); bool CheckQuestItem(uint32_t item); void SetQuestItem(uint32_t item, bool state); + int8_t GetUsedSmallKeyCount(SceneID sceneId); uint8_t GetSmallKeyCount(uint32_t dungeonIndex); void SetSmallKeyCount(uint32_t dungeonIndex, uint8_t count); bool CheckDungeonItem(uint32_t item, uint32_t dungeonIndex); From d99aaf92450fe1729d98fd6473ef0b9881bb6977 Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 7 Apr 2025 21:58:57 -0600 Subject: [PATCH 017/157] Some Ganon health check fixes. --- soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c index a32ec8d7292..18a0e6fd53c 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c @@ -1464,7 +1464,7 @@ void func_80900890(BossGanon2* this, PlayState* play) { if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { func_808FFDB0(this, play); if (this->unk_334 == 0) { - this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(25, this->actor.level); + this->actor.colChkInfo.health = (u16)(this->actor.maximumHealth * (25.0f / 30.0f)); } this->unk_336 = 1; } @@ -1967,7 +1967,7 @@ void func_80902524(BossGanon2* this, PlayState* play) { } ActorDamageNumber_New(&this->actor, damage); temp_v0_4 = this->actor.colChkInfo.health; - if (temp_v0_4 < GetActorStat_EnemyMaxHealth(21, this->actor.level) && this->unk_334 == 0) { + if (temp_v0_4 < (u16)(this->actor.maximumHealth * (21.0f / 30.0f)) && this->unk_334 == 0) { func_80900818(this, play); } else { if (temp_v0_4 <= 0) { @@ -2010,7 +2010,7 @@ void func_80902524(BossGanon2* this, PlayState* play) { ActorDamageNumber_New(&this->actor, phi_v1_2); temp_v0_4 = this->actor.colChkInfo.health; - if ((temp_v0_4 < GetActorStat_EnemyMaxHealth(21, this->actor.level)) && (this->unk_334 == 0)) { + if ((temp_v0_4 < (u16)(this->actor.maximumHealth * (21.0f / 30.0f))) && (this->unk_334 == 0)) { func_80900818(this, play); } else if ((temp_v0_4 <= 0) && (baseDamage >= 2)) { func_80901020(this, play); From 71bd48cba91e93492075e63eea6b530d74c9e78e Mon Sep 17 00:00:00 2001 From: Malkierian Date: Tue, 8 Apr 2025 04:45:35 -0700 Subject: [PATCH 018/157] Move the white iron knuckle entry in `randomizedEnemySpawnTable` to match the order of `enemyCvarList`. (#5350) --- soh/soh/Enhancements/enemyrandomizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/enemyrandomizer.cpp b/soh/soh/Enhancements/enemyrandomizer.cpp index 61113158413..f202e1b1fc9 100644 --- a/soh/soh/Enhancements/enemyrandomizer.cpp +++ b/soh/soh/Enhancements/enemyrandomizer.cpp @@ -120,7 +120,6 @@ static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = { ACTOR_EN_CROW, 0 }, // Guay { ACTOR_EN_FIREFLY, 4 }, // Ice Keese { ACTOR_EN_ST, 2 }, // Skulltula (invisible) - { ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing) { ACTOR_EN_FIREFLY, 2 }, // Regular Keese { ACTOR_EN_DEKUBABA, 1 }, // Deku Baba (large) { ACTOR_EN_RR, 0 }, // Like-Like @@ -141,6 +140,7 @@ static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = { ACTOR_EN_TP, -1 }, // Electric Tailpasaran { ACTOR_EN_BW, 0 }, // Torch Slug { ACTOR_EN_WALLMAS, 1 }, // Wallmaster + { ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing) { ACTOR_EN_WF, 1 }, // Wolfos (white) { ACTOR_EN_KAREBABA, 0 }, // Withered Deku Baba From ae7654cb0e1efb7a2d09c0d599e0619fa08879c2 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Tue, 8 Apr 2025 07:13:08 -0700 Subject: [PATCH 019/157] Change check total array types to s16 to handle more check totals (thanks storage temple). (#5348) Fix typos regarding `overridden` in various parts of the codebase. --- soh/soh/Enhancements/randomizer/hook_handlers.cpp | 2 +- soh/soh/Enhancements/randomizer/option.h | 2 +- .../Enhancements/randomizer/randomizer_check_tracker.cpp | 8 ++++---- .../randomizer/randomizer_entrance_tracker.cpp | 2 +- soh/src/overlays/gamestates/ovl_select/z_select.c | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 5a803f39bd4..4c96d839d30 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -1632,7 +1632,7 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l gPlayState->actorCtx.flags.tempCollect = 0; // If the respawnFlag is set for a grotto return, we don't want the void out to happen. - // Set the data flag to one to prevent the respawn point from being overriden by dungeon doors. + // Set the data flag to one to prevent the respawn point from being overridden by dungeon doors. if (gSaveContext.respawnFlag == 2) { gSaveContext.respawn[RESPAWN_MODE_DOWN].data = 1; *should = false; diff --git a/soh/soh/Enhancements/randomizer/option.h b/soh/soh/Enhancements/randomizer/option.h index d92fa447375..507729498e4 100644 --- a/soh/soh/Enhancements/randomizer/option.h +++ b/soh/soh/Enhancements/randomizer/option.h @@ -302,7 +302,7 @@ class Option { /** * @brief Automatically renders a widget for this option in ImGui, based on the various * properties of this Option. Typically, Bool options are rendered as Checkboxes and - * U8 options are rendered as Comboboxes, but this can be overriden during construction with + * U8 options are rendered as Comboboxes, but this can be overridden during construction with * the `widgetType` property. */ bool RenderImGui(); diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 5eeabb99c63..6be7f8f4041 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -137,9 +137,9 @@ std::map> checksByArea; bool areasFullyChecked[RCAREA_INVALID]; u32 areasSpoiled = 0; bool showVOrMQ; -s8 areaChecksGotten[RCAREA_INVALID]; //| "Kokiri Forest (4/9)" -s8 areaChecksAvailable[RCAREA_INVALID]; -s8 areaCheckTotals[RCAREA_INVALID]; +s16 areaChecksGotten[RCAREA_INVALID]; //| "Kokiri Forest (4/9)" +s16 areaChecksAvailable[RCAREA_INVALID]; +s16 areaCheckTotals[RCAREA_INVALID]; uint16_t totalChecks = 0; uint16_t totalChecksAvailable = 0; uint16_t totalChecksGotten = 0; @@ -1932,7 +1932,7 @@ void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName, "Hidden", cvarHideName, UIWidgets::CheckboxOptions( { { .tooltip = "When active, checks will hide by default when updated to this state. Can " - "be overriden with the \"Show Hidden Items\" option." } }) + "be overridden with the \"Show Hidden Items\" option." } }) .Color(theme)); ImGui::PopID(); } diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp index c8e12f44980..f9317264782 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp @@ -423,7 +423,7 @@ bool IsEntranceDiscovered(s16 index) { bool isDiscovered = Entrance_GetIsEntranceDiscovered(index); if (!isDiscovered) { // If the pair included one of the hyrule field <-> zora's river entrances, - // the randomizer will have also overriden the water-based entrances, so check those too + // the randomizer will have also overridden the water-based entrances, so check those too if ((index == ENTR_ZORAS_RIVER_WEST_EXIT && Entrance_GetIsEntranceDiscovered(ENTR_ZORAS_RIVER_3)) || (index == ENTR_ZORAS_RIVER_3 && Entrance_GetIsEntranceDiscovered(ENTR_ZORAS_RIVER_WEST_EXIT))) { isDiscovered = true; diff --git a/soh/src/overlays/gamestates/ovl_select/z_select.c b/soh/src/overlays/gamestates/ovl_select/z_select.c index 3e1fcf220d4..e08ba7c97b8 100644 --- a/soh/src/overlays/gamestates/ovl_select/z_select.c +++ b/soh/src/overlays/gamestates/ovl_select/z_select.c @@ -40,7 +40,7 @@ void Select_LoadGame(SelectContext* this, s32 entranceIndex) { Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_STOP); gSaveContext.entranceIndex = entranceIndex; - // Check the entrance to see if the exit should be overriden to a grotto return point for entrance rando + // Check the entrance to see if the exit should be overridden to a grotto return point for entrance rando if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { // Ignore return value as we want to load into the entrance specified by the debug menu Grotto_OverrideSpecialEntrance(Entrance_GetOverride(entranceIndex)); @@ -105,7 +105,7 @@ void Select_Grotto_LoadGame(SelectContext* this, s32 grottoIndex) { gSaveContext.respawn[RESPAWN_MODE_RETURN].playerParams = 0x4ff; gSaveContext.respawn[RESPAWN_MODE_RETURN].pos = this->betterGrottos[grottoIndex].pos; - // Check the entrance to see if the exit should be overriden to a grotto return point for entrance rando + // Check the entrance to see if the exit should be overridden to a grotto return point for entrance rando if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { // Use grotto content and parent scene num to identify the right grotto s16 grottoEntrance = Grotto_GetRenamedGrottoIndexFromOriginal(this->betterGrottos[grottoIndex].data, From 88154d78c813bab30a7a4af51ce9b24cfee1f00e Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Tue, 8 Apr 2025 16:26:04 +0100 Subject: [PATCH 020/157] Fix Market Grass and Crate logic (#5340) * fix market grass and crate logic * apply clang --- .../dungeons/bottom_of_the_well.cpp | 4 +- .../location_access/overworld/market.cpp | 25 +++++----- soh/soh/Enhancements/randomizer/logic.cpp | 46 ++++++++----------- soh/soh/Enhancements/randomizer/logic.h | 1 + 4 files changed, 35 insertions(+), 41 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp index 4ec6b677306..44c3fa8177c 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp @@ -50,7 +50,7 @@ void RegionTable_Init_BottomOfTheWell() { LOCATION(RC_BOTTOM_OF_THE_WELL_FRONT_LEFT_FAKE_WALL_CHEST, true), LOCATION(RC_BOTTOM_OF_THE_WELL_RIGHT_BOTTOM_FAKE_WALL_CHEST, true), LOCATION(RC_BOTTOM_OF_THE_WELL_COMPASS_CHEST, true), - //N64 has no extra check here, but I can't get past without dealing with the spider or taking a hit, they probably assume sticks + //You can just barely pass the spider on the right side without damage or items, but it's probably tight enough to count as as a trick LOCATION(RC_BOTTOM_OF_THE_WELL_CENTER_SKULLTULA_CHEST, logic->CanPassEnemy(RE_BIG_SKULLTULA) || logic->TakeDamage()), //Not technically behind a wall, but still logically needs lens due to pits LOCATION(RC_BOTTOM_OF_THE_WELL_BACK_LEFT_BOMBABLE_CHEST, logic->HasExplosives()), @@ -160,7 +160,7 @@ void RegionTable_Init_BottomOfTheWell() { }, { //Exits Entrance(RR_BOTTOM_OF_THE_WELL_SOUTHWEST_ROOM, []{return logic->IsChild && logic->CanPassEnemy(RE_BIG_SKULLTULA);}), - //It's possible top abuse boulder's limited range of collision detection to detonate the flowers through the boulder with bow, but this is a glitch + //It's possible to abuse boulder's limited range of collision detection to detonate the flowers through the boulder with bow, but this is a glitch //the exact range is just past the furthest away plank in the green goo section Entrance(RR_BOTTOM_OF_THE_WELL_BASEMENT_USEFUL_BOMB_FLOWERS, []{return Here(RR_BOTTOM_OF_THE_WELL_BASEMENT, []{return logic->BlastOrSmash() || logic->CanUse(RG_DINS_FIRE) || (logic->CanUse(RG_STICKS) && ctx->GetTrickOption(RT_BOTW_BASEMENT));});}), }); diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp index 2016b24cd2f..0314bfde0c5 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp @@ -14,18 +14,19 @@ void RegionTable_Init_Market() { areaTable[RR_THE_MARKET] = Region("Market", "Market", {RA_THE_MARKET}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_MARKET_GRASS_1, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_2, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_3, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_4, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_5, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_6, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_7, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MARKET_GRASS_8, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_MK_NEAR_BAZAAR_CRATE_1, logic->IsChild && logic->CanBreakCrates()), - LOCATION(RC_MK_NEAR_BAZAAR_CRATE_2, logic->IsChild && logic->CanBreakCrates()), - LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_1, logic->IsChild && logic->CanBreakCrates()), - LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_2, logic->IsChild && logic->CanBreakCrates()), + //RANDOTODO add item avalibility to regions to remove need to hardcode logic in limited item use situations + LOCATION(RC_MARKET_GRASS_1, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_2, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_3, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_4, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_5, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_6, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_7, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MARKET_GRASS_8, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_MK_NEAR_BAZAAR_CRATE_1, logic->IsChild /*&& logic->CanRoll()*/), + LOCATION(RC_MK_NEAR_BAZAAR_CRATE_2, logic->IsChild /*&& logic->CanRoll()*/), + LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_1, logic->IsChild /*&& logic->CanRoll()*/), + LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_2, logic->IsChild /*&& logic->CanRoll()*/), }, { //Exits Entrance(RR_MARKET_ENTRANCE, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index b2856391fd3..ed91b21b8c0 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -558,9 +558,8 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal } return killed; case RE_DODONGO: - return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - CanUse(RG_MEGATON_HAMMER) || (quantity <= 5 && CanUse(RG_STICKS)) || HasExplosives() || - CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW); + return CanUseSword() || CanUse(RG_MEGATON_HAMMER) || (quantity <= 5 && CanUse(RG_STICKS)) || + HasExplosives() || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW); case RE_LIZALFOS: return CanJumpslash() || HasExplosives() || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW); case RE_KEESE: @@ -604,11 +603,9 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal (CanUse(RG_NUTS) || HookshotOrBoomerang() || CanStandingShield())); case RE_DEAD_HAND: // RANDOTODO change Dead Hand trick to be sticks Dead Hand - return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - (CanUse(RG_STICKS) && ctx->GetTrickOption(RT_BOTW_CHILD_DEADHAND)); + return CanUseSword() || (CanUse(RG_STICKS) && ctx->GetTrickOption(RT_BOTW_CHILD_DEADHAND)); case RE_WITHERED_DEKU_BABA: - return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - CanUse(RG_BOOMERANG); + return CanUseSword() || CanUse(RG_BOOMERANG); case RE_LIKE_LIKE: case RE_FLOORMASTER: return CanDamage(); @@ -645,8 +642,7 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal // bow and sling can wake them and damage after they shed their armour, so could reduce ammo requirements for // explosives to 10. requires 8 sticks to kill so would be a trick unless we apply higher stick bag logic case RE_IRON_KNUCKLE: - return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - CanUse(RG_MEGATON_HAMMER) || HasExplosives(); + return CanUseSword() || CanUse(RG_MEGATON_HAMMER) || HasExplosives(); // To stun flare dancer with chus, you have to hit the flame under it while it is spinning. It should eventually // return to spinning after dashing for a while if you miss the window it is possible to damage the core with // explosives, but difficult to get all 4 hits in even with chus, and if it reconstructs the core heals, so it @@ -695,9 +691,8 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal CanUse(RG_STICKS) || HasExplosives() || CanUse(RG_HOOKSHOT) || CanUse(RG_DINS_FIRE) || CanUse(RG_FIRE_ARROWS); case RE_SHELL_BLADE: - return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - CanUse(RG_MEGATON_HAMMER) || CanUse(RG_STICKS) || HasExplosives() || CanUse(RG_HOOKSHOT) || - CanUse(RG_FAIRY_BOW) || CanUse(RG_DINS_FIRE); + return CanJumpslash() || HasExplosives() || CanUse(RG_HOOKSHOT) || CanUse(RG_FAIRY_BOW) || + CanUse(RG_DINS_FIRE); case RE_SPIKE: return CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_MEGATON_HAMMER) || CanUse(RG_STICKS) || HasExplosives() || CanUse(RG_HOOKSHOT) || CanUse(RG_FAIRY_BOW) || @@ -744,25 +739,20 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal case RE_BARINADE: return HasBossSoul(RG_BARINADE_SOUL) && CanUse(RG_BOOMERANG) && CanJumpslashExceptHammer(); case RE_PHANTOM_GANON: - return HasBossSoul(RG_PHANTOM_GANON_SOUL) && - (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD)) && + return HasBossSoul(RG_PHANTOM_GANON_SOUL) && CanUseSword() && (CanUse(RG_HOOKSHOT) || CanUse(RG_FAIRY_BOW) || CanUse(RG_FAIRY_SLINGSHOT)); case RE_VOLVAGIA: return HasBossSoul(RG_VOLVAGIA_SOUL) && CanUse(RG_MEGATON_HAMMER); case RE_MORPHA: - return HasBossSoul(RG_MORPHA_SOUL) && CanUse(RG_HOOKSHOT) && - (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - CanUse(RG_MEGATON_HAMMER)); + return HasBossSoul(RG_MORPHA_SOUL) && CanUse(RG_HOOKSHOT) && (CanUseSword() || CanUse(RG_MEGATON_HAMMER)); case RE_BONGO_BONGO: return HasBossSoul(RG_BONGO_BONGO_SOUL) && - (CanUse(RG_LENS_OF_TRUTH) || ctx->GetTrickOption(RT_LENS_BONGO)) && - (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD)) && + (CanUse(RG_LENS_OF_TRUTH) || ctx->GetTrickOption(RT_LENS_BONGO)) && CanUseSword() && (CanUse(RG_HOOKSHOT) || CanUse(RG_FAIRY_BOW) || CanUse(RG_FAIRY_SLINGSHOT) || ctx->GetTrickOption(RT_SHADOW_BONGO)); case RE_TWINROVA: return HasBossSoul(RG_TWINROVA_SOUL) && CanUse(RG_MIRROR_SHIELD) && - (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || - CanUse(RG_MEGATON_HAMMER)); + (CanUseSword() || CanUse(RG_MEGATON_HAMMER)); case RE_GANONDORF: // RANDOTODO: Trick to use hammer (no jumpslash) or stick (only jumpslash) instead of a sword to reflect the // energy ball and either of them regardless of jumpslashing to damage and kill ganondorf @@ -771,8 +761,7 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal // for killing ganondorf and all of those can reflect the energy ball // This will not be the case once ammo logic in taken into account as // sticks are limited and using a bottle might become a requirement in that case - return HasBossSoul(RG_GANON_SOUL) && CanUse(RG_LIGHT_ARROWS) && - (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD)); + return HasBossSoul(RG_GANON_SOUL) && CanUse(RG_LIGHT_ARROWS) && CanUseSword(); case RE_GANON: return HasBossSoul(RG_GANON_SOUL) && CanUse(RG_MASTER_SWORD); case RE_DARK_LINK: @@ -792,8 +781,7 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal return CanUse(RG_BOOMERANG); case RE_BARI: return HookshotOrBoomerang() || CanUse(RG_FAIRY_BOW) || HasExplosives() || CanUse(RG_MEGATON_HAMMER) || - CanUse(RG_STICKS) || CanUse(RG_DINS_FIRE) || - (TakeDamage() && (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD))); + CanUse(RG_STICKS) || CanUse(RG_DINS_FIRE) || (TakeDamage() && CanUseSword()); case RE_SHABOM: // RANDOTODO when you add better damage logic, you can kill this by taking hits return CanUse(RG_BOOMERANG) || CanUse(RG_NUTS) || CanJumpslash() || CanUse(RG_DINS_FIRE) || @@ -962,7 +950,7 @@ bool Logic::CanBreakMudWalls() { } bool Logic::CanGetDekuBabaSticks() { - return (CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_BOOMERANG)); + return CanUseSword() || CanUse(RG_BOOMERANG); } bool Logic::CanGetDekuBabaNuts() { @@ -1060,9 +1048,13 @@ bool Logic::HasBottle() { return BottleCount() >= 1; } +bool Logic::CanUseSword() { + return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD); +} + bool Logic::CanJumpslashExceptHammer() { // Not including hammer as hammer jump attacks can be weird; - return CanUse(RG_STICKS) || CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD); + return CanUse(RG_STICKS) || CanUseSword(); } bool Logic::CanJumpslash() { diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 7e0daac904d..4f966f5ded9 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -209,6 +209,7 @@ class Logic { uint8_t BottleCount(); uint8_t OcarinaButtons(); bool HasBottle(); + bool CanUseSword(); bool CanJumpslashExceptHammer(); bool CanJumpslash(); bool CanHitSwitch(EnemyDistance distance = ED_CLOSE, bool inWater = false); From d40fa00b9e40d238f195b283b1910102b48f0fb8 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Tue, 8 Apr 2025 16:26:18 +0100 Subject: [PATCH 021/157] Mixed pools now properly disables itself when hidden. (#5341) * mixed pools now sets itself off when hidden * the clanging never ends --- soh/soh/Enhancements/randomizer/settings.cpp | 44 ++++++++++++++++---- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index c40f9916884..6f355326179 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -2014,16 +2014,16 @@ void Settings::UpdateOptionProperties() { } } - int dungeonShuffle = + bool dungeonShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonsEntrances"), RO_DUNGEON_ENTRANCE_SHUFFLE_OFF); - int bossShuffle = + bool bossShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleBossEntrances"), RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF); - int overworldShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOverworldEntrances"), RO_GENERIC_OFF); - int interiorShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleInteriorsEntrances"), RO_GENERIC_OFF); - int grottoShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGrottosEntrances"), RO_GENERIC_OFF); + bool overworldShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOverworldEntrances"), RO_GENERIC_OFF); + bool interiorShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleInteriorsEntrances"), RO_GENERIC_OFF); + bool grottoShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGrottosEntrances"), RO_GENERIC_OFF); - // Hide Mixed Entrances option if no applicable entrance shuffles are visible - if (!dungeonShuffle && !bossShuffle && !overworldShuffle && !interiorShuffle && !grottoShuffle) { + // Hide Mixed Entrances option if 1 or no applicable entrance shuffles are visible + if (dungeonShuffle + bossShuffle + overworldShuffle + interiorShuffle + grottoShuffle <= 1) { mOptions[RSK_MIXED_ENTRANCE_POOLS].Hide(); } else { mOptions[RSK_MIXED_ENTRANCE_POOLS].Unhide(); @@ -2782,6 +2782,36 @@ void Context::FinalizeSettings(const std::set& excludedLocation trials[i]->SetAsRequired(); } + bool dungeonShuffle = !mOptions[RSK_SHUFFLE_DUNGEON_ENTRANCES].Is(RO_GENERIC_OFF); + bool bossShuffle = !mOptions[RSK_SHUFFLE_BOSS_ENTRANCES].Is(RO_GENERIC_OFF); + bool overworldShuffle = !mOptions[RSK_SHUFFLE_OVERWORLD_ENTRANCES].Is(RO_GENERIC_OFF); + bool interiorShuffle = !mOptions[RSK_SHUFFLE_INTERIOR_ENTRANCES].Is(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF); + bool grottoShuffle = !mOptions[RSK_SHUFFLE_GROTTO_ENTRANCES].Is(RO_GENERIC_OFF); + + if (dungeonShuffle + bossShuffle + overworldShuffle + interiorShuffle + grottoShuffle <= 1) { + mOptions[RSK_MIXED_ENTRANCE_POOLS].Set(RO_GENERIC_OFF); + } + + if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !dungeonShuffle) { + mOptions[RSK_MIX_DUNGEON_ENTRANCES].Set(RO_GENERIC_OFF); + } + + if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !bossShuffle) { + mOptions[RSK_MIX_BOSS_ENTRANCES].Set(RO_GENERIC_OFF); + } + + if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !overworldShuffle) { + mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Set(RO_GENERIC_OFF); + } + + if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !interiorShuffle) { + mOptions[RSK_MIX_INTERIOR_ENTRANCES].Set(RO_GENERIC_OFF); + } + + if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !grottoShuffle) { + mOptions[RSK_MIX_GROTTO_ENTRANCES].Set(RO_GENERIC_OFF); + } + if (mOptions[RSK_FOREST].Is(RO_CLOSED_FOREST_ON) && (mOptions[RSK_SHUFFLE_INTERIOR_ENTRANCES].Is(RO_INTERIOR_ENTRANCE_SHUFFLE_ALL) || mOptions[RSK_SHUFFLE_OVERWORLD_ENTRANCES] || mOptions[RSK_SHUFFLE_OVERWORLD_SPAWNS] || From 4a3d60d0616ec2fe99d4c864137222831aad7db9 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:27:08 -0400 Subject: [PATCH 022/157] Fix Rando Give for Adult Fishing as Child Glitch (#5349) * VBVBVB * Update soh/soh/Enhancements/randomizer/hook_handlers.cpp * Update soh/soh/Enhancements/randomizer/hook_handlers.cpp * first try --- .../vanilla-behavior/GIVanillaBehavior.h | 8 ++++++++ soh/soh/Enhancements/randomizer/hook_handlers.cpp | 11 +++++++++++ soh/src/overlays/actors/ovl_Fishing/z_fishing.c | 4 +++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 1c516fc6eab..6c18a6edb1d 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -946,6 +946,14 @@ typedef enum { // - `*VBFishingData` VB_GIVE_RANDO_FISHING_PRIZE, + // #### `result` + // ```c + // false + // ``` + // #### `args` + // - '*Fishing' (&this) + VB_GIVE_RANDO_GLITCH_FISHING_PRIZE, + // #### `result` // ```c // true diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 4c96d839d30..a10e8b67fab 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -1591,6 +1591,17 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l } break; } + case VB_GIVE_RANDO_GLITCH_FISHING_PRIZE: { + if (IS_RANDO) { + Fishing* fishing = va_arg(args, Fishing*); + if (!Flags_GetRandomizerInf(RAND_INF_ADULT_FISHING)) { + Flags_SetRandomizerInf(RAND_INF_ADULT_FISHING); + } + *should = true; + fishing->stateAndTimer = 0; + } + break; + } case VB_TRADE_TIMER_EYEDROPS: { EnMk* enMk = va_arg(args, EnMk*); Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LH_TRADE_FROG); diff --git a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c index 3c2028ce017..94d7e15ca67 100644 --- a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c +++ b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c @@ -5170,7 +5170,9 @@ void Fishing_HandleOwnerDialog(Fishing* this, PlayState* play) { if (Actor_HasParent(&this->actor, play)) { this->stateAndTimer = 24; } else { - Actor_OfferGetItem(&this->actor, play, GI_SCALE_GOLDEN, 2000.0f, 1000.0f); + if (!GameInteractor_Should(VB_GIVE_RANDO_GLITCH_FISHING_PRIZE, false, &this)) { + Actor_OfferGetItem(&this->actor, play, GI_SCALE_GOLDEN, 2000.0f, 1000.0f); + } } break; From 88669742a20ee5b5a93c5a26b6997c63f89dc723 Mon Sep 17 00:00:00 2001 From: Archez Date: Tue, 8 Apr 2025 11:38:28 -0400 Subject: [PATCH 023/157] Fix copyright tex for GC JP (#5324) * Fix copyright tex for GC JP * add 2004 texture for jp ce --- soh/assets/objects/object_mag/object_mag.h | 6 ++++++ soh/assets/xml/GC_MQ_NTSC_J/objects/object_mag.xml | 4 ++-- .../xml/GC_NMQ_NTSC_J/objects/object_mag.xml | 4 ++-- .../xml/GC_NMQ_NTSC_J_CE/objects/object_mag.xml | 6 +++--- soh/src/overlays/actors/ovl_En_Mag/z_en_mag.c | 14 +++++++++++++- 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/soh/assets/objects/object_mag/object_mag.h b/soh/assets/objects/object_mag/object_mag.h index b470b71dd65..720d0cbe59d 100644 --- a/soh/assets/objects/object_mag/object_mag.h +++ b/soh/assets/objects/object_mag/object_mag.h @@ -18,6 +18,12 @@ static const ALIGN_ASSET(2) char gTitleCopyright19982002Tex[] = dgTitleCopyright #define dgTitleCopyright19982003Tex "__OTR__objects/object_mag/gTitleCopyright19982003Tex" static const ALIGN_ASSET(2) char gTitleCopyright19982003Tex[] = dgTitleCopyright19982003Tex; +#define dgTitleCopyright19982004EngTex "__OTR__objects/object_mag/gTitleCopyright19982004EngTex" +static const ALIGN_ASSET(2) char gTitleCopyright19982004EngTex[] = dgTitleCopyright19982004EngTex; + +#define dgTitleCopyright19982004JpnTex "__OTR__objects/object_mag/gTitleCopyright19982004JpnTex" +static const ALIGN_ASSET(2) char gTitleCopyright19982004JpnTex[] = dgTitleCopyright19982004JpnTex; + #define dgTitleMasterQuestSubtitleTex "__OTR__objects/object_mag/gTitleMasterQuestSubtitleTex" static const ALIGN_ASSET(2) char gTitleMasterQuestSubtitleTex[] = dgTitleMasterQuestSubtitleTex; diff --git a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mag.xml b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mag.xml index b2b082e6b36..6e07fa2cdb6 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mag.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/objects/object_mag.xml @@ -1,7 +1,7 @@ - + @@ -18,4 +18,4 @@ - \ No newline at end of file + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mag.xml b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mag.xml index b68d4227ad3..063d801a3f9 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mag.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/objects/object_mag.xml @@ -1,7 +1,7 @@ - + @@ -17,4 +17,4 @@ - \ No newline at end of file + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mag.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mag.xml index 84467d76d5d..6276dee1da6 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mag.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/objects/object_mag.xml @@ -1,8 +1,8 @@ - - + + @@ -18,4 +18,4 @@ - \ No newline at end of file + diff --git a/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.c b/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.c index b7ba0617a19..6290d65bee7 100644 --- a/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.c +++ b/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.c @@ -484,8 +484,17 @@ bool EnMag_ShouldDrawPressStart(Font* font, Gfx** gfxP, bool isActualText) { // Title logo is shifted to the left in Master Quest #define LOGO_X_SHIFT (isMQ ? 0 : -8) #define LOGO_TEX (isMQ ? gTitleZeldaShieldLogoMQTex : gTitleZeldaShieldLogoTex) +// Copyright texture is different depending on the version +// JPN CE displays two slightly different 2004 copyrights when lang is jpn or not +// Otherwise the other GC JPN versions either display 2002 for JPN or 2003 for others +// Else fallback to 2003 for GC or 1998 for N64 +#define COPYRIGHT_TEX \ + (isJpnGC_CE \ + ? (gSaveContext.language == LANGUAGE_JPN ? gTitleCopyright19982004JpnTex : gTitleCopyright19982004EngTex) \ + : (isGC ? ((isJpnGC_notCE || gSaveContext.language == LANGUAGE_JPN) ? gTitleCopyright19982002Tex \ + : gTitleCopyright19982003Tex) \ + : gTitleCopyright1998Tex)) // Copyright texture is larger on GC -#define COPYRIGHT_TEX (isGC ? gTitleCopyright19982003Tex : gTitleCopyright1998Tex) #define COPYRIGHT_TEX_WIDTH (isGC ? 160 : 128) #define COPYRIGHT_TEX_LEFT (isGC ? 78 : 94) @@ -515,6 +524,9 @@ void EnMag_DrawInner(Actor* thisx, PlayState* play, Gfx** gfxP) { u16 rectTop; bool isMQ = ResourceMgr_IsGameMasterQuest(); bool isGC = ResourceMgr_GetGamePlatform(0) == GAME_PLATFORM_GC; + bool isJpnGC_CE = isGC && ResourceMgr_GetGameVersion(0) == OOT_NTSC_JP_GC_CE; + bool isJpnGC_notCE = + isGC && (ResourceMgr_GetGameVersion(0) == OOT_NTSC_JP_GC || ResourceMgr_GetGameVersion(0) == OOT_NTSC_JP_MQ); gSPSegment(gfx++, 0x06, play->objectCtx.status[this->actor.objBankIndex].segment); From 1b17fea3d07f8ccc4a250b21adabd4c9d06800c9 Mon Sep 17 00:00:00 2001 From: Archez Date: Tue, 8 Apr 2025 12:53:39 -0400 Subject: [PATCH 024/157] Add temporary patch for invisible boulder fragments (#5352) --- .../Enhancements/cosmetics/authenticGfxPatches.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp index 477fe6f0af5..b2b473166d8 100644 --- a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp +++ b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp @@ -281,11 +281,24 @@ void PatchIronKnuckleTextureOverflow() { } } +void PatchBoulderFragment() { + // The boulder fragment renders invisible due to the change made by https://github.com/Kenix3/libultraship/pull/721 + // Until it is known wether this change is approriate or something else should be done to it, the following patches + // adjust the render mode for the DL to not become invisible + ResourceMgr_PatchGfxByName(gBoulderFragmentsDL, "boulderFragmentRenderFix3", 3, + gsDPSetRenderMode(G_RM_FOG_SHADE_A, G_RM_AA_ZB_OPA_SURF2)); + ResourceMgr_PatchGfxByName(gBoulderFragmentsDL, "boulderFragmentRenderFix6", 6, + gsDPSetCombineMode(G_CC_MODULATEIDECALA, G_CC_MODULATEIA_PRIM2)); +} + void ApplyAuthenticGfxPatches() { + // Overflow textures PatchArrowTipTexture(); PatchDekuStickTextureOverflow(); PatchFreezardTextureOverflow(); PatchIronKnuckleTextureOverflow(); + + PatchBoulderFragment(); } // Patches the Sold Out GI DL to render the texture in the mirror boundary From f0f6259d2f9c9c2acce5c10051fddbb71763d764 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Tue, 8 Apr 2025 10:22:25 -0700 Subject: [PATCH 025/157] Fix Docked Tracker Ghosting (#5354) * Dynamically changes a tracker's chosen color's opacity to full when it registers as docked to fix the framebuffer ghosting. * remove debugging --- .../Enhancements/randomizer/randomizer_item_tracker.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index 36b24aab71a..b86da3c0c25 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -1161,8 +1161,12 @@ void BeginFloatingWindows(std::string UniqueName, ImGuiWindowFlags flags = 0) { windowFlags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove; } } - ImGui::PushStyleColor(ImGuiCol_WindowBg, - VecFromRGBA8(CVarGetColor(CVAR_TRACKER_ITEM("BgColor.Value"), { 0, 0, 0, 0 }))); + auto color = VecFromRGBA8(CVarGetColor(CVAR_TRACKER_ITEM("BgColor.Value"), { 0, 0, 0, 0 })); + ImGuiWindow* window = ImGui::FindWindowByName(UniqueName.c_str()); + if (window != NULL && window->DockTabIsVisible) { + color.w = 1.0f; + } + ImGui::PushStyleColor(ImGuiCol_WindowBg, color); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); ImGui::Begin(UniqueName.c_str(), nullptr, windowFlags); From f5e0179e2b19c5ccef35e0e68166ce668b35d700 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Tue, 8 Apr 2025 11:44:24 -0700 Subject: [PATCH 026/157] Fix a couple typos in enhancement descriptions. (#5358) --- soh/soh/SohGui/SohMenuEnhancements.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 7f5f28fcab9..7861838827d 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -745,7 +745,7 @@ void SohMenu::AddMenuEnhancements() { }) .Options(CheckboxOptions().Tooltip( "Allows Light Arrows to activate Sun Switches. May require a room reload if toggled during gameplay.")); - AddWidget(path, "Bow and Child/Slingshot as Adult", WIDGET_CVAR_CHECKBOX) + AddWidget(path, "Bow as Child/Slingshot as Adult", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("BowSlingshotAmmoFix")) .Options(CheckboxOptions().Tooltip("Allows Child Link to use a Bow with Arrows.\n" "Allows Adult Link to use a Slingshot with Seeds.\n\n" @@ -1097,7 +1097,7 @@ void SohMenu::AddMenuEnhancements() { .CVar(CVAR_ENHANCEMENT("DeleteFileOnDeath")) .Options(CheckboxOptions().Tooltip("Dying will delete your file.\n\n" ICON_FA_EXCLAMATION_TRIANGLE " WARNING " ICON_FA_EXCLAMATION_TRIANGLE - "\nTHIS IS NOT REVERSABLE!\nUSE AT YOUR OWN RISK!")); + "\nTHIS IS NOT REVERSIBLE!\nUSE AT YOUR OWN RISK!")); AddWidget(path, "Always Win Goron Pot", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("GoronPot")) .Options(CheckboxOptions().Tooltip("Always get the Heart Piece/Purple Rupee from the Spinning Goron Pot.")); From c1885991fa33e546c36d1fb9cdd36f2faf1e8f95 Mon Sep 17 00:00:00 2001 From: Extloga <141232749+Extloga@users.noreply.github.com> Date: Tue, 8 Apr 2025 22:31:52 +0200 Subject: [PATCH 027/157] Additions for the German translation in two files (#5351) * Additions for the German translation in hint_list_exclude_dungeon.cpp * Additions for the German translation in hint_list_exclude_overworld.cpp * Additions for the German translation in hint_list_exclude_overworld.cpp --- .../hint_list/hint_list_exclude_dungeon.cpp | 56 +-- .../hint_list/hint_list_exclude_overworld.cpp | 324 ++++++++++-------- 2 files changed, 209 insertions(+), 171 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp index 73149f9096f..f29f2ecfb9b 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp @@ -124,11 +124,11 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, un #coeur dans l'Arbre Mojo# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DEKU_TREE_GRASS] = HintText(CustomMessage("They say that some #grass in the Deku Tree# hides #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß etwas #Gras im Deku-Baum# #[[1]]# verstecke.", /*french*/ "Selon moi, de l'#herbe dans l'Arbre Mojo# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_DEKU_TREE] = HintText(CustomMessage("They say that a #crate in the Deku Tree# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß eine #Kiste im Deku-Baum# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans l'Arbre Mojo# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -306,11 +306,11 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, #jouer un air orageux pour une pierre étrange dans la Caverne Dodongo# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DODONGOS_CAVERN_GRASS] = HintText(CustomMessage("They say that some #grass in Dodongo's Cavern# hides #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß etwas #Gras in Dodongos Höhle# #[[1]]# verstecke.", /*french*/ "Selon moi, de l'#herbe dans la Caverne Dodongo# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_DODONGOS_CAVERN] = HintText(CustomMessage("They say that a #crate in Dodongo's Cavern# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß eine #Kiste in Dodongos Höhle# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans la Caverne Dodongo# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -473,11 +473,11 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, près d'un #ascenceur central dans le Ventre de Jabu-Jabu# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_JABU_JABUS_BELLY_GRASS] = HintText(CustomMessage("They say that some #grass in Jabu Jabu's Belly# hides #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß etwas #Gras in Jabu-Jabus Bauch# #[[1]]# verstecke.", /*french*/ "Selon moi, de l'#herbe dans le Ventre de Jabu-Jabu# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_JABU_JABU] = HintText(CustomMessage("They say that a #crate in Jabu Jabu's Belly# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß eine #Kiste in Jabu-Jabus Bauch# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Ventre de Jabu-Jabu# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -706,12 +706,12 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß eine #Vase im Waldtempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #jarre dans le Temple de la Fôret# contient #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_FOREST_TEMPLE_HEART] = HintText(CustomMessage("They say that a #heart in the Forest Temple# hides #[[1]]#.", - /*german*/ "Man erzählt sich, daß ein #Herz im Waldtempel# #[[1]]# verstecke.", - /*french*/ "Selon moi, un #coeur dans le Temple de la Fôret# cache #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_FOREST_TEMPLE_HEART] = HintText(CustomMessage("They say that a #heart in the Forest Temple# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß ein #Herz im Waldtempel# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #coeur dans le Temple de la Fôret# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_FOREST_TEMPLE] = HintText(CustomMessage("They say that a #crate in Forest Temple# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß eine #Kiste im Waldtempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple de la Fôret# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -911,15 +911,15 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, un #coeur dans le Temple du Feu# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_FIRE_TEMPLE_MQ_LOOP_STALFOS_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in a hot arena# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne in einer heißen Arena# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler le soleil dans une arène chaude# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_FIRE_TEMPLE_MQ_LOOP_KNUCKLE_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun behind a knight's throne in a volcano# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne hinter dem Thron eines Ritters in einem Vulkan# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler le soleil derrière le trône d’un chevalier dans un volcan# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_FIRE_TEMPLE] = HintText(CustomMessage("They say that a #crate in Fire Temple# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß eine #Kiste im Feuertempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple du Feu# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -1058,25 +1058,25 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, une #rivière dans le Temple de l'Eau# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_WATER_TEMPLE_MQ_DARK_LINK_PILAR_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun atop a small pillar before a duel with one's shadow# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne auf einer kleinen Säule vor einem Duell mit seinem Schatten# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler le soleil au sommet d’un petit pilier avant un duel avec son ombre# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_WATER_TEMPLE_MQ_DARK_LINK_LEFT_STORM_FAIRY] = HintText(CustomMessage("They say that #calling the rain before a duel with one's shadow# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Anrufen des Regens vor einem Duell mit seinem Schatten# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler la pluie avant un duel avec son ombre# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_WATER_TEMPLE_MQ_DARK_LINK_RIGHT_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun before a duel with one's shadow# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne vor einem Duell mit seinem Schatten# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler le soleil avant un duel avec son ombre# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_WATER_TEMPLE] = HintText(CustomMessage("They say that a #crate in Water Temple# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß eine #Kiste im Wassertempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple de l'Eau# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- | SPIRIT TEMPLE | ---------------------------*/ - hintTextTable[RHT_SPIRIT_TEMPLE_CHILD_BRIDGE_CHEST] = HintText(CustomMessage("They say that a child conquers a #skull in green fire# in the Spirit Temple to reach #[[1]]#.", + hintTextTable[RHT_SPIRIT_TEMPLE_CHILD_BRIDGE_CHEST] = HintText(CustomMessage("They say that a child conquers a #skull in green fire# in the Spirit Temple to reach #[[1]]#.", /*german*/ "Man erzählt sich, daß ein Kind einen #Schädel in grünem Feuer# im Geistertempel erobere, um #[[1]]# zu erreichen.", /*french*/ "Selon moi, le #crâne au halo vert dans le colosse# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, el joven que #baje el puente# del Templo del Espíritu encontrará #[[1]]#. @@ -1311,15 +1311,15 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, une #jarre dans le Temple de l'Esprit# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SPIRIT_TEMPLE_HEART] = HintText(CustomMessage("They say that on a #small platform# in the Spirit Temple lies #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß auf einer #kleinen Plattform# im Geistertempel #[[1]]# läge.", /*french*/ "Selon moi, sur une #petite plateforme# dans le Temple de l'Esprit repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SPIRIT_TEMPLE_MQ_HEART] = HintText(CustomMessage("They say that guarded by a #ring of flame# in the Spirit Temple is #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß sich bewacht von einem #Ring der Flammen# im Geistertempel #[[1]]# befände.", /*french*/ "Selon moi, protégé par un #cercle de flammes# dans le Temple de l'Esprit se trouve #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_SPIRIT_TEMPLE] = HintText(CustomMessage("They say that a #crate in Spirit Temple# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß eine #Kiste im Geistertempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple de l'Esprit# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -1574,19 +1574,19 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, un #coeur dans le Temple de l'Ombre# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY] = HintText(CustomMessage("They say that an #calling the rain for a sentry guarding a house of the dead# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Anrufen des Regens für einen über ein Haus der Toten wachenden Posten# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler la pluie pour une sentinelle gardant une maison des morts# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SHADOW_TEMPLE_PIT_STORM_FAIRY] = HintText(CustomMessage("They say that an #calling the rain on a platform suspended above a bottomless pit# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Anrufen des Regens auf einer über einer bodenlosen Grube hängenden Plattform# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler la pluie sur une plateforme suspendue au-dessus d’un gouffre sans fond# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_SHADOW_TEMPLE_WIND_HINT_SUN_FAIRY] = HintText(CustomMessage("They say that an #calling the sun near an invisible chest guarded by the dead# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne nahe einer von Toten bewachten unsichtbaren Truhe# #[[1]]# enthülle.", /*french*/ "Selon moi, #appeler le soleil près d’un coffre invisible gardé par les morts# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_SHADOW_TEMPLE] = HintText(CustomMessage("They say that a #crate in Shadow Temple# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß eine #Kiste im Schattentempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple de l'Ombre# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- @@ -1716,7 +1716,7 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, un #coeur dans le Puits# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_BOTTOM_OF_THE_WELL_RUPEE] = HintText(CustomMessage("They say that a #hidden path through the floor# in the well leads to #[[1]]#.", - /*german*/ "Man erzählt sich, daß ein #versteckter Pfad durch den Grund# im Brunnen zu #[[1]]# führe.", + /*german*/ "Man erzählt sich, daß ein #versteckter Pfad durch den Boden# im Brunnen zu #[[1]]# führe.", /*french*/ "Selon moi, un #passage caché sous le sol# dans le puits mène à #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_BOTTOM_OF_THE_WELL_BASEMENT_SUN_FAIRY] = HintText(CustomMessage("They say that an #calling the sun a dead end# within the well reveals #[[1]]#.", @@ -1732,7 +1732,7 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, #appeler le soleil dans une impasse# à l’intérieur du puits révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_BOTTOM_OF_THE_WELL_GRASS] = HintText(CustomMessage("They say that some #grass in the Bottom of the Well# hides #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß etwas #Gras auf dem Grund des Brunnens# #[[1]]# verstecke.", /*french*/ "Selon moi, de l'#herbe dans le Puits# cache #[[1]]#.", {QM_RED, QM_GREEN})); @@ -2013,7 +2013,7 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, dans #une épreuve avec un Dinolfos# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_GERUDO_TRAINING_GROUND] = HintText(CustomMessage("They say that a #crate in Gerudo Training Ground# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß eine #Kiste auf dem Gerudo-Trainingsgelände# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Gymnase Gerudo# contient #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp index 685934dfa8d..1329bb377e4 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp @@ -6,7 +6,7 @@ namespace Rando { void StaticData::HintTable_Init_Exclude_Overworld() { // clang-format off - hintTextTable[RHT_KF_KOKIRI_SWORD_CHEST] = HintText(CustomMessage("They say that the #hidden treasure of the Kokiri# is #[[1]]#.", + hintTextTable[RHT_KF_KOKIRI_SWORD_CHEST] = HintText(CustomMessage("They say that the #hidden treasure of the Kokiri# is #[[1]]#.", /*german*/ "Man erzählt sich, daß der #versteckte Schatz der Kokiri# #[[1]]# sei.", /*french*/ "Selon moi, le #trésor des Kokiri# est #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, el #tesoro oculto de los Kokiri# esconde #[[1]]#. @@ -1557,463 +1557,501 @@ void StaticData::HintTable_Init_Exclude_Overworld() { /*french*/ "Selon moi, un cœur dans la #maison d'une amie chère# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_LOST_WOODS_RUPEE] = HintText(CustomMessage("They say that under a #boulder in the woods# lies #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß unter einem #Felsen in den Wäldern# #[[1]]# läge.", /*french*/ "Selon moi, sous un #rocher dans les bois# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_LOST_WOODS_SHORTCUT_RUPEE] = HintText(CustomMessage("They say that in a #pool of water in the woods# lies #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß in einem #Wasserbecken in den Wäldern# #[[1]]# läge.", /*french*/ "Selon moi, dans une #mare d'eau dans les bois# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_LAKE_HYLIA_RUPEE] = HintText(CustomMessage("They say that just off the #coast of a lake# lies #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß an der #Küste eines Sees# #[[1]]# läge.", /*french*/ "Selon moi, juste au large de la #côte d'un lac# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_LABORATORY_RUPEE] = HintText(CustomMessage("They say that at the #bottom of a tank# lies #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß auf dem #Grund eines Reservoirs# #[[1]]# läge.", /*french*/ "Selon moi, au #fond d'un réservoir# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DAMPES_GRAVE_RUPEE] = HintText(CustomMessage("They say that within a #quick-footed spirit's grave# lies #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß innerhalb eines #Grabes eines leichtfüßigen Geistes# #[[1]]# läge.", /*french*/ "Selon moi, dans la #tombe d'un esprit à pieds rapides# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_GERUDO_VALLEY_GROTTO_RUPEE] = HintText(CustomMessage("They say that an Octarok in an #underground spring# guards #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß ein Oktorok in einer #untergründigen Quelle# #[[1]]# bewache.", /*french*/ "Selon moi, un Octrocher dans une #source souterraine# garde #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DEATH_MOUNTAIN_TRAIL_RUPEE] = HintText(CustomMessage("They say that beneath a boulder on a #mountain's cliffside# lies #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß unterhalb eines Felsens an einem #abfallenden Felswand eines Berges# #[[1]]# läge.", /*french*/ "Selon moi, sous un rocher sur la #falaise d'une montagne# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DEATH_MOUNTAIN_TRAIL_GROTTO_HEART] = HintText(CustomMessage("They say that accompanying a #cow in a small cave# is #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß unterhalb eines Felsens an einem #abfallenden Felswand eines Berges# #[[1]]# läge.", /*french*/ "Selon moi, accompagner une #vache dans une petite grotte# est #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DEATH_MOUNTAIN_TRAIL_GROTTO_RUPEE] = HintText(CustomMessage("They say that accompanying a #cow in a small cave# is #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das Begleiten einer #Kuh in einer kleinen Höhle# #[[1]]# sei.", /*french*/ "Selon moi, accompagner une #vache dans une petite grotte# est #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_DEATH_MOUNTAIN_CRATER_RUPEE] = HintText(CustomMessage("They say that on a #small platform suspended above lava# lies #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß auf einer #oberhalb von Lava hängenden kleinen Plattform# #[[1]]# läge.", /*french*/ "Selon moi, sur une #petite plateforme suspendue au-dessus de la lave# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_ZORAS_RIVER_WATERFALL_RUPEE] = HintText(CustomMessage("They say that beneath a #waterfall feeding a narrow river# lies #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß unterhalb eines #einen schmalen Fluss nährenden Wasserfall# #[[1]]# läge.", /*french*/ "Selon moi, sous une #cascade alimentant une rivière étroite# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_ZORAS_FOUNTAIN_RUPEE] = HintText(CustomMessage("They say that at the bottom of a #partially-frozen spring# lies #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß auf dem Grund einer #teilweise gefrorenen Quelle# #[[1]]# läge.", /*french*/ "Selon moi, au fond d'une #source partiellement gelée# se trouve #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ hintTextTable[RHT_SFM_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath a forest meadow# rests #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß innerhalb #eines Brunnens unter einer Waldwiese# #[[1]]# ruhe.", /*french*/ "Selon moi, dans #une fontaine sous un bosquet forestier# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath a river# rests #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß innerhalb #eines Brunnens unter einem Fluß# #[[1]]# ruhe.", /*french*/ "Selon moi, dans #une fontaine sous une rivière# repose #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_HF_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath a a few trees bordering a wide field# rests #[[1]]#.", - /*german*/ TODO_TRANSLATE, + hintTextTable[RHT_HF_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath a few trees bordering a wide field# rests #[[1]]#.", + /*german*/ "Man erzählt sich, daß innerhalb #eines Brunnens unter ein weites Feld angrenzenden Bäumen# #[[1]]# ruhe.", /*french*/ "Selon moi, dans #une fontaine sous quelques arbres bordant un vaste champ# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZD_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath the home of the Zoras# rests #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß innerhalb #eines Brunnens unter der Heimat der Zoras# #[[1]]# ruhe.", /*french*/ "Selon moi, dans #une fontaine sous la maison des Zora# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GF_FAIRY_GROTTO_FAIRY] = HintText(CustomMessage("They say that within #a fountain beneath the home of thieves# rests #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß innerhalb #eines Brunnens unter der Heimat von Dieben# #[[1]]# ruhe.", /*french*/ "Selon moi, dans #une fontaine sous la maison des voleurs# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GRAVEYARD_SHIELD_GRAVE_FAIRY] = HintText(CustomMessage("They say that within #a fountain behind a wall within a grave# rests #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß innerhalb #eines Brunnens unter einer Mauer innerhalb eines Grabes# #[[1]]# ruhe.", /*french*/ "Selon moi, dans #une fontaine derrière un mur dans une tombe# repose #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_COLOSSUS_OASIS_FAIRY] = HintText(CustomMessage("They say that #restoring water to a dried oasis# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Wiederbringen von Wasser zu einer ausgetrockneten Oase# #[[1]]# enthülle.", /*french*/ "Selon moi, #restaurer l'eau dans un oasis asséché# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the riverside# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses am Flußufer# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse au bord de la rivière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout near a forest shop# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses nahe des Waldladens# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse près d'un magasin forestier# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_BEAN_SPROUT_NEAR_BRIDGE_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout near the entrance to the forest# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses nahe des Eingangs zum Wald# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse près de l'entrée de la forêt# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_BEAN_SPROUT_NEAR_THEATER_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout above a sylvan theatre# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses oberhalb eines waldigen Theaters# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse au-dessus d'un théâtre sylvestre# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the lakeside# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses am Seeufer# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse au bord du lac# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GV_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the side of a canyon# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses an der Seite einer Schlucht# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse sur le côté d'un canyon# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_COLOSSUS_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout near a temple of the sand# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses nahe eines Tempels des Sandes# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse près d'un temple du sable# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GRAVEYARD_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout within a graveyard# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses auf einem Friedhof# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse dans un cimetière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout within a volcano# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses innerhalb eines Vulkans# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse dans un volcan# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_DMT_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the moutainside# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + hintTextTable[RHT_DMT_BEAN_SPROUT_FAIRY] = HintText(CustomMessage("They say that #watering a young sprout on the mountainside# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Wässern eines jungen Sprosses an einem Berghang# #[[1]]# enthülle.", /*french*/ "Selon moi, #arroser une jeune pousse sur le flanc d'une montagne# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_TOT_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone in the ouskirts of the market# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + hintTextTable[RHT_TOT_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone in the outskirts of the market# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein am Rand des Marktes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange en périphérie du marché# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_TOT_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone in the ouskirts of the market# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + hintTextTable[RHT_TOT_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone in the outskirts of the market# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein am Rand des Marktes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange en périphérie du marché# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within a volcano# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein innerhalb eines Vulkans# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange dans un volcan# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within a volcano# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein innerhalb eines Vulkans# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange dans un volcan# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone on a mountain cliff face# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein an einer Bergklippe# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sur une falaise de montagne# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone on a mountain cliff face# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein an einer Bergklippe# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sur une falaise de montagne# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_COLOSSUS_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near the temple of the sane# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + hintTextTable[RHT_COLOSSUS_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near the temple of the sand# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein nahe des Tempels des Sandes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près du temple du sable# révèle #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_COLOSSUS_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near the temple of the sane# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + hintTextTable[RHT_COLOSSUS_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near the temple of the sand# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein nahe des Tempels des Sandes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près du temple du sable# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GV_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near a canyon waterfall# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein nahe eines Wasserfalls in einer Schlucht# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près d'une cascade de canyon# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GV_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near a canyon waterfall# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein nahe eines Wasserfalls in einer Schlucht# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près d'une cascade de canyon# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GC_MAZE_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone behind a maze of rock# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein hinter einem Felslabyrinth# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange derrière un labyrinthe de roches# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GC_MAZE_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone behind a maze of rock# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein hinter einem Felslabyrinth# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange derrière un labyrinthe de roches# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GC_MEDIGORON_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near a blacksmith# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein nahe eines Schmiedes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près d'un forgeron# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GC_MEDIGORON_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near a blacksmith# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein nahe eines Schmiedes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près d'un forgeron# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GRAVEYARD_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking the graveyard# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher den Friedhof überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant le cimetière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GRAVEYARD_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking the graveyard# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher den Friedhof überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant le cimetière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_MALON_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking the path to the castle# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher den Pfad zum Schloß überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant le chemin du château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_MALON_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking the path to the castle# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher den Pfad zum Schloß überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant le chemin du château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_ROCK_WALL_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near a secret path to the castle# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein nahe eines geheimen Pfades zum Schloß# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près d'un chemin secret vers le château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_ROCK_WALL_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near a secret path to the castle# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein nahe eines geheimen Pfades zum Schloß# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près d'un chemin secret vers le château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_STORMS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath the castle# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb des Schloßes# #[[1]]# enthülle.", /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous le château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_STORMS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath the castle# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb des Schloßes# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous le château révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_DEKU_TREE_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near an ancient tree# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein nahe eines antiken Baumes# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange près d'un arbre ancien révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_DEKU_TREE_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near an ancient tree# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein nahe eines antiken Baumes# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange près d'un arbre ancien révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking a forest village# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher ein Dorf im Wald überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking a forest village# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher ein Dorf im Wald überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_STORMS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath a forest village# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Dorfes im Wald# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_STORMS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath a forest village# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Dorfes im Wald# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_LAB_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking the river feeding a lake# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher einen See nährenden Fluß überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant la rivière qui alimente un lac révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_LAB_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking the river feeding a lake# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher einen See nährenden Fluß überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant la rivière qui alimente un lac révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHEAST_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone at the back of a lake# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein am Rücken eines Sees# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHEAST_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone at the back of a lake# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein am Rücken eines Sees# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHWEST_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone at the back of a lake# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein am Rücken eines Sees# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHWEST_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone at the back of a lake# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein am Rücken eines Sees# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within a perplexing wood# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein innerhalb eines verwirrenden Waldes# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange dans un bois déroutant révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within a perplexing wood# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein innerhalb eines verwirrenden Waldes# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange dans un bois déroutant révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_MAZE_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking a forest maze# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher ein Waldlabyrinth überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant un labyrinthe forestier révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_MAZE_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking a forest maze# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher ein Waldlabyrinth überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant un labyrinthe forestier révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_SARIA_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone watching a hiding place in the woods# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher ein Versteck im Wald überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surveillant une cachette dans les bois révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_SARIA_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone watching a hiding place in the woods# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher ein Versteck im Wald überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surveillant une cachette dans les bois révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZD_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone listening to an aquatic king# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher einem aquatischen König zuhört#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange écoutant un roi aquatique révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZD_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone listening to an aquatic king# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher einem aquatischen König zuhört#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange écoutant un roi aquatique révèle [[1]].", {QM_RED, QM_GREEN})); - hintTextTable[RHT_ZF_FAIRY_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone in the ouskirts of a deap fountain# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + hintTextTable[RHT_ZF_FAIRY_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone in the outskirts of a deep fountain# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein am Rand eines tiefen Brunnens# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange en périphérie d'une profonde fontaine révèle [[1]].", {QM_RED, QM_GREEN})); - hintTextTable[RHT_ZF_FAIRY_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone in the ouskirts of a deap fountain# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + hintTextTable[RHT_ZF_FAIRY_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone in the outskirts of a deep fountain# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein am Rand eines tiefen Brunnens# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange en périphérie d'une profonde fontaine révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZF_JABU_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone watching a guardian of the sea# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher einen Wächter des Meeres beobachtet#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surveillant un gardien de la mer révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZF_JABU_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone watching a guardian of the sea# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher einen Wächter des Meeres beobachtet#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surveillant un gardien de la mer révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_GROTTOS_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overwatching a river# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher einen Fluß überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant une rivière révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_GROTTOS_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overwatching a river# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher einen Fluß überblickt#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant une rivière révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_DOMAIN_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath a waterfall# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Wasserfalls# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous une cascade révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_DOMAIN_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath a waterfall# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Wasserfalls# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous une cascade révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_COW_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone hiding near a cow# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher sich nahe einer Kuh versteckt#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange cachée près d'une vache révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_COW_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone hiding near a cow# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher sich nahe einer Kuh versteckt#, #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange cachée près d'une vache révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath the entrance to the market# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb des Eingangs zum Markt# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous l'entrée du marché révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath the entrance to the market# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb des Eingangs zum Markt# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous l'entrée du marché révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_SOUTHEAST_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath trees guarded by a Peahat# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb von Killeranas bewachten Bäumen# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous des arbres gardés par un Peahat révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_SOUTHEAST_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath trees guarded by a Peahat# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb von Killeranas bewachten Bäumen# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous des arbres gardés par un Peahat révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KAK_OPEN_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath a village at the base of a mountain# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Dorfes am Fuße eines Berges# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous un village au pied d'une montagne révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KAK_OPEN_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath a village at the base of a mountain# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Dorfes am Fuße eines Berges# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous un village au pied d'une montagne révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_OPEN_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within a plateau by a river# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein auf einem Plateau von einem Fluß# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange dans un plateau près d'une rivière révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_OPEN_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within a plateau by a river# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein auf einem Plateau bei einem Fluß# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange dans un plateau près d'une rivière révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath an escape from the forest# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Auswegs des Waldes# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous une issue de la forêt révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath an escape from the forest# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Auswegs des Waldes# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous une issue de la forêt révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_STORMS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath the entrance to a village within a mountain# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Eingangs zu einem Dorf innerhalb eines Berges# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous l'entrée d'un village dans une montagne révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_STORMS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath the entrance to a village within a mountain# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Eingangs zu einem Dorf innerhalb eines Berges# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous l'entrée d'un village dans une montagne révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_UPPER_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within the side of a crater# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein innerhalb der Seite eines Kraters# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange dans le côté d'un cratère révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_UPPER_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within the side of a crater# reveals #[[1]]#.", - /*german*/ TODO_TRANSLATE, + /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein innerhalb der Seite eines Kraters# #[[1]]# enthülle.", /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange dans le côté d'un cratère révèle [[1]].", {QM_RED, QM_GREEN})); - hintTextTable[RHT_LH_ISLAND_SUN_FAIRY] = HintText(CustomMessage("They say that #summoning the sun on the lake's island# calls #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_LH_ISLAND_SUN_FAIRY] = HintText(CustomMessage("They say that #summoning the sun on the lake's island# calls #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Beschwören der Sonne auf der Insel eines Sees# #[[1]]# anrufe.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_HF_POND_STORMS_FAIRY] = HintText(CustomMessage("They say that #calling rain to the field's pond# summons #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_HF_POND_STORMS_FAIRY] = HintText(CustomMessage("They say that #calling rain to the field's pond# summons #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Anrufen des Regens für den Tümpel eines Feldes# #[[1]]# beschwöre.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_HF_FENCE_GROTTO_STORMS_FAIRY] = HintText(CustomMessage("They say that #making it rain in a scrub's cave# wakes #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_HF_FENCE_GROTTO_STORMS_FAIRY] = HintText(CustomMessage("They say that #making it rain in a scrub's cave# wakes #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Machen von Regen in der Höhle eines Dekus# #[[1]]# erwecke.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_DMT_FLAG_SUN_FAIRY] = HintText(CustomMessage("They say that #changing the time in front of the trail's flag# reveals #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_DMT_FLAG_SUN_FAIRY] = HintText(CustomMessage("They say that #changing the time in front of the trail's flag# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Verändern der Zeit im Angesicht der Flagge eines Pfades# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_DMT_COW_GROTTO_STORMS_FAIRY] = HintText(CustomMessage("They say that #calling a storm for a lonely cow# reveals #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_DMT_COW_GROTTO_STORMS_FAIRY] = HintText(CustomMessage("They say that #calling a storm for a lonely cow# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Anrufen eines Sturms für eine einsame Kuh# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_LW_SHORTCUT_STORMS_FAIRY] = HintText(CustomMessage("They say that #making it rain in the Lost Woods# reveals #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_LW_SHORTCUT_STORMS_FAIRY] = HintText(CustomMessage("They say that #making it rain in the Lost Woods# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Machen von Regen in den verlorenen Wäldern# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_GF_KITCHEN_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in a guarded kitchen# exposes #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_GF_KITCHEN_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in a guarded kitchen# exposes #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne in einer bewachten Küche# #[[1]]# freilege.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun for scrubs in the Lost Woods# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun for scrubs in the Lost Woods# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne für Dekus in den verlorenen Wäldern# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in a royal tomb# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in a royal tomb# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne in einer königlichen Gruft# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_KF_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a forest# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_KF_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a forest# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in einem Wald# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_LW_GRASS] = HintText(CustomMessage("They say that #cutting some grass in the woods# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_LW_GRASS] = HintText(CustomMessage("They say that #cutting some grass in the woods# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in den Wäldern# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_MARKET_GRASS] = HintText(CustomMessage("They say that #cutting some grass in the market# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_MARKET_GRASS] = HintText(CustomMessage("They say that #cutting some grass in the market# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser auf dem Markt# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_HC_GRASS] = HintText(CustomMessage("They say that #cutting some grass near the castle# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_HC_GRASS] = HintText(CustomMessage("They say that #cutting some grass near the castle# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in der Nähe vom Schloß# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_KAK_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a village# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_KAK_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a village# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in einem Dorf# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_GY_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a graveyard# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_GY_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a graveyard# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser auf einem Friedhof# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_LH_GRASS] = HintText(CustomMessage("They say that #cutting some grass near a lake# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_LH_GRASS] = HintText(CustomMessage("They say that #cutting some grass near a lake# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser nahe eines Sees# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_HF_GRASS] = HintText(CustomMessage("They say that #cutting some grass on a field# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_HF_GRASS] = HintText(CustomMessage("They say that #cutting some grass on a field# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser auf einem Feld# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_ZR_GRASS] = HintText(CustomMessage("They say that #cutting some grass near a river# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_ZR_GRASS] = HintText(CustomMessage("They say that #cutting some grass near a river# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in der Nähe von einem Fluß# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); - hintTextTable[RHT_GROTTO_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a grotto# reveals #[[1]]#.", { QM_RED, QM_GREEN })); + hintTextTable[RHT_GROTTO_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a grotto# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in einer Grotte# #[[1]]# enthülle.", + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_GERUDO_VALLEY] = HintText(CustomMessage("They say that a #crate in Gerudo Valley# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, - /*french*/ "Selon moi, une #caisse dans une vallée# a #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste im Gerudotal# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans une vallée# a #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_GERUDOS_FORTRESS] = HintText(CustomMessage("They say that a #crate in Gerudo Fortress# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, - /*french*/ "Selon moi, une #caisse dans la Forteresse Gerudo# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste in der Gerudofestung# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans la Forteresse Gerudo# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_WASTELAND] = HintText(CustomMessage("They say that a #crate in Haunted Wasteland# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, - /*french*/ "Selon moi, une #caisse dans le Désert Hanté# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste im Gerudotal# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans le Désert Hanté# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_MARKET] = HintText(CustomMessage("They say that a #crate in the Market# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, - /*french*/ "Selon moi, une #caisse dans la Place du Marché# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste in der Gespensterwüste# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans la Place du Marché# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_KAKARIKO_VILLAGE] = HintText(CustomMessage("They say that a #crate in Kakariko Village# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, - /*french*/ "Selon moi, une #caisse dans le Village de Cocorico# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste im Dorf von Kakariko# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans le Village de Cocorico# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_GRAVEYARD] = HintText(CustomMessage("They say that a #crate in the Graveyard# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, - /*french*/ "Selon moi, une #caisse dans le Cimetière# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste auf dem Friedhof# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans le Cimetière# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_GORON_CITY] = HintText(CustomMessage("They say that a #crate in Goron City# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, - /*french*/ "Selon moi, une #caisse dans le Village Goron# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste in Goronia# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans le Village Goron# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_DEATH_MOUNTAIN_CRATER] = HintText(CustomMessage("They say that a #crate in Death Mountain Crater# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, - /*french*/ "Selon moi, une #caisse sur le Mont du Péril# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste im Todeskrater# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse sur le Mont du Péril# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_LON_LON_RANCH] = HintText(CustomMessage("They say that a #crate in Lon Lon Ranch# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, - /*french*/ "Selon moi, une #caisse dans le Ranch Lon Lon# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste auf der Lon Lon-Farm# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans le Ranch Lon Lon# contient #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_LAKESIDE_LABORATORY] = HintText(CustomMessage("They say that a #crate in the Laboratory# contains #[[1]]#.", - /*german*/ TODO_TRANSLATE, - /*french*/ "Selon moi, une #caisse dans un laboratoire# contient #[[1]]#.", { QM_RED, QM_GREEN })); + /*german*/ "Man erzählt sich, daß eine #Kiste im Laboratorium# #[[1]]# enthielte.", + /*french*/ "Selon moi, une #caisse dans un laboratoire# contient #[[1]]#.", {QM_RED, QM_GREEN})); // clang-format on } From f2161297a8d63c416d2bbed03ccde73cb4ac96c5 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 9 Apr 2025 16:21:22 +0100 Subject: [PATCH 028/157] Fix break room access and crates underneath crates logic (#5347) * fix break room acces and crates underneath crates logic * apply GF_JUMP to the jump from NEAR_GS to LONG_ROOF to reach BREAK_ROOM --- .../location_access/gerudo_fortress.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/gerudo_fortress.cpp b/soh/soh/Enhancements/randomizer/location_access/gerudo_fortress.cpp index 4b4579210a0..dd95b11fb01 100644 --- a/soh/soh/Enhancements/randomizer/location_access/gerudo_fortress.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/gerudo_fortress.cpp @@ -27,8 +27,8 @@ void RegionTable_Init_GerudoFortress() { LOCATION(RC_GF_GERUDO_MEMBERSHIP_CARD, logic->CanFinishGerudoFortress()), LOCATION(RC_GF_GS_ARCHERY_RANGE, logic->IsAdult && logic->HookshotOrBoomerang() && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) && logic->CanGetNightTimeGS()), LOCATION(RC_GF_GS_TOP_FLOOR, logic->IsAdult && (logic->CanJumpslashExceptHammer() || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->HasExplosives() || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_DINS_FIRE)) && (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_GF_KITCHEN) || ctx->GetTrickOption(RT_GF_JUMP)) && logic->CanGetNightTimeGS()), - LOCATION(RC_GF_BREAK_ROOM_POT_1, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakPots()), - LOCATION(RC_GF_BREAK_ROOM_POT_2, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakPots()), + LOCATION(RC_GF_BREAK_ROOM_POT_1, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || (logic->CanUse(RG_HOVER_BOOTS) || (ctx->GetTrickOption(RT_GF_JUMP) && (ctx->GetTrickOption(RT_GF_KITCHEN) || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT))))) && logic->CanBreakPots()), + LOCATION(RC_GF_BREAK_ROOM_POT_2, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || (logic->CanUse(RG_HOVER_BOOTS) || (ctx->GetTrickOption(RT_GF_JUMP) && (ctx->GetTrickOption(RT_GF_KITCHEN) || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT))))) && logic->CanBreakPots()), LOCATION(RC_GF_KITCHEN_POT_1, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakPots()), LOCATION(RC_GF_KITCHEN_POT_2, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakPots()), LOCATION(RC_GF_NORTH_F1_CARPENTER_POT_1, logic->CanBreakPots()), @@ -43,7 +43,7 @@ void RegionTable_Init_GerudoFortress() { LOCATION(RC_GF_SOUTH_F1_CARPENTER_CELL_POT_2, logic->CanBreakPots()), LOCATION(RC_GF_SOUTH_F1_CARPENTER_CELL_POT_3, logic->CanBreakPots()), LOCATION(RC_GF_SOUTH_F1_CARPENTER_CELL_POT_4, logic->CanBreakPots()), - LOCATION(RC_GF_ABOVE_JAIL_CRATE, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), + LOCATION(RC_GF_ABOVE_JAIL_CRATE, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || (logic->CanUse(RG_HOOKSHOT) && ctx->GetTrickOption(RT_GF_JUMP))) && logic->CanBreakCrates()), LOCATION(RC_GF_OUTSIDE_CENTER_CRATE_1, logic->CanBreakCrates()), LOCATION(RC_GF_OUTSIDE_CENTER_CRATE_2, logic->CanBreakCrates()), LOCATION(RC_GF_OUTSIDE_CENTER_CRATE_3, logic->CanBreakCrates()), @@ -60,7 +60,7 @@ void RegionTable_Init_GerudoFortress() { LOCATION(RC_GF_ARCHERY_START_CRATE_1, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && logic->CanBreakCrates()), LOCATION(RC_GF_ARCHERY_START_CRATE_2, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && logic->CanBreakCrates()), LOCATION(RC_GF_ARCHERY_LEFT_END_CRATE_1, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && logic->CanBreakCrates()), - LOCATION(RC_GF_ARCHERY_LEFT_END_CRATE_2, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && logic->CanBreakCrates()), + LOCATION(RC_GF_ARCHERY_LEFT_END_CRATE_2, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && (logic->IsAdult || (logic->BlastOrSmash() || logic->HookshotOrBoomerang() || logic->CanUse(RG_HOVER_BOOTS)))), LOCATION(RC_GF_ARCHERY_LEFT_END_CHILD_CRATE, logic->IsChild && logic->HasExplosives() && logic->CanBreakCrates()), LOCATION(RC_GF_ARCHERY_RIGHT_END_CRATE_1, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && logic->CanBreakCrates()), LOCATION(RC_GF_ARCHERY_RIGHT_END_CRATE_2, (logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) && logic->CanBreakCrates()), @@ -69,10 +69,10 @@ void RegionTable_Init_GerudoFortress() { LOCATION(RC_GF_KITCHEN_CRATE_3, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), LOCATION(RC_GF_KITCHEN_CRATE_4, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), LOCATION(RC_GF_KITCHEN_CRATE_5, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), - LOCATION(RC_GF_BREAK_ROOM_CRATE_1, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), - LOCATION(RC_GF_BREAK_ROOM_CRATE_2, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), - LOCATION(RC_GF_BREAK_ROOM_CRATE_3, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), - LOCATION(RC_GF_BREAK_ROOM_CRATE_4, ((logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), + LOCATION(RC_GF_BREAK_ROOM_CRATE_1, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || (logic->CanUse(RG_HOVER_BOOTS) || (ctx->GetTrickOption(RT_GF_JUMP) && (ctx->GetTrickOption(RT_GF_KITCHEN) || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT))))) && logic->CanBreakCrates()), + LOCATION(RC_GF_BREAK_ROOM_CRATE_2, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || (logic->CanUse(RG_HOVER_BOOTS) || (ctx->GetTrickOption(RT_GF_JUMP) && (ctx->GetTrickOption(RT_GF_KITCHEN) || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT))))) && logic->CanBreakCrates()), + LOCATION(RC_GF_BREAK_ROOM_CRATE_3, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || ((logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_GF_JUMP)) && (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW)))) && logic->CanBreakCrates()), + LOCATION(RC_GF_BREAK_ROOM_CRATE_4, (logic->CanUse(RG_LONGSHOT) || (logic->IsAdult && logic->CanUse(RG_SCARECROW)) || ((logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_GF_JUMP)) && (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW)))) && logic->CanBreakCrates()), LOCATION(RC_GF_NORTH_F1_CARPENTER_CRATE, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), LOCATION(RC_GF_NORTH_F3_CARPENTER_CRATE, (logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT)) && logic->CanBreakCrates()), LOCATION(RC_GF_SOUTH_F2_CARPENTER_CRATE_1, logic->CanBreakCrates()), From 30520a7565b239c4e58ea514a20464f8f59f76c2 Mon Sep 17 00:00:00 2001 From: PurpleHato Date: Wed, 9 Apr 2025 17:35:39 +0200 Subject: [PATCH 029/157] Rando: Last missing french + tiny tweak (#5359) * Update hint_list_exclude_overworld.cpp * missing ones * Oops forgot a ' --- .../hint_list/hint_list_exclude_dungeon.cpp | 2 +- .../hint_list/hint_list_exclude_overworld.cpp | 148 +++++++++--------- 2 files changed, 75 insertions(+), 75 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp index f29f2ecfb9b..75072e72a24 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp @@ -303,7 +303,7 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { hintTextTable[RHT_DODONGOS_CAVERN_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone in Dodongo's Cavern# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein in Dodongos Höhle# #[[1]]# enthülle.", - /*french*/ "Selon moi, #jouer un air orageux pour une pierre étrange dans la Caverne Dodongo# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange dans la Caverne Dodongo# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DODONGOS_CAVERN_GRASS] = HintText(CustomMessage("They say that some #grass in Dodongo's Cavern# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß etwas #Gras in Dodongos Höhle# #[[1]]# verstecke.", diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp index 1329bb377e4..b47c6864623 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp @@ -1759,259 +1759,259 @@ void StaticData::HintTable_Init_Exclude_Overworld() { hintTextTable[RHT_HC_STORMS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath the castle# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb des Schloßes# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous le château révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous le château# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_DEKU_TREE_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone near an ancient tree# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein nahe eines antiken Baumes# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange près d'un arbre ancien révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange près d'un arbre ancien# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_DEKU_TREE_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone near an ancient tree# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein nahe eines antiken Baumes# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange près d'un arbre ancien révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange près d'un arbre ancien# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking a forest village# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher ein Dorf im Wald überblickt#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant un village forestier# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking a forest village# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher ein Dorf im Wald überblickt#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant un village forestier# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_STORMS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath a forest village# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Dorfes im Wald# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous un village forestier# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_STORMS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath a forest village# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Dorfes im Wald# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous un village forestier révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous un village forestier# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_LAB_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking the river feeding a lake# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher einen See nährenden Fluß überblickt#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant la rivière qui alimente un lac révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant la rivière qui alimente un lac# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_LAB_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking the river feeding a lake# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher einen See nährenden Fluß überblickt#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant la rivière qui alimente un lac révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant la rivière qui alimente un lac# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHEAST_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone at the back of a lake# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein am Rücken eines Sees# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange au fond d'un lac# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHEAST_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone at the back of a lake# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein am Rücken eines Sees# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange au fond d'un lac# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHWEST_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone at the back of a lake# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein am Rücken eines Sees# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange au fond d'un lac# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_SOUTHWEST_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone at the back of a lake# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein am Rücken eines Sees# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange au fond d'un lac révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange au fond d'un lac# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within a perplexing wood# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein innerhalb eines verwirrenden Waldes# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange dans un bois déroutant révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange dans un bois déroutant# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within a perplexing wood# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein innerhalb eines verwirrenden Waldes# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange dans un bois déroutant révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange dans un bois déroutant# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_MAZE_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overlooking a forest maze# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher ein Waldlabyrinth überblickt#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant un labyrinthe forestier révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant un labyrinthe forestier# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_MAZE_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overlooking a forest maze# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher ein Waldlabyrinth überblickt#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant un labyrinthe forestier révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant un labyrinthe forestier# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_SARIA_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone watching a hiding place in the woods# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher ein Versteck im Wald überblickt#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surveillant une cachette dans les bois révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surveillant une cachette dans les bois# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_SFM_SARIA_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone watching a hiding place in the woods# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher ein Versteck im Wald überblickt#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surveillant une cachette dans les bois révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surveillant une cachette dans les bois# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZD_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone listening to an aquatic king# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher einem aquatischen König zuhört#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange écoutant un roi aquatique révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange écoutant un roi aquatique# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZD_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone listening to an aquatic king# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher einem aquatischen König zuhört#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange écoutant un roi aquatique révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange écoutant un roi aquatique# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZF_FAIRY_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone in the outskirts of a deep fountain# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein am Rand eines tiefen Brunnens# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange en périphérie d'une profonde fontaine révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange en périphérie d'une profonde fontaine# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZF_FAIRY_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone in the outskirts of a deep fountain# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein am Rand eines tiefen Brunnens# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange en périphérie d'une profonde fontaine révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange en périphérie d'une profonde fontaine# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZF_JABU_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone watching a guardian of the sea# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher einen Wächter des Meeres beobachtet#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surveillant un gardien de la mer révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surveillant un gardien de la mer# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZF_JABU_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone watching a guardian of the sea# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher einen Wächter des Meeres beobachtet#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surveillant un gardien de la mer révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surveillant un gardien de la mer# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_GROTTOS_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone overwatching a river# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher einen Fluß überblickt#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange surplombant une rivière révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange surplombant une rivière# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_GROTTOS_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone overwatching a river# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher einen Fluß überblickt#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange surplombant une rivière révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange surplombant une rivière# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_DOMAIN_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath a waterfall# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Wasserfalls# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous une cascade révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous une cascade# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_NEAR_DOMAIN_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath a waterfall# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Wasserfalls# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous une cascade révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous une cascade# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_COW_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone hiding near a cow# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein, welcher sich nahe einer Kuh versteckt#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange cachée près d'une vache révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange cachée près d'une vache# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_COW_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone hiding near a cow# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein, welcher sich nahe einer Kuh versteckt#, #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange cachée près d'une vache révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange cachée près d'une vache# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath the entrance to the market# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb des Eingangs zum Markt# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous l'entrée du marché révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous l'entrée du marché# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath the entrance to the market# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb des Eingangs zum Markt# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous l'entrée du marché révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous l'entrée du marché# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_SOUTHEAST_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath trees guarded by a Peahat# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb von Killeranas bewachten Bäumen# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous des arbres gardés par un Peahat révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous des arbres gardés par un Peahat# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_SOUTHEAST_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath trees guarded by a Peahat# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb von Killeranas bewachten Bäumen# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous des arbres gardés par un Peahat révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous des arbres gardés par un Peahat# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KAK_OPEN_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath a village at the base of a mountain# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Dorfes am Fuße eines Berges# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous un village au pied d'une montagne révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous un village au pied d'une montagne# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_KAK_OPEN_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath a village at the base of a mountain# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Dorfes am Fuße eines Berges# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous un village au pied d'une montagne révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous un village au pied d'une montagne# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_OPEN_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within a plateau by a river# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein auf einem Plateau von einem Fluß# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange dans un plateau près d'une rivière révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange dans un plateau près d'une rivière# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_OPEN_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within a plateau by a river# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein auf einem Plateau bei einem Fluß# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange dans un plateau près d'une rivière révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange dans un plateau près d'une rivière# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath an escape from the forest# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Auswegs des Waldes# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous une issue de la forêt révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous une issue de la forêt# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath an escape from the forest# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Auswegs des Waldes# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous une issue de la forêt révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous une issue de la forêt# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_STORMS_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone beneath the entrance to a village within a mountain# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein unterhalb eines Eingangs zu einem Dorf innerhalb eines Berges# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange sous l'entrée d'un village dans une montagne révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange sous l'entrée d'un village dans une montagne# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_STORMS_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone beneath the entrance to a village within a mountain# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein unterhalb eines Eingangs zu einem Dorf innerhalb eines Berges# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange sous l'entrée d'un village dans une montagne révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange sous l'entrée d'un village dans une montagne# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_UPPER_GROTTO_GOSSIP_STONE_FAIRY] = HintText(CustomMessage("They say that #playing a tune for an odd stone within the side of a crater# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer Melodie für einen seltsamen Stein innerhalb der Seite eines Kraters# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie pour une pierre étrange dans le côté d'un cratère révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie pour une pierre étrange dans le côté d'un cratère# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMC_UPPER_GROTTO_GOSSIP_STONE_FAIRY_BIG] = HintText(CustomMessage("They say that #playing a stormy tune for an odd stone within the side of a crater# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Spielen einer stürmischen Melodie für einen seltsamen Stein innerhalb der Seite eines Kraters# #[[1]]# enthülle.", - /*french*/ "Selon moi, jouer une mélodie orageuse pour une pierre étrange dans le côté d'un cratère révèle [[1]].", {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une pierre étrange dans le côté d'un cratère# révèle [[1]].", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_ISLAND_SUN_FAIRY] = HintText(CustomMessage("They say that #summoning the sun on the lake's island# calls #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Beschwören der Sonne auf der Insel eines Sees# #[[1]]# anrufe.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #appeler le soleil sur l'île du lac # révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_POND_STORMS_FAIRY] = HintText(CustomMessage("They say that #calling rain to the field's pond# summons #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Anrufen des Regens für den Tümpel eines Feldes# #[[1]]# beschwöre.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse dans l'étang d'une plaine# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_FENCE_GROTTO_STORMS_FAIRY] = HintText(CustomMessage("They say that #making it rain in a scrub's cave# wakes #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Machen von Regen in der Höhle eines Dekus# #[[1]]# erwecke.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse dans la grotte d'une Peste Mojo# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_FLAG_SUN_FAIRY] = HintText(CustomMessage("They say that #changing the time in front of the trail's flag# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Verändern der Zeit im Angesicht der Flagge eines Pfades# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #appeler le soleil devant un drapeau du Mont du Péril # révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_DMT_COW_GROTTO_STORMS_FAIRY] = HintText(CustomMessage("They say that #calling a storm for a lonely cow# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Anrufen eines Sturms für eine einsame Kuh# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse pour une vache solitaire# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_SHORTCUT_STORMS_FAIRY] = HintText(CustomMessage("They say that #making it rain in the Lost Woods# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Machen von Regen in den verlorenen Wäldern# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #jouer une mélodie orageuse dans les Bois Perdus# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GF_KITCHEN_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in a guarded kitchen# exposes #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne in einer bewachten Küche# #[[1]]# freilege.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #appeler le soleil dans une cuisine gardée# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun for scrubs in the Lost Woods# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne für Dekus in den verlorenen Wäldern# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #appeler le soleil pour les Pestes dans les Bois Perdus # révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in a royal tomb# reveals #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Anrufen der Sonne in einer königlichen Gruft# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*french*/ "Selon moi, #appeler le soleil dans une tombe royale# révèle #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_KF_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a forest# reveals #[[1]]#.", - /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in einem Wald# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in einem Wald# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe dans un fôret# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_LW_GRASS] = HintText(CustomMessage("They say that #cutting some grass in the woods# reveals #[[1]]#.", - /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in den Wäldern# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in den Wäldern# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe dans des bois# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_MARKET_GRASS] = HintText(CustomMessage("They say that #cutting some grass in the market# reveals #[[1]]#.", - /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser auf dem Markt# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser auf dem Markt# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe dans un marché# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HC_GRASS] = HintText(CustomMessage("They say that #cutting some grass near the castle# reveals #[[1]]#.", - /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in der Nähe vom Schloß# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in der Nähe vom Schloß# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe près du château# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_KAK_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a village# reveals #[[1]]#.", - /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in einem Dorf# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in einem Dorf# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe dans un village# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GY_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a graveyard# reveals #[[1]]#.", - /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser auf einem Friedhof# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser auf einem Friedhof# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe dans un cimetère# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_LH_GRASS] = HintText(CustomMessage("They say that #cutting some grass near a lake# reveals #[[1]]#.", - /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser nahe eines Sees# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser nahe eines Sees# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe près d'un lac# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_HF_GRASS] = HintText(CustomMessage("They say that #cutting some grass on a field# reveals #[[1]]#.", - /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser auf einem Feld# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser auf einem Feld# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe dans une plaine# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_ZR_GRASS] = HintText(CustomMessage("They say that #cutting some grass near a river# reveals #[[1]]#.", - /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in der Nähe von einem Fluß# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in der Nähe von einem Fluß# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe près d'une rivière# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_GROTTO_GRASS] = HintText(CustomMessage("They say that #cutting some grass in a grotto# reveals #[[1]]#.", - /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in einer Grotte# #[[1]]# enthülle.", - /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + /*german*/ "Man erzählt sich, daß das #Schneiden einiger Gräser in einer Grotte# #[[1]]# enthülle.", + /*french*/ "Selon moi, de l'#herbe# cache #[[1]]#.", {QM_RED, QM_GREEN})); hintTextTable[RHT_CRATE_GERUDO_VALLEY] = HintText(CustomMessage("They say that a #crate in Gerudo Valley# contains #[[1]]#.", /*german*/ "Man erzählt sich, daß eine #Kiste im Gerudotal# #[[1]]# enthielte.", From 2576f75b1a1ca06f325108469cd3c51cf4ffa6ff Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Wed, 9 Apr 2025 12:20:37 -0400 Subject: [PATCH 030/157] Fix non-loach fish making it into the pool when Loach Only is selected. (#5360) --- soh/soh/Enhancements/randomizer/context.cpp | 2 -- soh/soh/Enhancements/randomizer/fishsanity.cpp | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp index fa755325eda..4127ab21a21 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/context.cpp @@ -180,8 +180,6 @@ void Context::GenerateLocationPool() { location.GetRandomizerCheck() == RC_HF_DEKU_SCRUB_GROTTO)) || (location.GetRCType() == RCTYPE_ADULT_TRADE && mOptions[RSK_SHUFFLE_ADULT_TRADE].Is(RO_GENERIC_OFF)) || (location.GetRCType() == RCTYPE_COW && mOptions[RSK_SHUFFLE_COWS].Is(RO_GENERIC_OFF)) || - (location.GetRandomizerCheck() == RC_LH_HYRULE_LOACH && - mOptions[RSK_FISHSANITY].IsNot(RO_FISHSANITY_HYRULE_LOACH)) || (location.GetRCType() == RCTYPE_FISH && !mFishsanity->GetFishLocationIncluded(&location)) || (location.GetRCType() == RCTYPE_POT && mOptions[RSK_SHUFFLE_POTS].Is(RO_SHUFFLE_POTS_OFF)) || (location.GetRCType() == RCTYPE_GRASS && mOptions[RSK_SHUFFLE_GRASS].Is(RO_SHUFFLE_GRASS_OFF)) || diff --git a/soh/soh/Enhancements/randomizer/fishsanity.cpp b/soh/soh/Enhancements/randomizer/fishsanity.cpp index fe9b4c0f171..8d4435f9a00 100644 --- a/soh/soh/Enhancements/randomizer/fishsanity.cpp +++ b/soh/soh/Enhancements/randomizer/fishsanity.cpp @@ -78,6 +78,9 @@ bool Fishsanity::GetFishLocationIncluded(Rando::Location* loc, FishsanityOptions return false; } RandomizerCheck rc = loc->GetRandomizerCheck(); + if (mode == RO_FISHSANITY_HYRULE_LOACH && rc != RC_LH_HYRULE_LOACH) { + return false; + } // Are pond fish enabled, and is this a pond fish location? if (mode != RO_FISHSANITY_OVERWORLD && numFish > 0 && loc->GetScene() == SCENE_FISHING_POND && loc->GetActorID() == ACTOR_FISHING) { From 9250af0216d5b559262f5fae4cf1a470e2683353 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Wed, 9 Apr 2025 13:01:26 -0400 Subject: [PATCH 031/157] add check for agony option (#5365) --- soh/soh/Enhancements/randomizer/ShuffleGrass.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/ShuffleGrass.cpp b/soh/soh/Enhancements/randomizer/ShuffleGrass.cpp index f318ad94884..b8d31144a93 100644 --- a/soh/soh/Enhancements/randomizer/ShuffleGrass.cpp +++ b/soh/soh/Enhancements/randomizer/ShuffleGrass.cpp @@ -35,8 +35,10 @@ extern "C" void EnKusa_RandomizerDraw(Actor* thisx, PlayState* play) { if (grassActor->grassIdentity.randomizerCheck != RC_MAX && Flags_GetRandomizerInf(grassActor->grassIdentity.randomizerInf) == 0) { int csmc = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchContents"), CSMC_DISABLED); + int requiresStoneAgony = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeDependsStoneOfAgony"), 0); - if (csmc == CSMC_BOTH || csmc == CSMC_TEXTURE) { + if ((csmc == CSMC_BOTH || csmc == CSMC_TEXTURE) && + (!requiresStoneAgony || (requiresStoneAgony && CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)))) { auto itemEntry = Rando::Context::GetInstance()->GetFinalGIEntry(grassActor->grassIdentity.randomizerCheck, true, GI_NONE); GetItemCategory getItemCategory = itemEntry.getItemCategory; From cf755203949cfd47727fe94b6db55105fde0bef8 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Wed, 9 Apr 2025 15:16:25 -0400 Subject: [PATCH 032/157] Fix crash in Message Viewer when playing with an NTSC OTR. (#5367) --- .../Enhancements/debugger/MessageViewer.cpp | 34 ++++++++----------- soh/soh/Enhancements/debugger/MessageViewer.h | 5 ++- soh/soh/SohGui/SohMenu.h | 7 ++++ soh/soh/SohGui/SohMenuSettings.cpp | 7 +--- 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/soh/soh/Enhancements/debugger/MessageViewer.cpp b/soh/soh/Enhancements/debugger/MessageViewer.cpp index fef875e94b6..83aac4d6c61 100644 --- a/soh/soh/Enhancements/debugger/MessageViewer.cpp +++ b/soh/soh/Enhancements/debugger/MessageViewer.cpp @@ -2,6 +2,7 @@ #include "soh/SohGui/UIWidgets.hpp" #include "soh/SohGui/SohGui.hpp" +#include "soh/SohGui/SohMenu.h" #include "soh/OTRGlobals.h" #include @@ -56,22 +57,12 @@ void MessageViewer::DrawElement() { memset(mTextIdBuf, 0, sizeof(char) * MAX_STRING_SIZE); } PopStyleCheckbox(); - ImGui::Text("Language"); - ImGui::SameLine(); - PushStyleCombobox(THEME_COLOR); - if (ImGui::BeginCombo("##Language", mLanguages[mLanguage])) { - // ReSharper disable CppDFAUnreachableCode - for (size_t i = 0; i < mLanguages.size(); i++) { - if (strlen(mLanguages[i]) > 0) { - if (ImGui::Selectable(mLanguages[i], i == mLanguage)) { - mLanguage = i; - } - } - } - ImGui::EndCombo(); - } - PopStyleCombobox(); - UIWidgets::InsertHelpHoverText("Which language to load from the selected text ID"); + SohGui::SohMenu::UpdateLanguageMap(SohGui::languages); + UIWidgets::Combobox("Language", &mLanguage, SohGui::languages, + UIWidgets::ComboboxOptions() + .Color(THEME_COLOR) + .DefaultIndex(0) + .Tooltip("Which language to load from the selected text ID")); PushStyleButton(THEME_COLOR); if (ImGui::Button("Display Message##ExistingMessage")) { mDisplayExistingMessageClicked = true; @@ -196,6 +187,11 @@ void MessageDebug_StartTextBox(const char* tableId, uint16_t textId, uint8_t lan R_TEXT_CHAR_SCALE = 75; R_TEXT_LINE_SPACING = 12; R_TEXT_INIT_XPOS = 65; + if (language == LANGUAGE_JPN) { + R_TEXT_CHAR_SCALE = 88; + R_TEXT_LINE_SPACING = 18; + R_TEXT_INIT_XPOS = 65; + } char* buffer = font->msgBuf; msgCtx->textId = textId; if (strlen(tableId) == 0) { @@ -207,10 +203,8 @@ void MessageDebug_StartTextBox(const char* tableId, uint16_t textId, uint8_t lan constexpr int maxBufferSize = sizeof(font->msgBuf); const CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(tableId, textId); font->charTexBuf[0] = (messageEntry.GetTextBoxType() << 4) | messageEntry.GetTextBoxPosition(); - switch (language) { - font->msgLength = - SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetForLanguage(language), maxBufferSize); - } + font->msgLength = + SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetForLanguage(language), maxBufferSize); msgCtx->msgLength = static_cast(font->msgLength); } msgCtx->textBoxProperties = font->charTexBuf[0]; diff --git a/soh/soh/Enhancements/debugger/MessageViewer.h b/soh/soh/Enhancements/debugger/MessageViewer.h index 20924a0b612..83e290114dc 100644 --- a/soh/soh/Enhancements/debugger/MessageViewer.h +++ b/soh/soh/Enhancements/debugger/MessageViewer.h @@ -4,7 +4,7 @@ #ifdef __cplusplus #include "GuiWindow.h" -#include +#include extern "C" { #endif /** @@ -41,7 +41,6 @@ class MessageViewer : public Ship::GuiWindow { void DisplayCustomMessage() const; static constexpr uint16_t MAX_STRING_SIZE = 1024; - static constexpr std::array mLanguages = { "English", "German", "French" }; static constexpr int HEXADECIMAL = 0; static constexpr int DECIMAL = 1; char* mTableIdBuf; @@ -49,7 +48,7 @@ class MessageViewer : public Ship::GuiWindow { char* mTextIdBuf; uint16_t mTextId; int mTextIdBase = HEXADECIMAL; - size_t mLanguage = LANGUAGE_ENG; + int32_t mLanguage = LANGUAGE_ENG; char* mCustomMessageBuf; std::string mCustomMessageString; bool mDisplayExistingMessageClicked = false; diff --git a/soh/soh/SohGui/SohMenu.h b/soh/soh/SohGui/SohMenu.h index 4a72938bb31..eb3b1f4f7e3 100644 --- a/soh/soh/SohGui/SohMenu.h +++ b/soh/soh/SohGui/SohMenu.h @@ -28,6 +28,13 @@ void disableBetaQuest(); namespace SohGui { +static std::unordered_map languages = { + { LANGUAGE_ENG, "English" }, + { LANGUAGE_GER, "German" }, + { LANGUAGE_FRA, "French" }, + { LANGUAGE_JPN, "Japanese" }, +}; + static const std::unordered_map menuThemeOptions = { { UIWidgets::Colors::Red, "Red" }, { UIWidgets::Colors::DarkRed, "Dark Red" }, diff --git a/soh/soh/SohGui/SohMenuSettings.cpp b/soh/soh/SohGui/SohMenuSettings.cpp index 94619a43b03..2b1f7779921 100644 --- a/soh/soh/SohGui/SohMenuSettings.cpp +++ b/soh/soh/SohGui/SohMenuSettings.cpp @@ -14,12 +14,7 @@ namespace SohGui { extern std::shared_ptr mSohMenu; using namespace UIWidgets; -static const std::unordered_map languages = { - { LANGUAGE_ENG, "English" }, - { LANGUAGE_GER, "German" }, - { LANGUAGE_FRA, "French" }, - { LANGUAGE_JPN, "Japanese" }, -}; + static std::unordered_map imguiScaleOptions = { { 0, "Small" }, { 1, "Normal" }, From 93d49d5c41fb144c00b72d76f5d2e810358ba1e1 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 9 Apr 2025 20:17:25 +0100 Subject: [PATCH 033/157] Fix ZD beehives and some small cleanup. (#5353) * clean up zora domain logic * revert useless short circuits --- .../location_access/overworld/zoras_domain.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_domain.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_domain.cpp index 07922bc8f00..3bee82b26b8 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_domain.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_domain.cpp @@ -17,8 +17,8 @@ void RegionTable_Init_ZorasDomain() { //Locations LOCATION(RC_ZD_DIVING_MINIGAME, logic->HasItem(RG_BRONZE_SCALE) && logic->HasItem(RG_CHILD_WALLET) && logic->IsChild), LOCATION(RC_ZD_CHEST, logic->IsChild && logic->CanUse(RG_STICKS)), - LOCATION(RC_ZD_KING_ZORA_THAWED, logic->KingZoraThawed), - LOCATION(RC_ZD_TRADE_PRESCRIPTION, logic->KingZoraThawed && logic->CanUse(RG_PRESCRIPTION)), + LOCATION(RC_ZD_KING_ZORA_THAWED, logic->IsAdult && logic->KingZoraThawed), + LOCATION(RC_ZD_TRADE_PRESCRIPTION, logic->IsAdult && logic->KingZoraThawed && logic->CanUse(RG_PRESCRIPTION)), LOCATION(RC_ZD_GS_FROZEN_WATERFALL, logic->IsAdult && (logic->HookshotOrBoomerang() || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || (logic->CanUse(RG_MAGIC_SINGLE) && (logic->CanUse(RG_MASTER_SWORD) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_BIGGORON_SWORD))) || (ctx->GetTrickOption(RT_ZD_GS) && logic->CanJumpslashExceptHammer())) && logic->CanGetNightTimeGS()), LOCATION(RC_ZD_FISH_1, logic->IsChild && logic->HasBottle()), LOCATION(RC_ZD_FISH_2, logic->IsChild && logic->HasBottle()), @@ -28,8 +28,8 @@ void RegionTable_Init_ZorasDomain() { LOCATION(RC_ZD_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), LOCATION(RC_ZD_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_ZD_GOSSIP_STONE, true), - LOCATION(RC_ZD_IN_FRONT_OF_KING_ZORA_BEEHIVE_LEFT, logic->CanBreakUpperBeehives()), - LOCATION(RC_ZD_IN_FRONT_OF_KING_ZORA_BEEHIVE_RIGHT, logic->CanBreakUpperBeehives()), + LOCATION(RC_ZD_IN_FRONT_OF_KING_ZORA_BEEHIVE_LEFT, logic->IsChild && logic->CanBreakUpperBeehives()), + LOCATION(RC_ZD_IN_FRONT_OF_KING_ZORA_BEEHIVE_RIGHT, logic->IsChild && logic->CanBreakUpperBeehives()), LOCATION(RC_ZD_NEAR_SHOP_POT_1, logic->CanBreakPots()), LOCATION(RC_ZD_NEAR_SHOP_POT_2, logic->CanBreakPots()), LOCATION(RC_ZD_NEAR_SHOP_POT_3, logic->CanBreakPots()), @@ -50,9 +50,12 @@ void RegionTable_Init_ZorasDomain() { Entrance(RR_ZD_STORMS_GROTTO, []{return logic->CanOpenStormsGrotto();}), }); - areaTable[RR_ZD_BEHIND_KING_ZORA] = Region("ZD Behind King Zora", "Zoras Domain", {RA_ZORAS_DOMAIN}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_ZD_BEHIND_KING_ZORA] = Region("ZD Behind King Zora", "Zoras Domain", {RA_ZORAS_DOMAIN}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->KingZoraThawed, []{return logic->IsAdult && logic->BlueFire();}), + }, { //Locations - LOCATION(RC_ZD_BEHIND_KING_ZORA_BEEHIVE, logic->CanBreakUpperBeehives()), + LOCATION(RC_ZD_BEHIND_KING_ZORA_BEEHIVE, logic->IsChild && logic->CanBreakUpperBeehives()), }, { //Exits Entrance(RR_ZORAS_DOMAIN, []{return logic->DeliverLetter || ctx->GetOption(RSK_ZORAS_FOUNTAIN).Is(RO_ZF_OPEN) || (ctx->GetOption(RSK_ZORAS_FOUNTAIN).Is(RO_ZF_CLOSED_CHILD) && logic->IsAdult);}), From c21c632c09533956bc5343bf221604bca62133b4 Mon Sep 17 00:00:00 2001 From: Extloga <141232749+Extloga@users.noreply.github.com> Date: Wed, 9 Apr 2025 21:24:05 +0200 Subject: [PATCH 034/157] Additions for the currencies in randomizer.cpp (#5344) * Rearrangement of the currencies in randomizer.cpp * Rearrangement of the currencies in randomizer.cpp * Rearrangement of the currencies in randomizer.cpp * Rearrangement of the currencies in randomizer.cpp * Rearrangement of the currencies in randomizer.cpp * Rearrangement of the currencies in randomizer.cpp * Rearrangement of the currencies in randomizer.cpp * Rearrangement of the currencies in randomizer.cpp * Rearrangement of the currencies in randomizer.cpp * Rearrangement of the currencies in randomizer.cpp * Rearrangement of the currencies in randomizer.cpp * Rearrangement of the currencies in randomizer.cpp --- .../Enhancements/randomizer/randomizer.cpp | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 9eaba3365cf..7a5f8dd6993 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -65,9 +65,10 @@ const std::string Randomizer::NaviRandoMessageTableID = "RandomizerNavi"; const std::string Randomizer::IceTrapRandoMessageTableID = "RandomizerIceTrap"; const std::string Randomizer::randoMiscHintsTableID = "RandomizerMiscHints"; -static const char* englishRupeeNames[175] = { +static const char* englishRupeeNames[190] = { "[P]", "Bad RNG Rolls", + "Baht", "Bananas", "Beanbean Coins", "Beans", @@ -86,6 +87,7 @@ static const char* englishRupeeNames[175] = { "BugFrags", "Canadian Dollars", "Cards", + "Cents", "Chaos Orbs", "Clams", "Coal", @@ -106,6 +108,8 @@ static const char* englishRupeeNames[175] = { "Darseks", "Dead Memes", "Diamonds", + "Dimes", + "Dinars", "DNA", "Doge", "Dogecoin", @@ -114,7 +118,9 @@ static const char* englishRupeeNames[175] = { "Dollarydoos", "Dosh", "Doubloons", + "Drakes", "Dwarfbucks", + "ECU", "Elexit", "Emeralds", "Energon", @@ -150,7 +156,7 @@ static const char* englishRupeeNames[175] = { "KF7 Ammo", "Kinstones", "Kremcoins", - "Kroner", + "Kronor", "Leaves", "Lemmings", "Lien", @@ -173,11 +179,14 @@ static const char* englishRupeeNames[175] = { "Munny", "Mushrooms", "Mysteries", + "Naira", "Neopoints", + "Nickels", "Notes", "Nuyen", "Orbs", "Ore", + "Pence", "Pix", "Pixels", "Plastyks", @@ -190,19 +199,23 @@ static const char* englishRupeeNames[175] = { "Pounds", "Power Pellets", "Primogems", - "Réals", + "Rand", + "Reais", "Refined Metal", "Remote Mines", "Retweets", "Rhinu", + "Rials", "Rings", "Riot Points", + "Riyals", "Robux", "Rubies", "Rubles", "Runite Ore", "Rupees", "Saint Quartz", + "Sapphires", "Septims", "Shekels", "Shillings", @@ -235,24 +248,27 @@ static const char* englishRupeeNames[175] = { "Vespene Gas", "Watts", "Widgets", + "Won", "Woolongs", "World Dollars", "Wumpa Fruit", "Yen", + "Yuan", "Zenny", "Zorkmids", }; -static const char* germanRupeeNames[65] = { - "Bananen", "Bitcoin", "Bonbons", "Bratwürste", "Brause UFOs", "Brötchen", "Cent", "Diamanten", - "Diridari", "Dogecoin", "ECU", "Elexit", "Erz", "Erzbrocken", "Euro", "EXP", - "Forint", "Franken", "Freunde", "Gil", "Gold", "Groschen", "Gulden", "Gummibären", - "Heller", "Juwelen", "Karolin", "Kartoffeln", "Kies", "Knete", "Knochen", "Kohle", - "Kraniche", "Kreuzer", "Kronen", "Kronkorken", "Kröten", "Mark", "Mäuse", "Monde", - "Moorhühner", "Moos", "Münzen", "Penunze", "Pesa", "Pfandflaschen", "Pfennig", "Pfund", - "Pilze", "Plastiks", "Pokédollar", "Radieschen", "Rappen", "Rubine", "Saphire", "Schilling", - "Seelen", "Smaragde", "Steine", "Sterne", "Sternis", "Tael", "Taler", "Wagenchips", - "Zenny" +static const char* germanRupeeNames[80] = { + "Baht", "Bananen", "Bitcoin", "Bonbons", "Bratwürste", "Brause UFOs", "Brötchen", "Cent", + "Diamanten", "Dinar", "Diridari", "Dogecoin", "Dollar", "Draken", "ECU", "Elexit", + "Erz", "Erzbrocken", "Euro", "EXP", "Forint", "Franken", "Freunde", "Gil", + "Gold", "Groschen", "Gulden", "Gummibären", "Heller", "Juwelen", "Karolin", "Kartoffeln", + "Kies", "Knete", "Knochen", "Kohle", "Kraniche", "Kreuzer", "Kronen", "Kronkorken", + "Kröten", "Lira", "Mark", "Mäuse", "Monde", "Moorhühner", "Moos", "Münzen", + "Naira", "Penunze", "Pesa", "Pfandflaschen", "Pfennig", "Pfund", "Pilze", "Plastiks", + "Pokédollar", "Radieschen", "Rand", "Rappen", "Real", "Rial", "Riyal", "Rubine", + "Rupien", "Saphire", "Schilling", "Seelen", "Septime", "Smaragde", "Steine", "Sterne", + "Sternis", "Tael", "Taler", "Wagenchips", "Won", "Yen", "Yuan", "Zenny", }; static const char* frenchRupeeNames[40] = { @@ -260,7 +276,7 @@ static const char* frenchRupeeNames[40] = { "Centimes", "Champignons", "Clochettes", "Crédits", "Croissants", "Diamants", "Dogecoin", "Dollars", "Émeraudes", "Éthers", "Étoiles", "Euros", "Florens", "Francs", "Galds", "Gils", "Grouses", "Halos", "Joyaux", "Lunes", "Mailles", "Munnies", "Orbes", "Orens", - "Pépètes", "Pièces", "Plastyks", "Pokédollars", "Pokémon", "Radis", "Rubis", "Zennies" + "Pépètes", "Pièces", "Plastyks", "Pokédollars", "Pokémon", "Radis", "Rubis", "Zennies", }; Randomizer::Randomizer() { From c3322c85ae74110b171851b3a072bb79ba84c557 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Wed, 9 Apr 2025 12:26:20 -0700 Subject: [PATCH 035/157] Swap call to `RecalculateAvailableChecks()` in `SaveFile()` to a variable that triggers a recalculation in `DrawElement()`. (#5366) --- .../Enhancements/randomizer/randomizer_check_tracker.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 6be7f8f4041..fcb86f9c354 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -245,6 +245,7 @@ Color_RGBA8 Color_Saved_Extra = { 0, 185, 0, 255 }; // Green std::vector buttons = { BTN_A, BTN_B, BTN_CUP, BTN_CDOWN, BTN_CLEFT, BTN_CRIGHT, BTN_L, BTN_Z, BTN_R, BTN_START, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT }; static ImGuiTextFilter checkSearch; +static bool recalculateAvailable = false; std::array filterAreasHidden = { 0 }; std::array filterChecksHidden = { 0 }; @@ -877,7 +878,7 @@ void SaveTrackerData(SaveContext* saveContext, int sectionID, bool fullSave) { void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) { SaveTrackerData(saveContext, sectionID, fullSave); if (fullSave) { - RecalculateAvailableChecks(); + recalculateAvailable = true; } } @@ -2032,6 +2033,10 @@ static std::unordered_map buttonStrings = { }; void CheckTrackerSettingsWindow::DrawElement() { + if (recalculateAvailable) { + recalculateAvailable = false; + RecalculateAvailableChecks(); + } ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f }); if (ImGui::BeginTable("CheckTrackerSettingsTable", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { ImGui::TableSetupColumn("General settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); From e98a6c3748f055429eb0229ce5b99eae1cf4ce98 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Wed, 9 Apr 2025 13:38:27 -0700 Subject: [PATCH 036/157] Fix migrators for tracker colors to match new menu formats. (#5369) Add temporary additional clearing to CVarColorPicker's reset functionality to resolve already migrated issues. --- .../randomizer/randomizer_check_tracker.cpp | 16 ++------ soh/soh/SohGui/UIWidgets.cpp | 6 +++ soh/soh/config/ConfigMigrators.h | 41 ++++++++++--------- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index fcb86f9c354..93798fea593 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -1087,14 +1087,6 @@ void CheckTrackerWindow::DrawElement() { bool doingCollapseOrExpand = optExpandAll || optCollapseAll; bool isThisAreaSpoiled; RandomizerCheckArea lastArea = RCAREA_INVALID; - Color_RGBA8 areaCompleteColor = - CVarGetColor(CVAR_TRACKER_CHECK("AreaComplete.MainColor.Value"), Color_Main_Default); - Color_RGBA8 areaIncompleteColor = - CVarGetColor(CVAR_TRACKER_CHECK("AreaIncomplete.MainColor.Value"), Color_Main_Default); - Color_RGBA8 extraCompleteColor = - CVarGetColor(CVAR_TRACKER_CHECK("AreaComplete.ExtraColor.Value"), Color_Area_Complete_Extra_Default); - Color_RGBA8 extraIncompleteColor = - CVarGetColor(CVAR_TRACKER_CHECK("AreaIncomplete.ExtraColor.Value"), Color_Area_Incomplete_Extra_Default); Color_RGBA8 mainColor; Color_RGBA8 extraColor; std::string stemp; @@ -1122,11 +1114,11 @@ void CheckTrackerWindow::DrawElement() { } else { // Get the colour for the area if (thisAreaFullyChecked) { - mainColor = areaCompleteColor; - extraColor = extraCompleteColor; + mainColor = Color_Area_Complete_Main; + extraColor = Color_Area_Complete_Extra; } else { - mainColor = areaIncompleteColor; - extraColor = extraIncompleteColor; + mainColor = Color_Area_Incomplete_Main; + extraColor = Color_Area_Incomplete_Extra; } // Draw the area diff --git a/soh/soh/SohGui/UIWidgets.cpp b/soh/soh/SohGui/UIWidgets.cpp index f5e84f302bb..3c4e91147dd 100644 --- a/soh/soh/SohGui/UIWidgets.cpp +++ b/soh/soh/SohGui/UIWidgets.cpp @@ -895,6 +895,12 @@ bool CVarColorPicker(const char* label, const char* cvarName, Color_RGBA8 defaul UIWidgets::ButtonOptions({ { .tooltip = "Resets this color to its default value" } }) .Color(themeColor) .Size(UIWidgets::Sizes::Inline))) { + // TODO: Remove for next minor or major version, temporary fix for already migrated configs to 3 for 9.0.0 + CVarClear((std::string(cvarName) + ".R").c_str()); + CVarClear((std::string(cvarName) + ".G").c_str()); + CVarClear((std::string(cvarName) + ".B").c_str()); + CVarClear((std::string(cvarName) + ".A").c_str()); + CVarClear((std::string(cvarName) + ".Type").c_str()); CVarClearBlock(valueCVar.c_str()); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } diff --git a/soh/soh/config/ConfigMigrators.h b/soh/soh/config/ConfigMigrators.h index a312be604e7..ee7e91792b4 100644 --- a/soh/soh/config/ConfigMigrators.h +++ b/soh/soh/config/ConfigMigrators.h @@ -1263,10 +1263,10 @@ std::vector version3Migrations = { { MigrationAction::Rename, "gUnfixGoronSpin", "gCosmetics.UnfixGoronSpin" }, { MigrationAction::Rename, "gNL_Diamond_Env", "gCosmetics.Magic.NayrusSecondary" }, { MigrationAction::Rename, "gUseSpellsCol", "gCosmetics.UseSpellsColors" }, - { MigrationAction::Rename, "gItemTrackerBgColorA", "gTrackers.ItemTracker.BgColorA" }, - { MigrationAction::Rename, "gItemTrackerBgColorB", "gTrackers.ItemTracker.BgColorB" }, - { MigrationAction::Rename, "gItemTrackerBgColorG", "gTrackers.ItemTracker.BgColorG" }, - { MigrationAction::Rename, "gItemTrackerBgColorR", "gTrackers.ItemTracker.BgColorR" }, + { MigrationAction::Rename, "gItemTrackerBgColorA", "gTrackers.ItemTracker.BgColor.Value.A" }, + { MigrationAction::Rename, "gItemTrackerBgColorB", "gTrackers.ItemTracker.BgColor.Value.B" }, + { MigrationAction::Rename, "gItemTrackerBgColorG", "gTrackers.ItemTracker.BgColor.Value.G" }, + { MigrationAction::Rename, "gItemTrackerBgColorR", "gTrackers.ItemTracker.BgColor.Value.R" }, { MigrationAction::Rename, "gItemTrackerCapacityTrack", "gTrackers.ItemTracker.ItemCountType" }, { MigrationAction::Rename, "gItemTrackerComboButton1", "gTrackers.ItemTracker.ComboButton1" }, { MigrationAction::Rename, "gItemTrackerComboButton2", "gTrackers.ItemTracker.ComboButton2" }, @@ -1310,22 +1310,24 @@ std::vector version3Migrations = { { MigrationAction::Rename, "gEntranceTrackerSortBy", "gTrackers.EntranceTracker.SortBy" }, { MigrationAction::Rename, "gCheckTrackerAreaCompleteHide", "gTrackers.CheckTracker.AreaComplete.Hide" }, { MigrationAction::Rename, "gCheckTrackerAreaExtraCompleteColor", - "gTrackers.CheckTracker.AreaComplete.ExtraColor" }, - { MigrationAction::Rename, "gCheckTrackerAreaExtraIncompleteColor", - "gTrackers.CheckTracker.AreaIncomplete.ExtraColor" }, + "gTrackers.CheckTracker.AreaComplete.ExtraColor.Value" }, + { MigrationAction::Rename, "gCheckTrackerAreaExtraIncompleteColor.Value", + "gTrackers.CheckTracker.AreaIncomplete.ExtraColor.Value" }, { MigrationAction::Rename, "gCheckTrackerAreaIncompleteHide", "gTrackers.CheckTracker.AreaIncomplete.Hide" }, - { MigrationAction::Rename, "gCheckTrackerAreaMainCompleteColor", "gTrackers.CheckTracker.AreaComplete.MainColor" }, - { MigrationAction::Rename, "gCheckTrackerAreaMainIncompleteColor", - "gTrackers.CheckTracker.AreaIncomplete.MainColor" }, - { MigrationAction::Rename, "gCheckTrackerBgColor", "gTrackers.CheckTracker.BgColor" }, - { MigrationAction::Rename, "gCheckTrackerCollectedExtraColor", "gTrackers.CheckTracker.Collected.ExtraColor" }, + { MigrationAction::Rename, "gCheckTrackerAreaMainCompleteColor", + "gTrackers.CheckTracker.AreaComplete.MainColor.Value" }, + { MigrationAction::Rename, "gCheckTrackerAreaMainIncompleteColor.Value", + "gTrackers.CheckTracker.AreaIncomplete.MainColor.Value" }, + { MigrationAction::Rename, "gCheckTrackerBgColor", "gTrackers.CheckTracker.BgColor.Value" }, + { MigrationAction::Rename, "gCheckTrackerCollectedExtraColor", + "gTrackers.CheckTracker.Collected.ExtraColor.Value" }, { MigrationAction::Rename, "gCheckTrackerCollectedHide", "gTrackers.CheckTracker.Collected.Hide" }, - { MigrationAction::Rename, "gCheckTrackerCollectedMainColor", "gTrackers.CheckTracker.Collected.MainColor" }, + { MigrationAction::Rename, "gCheckTrackerCollectedMainColor", "gTrackers.CheckTracker.Collected.MainColor.Value" }, { MigrationAction::Rename, "gCheckTrackerComboButton1", "gTrackers.CheckTracker.ComboButton1" }, { MigrationAction::Rename, "gCheckTrackerComboButton2", "gTrackers.CheckTracker.ComboButton2" }, { MigrationAction::Rename, "gCheckTrackerDisplayType", "gTrackers.CheckTracker.DisplayType" }, - { MigrationAction::Rename, "gCheckTrackerHintedExtraColor", "gTrackers.CheckTracker.Hinted.ExtraColor" }, - { MigrationAction::Rename, "gCheckTrackerHintedMainColor", "gTrackers.CheckTracker.Hinted.MainColor" }, + { MigrationAction::Rename, "gCheckTrackerHintedExtraColor", "gTrackers.CheckTracker.Hinted.ExtraColor.Value" }, + { MigrationAction::Rename, "gCheckTrackerHintedMainColor", "gTrackers.CheckTracker.Hinted.MainColor.Value" }, { MigrationAction::Rename, "gCheckTrackerHintedHide", "gTrackers.CheckTracker.Hinted.Hide" }, { MigrationAction::Rename, "gCheckTrackerHudEditMode", "gTrackers.CheckTracker.Draggable" }, { MigrationAction::Rename, "gCheckTrackerKnownHide", "gTrackers.CheckTracker.Scummed.Hide" }, @@ -1334,7 +1336,7 @@ std::vector version3Migrations = { "gTrackers.CheckTracker.HideUnshuffledShopChecks" }, { MigrationAction::Rename, "gCheckTrackerOptionMQSpoilers", "gTrackers.CheckTracker.MQSpoilers" }, { MigrationAction::Rename, "gCheckTrackerOptionShowHidden", "gTrackers.CheckTracker.ShowHidden" }, - { MigrationAction::Rename, "gCheckTrackerSavedExtraColor", "gTrackers.CheckTracker.Saved.ExtraColor" }, + { MigrationAction::Rename, "gCheckTrackerSavedExtraColor", "gTrackers.CheckTracker.Saved.ExtraColor.Value" }, { MigrationAction::Rename, "gCheckTrackerSavedHide", "gTrackers.CheckTracker.Saved.Hide" }, { MigrationAction::Rename, "gCheckTrackerSavedMainColor", "gTrackers.CheckTracker.Saved.MainColor" }, { MigrationAction::Rename, "gCheckTrackerScummedExtraColor", "gTrackers.CheckTracker.Scummed.ExtraColor" }, @@ -1345,10 +1347,11 @@ std::vector version3Migrations = { { MigrationAction::Rename, "gCheckTrackerShowOnlyPaused", "gTrackers.CheckTracker.ShowOnlyPaused" }, { MigrationAction::Rename, "gCheckTrackerSkippedExtraColor", "gTrackers.CheckTracker.Skipped.ExtraColor" }, { MigrationAction::Rename, "gCheckTrackerSkippedHide", "gTrackers.CheckTracker.Skipped.Hide" }, - { MigrationAction::Rename, "gCheckTrackerSkippedMainColor", "gTrackers.CheckTracker.Skipped.MainColor" }, - { MigrationAction::Rename, "gCheckTrackerUncheckedExtraColor", "gTrackers.CheckTracker.Unchecked.ExtraColor" }, + { MigrationAction::Rename, "gCheckTrackerSkippedMainColor", "gTrackers.CheckTracker.Skipped.MainColor.Value" }, + { MigrationAction::Rename, "gCheckTrackerUncheckedExtraColor", + "gTrackers.CheckTracker.Unchecked.ExtraColor.Value" }, { MigrationAction::Rename, "gCheckTrackerUncheckedHide", "gTrackers.CheckTracker.Unchecked.Hide" }, - { MigrationAction::Rename, "gCheckTrackerUncheckedMainColor", "gTrackers.CheckTracker.Unchecked.MainColor" }, + { MigrationAction::Rename, "gCheckTrackerUncheckedMainColor", "gTrackers.CheckTracker.Unchecked.MainColor.Value" }, { MigrationAction::Rename, "gCheckTrackerWindowType", "gTrackers.CheckTracker.WindowType" }, { MigrationAction::Rename, "gRandomize10GSHint", "gRandoSettings.10GSHint" }, { MigrationAction::Rename, "gRandomize20GSHint", "gRandoSettings.20GSHint" }, From 73400f58560719e21703c3b867677856412f9204 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 9 Apr 2025 21:46:43 +0100 Subject: [PATCH 037/157] IC ledge to fountain child logic fixed (#5368) * IC ledge to fountain child logic fixed * fully unblob ZF --- soh/soh/Enhancements/randomizer/entrance.cpp | 8 +- .../location_access/dungeons/ice_cavern.cpp | 2 +- .../overworld/zoras_fountain.cpp | 97 +++++++++++++++---- .../Enhancements/randomizer/randomizerTypes.h | 6 ++ 4 files changed, 87 insertions(+), 26 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/entrance.cpp b/soh/soh/Enhancements/randomizer/entrance.cpp index 05638a2b3fe..40eaca21207 100644 --- a/soh/soh/Enhancements/randomizer/entrance.cpp +++ b/soh/soh/Enhancements/randomizer/entrance.cpp @@ -878,10 +878,10 @@ int EntranceShuffler::ShuffleAllEntrances() { { EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_OUTSIDE_TEMPLE } }, { { EntranceType::Dungeon, RR_KAK_WELL, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, ENTR_BOTTOM_OF_THE_WELL_ENTRANCE }, { EntranceType::Dungeon, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, RR_KAK_WELL, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BOTTOM_OF_THE_WELL } }, - { { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_ICE_CAVERN_ENTRYWAY, ENTR_ICE_CAVERN_ENTRANCE }, - { EntranceType::Dungeon, RR_ICE_CAVERN_ENTRYWAY, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_ICE_CAVERN } }, - { { EntranceType::Dungeon, RR_GERUDO_FORTRESS, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE }, - { EntranceType::Dungeon, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND } }, + { { EntranceType::Dungeon, RR_ZF_LEDGE, RR_ICE_CAVERN_ENTRYWAY, ENTR_ICE_CAVERN_ENTRANCE }, + { EntranceType::Dungeon, RR_ICE_CAVERN_ENTRYWAY, RR_ZF_LEDGE, ENTR_ZORAS_FOUNTAIN_OUTSIDE_ICE_CAVERN } }, + { { EntranceType::Dungeon, RR_GERUDO_FORTRESS, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE }, + { EntranceType::Dungeon, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND } }, { { EntranceType::GanonDungeon, RR_GANONS_CASTLE_LEDGE, RR_GANONS_CASTLE_ENTRYWAY, ENTR_INSIDE_GANONS_CASTLE_ENTRANCE }, { EntranceType::GanonDungeon, RR_GANONS_CASTLE_ENTRYWAY, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT } }, diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp index ff2bf959437..7aa2066ca4d 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp @@ -11,7 +11,7 @@ void RegionTable_Init_IceCavern() { //Exits Entrance(RR_ICE_CAVERN_BEGINNING, []{return ctx->GetDungeon(ICE_CAVERN)->IsVanilla();}), Entrance(RR_ICE_CAVERN_MQ_BEGINNING, []{return ctx->GetDungeon(ICE_CAVERN)->IsMQ() && logic->CanUseProjectile();}), - Entrance(RR_ZORAS_FOUNTAIN, []{return true;}), + Entrance(RR_ZF_LEDGE, []{return true;}), }); #pragma region Vanilla diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp index a0307045406..3ac0921a3e6 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp @@ -11,18 +11,44 @@ void RegionTable_Init_ZorasFountain() { EventAccess(&logic->ButterflyFairy, []{return logic->ButterflyFairy || (logic->CanUse(RG_STICKS) && logic->AtDay);}), }, { //Locations - LOCATION(RC_ZF_ICEBERG_FREESTANDING_POH, logic->IsAdult), + LOCATION(RC_ZF_GS_TREE, logic->IsChild), + LOCATION(RC_ZF_GS_ABOVE_THE_LOG, logic->IsChild && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), + LOCATION(RC_ZF_FAIRY_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), + LOCATION(RC_ZF_FAIRY_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_ZF_JABU_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), + LOCATION(RC_ZF_JABU_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_ZF_FAIRY_GOSSIP_STONE, true), + LOCATION(RC_ZF_JABU_GOSSIP_STONE, true), + LOCATION(RC_ZF_NEAR_JABU_POT_1, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_ZF_NEAR_JABU_POT_2, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_ZF_NEAR_JABU_POT_3, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_ZF_NEAR_JABU_POT_4, logic->IsChild && logic->CanBreakPots()), + }, { + //Exits + Entrance(RR_ZD_BEHIND_KING_ZORA, []{return true;}), + Entrance(RR_ZF_ICEBERGS, []{return logic->IsAdult;}), + Entrance(RR_ZF_LAKEBED, []{return logic->CanUse(RG_IRON_BOOTS);}), + //child can break the brown rock without lifting the silver rock and it stays gone for adult, but it's not intuitive and there's no reasonable case where it matters. + Entrance(RR_ZF_HIDDEN_CAVE, []{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->BlastOrSmash();}), + Entrance(RR_ZF_ROCK, []{return logic->IsAdult && logic->CanUse(RG_SCARECROW);}), + Entrance(RR_JABU_JABUS_BELLY_ENTRYWAY, []{return (logic->IsChild && logic->CanUse(RG_BOTTLE_WITH_FISH));}), + Entrance(RR_ZF_GREAT_FAIRY_FOUNTAIN, []{return logic->HasExplosives() || (ctx->GetTrickOption(RT_ZF_GREAT_FAIRY_WITHOUT_EXPLOSIVES) && logic->CanUse(RG_MEGATON_HAMMER) && logic->CanUse(RG_SILVER_GAUNTLETS));}), + }); + + areaTable[RR_ZF_ICEBERGS] = Region("ZF Icebergs", "Zoras Fountain", {RA_ZORAS_FOUNTAIN}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_ZF_ICEBERG_FREESTANDING_POH, logic->IsAdult), + }, { + //Exits + //This hover is pretty tight, come at it with momentum and aim for the small corner polygon of the big iceburg while spamming roll + Entrance(RR_ZORAS_FOUNTAIN, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->HasItem(RG_HOVER_BOOTS);}), + Entrance(RR_ZF_LAKEBED, []{return logic->CanUse(RG_IRON_BOOTS);}), + Entrance(RR_ZF_LEDGE, []{return true;}), + }); + + areaTable[RR_ZF_LAKEBED] = Region("ZF Lakebed", "Zoras Fountain", {RA_ZORAS_FOUNTAIN}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations LOCATION(RC_ZF_BOTTOM_FREESTANDING_POH, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), - LOCATION(RC_ZF_GS_TREE, logic->IsChild), - LOCATION(RC_ZF_GS_ABOVE_THE_LOG, logic->IsChild && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), - LOCATION(RC_ZF_GS_HIDDEN_CAVE, logic->CanUse(RG_SILVER_GAUNTLETS) && logic->BlastOrSmash() && logic->HookshotOrBoomerang() && logic->IsAdult && logic->CanGetNightTimeGS()), - LOCATION(RC_ZF_HIDDEN_CAVE_POT_1, logic->CanUse(RG_SILVER_GAUNTLETS) && logic->IsAdult && logic->BlastOrSmash() && logic->CanBreakPots()), - LOCATION(RC_ZF_HIDDEN_CAVE_POT_2, logic->CanUse(RG_SILVER_GAUNTLETS) && logic->IsAdult && logic->BlastOrSmash() && logic->CanBreakPots()), - LOCATION(RC_ZF_HIDDEN_CAVE_POT_3, logic->CanUse(RG_SILVER_GAUNTLETS) && logic->IsAdult && logic->BlastOrSmash() && logic->CanBreakPots()), - LOCATION(RC_ZF_FAIRY_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), - LOCATION(RC_ZF_FAIRY_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_ZF_JABU_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), - LOCATION(RC_ZF_JABU_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_ZF_BOTTOM_NORTH_INNER_RUPEE, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), LOCATION(RC_ZF_BOTTOM_NORTHEAST_INNER_RUPEE, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), LOCATION(RC_ZF_BOTTOM_SOUTHEAST_INNER_RUPEE, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), @@ -41,18 +67,47 @@ void RegionTable_Init_ZorasFountain() { LOCATION(RC_ZF_BOTTOM_SOUTH_OUTER_RUPEE, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), LOCATION(RC_ZF_BOTTOM_SOUTHWEST_OUTER_RUPEE, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), LOCATION(RC_ZF_BOTTOM_NORTHWEST_OUTER_RUPEE, logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), - LOCATION(RC_ZF_FAIRY_GOSSIP_STONE, true), - LOCATION(RC_ZF_JABU_GOSSIP_STONE, true), - LOCATION(RC_ZF_NEAR_JABU_POT_1, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_ZF_NEAR_JABU_POT_2, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_ZF_NEAR_JABU_POT_3, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_ZF_NEAR_JABU_POT_4, logic->IsChild && logic->CanBreakPots()), }, { //Exits - Entrance(RR_ZD_BEHIND_KING_ZORA, []{return true;}), - Entrance(RR_JABU_JABUS_BELLY_ENTRYWAY, []{return (logic->IsChild && logic->CanUse(RG_BOTTLE_WITH_FISH));}), - Entrance(RR_ICE_CAVERN_ENTRYWAY, []{return logic->IsAdult;}), - Entrance(RR_ZF_GREAT_FAIRY_FOUNTAIN, []{return logic->HasExplosives() || (ctx->GetTrickOption(RT_ZF_GREAT_FAIRY_WITHOUT_EXPLOSIVES) && logic->CanUse(RG_MEGATON_HAMMER) && logic->CanUse(RG_SILVER_GAUNTLETS));}), + Entrance(RR_ZORAS_FOUNTAIN, []{return logic->HasItem(RG_BRONZE_SCALE);}), + }); + + areaTable[RR_ZF_LEDGE] = Region("ZF Ledge", "Zoras Fountain", {RA_ZORAS_FOUNTAIN}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_ZORAS_FOUNTAIN, []{return logic->HasItem(RG_BRONZE_SCALE);}), + Entrance(RR_ZF_ICEBERGS, []{return logic->IsAdult;}), + Entrance(RR_ZF_LAKEBED, []{return logic->CanUse(RG_IRON_BOOTS);}), + Entrance(RR_ICE_CAVERN_ENTRYWAY, []{return true;}), + }); + + areaTable[RR_ZF_HIDDEN_CAVE] = Region("ZF Hidden Cave", "Zoras Fountain", {RA_ZORAS_FOUNTAIN}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_ZF_HIDDEN_CAVE_POT_1, logic->IsAdult && logic->CanBreakPots()), + LOCATION(RC_ZF_HIDDEN_CAVE_POT_2, logic->IsAdult && logic->CanBreakPots()), + LOCATION(RC_ZF_HIDDEN_CAVE_POT_3, logic->IsAdult && logic->CanBreakPots()), + }, { + //Exits + //There are invisible big skultullas here as adult but they do not block the path and can be "seen" with Z-target + //Lens is not currently needed for this either, implying they are not considered blocking, but it's open for discussion long-term + Entrance(RR_ZF_HIDDEN_LEDGE, []{return true;}), + }); + + areaTable[RR_ZF_HIDDEN_LEDGE] = Region("ZF Hidden Ledge", "Zoras Fountain", {RA_ZORAS_FOUNTAIN}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_ZF_GS_HIDDEN_CAVE, logic->IsAdult && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOMB_THROW) && logic->CanGetNightTimeGS()), + }, { + //Exits + //It is possible to avoid fall damage by jumping towards the right and landing in deeper water, but this is basically never relevent + Entrance(RR_ZORAS_FOUNTAIN, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->TakeDamage();}), + Entrance(RR_ZF_HIDDEN_CAVE, []{return true;}), + }); + + areaTable[RR_ZF_ROCK] = Region("ZF Rock", "Zoras Fountain", {RA_ZORAS_FOUNTAIN}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + //Has a wonder item + }, { + //Exits + Entrance(RR_ZORAS_FOUNTAIN, []{return true;}), }); areaTable[RR_ZF_GREAT_FAIRY_FOUNTAIN] = Region("ZF Great Fairy Fountain", "ZF Great Fairy Fountain", {}, NO_DAY_NIGHT_CYCLE, {}, { diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 34bddd0a5ad..ce074bf4cd7 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -509,6 +509,12 @@ typedef enum { RR_ZD_SHOP, RR_ZD_STORMS_GROTTO, RR_ZORAS_FOUNTAIN, + RR_ZF_ICEBERGS, + RR_ZF_LAKEBED, + RR_ZF_LEDGE, + RR_ZF_HIDDEN_CAVE, + RR_ZF_HIDDEN_LEDGE, + RR_ZF_ROCK, RR_ZF_GREAT_FAIRY_FOUNTAIN, RR_LON_LON_RANCH, RR_LLR_TALONS_HOUSE, From c12349a7a3643931aa0853ef6d2f18e76094c36d Mon Sep 17 00:00:00 2001 From: Malkierian Date: Wed, 9 Apr 2025 13:58:06 -0700 Subject: [PATCH 038/157] Add check for bean inventory slot having bean on it to allow availability to succeed with ammo count 0. (#5370) --- soh/soh/Enhancements/randomizer/logic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index ed91b21b8c0..9fd460d93fc 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -71,7 +71,7 @@ bool Logic::HasItem(RandomizerGet itemName) { case RG_DISTANT_SCARECROW: return ScarecrowsSong() && CanUse(RG_LONGSHOT); case RG_MAGIC_BEAN: - return GetAmmo(ITEM_BEAN) > 0; + return GetAmmo(ITEM_BEAN) > 0 || CheckInventory(ITEM_BEAN, true); case RG_KOKIRI_SWORD: case RG_DEKU_SHIELD: case RG_GORON_TUNIC: From a99e8836fb113ae72f69e0e0821706f3edddac3f Mon Sep 17 00:00:00 2001 From: Malkierian Date: Wed, 9 Apr 2025 15:18:44 -0700 Subject: [PATCH 039/157] Allow seeded enemy rando to use the values of the enemy list selection. (#5371) --- soh/soh/Enhancements/enemyrandomizer.cpp | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/soh/soh/Enhancements/enemyrandomizer.cpp b/soh/soh/Enhancements/enemyrandomizer.cpp index f202e1b1fc9..84e1d040e07 100644 --- a/soh/soh/Enhancements/enemyrandomizer.cpp +++ b/soh/soh/Enhancements/enemyrandomizer.cpp @@ -318,26 +318,20 @@ extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* po return 1; } -std::vector selectedEnemyList; +static std::vector selectedEnemyList; void GetSelectedEnemies() { selectedEnemyList.clear(); - if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), ENEMY_RANDOMIZER_OFF) == ENEMY_RANDOMIZER_RANDOM) { - for (int i = 0; i < 49; i++) { - if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0)) { - selectedEnemyList.push_back(randomizedEnemySpawnTable[i]); - } else if (CVarGetInteger(enemyCVarList[i], 1)) { - selectedEnemyList.push_back(randomizedEnemySpawnTable[i]); - } - } - if (selectedEnemyList.size() == 0) { - selectedEnemyList.push_back(randomizedEnemySpawnTable[0]); - } - } else { - for (int i = 0; i < 49; i++) { + for (int i = 0; i < 49; i++) { + if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemyList.All"), 0)) { + selectedEnemyList.push_back(randomizedEnemySpawnTable[i]); + } else if (CVarGetInteger(enemyCVarList[i], 1)) { selectedEnemyList.push_back(randomizedEnemySpawnTable[i]); } } + if (selectedEnemyList.size() == 0) { + selectedEnemyList.push_back(randomizedEnemySpawnTable[0]); + } } EnemyEntry GetRandomizedEnemyEntry(uint32_t seed) { @@ -348,7 +342,7 @@ EnemyEntry GetRandomizedEnemyEntry(uint32_t seed) { uint32_t finalSeed = seed + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() : gSaveContext.ship.stats.fileCreatedAt); Random_Init(finalSeed); - uint32_t randomNumber = Random(0, RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE); + uint32_t randomNumber = Random(0, selectedEnemyList.size()); return selectedEnemyList[randomNumber]; } else { uint32_t randomSelectedEnemy = Random(0, selectedEnemyList.size()); From 85bc67ef24fef818f7c71123322c3c008547fb24 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Wed, 9 Apr 2025 19:04:28 -0400 Subject: [PATCH 040/157] Blair Bravo version bump (#5372) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c394545900..6dc479fb408 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") -project(Ship VERSION 9.0.0 LANGUAGES C CXX) +project(Ship VERSION 9.0.1 LANGUAGES C CXX) include(CMake/soh-cvars.cmake) include(CMake/lus-cvars.cmake) From 1791a0c52ccf8ed885210268ca4509dd58edc904 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Thu, 10 Apr 2025 06:49:21 -0700 Subject: [PATCH 041/157] Fix input viewer's first-open position calculation. (#5376) --- soh/soh/Enhancements/controls/InputViewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/controls/InputViewer.cpp b/soh/soh/Enhancements/controls/InputViewer.cpp index 3ae3bc1baa2..1d6695d2538 100644 --- a/soh/soh/Enhancements/controls/InputViewer.cpp +++ b/soh/soh/Enhancements/controls/InputViewer.cpp @@ -151,7 +151,7 @@ void InputViewer::DrawElement() { } ImVec2 mainPos = ImGui::GetWindowPos(); - ImVec2 size = ImGui::GetContentRegionAvail(); + ImVec2 size = ImGui::GetMainViewport()->WorkSize; #ifdef __WIIU__ const float scale = CVarGetFloat(CVAR_INPUT_VIEWER("Scale"), 1.0f) * 2.0f; From 9783f918d2b231c3138f3a5d83150fa79b59ad3b Mon Sep 17 00:00:00 2001 From: Malkierian Date: Thu, 10 Apr 2025 06:50:35 -0700 Subject: [PATCH 042/157] Add `ImGuiWindowFlags_NoSavedSettings` to notification windows to prevent cluttering the imgui.ini (#5380) --- soh/soh/Notification/Notification.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Notification/Notification.cpp b/soh/soh/Notification/Notification.cpp index 4ba779e5487..14873b0684e 100644 --- a/soh/soh/Notification/Notification.cpp +++ b/soh/soh/Notification/Notification.cpp @@ -61,7 +61,7 @@ void Window::Draw() { ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoScrollbar); + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings); ImGui::SetWindowFontScale(CVarGetFloat(CVAR_SETTING("Notifications.Size"), 1.8f)); // Make this adjustable From a93917bb4a431485741db0c54f52e789e5ad97c0 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Thu, 10 Apr 2025 17:14:25 -0400 Subject: [PATCH 043/157] Fix Adult Fishing as Child Softlock (#5377) * pass a pointer not a pointer to a pointer * update GIVanillaBehavior comment * also drop this --- .../game-interactor/vanilla-behavior/GIVanillaBehavior.h | 2 +- soh/src/overlays/actors/ovl_Fishing/z_fishing.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 6c18a6edb1d..d57d5029aa5 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -951,7 +951,7 @@ typedef enum { // false // ``` // #### `args` - // - '*Fishing' (&this) + // - '*Fishing' VB_GIVE_RANDO_GLITCH_FISHING_PRIZE, // #### `result` diff --git a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c index 94d7e15ca67..4d42de65f58 100644 --- a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c +++ b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c @@ -5170,7 +5170,7 @@ void Fishing_HandleOwnerDialog(Fishing* this, PlayState* play) { if (Actor_HasParent(&this->actor, play)) { this->stateAndTimer = 24; } else { - if (!GameInteractor_Should(VB_GIVE_RANDO_GLITCH_FISHING_PRIZE, false, &this)) { + if (!GameInteractor_Should(VB_GIVE_RANDO_GLITCH_FISHING_PRIZE, false, this)) { Actor_OfferGetItem(&this->actor, play, GI_SCALE_GOLDEN, 2000.0f, 1000.0f); } } From 05e96f310d28af3817369484999abda4fed2843e Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Fri, 11 Apr 2025 10:15:31 -0400 Subject: [PATCH 044/157] Fix Fishing-related checks location pool (again) (#5378) --- soh/soh/Enhancements/randomizer/context.cpp | 2 ++ soh/soh/Enhancements/randomizer/fishsanity.cpp | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp index 4127ab21a21..fa755325eda 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/context.cpp @@ -180,6 +180,8 @@ void Context::GenerateLocationPool() { location.GetRandomizerCheck() == RC_HF_DEKU_SCRUB_GROTTO)) || (location.GetRCType() == RCTYPE_ADULT_TRADE && mOptions[RSK_SHUFFLE_ADULT_TRADE].Is(RO_GENERIC_OFF)) || (location.GetRCType() == RCTYPE_COW && mOptions[RSK_SHUFFLE_COWS].Is(RO_GENERIC_OFF)) || + (location.GetRandomizerCheck() == RC_LH_HYRULE_LOACH && + mOptions[RSK_FISHSANITY].IsNot(RO_FISHSANITY_HYRULE_LOACH)) || (location.GetRCType() == RCTYPE_FISH && !mFishsanity->GetFishLocationIncluded(&location)) || (location.GetRCType() == RCTYPE_POT && mOptions[RSK_SHUFFLE_POTS].Is(RO_SHUFFLE_POTS_OFF)) || (location.GetRCType() == RCTYPE_GRASS && mOptions[RSK_SHUFFLE_GRASS].Is(RO_SHUFFLE_GRASS_OFF)) || diff --git a/soh/soh/Enhancements/randomizer/fishsanity.cpp b/soh/soh/Enhancements/randomizer/fishsanity.cpp index 8d4435f9a00..99636d1a427 100644 --- a/soh/soh/Enhancements/randomizer/fishsanity.cpp +++ b/soh/soh/Enhancements/randomizer/fishsanity.cpp @@ -74,13 +74,10 @@ Fishsanity::~Fishsanity() { bool Fishsanity::GetFishLocationIncluded(Rando::Location* loc, FishsanityOptionsSource optionsSource) { auto [mode, numFish, ageSplit] = GetOptions(optionsSource); - if (loc->GetRCType() != RCTYPE_FISH || mode == RO_FISHSANITY_OFF) { + if (loc->GetRCType() != RCTYPE_FISH || mode == RO_FISHSANITY_OFF || mode == RO_FISHSANITY_HYRULE_LOACH) { return false; } RandomizerCheck rc = loc->GetRandomizerCheck(); - if (mode == RO_FISHSANITY_HYRULE_LOACH && rc != RC_LH_HYRULE_LOACH) { - return false; - } // Are pond fish enabled, and is this a pond fish location? if (mode != RO_FISHSANITY_OVERWORLD && numFish > 0 && loc->GetScene() == SCENE_FISHING_POND && loc->GetActorID() == ACTOR_FISHING) { From 86e1e8e3aa15939cb5c9baa72b810225823b7577 Mon Sep 17 00:00:00 2001 From: Archez Date: Fri, 11 Apr 2025 13:42:08 -0400 Subject: [PATCH 045/157] Fix boss key kaleido text for NTSC (#5389) --- soh/assets/xml/GC_MQ_NTSC_J/textures/item_name_static.xml | 2 +- soh/assets/xml/GC_MQ_NTSC_U/textures/item_name_static.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J/textures/item_name_static.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_J_CE/textures/item_name_static.xml | 2 +- soh/assets/xml/GC_NMQ_NTSC_U/textures/item_name_static.xml | 2 +- soh/assets/xml/N64_NTSC_10/textures/item_name_static.xml | 2 +- soh/assets/xml/N64_NTSC_11/textures/item_name_static.xml | 2 +- soh/assets/xml/N64_NTSC_12/textures/item_name_static.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/soh/assets/xml/GC_MQ_NTSC_J/textures/item_name_static.xml b/soh/assets/xml/GC_MQ_NTSC_J/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/GC_MQ_NTSC_J/textures/item_name_static.xml +++ b/soh/assets/xml/GC_MQ_NTSC_J/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/GC_MQ_NTSC_U/textures/item_name_static.xml b/soh/assets/xml/GC_MQ_NTSC_U/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/GC_MQ_NTSC_U/textures/item_name_static.xml +++ b/soh/assets/xml/GC_MQ_NTSC_U/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J/textures/item_name_static.xml b/soh/assets/xml/GC_NMQ_NTSC_J/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J/textures/item_name_static.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_J_CE/textures/item_name_static.xml b/soh/assets/xml/GC_NMQ_NTSC_J_CE/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_J_CE/textures/item_name_static.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_J_CE/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/GC_NMQ_NTSC_U/textures/item_name_static.xml b/soh/assets/xml/GC_NMQ_NTSC_U/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/GC_NMQ_NTSC_U/textures/item_name_static.xml +++ b/soh/assets/xml/GC_NMQ_NTSC_U/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_10/textures/item_name_static.xml b/soh/assets/xml/N64_NTSC_10/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/N64_NTSC_10/textures/item_name_static.xml +++ b/soh/assets/xml/N64_NTSC_10/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_11/textures/item_name_static.xml b/soh/assets/xml/N64_NTSC_11/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/N64_NTSC_11/textures/item_name_static.xml +++ b/soh/assets/xml/N64_NTSC_11/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + diff --git a/soh/assets/xml/N64_NTSC_12/textures/item_name_static.xml b/soh/assets/xml/N64_NTSC_12/textures/item_name_static.xml index 266b3c20036..1bc074c2ffe 100644 --- a/soh/assets/xml/N64_NTSC_12/textures/item_name_static.xml +++ b/soh/assets/xml/N64_NTSC_12/textures/item_name_static.xml @@ -239,7 +239,7 @@ - + From 698fca8862b791e6458c98e4e3d7849189d0a213 Mon Sep 17 00:00:00 2001 From: Proloe Date: Fri, 11 Apr 2025 20:44:40 +0100 Subject: [PATCH 046/157] Fixed right stick aiming being significantly less precise than left stick aiming. (#5388) --- .../actors/ovl_player_actor/z_player.c | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index ddee9d0af37..6638893f529 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -12773,9 +12773,8 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) { if (!CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0)) { temp2 += sControlInput->rel.stick_y * 240.0f * invertYAxisMulti * yAxisMulti; } - if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0) && - fabsf(sControlInput->cur.right_stick_y) > 15.0f) { - temp2 += sControlInput->cur.right_stick_y * 240.0f * invertYAxisMulti * yAxisMulti; + if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) { + temp2 += sControlInput->rel.right_stick_y * 240.0f * invertYAxisMulti * yAxisMulti; } if (fabsf(sControlInput->cur.gyro_x) > 0.01f) { temp2 += (-sControlInput->cur.gyro_x) * 750.0f; @@ -12792,9 +12791,8 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) { if (!CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0)) { temp2 += sControlInput->rel.stick_x * -16.0f * invertXAxisMulti * xAxisMulti; } - if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0) && - fabsf(sControlInput->cur.right_stick_x) > 15.0f) { - temp2 += sControlInput->cur.right_stick_x * -16.0f * invertXAxisMulti * xAxisMulti; + if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) { + temp2 += sControlInput->rel.right_stick_x * -16.0f * invertXAxisMulti * xAxisMulti; } if (fabsf(sControlInput->cur.gyro_y) > 0.01f) { temp2 += (sControlInput->cur.gyro_y) * 750.0f * invertXAxisMulti; @@ -12810,10 +12808,9 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) { (s32)((1.0f - Math_CosS(sControlInput->rel.stick_y * 200)) * 1500.0f) * invertYAxisMulti * yAxisMulti; } - if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0) && - fabsf(sControlInput->cur.right_stick_y) > 15.0f) { - temp3 += ((sControlInput->cur.right_stick_y >= 0) ? 1 : -1) * - (s32)((1.0f - Math_CosS(sControlInput->cur.right_stick_y * 200)) * 1500.0f) * invertYAxisMulti * + if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) { + temp3 += ((sControlInput->rel.right_stick_y >= 0) ? 1 : -1) * + (s32)((1.0f - Math_CosS(sControlInput->rel.right_stick_y * 200)) * 1500.0f) * invertYAxisMulti * yAxisMulti; } if (fabsf(sControlInput->cur.gyro_x) > 0.01f) { @@ -12831,10 +12828,9 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) { (s32)((1.0f - Math_CosS(sControlInput->rel.stick_x * 200)) * -1500.0f) * invertXAxisMulti * xAxisMulti; } - if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0) && - fabsf(sControlInput->cur.right_stick_x) > 15.0f) { - temp3 += ((sControlInput->cur.right_stick_x >= 0) ? 1 : -1) * - (s32)((1.0f - Math_CosS(sControlInput->cur.right_stick_x * 200)) * -1500.0f) * invertXAxisMulti * + if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) { + temp3 += ((sControlInput->rel.right_stick_x >= 0) ? 1 : -1) * + (s32)((1.0f - Math_CosS(sControlInput->rel.right_stick_x * 200)) * -1500.0f) * invertXAxisMulti * xAxisMulti; } if (fabsf(sControlInput->cur.gyro_y) > 0.01f) { From f0f2157a4c2b4bc6cbe239eebb1e4a2b54c80321 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Fri, 11 Apr 2025 12:49:34 -0700 Subject: [PATCH 047/157] Move processing for RAND_INF_WEIRD_EGG to trade item processing so it happens whenever you get the egg. (#5382) Remove RSK check for garden entrance because access is still handled by `CanUse(RG_WEIRD_EGG)`. --- soh/soh/Enhancements/randomizer/hook_handlers.cpp | 4 ---- .../randomizer/location_access/overworld/castle_grounds.cpp | 2 +- soh/src/code/z_parameter.c | 3 +++ 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index a10e8b67fab..4ea81a9436f 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -245,10 +245,6 @@ void RandomizerOnFlagSetHandler(int16_t flagType, int16_t flag) { Flags_SetRandomizerInf(RAND_INF_ZELDAS_LETTER); } - if (flagType == FLAG_EVENT_CHECK_INF && flag == EVENTCHKINF_OBTAINED_POCKET_EGG) { - Flags_SetRandomizerInf(RAND_INF_WEIRD_EGG); - } - RandomizerCheck rc = GetRandomizerCheckFromFlag(flagType, flag); if (rc == RC_UNKNOWN_CHECK) return; diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp index bdcdf9a4ccc..dbf467b0f48 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp @@ -34,7 +34,7 @@ void RegionTable_Init_CastleGrounds() { }, { //Exits Entrance(RR_CASTLE_GROUNDS, []{return true;}), - Entrance(RR_HC_GARDEN, []{return logic->CanUse(RG_WEIRD_EGG) || !ctx->GetOption(RSK_SHUFFLE_WEIRD_EGG) || (ctx->GetTrickOption(RT_DAMAGE_BOOST_SIMPLE) && logic->HasExplosives() && logic->CanJumpslash());}), + Entrance(RR_HC_GARDEN, []{return logic->CanUse(RG_WEIRD_EGG) || (ctx->GetTrickOption(RT_DAMAGE_BOOST_SIMPLE) && logic->HasExplosives() && logic->CanJumpslash());}), Entrance(RR_HC_GREAT_FAIRY_FOUNTAIN, []{return logic->BlastOrSmash();}), Entrance(RR_HC_STORMS_GROTTO, []{return logic->CanOpenStormsGrotto();}), }); diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index facbbfd818f..2a6d3369dce 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2434,6 +2434,9 @@ u8 Item_Give(PlayState* play, u8 item) { } } else { Flags_SetRandomizerInf(item - ITEM_WEIRD_EGG + RAND_INF_CHILD_TRADES_HAS_WEIRD_EGG); + if (item == ITEM_WEIRD_EGG) { + Flags_SetRandomizerInf(RAND_INF_WEIRD_EGG); + } } } From fc0cbbe9e72c487ba0dbdd06ec1e46f5dd852ac2 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Fri, 11 Apr 2025 19:54:52 -0700 Subject: [PATCH 048/157] Improve tracker dock bgs (#5383) * Modify the flow for applying opacity to docked tracker windows only when they are docked to the "Main - Deck" dockspace. * Add opacity clamping for check tracker. --- .../Enhancements/randomizer/randomizer_check_tracker.cpp | 9 +++++++-- .../Enhancements/randomizer/randomizer_item_tracker.cpp | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 93798fea593..b1be07b6eaf 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -1258,8 +1258,13 @@ void BeginFloatWindows(std::string UniqueName, bool& open, ImGuiWindowFlags flag windowFlags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove; } } - ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(Color_Background.r / 255.0f, Color_Background.g / 255.0f, - Color_Background.b / 255.0f, Color_Background.a / 255.0f)); + auto maybeParent = ImGui::GetCurrentWindow(); + ImGuiWindow* window = ImGui::FindWindowByName(UniqueName.c_str()); + if (window != NULL && window->DockTabIsVisible && window->ParentWindow != NULL && + std::string(window->ParentWindow->Name).compare(0, strlen("Main - Deck"), "Main - Deck") == 0) { + Color_Background.a = 255; + } + ImGui::PushStyleColor(ImGuiCol_WindowBg, VecFromRGBA8(Color_Background)); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); ImGui::Begin(UniqueName.c_str(), &open, windowFlags); diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index b86da3c0c25..9b7b1fd395e 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -1162,8 +1162,10 @@ void BeginFloatingWindows(std::string UniqueName, ImGuiWindowFlags flags = 0) { } } auto color = VecFromRGBA8(CVarGetColor(CVAR_TRACKER_ITEM("BgColor.Value"), { 0, 0, 0, 0 })); + auto maybeParent = ImGui::GetCurrentWindow(); ImGuiWindow* window = ImGui::FindWindowByName(UniqueName.c_str()); - if (window != NULL && window->DockTabIsVisible) { + if (window != NULL && window->DockTabIsVisible && window->ParentWindow != NULL && + std::string(window->ParentWindow->Name).compare(0, strlen("Main - Deck"), "Main - Deck") == 0) { color.w = 1.0f; } ImGui::PushStyleColor(ImGuiCol_WindowBg, color); From f304de2eb162c1a1d501ab9ee1f69258d2610b00 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sat, 12 Apr 2025 03:56:47 +0100 Subject: [PATCH 049/157] fix oversight in forest trial first room (#5381) --- .../randomizer/location_access/dungeons/ganons_castle.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp index ded6c827950..45122527371 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp @@ -63,8 +63,8 @@ void RegionTable_Init_GanonsCastle() { }, { //Locations LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_CHEST, logic->CanKillEnemy(RE_WOLFOS)), - LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_POT_1, logic->CanBreakPots() && (logic->CanUse(RG_FIRE_ARROWS) || logic->CanUse(RG_DINS_FIRE))), - LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_POT_2, logic->CanBreakPots() && (logic->CanUse(RG_FIRE_ARROWS) || logic->CanUse(RG_DINS_FIRE))), + LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_POT_1, logic->CanBreakPots() && (logic->CanUse(RG_FIRE_ARROWS) || (logic->CanUse(RG_DINS_FIRE) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT))))), + LOCATION(RC_GANONS_CASTLE_FOREST_TRIAL_POT_2, logic->CanBreakPots() && (logic->CanUse(RG_FIRE_ARROWS) || (logic->CanUse(RG_DINS_FIRE) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT))))), }, {}); areaTable[RR_GANONS_CASTLE_FIRE_TRIAL] = Region("Ganon's Castle Fire Trial", "Ganon's Castle", {RA_GANONS_CASTLE}, NO_DAY_NIGHT_CYCLE, { From 1416b2490a5984e3348fb44764418d470cb76661 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Sat, 12 Apr 2025 06:14:13 -0700 Subject: [PATCH 050/157] Change the Reset All callback back to `CosmeticsEditor_ResetAll`, because it's already set up to not reset HUD position stuff. (#5385) --- soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index e0f530bc202..914f79a9652 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -2413,8 +2413,7 @@ void CosmeticsEditorWindow::DrawElement() { } ImGui::SameLine(); if (UIWidgets::Button("Reset All", UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { - CVarClearBlock("gCosmetics"); - ApplyOrResetCustomGfxPatches(); + CosmeticsEditor_ResetAll(); } if (UIWidgets::Button("Lock All", UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { From 46b40b0220fd9758acb386fbe6d4622bc4465d9e Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Sat, 12 Apr 2025 12:12:48 -0400 Subject: [PATCH 051/157] Fix misspelling of Broken Giant's Knife fix CVar (#5395) --- soh/soh/Enhancements/Presets/PresetEntries.cpp | 6 +++--- soh/soh/SohGui/SohMenuEnhancements.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/soh/soh/Enhancements/Presets/PresetEntries.cpp b/soh/soh/Enhancements/Presets/PresetEntries.cpp index ebf6d4a195e..c9ca5c24f69 100644 --- a/soh/soh/Enhancements/Presets/PresetEntries.cpp +++ b/soh/soh/Enhancements/Presets/PresetEntries.cpp @@ -46,7 +46,7 @@ const std::vector vanillaPlusPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixZoraHintDialogue"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixVineFall"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("EnemySpawnsOverWaterboxes"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixSawSoftlock"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DekuNutUpgradeFix"), 1), - PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixGrokenGiantsKnife"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixMenuLR"), 1), + PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixBrokenGiantsKnife"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixMenuLR"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixDungeonMinimapIcon"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TwoHandedIdle"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("NaviTextFix"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("GerudoWarriorClothingFix"), 1), @@ -125,7 +125,7 @@ const std::vector enhancedPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixZoraHintDialogue"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixVineFall"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("EnemySpawnsOverWaterboxes"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixSawSoftlock"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DekuNutUpgradeFix"), 1), - PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixGrokenGiantsKnife"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixMenuLR"), 1), + PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixBrokenGiantsKnife"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixMenuLR"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixDungeonMinimapIcon"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TwoHandedIdle"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("NaviTextFix"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("GerudoWarriorClothingFix"), 1), @@ -251,7 +251,7 @@ const std::vector randomizerPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("EnemySpawnsOverWaterboxes"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixSawSoftlock"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DekuNutUpgradeFix"), 1), - PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixGrokenGiantsKnife"), 1), + PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixBrokenGiantsKnife"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixMenuLR"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("FixDungeonMinimapIcon"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TwoHandedIdle"), 1), diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 7861838827d..cfa50a81347 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -860,7 +860,7 @@ void SohMenu::AddMenuEnhancements() { [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("CrouchStabHammerFix"), 0) == 0; }) .Options(CheckboxOptions().Tooltip("Make crouch stabbing always do the same damage as a regular slash.")); AddWidget(path, "Fix Broken Giant's Knife Bug", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("FixGrokenGiantsKnife")) + .CVar(CVAR_ENHANCEMENT("FixBrokenGiantsKnife")) .PreFunc([](WidgetInfo& info) { info.options->disabled = IS_RANDO && GameInteractor::IsSaveLoaded(true); info.options->disabledTooltip = "This setting is forcefully enabled when you are playing a Randomizer."; From 5e399fe7a281944711ae5ad6936874738b51a5cb Mon Sep 17 00:00:00 2001 From: Malkierian Date: Sun, 13 Apr 2025 16:29:29 -0700 Subject: [PATCH 052/157] Race Lockout (#5364) * Implement race lockout on the new menu, with option for opt-out. * Cal's requests. --- .../cosmetics/CosmeticsEditor.cpp | 10 ++ .../Enhancements/debugger/MessageViewer.cpp | 2 + .../debugger/SohConsoleWindow.cpp | 2 + .../debugger/SohGfxDebuggerWindow.cpp | 2 + soh/soh/Enhancements/debugger/actorViewer.cpp | 2 + soh/soh/Enhancements/debugger/colViewer.cpp | 2 + .../Enhancements/debugger/debugSaveEditor.cpp | 2 + soh/soh/Enhancements/debugger/dlViewer.cpp | 11 +- .../Enhancements/debugger/hookDebugger.cpp | 2 + soh/soh/Enhancements/debugger/valueViewer.cpp | 2 + soh/soh/Enhancements/randomizer/option.cpp | 4 +- .../Enhancements/randomizer/randomizer.cpp | 30 ++--- .../randomizer/randomizer_check_tracker.cpp | 4 + .../randomizer_entrance_tracker.cpp | 5 +- soh/soh/SohGui/Menu.cpp | 13 +- soh/soh/SohGui/MenuTypes.h | 5 + soh/soh/SohGui/ResolutionEditor.cpp | 111 ++++++++++-------- soh/soh/SohGui/SohMenuDevTools.cpp | 1 + soh/soh/SohGui/SohMenuEnhancements.cpp | 73 ++++++++++-- soh/soh/SohGui/SohMenuNetwork.cpp | 4 +- soh/soh/SohGui/SohMenuRandomizer.cpp | 10 ++ soh/soh/SohGui/SohMenuSettings.cpp | 43 ++++++- 22 files changed, 251 insertions(+), 89 deletions(-) diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index 914f79a9652..8074fa70749 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -2376,10 +2376,12 @@ void CosmeticsEditorWindow::DrawElement() { .Step(0.01f) .Size(ImVec2(300.0f, 0.0f)) .Color(THEME_COLOR)); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::CVarCheckbox("Randomize All on New Scene", CVAR_COSMETIC("RandomizeAllOnNewScene"), UIWidgets::CheckboxOptions() .Color(THEME_COLOR) .Tooltip("Enables randomizing all unlocked cosmetics when you enter a new scene.")); + ImGui::EndDisabled(); UIWidgets::CVarCheckbox( "Advanced Mode", CVAR_COSMETIC("AdvancedMode"), UIWidgets::CheckboxOptions() @@ -2408,9 +2410,12 @@ void CosmeticsEditorWindow::DrawElement() { } } } + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); if (UIWidgets::Button("Randomize All", UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { CosmeticsEditor_RandomizeAll(); } + ImGui::EndDisabled(); + ImGui::SameLine(); if (UIWidgets::Button("Reset All", UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { CosmeticsEditor_ResetAll(); @@ -2431,6 +2436,7 @@ void CosmeticsEditorWindow::DrawElement() { } } + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); if (UIWidgets::Button("Rainbow All", UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { if (!CVarGetInteger(cosmeticOption.lockedCvar, 0) && @@ -2440,6 +2446,8 @@ void CosmeticsEditorWindow::DrawElement() { } } } + ImGui::EndDisabled(); + ImGui::SameLine(); if (UIWidgets::Button("Un-Rainbow All", UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) { for (auto& [id, cosmeticOption] : cosmeticOptions) { @@ -2469,6 +2477,7 @@ void CosmeticsEditorWindow::DrawElement() { if (ImGui::BeginTabItem("Keys")) { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::Separator(true, true, 2.0f, 2.0f); if (UIWidgets::Button("Give all keys dungeon-specific colors", @@ -2482,6 +2491,7 @@ void CosmeticsEditorWindow::DrawElement() { DrawCosmeticGroup(COSMETICS_GROUP_SMALL_KEYS); DrawCosmeticGroup(COSMETICS_GROUP_BOSS_KEYS); + ImGui::EndDisabled(); ImGui::EndTabItem(); } diff --git a/soh/soh/Enhancements/debugger/MessageViewer.cpp b/soh/soh/Enhancements/debugger/MessageViewer.cpp index 83aac4d6c61..c018b001707 100644 --- a/soh/soh/Enhancements/debugger/MessageViewer.cpp +++ b/soh/soh/Enhancements/debugger/MessageViewer.cpp @@ -27,6 +27,7 @@ void MessageViewer::InitElement() { } void MessageViewer::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); ImGui::Text("Table ID"); ImGui::SameLine(); PushStyleInput(THEME_COLOR); @@ -78,6 +79,7 @@ void MessageViewer::DrawElement() { mDisplayCustomMessageClicked = true; } PopStyleButton(); + ImGui::EndDisabled(); } void MessageViewer::UpdateElement() { diff --git a/soh/soh/Enhancements/debugger/SohConsoleWindow.cpp b/soh/soh/Enhancements/debugger/SohConsoleWindow.cpp index d5090e7e593..19be376ed7c 100644 --- a/soh/soh/Enhancements/debugger/SohConsoleWindow.cpp +++ b/soh/soh/Enhancements/debugger/SohConsoleWindow.cpp @@ -12,6 +12,7 @@ void SohConsoleWindow::UpdateElement() { } void SohConsoleWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::PushStyleInput(THEME_COLOR); // Small font (13) to match hardcoded width values in the LUS window.. set large font after below TODO addressed ImGui::PushFont(OTRGlobals::Instance->fontMonoSmall); @@ -27,4 +28,5 @@ void SohConsoleWindow::DrawElement() { ImGui::PopFont(); UIWidgets::PopStyleInput(); + ImGui::EndDisabled(); } diff --git a/soh/soh/Enhancements/debugger/SohGfxDebuggerWindow.cpp b/soh/soh/Enhancements/debugger/SohGfxDebuggerWindow.cpp index 06d81577d6e..def23caccb1 100644 --- a/soh/soh/Enhancements/debugger/SohGfxDebuggerWindow.cpp +++ b/soh/soh/Enhancements/debugger/SohGfxDebuggerWindow.cpp @@ -10,7 +10,9 @@ void SohGfxDebuggerWindow::UpdateElement() { } void SohGfxDebuggerWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); ImGui::PushFont(OTRGlobals::Instance->fontMonoLarger); GfxDebuggerWindow::DrawElement(); ImGui::PopFont(); + ImGui::EndDisabled(); } diff --git a/soh/soh/Enhancements/debugger/actorViewer.cpp b/soh/soh/Enhancements/debugger/actorViewer.cpp index 8ae940ffb99..515da6491ac 100644 --- a/soh/soh/Enhancements/debugger/actorViewer.cpp +++ b/soh/soh/Enhancements/debugger/actorViewer.cpp @@ -849,6 +849,7 @@ void ActorViewer_AddTagForAllActors() { } void ActorViewerWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); static Actor* display; static Actor empty{}; static Actor* fetch = NULL; @@ -1186,6 +1187,7 @@ void ActorViewerWindow::DrawElement() { actors.clear(); } } + ImGui::EndDisabled(); } void ActorViewerWindow::InitElement() { diff --git a/soh/soh/Enhancements/debugger/colViewer.cpp b/soh/soh/Enhancements/debugger/colViewer.cpp index e3abba484ec..352d4b9626e 100644 --- a/soh/soh/Enhancements/debugger/colViewer.cpp +++ b/soh/soh/Enhancements/debugger/colViewer.cpp @@ -58,6 +58,7 @@ using namespace UIWidgets; // Draws the ImGui window for the collision viewer void ColViewerWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); CheckboxOptions checkOpt = CheckboxOptions().Color(THEME_COLOR); ComboboxOptions comboOpt = ComboboxOptions().Color(THEME_COLOR); CVarCheckbox("Enabled", CVAR_DEVELOPER_TOOLS("ColViewer.Enabled"), checkOpt); @@ -139,6 +140,7 @@ void ColViewerWindow::DrawElement() { UIWidgets::Tooltip(colorHelpText.c_str()); } PopStyleHeader(); + ImGui::EndDisabled(); } // Calculates the normal for a triangle at the 3 specified points diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index a97a41d16f5..499e8d1addc 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -1665,6 +1665,7 @@ void ResetBaseOptions() { void SaveEditorWindow::DrawElement() { PushStyleTabs(THEME_COLOR); ImGui::PushFont(OTRGlobals::Instance->fontMonoLarger); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); if (ImGui::BeginTabBar("SaveContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { ResetBaseOptions(); @@ -1706,6 +1707,7 @@ void SaveEditorWindow::DrawElement() { ImGui::EndTabBar(); } + ImGui::EndDisabled(); ImGui::PopFont(); PopStyleTabs(); } diff --git a/soh/soh/Enhancements/debugger/dlViewer.cpp b/soh/soh/Enhancements/debugger/dlViewer.cpp index 7cedcdf26d2..a02aa7d95df 100644 --- a/soh/soh/Enhancements/debugger/dlViewer.cpp +++ b/soh/soh/Enhancements/debugger/dlViewer.cpp @@ -89,6 +89,7 @@ void PerformDisplayListSearch() { } void DLViewerWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); // Debounce the search field as listing otr files is expensive UIWidgets::PushStyleInput(THEME_COLOR); ImGui::PushFont(OTRGlobals::Instance->fontMonoLarger); @@ -122,6 +123,7 @@ void DLViewerWindow::DrawElement() { if (activeDisplayList == "") { ImGui::PopFont(); + ImGui::EndDisabled(); return; } @@ -131,6 +133,8 @@ void DLViewerWindow::DrawElement() { if (res->GetInitData()->Type != static_cast(Fast::ResourceType::DisplayList)) { ImGui::Text("Resource type is not a Display List. Please choose another."); + ImGui::PopFont(); + ImGui::EndDisabled(); return; } @@ -325,13 +329,10 @@ void DLViewerWindow::DrawElement() { } ImGui::EndGroup(); } - } catch (const std::exception& e) { - ImGui::Text("Error displaying DL instructions."); - ImGui::PopFont(); - return; - } + } catch (const std::exception& e) { ImGui::Text("Error displaying DL instructions."); } ImGui::PopFont(); + ImGui::EndDisabled(); } void DLViewerWindow::InitElement() { diff --git a/soh/soh/Enhancements/debugger/hookDebugger.cpp b/soh/soh/Enhancements/debugger/hookDebugger.cpp index 11386228278..146db2ddc9b 100644 --- a/soh/soh/Enhancements/debugger/hookDebugger.cpp +++ b/soh/soh/Enhancements/debugger/hookDebugger.cpp @@ -77,6 +77,7 @@ void DrawHookRegisteringInfos(const char* hookName) { } void HookDebuggerWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); #ifndef __cpp_lib_source_location ImGui::TextColored(yellow, "Some features of the Hook Debugger are unavailable because SoH was compiled " "without \"\" support " @@ -93,6 +94,7 @@ void HookDebuggerWindow::DrawElement() { } ImGui::PopFont(); + ImGui::EndDisabled(); } void HookDebuggerWindow::InitElement() { diff --git a/soh/soh/Enhancements/debugger/valueViewer.cpp b/soh/soh/Enhancements/debugger/valueViewer.cpp index 1ea11744423..4544979cb53 100644 --- a/soh/soh/Enhancements/debugger/valueViewer.cpp +++ b/soh/soh/Enhancements/debugger/valueViewer.cpp @@ -154,6 +154,7 @@ void RegisterValueViewerHooks() { RegisterShipInitFunc initFunc(RegisterValueViewerHooks, { CVAR_NAME }); void ValueViewerWindow::DrawElement() { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::CVarCheckbox("Enable Printing", CVAR_NAME, UIWidgets::CheckboxOptions().Color(THEME_COLOR)); ImGui::BeginGroup(); @@ -275,6 +276,7 @@ void ValueViewerWindow::DrawElement() { } ImGui::EndGroup(); } + ImGui::EndDisabled(); } void ValueViewerWindow::InitElement() { diff --git a/soh/soh/Enhancements/randomizer/option.cpp b/soh/soh/Enhancements/randomizer/option.cpp index 43fcc2f6589..bec719d4b20 100644 --- a/soh/soh/Enhancements/randomizer/option.cpp +++ b/soh/soh/Enhancements/randomizer/option.cpp @@ -368,7 +368,9 @@ void OptionGroup::Disable() { bool OptionGroup::RenderImGui() const { // NOLINT(*-no-recursion) ImGuiWindow* window = ImGui::GetCurrentWindow(); bool changed = false; - ImGui::BeginDisabled(mDisabled); + ImGui::BeginDisabled(mDisabled || CVarGetInteger(CVAR_SETTING("DisableChanges"), 0) || + CVarGetInteger(CVAR_GENERAL("RandoGenerating"), 0) || + CVarGetInteger(CVAR_GENERAL("OnFileSelectNameEntry"), 0)); if (mContainerType == WidgetContainerType::TABLE) { if (ImGui::BeginTable(mName.c_str(), static_cast(mSubGroups.size()), ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 7a5f8dd6993..f1ecd608c8c 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -3690,6 +3690,7 @@ void RandomizerSettingsWindow::DrawElement() { locationsTabOpen = false; tricksTabOpen = false; } + ImGui::EndDisabled(); UIWidgets::Spacer(0); UIWidgets::CVarCheckbox("Manual seed entry", CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), @@ -3725,9 +3726,7 @@ void RandomizerSettingsWindow::DrawElement() { } UIWidgets::Spacer(0); - ImGui::BeginDisabled((CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0) && - gSaveContext.gameMode != GAMEMODE_FILE_SELECT) || - GameInteractor::IsSaveLoaded()); + ImGui::BeginDisabled((gSaveContext.gameMode != GAMEMODE_FILE_SELECT) || GameInteractor::IsSaveLoaded()); if (UIWidgets::Button("Generate Randomizer", UIWidgets::ButtonOptions().Size(ImVec2(250.f, 0.f)).Color(THEME_COLOR))) { ctx->SetSpoilerLoaded(false); @@ -3746,7 +3745,6 @@ void RandomizerSettingsWindow::DrawElement() { // ImGui::Text("Settings File: %s", presetfilepath.c_str()); UIWidgets::Separator(true, true, 0.f, 0.f); - ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); ImGuiWindow* window = ImGui::GetCurrentWindow(); static ImVec2 cellPadding(8.0f, 8.0f); @@ -3762,8 +3760,6 @@ void RandomizerSettingsWindow::DrawElement() { ImGui::EndTabItem(); } - ImGui::BeginDisabled(CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == - RO_LOGIC_VANILLA); if (ImGui::BeginTabItem("Items")) { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); ImGui::BeginDisabled(CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == @@ -3775,23 +3771,23 @@ void RandomizerSettingsWindow::DrawElement() { ImGui::PopStyleVar(1); ImGui::EndTabItem(); } - ImGui::EndDisabled(); - ImGui::BeginDisabled(CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == - RO_LOGIC_VANILLA); if (ImGui::BeginTabItem("Gameplay")) { + ImGui::BeginDisabled(CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == + RO_LOGIC_VANILLA); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); if (mSettings->GetOptionGroup(RSG_GAMEPLAY_IMGUI_TABLE).RenderImGui()) { mNeedsUpdate = true; } + ImGui::EndDisabled(); ImGui::PopStyleVar(1); ImGui::EndTabItem(); } - ImGui::EndDisabled(); - ImGui::BeginDisabled(CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == - RO_LOGIC_VANILLA); if (ImGui::BeginTabItem("Locations")) { + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0) || disableEditingRandoSettings || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == + RO_LOGIC_VANILLA); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); if (!locationsTabOpen) { locationsTabOpen = true; @@ -3924,10 +3920,10 @@ void RandomizerSettingsWindow::DrawElement() { } ImGui::PopStyleVar(1); ImGui::EndTabItem(); + ImGui::EndDisabled(); } else { locationsTabOpen = false; } - ImGui::EndDisabled(); if (ImGui::BeginTabItem("Tricks/Glitches")) { if (!tricksTabOpen) { @@ -3975,8 +3971,9 @@ void RandomizerSettingsWindow::DrawElement() { ImGui::EndTable(); } - ImGui::BeginDisabled(CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == - RO_LOGIC_VANILLA); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0) || disableEditingRandoSettings || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == + RO_LOGIC_VANILLA); // Tricks static std::unordered_map areaTreeDisabled{ @@ -4354,9 +4351,6 @@ void RandomizerSettingsWindow::DrawElement() { ImGui::EndTabBar(); } UIWidgets::PopStyleTabs(); - - ImGui::EndDisabled(); - ImGui::EndDisabled(); } void RandomizerSettingsWindow::UpdateElement() { diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index b1be07b6eaf..6c670e447d8 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -2080,11 +2080,13 @@ void CheckTrackerSettingsWindow::DrawElement() { .DefaultIndex(TRACKER_COMBO_BUTTON_L)); } } + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::CVarCheckbox("Vanilla/MQ Dungeon Spoilers", CVAR_TRACKER_CHECK("MQSpoilers"), UIWidgets::CheckboxOptions() .Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. " "Otherwise, Vanilla/MQ dungeon locations must be unlocked.") .Color(THEME_COLOR)); + ImGui::EndDisabled(); if (UIWidgets::CVarCheckbox( "Hide unshuffled shop item checks", CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"), UIWidgets::CheckboxOptions() @@ -2105,6 +2107,7 @@ void CheckTrackerSettingsWindow::DrawElement() { UIWidgets::CheckboxOptions() .Tooltip("If enabled, will show a check's logic when hovering over it.") .Color(THEME_COLOR)); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); if (UIWidgets::CVarCheckbox("Enable Available Checks", CVAR_TRACKER_CHECK("EnableAvailableChecks"), UIWidgets::CheckboxOptions() .Tooltip("If enabled, will show the checks that are available to be collected " @@ -2113,6 +2116,7 @@ void CheckTrackerSettingsWindow::DrawElement() { enableAvailableChecks = CVarGetInteger(CVAR_TRACKER_CHECK("EnableAvailableChecks"), 0); RecalculateAvailableChecks(); } + ImGui::EndDisabled(); // Filtering settings UIWidgets::PaddedSeparator(); diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp index f9317264782..b71bae5b315 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp @@ -690,6 +690,7 @@ void EntranceTrackerSettingsWindow::DrawElement() { UIWidgets::CheckboxOptions() .Tooltip("Automatically scroll to the first available entrance in the current scene") .Color(THEME_COLOR)); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::CVarCheckbox("Highlight previous", CVAR_TRACKER_ENTRANCE("HighlightPrevious"), UIWidgets::CheckboxOptions() .Tooltip("Highlight the previous entrance that Link came from") @@ -698,6 +699,7 @@ void EntranceTrackerSettingsWindow::DrawElement() { UIWidgets::CheckboxOptions() .Tooltip("Highlight available entrances in the current scene") .Color(THEME_COLOR)); + ImGui::EndDisabled(); UIWidgets::CVarCheckbox("Hide undiscovered", CVAR_TRACKER_ENTRANCE("CollapseUndiscovered"), UIWidgets::CheckboxOptions() .Tooltip("Collapse undiscovered entrances towards the bottom of each group") @@ -724,6 +726,7 @@ void EntranceTrackerSettingsWindow::DrawElement() { UIWidgets::RadioButtonsOptions().Color(THEME_COLOR).Tooltip("Group entrances by their entrance type")); ImGui::Text("Spoiler Reveal"); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::CVarCheckbox( "Show Source", CVAR_TRACKER_ENTRANCE("ShowFrom"), UIWidgets::CheckboxOptions().Tooltip("Reveal the sourcefor undiscovered entrances").Color(THEME_COLOR)); @@ -731,7 +734,7 @@ void EntranceTrackerSettingsWindow::DrawElement() { UIWidgets::CheckboxOptions() .Tooltip("Reveal the destination for undiscovered entrances") .Color(THEME_COLOR)); - + ImGui::EndDisabled(); ImGui::EndTable(); } diff --git a/soh/soh/SohGui/Menu.cpp b/soh/soh/SohGui/Menu.cpp index a1321a6f661..87c36249600 100644 --- a/soh/soh/SohGui/Menu.cpp +++ b/soh/soh/SohGui/Menu.cpp @@ -65,6 +65,8 @@ uint32_t GetVectorIndexOf(std::vector& vector, std::string value) { return std::distance(vector.begin(), std::find(vector.begin(), vector.end(), value)); } +static bool raceDisableActive = false; + void Menu::InsertSidebarSearch() { menuEntries["Settings"].sidebars.emplace("Search", searchSidebarEntry); uint32_t curIndex = 0; @@ -228,7 +230,7 @@ std::unordered_map& Menu::GetDisabledMap() { } void Menu::MenuDrawItem(WidgetInfo& widget, uint32_t width, UIWidgets::Colors menuThemeIndex) { - disabledTempTooltip = "This setting is disabled because: \n\n"; + disabledTempTooltip = "This setting is disabled because: \n"; disabledValue = false; disabledTooltip = " "; @@ -241,11 +243,16 @@ void Menu::MenuDrawItem(WidgetInfo& widget, uint32_t width, UIWidgets::Colors me if (!widget.activeDisables.empty()) { widget.options->disabled = true; for (auto option : widget.activeDisables) { - disabledTempTooltip += std::string("- ") + disabledMap.at(option).reason + std::string("\n"); + disabledTempTooltip += std::string("\n- ") + disabledMap.at(option).reason; } widget.options->disabledTooltip = disabledTempTooltip.c_str(); } } + if (widget.raceDisable && raceDisableActive) { + widget.options->disabled = true; + disabledTempTooltip += std::string("\n- Race Lockout Active"); + widget.options->disabledTooltip = disabledTempTooltip.c_str(); + } if (widget.sameLine) { ImGui::SameLine(); @@ -492,6 +499,8 @@ void Menu::DrawElement() { info.active = info.evaluation(info); } + raceDisableActive = CVarGetInteger(CVAR_SETTING("DisableChanges"), 0); + windowHeight = ImGui::GetMainViewport()->WorkSize.y; windowWidth = ImGui::GetMainViewport()->WorkSize.x; auto windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings; diff --git a/soh/soh/SohGui/MenuTypes.h b/soh/soh/SohGui/MenuTypes.h index 162f80ac5f3..de78e89612c 100644 --- a/soh/soh/SohGui/MenuTypes.h +++ b/soh/soh/SohGui/MenuTypes.h @@ -109,6 +109,7 @@ struct WidgetInfo { const char* windowName = ""; bool isHidden = false; bool sameLine = false; + bool raceDisable = true; WidgetInfo& CVar(const char* cVar_) { cVar = cVar_; @@ -191,6 +192,10 @@ struct WidgetInfo { customFunction = customFunction_; return *this; } + WidgetInfo& RaceDisable(bool disable) { + raceDisable = disable; + return *this; + } }; struct WidgetPath { diff --git a/soh/soh/SohGui/ResolutionEditor.cpp b/soh/soh/SohGui/ResolutionEditor.cpp index 298e2c470d3..d33a7b43c06 100644 --- a/soh/soh/SohGui/ResolutionEditor.cpp +++ b/soh/soh/SohGui/ResolutionEditor.cpp @@ -371,29 +371,37 @@ void RegisterResolutionWidgets() { WidgetPath path = { "Settings", "Graphics", SECTION_COLUMN_2 }; // Resolution visualiser - mSohMenu->AddWidget(path, "Viewport dimensions: {} x {}", WIDGET_TEXT).PreFunc([](WidgetInfo& info) { - info.name = fmt::format("Viewport dimensions: {} x {}", gfx_current_game_window_viewport.width, - gfx_current_game_window_viewport.height); - }); - mSohMenu->AddWidget(path, "Internal resolution: {} x {}", WIDGET_TEXT).PreFunc([](WidgetInfo& info) { - info.name = - fmt::format("Internal resolution: {} x {}", gfx_current_dimensions.width, gfx_current_dimensions.height); - }); + mSohMenu->AddWidget(path, "Viewport dimensions: {} x {}", WIDGET_TEXT) + .RaceDisable(false) + .PreFunc([](WidgetInfo& info) { + info.name = fmt::format("Viewport dimensions: {} x {}", gfx_current_game_window_viewport.width, + gfx_current_game_window_viewport.height); + }); + mSohMenu->AddWidget(path, "Internal resolution: {} x {}", WIDGET_TEXT) + .RaceDisable(false) + .PreFunc([](WidgetInfo& info) { + info.name = fmt::format("Internal resolution: {} x {}", gfx_current_dimensions.width, + gfx_current_dimensions.height); + }); // Activator mSohMenu->AddWidget(path, "Enable advanced settings.", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled"); + .CVar(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled") + .RaceDisable(false); // Error/Warning display mSohMenu ->AddWidget(path, ICON_FA_EXCLAMATION_TRIANGLE " Significant frame rate (FPS) drops may be occuring.", WIDGET_TEXT) + .RaceDisable(false) .PreFunc( [](WidgetInfo& info) { info.isHidden = !(!CVarGetInteger(CVAR_LOW_RES_MODE, 0) && IsDroppingFrames()); }) .Options(TextOptions().Color(Colors::Orange)); mSohMenu->AddWidget(path, ICON_FA_QUESTION_CIRCLE " \"N64 Mode\" is overriding these settings.", WIDGET_TEXT) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_LOW_RES_MODE, 0); }) .Options(TextOptions().Color(Colors::LightBlue)); mSohMenu->AddWidget(path, "Click to disable N64 mode", WIDGET_BUTTON) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_LOW_RES_MODE, 0); }) .Callback([](WidgetInfo& info) { CVarSetInteger(CVAR_LOW_RES_MODE, 0); @@ -401,17 +409,18 @@ void RegisterResolutionWidgets() { }); // Aspect Ratio - mSohMenu->AddWidget(path, "AspectSep", WIDGET_SEPARATOR).PreFunc([](WidgetInfo& info) { + mSohMenu->AddWidget(path, "AspectSep", WIDGET_SEPARATOR).RaceDisable(false).PreFunc([](WidgetInfo& info) { if (mSohMenu->GetDisabledMap().at(DISABLE_FOR_ADVANCED_RESOLUTION_OFF).active) { info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_OFF); } }); - mSohMenu->AddWidget(path, "Force aspect ratio:", WIDGET_TEXT).PreFunc([](WidgetInfo& info) { + mSohMenu->AddWidget(path, "Force aspect ratio:", WIDGET_TEXT).RaceDisable(false).PreFunc([](WidgetInfo& info) { if (mSohMenu->GetDisabledMap().at(DISABLE_FOR_ADVANCED_RESOLUTION_OFF).active) { info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_OFF); } }); mSohMenu->AddWidget(path, "(Select \"Off\" to disable.)", WIDGET_TEXT) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { if (mSohMenu->GetDisabledMap().at(DISABLE_FOR_ADVANCED_RESOLUTION_OFF).active) { info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_OFF); @@ -422,6 +431,7 @@ void RegisterResolutionWidgets() { // Presets mSohMenu->AddWidget(path, "Aspect Ratio", WIDGET_COMBOBOX) .ValuePointer(&item_aspectRatio) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { if (mSohMenu->GetDisabledMap().at(DISABLE_FOR_ADVANCED_RESOLUTION_OFF).active) { info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_OFF); @@ -443,44 +453,49 @@ void RegisterResolutionWidgets() { Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); }) .Options(ComboboxOptions().ComboMap(aspectRatioPresetLabels)); - mSohMenu->AddWidget(path, "AspectRatioCustom", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { - // Hide aspect ratio input fields if using one of the presets. - if (item_aspectRatio == default_aspectRatio && !showHorizontalResField) { - // Declare input interaction bools outside of IF statement to prevent Y field from disappearing. - const bool input_X = - UIWidgets::SliderFloat("X", &aspectRatioX, - UIWidgets::FloatSliderOptions({ { .disabled = disabled_everything } }) - .Min(0.1f) - .Max(32.0f) - .Step(0.001f) - .Format("%3f") - .Color(THEME_COLOR) - .LabelPosition(UIWidgets::LabelPositions::Near) - .ComponentAlignment(UIWidgets::ComponentAlignments::Right)); - const bool input_Y = - UIWidgets::SliderFloat("Y", &aspectRatioY, - UIWidgets::FloatSliderOptions({ { .disabled = disabled_everything } }) - .Min(0.1f) - .Max(24.0f) - .Step(0.001f) - .Format("%3f") - .Color(THEME_COLOR) - .LabelPosition(UIWidgets::LabelPositions::Near) - .ComponentAlignment(UIWidgets::ComponentAlignments::Right)); - if (input_X || input_Y) { - item_aspectRatio = default_aspectRatio; - update[UPDATE_aspectRatioX] = true; - update[UPDATE_aspectRatioY] = true; - } - } else if (showHorizontalResField) { // Show calculated aspect ratio - if (item_aspectRatio) { - ImGui::Dummy({ 0, 2 }); - const float resolvedAspectRatio = (float)gfx_current_dimensions.width / gfx_current_dimensions.height; - ImGui::Text("Aspect ratio: %.2f:1", resolvedAspectRatio); + mSohMenu->AddWidget(path, "AspectRatioCustom", WIDGET_CUSTOM) + .RaceDisable(false) + .CustomFunction([](WidgetInfo& info) { + // Hide aspect ratio input fields if using one of the presets. + if (item_aspectRatio == default_aspectRatio && !showHorizontalResField) { + // Declare input interaction bools outside of IF statement to prevent Y field from disappearing. + const bool input_X = + UIWidgets::SliderFloat("X", &aspectRatioX, + UIWidgets::FloatSliderOptions({ { .disabled = disabled_everything } }) + .Min(0.1f) + .Max(32.0f) + .Step(0.001f) + .Format("%3f") + .Color(THEME_COLOR) + .LabelPosition(UIWidgets::LabelPositions::Near) + .ComponentAlignment(UIWidgets::ComponentAlignments::Right)); + const bool input_Y = + UIWidgets::SliderFloat("Y", &aspectRatioY, + UIWidgets::FloatSliderOptions({ { .disabled = disabled_everything } }) + .Min(0.1f) + .Max(24.0f) + .Step(0.001f) + .Format("%3f") + .Color(THEME_COLOR) + .LabelPosition(UIWidgets::LabelPositions::Near) + .ComponentAlignment(UIWidgets::ComponentAlignments::Right)); + if (input_X || input_Y) { + item_aspectRatio = default_aspectRatio; + update[UPDATE_aspectRatioX] = true; + update[UPDATE_aspectRatioY] = true; + } + } else if (showHorizontalResField) { // Show calculated aspect ratio + if (item_aspectRatio) { + ImGui::Dummy({ 0, 2 }); + const float resolvedAspectRatio = + (float)gfx_current_dimensions.width / gfx_current_dimensions.height; + ImGui::Text("Aspect ratio: %.2f:1", resolvedAspectRatio); + } } - } - }); - mSohMenu->AddWidget(path, "MoreResolutionSettings", WIDGET_CUSTOM).CustomFunction(ResolutionCustomWidget); + }); + mSohMenu->AddWidget(path, "MoreResolutionSettings", WIDGET_CUSTOM) + .CustomFunction(ResolutionCustomWidget) + .RaceDisable(false); } void UpdateResolutionVars() { diff --git a/soh/soh/SohGui/SohMenuDevTools.cpp b/soh/soh/SohGui/SohMenuDevTools.cpp index 0588ae508fa..91d456c95ca 100644 --- a/soh/soh/SohGui/SohMenuDevTools.cpp +++ b/soh/soh/SohGui/SohMenuDevTools.cpp @@ -93,6 +93,7 @@ void SohMenu::AddMenuDevTools() { AddSidebarEntry("Dev Tools", path.sidebarName, 1); AddWidget(path, "Popout Stats Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("SohStats")) + .RaceDisable(false) .WindowName("Stats##Soh") .Options(WindowButtonOptions().Tooltip("Enables the separate Stats Window.")); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index cfa50a81347..0b78892d48a 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -85,6 +85,7 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Saving", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Autosave", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("Autosave")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Save the game automatically on a 3 minute interval and when soft-resetting the game. The interval " "autosave will wait if the game is paused in any way (dialogue, pause screen up, cutscenes, " @@ -168,9 +169,11 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Controls", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Answer Navi Prompt with L Button", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("NaviOnL")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Speak to Navi with L but enter First-Person Camera with C-Up.")); AddWidget(path, "Don't Require Input for Credits Sequence", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("NoInputForCredits")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Removes the Input Requirement on Text boxes after defeating Ganon, allowing the Credits " "Sequence to continue to progress.")); @@ -202,14 +205,20 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Item Count Messages", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Gold Skulltula Tokens", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("InjectItemCounts.GoldSkulltula")); - AddWidget(path, "Pieces of Heart", WIDGET_CVAR_CHECKBOX).CVar(CVAR_ENHANCEMENT("InjectItemCounts.HeartPiece")); - AddWidget(path, "Heart Containers", WIDGET_CVAR_CHECKBOX).CVar(CVAR_ENHANCEMENT("InjectItemCounts.HeartContainer")); + .CVar(CVAR_ENHANCEMENT("InjectItemCounts.GoldSkulltula")) + .RaceDisable(false); + AddWidget(path, "Pieces of Heart", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("InjectItemCounts.HeartPiece")) + .RaceDisable(false); + AddWidget(path, "Heart Containers", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("InjectItemCounts.HeartContainer")) + .RaceDisable(false); path.column = SECTION_COLUMN_3; AddWidget(path, "Misc", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Disable Crit Wiggle", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableCritWiggle")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Disable Random Camera Wiggle at Low Health.")); AddWidget(path, "Better Owl", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("BetterOwl")) @@ -430,27 +439,32 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Mods", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Use Alternate Assets", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("AltAssets")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Toggle between standard assets and alternate assets. Usually mods will indicate if " "this setting has to be used or not.")); AddWidget(path, "Disable Bomb Billboarding", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableBombBillboarding")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Disables bombs always rotating to face the camera. To be used in conjunction with mods that want to " "replace bombs with 3D objects.")); AddWidget(path, "Disable Grotto Fixed Rotation", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableGrottoRotation")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Disables Grottos rotating with the Camera. To be used in conjuction with mods that want to " "replace grottos with 3D objects.")); AddWidget(path, "Ingame Text Spacing: %d", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_ENHANCEMENT("TextSpacing")) + .RaceDisable(false) .Options(IntSliderOptions().Min(4).Max(6).DefaultValue(6).Tooltip( "Space between text characters (useful for HD font textures).")); AddWidget(path, "Models & Textures", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Disable LOD", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableLOD")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Turns off the Level of Detail setting, making models use their Higher-Poly variants at any distance.")); AddWidget(path, "Enemy Health Bars", WIDGET_CVAR_CHECKBOX) @@ -458,18 +472,22 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().Tooltip("Renders a health bar for Enemies when Z-Targeted.")); AddWidget(path, "Enable 3D Dropped Items/Projectiles", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("NewDrops")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Replaces most 2D items and projectiles on the overworld with their equivalent 3D models.")); AddWidget(path, "Animated Link in Pause Menu", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("PauseMenuAnimatedLink")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Turns the Static Image of Link in the Pause Menu's Equipment Subscreen " "into a model cycling through his idle animations.")); AddWidget(path, "Show Age-Dependent Equipment", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("EquipmentAlwaysVisible")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { UpdatePatchHand(); }) .Options(CheckboxOptions().Tooltip("Makes all equipment visible, regardless of age.")); AddWidget(path, "Scale Adult Equipment as Child", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ScaleAdultEquipmentAsChild")) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), 0) == 0; }) @@ -478,11 +496,14 @@ void SohMenu::AddMenuEnhancements() { "not work properly with some mods.")); AddWidget(path, "Show Gauntlets in First Person", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FirstPersonGauntlets")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Renders Gauntlets when using the Bow and Hookshot like in OoT3D.")); AddWidget(path, "Show Chains on Both Sides of Locked Doors", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("ShowDoorLocksOnBothSides")); + .CVar(CVAR_ENHANCEMENT("ShowDoorLocksOnBothSides")) + .RaceDisable(false); AddWidget(path, "Color Temple of Time's Medallions", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ToTMedallionsColors")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { PatchToTMedallions(); }) .Options(CheckboxOptions().Tooltip( "When Medallions are collected, the Medallion imprints around the Master Sword Pedestal in the Temple " @@ -500,15 +521,18 @@ void SohMenu::AddMenuEnhancements() { "in Hot/Underwater conditions.")); AddWidget(path, "Remember Minimap State Between Areas", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("RememberMapToggleState")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( - "Preverse the minimap visibility state when going between areas rather than default it to \"on\" " + "Preserve the minimap visibility state when going between areas rather than default it to \"on\" " "when going through loading zones.")); AddWidget(path, "Visual Stone of Agony", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("VisualAgony")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Displays an icon and plays a sound when Stone of Agony should be activated, for those without rumble.")); AddWidget(path, "Disable HUD Heart Animations", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("NoHUDHeartAnimation")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Disables the Beating Animation of the Hearts on the HUD.")); AddWidget(path, "Glitch Line-up Tick", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DrawLineupTick")) @@ -517,26 +541,32 @@ void SohMenu::AddMenuEnhancements() { "UI based line-ups do not work outside of 4:3")); AddWidget(path, "Disable Black Bar Letterboxes", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableBlackBars")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Disables Black Bar Letterboxes during cutscenes and Z-Targeting. NOTE: There may be minor visual " "glitches that were covered up by the black bars. Please disable this setting before reporting a bug.")); AddWidget(path, "Dynamic Wallet Icon", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DynamicWalletIcon")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Changes the Rupee in the Wallet icon to match the wallet size you currently have.")); AddWidget(path, "Always Show Dungeon Entrances", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("AlwaysShowDungeonMinimapIcon")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Always shows dungeon entrance icons on the Minimap.")); AddWidget(path, "More Info in File Select", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FileSelectMoreInfo")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Shows what items you have collected in the File Select screen, like in N64 Randomizer.")); AddWidget(path, "Better Ammo Rendering in Pause Menu", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("BetterAmmoRendering")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Ammo counts in the pause menu will work correctly regardless of the position of items in the Inventory.")); AddWidget(path, "Enable Passage of Time on File Select", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("TimeFlowFileSelect")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("The skybox in the background of the File Select screen will go through the " "day and night cycle over time.")); @@ -544,14 +574,17 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Misc.", WIDGET_SEPARATOR_TEXT); AddWidget(path, "N64 Mode", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_LOW_RES_MODE) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Sets the aspect ratio to 4:3 and lowers resolution to 240p, the N64's native resolution.")); AddWidget(path, "Remove Spin Attack Darkness", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("RemoveSpinAttackDarkness")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Remove the Darkness that appears when charging a Spin Attack.")); - AddWidget(path, "Draw Distance", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Draw Distance", WIDGET_SEPARATOR_TEXT).RaceDisable(false); AddWidget(path, "Increase Actor Draw Distance: %dx", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_ENHANCEMENT("DisableDrawDistance")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { if (CVarGetInteger(CVAR_ENHANCEMENT("DisableDrawDistance"), 1) <= 1) { CVarSetInteger(CVAR_ENHANCEMENT("DisableKokiriDrawDistance"), 0); @@ -561,6 +594,7 @@ void SohMenu::AddMenuEnhancements() { "Increases the range in which Actors/Objects are drawn.")); AddWidget(path, "Kokiri Draw Distance", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableKokiriDrawDistance")) + .RaceDisable(false) .PreFunc( [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("DisableDrawDistance"), 1) <= 1; }) .Options(CheckboxOptions().Tooltip( @@ -896,22 +930,27 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Graphical Fixes", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Fix L&R Pause Menu", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FixMenuLR")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Makes the L and R buttons in the pause menu the same color.")); AddWidget(path, "Fix Dungeon Entrances", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FixDungeonMinimapIcon")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Removes the Dungeon Entrance icon on the top-left corner of the screen when no dungeon is present on the " "current map.")); AddWidget(path, "Fix Two-Handed Idle Animations", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("TwoHandedIdle")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Re-Enables the two-handed idle animation, a seemingly finished animation that was disabled on accident " "in the original game.")); AddWidget(path, "Fix Navi Text HUD Position", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("NaviTextFix")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Correctly centers the Navi text prompt on the HUD's C-Up button.")); AddWidget(path, "Fix Gerudo Warrior's Clothing Colors", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("GerudoWarriorClothingFix")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Prevent the Gerudo Warrior's clothes changing color when changing Link's tunic or " "using bombs in front of her.")); @@ -922,15 +961,18 @@ void SohMenu::AddMenuEnhancements() { "Fixes authentic out of bounds texture reads, instead loading textures with the correct size.")); AddWidget(path, "Fix Link's Eyes Open while Sleeping", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FixEyesOpenWhileSleeping")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Fixes Link's eyes being open in the opening cutscene when he is supposed to be sleeping.")); AddWidget(path, "Fix Hand Holding Hammer", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FixHammerHand")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { UpdatePatchHand(); }) .Options(CheckboxOptions().Tooltip( "Fixes Adult Link having a backwards Left hand when holding the Megaton Hammer.")); AddWidget(path, "Fix Vanishing Paths", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_ENHANCEMENT("SceneSpecificDirtPathFix")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { if (gPlayState != NULL) { UpdateDirtPathFixState(gPlayState->sceneNum); @@ -948,6 +990,7 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Audio Fixes", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Fix Missing Jingle after 5 Silver Rupees", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("SilverRupeeJingleExtend")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Adds 5 higher pitches for the Silver Rupee Jingle for the rooms with more than 5 Silver Rupees. " "Only relevant for playthroughs involving Master Quest Dungeons.")); @@ -955,10 +998,12 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Desync Fixes", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Fix Darunia Dancing too Fast", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FixDaruniaDanceSpeed")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Fixes Darunia's dancing speed so he dances to the beat of Saria's Song, like in the Original Game.")); AddWidget(path, "Fix Credits Timing (PAL)", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("CreditsFix")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Extend certain credits scenes so the music lines up properly with the visuals. (PAL only)")); @@ -966,6 +1011,7 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Graphical Restorations", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Red Ganon Blood", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("RedGanonBlood")) + .RaceDisable(false) .Options( CheckboxOptions().Tooltip("Restore the original red blood from NTSC 1.0/1.1. Disable for Green blood.")); AddWidget(path, "Restore Old Gold Skulltula Cutscene", WIDGET_CVAR_CHECKBOX) @@ -974,6 +1020,7 @@ void SohMenu::AddMenuEnhancements() { "Restore pre-release behavior where defeating a Gold Skulltula will play a cutscene showing it die.")); AddWidget(path, "Pulsate Boss Icon", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("PulsateBossIcon")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Restores an unfinished feature to pulsate the boss room icon when you are in the boss room.")); @@ -1657,6 +1704,7 @@ void SohMenu::AddMenuEnhancements() { path.column = SECTION_COLUMN_1; AddWidget(path, "Popout Cosmetics Editor Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("CosmeticsEditor")) + .RaceDisable(false) .WindowName("Cosmetics Editor") .Options(WindowButtonOptions().Tooltip("Enables the separate Cosmetics Editor Window.")); @@ -1665,6 +1713,7 @@ void SohMenu::AddMenuEnhancements() { AddSidebarEntry("Enhancements", path.sidebarName, 1); AddWidget(path, "Popout Audio Editor Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("AudioEditor")) + .RaceDisable(false) .WindowName("Audio Editor") .Options(WindowButtonOptions().Tooltip("Enables the separate Audio Editor Window.")); @@ -1673,6 +1722,7 @@ void SohMenu::AddMenuEnhancements() { AddSidebarEntry("Enhancements", path.sidebarName, 2); AddWidget(path, "Popout Gameplay Stats Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("GameplayStats")) + .RaceDisable(false) .WindowName("Gameplay Stats") .Options(WindowButtonOptions().Tooltip("Enables the separate Gameplay Stats Window.")); @@ -1681,6 +1731,7 @@ void SohMenu::AddMenuEnhancements() { AddSidebarEntry("Enhancements", path.sidebarName, 1); AddWidget(path, "Popout Time Splits Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("TimeSplits")) + .RaceDisable(false) .WindowName("Time Splits") .Options(WindowButtonOptions().Tooltip("Enables the separate Time Splits Window.")); @@ -1689,19 +1740,23 @@ void SohMenu::AddMenuEnhancements() { AddSidebarEntry("Enhancements", path.sidebarName, 3); AddWidget(path, "Toggle Timers Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("TimeDisplayEnabled")) + .RaceDisable(false) .WindowName("Additional Timers") .Options(WindowButtonOptions().Tooltip("Enables the separate Additional Timers Window.")); AddWidget(path, "Font Scale: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) .CVar(CVAR_TIME_DISPLAY("FontScale")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { TimeDisplayInitSettings(); }) .Options(FloatSliderOptions().Min(1.0f).Max(5.0f).DefaultValue(1.0f).Format("%.2fx")); AddWidget(path, "Hide Background", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_TIME_DISPLAY("ShowWindowBG")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { TimeDisplayInitSettings(); }); for (auto& timer : timeDisplayList) { - AddWidget(path, timer.timeLabel, WIDGET_CVAR_CHECKBOX).CVar(timer.timeEnable).Callback([](WidgetInfo& info) { - TimeDisplayUpdateDisplayOptions(); - }); + AddWidget(path, timer.timeLabel, WIDGET_CVAR_CHECKBOX) + .RaceDisable(false) + .CVar(timer.timeEnable) + .Callback([](WidgetInfo& info) { TimeDisplayUpdateDisplayOptions(); }); } } diff --git a/soh/soh/SohGui/SohMenuNetwork.cpp b/soh/soh/SohGui/SohMenuNetwork.cpp index 1c67ce28d89..637c782854a 100644 --- a/soh/soh/SohGui/SohMenuNetwork.cpp +++ b/soh/soh/SohGui/SohMenuNetwork.cpp @@ -39,7 +39,7 @@ void SohMenu::AddMenuNetwork() { }) .Options(ButtonOptions().Tooltip("https://github.com/HarbourMasters/sail")); AddWidget(path, "Host & Port", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { - ImGui::BeginDisabled(Sail::Instance->isEnabled); + ImGui::BeginDisabled(Sail::Instance->isEnabled || CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); ImGui::Text("%s", info.name.c_str()); CVarInputString("##HostSail", CVAR_REMOTE_SAIL("Host"), InputOptions() @@ -111,7 +111,7 @@ void SohMenu::AddMenuNetwork() { }) .Options(ButtonOptions().Tooltip("https://crowdcontrol.live")); AddWidget(path, "Host & Port", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { - ImGui::BeginDisabled(CrowdControl::Instance->isEnabled); + ImGui::BeginDisabled(CrowdControl::Instance->isEnabled || CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); ImGui::Text("%s", info.name.c_str()); CVarInputString("##HostCrowdControl", CVAR_REMOTE_CROWD_CONTROL("Host"), InputOptions() diff --git a/soh/soh/SohGui/SohMenuRandomizer.cpp b/soh/soh/SohGui/SohMenuRandomizer.cpp index 9c95431e896..8f4e6e43b29 100644 --- a/soh/soh/SohGui/SohMenuRandomizer.cpp +++ b/soh/soh/SohGui/SohMenuRandomizer.cpp @@ -29,6 +29,7 @@ void SohMenu::AddMenuRandomizer() { .DefaultValue(true)); AddWidget(path, "Random Rupee Names", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_RANDOMIZER_ENHANCEMENT("RandomizeRupeeNames")) + .RaceDisable(false) .Options(CheckboxOptions() .Tooltip("When obtaining Rupees, randomize what the Rupee is called in the textbox.") .DefaultValue(true)); @@ -60,6 +61,7 @@ void SohMenu::AddMenuRandomizer() { .DefaultValue(true)); AddWidget(path, "Quest Item Fanfares", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_RANDOMIZER_ENHANCEMENT("QuestItemFanfares")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Play unique fanfares when obtaining quest items (medallions/stones/songs). Note that these " "fanfares can be longer than usual.")); @@ -71,6 +73,7 @@ void SohMenu::AddMenuRandomizer() { "so you never know what you're getting.")); AddWidget(path, "Simpler Boss Soul Models", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_RANDOMIZER_ENHANCEMENT("SimplerBossSoulModels")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "When shuffling boss souls, they'll appear as a simpler model instead of showing the boss' models." "This might make boss souls more distinguishable from a distance, and can help with performance.")); @@ -93,6 +96,7 @@ void SohMenu::AddMenuRandomizer() { AddSidebarEntry("Randomizer", path.sidebarName, 1); AddWidget(path, "Popout Plandomizer Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("PlandomizerEditor")) + .RaceDisable(false) .WindowName("Plandomizer Editor") .Options(WindowButtonOptions().Tooltip("Enables the separate Randomizer Settings Window.")); @@ -103,12 +107,14 @@ void SohMenu::AddMenuRandomizer() { AddWidget(path, "Item Tracker", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Toggle Item Tracker", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("ItemTracker")) + .RaceDisable(false) .WindowName("Item Tracker") .Options(WindowButtonOptions().Tooltip("Toggles the Item Tracker.").EmbedWindow(false)); AddWidget(path, "Item Tracker Settings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Popout Item Tracker Settings", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("ItemTrackerSettings")) + .RaceDisable(false) .WindowName("Item Tracker Settings") .Options(WindowButtonOptions().Tooltip("Enables the separate Item Tracker Settings Window.")); @@ -119,12 +125,14 @@ void SohMenu::AddMenuRandomizer() { AddWidget(path, "Entrance Tracker", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Toggle Entrance Tracker", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("EntranceTracker")) + .RaceDisable(false) .WindowName("Entrance Tracker") .Options(WindowButtonOptions().Tooltip("Toggles the Entrance Tracker.").EmbedWindow(false)); AddWidget(path, "Entrance Tracker Settings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Popout Entrance Tracker Settings", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("EntranceTrackerSettings")) + .RaceDisable(false) .WindowName("Entrance Tracker Settings") .Options(WindowButtonOptions().Tooltip("Enables the separate Entrance Tracker Settings Window.")); @@ -135,12 +143,14 @@ void SohMenu::AddMenuRandomizer() { AddWidget(path, "Check Tracker", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Toggle Check Tracker", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("CheckTracker")) + .RaceDisable(false) .WindowName("Check Tracker") .Options(WindowButtonOptions().Tooltip("Toggles the Check Tracker.").EmbedWindow(false)); AddWidget(path, "Check Tracker Settings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Popout Check Tracker Settings", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("CheckTrackerSettings")) + .RaceDisable(false) .WindowName("Check Tracker Settings") .Options(WindowButtonOptions().Tooltip("Enables the separate Check Tracker Settings Window.")); } diff --git a/soh/soh/SohGui/SohMenuSettings.cpp b/soh/soh/SohGui/SohMenuSettings.cpp index 2b1f7779921..909c647bd64 100644 --- a/soh/soh/SohGui/SohMenuSettings.cpp +++ b/soh/soh/SohGui/SohMenuSettings.cpp @@ -95,6 +95,7 @@ void SohMenu::AddMenuSettings() { AddWidget(path, "Menu Settings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Menu Theme", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_SETTING("Menu.Theme")) + .RaceDisable(false) .Options(ComboboxOptions() .Tooltip("Changes the Theme of the Menu Widgets.") .ComboMap(menuThemeOptions) @@ -102,18 +103,21 @@ void SohMenu::AddMenuSettings() { #if not defined(__SWITCH__) and not defined(__WIIU__) AddWidget(path, "Menu Controller Navigation", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_IMGUI_CONTROLLER_NAV) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Allows controller navigation of the port menu (Settings, Enhancements,...)\nCAUTION: " "This will disable game inputs while the menu is visible.\n\nD-pad to move between " "items, A to select, B to move up in scope.")); AddWidget(path, "Menu Background Opacity", WIDGET_CVAR_SLIDER_FLOAT) .CVar(CVAR_SETTING("Menu.BackgroundOpacity")) + .RaceDisable(false) .Options(FloatSliderOptions().DefaultValue(0.85f).IsPercentage().Tooltip( "Sets the opacity of the background of the port menu.")); AddWidget(path, "General Settings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Cursor Always Visible", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("CursorVisibility")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { Ship::Context::GetInstance()->GetWindow()->SetForceCursorVisibility( CVarGetInteger(CVAR_SETTING("CursorVisibility"), 0)); @@ -122,6 +126,7 @@ void SohMenu::AddMenuSettings() { #endif AddWidget(path, "Search In Sidebar", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("Menu.SidebarSearch")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { if (CVarGetInteger(CVAR_SETTING("Menu.SidebarSearch"), 0)) { mSohMenu->InsertSidebarSearch(); @@ -133,13 +138,16 @@ void SohMenu::AddMenuSettings() { "Displays the Search menu as a sidebar entry in Settings instead of in the header.")); AddWidget(path, "Search Input Autofocus", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("Menu.SearchAutofocus")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip( "Search input box gets autofocus when visible. Does not affect using other widgets.")); AddWidget(path, "Alt Assets Tab hotkey", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("Mods.AlternateAssetsHotkey")) + .RaceDisable(false) .Options( CheckboxOptions().Tooltip("Allows pressing the Tab key to toggle alternate assets").DefaultValue(true)); AddWidget(path, "Open App Files Folder", WIDGET_BUTTON) + .RaceDisable(false) .Callback([](WidgetInfo& info) { std::string filesPath = Ship::Context::GetInstance()->GetAppDirectoryPath(); SDL_OpenURL(std::string("file:///" + std::filesystem::absolute(filesPath).string()).c_str()); @@ -149,6 +157,7 @@ void SohMenu::AddMenuSettings() { AddWidget(path, "Boot", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Boot Sequence", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_SETTING("BootSequence")) + .RaceDisable(false) .Options(ComboboxOptions() .DefaultIndex(BOOTSEQUENCE_DEFAULT) .LabelPosition(LabelPositions::Far) @@ -160,9 +169,12 @@ void SohMenu::AddMenuSettings() { "File Select: Skip to file select menu")); AddWidget(path, "Languages", WIDGET_SEPARATOR_TEXT); - AddWidget(path, "Translate Title Screen", WIDGET_CVAR_CHECKBOX).CVar(CVAR_SETTING("TitleScreenTranslation")); + AddWidget(path, "Translate Title Screen", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_SETTING("TitleScreenTranslation")) + .RaceDisable(false); AddWidget(path, "Language", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_SETTING("Languages")) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { auto options = std::static_pointer_cast(info.options); SohMenu::UpdateLanguageMap(options->comboMap); @@ -176,14 +188,17 @@ void SohMenu::AddMenuSettings() { #if defined(_WIN32) || defined(__APPLE__) AddWidget(path, "Text to Speech", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("A11yTTS")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Enables text to speech for in game dialog")); #endif AddWidget(path, "Disable Idle Camera Re-Centering", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("A11yDisableIdleCam")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Disables the automatic re-centering of the camera when idle.")); AddWidget(path, "EXPERIMENTAL", WIDGET_SEPARATOR_TEXT).Options(TextOptions().Color(Colors::Orange)); AddWidget(path, "ImGui Menu Scaling", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_SETTING("ImGuiScale")) + .RaceDisable(false) .Options(ComboboxOptions() .ComboMap(imguiScaleOptions) .Tooltip("Changes the scaling of the ImGui menu elements.") @@ -214,9 +229,11 @@ void SohMenu::AddMenuSettings() { AddWidget(path, "Master Volume: %d %%", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_SETTING("Volume.Master")) + .RaceDisable(false) .Options(IntSliderOptions().Min(0).Max(100).DefaultValue(40).ShowButtons(true).Format("")); AddWidget(path, "Main Music Volume: %d %%", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_SETTING("Volume.MainMusic")) + .RaceDisable(false) .Options(IntSliderOptions().Min(0).Max(100).DefaultValue(100).ShowButtons(true).Format("")) .Callback([](WidgetInfo& info) { Audio_SetGameVolume(SEQ_PLAYER_BGM_MAIN, @@ -224,6 +241,7 @@ void SohMenu::AddMenuSettings() { }); AddWidget(path, "Sub Music Volume: %d %%", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_SETTING("Volume.SubMusic")) + .RaceDisable(false) .Options(IntSliderOptions().Min(0).Max(100).DefaultValue(100).ShowButtons(true).Format("")) .Callback([](WidgetInfo& info) { Audio_SetGameVolume(SEQ_PLAYER_BGM_SUB, @@ -231,6 +249,7 @@ void SohMenu::AddMenuSettings() { }); AddWidget(path, "Fanfare Volume: %d %%", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_SETTING("Volume.Fanfare")) + .RaceDisable(false) .Options(IntSliderOptions().Min(0).Max(100).DefaultValue(100).ShowButtons(true).Format("")) .Callback([](WidgetInfo& info) { Audio_SetGameVolume(SEQ_PLAYER_FANFARE, @@ -238,11 +257,12 @@ void SohMenu::AddMenuSettings() { }); AddWidget(path, "Sound Effects Volume: %d %%", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_SETTING("Volume.SFX")) + .RaceDisable(false) .Options(IntSliderOptions().Min(0).Max(100).DefaultValue(100).ShowButtons(true).Format("")) .Callback([](WidgetInfo& info) { Audio_SetGameVolume(SEQ_PLAYER_SFX, ((float)CVarGetInteger(CVAR_SETTING("Volume.SFX"), 100) / 100.0f)); }); - AddWidget(path, "Audio API (Needs reload)", WIDGET_AUDIO_BACKEND); + AddWidget(path, "Audio API (Needs reload)", WIDGET_AUDIO_BACKEND).RaceDisable(false); // Graphics Settings static int32_t maxFps = 360; @@ -253,10 +273,12 @@ void SohMenu::AddMenuSettings() { AddSidebarEntry("Settings", "Graphics", 3); AddWidget(path, "Graphics Options", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Toggle Fullscreen", WIDGET_BUTTON) + .RaceDisable(false) .Callback([](WidgetInfo& info) { Ship::Context::GetInstance()->GetWindow()->ToggleFullscreen(); }) .Options(ButtonOptions().Tooltip("Toggles Fullscreen On/Off.")); AddWidget(path, "Internal Resolution", WIDGET_CVAR_SLIDER_FLOAT) .CVar(CVAR_INTERNAL_RESOLUTION) + .RaceDisable(false) .Callback([](WidgetInfo& info) { Ship::Context::GetInstance()->GetWindow()->SetResolutionMultiplier( CVarGetFloat(CVAR_INTERNAL_RESOLUTION, 1)); @@ -281,6 +303,7 @@ void SohMenu::AddMenuSettings() { #ifndef __WIIU__ AddWidget(path, "Anti-aliasing (MSAA)", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_MSAA_VALUE) + .RaceDisable(false) .Callback([](WidgetInfo& info) { Ship::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger(CVAR_MSAA_VALUE, 1)); }) @@ -297,6 +320,7 @@ void SohMenu::AddMenuSettings() { const char* fpsFormat = fps == 20 ? "Original (%d)" : "%d"; AddWidget(path, "Current FPS", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_SETTING("InterpolationFPS")) + .RaceDisable(false) .Callback([](WidgetInfo& info) { auto options = std::static_pointer_cast(info.options); int32_t defaultValue = options->defaultValue; @@ -313,28 +337,33 @@ void SohMenu::AddMenuSettings() { .Options(IntSliderOptions().Tooltip(tooltip).Min(20).Max(maxFps).DefaultValue(20).Format(fpsFormat)); AddWidget(path, "Match Refresh Rate", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SETTING("MatchRefreshRate")) + .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Matches interpolation value to the refresh rate of your display.")); - AddWidget(path, "Renderer API (Needs reload)", WIDGET_VIDEO_BACKEND); + AddWidget(path, "Renderer API (Needs reload)", WIDGET_VIDEO_BACKEND).RaceDisable(false); AddWidget(path, "Enable Vsync", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_VSYNC_ENABLED) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_NO_VSYNC).active; }) .Options(CheckboxOptions() .Tooltip("Removes tearing, but clamps your max FPS to your displays refresh rate.") .DefaultValue(true)); AddWidget(path, "Windowed Fullscreen", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_SDL_WINDOWED_FULLSCREEN) + .RaceDisable(false) .PreFunc([](WidgetInfo& info) { info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_NO_WINDOWED_FULLSCREEN).active; }) .Options(CheckboxOptions().Tooltip("Enables Windowed Fullscreen Mode.")); AddWidget(path, "Allow multi-windows", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENABLE_MULTI_VIEWPORTS) + .RaceDisable(false) .PreFunc( [](WidgetInfo& info) { info.isHidden = mSohMenu->disabledMap.at(DISABLE_FOR_NO_MULTI_VIEWPORT).active; }) .Options(CheckboxOptions().Tooltip( "Allows multiple windows to be opened at once. Requires a reload to take effect.")); AddWidget(path, "Texture Filter (Needs reload)", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_TEXTURE_FILTER) + .RaceDisable(false) .Options(ComboboxOptions().Tooltip("Sets the applied Texture Filtering.").ComboMap(textureFilteringMap)); path.column = SECTION_COLUMN_2; @@ -347,6 +376,7 @@ void SohMenu::AddMenuSettings() { AddWidget(path, "Controller Bindings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Popout Bindings Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("ControllerConfiguration")) + .RaceDisable(false) .WindowName("Configure Controller") .Options(WindowButtonOptions().Tooltip("Enables the separate Bindings Window.")); @@ -356,12 +386,14 @@ void SohMenu::AddMenuSettings() { AddWidget(path, "Input Viewer", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Toggle Input Viewer", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("InputViewer")) + .RaceDisable(false) .WindowName("Input Viewer") .Options(WindowButtonOptions().Tooltip("Toggles the Input Viewer.").EmbedWindow(false)); AddWidget(path, "Input Viewer Settings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Popout Input Viewer Settings", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("InputViewerSettings")) + .RaceDisable(false) .WindowName("Input Viewer Settings") .Options(WindowButtonOptions().Tooltip("Enables the separate Input Viewer Settings Window.")); @@ -371,12 +403,14 @@ void SohMenu::AddMenuSettings() { AddSidebarEntry("Settings", path.sidebarName, 3); AddWidget(path, "Position", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_SETTING("Notifications.Position")) + .RaceDisable(false) .Options(ComboboxOptions() .Tooltip("Which corner of the screen notifications appear in.") .ComboMap(notificationPosition) .DefaultIndex(3)); AddWidget(path, "Duration (seconds):", WIDGET_CVAR_SLIDER_FLOAT) .CVar(CVAR_SETTING("Notifications.Duration")) + .RaceDisable(false) .Options(FloatSliderOptions() .Tooltip("How long notifications are displayed for.") .Format("%.1f") @@ -386,12 +420,14 @@ void SohMenu::AddMenuSettings() { .DefaultValue(10.0f)); AddWidget(path, "Background Opacity", WIDGET_CVAR_SLIDER_FLOAT) .CVar(CVAR_SETTING("Notifications.BgOpacity")) + .RaceDisable(false) .Options(FloatSliderOptions() .Tooltip("How opaque the background of notifications is.") .DefaultValue(0.5f) .IsPercentage()); AddWidget(path, "Size:", WIDGET_CVAR_SLIDER_FLOAT) .CVar(CVAR_SETTING("Notifications.Size")) + .RaceDisable(false) .Options(FloatSliderOptions() .Tooltip("How large notifications are.") .Format("%.1f") @@ -400,6 +436,7 @@ void SohMenu::AddMenuSettings() { .Max(5.0f) .DefaultValue(1.8f)); AddWidget(path, "Test Notification", WIDGET_BUTTON) + .RaceDisable(false) .Callback([](WidgetInfo& info) { Notification::Emit({ .itemIcon = "__OTR__textures/icon_item_24_static/gQuestIconGoldSkulltulaTex", From 5ae8ce4eacda726abd944397a8b4e9c0602a730a Mon Sep 17 00:00:00 2001 From: Archez Date: Mon, 14 Apr 2025 19:11:36 -0400 Subject: [PATCH 053/157] Fix Entrance Rando weather override behavior (#5403) * Add AfterSceneCommands hook * move ER weather override to AfterSceneCommands --- .../game-interactor/GameInteractor_HookTable.h | 1 + .../game-interactor/GameInteractor_Hooks.cpp | 8 +++++++- .../game-interactor/GameInteractor_Hooks.h | 1 + .../Enhancements/randomizer/hook_handlers.cpp | 16 ++++++++++++---- soh/soh/z_play_otr.cpp | 5 +++-- 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 44d0e643a6b..ecd970b3120 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -15,6 +15,7 @@ DEFINE_HOOK(OnItemReceive, (GetItemEntry itemEntry)); DEFINE_HOOK(OnSaleEnd, (GetItemEntry itemEntry)); DEFINE_HOOK(OnTransitionEnd, (int16_t sceneNum)); DEFINE_HOOK(OnSceneInit, (int16_t sceneNum)); +DEFINE_HOOK(AfterSceneCommands, (int16_t sceneNum)); DEFINE_HOOK(OnSceneFlagSet, (int16_t sceneNum, int16_t flagType, int16_t flag)); DEFINE_HOOK(OnSceneFlagUnset, (int16_t sceneNum, int16_t flagType, int16_t flag)); DEFINE_HOOK(OnFlagSet, (int16_t flagType, int16_t flag)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 9231da8438b..5e52bed7153 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -45,12 +45,18 @@ void GameInteractor_ExecuteOnTransitionEndHooks(int16_t sceneNum) { GameInteractor::Instance->ExecuteHooksForFilter(sceneNum); } -void GameInteractor_ExecuteOnSceneInitHooks(int16_t sceneNum) { +void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum) { GameInteractor::Instance->ExecuteHooks(sceneNum); GameInteractor::Instance->ExecuteHooksForID(sceneNum, sceneNum); GameInteractor::Instance->ExecuteHooksForFilter(sceneNum); } +void GameInteractor_ExecuteAfterSceneCommands(int16_t sceneNum) { + GameInteractor::Instance->ExecuteHooks(sceneNum); + GameInteractor::Instance->ExecuteHooksForID(sceneNum, sceneNum); + GameInteractor::Instance->ExecuteHooksForFilter(sceneNum); +} + void GameInteractor_ExecuteOnSceneFlagSet(int16_t sceneNum, int16_t flagType, int16_t flag) { GameInteractor::Instance->ExecuteHooks(sceneNum, flagType, flag); GameInteractor::Instance->ExecuteHooksForFilter(sceneNum, flagType, flag); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 33c8d2681bf..b1f9195b4bb 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -18,6 +18,7 @@ void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry); void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry); void GameInteractor_ExecuteOnTransitionEndHooks(int16_t sceneNum); void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum); +void GameInteractor_ExecuteAfterSceneCommands(int16_t sceneNum); void GameInteractor_ExecuteOnSceneFlagSet(int16_t sceneNum, int16_t flagType, int16_t flag); void GameInteractor_ExecuteOnSceneFlagUnset(int16_t sceneNum, int16_t flagType, int16_t flag); void GameInteractor_ExecuteOnFlagSet(int16_t flagType, int16_t flag); diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 4ea81a9436f..bd267aebe68 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -1761,10 +1761,6 @@ void RandomizerOnSceneInitHandler(int16_t sceneNum) { // Handle updated link spawn positions Entrance_OverrideSpawnScene(sceneNum, gPlayState->curSpawn); - - Entrance_OverrideWeatherState(); - // Need to reinitialize the environment after replacing the weather mode - Play_InitEnvironment(gPlayState, gPlayState->skyboxId); } // LACs & Prelude checks @@ -1803,6 +1799,13 @@ void RandomizerOnSceneInitHandler(int16_t sceneNum) { }); } +void RandomizerAfterSceneCommandsHandler(int16_t sceneNum) { + // ENTRTODO: Move all entrance rando handling to a dedicated file + if (RAND_GET_OPTION(RSK_SHUFFLE_ENTRANCES)) { + Entrance_OverrideWeatherState(); + } +} + void EnSi_DrawRandomizedItem(EnSi* enSi, PlayState* play) { GetItemEntry randoItem = enSi->sohGetItemEntry; if (CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("MysteriousShuffle"), 0)) { @@ -2358,6 +2361,7 @@ void RandomizerRegisterHooks() { static uint32_t onDialogMessageHook = 0; static uint32_t onVanillaBehaviorHook = 0; static uint32_t onSceneInitHook = 0; + static uint32_t afterSceneCommandsHook = 0; static uint32_t onActorInitHook = 0; static uint32_t onActorUpdateHook = 0; static uint32_t onPlayerUpdateHook = 0; @@ -2394,6 +2398,7 @@ void RandomizerRegisterHooks() { GameInteractor::Instance->UnregisterGameHook(onDialogMessageHook); GameInteractor::Instance->UnregisterGameHook(onVanillaBehaviorHook); GameInteractor::Instance->UnregisterGameHook(onSceneInitHook); + GameInteractor::Instance->UnregisterGameHook(afterSceneCommandsHook); GameInteractor::Instance->UnregisterGameHook(onActorInitHook); GameInteractor::Instance->UnregisterGameHook(onActorUpdateHook); GameInteractor::Instance->UnregisterGameHook(onPlayerUpdateHook); @@ -2426,6 +2431,7 @@ void RandomizerRegisterHooks() { onDialogMessageHook = 0; onVanillaBehaviorHook = 0; onSceneInitHook = 0; + afterSceneCommandsHook = 0; onActorInitHook = 0; onActorUpdateHook = 0; onPlayerUpdateHook = 0; @@ -2477,6 +2483,8 @@ void RandomizerRegisterHooks() { RandomizerOnVanillaBehaviorHandler); onSceneInitHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnSceneInitHandler); + afterSceneCommandsHook = GameInteractor::Instance->RegisterGameHook( + RandomizerAfterSceneCommandsHandler); onActorInitHook = GameInteractor::Instance->RegisterGameHook(RandomizerOnActorInitHandler); onActorUpdateHook = diff --git a/soh/soh/z_play_otr.cpp b/soh/soh/z_play_otr.cpp index 54f4b00169b..23e61261c7b 100644 --- a/soh/soh/z_play_otr.cpp +++ b/soh/soh/z_play_otr.cpp @@ -3,7 +3,7 @@ #include #include "soh/resource/type/Scene.h" #include -#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "global.h" #include "vt.h" #include @@ -59,7 +59,7 @@ extern "C" void OTRPlay_SpawnScene(PlayState* play, s32 sceneId, s32 spawn) { osSyncPrintf("ROOM SIZE=%fK\n", roomSize / 1024.0f); - GameInteractor::Instance->ExecuteHooks(play->sceneNum); + GameInteractor_ExecuteOnSceneInit(play->sceneNum); SPDLOG_INFO("Scene Init - sceneNum: {0:#x}, entranceIndex: {1:#x}", play->sceneNum, gSaveContext.entranceIndex); } @@ -79,6 +79,7 @@ void OTRPlay_InitScene(PlayState* play, s32 spawn) { YREG(15) = 0; gSaveContext.worldMapArea = 0; OTRScene_ExecuteCommands(play, (SOH::Scene*)play->sceneSegment); + GameInteractor_ExecuteAfterSceneCommands(play->sceneNum); Play_InitEnvironment(play, play->skyboxId); /* auto data = static_cast(Ship::Context::GetInstance() ->GetResourceManager() From 02ef33393aa26e3fd154dd3eb23f060332975e10 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Tue, 15 Apr 2025 10:13:30 -0700 Subject: [PATCH 054/157] Implements a rand inf for Ruto's letter to apply to logic to track it separate from the underwater item check. (#5405) --- soh/soh/Enhancements/randomizer/logic.cpp | 6 +++--- soh/soh/Enhancements/randomizer/randomizer_inf.h | 1 + soh/src/code/z_parameter.c | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 9fd460d93fc..0c4ed2e89fd 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -121,8 +121,6 @@ bool Logic::HasItem(RandomizerGet itemName) { case RG_STONE_OF_AGONY: case RG_GERUDO_MEMBERSHIP_CARD: return CheckQuestItem(RandoGetToQuestItem.at(itemName)); - case RG_RUTOS_LETTER: - return CheckEventChkInf(EVENTCHKINF_OBTAINED_RUTOS_LETTER); case RG_DOUBLE_DEFENSE: return GetSaveContext()->isDoubleDefenseAcquired; case RG_FISHING_POLE: @@ -171,6 +169,7 @@ bool Logic::HasItem(RandomizerGet itemName) { case RG_BACK_TOWER_KEY: case RG_HYLIA_LAB_KEY: case RG_FISHING_HOLE_KEY: + case RG_RUTOS_LETTER: return CheckRandoInf(RandoGetToRandInf.at(itemName)); // Boss Keys case RG_EPONA: @@ -1455,6 +1454,7 @@ std::map Logic::RandoGetToEquipFlag = { std::map Logic::RandoGetToRandInf = { { RG_ZELDAS_LETTER, RAND_INF_ZELDAS_LETTER }, { RG_WEIRD_EGG, RAND_INF_WEIRD_EGG }, + { RG_RUTOS_LETTER, RAND_INF_OBTAINED_RUTOS_LETTER }, { RG_GOHMA_SOUL, RAND_INF_GOHMA_SOUL }, { RG_KING_DODONGO_SOUL, RAND_INF_KING_DODONGO_SOUL }, { RG_BARINADE_SOUL, RAND_INF_BARINADE_SOUL }, @@ -1819,7 +1819,7 @@ void Logic::ApplyItemEffect(Item& item, bool state) { mSaveContext->inventory.items[slot] = itemId; } break; case RG_RUTOS_LETTER: - SetEventChkInf(EVENTCHKINF_OBTAINED_RUTOS_LETTER, state); + SetRandoInf(RAND_INF_OBTAINED_RUTOS_LETTER, state); break; case RG_GOHMA_SOUL: case RG_KING_DODONGO_SOUL: diff --git a/soh/soh/Enhancements/randomizer/randomizer_inf.h b/soh/soh/Enhancements/randomizer/randomizer_inf.h index 048fcc86ca4..b6cb4d1ddd8 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_inf.h +++ b/soh/soh/Enhancements/randomizer/randomizer_inf.h @@ -1952,6 +1952,7 @@ typedef enum { RAND_INF_DEKU_TREE_QUEEN_GOHMA_GRASS_7, RAND_INF_DEKU_TREE_QUEEN_GOHMA_GRASS_8, // End Grass + RAND_INF_OBTAINED_RUTOS_LETTER, // If you add anything to this list, you need to update the size of randomizerInf in z64save.h to be // ceil(RAND_INF_MAX / 16) diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 2a6d3369dce..4ead0d2684d 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2411,6 +2411,9 @@ u8 Item_Give(PlayState* play, u8 item) { } } } else { + if (item == ITEM_LETTER_RUTO) { + Flags_SetRandomizerInf(RAND_INF_OBTAINED_RUTOS_LETTER); + } for (i = 0; i < 4; i++) { if (gSaveContext.inventory.items[temp + i] == ITEM_NONE) { gSaveContext.inventory.items[temp + i] = item; From 1b6dc13491460462f3a04e56b8fe1a28ada5d2a3 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Tue, 15 Apr 2025 14:27:55 -0400 Subject: [PATCH 055/157] Blair Charlie version bump (#5407) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dc479fb408..ec9f14f5b1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") -project(Ship VERSION 9.0.1 LANGUAGES C CXX) +project(Ship VERSION 9.0.2 LANGUAGES C CXX) include(CMake/soh-cvars.cmake) include(CMake/lus-cvars.cmake) From 29af294b0c1e67febd530d7f1342b78feec98556 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Thu, 17 Apr 2025 15:22:12 +0100 Subject: [PATCH 056/157] fix TODO_TRANSLATE language replacement in cases where the text is already formatted (#5415) * fix TODO_TRANSLATE language replacement in cases where the text is already formatted * oops --- soh/include/z64.h | 2 +- .../Enhancements/custom-message/CustomMessageManager.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/soh/include/z64.h b/soh/include/z64.h index 83e1498dba5..53b76e8fb07 100644 --- a/soh/include/z64.h +++ b/soh/include/z64.h @@ -544,7 +544,7 @@ typedef enum { LANGUAGE_MAX } Language; -#define TODO_TRANSLATE "__Translate_This__" +#define TODO_TRANSLATE "TranslateThis" // TODO get these properties from the textures themselves #define FONT_CHAR_TEX_WIDTH 16 diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp index 278b8db0c60..a9c19f11f98 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp @@ -155,12 +155,12 @@ const std::string CustomMessage::GetFrench(MessageFormat format) const { } const std::string CustomMessage::GetForCurrentLanguage(MessageFormat format) const { - return GetForLanguage(((Language)gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language, - format); + return GetForLanguage( + ((Language)gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : (Language)gSaveContext.language, format); } const std::string CustomMessage::GetForLanguage(uint8_t language, MessageFormat format) const { - std::string output = messages[language] != TODO_TRANSLATE ? messages[language] : messages[LANGUAGE_ENG]; + std::string output = !messages[language].starts_with(TODO_TRANSLATE) ? messages[language] : messages[LANGUAGE_ENG]; ProcessMessageFormat(output, format); return output; } From 3c4f38e2f02f6d16de6faf8ddb162033851aceb1 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 23 Apr 2025 01:16:16 +0100 Subject: [PATCH 057/157] make sure adult is spawned for big poe check (#5431) --- soh/soh/Enhancements/randomizer/3drando/fill.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index dcae4842c12..8885117c326 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -625,6 +625,8 @@ void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAcce if (ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF)) { Rando::StaticData::RetrieveItem(RG_GUARD_HOUSE_KEY).ApplyEffect(); } + RegionTable(RR_ROOT)->adultNight = true; + RegionTable(RR_ROOT)->adultDay = true; } else { ApplyAllAdvancmentItems(); } From 103a36e5fefd817f8254e43ee15e3083505bcff3 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 23 Apr 2025 04:38:14 +0100 Subject: [PATCH 058/157] fix MQGTG right side resetting (#5442) --- soh/soh/Enhancements/randomizer/logic.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 0c4ed2e89fd..2528064735f 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2498,6 +2498,7 @@ void Logic::Reset() { ForestOpenBossCorridor = false; ShadowTrialFirstChest = false; MQGTGMazeSwitch = false; + MQGTGRightSideSwitch = false; GTGPlatformSilverRupees = false; MQJabuHolesRoomDoor = false; JabuWestTentacle = false; From 8a8ea676babef7587e6ce12188ea4cf114850cde Mon Sep 17 00:00:00 2001 From: Malkierian Date: Tue, 22 Apr 2025 20:38:45 -0700 Subject: [PATCH 059/157] Prevents hidden button combo setting from hiding the personal notes window when window type is Window. (#5441) --- .../randomizer/randomizer_item_tracker.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index 9b7b1fd395e..5996fea1f5b 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -1532,8 +1532,9 @@ void ItemTrackerWindow::DrawElement() { if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Notes"), SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_MAIN_WINDOW && - CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == - TRACKER_DISPLAY_ALWAYS) { + (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING && + CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == + TRACKER_DISPLAY_ALWAYS)) { DrawNotes(); } EndFloatingWindows(); @@ -1642,7 +1643,10 @@ void ItemTrackerWindow::DrawElement() { if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Notes"), SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_SEPARATE && - CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_ALWAYS) { + (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_WINDOW || + (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING && + CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) != + TRACKER_DISPLAY_COMBO_BUTTON))) { ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver); BeginFloatingWindows("Personal Notes", ImGuiWindowFlags_NoFocusOnAppearing); DrawNotes(true); @@ -1943,7 +1947,10 @@ void ItemTrackerSettingsWindow::DrawElement() { shouldUpdateVectors = true; } - if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_ALWAYS) { + if (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_WINDOW || + (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING && + CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) != + TRACKER_DISPLAY_COMBO_BUTTON)) { if (CVarCombobox("Personal notes", CVAR_TRACKER_ITEM("DisplayType.Notes"), displayTypes, ComboboxOptions() .DefaultIndex(SECTION_DISPLAY_HIDDEN) From 3d3d9c5226c373ae19c37f6f4106e21b4879ef75 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:39:10 -0400 Subject: [PATCH 060/157] add some imgui ids in input editor (#5439) * add some imgui ids in input editor * clang format --- .../controls/SohInputEditorWindow.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp index 3f77d1ed4c8..8a8227073e2 100644 --- a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp +++ b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp @@ -642,10 +642,14 @@ void SohInputEditorWindow::DrawStickSection(uint8_t port, uint8_t stick, int32_t ImGui::SameLine(); ImGui::BeginGroup(); - DrawStickDirectionLine(ICON_FA_ARROW_UP, port, stick, Ship::UP, color); - DrawStickDirectionLine(ICON_FA_ARROW_DOWN, port, stick, Ship::DOWN, color); - DrawStickDirectionLine(ICON_FA_ARROW_LEFT, port, stick, Ship::LEFT, color); - DrawStickDirectionLine(ICON_FA_ARROW_RIGHT, port, stick, Ship::RIGHT, color); + DrawStickDirectionLine(StringHelper::Sprintf("%s##%d", ICON_FA_ARROW_UP, stick).c_str(), port, stick, Ship::UP, + color); + DrawStickDirectionLine(StringHelper::Sprintf("%s##%d", ICON_FA_ARROW_DOWN, stick).c_str(), port, stick, Ship::DOWN, + color); + DrawStickDirectionLine(StringHelper::Sprintf("%s##%d", ICON_FA_ARROW_LEFT, stick).c_str(), port, stick, Ship::LEFT, + color); + DrawStickDirectionLine(StringHelper::Sprintf("%s##%d", ICON_FA_ARROW_RIGHT, stick).c_str(), port, stick, + Ship::RIGHT, color); ImGui::EndGroup(); ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode(StringHelper::Sprintf("Analog Stick Options##%d", id).c_str())) { @@ -1335,12 +1339,12 @@ void SohInputEditorWindow::DrawOcarinaControlPanel() { ImGui::AlignTextToFramePadding(); ImGui::BulletText("Disable song detection"); - DrawButtonLine(ICON_FA_BAN, 0, BTN_CUSTOM_OCARINA_DISABLE_SONGS); + DrawButtonLine(ICON_FA_BAN "##DisableSongDetection", 0, BTN_CUSTOM_OCARINA_DISABLE_SONGS); ImGui::AlignTextToFramePadding(); ImGui::BulletText("Pitch"); - DrawButtonLine(ICON_FA_ARROW_UP, 0, BTN_CUSTOM_OCARINA_PITCH_UP); - DrawButtonLine(ICON_FA_ARROW_DOWN, 0, BTN_CUSTOM_OCARINA_PITCH_DOWN); + DrawButtonLine(ICON_FA_ARROW_UP "##Pitch", 0, BTN_CUSTOM_OCARINA_PITCH_UP); + DrawButtonLine(ICON_FA_ARROW_DOWN "##Pitch", 0, BTN_CUSTOM_OCARINA_PITCH_DOWN); if (!CVarGetInteger(CVAR_SETTING("CustomOcarina.Enabled"), 0)) { ImGui::EndDisabled(); From fec676bbf89109394b867e526841176843c33851 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Wed, 23 Apr 2025 17:29:44 -0400 Subject: [PATCH 061/157] Fix C-Down Position with Anchor Left (#5423) --- soh/src/code/z_parameter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 4ead0d2684d..ba9e1f70364 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -4112,7 +4112,8 @@ void Interface_DrawItemButtons(PlayState* play) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.UseMargins"), 0) != 0) { X_Margins_CD = Left_HUD_Margin; }; - C_Down_BTN_Pos[0] = (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosX"), 0) + X_Margins_CD); + C_Down_BTN_Pos[0] = + OTRGetDimensionFromLeftEdge(CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosX"), 0) + X_Margins_CD); } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosType"), 0) == ANCHOR_RIGHT) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.UseMargins"), 0) != 0) { X_Margins_CD = Right_HUD_Margin; From 17ed54dbc51cbb6937f7ba5c768d90360522d865 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Tue, 29 Apr 2025 17:20:04 -0400 Subject: [PATCH 062/157] add DC boss switch exception + tooltip (#5447) --- soh/soh/Enhancements/timesaver_hook_handlers.cpp | 4 ++++ soh/soh/SohGui/SohMenuEnhancements.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index b738e2fc16c..52302105049 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -275,6 +275,10 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li } switch (actor->id) { case ACTOR_OBJ_SWITCH: { + if (actor->params == 8224 && gPlayState->sceneNum == SCENE_DODONGOS_CAVERN && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) { + break; + } ObjSwitch* switchActor = (ObjSwitch*)actor; switchActor->cooldownTimer = 0; *should = false; diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 0b78892d48a..415c5a33f75 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -334,10 +334,10 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().DefaultValue(IS_RANDO)); AddWidget(path, "Exclude Glitch-Aiding Cutscenes", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding")) - .Options(CheckboxOptions().Tooltip( - "Don't skip cutscenes that are associated with useful glitches. Currently, it is " - "only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS, and the Box Skip One " - "Point in Jabu.")); + .Options( + CheckboxOptions().Tooltip("Don't skip cutscenes that are associated with useful glitches. Currently, it is " + "only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS, Dodongo Boss " + "Door Switch, and the Box Skip One Point in Jabu.")); AddWidget(path, "Text", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Skip Pickup Messages", WIDGET_CVAR_CHECKBOX) From d304e135b3300591896470e68778c88c938640fc Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 30 Apr 2025 02:33:43 -0600 Subject: [PATCH 063/157] Fix Ganon's Castle Max Level setting. --- soh/soh/SohGui/SohMenuLeveled.cpp | 2 +- soh/src/code/leveled_map_levels.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/soh/soh/SohGui/SohMenuLeveled.cpp b/soh/soh/SohGui/SohMenuLeveled.cpp index 08f7d3ac822..feadbe3eac9 100644 --- a/soh/soh/SohGui/SohMenuLeveled.cpp +++ b/soh/soh/SohGui/SohMenuLeveled.cpp @@ -142,7 +142,7 @@ namespace SohGui { .CVar("gLeveled.Difficulty.Enemy.MaxLevelInGanonCastle") .Options(CheckboxOptions() .Tooltip("If all tokens are collected, all enemies in Ganon's Casle are level 99.") - .DefaultValue(true)); + .DefaultValue(false)); // Enemy HP AddWidget(path, "Enemy HP: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) .CVar("gLeveled.Difficulty.Enemy.HPPercent") diff --git a/soh/src/code/leveled_map_levels.c b/soh/src/code/leveled_map_levels.c index ea1616b8043..97191a3e687 100644 --- a/soh/src/code/leveled_map_levels.c +++ b/soh/src/code/leveled_map_levels.c @@ -40,6 +40,7 @@ s8 Leveled_GetSceneLevel(s16 sceneId) { case SCENE_ICE_CAVERN: return 32; // Ice Cavern + case SCENE_INSIDE_GANONS_CASTLE: case SCENE_GANONS_TOWER: case SCENE_GANONDORF_BOSS: case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR: @@ -47,7 +48,7 @@ s8 Leveled_GetSceneLevel(s16 sceneId) { if (CVarGetInteger("gLeveled.Difficulty.Enemy.MaxLevelInGanonCastle", 0) == 1 && gSaveContext.inventory.gsTokens == 100) return 120; else - return 50; + return 52; case SCENE_GERUDO_TRAINING_GROUND: return 43; @@ -55,9 +56,6 @@ s8 Leveled_GetSceneLevel(s16 sceneId) { case SCENE_THIEVES_HIDEOUT: return 42; - case SCENE_INSIDE_GANONS_CASTLE: - return 52; - case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE: return 53; From a01645304262a91b8242885ef2cc1d1b5b77be9e Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Thu, 1 May 2025 21:59:37 -0400 Subject: [PATCH 064/157] Ganon's Tower barrier dispelled if cutscene is skipped (#5462) * Ganon's Tower barrier dispelled if cutscene is skipped * More format-compliant comment --- .../Enhancements/timesaver_hook_handlers.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 52302105049..cffa706432c 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -217,6 +217,25 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), IS_RANDO) && (entranceFlag != EVENTCHKINF_EPONA_OBTAINED) && entranceIndex != ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE) { *should = false; + + // Check for dispulsion of Ganon's Tower barrier + switch (entranceIndex) { + case ENTR_INSIDE_GANONS_CASTLE_2: + case ENTR_INSIDE_GANONS_CASTLE_3: + case ENTR_INSIDE_GANONS_CASTLE_4: + case ENTR_INSIDE_GANONS_CASTLE_5: + case ENTR_INSIDE_GANONS_CASTLE_6: + case ENTR_INSIDE_GANONS_CASTLE_7: + if (Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_FOREST_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_WATER_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_SHADOW_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_FIRE_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_LIGHT_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_SPIRIT_TRIAL)) { + Flags_SetEventChkInf(EVENTCHKINF_DISPELLED_GANONS_TOWER_BARRIER); + } + break; + } } break; } From 6c14311b66bc4398a440c1d4362bcfe707616ae9 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Thu, 1 May 2025 21:59:47 -0400 Subject: [PATCH 065/157] add dragon room CS (#5461) --- soh/soh/Enhancements/timesaver_hook_handlers.cpp | 5 +++-- soh/soh/SohGui/SohMenuEnhancements.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index cffa706432c..32de18552a4 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -294,8 +294,9 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li } switch (actor->id) { case ACTOR_OBJ_SWITCH: { - if (actor->params == 8224 && gPlayState->sceneNum == SCENE_DODONGOS_CAVERN && - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) { + if ((actor->params == 8224 && gPlayState->sceneNum == SCENE_DODONGOS_CAVERN) || + (actor->params == 6979 && gPlayState->sceneNum == SCENE_WATER_TEMPLE) && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) { break; } ObjSwitch* switchActor = (ObjSwitch*)actor; diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 415c5a33f75..9305c8ec548 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -334,10 +334,10 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().DefaultValue(IS_RANDO)); AddWidget(path, "Exclude Glitch-Aiding Cutscenes", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding")) - .Options( - CheckboxOptions().Tooltip("Don't skip cutscenes that are associated with useful glitches. Currently, it is " - "only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS, Dodongo Boss " - "Door Switch, and the Box Skip One Point in Jabu.")); + .Options(CheckboxOptions().Tooltip( + "Don't skip cutscenes that are associated with useful glitches. Currently, it is " + "only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS, Dodongo Boss " + "Door Switch CS, Water Temple Dragon Switch CS, and the Box Skip One Point in Jabu.")); AddWidget(path, "Text", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Skip Pickup Messages", WIDGET_CVAR_CHECKBOX) From baa91cbadbd357dfb3244fa53322b77f10161016 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Fri, 2 May 2025 03:00:22 +0100 Subject: [PATCH 066/157] Format map GI text (#5417) * Format map GI text * dum --- .../Enhancements/randomizer/3drando/hint_list.cpp | 12 ++++++------ soh/soh/Enhancements/randomizer/randomizer.cpp | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp index 90ddcef0650..72c90fa1997 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp @@ -2412,13 +2412,13 @@ void StaticData::HintTable_Init() { hintTextTable[RHT_ISOLATED_PLACE] = HintText(CustomMessage("an Isolated Place")); - hintTextTable[RHT_DUNGEON_ORDINARY] = HintText(CustomMessage(" It's ordinary.", - /*german*/ "&Sieht aus wie immer.", - /*french*/ "&Elle vous semble %rordinaire%w.")); + hintTextTable[RHT_DUNGEON_ORDINARY] = HintText(CustomMessage("&It's %gordinary%w.", + /*german*/ "&Sieht aus %gwie immer%w.", + /*french*/ "&Elle vous semble %gordinaire%w.")); - hintTextTable[RHT_DUNGEON_MASTERFUL] = HintText(CustomMessage(" It's masterful!", - /*german*/ "&Man kann darauf die Worte&%r\"Master Quest\"%w entziffern...", - /*french*/ "&Étrange... les mots %r\"Master&Quest\"%w sont gravés dessus.")); + hintTextTable[RHT_DUNGEON_MASTERFUL] = HintText(CustomMessage("&It's %rmasterful%w!", + /*german*/ "&Man kann darauf die Worte %r\"Master_Quest\"%w entziffern...", + /*french*/ "&Étrange... les mots %r\"Master_Quest\"%w sont gravés dessus.")); // clang-format on } diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index f1ecd608c8c..29d1c42936d 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -4515,6 +4515,9 @@ CustomMessage Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) { messageEntry.Replace("[[typeHint]]", Rando::StaticData::hintTextTable[RHT_DUNGEON_ORDINARY].GetHintMessage()); } + // BUG: the icon is not in the message yet so are not accounted for, so overflows are possible + messageEntry.AutoFormat(); + return messageEntry; } From bc3b17f4abdae665b0aafbd7b877a110efe90695 Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Thu, 1 May 2025 22:00:43 -0400 Subject: [PATCH 067/157] Add coloured text as current BGM indicator in Audio Editor (#5392) --- soh/soh/Enhancements/audio/AudioEditor.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp index 9865615bda4..4cf1f389d02 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.cpp +++ b/soh/soh/Enhancements/audio/AudioEditor.cpp @@ -265,6 +265,9 @@ void Draw_SfxTab(const std::string& tabId, SeqType type, const std::string& tabN } } + auto playingFromMenu = CVarGetInteger(CVAR_AUDIO("Playing"), 0); + auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); + // Longest text in Audio Editor ImVec2 columnSize = ImGui::CalcTextSize("Navi - Look/Hey/Watchout (Target Enemy)"); ImGui::BeginTable(tabId.c_str(), 3, ImGuiTableFlags_SizingFixedFit); @@ -291,10 +294,13 @@ void Draw_SfxTab(const std::string& tabId, SeqType type, const std::string& tabN const std::string lockedButton = ICON_FA_LOCK + hiddenKey; const std::string unlockedButton = ICON_FA_UNLOCK + hiddenKey; const int currentValue = CVarGetInteger(cvarKey.c_str(), defaultValue); + const bool isCurrentlyPlaying = currentValue == playingFromMenu || seqData.sequenceId == currentBGM; ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::Text("%s", seqData.label.c_str()); + ImGui::TextColored( + UIWidgets::ColorValues.at(isCurrentlyPlaying ? UIWidgets::Colors::Yellow : UIWidgets::Colors::White), "%s", + seqData.label.c_str()); ImGui::TableNextColumn(); ImGui::PushItemWidth(-FLT_MIN); const int initialValue = map.contains(currentValue) ? currentValue : defaultValue; From 19eb4f39abe6e10cbc603c3d2f2e407e6d173b80 Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Thu, 1 May 2025 23:27:20 -0400 Subject: [PATCH 068/157] Skip trial barrier dispel cutscenes (#5464) --- .../vanilla-behavior/GIVanillaBehavior.h | 8 +++++++ .../Enhancements/timesaver_hook_handlers.cpp | 21 +++++++++++++++++++ .../actors/ovl_Demo_Kekkai/z_demo_kekkai.c | 17 ++++++++------- .../actors/ovl_Demo_Kekkai/z_demo_kekkai.h | 2 +- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index d57d5029aa5..3eb7916a71b 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -1358,6 +1358,14 @@ typedef enum { // - `*BgTreemouth` VB_PLAY_DEKU_TREE_INTRO_CS, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*DemoKekkai` + VB_PLAY_DISPEL_BARRIER_CS, + // #### `result` // ```c // true diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 32de18552a4..0a9a77e40a1 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -19,6 +19,7 @@ extern "C" { #include "src/overlays/actors/ovl_En_Zl4/z_en_zl4.h" #include "src/overlays/actors/ovl_En_Box/z_en_box.h" #include "src/overlays/actors/ovl_Demo_Im/z_demo_im.h" +#include "src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h" #include "src/overlays/actors/ovl_En_Sa/z_en_sa.h" #include "src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.h" #include "src/overlays/actors/ovl_En_Tk/z_en_tk.h" @@ -465,6 +466,26 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li *should = false; } break; + case VB_PLAY_DISPEL_BARRIER_CS: { + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO)) { + static s16 trialEntrances[] = { + 0, + ENTR_INSIDE_GANONS_CASTLE_3, + ENTR_INSIDE_GANONS_CASTLE_6, + ENTR_INSIDE_GANONS_CASTLE_5, + ENTR_INSIDE_GANONS_CASTLE_4, + ENTR_INSIDE_GANONS_CASTLE_7, + ENTR_INSIDE_GANONS_CASTLE_2, + }; + RateLimitedSuccessChime(); + DemoKekkai* kekkai = va_arg(args, DemoKekkai*); + gPlayState->nextEntranceIndex = trialEntrances[kekkai->actor.params]; + gPlayState->transitionTrigger = TRANS_TRIGGER_START; + gPlayState->transitionType = TRANS_TYPE_FADE_BLACK; + *should = false; + } + break; + } case VB_OWL_INTERACTION: { if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), IS_RANDO) && *should) { EnOwl* enOwl = va_arg(args, EnOwl*); diff --git a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c index ad1c76a3dde..3d50bf3303b 100644 --- a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c +++ b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c @@ -7,6 +7,7 @@ #include "z_demo_kekkai.h" #include "objects/object_demo_kekkai/object_demo_kekkai.h" #include "scenes/dungeons/ganontika/ganontika_scene.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/ResourceManagerHelpers.h" #define FLAGS (ACTOR_FLAG_UPDATE_CULLING_DISABLED | ACTOR_FLAG_DRAW_CULLING_DISABLED) @@ -257,13 +258,15 @@ void DemoKekkai_TrialBarrierIdle(Actor* thisx, PlayState* play) { CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider1.base); CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider1.base); if (this->collider2.base.acFlags & AC_HIT) { - Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME); - // "I got it" - LOG_STRING("当ったよ"); - this->actor.update = DemoKekkai_TrialBarrierDispel; - this->timer = 0; - play->csCtx.segment = SEGMENTED_TO_VIRTUAL(sSageCutscenes[this->actor.params]); - gSaveContext.cutsceneTrigger = 1; + if (GameInteractor_Should(VB_PLAY_DISPEL_BARRIER_CS, true, this)) { + Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME); + // "I got it" + LOG_STRING("当ったよ"); + this->actor.update = DemoKekkai_TrialBarrierDispel; + this->timer = 0; + play->csCtx.segment = SEGMENTED_TO_VIRTUAL(sSageCutscenes[this->actor.params]); + gSaveContext.cutsceneTrigger = 1; + } } CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider2.base); func_8002F974(&this->actor, NA_SE_EV_TOWER_ENERGY - SFX_FLAG); diff --git a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h index 4e4a06c181b..a8df145c2fa 100644 --- a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h +++ b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h @@ -6,7 +6,7 @@ struct DemoKekkai; -typedef void (*DemoKekkaiUpdateFunc)(struct DemoKekkai* this, PlayState* play); +typedef void (*DemoKekkaiUpdateFunc)(struct DemoKekkai* thisx, PlayState* play); typedef struct DemoKekkai { /* 0x0000 */ Actor actor; From 6cbb298f76f2c1b809891eeb38c4d9d6110ecf82 Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Tue, 6 May 2025 20:01:36 -0400 Subject: [PATCH 069/157] Skip Kakariko and Hyrule Castle gate cutscenes (#5314) * Skip Kakariko gate cutscenes * Simplify a bit * Revert "Simplify a bit" This reverts commit ffa68c130fede6962bb4488b85c9ec61ccc60170. * Add Hyrule Castle gate skip * Add missing backtick * Redo simplifications * Run clang-format * Proper casting of clearCamera argument Co-authored-by: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> --------- Co-authored-by: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> --- .../vanilla-behavior/GIVanillaBehavior.h | 9 ++ .../Enhancements/timesaver_hook_handlers.cpp | 13 +++ .../actors/ovl_En_Heishi2/z_en_heishi2.c | 84 +++++++++++-------- 3 files changed, 70 insertions(+), 36 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 3eb7916a71b..0c11e3c9393 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -1430,6 +1430,15 @@ typedef enum { // - None VB_PLAY_FIRE_ARROW_CS, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*EnHeishi2` + // - `bool` (clearCamera - true if the code clears a sub-camera, false otherwise) + VB_PLAY_GATE_OPENING_OR_CLOSING_CS, + // #### `result` // ```c // true diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 0a9a77e40a1..13c14b0a64a 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -13,6 +13,7 @@ extern "C" { #include "src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.h" #include "src/overlays/actors/ovl_En_Owl/z_en_owl.h" #include "src/overlays/actors/ovl_En_Go2/z_en_go2.h" +#include "src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.h" #include "src/overlays/actors/ovl_En_Ko/z_en_ko.h" #include "src/overlays/actors/ovl_En_Ma1/z_en_ma1.h" #include "src/overlays/actors/ovl_En_Ru2/z_en_ru2.h" @@ -765,6 +766,18 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li break; } + case VB_PLAY_GATE_OPENING_OR_CLOSING_CS: { + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) { + EnHeishi2* enHeishi2 = va_arg(args, EnHeishi2*); + enHeishi2->unk_2F2[0] = 0; + + // The second argument determines whether the vanilla code should be run anyway. It + // should be set to `true` ONLY IF said code calls `Play_ClearCamera`, false otherwise. + bool clearCamera = (bool)va_arg(args, int); + *should = clearCamera && enHeishi2->cameraId != MAIN_CAM; + } + break; + } case VB_PLAY_RAINBOW_BRIDGE_CS: { if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) { *should = false; diff --git a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c index 61a10b056bd..192d749d493 100644 --- a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c +++ b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c @@ -314,19 +314,21 @@ void func_80A5372C(EnHeishi2* this, PlayState* play) { f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); - this->unk_2F2[0] = 200; - this->cameraId = Play_CreateSubCamera(play); - Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); - Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); - this->unk_280.x = 947.0f; - this->unk_280.y = 1195.0f; - this->unk_280.z = 2682.0f; - - this->unk_28C.x = 1164.0f; - this->unk_28C.y = 1145.0f; - this->unk_28C.z = 3014.0f; - - Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) { + this->unk_2F2[0] = 200; + this->cameraId = Play_CreateSubCamera(play); + Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); + Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); + this->unk_280.x = 947.0f; + this->unk_280.y = 1195.0f; + this->unk_280.z = 2682.0f; + + this->unk_28C.x = 1164.0f; + this->unk_28C.y = 1145.0f; + this->unk_28C.z = 3014.0f; + + Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + } this->actionFunc = func_80A53850; } @@ -334,11 +336,15 @@ void func_80A53850(EnHeishi2* this, PlayState* play) { BgSpot15Saku* gate; SkelAnime_Update(&this->skelAnime); - Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) { + Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + } gate = (BgSpot15Saku*)this->gate; if ((this->unk_2F2[0] == 0) || (gate->unk_168 == 0)) { - Play_ClearCamera(play, this->cameraId); - Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, true)) { + Play_ClearCamera(play, this->cameraId); + Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + } Message_CloseTextbox(play); this->unk_30C = 1; Player_SetCsActionWithHaltedActors(play, NULL, 7); @@ -479,23 +485,25 @@ void func_80A53DF8(EnHeishi2* this, PlayState* play) { f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); - this->unk_2F2[0] = 200; - this->cameraId = Play_CreateSubCamera(play); - Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); - Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); - this->unk_2BC.x = -71.0f; - this->unk_280.x = -71.0f; - this->unk_2BC.y = 571.0f; - this->unk_280.y = 571.0f; - this->unk_2BC.z = -1487.0f; - this->unk_280.z = -1487.0f; - this->unk_298.x = 181.0f; - this->unk_28C.x = 181.0f; - this->unk_298.y = 417.0f; - this->unk_28C.y = 417.0f; - this->unk_298.z = -1079.0f; - this->unk_28C.z = -1079.0f; - Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) { + this->unk_2F2[0] = 200; + this->cameraId = Play_CreateSubCamera(play); + Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); + Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); + this->unk_2BC.x = -71.0f; + this->unk_280.x = -71.0f; + this->unk_2BC.y = 571.0f; + this->unk_280.y = 571.0f; + this->unk_2BC.z = -1487.0f; + this->unk_280.z = -1487.0f; + this->unk_298.x = 181.0f; + this->unk_28C.x = 181.0f; + this->unk_298.y = 417.0f; + this->unk_28C.y = 417.0f; + this->unk_298.z = -1079.0f; + this->unk_28C.z = -1079.0f; + Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + } this->actionFunc = func_80A53F30; } @@ -503,11 +511,15 @@ void func_80A53F30(EnHeishi2* this, PlayState* play) { BgGateShutter* gate; SkelAnime_Update(&this->skelAnime); - Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) { + Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + } gate = (BgGateShutter*)this->gate; if ((this->unk_2F2[0] == 0) || (gate->openingState == 0)) { - Play_ClearCamera(play, this->cameraId); - Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, true)) { + Play_ClearCamera(play, this->cameraId); + Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + } if ((this->unk_30A != 2)) { if (this->unk_30A == 0) { this->actor.textId = 0x2015; From 804a24861b2dd888efbd4be57d65fb24ecbf0ad1 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Wed, 7 May 2025 17:45:42 -0400 Subject: [PATCH 070/157] add option description to LACS reward options (#5472) --- soh/soh/Enhancements/randomizer/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 6f355326179..6e494a6b5d5 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -250,7 +250,7 @@ void Settings::CreateOptions() { OPT_U8(RSK_LACS_REWARD_COUNT, "GCBK Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardCount"), "", WidgetType::Slider, 9, true); OPT_U8(RSK_LACS_DUNGEON_COUNT, "GCBK Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsDungeonCount"), "", WidgetType::Slider, 8, true); OPT_U8(RSK_LACS_TOKEN_COUNT, "GCBK Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsTokenCount"), "", WidgetType::Slider, 100, true); - OPT_U8(RSK_LACS_OPTIONS, "GCBK LACS Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), "", WidgetType::Combobox, RO_LACS_STANDARD_REWARD); + OPT_U8(RSK_LACS_OPTIONS, "GCBK LACS Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), mOptionDescriptions[RSK_LACS_OPTIONS], WidgetType::Combobox, RO_LACS_STANDARD_REWARD); OPT_U8(RSK_KEYRINGS, "Key Rings", {"Off", "Random", "Count", "Selection"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), mOptionDescriptions[RSK_KEYRINGS], WidgetType::Combobox, RO_KEYRINGS_OFF); OPT_U8(RSK_KEYRINGS_RANDOM_COUNT, "Keyring Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsRandomCount"), "", WidgetType::Slider, 8); OPT_U8(RSK_KEYRINGS_GERUDO_FORTRESS, "Gerudo Fortress Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGerudoFortress"), "", WidgetType::Combobox, 0); From f72085b6caff7817d5c5872885f6acdfb4aab8dc Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Tue, 13 May 2025 04:31:40 +0100 Subject: [PATCH 071/157] fix oversight in dragon room of MQ water (#5482) --- .../randomizer/location_access/dungeons/water_temple.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp index 0ae5ac28983..04c1279bd54 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp @@ -658,7 +658,7 @@ void RegionTable_Init_WaterTemple() { areaTable[RR_WATER_TEMPLE_MQ_DRAGON_ROOM_ALCOVE] = Region("Water Temple MQ Dragon Room Alcove", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->MQWaterDragonTorches, []{return true;}), + EventAccess(&logic->MQWaterDragonTorches, []{return logic->HasFireSource();}), }, { //Locations From e8c2db815129a8a3e93425c7f08384ce9d1d80ae Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Fri, 16 May 2025 03:51:14 +0100 Subject: [PATCH 072/157] remove all refernces to Doge (#5488) --- .../Enhancements/randomizer/randomizer.cpp | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 29d1c42936d..d3e2c83b5cb 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -65,7 +65,7 @@ const std::string Randomizer::NaviRandoMessageTableID = "RandomizerNavi"; const std::string Randomizer::IceTrapRandoMessageTableID = "RandomizerIceTrap"; const std::string Randomizer::randoMiscHintsTableID = "RandomizerMiscHints"; -static const char* englishRupeeNames[190] = { +static const char* englishRupeeNames[188] = { "[P]", "Bad RNG Rolls", "Baht", @@ -111,8 +111,6 @@ static const char* englishRupeeNames[190] = { "Dimes", "Dinars", "DNA", - "Doge", - "Dogecoin", "Doll Hairs", "Dollars", "Dollarydoos", @@ -258,25 +256,25 @@ static const char* englishRupeeNames[190] = { "Zorkmids", }; -static const char* germanRupeeNames[80] = { - "Baht", "Bananen", "Bitcoin", "Bonbons", "Bratwürste", "Brause UFOs", "Brötchen", "Cent", - "Diamanten", "Dinar", "Diridari", "Dogecoin", "Dollar", "Draken", "ECU", "Elexit", - "Erz", "Erzbrocken", "Euro", "EXP", "Forint", "Franken", "Freunde", "Gil", - "Gold", "Groschen", "Gulden", "Gummibären", "Heller", "Juwelen", "Karolin", "Kartoffeln", - "Kies", "Knete", "Knochen", "Kohle", "Kraniche", "Kreuzer", "Kronen", "Kronkorken", - "Kröten", "Lira", "Mark", "Mäuse", "Monde", "Moorhühner", "Moos", "Münzen", - "Naira", "Penunze", "Pesa", "Pfandflaschen", "Pfennig", "Pfund", "Pilze", "Plastiks", - "Pokédollar", "Radieschen", "Rand", "Rappen", "Real", "Rial", "Riyal", "Rubine", - "Rupien", "Saphire", "Schilling", "Seelen", "Septime", "Smaragde", "Steine", "Sterne", - "Sternis", "Tael", "Taler", "Wagenchips", "Won", "Yen", "Yuan", "Zenny", +static const char* germanRupeeNames[79] = { + "Baht", "Bananen", "Bitcoin", "Bonbons", "Bratwürste", "Brause UFOs", "Brötchen", "Cent", + "Diamanten", "Dinar", "Diridari", "Dollar", "Draken", "ECU", "Elexit", "Erz", + "Erzbrocken", "Euro", "EXP", "Forint", "Franken", "Freunde", "Gil", "Gold", + "Groschen", "Gulden", "Gummibären", "Heller", "Juwelen", "Karolin", "Kartoffeln", "Kies", + "Knete", "Knochen", "Kohle", "Kraniche", "Kreuzer", "Kronen", "Kronkorken", "Kröten", + "Lira", "Mark", "Mäuse", "Monde", "Moorhühner", "Moos", "Münzen", "Naira", + "Penunze", "Pesa", "Pfandflaschen", "Pfennig", "Pfund", "Pilze", "Plastiks", "Pokédollar", + "Radieschen", "Rand", "Rappen", "Real", "Rial", "Riyal", "Rubine", "Rupien", + "Saphire", "Schilling", "Seelen", "Septime", "Smaragde", "Steine", "Sterne", "Sternis", + "Tael", "Taler", "Wagenchips", "Won", "Yen", "Yuan", "Zenny", }; -static const char* frenchRupeeNames[40] = { - "Anneaux", "Baguettes", "Balles", "Bananes", "Bitcoin", "Blés", "Bling", "Capsules", - "Centimes", "Champignons", "Clochettes", "Crédits", "Croissants", "Diamants", "Dogecoin", "Dollars", - "Émeraudes", "Éthers", "Étoiles", "Euros", "Florens", "Francs", "Galds", "Gils", - "Grouses", "Halos", "Joyaux", "Lunes", "Mailles", "Munnies", "Orbes", "Orens", - "Pépètes", "Pièces", "Plastyks", "Pokédollars", "Pokémon", "Radis", "Rubis", "Zennies", +static const char* frenchRupeeNames[39] = { + "Anneaux", "Baguettes", "Balles", "Bananes", "Bitcoin", "Blés", "Bling", "Capsules", + "Centimes", "Champignons", "Clochettes", "Crédits", "Croissants", "Diamants", "Dollars", "Émeraudes", + "Éthers", "Étoiles", "Euros", "Florens", "Francs", "Galds", "Gils", "Grouses", + "Halos", "Joyaux", "Lunes", "Mailles", "Munnies", "Orbes", "Orens", "Pépètes", + "Pièces", "Plastyks", "Pokédollars", "Pokémon", "Radis", "Rubis", "Zennies", }; Randomizer::Randomizer() { From 4fd0bf402ee2cc354225f921d1f2e976bfa9f486 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Fri, 16 May 2025 18:07:52 -0400 Subject: [PATCH 073/157] set c standard to 17 (#5492) --- CMakeLists.txt | 1 + soh/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ec9f14f5b1e..b9a5afe4c0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.26.0 FATAL_ERROR) set(CMAKE_SYSTEM_VERSION 10.0 CACHE STRING "" FORCE) set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") +set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 7277b2dfbda..3511a0caf90 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -4,6 +4,7 @@ set(CMAKE_SYSTEM_VERSION 10.0 CACHE STRING "" FORCE) project(soh LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") +set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use") if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") enable_language(OBJCXX) From 6acabae38f9445b199979861bcadf432d37522a7 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Sat, 17 May 2025 02:11:04 -0400 Subject: [PATCH 074/157] Prevent another use of gSaveContext during Seed Generation (#5458) * Prevent BeanPlanted using gSaveContext for seed gen * address malk's comment about extern --------- Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com> --- soh/soh/Enhancements/randomizer/location_access.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index 6cf2aa2ab3b..ade8cbf7263 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -12,7 +12,6 @@ #include extern "C" { -extern SaveContext gSaveContext; extern PlayState* gPlayState; } @@ -275,7 +274,7 @@ bool BeanPlanted(const RandomizerRegion region) { if (gPlayState != nullptr && gPlayState->sceneNum == sceneID) { swch = gPlayState->actorCtx.flags.swch; } else if (sceneID != SCENE_ID_MAX) { - swch = gSaveContext.sceneFlags[sceneID].swch; + swch = Rando::Context::GetInstance()->GetLogic()->GetSaveContext()->sceneFlags[sceneID].swch; } else { swch = 0; } From 9cb65308584bb863d00c016d77210057905ea47a Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Sat, 17 May 2025 19:29:53 -0500 Subject: [PATCH 075/157] Fix Enable Available Checks from title screen. (#5502) --- soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 6c670e447d8..5346a31ef57 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -2114,7 +2114,10 @@ void CheckTrackerSettingsWindow::DrawElement() { "with your current progress.") .Color(THEME_COLOR))) { enableAvailableChecks = CVarGetInteger(CVAR_TRACKER_CHECK("EnableAvailableChecks"), 0); - RecalculateAvailableChecks(); + + if (GameInteractor::IsSaveLoaded(true)) { + RecalculateAvailableChecks(); + } } ImGui::EndDisabled(); From 66351fa4e4b23711bc80fd9fe3e00c2a05fbdb11 Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Sat, 17 May 2025 19:30:01 -0500 Subject: [PATCH 076/157] Remove freestanding key from Bottom of the Well Perimeter. (#5496) --- .../randomizer/location_access/dungeons/bottom_of_the_well.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp index 44c3fa8177c..8a8393ba18a 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp @@ -25,7 +25,6 @@ void RegionTable_Init_BottomOfTheWell() { }, { //Locations LOCATION(RC_BOTTOM_OF_THE_WELL_FRONT_CENTER_BOMBABLE_CHEST, logic->HasExplosives()), - LOCATION(RC_BOTTOM_OF_THE_WELL_FREESTANDING_KEY, (logic->HasItem(RG_BRONZE_SCALE) || logic->LoweredWaterInsideBotw) && logic->CanUse(RG_STICKS) || logic->CanUse(RG_DINS_FIRE)), LOCATION(RC_BOTTOM_OF_THE_WELL_UNDERWATER_FRONT_CHEST, logic->LoweredWaterInsideBotw), LOCATION(RC_BOTTOM_OF_THE_WELL_UNDERWATER_LEFT_CHEST, logic->LoweredWaterInsideBotw), LOCATION(RC_BOTTOM_OF_THE_WELL_NEAR_ENTRANCE_POT_1, logic->CanBreakPots()), From b30fff5d57500c65e8fe246fabb49c2303e74a60 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Sat, 17 May 2025 20:56:57 -0400 Subject: [PATCH 077/157] Skip Forest Temple Basement Pillars Cutscene (#5473) * vb forest pillar cs skip * change to one point cutscene skip * rm whitespace --- .../vanilla-behavior/GIVanillaBehavior.h | 8 ++++++++ soh/soh/Enhancements/timesaver_hook_handlers.cpp | 5 +++++ .../ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c | 13 ++++++++++--- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 0c11e3c9393..223001711a5 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -474,6 +474,14 @@ typedef enum { // - `*BgHeavyBlock` VB_FREEZE_LINK_FOR_BLOCK_THROW, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - None + VB_FREEZE_LINK_FOR_FOREST_PILLARS, + // #### `result` // ```c // true diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 13c14b0a64a..9f488efce10 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -375,6 +375,11 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li } break; } + case VB_FREEZE_LINK_FOR_FOREST_PILLARS: + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO)) { + *should = false; + } + break; case VB_SHOW_TITLE_CARD: if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), IS_RANDO)) { *should = false; diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c b/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c index 04aff53beaf..692497415e2 100644 --- a/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c @@ -6,6 +6,7 @@ #include "z_bg_mori_kaitenkabe.h" #include "objects/object_mori_objects/object_mori_objects.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS 0 @@ -97,7 +98,9 @@ void BgMoriKaitenkabe_Wait(BgMoriKaitenkabe* this, PlayState* play) { if ((this->timer > (28 - CVarGetInteger(CVAR_ENHANCEMENT("FasterBlockPush"), 0) * 4)) && !Player_InCsMode(play)) { BgMoriKaitenkabe_SetupRotate(this); - Player_SetCsActionWithHaltedActors(play, &this->dyna.actor, 8); + if (GameInteractor_Should(VB_FREEZE_LINK_FOR_FOREST_PILLARS, true)) { + Player_SetCsActionWithHaltedActors(play, &this->dyna.actor, 8); + } Math_Vec3f_Copy(&this->lockedPlayerPos, &player->actor.world.pos); push.x = Math_SinS(this->dyna.unk_158); push.y = 0.0f; @@ -131,7 +134,9 @@ void BgMoriKaitenkabe_Rotate(BgMoriKaitenkabe* this, PlayState* play) { Math_StepToF(&this->rotSpeed, 0.6f, 0.02f); if (Math_StepToF(&this->rotYdeg, this->rotDirection * 45.0f, this->rotSpeed)) { BgMoriKaitenkabe_SetupWait(this); - Player_SetCsActionWithHaltedActors(play, thisx, 7); + if (GameInteractor_Should(VB_FREEZE_LINK_FOR_FOREST_PILLARS, true)) { + Player_SetCsActionWithHaltedActors(play, thisx, 7); + } if (this->rotDirection > 0.0f) { thisx->home.rot.y += 0x2000; } else { @@ -148,7 +153,9 @@ void BgMoriKaitenkabe_Rotate(BgMoriKaitenkabe* this, PlayState* play) { this->dyna.unk_150 = 0.0f; player->stateFlags2 &= ~PLAYER_STATE2_MOVING_DYNAPOLY; } - Math_Vec3f_Copy(&player->actor.world.pos, &this->lockedPlayerPos); + if (GameInteractor_Should(VB_FREEZE_LINK_FOR_FOREST_PILLARS, true)) { + Math_Vec3f_Copy(&player->actor.world.pos, &this->lockedPlayerPos); + } } void BgMoriKaitenkabe_Update(Actor* thisx, PlayState* play) { From e0d5fbec42d270a17e0b14c4a1d836136c6fe66e Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Sat, 17 May 2025 20:05:20 -0500 Subject: [PATCH 078/157] Available Checks Prices (#5446) * Improved the item location price availability. * Moved the available checks price logic into location_access.cpp. * Fixed typo and clarified check status identified. --- .../Enhancements/randomizer/3drando/fill.cpp | 2 +- .../Enhancements/randomizer/3drando/fill.hpp | 2 +- .../randomizer/location_access.cpp | 66 +++++++++++++++++-- .../Enhancements/randomizer/location_access.h | 3 +- .../randomizer/randomizer_check_tracker.cpp | 11 +--- 5 files changed, 67 insertions(+), 17 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 8885117c326..94b495639da 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -421,7 +421,7 @@ bool AddCheckToLogic(LocationAccess& locPair, GetAccessibleLocationsStruct& gals Rando::ItemLocation* location = ctx->GetItemLocation(loc); RandomizerGet locItem = location->GetPlacedRandomizerGet(); - if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, gals.calculatingAvailableChecks)) { + if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion)) { if (gals.calculatingAvailableChecks) { gals.accessibleLocations.push_back(loc); StopPerformanceTimer(PT_LOCATION_LOGIC); diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.hpp b/soh/soh/Enhancements/randomizer/3drando/fill.hpp index bb013cc1395..a9b864ce2aa 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.hpp @@ -70,4 +70,4 @@ void GeneratePlaythrough(); bool CheckBeatable(RandomizerGet ignore=RG_NONE); -void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAccess); \ No newline at end of file +void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAccess); diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index ade8cbf7263..e9da7568e5b 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -10,7 +10,9 @@ #include "soh/Enhancements/debugger/performanceTimer.h" #include +#include +#include "3drando/shops.hpp" extern "C" { extern PlayState* gPlayState; } @@ -31,7 +33,7 @@ bool LocationAccess::CheckConditionAtAgeTime(bool& age, bool& time) const { return GetConditionsMet(); } -bool LocationAccess::ConditionsMet(Region* parentRegion, bool calculatingAvailableChecks) const { +bool LocationAccess::ConditionsMet(Region* parentRegion) const { // WARNING enterance 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 @@ -44,17 +46,71 @@ bool LocationAccess::ConditionsMet(Region* parentRegion, bool calculatingAvailab conditionsMet = true; } - return conditionsMet && - (calculatingAvailableChecks || CanBuy()); // TODO: run CanBuy when price is known due to settings + return conditionsMet && CanBuy(); +} + +static uint16_t GetMinimumPrice(const Rando::Location* loc) { + extern PriceSettingsStruct shopsanityPrices; + extern PriceSettingsStruct scrubPrices; + extern PriceSettingsStruct merchantPrices; + PriceSettingsStruct priceSettings = loc->GetRCType() == RCTYPE_SHOP ? shopsanityPrices + : loc->GetRCType() == RCTYPE_SCRUB ? scrubPrices + : merchantPrices; + + auto ctx = Rando::Context::GetInstance(); + switch (ctx->GetOption(priceSettings.main).Get()) { + case RO_PRICE_VANILLA: + return loc->GetVanillaPrice(); + case RO_PRICE_CHEAP_BALANCED: + return 0; + case RO_PRICE_BALANCED: + return 0; + case RO_PRICE_FIXED: + return ctx->GetOption(priceSettings.fixedPrice).Get() * 5; + case RO_PRICE_RANGE: { + uint16_t range1 = ctx->GetOption(priceSettings.range1).Get() * 5; + uint16_t range2 = ctx->GetOption(priceSettings.range1).Get() * 5; + return range1 < range2 ? range1 : range2; + } + case RO_PRICE_SET_BY_WALLET: { + if (ctx->GetOption(priceSettings.noWallet).Get()) { + return 0; + } else if (ctx->GetOption(priceSettings.childWallet).Get()) { + return 1; + } else if (ctx->GetOption(priceSettings.adultWallet).Get()) { + return 100; + } else if (ctx->GetOption(priceSettings.giantWallet).Get()) { + return 201; + } else { + return 501; + } + } + default: + return 0; + } } bool LocationAccess::CanBuy() const { - return CanBuyAnother(location); + const auto& loc = Rando::StaticData::GetLocation(location); + const auto& itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(location); + + if (loc->GetRCType() == RCTYPE_SHOP || loc->GetRCType() == RCTYPE_SCRUB || loc->GetRCType() == RCTYPE_MERCHANT) { + // Checks should only be identified while playing + if (itemLoc->GetCheckStatus() != RCSHOW_IDENTIFIED) { + return CanBuyAnother(GetMinimumPrice(loc)); + } else { + return CanBuyAnother(itemLoc->GetPrice()); + } + } + + return true; } bool CanBuyAnother(RandomizerCheck rc) { - uint16_t price = ctx->GetItemLocation(rc)->GetPrice(); + return CanBuyAnother(ctx->GetItemLocation(rc)->GetPrice()); +} +bool CanBuyAnother(uint16_t price) { if (price > 500) { return logic->HasItem(RG_TYCOON_WALLET); } else if (price > 200) { diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 828c31a0834..8d6815089eb 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -84,7 +84,7 @@ class LocationAccess { bool CheckConditionAtAgeTime(bool& age, bool& time) const; - bool ConditionsMet(Region* parentRegion, bool calculatingAvailableChecks) const; + bool ConditionsMet(Region* parentRegion) const; RandomizerCheck GetLocation() const { return location; @@ -103,6 +103,7 @@ class LocationAccess { bool CanBuy() const; }; +bool CanBuyAnother(uint16_t price); bool CanBuyAnother(RandomizerCheck rc); namespace Rando { diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 5346a31ef57..8648c982666 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -1967,7 +1967,7 @@ void RecalculateAvailableChecks() { StartPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS); std::vector targetLocations; - targetLocations.reserve(RR_MAX); + targetLocations.reserve(RC_MAX); for (auto& location : Rando::StaticData::GetLocationTable()) { RandomizerCheck rc = location.GetRandomizerCheck(); Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); @@ -1979,15 +1979,8 @@ void RecalculateAvailableChecks() { std::vector availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true); for (auto& rc : availableChecks) { - const auto& location = Rando::StaticData::GetLocation(rc); const auto& itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); - if (location->GetRCType() == RCTYPE_SHOP && itemLocation->GetCheckStatus() == RCSHOW_IDENTIFIED) { - if (CanBuyAnother(rc)) { - itemLocation->SetAvailable(true); - } - } else { - itemLocation->SetAvailable(true); - } + itemLocation->SetAvailable(true); } totalChecksAvailable = 0; From 8e349429245aee3590b801ed04027cdb46695c28 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 18 May 2025 02:06:24 +0100 Subject: [PATCH 079/157] Toggle the vanilla flags instead of the rando flags when removing one time scrubs (#5504) --- soh/soh/Enhancements/randomizer/savefile.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index cb8c7eaef70..1bed22a714f 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -269,9 +269,9 @@ extern "C" void Randomizer_InitSaveFile() { // Remove One Time Scrubs with Scrubsanity off if (Randomizer_GetSettingValue(RSK_SHUFFLE_SCRUBS) == RO_SCRUBS_OFF) { - Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_BRIDGE); - Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_FRONT); - Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_HF_DEKU_SCRUB_GROTTO); + Flags_SetItemGetInf(ITEMGETINF_DEKU_SCRUB_HEART_PIECE); + Flags_SetInfTable(INFTABLE_BOUGHT_STICK_UPGRADE); + Flags_SetInfTable(INFTABLE_BOUGHT_NUT_UPGRADE); } int startingAge = OTRGlobals::Instance->gRandoContext->GetOption(RSK_SELECTED_STARTING_AGE).Get(); From ecad59e31fe134dea65f8da7b160b17692276011 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Sat, 17 May 2025 18:52:45 -0700 Subject: [PATCH 080/157] Add tooltip to Generate Seed button when disabled indicating the need to be on File Select. (#5509) --- soh/soh/Enhancements/randomizer/randomizer.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index d3e2c83b5cb..18a23df5312 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -3724,13 +3724,15 @@ void RandomizerSettingsWindow::DrawElement() { } UIWidgets::Spacer(0); - ImGui::BeginDisabled((gSaveContext.gameMode != GAMEMODE_FILE_SELECT) || GameInteractor::IsSaveLoaded()); - if (UIWidgets::Button("Generate Randomizer", - UIWidgets::ButtonOptions().Size(ImVec2(250.f, 0.f)).Color(THEME_COLOR))) { + UIWidgets::ButtonOptions options = UIWidgets::ButtonOptions().Size(ImVec2(250.f, 0.f)).Color(THEME_COLOR); + options.Disabled((gSaveContext.gameMode != GAMEMODE_FILE_SELECT) || GameInteractor::IsSaveLoaded()); + if (options.disabled) { + options.DisabledTooltip("Must be on File Select to generate a randomizer seed."); + } + if (UIWidgets::Button("Generate Randomizer", options)) { ctx->SetSpoilerLoaded(false); GenerateRandomizer(CVarGetInteger(CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), 0) ? seedString : ""); } - ImGui::EndDisabled(); ImGui::SameLine(); if (!CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) { From 53566c9a73a3f176824547af2f940085e5f425ae Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Sun, 18 May 2025 16:25:24 -0400 Subject: [PATCH 081/157] fix incorrect `__VA_ARGS__` use in `lusprintf` version of `osSyncPrintf` (#5510) --- soh/include/functions.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/include/functions.h b/soh/include/functions.h index 86e3bf77f6e..f29153a9ce5 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -15,7 +15,7 @@ extern "C" #include #if defined(INCLUDE_GAME_PRINTF) && defined(_DEBUG) -#define osSyncPrintf(fmt, ...) lusprintf(__FILE__, __LINE__, 0, fmt, __VA_ARGS__) +#define osSyncPrintf(fmt, ...) lusprintf(__FILE__, __LINE__, 0, fmt, ##__VA_ARGS__) #else #define osSyncPrintf(fmt, ...) osSyncPrintfUnused(fmt, ##__VA_ARGS__) #endif From f16e34e8b8f538725229c2d4ff9018e50596d7de Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 18 May 2025 22:25:18 +0100 Subject: [PATCH 082/157] fix and rename IsKeysanity to IsFireLoopLocked (#5515) * c * fix and rename IsKeysanity * add comment --- .../randomizer/location_access/dungeons/fire_temple.cpp | 4 ++-- soh/soh/Enhancements/randomizer/logic.cpp | 9 +++++---- soh/soh/Enhancements/randomizer/logic.h | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp index 8888cdac9be..70cf890b003 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp @@ -20,7 +20,7 @@ void RegionTable_Init_FireTemple() { //Exits Entrance(RR_FIRE_TEMPLE_ENTRYWAY, []{return true;}), Entrance(RR_FIRE_TEMPLE_NEAR_BOSS_ROOM, []{return logic->FireTimer() >= 24;}), - Entrance(RR_FIRE_TEMPLE_LOOP_ENEMIES, []{return Here(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->CanUse(RG_MEGATON_HAMMER);}) && (logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsKeysanity);}), + Entrance(RR_FIRE_TEMPLE_LOOP_ENEMIES, []{return Here(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->CanUse(RG_MEGATON_HAMMER);}) && (logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsFireLoopLocked);}), Entrance(RR_FIRE_TEMPLE_LOOP_EXIT, []{return true;}), Entrance(RR_FIRE_TEMPLE_BIG_LAVA_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 2) && logic->FireTimer() >= 24;}), }); @@ -43,7 +43,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_LOOP_ENEMIES] = Region("Fire Temple Loop Enemies", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsKeysanity;}), + Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsFireLoopLocked;}), Entrance(RR_FIRE_TEMPLE_LOOP_TILES, []{return Here(RR_FIRE_TEMPLE_LOOP_ENEMIES, []{return logic->CanKillEnemy(RE_TORCH_SLUG) && logic->CanKillEnemy(RE_FIRE_KEESE);});}), }); diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 2528064735f..7f4b7e1025e 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -1389,6 +1389,7 @@ bool Logic::SmallKeys(RandomizerRegion dungeon, uint8_t requiredAmountGlitchless static_cast(GlitchDifficulty::INTERMEDIATE) || GetDifficultyValueFromString(GlitchHover) >= static_cast(GlitchDifficulty::INTERMEDIATE))) { return FireTempleKeys >= requiredAmountGlitched; }*/ + // If the Fire Temple loop lock is removed, Small key Count is set to 1 before starting return GetSmallKeyCount(SCENE_FIRE_TEMPLE) >= requiredAmountGlitchless; case RR_WATER_TEMPLE: @@ -2336,9 +2337,9 @@ void Logic::Reset() { StartPerformanceTimer(PT_LOGIC_RESET); memset(inLogic, false, sizeof(inLogic)); // Settings-dependent variables - IsKeysanity = ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || - ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || - ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE); + IsFireLoopLocked = ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || + ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_OVERWORLD) || + ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANY_DUNGEON); // AmmoCanDrop = /*AmmoDrops.IsNot(AMMODROPS_NONE)*/ false; TODO: AmmoDrop setting @@ -2400,7 +2401,7 @@ void Logic::Reset() { // If not keysanity, start with 1 logical key to account for automatically unlocking the basement door in vanilla // FiT - if (!IsKeysanity && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) { + if (!IsFireLoopLocked && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) { SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1); } diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 4f966f5ded9..77c49d25feb 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -59,7 +59,7 @@ class Logic { bool LightTrialClear = false; // Logical keysanity - bool IsKeysanity = false; + bool IsFireLoopLocked = false; // Bottle Count uint8_t Bottles = 0; From f0e40fd1dc09b894c0ee6956ca2e4fc369436e85 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Sun, 18 May 2025 17:26:10 -0400 Subject: [PATCH 083/157] fix incorrect token count in messages when tokensanity is off (#5503) * fix incorrect token count in messages when tokensanity is off * Update OTRGlobals.cpp --- soh/soh/OTRGlobals.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 72030657e14..dcabfca7097 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2437,15 +2437,15 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { // animation until the text box auto-dismisses. // RANDOTODO: Implement a way to determine if an item came from a skulltula and // inject the auto-dismiss control code if it did. - if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 && - !(IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_TOKENS) != RO_TOKENSANITY_OFF)) { + bool gsTokensShuffled = Randomizer_GetSettingValue(RSK_SHUFFLE_TOKENS) != RO_TOKENSANITY_OFF; + if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 && !(IS_RANDO && gsTokensShuffled)) { textId = TEXT_GS_NO_FREEZE; } else { textId = TEXT_GS_FREEZE; } // In vanilla, GS token count is incremented prior to the text box displaying // In rando we need to bump the token count by one to show the correct count - s16 gsCount = gSaveContext.inventory.gsTokens + (IS_RANDO ? 1 : 0); + s16 gsCount = gSaveContext.inventory.gsTokens + ((IS_RANDO && gsTokensShuffled) ? 1 : 0); messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId, MF_FORMATTED); messageEntry.Replace("[[gsCount]]", std::to_string(gsCount)); } else if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 && From 4334a132e31ef12a1cccf2c77e7006ed9c54c88d Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 18 May 2025 22:28:55 +0100 Subject: [PATCH 084/157] fix the wrong codepath being used in CanBuy when generating seeds (#5514) --- soh/soh/Enhancements/randomizer/3drando/fill.cpp | 2 +- soh/soh/Enhancements/randomizer/location_access.cpp | 8 ++++---- soh/soh/Enhancements/randomizer/location_access.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 94b495639da..8885117c326 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -421,7 +421,7 @@ bool AddCheckToLogic(LocationAccess& locPair, GetAccessibleLocationsStruct& gals Rando::ItemLocation* location = ctx->GetItemLocation(loc); RandomizerGet locItem = location->GetPlacedRandomizerGet(); - if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion)) { + if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, gals.calculatingAvailableChecks)) { if (gals.calculatingAvailableChecks) { gals.accessibleLocations.push_back(loc); StopPerformanceTimer(PT_LOCATION_LOGIC); diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index e9da7568e5b..cdd684f0423 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -33,7 +33,7 @@ bool LocationAccess::CheckConditionAtAgeTime(bool& age, bool& time) const { return GetConditionsMet(); } -bool LocationAccess::ConditionsMet(Region* parentRegion) const { +bool LocationAccess::ConditionsMet(Region* parentRegion, bool calculatingAvailableChecks) const { // WARNING enterance 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 @@ -46,7 +46,7 @@ bool LocationAccess::ConditionsMet(Region* parentRegion) const { conditionsMet = true; } - return conditionsMet && CanBuy(); + return conditionsMet && CanBuy(calculatingAvailableChecks); } static uint16_t GetMinimumPrice(const Rando::Location* loc) { @@ -90,13 +90,13 @@ static uint16_t GetMinimumPrice(const Rando::Location* loc) { } } -bool LocationAccess::CanBuy() const { +bool LocationAccess::CanBuy(bool calculatingAvailableChecks) const { const auto& loc = Rando::StaticData::GetLocation(location); const auto& itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(location); if (loc->GetRCType() == RCTYPE_SHOP || loc->GetRCType() == RCTYPE_SCRUB || loc->GetRCType() == RCTYPE_MERCHANT) { // Checks should only be identified while playing - if (itemLoc->GetCheckStatus() != RCSHOW_IDENTIFIED) { + if (calculatingAvailableChecks && itemLoc->GetCheckStatus() != RCSHOW_IDENTIFIED) { return CanBuyAnother(GetMinimumPrice(loc)); } else { return CanBuyAnother(itemLoc->GetPrice()); diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 8d6815089eb..9c8df0e9bbc 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -84,7 +84,7 @@ class LocationAccess { bool CheckConditionAtAgeTime(bool& age, bool& time) const; - bool ConditionsMet(Region* parentRegion) const; + bool ConditionsMet(Region* parentRegion, bool calculatingAvailableChecks) const; RandomizerCheck GetLocation() const { return location; @@ -100,7 +100,7 @@ class LocationAccess { std::string condition_str; // Makes sure shop locations are buyable - bool CanBuy() const; + bool CanBuy(bool calculatingAvailableChecks) const; }; bool CanBuyAnother(uint16_t price); From 081f82875a8afbacc77d2baa1230040dc785266c Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Mon, 19 May 2025 00:24:57 -0400 Subject: [PATCH 085/157] fix link voice missing when hanging off ledges (#5506) * fix link voice missing when hanging off ledges * remove unused bitrate var * hardcoded but justified * format --- soh/soh/stubs.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/soh/soh/stubs.c b/soh/soh/stubs.c index 56013e56f3d..118b36e724f 100644 --- a/soh/soh/stubs.c +++ b/soh/soh/stubs.c @@ -145,7 +145,47 @@ void Audio_osWritebackDCache(void* mem, s32 size) { } s32 osAiSetFrequency(u32 freq) { - return 1; + // this is based off the math from the original method + /* + + s32 osAiSetFrequency(u32 frequency) { + u8 bitrate; + f32 dacRateF = ((f32)osViClock / frequency) + 0.5f; + u32 dacRate = dacRateF; + + if (dacRate < 132) { + return -1; + } + + bitrate = (dacRate / 66); + if (bitrate > 16) { + bitrate = 16; + } + + HW_REG(AI_DACRATE_REG, u32) = dacRate - 1; + HW_REG(AI_BITRATE_REG, u32) = bitrate - 1; + return osViClock / (s32)dacRate; + } + + */ + + // bitrate is unused + + // osViClock comes from + // #define VI_NTSC_CLOCK 48681812 /* Hz = 48.681812 MHz */ + // s32 osViClock = VI_NTSC_CLOCK; + + // frequency was originally 32000 + + // given all of that, dacRate is + // (u32)(((f32)48681812 / 32000) + 0.5f) + // which evaluates to 1521 (which is > 132) + + // this leaves us with a final calculation of + // 48681812 / 1521 + // which evaluates to 32006 + + return 32006; } void osInvalDCache(void* vaddr, s32 nbytes) { From 5bf3761a18160c298d29d98c6094840cb0a47eaa Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 21 May 2025 16:51:34 +0100 Subject: [PATCH 086/157] Fix Boss and Tower entryways skipping requirements in decoupled (#5484) * Fix boss and tower entryways in decoupled * Add patches to child boss room entryways * Apply fixes * remember to set ALL the new exits --- soh/soh/Enhancements/randomizer/entrance.cpp | 44 +++++++++---------- .../location_access/dungeons/deku_tree.cpp | 11 +++-- .../dungeons/dodongos_cavern.cpp | 11 +++-- .../location_access/dungeons/fire_temple.cpp | 6 +-- .../dungeons/forest_temple.cpp | 6 +-- .../dungeons/ganons_castle.cpp | 23 ++++------ .../dungeons/jabujabus_belly.cpp | 10 +++-- .../dungeons/shadow_temple.cpp | 6 +-- .../dungeons/spirit_temple.cpp | 6 +-- .../location_access/dungeons/water_temple.cpp | 6 +-- .../Enhancements/randomizer/randomizerTypes.h | 4 ++ 11 files changed, 73 insertions(+), 60 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/entrance.cpp b/soh/soh/Enhancements/randomizer/entrance.cpp index 40eaca21207..66da4ee18d4 100644 --- a/soh/soh/Enhancements/randomizer/entrance.cpp +++ b/soh/soh/Enhancements/randomizer/entrance.cpp @@ -1131,22 +1131,22 @@ int EntranceShuffler::ShuffleAllEntrances() { { { EntranceType::WarpSong, RR_PRELUDE_OF_LIGHT_WARP, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_WARP_PAD }, NO_RETURN_ENTRANCE }, - { { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, ENTR_DEKU_TREE_BOSS_ENTRANCE }, - { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY, ENTR_DEKU_TREE_BOSS_DOOR } }, - { { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE }, - { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, ENTR_DODONGOS_CAVERN_BOSS_DOOR } }, - { { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, ENTR_JABU_JABU_BOSS_ENTRANCE }, - { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, ENTR_JABU_JABU_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, ENTR_FOREST_TEMPLE_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, ENTR_FIRE_TEMPLE_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, ENTR_WATER_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, ENTR_WATER_TEMPLE_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, ENTR_SPIRIT_TEMPLE_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, ENTR_SHADOW_TEMPLE_BOSS_DOOR } }, + { { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, ENTR_DEKU_TREE_BOSS_ENTRANCE }, + { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT, ENTR_DEKU_TREE_BOSS_DOOR } }, + { { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE }, + { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT, ENTR_DODONGOS_CAVERN_BOSS_DOOR } }, + { { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, ENTR_JABU_JABU_BOSS_ENTRANCE }, + { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT, ENTR_JABU_JABU_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, ENTR_FOREST_TEMPLE_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, ENTR_FIRE_TEMPLE_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, ENTR_WATER_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, ENTR_WATER_TEMPLE_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, ENTR_SPIRIT_TEMPLE_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, ENTR_SHADOW_TEMPLE_BOSS_DOOR } }, { { EntranceType::BlueWarp, RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_DEKU_TREE_BLUE_WARP }, NO_RETURN_ENTRANCE }, @@ -1488,11 +1488,11 @@ int EntranceShuffler::ShuffleAllEntrances() { if (true /* ctx->GetOption(RSK_SHUFFLE_BLUEWARP_ENTRANCES).Is(RO_BLUEWARP_ENTRANCE_SHUFFLE_DUNGEON) */) { // If a boss room is inside a boss door, make the blue warp go outside the dungeon's entrance std::map bossExits = { - { EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY), + { EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT), GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE)) }, - { EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY), + { EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT), GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL)) }, - { EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY), + { EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT), GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN)) }, { EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY), GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW)) }, @@ -1530,11 +1530,11 @@ int EntranceShuffler::ShuffleAllEntrances() { // Pair std::vector bossRoomExitPairs = { { GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE)), - GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY)) }, + GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT)) }, { GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL)), - GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY)) }, + GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT)) }, { GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN)), - GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY)) }, + GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT)) }, { GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW)), GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY)) }, { GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL)), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp index f327c9992b0..c829d1bc97f 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp @@ -436,11 +436,16 @@ void RegionTable_Init_DekuTree() { #pragma endregion // Boss Room + // RANDOTODO make it so entrance randomiser can properly handle more than 1 access to that entrance areaTable[RR_DEKU_TREE_BOSS_ENTRYWAY] = Region("Deku Tree Boss Entryway", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + // Exits + Entrance(RR_DEKU_TREE_BOSS_ROOM, []{return true;}), + }); + + areaTable[RR_DEKU_TREE_BOSS_EXIT] = Region("Deku Tree Boss Exit", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits Entrance(RR_DEKU_TREE_OUTSIDE_BOSS_ROOM, []{return ctx->GetDungeon(DEKU_TREE)->IsVanilla();}), Entrance(RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM, []{return ctx->GetDungeon(DEKU_TREE)->IsMQ();}), - Entrance(RR_DEKU_TREE_BOSS_ROOM, []{return true;}), }); areaTable[RR_DEKU_TREE_BOSS_ROOM] = Region("Deku Tree Boss Room", "Deku Tree", {}, NO_DAY_NIGHT_CYCLE, { @@ -460,8 +465,8 @@ void RegionTable_Init_DekuTree() { LOCATION(RC_DEKU_TREE_QUEEN_GOHMA_GRASS_8, logic->CanCutShrubs()), }, { // Exits - Entrance(RR_DEKU_TREE_BOSS_ENTRYWAY, []{return true;}), - Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false), + Entrance(RR_DEKU_TREE_BOSS_EXIT, []{return true;}), + Entrance(RR_KF_OUTSIDE_DEKU_TREE, []{return logic->DekuTreeClear;}, false), }); // clang-format on diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp index 8cd60f268e7..9488f2d8229 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp @@ -558,11 +558,16 @@ void RegionTable_Init_DodongosCavern() { #pragma endregion // Boss Room + // RANDOTODO make it so entrance randomiser can properly handle more than 1 access to that entrance areaTable[RR_DODONGOS_CAVERN_BOSS_ENTRYWAY] = Region("Dodongos Cavern Boss Entryway", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {}, { + // Exits + Entrance(RR_DODONGOS_CAVERN_BOSS_ROOM, []{return true;}), + }); + + areaTable[RR_DODONGOS_CAVERN_BOSS_EXIT] = Region("Dodongos Cavern Boss Exit", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits Entrance(RR_DODONGOS_CAVERN_BOSS_AREA, []{return ctx->GetDungeon(DODONGOS_CAVERN)->IsVanilla();}), Entrance(RR_DODONGOS_CAVERN_MQ_BEHIND_MOUTH, []{return ctx->GetDungeon(DODONGOS_CAVERN)->IsMQ();}), - Entrance(RR_DODONGOS_CAVERN_BOSS_ROOM, []{return true;}), }); areaTable[RR_DODONGOS_CAVERN_BOSS_ROOM] = Region("Dodongos Cavern Boss Room", "Dodongos Cavern", {}, NO_DAY_NIGHT_CYCLE, { @@ -575,8 +580,8 @@ void RegionTable_Init_DodongosCavern() { LOCATION(RC_KING_DODONGO, logic->DodongosCavernClear), }, { // Exits - Entrance(RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, []{return true;}), - Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false), + Entrance(RR_DODONGOS_CAVERN_BOSS_EXIT, []{return true;}), + Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->DodongosCavernClear;}, false), }); // clang-format on diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp index 70cf890b003..2e1a6ef1491 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp @@ -38,7 +38,7 @@ void RegionTable_Init_FireTemple() { }, { //Exits Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return true;}), - Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY) && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || Here(RR_FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return logic->CanUse(RG_MEGATON_HAMMER);}))) || logic->CanUse(RG_HOVER_BOOTS));}), + Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || Here(RR_FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return logic->CanUse(RG_MEGATON_HAMMER);}) || logic->CanUse(RG_HOVER_BOOTS));}), }); areaTable[RR_FIRE_TEMPLE_LOOP_ENEMIES] = Region("Fire Temple Loop Enemies", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -445,7 +445,7 @@ void RegionTable_Init_FireTemple() { Entrance(RR_FIRE_TEMPLE_MQ_FIRST_ROOM_UPPER, []{return true;}), //Child cannot make it to the north side torches without a hook without specifically bunny hood speed + hover boots Entrance(RR_FIRE_TEMPLE_MQ_NEAR_BOSS_ROOM_NORTH, []{return logic->FireTimer() > 32 && (logic->CanUse(RG_HOOKSHOT) || (logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS)));}), - Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY) && logic->FireTimer() >= 15 && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || logic->CanUse(RG_HOVER_BOOTS))) || (logic->IsAdult && logic->HitFireTemplePlatform) || (logic->HitFireTemplePlatform && logic->CanUse(RG_HOVER_BOOTS)));}), + Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, []{return logic->FireTimer() >= 15 && ((logic->IsAdult && (ctx->GetTrickOption(RT_FIRE_BOSS_DOOR_JUMP) || logic->CanUse(RG_HOVER_BOOTS))) || (logic->IsAdult && logic->HitFireTemplePlatform) || (logic->HitFireTemplePlatform && logic->CanUse(RG_HOVER_BOOTS)));}), }); //This room assumes tunic logic is handled on entry. @@ -740,7 +740,7 @@ void RegionTable_Init_FireTemple() { // Exits Entrance(RR_FIRE_TEMPLE_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(FIRE_TEMPLE)->IsVanilla() && false;}), Entrance(RR_FIRE_TEMPLE_MQ_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(FIRE_TEMPLE)->IsMQ() && false;}), - Entrance(RR_FIRE_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_FIRE_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_FIRE_TEMPLE_BOSS_KEY);}), }); areaTable[RR_FIRE_TEMPLE_BOSS_ROOM] = Region("Fire Temple Boss Room", "Fire Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp index 2f5ba96bf73..c8d80f98df2 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp @@ -298,7 +298,7 @@ void RegionTable_Init_ForestTemple() { }, { //Exits Entrance(RR_FOREST_TEMPLE_LOBBY, []{return true;}), - Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}), + Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -593,7 +593,7 @@ void RegionTable_Init_ForestTemple() { areaTable[RR_FOREST_TEMPLE_MQ_BOSS_REGION] = Region("Forest Temple MQ Boss Region", "Forest Temple", {RA_FOREST_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_FOREST_TEMPLE_MQ_BASEMENT, []{return logic->ForestOpenBossCorridor;}), - Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}), + Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -603,7 +603,7 @@ void RegionTable_Init_ForestTemple() { // Exits Entrance(RR_FOREST_TEMPLE_BOSS_REGION, []{return ctx->GetDungeon(FOREST_TEMPLE)->IsVanilla() && false;}), Entrance(RR_FOREST_TEMPLE_MQ_BOSS_REGION, []{return ctx->GetDungeon(FOREST_TEMPLE)->IsMQ() && false;}), - Entrance(RR_FOREST_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_FOREST_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_FOREST_TEMPLE_BOSS_KEY);}), }); areaTable[RR_FOREST_TEMPLE_BOSS_ROOM] = Region("Forest Temple Boss Room", "Forest Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp index 45122527371..e4c33b49a41 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp @@ -29,12 +29,7 @@ void RegionTable_Init_GanonsCastle() { Entrance(RR_GANONS_CASTLE_SHADOW_TRIAL, []{return true;}), Entrance(RR_GANONS_CASTLE_SPIRIT_TRIAL, []{return true;}), Entrance(RR_GANONS_CASTLE_LIGHT_TRIAL, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);}), - Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && - (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) && - (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) && - (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) && - (logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) && - (logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}), + Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return true;}), Entrance(RR_GANONS_CASTLE_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON) || logic->CanUse(RG_LENS_OF_TRUTH);}), }); @@ -162,13 +157,7 @@ void RegionTable_Init_GanonsCastle() { Entrance(RR_GANONS_CASTLE_MQ_SHADOW_TRIAL_STARTING_LEDGE, []{return true;}), Entrance(RR_GANONS_CASTLE_MQ_SPIRIT_TRIAL_CHAIRS_ROOM, []{return true;}), Entrance(RR_GANONS_CASTLE_MQ_LIGHT_TRIAL_DINOLFOS_ROOM, []{return Here(RR_GANONS_CASTLE_MQ_MAIN, []{return logic->CanUse(RG_GOLDEN_GAUNTLETS);});}), - //RANDOTODO could we just set these events automatically based on the setting? - Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && - (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) && - (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) && - (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) && - (logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) && - (logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}), + Entrance(RR_GANONS_TOWER_ENTRYWAY, []{return true;}), Entrance(RR_GANONS_CASTLE_MQ_DEKU_SCRUBS, []{return ctx->GetTrickOption(RT_LENS_GANON_MQ) || logic->CanUse(RG_LENS_OF_TRUTH);}), }); @@ -436,7 +425,13 @@ void RegionTable_Init_GanonsCastle() { //Exits Entrance(RR_GANONS_CASTLE_LOBBY, []{return ctx->GetDungeon(GANONS_CASTLE)->IsVanilla();}), Entrance(RR_GANONS_CASTLE_MQ_MAIN, []{return ctx->GetDungeon(GANONS_CASTLE)->IsMQ();}), - Entrance(RR_GANONS_TOWER_FLOOR_1, []{return true;}), + //RANDOTODO could we just set these events automatically based on the setting? + Entrance(RR_GANONS_TOWER_FLOOR_1, []{return (logic->ForestTrialClear || ctx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) && + (logic->FireTrialClear || ctx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) && + (logic->WaterTrialClear || ctx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) && + (logic->ShadowTrialClear || ctx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) && + (logic->SpiritTrialClear || ctx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) && + (logic->LightTrialClear || ctx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}), }); areaTable[RR_GANONS_TOWER_FLOOR_1] = Region("Ganon's Tower Floor 1", "Ganons Castle", {RA_GANONS_CASTLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp index 3c45682ae6c..95494212a38 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp @@ -351,10 +351,14 @@ void RegionTable_Init_JabuJabusBelly() { // Boss Room areaTable[RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY] = Region("Jabu Jabus Belly Boss Entryway", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, { + // Exits + Entrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, []{return true;}), + }); + + areaTable[RR_JABU_JABUS_BELLY_BOSS_EXIT] = Region("Jabu Jabus Belly Boss Exit", "Jabu Jabus Belly", {RA_JABU_JABUS_BELLY}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits Entrance(RR_JABU_JABUS_BELLY_NEAR_BOSS_ROOM, []{return ctx->GetDungeon(JABU_JABUS_BELLY)->IsVanilla();}), Entrance(RR_JABU_JABUS_BELLY_MQ_EAST_ROOM, []{return ctx->GetDungeon(JABU_JABUS_BELLY)->IsMQ();}), - Entrance(RR_JABU_JABUS_BELLY_BOSS_ROOM, []{return true;}), }); areaTable[RR_JABU_JABUS_BELLY_BOSS_ROOM] = Region("Jabu Jabus Belly Boss Room", "Jabu Jabus Belly", {}, NO_DAY_NIGHT_CYCLE, { @@ -372,8 +376,8 @@ void RegionTable_Init_JabuJabusBelly() { LOCATION(RC_BARINADE, logic->JabuJabusBellyClear), }, { // Exits - Entrance(RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, []{return false;}), - Entrance(RR_ZORAS_FOUNTAIN, []{return logic->JabuJabusBellyClear;}, false), + Entrance(RR_JABU_JABUS_BELLY_BOSS_EXIT, []{return false;}), + Entrance(RR_ZORAS_FOUNTAIN, []{return logic->JabuJabusBellyClear;}, false), }); // clang-format on diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp index 3be1acac42f..eef68c78baf 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp @@ -112,7 +112,7 @@ void RegionTable_Init_ShadowTemple() { LOCATION(RC_SHADOW_TEMPLE_AFTER_SHIP_LOWER_HEART, (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->CanUse(RG_SONG_OF_TIME) || (logic->CanUse(RG_DISTANT_SCARECROW) && logic->CanUse(RG_HOVER_BOOTS))), }, { //Exits - Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->SmallKeys(RR_SHADOW_TEMPLE, 5) && logic->CanUse(RG_HOVER_BOOTS) && logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}) + Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_DISTANT_SCARECROW) || (ctx->GetTrickOption(RT_SHADOW_STATUE) && logic->CanUse(RG_BOMBCHU_5))) && logic->SmallKeys(RR_SHADOW_TEMPLE, 5) && logic->CanUse(RG_HOVER_BOOTS);}) }); #pragma endregion @@ -370,7 +370,7 @@ void RegionTable_Init_ShadowTemple() { }, { //Exits Entrance(RR_SHADOW_TEMPLE_MQ_ACROSS_CHASM, []{return logic->CanUse(RG_HOVER_BOOTS) && (ctx->GetTrickOption(RT_LENS_SHADOW_MQ) || logic->CanUse(RG_LENS_OF_TRUTH));}), - Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}), + Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); //Assumes lens is checked on entry @@ -403,7 +403,7 @@ void RegionTable_Init_ShadowTemple() { // Exits Entrance(RR_SHADOW_TEMPLE_BEYOND_BOAT, []{return ctx->GetDungeon(SHADOW_TEMPLE)->IsVanilla() && false;}), Entrance(RR_SHADOW_TEMPLE_MQ_BEYOND_BOAT, []{return ctx->GetDungeon(SHADOW_TEMPLE)->IsMQ() && false;}), - Entrance(RR_SHADOW_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_SHADOW_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_SHADOW_TEMPLE_BOSS_KEY);}), }); areaTable[RR_SHADOW_TEMPLE_BOSS_ROOM] = Region("Shadow Temple Boss Room", "Shadow Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index e7f9c0ee63f..72aa9974b5b 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -145,7 +145,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD] = Region("Spirit Temple Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -541,7 +541,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD] = Region("Spirit Temple MQ Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -551,7 +551,7 @@ void RegionTable_Init_SpiritTemple() { // Exits Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla() && false;}), Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ() && false;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), }); areaTable[RR_SPIRIT_TEMPLE_BOSS_ROOM] = Region("Spirit Temple Boss Room", "Spirit Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp index 04c1279bd54..ccbc2f6884f 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp @@ -277,7 +277,7 @@ void RegionTable_Init_WaterTemple() { }, { //Exits Entrance(RR_WATER_TEMPLE_LOBBY, []{return true;}), - Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}), + Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); #pragma endregion @@ -365,7 +365,7 @@ void RegionTable_Init_WaterTemple() { areaTable[RR_WATER_TEMPLE_MQ_BOSS_DOOR] = Region("Water Temple MQ Main", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_WATER_TEMPLE_MQ_3F_NORTH_LEDGE, []{return logic->CanUse(RG_ICE_ARROWS) || logic->TakeDamage();}), - Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}), + Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return true;}), }); areaTable[RR_WATER_TEMPLE_MQ_EAST_TOWER] = Region("Water Temple MQ East Tower", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, { @@ -843,7 +843,7 @@ void RegionTable_Init_WaterTemple() { // Exits Entrance(RR_WATER_TEMPLE_PRE_BOSS_ROOM, []{return ctx->GetDungeon(WATER_TEMPLE)->IsVanilla() && false;}), Entrance(RR_WATER_TEMPLE_MQ_BOSS_DOOR, []{return ctx->GetDungeon(WATER_TEMPLE)->IsMQ() && false;}), - Entrance(RR_WATER_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_WATER_TEMPLE_BOSS_ROOM, []{return logic->HasItem(RG_WATER_TEMPLE_BOSS_KEY);}), }); areaTable[RR_WATER_TEMPLE_BOSS_ROOM] = Region("Water Temple Boss Room", "Water Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index ce074bf4cd7..396a37b2336 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -566,6 +566,7 @@ typedef enum { RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY, + RR_DEKU_TREE_BOSS_EXIT, RR_DEKU_TREE_BOSS_ROOM, RR_DODONGOS_CAVERN_BEGINNING, @@ -613,7 +614,9 @@ typedef enum { RR_DODONGOS_CAVERN_MQ_BEHIND_MOUTH, RR_DODONGOS_CAVERN_MQ_BACK_BEHIND_FIRE, RR_DODONGOS_CAVERN_MQ_BACK_SWITCH_GRAVE, + RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, + RR_DODONGOS_CAVERN_BOSS_EXIT, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_JABU_JABUS_BELLY_BEGINNING, @@ -642,6 +645,7 @@ typedef enum { RR_JABU_JABUS_BELLY_MQ_EAST_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, + RR_JABU_JABUS_BELLY_BOSS_EXIT, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_FOREST_TEMPLE_FIRST_ROOM, From 01ce1eeac8d8dacfc962c52666e622e77f720eba Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 21 May 2025 16:51:43 +0100 Subject: [PATCH 087/157] Fix oversight in lab boots trick, you need to be able to swim back up (#5524) --- .../randomizer/location_access/overworld/lake_hylia.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp index 36d3ee1a39d..19e0497f094 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp @@ -105,7 +105,7 @@ void RegionTable_Init_LakeHylia() { areaTable[RR_LH_LAB] = Region("LH Lab", "LH Lab", {}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_LH_LAB_DIVE, logic->HasItem(RG_GOLDEN_SCALE) || (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT))), + LOCATION(RC_LH_LAB_DIVE, logic->HasItem(RG_GOLDEN_SCALE) || (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_BRONZE_SCALE))), LOCATION(RC_LH_TRADE_FROG, logic->IsAdult && logic->CanUse(RG_EYEBALL_FROG)), LOCATION(RC_LH_GS_LAB_CRATE, logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->CanBreakCrates()), LOCATION(RC_LH_LAB_FRONT_RUPEE, logic->CanUse(RG_IRON_BOOTS) || logic->HasItem(RG_GOLDEN_SCALE)), From 2511275b8ba11f5e2149c312f42e402e23891ae8 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 21 May 2025 23:01:54 +0100 Subject: [PATCH 088/157] Fix getting heart containers on pocket and skipped impas song (#5518) * fix getting heart containers on pocket and skipped impas song * curse you clang! CURSE YOU! --- soh/soh/Enhancements/randomizer/savefile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index 1bed22a714f..91b5a50fc24 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -232,6 +232,8 @@ extern "C" void Randomizer_InitSaveFile() { // Reset triforce pieces collected. gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected = 0; + SetStartingItems(); + // Set Cutscene flags and texts to skip them. Flags_SetEventChkInf(EVENTCHKINF_FIRST_SPOKE_TO_MIDO); Flags_SetInfTable(INFTABLE_SPOKE_TO_KAEPORA_IN_LAKE_HYLIA); @@ -430,6 +432,4 @@ extern "C" void Randomizer_InitSaveFile() { gSaveContext.itemGetInf[3] |= 0x800; // Bunny Hood related gSaveContext.itemGetInf[3] |= 0x8000; // Obtained Mask of Truth } - - SetStartingItems(); } From a9fc317a5a916389449d68d666ff2e2965a0b023 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Thu, 22 May 2025 23:23:14 +0100 Subject: [PATCH 089/157] Fix crash when no valid enemies are selected (#5519) * fix crash when no valid enemies are selected * Clanged --- soh/soh/Enhancements/enemyrandomizer.cpp | 29 ++++++++++++------------ soh/soh/Enhancements/enemyrandomizer.h | 3 ++- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/soh/soh/Enhancements/enemyrandomizer.cpp b/soh/soh/Enhancements/enemyrandomizer.cpp index 84e1d040e07..bdb25971965 100644 --- a/soh/soh/Enhancements/enemyrandomizer.cpp +++ b/soh/soh/Enhancements/enemyrandomizer.cpp @@ -270,15 +270,7 @@ extern "C" uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, f32* po // Get randomized enemy ID and parameter. uint32_t seed = play->sceneNum + *actorId + (int)*posX + (int)*posY + (int)*posZ + *rotX + *rotY + *rotZ + *params; - EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed); - - int8_t timesRandomized = 1; - - // While randomized enemy isn't allowed in certain situations, randomize again. - while (!IsEnemyAllowedToSpawn(play->sceneNum, play->roomCtx.curRoom.num, randomEnemy)) { - randomEnemy = GetRandomizedEnemyEntry(seed + timesRandomized); - timesRandomized++; - } + EnemyEntry randomEnemy = GetRandomizedEnemyEntry(seed, play); *actorId = randomEnemy.id; *params = randomEnemy.params; @@ -334,19 +326,28 @@ void GetSelectedEnemies() { } } -EnemyEntry GetRandomizedEnemyEntry(uint32_t seed) { +EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play) { + std::vector filteredEnemyList = {}; if (selectedEnemyList.size() == 0) { GetSelectedEnemies(); } + for (EnemyEntry enemy : selectedEnemyList) { + if (IsEnemyAllowedToSpawn(play->sceneNum, play->roomCtx.curRoom.num, enemy)) { + filteredEnemyList.push_back(enemy); + } + } + if (filteredEnemyList.size() == 0) { + filteredEnemyList = selectedEnemyList; + } if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), ENEMY_RANDOMIZER_OFF) == ENEMY_RANDOMIZER_RANDOM_SEEDED) { uint32_t finalSeed = seed + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() : gSaveContext.ship.stats.fileCreatedAt); Random_Init(finalSeed); - uint32_t randomNumber = Random(0, selectedEnemyList.size()); - return selectedEnemyList[randomNumber]; + uint32_t randomNumber = Random(0, filteredEnemyList.size()); + return filteredEnemyList[randomNumber]; } else { - uint32_t randomSelectedEnemy = Random(0, selectedEnemyList.size()); - return selectedEnemyList[randomSelectedEnemy]; + uint32_t randomSelectedEnemy = Random(0, filteredEnemyList.size()); + return filteredEnemyList[randomSelectedEnemy]; } } diff --git a/soh/soh/Enhancements/enemyrandomizer.h b/soh/soh/Enhancements/enemyrandomizer.h index 4c4fd55763e..6f80503df96 100644 --- a/soh/soh/Enhancements/enemyrandomizer.h +++ b/soh/soh/Enhancements/enemyrandomizer.h @@ -1,6 +1,7 @@ #pragma once #include +#include "item-tables/ItemTableTypes.h" typedef struct EnemyEntry { int16_t id; @@ -11,7 +12,7 @@ typedef struct EnemyEntry { bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId, int16_t params, float posX); bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy); -EnemyEntry GetRandomizedEnemyEntry(uint32_t seed); +EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play); extern const char* enemyCVarList[]; extern const char* enemyNameList[]; From d69a45674f7641eae3764f8bbbc6f016314c8854 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Fri, 23 May 2025 14:06:25 -0700 Subject: [PATCH 090/157] Adds a check for "OneDrive" in the execution path to the startup errors that prevent running. (#5522) --- soh/soh/OTRGlobals.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index dcabfca7097..3195bf2b7b8 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1145,6 +1145,13 @@ extern "C" void InitOTR() { "Error", "SoH does not have proper file permissions. Please move it to a folder that does and run again."); exit(1); } + if (ownPath.string().find("OneDrive") != std::string::npos) { + Extractor::ShowErrorBox( + "Error", + "SoH appears to be in a OneDrive folder, which will cause issues. " + "Please move it to a folder outside of OneDrive, like the root of a drive (e.g. \"C:\\Games\\SoH\")."); + exit(1); + } #endif #if not defined(__SWITCH__) && not defined(__WIIU__) From d330f22071e4257759dc5e04f2b47a069b68e29b Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Fri, 23 May 2025 19:49:01 -0500 Subject: [PATCH 091/157] Available Checks Entrance Shuffle (#5363) * EntranceShuffler ApplyEntranceOverrides. * Updated ApplyEntranceOverrides. * Updated ApplyEntranceOverrides. * Initial Entrance Discovery. * Added Randomizer_EntranceDiscovered. * Updated Randomizer_DiscoverRegion to discover unshuffled connected regions. * Removed extra semi-colon. * Update to latest entranceShuffleTable. * Format fixes. * Updated EntranceDiscovered to account for entrances not in the entranceShuffleTable. * Rediscover regions when loading a game. * Rediscover entrances when enabling Available Checks. * Added Rando::Context::ParseTricksJson. * Updated ApplyEntranceOverrides to skip default (unset) overrides. * Fix clang-format failed check. * Set mLACSCondition when loading a Randomizer game. * Updated rediscover loop to use MAX_ENTRANCE_RANDO_USED_INDEX. * Move entrance discovered into ProcessExits. * Discover spawns when pulling/placing the master sword. * Discover adult/child spawn when using Song of Time to switch age. * Reset logic and ApplyOrStoreItems without applying the item effects to the save when calculating available checks. * Removed !itemLoc->IsAddedToPool() from the check tracker. * Remove region discovery as its not needed anymore. * Cleanup changed files for PR. * Added Available Checks - Process Undiscovered Exits DebugConsole command. * Added initial Available Checks - Recalculate DebugConsole command. * ACPUE, recalculate only if save loaded. ACR, set age and times for the starting region. --- soh/soh/Enhancements/debugconsole.cpp | 60 ++ soh/soh/Enhancements/mods.cpp | 7 + .../Enhancements/randomizer/3drando/fill.cpp | 45 +- .../Enhancements/randomizer/3drando/fill.hpp | 4 +- soh/soh/Enhancements/randomizer/context.cpp | 16 + soh/soh/Enhancements/randomizer/context.h | 10 + soh/soh/Enhancements/randomizer/entrance.cpp | 766 +++++++++--------- soh/soh/Enhancements/randomizer/entrance.h | 1 + soh/soh/Enhancements/randomizer/item.cpp | 14 +- .../Enhancements/randomizer/location_access.h | 1 - soh/soh/Enhancements/randomizer/logic.cpp | 70 +- soh/soh/Enhancements/randomizer/logic.h | 5 +- .../randomizer/randomizer_check_tracker.cpp | 48 +- .../randomizer/randomizer_check_tracker.h | 2 +- .../randomizer/randomizer_entrance.c | 1 + soh/soh/Enhancements/randomizer/settings.cpp | 8 + soh/soh/Enhancements/randomizer/settings.h | 8 + soh/soh/OTRGlobals.cpp | 5 +- soh/soh/OTRGlobals.h | 1 + .../actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c | 6 + 20 files changed, 644 insertions(+), 434 deletions(-) diff --git a/soh/soh/Enhancements/debugconsole.cpp b/soh/soh/Enhancements/debugconsole.cpp index db5cc824f7a..32fc8e72f44 100644 --- a/soh/soh/Enhancements/debugconsole.cpp +++ b/soh/soh/Enhancements/debugconsole.cpp @@ -11,6 +11,7 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/cosmetics/CosmeticsEditor.h" #include "soh/Enhancements/audio/AudioEditor.h" +#include "soh/Enhancements/randomizer/logic.h" #define Path _Path #define PATH_HACK @@ -1450,6 +1451,55 @@ static bool SfxHandler(std::shared_ptr Console, const std::vector return 0; } +static bool AvailabeChecksProcessUndiscoveredExitsHandler(std::shared_ptr Console, + const std::vector& args, std::string* output) { + const auto& logic = Rando::Context::GetInstance()->GetLogic(); + bool enabled = false; + + if (args.size() == 1) { + enabled = !logic->ACProcessUndiscoveredExits; + } else { + try { + enabled = std::stoi(args[1]); + } catch (std::invalid_argument const& ex) { + ERROR_MESSAGE("[SOH] Enable should be 0 or 1"); + return 1; + } + } + + logic->ACProcessUndiscoveredExits = enabled; + INFO_MESSAGE("[SOH] Available Checks - Process Undiscovered Exits %s", + logic->ACProcessUndiscoveredExits ? "enabled" : "disabled"); + + if (GameInteractor::IsSaveLoaded(true)) { + CheckTracker::RecalculateAvailableChecks(); + } + + return 0; +} + +static bool AvailabeChecksRecalculateHandler(std::shared_ptr Console, + const std::vector& args, std::string* output) { + RandomizerRegion startingRegion = RR_ROOT; + + if (args.size() > 1) { + try { + startingRegion = static_cast(std::stoi(args[1])); + } catch (std::invalid_argument const& ex) { + ERROR_MESSAGE("[SOH] Region should be a number"); + return 1; + } + + if (startingRegion <= RR_NONE || startingRegion >= RR_MAX) { + ERROR_MESSAGE("[SOH] Region should be between 1 and %d", RR_MAX - 1); + return 1; + } + } + + CheckTracker::RecalculateAvailableChecks(startingRegion); + return 0; +} + void DebugConsole_Init(void) { // Console CMD_REGISTER("file_select", { FileSelectHandler, "Returns to the file select." }); @@ -1708,5 +1758,15 @@ void DebugConsole_Init(void) { { "group_name", Ship::ArgumentType::TEXT, true }, } }); + CMD_REGISTER("acpue", { AvailabeChecksProcessUndiscoveredExitsHandler, + "Available Checks - Process Undiscovered Exits", + { { "enable", Ship::ArgumentType::NUMBER, true } } }); + + CMD_REGISTER("acr", { AvailabeChecksRecalculateHandler, + "Available Checks - Recalculate", + { + { "starting_region", Ship::ArgumentType::NUMBER, true }, + } }); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 2c357fd34f6..180ba4f5eaf 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -99,6 +99,13 @@ void SwitchAge() { gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK_FAST; gPlayState->linkAgeOnLoad ^= 1; + // Discover adult/child spawns + if (gPlayState->linkAgeOnLoad == LINK_AGE_ADULT) { + Entrance_SetEntranceDiscovered(ENTR_HYRULE_FIELD_10, false); + } else { + Entrance_SetEntranceDiscovered(ENTR_LINKS_HOUSE_CHILD_SPAWN, false); + } + static HOOK_ID hookId = 0; hookId = REGISTER_VB_SHOULD(VB_INFLICT_VOID_DAMAGE, { *should = false; diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 8885117c326..f2b0e782c1e 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -209,6 +209,13 @@ void ProcessExits(Region* region, GetAccessibleLocationsStruct& gals, Randomizer bool stopOnBeatable = false, bool addToPlaythrough = false) { auto ctx = Rando::Context::GetInstance(); for (auto& exit : region->exits) { + int16_t entranceIndex = exit.GetIndex(); + if (!logic->ACProcessUndiscoveredExits && logic->CalculatingAvailableChecks && + ctx->GetOption(RSK_SHUFFLE_ENTRANCES).Get() && exit.IsShuffled() && entranceIndex != -1 && + !Entrance_GetIsEntranceDiscovered(entranceIndex)) { + continue; + } + Region* exitRegion = exit.GetConnectedRegion(); // Update Time of Day Access for the exit if (UpdateToDAccess(&exit, exitRegion)) { @@ -421,18 +428,13 @@ bool AddCheckToLogic(LocationAccess& locPair, GetAccessibleLocationsStruct& gals Rando::ItemLocation* location = ctx->GetItemLocation(loc); RandomizerGet locItem = location->GetPlacedRandomizerGet(); - if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, gals.calculatingAvailableChecks)) { - if (gals.calculatingAvailableChecks) { - gals.accessibleLocations.push_back(loc); - StopPerformanceTimer(PT_LOCATION_LOGIC); - return false; - } - + if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, logic->CalculatingAvailableChecks)) { location->AddToPool(); - if (locItem == RG_NONE) { + if (locItem == RG_NONE || logic->CalculatingAvailableChecks) { gals.accessibleLocations.push_back(loc); // Empty location, consider for placement - } else { + } + if (locItem != RG_NONE) { // If ignore has a value, we want to check if the item location should be considered or not // This is necessary due to the below preprocessing for playthrough generation if (ignore != RG_NONE) { @@ -528,11 +530,32 @@ void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, Randomize // Return any of the targetLocations that are accessible in logic std::vector ReachabilitySearch(const std::vector& targetLocations, RandomizerGet ignore /* = RG_NONE*/, - bool calculatingAvailableChecks /* = false */) { + bool calculatingAvailableChecks /* = false */, + RandomizerRegion startingRegion /* = RR_ROOT */) { auto ctx = Rando::Context::GetInstance(); GetAccessibleLocationsStruct gals(0); - gals.calculatingAvailableChecks = calculatingAvailableChecks; ResetLogic(ctx, gals, !calculatingAvailableChecks); + if (startingRegion != RR_ROOT) { + gals.regionPool.insert(gals.regionPool.begin(), startingRegion); + + const auto& region = RegionTable(startingRegion); + if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_CHILD)) { + region->childDay = true; + } else { + region->adultDay = true; + } + if (region->timePass) { + if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_CHILD)) { + region->childNight = true; + } else { + region->adultNight = true; + } + } + } + if (calculatingAvailableChecks) { + logic->Reset(false); + logic->CalculatingAvailableChecks = true; + } do { gals.InitLoop(); for (size_t i = 0; i < gals.regionPool.size(); i++) { diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.hpp b/soh/soh/Enhancements/randomizer/3drando/fill.hpp index a9b864ce2aa..67043952f39 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.hpp @@ -34,8 +34,6 @@ struct GetAccessibleLocationsStruct { std::vector itemSphere; std::list entranceSphere; - bool calculatingAvailableChecks = false; - GetAccessibleLocationsStruct(int _maxGsCount){ regionPool = {RR_ROOT}; gsCount = 0; @@ -64,7 +62,7 @@ std::vector GetEmptyLocations(std::vector allo void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, RandomizerGet ignore = RG_NONE, bool stopOnBeatable = false, bool addToPlaythrough = false); -std::vector ReachabilitySearch(const std::vector& allowedLocations, RandomizerGet ignore=RG_NONE, bool calculatingAvailableChecks=false); +std::vector ReachabilitySearch(const std::vector& allowedLocations, RandomizerGet ignore=RG_NONE, bool calculatingAvailableChecks=false, RandomizerRegion startingRegion=RR_ROOT); void GeneratePlaythrough(); diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp index fa755325eda..7178dd18dae 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/context.cpp @@ -401,6 +401,7 @@ void Context::ParseSpoiler(const char* spoilerFileName) { Rando::Settings::GetInstance()->ParseJson(spoilerFileJson); ParseItemLocationsJson(spoilerFileJson); ParseHintJson(spoilerFileJson); + ParseTricksJson(spoilerFileJson); mEntranceShuffler->ParseJson(spoilerFileJson); mDungeons->ParseJson(spoilerFileJson); mTrials->ParseJson(spoilerFileJson); @@ -469,6 +470,17 @@ void Context::ParseHintJson(nlohmann::json spoilerFileJson) { CreateStaticHints(); } +void Context::ParseTricksJson(nlohmann::json spoilerFileJson) { + nlohmann::json enabledTricksJson = spoilerFileJson["enabledTricks"]; + const auto& settings = Rando::Settings::GetInstance(); + for (auto it : enabledTricksJson) { + int rt = settings->GetRandomizerTrickByName(it); + if (rt != -1) { + mTrickOptions[rt].Set(RO_GENERIC_ON); + } + } +} + std::shared_ptr Context::GetEntranceShuffler() { return mEntranceShuffler; } @@ -524,6 +536,10 @@ RandoOptionLACSCondition Context::LACSCondition() const { return mLACSCondition; } +void Context::LACSCondition(RandoOptionLACSCondition lacsCondition) { + mLACSCondition = lacsCondition; +} + std::shared_ptr Context::GetKaleido() { if (mKaleido == nullptr) { mKaleido = std::make_shared(); diff --git a/soh/soh/Enhancements/randomizer/context.h b/soh/soh/Enhancements/randomizer/context.h index 7f18f7a9d5e..c7e3b3c1919 100644 --- a/soh/soh/Enhancements/randomizer/context.h +++ b/soh/soh/Enhancements/randomizer/context.h @@ -104,12 +104,22 @@ class Context { * @return RandoOptionLACSCondition */ RandoOptionLACSCondition LACSCondition() const; + + /** + * @brief Sets the resolved Light Arrow CutScene check condition. + * There is no direct option for this, it is inferred based on the value of a few other options. + * + * @param lacsCondition + */ + void LACSCondition(RandoOptionLACSCondition lacsCondition); + GetItemEntry GetFinalGIEntry(RandomizerCheck rc, bool checkObtainability = true, GetItemID ogItemId = GI_NONE); void ParseSpoiler(const char* spoilerFileName); void ParseHashIconIndexesJson(nlohmann::json spoilerFileJson); void ParseItemLocationsJson(nlohmann::json spoilerFileJson); void WriteHintJson(nlohmann::ordered_json& spoilerFileJson); void ParseHintJson(nlohmann::json spoilerFileJson); + void ParseTricksJson(nlohmann::json spoilerFileJson); std::map overrides = {}; std::vector> playthroughLocations = {}; std::vector everyPossibleLocation = {}; diff --git a/soh/soh/Enhancements/randomizer/entrance.cpp b/soh/soh/Enhancements/randomizer/entrance.cpp index 66da4ee18d4..45cde2f6f76 100644 --- a/soh/soh/Enhancements/randomizer/entrance.cpp +++ b/soh/soh/Enhancements/randomizer/entrance.cpp @@ -250,7 +250,319 @@ std::string EntranceNameByRegions(RandomizerRegion parentRegion, RandomizerRegio return RegionTable(parentRegion)->regionName + " -> " + RegionTable(connectedRegion)->regionName; } -void SetAllEntrancesData(std::vector& entranceShuffleTable) { +std::unordered_map entranceMap; + +void SetAllEntrancesData() { + std::vector entranceShuffleTable = { + // clang-format off + // Type Parent Region Connected Region Index + { { EntranceType::Dungeon, RR_KF_OUTSIDE_DEKU_TREE, RR_DEKU_TREE_ENTRYWAY, ENTR_DEKU_TREE_ENTRANCE }, + { EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_OUTSIDE_DEKU_TREE } }, + { { EntranceType::Dungeon, RR_DEATH_MOUNTAIN_TRAIL, RR_DODONGOS_CAVERN_ENTRYWAY, ENTR_DODONGOS_CAVERN_ENTRANCE }, + { EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN } }, + { { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_JABU_JABUS_BELLY_ENTRYWAY, ENTR_JABU_JABU_ENTRANCE }, + { EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_JABU_JABU } }, + { { EntranceType::Dungeon, RR_SACRED_FOREST_MEADOW, RR_FOREST_TEMPLE_ENTRYWAY, ENTR_FOREST_TEMPLE_ENTRANCE }, + { EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_OUTSIDE_TEMPLE } }, + { { EntranceType::Dungeon, RR_DMC_CENTRAL_LOCAL, RR_FIRE_TEMPLE_ENTRYWAY, ENTR_FIRE_TEMPLE_ENTRANCE }, + { EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_OUTSIDE_TEMPLE } }, + { { EntranceType::Dungeon, RR_LAKE_HYLIA, RR_WATER_TEMPLE_ENTRYWAY, ENTR_WATER_TEMPLE_ENTRANCE }, + { EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_TEMPLE } }, + { { EntranceType::Dungeon, RR_DESERT_COLOSSUS, RR_SPIRIT_TEMPLE_ENTRYWAY, ENTR_SPIRIT_TEMPLE_ENTRANCE }, + { EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE, ENTR_DESERT_COLOSSUS_OUTSIDE_TEMPLE } }, + { { EntranceType::Dungeon, RR_GRAVEYARD_WARP_PAD_REGION, RR_SHADOW_TEMPLE_ENTRYWAY, ENTR_SHADOW_TEMPLE_ENTRANCE }, + { EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_OUTSIDE_TEMPLE } }, + { { EntranceType::Dungeon, RR_KAK_WELL, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, ENTR_BOTTOM_OF_THE_WELL_ENTRANCE }, + { EntranceType::Dungeon, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, RR_KAK_WELL, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BOTTOM_OF_THE_WELL } }, + { { EntranceType::Dungeon, RR_ZF_LEDGE, RR_ICE_CAVERN_ENTRYWAY, ENTR_ICE_CAVERN_ENTRANCE }, + { EntranceType::Dungeon, RR_ICE_CAVERN_ENTRYWAY, RR_ZF_LEDGE, ENTR_ZORAS_FOUNTAIN_OUTSIDE_ICE_CAVERN } }, + { { EntranceType::Dungeon, RR_GERUDO_FORTRESS, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE }, + { EntranceType::Dungeon, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND } }, + { { EntranceType::GanonDungeon, RR_GANONS_CASTLE_LEDGE, RR_GANONS_CASTLE_ENTRYWAY, ENTR_INSIDE_GANONS_CASTLE_ENTRANCE }, + { EntranceType::GanonDungeon, RR_GANONS_CASTLE_ENTRYWAY, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT } }, + + { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_MIDOS_HOUSE, ENTR_MIDOS_HOUSE_0 }, + { EntranceType::Interior, RR_KF_MIDOS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_MIDOS_HOUSE } }, + { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_SARIAS_HOUSE, ENTR_SARIAS_HOUSE_0 }, + { EntranceType::Interior, RR_KF_SARIAS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SARIAS_HOUSE } }, + { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_HOUSE_OF_TWINS, ENTR_TWINS_HOUSE_0 }, + { EntranceType::Interior, RR_KF_HOUSE_OF_TWINS, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_TWINS_HOUSE } }, + { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KNOW_IT_ALL_HOUSE, ENTR_KNOW_IT_ALL_BROS_HOUSE_0 }, + { EntranceType::Interior, RR_KF_KNOW_IT_ALL_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_KNOW_IT_ALL_HOUSE } }, + { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KOKIRI_SHOP, ENTR_KOKIRI_SHOP_0 }, + { EntranceType::Interior, RR_KF_KOKIRI_SHOP, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SHOP } }, + { { EntranceType::Interior, RR_LAKE_HYLIA, RR_LH_LAB, ENTR_LAKESIDE_LABORATORY_0 }, + { EntranceType::Interior, RR_LH_LAB, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_LAB } }, + { { EntranceType::Interior, RR_LH_FISHING_ISLAND, RR_LH_FISHING_POND, ENTR_FISHING_POND_0 }, + { EntranceType::Interior, RR_LH_FISHING_POND, RR_LH_FISHING_ISLAND, ENTR_LAKE_HYLIA_OUTSIDE_FISHING_POND } }, + { { EntranceType::Interior, RR_GV_FORTRESS_SIDE, RR_GV_CARPENTER_TENT, ENTR_CARPENTERS_TENT_0 }, + { EntranceType::Interior, RR_GV_CARPENTER_TENT, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_OUTSIDE_TENT } }, + { { EntranceType::Interior, RR_MARKET_ENTRANCE, RR_MARKET_GUARD_HOUSE, ENTR_MARKET_GUARD_HOUSE_0 }, + { EntranceType::Interior, RR_MARKET_GUARD_HOUSE, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_OUTSIDE_GUARD_HOUSE } }, + { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_MASK_SHOP, ENTR_HAPPY_MASK_SHOP_0 }, + { EntranceType::Interior, RR_MARKET_MASK_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_HAPPY_MASK_SHOP } }, + { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BOMBCHU_BOWLING, ENTR_BOMBCHU_BOWLING_ALLEY_0 }, + { EntranceType::Interior, RR_MARKET_BOMBCHU_BOWLING, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BOMBCHU_BOWLING } }, + { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_POTION_SHOP, ENTR_POTION_SHOP_MARKET_0 }, + { EntranceType::Interior, RR_MARKET_POTION_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_POTION_SHOP } }, + { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_TREASURE_CHEST_GAME, ENTR_TREASURE_BOX_SHOP_0 }, + { EntranceType::Interior, RR_MARKET_TREASURE_CHEST_GAME, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_TREASURE_BOX_SHOP } }, + { { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_BOMBCHU_SHOP, ENTR_BOMBCHU_SHOP_1 }, + { EntranceType::Interior, RR_MARKET_BOMBCHU_SHOP, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_BOMBCHU_SHOP } }, + { { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_MAN_IN_GREEN_HOUSE, ENTR_BACK_ALLEY_MAN_IN_GREEN_HOUSE }, + { EntranceType::Interior, RR_MARKET_MAN_IN_GREEN_HOUSE, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_MAN_IN_GREEN_HOUSE } }, + { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_CARPENTER_BOSS_HOUSE, ENTR_KAKARIKO_CENTER_GUEST_HOUSE_0 }, + { EntranceType::Interior, RR_KAK_CARPENTER_BOSS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_CENTER_GUEST_HOUSE } }, + { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_HOUSE_OF_SKULLTULA, ENTR_HOUSE_OF_SKULLTULA_0 }, + { EntranceType::Interior, RR_KAK_HOUSE_OF_SKULLTULA, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SKULKLTULA_HOUSE } }, + { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_IMPAS_HOUSE, ENTR_IMPAS_HOUSE_FRONT }, + { EntranceType::Interior, RR_KAK_IMPAS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_FRONT } }, + { { EntranceType::Interior, RR_KAK_IMPAS_LEDGE, RR_KAK_IMPAS_HOUSE_BACK, ENTR_IMPAS_HOUSE_BACK }, + { EntranceType::Interior, RR_KAK_IMPAS_HOUSE_BACK, RR_KAK_IMPAS_LEDGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_BACK } }, + { { EntranceType::Interior, RR_KAK_BACKYARD, RR_KAK_ODD_POTION_BUILDING, ENTR_POTION_SHOP_GRANNY_0 }, + { EntranceType::Interior, RR_KAK_ODD_POTION_BUILDING, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOP_GRANNY } }, + { { EntranceType::Interior, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_HOUSE, ENTR_GRAVEKEEPERS_HUT_0 }, + { EntranceType::Interior, RR_GRAVEYARD_DAMPES_HOUSE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_OUTSIDE_DAMPES_HUT } }, + { { EntranceType::Interior, RR_GORON_CITY, RR_GC_SHOP, ENTR_GORON_SHOP_0 }, + { EntranceType::Interior, RR_GC_SHOP, RR_GORON_CITY, ENTR_GORON_CITY_OUTSIDE_SHOP } }, + { { EntranceType::Interior, RR_ZORAS_DOMAIN, RR_ZD_SHOP, ENTR_ZORA_SHOP_0 }, + { EntranceType::Interior, RR_ZD_SHOP, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_OUTSIDE_SHOP } }, + { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TALONS_HOUSE, ENTR_LON_LON_BUILDINGS_TALONS_HOUSE }, + { EntranceType::Interior, RR_LLR_TALONS_HOUSE, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TALONS_HOUSE } }, + { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_STABLES, ENTR_STABLE_0 }, + { EntranceType::Interior, RR_LLR_STABLES, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_STABLES } }, + { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TOWER, ENTR_LON_LON_BUILDINGS_TOWER }, + { EntranceType::Interior, RR_LLR_TOWER, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TOWER } }, + { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BAZAAR, ENTR_BAZAAR_1 }, + { EntranceType::Interior, RR_MARKET_BAZAAR, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BAZAAR } }, + { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_1 }, + { EntranceType::Interior, RR_MARKET_SHOOTING_GALLERY, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_SHOOTING_GALLERY } }, + { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_BAZAAR, ENTR_BAZAAR_0 }, + { EntranceType::Interior, RR_KAK_BAZAAR, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BAZAAR } }, + { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_0 }, + { EntranceType::Interior, RR_KAK_SHOOTING_GALLERY, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOOTING_GALLERY } }, + { { EntranceType::Interior, RR_DESERT_COLOSSUS, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_NAYRUS_COLOSSUS }, + { EntranceType::Interior, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_GREAT_FAIRY_EXIT } }, + { { EntranceType::Interior, RR_HYRULE_CASTLE_GROUNDS, RR_HC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_DINS_HC }, + { EntranceType::Interior, RR_HC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_GREAT_FAIRY_EXIT } }, + { { EntranceType::Interior, RR_GANONS_CASTLE_GROUNDS, RR_OGC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_OGC_DD }, + // 0x3E8 is an unused entrance index repruposed to differentiate between the HC and OGC fairy + // fountain exits (normally they both use 0x340) + { EntranceType::Interior, RR_OGC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_POTION_SHOP_KAKARIKO_1 } }, + { { EntranceType::Interior, RR_DMC_LOWER_NEARBY, RR_DMC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMC }, + { EntranceType::Interior, RR_DMC_GREAT_FAIRY_FOUNTAIN, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GREAT_FAIRY_EXIT } }, + { { EntranceType::Interior, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMT }, + { EntranceType::Interior, RR_DMT_GREAT_FAIRY_FOUNTAIN, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_GREAT_FAIRY_EXIT } }, + { { EntranceType::Interior, RR_ZORAS_FOUNTAIN, RR_ZF_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_FARORES_ZF }, + { EntranceType::Interior, RR_ZF_GREAT_FAIRY_FOUNTAIN, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_GREAT_FAIRY } }, + + { { EntranceType::SpecialInterior, RR_KOKIRI_FOREST, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_1 }, + { EntranceType::SpecialInterior, RR_KF_LINKS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_LINKS_HOUSE } }, + { { EntranceType::SpecialInterior, RR_TOT_ENTRANCE, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_ENTRANCE }, + { EntranceType::SpecialInterior, RR_TEMPLE_OF_TIME, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_OUTSIDE_TEMPLE } }, + { { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_WINDMILL, ENTR_WINDMILL_AND_DAMPES_GRAVE_WINDMILL }, + { EntranceType::SpecialInterior, RR_KAK_WINDMILL, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_WINDMILL } }, + { { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_POTION_SHOP_FRONT, ENTR_POTION_SHOP_KAKARIKO_FRONT }, + { EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_FRONT, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_FRONT } }, + { { EntranceType::SpecialInterior, RR_KAK_BACKYARD, RR_KAK_POTION_SHOP_BACK, ENTR_POTION_SHOP_KAKARIKO_BACK }, + { EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_BACK, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_BACK } }, + + // Grotto Loads use an entrance index of 0x0700 + their grotto id. The id is used as index for the + // grottoLoadTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c + // Grotto Returns use an entrance index of 0x0800 + their grotto id. The id is used as index for the + // grottoReturnTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c + { { EntranceType::GrottoGrave, RR_DESERT_COLOSSUS, RR_COLOSSUS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_COLOSSUS_OFFSET) }, + { EntranceType::GrottoGrave, RR_COLOSSUS_GROTTO, RR_DESERT_COLOSSUS, ENTRANCE_GROTTO_EXIT(GROTTO_COLOSSUS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_LAKE_HYLIA, RR_LH_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LH_OFFSET) }, + { EntranceType::GrottoGrave, RR_LH_GROTTO, RR_LAKE_HYLIA, ENTRANCE_GROTTO_EXIT(GROTTO_LH_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_ZR_STORMS_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_FAIRY_OFFSET) }, + { EntranceType::GrottoGrave, RR_ZR_FAIRY_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_FAIRY_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_OPEN_OFFSET) }, + { EntranceType::GrottoGrave, RR_ZR_OPEN_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_OPEN_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_DMC_LOWER_NEARBY, RR_DMC_HAMMER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_HAMMER_OFFSET) }, + { EntranceType::GrottoGrave, RR_DMC_HAMMER_GROTTO, RR_DMC_LOWER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_HAMMER_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_DMC_UPPER_NEARBY, RR_DMC_UPPER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_UPPER_OFFSET) }, + { EntranceType::GrottoGrave, RR_DMC_UPPER_GROTTO, RR_DMC_UPPER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_UPPER_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_GC_GROTTO_PLATFORM, RR_GC_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GORON_CITY_OFFSET) }, + { EntranceType::GrottoGrave, RR_GC_GROTTO, RR_GC_GROTTO_PLATFORM, ENTRANCE_GROTTO_EXIT(GROTTO_GORON_CITY_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_TRAIL, RR_DMT_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_DMT_STORMS_GROTTO, RR_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_COW_OFFSET) }, + { EntranceType::GrottoGrave, RR_DMT_COW_GROTTO, RR_DEATH_MOUNTAIN_SUMMIT, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_COW_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_KAK_BACKYARD, RR_KAK_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_OPEN_OFFSET) }, + { EntranceType::GrottoGrave, RR_KAK_OPEN_GROTTO, RR_KAK_BACKYARD, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_OPEN_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_KAKARIKO_VILLAGE, RR_KAK_REDEAD_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_REDEAD_OFFSET) }, + { EntranceType::GrottoGrave, RR_KAK_REDEAD_GROTTO, RR_KAKARIKO_VILLAGE, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_REDEAD_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_CASTLE_GROUNDS, RR_HC_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HC_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_HC_STORMS_GROTTO, RR_CASTLE_GROUNDS, ENTRANCE_GROTTO_EXIT(GROTTO_HC_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_TEKTITE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_TEKTITE_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_TEKTITE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_TEKTITE_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_KAK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_KAK_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_NEAR_KAK_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_KAK_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_FAIRY_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_FAIRY_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_FAIRY_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_MARKET_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_MARKET_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_NEAR_MARKET_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_MARKET_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_COW_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_COW_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_COW_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_INSIDE_FENCE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_INSIDE_FENCE_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_INSIDE_FENCE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_INSIDE_FENCE_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_OPEN_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_OPEN_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_OPEN_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_SOUTHEAST_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_SOUTHEAST_OFFSET) }, + { EntranceType::GrottoGrave, RR_HF_SOUTHEAST_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_SOUTHEAST_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_LON_LON_RANCH, RR_LLR_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LLR_OFFSET) }, + { EntranceType::GrottoGrave, RR_LLR_GROTTO, RR_LON_LON_RANCH, ENTRANCE_GROTTO_EXIT(GROTTO_LLR_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_SFM_ENTRYWAY, RR_SFM_WOLFOS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_WOLFOS_OFFSET) }, + { EntranceType::GrottoGrave, RR_SFM_WOLFOS_GROTTO, RR_SFM_ENTRYWAY, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_WOLFOS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_SFM_STORMS_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_FAIRY_OFFSET) }, + { EntranceType::GrottoGrave, RR_SFM_FAIRY_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_FAIRY_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_LW_SCRUBS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_SCRUBS_OFFSET) }, + { EntranceType::GrottoGrave, RR_LW_SCRUBS_GROTTO, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_SCRUBS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_THE_LOST_WOODS, RR_LW_NEAR_SHORTCUTS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) }, + { EntranceType::GrottoGrave, RR_LW_NEAR_SHORTCUTS_GROTTO, RR_THE_LOST_WOODS, ENTRANCE_GROTTO_EXIT(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_KOKIRI_FOREST, RR_KF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KF_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_KF_STORMS_GROTTO, RR_KOKIRI_FOREST, ENTRANCE_GROTTO_EXIT(GROTTO_KF_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_ZORAS_DOMAIN_ISLAND, RR_ZD_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZD_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_ZD_STORMS_GROTTO, RR_ZORAS_DOMAIN_ISLAND, ENTRANCE_GROTTO_EXIT(GROTTO_ZD_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_GERUDO_FORTRESS, RR_GF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_GF_STORMS_GROTTO, RR_GERUDO_FORTRESS, ENTRANCE_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_GV_FORTRESS_SIDE, RR_GV_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_STORMS_OFFSET) }, + { EntranceType::GrottoGrave, RR_GV_STORMS_GROTTO, RR_GV_FORTRESS_SIDE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_STORMS_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_GV_GROTTO_LEDGE, RR_GV_OCTOROK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_OCTOROK_OFFSET) }, + { EntranceType::GrottoGrave, RR_GV_OCTOROK_GROTTO, RR_GV_GROTTO_LEDGE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_OCTOROK_OFFSET) } }, + { { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_DEKU_THEATER, ENTRANCE_GROTTO_LOAD(GROTTO_LW_DEKU_THEATRE_OFFSET) }, + { EntranceType::GrottoGrave, RR_DEKU_THEATER, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_DEKU_THEATRE_OFFSET) } }, + + // Graves have their own specified entrance indices + { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_SHIELD_GRAVE, ENTR_GRAVE_WITH_FAIRYS_FOUNTAIN_0 }, + { EntranceType::GrottoGrave, RR_GRAVEYARD_SHIELD_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_SHIELD_GRAVE_EXIT } }, + { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_HEART_PIECE_GRAVE, ENTR_REDEAD_GRAVE_0 }, + { EntranceType::GrottoGrave, RR_GRAVEYARD_HEART_PIECE_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_HEART_PIECE_GRAVE_EXIT } }, + { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_COMPOSERS_GRAVE, ENTR_ROYAL_FAMILYS_TOMB_0 }, + { EntranceType::GrottoGrave, RR_GRAVEYARD_COMPOSERS_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ROYAL_TOMB_EXIT } }, + { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_GRAVE, ENTR_WINDMILL_AND_DAMPES_GRAVE_GRAVE }, + { EntranceType::GrottoGrave, RR_GRAVEYARD_DAMPES_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_DAMPES_GRAVE_EXIT } }, + + { { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_LW_BRIDGE_FROM_FOREST, ENTR_LOST_WOODS_BRIDGE_EAST_EXIT }, + { EntranceType::Overworld, RR_LW_BRIDGE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_LOWER_EXIT } }, + { { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_SOUTH_EXIT }, + { EntranceType::Overworld, RR_LW_FOREST_EXIT, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_UPPER_EXIT } }, + { { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_GC_WOODS_WARP, ENTR_GORON_CITY_TUNNEL_SHORTCUT }, + { EntranceType::Overworld, RR_GC_WOODS_WARP, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_TUNNEL_SHORTCUT } }, + { { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_ZORAS_RIVER, ENTR_ZORAS_RIVER_UNDERWATER_SHORTCUT }, + { EntranceType::Overworld, RR_ZORAS_RIVER, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_UNDERWATER_SHORTCUT } }, + { { EntranceType::Overworld, RR_LW_BEYOND_MIDO, RR_SFM_ENTRYWAY, ENTR_SACRED_FOREST_MEADOW_SOUTH_EXIT }, + { EntranceType::Overworld, RR_SFM_ENTRYWAY, RR_LW_BEYOND_MIDO, ENTR_LOST_WOODS_NORTH_EXIT } }, + { { EntranceType::Overworld, RR_LW_BRIDGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_WOODED_EXIT }, + { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LW_BRIDGE, ENTR_LOST_WOODS_BRIDGE_WEST_EXIT } }, + { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_NORTH_EXIT }, + { EntranceType::Overworld, RR_LAKE_HYLIA, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_FENCE_EXIT } }, + { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_GERUDO_VALLEY, ENTR_GERUDO_VALLEY_EAST_EXIT }, + { EntranceType::Overworld, RR_GERUDO_VALLEY, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ROCKY_PATH } }, + { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NEAR_GUARD_EXIT }, + { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ON_BRIDGE_SPAWN } }, + { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_FRONT_GATE }, + { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_STAIRS_EXIT } }, + { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_ZR_FRONT, ENTR_ZORAS_RIVER_WEST_EXIT }, + { EntranceType::Overworld, RR_ZR_FRONT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_RIVER_EXIT } }, + { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_ENTRANCE }, + { EntranceType::Overworld, RR_LON_LON_RANCH, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_CENTER_EXIT } }, + { { EntranceType::Overworld, RR_LAKE_HYLIA, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_UNDERWATER_SHORTCUT }, + { EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_UNDERWATER_SHORTCUT } }, + { { EntranceType::Overworld, RR_GV_FORTRESS_SIDE, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_EAST_EXIT }, + { EntranceType::Overworld, RR_GERUDO_FORTRESS, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_WEST_EXIT } }, + { { EntranceType::Overworld, RR_GF_OUTSIDE_GATE, RR_WASTELAND_NEAR_FORTRESS, ENTR_HAUNTED_WASTELAND_EAST_EXIT }, + { EntranceType::Overworld, RR_WASTELAND_NEAR_FORTRESS, RR_GF_OUTSIDE_GATE, ENTR_GERUDOS_FORTRESS_GATE_EXIT } }, + { { EntranceType::Overworld, RR_WASTELAND_NEAR_COLOSSUS, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_EAST_EXIT }, + { EntranceType::Overworld, RR_DESERT_COLOSSUS, RR_WASTELAND_NEAR_COLOSSUS, ENTR_HAUNTED_WASTELAND_WEST_EXIT } }, + { { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_SOUTH_EXIT }, + { EntranceType::Overworld, RR_THE_MARKET, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NORTH_EXIT } }, + { { EntranceType::Overworld, RR_THE_MARKET, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_SOUTH_EXIT }, + { EntranceType::Overworld, RR_CASTLE_GROUNDS, RR_THE_MARKET, ENTR_MARKET_DAY_CASTLE_EXIT } }, + { { EntranceType::Overworld, RR_THE_MARKET, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_GOSSIP_STONE_EXIT }, + { EntranceType::Overworld, RR_TOT_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_DAY_TEMPLE_EXIT } }, + { { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ENTRANCE }, + { EntranceType::Overworld, RR_THE_GRAVEYARD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_SOUTHEAST_EXIT } }, + { { EntranceType::Overworld, RR_KAK_BEHIND_GATE, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_BOTTOM_EXIT }, + { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_KAK_BEHIND_GATE, ENTR_KAKARIKO_VILLAGE_GUARD_GATE } }, + { { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_GORON_CITY, ENTR_GORON_CITY_UPPER_EXIT }, + { EntranceType::Overworld, RR_GORON_CITY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_GC_EXIT } }, + { { EntranceType::Overworld, RR_GC_DARUNIAS_CHAMBER, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GC_EXIT }, + { EntranceType::Overworld, RR_DMC_LOWER_NEARBY, RR_GC_DARUNIAS_CHAMBER, ENTR_GORON_CITY_DARUNIA_ROOM_EXIT } }, + { { EntranceType::Overworld, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMC_UPPER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_UPPER_EXIT }, + { EntranceType::Overworld, RR_DMC_UPPER_NEARBY, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_SUMMIT_EXIT } }, + { { EntranceType::Overworld, RR_ZR_BEHIND_WATERFALL, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_ENTRANCE }, + { EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_ZR_BEHIND_WATERFALL, ENTR_ZORAS_RIVER_WATERFALL_EXIT } }, + { { EntranceType::Overworld, RR_ZD_BEHIND_KING_ZORA, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_TUNNEL_EXIT }, + { EntranceType::Overworld, RR_ZORAS_FOUNTAIN, RR_ZD_BEHIND_KING_ZORA, ENTR_ZORAS_DOMAIN_KING_ZORA_EXIT } }, + + { { EntranceType::Overworld, RR_GV_LOWER_STREAM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_RIVER_EXIT }, + NO_RETURN_ENTRANCE }, + + { { EntranceType::OwlDrop, RR_LH_OWL_FLIGHT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_OWL_DROP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::OwlDrop, RR_DMT_OWL_FLIGHT, RR_KAK_IMPAS_ROOFTOP, ENTR_KAKARIKO_VILLAGE_OWL_DROP }, + NO_RETURN_ENTRANCE }, + + { { EntranceType::Spawn, RR_CHILD_SPAWN, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_CHILD_SPAWN }, + NO_RETURN_ENTRANCE }, + { { EntranceType::Spawn, RR_ADULT_SPAWN, RR_TEMPLE_OF_TIME, ENTR_HYRULE_FIELD_10 }, + NO_RETURN_ENTRANCE }, // 0x282 is an unused entrance index repurposed to differentiate between + // Adult Spawn and prelude of light (normally they both use 0x5F4) + { { EntranceType::WarpSong, RR_MINUET_OF_FOREST_WARP, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_WARP_PAD }, + NO_RETURN_ENTRANCE }, + { { EntranceType::WarpSong, RR_BOLERO_OF_FIRE_WARP, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD }, + NO_RETURN_ENTRANCE }, + { { EntranceType::WarpSong, RR_SERENADE_OF_WATER_WARP, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WARP_PAD }, + NO_RETURN_ENTRANCE }, + { { EntranceType::WarpSong, RR_REQUIEM_OF_SPIRIT_WARP, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_WARP_PAD }, + NO_RETURN_ENTRANCE }, + { { EntranceType::WarpSong, RR_NOCTURNE_OF_SHADOW_WARP, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_WARP_PAD }, + NO_RETURN_ENTRANCE }, + { { EntranceType::WarpSong, RR_PRELUDE_OF_LIGHT_WARP, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_WARP_PAD }, + NO_RETURN_ENTRANCE }, + + { { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, ENTR_DEKU_TREE_BOSS_ENTRANCE }, + { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT, ENTR_DEKU_TREE_BOSS_DOOR } }, + { { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE }, + { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT, ENTR_DODONGOS_CAVERN_BOSS_DOOR } }, + { { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, ENTR_JABU_JABU_BOSS_ENTRANCE }, + { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT, ENTR_JABU_JABU_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, ENTR_FOREST_TEMPLE_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, ENTR_FIRE_TEMPLE_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, ENTR_WATER_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, ENTR_WATER_TEMPLE_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, ENTR_SPIRIT_TEMPLE_BOSS_DOOR } }, + { { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE }, + { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, ENTR_SHADOW_TEMPLE_BOSS_DOOR } }, + + { { EntranceType::BlueWarp, RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_DEKU_TREE_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_DODONGO_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_JABU_JABU_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_FOREST_TEMPLE_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_FIRE_TEMPLE_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WATER_TEMPLE_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_SPIRIT_TEMPLE_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + { { EntranceType::BlueWarp, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_SHADOW_TEMPLE_BLUE_WARP }, + NO_RETURN_ENTRANCE }, + // clang-format on + }; + auto ctx = Rando::Context::GetInstance(); for (auto& entrancePair : entranceShuffleTable) { @@ -262,6 +574,7 @@ void SetAllEntrancesData(std::vector& entranceShuffleTable) { forwardEntrance->SetIndex(forwardEntry.index); forwardEntrance->SetType(forwardEntry.type); forwardEntrance->SetAsPrimary(); + entranceMap[forwardEntry.index] = forwardEntrance; // When decouple entrances is on, mark the forward entrance if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) { @@ -273,6 +586,7 @@ void SetAllEntrancesData(std::vector& entranceShuffleTable) { returnEntrance->SetIndex(returnEntry.index); returnEntrance->SetType(returnEntry.type); forwardEntrance->BindTwoWay(returnEntrance); + entranceMap[returnEntry.index] = returnEntrance; // Mark reverse entrance as decoupled if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) { @@ -790,382 +1104,72 @@ bool EntranceShuffler::ShuffleEntrances(std::vector& entrances, std:: } } - // all entrances were validly connected - return true; -} - -void EntranceShuffler::ShuffleEntrancePool(std::vector& entrancePool, - std::vector& targetEntrances, int retryCount) { - mNoRandomEntrances = false; - - auto splitEntrances = SplitEntrancesByRequirements(entrancePool, targetEntrances); - - auto& restrictiveEntrances = splitEntrances[0]; - auto& softEntrances = splitEntrances[1]; - - int retries = retryCount; - while (retries > 0) { - if (retries != retryCount) { -#ifdef ENABLE_DEBUG - std::string ticks = std::to_string(svcGetSystemTick()); - auto message = "Failed to connect entrances. Retrying " + std::to_string(retries) + - " more times.\nDumping World Graph at " + ticks + "\n"; - SPDLOG_DEBUG(message); - // Regions::DumpWorldGraph(ticks); -#endif - } - retries--; - - std::vector rollbacks = {}; - - // Shuffle Restrictive Entrances first while more regions are available in - // order to heavily reduce the chances of the placement failing - bool success = ShuffleEntrances(restrictiveEntrances, targetEntrances, rollbacks); - if (success) { - success = ShuffleEntrances(softEntrances, targetEntrances, rollbacks); - if (!success) { - for (auto& pair : rollbacks) { - RestoreConnections(pair.first, pair.second); - mCurNumRandomizedEntrances--; - } - continue; - } - } else { - for (auto& pair : rollbacks) { - RestoreConnections(pair.first, pair.second); - mCurNumRandomizedEntrances--; - } - continue; - } - - // If there are no issues, log the connections and continue - for (auto& pair : rollbacks) { - ConfirmReplacement(pair.first, pair.second); - } - break; - } - - if (retries <= 0) { - SPDLOG_DEBUG("Entrance placement attempt count exceeded. Restarting randomization completely"); - mEntranceShuffleFailure = true; - } -} - -int EntranceShuffler::ShuffleAllEntrances() { - auto ctx = Rando::Context::GetInstance(); - - mTotalRandomizableEntrances = 0; - mCurNumRandomizedEntrances = 0; - - std::vector entranceShuffleTable = { - // clang-format off - // Type Parent Region Connected Region Index - { { EntranceType::Dungeon, RR_KF_OUTSIDE_DEKU_TREE, RR_DEKU_TREE_ENTRYWAY, ENTR_DEKU_TREE_ENTRANCE }, - { EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_OUTSIDE_DEKU_TREE } }, - { { EntranceType::Dungeon, RR_DEATH_MOUNTAIN_TRAIL, RR_DODONGOS_CAVERN_ENTRYWAY, ENTR_DODONGOS_CAVERN_ENTRANCE }, - { EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_OUTSIDE_DODONGOS_CAVERN } }, - { { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_JABU_JABUS_BELLY_ENTRYWAY, ENTR_JABU_JABU_ENTRANCE }, - { EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_JABU_JABU } }, - { { EntranceType::Dungeon, RR_SACRED_FOREST_MEADOW, RR_FOREST_TEMPLE_ENTRYWAY, ENTR_FOREST_TEMPLE_ENTRANCE }, - { EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_OUTSIDE_TEMPLE } }, - { { EntranceType::Dungeon, RR_DMC_CENTRAL_LOCAL, RR_FIRE_TEMPLE_ENTRYWAY, ENTR_FIRE_TEMPLE_ENTRANCE }, - { EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_OUTSIDE_TEMPLE } }, - { { EntranceType::Dungeon, RR_LAKE_HYLIA, RR_WATER_TEMPLE_ENTRYWAY, ENTR_WATER_TEMPLE_ENTRANCE }, - { EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_TEMPLE } }, - { { EntranceType::Dungeon, RR_DESERT_COLOSSUS, RR_SPIRIT_TEMPLE_ENTRYWAY, ENTR_SPIRIT_TEMPLE_ENTRANCE }, - { EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_OUTSIDE_TEMPLE, ENTR_DESERT_COLOSSUS_OUTSIDE_TEMPLE } }, - { { EntranceType::Dungeon, RR_GRAVEYARD_WARP_PAD_REGION, RR_SHADOW_TEMPLE_ENTRYWAY, ENTR_SHADOW_TEMPLE_ENTRANCE }, - { EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_OUTSIDE_TEMPLE } }, - { { EntranceType::Dungeon, RR_KAK_WELL, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, ENTR_BOTTOM_OF_THE_WELL_ENTRANCE }, - { EntranceType::Dungeon, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, RR_KAK_WELL, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BOTTOM_OF_THE_WELL } }, - { { EntranceType::Dungeon, RR_ZF_LEDGE, RR_ICE_CAVERN_ENTRYWAY, ENTR_ICE_CAVERN_ENTRANCE }, - { EntranceType::Dungeon, RR_ICE_CAVERN_ENTRYWAY, RR_ZF_LEDGE, ENTR_ZORAS_FOUNTAIN_OUTSIDE_ICE_CAVERN } }, - { { EntranceType::Dungeon, RR_GERUDO_FORTRESS, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, ENTR_GERUDO_TRAINING_GROUND_ENTRANCE }, - { EntranceType::Dungeon, RR_GERUDO_TRAINING_GROUND_ENTRYWAY, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_OUTSIDE_GERUDO_TRAINING_GROUND } }, - { { EntranceType::GanonDungeon, RR_GANONS_CASTLE_LEDGE, RR_GANONS_CASTLE_ENTRYWAY, ENTR_INSIDE_GANONS_CASTLE_ENTRANCE }, - { EntranceType::GanonDungeon, RR_GANONS_CASTLE_ENTRYWAY, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, ENTR_CASTLE_GROUNDS_RAINBOW_BRIDGE_EXIT } }, - - { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_MIDOS_HOUSE, ENTR_MIDOS_HOUSE_0 }, - { EntranceType::Interior, RR_KF_MIDOS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_MIDOS_HOUSE } }, - { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_SARIAS_HOUSE, ENTR_SARIAS_HOUSE_0 }, - { EntranceType::Interior, RR_KF_SARIAS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SARIAS_HOUSE } }, - { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_HOUSE_OF_TWINS, ENTR_TWINS_HOUSE_0 }, - { EntranceType::Interior, RR_KF_HOUSE_OF_TWINS, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_TWINS_HOUSE } }, - { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KNOW_IT_ALL_HOUSE, ENTR_KNOW_IT_ALL_BROS_HOUSE_0 }, - { EntranceType::Interior, RR_KF_KNOW_IT_ALL_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_KNOW_IT_ALL_HOUSE } }, - { { EntranceType::Interior, RR_KOKIRI_FOREST, RR_KF_KOKIRI_SHOP, ENTR_KOKIRI_SHOP_0 }, - { EntranceType::Interior, RR_KF_KOKIRI_SHOP, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_SHOP } }, - { { EntranceType::Interior, RR_LAKE_HYLIA, RR_LH_LAB, ENTR_LAKESIDE_LABORATORY_0 }, - { EntranceType::Interior, RR_LH_LAB, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_OUTSIDE_LAB } }, - { { EntranceType::Interior, RR_LH_FISHING_ISLAND, RR_LH_FISHING_POND, ENTR_FISHING_POND_0 }, - { EntranceType::Interior, RR_LH_FISHING_POND, RR_LH_FISHING_ISLAND, ENTR_LAKE_HYLIA_OUTSIDE_FISHING_POND } }, - { { EntranceType::Interior, RR_GV_FORTRESS_SIDE, RR_GV_CARPENTER_TENT, ENTR_CARPENTERS_TENT_0 }, - { EntranceType::Interior, RR_GV_CARPENTER_TENT, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_OUTSIDE_TENT } }, - { { EntranceType::Interior, RR_MARKET_ENTRANCE, RR_MARKET_GUARD_HOUSE, ENTR_MARKET_GUARD_HOUSE_0 }, - { EntranceType::Interior, RR_MARKET_GUARD_HOUSE, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_OUTSIDE_GUARD_HOUSE } }, - { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_MASK_SHOP, ENTR_HAPPY_MASK_SHOP_0 }, - { EntranceType::Interior, RR_MARKET_MASK_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_HAPPY_MASK_SHOP } }, - { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BOMBCHU_BOWLING, ENTR_BOMBCHU_BOWLING_ALLEY_0 }, - { EntranceType::Interior, RR_MARKET_BOMBCHU_BOWLING, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BOMBCHU_BOWLING } }, - { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_POTION_SHOP, ENTR_POTION_SHOP_MARKET_0 }, - { EntranceType::Interior, RR_MARKET_POTION_SHOP, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_POTION_SHOP } }, - { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_TREASURE_CHEST_GAME, ENTR_TREASURE_BOX_SHOP_0 }, - { EntranceType::Interior, RR_MARKET_TREASURE_CHEST_GAME, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_TREASURE_BOX_SHOP } }, - { { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_BOMBCHU_SHOP, ENTR_BOMBCHU_SHOP_1 }, - { EntranceType::Interior, RR_MARKET_BOMBCHU_SHOP, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_BOMBCHU_SHOP } }, - { { EntranceType::Interior, RR_MARKET_BACK_ALLEY, RR_MARKET_MAN_IN_GREEN_HOUSE, ENTR_BACK_ALLEY_MAN_IN_GREEN_HOUSE }, - { EntranceType::Interior, RR_MARKET_MAN_IN_GREEN_HOUSE, RR_MARKET_BACK_ALLEY, ENTR_BACK_ALLEY_DAY_OUTSIDE_MAN_IN_GREEN_HOUSE } }, - { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_CARPENTER_BOSS_HOUSE, ENTR_KAKARIKO_CENTER_GUEST_HOUSE_0 }, - { EntranceType::Interior, RR_KAK_CARPENTER_BOSS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_CENTER_GUEST_HOUSE } }, - { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_HOUSE_OF_SKULLTULA, ENTR_HOUSE_OF_SKULLTULA_0 }, - { EntranceType::Interior, RR_KAK_HOUSE_OF_SKULLTULA, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SKULKLTULA_HOUSE } }, - { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_IMPAS_HOUSE, ENTR_IMPAS_HOUSE_FRONT }, - { EntranceType::Interior, RR_KAK_IMPAS_HOUSE, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_FRONT } }, - { { EntranceType::Interior, RR_KAK_IMPAS_LEDGE, RR_KAK_IMPAS_HOUSE_BACK, ENTR_IMPAS_HOUSE_BACK }, - { EntranceType::Interior, RR_KAK_IMPAS_HOUSE_BACK, RR_KAK_IMPAS_LEDGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_IMPAS_HOUSE_BACK } }, - { { EntranceType::Interior, RR_KAK_BACKYARD, RR_KAK_ODD_POTION_BUILDING, ENTR_POTION_SHOP_GRANNY_0 }, - { EntranceType::Interior, RR_KAK_ODD_POTION_BUILDING, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOP_GRANNY } }, - { { EntranceType::Interior, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_HOUSE, ENTR_GRAVEKEEPERS_HUT_0 }, - { EntranceType::Interior, RR_GRAVEYARD_DAMPES_HOUSE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_OUTSIDE_DAMPES_HUT } }, - { { EntranceType::Interior, RR_GORON_CITY, RR_GC_SHOP, ENTR_GORON_SHOP_0 }, - { EntranceType::Interior, RR_GC_SHOP, RR_GORON_CITY, ENTR_GORON_CITY_OUTSIDE_SHOP } }, - { { EntranceType::Interior, RR_ZORAS_DOMAIN, RR_ZD_SHOP, ENTR_ZORA_SHOP_0 }, - { EntranceType::Interior, RR_ZD_SHOP, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_OUTSIDE_SHOP } }, - { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TALONS_HOUSE, ENTR_LON_LON_BUILDINGS_TALONS_HOUSE }, - { EntranceType::Interior, RR_LLR_TALONS_HOUSE, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TALONS_HOUSE } }, - { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_STABLES, ENTR_STABLE_0 }, - { EntranceType::Interior, RR_LLR_STABLES, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_STABLES } }, - { { EntranceType::Interior, RR_LON_LON_RANCH, RR_LLR_TOWER, ENTR_LON_LON_BUILDINGS_TOWER }, - { EntranceType::Interior, RR_LLR_TOWER, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_OUTSIDE_TOWER } }, - { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_BAZAAR, ENTR_BAZAAR_1 }, - { EntranceType::Interior, RR_MARKET_BAZAAR, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_BAZAAR } }, - { { EntranceType::Interior, RR_THE_MARKET, RR_MARKET_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_1 }, - { EntranceType::Interior, RR_MARKET_SHOOTING_GALLERY, RR_THE_MARKET, ENTR_MARKET_DAY_OUTSIDE_SHOOTING_GALLERY } }, - { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_BAZAAR, ENTR_BAZAAR_0 }, - { EntranceType::Interior, RR_KAK_BAZAAR, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_BAZAAR } }, - { { EntranceType::Interior, RR_KAKARIKO_VILLAGE, RR_KAK_SHOOTING_GALLERY, ENTR_SHOOTING_GALLERY_0 }, - { EntranceType::Interior, RR_KAK_SHOOTING_GALLERY, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_SHOOTING_GALLERY } }, - { { EntranceType::Interior, RR_DESERT_COLOSSUS, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_NAYRUS_COLOSSUS }, - { EntranceType::Interior, RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_GREAT_FAIRY_EXIT } }, - { { EntranceType::Interior, RR_HYRULE_CASTLE_GROUNDS, RR_HC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_DINS_HC }, - { EntranceType::Interior, RR_HC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_GREAT_FAIRY_EXIT } }, - { { EntranceType::Interior, RR_GANONS_CASTLE_GROUNDS, RR_OGC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_OGC_DD }, - // 0x3E8 is an unused entrance index repruposed to differentiate between the HC and OGC fairy - // fountain exits (normally they both use 0x340) - { EntranceType::Interior, RR_OGC_GREAT_FAIRY_FOUNTAIN, RR_CASTLE_GROUNDS, ENTR_POTION_SHOP_KAKARIKO_1 } }, - { { EntranceType::Interior, RR_DMC_LOWER_NEARBY, RR_DMC_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMC }, - { EntranceType::Interior, RR_DMC_GREAT_FAIRY_FOUNTAIN, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GREAT_FAIRY_EXIT } }, - { { EntranceType::Interior, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_MAGIC_DMT }, - { EntranceType::Interior, RR_DMT_GREAT_FAIRY_FOUNTAIN, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_GREAT_FAIRY_EXIT } }, - { { EntranceType::Interior, RR_ZORAS_FOUNTAIN, RR_ZF_GREAT_FAIRY_FOUNTAIN, ENTR_GREAT_FAIRYS_FOUNTAIN_SPELLS_FARORES_ZF }, - { EntranceType::Interior, RR_ZF_GREAT_FAIRY_FOUNTAIN, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_OUTSIDE_GREAT_FAIRY } }, - - { { EntranceType::SpecialInterior, RR_KOKIRI_FOREST, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_1 }, - { EntranceType::SpecialInterior, RR_KF_LINKS_HOUSE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_OUTSIDE_LINKS_HOUSE } }, - { { EntranceType::SpecialInterior, RR_TOT_ENTRANCE, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_ENTRANCE }, - { EntranceType::SpecialInterior, RR_TEMPLE_OF_TIME, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_OUTSIDE_TEMPLE } }, - { { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_WINDMILL, ENTR_WINDMILL_AND_DAMPES_GRAVE_WINDMILL }, - { EntranceType::SpecialInterior, RR_KAK_WINDMILL, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_WINDMILL } }, - { { EntranceType::SpecialInterior, RR_KAKARIKO_VILLAGE, RR_KAK_POTION_SHOP_FRONT, ENTR_POTION_SHOP_KAKARIKO_FRONT }, - { EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_FRONT, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_FRONT } }, - { { EntranceType::SpecialInterior, RR_KAK_BACKYARD, RR_KAK_POTION_SHOP_BACK, ENTR_POTION_SHOP_KAKARIKO_BACK }, - { EntranceType::SpecialInterior, RR_KAK_POTION_SHOP_BACK, RR_KAK_BACKYARD, ENTR_KAKARIKO_VILLAGE_OUTSIDE_POTION_SHOP_BACK } }, - - // Grotto Loads use an entrance index of 0x0700 + their grotto id. The id is used as index for the - // grottoLoadTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c - // Grotto Returns use an entrance index of 0x0800 + their grotto id. The id is used as index for the - // grottoReturnTable in soh/soh/Enhancements/randomizer/randomizer_grotto.c - { { EntranceType::GrottoGrave, RR_DESERT_COLOSSUS, RR_COLOSSUS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_COLOSSUS_OFFSET) }, - { EntranceType::GrottoGrave, RR_COLOSSUS_GROTTO, RR_DESERT_COLOSSUS, ENTRANCE_GROTTO_EXIT(GROTTO_COLOSSUS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_LAKE_HYLIA, RR_LH_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LH_OFFSET) }, - { EntranceType::GrottoGrave, RR_LH_GROTTO, RR_LAKE_HYLIA, ENTRANCE_GROTTO_EXIT(GROTTO_LH_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_ZR_STORMS_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_FAIRY_OFFSET) }, - { EntranceType::GrottoGrave, RR_ZR_FAIRY_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_FAIRY_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_ZORAS_RIVER, RR_ZR_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZR_OPEN_OFFSET) }, - { EntranceType::GrottoGrave, RR_ZR_OPEN_GROTTO, RR_ZORAS_RIVER, ENTRANCE_GROTTO_EXIT(GROTTO_ZR_OPEN_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_DMC_LOWER_NEARBY, RR_DMC_HAMMER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_HAMMER_OFFSET) }, - { EntranceType::GrottoGrave, RR_DMC_HAMMER_GROTTO, RR_DMC_LOWER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_HAMMER_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_DMC_UPPER_NEARBY, RR_DMC_UPPER_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMC_UPPER_OFFSET) }, - { EntranceType::GrottoGrave, RR_DMC_UPPER_GROTTO, RR_DMC_UPPER_LOCAL, ENTRANCE_GROTTO_EXIT(GROTTO_DMC_UPPER_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_GC_GROTTO_PLATFORM, RR_GC_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GORON_CITY_OFFSET) }, - { EntranceType::GrottoGrave, RR_GC_GROTTO, RR_GC_GROTTO_PLATFORM, ENTRANCE_GROTTO_EXIT(GROTTO_GORON_CITY_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_TRAIL, RR_DMT_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_DMT_STORMS_GROTTO, RR_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMT_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_DMT_COW_OFFSET) }, - { EntranceType::GrottoGrave, RR_DMT_COW_GROTTO, RR_DEATH_MOUNTAIN_SUMMIT, ENTRANCE_GROTTO_EXIT(GROTTO_DMT_COW_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_KAK_BACKYARD, RR_KAK_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_OPEN_OFFSET) }, - { EntranceType::GrottoGrave, RR_KAK_OPEN_GROTTO, RR_KAK_BACKYARD, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_OPEN_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_KAKARIKO_VILLAGE, RR_KAK_REDEAD_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KAK_REDEAD_OFFSET) }, - { EntranceType::GrottoGrave, RR_KAK_REDEAD_GROTTO, RR_KAKARIKO_VILLAGE, ENTRANCE_GROTTO_EXIT(GROTTO_KAK_REDEAD_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_CASTLE_GROUNDS, RR_HC_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HC_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_HC_STORMS_GROTTO, RR_CASTLE_GROUNDS, ENTRANCE_GROTTO_EXIT(GROTTO_HC_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_TEKTITE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_TEKTITE_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_TEKTITE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_TEKTITE_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_KAK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_KAK_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_NEAR_KAK_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_KAK_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_FAIRY_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_FAIRY_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_FAIRY_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_NEAR_MARKET_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_NEAR_MARKET_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_NEAR_MARKET_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_NEAR_MARKET_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_COW_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_COW_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_COW_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_COW_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_INSIDE_FENCE_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_INSIDE_FENCE_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_INSIDE_FENCE_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_INSIDE_FENCE_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_OPEN_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_OPEN_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_OPEN_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_OPEN_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_HYRULE_FIELD, RR_HF_SOUTHEAST_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_HF_SOUTHEAST_OFFSET) }, - { EntranceType::GrottoGrave, RR_HF_SOUTHEAST_GROTTO, RR_HYRULE_FIELD, ENTRANCE_GROTTO_EXIT(GROTTO_HF_SOUTHEAST_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_LON_LON_RANCH, RR_LLR_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LLR_OFFSET) }, - { EntranceType::GrottoGrave, RR_LLR_GROTTO, RR_LON_LON_RANCH, ENTRANCE_GROTTO_EXIT(GROTTO_LLR_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_SFM_ENTRYWAY, RR_SFM_WOLFOS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_WOLFOS_OFFSET) }, - { EntranceType::GrottoGrave, RR_SFM_WOLFOS_GROTTO, RR_SFM_ENTRYWAY, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_WOLFOS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_SFM_STORMS_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_SACRED_FOREST_MEADOW, RR_SFM_FAIRY_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_SFM_FAIRY_OFFSET) }, - { EntranceType::GrottoGrave, RR_SFM_FAIRY_GROTTO, RR_SACRED_FOREST_MEADOW, ENTRANCE_GROTTO_EXIT(GROTTO_SFM_FAIRY_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_LW_SCRUBS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_SCRUBS_OFFSET) }, - { EntranceType::GrottoGrave, RR_LW_SCRUBS_GROTTO, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_SCRUBS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_THE_LOST_WOODS, RR_LW_NEAR_SHORTCUTS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) }, - { EntranceType::GrottoGrave, RR_LW_NEAR_SHORTCUTS_GROTTO, RR_THE_LOST_WOODS, ENTRANCE_GROTTO_EXIT(GROTTO_LW_NEAR_SHORTCUTS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_KOKIRI_FOREST, RR_KF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_KF_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_KF_STORMS_GROTTO, RR_KOKIRI_FOREST, ENTRANCE_GROTTO_EXIT(GROTTO_KF_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_ZORAS_DOMAIN_ISLAND, RR_ZD_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_ZD_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_ZD_STORMS_GROTTO, RR_ZORAS_DOMAIN_ISLAND, ENTRANCE_GROTTO_EXIT(GROTTO_ZD_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_GERUDO_FORTRESS, RR_GF_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GF_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_GF_STORMS_GROTTO, RR_GERUDO_FORTRESS, ENTRANCE_GROTTO_EXIT(GROTTO_GF_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_GV_FORTRESS_SIDE, RR_GV_STORMS_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_STORMS_OFFSET) }, - { EntranceType::GrottoGrave, RR_GV_STORMS_GROTTO, RR_GV_FORTRESS_SIDE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_STORMS_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_GV_GROTTO_LEDGE, RR_GV_OCTOROK_GROTTO, ENTRANCE_GROTTO_LOAD(GROTTO_GV_OCTOROK_OFFSET) }, - { EntranceType::GrottoGrave, RR_GV_OCTOROK_GROTTO, RR_GV_GROTTO_LEDGE, ENTRANCE_GROTTO_EXIT(GROTTO_GV_OCTOROK_OFFSET) } }, - { { EntranceType::GrottoGrave, RR_LW_BEYOND_MIDO, RR_DEKU_THEATER, ENTRANCE_GROTTO_LOAD(GROTTO_LW_DEKU_THEATRE_OFFSET) }, - { EntranceType::GrottoGrave, RR_DEKU_THEATER, RR_LW_BEYOND_MIDO, ENTRANCE_GROTTO_EXIT(GROTTO_LW_DEKU_THEATRE_OFFSET) } }, + // all entrances were validly connected + return true; +} - // Graves have their own specified entrance indices - { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_SHIELD_GRAVE, ENTR_GRAVE_WITH_FAIRYS_FOUNTAIN_0 }, - { EntranceType::GrottoGrave, RR_GRAVEYARD_SHIELD_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_SHIELD_GRAVE_EXIT } }, - { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_HEART_PIECE_GRAVE, ENTR_REDEAD_GRAVE_0 }, - { EntranceType::GrottoGrave, RR_GRAVEYARD_HEART_PIECE_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_HEART_PIECE_GRAVE_EXIT } }, - { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_COMPOSERS_GRAVE, ENTR_ROYAL_FAMILYS_TOMB_0 }, - { EntranceType::GrottoGrave, RR_GRAVEYARD_COMPOSERS_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ROYAL_TOMB_EXIT } }, - { { EntranceType::GrottoGrave, RR_THE_GRAVEYARD, RR_GRAVEYARD_DAMPES_GRAVE, ENTR_WINDMILL_AND_DAMPES_GRAVE_GRAVE }, - { EntranceType::GrottoGrave, RR_GRAVEYARD_DAMPES_GRAVE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_DAMPES_GRAVE_EXIT } }, +void EntranceShuffler::ShuffleEntrancePool(std::vector& entrancePool, + std::vector& targetEntrances, int retryCount) { + mNoRandomEntrances = false; - { { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_LW_BRIDGE_FROM_FOREST, ENTR_LOST_WOODS_BRIDGE_EAST_EXIT }, - { EntranceType::Overworld, RR_LW_BRIDGE, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_LOWER_EXIT } }, - { { EntranceType::Overworld, RR_KOKIRI_FOREST, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_SOUTH_EXIT }, - { EntranceType::Overworld, RR_LW_FOREST_EXIT, RR_KOKIRI_FOREST, ENTR_KOKIRI_FOREST_UPPER_EXIT } }, - { { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_GC_WOODS_WARP, ENTR_GORON_CITY_TUNNEL_SHORTCUT }, - { EntranceType::Overworld, RR_GC_WOODS_WARP, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_TUNNEL_SHORTCUT } }, - { { EntranceType::Overworld, RR_THE_LOST_WOODS, RR_ZORAS_RIVER, ENTR_ZORAS_RIVER_UNDERWATER_SHORTCUT }, - { EntranceType::Overworld, RR_ZORAS_RIVER, RR_THE_LOST_WOODS, ENTR_LOST_WOODS_UNDERWATER_SHORTCUT } }, - { { EntranceType::Overworld, RR_LW_BEYOND_MIDO, RR_SFM_ENTRYWAY, ENTR_SACRED_FOREST_MEADOW_SOUTH_EXIT }, - { EntranceType::Overworld, RR_SFM_ENTRYWAY, RR_LW_BEYOND_MIDO, ENTR_LOST_WOODS_NORTH_EXIT } }, - { { EntranceType::Overworld, RR_LW_BRIDGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_WOODED_EXIT }, - { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LW_BRIDGE, ENTR_LOST_WOODS_BRIDGE_WEST_EXIT } }, - { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_NORTH_EXIT }, - { EntranceType::Overworld, RR_LAKE_HYLIA, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_FENCE_EXIT } }, - { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_GERUDO_VALLEY, ENTR_GERUDO_VALLEY_EAST_EXIT }, - { EntranceType::Overworld, RR_GERUDO_VALLEY, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ROCKY_PATH } }, - { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NEAR_GUARD_EXIT }, - { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_ON_BRIDGE_SPAWN } }, - { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_FRONT_GATE }, - { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_STAIRS_EXIT } }, - { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_ZR_FRONT, ENTR_ZORAS_RIVER_WEST_EXIT }, - { EntranceType::Overworld, RR_ZR_FRONT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_RIVER_EXIT } }, - { { EntranceType::Overworld, RR_HYRULE_FIELD, RR_LON_LON_RANCH, ENTR_LON_LON_RANCH_ENTRANCE }, - { EntranceType::Overworld, RR_LON_LON_RANCH, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_CENTER_EXIT } }, - { { EntranceType::Overworld, RR_LAKE_HYLIA, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_UNDERWATER_SHORTCUT }, - { EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_UNDERWATER_SHORTCUT } }, - { { EntranceType::Overworld, RR_GV_FORTRESS_SIDE, RR_GERUDO_FORTRESS, ENTR_GERUDOS_FORTRESS_EAST_EXIT }, - { EntranceType::Overworld, RR_GERUDO_FORTRESS, RR_GV_FORTRESS_SIDE, ENTR_GERUDO_VALLEY_WEST_EXIT } }, - { { EntranceType::Overworld, RR_GF_OUTSIDE_GATE, RR_WASTELAND_NEAR_FORTRESS, ENTR_HAUNTED_WASTELAND_EAST_EXIT }, - { EntranceType::Overworld, RR_WASTELAND_NEAR_FORTRESS, RR_GF_OUTSIDE_GATE, ENTR_GERUDOS_FORTRESS_GATE_EXIT } }, - { { EntranceType::Overworld, RR_WASTELAND_NEAR_COLOSSUS, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_EAST_EXIT }, - { EntranceType::Overworld, RR_DESERT_COLOSSUS, RR_WASTELAND_NEAR_COLOSSUS, ENTR_HAUNTED_WASTELAND_WEST_EXIT } }, - { { EntranceType::Overworld, RR_MARKET_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_SOUTH_EXIT }, - { EntranceType::Overworld, RR_THE_MARKET, RR_MARKET_ENTRANCE, ENTR_MARKET_ENTRANCE_NORTH_EXIT } }, - { { EntranceType::Overworld, RR_THE_MARKET, RR_CASTLE_GROUNDS, ENTR_CASTLE_GROUNDS_SOUTH_EXIT }, - { EntranceType::Overworld, RR_CASTLE_GROUNDS, RR_THE_MARKET, ENTR_MARKET_DAY_CASTLE_EXIT } }, - { { EntranceType::Overworld, RR_THE_MARKET, RR_TOT_ENTRANCE, ENTR_TEMPLE_OF_TIME_EXTERIOR_DAY_GOSSIP_STONE_EXIT }, - { EntranceType::Overworld, RR_TOT_ENTRANCE, RR_THE_MARKET, ENTR_MARKET_DAY_TEMPLE_EXIT } }, - { { EntranceType::Overworld, RR_KAKARIKO_VILLAGE, RR_THE_GRAVEYARD, ENTR_GRAVEYARD_ENTRANCE }, - { EntranceType::Overworld, RR_THE_GRAVEYARD, RR_KAKARIKO_VILLAGE, ENTR_KAKARIKO_VILLAGE_SOUTHEAST_EXIT } }, - { { EntranceType::Overworld, RR_KAK_BEHIND_GATE, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_BOTTOM_EXIT }, - { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_KAK_BEHIND_GATE, ENTR_KAKARIKO_VILLAGE_GUARD_GATE } }, - { { EntranceType::Overworld, RR_DEATH_MOUNTAIN_TRAIL, RR_GORON_CITY, ENTR_GORON_CITY_UPPER_EXIT }, - { EntranceType::Overworld, RR_GORON_CITY, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_GC_EXIT } }, - { { EntranceType::Overworld, RR_GC_DARUNIAS_CHAMBER, RR_DMC_LOWER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_GC_EXIT }, - { EntranceType::Overworld, RR_DMC_LOWER_NEARBY, RR_GC_DARUNIAS_CHAMBER, ENTR_GORON_CITY_DARUNIA_ROOM_EXIT } }, - { { EntranceType::Overworld, RR_DEATH_MOUNTAIN_SUMMIT, RR_DMC_UPPER_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_UPPER_EXIT }, - { EntranceType::Overworld, RR_DMC_UPPER_NEARBY, RR_DEATH_MOUNTAIN_SUMMIT, ENTR_DEATH_MOUNTAIN_TRAIL_SUMMIT_EXIT } }, - { { EntranceType::Overworld, RR_ZR_BEHIND_WATERFALL, RR_ZORAS_DOMAIN, ENTR_ZORAS_DOMAIN_ENTRANCE }, - { EntranceType::Overworld, RR_ZORAS_DOMAIN, RR_ZR_BEHIND_WATERFALL, ENTR_ZORAS_RIVER_WATERFALL_EXIT } }, - { { EntranceType::Overworld, RR_ZD_BEHIND_KING_ZORA, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_TUNNEL_EXIT }, - { EntranceType::Overworld, RR_ZORAS_FOUNTAIN, RR_ZD_BEHIND_KING_ZORA, ENTR_ZORAS_DOMAIN_KING_ZORA_EXIT } }, + auto splitEntrances = SplitEntrancesByRequirements(entrancePool, targetEntrances); - { { EntranceType::Overworld, RR_GV_LOWER_STREAM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_RIVER_EXIT }, - NO_RETURN_ENTRANCE }, + auto& restrictiveEntrances = splitEntrances[0]; + auto& softEntrances = splitEntrances[1]; - { { EntranceType::OwlDrop, RR_LH_OWL_FLIGHT, RR_HYRULE_FIELD, ENTR_HYRULE_FIELD_OWL_DROP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::OwlDrop, RR_DMT_OWL_FLIGHT, RR_KAK_IMPAS_ROOFTOP, ENTR_KAKARIKO_VILLAGE_OWL_DROP }, - NO_RETURN_ENTRANCE }, + int retries = retryCount; + while (retries > 0) { + if (retries != retryCount) { +#ifdef ENABLE_DEBUG + std::string ticks = std::to_string(svcGetSystemTick()); + auto message = "Failed to connect entrances. Retrying " + std::to_string(retries) + + " more times.\nDumping World Graph at " + ticks + "\n"; + SPDLOG_DEBUG(message); + // Regions::DumpWorldGraph(ticks); +#endif + } + retries--; - { { EntranceType::Spawn, RR_CHILD_SPAWN, RR_KF_LINKS_HOUSE, ENTR_LINKS_HOUSE_CHILD_SPAWN }, - NO_RETURN_ENTRANCE }, - { { EntranceType::Spawn, RR_ADULT_SPAWN, RR_TEMPLE_OF_TIME, ENTR_HYRULE_FIELD_10 }, - NO_RETURN_ENTRANCE }, // 0x282 is an unused entrance index repurposed to differentiate between - // Adult Spawn and prelude of light (normally they both use 0x5F4) - { { EntranceType::WarpSong, RR_MINUET_OF_FOREST_WARP, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_WARP_PAD }, - NO_RETURN_ENTRANCE }, - { { EntranceType::WarpSong, RR_BOLERO_OF_FIRE_WARP, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD }, - NO_RETURN_ENTRANCE }, - { { EntranceType::WarpSong, RR_SERENADE_OF_WATER_WARP, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WARP_PAD }, - NO_RETURN_ENTRANCE }, - { { EntranceType::WarpSong, RR_REQUIEM_OF_SPIRIT_WARP, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_WARP_PAD }, - NO_RETURN_ENTRANCE }, - { { EntranceType::WarpSong, RR_NOCTURNE_OF_SHADOW_WARP, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_WARP_PAD }, - NO_RETURN_ENTRANCE }, - { { EntranceType::WarpSong, RR_PRELUDE_OF_LIGHT_WARP, RR_TEMPLE_OF_TIME, ENTR_TEMPLE_OF_TIME_WARP_PAD }, - NO_RETURN_ENTRANCE }, + std::vector rollbacks = {}; - { { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, ENTR_DEKU_TREE_BOSS_ENTRANCE }, - { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_EXIT, ENTR_DEKU_TREE_BOSS_DOOR } }, - { { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, ENTR_DODONGOS_CAVERN_BOSS_ENTRANCE }, - { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_EXIT, ENTR_DODONGOS_CAVERN_BOSS_DOOR } }, - { { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, ENTR_JABU_JABU_BOSS_ENTRANCE }, - { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_EXIT, ENTR_JABU_JABU_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, ENTR_FOREST_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, ENTR_FOREST_TEMPLE_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, ENTR_FIRE_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, ENTR_FIRE_TEMPLE_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, ENTR_WATER_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, ENTR_WATER_TEMPLE_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, ENTR_SPIRIT_TEMPLE_BOSS_DOOR } }, - { { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, ENTR_SHADOW_TEMPLE_BOSS_ENTRANCE }, - { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, ENTR_SHADOW_TEMPLE_BOSS_DOOR } }, + // Shuffle Restrictive Entrances first while more regions are available in + // order to heavily reduce the chances of the placement failing + bool success = ShuffleEntrances(restrictiveEntrances, targetEntrances, rollbacks); + if (success) { + success = ShuffleEntrances(softEntrances, targetEntrances, rollbacks); + if (!success) { + for (auto& pair : rollbacks) { + RestoreConnections(pair.first, pair.second); + mCurNumRandomizedEntrances--; + } + continue; + } + } else { + for (auto& pair : rollbacks) { + RestoreConnections(pair.first, pair.second); + mCurNumRandomizedEntrances--; + } + continue; + } - { { EntranceType::BlueWarp, RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE, ENTR_KOKIRI_FOREST_DEKU_TREE_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL, ENTR_DEATH_MOUNTAIN_TRAIL_DODONGO_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN, ENTR_ZORAS_FOUNTAIN_JABU_JABU_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW, ENTR_SACRED_FOREST_MEADOW_FOREST_TEMPLE_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL, ENTR_DEATH_MOUNTAIN_CRATER_FIRE_TEMPLE_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA, ENTR_LAKE_HYLIA_WATER_TEMPLE_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS, ENTR_DESERT_COLOSSUS_SPIRIT_TEMPLE_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - { { EntranceType::BlueWarp, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION, ENTR_GRAVEYARD_SHADOW_TEMPLE_BLUE_WARP }, - NO_RETURN_ENTRANCE }, - // clang-format on - }; + // If there are no issues, log the connections and continue + for (auto& pair : rollbacks) { + ConfirmReplacement(pair.first, pair.second); + } + break; + } + + if (retries <= 0) { + SPDLOG_DEBUG("Entrance placement attempt count exceeded. Restarting randomization completely"); + mEntranceShuffleFailure = true; + } +} + +int EntranceShuffler::ShuffleAllEntrances() { + auto ctx = Rando::Context::GetInstance(); + + mTotalRandomizableEntrances = 0; + mCurNumRandomizedEntrances = 0; std::map priorityEntranceTable = { { "Bolero", { { RR_DMC_CENTRAL_LOCAL }, { EntranceType::OwlDrop, EntranceType::WarpSong } } }, @@ -1178,7 +1182,7 @@ int EntranceShuffler::ShuffleAllEntrances() { }; mEntranceShuffleFailure = false; - SetAllEntrancesData(entranceShuffleTable); + SetAllEntrancesData(); EntrancePools oneWayEntrancePools = {}; EntrancePools entrancePools = {}; @@ -1663,6 +1667,26 @@ void EntranceShuffler::ParseJson(nlohmann::json spoilerFileJson) { } } catch (const std::exception& e) { throw e; } } + +void EntranceShuffler::ApplyEntranceOverrides() { + SetAllEntrancesData(); + + for (size_t i = 0; i < entranceOverrides.size(); i++) { + EntranceOverride entranceOverride = entranceOverrides[i]; + + if (entranceOverride.index == 0 && entranceOverride.destination == 0 && entranceOverride.override == 0 && + entranceOverride.overrideDestination == 0) { + continue; + } + + Entrance* entrance = entranceMap[entranceOverride.index]; + Entrance* overrideEntrance = entranceMap[entranceOverride.override]; + + entrance->Disconnect(); + entrance->Connect(overrideEntrance->GetOriginalConnectedRegionKey()); + entrance->SetAsShuffled(); + } +} } // namespace Rando extern "C" EntranceOverride* Randomizer_GetEntranceOverrides() { diff --git a/soh/soh/Enhancements/randomizer/entrance.h b/soh/soh/Enhancements/randomizer/entrance.h index 026da742644..6cb631db30f 100644 --- a/soh/soh/Enhancements/randomizer/entrance.h +++ b/soh/soh/Enhancements/randomizer/entrance.h @@ -128,6 +128,7 @@ class EntranceShuffler { void CreateEntranceOverrides(); void UnshuffleAllEntrances(); void ParseJson(nlohmann::json spoilerFileJson); + void ApplyEntranceOverrides(); private: std::vector AssumeEntrancePool(std::vector& entrancePool); diff --git a/soh/soh/Enhancements/randomizer/item.cpp b/soh/soh/Enhancements/randomizer/item.cpp index ee0dc940385..3b75a0c9546 100644 --- a/soh/soh/Enhancements/randomizer/item.cpp +++ b/soh/soh/Enhancements/randomizer/item.cpp @@ -46,14 +46,20 @@ Item::~Item() = default; void Item::ApplyEffect() const { auto ctx = Rando::Context::GetInstance(); - ctx->GetLogic()->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), true); - ctx->GetLogic()->SetInLogic(logicVal, true); + auto logic = ctx->GetLogic(); + if (!logic->CalculatingAvailableChecks) { + logic->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), true); + } + logic->SetInLogic(logicVal, true); } void Item::UndoEffect() const { auto ctx = Rando::Context::GetInstance(); - ctx->GetLogic()->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), false); - ctx->GetLogic()->SetInLogic(logicVal, false); + auto logic = ctx->GetLogic(); + if (!logic->CalculatingAvailableChecks) { + logic->ApplyItemEffect(StaticData::RetrieveItem(randomizerGet), false); + } + logic->SetInLogic(logicVal, false); } const Text& Item::GetName() const { diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 9c8df0e9bbc..8f7bb82d3cc 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -137,7 +137,6 @@ class Region { bool adultDay = false; bool adultNight = false; bool addedToPool = false; - ; void ApplyTimePass(); diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 7f4b7e1025e..597feb463a1 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2332,8 +2332,10 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) { inLogic[logicVal] = value; } -void Logic::Reset() { - NewSaveContext(); +void Logic::Reset(bool resetSaveContext /*= true*/) { + if (resetSaveContext) { + NewSaveContext(); + } StartPerformanceTimer(PT_LOGIC_RESET); memset(inLogic, false, sizeof(inLogic)); // Settings-dependent variables @@ -2372,37 +2374,39 @@ void Logic::Reset() { ShadowTrialClear = false; LightTrialClear = false; - // Ocarina C Buttons - bool ocBtnShuffle = ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS).Is(true); - SetRandoInf(RAND_INF_HAS_OCARINA_A, !ocBtnShuffle); - SetRandoInf(RAND_INF_HAS_OCARINA_C_UP, !ocBtnShuffle); - SetRandoInf(RAND_INF_HAS_OCARINA_C_DOWN, !ocBtnShuffle); - SetRandoInf(RAND_INF_HAS_OCARINA_C_LEFT, !ocBtnShuffle); - SetRandoInf(RAND_INF_HAS_OCARINA_C_RIGHT, !ocBtnShuffle); - - // Progressive Items - SetUpgrade(UPG_STICKS, ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG).Is(true) ? 0 : 1); - SetUpgrade(UPG_NUTS, ctx->GetOption(RSK_SHUFFLE_DEKU_NUT_BAG).Is(true) ? 0 : 1); - - // If we're not shuffling swim, we start with it - if (ctx->GetOption(RSK_SHUFFLE_SWIM).Is(false)) { - SetRandoInf(RAND_INF_CAN_SWIM, true); - } + if (resetSaveContext) { + // Ocarina C Buttons + bool ocBtnShuffle = ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS).Is(true); + SetRandoInf(RAND_INF_HAS_OCARINA_A, !ocBtnShuffle); + SetRandoInf(RAND_INF_HAS_OCARINA_C_UP, !ocBtnShuffle); + SetRandoInf(RAND_INF_HAS_OCARINA_C_DOWN, !ocBtnShuffle); + SetRandoInf(RAND_INF_HAS_OCARINA_C_LEFT, !ocBtnShuffle); + SetRandoInf(RAND_INF_HAS_OCARINA_C_RIGHT, !ocBtnShuffle); + + // Progressive Items + SetUpgrade(UPG_STICKS, ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG).Is(true) ? 0 : 1); + SetUpgrade(UPG_NUTS, ctx->GetOption(RSK_SHUFFLE_DEKU_NUT_BAG).Is(true) ? 0 : 1); + + // If we're not shuffling swim, we start with it + if (ctx->GetOption(RSK_SHUFFLE_SWIM).Is(false)) { + SetRandoInf(RAND_INF_CAN_SWIM, true); + } - // If we're not shuffling child's wallet, we start with it - if (ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET).Is(false)) { - SetRandoInf(RAND_INF_HAS_WALLET, true); - } + // If we're not shuffling child's wallet, we start with it + if (ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET).Is(false)) { + SetRandoInf(RAND_INF_HAS_WALLET, true); + } - // If we're not shuffling fishing pole, we start with it - if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE).Is(false)) { - SetRandoInf(RAND_INF_FISHING_POLE_FOUND, true); - } + // If we're not shuffling fishing pole, we start with it + if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE).Is(false)) { + SetRandoInf(RAND_INF_FISHING_POLE_FOUND, true); + } - // If not keysanity, start with 1 logical key to account for automatically unlocking the basement door in vanilla - // FiT - if (!IsFireLoopLocked && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) { - SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1); + // If not keysanity, start with 1 logical key to account for automatically unlocking the basement door in + // vanilla FiT + if (!IsFireLoopLocked && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) { + SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1); + } } // Bottle Count @@ -2455,7 +2459,9 @@ void Logic::Reset() { // Other AtDay = false; AtNight = false; - GetSaveContext()->linkAge = !ctx->GetOption(RSK_SELECTED_STARTING_AGE).Get(); + if (resetSaveContext) { + GetSaveContext()->linkAge = !ctx->GetOption(RSK_SELECTED_STARTING_AGE).Get(); + } // Events ShowedMidoSwordAndShield = false; @@ -2520,6 +2526,8 @@ void Logic::Reset() { Spirit1FSilverRupees = false; JabuRutoIn1F = false; + CalculatingAvailableChecks = false; + StopPerformanceTimer(PT_LOGIC_RESET); } } // namespace Rando diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 77c49d25feb..0c138a3ebf0 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -183,6 +183,9 @@ class Logic { /* --- END OF HELPERS AND LOCATION ACCESS --- */ + bool CalculatingAvailableChecks = false; + bool ACProcessUndiscoveredExits = false; + SaveContext* mSaveContext = nullptr; Logic(); bool CanUse(RandomizerGet itemName); @@ -254,7 +257,7 @@ class Logic { bool CanUseProjectile(); bool CanBuildRainbowBridge(); bool CanTriggerLACS(); - void Reset(); + void Reset(bool resetSaveContext = true); void SetContext(std::shared_ptr _ctx); bool GetInLogic(LogicVal logicVal); void SetInLogic(LogicVal logicVal, bool remove); diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 8648c982666..3a2c5e4758f 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -580,6 +580,13 @@ void CheckTrackerLoadGame(int32_t fileNum) { UpdateAllOrdering(); UpdateInventoryChecks(); UpdateFilters(); + + RegionTable_Init(); + + if (Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_ENTRANCES).Get()) { + Rando::Context::GetInstance()->GetEntranceShuffler()->ApplyEntranceOverrides(); + } + RecalculateAvailableChecks(); } @@ -898,7 +905,6 @@ void LoadFile() { SaveManager::Instance->LoadData("areasSpoiled", areasSpoiled, (uint32_t)0); UpdateAllOrdering(); UpdateAllAreas(); - RegionTable_Init(); } void Teardown() { @@ -1481,6 +1487,27 @@ void LoadSettings() { showOverworldFreestanding = false; showDungeonFreestanding = true; } + + switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GANONS_BOSS_KEY)) { + case RO_GANON_BOSS_KEY_LACS_STONES: + Rando::Context::GetInstance()->LACSCondition(RO_LACS_STONES); + break; + case RO_GANON_BOSS_KEY_LACS_MEDALLIONS: + Rando::Context::GetInstance()->LACSCondition(RO_LACS_MEDALLIONS); + break; + case RO_GANON_BOSS_KEY_LACS_REWARDS: + Rando::Context::GetInstance()->LACSCondition(RO_LACS_REWARDS); + break; + case RO_GANON_BOSS_KEY_LACS_DUNGEONS: + Rando::Context::GetInstance()->LACSCondition(RO_LACS_DUNGEONS); + break; + case RO_GANON_BOSS_KEY_LACS_TOKENS: + Rando::Context::GetInstance()->LACSCondition(RO_LACS_TOKENS); + break; + default: + Rando::Context::GetInstance()->LACSCondition(RO_LACS_VANILLA); + break; + } } bool IsCheckShuffled(RandomizerCheck rc) { @@ -1820,7 +1847,7 @@ void DrawLocation(RandomizerCheck rc) { case RCSHOW_IDENTIFIED: case RCSHOW_SEEN: if (IS_RANDO) { - if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP && !mystery && !itemLoc->IsAddedToPool()) { + if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP && !mystery) { if (status == RCSHOW_IDENTIFIED) { txt = OTRGlobals::Instance->gRandoContext->overrides[rc].GetTrickName().GetForLanguage( gSaveContext.language); @@ -1830,11 +1857,10 @@ void DrawLocation(RandomizerCheck rc) { .GetName() .GetForLanguage(gSaveContext.language); } - } else if (!mystery && !itemLoc->IsAddedToPool()) { + } else if (!mystery) { txt = itemLoc->GetPlacedItem().GetName().GetForLanguage(gSaveContext.language); } - if (IsVisibleInCheckTracker(rc) && status == RCSHOW_IDENTIFIED && !mystery && - !itemLoc->IsAddedToPool()) { + if (IsVisibleInCheckTracker(rc) && status == RCSHOW_IDENTIFIED && !mystery) { auto price = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetPrice(); if (price) { txt += fmt::format(" - {}", price); @@ -1958,7 +1984,7 @@ void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName, UIWidgets::PopStyleCombobox(); } -void RecalculateAvailableChecks() { +void RecalculateAvailableChecks(RandomizerRegion startingRegion /* = RR_ROOT */) { if (!enableAvailableChecks) { return; } @@ -1966,20 +1992,22 @@ void RecalculateAvailableChecks() { ResetPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS); StartPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS); + const auto& ctx = Rando::Context::GetInstance(); + std::vector targetLocations; targetLocations.reserve(RC_MAX); for (auto& location : Rando::StaticData::GetLocationTable()) { RandomizerCheck rc = location.GetRandomizerCheck(); - Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); + Rando::ItemLocation* itemLocation = ctx->GetItemLocation(rc); itemLocation->SetAvailable(false); if (!itemLocation->HasObtained()) { targetLocations.emplace_back(rc); } } - std::vector availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true); + std::vector availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true, startingRegion); for (auto& rc : availableChecks) { - const auto& itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); + const auto& itemLocation = ctx->GetItemLocation(rc); itemLocation->SetAvailable(true); } @@ -1987,7 +2015,7 @@ void RecalculateAvailableChecks() { for (auto& [rcArea, vec] : checksByArea) { areaChecksAvailable[rcArea] = 0; for (auto& rc : vec) { - Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); + Rando::ItemLocation* itemLocation = ctx->GetItemLocation(rc); if (itemLocation->IsAvailable() && IsVisibleInCheckTracker(rc) && !IsCheckHidden(rc)) { areaChecksAvailable[rcArea]++; } diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h index d6fd503b361..7966c4b370a 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h @@ -61,5 +61,5 @@ void UpdateAllOrdering(); void UpdateAllAreas(); void RecalculateAllAreaTotals(); void SpoilAreaFromCheck(RandomizerCheck rc); -void RecalculateAvailableChecks(); +void RecalculateAvailableChecks(RandomizerRegion startingRegion = RR_ROOT); } // namespace CheckTracker diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance.c b/soh/soh/Enhancements/randomizer/randomizer_entrance.c index 11d17e6218e..faec117eb05 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance.c +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance.c @@ -809,6 +809,7 @@ void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance) { if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) { u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex)); gSaveContext.ship.stats.entrancesDiscovered[idx] |= entranceBit; + CheckTracker_RecalculateAvailableChecks(); // Set reverse entrance when not decoupled if (!Randomizer_GetSettingValue(RSK_DECOUPLED_ENTRANCES) && !isReversedEntrance) { diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 6e494a6b5d5..a2db7ee0ea8 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -1739,6 +1739,14 @@ TrickOption& Settings::GetTrickOption(const RandomizerTrick key) { return mTrickOptions[key]; } +int Settings::GetRandomizerTrickByName(const std::string& name) { + const auto& it = mTrickNameToEnum.find(name); + if (it == mTrickNameToEnum.end()) { + return -1; + } + return it->second; +} + void Context::ResetTrickOptions() { for (int count = 0; count < RT_MAX; count++) { mTrickOptions[count].Set(0); // RANDOTODO this can probably be done better diff --git a/soh/soh/Enhancements/randomizer/settings.h b/soh/soh/Enhancements/randomizer/settings.h index c820a9df9b4..ed214471a51 100644 --- a/soh/soh/Enhancements/randomizer/settings.h +++ b/soh/soh/Enhancements/randomizer/settings.h @@ -50,6 +50,14 @@ class Settings { */ TrickOption& GetTrickOption(RandomizerTrick key); + /** + * @brief Get the RandomizerTrick corresponding to the provided name. + * + * @param name + * @return int RandomizerTrick index or -1 if not found + */ + int GetRandomizerTrickByName(const std::string& name); + /** * @brief Returns a reference to the entire array of options. * diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 3195bf2b7b8..9bd3008f803 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2615,4 +2615,7 @@ void SoH_ProcessDroppedFiles(std::string filePath) { return; } } -// #endregion + +extern "C" void CheckTracker_RecalculateAvailableChecks() { + CheckTracker::RecalculateAvailableChecks(); +} diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index b14d322d281..bcc5c86575b 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -163,6 +163,7 @@ void Gfx_UnregisterBlendedTexture(const char* name); void Gfx_TextureCacheDelete(const uint8_t* addr); void SaveManager_ThreadPoolWait(); void CheckTracker_OnMessageClose(); +void CheckTracker_RecalculateAvailableChecks(); GetItemID RetrieveGetItemIDFromItemID(ItemID itemID); RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID); diff --git a/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c index 81542eaad3e..a11180a7b5d 100644 --- a/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c +++ b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c @@ -131,8 +131,14 @@ void func_808BAF40(BgTokiSwd* this, PlayState* play) { Item_Give(play, ITEM_SWORD_MASTER); } play->csCtx.segment = D_808BB2F0; + + // Discover adult spawn + Entrance_SetEntranceDiscovered(ENTR_HYRULE_FIELD_10, false); } else { play->csCtx.segment = D_808BB7A0; + + // Discover child spawn + Entrance_SetEntranceDiscovered(ENTR_LINKS_HOUSE_CHILD_SPAWN, false); } Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_STOP); Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_MASTER_SWORD); From de96f3cd0a7f6d6adf4bf7e20719f0ef7b843843 Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Sat, 24 May 2025 12:39:53 -0500 Subject: [PATCH 092/157] Typo Fixes (#5533) * Fixed a couple typos. * Ran clang format. --- soh/soh/Enhancements/debugconsole.cpp | 12 ++++++------ soh/soh/Enhancements/randomizer/ShuffleCrates.cpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/soh/soh/Enhancements/debugconsole.cpp b/soh/soh/Enhancements/debugconsole.cpp index 32fc8e72f44..d25c3cfffef 100644 --- a/soh/soh/Enhancements/debugconsole.cpp +++ b/soh/soh/Enhancements/debugconsole.cpp @@ -1451,8 +1451,8 @@ static bool SfxHandler(std::shared_ptr Console, const std::vector return 0; } -static bool AvailabeChecksProcessUndiscoveredExitsHandler(std::shared_ptr Console, - const std::vector& args, std::string* output) { +static bool AvailableChecksProcessUndiscoveredExitsHandler(std::shared_ptr Console, + const std::vector& args, std::string* output) { const auto& logic = Rando::Context::GetInstance()->GetLogic(); bool enabled = false; @@ -1478,8 +1478,8 @@ static bool AvailabeChecksProcessUndiscoveredExitsHandler(std::shared_ptr Console, - const std::vector& args, std::string* output) { +static bool AvailableChecksRecalculateHandler(std::shared_ptr Console, + const std::vector& args, std::string* output) { RandomizerRegion startingRegion = RR_ROOT; if (args.size() > 1) { @@ -1758,11 +1758,11 @@ void DebugConsole_Init(void) { { "group_name", Ship::ArgumentType::TEXT, true }, } }); - CMD_REGISTER("acpue", { AvailabeChecksProcessUndiscoveredExitsHandler, + CMD_REGISTER("acpue", { AvailableChecksProcessUndiscoveredExitsHandler, "Available Checks - Process Undiscovered Exits", { { "enable", Ship::ArgumentType::NUMBER, true } } }); - CMD_REGISTER("acr", { AvailabeChecksRecalculateHandler, + CMD_REGISTER("acr", { AvailableChecksRecalculateHandler, "Available Checks - Recalculate", { { "starting_region", Ship::ArgumentType::NUMBER, true }, diff --git a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp index 3cb3419806c..fa88d455b1a 100644 --- a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp +++ b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp @@ -362,7 +362,7 @@ void Rando::StaticData::RegisterCrateLocations() { locationTable[RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_1] = Location::Crate(RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-389, 1518), "Near Impas House Adult Crate 1", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_1)); locationTable[RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-389, 1470), "Near Impas House Adult Crate 2", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_IMPAS_HOUSE_ADULT_CRATE_2)); locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_1] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-433, -401), "Near Bazaar Adult Crate 1", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_1)); - locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-489, -424), "Near Bazaar Adult Crate 2`", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_2)); + locationTable[RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2] = Location::Crate(RC_KAK_NEAR_BAZAAR_ADULT_CRATE_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-489, -424), "Near Bazaar Adult Crate 2", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_BAZAAR_ADULT_CRATE_2)); locationTable[RC_KAK_BEHIND_GS_HOUSE_ADULT_CRATE] = Location::Crate(RC_KAK_BEHIND_GS_HOUSE_ADULT_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-724, 871), "Behind GS House Adult Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_BEHIND_GS_HOUSE_ADULT_CRATE)); locationTable[RC_KAK_NEAR_GY_CHILD_CRATE] = Location::Crate(RC_KAK_NEAR_GY_CHILD_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1732, 1366), "Near Graveyard Child Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_GY_CHILD_CRATE)); locationTable[RC_KAK_NEAR_WINDMILL_CHILD_CRATE] = Location::Crate(RC_KAK_NEAR_WINDMILL_CHILD_CRATE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1170, 601), "Near Windmill Child Crate", RHT_CRATE_KAKARIKO_VILLAGE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_WINDMILL_CHILD_CRATE)); From f0e36c2694fe004cbadaa45d9149c8b67c8eba88 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Tue, 27 May 2025 21:19:30 +0100 Subject: [PATCH 093/157] Fix failure to assign areas to regions on initial spoiler load (#5540) --- soh/soh/Enhancements/randomizer/3drando/fill.hpp | 1 + soh/soh/Enhancements/randomizer/context.cpp | 2 +- soh/soh/Enhancements/randomizer/entrance.cpp | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.hpp b/soh/soh/Enhancements/randomizer/3drando/fill.hpp index 67043952f39..e8124c0dce0 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.hpp @@ -56,6 +56,7 @@ struct GetAccessibleLocationsStruct { void ClearProgress(); void VanillaFill(); int Fill(); +void SetAreas(); std::vector GetEmptyLocations(std::vector allowedLocations); diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp index 7178dd18dae..550cd0e9333 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/context.cpp @@ -400,9 +400,9 @@ void Context::ParseSpoiler(const char* spoilerFileName) { ParseHashIconIndexesJson(spoilerFileJson); Rando::Settings::GetInstance()->ParseJson(spoilerFileJson); ParseItemLocationsJson(spoilerFileJson); - ParseHintJson(spoilerFileJson); ParseTricksJson(spoilerFileJson); mEntranceShuffler->ParseJson(spoilerFileJson); + ParseHintJson(spoilerFileJson); mDungeons->ParseJson(spoilerFileJson); mTrials->ParseJson(spoilerFileJson); mSpoilerLoaded = true; diff --git a/soh/soh/Enhancements/randomizer/entrance.cpp b/soh/soh/Enhancements/randomizer/entrance.cpp index 45cde2f6f76..ea9b80ee30c 100644 --- a/soh/soh/Enhancements/randomizer/entrance.cpp +++ b/soh/soh/Enhancements/randomizer/entrance.cpp @@ -1666,6 +1666,10 @@ void EntranceShuffler::ParseJson(nlohmann::json spoilerFileJson) { } } } catch (const std::exception& e) { throw e; } + // We may need to reset more things here or elsewhere in spoiler loading + RegionTable_Init(); + ApplyEntranceOverrides(); + SetAreas(); } void EntranceShuffler::ApplyEntranceOverrides() { From fc10e36cdb72e39235852d059580761ae78a0954 Mon Sep 17 00:00:00 2001 From: Hunter Marshall Date: Tue, 27 May 2025 16:34:36 -0500 Subject: [PATCH 094/157] Fix minor mistakes in region table (#5545) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix minor mistakes in region table * Update soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp Co-authored-by: Philip Dubé --------- Co-authored-by: Philip Dubé --- .../randomizer/location_access/dungeons/dodongos_cavern.cpp | 5 +++-- .../randomizer/location_access/dungeons/fire_temple.cpp | 2 +- .../randomizer/location_access/dungeons/forest_temple.cpp | 2 +- .../location_access/dungeons/gerudo_training_ground.cpp | 2 +- .../randomizer/location_access/dungeons/ice_cavern.cpp | 2 +- .../randomizer/location_access/dungeons/spirit_temple.cpp | 6 +++--- .../randomizer/location_access/dungeons/water_temple.cpp | 6 +++--- .../randomizer/location_access/overworld/kakariko.cpp | 2 +- 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp index 9488f2d8229..bffc6c290ad 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp @@ -268,6 +268,7 @@ void RegionTable_Init_DodongosCavern() { LOCATION(RC_DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_FRONT, logic->CanStunDeku()), }, { //Exits + Entrance(RR_DODONGOS_CAVERN_MQ_BEGINNING, []{return true;}), Entrance(RR_DODONGOS_CAVERN_MQ_GOSSIP_STONE, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->CanBreakMudWalls() || logic->HasItem(RG_GORONS_BRACELET);});}), Entrance(RR_DODONGOS_CAVERN_MQ_MOUTH_SIDE_BRIDGE, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->BlastOrSmash() || logic->HasItem(RG_GORONS_BRACELET);});}), Entrance(RR_DODONGOS_CAVERN_MQ_STAIRS_LOWER, []{return Here(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return logic->BlastOrSmash() || logic->HasItem(RG_GORONS_BRACELET);});}), @@ -386,7 +387,7 @@ void RegionTable_Init_DodongosCavern() { Entrance(RR_DODONGOS_CAVERN_MQ_UPPER_LIZALFOS, []{return logic->CanUse(RG_STICKS) && logic->HasItem(RG_GORONS_BRACELET);}), //Implies access to RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM from here }); - areaTable[RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM] = Region("Dodongos Cavern MQ Torch Puzzle Lower", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_DODONGOS_CAVERN_MQ_BIG_BLOCK_ROOM] = Region("Dodongos Cavern MQ Big Block Room", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_DODONGOS_CAVERN_MQ_BIG_BLOCK_POT_1, logic->CanBreakPots()), LOCATION(RC_DODONGOS_CAVERN_MQ_BIG_BLOCK_POT_2, logic->CanBreakPots()), @@ -426,7 +427,7 @@ void RegionTable_Init_DodongosCavern() { Entrance(RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM, []{return Here(RR_DODONGOS_CAVERN_MQ_UPPER_LIZALFOS, []{return logic->CanKillEnemy(RE_LIZALFOS);});}), }); - areaTable[RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM] = Region("Dodongos Cavern MQ Before Upper Lizalfos", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_DODONGOS_CAVERN_MQ_TWO_FIRES_ROOM] = Region("Dodongos Cavern MQ Two Fires Room", "Dodongos Cavern", {RA_DODONGOS_CAVERN}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_1, logic->CanBreakPots()), LOCATION(RC_DODONGOS_CAVERN_MQ_TWO_FLAMES_POT_2, logic->CanBreakPots()), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp index 2e1a6ef1491..978f5eabf06 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp @@ -701,7 +701,7 @@ void RegionTable_Init_FireTemple() { Entrance(RR_FIRE_TEMPLE_MQ_UPPER_FLARE_DANCER, []{return true;}), }); - areaTable[RR_FIRE_TEMPLE_MQ_UPPER_FLARE_DANCER] = Region("Fire Temple MQ North Fire Maze", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_FIRE_TEMPLE_MQ_UPPER_FLARE_DANCER] = Region("Fire Temple MQ Upper Flare Dancer", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_FIRE_TEMPLE_MQ_FREESTANDING_KEY, logic->CanKillEnemy(RE_FLARE_DANCER)), }, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp index c8d80f98df2..3deacb0426e 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/forest_temple.cpp @@ -481,7 +481,7 @@ void RegionTable_Init_ForestTemple() { Entrance(RR_FOREST_TEMPLE_MQ_FALLING_ROOM, []{return logic->CanUse(RG_SONG_OF_TIME);}), }); - areaTable[RR_FOREST_TEMPLE_MQ_JOELLE_ROOM] = Region("Forest Temple MQ Joelle room", "Forest Temple", {RA_FOREST_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_FOREST_TEMPLE_MQ_JOELLE_ROOM] = Region("Forest Temple MQ Joelle Room", "Forest Temple", {RA_FOREST_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events EventAccess(&logic->ForestTempleJoelle, []{return logic->CanUse(RG_FAIRY_BOW);}), }, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp index 5466c598c9e..f1a0ebee7c5 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp @@ -149,7 +149,7 @@ void RegionTable_Init_GerudoTrainingGround() { Entrance(RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER, []{return logic->SmallKeys(RR_GERUDO_TRAINING_GROUND, 3);}), }); - areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER] = Region("Gerudo Training Ground MQ Center", "Gerudo Training Ground", {RA_GERUDO_TRAINING_GROUND}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER] = Region("Gerudo Training Ground MQ Maze Center", "Gerudo Training Ground", {RA_GERUDO_TRAINING_GROUND}, NO_DAY_NIGHT_CYCLE, { //Events EventAccess(&logic->MQGTGMazeSwitch, []{return logic->CanUse(RG_MEGATON_HAMMER);}), }, diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp index 7aa2066ca4d..20ff9b1e1a0 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp @@ -84,7 +84,7 @@ void RegionTable_Init_IceCavern() { //Exits //the switch for the glass blocking the entrance is linked to the switch that controls the glass around the skulltulla in RR_ICE_CAVERN_MQ_SCARECROW_ROOM //if you clear the ice, you can hit it with a pot from here. - Entrance(RR_ICE_CAVERN_BEGINNING, []{return logic->BlueFire();}), + Entrance(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->BlueFire();}), Entrance(RR_ICE_CAVERN_MQ_MAP_ROOM, []{return Here(RR_ICE_CAVERN_MQ_BEGINNING, []{return logic->CanKillEnemy(RE_WHITE_WOLFOS) && logic->CanKillEnemy(RE_FREEZARD);});}), Entrance(RR_ICE_CAVERN_MQ_COMPASS_ROOM, []{return logic->IsAdult && logic->BlueFire();}), Entrance(RR_ICE_CAVERN_MQ_SCARECROW_ROOM, []{return logic->BlueFire();}), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index 72aa9974b5b..7589c7b746b 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -194,13 +194,13 @@ void RegionTable_Init_SpiritTemple() { }); // Room to store the 2 pots in to handle glitch logic going backwards around the loop later - areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH] = Region("Spirit Temple MQ Gibdo Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH] = Region("Spirit Temple MQ 1F Gibdo Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_2, logic->CanBreakPots()), }, {}); - areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple MQ Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events //For non-fairy pot items, you can also get them with rang without killing the stalfos EventAccess(&logic->FairyPot, []{return Here(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->CanKillEnemy(RE_STALFOS);});}), @@ -315,7 +315,7 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ West Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);}), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp index ccbc2f6884f..61fb0962db8 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp @@ -362,7 +362,7 @@ void RegionTable_Init_WaterTemple() { Entrance(RR_WATER_TEMPLE_MQ_BOSS_DOOR, []{return logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_ICE_ARROWS) || logic->CanUse(RG_NAYRUS_LOVE);}), }); - areaTable[RR_WATER_TEMPLE_MQ_BOSS_DOOR] = Region("Water Temple MQ Main", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_WATER_TEMPLE_MQ_BOSS_DOOR] = Region("Water Temple MQ Boss Door", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_WATER_TEMPLE_MQ_3F_NORTH_LEDGE, []{return logic->CanUse(RG_ICE_ARROWS) || logic->TakeDamage();}), Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, []{return true;}), @@ -507,7 +507,7 @@ void RegionTable_Init_WaterTemple() { Entrance(RR_WATER_TEMPLE_MQ_BEHIND_BLUE_SWITCH_3F, []{return logic->CanUse(RG_LONGSHOT);}), }); - areaTable[RR_WATER_TEMPLE_MQ_BEHIND_BLUE_SWITCH_3F] = Region("Water Temple MQ Behind Blue Switch 2F", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_WATER_TEMPLE_MQ_BEHIND_BLUE_SWITCH_3F] = Region("Water Temple MQ Behind Blue Switch 3F", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_WATER_TEMPLE_MQ_GS_BEFORE_UPPER_WATER_SWITCH, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)), }, { @@ -552,7 +552,7 @@ void RegionTable_Init_WaterTemple() { }, {}); //This room exists to hold the wonderitems that drop from the emblems here. Specifically this assumes you are standing on the final ledge - areaTable[RR_WATER_TEMPLE_MQ_WATERFALL] = Region("Water Temple Waterfall", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_WATER_TEMPLE_MQ_WATERFALL] = Region("Water Temple MQ Waterfall", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_WATER_TEMPLE_MQ_3F_CENTRAL, []{return logic->SmallKeys(RR_WATER_TEMPLE, 1) && logic->CanUse(RG_LONGSHOT);}), Entrance(RR_WATER_TEMPLE_MQ_STALFOS_PIT, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp index 777e2905061..3e84bb72a1e 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp @@ -265,7 +265,7 @@ void RegionTable_Init_Kakariko() { Entrance(RR_DEATH_MOUNTAIN_TRAIL, []{return true;}), }); - areaTable[RR_KAK_WELL] = Region("Kak Behind Gate", "Kakariko Village", {RA_KAKARIKO_VILLAGE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_KAK_WELL] = Region("Kak Well", "Kakariko Village", {RA_KAKARIKO_VILLAGE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_KAKARIKO_VILLAGE, []{return logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE) || logic->DrainWell;}), Entrance(RR_BOTTOM_OF_THE_WELL_ENTRYWAY, []{return logic->IsChild || (logic->DrainWell && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).IsNot(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF));}), From 40da9997c59436d9acc52ba8a86b6c77a050b913 Mon Sep 17 00:00:00 2001 From: Pepe20129 <72659707+Pepe20129@users.noreply.github.com> Date: Thu, 29 May 2025 16:51:35 +0200 Subject: [PATCH 095/157] Fix some dark link issues (#5532) --- soh/soh/Enhancements/enemyrandomizer.cpp | 49 ++++++++++++++++++- .../vanilla-behavior/GIVanillaBehavior.h | 32 ++++++++++++ soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c | 2 +- .../actors/ovl_En_Torch2/z_en_torch2.c | 2 +- .../actors/ovl_player_actor/z_player.c | 20 ++++++-- 5 files changed, 97 insertions(+), 8 deletions(-) diff --git a/soh/soh/Enhancements/enemyrandomizer.cpp b/soh/soh/Enhancements/enemyrandomizer.cpp index bdb25971965..682ef026c7b 100644 --- a/soh/soh/Enhancements/enemyrandomizer.cpp +++ b/soh/soh/Enhancements/enemyrandomizer.cpp @@ -11,8 +11,13 @@ extern "C" { #include +#include "src/overlays/actors/ovl_En_Rr/z_en_rr.h" } +#define CVAR_ENEMY_RANDOMIZER_NAME CVAR_ENHANCEMENT("RandomizedEnemies") +#define CVAR_ENEMY_RANDOMIZER_DEFAULT ENEMY_RANDOMIZER_OFF +#define CVAR_ENEMY_RANDOMIZER_VALUE CVarGetInteger(CVAR_ENEMY_RANDOMIZER_NAME, CVAR_ENEMY_RANDOMIZER_DEFAULT) + const char* enemyCVarList[] = { CVAR_ENHANCEMENT("RandomizedEnemyList.Armos"), CVAR_ENHANCEMENT("RandomizedEnemyList.Arwing"), CVAR_ENHANCEMENT("RandomizedEnemyList.BabyDodongo"), CVAR_ENHANCEMENT("RandomizedEnemyList.Bari"), @@ -339,7 +344,7 @@ EnemyEntry GetRandomizedEnemyEntry(uint32_t seed, PlayState* play) { if (filteredEnemyList.size() == 0) { filteredEnemyList = selectedEnemyList; } - if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), ENEMY_RANDOMIZER_OFF) == ENEMY_RANDOMIZER_RANDOM_SEEDED) { + if (CVAR_ENEMY_RANDOMIZER_VALUE == ENEMY_RANDOMIZER_RANDOM_SEEDED) { uint32_t finalSeed = seed + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() : gSaveContext.ship.stats.fileCreatedAt); Random_Init(finalSeed); @@ -533,3 +538,45 @@ bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) { return 1; } } + +void RegisterEnemyRandomizer() { + // prevent dark link from triggering a voidout + COND_VB_SHOULD(VB_TRIGGER_VOIDOUT, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, { + Actor* actor = va_arg(args, Actor*); + + if (actor->category != ACTORCAT_PLAYER) { + *should = false; + Actor_Kill(actor); + } + }); + + // prevent dark link dealing fall damage to the player + COND_VB_SHOULD(VB_RECIEVE_FALL_DAMAGE, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, { + Actor* actor = va_arg(args, Actor*); + + if (actor->category != ACTORCAT_PLAYER) { + *should = false; + } + }); + + // prevent dark link from interfering with HESS/recoil/etc when at more than 100 away from him + COND_VB_SHOULD(VB_TORCH2_HANDLE_CLANKING, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, { + Actor* darkLink = va_arg(args, Actor*); + + if (darkLink->xzDistToPlayer > 100.0f) { + *should = false; + } + }); + + // prevent dark link from being grabbed by like likes and therefore grabbing the player + COND_VB_SHOULD(VB_LIKE_LIKE_GRAB_PLAYER, CVAR_ENEMY_RANDOMIZER_VALUE != CVAR_ENEMY_RANDOMIZER_DEFAULT, { + EnRr* likeLike = va_arg(args, EnRr*); + + if (!(likeLike->collider1.base.oc != NULL && likeLike->collider1.base.oc->category == ACTORCAT_PLAYER) && + !(likeLike->collider2.base.oc != NULL && likeLike->collider2.base.oc->category == ACTORCAT_PLAYER)) { + *should = false; + } + }); +} + +static RegisterShipInitFunc initFunc(RegisterEnemyRandomizer, { CVAR_ENEMY_RANDOMIZER_NAME }); \ No newline at end of file diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 223001711a5..e624515b45a 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -2011,6 +2011,38 @@ typedef enum { // #### `args` // - `*EnWonderTalk2` VB_WONDER_TALK, + + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*Actor` + VB_TRIGGER_VOIDOUT, + + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*Actor` + VB_TORCH2_HANDLE_CLANKING, + + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*Actor` + VB_RECIEVE_FALL_DAMAGE, + + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*EnRr` + VB_LIKE_LIKE_GRAB_PLAYER, } GIVanillaBehavior; #endif diff --git a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c index 3797adafe55..831e11b8c38 100644 --- a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c +++ b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c @@ -507,7 +507,7 @@ void EnRr_CollisionCheck(EnRr* this, PlayState* play) { this->collider2.base.ocFlags1 &= ~OC1_HIT; // "catch" osSyncPrintf(VT_FGCOL(GREEN) "キャッチ(%d)!!" VT_RST "\n", this->frameCount); - if (play->grabPlayer(play, player)) { + if (GameInteractor_Should(VB_LIKE_LIKE_GRAB_PLAYER, true, this) && play->grabPlayer(play, player)) { player->actor.parent = &this->actor; this->stopScroll = false; EnRr_SetupGrabPlayer(this, player); diff --git a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c index 4925e73cb31..ceca597df8b 100644 --- a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c +++ b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c @@ -706,7 +706,7 @@ void EnTorch2_Update(Actor* thisx, PlayState* play2) { sStaggerCount = 0; } } - if (player->linearVelocity == -18.0f) { + if (GameInteractor_Should(VB_TORCH2_HANDLE_CLANKING, player->linearVelocity == -18.0f, this)) { if (this->actor.xzDistToPlayer > 80.0f) { player->linearVelocity = 1.2f; } else if (this->actor.xzDistToPlayer < 70.0f) { diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 6638893f529..b2ebcbb88ca 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -4764,7 +4764,9 @@ s32 func_808382DC(Player* this, PlayState* play) { gSaveContext.respawn[RESPAWN_MODE_DOWN].yaw = respawnInfo->yaw; } - Play_TriggerVoidOut(play); + if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { + Play_TriggerVoidOut(play); + } } Player_PlayVoiceSfx(this, NA_SE_VO_LI_TAKEN_AWAY); @@ -5129,7 +5131,9 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol } if (exitIndex == 0) { - Play_TriggerVoidOut(play); + if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { + Play_TriggerVoidOut(play); + } Scene_SetTransitionForNextEntrance(play); } else { play->nextEntranceIndex = play->setupExitList[exitIndex - 1]; @@ -5163,7 +5167,9 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol SurfaceType_GetSlope(&play->colCtx, poly, bgId) == 2, play->setupExitList[exitIndex - 1])) { gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex = play->nextEntranceIndex; - Play_TriggerVoidOut(play); + if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { + Play_TriggerVoidOut(play); + } gSaveContext.respawnFlag = -2; } gSaveContext.retainWeatherMode = 1; @@ -5226,7 +5232,7 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol if (this->actor.bgCheckFlags & 1) { if (this->floorProperty == 5) { Play_TriggerRespawn(play); - } else { + } else if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { Play_TriggerVoidOut(play); } play->transitionType = TRANS_TYPE_FADE_BLACK_FAST; @@ -9613,6 +9619,10 @@ static FallImpactInfo D_80854600[] = { s32 func_80843E64(PlayState* play, Player* this) { s32 sp34; + if (!GameInteractor_Should(VB_RECIEVE_FALL_DAMAGE, true, this)) { + return 0; + } + if ((sFloorType == 6) || (sFloorType == 9)) { sp34 = 0; } else { @@ -15009,7 +15019,7 @@ void Player_Action_8084F88C(Player* this, PlayState* play) { if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { Grotto_ForceRegularVoidOut(); } - } else { + } else if (GameInteractor_Should(VB_TRIGGER_VOIDOUT, true, this)) { Play_TriggerVoidOut(play); } From ad850e50b10f75df541dad5e8bf1ff506be5befa Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:35:49 -0400 Subject: [PATCH 096/157] prevent OOB write for BGM fix in grotto mixed pools (#5572) --- soh/soh/Enhancements/randomizer/randomizer_entrance.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance.c b/soh/soh/Enhancements/randomizer/randomizer_entrance.c index faec117eb05..e32223124ee 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance.c +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance.c @@ -253,7 +253,9 @@ void Entrance_Init(void) { for (s16 i = 0; i < 4; i++) { // Zero out the bit in the field which tells the game to keep playing // background music for all four scene setups at each index - gEntranceTable[override + i].field &= ~ENTRANCE_INFO_CONTINUE_BGM_FLAG; + if (override + i < ENTRANCE_TABLE_SIZE) { + gEntranceTable[override + i].field &= ~ENTRANCE_INFO_CONTINUE_BGM_FLAG; + } } } } From 0b9fe2d9b967737d643cca55de936547d6749c18 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Wed, 11 Jun 2025 14:35:57 -0700 Subject: [PATCH 097/157] Fix Search Crash from Scrolling (#5571) * Fix search crashing when section child scrolled too far. * clang --- soh/soh/SohGui/Menu.cpp | 54 +++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/soh/soh/SohGui/Menu.cpp b/soh/soh/SohGui/Menu.cpp index 87c36249600..5a609a935e1 100644 --- a/soh/soh/SohGui/Menu.cpp +++ b/soh/soh/SohGui/Menu.cpp @@ -185,33 +185,35 @@ bool ModernMenuHeaderEntry(std::string label) { } uint32_t Menu::DrawSearchResults(std::string& menuSearchText) { - ImGui::BeginChild("Search Results"); int searchCount = 0; - for (auto& menuLabel : menuOrder) { - auto& menuEntry = menuEntries.at(menuLabel); - for (auto& sidebarLabel : menuEntry.sidebarOrder) { - auto& sidebar = menuEntry.sidebars[sidebarLabel]; - for (int i = 0; i < sidebar.columnWidgets.size(); i++) { - auto& column = sidebar.columnWidgets.at(i); - for (auto& info : column) { - if (info.type == WIDGET_SEARCH || info.type == WIDGET_SEPARATOR || - info.type == WIDGET_SEPARATOR_TEXT || info.isHidden) { - continue; - } - const char* tooltip = info.options->tooltip; - std::string widgetStr = std::string(info.name) + std::string(tooltip != NULL ? tooltip : ""); - std::transform(menuSearchText.begin(), menuSearchText.end(), menuSearchText.begin(), ::tolower); - menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '), - menuSearchText.end()); - std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower); - widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end()); - if (widgetStr.find(menuSearchText) != std::string::npos) { - MenuDrawItem(info, 90 / sidebar.columnCount, menuThemeIndex); - ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray)); - std::string origin = fmt::format(" ({} -> {}, Col {})", menuEntry.label, sidebarLabel, i + 1); - ImGui::Text("%s", origin.c_str()); - ImGui::PopStyleColor(); - searchCount++; + if (ImGui::BeginChild("Search Results")) { + for (auto& menuLabel : menuOrder) { + auto& menuEntry = menuEntries.at(menuLabel); + for (auto& sidebarLabel : menuEntry.sidebarOrder) { + auto& sidebar = menuEntry.sidebars[sidebarLabel]; + for (int i = 0; i < sidebar.columnWidgets.size(); i++) { + auto& column = sidebar.columnWidgets.at(i); + for (auto& info : column) { + if (info.type == WIDGET_SEARCH || info.type == WIDGET_SEPARATOR || + info.type == WIDGET_SEPARATOR_TEXT || info.isHidden) { + continue; + } + const char* tooltip = info.options->tooltip; + std::string widgetStr = std::string(info.name) + std::string(tooltip != NULL ? tooltip : ""); + std::transform(menuSearchText.begin(), menuSearchText.end(), menuSearchText.begin(), ::tolower); + menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '), + menuSearchText.end()); + std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower); + widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end()); + if (widgetStr.find(menuSearchText) != std::string::npos) { + MenuDrawItem(info, 90 / sidebar.columnCount, menuThemeIndex); + ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray)); + std::string origin = + fmt::format(" ({} -> {}, Col {})", menuEntry.label, sidebarLabel, i + 1); + ImGui::Text("%s", origin.c_str()); + ImGui::PopStyleColor(); + searchCount++; + } } } } From 69792e9717c582cfce0be8a14ead95154e74a907 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Wed, 11 Jun 2025 14:39:15 -0700 Subject: [PATCH 098/157] Save and Randomizer Version Control (#5557) * Add version control for Rachael saves and old rando saves. * Change spoiler drop success sound to puzzle success chime. Add spoiler drop fail sound (sys_error). Improve path sanitizer operation. Add check for a spoiler having a version and it equaling running version. Deletes spoiler CVar if spoiler becomes unusuable while running, and prevents loading dropped spoilers that don't match. * clang * Remove backslash escaping from Sanitize. Remove duplicate Sanitize from Context and make it use SohUtils. Fix typo. --- soh/soh/Enhancements/randomizer/context.cpp | 21 +- .../Enhancements/randomizer/randomizer.cpp | 18 +- soh/soh/SaveManager.cpp | 286 ++++-------------- soh/soh/SaveManager.h | 6 +- soh/soh/util.cpp | 11 - .../ovl_file_choose/z_file_choose.c | 16 +- 6 files changed, 83 insertions(+), 275 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp index 550cd0e9333..a0f7f223b5e 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/context.cpp @@ -11,6 +11,7 @@ #include "fishsanity.h" #include "macros.h" #include "3drando/hints.hpp" +#include "soh/util.h" #include "../kaleido.h" #include @@ -370,25 +371,8 @@ GetItemEntry Context::GetFinalGIEntry(const RandomizerCheck rc, const bool check return giEntry; } -std::string sanitize(std::string stringValue) { - // Add backslashes. - for (auto i = stringValue.begin();;) { - auto const pos = - std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; }); - if (pos == stringValue.end()) { - break; - } - i = std::next(stringValue.insert(pos, '\\'), 2); - } - - // Removes others. - std::erase_if(stringValue, [](char const c) { return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }); - - return stringValue; -} - void Context::ParseSpoiler(const char* spoilerFileName) { - std::ifstream spoilerFileStream(sanitize(spoilerFileName)); + std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName)); if (!spoilerFileStream) { return; } @@ -397,6 +381,7 @@ void Context::ParseSpoiler(const char* spoilerFileName) { try { nlohmann::json spoilerFileJson; spoilerFileStream >> spoilerFileJson; + spoilerFileStream.close(); ParseHashIconIndexesJson(spoilerFileJson); Rando::Settings::GetInstance()->ParseJson(spoilerFileJson); ParseItemLocationsJson(spoilerFileJson); diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 18a23df5312..7809a39c11a 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -361,10 +361,20 @@ std::unordered_map getItemIdToItemId = { bool Randomizer::SpoilerFileExists(const char* spoilerFileName) { if (strcmp(spoilerFileName, "") != 0) { std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName)); - if (!spoilerFileStream) { - return false; - } else { - return true; + if (spoilerFileStream) { + nlohmann::json contents; + spoilerFileStream >> contents; + spoilerFileStream.close(); + if (contents.contains("version") && + strcmp(std::string(contents["version"]).c_str(), (char*)gBuildVersion) == 0) { + return true; + } else { + SohGui::RegisterPopup( + "Old Spoiler Version", + "The spoiler file located at\n" + std::string(spoilerFileName) + + "\nwas made by a version that doesn't match the currently running version.\n" + + "Loading for this file has been cancelled."); + } } } diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 97ab6a52a16..e023f9afea9 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -119,10 +119,8 @@ SaveManager::SaveManager() { AddLoadFunction("base", 4, LoadBaseVersion4); AddSaveFunction("base", 4, SaveBase, true, SECTION_PARENT_NONE); - AddLoadFunction("randomizer", 1, LoadRandomizerVersion1); - AddLoadFunction("randomizer", 2, LoadRandomizerVersion2); - AddLoadFunction("randomizer", 3, LoadRandomizerVersion3); - AddSaveFunction("randomizer", 3, SaveRandomizer, true, SECTION_PARENT_NONE); + AddLoadFunction("randomizer", 1, LoadRandomizer); + AddSaveFunction("randomizer", 1, SaveRandomizer, true, SECTION_PARENT_NONE); AddInitFunction(InitFileImpl); @@ -157,234 +155,7 @@ SaveManager::SaveManager() { } } -// RANDOTODO should we just have dummy functions that raise warnings instead if these aren't supported? -void SaveManager::LoadRandomizerVersion1() { - auto randoContext = Rando::Context::GetInstance(); - RandomizerCheck location = RC_UNKNOWN_CHECK; - for (int i = 0; i < RC_MAX; i++) { - SaveManager::Instance->LoadData("check" + std::to_string(i), location); - SaveManager::Instance->LoadStruct("get" + std::to_string(i), [&]() { - SaveManager::Instance->LoadData("rgID", randoContext->GetItemLocation(location)->RefPlacedItem()); - if (randoContext->GetItemLocation(location)->GetPlacedRandomizerGet() == RG_ICE_TRAP) { - randoContext->overrides[location].SetLocation(location); - SaveManager::Instance->LoadData("fakeRgID", randoContext->overrides[location].RefLooksLike()); - SaveManager::Instance->LoadData("trickName", randoContext->overrides[location].GetTrickName().english); - SaveManager::Instance->LoadData("trickName", randoContext->overrides[location].GetTrickName().french); - } - }); - } - - for (uint32_t i = 0; i < randoContext->hashIconIndexes.size(); i++) { - SaveManager::Instance->LoadData("seed" + std::to_string(i), randoContext->hashIconIndexes[i]); - } - - for (int i = 0; i < RSK_MAX; i++) { - int key, value; - SaveManager::Instance->LoadData("sk" + std::to_string(i), key); - SaveManager::Instance->LoadData("sv" + std::to_string(i), value); - randoContext->GetOption(RandomizerSettingKey(key)).Set(value); - } - - for (int i = 0; i < 50; i++) { - RandomizerCheck check; - char hintText[200]; - SaveManager::Instance->LoadData("hc" + std::to_string(i), check); - for (int j = 0; j < ARRAY_COUNT(hintText); j++) { - SaveManager::Instance->LoadData("ht" + std::to_string(i) + "-" + std::to_string(j), hintText[j]); - } - RandomizerHint stoneHint = Rando::StaticData::oldVerHintOrder[i - Rando::StaticData::oldVerGossipStoneStart]; - randoContext->AddHint(stoneHint, Rando::Hint(stoneHint, { CustomMessage(hintText) })); - } - - char childAltarText[250]; - for (int i = 0; i < ARRAY_COUNT(childAltarText); i++) { - SaveManager::Instance->LoadData("cat" + std::to_string(i), childAltarText[i]); - } - randoContext->AddHint(RH_ALTAR_CHILD, Rando::Hint(RH_ALTAR_CHILD, { CustomMessage(childAltarText) })); - - char adultAltarText[750]; - for (int i = 0; i < ARRAY_COUNT(adultAltarText); i++) { - SaveManager::Instance->LoadData("aat" + std::to_string(i), adultAltarText[i]); - } - randoContext->AddHint(RH_ALTAR_ADULT, Rando::Hint(RH_ALTAR_ADULT, { CustomMessage(adultAltarText) })); - - char ganonHintText[150]; - for (int i = 0; i < ARRAY_COUNT(ganonHintText); i++) { - SaveManager::Instance->LoadData("ght" + std::to_string(i), ganonHintText[i]); - } - randoContext->AddHint(RH_GANONDORF_HINT, Rando::Hint(RH_GANONDORF_HINT, { CustomMessage(ganonHintText) })); - - char ganonText[250]; - for (int i = 0; i < ARRAY_COUNT(ganonText); i++) { - SaveManager::Instance->LoadData("gt" + std::to_string(i), ganonText[i]); - } - randoContext->AddHint(RH_GANONDORF_JOKE, Rando::Hint(RH_GANONDORF_JOKE, { CustomMessage(ganonText) })); - - SaveManager::Instance->LoadData("triforcePiecesCollected", - gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected); - - SaveManager::Instance->LoadData("pendingIceTrapCount", gSaveContext.ship.pendingIceTrapCount); - - size_t merchantPricesSize = 0; - if (randoContext->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_OFF)) { - merchantPricesSize += NUM_SCRUBS; - } - if (randoContext->GetOption(RSK_SHOPSANITY).Is(RO_SHOPSANITY_OFF)) { - merchantPricesSize += NUM_SHOP_ITEMS; - } - - SaveManager::Instance->LoadArray("merchantPrices", merchantPricesSize, [&](size_t i) { - SaveManager::Instance->LoadStruct("", [&]() { - RandomizerCheck rc; - SaveManager::Instance->LoadData("check", rc); - uint32_t price; - SaveManager::Instance->LoadData("price", price); - randoContext->GetItemLocation(rc)->SetCustomPrice(price); - }); - }); -} - -// RANDOTODO if we actually support this, be less lazy -void SaveManager::LoadRandomizerVersion2() { - auto randoContext = Rando::Context::GetInstance(); - SaveManager::Instance->LoadArray("itemLocations", RC_MAX, [&](size_t i) { - SaveManager::Instance->LoadStruct("", [&]() { - SaveManager::Instance->LoadData("rgID", randoContext->GetItemLocation(i)->RefPlacedItem()); - RandomizerGet rg = RG_NONE; - SaveManager::Instance->LoadData("fakeRgID", rg, RG_NONE); - if (rg != RG_NONE) { - randoContext->overrides[static_cast(i)] = - Rando::ItemOverride(static_cast(i), rg); - SaveManager::Instance->LoadData( - "trickName", randoContext->overrides[static_cast(i)].GetTrickName().english); - SaveManager::Instance->LoadData( - "trickName", randoContext->overrides[static_cast(i)].GetTrickName().french); - } - }); - }); - - auto entranceCtx = randoContext->GetEntranceShuffler(); - SaveManager::Instance->LoadArray("entrances", ARRAY_COUNT(entranceCtx->entranceOverrides), [&](size_t i) { - SaveManager::Instance->LoadStruct("", [&]() { - SaveManager::Instance->LoadData("type", entranceCtx->entranceOverrides[i].type); - SaveManager::Instance->LoadData("index", entranceCtx->entranceOverrides[i].index); - SaveManager::Instance->LoadData("destination", entranceCtx->entranceOverrides[i].destination); - SaveManager::Instance->LoadData("override", entranceCtx->entranceOverrides[i].override); - SaveManager::Instance->LoadData("overrideDestination", - entranceCtx->entranceOverrides[i].overrideDestination); - }); - }); - - SaveManager::Instance->LoadArray("seed", randoContext->hashIconIndexes.size(), [&](size_t i) { - SaveManager::Instance->LoadData("", randoContext->hashIconIndexes[i]); - }); - - std::string inputSeed; - SaveManager::Instance->LoadData("inputSeed", inputSeed); - randoContext->SetSeedString(inputSeed); - - uint32_t finalSeed; - SaveManager::Instance->LoadData("finalSeed", finalSeed); - randoContext->SetSeed(finalSeed); - - SaveManager::Instance->LoadArray("randoSettings", RSK_MAX, [&](size_t i) { - int value = 0; - SaveManager::Instance->LoadData("", value); - randoContext->GetOption(RandomizerSettingKey(i)).Set(value); - }); - - SaveManager::Instance->LoadArray("hintLocations", RH_ZR_OPEN_GROTTO_GOSSIP_STONE + 1, [&](size_t i) { - SaveManager::Instance->LoadStruct("", [&]() { - RandomizerCheck rc = RC_UNKNOWN_CHECK; - SaveManager::Instance->LoadData("check", rc); - if (rc != RC_UNKNOWN_CHECK) { - std::string hintText; - SaveManager::Instance->LoadData("hintText", hintText); - RandomizerHint stoneHint = - Rando::StaticData::oldVerHintOrder[rc - Rando::StaticData::oldVerGossipStoneStart]; - randoContext->AddHint(stoneHint, Rando::Hint(stoneHint, { CustomMessage(hintText) })); - } - }); - }); - - std::string childAltarText; - SaveManager::Instance->LoadData("childAltarText", childAltarText); - randoContext->AddHint(RH_ALTAR_CHILD, Rando::Hint(RH_ALTAR_CHILD, { CustomMessage(childAltarText) })); - std::string adultAltarText; - SaveManager::Instance->LoadData("adultAltarText", adultAltarText); - randoContext->AddHint(RH_ALTAR_ADULT, Rando::Hint(RH_ALTAR_ADULT, { CustomMessage(adultAltarText) })); - std::string ganonHintText; - SaveManager::Instance->LoadData("ganonHintText", ganonHintText); - randoContext->AddHint(RH_GANONDORF_HINT, Rando::Hint(RH_GANONDORF_HINT, { CustomMessage(ganonHintText) })); - std::string ganonText; - SaveManager::Instance->LoadData("ganonText", ganonText); - randoContext->AddHint(RH_GANONDORF_JOKE, Rando::Hint(RH_GANONDORF_JOKE, { CustomMessage(ganonText) })); - std::string dampeText; - SaveManager::Instance->LoadData("dampeText", dampeText); - randoContext->AddHint(RH_DAMPES_DIARY, Rando::Hint(RH_DAMPES_DIARY, { CustomMessage(dampeText) })); - std::string gregHintText; - SaveManager::Instance->LoadData("gregHintText", gregHintText); - randoContext->AddHint(RH_GREG_RUPEE, Rando::Hint(RH_GREG_RUPEE, { CustomMessage(gregHintText) })); - std::string sheikText; - SaveManager::Instance->LoadData("sheikText", sheikText); - randoContext->AddHint(RH_SHEIK_HINT, Rando::Hint(RH_SHEIK_HINT, { CustomMessage(sheikText) })); - std::string sariaText; - SaveManager::Instance->LoadData("sariaText", sariaText); - randoContext->AddHint(RH_SARIA_HINT, Rando::Hint(RH_SARIA_HINT, { CustomMessage(sariaText) })); - std::string fishingPoleText; - SaveManager::Instance->LoadData("fishingPoleText", fishingPoleText); - randoContext->AddHint(RH_FISHING_POLE, Rando::Hint(RH_FISHING_POLE, { CustomMessage(fishingPoleText) })); - std::string warpMinuetText; - SaveManager::Instance->LoadData("warpMinuetText", warpMinuetText); - randoContext->AddHint(RH_MINUET_WARP_LOC, Rando::Hint(RH_MINUET_WARP_LOC, { CustomMessage(warpMinuetText) })); - std::string warpBoleroText; - SaveManager::Instance->LoadData("warpBoleroText", warpBoleroText); - randoContext->AddHint(RH_BOLERO_WARP_LOC, Rando::Hint(RH_BOLERO_WARP_LOC, { CustomMessage(warpBoleroText) })); - std::string warpSerenadeText; - SaveManager::Instance->LoadData("warpSerenadeText", warpSerenadeText); - randoContext->AddHint(RH_SERENADE_WARP_LOC, Rando::Hint(RH_SERENADE_WARP_LOC, { CustomMessage(warpSerenadeText) })); - std::string warpRequiemText; - SaveManager::Instance->LoadData("warpRequiemText", warpRequiemText); - randoContext->AddHint(RH_REQUIEM_WARP_LOC, Rando::Hint(RH_REQUIEM_WARP_LOC, { CustomMessage(warpRequiemText) })); - std::string warpNocturneText; - SaveManager::Instance->LoadData("warpNocturneText", warpNocturneText); - randoContext->AddHint(RH_NOCTURNE_WARP_LOC, Rando::Hint(RH_NOCTURNE_WARP_LOC, { CustomMessage(warpNocturneText) })); - std::string warpPreludeText; - SaveManager::Instance->LoadData("warpPreludeText", warpPreludeText); - randoContext->AddHint(RH_PRELUDE_WARP_LOC, Rando::Hint(RH_PRELUDE_WARP_LOC, { CustomMessage(warpPreludeText) })); - - SaveManager::Instance->LoadData("triforcePiecesCollected", - gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected); - - SaveManager::Instance->LoadData("pendingIceTrapCount", gSaveContext.ship.pendingIceTrapCount); - - std::shared_ptr randomizer = OTRGlobals::Instance->gRandomizer; - - size_t merchantPricesSize = 0; - SaveManager::Instance->LoadData("merchantPricesSize", merchantPricesSize); - - SaveManager::Instance->LoadArray("merchantPrices", merchantPricesSize, [&](size_t i) { - SaveManager::Instance->LoadStruct("", [&]() { - RandomizerCheck rc; - SaveManager::Instance->LoadData("check", rc); - uint32_t price; - SaveManager::Instance->LoadData("price", price); - randoContext->GetItemLocation(rc)->SetCustomPrice(price); - }); - }); - - size_t mqDungeonCount; - SaveManager::Instance->LoadData("masterQuestDungeonCount", mqDungeonCount, (size_t)0); - - randoContext->GetDungeons()->ClearAllMQ(); - SaveManager::Instance->LoadArray("masterQuestDungeons", mqDungeonCount, [&](size_t i) { - uint16_t scene; - SaveManager::Instance->LoadData("", scene); - randoContext->GetDungeons()->GetDungeonFromScene(SceneID(scene))->SetMQ(); - }); -} - -void SaveManager::LoadRandomizerVersion3() { +void SaveManager::LoadRandomizer() { auto randoContext = Rando::Context::GetInstance(); SaveManager::Instance->LoadArray("itemLocations", RC_MAX, [&](size_t i) { SaveManager::Instance->LoadStruct("", [&]() { @@ -1343,8 +1114,57 @@ void SaveManager::LoadFile(int fileNum) { switch (saveBlock["version"].get()) { case 1: for (auto& block : saveBlock["sections"].items()) { - int sectionVersion = block.value()["version"]; std::string sectionName = block.key(); + if (sectionName == "randomizer") { + bool hasStats = saveBlock["sections"].contains("sohStats"); + if (block.value()["data"].contains("aat0") || !hasStats) { // Rachael rando data + SohGui::RegisterPopup( + "Loading old file", + "The file in slot " + std::to_string(fileNum + 1) + + " appears to contain randomizer data, but is a very old format.\n" + + "The randomizer data has been removed, and this file will be treated as a vanilla " + "file.\n" + + "If this was a randomizer file, the file will not work, and should be deleted."); + input.close(); + saveMtx.unlock(); + SaveFile(fileNum); + return; + } + s16 major = saveBlock["sections"]["sohStats"]["data"]["buildVersionMajor"]; + s16 minor = saveBlock["sections"]["sohStats"]["data"]["buildVersionMinor"]; + s16 patch = saveBlock["sections"]["sohStats"]["data"]["buildVersionPatch"]; + // block loading outdated rando save + if (!(major == gBuildVersionMajor && minor == gBuildVersionMinor && + patch == gBuildVersionPatch)) { + input.close(); + std::string newFileName = Ship::Context::GetPathRelativeToAppDirectory("Save") + + ("/file" + std::to_string(fileNum + 1) + "-" + + std::to_string(GetUnixTimestamp()) + ".bak"); + std::filesystem::path newFile(newFileName); + +#if defined(__SWITCH__) || defined(__WIIU__) + copy_file(fileName.c_str(), newFile.c_str()); +#else + std::filesystem::copy_file(fileName, newFile); +#endif + + std::filesystem::remove(fileName); + SohGui::RegisterPopup( + "Outdated Randomizer Save", + "The SoH version in the file in slot " + std::to_string(fileNum + 1) + + " does not match the currently running version.\n" + + "Non-matching rando saves are unsupported, and the file has been renamed to\n" + + " " + newFileName + "\n" + + "If this was not in error, the file should be deleted."); + saveMtx.unlock(); + SaveFile(fileNum); + return; + } + } + int sectionVersion = block.value()["version"]; + if (sectionName == "randomizer" && sectionVersion != 1) { + sectionVersion = 1; + } if (!sectionLoadHandlers.contains(sectionName)) { // Unloadable sections aren't necessarily errors, they are probably mods that were unloaded // TODO report in a more noticeable manner diff --git a/soh/soh/SaveManager.h b/soh/soh/SaveManager.h index 3c4a3124323..6d7ef3b33a5 100644 --- a/soh/soh/SaveManager.h +++ b/soh/soh/SaveManager.h @@ -166,11 +166,7 @@ class SaveManager { static void InitFileDebug(); static void InitFileMaxed(); - static void LoadRandomizerVersion1(); - static void LoadRandomizerVersion2(); - static void LoadRandomizerVersion3(); - static void LoadTrackerData(); - static void SaveTrackerData(SaveContext* saveContext, int sectionID, bool fullSave); + static void LoadRandomizer(); static void SaveRandomizer(SaveContext* saveContext, int sectionID, bool fullSave); static void LoadBaseVersion1(); diff --git a/soh/soh/util.cpp b/soh/soh/util.cpp index 1e5bb2ae824..a991c35ca57 100644 --- a/soh/soh/util.cpp +++ b/soh/soh/util.cpp @@ -369,17 +369,6 @@ void SohUtils::CopyStringToCharArray(char* destination, std::string source, size } std::string SohUtils::Sanitize(std::string stringValue) { - // Add backslashes. - for (auto i = stringValue.begin();;) { - auto const pos = - std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; }); - if (pos == stringValue.end()) { - break; - } - i = std::next(stringValue.insert(pos, '\\'), 2); - } - - // Removes others. stringValue.erase(std::remove_if(stringValue.begin(), stringValue.end(), [](char const c) { return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }), stringValue.end()); diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 6dae312ac4a..d170beb95c7 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -26,6 +26,7 @@ #include "soh/SaveManager.h" #include "soh/OTRGlobals.h" #include "soh/ResourceManagerHelpers.h" +#include "soh/ShipUtils.h" typedef struct { s16 left; @@ -1053,20 +1054,27 @@ void FileChoose_UpdateRandomizer() { if (!SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) && !CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) { CVarSetString(CVAR_GENERAL("SpoilerLog"), ""); + Randomizer_SetSpoilerLoaded(false); } if (CVarGetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0) != 0 || !(Randomizer_IsSeedGenerated() || Randomizer_IsSpoilerLoaded()) && SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) && !fileSelectSpoilerFileLoaded) { if (CVarGetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0) != 0) { - CVarSetString(CVAR_GENERAL("SpoilerLog"), CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), "")); - Audio_PlayFanfare(NA_BGM_HORSE_GOAL); + if (SpoilerFileExists(CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), ""))) { + CVarSetString(CVAR_GENERAL("SpoilerLog"), CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), "")); + Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME); + } else { + Sfx_PlaySfxCentered(NA_SE_SY_ERROR); + } } const char* fileLoc = CVarGetString(CVAR_GENERAL("SpoilerLog"), ""); CVarSetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0); CVarSetString(CVAR_GENERAL("RandomizerDroppedFile"), ""); - Randomizer_ParseSpoiler(fileLoc); - fileSelectSpoilerFileLoaded = true; + if (!Ship_IsCStringEmpty(fileLoc)) { + Randomizer_ParseSpoiler(fileLoc); + fileSelectSpoilerFileLoaded = true; + } if (SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) && CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) { From c7e3e08f8df387c5545f06d247856de8446c4520 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Thu, 12 Jun 2025 21:23:42 +0100 Subject: [PATCH 099/157] Fix club moblins moving out of bounds and remove them from the no-clear-room list (#5569) --- soh/soh/Enhancements/enemyrandomizer.cpp | 4 +--- soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/soh/soh/Enhancements/enemyrandomizer.cpp b/soh/soh/Enhancements/enemyrandomizer.cpp index 682ef026c7b..227a841276e 100644 --- a/soh/soh/Enhancements/enemyrandomizer.cpp +++ b/soh/soh/Enhancements/enemyrandomizer.cpp @@ -433,11 +433,9 @@ bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) { // Shell Blade & Spike - Child Link can't kill these with sword or Deku Stick. // Arwing & Dark Link - Both go out of bounds way too easily, softlocking the player. // Wallmaster - Not easily visible, often makes players think they're softlocked and that there's no enemies left. - // Club Moblin - Many issues with them falling or placing out of bounds. Maybe fixable in the future? bool enemiesToExcludeClearRooms = enemy.id == ACTOR_EN_FZ || enemy.id == ACTOR_EN_VM || enemy.id == ACTOR_EN_SB || enemy.id == ACTOR_EN_NY || enemy.id == ACTOR_EN_CLEAR_TAG || - enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2 || - enemy.id == ACTOR_EN_MB; + enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2; // Bari - Spawns 3 more enemies, potentially extremely difficult in timed rooms. bool enemiesToExcludeTimedRooms = enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI; diff --git a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c index 50ac7e11ba6..78f75dabd5a 100644 --- a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c +++ b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c @@ -303,7 +303,7 @@ void EnMb_Init(Actor* thisx, PlayState* play) { relYawFromPlayer = this->actor.world.rot.y - Math_Vec3f_Yaw(&this->actor.world.pos, &player->actor.world.pos); - if (ABS(relYawFromPlayer) > 0x4000) { + if (ABS(relYawFromPlayer) > 0x4000 && !CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0)) { this->actor.world.rot.y = thisx->world.rot.y + 0x8000; this->actor.shape.rot.y = thisx->world.rot.y; this->actor.world.pos.z = thisx->world.pos.z + 600.0f; From ab9af742fa730b0ea14fa4fb5651d703e34cb34a Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Sat, 14 Jun 2025 01:43:31 -0400 Subject: [PATCH 100/157] Finish hint translations for fish by waterfall (#5466) * Finish hint translations for fish by waterfall * Revise --- .../3drando/hint_list/hint_list_exclude_overworld.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp index b47c6864623..7129a0f5fce 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp @@ -313,11 +313,11 @@ void StaticData::HintTable_Init_Exclude_Overworld() { /*german*/ "Man erzählt sich, daß ein #Tauchexperiment# mit #[[1]]# belohnt würde.", /*french*/ "Selon moi, l'#expérience de plongée# donne #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, #bucear para un experimento# se premia con #[[1]]#. - // RANDOTODO: needs translation + hintTextTable[RHT_ZD_FISH] = HintText(CustomMessage("They say that a #fish by a waterfall# hoards #[[1]]#.", /*german*/ "Man erzählt sich, daß ein #Fisch nahe eines Wasserfalls# #[[1]]# horte.", - /*french*/ "Selon moi, #[[1]]#.", {QM_RED, QM_GREEN})); - // /*spanish*/ Según dicen, #[[1]]#. + /*french*/ "Selon moi, un #poisson près d'une cascade# a #[[1]]#.", {QM_RED, QM_GREEN})); + // /*spanish*/ Según dicen, un #pez junto a una cascada# otorga #[[1]]#. hintTextTable[RHT_GC_ROLLING_GORON_AS_ADULT] = HintText(CustomMessage("They say that #reassuring a young Goron# is rewarded with #[[1]]#.", From eefe7729ab69a0ceabc61a46a36084f461d6d739 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sat, 14 Jun 2025 15:51:33 +0100 Subject: [PATCH 101/157] CanUse instead of HasItem for Hover Boots to reach jabu switch ledge (#5581) * CanUse instead of HasItem for Hover Boots to reach jabu switch * also fix a similar issue in fountain --- .../randomizer/location_access/dungeons/jabujabus_belly.cpp | 4 ++-- .../randomizer/location_access/overworld/zoras_fountain.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp index 95494212a38..305ec3e0d69 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp @@ -64,7 +64,7 @@ void RegionTable_Init_JabuJabusBelly() { //Exits Entrance(RR_JABU_JABUS_BELLY_MAIN, []{return true;}), //there's tricks for getting here with bunny-jumps or just side-hops - Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_LEDGE, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->HasItem(RG_HOVER_BOOTS);}), + Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_LEDGE, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_HOVER_BOOTS);}), Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_SOUTH, []{return logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE);}), }); @@ -90,7 +90,7 @@ void RegionTable_Init_JabuJabusBelly() { }, { //Exits Entrance(RR_JABU_JABUS_BELLY_B1_NORTH, []{return logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE);}), - Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_LEDGE, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->HasItem(RG_HOVER_BOOTS);}), + Entrance(RR_JABU_JABUS_BELLY_WATER_SWITCH_ROOM_LEDGE, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_HOVER_BOOTS);}), Entrance(RR_JABU_JABUS_BELLY_MAIN, []{return logic->CanUseProjectile();}), }); diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp index 3ac0921a3e6..c2287f2d00a 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp @@ -41,7 +41,7 @@ void RegionTable_Init_ZorasFountain() { }, { //Exits //This hover is pretty tight, come at it with momentum and aim for the small corner polygon of the big iceburg while spamming roll - Entrance(RR_ZORAS_FOUNTAIN, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->HasItem(RG_HOVER_BOOTS);}), + Entrance(RR_ZORAS_FOUNTAIN, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_HOVER_BOOTS);}), Entrance(RR_ZF_LAKEBED, []{return logic->CanUse(RG_IRON_BOOTS);}), Entrance(RR_ZF_LEDGE, []{return true;}), }); From b600836e4f8f8cbd9970d2a2c36da6331980dd5e Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Sun, 15 Jun 2025 13:19:52 -0400 Subject: [PATCH 102/157] New options that affect feeding Jabu-Jabu (#5397) * New options that affect feeding Jabu-Jabu * Add hints for what Jabu-Jabu wants * Roll back "key" settings to just closed/open, finish rando logic * Revert "Add hints for what Jabu-Jabu wants" This reverts commit 2a1974a669a51f3ff0f33ce8ba251769614cfde5. * Fix vanilla enhancement disabler --- .../Enhancements/Presets/PresetEntries.cpp | 2 + .../overworld/zoras_fountain.cpp | 2 +- .../randomizer/option_descriptions.cpp | 2 + .../Enhancements/randomizer/randomizerTypes.h | 7 ++++ soh/soh/Enhancements/randomizer/settings.cpp | 3 ++ .../Enhancements/timesaver_hook_handlers.cpp | 40 +++++++++++++++++++ soh/soh/SohGui/SohMenuEnhancements.cpp | 9 +++++ 7 files changed, 64 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/Presets/PresetEntries.cpp b/soh/soh/Enhancements/Presets/PresetEntries.cpp index c9ca5c24f69..cf86793e24f 100644 --- a/soh/soh/Enhancements/Presets/PresetEntries.cpp +++ b/soh/soh/Enhancements/Presets/PresetEntries.cpp @@ -82,6 +82,7 @@ const std::vector enhancedPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DisableCritWiggle"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("BetterOwl"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("QuitFishingAtDoor"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("InstantPutaway"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall"), 1), + PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TimeSavers.SkipJabuJabuFish"), 1), // Skips & Speed-ups PRESET_ENTRY_S32(CVAR_ENHANCEMENT("SkipText"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TextSpeed"), 5), @@ -176,6 +177,7 @@ const std::vector randomizerPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("QuitFishingAtDoor"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("InstantPutaway"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall"), 1), + PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TimeSavers.SkipJabuJabuFish"), 1), // Skips & Speed-ups PRESET_ENTRY_S32(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), 1), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp index c2287f2d00a..2a5390e99fc 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp @@ -31,7 +31,7 @@ void RegionTable_Init_ZorasFountain() { //child can break the brown rock without lifting the silver rock and it stays gone for adult, but it's not intuitive and there's no reasonable case where it matters. Entrance(RR_ZF_HIDDEN_CAVE, []{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->BlastOrSmash();}), Entrance(RR_ZF_ROCK, []{return logic->IsAdult && logic->CanUse(RG_SCARECROW);}), - Entrance(RR_JABU_JABUS_BELLY_ENTRYWAY, []{return (logic->IsChild && logic->CanUse(RG_BOTTLE_WITH_FISH));}), + Entrance(RR_JABU_JABUS_BELLY_ENTRYWAY, []{return logic->IsChild && (ctx->GetOption(RSK_JABU_OPEN).Is(RO_JABU_OPEN) || logic->CanUse(RG_BOTTLE_WITH_FISH));}), Entrance(RR_ZF_GREAT_FAIRY_FOUNTAIN, []{return logic->HasExplosives() || (ctx->GetTrickOption(RT_ZF_GREAT_FAIRY_WITHOUT_EXPLOSIVES) && logic->CanUse(RG_MEGATON_HAMMER) && logic->CanUse(RG_SILVER_GAUNTLETS));}), }); diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp index 8dc07ce7a2d..cc5c5fec118 100644 --- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp @@ -42,6 +42,8 @@ void Settings::CreateOptionDescriptions() { "\n" "Open - Sleeping Waterfall is always open. " "Link may always enter Zora's Domain."; + mOptionDescriptions[RSK_JABU_OPEN] = "Closed - A fish is required to open Jabu-Jabu's mouth.\n\n" + "Open - Jabu-Jabu's mouth opens without the need for a fish."; mOptionDescriptions[RSK_LOCK_OVERWORLD_DOORS] = "Add locks to all wooden overworld doors, requiring specific small keys to open them"; mOptionDescriptions[RSK_STARTING_AGE] = diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 396a37b2336..d19d32c9954 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -5688,6 +5688,7 @@ typedef enum { RSK_DOOR_OF_TIME, RSK_ZORAS_FOUNTAIN, RSK_SLEEPING_WATERFALL, + RSK_JABU_OPEN, RSK_STARTING_AGE, RSK_SELECTED_STARTING_AGE, RSK_GERUDO_FORTRESS, @@ -5938,6 +5939,12 @@ typedef enum { RO_WATERFALL_OPEN, } RandoOptionSleepingWaterfall; +// Jabu-Jabu settings (closed, open) +typedef enum { + RO_JABU_CLOSED, + RO_JABU_OPEN, +} RandoOptionJabu; + // Starting Age settings (child, adult, random) typedef enum { RO_AGE_CHILD, diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index a2db7ee0ea8..a9f7b4e64ee 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -120,6 +120,7 @@ void Settings::CreateOptions() { OPT_U8(RSK_DOOR_OF_TIME, "Door of Time", {"Closed", "Song only", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("DoorOfTime"), mOptionDescriptions[RSK_DOOR_OF_TIME], WidgetType::Combobox); OPT_U8(RSK_ZORAS_FOUNTAIN, "Zora's Fountain", {"Closed", "Closed as child", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ZorasFountain"), mOptionDescriptions[RSK_ZORAS_FOUNTAIN]); OPT_U8(RSK_SLEEPING_WATERFALL, "Sleeping Waterfall", {"Closed", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SleepingWaterfall"), mOptionDescriptions[RSK_SLEEPING_WATERFALL]); + OPT_U8(RSK_JABU_OPEN, "Jabu-Jabu", {"Closed", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("JabuJabu"), mOptionDescriptions[RSK_JABU_OPEN]); OPT_BOOL(RSK_LOCK_OVERWORLD_DOORS, "Lock Overworld Doors", CVAR_RANDOMIZER_SETTING("LockOverworldDoors"), mOptionDescriptions[RSK_LOCK_OVERWORLD_DOORS]); OPT_U8(RSK_GERUDO_FORTRESS, "Fortress Carpenters", {"Normal", "Fast", "Free"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("FortressCarpenters"), mOptionDescriptions[RSK_GERUDO_FORTRESS]); OPT_U8(RSK_RAINBOW_BRIDGE, "Rainbow Bridge", {"Vanilla", "Always open", "Stones", "Medallions", "Dungeon rewards", "Dungeons", "Tokens", "Greg"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RainbowBridge"), mOptionDescriptions[RSK_RAINBOW_BRIDGE], WidgetType::Combobox, RO_BRIDGE_VANILLA, false, IMFLAG_NONE); @@ -1151,6 +1152,7 @@ void Settings::CreateOptions() { &mOptions[RSK_DOOR_OF_TIME], &mOptions[RSK_ZORAS_FOUNTAIN], &mOptions[RSK_SLEEPING_WATERFALL], + &mOptions[RSK_JABU_OPEN], &mOptions[RSK_LOCK_OVERWORLD_DOORS], }, WidgetContainerType::COLUMN); @@ -1405,6 +1407,7 @@ void Settings::CreateOptions() { &mOptions[RSK_DOOR_OF_TIME], &mOptions[RSK_ZORAS_FOUNTAIN], &mOptions[RSK_SLEEPING_WATERFALL], + &mOptions[RSK_JABU_OPEN], &mOptions[RSK_LOCK_OVERWORLD_DOORS], &mOptions[RSK_GERUDO_FORTRESS], &mOptions[RSK_RAINBOW_BRIDGE], diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 9f488efce10..9a602323d8b 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -25,6 +25,7 @@ extern "C" { #include "src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.h" #include "src/overlays/actors/ovl_En_Tk/z_en_tk.h" #include "src/overlays/actors/ovl_En_Fu/z_en_fu.h" +#include "src/overlays/actors/ovl_En_Jj/z_en_jj.h" #include "src/overlays/actors/ovl_En_Daiku/z_en_daiku.h" #include "src/overlays/actors/ovl_Bg_Spot02_Objects/z_bg_spot02_objects.h" #include "src/overlays/actors/ovl_Bg_Spot03_Taki/z_bg_spot03_taki.h" @@ -47,6 +48,10 @@ extern void EnGo2_CurledUp(EnGo2* enGo2, PlayState* play); extern void EnRu2_SetEncounterSwitchFlag(EnRu2* enRu2, PlayState* play); extern void EnDaiku_EscapeSuccess(EnDaiku* enDaiku, PlayState* play); + +extern void EnJj_WaitToOpenMouth(EnJj* enJj, PlayState* play); +extern void EnJj_WaitForFish(EnJj* enJj, PlayState* play); +extern void EnJj_SetupAction(EnJj* enJj, EnJjActionFunc actionFunc); } #define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get() @@ -869,6 +874,8 @@ static uint32_t enMa1UpdateHook = 0; static uint32_t enMa1KillHook = 0; static uint32_t enFuUpdateHook = 0; static uint32_t enFuKillHook = 0; +static uint32_t enJjUpdateHook = 0; +static uint32_t enJjKillHook = 0; static uint32_t bgSpot02UpdateHook = 0; static uint32_t bgSpot02KillHook = 0; static uint32_t bgSpot03UpdateHook = 0; @@ -934,6 +941,39 @@ void TimeSaverOnActorInitHandler(void* actorRef) { }); } + if (actor->id == ACTOR_EN_JJ && !IS_RANDO) { + enJjUpdateHook = + GameInteractor::Instance->RegisterGameHook([](void* innerActorRef) mutable { + Actor* innerActor = static_cast(innerActorRef); + + if (innerActor->id != ACTOR_EN_JJ || Flags_GetEventChkInf(EVENTCHKINF_OFFERED_FISH_TO_JABU_JABU)) { + return; + } + + bool shouldOpen = IS_RANDO ? RAND_GET_OPTION(RSK_JABU_OPEN) + : CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipJabuJabuFish"), 0); + if (!shouldOpen) { + return; + } + + EnJj* enJj = static_cast(innerActorRef); + if (enJj->actionFunc == EnJj_WaitForFish) { + EnJj_SetupAction(enJj, EnJj_WaitToOpenMouth); + GameInteractor::Instance->UnregisterGameHook(enJjUpdateHook); + GameInteractor::Instance->UnregisterGameHook(enJjKillHook); + enJjUpdateHook = 0; + enJjKillHook = 0; + } + }); + enJjKillHook = + GameInteractor::Instance->RegisterGameHook([](int16_t sceneNum) mutable { + GameInteractor::Instance->UnregisterGameHook(enJjUpdateHook); + GameInteractor::Instance->UnregisterGameHook(enJjKillHook); + enJjUpdateHook = 0; + enJjKillHook = 0; + }); + } + if (actor->id == ACTOR_EN_OWL && gPlayState->sceneNum == SCENE_ZORAS_RIVER && CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SleepingWaterfall"), 0) == 2) { Actor_Kill(actor); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 9305c8ec548..1ded80e7446 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -262,6 +262,15 @@ void SohMenu::AddMenuEnhancements() { "open permanently.\n" "Never: Link never needs to play Zelda's Lullaby to open the waterfall. He only needs to have " "learned it and have an Ocarina.")); + AddWidget(path, "Skip Feeding Jabu-Jabu", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipJabuJabuFish")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = + IS_RANDO && OTRGlobals::Instance->gRandoContext->GetOption(RSK_JABU_OPEN).Is(RO_JABU_OPEN); + info.options->disabledTooltip = + "This setting is disabled because a randomizer savefile with \"Jabu-Jaby: Open\" is loaded."; + }) + .Options(CheckboxOptions().Tooltip("Allow Link to enter Jabu-Jabu without feeding him a fish.")); // Skips & Speed-ups path.sidebarName = "Skips & Speed-ups"; From 35361c9e8d59c92ea820edd25ead124d086d3b52 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Sun, 15 Jun 2025 10:20:09 -0700 Subject: [PATCH 103/157] Create macro to make All and None buttons for cutscene skips trigger ShipInit for each CVar changed. (#5576) --- soh/soh/SohGui/SohMenuEnhancements.cpp | 44 ++++++++++++++------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 1ded80e7446..f6f627f78c7 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -7,6 +7,10 @@ #include #include +#define CVAR_INT_SHIP_INIT(cvar, val) \ + CVarSetInteger(cvar, val); \ + ShipInit::Init(cvar); + static std::string comboboxTooltip = ""; static int32_t enhancementPresetSelected = ENHANCEMENT_PRESET_DEFAULT; bool isBetaQuestEnabled = false; @@ -281,16 +285,16 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "All##Skips", WIDGET_BUTTON) .Options(ButtonOptions().Size(Sizes::Inline)) .Callback([](WidgetInfo& info) { - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), true); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), true); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), true); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); }); @@ -298,16 +302,16 @@ void SohMenu::AddMenuEnhancements() { .SameLine(true) .Options(ButtonOptions().Size(Sizes::Inline)) .Callback([](WidgetInfo& info) { - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), false); - CVarSetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.LearnSong"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), false); + CVAR_INT_SHIP_INIT(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), false); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); }); From 620d08002c33ac6b1c70c4f1726de710ba06488c Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Sun, 15 Jun 2025 12:22:03 -0500 Subject: [PATCH 104/157] RecalculateAvailableChecks Fix - IsSaveLoaded Guard (#5587) * Added IsSaveLoaded guard clause to RecalculateAvailableChecks. * Combined RecalculateAvailableChecks guard clauses. --- soh/soh/Enhancements/debugconsole.cpp | 5 +---- .../Enhancements/randomizer/randomizer_check_tracker.cpp | 7 ++----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/soh/soh/Enhancements/debugconsole.cpp b/soh/soh/Enhancements/debugconsole.cpp index d25c3cfffef..91c0d4c1cc3 100644 --- a/soh/soh/Enhancements/debugconsole.cpp +++ b/soh/soh/Enhancements/debugconsole.cpp @@ -1471,10 +1471,7 @@ static bool AvailableChecksProcessUndiscoveredExitsHandler(std::shared_ptrACProcessUndiscoveredExits ? "enabled" : "disabled"); - if (GameInteractor::IsSaveLoaded(true)) { - CheckTracker::RecalculateAvailableChecks(); - } - + CheckTracker::RecalculateAvailableChecks(); return 0; } diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 3a2c5e4758f..887bc373162 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -1985,7 +1985,7 @@ void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName, } void RecalculateAvailableChecks(RandomizerRegion startingRegion /* = RR_ROOT */) { - if (!enableAvailableChecks) { + if (!enableAvailableChecks || !GameInteractor::IsSaveLoaded(true)) { return; } @@ -2135,10 +2135,7 @@ void CheckTrackerSettingsWindow::DrawElement() { "with your current progress.") .Color(THEME_COLOR))) { enableAvailableChecks = CVarGetInteger(CVAR_TRACKER_CHECK("EnableAvailableChecks"), 0); - - if (GameInteractor::IsSaveLoaded(true)) { - RecalculateAvailableChecks(); - } + RecalculateAvailableChecks(); } ImGui::EndDisabled(); From f2bc7cd1dce0918dacaa8bb71c2f867903006c64 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 15 Jun 2025 18:22:36 +0100 Subject: [PATCH 105/157] Fix rare crash in randomiser generation (#5585) --- soh/soh/Enhancements/randomizer/randomizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 7809a39c11a..f0f57b35311 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -3586,7 +3586,7 @@ std::thread randoThread; void GenerateRandomizerImgui(std::string seed = "") { CVarSetInteger(CVAR_GENERAL("RandoGenerating"), 1); - CVarSave(); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); auto ctx = Rando::Context::GetInstance(); // RANDOTODO proper UI for selecting if a spoiler loaded should be used for settings Rando::Settings::GetInstance()->SetAllToContext(); From aa7693a10351e8221096c79b23e4d6a14a9e2cfc Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Mon, 16 Jun 2025 23:29:22 +0100 Subject: [PATCH 106/157] fix various oversights with altar hint off (#5589) --- .../custom-message/CustomMessageManager.h | 1 + .../randomizer/3drando/hint_list.cpp | 26 +++++++------- .../Enhancements/randomizer/3drando/hints.cpp | 36 ++++++++++--------- soh/soh/Enhancements/randomizer/hint.cpp | 4 +++ soh/soh/OTRGlobals.cpp | 2 +- 5 files changed, 38 insertions(+), 31 deletions(-) diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.h b/soh/soh/Enhancements/custom-message/CustomMessageManager.h index f312d9f8513..6b14c8060e1 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageManager.h +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.h @@ -47,6 +47,7 @@ class CustomMessage { TextBoxPosition position_ = TEXTBOX_POS_BOTTOM); CustomMessage(std::string english_, TextBoxType type_ = TEXTBOX_TYPE_BLACK, TextBoxPosition position_ = TEXTBOX_POS_BOTTOM); + // RANDOTODO trying to declare this with capital and type causes ambiguity with the first signature CustomMessage(std::string english_, std::vector colors_, std::vector capital_ = {}, TextBoxType type_ = TEXTBOX_TYPE_BLACK, TextBoxPosition position_ = TEXTBOX_POS_BOTTOM); CustomMessage(Text text, TextBoxType type_ = TEXTBOX_TYPE_BLACK, TextBoxPosition position_ = TEXTBOX_POS_BOTTOM); diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp index 72c90fa1997..0670f913990 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp @@ -1976,27 +1976,27 @@ void StaticData::HintTable_Init() { hintTextTable[RHT_BRIDGE_OPEN_HINT] = HintText(CustomMessage("$lThe awakened ones have #already created a bridge# to the castle where the evil dwells.^", /*german*/ "$lDie Weisen haben #bereits&eine Brücke zum Portal von&Ganons Schloß gelegt#...^", /*french*/ "$lLes êtres de sagesse ont#déjà créé un pont# vers le repaire du mal.^", - {QM_LBLUE})); + {QM_LBLUE}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$lLos sabios #ya habrán creado un puente#&al castillo, de donde emana el mal.^ hintTextTable[RHT_BRIDGE_VANILLA_HINT] = HintText(CustomMessage("$6The awakened ones require the #Shadow and Spirit Medallions# as well as the #Light Arrows#.^", /*german*/ "$6Die Weisen werden darauf warten, daß der Held das #Amulett des Schattens, Amulett der Geister# und die #Licht-Pfeile# sammelt.^", /*french*/ "$6Les êtres de sagesse attendront le héros muni des #Médaillons de l'Ombre et l'Esprit# et des #Flèches de Lumière#.^", - {QM_RED, QM_YELLOW})); + {QM_RED, QM_YELLOW}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$6Los sabios aguardarán a que el héroe obtenga tanto el #Medallón de las Sombras y el del Espíritu# junto // a la #flecha de luz#.^ hintTextTable[RHT_BRIDGE_STONES_HINT] = HintText(CustomMessage("$0The awakened ones will await for the Hero to collect #[[d]] Spiritual Stone||s|#.^", /*german*/ "$0Die Weisen werden darauf warten, daß der Held #[[d]] |Heiligen Stein|Heilige Steine|# sammelt.^", /*french*/ "$0Les êtres de sagesse attendront le héros muni de #[[d]] |Pierre Ancestrale|Pierres Ancestrales|#.^", - {QM_BLUE})); + {QM_BLUE}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$0Los sabios aguardarán a que el héroe&obtenga #[[d]] |piedra espiritual|piedras espirituales|#.^ hintTextTable[RHT_BRIDGE_MEDALLIONS_HINT] = HintText(CustomMessage("$8The awakened ones will await for the Hero to collect #[[d]] Medallion||s|#.^", /*german*/ "$8Die Weisen werden darauf warten, daß der Held #[[d]] Amulett||e|# sammelt.^", /*french*/ "$8Les êtres de sagesse attendront le héros muni de #[[d]] Médaillon||s|#.^", - {QM_RED})); + {QM_RED}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$8Los sabios aguardarán a que el héroe&obtenga #[[d]] |medallón|medallones|#.^ hintTextTable[RHT_BRIDGE_REWARDS_HINT] = HintText(CustomMessage("$CThe awakened ones will await for the Hero to collect #[[d]]# |#Spiritual Stone# or #Medallion#|" @@ -2005,24 +2005,24 @@ void StaticData::HintTable_Init() { "#Heilige Steine# oder #Amulette#| sammelt.^", /*french*/ "$CLes êtres de sagesse attendront le héros muni de #[[d]]# |#Pierre Ancestrale# ou #Médaillon#" "|#Pierres Ancestrales# ou #Médaillons#|.^", - {QM_YELLOW, QM_BLUE, QM_RED})); + {QM_YELLOW, QM_BLUE, QM_RED}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$CLos sabios aguardarán a que el héroe obtenga #[[d]]# |#piedra espiritual# o #medallón#| //#piedras espirtuales# y #medallones#|.^ hintTextTable[RHT_BRIDGE_DUNGEONS_HINT] = HintText(CustomMessage("$mThe awakened ones will await for the Hero to conquer #[[d]] Dungeon||s|#.^", /*german*/ "$mDie Weisen werden darauf warten, daß der Held #[[d]] Labyrinth||e|# abschließt.^", /*french*/ "$mLes êtres de sagesse attendront la conquête de #[[d]] Donjon||s|#.^", - {QM_PINK})); + {QM_PINK}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$mLos sabios aguardarán a que el héroe complete #[[d]] mazmorra||s|#.^ hintTextTable[RHT_BRIDGE_TOKENS_HINT] = HintText(CustomMessage("$sThe awakened ones will await for the Hero to collect #[[d]] Gold Skulltula Token||s|#.^", /*german*/ "$sDie Weisen werden darauf warten, daß der Held #[[d]] Skulltula-Symbol||e|# sammelt.^", /*french*/ "$sLes êtres de sagesse attendront le héros muni de #[[d]] Symbole||s| de Skulltula d'or#.^", - {QM_YELLOW})); + {QM_YELLOW}, {}, TEXTBOX_TYPE_BLUE)); // /*spanish*/$sLos sabios aguardarán a que el héroe obtenga #[[d]] símbolo||s| de skulltula dorada#.^ - hintTextTable[RHT_BRIDGE_GREG_HINT] = HintText(CustomMessage("$gThe awakened ones will await for the Hero to find #Greg#.^", - {QM_GREEN})); + hintTextTable[RHT_BRIDGE_GREG_HINT] = HintText(CustomMessage("$gThe awakened ones will await for the Hero to find #Greg#.^", TODO_TRANSLATE, TODO_TRANSLATE, + {QM_GREEN}, {}, TEXTBOX_TYPE_BLUE)); /*-------------------------- @@ -2176,22 +2176,22 @@ void StaticData::HintTable_Init() { hintTextTable[RHT_CHILD_ALTAR_STONES] = HintText(CustomMessage("3 Spiritual Stones found in Hyrule...^$0#[[1]]#...^$1#[[2]]#...^$2#[[3]]#...^", /*german*/ "Drei Heilige Steine, zu finden in Hyrule...$0#[[1]]#...^$1#[[2]]#...^$2#[[3]]#...^", /*french*/ "Les trois Pierres Ancestrales cachées&dans Hyrule...$0#[[1]]#...^$1#[[2]]#...^$2#[[3]]#...^", - {QM_GREEN, QM_RED, QM_BLUE}, {true, true, true})); + {QM_GREEN, QM_RED, QM_BLUE}, {true, true, true}, TEXTBOX_TYPE_BLUE)); // /*spanish*/ Tres piedras espirituales halladas por Hyrule...$0#[[1]]#...^$1#[[2]]#...^$2#[[3]]#...^ hintTextTable[RHT_CHILD_ALTAR_TEXT_END_DOTOPEN] = HintText(CustomMessage("$oYe who may become a Hero...&The path to the future is open...", /*german*/ "$oJener auf dem Weg des Helden...&Der Pfad zur Zukunft sei geöffnet...", - /*french*/ "$oÀ celui qui a quête de devenir&héros...&Le futur vous accueille béant...")); + /*french*/ "$oÀ celui qui a quête de devenir&héros...&Le futur vous accueille béant...", TEXTBOX_TYPE_BLUE)); // /*spanish*/$oPara aquel que se convierta en el héroe...&La puerta al futuro está a su disposición... hintTextTable[RHT_CHILD_ALTAR_TEXT_END_DOTSONGONLY] = HintText(CustomMessage("$cYe who may become a Hero...&Stand with the Ocarina and&play the Song of Time.", /*german*/ "$cJener auf dem Weg des Helden...&Nehme er seine Okarina zur Hand und&spiele hier die Hymne der Zeit.", - /*french*/ "$cÀ celui qui a quête de devenir&héros...&Portez l'Ocarina et jouez&le chant du temps.")); + /*french*/ "$cÀ celui qui a quête de devenir&héros...&Portez l'Ocarina et jouez&le chant du temps.", TEXTBOX_TYPE_BLUE)); // /*spanish*/$cPara aquel que se convierta en el héroe...&Tome la ocarina y&entone la Canción del Tiempo. hintTextTable[RHT_CHILD_ALTAR_TEXT_END_DOTCLOSED] = HintText(CustomMessage("$iYe who may become a Hero...&Offer the spiritual stones and&play the Song of Time.", /*german*/ "$iJener mit den drei Heiligen Steinen&nehme seine Okarina zur Hand und&spiele hier die Hymne der Zeit.", - /*french*/ "$iÀ celui qui a quête de devenir&héros... Présentez les Pierres&Ancestrales et jouez&le chant du temps.")); + /*french*/ "$iÀ celui qui a quête de devenir&héros... Présentez les Pierres&Ancestrales et jouez&le chant du temps.", TEXTBOX_TYPE_BLUE)); // /*spanish*/$iPara aquel que se convierta en el héroe...&Tome las piedras espirituales y&entone la Canción del Tiempo. hintTextTable[RHT_ADULT_ALTAR_MEDALLIONS] = HintText(CustomMessage("An awakening voice from the Sacred Realm will call those destined to be Sages, who dwell in the #five temples#.^" diff --git a/soh/soh/Enhancements/randomizer/3drando/hints.cpp b/soh/soh/Enhancements/randomizer/3drando/hints.cpp index 8e928185fbb..c38d57d8b62 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hints.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hints.cpp @@ -728,20 +728,22 @@ std::vector FindItemsAndMarkHinted(std::vector i void CreateChildAltarHint() { auto ctx = Rando::Context::GetInstance(); - if (!ctx->GetHint(RH_ALTAR_CHILD)->IsEnabled() && ctx->GetOption(RSK_TOT_ALTAR_HINT)) { + if (!ctx->GetHint(RH_ALTAR_CHILD)->IsEnabled()) { std::vector stoneLocs = {}; - // force marking the rewards as hinted if they are at the end of dungeons as they can be inferred - if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON) || - ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_VANILLA)) { - stoneLocs = FindItemsAndMarkHinted({ RG_KOKIRI_EMERALD, RG_GORON_RUBY, RG_ZORA_SAPPHIRE }, {}); - } else { - stoneLocs = - FindItemsAndMarkHinted({ RG_KOKIRI_EMERALD, RG_GORON_RUBY, RG_ZORA_SAPPHIRE }, { RC_ALTAR_HINT_CHILD }); - } std::vector stoneAreas = {}; - for (auto loc : stoneLocs) { - if (loc != RC_UNKNOWN_CHECK) { - stoneAreas.push_back(ctx->GetItemLocation(loc)->GetRandomArea()); + if (ctx->GetOption(RSK_TOT_ALTAR_HINT)) { + // force marking the rewards as hinted if they are at the end of dungeons as they can be inferred + if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON) || + ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_VANILLA)) { + stoneLocs = FindItemsAndMarkHinted({ RG_KOKIRI_EMERALD, RG_GORON_RUBY, RG_ZORA_SAPPHIRE }, {}); + } else { + stoneLocs = FindItemsAndMarkHinted({ RG_KOKIRI_EMERALD, RG_GORON_RUBY, RG_ZORA_SAPPHIRE }, + { RC_ALTAR_HINT_CHILD }); + } + for (auto loc : stoneLocs) { + if (loc != RC_UNKNOWN_CHECK) { + stoneAreas.push_back(ctx->GetItemLocation(loc)->GetRandomArea()); + } } } ctx->AddHint(RH_ALTAR_CHILD, Hint(RH_ALTAR_CHILD, HINT_TYPE_ALTAR_CHILD, {}, stoneLocs, stoneAreas)); @@ -752,6 +754,7 @@ void CreateAdultAltarHint() { auto ctx = Rando::Context::GetInstance(); if (!ctx->GetHint(RH_ALTAR_ADULT)->IsEnabled()) { std::vector medallionLocs = {}; + std::vector medallionAreas = {}; if (ctx->GetOption(RSK_TOT_ALTAR_HINT)) { // force marking the rewards as hinted if they are at the end of dungeons as they can be inferred if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON) || @@ -764,11 +767,10 @@ void CreateAdultAltarHint() { RG_WATER_MEDALLION, RG_SPIRIT_MEDALLION, RG_SHADOW_MEDALLION }, { RC_ALTAR_HINT_ADULT }); } - } - std::vector medallionAreas = {}; - for (auto loc : medallionLocs) { - if (loc != RC_UNKNOWN_CHECK) { - medallionAreas.push_back(ctx->GetItemLocation(loc)->GetRandomArea()); + for (auto loc : medallionLocs) { + if (loc != RC_UNKNOWN_CHECK) { + medallionAreas.push_back(ctx->GetItemLocation(loc)->GetRandomArea()); + } } } ctx->AddHint(RH_ALTAR_ADULT, Hint(RH_ALTAR_ADULT, HINT_TYPE_ALTAR_ADULT, {}, medallionLocs, medallionAreas)); diff --git a/soh/soh/Enhancements/randomizer/hint.cpp b/soh/soh/Enhancements/randomizer/hint.cpp index d81e94f2f88..c2011c10165 100644 --- a/soh/soh/Enhancements/randomizer/hint.cpp +++ b/soh/soh/Enhancements/randomizer/hint.cpp @@ -299,6 +299,8 @@ const CustomMessage Hint::GetHintMessage(MessageFormat format, uint8_t id) const } else if (hintType == HINT_TYPE_ALTAR_CHILD) { if (ctx->GetOption(RSK_TOT_ALTAR_HINT)) { hintText = StaticData::hintTextTable[RHT_CHILD_ALTAR_STONES].GetHintMessage(); + } else { + hintText.SetTextBoxType(TEXTBOX_TYPE_BLUE); } if (ctx->GetOption(RSK_DOOR_OF_TIME).Is(RO_DOOROFTIME_OPEN)) { hintText += CustomMessage(StaticData::hintTextTable[RHT_CHILD_ALTAR_TEXT_END_DOTOPEN].GetHintMessage()); @@ -310,6 +312,8 @@ const CustomMessage Hint::GetHintMessage(MessageFormat format, uint8_t id) const } else if (hintType == HINT_TYPE_ALTAR_ADULT) { if (ctx->GetOption(RSK_TOT_ALTAR_HINT)) { hintText = StaticData::hintTextTable[RHT_ADULT_ALTAR_MEDALLIONS].GetHintMessage(); + } else { + hintText.SetTextBoxType(TEXTBOX_TYPE_BLUE); } hintText += GetBridgeReqsText() + GetGanonBossKeyText() + StaticData::hintTextTable[RHT_ADULT_ALTAR_TEXT_END].GetHintMessage(); diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 9bd3008f803..630ffeaaa34 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2258,7 +2258,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { } } else if ((textId == TEXT_ALTAR_CHILD || textId == TEXT_ALTAR_ADULT)) { // rando hints at altar - messageEntry = (LINK_IS_ADULT) ? ctx->GetHint(RH_ALTAR_ADULT)->GetHintMessage() + messageEntry = (LINK_IS_ADULT) ? ctx->GetHint(RH_ALTAR_ADULT)->GetHintMessage(MF_AUTO_FORMAT) : ctx->GetHint(RH_ALTAR_CHILD)->GetHintMessage(MF_AUTO_FORMAT); } else if (textId == TEXT_GANONDORF) { if (ctx->GetOption(RSK_GANONDORF_HINT)) { From 19e9f39a9ab1be147a18e437728d41097fe439de Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Mon, 16 Jun 2025 23:29:46 +0100 Subject: [PATCH 107/157] Hardcode spirit hands to be dungeon checks (#5590) * Hardcode spirit hands to be dungeon checks * CLANG CLANG CLANG! --- soh/soh/Enhancements/randomizer/location.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location.cpp b/soh/soh/Enhancements/randomizer/location.cpp index f474397aa4a..6c27e3aef98 100644 --- a/soh/soh/Enhancements/randomizer/location.cpp +++ b/soh/soh/Enhancements/randomizer/location.cpp @@ -54,8 +54,9 @@ const std::string& Rando::Location::GetShortName() const { bool Rando::Location::IsDungeon() const { return (checkType != RCTYPE_SKULL_TOKEN && - (scene <= SCENE_GERUDO_TRAINING_GROUND || scene == SCENE_INSIDE_GANONS_CASTLE || - (scene >= SCENE_DEKU_TREE_BOSS && scene <= SCENE_GANONDORF_BOSS))) || + (scene <= SCENE_GERUDO_TRAINING_GROUND || scene == SCENE_INSIDE_GANONS_CASTLE || + (scene >= SCENE_DEKU_TREE_BOSS && scene <= SCENE_GANONDORF_BOSS)) || + (rc == RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST || rc == RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST)) || (checkType == RCTYPE_SKULL_TOKEN && scene <= SCENE_ICE_CAVERN); } From b932b8f6c9912cfd57a0bd37b8091f2047a89396 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 18 Jun 2025 16:39:09 +0100 Subject: [PATCH 108/157] Clarify hint text for pots in interiors (#5591) * Clarify hint text for pots in interors * Make dampe's pots text less like a naming error --- .../hint_list/hint_list_exclude_overworld.cpp | 47 ++++-- .../Enhancements/randomizer/ShufflePots.cpp | 156 +++++++++--------- .../Enhancements/randomizer/randomizerTypes.h | 15 +- 3 files changed, 122 insertions(+), 96 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp index 7129a0f5fce..754709f9729 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp @@ -1492,9 +1492,17 @@ void StaticData::HintTable_Init_Exclude_Overworld() { /*french*/ "Selon moi, une #rucheau derrière le Roi des Zoras# cache #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #colmena detrás del rey de los zoras# esconde #[[1]]#. - hintTextTable[RHT_POT_KOKIRI_FOREST] = HintText(CustomMessage("They say that a #pot in Kokiri Forest# contains #[[1]]#.", - /*german*/ "Man erzählt sich, daß eine #Vase im Kokiri-Wald# #[[1]]# enthielte.", - /*french*/ "Selon moi, une #jarre dans la Fôret Kokiri# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_POT_LINKS_HOUSE] = HintText(CustomMessage("They say that the #pot in the hero's house# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase im Kokiri-Wald# #[[1]]# enthielte.", //TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Fôret Kokiri# contient #[[1]]#.", {QM_RED, QM_GREEN})); //TODO_TRANSLATE update to match + + hintTextTable[RHT_POT_TWINS_HOUSE] = HintText(CustomMessage("They say that a #pot shared by twins# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase im Kokiri-Wald# #[[1]]# enthielte.", //TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Fôret Kokiri# contient #[[1]]#.", {QM_RED, QM_GREEN})); //TODO_TRANSLATE update to match + + hintTextTable[RHT_POT_KNOW_IT_ALL] = HintText(CustomMessage("They say that a #know-it-all bother's pot# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase im Kokiri-Wald# #[[1]]# enthielte.", //TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Fôret Kokiri# contient #[[1]]#.", {QM_RED, QM_GREEN})); //TODO_TRANSLATE update to match hintTextTable[RHT_POT_GERUDO_FORTRESS] = HintText(CustomMessage("They say that a #pot in Gerudo Fortress# contains #[[1]]#.", /*german*/ "Man erzählt sich, daß eine #Vase in der Gerudo-Festung# #[[1]]# enthielte.", @@ -1504,15 +1512,24 @@ void StaticData::HintTable_Init_Exclude_Overworld() { /*german*/ "Man erzählt sich, daß eine #Vase in der Gespensterwüste# #[[1]]# enthielte.", /*french*/ "Selon moi, une #jarre dans le Désert Hanté# contient #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_POT_MARKET] = HintText(CustomMessage("They say that a #pot in Market# contains #[[1]]#.", - /*german*/ "Man erzählt sich, daß eine #Vase auf dem Markt# #[[1]]# enthielte.", - /*french*/ "Selon moi, une #jarre dans la Place du Marché# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_POT_GUARD_HOUSE] = HintText(CustomMessage("They say that a #pot in the Guard House# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase auf dem Markt# #[[1]]# enthielte.",//TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Place du Marché# contient #[[1]]#.", {QM_RED, QM_GREEN}));//TODO_TRANSLATE update to match + + hintTextTable[RHT_POT_POE_SHOP] = HintText(CustomMessage("They say that a #pot in the Poe Shop# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase auf dem Markt# #[[1]]# enthielte.",//TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Place du Marché# contient #[[1]]#.", {QM_RED, QM_GREEN}));//TODO_TRANSLATE update to match + + hintTextTable[RHT_POT_ALLY_HOUSE] = HintText(CustomMessage("They say that a #bearded man's pot# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase auf dem Markt# #[[1]]# enthielte.",//TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Place du Marché# contient #[[1]]#.", {QM_RED, QM_GREEN}));//TODO_TRANSLATE update to match hintTextTable[RHT_POT_KAKARIKO] = HintText(CustomMessage("They say that a #pot in Kakariko Village# contains #[[1]]#.", /*german*/ "Man erzählt sich, daß eine #Vase in Kakariko# #[[1]]# enthielte.", /*french*/ "Selon moi, une #jarre dans le Village de Cocorico# contient #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_POT_GRAVEYARD] = HintText(CustomMessage("They say that a #pot in Graveyard# contains #[[1]]#.", + hintTextTable[RHT_POT_DAMPE] = HintText(CustomMessage("They say that a #pot in gravekeeper's tomb# contains #[[1]]#.", + //TODO_TRANSLATE check these to make sure they refernce dampe's tomb not the graveyard area /*german*/ "Man erzählt sich, daß eine #Vase auf dem Friedhof# #[[1]]# enthielte.", /*french*/ "Selon moi, une #jarre dans le Cimetière# contient #[[1]]#.", {QM_RED, QM_GREEN})); @@ -1536,13 +1553,17 @@ void StaticData::HintTable_Init_Exclude_Overworld() { /*german*/ "Man erzählt sich, daß eine #Vase auf der Lon Lon-Farm# #[[1]]# enthielte.", /*french*/ "Selon moi, une #jarre dans le Ranch Lon Lon# contient #[[1]]#.", {QM_RED, QM_GREEN})); - hintTextTable[RHT_POT_HYRULE_FIELD] = HintText(CustomMessage("They say that a #pot in Hyrule Field# contains #[[1]]#.", - /*german*/ "Man erzählt sich, daß eine #Vase in der Hylianischen Steppe# #[[1]]# enthielte.", - /*french*/ "Selon moi, une #jarre dans la Plaine d'Hyrule# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_POT_TALONS_HOUSE] = HintText(CustomMessage("They say that a #pot in Talon's Bedroom# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase auf der Lon Lon-Farm# #[[1]]# enthielte.",//TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans le Ranch Lon Lon# contient #[[1]]#.", {QM_RED, QM_GREEN}));//TODO_TRANSLATE update to match + + hintTextTable[RHT_POT_WEB_GROTTO] = HintText(CustomMessage("They say that a #pot behind a grotto's webbing# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase in der Hylianischen Steppe# #[[1]]# enthielte.",//TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans la Plaine d'Hyrule# contient #[[1]]#.", {QM_RED, QM_GREEN}));//TODO_TRANSLATE update to match - hintTextTable[RHT_POT_HYRULE_CASTLE] = HintText(CustomMessage("They say that a #pot in Hyrule Castle# contains #[[1]]#.", - /*german*/ "Man erzählt sich, daß eine #Vase in Schloß Hyrule# #[[1]]# enthielte.", - /*french*/ "Selon moi, une #jarre dans le Château d'Hyrule# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_POT_MUD_WALL_GROTTO] = HintText(CustomMessage("They say that a #pot walled off in a grotto# contains #[[1]]#.", + /*german*/ "Man erzählt sich, daß eine #Vase in Schloß Hyrule# #[[1]]# enthielte.",//TODO_TRANSLATE update to match + /*french*/ "Selon moi, une #jarre dans le Château d'Hyrule# contient #[[1]]#.", {QM_RED, QM_GREEN}));//TODO_TRANSLATE update to match hintTextTable[RHT_KOKIRI_FOREST_RUPEE] = HintText(CustomMessage("They say that a rupee in a #tranquil forest# hides #[[1]]#.", /*german*/ "Man erzählt sich, daß ein Rubin in einem #ruhigen Wald# #[[1]]# verstecke.", diff --git a/soh/soh/Enhancements/randomizer/ShufflePots.cpp b/soh/soh/Enhancements/randomizer/ShufflePots.cpp index 9e6f50f4274..5afd9859c3b 100644 --- a/soh/soh/Enhancements/randomizer/ShufflePots.cpp +++ b/soh/soh/Enhancements/randomizer/ShufflePots.cpp @@ -103,11 +103,11 @@ void Rando::StaticData::RegisterPotLocations() { // clang-format off // Overworld Pots // Randomizer Check Randomizer Check Quest Area Scene ID Params Short Name Hint Text Key Vanilla Spoiler Collection Check - locationTable[RC_KF_LINKS_HOUSE_POT] = Location::Pot(RC_KF_LINKS_HOUSE_POT, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_LINKS_HOUSE, TWO_ACTOR_PARAMS(-118, 51), "Links House Pot", RHT_POT_KOKIRI_FOREST, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_LINKS_HOUSE_POT)); - locationTable[RC_KF_TWINS_HOUSE_POT_2] = Location::Pot(RC_KF_TWINS_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_TWINS_HOUSE, TWO_ACTOR_PARAMS(35, 57), "Twins House Pot 2", RHT_POT_KOKIRI_FOREST, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_TWINS_HOUSE_POT_2)); - locationTable[RC_KF_TWINS_HOUSE_POT_1] = Location::Pot(RC_KF_TWINS_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_TWINS_HOUSE, TWO_ACTOR_PARAMS(33, -55), "Twins House Pot 1", RHT_POT_KOKIRI_FOREST, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_TWINS_HOUSE_POT_1)); - locationTable[RC_KF_BROTHERS_HOUSE_POT_1] = Location::Pot(RC_KF_BROTHERS_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KNOW_IT_ALL_BROS_HOUSE, TWO_ACTOR_PARAMS(-134, -29), "Brothers House Pot 1", RHT_POT_KOKIRI_FOREST, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_BROTHERS_HOUSE_POT_1)); - locationTable[RC_KF_BROTHERS_HOUSE_POT_2] = Location::Pot(RC_KF_BROTHERS_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KNOW_IT_ALL_BROS_HOUSE, TWO_ACTOR_PARAMS(-68, 114), "Brothers House Pot 2", RHT_POT_KOKIRI_FOREST, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_BROTHERS_HOUSE_POT_2)); + locationTable[RC_KF_LINKS_HOUSE_POT] = Location::Pot(RC_KF_LINKS_HOUSE_POT, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_LINKS_HOUSE, TWO_ACTOR_PARAMS(-118, 51), "Links House Pot", RHT_POT_LINKS_HOUSE, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_LINKS_HOUSE_POT)); + locationTable[RC_KF_TWINS_HOUSE_POT_2] = Location::Pot(RC_KF_TWINS_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_TWINS_HOUSE, TWO_ACTOR_PARAMS(35, 57), "Twins House Pot 2", RHT_POT_TWINS_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_TWINS_HOUSE_POT_2)); + locationTable[RC_KF_TWINS_HOUSE_POT_1] = Location::Pot(RC_KF_TWINS_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_TWINS_HOUSE, TWO_ACTOR_PARAMS(33, -55), "Twins House Pot 1", RHT_POT_TWINS_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_TWINS_HOUSE_POT_1)); + locationTable[RC_KF_BROTHERS_HOUSE_POT_1] = Location::Pot(RC_KF_BROTHERS_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KNOW_IT_ALL_BROS_HOUSE, TWO_ACTOR_PARAMS(-134, -29), "Brothers House Pot 1", RHT_POT_KNOW_IT_ALL, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_BROTHERS_HOUSE_POT_1)); + locationTable[RC_KF_BROTHERS_HOUSE_POT_2] = Location::Pot(RC_KF_BROTHERS_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KNOW_IT_ALL_BROS_HOUSE, TWO_ACTOR_PARAMS(-68, 114), "Brothers House Pot 2", RHT_POT_KNOW_IT_ALL, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_BROTHERS_HOUSE_POT_2)); locationTable[RC_GF_BREAK_ROOM_POT_1] = Location::Pot(RC_GF_BREAK_ROOM_POT_1, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(1438, -3629), "Break Room Pot 1", RHT_POT_GERUDO_FORTRESS, RG_ARROWS_10, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_BREAK_ROOM_POT_1)); locationTable[RC_GF_BREAK_ROOM_POT_2] = Location::Pot(RC_GF_BREAK_ROOM_POT_2, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(1403, -3679), "Break Room Pot 2", RHT_POT_GERUDO_FORTRESS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_BREAK_ROOM_POT_2)); locationTable[RC_GF_KITCHEN_POT_1] = Location::Pot(RC_GF_KITCHEN_POT_1, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(1908, -789), "Kitchen Pot 1", RHT_POT_GERUDO_FORTRESS, RG_ARROWS_10, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_KITCHEN_POT_1)); @@ -128,64 +128,64 @@ void Rando::StaticData::RegisterPotLocations() { locationTable[RC_WASTELAND_NEAR_GS_POT_2] = Location::Pot(RC_WASTELAND_NEAR_GS_POT_2, RCQUEST_BOTH, RCAREA_WASTELAND, SCENE_HAUNTED_WASTELAND, TWO_ACTOR_PARAMS(485, -2463), "Near GS Pot 2", RHT_POT_GERUDO_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WASTELAND_NEAR_GS_POT_2)); locationTable[RC_WASTELAND_NEAR_GS_POT_3] = Location::Pot(RC_WASTELAND_NEAR_GS_POT_3, RCQUEST_BOTH, RCAREA_WASTELAND, SCENE_HAUNTED_WASTELAND, TWO_ACTOR_PARAMS(806, -2426), "Near GS Pot 3", RHT_POT_GERUDO_FORTRESS, RG_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WASTELAND_NEAR_GS_POT_3)); locationTable[RC_WASTELAND_NEAR_GS_POT_4] = Location::Pot(RC_WASTELAND_NEAR_GS_POT_4, RCQUEST_BOTH, RCAREA_WASTELAND, SCENE_HAUNTED_WASTELAND, TWO_ACTOR_PARAMS(801, -2460), "Near GS Pot 4", RHT_POT_GERUDO_FORTRESS, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WASTELAND_NEAR_GS_POT_4)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_1] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-60, 27), "Guard House Child Pot 1", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_1)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_2] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-89, 28), "Guard House Child Pot 2", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_2)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_3] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_3, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-110, 6), "Guard House Child Pot 3", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_3)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_4] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_4, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-58, -7), "Guard House Child Pot 4", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_4)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_5] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_5, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-80, -7), "Guard House Child Pot 5", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_5)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_6] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_6, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-65, -45), "Guard House Child Pot 6", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_6)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_7] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_7, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-85, -41), "Guard House Child Pot 7", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_7)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_8] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_8, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-107, -45), "Guard House Child Pot 8", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_8)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_9] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_9, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-66, -79), "Guard House Child Pot 9", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_9)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_10] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_10, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-88, -84), "Guard House Child Pot 10", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_10)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_11] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_11, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(70, 215), "Guard House Child Pot 11", RHT_POT_MARKET, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_11)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_12] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_12, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(68, 148), "Guard House Child Pot 12", RHT_POT_MARKET, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_12)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_13] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_13, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(73, 117), "Guard House Child Pot 13", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_13)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_14] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_14, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(40, 123), "Guard House Child Pot 14", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_14)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_15] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_15, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(43, 89), "Guard House Child Pot 15", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_15)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_16] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_16, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(68, 81), "Guard House Child Pot 16", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_16)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_17] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_17, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(21, 73), "Guard House Child Pot 17", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_17)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_18] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_18, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(64, 45), "Guard House Child Pot 18", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_18)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_19] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_19, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(84, 31), "Guard House Child Pot 19", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_19)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_20] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_20, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(42, 26), "Guard House Child Pot 20", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_20)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_21] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_21, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(20, 34), "Guard House Child Pot 21", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_21)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_22] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_22, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(89, -2), "Guard House Child Pot 22", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_22)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_23] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_23, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(70, -12), "Guard House Child Pot 23", RHT_POT_MARKET, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_23)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_24] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_24, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(42, -5), "Guard House Child Pot 24", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_24)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_25] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_25, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(16, -6), "Guard House Child Pot 25", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_25)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_26] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_26, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(68, -44), "Guard House Child Pot 26", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_26)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_27] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_27, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(37, -40), "Guard House Child Pot 27", RHT_POT_MARKET, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_27)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_28] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_28, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(70, -80), "Guard House Child Pot 28", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_28)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_29] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_29, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(50, -74), "Guard House Child Pot 29", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_29)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_30] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_30, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(28, -79), "Guard House Child Pot 30", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_30)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_31] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_31, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(39, -111), "Guard House Child Pot 31", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_31)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_32] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_32, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(169, 216), "Guard House Child Pot 32", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_32)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_33] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_33, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(168, 166), "Guard House Child Pot 33", RHT_POT_MARKET, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_33)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_34] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_34, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, 120), "Guard House Child Pot 34", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_34)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_35] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_35, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(177, 85), "Guard House Child Pot 35", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_35)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_36] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_36, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(155, 39), "Guard House Child Pot 36", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_36)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_37] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_37, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(184, 13), "Guard House Child Pot 37", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_37)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_38] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_38, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(156, -1), "Guard House Child Pot 38", RHT_POT_MARKET, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_38)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_39] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_39, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(181, -33), "Guard House Child Pot 39", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_39)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_40] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_40, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(156, -45), "Guard House Child Pot 40", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_40)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_41] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_41, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(172, -82), "Guard House Child Pot 41", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_41)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_42] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_42, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, -120), "Guard House Child Pot 42", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_42)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_43] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_43, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, -166), "Guard House Child Pot 43", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_43)); - locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_44] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_44, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, -216), "Guard House Child Pot 44", RHT_POT_MARKET, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_44)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_1] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(61, 204), "Guard House Adult Pot 1", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_1)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_2] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(71, 132), "Guard House Adult Pot 2", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_2)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_3] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_3, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(74, 23), "Guard House Adult Pot 3", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_3)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_4] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_4, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(40, 4), "Guard House Adult Pot 4", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_4)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_5] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_5, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(71, -22), "Guard House Adult Pot 5", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_5)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_6] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_6, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(44, -151), "Guard House Adult Pot 6", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_6)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_7] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_7, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(79, -182), "Guard House Adult Pot 7", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_7)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_8] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_8, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(44, -198), "Guard House Adult Pot 8", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_8)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_9] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_9, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(168, 210), "Guard House Adult Pot 9", RHT_POT_MARKET, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_9)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_10] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_10, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(167, -122), "Guard House Adult Pot 10", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_10)); - locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_11] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_11, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(167, -210), "Guard House Adult Pot 11", RHT_POT_MARKET, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_11)); - locationTable[RC_MK_BACK_ALLEY_HOUSE_POT_1] = Location::Pot(RC_MK_BACK_ALLEY_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_BACK_ALLEY_HOUSE, TWO_ACTOR_PARAMS(100, 45), "Back Alley House Pot 1", RHT_POT_MARKET, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BACK_ALLEY_HOUSE_POT_1)); - locationTable[RC_MK_BACK_ALLEY_HOUSE_POT_2] = Location::Pot(RC_MK_BACK_ALLEY_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_BACK_ALLEY_HOUSE, TWO_ACTOR_PARAMS(12, -180), "Back Alley House Pot 2", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BACK_ALLEY_HOUSE_POT_2)); - locationTable[RC_MK_BACK_ALLEY_HOUSE_POT_3] = Location::Pot(RC_MK_BACK_ALLEY_HOUSE_POT_3, RCQUEST_BOTH, RCAREA_MARKET, SCENE_BACK_ALLEY_HOUSE, TWO_ACTOR_PARAMS(-54, -180), "Back Alley House Pot 3", RHT_POT_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BACK_ALLEY_HOUSE_POT_3)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_1] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-60, 27), "Guard House Child Pot 1", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_1)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_2] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-89, 28), "Guard House Child Pot 2", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_2)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_3] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_3, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-110, 6), "Guard House Child Pot 3", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_3)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_4] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_4, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-58, -7), "Guard House Child Pot 4", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_4)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_5] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_5, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-80, -7), "Guard House Child Pot 5", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_5)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_6] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_6, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-65, -45), "Guard House Child Pot 6", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_6)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_7] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_7, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-85, -41), "Guard House Child Pot 7", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_7)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_8] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_8, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-107, -45), "Guard House Child Pot 8", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_8)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_9] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_9, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-66, -79), "Guard House Child Pot 9", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_9)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_10] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_10, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(-88, -84), "Guard House Child Pot 10", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_10)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_11] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_11, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(70, 215), "Guard House Child Pot 11", RHT_POT_GUARD_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_11)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_12] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_12, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(68, 148), "Guard House Child Pot 12", RHT_POT_GUARD_HOUSE, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_12)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_13] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_13, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(73, 117), "Guard House Child Pot 13", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_13)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_14] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_14, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(40, 123), "Guard House Child Pot 14", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_14)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_15] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_15, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(43, 89), "Guard House Child Pot 15", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_15)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_16] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_16, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(68, 81), "Guard House Child Pot 16", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_16)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_17] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_17, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(21, 73), "Guard House Child Pot 17", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_17)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_18] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_18, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(64, 45), "Guard House Child Pot 18", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_18)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_19] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_19, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(84, 31), "Guard House Child Pot 19", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_19)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_20] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_20, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(42, 26), "Guard House Child Pot 20", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_20)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_21] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_21, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(20, 34), "Guard House Child Pot 21", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_21)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_22] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_22, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(89, -2), "Guard House Child Pot 22", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_22)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_23] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_23, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(70, -12), "Guard House Child Pot 23", RHT_POT_GUARD_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_23)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_24] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_24, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(42, -5), "Guard House Child Pot 24", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_24)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_25] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_25, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(16, -6), "Guard House Child Pot 25", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_25)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_26] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_26, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(68, -44), "Guard House Child Pot 26", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_26)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_27] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_27, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(37, -40), "Guard House Child Pot 27", RHT_POT_GUARD_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_27)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_28] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_28, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(70, -80), "Guard House Child Pot 28", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_28)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_29] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_29, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(50, -74), "Guard House Child Pot 29", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_29)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_30] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_30, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(28, -79), "Guard House Child Pot 30", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_30)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_31] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_31, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(39, -111), "Guard House Child Pot 31", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_31)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_32] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_32, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(169, 216), "Guard House Child Pot 32", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_32)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_33] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_33, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(168, 166), "Guard House Child Pot 33", RHT_POT_GUARD_HOUSE, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_33)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_34] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_34, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, 120), "Guard House Child Pot 34", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_34)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_35] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_35, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(177, 85), "Guard House Child Pot 35", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_35)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_36] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_36, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(155, 39), "Guard House Child Pot 36", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_36)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_37] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_37, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(184, 13), "Guard House Child Pot 37", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_37)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_38] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_38, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(156, -1), "Guard House Child Pot 38", RHT_POT_GUARD_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_38)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_39] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_39, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(181, -33), "Guard House Child Pot 39", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_39)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_40] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_40, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(156, -45), "Guard House Child Pot 40", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_40)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_41] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_41, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(172, -82), "Guard House Child Pot 41", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_41)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_42] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_42, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, -120), "Guard House Child Pot 42", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_42)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_43] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_43, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, -166), "Guard House Child Pot 43", RHT_POT_GUARD_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_43)); + locationTable[RC_MK_GUARD_HOUSE_CHILD_POT_44] = Location::Pot(RC_MK_GUARD_HOUSE_CHILD_POT_44, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(170, -216), "Guard House Child Pot 44", RHT_POT_GUARD_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_CHILD_POT_44)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_1] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(61, 204), "Guard House Adult Pot 1", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_1)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_2] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(71, 132), "Guard House Adult Pot 2", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_2)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_3] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_3, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(74, 23), "Guard House Adult Pot 3", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_3)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_4] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_4, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(40, 4), "Guard House Adult Pot 4", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_4)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_5] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_5, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(71, -22), "Guard House Adult Pot 5", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_5)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_6] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_6, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(44, -151), "Guard House Adult Pot 6", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_6)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_7] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_7, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(79, -182), "Guard House Adult Pot 7", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_7)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_8] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_8, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(44, -198), "Guard House Adult Pot 8", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_8)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_9] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_9, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(168, 210), "Guard House Adult Pot 9", RHT_POT_POE_SHOP, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_9)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_10] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_10, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(167, -122), "Guard House Adult Pot 10", RHT_POT_POE_SHOP, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_10)); + locationTable[RC_MK_GUARD_HOUSE_ADULT_POT_11] = Location::Pot(RC_MK_GUARD_HOUSE_ADULT_POT_11, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_GUARD_HOUSE, TWO_ACTOR_PARAMS(167, -210), "Guard House Adult Pot 11", RHT_POT_POE_SHOP, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_GUARD_HOUSE_ADULT_POT_11)); + locationTable[RC_MK_BACK_ALLEY_HOUSE_POT_1] = Location::Pot(RC_MK_BACK_ALLEY_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_BACK_ALLEY_HOUSE, TWO_ACTOR_PARAMS(100, 45), "Back Alley House Pot 1", RHT_POT_ALLY_HOUSE, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BACK_ALLEY_HOUSE_POT_1)); + locationTable[RC_MK_BACK_ALLEY_HOUSE_POT_2] = Location::Pot(RC_MK_BACK_ALLEY_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_BACK_ALLEY_HOUSE, TWO_ACTOR_PARAMS(12, -180), "Back Alley House Pot 2", RHT_POT_ALLY_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BACK_ALLEY_HOUSE_POT_2)); + locationTable[RC_MK_BACK_ALLEY_HOUSE_POT_3] = Location::Pot(RC_MK_BACK_ALLEY_HOUSE_POT_3, RCQUEST_BOTH, RCAREA_MARKET, SCENE_BACK_ALLEY_HOUSE, TWO_ACTOR_PARAMS(-54, -180), "Back Alley House Pot 3", RHT_POT_ALLY_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BACK_ALLEY_HOUSE_POT_3)); locationTable[RC_KAK_NEAR_POTION_SHOP_POT_1] = Location::Pot(RC_KAK_NEAR_POTION_SHOP_POT_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(222, -377), "Near Potion Shop Pot 1", RHT_POT_KAKARIKO, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_POTION_SHOP_POT_1)); locationTable[RC_KAK_NEAR_POTION_SHOP_POT_2] = Location::Pot(RC_KAK_NEAR_POTION_SHOP_POT_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(255, -366), "Near Potion Shop Pot 2", RHT_POT_KAKARIKO, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_POTION_SHOP_POT_2)); locationTable[RC_KAK_NEAR_POTION_SHOP_POT_3] = Location::Pot(RC_KAK_NEAR_POTION_SHOP_POT_3, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(284, -356), "Near Potion Shop Pot 3", RHT_POT_KAKARIKO, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_POTION_SHOP_POT_3)); @@ -197,12 +197,12 @@ void Rando::StaticData::RegisterPotLocations() { locationTable[RC_KAK_NEAR_GUARDS_HOUSE_POT_3] = Location::Pot(RC_KAK_NEAR_GUARDS_HOUSE_POT_3, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-450, -895), "Near Guards House Pot 3", RHT_POT_KAKARIKO, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_GUARDS_HOUSE_POT_3)); locationTable[RC_KAK_NEAR_MEDICINE_SHOP_POT_1] = Location::Pot(RC_KAK_NEAR_MEDICINE_SHOP_POT_1, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(781, 89), "Near Medicine Shop Pot 1", RHT_POT_KAKARIKO, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_MEDICINE_SHOP_POT_1)); locationTable[RC_KAK_NEAR_MEDICINE_SHOP_POT_2] = Location::Pot(RC_KAK_NEAR_MEDICINE_SHOP_POT_2, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(815, 89), "Near Medicine Shop Pot 2", RHT_POT_KAKARIKO, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_NEAR_MEDICINE_SHOP_POT_2)); - locationTable[RC_GY_DAMPES_GRAVE_POT_1] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_1, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(-319, -1542), "Dampes Grave Pot 1", RHT_POT_GRAVEYARD, RG_ARROWS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_1)); - locationTable[RC_GY_DAMPES_GRAVE_POT_2] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_2, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(-319, -1600), "Dampes Grave Pot 2", RHT_POT_GRAVEYARD, RG_BOMBS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_2)); - locationTable[RC_GY_DAMPES_GRAVE_POT_3] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_3, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(-364, -1571), "Dampes Grave Pot 3", RHT_POT_GRAVEYARD, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_3)); - locationTable[RC_GY_DAMPES_GRAVE_POT_4] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_4, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(198, -1540), "Dampes Grave Pot 4", RHT_POT_GRAVEYARD, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_4)); - locationTable[RC_GY_DAMPES_GRAVE_POT_5] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_5, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(198, -1608), "Dampes Grave Pot 5", RHT_POT_GRAVEYARD, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_5)); - locationTable[RC_GY_DAMPES_GRAVE_POT_6] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_6, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(239, -1577), "Dampes Grave Pot 6", RHT_POT_GRAVEYARD, RG_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_6)); + locationTable[RC_GY_DAMPES_GRAVE_POT_1] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_1, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(-319, -1542), "Dampes Grave Pot 1", RHT_POT_DAMPE, RG_ARROWS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_1)); + locationTable[RC_GY_DAMPES_GRAVE_POT_2] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_2, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(-319, -1600), "Dampes Grave Pot 2", RHT_POT_DAMPE, RG_BOMBS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_2)); + locationTable[RC_GY_DAMPES_GRAVE_POT_3] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_3, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(-364, -1571), "Dampes Grave Pot 3", RHT_POT_DAMPE, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_3)); + locationTable[RC_GY_DAMPES_GRAVE_POT_4] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_4, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(198, -1540), "Dampes Grave Pot 4", RHT_POT_DAMPE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_4)); + locationTable[RC_GY_DAMPES_GRAVE_POT_5] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_5, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(198, -1608), "Dampes Grave Pot 5", RHT_POT_DAMPE, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_5)); + locationTable[RC_GY_DAMPES_GRAVE_POT_6] = Location::Pot(RC_GY_DAMPES_GRAVE_POT_6, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(239, -1577), "Dampes Grave Pot 6", RHT_POT_DAMPE, RG_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_DAMPES_GRAVE_POT_6)); locationTable[RC_GC_LOWER_STAIRCASE_POT_1] = Location::Pot(RC_GC_LOWER_STAIRCASE_POT_1, RCQUEST_BOTH, RCAREA_GORON_CITY, SCENE_GORON_CITY, TWO_ACTOR_PARAMS(-189, 866), "Lower Staircase Pot 1", RHT_POT_GORON_CITY, RG_DEKU_STICK_1, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GC_LOWER_STAIRCASE_POT_1)); locationTable[RC_GC_LOWER_STAIRCASE_POT_2] = Location::Pot(RC_GC_LOWER_STAIRCASE_POT_2, RCQUEST_BOTH, RCAREA_GORON_CITY, SCENE_GORON_CITY, TWO_ACTOR_PARAMS(-271, 825), "Lower Staircase Pot 2", RHT_POT_GORON_CITY, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GC_LOWER_STAIRCASE_POT_2)); locationTable[RC_GC_UPPER_STAIRCASE_POT_1] = Location::Pot(RC_GC_UPPER_STAIRCASE_POT_1, RCQUEST_BOTH, RCAREA_GORON_CITY, SCENE_GORON_CITY, TWO_ACTOR_PARAMS(-1170, 60), "Upper Staircase Pot 1", RHT_POT_GORON_CITY, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GC_UPPER_STAIRCASE_POT_1)); @@ -235,15 +235,15 @@ void Rando::StaticData::RegisterPotLocations() { locationTable[RC_LLR_RAIN_SHED_POT_1] = Location::Pot(RC_LLR_RAIN_SHED_POT_1, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_RANCH, TWO_ACTOR_PARAMS(852, 172), "Rain Shed Pot 1", RHT_POT_LON_LON_RANCH, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_RAIN_SHED_POT_1)); locationTable[RC_LLR_RAIN_SHED_POT_2] = Location::Pot(RC_LLR_RAIN_SHED_POT_2, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_RANCH, TWO_ACTOR_PARAMS(840, 212), "Rain Shed Pot 2", RHT_POT_LON_LON_RANCH, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_RAIN_SHED_POT_2)); locationTable[RC_LLR_RAIN_SHED_POT_3] = Location::Pot(RC_LLR_RAIN_SHED_POT_3, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_RANCH, TWO_ACTOR_PARAMS(872, 219), "Rain Shed Pot 3", RHT_POT_LON_LON_RANCH, RG_RECOVERY_HEART, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_RAIN_SHED_POT_3)); - locationTable[RC_LLR_TALONS_HOUSE_POT_1] = Location::Pot(RC_LLR_TALONS_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_BUILDINGS, TWO_ACTOR_PARAMS(1255, 47), "Talons House Pot 1", RHT_POT_LON_LON_RANCH, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_TALONS_HOUSE_POT_1)); - locationTable[RC_LLR_TALONS_HOUSE_POT_2] = Location::Pot(RC_LLR_TALONS_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_BUILDINGS, TWO_ACTOR_PARAMS(1256, -51), "Talons House Pot 2", RHT_POT_LON_LON_RANCH, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_TALONS_HOUSE_POT_2)); - locationTable[RC_LLR_TALONS_HOUSE_POT_3] = Location::Pot(RC_LLR_TALONS_HOUSE_POT_3, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_BUILDINGS, TWO_ACTOR_PARAMS(1256, -78), "Talons House Pot 3", RHT_POT_LON_LON_RANCH, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_TALONS_HOUSE_POT_3)); - locationTable[RC_HF_COW_GROTTO_POT_1] = Location::Pot(RC_HF_COW_GROTTO_POT_1, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, TWO_ACTOR_PARAMS(3410, -223), "Cow Grotto Pot 1", RHT_POT_HYRULE_FIELD, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_COW_GROTTO_POT_1)); - locationTable[RC_HF_COW_GROTTO_POT_2] = Location::Pot(RC_HF_COW_GROTTO_POT_2, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, TWO_ACTOR_PARAMS(3390, -258), "Cow Grotto Pot 2", RHT_POT_HYRULE_FIELD, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_COW_GROTTO_POT_2)); - locationTable[RC_HC_STORMS_GROTTO_POT_1] = Location::Pot(RC_HC_STORMS_GROTTO_POT_1, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1843, 1014), "Storms Grotto Pot 1", RHT_POT_HYRULE_CASTLE, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_1)); - locationTable[RC_HC_STORMS_GROTTO_POT_2] = Location::Pot(RC_HC_STORMS_GROTTO_POT_2, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1769, 954), "Storms Grotto Pot 2", RHT_POT_HYRULE_CASTLE, RG_ARROWS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_2)); - locationTable[RC_HC_STORMS_GROTTO_POT_3] = Location::Pot(RC_HC_STORMS_GROTTO_POT_3, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1857, 897), "Storms Grotto Pot 3", RHT_POT_HYRULE_CASTLE, RG_BOMBS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_3)); - locationTable[RC_HC_STORMS_GROTTO_POT_4] = Location::Pot(RC_HC_STORMS_GROTTO_POT_4, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1764, 847), "Storms Grotto Pot 4", RHT_POT_HYRULE_CASTLE, RG_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_4)); + locationTable[RC_LLR_TALONS_HOUSE_POT_1] = Location::Pot(RC_LLR_TALONS_HOUSE_POT_1, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_BUILDINGS, TWO_ACTOR_PARAMS(1255, 47), "Talons House Pot 1", RHT_POT_TALONS_HOUSE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_TALONS_HOUSE_POT_1)); + locationTable[RC_LLR_TALONS_HOUSE_POT_2] = Location::Pot(RC_LLR_TALONS_HOUSE_POT_2, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_BUILDINGS, TWO_ACTOR_PARAMS(1256, -51), "Talons House Pot 2", RHT_POT_TALONS_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_TALONS_HOUSE_POT_2)); + locationTable[RC_LLR_TALONS_HOUSE_POT_3] = Location::Pot(RC_LLR_TALONS_HOUSE_POT_3, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_BUILDINGS, TWO_ACTOR_PARAMS(1256, -78), "Talons House Pot 3", RHT_POT_TALONS_HOUSE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_TALONS_HOUSE_POT_3)); + locationTable[RC_HF_COW_GROTTO_POT_1] = Location::Pot(RC_HF_COW_GROTTO_POT_1, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, TWO_ACTOR_PARAMS(3410, -223), "Cow Grotto Pot 1", RHT_POT_WEB_GROTTO, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_COW_GROTTO_POT_1)); + locationTable[RC_HF_COW_GROTTO_POT_2] = Location::Pot(RC_HF_COW_GROTTO_POT_2, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, TWO_ACTOR_PARAMS(3390, -258), "Cow Grotto Pot 2", RHT_POT_WEB_GROTTO, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_COW_GROTTO_POT_2)); + locationTable[RC_HC_STORMS_GROTTO_POT_1] = Location::Pot(RC_HC_STORMS_GROTTO_POT_1, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1843, 1014), "Storms Grotto Pot 1", RHT_POT_MUD_WALL_GROTTO, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_1)); + locationTable[RC_HC_STORMS_GROTTO_POT_2] = Location::Pot(RC_HC_STORMS_GROTTO_POT_2, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1769, 954), "Storms Grotto Pot 2", RHT_POT_MUD_WALL_GROTTO, RG_ARROWS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_2)); + locationTable[RC_HC_STORMS_GROTTO_POT_3] = Location::Pot(RC_HC_STORMS_GROTTO_POT_3, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1857, 897), "Storms Grotto Pot 3", RHT_POT_MUD_WALL_GROTTO, RG_BOMBS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_3)); + locationTable[RC_HC_STORMS_GROTTO_POT_4] = Location::Pot(RC_HC_STORMS_GROTTO_POT_4, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, TWO_ACTOR_PARAMS(1764, 847), "Storms Grotto Pot 4", RHT_POT_MUD_WALL_GROTTO, RG_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_STORMS_GROTTO_POT_4)); // Dungeon Pots // Randomizer Check Randomizer Check Quest Area Scene ID Params Short Name Spoiler Name Hint Text Key Vanilla Spoiler Collection Check diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index d19d32c9954..d9eae0af8e6 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -5370,19 +5370,24 @@ typedef enum { RHT_SKULLS_HINT, RHT_MASK_SHOP_HINT, // Shuffle Pots - RHT_POT_KOKIRI_FOREST, + RHT_POT_LINKS_HOUSE, + RHT_POT_TWINS_HOUSE, + RHT_POT_KNOW_IT_ALL, RHT_POT_GERUDO_FORTRESS, RHT_POT_WASTELAND, - RHT_POT_MARKET, + RHT_POT_GUARD_HOUSE, + RHT_POT_POE_SHOP, + RHT_POT_ALLY_HOUSE, RHT_POT_KAKARIKO, - RHT_POT_GRAVEYARD, + RHT_POT_DAMPE, RHT_POT_GORON_CITY, RHT_POT_DEATH_MOUNTAIN_CRATER, RHT_POT_ZORAS_DOMAIN, RHT_POT_ZORAS_FOUNTAIN, RHT_POT_LON_LON_RANCH, - RHT_POT_HYRULE_FIELD, - RHT_POT_HYRULE_CASTLE, + RHT_POT_TALONS_HOUSE, + RHT_POT_WEB_GROTTO, + RHT_POT_MUD_WALL_GROTTO, RHT_POT_DODONGOS_CAVERN, RHT_POT_JABU_JABUS_BELLY, RHT_POT_FOREST_TEMPLE, From 2daf343755be6968b2b259e3f2b42f50f343ce28 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:26:30 +0100 Subject: [PATCH 109/157] Change excluded checks to have junk instead of non-advancment. (#5592) * Change excluded checks to have junk instead of non-advancment * loud frying pan noises --- .../randomizer/3drando/item_pool.cpp | 2 +- soh/soh/Enhancements/randomizer/item.cpp | 14 +++++--- soh/soh/Enhancements/randomizer/item.h | 5 ++- soh/soh/Enhancements/randomizer/item_list.cpp | 32 +++++++++---------- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index a04a7d65c68..fb2ac94cc1f 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -354,7 +354,7 @@ void PlaceJunkInExcludedLocation(const RandomizerCheck il) { // place a non-advancement item in this location auto ctx = Rando::Context::GetInstance(); for (size_t i = 0; i < ItemPool.size(); i++) { - if (!Rando::StaticData::RetrieveItem(ItemPool[i]).IsAdvancement()) { + if (Rando::StaticData::RetrieveItem(ItemPool[i]).GetCategory() == ITEM_CATEGORY_JUNK) { ctx->PlaceItemInLocation(il, ItemPool[i]); ItemPool.erase(ItemPool.begin() + i); return; diff --git a/soh/soh/Enhancements/randomizer/item.cpp b/soh/soh/Enhancements/randomizer/item.cpp index 3b75a0c9546..71f9fff38e6 100644 --- a/soh/soh/Enhancements/randomizer/item.cpp +++ b/soh/soh/Enhancements/randomizer/item.cpp @@ -21,7 +21,8 @@ Item::Item(const RandomizerGet randomizerGet_, Text name_, const ItemType type_, const int16_t chestAnimation_, const GetItemCategory category_, const uint16_t modIndex_, const bool progressive_, const uint16_t price_) : randomizerGet(randomizerGet_), name(std::move(name_)), type(type_), getItemId(getItemId_), - advancement(advancement_), logicVal(logicVal_), hintKey(hintKey_), progressive(progressive_), price(price_) { + advancement(advancement_), logicVal(logicVal_), hintKey(hintKey_), category(category_), progressive(progressive_), + price(price_) { if (modIndex_ == MOD_RANDOMIZER || getItemId > 0x7D) { giEntry = std::make_shared(GetItemEntry{ itemId_, field_, static_cast((chestAnimation_ != CHEST_ANIM_SHORT ? 1 : -1) * (gid_ + 1)), textId_, @@ -36,10 +37,11 @@ Item::Item(const RandomizerGet randomizerGet_, Text name_, const ItemType type_, } Item::Item(const RandomizerGet randomizerGet_, Text name_, const ItemType type_, const int16_t getItemId_, - const bool advancement_, LogicVal logicVal_, const RandomizerHintTextKey hintKey_, const bool progressive_, - const uint16_t price_) + const bool advancement_, LogicVal logicVal_, const RandomizerHintTextKey hintKey_, + const GetItemCategory category_, const bool progressive_, const uint16_t price_) : randomizerGet(randomizerGet_), name(std::move(name_)), type(type_), getItemId(getItemId_), - advancement(advancement_), logicVal(logicVal_), hintKey(hintKey_), progressive(progressive_), price(price_) { + advancement(advancement_), logicVal(logicVal_), hintKey(hintKey_), category(category_), progressive(progressive_), + price(price_) { } Item::~Item() = default; @@ -456,6 +458,10 @@ const HintText& Item::GetHint() const { return StaticData::hintTextTable[hintKey]; } +GetItemCategory Item::GetCategory() { + return category; +} + bool Item::operator==(const Item& right) const { return type == right.GetItemType() && getItemId == right.GetItemID(); } diff --git a/soh/soh/Enhancements/randomizer/item.h b/soh/soh/Enhancements/randomizer/item.h index da86321518c..13a654d9c22 100644 --- a/soh/soh/Enhancements/randomizer/item.h +++ b/soh/soh/Enhancements/randomizer/item.h @@ -35,7 +35,8 @@ class Item { uint16_t textId_, uint16_t field_, int16_t chestAnimation_, GetItemCategory category_, uint16_t modIndex_, bool progressive_ = false, uint16_t price_ = 0); Item(RandomizerGet randomizerGet_, Text name_, ItemType type_, int16_t getItemId_, bool advancement_, - LogicVal logicVal_, RandomizerHintTextKey hintKey_, bool progressive_ = false, uint16_t price_ = 0); + LogicVal logicVal_, RandomizerHintTextKey hintKey_, GetItemCategory category_, bool progressive_ = false, + uint16_t price_ = 0); ~Item(); void ApplyEffect() const; @@ -58,6 +59,7 @@ class Item { bool IsMajorItem() const; RandomizerHintTextKey GetHintKey() const; const HintText& GetHint() const; + GetItemCategory GetCategory(); bool operator==(const Item& right) const; bool operator!=(const Item& right) const; @@ -69,6 +71,7 @@ class Item { bool advancement; LogicVal logicVal; RandomizerHintTextKey hintKey; + GetItemCategory category; bool progressive; uint16_t price; bool playthrough = false; diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index e8198737c4f..f00796f317f 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -59,19 +59,19 @@ void Rando::StaticData::InitItemTable() { // Skulltula Token itemTable[RG_GOLD_SKULLTULA_TOKEN] = Item(RG_GOLD_SKULLTULA_TOKEN, Text{ "Gold Skulltula Token", "Symbole de Skulltula d'Or", "Goldenes Skulltula-Symbol" }, ITEMTYPE_TOKEN, GI_SKULL_TOKEN, true, LOGIC_GOLD_SKULLTULA_TOKENS, RHT_GOLD_SKULLTULA_TOKEN, ITEM_SKULL_TOKEN, OBJECT_GI_SUTARU, GID_SKULL_TOKEN, 0xB4, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SKULLTULA_TOKEN, MOD_NONE); // Progressive Items - itemTable[RG_PROGRESSIVE_HOOKSHOT] = Item(RG_PROGRESSIVE_HOOKSHOT, Text{ "Progressive Hookshot", "Grappin (prog.)", "Progressiver Fanghaken" }, ITEMTYPE_ITEM, 0x80, true, LOGIC_PROGRESSIVE_HOOKSHOT, RHT_PROGRESSIVE_HOOKSHOT, true); - itemTable[RG_PROGRESSIVE_STRENGTH] = Item(RG_PROGRESSIVE_STRENGTH, Text{ "Strength Upgrade", "Amélioration de Force (prog.)", "Progressives Kraft-Upgrade" }, ITEMTYPE_ITEM, 0x81, true, LOGIC_PROGRESSIVE_STRENGTH, RHT_PROGRESSIVE_STRENGTH, true); - itemTable[RG_PROGRESSIVE_BOMB_BAG] = Item(RG_PROGRESSIVE_BOMB_BAG, Text{ "Progressive Bomb Bag", "Sac de Bombes (prog.)", "Progressive Bombentasche" }, ITEMTYPE_ITEM, 0x82, true, LOGIC_PROGRESSIVE_BOMB_BAG, RHT_PROGRESSIVE_BOMB_BAG, true); - itemTable[RG_PROGRESSIVE_BOW] = Item(RG_PROGRESSIVE_BOW, Text{ "Progressive Bow", "Arc (prog.)", "Progressiver Bogen" }, ITEMTYPE_ITEM, 0x83, true, LOGIC_PROGRESSIVE_BOW, RHT_PROGRESSIVE_BOW, true); - itemTable[RG_PROGRESSIVE_SLINGSHOT] = Item(RG_PROGRESSIVE_SLINGSHOT, Text{ "Progressive Slingshot", "Lance-Pierre (prog.)", "Progressive Steinschleuder" }, ITEMTYPE_ITEM, 0x84, true, LOGIC_PROGRESSIVE_BULLET_BAG, RHT_PROGRESSIVE_SLINGSHOT, true); - itemTable[RG_PROGRESSIVE_WALLET] = Item(RG_PROGRESSIVE_WALLET, Text{ "Progressive Wallet", "Bourse (prog.)", "Progressive Geldbörse" }, ITEMTYPE_ITEM, 0x85, true, LOGIC_PROGRESSIVE_WALLET, RHT_PROGRESSIVE_WALLET, true); - itemTable[RG_PROGRESSIVE_SCALE] = Item(RG_PROGRESSIVE_SCALE, Text{ "Progressive Scale", "Écaille (prog.)", "Progressive Schuppe" }, ITEMTYPE_ITEM, 0x86, true, LOGIC_PROGRESSIVE_SCALE, RHT_PROGRESSIVE_SCALE, true); - itemTable[RG_PROGRESSIVE_NUT_UPGRADE] = Item(RG_PROGRESSIVE_NUT_UPGRADE, Text{ "Progressive Nut Capacity", "Capacité de Noix (prog.)", "Progressive Nuß-Kapazität" }, ITEMTYPE_ITEM, 0x87, true, LOGIC_PROGRESSIVE_NUT_BAG, RHT_PROGRESSIVE_NUT_UPGRADE, true); - itemTable[RG_PROGRESSIVE_STICK_UPGRADE] = Item(RG_PROGRESSIVE_STICK_UPGRADE, Text{ "Progressive Stick Capacity", "Capacité de Bâtons (prog.)", "Progressive Stab-Kapazität" }, ITEMTYPE_ITEM, 0x88, true, LOGIC_PROGRESSIVE_STICK_BAG, RHT_PROGRESSIVE_STICK_UPGRADE, true); - itemTable[RG_PROGRESSIVE_BOMBCHUS] = Item(RG_PROGRESSIVE_BOMBCHUS, Text{ "Progressive Bombchu", "Missiles (prog.)", "Progressive Krabbelminen" }, ITEMTYPE_ITEM, 0x89, true, LOGIC_BOMBCHUS, RHT_PROGRESSIVE_BOMBCHUS, true); - itemTable[RG_PROGRESSIVE_MAGIC_METER] = Item(RG_PROGRESSIVE_MAGIC_METER, Text{ "Progressive Magic Meter", "Jauge de Magie (prog.)", "Progressives Magisches Maß" }, ITEMTYPE_ITEM, 0x8A, true, LOGIC_PROGRESSIVE_MAGIC, RHT_PROGRESSIVE_MAGIC_METER, true); - itemTable[RG_PROGRESSIVE_OCARINA] = Item(RG_PROGRESSIVE_OCARINA, Text{ "Progressive Ocarina", "Ocarina (prog.)", "Progressive Okarina" }, ITEMTYPE_ITEM, 0x8B, true, LOGIC_PROGRESSIVE_OCARINA, RHT_PROGRESSIVE_OCARINA, true); - itemTable[RG_PROGRESSIVE_GORONSWORD] = Item(RG_PROGRESSIVE_GORONSWORD, Text{ "Progressive Goron Sword", "Épée Goron (prog.)", "Progressives Goronen-Schwert" }, ITEMTYPE_ITEM, 0xD4, true, LOGIC_PROGRESSIVE_GIANT_KNIFE, RHT_PROGRESSIVE_GORONSWORD, true); + itemTable[RG_PROGRESSIVE_HOOKSHOT] = Item(RG_PROGRESSIVE_HOOKSHOT, Text{ "Progressive Hookshot", "Grappin (prog.)", "Progressiver Fanghaken" }, ITEMTYPE_ITEM, 0x80, true, LOGIC_PROGRESSIVE_HOOKSHOT, RHT_PROGRESSIVE_HOOKSHOT, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_STRENGTH] = Item(RG_PROGRESSIVE_STRENGTH, Text{ "Strength Upgrade", "Amélioration de Force (prog.)", "Progressives Kraft-Upgrade" }, ITEMTYPE_ITEM, 0x81, true, LOGIC_PROGRESSIVE_STRENGTH, RHT_PROGRESSIVE_STRENGTH, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_BOMB_BAG] = Item(RG_PROGRESSIVE_BOMB_BAG, Text{ "Progressive Bomb Bag", "Sac de Bombes (prog.)", "Progressive Bombentasche" }, ITEMTYPE_ITEM, 0x82, true, LOGIC_PROGRESSIVE_BOMB_BAG, RHT_PROGRESSIVE_BOMB_BAG, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_BOW] = Item(RG_PROGRESSIVE_BOW, Text{ "Progressive Bow", "Arc (prog.)", "Progressiver Bogen" }, ITEMTYPE_ITEM, 0x83, true, LOGIC_PROGRESSIVE_BOW, RHT_PROGRESSIVE_BOW, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_SLINGSHOT] = Item(RG_PROGRESSIVE_SLINGSHOT, Text{ "Progressive Slingshot", "Lance-Pierre (prog.)", "Progressive Steinschleuder" }, ITEMTYPE_ITEM, 0x84, true, LOGIC_PROGRESSIVE_BULLET_BAG, RHT_PROGRESSIVE_SLINGSHOT, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_WALLET] = Item(RG_PROGRESSIVE_WALLET, Text{ "Progressive Wallet", "Bourse (prog.)", "Progressive Geldbörse" }, ITEMTYPE_ITEM, 0x85, true, LOGIC_PROGRESSIVE_WALLET, RHT_PROGRESSIVE_WALLET, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_SCALE] = Item(RG_PROGRESSIVE_SCALE, Text{ "Progressive Scale", "Écaille (prog.)", "Progressive Schuppe" }, ITEMTYPE_ITEM, 0x86, true, LOGIC_PROGRESSIVE_SCALE, RHT_PROGRESSIVE_SCALE, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_NUT_UPGRADE] = Item(RG_PROGRESSIVE_NUT_UPGRADE, Text{ "Progressive Nut Capacity", "Capacité de Noix (prog.)", "Progressive Nuß-Kapazität" }, ITEMTYPE_ITEM, 0x87, true, LOGIC_PROGRESSIVE_NUT_BAG, RHT_PROGRESSIVE_NUT_UPGRADE, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_STICK_UPGRADE] = Item(RG_PROGRESSIVE_STICK_UPGRADE, Text{ "Progressive Stick Capacity", "Capacité de Bâtons (prog.)", "Progressive Stab-Kapazität" }, ITEMTYPE_ITEM, 0x88, true, LOGIC_PROGRESSIVE_STICK_BAG, RHT_PROGRESSIVE_STICK_UPGRADE, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_BOMBCHUS] = Item(RG_PROGRESSIVE_BOMBCHUS, Text{ "Progressive Bombchu", "Missiles (prog.)", "Progressive Krabbelminen" }, ITEMTYPE_ITEM, 0x89, true, LOGIC_BOMBCHUS, RHT_PROGRESSIVE_BOMBCHUS, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_MAGIC_METER] = Item(RG_PROGRESSIVE_MAGIC_METER, Text{ "Progressive Magic Meter", "Jauge de Magie (prog.)", "Progressives Magisches Maß" }, ITEMTYPE_ITEM, 0x8A, true, LOGIC_PROGRESSIVE_MAGIC, RHT_PROGRESSIVE_MAGIC_METER, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_OCARINA] = Item(RG_PROGRESSIVE_OCARINA, Text{ "Progressive Ocarina", "Ocarina (prog.)", "Progressive Okarina" }, ITEMTYPE_ITEM, 0x8B, true, LOGIC_PROGRESSIVE_OCARINA, RHT_PROGRESSIVE_OCARINA, ITEM_CATEGORY_MAJOR, true); + itemTable[RG_PROGRESSIVE_GORONSWORD] = Item(RG_PROGRESSIVE_GORONSWORD, Text{ "Progressive Goron Sword", "Épée Goron (prog.)", "Progressives Goronen-Schwert" }, ITEMTYPE_ITEM, 0xD4, true, LOGIC_PROGRESSIVE_GIANT_KNIFE, RHT_PROGRESSIVE_GORONSWORD, ITEM_CATEGORY_MAJOR, true); // Bottles itemTable[RG_EMPTY_BOTTLE] = Item(RG_EMPTY_BOTTLE, Text{ "Empty Bottle", "Bouteille Vide", "Leere Flasche" }, ITEMTYPE_ITEM, GI_BOTTLE, true, LOGIC_BOTTLES, RHT_BOTTLE_WITH_MILK, ITEM_BOTTLE, OBJECT_GI_BOTTLE, GID_BOTTLE, 0x42, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE); itemTable[RG_BOTTLE_WITH_MILK] = Item(RG_BOTTLE_WITH_MILK, Text{ "Bottle with Milk", "Bouteille avec du Lait", "Flasche mit Milch" }, ITEMTYPE_ITEM, GI_MILK_BOTTLE, true, LOGIC_BOTTLES, RHT_BOTTLE_WITH_MILK, ITEM_MILK_BOTTLE, OBJECT_GI_MILK, GID_MILK, 0x98, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE); @@ -305,7 +305,7 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_BUY_BOMBCHUS_10] = Item(RG_BUY_BOMBCHUS_10, Text{ "Buy Bombchu (10)", "Acheter: Missiles (10)", "Krabbelminen kaufen (10)" }, ITEMTYPE_SHOP, GI_BOMBCHUS_10, true, LOGIC_BUY_BOMBCHUS, RHT_BOMBCHUS_10, ITEM_BOMBCHU, OBJECT_GI_BOMB_2, GID_BOMBCHU, 0x33, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 99); itemTable[RG_BUY_BOMBCHUS_20] = Item(RG_BUY_BOMBCHUS_20, Text{ "Buy Bombchu (20)", "Acheter: Missiles (20)", "Krabbelminen kaufen (20)" }, ITEMTYPE_SHOP, GI_BOMBCHUS_20, true, LOGIC_BUY_BOMBCHUS, RHT_BOMBCHUS_20, ITEM_BOMBCHUS_20, OBJECT_GI_BOMB_2, GID_BOMBCHU, 0x33, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 180); itemTable[RG_BUY_DEKU_SEEDS_30] = Item(RG_BUY_DEKU_SEEDS_30, Text{ "Buy Deku Seeds (30)", "Acheter: Graines Mojo (30)", "Deku-Samen kaufen (30)" }, ITEMTYPE_SHOP, GI_SEEDS_30, true, LOGIC_BUY_SEED, RHT_DEKU_SEEDS_30, ITEM_SEEDS_30, OBJECT_GI_SEED, GID_SEEDS, 0xDC, 0x50, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, false, 30); - itemTable[RG_SOLD_OUT] = Item(RG_SOLD_OUT, Text{ "Sold Out", "Rupture de stock", "Ausverkauft" }, ITEMTYPE_SHOP, RG_SOLD_OUT, false, LOGIC_NONE, RHT_NONE, false, 0); + itemTable[RG_SOLD_OUT] = Item(RG_SOLD_OUT, Text{ "Sold Out", "Rupture de stock", "Ausverkauft" }, ITEMTYPE_SHOP, RG_SOLD_OUT, false, LOGIC_NONE, RHT_NONE, ITEM_CATEGORY_JUNK, false, 0); itemTable[RG_BUY_BLUE_FIRE] = Item(RG_BUY_BLUE_FIRE, Text{ "Buy Blue Fire", "Acheter: Flamme Bleue", "Blaues Feuer kaufen" }, ITEMTYPE_SHOP, GI_BLUE_FIRE, true, LOGIC_BLUE_FIRE_ACCESS, RHT_BOTTLE_WITH_BLUE_FIRE, ITEM_BLUE_FIRE, OBJECT_GI_FIRE, GID_BLUE_FIRE, 0x5D, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_JUNK, MOD_NONE, false, 300); itemTable[RG_BUY_BOTTLE_BUG] = Item(RG_BUY_BOTTLE_BUG, Text{ "Buy Bottle Bug", "Acheter: Insecte en bouteille", "Flaschenkäfer kaufen" }, ITEMTYPE_SHOP, GI_BUGS, true, LOGIC_BUGS_ACCESS, RHT_BOTTLE_WITH_BUGS, ITEM_BUG, OBJECT_GI_INSECT, GID_BUG, 0x7A, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_JUNK, MOD_NONE, false, 50); itemTable[RG_BUY_POE] = Item(RG_BUY_POE, Text{ "Buy Poe", "Acheter: Esprit", "Geist kaufen" }, ITEMTYPE_SHOP, RG_BUY_POE, false, LOGIC_NONE, RHT_BOTTLE_WITH_BIG_POE, ITEM_POE, OBJECT_GI_GHOST, GID_POE, 0x97, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_JUNK, MOD_NONE, false, 30); @@ -372,8 +372,8 @@ 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); - itemTable[RG_TRIFORCE] = Item(RG_TRIFORCE, Text{ "Triforce", "Triforce", "Triforce" }, ITEMTYPE_EVENT, RG_TRIFORCE, false, LOGIC_NONE, RHT_NONE); - itemTable[RG_HINT] = Item(RG_HINT, Text{ "Hint", "Indice", "Hinweis" }, ITEMTYPE_EVENT, RG_HINT, false, LOGIC_NONE, RHT_NONE); + itemTable[RG_TRIFORCE] = Item(RG_TRIFORCE, Text{ "Triforce", "Triforce", "Triforce" }, ITEMTYPE_EVENT, RG_TRIFORCE, false, LOGIC_NONE, RHT_NONE, ITEM_CATEGORY_MAJOR); + 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); itemTable[RG_LONGSHOT] = Item(RG_LONGSHOT, Text{ "Longshot", "Super-Grappin", "Enterhaken" }, ITEMTYPE_ITEM, GI_LONGSHOT, true, LOGIC_PROGRESSIVE_HOOKSHOT, RHT_LONGSHOT, ITEM_LONGSHOT, OBJECT_GI_HOOKSHOT, GID_LONGSHOT, 0x4F, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE); From a9a49ccbe6f963ffc84c59715e30107f08c868a2 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Thu, 19 Jun 2025 13:26:43 -0700 Subject: [PATCH 110/157] Adds saving and loading for location exclusion, and hooks the check tracker up to return false from IsVisibleInCheckTracker if a location was excluded. (#5594) --- .../Enhancements/randomizer/randomizer_check_tracker.cpp | 9 +++++---- soh/soh/SaveManager.cpp | 6 ++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 887bc373162..d0cad25446b 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -1588,10 +1588,11 @@ bool IsCheckShuffled(RandomizerCheck rc) { bool IsVisibleInCheckTracker(RandomizerCheck rc) { auto loc = Rando::StaticData::GetLocation(rc); if (IS_RANDO) { - return IsCheckShuffled(rc) || - (alwaysShowGS && loc->GetRCType() == RCTYPE_SKULL_TOKEN && - OTRGlobals::Instance->gRandoContext->IsQuestOfLocationActive(rc)) || - (loc->GetRCType() == RCTYPE_SHOP && showShops && !hideShopUnshuffledChecks); + return !Rando::Context::GetInstance()->GetItemLocation(rc)->IsExcluded() && + (IsCheckShuffled(rc) || + (alwaysShowGS && loc->GetRCType() == RCTYPE_SKULL_TOKEN && + OTRGlobals::Instance->gRandoContext->IsQuestOfLocationActive(rc)) || + (loc->GetRCType() == RCTYPE_SHOP && showShops && !hideShopUnshuffledChecks)); } else { return loc->IsVanillaCompletion() && (!loc->IsDungeon() || (loc->IsDungeon() && loc->GetQuest() == gSaveContext.ship.quest.id)); diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index e023f9afea9..43bd25b23e8 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -179,6 +179,9 @@ void SaveManager::LoadRandomizer() { // all ItemLocations is 0 anyway. randoContext->GetItemLocation(i)->SetCustomPrice(price); } + bool excluded = false; + SaveManager::Instance->LoadData("excluded", excluded, false); + randoContext->GetItemLocation(i)->SetExcludedOption(excluded); }); }); @@ -269,6 +272,9 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f // TODO: German (trick names don't have german translations yet) }); } + if (randoContext->GetItemLocation(i)->IsExcluded()) { + SaveManager::Instance->SaveData("excluded", true); + } if (randoContext->GetItemLocation(i)->HasCustomPrice()) { SaveManager::Instance->SaveData("price", randoContext->GetItemLocation(i)->GetPrice()); } From b56414838078d58d240b9e964f7c9ea95a2534fa Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:28:50 +0100 Subject: [PATCH 111/157] Add wallet to mask logic, clean up mask logic (#5567) --- .../location_access/overworld/graveyard.cpp | 7 ++++--- .../location_access/overworld/hyrule_field.cpp | 3 ++- .../randomizer/location_access/overworld/kakariko.cpp | 2 ++ .../location_access/overworld/lost_woods.cpp | 11 ++++++----- .../randomizer/location_access/overworld/market.cpp | 10 ++++++++-- soh/soh/Enhancements/randomizer/logic.cpp | 9 ++++++--- soh/soh/Enhancements/randomizer/logic.h | 9 ++++++--- 7 files changed, 34 insertions(+), 17 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp index 012503d9a21..d04606018db 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp @@ -7,9 +7,10 @@ void RegionTable_Init_Graveyard() { // clang-format off areaTable[RR_THE_GRAVEYARD] = Region("The Graveyard", "The Graveyard", {RA_THE_GRAVEYARD}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->ButterflyFairy, []{return logic->ButterflyFairy || (logic->CanUse(RG_STICKS) && logic->AtDay);}), - EventAccess(&logic->BeanPlantFairy, []{return logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS);}), - EventAccess(&logic->BugRock, []{return true;}), + EventAccess(&logic->ButterflyFairy, []{return logic->ButterflyFairy || (logic->CanUse(RG_STICKS) && logic->AtDay);}), + EventAccess(&logic->BeanPlantFairy, []{return logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS);}), + EventAccess(&logic->BugRock, []{return true;}), + EventAccess(&logic->BorrowBunnyHood, []{return logic->IsChild && logic->AtDay && logic->BorrowSpookyMask && logic->HasItem(RG_CHILD_WALLET);}), }, { //Locations LOCATION(RC_GRAVEYARD_FREESTANDING_POH, (((logic->IsAdult && CanPlantBean(RR_THE_GRAVEYARD)) || logic->CanUse(RG_LONGSHOT)) && logic->CanBreakCrates()) || (ctx->GetTrickOption(RT_GY_POH) && logic->CanUse(RG_BOOMERANG))), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/hyrule_field.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/hyrule_field.cpp index add3a2f8647..3359089e0db 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/hyrule_field.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/hyrule_field.cpp @@ -7,7 +7,8 @@ void RegionTable_Init_HyruleField() { // clang-format off areaTable[RR_HYRULE_FIELD] = Region("Hyrule Field", "Hyrule Field", {RA_HYRULE_FIELD}, DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->BigPoeKill, []{return logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_EPONA) && logic->HasBottle();}), + EventAccess(&logic->BigPoeKill, []{return logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_EPONA) && logic->HasBottle();}), + EventAccess(&logic->BorrowRightMasks, []{return logic->IsChild && logic->BorrowBunnyHood && logic->HasItem(RG_KOKIRI_EMERALD) && logic->HasItem(RG_GORON_RUBY) && logic->HasItem(RG_ZORA_SAPPHIRE) && logic->HasItem(RG_CHILD_WALLET);}), }, { //Locations LOCATION(RC_HF_OCARINA_OF_TIME_ITEM, logic->IsChild && logic->StoneCount() == 3 && logic->HasItem(RG_BRONZE_SCALE)), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp index 3e84bb72a1e..3247ef5f4c1 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp @@ -10,6 +10,8 @@ void RegionTable_Init_Kakariko() { EventAccess(&logic->BugRock, []{return true;}), //Open Gate setting is applied in RR_ROOT EventAccess(&logic->KakarikoVillageGateOpen, []{return logic->IsChild && logic->HasItem(RG_ZELDAS_LETTER);}), + //Needs wallet to be able to get another mask after selling Keaton + EventAccess(&logic->BorrowSkullMask, []{return logic->IsChild && logic->CanBorrowMasks && logic->HasItem(RG_CHILD_WALLET);}), }, { //Locations LOCATION(RC_SHEIK_IN_KAKARIKO, logic->IsAdult && logic->HasItem(RG_FOREST_MEDALLION) && logic->HasItem(RG_FIRE_MEDALLION) && logic->HasItem(RG_WATER_MEDALLION)), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp index 812b33a557a..a83664fd620 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp @@ -12,9 +12,10 @@ void RegionTable_Init_LostWoods() { areaTable[RR_THE_LOST_WOODS] = Region("Lost Woods", "Lost Woods", {RA_THE_LOST_WOODS}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->GossipStoneFairy, []{return logic->CallGossipFairyExceptSuns();}), - EventAccess(&logic->BeanPlantFairy, []{return logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS);}), - EventAccess(&logic->BugShrub, []{return logic->IsChild && logic->CanCutShrubs();}), + EventAccess(&logic->GossipStoneFairy, []{return logic->CallGossipFairyExceptSuns();}), + EventAccess(&logic->BeanPlantFairy, []{return logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS);}), + EventAccess(&logic->BugShrub, []{return logic->IsChild && logic->CanCutShrubs();}), + EventAccess(&logic->BorrowSpookyMask, []{return logic->IsChild && logic->BorrowSkullMask && logic->CanUse(RG_SARIAS_SONG) && logic->HasItem(RG_CHILD_WALLET);}), }, { //Locations LOCATION(RC_LW_SKULL_KID, logic->IsChild && logic->CanUse(RG_SARIAS_SONG)), @@ -110,8 +111,8 @@ void RegionTable_Init_LostWoods() { areaTable[RR_DEKU_THEATER] = Region("Deku Theater", "Deku Theater", {}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_DEKU_THEATER_SKULL_MASK, logic->IsChild && logic->SkullMask), - LOCATION(RC_DEKU_THEATER_MASK_OF_TRUTH, logic->IsChild && logic->MaskOfTruth), + LOCATION(RC_DEKU_THEATER_SKULL_MASK, logic->IsChild && logic->BorrowSkullMask), + LOCATION(RC_DEKU_THEATER_MASK_OF_TRUTH, logic->IsChild && logic->BorrowRightMasks), }, { //Exits Entrance(RR_LW_BEYOND_MIDO, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp index 0314bfde0c5..b1090d3614a 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp @@ -138,8 +138,14 @@ void RegionTable_Init_Market() { areaTable[RR_MARKET_MASK_SHOP] = Region("Market Mask Shop", "Market Mask Shop", {}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->SkullMask, []{return logic->SkullMask || (logic->HasItem(RG_ZELDAS_LETTER) && (ctx->GetOption(RSK_COMPLETE_MASK_QUEST) || ChildCanAccess(RR_KAKARIKO_VILLAGE)));}), //RANDOTODO Complete mask quest does not need this location, so should be tied to link's pocket - EventAccess(&logic->MaskOfTruth, []{return logic->MaskOfTruth || (logic->SkullMask && (ctx->GetOption(RSK_COMPLETE_MASK_QUEST) || (ChildCanAccess(RR_THE_LOST_WOODS) && logic->CanUse(RG_SARIAS_SONG) && RegionTable(RR_THE_GRAVEYARD)->childDay && ChildCanAccess(RR_HYRULE_FIELD) && logic->StoneCount() == 3)));}), + //Currently, mask swap in menu doesn't need access to the mask shop + //If it is forced on/a setting, a copy of these events should be added to root + //it also doesn't need you to open kak gate, but that might be best treated as a bug + EventAccess(&logic->CanBorrowMasks, []{return logic->HasItem(RG_ZELDAS_LETTER) && logic->KakarikoVillageGateOpen;}), + EventAccess(&logic->BorrowSkullMask, []{return ctx->GetOption(RSK_COMPLETE_MASK_QUEST) && logic->CanBorrowMasks;}), + EventAccess(&logic->BorrowSpookyMask, []{return ctx->GetOption(RSK_COMPLETE_MASK_QUEST) && logic->CanBorrowMasks;}), + EventAccess(&logic->BorrowBunnyHood, []{return ctx->GetOption(RSK_COMPLETE_MASK_QUEST) && logic->CanBorrowMasks;}), + EventAccess(&logic->BorrowRightMasks, []{return ctx->GetOption(RSK_COMPLETE_MASK_QUEST) && logic->CanBorrowMasks;}), }, { //Locations LOCATION(RC_MASK_SHOP_HINT, true), diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 597feb463a1..710437dc661 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2345,9 +2345,12 @@ void Logic::Reset(bool resetSaveContext /*= true*/) { // AmmoCanDrop = /*AmmoDrops.IsNot(AMMODROPS_NONE)*/ false; TODO: AmmoDrop setting - // Child item logic - SkullMask = false; - MaskOfTruth = false; + // Mask quest + CanBorrowMasks = false; + BorrowSkullMask = false; + BorrowSpookyMask = false; + BorrowBunnyHood = false; + BorrowRightMasks = false; // Adult logic FreedEpona = false; diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 0c138a3ebf0..05e44b402c0 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -30,9 +30,12 @@ class Logic { public: bool noVariable = false; - // Child item logic - bool SkullMask = false; - bool MaskOfTruth = false; + // Mask Quest + bool CanBorrowMasks = false; + bool BorrowSkullMask = false; + bool BorrowSpookyMask = false; + bool BorrowBunnyHood = false; + bool BorrowRightMasks = false; // Adult logic bool FreedEpona = false; From 17613d1f508cf6895df9785d42e089dc8ad1f606 Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Mon, 23 Jun 2025 17:52:17 -0500 Subject: [PATCH 112/157] Fix Available Checks On Load (#5599) * Setup call to RecalculateAvailableChecks instead of direct call. * Remove RecalculateAvailableChecks from CheckTrackerSettingsWindow::DrawElement. --- .../randomizer/randomizer_check_tracker.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index d0cad25446b..1f53e4caf84 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -587,7 +587,7 @@ void CheckTrackerLoadGame(int32_t fileNum) { Rando::Context::GetInstance()->GetEntranceShuffler()->ApplyEntranceOverrides(); } - RecalculateAvailableChecks(); + recalculateAvailable = true; } void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) { @@ -994,6 +994,11 @@ void CheckTrackerWindow::DrawElement() { return; } + if (recalculateAvailable) { + recalculateAvailable = false; + RecalculateAvailableChecks(); + } + SceneID sceneId = SCENE_ID_MAX; if (gPlayState != nullptr) { sceneId = (SceneID)gPlayState->sceneNum; @@ -1986,7 +1991,7 @@ void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName, } void RecalculateAvailableChecks(RandomizerRegion startingRegion /* = RR_ROOT */) { - if (!enableAvailableChecks || !GameInteractor::IsSaveLoaded(true)) { + if (!enableAvailableChecks || !GameInteractor::IsSaveLoaded()) { return; } @@ -2052,10 +2057,6 @@ static std::unordered_map buttonStrings = { }; void CheckTrackerSettingsWindow::DrawElement() { - if (recalculateAvailable) { - recalculateAvailable = false; - RecalculateAvailableChecks(); - } ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f }); if (ImGui::BeginTable("CheckTrackerSettingsTable", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { ImGui::TableSetupColumn("General settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); From afde504a0f0b4ef36f236437142fbe42362314dd Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Mon, 23 Jun 2025 18:01:49 -0500 Subject: [PATCH 113/157] Updated SpoilerFileExists to cache the results. (#5606) --- .../Enhancements/randomizer/randomizer.cpp | 62 ++++++++++++++++--- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index f0f57b35311..581861158e5 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -359,26 +359,72 @@ std::unordered_map getItemIdToItemId = { #pragma GCC push_options #pragma GCC optimize("O0") bool Randomizer::SpoilerFileExists(const char* spoilerFileName) { - if (strcmp(spoilerFileName, "") != 0) { - std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName)); + static std::unordered_map existsCache; + static std::unordered_map lastModifiedCache; + + if (strcmp(spoilerFileName, "") == 0) { + return false; + } + + std::string sanitizedFileName = SohUtils::Sanitize(spoilerFileName); + + try { + // Check if file exists and get last modified time + std::filesystem::path filePath(sanitizedFileName); + if (!std::filesystem::exists(filePath)) { + // Cache and return false if file doesn't exist + existsCache[sanitizedFileName] = false; + lastModifiedCache.erase(sanitizedFileName); + return false; + } + + auto currentLastModified = std::filesystem::last_write_time(filePath); + + // Check cache first + auto existsCacheIt = existsCache.find(sanitizedFileName); + auto lastModifiedCacheIt = lastModifiedCache.find(sanitizedFileName); + + // If we have a valid cache entry and the file hasn't been modified + if (existsCacheIt != existsCache.end() && lastModifiedCacheIt != lastModifiedCache.end() && + lastModifiedCacheIt->second == currentLastModified) { + return existsCacheIt->second; + } + + // Cache miss or file modified - need to check contents + std::ifstream spoilerFileStream(sanitizedFileName); if (spoilerFileStream) { nlohmann::json contents; spoilerFileStream >> contents; spoilerFileStream.close(); - if (contents.contains("version") && - strcmp(std::string(contents["version"]).c_str(), (char*)gBuildVersion) == 0) { - return true; - } else { + + bool isValid = contents.contains("version") && + strcmp(std::string(contents["version"]).c_str(), (char*)gBuildVersion) == 0; + + if (!isValid) { SohGui::RegisterPopup( "Old Spoiler Version", "The spoiler file located at\n" + std::string(spoilerFileName) + "\nwas made by a version that doesn't match the currently running version.\n" + "Loading for this file has been cancelled."); } + + // Update cache + existsCache[sanitizedFileName] = isValid; + lastModifiedCache[sanitizedFileName] = currentLastModified; + return isValid; } - } - return false; + // File couldn't be opened + existsCache[sanitizedFileName] = false; + lastModifiedCache.erase(sanitizedFileName); + return false; + + } catch (const std::filesystem::filesystem_error&) { + // Handle filesystem errors by invalidating cache + existsCache[sanitizedFileName] = false; + lastModifiedCache.erase(sanitizedFileName); + return false; + } } #pragma GCC pop_options #pragma optimize("", on) From 4676242086b5066abfa625677c5985632bc93101 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 23 Jun 2025 16:02:04 -0700 Subject: [PATCH 114/157] Fix dpad navigation on Quest Status pause screen. (#5607) --- .../ovl_kaleido_scope/z_kaleido_collect.c | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c index 17f1aa89eda..78a072c6fbc 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c @@ -124,7 +124,17 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { s16 pad2; s16 phi_s0_2; s16 sp208[3]; - bool dpad = CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0); + if (CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0)) { + if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) { + pauseCtx->stickRelX = -35; + } else if (CHECK_BTN_ALL(input->press.button, BTN_DRIGHT)) { + pauseCtx->stickRelX = 35; + } else if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { + pauseCtx->stickRelY = -35; + } else if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { + pauseCtx->stickRelY = 35; + } + } OPEN_DISPS(gfxCtx); @@ -140,7 +150,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { } else { phi_s3 = pauseCtx->cursorPoint[PAUSE_QUEST]; - if ((pauseCtx->stickRelX < -30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) { + if ((pauseCtx->stickRelX < -30)) { phi_s0 = D_8082A1AC[phi_s3][2]; if (phi_s0 == -3) { KaleidoScope_MoveCursorToSpecialPos(play, PAUSE_CURSOR_PAGE_LEFT); @@ -153,7 +163,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { phi_s0 = D_8082A1AC[phi_s0][2]; } } - } else if ((pauseCtx->stickRelX > 30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) { + } else if ((pauseCtx->stickRelX > 30)) { phi_s0 = D_8082A1AC[phi_s3][3]; if (phi_s0 == -2) { KaleidoScope_MoveCursorToSpecialPos(play, PAUSE_CURSOR_PAGE_RIGHT); @@ -168,7 +178,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { } } - if ((pauseCtx->stickRelY < -30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DDOWN))) { + if ((pauseCtx->stickRelY < -30)) { phi_s0 = D_8082A1AC[phi_s3][1]; while (phi_s0 >= 0) { if ((s16)KaleidoScope_UpdateQuestStatusPoint(pauseCtx, phi_s0) != 0) { @@ -176,7 +186,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { } phi_s0 = D_8082A1AC[phi_s0][1]; } - } else if ((pauseCtx->stickRelY > 30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DUP))) { + } else if ((pauseCtx->stickRelY > 30)) { phi_s0 = D_8082A1AC[phi_s3][0]; while (phi_s0 >= 0) { if ((s16)KaleidoScope_UpdateQuestStatusPoint(pauseCtx, phi_s0) != 0) { @@ -267,7 +277,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { } } } else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) { - if ((pauseCtx->stickRelX > 30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) { + if ((pauseCtx->stickRelX > 30)) { pauseCtx->cursorPoint[PAUSE_QUEST] = 0x15; pauseCtx->nameDisplayTimer = 0; pauseCtx->cursorSpecialPos = 0; @@ -285,7 +295,7 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { pauseCtx->cursorSlot[pauseCtx->pageIndex] = sp216; } } else { - if ((pauseCtx->stickRelX < -30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) { + if ((pauseCtx->stickRelX < -30)) { pauseCtx->cursorPoint[PAUSE_QUEST] = 0; pauseCtx->nameDisplayTimer = 0; pauseCtx->cursorSpecialPos = 0; From 3b82b8eeffb1694b98997fdb1adb825e32dba01a Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Wed, 25 Jun 2025 12:27:45 -0400 Subject: [PATCH 115/157] Ruto no longer targetable with sitting down skip (#5611) --- .../SkipMiscInteractions/SkipChildRutoInteractions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp b/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp index 97b65a54360..42862d4d99a 100644 --- a/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp +++ b/soh/soh/Enhancements/TimeSavers/SkipMiscInteractions/SkipChildRutoInteractions.cpp @@ -93,7 +93,7 @@ void SkipChildRutoInteractions_Register() { if (enRu1->action == 22) { enRu1->action = 27; enRu1->drawConfig = 1; - enRu1->actor.flags |= ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_FRIENDLY; + enRu1->actor.flags &= ~(ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_FRIENDLY); Animation_Change(&enRu1->skelAnime, (AnimationHeader*)&gRutoChildSittingAnim, 1.0f, 0.0f, Animation_GetLastFrame((void*)&gRutoChildSittingAnim), ANIMMODE_LOOP, 0.0f); } From 8b616c8709a307426017ec511c939259c78dd114 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Thu, 26 Jun 2025 00:54:57 -0400 Subject: [PATCH 116/157] first person to first-person (#5616) --- soh/soh/Enhancements/controls/SohInputEditorWindow.cpp | 4 ++-- soh/soh/SohGui/SohMenuEnhancements.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp index 8a8227073e2..f9fb48ac1ce 100644 --- a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp +++ b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp @@ -1366,10 +1366,10 @@ void SohInputEditorWindow::DrawCameraControlPanel() { .Color(THEME_COLOR) .Tooltip("Allows for aiming with the right stick in:\n-First-Person/C-Up view\n-Weapon Aiming")); if (CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) { - CVarCheckbox("Allow moving while in first person mode", CVAR_SETTING("MoveInFirstPerson"), + CVarCheckbox("Allow moving while in first-person mode", CVAR_SETTING("MoveInFirstPerson"), CheckboxOptions() .Color(THEME_COLOR) - .Tooltip("Changes the left stick to move the player while in first person mode")); + .Tooltip("Changes the left stick to move the player while in first-person mode")); } CVarCheckbox("Invert Aiming X Axis", CVAR_SETTING("Controls.InvertAimingXAxis"), CheckboxOptions() diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index f6f627f78c7..8e1e94c08dc 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -507,7 +507,7 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().Tooltip( "Scales all of the Adult Equipment, as well as moving some a bit, to fit on Child Link better. May " "not work properly with some mods.")); - AddWidget(path, "Show Gauntlets in First Person", WIDGET_CVAR_CHECKBOX) + AddWidget(path, "Show Gauntlets in First-Person", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FirstPersonGauntlets")) .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Renders Gauntlets when using the Bow and Hookshot like in OoT3D.")); From 1161ce3546aafadec2bf0f0eff46e912b6675867 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Sat, 28 Jun 2025 07:50:06 -0700 Subject: [PATCH 117/157] Expand tooltip for Free Look to explain camera locks and scene reload. (#5624) --- soh/soh/Enhancements/controls/SohInputEditorWindow.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp index f9fb48ac1ce..41a31f8bed9 100644 --- a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp +++ b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp @@ -1432,7 +1432,9 @@ void SohInputEditorWindow::DrawCameraControlPanel() { CheckboxOptions() .Color(THEME_COLOR) .Tooltip("Enables free look camera control\nNote: You must remap C buttons off of the right stick in the " - "controller config menu, and map the camera stick to the right stick.")); + "controller config menu, and map the camera stick to the right stick.\n" + "Doesn't work in areas were the game locks the camera.\n" + "Scene reload may be necessary to enable.")); CVarCheckbox("Invert Camera X Axis", CVAR_SETTING("FreeLook.InvertXAxis"), CheckboxOptions().Color(THEME_COLOR).Tooltip("Inverts the Camera X Axis in:\n-Free look")); CVarCheckbox( From a9b857469ef1fae2fcec6ee706e5a53a33470c53 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Sat, 28 Jun 2025 17:48:21 -0700 Subject: [PATCH 118/157] Fix Boss Rush scene/cutscene setup after Bongo/Twinrova. (#5623) Also prevent check tracker crash loading Boss Rush. --- .../Enhancements/randomizer/randomizer_check_tracker.cpp | 3 +++ soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 1f53e4caf84..8ff302bfe3e 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -495,6 +495,9 @@ void SetShopSeen(uint32_t sceneNum, bool prices) { } void CheckTrackerLoadGame(int32_t fileNum) { + if (!IS_RANDO) { + return; + } LoadSettings(); TrySetAreas(); for (auto& entry : Rando::StaticData::GetLocationTable()) { diff --git a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c index a9076471761..54e9768b4a2 100644 --- a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c +++ b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c @@ -813,7 +813,8 @@ void DoorWarp1_AdultWarpOut(DoorWarp1* this, PlayState* play) { gSaveContext.nextCutsceneIndex = 0; } } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE_BOSS) { - if (GameInteractor_Should(VB_PLAY_BLUE_WARP_CS, !CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT), + if (GameInteractor_Should(VB_PLAY_BLUE_WARP_CS, + !Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE), RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE)) { Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE); if (GameInteractor_Should(VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_MEDALLION_SPIRIT)) { @@ -831,7 +832,8 @@ void DoorWarp1_AdultWarpOut(DoorWarp1* this, PlayState* play) { gSaveContext.nextCutsceneIndex = 0; } } else if (play->sceneNum == SCENE_SHADOW_TEMPLE_BOSS) { - if (GameInteractor_Should(VB_PLAY_BLUE_WARP_CS, !CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW), + if (GameInteractor_Should(VB_PLAY_BLUE_WARP_CS, + !Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE), RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE)) { Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE); if (GameInteractor_Should(VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_MEDALLION_SHADOW)) { From 32683e2a748da238250de1f192d2fe36eee976d3 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Sun, 29 Jun 2025 19:12:04 -0700 Subject: [PATCH 119/157] Change early return in CheckTrackerLoadGame to check specifically for Boss Rush instead of Not Rando. (#5634) --- soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 8ff302bfe3e..d1ec624efcb 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -495,7 +495,7 @@ void SetShopSeen(uint32_t sceneNum, bool prices) { } void CheckTrackerLoadGame(int32_t fileNum) { - if (!IS_RANDO) { + if (IS_BOSS_RUSH) { return; } LoadSettings(); From dfa10383e400d06f1100082c1ca786a743f6ac79 Mon Sep 17 00:00:00 2001 From: OtherBlue <93625085+OtherBlue@users.noreply.github.com> Date: Thu, 3 Jul 2025 01:35:49 -0300 Subject: [PATCH 120/157] Split Skip Pickup Messages (#5648) * Split Skip Pickup Messages following ZFG's comment https://youtube.com/clip/UgkxF-LDaR-zyTkqSkqtP3dkLrCca_KGScIw?si=RJY9SIG8QKSiWgVl splits the Skip Pickup Messages time saver for consumables and bottle pickups * CVar updates I missed * another cvar I missed --- soh/soh/SohGui/SohMenuEnhancements.cpp | 7 +++++-- soh/src/overlays/actors/ovl_player_actor/z_player.c | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 8e1e94c08dc..c08e9c7baf2 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -353,9 +353,12 @@ void SohMenu::AddMenuEnhancements() { "Door Switch CS, Water Temple Dragon Switch CS, and the Box Skip One Point in Jabu.")); AddWidget(path, "Text", WIDGET_SEPARATOR_TEXT); - AddWidget(path, "Skip Pickup Messages", WIDGET_CVAR_CHECKBOX) + AddWidget(path, "Skip Bottle Pickup Messages", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("FastBottles")) + .Options(CheckboxOptions().Tooltip("Skip Pickup Messages for Bottle Swipes.")); + AddWidget(path, "Skip Consumable Item Pickup Messages", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FastDrops")) - .Options(CheckboxOptions().Tooltip("Skip Pickup Messages for new Consumable Items and Bottle Swipes.")); + .Options(CheckboxOptions().Tooltip("Skip Pickup Messages for new Consumable Items.")); AddWidget(path, "Skip Forced Dialog", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipForcedDialog")) .Options(ComboboxOptions() diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index b2ebcbb88ca..777de3e5965 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -14697,7 +14697,7 @@ void Player_Action_SwingBottle(Player* this, PlayState* play) { if (LinkAnimation_Update(play, &this->skelAnime)) { if (this->av1.bottleCatchType != BOTTLE_CATCH_NONE) { if (!this->av2.startedTextbox) { - if (CVarGetInteger(CVAR_ENHANCEMENT("FastDrops"), 0)) { + if (CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0)) { this->av1.bottleCatchType = BOTTLE_CATCH_NONE; } else { // 1 is subtracted because `sBottleCatchInfo` does not have an entry for `BOTTLE_CATCH_NONE` @@ -14740,13 +14740,13 @@ void Player_Action_SwingBottle(Player* this, PlayState* play) { this->av1.bottleCatchType = i + 1; this->av2.startedTextbox = false; - if (!CVarGetInteger(CVAR_ENHANCEMENT("FastDrops"), 0)) { + if (!CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0)) { this->stateFlags1 |= PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE; } this->interactRangeActor->parent = &this->actor; Player_UpdateBottleHeld(play, this, catchInfo->itemId, ABS(catchInfo->itemAction)); - if (!CVarGetInteger(CVAR_ENHANCEMENT("FastDrops"), 0)) { + if (!CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0)) { Player_AnimPlayOnceAdjusted(play, this, swingEntry->catchAnimation); func_80835EA4(play, 4); } From a93b484cf38d02f49932776cefda5292960c6019 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 21 Jul 2025 10:44:53 -0700 Subject: [PATCH 121/157] Add Clear Devices button that will erase the controllers block. (#5683) --- soh/soh/SohGui/SohGui.cpp | 6 ++---- soh/soh/SohGui/SohMenuSettings.cpp | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/soh/soh/SohGui/SohGui.cpp b/soh/soh/SohGui/SohGui.cpp index 7eb3fc6c711..67fb99f0a6a 100644 --- a/soh/soh/SohGui/SohGui.cpp +++ b/soh/soh/SohGui/SohGui.cpp @@ -71,7 +71,6 @@ std::shared_ptr mSohMenuBar; std::shared_ptr mConsoleWindow; std::shared_ptr mStatsWindow; std::shared_ptr mGfxDebuggerWindow; -std::shared_ptr mInputEditorWindow; std::shared_ptr mSohMenu; std::shared_ptr mAudioEditorWindow; @@ -131,10 +130,10 @@ void SetupGuiElements() { mStatsWindow = std::make_shared(CVAR_WINDOW("SohStats"), "Stats##Soh", ImVec2(400, 100)); gui->AddGuiWindow(mStatsWindow); - mInputEditorWindow = gui->GetGuiWindow("Controller Configuration"); + /*mInputEditorWindow = gui->GetGuiWindow("Controller Configuration"); if (mInputEditorWindow == nullptr) { SPDLOG_ERROR("Could not find input editor window"); - } + }*/ mAudioEditorWindow = std::make_shared(CVAR_WINDOW("AudioEditor"), "Audio Editor", ImVec2(820, 630)); gui->AddGuiWindow(mAudioEditorWindow); @@ -228,7 +227,6 @@ void Destroy() { mActorViewerWindow = nullptr; mCosmeticsEditorWindow = nullptr; mAudioEditorWindow = nullptr; - mInputEditorWindow = nullptr; mStatsWindow = nullptr; mConsoleWindow = nullptr; mGfxDebuggerWindow = nullptr; diff --git a/soh/soh/SohGui/SohMenuSettings.cpp b/soh/soh/SohGui/SohMenuSettings.cpp index 909c647bd64..6fb9e466c4d 100644 --- a/soh/soh/SohGui/SohMenuSettings.cpp +++ b/soh/soh/SohGui/SohMenuSettings.cpp @@ -1,5 +1,7 @@ #include "SohMenu.h" #include "soh/Notification/Notification.h" +#include "soh/Enhancements/controls/SohInputEditorWindow.h" +#include "SohModals.h" #include #include "soh/ResourceManagerHelpers.h" #include "UIWidgets.hpp" @@ -13,6 +15,7 @@ extern "C" { namespace SohGui { extern std::shared_ptr mSohMenu; +extern std::shared_ptr mModalWindow; using namespace UIWidgets; static std::unordered_map imguiScaleOptions = { @@ -373,6 +376,20 @@ void SohMenu::AddMenuSettings() { path.sidebarName = "Controls"; path.column = SECTION_COLUMN_1; AddSidebarEntry("Settings", "Controls", 2); + AddWidget(path, "Clear Devices", WIDGET_BUTTON) + .Callback([](WidgetInfo& info) { + SohGui::mModalWindow->RegisterPopup( + "Clear Config", + "This will completely erase the controls config, including registered devices.\nContinue?", "Clear", + "Cancel", + []() { + Ship::Context::GetInstance()->GetConsoleVariables()->ClearBlock(CVAR_PREFIX_SETTING ".Controllers"); + uint8_t bits = 0; + Ship::Context::GetInstance()->GetControlDeck()->Init(&bits); + }, + nullptr); + }) + .Options(ButtonOptions().Size(Sizes::Inline)); AddWidget(path, "Controller Bindings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Popout Bindings Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("ControllerConfiguration")) From 681e8dda2636f47b6bf6b55d03b3cd723948d7e7 Mon Sep 17 00:00:00 2001 From: Shishu the Dragon <183069616+ShishuTheDragon@users.noreply.github.com> Date: Tue, 22 Jul 2025 05:47:03 +1200 Subject: [PATCH 122/157] Ivan: collect deku seeds (#5654) --- soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c b/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c index ab8ca1a6f7d..ab379267eee 100644 --- a/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c +++ b/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c @@ -654,7 +654,7 @@ void EnPartner_Update(Actor* thisx, PlayState* play) { itemActor->params == ITEM00_ARROWS_MEDIUM || itemActor->params == ITEM00_ARROWS_LARGE || itemActor->params == ITEM00_BOMBCHU || itemActor->params == ITEM00_MAGIC_SMALL || itemActor->params == ITEM00_MAGIC_LARGE || itemActor->params == ITEM00_NUTS || - itemActor->params == ITEM00_STICK) { + itemActor->params == ITEM00_STICK || itemActor->params == ITEM00_SEEDS) { f32 distanceToObject = Actor_WorldDistXYZToActor(&this->actor, itemActor); if (distanceToObject <= 20.0f) { itemActor->world.pos = GET_PLAYER(play)->actor.world.pos; From 37fb25d2b337cc65e0cc0174f1daf40065efdf7a Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Mon, 21 Jul 2025 19:00:39 +0100 Subject: [PATCH 123/157] Fix logic for items given at run start due to settings (#5665) * initial changes * submodules * fix master sword, skip zelda and skip epona logic * I for one welcome our new clang overlords... --- soh/soh/Enhancements/randomizer/3drando/hints.cpp | 3 +++ soh/soh/Enhancements/randomizer/location_access.cpp | 3 +++ .../randomizer/location_access/overworld/lon_lon_ranch.cpp | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/hints.cpp b/soh/soh/Enhancements/randomizer/3drando/hints.cpp index c38d57d8b62..bf71ef023cb 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hints.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hints.cpp @@ -648,6 +648,9 @@ void CreateStoneHints() { if (ctx->GetOption(RSK_SKIP_CHILD_ZELDA)) { ctx->GetItemLocation(RC_SONG_FROM_IMPA)->SetHintAccesible(); } + if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_ADULT)) { + ctx->GetItemLocation(RC_TOT_MASTER_SWORD)->SetHintAccesible(); + } // Add 'always' location hints std::vector alwaysHintLocations = {}; diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index cdd684f0423..26ec51f271e 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -382,11 +382,14 @@ void RegionTable_Init() { //The big poes bottle softlock safety check does not account for the guard house lock if the guard house is not shuffled, so the key is needed before we can safely allow bottle use in logic //RANDOTODO a setting that lets you drink/dump big poes so we don't need this logic EventAccess(&logic->CouldEmptyBigPoes, []{return !ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF) || logic->CanOpenOverworldDoor(RG_GUARD_HOUSE_KEY);}), + EventAccess(&logic->FreedEpona, []{return (bool)ctx->GetOption(RSK_SKIP_EPONA_RACE);}), }, { //Locations LOCATION(RC_LINKS_POCKET, true), LOCATION(RC_TRIFORCE_COMPLETED, logic->GetSaveContext()->ship.quest.data.randomizer.triforcePiecesCollected >= ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_REQUIRED).Get() + 1;), LOCATION(RC_SARIA_SONG_HINT, logic->CanUse(RG_SARIAS_SONG)), + LOCATION(RC_SONG_FROM_IMPA, (bool)ctx->GetOption(RSK_SKIP_CHILD_ZELDA)), + LOCATION(RC_TOT_MASTER_SWORD, (bool)ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_ADULT)), }, { //Exits Entrance(RR_ROOT_EXITS, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp index fae41664e8e..6fe85afc7bd 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp @@ -7,8 +7,8 @@ void RegionTable_Init_LonLonRanch() { // clang-format off areaTable[RR_LON_LON_RANCH] = Region("Lon Lon Ranch", "Lon Lon Ranch", {RA_LON_LON_RANCH}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->FreedEpona, []{return logic->FreedEpona || ((logic->HasItem(RG_CHILD_WALLET) || ctx->GetOption(RSK_SKIP_EPONA_RACE)) && logic->CanUse(RG_EPONAS_SONG) && logic->IsAdult && logic->AtDay);}), - EventAccess(&logic->LinksCow, []{return logic->LinksCow || (logic->HasItem(RG_CHILD_WALLET) && logic->CanUse(RG_EPONAS_SONG) && logic->IsAdult && logic->AtDay);}), + EventAccess(&logic->FreedEpona, []{return logic->HasItem(RG_CHILD_WALLET) && logic->CanUse(RG_EPONAS_SONG) && logic->IsAdult && logic->AtDay;}), + EventAccess(&logic->LinksCow, []{return logic->HasItem(RG_CHILD_WALLET) && logic->CanUse(RG_EPONA) && logic->IsAdult && logic->AtDay;}), }, { //Locations LOCATION(RC_SONG_FROM_MALON, logic->IsChild && logic->HasItem(RG_ZELDAS_LETTER) && logic->HasItem(RG_FAIRY_OCARINA) && logic->AtDay), From 47c5a7f30841045c833bbf4712d8c85ffd6f0000 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 21 Jul 2025 14:31:48 -0700 Subject: [PATCH 124/157] Bump version to Blair Delta 9.0.3, and fix a typo. (#5687) --- CMakeLists.txt | 2 +- soh/soh/SohGui/SohMenuEnhancements.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9a5afe4c0a..39cad6d8f52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") -project(Ship VERSION 9.0.2 LANGUAGES C CXX) +project(Ship VERSION 9.0.3 LANGUAGES C CXX) include(CMake/soh-cvars.cmake) include(CMake/lus-cvars.cmake) diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index c08e9c7baf2..41cbbd90430 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -272,7 +272,7 @@ void SohMenu::AddMenuEnhancements() { info.options->disabled = IS_RANDO && OTRGlobals::Instance->gRandoContext->GetOption(RSK_JABU_OPEN).Is(RO_JABU_OPEN); info.options->disabledTooltip = - "This setting is disabled because a randomizer savefile with \"Jabu-Jaby: Open\" is loaded."; + "This setting is disabled because a randomizer savefile with \"Jabu-Jabu: Open\" is loaded."; }) .Options(CheckboxOptions().Tooltip("Allow Link to enter Jabu-Jabu without feeding him a fish.")); From 5066fbf82c541ce3bf861a52e9892f5a3719f59e Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 21 Jul 2025 17:57:40 -0700 Subject: [PATCH 125/157] Fix old file deletion. (#5688) --- soh/soh/SaveManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 43bd25b23e8..b62dbfee6ac 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -1163,7 +1163,6 @@ void SaveManager::LoadFile(int fileNum) { " " + newFileName + "\n" + "If this was not in error, the file should be deleted."); saveMtx.unlock(); - SaveFile(fileNum); return; } } From 35ad68578e6df5c51acd77546691c7ee98d14ac5 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 21 Jul 2025 22:19:04 -0700 Subject: [PATCH 126/157] Prevent empty randomizer blocks from triggering the rando version flow. (#5689) Clear SpoilerLog CVar when unsupported spoiler log is discovered on load. --- soh/soh/Enhancements/randomizer/randomizer.cpp | 2 ++ soh/soh/SaveManager.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 581861158e5..ac1716fc3a0 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -406,6 +406,8 @@ bool Randomizer::SpoilerFileExists(const char* spoilerFileName) { "The spoiler file located at\n" + std::string(spoilerFileName) + "\nwas made by a version that doesn't match the currently running version.\n" + "Loading for this file has been cancelled."); + CVarClear(CVAR_GENERAL("SpoilerLog")); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); } // Update cache diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index b62dbfee6ac..2e2f05f725f 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -1111,6 +1111,7 @@ void SaveManager::LoadFile(int fileNum) { std::ifstream input(fileName); try { + bool deleteRando = false; saveBlock = nlohmann::json::object(); input >> saveBlock; if (!saveBlock.contains("version")) { @@ -1122,6 +1123,10 @@ void SaveManager::LoadFile(int fileNum) { for (auto& block : saveBlock["sections"].items()) { std::string sectionName = block.key(); if (sectionName == "randomizer") { + if (block.value()["data"].empty()) { + deleteRando = true; + continue; + } bool hasStats = saveBlock["sections"].contains("sohStats"); if (block.value()["data"].contains("aat0") || !hasStats) { // Rachael rando data SohGui::RegisterPopup( @@ -1201,6 +1206,11 @@ void SaveManager::LoadFile(int fileNum) { assert(false); break; } + if (deleteRando) { + saveBlock["sections"].erase(saveBlock["sections"].find("randomizer")); + SaveFile(fileNum); + deleteRando = false; + } InitMeta(fileNum); GameInteractor::Instance->ExecuteHooks(fileNum); } catch (const std::exception& e) { From c588d48672b56b329f07aa181af3554ab38914cb Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 21 Jul 2025 22:21:59 -0700 Subject: [PATCH 127/157] Bump version to Blair Echo. (#5690) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 39cad6d8f52..d7db400c5d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") -project(Ship VERSION 9.0.3 LANGUAGES C CXX) +project(Ship VERSION 9.0.4 LANGUAGES C CXX) include(CMake/soh-cvars.cmake) include(CMake/lus-cvars.cmake) From d51e88b9722f99096033a90080039d7d44e68e91 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Wed, 23 Jul 2025 20:45:22 -0700 Subject: [PATCH 128/157] Rando Versioning (Again) (#5691) * Adds Sulu/Spock rando block check (data not empty, but all sub-entries null), and put that and data being empty to the old file flow. Also moves the `SaveFile` call to after everything else is loaded to preserve sohStats block. * Add check for blank buildVersion in sohStats block for determining old saves. * clang --- soh/soh/SaveManager.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 2e2f05f725f..bdb2800e380 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -1121,25 +1121,24 @@ void SaveManager::LoadFile(int fileNum) { switch (saveBlock["version"].get()) { case 1: for (auto& block : saveBlock["sections"].items()) { + bool oldVanilla = + block.value()["data"].empty() || block.value()["data"].contains("aat0") || + block.value()["data"]["entrances"].empty() || + SohUtils::IsStringEmpty(saveBlock["sections"]["sohStats"]["data"]["buildVersion"]); std::string sectionName = block.key(); if (sectionName == "randomizer") { - if (block.value()["data"].empty()) { - deleteRando = true; - continue; - } bool hasStats = saveBlock["sections"].contains("sohStats"); - if (block.value()["data"].contains("aat0") || !hasStats) { // Rachael rando data + if (oldVanilla || !hasStats) { // Vanilla "rando" data SohGui::RegisterPopup( "Loading old file", "The file in slot " + std::to_string(fileNum + 1) + - " appears to contain randomizer data, but is a very old format.\n" + + " appears to contain randomizer data, but is a very old format or is empty.\n" + "The randomizer data has been removed, and this file will be treated as a vanilla " - "file.\n" + + "file.\nIf this was a vanilla file, it still is, and you shouldn't see this " + "message again.\n" + "If this was a randomizer file, the file will not work, and should be deleted."); - input.close(); - saveMtx.unlock(); - SaveFile(fileNum); - return; + deleteRando = true; + continue; } s16 major = saveBlock["sections"]["sohStats"]["data"]["buildVersionMajor"]; s16 minor = saveBlock["sections"]["sohStats"]["data"]["buildVersionMinor"]; @@ -1206,6 +1205,7 @@ void SaveManager::LoadFile(int fileNum) { assert(false); break; } + input.close(); if (deleteRando) { saveBlock["sections"].erase(saveBlock["sections"].find("randomizer")); SaveFile(fileNum); From d06cf6bf1032037d67b46bad37b8320bec9643d6 Mon Sep 17 00:00:00 2001 From: AltoXorg <56553686+Alto1772@users.noreply.github.com> Date: Thu, 24 Jul 2025 11:45:46 +0800 Subject: [PATCH 129/157] `timesplitdata.json` obey app directory (#5693) --- soh/soh/Enhancements/timesplits/TimeSplits.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/timesplits/TimeSplits.cpp b/soh/soh/Enhancements/timesplits/TimeSplits.cpp index f08235970e0..d590bbd0742 100644 --- a/soh/soh/Enhancements/timesplits/TimeSplits.cpp +++ b/soh/soh/Enhancements/timesplits/TimeSplits.cpp @@ -4,6 +4,7 @@ #include "soh/util.h" #include #include "include/z64item.h" +#include "Context.h" #include #include @@ -363,7 +364,7 @@ void TimeSplitsSkipSplit(uint32_t index) { } void TimeSplitsFileManagement(uint32_t action, const char* listEntry, std::vector listData) { - std::string filename = "timesplitdata.json"; + std::string filename = Ship::Context::GetPathRelativeToAppDirectory("timesplitdata.json"); json saveFile; json listArray = nlohmann::json::array(); @@ -948,9 +949,10 @@ void TimeSplitsDrawManageList() { } void InitializeSplitDataFile() { - if (!std::filesystem::exists("timesplitdata.json")) { + std::string filename = Ship::Context::GetPathRelativeToAppDirectory("timesplitdata.json"); + if (!std::filesystem::exists(filename)) { json j; - std::ofstream file("timesplitdata.json"); + std::ofstream file(filename); file << j.dump(4); file.close(); } From ccf3d4b6a0e6a4e2276da6336fea86b30c32428a Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Wed, 23 Jul 2025 23:48:01 -0400 Subject: [PATCH 130/157] Add playing icon to improve Audio Editor indicator (#5686) --- soh/soh/Enhancements/audio/AudioEditor.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp index 4cf1f389d02..6660bee7da3 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.cpp +++ b/soh/soh/Enhancements/audio/AudioEditor.cpp @@ -298,9 +298,12 @@ void Draw_SfxTab(const std::string& tabId, SeqType type, const std::string& tabN ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::TextColored( - UIWidgets::ColorValues.at(isCurrentlyPlaying ? UIWidgets::Colors::Yellow : UIWidgets::Colors::White), "%s", - seqData.label.c_str()); + if (isCurrentlyPlaying) { + ImGui::TextColored(UIWidgets::ColorValues.at(UIWidgets::Colors::Yellow), "%s %s", ICON_FA_PLAY, + seqData.label.c_str()); + } else { + ImGui::Text("%s", seqData.label.c_str()); + } ImGui::TableNextColumn(); ImGui::PushItemWidth(-FLT_MIN); const int initialValue = map.contains(currentValue) ? currentValue : defaultValue; From 7d7072f717c853678c4aed7cce39aa1eba592994 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Wed, 23 Jul 2025 21:31:13 -0700 Subject: [PATCH 131/157] Bump version to 9.0.5 (#5694) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7db400c5d9..89f32f74e4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") -project(Ship VERSION 9.0.4 LANGUAGES C CXX) +project(Ship VERSION 9.0.5 LANGUAGES C CXX) include(CMake/soh-cvars.cmake) include(CMake/lus-cvars.cmake) From be77a9be713b669a715e1006e78484e469258e4c Mon Sep 17 00:00:00 2001 From: Malkierian Date: Tue, 29 Jul 2025 18:47:43 -0700 Subject: [PATCH 132/157] Fix Dpad Navigation and Dpad equips on Inventory (#5708) * Encapsulates ship-specific stuff inside the first state checks in menu draw functions to prevent function bleed. * clang --- .../ovl_kaleido_scope/z_kaleido_collect.c | 113 +++++++++--------- .../ovl_kaleido_scope/z_kaleido_equipment.c | 9 +- .../misc/ovl_kaleido_scope/z_kaleido_item.c | 11 +- .../ovl_kaleido_scope/z_kaleido_map_PAL.c | 2 +- 4 files changed, 69 insertions(+), 66 deletions(-) diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c index 78a072c6fbc..ade62acc3e7 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c @@ -8,51 +8,6 @@ extern const char* digitTextures[]; void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { - Color_RGB8 aButtonColor = { 80, 150, 255 }; - if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.Changed"), 0)) { - aButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.AButton.Value"), aButtonColor); - } else if (CVarGetInteger(CVAR_COSMETIC("DefaultColorScheme"), COLORSCHEME_N64) == COLORSCHEME_GAMECUBE) { - aButtonColor = (Color_RGB8){ 80, 255, 150 }; - } - if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D4, true)) { - aButtonColor = (Color_RGB8){ 191, 191, 191 }; - } - - Color_RGB8 cButtonsColor = { 255, 255, 50 }; - if (CVarGetInteger(CVAR_COSMETIC("HUD.CButtons.Changed"), 0)) { - cButtonsColor = CVarGetColor24(CVAR_COSMETIC("HUD.CButtons.Value"), cButtonsColor); - } - Color_RGB8 cUpButtonColor = cButtonsColor; - if (CVarGetInteger(CVAR_COSMETIC("HUD.CUpButton.Changed"), 0)) { - cUpButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CUpButton.Value"), cUpButtonColor); - } - if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D5, true)) { - cUpButtonColor = (Color_RGB8){ 191, 191, 191 }; - } - - Color_RGB8 cDownButtonColor = cButtonsColor; - if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.Changed"), 0)) { - cDownButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CDownButton.Value"), cDownButtonColor); - } - if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_F4, true)) { - cDownButtonColor = (Color_RGB8){ 191, 191, 191 }; - } - - Color_RGB8 cLeftButtonColor = cButtonsColor; - if (CVarGetInteger(CVAR_COSMETIC("HUD.CLeftButton.Changed"), 0)) { - cLeftButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CLeftButton.Value"), cLeftButtonColor); - } - if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_B4, true)) { - cLeftButtonColor = (Color_RGB8){ 191, 191, 191 }; - } - - Color_RGB8 cRightButtonColor = cButtonsColor; - if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.Changed"), 0)) { - cRightButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CRightButton.Value"), cRightButtonColor); - } - if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_A4, true)) { - cRightButtonColor = (Color_RGB8){ 191, 191, 191 }; - } static s16 D_8082A070[][4] = { { 255, 0, 0, 255 }, @@ -124,22 +79,22 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { s16 pad2; s16 phi_s0_2; s16 sp208[3]; - if (CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0)) { - if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) { - pauseCtx->stickRelX = -35; - } else if (CHECK_BTN_ALL(input->press.button, BTN_DRIGHT)) { - pauseCtx->stickRelX = 35; - } else if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { - pauseCtx->stickRelY = -35; - } else if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { - pauseCtx->stickRelY = 35; - } - } OPEN_DISPS(gfxCtx); if (((pauseCtx->unk_1E4 == 0) || (pauseCtx->unk_1E4 == 5) || (pauseCtx->unk_1E4 == 8)) && (pauseCtx->pageIndex == PAUSE_QUEST)) { + if (CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0)) { + if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) { + pauseCtx->stickRelX = -35; + } else if (CHECK_BTN_ALL(input->press.button, BTN_DRIGHT)) { + pauseCtx->stickRelX = 35; + } else if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { + pauseCtx->stickRelY = -35; + } else if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { + pauseCtx->stickRelY = 35; + } + } pauseCtx->cursorColorSet = 0; if (pauseCtx->cursorSpecialPos == 0) { @@ -516,6 +471,52 @@ void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx) { } if (pauseCtx->state == 6) { + Color_RGB8 aButtonColor = { 80, 150, 255 }; + if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.Changed"), 0)) { + aButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.AButton.Value"), aButtonColor); + } else if (CVarGetInteger(CVAR_COSMETIC("DefaultColorScheme"), COLORSCHEME_N64) == COLORSCHEME_GAMECUBE) { + aButtonColor = (Color_RGB8){ 80, 255, 150 }; + } + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D4, true)) { + aButtonColor = (Color_RGB8){ 191, 191, 191 }; + } + + Color_RGB8 cButtonsColor = { 255, 255, 50 }; + if (CVarGetInteger(CVAR_COSMETIC("HUD.CButtons.Changed"), 0)) { + cButtonsColor = CVarGetColor24(CVAR_COSMETIC("HUD.CButtons.Value"), cButtonsColor); + } + Color_RGB8 cUpButtonColor = cButtonsColor; + if (CVarGetInteger(CVAR_COSMETIC("HUD.CUpButton.Changed"), 0)) { + cUpButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CUpButton.Value"), cUpButtonColor); + } + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_D5, true)) { + cUpButtonColor = (Color_RGB8){ 191, 191, 191 }; + } + + Color_RGB8 cDownButtonColor = cButtonsColor; + if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.Changed"), 0)) { + cDownButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CDownButton.Value"), cDownButtonColor); + } + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_F4, true)) { + cDownButtonColor = (Color_RGB8){ 191, 191, 191 }; + } + + Color_RGB8 cLeftButtonColor = cButtonsColor; + if (CVarGetInteger(CVAR_COSMETIC("HUD.CLeftButton.Changed"), 0)) { + cLeftButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CLeftButton.Value"), cLeftButtonColor); + } + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_B4, true)) { + cLeftButtonColor = (Color_RGB8){ 191, 191, 191 }; + } + + Color_RGB8 cRightButtonColor = cButtonsColor; + if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.Changed"), 0)) { + cRightButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CRightButton.Value"), cRightButtonColor); + } + if (!GameInteractor_Should(VB_HAVE_OCARINA_NOTE_A4, true)) { + cRightButtonColor = (Color_RGB8){ 191, 191, 191 }; + } + gDPPipeSync(POLY_OPA_DISP++); gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c index aa18912eab1..cec05af725c 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c @@ -183,10 +183,6 @@ void KaleidoScope_DrawEquipment(PlayState* play) { s16 cursorX; s16 cursorY; s16 oldCursorPoint; - bool dpad = (CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0) && !CHECK_BTN_ALL(input->cur.button, BTN_CUP)); - bool pauseAnyCursor = - (CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_RANDO_ONLY && IS_RANDO) || - (CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_ALWAYS_ON); OPEN_DISPS(play->state.gfxCtx); @@ -204,6 +200,11 @@ void KaleidoScope_DrawEquipment(PlayState* play) { } if ((pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0) && (pauseCtx->pageIndex == PAUSE_EQUIP)) { + bool dpad = (CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0) && !CHECK_BTN_ALL(input->cur.button, BTN_CUP)); + bool pauseAnyCursor = + (CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_RANDO_ONLY && IS_RANDO) || + (CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_ALWAYS_ON); + oldCursorPoint = pauseCtx->cursorPoint[PAUSE_EQUIP]; pauseCtx->cursorColorSet = 0; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c index beef301c411..7f0cfcc7a1a 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c @@ -421,11 +421,6 @@ void KaleidoScope_DrawItemSelect(PlayState* play) { s16 cursorY; s16 oldCursorPoint; s16 moveCursorResult; - bool dpad = (CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0) && !CHECK_BTN_ALL(input->cur.button, BTN_CUP)); - bool pauseAnyCursor = - pauseCtx->cursorSpecialPos == 0 && - ((CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_RANDO_ONLY && IS_RANDO) || - (CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_ALWAYS_ON)); OPEN_DISPS(play->state.gfxCtx); @@ -437,6 +432,12 @@ void KaleidoScope_DrawItemSelect(PlayState* play) { pauseCtx->nameColorSet = 0; if ((pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0) && (pauseCtx->pageIndex == PAUSE_ITEM)) { + bool dpad = (CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0) && !CHECK_BTN_ALL(input->cur.button, BTN_CUP)); + bool pauseAnyCursor = + pauseCtx->cursorSpecialPos == 0 && + ((CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_RANDO_ONLY && IS_RANDO) || + (CVarGetInteger(CVAR_ENHANCEMENT("PauseAnyCursor"), 0) == PAUSE_ANY_CURSOR_ALWAYS_ON)); + moveCursorResult = 0 || IsItemCycling(); oldCursorPoint = pauseCtx->cursorPoint[PAUSE_ITEM]; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c index 3ff61d7833b..3e79eb6ad04 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c @@ -59,11 +59,11 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { s16 stepG; s16 stepB; u16 rgba16; - bool dpad = CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0); OPEN_DISPS(gfxCtx); if ((pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0) && (pauseCtx->pageIndex == PAUSE_MAP)) { + bool dpad = CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0); pauseCtx->cursorColorSet = 0; oldCursorPoint = pauseCtx->cursorPoint[PAUSE_MAP]; From b87f1432fd245ce3409fbc769f17e8e94d69db24 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 6 Aug 2025 04:43:19 +0100 Subject: [PATCH 133/157] properly clear location subcatagories when generating a seed (#5707) --- soh/soh/Enhancements/randomizer/context.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp index a0f7f223b5e..243f22ea670 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/context.cpp @@ -161,6 +161,10 @@ bool Context::IsQuestOfLocationActive(RandomizerCheck rc) { void Context::GenerateLocationPool() { allLocations.clear(); + overworldLocations.clear(); + for (auto dungeon : ctx->GetDungeons()->GetDungeonList()) { + dungeon->locations.clear(); + } for (Location& location : StaticData::GetLocationTable()) { // skip RCs that shouldn't be in the pool for any reason (i.e. settings, unsupported check type, etc.) // TODO: Exclude checks for some of the older shuffles from the pool too i.e. Frog Songs, Scrubs, etc.) From 89c1c97522763c280226f60fcba85620d26ed5ad Mon Sep 17 00:00:00 2001 From: Malkierian Date: Sat, 9 Aug 2025 11:17:33 -0700 Subject: [PATCH 134/157] Add missing CVar check for tab key processing for alt asset toggle. (#5736) --- soh/soh/OTRGlobals.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 630ffeaaa34..aace117a803 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1435,7 +1435,9 @@ extern "C" void Graph_StartFrame() { } #endif case KbScancode::LUS_KB_TAB: { - CVarSetInteger(CVAR_SETTING("AltAssets"), !CVarGetInteger(CVAR_SETTING("AltAssets"), 0)); + if (CVarGetInteger(CVAR_SETTING("Mods.AlternateAssetsHotkey"), 1)) { + CVarSetInteger(CVAR_SETTING("AltAssets"), !CVarGetInteger(CVAR_SETTING("AltAssets"), 0)); + } break; } } From 353ad944be88b969e44faa0619cc5adfed98d561 Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Tue, 12 Aug 2025 21:55:38 -0500 Subject: [PATCH 135/157] Removed SHUTTER_BACK_LOCKED and SHUTTER_BOSS from GetDungeonSmallKeyDoors. (#5738) --- soh/soh/Enhancements/randomizer/logic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 710437dc661..a9e7264a0ff 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2228,7 +2228,7 @@ const std::vector& GetDungeonSmallKeyDoors(SceneID sceneId) { } } else if (transitionActor.id == ACTOR_DOOR_SHUTTER) { uint8_t doorType = (transitionActor.params >> 7) & 15; - if (doorType == SHUTTER_BACK_LOCKED || doorType == SHUTTER_BOSS || doorType == SHUTTER_KEY_LOCKED) { + if (doorType == SHUTTER_KEY_LOCKED) { dungeonSmallKeyDoors[key].emplace_back(transitionActor.params & 0x3F); } } From e104870e6ad6fb1e6c9600d6c0d9691d9a53b0e5 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Tue, 12 Aug 2025 22:56:07 -0400 Subject: [PATCH 136/157] remove !IS_RANDO check (#5728) --- soh/soh/Enhancements/timesaver_hook_handlers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 9a602323d8b..b847fca51f3 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -941,7 +941,7 @@ void TimeSaverOnActorInitHandler(void* actorRef) { }); } - if (actor->id == ACTOR_EN_JJ && !IS_RANDO) { + if (actor->id == ACTOR_EN_JJ) { enJjUpdateHook = GameInteractor::Instance->RegisterGameHook([](void* innerActorRef) mutable { Actor* innerActor = static_cast(innerActorRef); From 568639dfc006e15e0d3d3dc4becbadca5aae23d1 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Tue, 12 Aug 2025 23:37:08 -0400 Subject: [PATCH 137/157] skip ItemGet for small keys after skeleton key (#5730) --- soh/soh/Enhancements/randomizer/hook_handlers.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index bd267aebe68..8df83359f40 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -327,7 +327,10 @@ void RandomizerOnPlayerUpdateForRCQueueHandler() { getItemEntry.modIndex == MOD_RANDOMIZER) && (getItemEntry.getItemCategory == ITEM_CATEGORY_JUNK || getItemEntry.getItemCategory == ITEM_CATEGORY_SKULLTULA_TOKEN || - getItemEntry.getItemCategory == ITEM_CATEGORY_LESSER))))) { + getItemEntry.getItemCategory == ITEM_CATEGORY_LESSER || + // Treat small keys as junk if Skeleton Key is obtained. + (getItemEntry.getItemCategory == ITEM_CATEGORY_SMALL_KEY && + Flags_GetRandomizerInf(RAND_INF_HAS_SKELETON_KEY))))))) { Item_DropCollectible(gPlayState, &spawnPos, ITEM00_SOH_GIVE_ITEM_ENTRY | 0x8000); } } From 4e50d780a9a721eb2c6ce77532ebd5b5b967e7c4 Mon Sep 17 00:00:00 2001 From: Gotest Date: Thu, 14 Aug 2025 20:13:39 -0600 Subject: [PATCH 138/157] Update gameplaystats.cpp Trigger rebuild of artifacts --- soh/soh/Enhancements/gameplaystats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp index c7cf007e357..836dd41de67 100644 --- a/soh/soh/Enhancements/gameplaystats.cpp +++ b/soh/soh/Enhancements/gameplaystats.cpp @@ -445,7 +445,7 @@ void DrawGameplayStatsHeader() { ImGui::TableSetupColumn("stat", ImGuiTableColumnFlags_WidthStretch); // if tag is empty (not a release build) if (gGitCommitTag[0] == 0) { - GameplayStatsRow("Leveled Mod Version:", "1.1.3"); + GameplayStatsRow("Leveled Mod Version:", "1.1.3.1"); GameplayStatsRow("A Mod By Gotest, AKA Arrenton", ""); GameplayStatsRow("Git Branch:", (char*)gGitBranch); GameplayStatsRow("Git Commit Hash:", (char*)gGitCommitHash); From 9ff9bebaa297a2d5afa5e58315fd4bb667ffbaf3 Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Mon, 25 Aug 2025 14:59:28 -0400 Subject: [PATCH 139/157] Fix faulty Nocturne trigger when receiving Fire Medallion (#5761) --- soh/soh/Enhancements/randomizer/hook_handlers.cpp | 2 +- soh/soh/Enhancements/timesaver_hook_handlers.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 8df83359f40..babea1c509e 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -868,7 +868,7 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l *should = !Flags_GetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL) && LINK_IS_ADULT && gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_KAKARIKO_VILLAGE && CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) && CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) && - CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER); + CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER) && gSaveContext.cutsceneIndex < 0xFFF0; break; case VB_BE_ELIGIBLE_FOR_CHILD_ROLLING_GORON_REWARD: { // Don't require a bomb bag to get prize in rando diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index b847fca51f3..27bf1f02d6d 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -165,7 +165,7 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li *should = false; } - u8 meetsBurningKakRequirements = LINK_IS_ADULT && + u8 meetsBurningKakRequirements = LINK_IS_ADULT && gSaveContext.cutsceneIndex < 0xFFF0 && gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_FRONT_GATE && Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP) && Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP) && From b6166f41c85c5834a4e68807bb94d3460f7033d1 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 25 Aug 2025 11:59:38 -0700 Subject: [PATCH 140/157] Restore previous state of `z_door_warp1.c`, and reimplement boss rush blue warp bypass. This also fixes the issue with shadow and spirit medallions not being given in vanilla without cutscene skips on. (#5763) Ensure shadow and spirit medallions get queued when skipping story cutscenes in vanilla. --- .../TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp | 9 +++++++++ soh/soh/Enhancements/boss-rush/BossRush.cpp | 4 ++++ soh/soh/Enhancements/timesaver_hook_handlers.cpp | 5 ++++- soh/soh/Enhancements/timesaver_hook_handlers.h | 3 +++ soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c | 6 ++---- 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp b/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp index 674adcdfbf2..de2e5fc5f43 100644 --- a/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp +++ b/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp @@ -1,6 +1,7 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/OTRGlobals.h" +#include "soh/Enhancements/timesaver_hook_handlers.h" extern "C" { #include "macros.h" @@ -12,6 +13,7 @@ extern "C" { #define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get() +extern "C" PlayState* gPlayState; static bool sEnteredBlueWarp = false; /** @@ -124,6 +126,13 @@ void SkipBlueWarp_ShouldPlayBlueWarpCS(GIVanillaBehavior _, bool* should, va_lis */ void SkipBlueWarp_ShouldGiveItem(GIVanillaBehavior _, bool* should, va_list originalArgs) { if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) { + if (IS_VANILLA) { + if (gPlayState->sceneNum == SCENE_SHADOW_TEMPLE_BOSS) { + TimeSaverQueueItem(RG_SHADOW_MEDALLION); + } else if (gPlayState->sceneNum == SCENE_SPIRIT_TEMPLE_BOSS) { + TimeSaverQueueItem(RG_SPIRIT_MEDALLION); + } + } *should = false; } } diff --git a/soh/soh/Enhancements/boss-rush/BossRush.cpp b/soh/soh/Enhancements/boss-rush/BossRush.cpp index 76cf5637e06..17ffd70c735 100644 --- a/soh/soh/Enhancements/boss-rush/BossRush.cpp +++ b/soh/soh/Enhancements/boss-rush/BossRush.cpp @@ -501,6 +501,10 @@ void BossRush_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li } break; } + case VB_PLAY_BLUE_WARP_CS: { + *should = false; + break; + } // Spawn clean blue warps (no ruto, adult animation, etc) case VB_SPAWN_BLUE_WARP: { switch (gPlayState->sceneNum) { diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 27bf1f02d6d..fdcc23ba317 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -1,6 +1,5 @@ #include #include "soh/OTRGlobals.h" -#include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/enhancementTypes.h" @@ -1199,6 +1198,10 @@ void TimeSaverOnSceneInitHandler(int16_t sceneNum) { static GetItemEntry vanillaQueuedItemEntry = GET_ITEM_NONE; +void TimeSaverQueueItem(RandomizerGet randoGet) { + vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(randoGet).GetGIEntry_Copy(); +} + void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) { // Do nothing when in a boss rush if (IS_BOSS_RUSH) { diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.h b/soh/soh/Enhancements/timesaver_hook_handlers.h index 41bccd8d99d..373c9eaf8b2 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.h +++ b/soh/soh/Enhancements/timesaver_hook_handlers.h @@ -1,6 +1,9 @@ #ifndef TIMESAVER_HOOK_HANDLERS_H #define TIMESAVER_HOOK_HANDLERS_H +#include "soh/Enhancements/randomizer/randomizerTypes.h" + void TimeSaverRegisterHooks(); +void TimeSaverQueueItem(RandomizerGet randoGet); #endif // TIMESAVER_HOOK_HANDLERS_H \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c index 54e9768b4a2..a9076471761 100644 --- a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c +++ b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c @@ -813,8 +813,7 @@ void DoorWarp1_AdultWarpOut(DoorWarp1* this, PlayState* play) { gSaveContext.nextCutsceneIndex = 0; } } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE_BOSS) { - if (GameInteractor_Should(VB_PLAY_BLUE_WARP_CS, - !Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE), + if (GameInteractor_Should(VB_PLAY_BLUE_WARP_CS, !CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT), RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE)) { Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE); if (GameInteractor_Should(VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_MEDALLION_SPIRIT)) { @@ -832,8 +831,7 @@ void DoorWarp1_AdultWarpOut(DoorWarp1* this, PlayState* play) { gSaveContext.nextCutsceneIndex = 0; } } else if (play->sceneNum == SCENE_SHADOW_TEMPLE_BOSS) { - if (GameInteractor_Should(VB_PLAY_BLUE_WARP_CS, - !Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE), + if (GameInteractor_Should(VB_PLAY_BLUE_WARP_CS, !CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW), RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE)) { Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE); if (GameInteractor_Should(VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_MEDALLION_SHADOW)) { From 224efe2946d5903a69855188b0bcba268b03638e Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Mon, 25 Aug 2025 19:59:47 +0100 Subject: [PATCH 141/157] Fix TOT_MASTER_SWORD when the master sword is stated with but not shuffled. (#5705) * Fix TOT_MASTER_SWORD when the master sword is stated with but not shuffled. * remove RC_MASTER_SWORD_PEDESTAL --- soh/soh/Enhancements/randomizer/context.cpp | 2 +- soh/soh/Enhancements/randomizer/hook_handlers.cpp | 2 +- soh/soh/Enhancements/randomizer/randomizerTypes.h | 2 -- soh/soh/Enhancements/randomizer/settings.cpp | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp index 243f22ea670..d291655a011 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/context.cpp @@ -170,7 +170,7 @@ void Context::GenerateLocationPool() { // TODO: Exclude checks for some of the older shuffles from the pool too i.e. Frog Songs, Scrubs, etc.) if (location.GetRandomizerCheck() == RC_UNKNOWN_CHECK || location.GetRandomizerCheck() == RC_TRIFORCE_COMPLETED || // already in pool - (location.GetRandomizerCheck() == RC_MASTER_SWORD_PEDESTAL && + (location.GetRandomizerCheck() == RC_TOT_MASTER_SWORD && mOptions[RSK_SHUFFLE_MASTER_SWORD].Is(RO_GENERIC_OFF)) || (location.GetRandomizerCheck() == RC_KAK_100_GOLD_SKULLTULA_REWARD && mOptions[RSK_SHUFFLE_100_GS_REWARD].Is(RO_GENERIC_OFF)) || diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index babea1c509e..9e77d27c482 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -884,7 +884,7 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l break; } case VB_GIVE_ITEM_MASTER_SWORD: - if (RAND_GET_OPTION(RSK_SHUFFLE_MASTER_SWORD)) { + if (RAND_GET_OPTION(RSK_SHUFFLE_MASTER_SWORD) || RAND_GET_OPTION(RSK_STARTING_MASTER_SWORD)) { *should = false; } else { *should = true; diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index d9eae0af8e6..63dbeb3896c 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -2609,7 +2609,6 @@ typedef enum { RC_PIERRE, RC_DELIVER_RUTOS_LETTER, - RC_MASTER_SWORD_PEDESTAL, RC_KF_DEKU_TREE_LEFT_GOSSIP_STONE, RC_KF_DEKU_TREE_RIGHT_GOSSIP_STONE, RC_KF_GOSSIP_STONE, @@ -4902,7 +4901,6 @@ typedef enum { RHT_GANONS_CASTLE_MQ_DEKU_SCRUB_RIGHT, RHT_GANONS_TOWER_BOSS_KEY_CHEST, RHT_DELIVER_RUTOS_LETTER, - RHT_MASTER_SWORD_PEDESTAL, // Beehives RHT_BEEHIVE_CHEST_GROTTO, RHT_BEEHIVE_COW_GROTTO, diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index a9f7b4e64ee..d2f93fe98c4 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -2571,7 +2571,7 @@ void Context::FinalizeSettings(const std::set& excludedLocation } if (!mOptions[RSK_SHUFFLE_MASTER_SWORD]) { if (mOptions[RSK_STARTING_MASTER_SWORD]) { - this->GetItemLocation(RC_MASTER_SWORD_PEDESTAL)->SetExcludedOption(1); + this->GetItemLocation(RC_TOT_MASTER_SWORD)->SetExcludedOption(1); } } if (!mOptions[RSK_SHUFFLE_OCARINA]) { From 820d097c84efccdbd30fa633df74a422200d5e53 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:00:04 -0400 Subject: [PATCH 142/157] Fix King Dodongo door switch cutscene (#5729) * fix parathenses to properly OR * clang clang clang --- soh/soh/Enhancements/timesaver_hook_handlers.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index fdcc23ba317..4af24ad19b4 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -300,9 +300,9 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li } switch (actor->id) { case ACTOR_OBJ_SWITCH: { - if ((actor->params == 8224 && gPlayState->sceneNum == SCENE_DODONGOS_CAVERN) || - (actor->params == 6979 && gPlayState->sceneNum == SCENE_WATER_TEMPLE) && - CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) { + if (((actor->params == 8224 && gPlayState->sceneNum == SCENE_DODONGOS_CAVERN) || + (actor->params == 6979 && gPlayState->sceneNum == SCENE_WATER_TEMPLE)) && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) { break; } ObjSwitch* switchActor = (ObjSwitch*)actor; From 21e37d9ac2ad14996d7d50585f3ea1682a7467b4 Mon Sep 17 00:00:00 2001 From: balloondude2 <55861555+balloondude2@users.noreply.github.com> Date: Mon, 25 Aug 2025 13:00:28 -0600 Subject: [PATCH 143/157] Fix the Show Horizontal Resolution Field option v2 (#5744) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * move showHorizontalResField initialization * remove redundant assignment Co-authored-by: Philip Dubé --------- Co-authored-by: Philip Dubé --- soh/soh/SohGui/ResolutionEditor.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/soh/soh/SohGui/ResolutionEditor.cpp b/soh/soh/SohGui/ResolutionEditor.cpp index d33a7b43c06..d3810f6a4ba 100644 --- a/soh/soh/SohGui/ResolutionEditor.cpp +++ b/soh/soh/SohGui/ResolutionEditor.cpp @@ -563,7 +563,6 @@ void UpdateResolutionVars() { verticalPixelCount = CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalPixelCount", pixelCountPresets[item_pixelCount]); // Additional settings - showHorizontalResField = false; horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; // Disabling flags disabled_everything = !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled", 0); From 7b3809366575c14a23d3ca781ee9f399da5c7d48 Mon Sep 17 00:00:00 2001 From: PurpleHato Date: Tue, 26 Aug 2025 21:34:29 +0200 Subject: [PATCH 144/157] [TWEAK] Localization support for the Notification System (#5751) * Localization support * stick/nuts updrage french wording * forgot german questItem and function adaptation for these * names * clang * name + clang * should be the last one :derp: * German typo --- .../Enhancements/randomizer/hook_handlers.cpp | 50 ++- soh/soh/util.cpp | 378 +++++++++++++++++- 2 files changed, 416 insertions(+), 12 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 9e77d27c482..454a892a413 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -1,4 +1,4 @@ -#include +#include #include "soh/OTRGlobals.h" #include "soh/ResourceManagerHelpers.h" #include "soh/Enhancements/enhancementTypes.h" @@ -1026,17 +1026,55 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l } if (item00->itemEntry.modIndex == MOD_NONE) { + std::string message; + + switch (gSaveContext.language) { + case LANGUAGE_FRA: + message = "Vous obtenez: "; + break; + case LANGUAGE_GER: + message = "Du erhältst: "; + break; + case LANGUAGE_ENG: + default: + message = "You found "; + break; + } + Notification::Emit({ .itemIcon = GetTextureForItemId(item00->itemEntry.itemId), - .message = "You found ", + .message = message, .suffix = SohUtils::GetItemName(item00->itemEntry.itemId), }); } else if (item00->itemEntry.modIndex == MOD_RANDOMIZER) { + std::string message; + std::string itemName; + + switch (gSaveContext.language) { + case LANGUAGE_FRA: + message = "Vous obtenez: "; + itemName = Rando::StaticData::RetrieveItem((RandomizerGet)item00->itemEntry.getItemId) + .GetName() + .french; + break; + case LANGUAGE_GER: + message = "Du erhältst: "; + itemName = Rando::StaticData::RetrieveItem((RandomizerGet)item00->itemEntry.getItemId) + .GetName() + .german; + break; + case LANGUAGE_ENG: + default: + message = "You found "; + itemName = Rando::StaticData::RetrieveItem((RandomizerGet)item00->itemEntry.getItemId) + .GetName() + .english; + break; + } + Notification::Emit({ - .message = "You found ", - .suffix = Rando::StaticData::RetrieveItem((RandomizerGet)item00->itemEntry.getItemId) - .GetName() - .english, + .message = message, + .suffix = itemName, }); } diff --git a/soh/soh/util.cpp b/soh/soh/util.cpp index a991c35ca57..bd551f93791 100644 --- a/soh/soh/util.cpp +++ b/soh/soh/util.cpp @@ -7,6 +7,7 @@ #include #include #include "Enhancements/randomizer/randomizerTypes.h" +#include std::vector sceneNames = { "Inside the Deku Tree", @@ -121,7 +122,7 @@ std::vector sceneNames = { "Treasure Chest Room", }; -std::vector itemNames = { +std::vector itemNamesEng = { "Deku Stick", "Deku Nut", "Bomb", @@ -280,7 +281,325 @@ std::vector itemNames = { "Deku Nut Upgrade (40)", }; -std::vector questItemNames = { +std::vector itemNamesFra = { + "Bâton Mojo", + "Noix Mojo", + "Bombe", + "Arc des Fées", + "Flèche de Feu", + "Feu de Din", + "Lance-Pierre des Fées", + "Ocarina des Fées", + "Ocarina du Temps", + "Missile Teigneux", + "Grappin", + "Super Grappin", + "Flèche de Glace", + "Vent de Farore", + "Boomerang", + "Monocle de Vérité", + "Haricot Magique", + "Masse des Titans", + "Flèche de Lumière", + "Amour de Nayru", + "Bouteille Vide", + "Potion Rouge", + "Potion Verte", + "Potion Bleue", + "Fée en Bouteille", + "Poisson", + "Lait Lon Lon et Bouteille", + "Lettre de Ruto", + "Flamme Bleue", + "Insectes", + "Grand Spectre", + "Lait Lon Lon (Demi)", + "Spectre", + "Oeuf Suspect", + "Poule", + "Lettre de Zelda", + "Masque Renard", + "Masque de Mort", + "Masque du Fantôme", + "Capuche de Lapin", + "Masque Goron", + "Masque Zora", + "Masque Gerudo", + "Masque de Vérité", + "ÉPUISÉ", + "Oeuf de Poche", + "Cocotte de Poche", + "Cojiro", + "Champignon Suspect", + "Potion Suspecte", + "Scie du Chasseur", + "Épée Goron (Cassée)", + "Ordonnance", + "Crapaud-qui-louche", + "Super Gouttes", + "Certificat", + "Arc des Fées & Flèche de Feu", + "Arc des Fées & Flèche de Glace", + "Arc des Fées & Flèche de Lumière", + "Épée Kokiri", + "Épée de Légende", + "Lame des Géants & Épée Biggoron", + "Bouclier Mojo", + "Bouclier Hylien", + "Bouclier Miroir", + "Tunique Kokiri", + "Tunique Goron", + "Tunique Zora", + "Bottes Kokiri", + "Bottes de Plomb", + "Bottes des Airs", + "Sac de Graines (30)", + "Sac de Graines (40)", + "Sac de Graines (50)", + "Carquois (30)", + "Grand Carquois (40)", + "Énorme Grand Carquois (50)", + "Sac de Bombes (20)", + "Gros Sac de Bombes (30)", + "Énorme Sac de Bombes (40)", + "Bracelet Goron", + "Gantelets d'Argent", + "Gantelets d'Or", + "Écaille d'Argent", + "Écaille d'Or", + "Lame des Géants (Cassée)", + "Grande Bourse", + "Bourse de Géant", + "Graines Mojo (5)", + "Canne à Pêche", + "Menuet des Bois", + "Boléro du Feu", + "Sérénade de l'Eau", + "Requiem de l'Esprit", + "Nocturne de l'Ombre", + "Prélude de la Lumière", + "Berceuse de Zelda", + "Chant d'Epona", + "Chant de Saria", + "Chant du Soleil", + "Chant du Temps", + "Chant des Tempêtes", + "Médaillon de la Forêt", + "Médaillon du Feu", + "Médaillon de l'Eau", + "Médaillon de l'Esprit", + "Médaillon de l'Ombre", + "Médaillon de la Lumière", + "Émeraude Kokiri", + "Rubis Goron", + "Saphir Zora", + "Pierre de Souffrance", + "Carte Gerudo", + "Symbole de Skulltula d'Or", + "Réceptacle de Coeur", + "Quart de Coeur", + "Clé du Boss", + "Boussole", + "Carte du Donjon", + "Petite Clé", + "Petite Magie", + "Grande Magie", + "Quart de Coeur", + "[Retiré]", + "[Retiré]", + "[Retiré]", + "[Retiré]", + "[Retiré]", + "[Retiré]", + "[Retiré]", + "Lait Lon Lon", + "Coeur", + "Rubis Vert", + "Rubis Bleu", + "Rubis Rouge", + "Rubis Pourpre", + "Énorme Rubis", + "[Retiré]", + "Bâtons Mojo (5)", + "Bâtons Mojo (10)", + "Noix Mojo (5)", + "Noix Mojo (10)", + "Bombes (5)", + "Bombes (10)", + "Bombes (20)", + "Bombes (30)", + "Flèches (Petites)", + "Flèches (Moyennes)", + "Flèches (Grandes)", + "Graines Mojo (30)", + "Missile Teigneux (5)", + "Missile Teigneux (20)", + "Amélioration des Bâtons Mojo (20)", + "Amélioration des Bâtons Mojo (30)", + "Amélioration des Noix Mojo (30)", + "Amélioration des Noix Mojo (40)", +}; + +std::vector itemNamesGer = { + "Deku-Stab", + "Deku-Nuss", + "Bombe", + "Feenbogen", + "Feuerpfeil", + "Dins Feuerinferno", + "Feen-Schleuder", + "Feen-Okarina", + "Okarina der Zeit", + "Krabbelmine", + "Enterhaken", + "Enterhaken (Lang)", + "Eispfeil", + "Farores Donnersturm", + "Bumerang", + "Auge der Wahrheit", + "Zauberbohne", + "Megatonhammer", + "Lichtpfeil", + "Nayrus Umarmung", + "Leere Flasche", + "Roter Trank", + "Grüner Trank", + "Blauer Trank", + "Fee in Flasche", + "Fisch", + "Lon Lon Milch & Flasche", + "Brief von Ruto", + "Blaue Flamme", + "Insekten", + "Großer Irrgeist", + "Lon Lon Milch (Halb)", + "Irrgeist", + "Seltsames Ei", + "Huhn", + "Brief von Zelda", + "Fuchsmaske", + "Totenkopfmaske", + "Schädelmaske", + "Hasenohren", + "Goron-Maske", + "Zora-Maske", + "Gerudo-Maske", + "Maske der Wahrheit", + "AUSVERKAUFT", + "Taschenei", + "Taschenhuhn", + "Cojiro", + "Seltsamer Pilz", + "Seltsamer Trank", + "Säge des Schmugglers", + "Goronen-Schwert (Kaputt)", + "Rezept", + "Augenfrosch", + "Augentropfen", + "Abholschein", + "Feenbogen & Feuerpfeil", + "Feenbogen & Eispfeil", + "Feenbogen & Lichtpfeil", + "Kokiri-Schwert", + "Master-Schwert", + "Riesenmesser & Biggorons Schwert", + "Deku-Schild", + "Hylia-Schild", + "Spiegelschild", + "Kokiri-Gewand", + "Goronen-Gewand", + "Zora-Gewand", + "Kokiri-Stiefel", + "Eisenstiefel", + "Schwebestiefel", + "Kugelbeutel (30)", + "Kugelbeutel (40)", + "Kugelbeutel (50)", + "Köcher (30)", + "Großer Köcher (40)", + "Riesenköcher (50)", + "Bombentasche (20)", + "Große Bombentasche (30)", + "Riesen-Bombentasche (40)", + "Goronen-Armband", + "Silberhandschuhe", + "Goldhandschuhe", + "Silberschuppe", + "Goldschuppe", + "Riesenmesser (Kaputt)", + "Erwachsenenbörse", + "Riesenbörse", + "Deku-Samen (5)", + "Angel", + "Menuett des Waldes", + "Bolero des Feuers", + "Serenade des Wassers", + "Requiem der Geister", + "Nocturne der Schatten", + "Präludium des Lichts", + "Zeldas Wiegenlied", + "Eponas Lied", + "Sarias Lied", + "Sonnenlied", + "Hymne der Zeit", + "Sturm-Lied", + "Amulett des Waldes", + "Amulett des Feuers", + "Amulett des Wassers", + "Amulett der Geister", + "Amulett der Schatten", + "Amulett des Lichts", + "Kokiri-Smaragd", + "Goronen-Rubin", + "Zora-Saphir", + "Stein des Wissens", + "Gerudo-Pass", + "Goldene Skulltula-Marke", + "Herzcontainer", + "Herzteil", + "Boss-Schlüssel", + "Kompass", + "Dungeon-Karte", + "Kleiner Schlüssel", + "Kleine Magie", + "Große Magie", + "Herzteil", + "[Entfernt]", + "[Entfernt]", + "[Entfernt]", + "[Entfernt]", + "[Entfernt]", + "[Entfernt]", + "[Entfernt]", + "Lon Lon Milch", + "Herz", + "Grüner Rubin", + "Blauer Rubin", + "Roter Rubin", + "Violetter Rubin", + "Silberner Rubin", + "[Entfernt]", + "Deku-Stäbe (5)", + "Deku-Stäbe (10)", + "Deku-Nüsse (5)", + "Deku-Nüsse (10)", + "Bomben (5)", + "Bomben (10)", + "Bomben (20)", + "Bomben (30)", + "Pfeile (Klein)", + "Pfeile (Mittel)", + "Pfeile (Groß)", + "Deku-Samen (30)", + "Krabbelmine (5)", + "Krabbelmine (20)", + "Deku-Stab-Erweiterung (20)", + "Deku-Stab-Erweiterung (30)", + "Deku-Nuss-Erweiterung (30)", + "Deku-Nuss-Erweiterung (40)", +}; + +std::vector questItemNamesEng = { "Forest Medallion", "Fire Medallion", "Water Medallion", "Spirit Medallion", "Shadow Medallion", "Light Medallion", "Minuet of Forest", "Bolero of Fire", "Serenade of Water", "Requiem of Spirit", "Nocturne of Shadow", "Prelude of Light", "Zelda's Lullaby", "Epona's Song", "Saria's Song", @@ -288,6 +607,24 @@ std::vector questItemNames = { "Zora's Sapphire", "Stone of Agony", "Gerudo's Card", "Gold Skulltula Token", }; +std::vector questItemNamesFra = { + "Médaillon de la Forêt", "Médaillon du Feu", "Médaillon de l'Eau", "Médaillon de l'Esprit", + "Médaillon de l'Ombre", "Médaillon de la Lumière", "Menuet des Bois", "Boléro du Feu", + "Sérénade de l'Eau", "Requiem de l'Esprit", "Nocturne de l'Ombre", "Prélude de la Lumière", + "Berceuse de Zelda", "Chant d'Epona", "Chant de Saria", "Chant du Soleil", + "Chant du Temps", "Chant des Tempêtes", "Émeraude Kokiri", "Rubis Goron", + "Saphir Zora", "Pierre de Souffrance", "Carte Gerudo", "Symbole de Skulltula d'Or", +}; + +std::vector questItemNamesGer = { + "Amulett des Waldes", "Amulett des Feuers", "Amulett des Wassers", "Amulett der Geister", + "Amulett der Schatten", "Amulett des Lichts", "Menuett des Waldes", "Bolero des Feuers", + "Serenade des Wassers", "Requiem der Geister", "Nocturne der Schatten", "Präludium des Lichts", + "Zeldas Wiegenlied", "Eponas Lied", "Sarias Lied", "Sonnenlied", + "Hymne der Zeit", "Sturm-Lied", "Kokiri-Smaragd", "Goronen-Rubin", + "Zora-Saphir", "Stein des Wissens", "Gerudo-Pass", "Goldene Skulltula-Marke", +}; + std::array rcareaPrefixes = { "KF", "LW", @@ -334,23 +671,52 @@ const std::string& SohUtils::GetSceneName(int32_t scene) { } const std::string& SohUtils::GetItemName(int32_t item) { - if (item > itemNames.size()) { + const std::vector* currentItemNames = nullptr; + + switch (gSaveContext.language) { + case LANGUAGE_FRA: + currentItemNames = &itemNamesFra; + break; + case LANGUAGE_GER: + currentItemNames = &itemNamesGer; + break; + case LANGUAGE_ENG: + default: + currentItemNames = &itemNamesEng; + break; + } + + if (item >= currentItemNames->size()) { SPDLOG_WARN("Passed invalid item id to SohUtils::GetItemName: ({})", item); assert(false); return ""; } - return itemNames[item]; + return (*currentItemNames)[item]; } const std::string& SohUtils::GetQuestItemName(int32_t item) { - if (item > questItemNames.size()) { + const std::vector* currentQuestItemNames = nullptr; + + switch (gSaveContext.language) { + case LANGUAGE_FRA: + currentQuestItemNames = &questItemNamesFra; + break; + case LANGUAGE_GER: + currentQuestItemNames = &questItemNamesGer; + break; + case LANGUAGE_ENG: + default: + currentQuestItemNames = &questItemNamesEng; + break; + } + if (item > questItemNamesEng.size()) { SPDLOG_WARN("Passed invalid quest item id to SohUtils::GetQuestItemName: ({})", item); assert(false); return ""; } - return questItemNames[item]; + return (*currentQuestItemNames)[item]; } const std::string& SohUtils::GetRandomizerCheckAreaPrefix(int32_t rcarea) { From cbd376afa9a14e2804efbb0cfd0e8aa7f98d33e7 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Thu, 28 Aug 2025 08:59:45 -0700 Subject: [PATCH 145/157] Search Tweaks (#5767) * Increase vibrancy of search field color. Set autofocus to only happen on fresh menu load. * Revert tooltip addition. --- soh/soh/SohGui/Menu.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/soh/soh/SohGui/Menu.cpp b/soh/soh/SohGui/Menu.cpp index 5a609a935e1..98db5d23907 100644 --- a/soh/soh/SohGui/Menu.cpp +++ b/soh/soh/SohGui/Menu.cpp @@ -496,6 +496,7 @@ void Menu::Draw() { SyncVisibilityConsoleVariable(); } +static bool freshOpen = true; void Menu::DrawElement() { for (auto& [reason, info] : disabledMap) { info.active = info.evaluation(info); @@ -538,6 +539,7 @@ void Menu::DrawElement() { if (!popout) { ImGui::PopStyleVar(); } + freshOpen = true; ImGui::PopStyleColor(); ImGui::End(); return; @@ -654,13 +656,13 @@ void Menu::DrawElement() { std::string menuSearchText = ""; if (headerSearch) { ImGui::SameLine(); - if (autoFocus && ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && !ImGui::IsAnyItemActive() && - !ImGui::IsMouseClicked(0)) { - ImGui::SetKeyboardFocusHere(0); + if (autoFocus && freshOpen) { + ImGui::SetKeyboardFocusHere(); } auto color = UIWidgets::ColorValues.at(menuThemeIndex); - color.w = 0.2f; + color.w = 0.6f; ImGui::PushStyleColor(ImGuiCol_FrameBg, color); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); menuSearch.Draw("##search", 200.0f); menuSearchText = menuSearch.InputBuf; menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '), menuSearchText.end()); @@ -668,6 +670,7 @@ void Menu::DrawElement() { ImGui::SameLine(headerWidth - 200.0f + style.ItemSpacing.x); ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "Search..."); } + ImGui::PopStyleVar(); ImGui::PopStyleColor(); } ImGui::EndChild(); @@ -853,6 +856,9 @@ void Menu::DrawElement() { poppedSize = ImGui::GetWindowSize(); poppedPos = ImGui::GetWindowPos(); } + if (freshOpen) { + freshOpen = false; + } ImGui::End(); } } // namespace Ship From 405fc7a31dab081d0169001d253794298025f4d4 Mon Sep 17 00:00:00 2001 From: Extloga <141232749+Extloga@users.noreply.github.com> Date: Tue, 9 Sep 2025 05:44:19 +0200 Subject: [PATCH 146/157] Fixes for the German translation in util.cpp (#5768) * Fixes for the German translation in util.cpp * Fixes for the German translation in item_list.cpp * Fixes for the German translation in item_list.cpp * Fixes for the German translation in util.cpp * Revert the fixes for the German translation in item_list.cpp * Fixes for the German translation in util.cpp * Fixes for the German translation in util.cpp * Fixes for the German translation in util.cpp * Fixes for the German translation in util.cpp * Fixes for the German translation in util.cpp * Fixes for the German translation in kaleidoscope_ger.json * Revert the fixes for the German translation in kaleidoscope_ger.json * Fixes for the German translation in util.cpp --- soh/soh/util.cpp | 188 ++++++++++++++++++++++++++--------------------- 1 file changed, 103 insertions(+), 85 deletions(-) diff --git a/soh/soh/util.cpp b/soh/soh/util.cpp index bd551f93791..45e59d14ec5 100644 --- a/soh/soh/util.cpp +++ b/soh/soh/util.cpp @@ -442,79 +442,79 @@ std::vector itemNamesFra = { std::vector itemNamesGer = { "Deku-Stab", - "Deku-Nuss", + "Deku-Nuß", "Bombe", - "Feenbogen", - "Feuerpfeil", + "Feen-Bogen", + "Feuer-Pfeil", "Dins Feuerinferno", "Feen-Schleuder", "Feen-Okarina", "Okarina der Zeit", "Krabbelmine", + "Fanghaken", "Enterhaken", - "Enterhaken (Lang)", - "Eispfeil", + "Eis-Pfeil", "Farores Donnersturm", "Bumerang", "Auge der Wahrheit", - "Zauberbohne", - "Megatonhammer", - "Lichtpfeil", + "Wundererbse", + "Stahlhammer", + "Licht-Pfeil", "Nayrus Umarmung", "Leere Flasche", - "Roter Trank", - "Grüner Trank", - "Blauer Trank", - "Fee in Flasche", + "Rotes Elixier", + "Grünes Elixier", + "Blaues Elixier", + "Flasche (Fee)", "Fisch", - "Lon Lon Milch & Flasche", - "Brief von Ruto", - "Blaue Flamme", - "Insekten", - "Großer Irrgeist", - "Lon Lon Milch (Halb)", - "Irrgeist", + "Flasche (Milch)", + "Rutos Brief", + "Blaues Feuer", + "Käfer", + "Nachtschwärmer", + "Lon Lon-Milch (Halbe Füllung)", + "Irrlicht", "Seltsames Ei", "Huhn", - "Brief von Zelda", - "Fuchsmaske", - "Totenkopfmaske", - "Schädelmaske", + "Zeldas Brief", + "Fuchs-Maske", + "Geister-Maske", + "Schädel-Maske", "Hasenohren", - "Goron-Maske", + "Goronen-Maske", "Zora-Maske", "Gerudo-Maske", - "Maske der Wahrheit", + "Maske des Wissens", "AUSVERKAUFT", - "Taschenei", - "Taschenhuhn", - "Cojiro", - "Seltsamer Pilz", - "Seltsamer Trank", - "Säge des Schmugglers", - "Goronen-Schwert (Kaputt)", + "Ei", + "Kiki", + "Henni", + "Schimmelpilz", + "Modertrank", + "Säge", + "Zerbr. Goronen-Schwert", "Rezept", - "Augenfrosch", + "Glotzfrosch", "Augentropfen", - "Abholschein", - "Feenbogen & Feuerpfeil", - "Feenbogen & Eispfeil", - "Feenbogen & Lichtpfeil", + "Zertifikat", + "Feen-Bogen & Feuer-Pfeil", + "Feen-Bogen & Eis-Pfeil", + "Feen-Bogen & Licht-Pfeil", "Kokiri-Schwert", "Master-Schwert", - "Riesenmesser & Biggorons Schwert", + "Langschwert & Biggoron-Schwert", "Deku-Schild", "Hylia-Schild", - "Spiegelschild", - "Kokiri-Gewand", - "Goronen-Gewand", - "Zora-Gewand", - "Kokiri-Stiefel", + "Spiegel-Schild", + "Kokiri-Rüstung", + "Goronen-Rüstung", + "Zora-Rüstung", + "Lederstiefel", "Eisenstiefel", - "Schwebestiefel", - "Kugelbeutel (30)", - "Kugelbeutel (40)", - "Kugelbeutel (50)", + "Gleitstiefel", + "Munitionstasche (30)", + "Große Munitionstasche (40)", + "Riesen-Munitionstasche (50)", "Köcher (30)", "Großer Köcher (40)", "Riesenköcher (50)", @@ -522,47 +522,47 @@ std::vector itemNamesGer = { "Große Bombentasche (30)", "Riesen-Bombentasche (40)", "Goronen-Armband", - "Silberhandschuhe", - "Goldhandschuhe", - "Silberschuppe", - "Goldschuppe", - "Riesenmesser (Kaputt)", - "Erwachsenenbörse", + "Krafthandschuhe", + "Titanhandschuhe", + "Silberne Schuppe", + "Goldene Schuppe", + "Zerbr. Langschwert", + "Große Börse", "Riesenbörse", - "Deku-Samen (5)", - "Angel", + "Deku-Kerne (5)", + "Angelrute", "Menuett des Waldes", "Bolero des Feuers", "Serenade des Wassers", "Requiem der Geister", - "Nocturne der Schatten", - "Präludium des Lichts", + "Nocturne des Schattens", + "Kantate des Lichts", "Zeldas Wiegenlied", "Eponas Lied", - "Sarias Lied", - "Sonnenlied", + "Salias Lied", + "Hymne der Sonne", "Hymne der Zeit", - "Sturm-Lied", + "Hymne des Sturms", "Amulett des Waldes", "Amulett des Feuers", "Amulett des Wassers", "Amulett der Geister", - "Amulett der Schatten", + "Amulett des Schattens", "Amulett des Lichts", "Kokiri-Smaragd", "Goronen-Rubin", "Zora-Saphir", "Stein des Wissens", - "Gerudo-Pass", - "Goldene Skulltula-Marke", + "Gerudo-Paß", + "Skulltula-Symbol", "Herzcontainer", "Herzteil", - "Boss-Schlüssel", - "Kompass", - "Dungeon-Karte", + "Master-Schlüssel", + "Kompaß", + "Labyrinth-Karte", "Kleiner Schlüssel", - "Kleine Magie", - "Große Magie", + "Kleine Magieflasche", + "Große Magieflasche", "Herzteil", "[Entfernt]", "[Entfernt]", @@ -571,7 +571,7 @@ std::vector itemNamesGer = { "[Entfernt]", "[Entfernt]", "[Entfernt]", - "Lon Lon Milch", + "Lon Lon-Milch", "Herz", "Grüner Rubin", "Blauer Rubin", @@ -587,16 +587,16 @@ std::vector itemNamesGer = { "Bomben (10)", "Bomben (20)", "Bomben (30)", - "Pfeile (Klein)", - "Pfeile (Mittel)", - "Pfeile (Groß)", - "Deku-Samen (30)", - "Krabbelmine (5)", - "Krabbelmine (20)", - "Deku-Stab-Erweiterung (20)", - "Deku-Stab-Erweiterung (30)", - "Deku-Nuss-Erweiterung (30)", - "Deku-Nuss-Erweiterung (40)", + "Pfeile (5)", + "Pfeile (10)", + "Pfeile (30)", + "Deku-Kerne (30)", + "Krabbelminen (5)", + "Krabbelminen (20)", + "Deku-Stab-Kapazität (20)", + "Deku-Stab-Kapazität (30)", + "Deku-Nuß-Kapazität (30)", + "Deku-Nuß-Kapazität (40)", }; std::vector questItemNamesEng = { @@ -617,12 +617,30 @@ std::vector questItemNamesFra = { }; std::vector questItemNamesGer = { - "Amulett des Waldes", "Amulett des Feuers", "Amulett des Wassers", "Amulett der Geister", - "Amulett der Schatten", "Amulett des Lichts", "Menuett des Waldes", "Bolero des Feuers", - "Serenade des Wassers", "Requiem der Geister", "Nocturne der Schatten", "Präludium des Lichts", - "Zeldas Wiegenlied", "Eponas Lied", "Sarias Lied", "Sonnenlied", - "Hymne der Zeit", "Sturm-Lied", "Kokiri-Smaragd", "Goronen-Rubin", - "Zora-Saphir", "Stein des Wissens", "Gerudo-Pass", "Goldene Skulltula-Marke", + "Amulett des Waldes", + "Amulett des Feuers", + "Amulett des Wassers", + "Amulett der Geister", + "Amulett des Schattens", + "Amulett des Lichts", + "Menuett des Waldes", + "Bolero des Feuers", + "Serenade des Wassers", + "Requiem der Geister", + "Nocturne des Schattens", + "Kantate des Lichts", + "Zeldas Wiegenlied", + "Eponas Lied", + "Salias Lied", + "Hymne der Sonne", + "Hymne der Zeit", + "Hymne des Sturms", + "Kokiri-Smaragd", + "Goronen-Rubin", + "Zora-Saphir", + "Stein des Wissens", + "Gerudo-Paß", + "Goldenes Skulltula-Symbol", }; std::array rcareaPrefixes = { From 837f497ea66e5d841f97aebdf3414ca38785effe Mon Sep 17 00:00:00 2001 From: Malkierian Date: Mon, 8 Sep 2025 20:44:27 -0700 Subject: [PATCH 147/157] Encapsulate `BeginTable` for item and check tracker settings windows. (#5778) --- .../randomizer/randomizer_check_tracker.cpp | 2 +- .../randomizer/randomizer_item_tracker.cpp | 427 +++++++++--------- 2 files changed, 215 insertions(+), 214 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index d1ec624efcb..81e1c09f022 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -2199,8 +2199,8 @@ void CheckTrackerSettingsWindow::DrawElement() { "Checks that you saved the game while having collected.", THEME_COLOR); ImGui::PopStyleVar(1); + ImGui::EndTable(); } - ImGui::EndTable(); } void CheckTrackerWindow::InitElement() { diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index 5996fea1f5b..aea0a75d11a 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -1713,260 +1713,261 @@ static std::unordered_map minimalDisplayTypes = { void ItemTrackerSettingsWindow::DrawElement() { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f }); - ImGui::BeginTable("itemTrackerSettingsTable", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV); - ImGui::TableSetupColumn("General settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::TableSetupColumn("Section settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::TableHeadersRow(); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); - CVarColorPicker("Background Color##gItemTrackerBgColor", CVAR_TRACKER_ITEM("BgColor"), { 0, 0, 0, 0 }, true, - ColorPickerRandomButton | ColorPickerResetButton, THEME_COLOR); - - ImGui::PopItemWidth(); - if (CVarCombobox("Window Type", CVAR_TRACKER_ITEM("WindowType"), windowTypes, - ComboboxOptions() - .DefaultIndex(TRACKER_WINDOW_FLOATING) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - - if (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING) { - if (CVarCheckbox("Enable Dragging", CVAR_TRACKER_ITEM("Draggable"), CheckboxOptions().Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCheckbox("Only enable while paused", CVAR_TRACKER_ITEM("ShowOnlyPaused"), - CheckboxOptions().Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCombobox("Display Mode", CVAR_TRACKER_ITEM("DisplayType.Main"), displayModes, + if (ImGui::BeginTable("itemTrackerSettingsTable", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { + ImGui::TableSetupColumn("General settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableSetupColumn("Section settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableHeadersRow(); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + CVarColorPicker("Background Color##gItemTrackerBgColor", CVAR_TRACKER_ITEM("BgColor"), { 0, 0, 0, 0 }, true, + ColorPickerRandomButton | ColorPickerResetButton, THEME_COLOR); + + ImGui::PopItemWidth(); + if (CVarCombobox("Window Type", CVAR_TRACKER_ITEM("WindowType"), windowTypes, ComboboxOptions() - .DefaultIndex(TRACKER_DISPLAY_ALWAYS) + .DefaultIndex(TRACKER_WINDOW_FLOATING) .ComponentAlignment(ComponentAlignments::Right) .LabelPosition(LabelPositions::Far) .Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == - TRACKER_DISPLAY_COMBO_BUTTON) { - if (CVarCombobox("Combo Button 1", CVAR_TRACKER_ITEM("ComboButton1"), buttons, - ComboboxOptions() - .DefaultIndex(TRACKER_COMBO_BUTTON_L) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { + + if (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING) { + if (CVarCheckbox("Enable Dragging", CVAR_TRACKER_ITEM("Draggable"), CheckboxOptions().Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarCheckbox("Only enable while paused", CVAR_TRACKER_ITEM("ShowOnlyPaused"), + CheckboxOptions().Color(THEME_COLOR))) { shouldUpdateVectors = true; } - if (CVarCombobox("Combo Button 2", CVAR_TRACKER_ITEM("ComboButton2"), buttons, + if (CVarCombobox("Display Mode", CVAR_TRACKER_ITEM("DisplayType.Main"), displayModes, ComboboxOptions() - .DefaultIndex(TRACKER_COMBO_BUTTON_R) + .DefaultIndex(TRACKER_DISPLAY_ALWAYS) .ComponentAlignment(ComponentAlignments::Right) .LabelPosition(LabelPositions::Far) .Color(THEME_COLOR))) { shouldUpdateVectors = true; } + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == + TRACKER_DISPLAY_COMBO_BUTTON) { + if (CVarCombobox("Combo Button 1", CVAR_TRACKER_ITEM("ComboButton1"), buttons, + ComboboxOptions() + .DefaultIndex(TRACKER_COMBO_BUTTON_L) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarCombobox("Combo Button 2", CVAR_TRACKER_ITEM("ComboButton2"), buttons, + ComboboxOptions() + .DefaultIndex(TRACKER_COMBO_BUTTON_R) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + } } - } - ImGui::Separator(); - CVarSliderInt("Icon size : %dpx", CVAR_TRACKER_ITEM("IconSize"), - IntSliderOptions().Min(25).Max(128).DefaultValue(36).Color(THEME_COLOR)); - CVarSliderInt("Icon margins : %dpx", CVAR_TRACKER_ITEM("IconSpacing"), - IntSliderOptions().Min(-5).Max(50).DefaultValue(12).Color(THEME_COLOR)); - CVarSliderInt("Text size : %dpx", CVAR_TRACKER_ITEM("TextSize"), - IntSliderOptions().Min(1).Max(30).DefaultValue(13).Color(THEME_COLOR)); - - ImGui::NewLine(); - CVarCombobox("Ammo/Capacity Tracking", CVAR_TRACKER_ITEM("ItemCountType"), itemTrackerCapacityTrackOptions, - ComboboxOptions() - .DefaultIndex(ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) - .ComponentAlignment(ComponentAlignments::Left) - .LabelPosition(LabelPositions::Above) - .Color(THEME_COLOR) - .Tooltip("Customize what the numbers under each item are tracking." - "\n\nNote: items without capacity upgrades will track ammo even in capacity mode")); - if (CVarGetInteger(CVAR_TRACKER_ITEM("ItemCountType"), ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) == - ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY || - CVarGetInteger(CVAR_TRACKER_ITEM("ItemCountType"), ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) == - ITEM_TRACKER_NUMBER_CURRENT_AMMO_ONLY) { - if (CVarCheckbox("Align count to left side", CVAR_TRACKER_ITEM("ItemCountAlignLeft"), - CheckboxOptions().Color(THEME_COLOR))) { - shouldUpdateVectors = true; + ImGui::Separator(); + CVarSliderInt("Icon size : %dpx", CVAR_TRACKER_ITEM("IconSize"), + IntSliderOptions().Min(25).Max(128).DefaultValue(36).Color(THEME_COLOR)); + CVarSliderInt("Icon margins : %dpx", CVAR_TRACKER_ITEM("IconSpacing"), + IntSliderOptions().Min(-5).Max(50).DefaultValue(12).Color(THEME_COLOR)); + CVarSliderInt("Text size : %dpx", CVAR_TRACKER_ITEM("TextSize"), + IntSliderOptions().Min(1).Max(30).DefaultValue(13).Color(THEME_COLOR)); + + ImGui::NewLine(); + CVarCombobox("Ammo/Capacity Tracking", CVAR_TRACKER_ITEM("ItemCountType"), itemTrackerCapacityTrackOptions, + ComboboxOptions() + .DefaultIndex(ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) + .ComponentAlignment(ComponentAlignments::Left) + .LabelPosition(LabelPositions::Above) + .Color(THEME_COLOR) + .Tooltip("Customize what the numbers under each item are tracking." + "\n\nNote: items without capacity upgrades will track ammo even in capacity mode")); + if (CVarGetInteger(CVAR_TRACKER_ITEM("ItemCountType"), ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) == + ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY || + CVarGetInteger(CVAR_TRACKER_ITEM("ItemCountType"), ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY) == + ITEM_TRACKER_NUMBER_CURRENT_AMMO_ONLY) { + if (CVarCheckbox("Align count to left side", CVAR_TRACKER_ITEM("ItemCountAlignLeft"), + CheckboxOptions().Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } } - } - CVarCombobox("Key Count Tracking", CVAR_TRACKER_ITEM("KeyCounts"), itemTrackerKeyTrackOptions, - ComboboxOptions() - .DefaultIndex(KEYS_COLLECTED_MAX) - .ComponentAlignment(ComponentAlignments::Left) - .LabelPosition(LabelPositions::Above) - .Color(THEME_COLOR) - .Tooltip("Customize what numbers are shown for key tracking.")); - - CVarCombobox("Triforce Piece Count Tracking", CVAR_TRACKER_ITEM("TriforcePieceCounts"), - itemTrackerTriforcePieceTrackOptions, - ComboboxOptions() - .DefaultIndex(TRIFORCE_PIECE_COLLECTED_REQUIRED_MAX) - .ComponentAlignment(ComponentAlignments::Left) - .LabelPosition(LabelPositions::Above) - .Color(THEME_COLOR) - .Tooltip("Customize what numbers are shown for triforce piece tracking.")); - - ImGui::TableNextColumn(); - - if (CVarCombobox("Inventory", CVAR_TRACKER_ITEM("DisplayType.Inventory"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCombobox("Equipment", CVAR_TRACKER_ITEM("DisplayType.Equipment"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCombobox("Misc", CVAR_TRACKER_ITEM("DisplayType.Misc"), displayTypes, + CVarCombobox("Key Count Tracking", CVAR_TRACKER_ITEM("KeyCounts"), itemTrackerKeyTrackOptions, ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCombobox("Dungeon Rewards", CVAR_TRACKER_ITEM("DisplayType.DungeonRewards"), displayTypes, + .DefaultIndex(KEYS_COLLECTED_MAX) + .ComponentAlignment(ComponentAlignments::Left) + .LabelPosition(LabelPositions::Above) + .Color(THEME_COLOR) + .Tooltip("Customize what numbers are shown for key tracking.")); + + CVarCombobox("Triforce Piece Count Tracking", CVAR_TRACKER_ITEM("TriforcePieceCounts"), + itemTrackerTriforcePieceTrackOptions, ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonRewards"), SECTION_DISPLAY_MAIN_WINDOW) == - SECTION_DISPLAY_SEPARATE) { - if (CVarCheckbox("Circle display", CVAR_TRACKER_ITEM("DungeonRewardsLayout"), - CheckboxOptions().DefaultValue(false).Color(THEME_COLOR))) { + .DefaultIndex(TRIFORCE_PIECE_COLLECTED_REQUIRED_MAX) + .ComponentAlignment(ComponentAlignments::Left) + .LabelPosition(LabelPositions::Above) + .Color(THEME_COLOR) + .Tooltip("Customize what numbers are shown for triforce piece tracking.")); + + ImGui::TableNextColumn(); + + if (CVarCombobox("Inventory", CVAR_TRACKER_ITEM("DisplayType.Inventory"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { shouldUpdateVectors = true; } - } - if (CVarCombobox("Songs", CVAR_TRACKER_ITEM("DisplayType.Songs"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCombobox("Dungeon Items", CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), SECTION_DISPLAY_HIDDEN) != - SECTION_DISPLAY_HIDDEN) { - if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), SECTION_DISPLAY_HIDDEN) == + if (CVarCombobox("Equipment", CVAR_TRACKER_ITEM("DisplayType.Equipment"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarCombobox("Misc", CVAR_TRACKER_ITEM("DisplayType.Misc"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarCombobox("Dungeon Rewards", CVAR_TRACKER_ITEM("DisplayType.DungeonRewards"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonRewards"), SECTION_DISPLAY_MAIN_WINDOW) == SECTION_DISPLAY_SEPARATE) { - if (CVarCheckbox("Horizontal display", CVAR_TRACKER_ITEM("DungeonItems.Layout"), + if (CVarCheckbox("Circle display", CVAR_TRACKER_ITEM("DungeonRewardsLayout"), + CheckboxOptions().DefaultValue(false).Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + } + if (CVarCombobox("Songs", CVAR_TRACKER_ITEM("DisplayType.Songs"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_MAIN_WINDOW) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarCombobox("Dungeon Items", CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), SECTION_DISPLAY_HIDDEN) != + SECTION_DISPLAY_HIDDEN) { + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.DungeonItems"), SECTION_DISPLAY_HIDDEN) == + SECTION_DISPLAY_SEPARATE) { + if (CVarCheckbox("Horizontal display", CVAR_TRACKER_ITEM("DungeonItems.Layout"), + CheckboxOptions().DefaultValue(true).Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + } + if (CVarCheckbox("Maps and compasses", CVAR_TRACKER_ITEM("DungeonItems.DisplayMaps"), CheckboxOptions().DefaultValue(true).Color(THEME_COLOR))) { shouldUpdateVectors = true; } } - if (CVarCheckbox("Maps and compasses", CVAR_TRACKER_ITEM("DungeonItems.DisplayMaps"), - CheckboxOptions().DefaultValue(true).Color(THEME_COLOR))) { + if (CVarCombobox("Greg", CVAR_TRACKER_ITEM("DisplayType.Greg"), extendedDisplayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_EXTENDED_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { shouldUpdateVectors = true; } - } - if (CVarCombobox("Greg", CVAR_TRACKER_ITEM("DisplayType.Greg"), extendedDisplayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_EXTENDED_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - if (CVarCombobox("Triforce Pieces", CVAR_TRACKER_ITEM("DisplayType.TriforcePieces"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } - - if (CVarCombobox("Boss Souls", CVAR_TRACKER_ITEM("DisplayType.BossSouls"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } + if (CVarCombobox("Triforce Pieces", CVAR_TRACKER_ITEM("DisplayType.TriforcePieces"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } - if (CVarCombobox("Ocarina Buttons", CVAR_TRACKER_ITEM("DisplayType.OcarinaButtons"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } + if (CVarCombobox("Boss Souls", CVAR_TRACKER_ITEM("DisplayType.BossSouls"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } - if (CVarCombobox("Overworld Keys", CVAR_TRACKER_ITEM("DisplayType.OverworldKeys"), displayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } + if (CVarCombobox("Ocarina Buttons", CVAR_TRACKER_ITEM("DisplayType.OcarinaButtons"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } - if (CVarCombobox("Fishing Pole", CVAR_TRACKER_ITEM("DisplayType.FishingPole"), extendedDisplayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_EXTENDED_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } + if (CVarCombobox("Overworld Keys", CVAR_TRACKER_ITEM("DisplayType.OverworldKeys"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } - if (CVarCombobox("Total Checks", "gTrackers.ItemTracker.TotalChecks.DisplayType", minimalDisplayTypes, - ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_MINIMAL_HIDDEN) - .ComponentAlignment(ComponentAlignments::Right) - .LabelPosition(LabelPositions::Far) - .Color(THEME_COLOR))) { - shouldUpdateVectors = true; - } + if (CVarCombobox("Fishing Pole", CVAR_TRACKER_ITEM("DisplayType.FishingPole"), extendedDisplayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_EXTENDED_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } - if (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_WINDOW || - (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING && - CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) != - TRACKER_DISPLAY_COMBO_BUTTON)) { - if (CVarCombobox("Personal notes", CVAR_TRACKER_ITEM("DisplayType.Notes"), displayTypes, + if (CVarCombobox("Total Checks", "gTrackers.ItemTracker.TotalChecks.DisplayType", minimalDisplayTypes, ComboboxOptions() - .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .DefaultIndex(SECTION_DISPLAY_MINIMAL_HIDDEN) .ComponentAlignment(ComponentAlignments::Right) .LabelPosition(LabelPositions::Far) .Color(THEME_COLOR))) { shouldUpdateVectors = true; } - } - CVarCheckbox("Show Hookshot Identifiers", CVAR_TRACKER_ITEM("HookshotIdentifier"), - CheckboxOptions() - .Tooltip("Shows an 'H' or an 'L' to more easiely distinguish between Hookshot and Longshot.") - .Color(THEME_COLOR)); - ImGui::PopStyleVar(1); - ImGui::EndTable(); + if (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_WINDOW || + (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING && + CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) != + TRACKER_DISPLAY_COMBO_BUTTON)) { + if (CVarCombobox("Personal notes", CVAR_TRACKER_ITEM("DisplayType.Notes"), displayTypes, + ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR))) { + shouldUpdateVectors = true; + } + } + CVarCheckbox("Show Hookshot Identifiers", CVAR_TRACKER_ITEM("HookshotIdentifier"), + CheckboxOptions() + .Tooltip("Shows an 'H' or an 'L' to more easiely distinguish between Hookshot and Longshot.") + .Color(THEME_COLOR)); + + ImGui::PopStyleVar(1); + ImGui::EndTable(); + } } void ItemTrackerWindow::InitElement() { From 27f2292f9d1c3342f2afbafacfe1761b13cfdfa9 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Tue, 9 Sep 2025 04:44:36 +0100 Subject: [PATCH 148/157] Fix gerudo card generation failures (#5774) * quick fix gerudo card * submodules pls --- soh/soh/Enhancements/randomizer/3drando/item_pool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index fb2ac94cc1f..2a2c294a5b1 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -861,7 +861,7 @@ void GenerateItemPool() { AddItemToMainPool(RG_GERUDO_MEMBERSHIP_CARD); ctx->possibleIceTrapModels.push_back(RG_GERUDO_MEMBERSHIP_CARD); } else if (ctx->GetOption(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD)) { - AddItemToPool(PendingJunkPool, RG_GERUDO_MEMBERSHIP_CARD); + AddItemToPool(ItemPool, RG_GERUDO_MEMBERSHIP_CARD); ctx->PlaceItemInLocation(RC_GF_GERUDO_MEMBERSHIP_CARD, RG_ICE_TRAP, false, true); } else { ctx->PlaceItemInLocation(RC_GF_GERUDO_MEMBERSHIP_CARD, RG_GERUDO_MEMBERSHIP_CARD, false, true); From ac93d7cc151e2e7c53b312b53e3938ecd9a014b8 Mon Sep 17 00:00:00 2001 From: Eric Hoey <121978037+A-Green-Spoon@users.noreply.github.com> Date: Mon, 8 Sep 2025 23:44:45 -0400 Subject: [PATCH 149/157] Add additional glitch-aiding cutscenes for MQ (#5769) * add mq jabu chest + mq spirit switch cs * update tooltip --- soh/soh/Enhancements/timesaver_hook_handlers.cpp | 14 ++++++++++++-- soh/soh/SohGui/SohMenuEnhancements.cpp | 9 +++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 4af24ad19b4..90b3ff0846d 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -301,7 +301,8 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li switch (actor->id) { case ACTOR_OBJ_SWITCH: { if (((actor->params == 8224 && gPlayState->sceneNum == SCENE_DODONGOS_CAVERN) || - (actor->params == 6979 && gPlayState->sceneNum == SCENE_WATER_TEMPLE)) && + (actor->params == 6979 && gPlayState->sceneNum == SCENE_WATER_TEMPLE) || + (actor->params == 8961 && gPlayState->sceneNum == SCENE_SPIRIT_TEMPLE)) && CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) { break; } @@ -340,6 +341,16 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li *should = false; break; } + case ACTOR_EN_BOX: { + if (actor->params == -30457 && gPlayState->sceneNum == SCENE_JABU_JABU && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) { + break; + } + EnBox* boxActor = (EnBox*)actor; + *should = false; + RateLimitedSuccessChime(); + break; + } case ACTOR_BG_HIDAN_FWBIG: case ACTOR_EN_EX_ITEM: case ACTOR_EN_DNT_NOMAL: @@ -352,7 +363,6 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li case ACTOR_DOOR_SHUTTER: case ACTOR_BG_ICE_SHUTTER: case ACTOR_OBJ_LIGHTSWITCH: - case ACTOR_EN_BOX: case ACTOR_OBJ_SYOKUDAI: case ACTOR_OBJ_TIMEBLOCK: case ACTOR_EN_PO_SISTERS: diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 41cbbd90430..4499782403d 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -347,10 +347,11 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().DefaultValue(IS_RANDO)); AddWidget(path, "Exclude Glitch-Aiding Cutscenes", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding")) - .Options(CheckboxOptions().Tooltip( - "Don't skip cutscenes that are associated with useful glitches. Currently, it is " - "only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS, Dodongo Boss " - "Door Switch CS, Water Temple Dragon Switch CS, and the Box Skip One Point in Jabu.")); + .Options( + CheckboxOptions().Tooltip("Don't skip cutscenes that are associated with useful glitches. Currently, it is " + "only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS, Dodongo Boss " + "Door Switch CS, Water Temple Dragon Switch CS, the Box Skip One Point in Jabu, " + "Early Hammer Switch CS in MQ Spirit, and Cow Switch Chest CS in MQ Jabu.")); AddWidget(path, "Text", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Skip Bottle Pickup Messages", WIDGET_CVAR_CHECKBOX) From 1240eb3ae6d27a8b0dce91f6c164b02d884a01b3 Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 4 Apr 2025 03:21:08 -0600 Subject: [PATCH 150/157] Implemented Leveled in to Blair Alfa. Changed to new UI for configuration. --- soh/include/functions.h | 8 +- soh/include/global.h | 3 + soh/include/leveled_actor_level_table.h | 6 + soh/include/leveled_overlays.h | 15 + soh/include/leveled_stat_math.h | 34 + soh/include/z64actor.h | 19 +- soh/include/z64player.h | 2 +- soh/include/z64save.h | 7 +- soh/soh/Enhancements/debugger/actorViewer.cpp | 6 +- .../Enhancements/debugger/debugSaveEditor.cpp | 17 +- .../game-interactor/GameInteractionEffect.cpp | 2 +- .../GameInteractor_RawAction.cpp | 8 +- soh/soh/Enhancements/gameplaystats.cpp | 2 + soh/soh/Enhancements/mods.cpp | 10 +- .../Enhancements/randomizer/randomizer.cpp | 6 +- .../Enhancements/timesaver_hook_handlers.cpp | 4 +- soh/soh/OTRGlobals.cpp | 44 + soh/soh/OTRGlobals.h | 1 + soh/soh/SaveManager.cpp | 22 +- soh/soh/SaveManager.h | 1 + soh/soh/SohGui/SohMenu.cpp | 1 + soh/soh/SohGui/SohMenu.h | 1 + soh/soh/SohGui/SohMenuLeveled.cpp | 159 +++ soh/src/code/leveled_actor_level_table.c | 970 ++++++++++++++++++ soh/src/code/leveled_map_levels.c | 181 ++++ soh/src/code/leveled_overlays.c | 776 ++++++++++++++ soh/src/code/leveled_stat_math.c | 267 +++++ soh/src/code/z_actor.c | 57 +- soh/src/code/z_collision_check.c | 13 + soh/src/code/z_en_item00.c | 2 +- soh/src/code/z_lifemeter.c | 25 +- soh/src/code/z_message_PAL.c | 17 +- soh/src/code/z_parameter.c | 101 +- soh/src/code/z_player_lib.c | 52 + soh/src/code/z_sram.c | 5 +- .../ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c | 10 +- .../actors/ovl_Boss_Dodongo/z_boss_dodongo.c | 11 +- .../overlays/actors/ovl_Boss_Fd/z_boss_fd.c | 15 +- .../overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c | 54 +- .../actors/ovl_Boss_Ganon/z_boss_ganon.c | 29 +- .../actors/ovl_Boss_Ganon2/z_boss_ganon2.c | 33 +- .../ovl_Boss_Ganondrof/z_boss_ganondrof.c | 37 +- .../actors/ovl_Boss_Goma/z_boss_goma.c | 11 +- .../overlays/actors/ovl_Boss_Mo/z_boss_mo.c | 13 +- .../overlays/actors/ovl_Boss_Sst/z_boss_sst.c | 5 +- .../overlays/actors/ovl_Boss_Tw/z_boss_tw.c | 19 +- .../overlays/actors/ovl_Boss_Va/z_boss_va.c | 18 +- soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c | 10 +- soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c | 10 +- .../actors/ovl_En_Bdfire/z_en_bdfire.c | 3 +- .../actors/ovl_En_Bigokuta/z_en_bigokuta.c | 1 + .../overlays/actors/ovl_En_Bili/z_en_bili.c | 14 +- .../ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c | 2 +- .../actors/ovl_En_Bubble/z_en_bubble.c | 2 +- soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c | 2 + .../actors/ovl_En_Clear_Tag/z_en_clear_tag.c | 13 +- .../overlays/actors/ovl_En_Crow/z_en_crow.c | 17 +- .../actors/ovl_En_Dekubaba/z_en_dekubaba.c | 1 + .../actors/ovl_En_Dekunuts/z_en_dekunuts.c | 6 +- soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c | 7 +- .../actors/ovl_En_Dodongo/z_en_dodongo.c | 1 + soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c | 2 +- .../actors/ovl_En_Firefly/z_en_firefly.c | 45 +- .../actors/ovl_En_Floormas/z_en_floormas.c | 8 +- soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c | 9 +- soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c | 11 +- .../overlays/actors/ovl_En_GirlA/z_en_girla.c | 2 +- .../overlays/actors/ovl_En_Goma/z_en_goma.c | 17 +- soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c | 15 +- soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c | 4 + .../actors/ovl_En_Peehat/z_en_peehat.c | 1 + soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c | 8 +- soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c | 3 + soh/src/overlays/actors/ovl_En_Si/z_en_si.c | 2 + soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c | 1 + soh/src/overlays/actors/ovl_En_St/z_en_st.c | 3 +- .../overlays/actors/ovl_En_Test/z_en_test.c | 4 +- .../overlays/actors/ovl_En_Tite/z_en_tite.c | 2 + soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c | 1 + soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c | 6 +- soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c | 1 + soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c | 4 +- soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c | 3 +- .../actors/ovl_player_actor/z_player.c | 28 +- .../ovl_file_choose/z_file_choose.c | 58 +- .../misc/ovl_kaleido_scope/z_kaleido_debug.c | 2 + .../ovl_kaleido_scope/z_kaleido_equipment.c | 6 + .../ovl_kaleido_scope/z_kaleido_scope_PAL.c | 2 +- 88 files changed, 3211 insertions(+), 225 deletions(-) create mode 100644 soh/include/leveled_actor_level_table.h create mode 100644 soh/include/leveled_overlays.h create mode 100644 soh/include/leveled_stat_math.h create mode 100644 soh/soh/SohGui/SohMenuLeveled.cpp create mode 100644 soh/src/code/leveled_actor_level_table.c create mode 100644 soh/src/code/leveled_map_levels.c create mode 100644 soh/src/code/leveled_overlays.c create mode 100644 soh/src/code/leveled_stat_math.c diff --git a/soh/include/functions.h b/soh/include/functions.h index f29153a9ce5..971ae5f66b5 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -27,6 +27,12 @@ void gSPDisplayListOffset(Gfx* pkt, Gfx* dl, int offset); void gSPVertex(Gfx* pkt, uintptr_t v, int n, int v0); void gSPInvalidateTexCache(Gfx* pkt, uintptr_t texAddr); +void Actor_RefreshLeveledStats(Actor* actor, Player* player); +Gfx* Gfx_TextureIA8(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy); +Gfx* Gfx_TextureI8(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy); +void Player_GainExperience(PlayState* play, u16 experience); void cleararena(void); void bootproc(void); @@ -548,7 +554,7 @@ s32 func_800354B4(PlayState* play, Actor* actor, f32 range, s16 arg3, s16 arg4, void func_8003555C(PlayState* play, Vec3f* pos, Vec3f* velocity, Vec3f* accel); void func_800355B8(PlayState* play, Vec3f* pos); u8 func_800355E4(PlayState* play, Collider* collider); -u8 Actor_ApplyDamage(Actor* actor); +u16 Actor_ApplyDamage(Actor* actor); void Actor_SetDropFlag(Actor* actor, ColliderInfo* colBody, s32 freezeFlag); void Actor_SetDropFlagJntSph(Actor* actor, ColliderJntSph* colBody, s32 freezeFlag); void func_80035844(Vec3f* arg0, Vec3f* arg1, Vec3s* arg2, s32 arg3); diff --git a/soh/include/global.h b/soh/include/global.h index b453439fd3f..8a7e92529ad 100644 --- a/soh/include/global.h +++ b/soh/include/global.h @@ -8,6 +8,9 @@ #include "functions.h" #include "variables.h" #include "macros.h" +#include "leveled_overlays.h" +#include "leveled_stat_math.h" +#include "leveled_actor_level_table.h" #include "soh/cvar_prefixes.h" #include "soh/Enhancements/gameconsole.h" #include "soh/Enhancements/gameplaystats.h" diff --git a/soh/include/leveled_actor_level_table.h b/soh/include/leveled_actor_level_table.h new file mode 100644 index 00000000000..7472b50c06c --- /dev/null +++ b/soh/include/leveled_actor_level_table.h @@ -0,0 +1,6 @@ +#ifndef LEVELED_ACTOR_LEVEL_TABLE_H +#define LEVELED_ACTOR_LEVEL_TABLE_H +#include "z64.h" +#endif + +void Actor_GetLevelAndExperience(PlayState* play, Actor* actor, u16 actorIdOverride); \ No newline at end of file diff --git a/soh/include/leveled_overlays.h b/soh/include/leveled_overlays.h new file mode 100644 index 00000000000..26f01abe4fc --- /dev/null +++ b/soh/include/leveled_overlays.h @@ -0,0 +1,15 @@ +#ifndef LEVELED_OVERLAYS_H +#define LEVELED_OVERLAYS_H +#include "z64.h" +#endif + +void ActorDamageNumber_New(Actor* actor, u16 damage); +void ActorExperienceNumber_New(Actor* actor, u16 experience); +void ActorLevelUp_New(Actor* actor, u8 powerDiff, u8 courageDiff, u16 healthDiff, u8 magicDiff); +void ActorDamageNumber_Draw(PlayState* play, Actor* actor); +void ActorExperienceNumber_Draw(PlayState* play, Actor* actor); +void Actor_LevelUpDraw(PlayState* play, Actor* actor); +void Leveled_ValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b); +void Leveled_BigValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b, u8 a); +void Leveled_KaleidoEquip_Stats(PlayState* play); +void Leveled_Interface_DrawNextLevel(PlayState* play); \ No newline at end of file diff --git a/soh/include/leveled_stat_math.h b/soh/include/leveled_stat_math.h new file mode 100644 index 00000000000..40dfa0c0326 --- /dev/null +++ b/soh/include/leveled_stat_math.h @@ -0,0 +1,34 @@ +#ifndef LEVELED_STAT_MATH_H +#define LEVELED_STAT_MATH_H +#include "z64.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +u16 GetActorStat_DisplayAttack(u16 attack, u8 power); +u16 GetActorStat_Attack(u16 attack, u8 power); +u8 GetActorStat_Power(u8 level); +u8 GetActorStat_Courage(u8 level); +u8 GetActorStat_PlayerPower(u8 level); +u8 GetActorStat_PlayerCourage(u8 level); +u16 GetActorStat_EnemyMaxHealth(u16 baseHealth, u8 level); +u8 GetPlayerStat_BonusHearts(u8 level); +u8 GetPlayerStat_MagicUnits(u8 level); +u16 GetPlayerStat_GetModifiedHealthCapacity(u16 baseHealth, u8 level); +u16 GetPlayerStat_NextLevelExpAtLevel(u8 level); +u16 GetActorStat_NextLevelExp(u8 level, u32 currentExp); +u16 GetEnemyExperienceReward(u8 level, u16 expRate); +f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage); +f32 Leveled_DamageModify(Actor * actor, Actor * attackingActor, f32 attack); +u16 Leveled_GoldSkulltulaExperience(u8 tokens); +void Leveled_SetPlayerModifiedStats(Player * player); +u8 Leveled_GetHealthAttackMultiplier(); +s8 Leveled_GetSceneLevel(s16 sceneId); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/soh/include/z64actor.h b/soh/include/z64actor.h index 6d8fd3ca07f..2c7922ee674 100644 --- a/soh/include/z64actor.h +++ b/soh/include/z64actor.h @@ -55,14 +55,14 @@ typedef struct { } DamageTable; typedef struct { - /* 0x00 */ u8 health; + /* 0x00 */ u16 health; /* 0x02 */ s16 cylRadius; /* 0x04 */ s16 cylHeight; /* 0x06 */ u8 mass; } CollisionCheckInfoInit; typedef struct { - /* 0x00 */ u8 health; + /* 0x00 */ u16 health; /* 0x02 */ s16 cylRadius; /* 0x04 */ s16 cylHeight; /* 0x06 */ s16 cylYShift; @@ -76,8 +76,8 @@ typedef struct { /* 0x12 */ s16 cylHeight; // Used for various purposes /* 0x14 */ s16 cylYShift; // Unused. Purpose inferred from Cylinder16 and CollisionCheck_CylSideVsLineSeg /* 0x16 */ u8 mass; // Used to compute displacement for OC collisions - /* 0x17 */ u8 health; // Note: some actors may use their own health variable instead of this one - /* 0x18 */ u8 damage; // Amount to decrement health by + /* 0x17 */ u16 health; // Note: some actors may use their own health variable instead of this one + /* 0x18 */ u16 damage; // Amount to decrement health by /* 0x19 */ u8 damageEffect; // Stores what effect should occur when hit by a weapon /* 0x1A */ u8 atHitEffect; // Stores what effect should occur when AT connects with an AC /* 0x1B */ u8 acHitEffect; // Stores what effect should occur when AC is touched by an AT @@ -269,6 +269,17 @@ typedef struct Actor { // #region SOH [General] /* */ u8 maximumHealth; // Max health value for use with health bars, set on actor init // #endregion + u16 exp; // Experience + u8 level; // Actor Level + u8 power; // e.g. Strength + u8 courage; // e.g. Defense + s8 powerModifier; // Modifies Power + s8 courageModifier; // Modifies Courage + u16 floatingNumber[7]; + u8 floatingNumberLife[7]; + Vec2f floatingNumberPosition[7]; + Vec2f floatingNumberVelocity[7]; + bool ignoreExpReward; // Actor handles exp reward differently } Actor; // size = 0x14C typedef enum { diff --git a/soh/include/z64player.h b/soh/include/z64player.h index a00d86557c7..992fe5a9611 100644 --- a/soh/include/z64player.h +++ b/soh/include/z64player.h @@ -909,7 +909,7 @@ typedef struct Player { /* 0x089A */ s16 floorPitchAlt; // the calculation for this value is bugged and doesn't represent anything meaningful /* 0x089C */ s16 unk_89C; /* 0x089E */ u16 floorSfxOffset; - /* 0x08A0 */ u8 knockbackDamage; + /* 0x08A0 */ u16 knockbackDamage; /* 0x08A1 */ u8 knockbackType; /* 0x08A2 */ s16 knockbackRot; /* 0x08A4 */ f32 knockbackSpeed; diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 751ad0b0f98..a450b0cb23e 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -208,7 +208,7 @@ typedef struct { /* 0x002E */ s16 healthCapacity; // "max_life" /* 0x0030 */ s16 health; // "now_life" /* 0x0032 */ s8 magicLevel; // 0 for no magic/new load, 1 for magic, 2 for double magic - /* 0x0033 */ s8 magic; // current magic available for use + /* 0x0033 */ u8 magic; // current magic available for use /* 0x0034 */ s16 rupees; /* 0x0036 */ u16 swordHealth; /* 0x0038 */ u16 naviTimer; @@ -307,6 +307,11 @@ typedef struct { /* 0x1422 */ s16 sunsSongState; // controls the effects of suns song /* 0x1424 */ s16 healthAccumulator; /* */ ShipSaveContextData ship; + u32 experience; + s16 heartContainers; + s16 healthCapacity2; // Modified max health + u8 magicUnits; // Modified magic units per magic level + s16 showNeededExpTimer; // Shows EXP required in the hud when greater than 0 } SaveContext; // size = 0x1428 typedef enum { diff --git a/soh/soh/Enhancements/debugger/actorViewer.cpp b/soh/soh/Enhancements/debugger/actorViewer.cpp index 515da6491ac..1acf780647d 100644 --- a/soh/soh/Enhancements/debugger/actorViewer.cpp +++ b/soh/soh/Enhancements/debugger/actorViewer.cpp @@ -923,6 +923,10 @@ void ActorViewerWindow::DrawElement() { ImGui::Text("Name: %s", ActorDB::Instance->RetrieveEntry(display->id).name.c_str()); ImGui::Text("Description: %s", GetActorDescription(display->id).c_str()); ImGui::Text("Category: %s", acMapping[display->category]); + if (display->category == ACTORCAT_BOSS || display->category == ACTORCAT_ENEMY) { + ImGui::Text("Level: %d", display->level); + ImGui::Text("EXP: %d", display->exp); + } ImGui::Text("ID: %d", display->id); ImGui::Text("Parameters: %d", display->params); }, @@ -958,7 +962,7 @@ void ActorViewerWindow::DrawElement() { if (display->category == ACTORCAT_BOSS || display->category == ACTORCAT_ENEMY) { PushStyleInput(THEME_COLOR); - ImGui::InputScalar("Enemy Health", ImGuiDataType_U8, &display->colChkInfo.health); + ImGui::InputScalar("Enemy Health", ImGuiDataType_U16, &display->colChkInfo.health); PopStyleInput(); UIWidgets::InsertHelpHoverText("Some actors might not use this!"); } diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index 499e8d1addc..2d3185f4687 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -20,6 +20,7 @@ extern "C" { #include "functions.h" #include "macros.h" #include "soh/cvar_prefixes.h" +#include "leveled_stat_math.h" extern PlayState* gPlayState; #include "textures/icon_item_static/icon_item_static.h" @@ -171,16 +172,22 @@ void DrawInfoTab() { PopStyleInput(); if (ImGui::IsItemDeactivated()) { gSaveContext.healthCapacity = healthIntermediary; + + Player* player = GET_PLAYER(gPlayState); + gSaveContext.healthCapacity2 = + GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, player->actor.level); + if (gSaveContext.health > gSaveContext.healthCapacity2) + gSaveContext.health = gSaveContext.healthCapacity2; } Tooltip("Maximum health. 16 units per full heart"); - if (gSaveContext.health > gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; // Clamp health to new max + if (gSaveContext.health > gSaveContext.healthCapacity2) { + gSaveContext.health = gSaveContext.healthCapacity2; // Clamp health to new max } int32_t health = (int32_t)gSaveContext.health; if (SliderInt("Health", &health, intSliderOptionsBase.Tooltip("Current health. 16 units per full heart") .Min(0) - .Max(gSaveContext.healthCapacity))) { + .Max(gSaveContext.healthCapacity2))) { gSaveContext.health = (int16_t)health; } @@ -196,7 +203,7 @@ void DrawInfoTab() { gSaveContext.isMagicAcquired = gSaveContext.magicLevel > 0; gSaveContext.isDoubleMagicAcquired = gSaveContext.magicLevel == 2; } - gSaveContext.magicCapacity = gSaveContext.magicLevel * 0x30; // Set to get the bar drawn in the UI + gSaveContext.magicCapacity = gSaveContext.magicLevel * gSaveContext.magicUnits; // Set to get the bar drawn in the UI if (gSaveContext.magic > gSaveContext.magicCapacity) { gSaveContext.magic = gSaveContext.magicCapacity; // Clamp magic to new max } @@ -206,7 +213,7 @@ void DrawInfoTab() { intSliderOptionsBase.Min(0) .Max(gSaveContext.magicCapacity) .Tooltip("Current magic. 48 units per magic level"))) { - gSaveContext.magic = (int8_t)magic; + gSaveContext.magic = (uint8_t)magic; } PushStyleInput(THEME_COLOR); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp index 95455926b34..0abb8e803a3 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp @@ -189,7 +189,7 @@ void ModifyGravity::_Remove() { GameInteractionEffectQueryResult ModifyHealth::CanBeApplied() { if (!GameInteractor::IsSaveLoaded()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; - } else if ((parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity) || + } else if ((parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity2) || (parameters[0] < 0 && (gSaveContext.health + (16 * parameters[0]) <= 0))) { return GameInteractionEffectQueryResult::NotPossible; } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp index ac99859b54c..2798e374b14 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -23,7 +23,7 @@ void GameInteractor::RawAction::AddOrRemoveHealthContainers(int16_t amount) { void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { // Full single magic = 48 // Full double magic = 96 - int8_t currentMagicCapacity = (gSaveContext.isDoubleMagicAcquired + 1) * 48; + int8_t currentMagicCapacity = (gSaveContext.isDoubleMagicAcquired + 1) * gSaveContext.magicUnits; if (gSaveContext.isMagicAcquired) { gSaveContext.prevMagicState = gSaveContext.magicState; @@ -46,17 +46,17 @@ void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { void GameInteractor::RawAction::HealOrDamagePlayer(int16_t hearts) { if (hearts > 0) { - Health_ChangeBy(gPlayState, hearts * 0x10); + Health_ChangeBy(gPlayState, hearts * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2); } else if (hearts < 0) { Player* player = GET_PLAYER(gPlayState); - Health_ChangeBy(gPlayState, hearts * 0x10); + Health_ChangeBy(gPlayState, hearts * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2); func_80837C0C(gPlayState, player, 0, 0, 0, 0, 0); player->invincibilityTimer = 28; } } void GameInteractor::RawAction::SetPlayerHealth(int16_t hearts) { - gSaveContext.health = hearts * 0x10; + gSaveContext.health = hearts * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; } void GameInteractor::RawAction::SetLinkInvisibility(bool active) { diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp index ca360543fa4..c7cf007e357 100644 --- a/soh/soh/Enhancements/gameplaystats.cpp +++ b/soh/soh/Enhancements/gameplaystats.cpp @@ -445,6 +445,8 @@ void DrawGameplayStatsHeader() { ImGui::TableSetupColumn("stat", ImGuiTableColumnFlags_WidthStretch); // if tag is empty (not a release build) if (gGitCommitTag[0] == 0) { + GameplayStatsRow("Leveled Mod Version:", "1.1.3"); + GameplayStatsRow("A Mod By Gotest, AKA Arrenton", ""); GameplayStatsRow("Git Branch:", (char*)gGitBranch); GameplayStatsRow("Git Commit Hash:", (char*)gGitCommitHash); } else { diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 180ba4f5eaf..5561bfa4de7 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -211,8 +211,8 @@ void UpdatePermanentHeartLossState() { 16 * (IS_RANDO ? (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_HEARTS) + 1) : 3); uint8_t newCapacity = startingHealth + (heartContainers * 16) + ((heartPieces - (heartPieces % 4)) * 4); - gSaveContext.healthCapacity = MAX(newCapacity, gSaveContext.healthCapacity); - gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity); + gSaveContext.healthCapacity = MAX(newCapacity, gSaveContext.healthCapacity2); + gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity2); hasAffectedHealth = false; } } @@ -229,7 +229,8 @@ void RegisterPermanentHeartLoss() { if (gSaveContext.healthCapacity > 16 && gSaveContext.healthCapacity - gSaveContext.health >= 16) { gSaveContext.healthCapacity -= 16; - gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity); + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity2); hasAffectedHealth = true; } }); @@ -827,6 +828,7 @@ void UpdateHurtContainerModeState(bool newState) { } else { gSaveContext.healthCapacity = 48 + ((getHeartPieces + getHeartContainers) * 16); } + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); } void RegisterHurtContainerModeHandler() { @@ -879,9 +881,11 @@ void RegisterRandomizedEnemySizes() { // Scale the health based on a smaller factor than randomScale float healthScalingFactor = 0.8f; // Adjust this factor as needed float scaledHealth = actor->colChkInfo.health * (randomScale * healthScalingFactor); + float scaledExp = actor->exp * (randomScale * healthScalingFactor); // Ensure the scaled health doesn't go below zero actor->colChkInfo.health = fmax(scaledHealth, 1.0f); + actor->exp = fmax(scaledExp, 1.0f); } else { return; } diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index ac1716fc3a0..41e2b3fa228 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -5883,7 +5883,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { switch (item) { case RG_MAGIC_SINGLE: gSaveContext.isMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_NORMAL_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits; Magic_Fill(play); break; case RG_MAGIC_DOUBLE: @@ -5891,7 +5891,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { gSaveContext.isMagicAcquired = true; } gSaveContext.isDoubleMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_DOUBLE_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits * 2; gSaveContext.magicLevel = 0; Magic_Fill(play); break; @@ -5904,7 +5904,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { case RG_DOUBLE_DEFENSE: gSaveContext.isDoubleDefenseAcquired = true; gSaveContext.inventory.defenseHearts = 20; - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; break; case RG_TYCOON_WALLET: Inventory_ChangeUpgrade(UPG_WALLET, 3); diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 90b3ff0846d..ce8ee63d854 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -837,7 +837,8 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li EnIk* ik = va_arg(args, EnIk*); if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.QuickBossDeaths"), IS_RANDO)) { // Because no CS in rando, we hide the death of the knuckle by spawning flames and kill the actor - if ((ik->actor.colChkInfo.health <= 10)) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(10, ik->actor.level); + if ((ik->actor.colChkInfo.health <= healthCheck)) { s32 i; Vec3f pos; Vec3f sp7C = { 0.0f, 0.5f, 0.0f }; @@ -850,6 +851,7 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li EffectSsDeadDb_Spawn(gPlayState, &pos, &sp7C, &sp7C, 100, 0, 255, 255, 255, 255, 0, 0, 255, 1, 9, true); } + Player_GainExperience(gPlayState, ik->actor.exp); Actor_Kill(&ik->actor); } *should = false; diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index aace117a803..a88a0102c23 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2043,6 +2043,50 @@ extern "C" int GetEquipNowMessage(char* buffer, char* src, const int maxBufferSi return 0; } +extern "C" int GetLeveledNaviEnemyInfo(char* buffer, char* src, const int maxBufferSize, Actor* actor) { + std::string postfix; + + if (!actor) + return 0; + + if (gSaveContext.language == LANGUAGE_FRA) { + postfix = ""; + } else if (gSaveContext.language == LANGUAGE_GER) { + postfix = ""; + } else { + postfix = ""; + if (CVarGetInteger("gLeveled.Navi.TellEnemyLevel", 1)) { + postfix += " \x05" + "F" + "Lv" + + std::to_string(actor->level); + } + if (CVarGetInteger("gLeveled.Navi.TellEnemyMaxHP", 1) && actor->maximumHealth > 0) { + postfix += " \x05" + "A" + "MaxHP " + + std::to_string(actor->maximumHealth); + } + } + std::string str; + std::string FixedBaseStr(src); + int FoundControlChar = FixedBaseStr.find_first_of("\x01"); + + if (FoundControlChar != std::string::npos) { + FixedBaseStr = FixedBaseStr.insert(FoundControlChar, postfix); + } + + str = FixedBaseStr; + + if (!str.empty()) { + memset(buffer, 0, maxBufferSize); + const int copiedCharLen = std::min(maxBufferSize - 1, str.length()); + memcpy(buffer, str.c_str(), copiedCharLen); + return copiedCharLen; + } + return 0; +} + extern "C" void Randomizer_ParseSpoiler(const char* fileLoc) { OTRGlobals::Instance->gRandoContext->ParseSpoiler(fileLoc); } diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index bcc5c86575b..f233f139805 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -164,6 +164,7 @@ void Gfx_TextureCacheDelete(const uint8_t* addr); void SaveManager_ThreadPoolWait(); void CheckTracker_OnMessageClose(); void CheckTracker_RecalculateAvailableChecks(); +int GetLeveledNaviEnemyInfo(char* buffer, char* src, const int maxBufferSize, Actor* actor); GetItemID RetrieveGetItemIDFromItemID(ItemID itemID); RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID); diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index bdb2800e380..69eb18da97e 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -15,6 +15,7 @@ #include "functions.h" #include "macros.h" #include +#include "leveled_stat_math.h" #include #include "soh/SohGui/SohGui.hpp" @@ -483,6 +484,12 @@ void SaveManager::InitMeta(int fileNum) { fileMetaInfo[fileNum].hasWallet = Flags_GetRandomizerInf(RAND_INF_HAS_WALLET) || !IS_RANDO; fileMetaInfo[fileNum].defense = gSaveContext.inventory.defenseHearts; fileMetaInfo[fileNum].health = gSaveContext.health; + fileMetaInfo[fileNum].level = 0; + + while (GetActorStat_NextLevelExp(fileMetaInfo[fileNum].level, gSaveContext.experience) <= 0 && + fileMetaInfo[fileNum].level < 99) { + fileMetaInfo[fileNum].level += 1; + } auto randoContext = Rando::Context::GetInstance(); for (int i = 0; i < ARRAY_COUNT(fileMetaInfo[fileNum].seedHash); i++) { @@ -537,9 +544,13 @@ void SaveManager::InitFileNormal() { gSaveContext.ship.filenameLanguage = (gSaveContext.language == LANGUAGE_JPN) ? NAME_LANGUAGE_NTSC_JPN : NAME_LANGUAGE_NTSC_ENG; } + gSaveContext.healthCapacity2 = 9999; + gSaveContext.magicUnits = 9999; + gSaveContext.experience = 0; + gSaveContext.showNeededExpTimer = 0; gSaveContext.n64ddFlag = 0; gSaveContext.healthCapacity = 0x30; - gSaveContext.health = 0x30; + gSaveContext.health = 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; gSaveContext.magicLevel = 0; gSaveContext.magic = 0x30; gSaveContext.rupees = 0; @@ -707,9 +718,11 @@ void SaveManager::InitFileDebug() { gSaveContext.ship.filenameLanguage = (gSaveContext.language == LANGUAGE_JPN) ? NAME_LANGUAGE_NTSC_JPN : NAME_LANGUAGE_NTSC_ENG; } + gSaveContext.experience = 80000; gSaveContext.n64ddFlag = 0; gSaveContext.healthCapacity = 0xE0; - gSaveContext.health = 0xE0; + gSaveContext.healthCapacity2 = 0x270F; + gSaveContext.health = 0x270F; gSaveContext.magicLevel = 0; gSaveContext.magic = 0x30; gSaveContext.rupees = 150; @@ -1317,6 +1330,7 @@ void SaveManager::CreateDefaultGlobal() { } void SaveManager::LoadBaseVersion1() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -1461,6 +1475,7 @@ void SaveManager::LoadBaseVersion1() { } void SaveManager::LoadBaseVersion2() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -1677,6 +1692,7 @@ void SaveManager::LoadBaseVersion2() { } void SaveManager::LoadBaseVersion3() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -1897,6 +1913,7 @@ void SaveManager::LoadBaseVersion3() { } void SaveManager::LoadBaseVersion4() { + SaveManager::Instance->LoadData("experience", gSaveContext.experience); SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex); SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge); SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex); @@ -2079,6 +2096,7 @@ void SaveManager::LoadBaseVersion4() { } void SaveManager::SaveBase(SaveContext* saveContext, int sectionID, bool fullSave) { + SaveManager::Instance->SaveData("experience", saveContext->experience); SaveManager::Instance->SaveData("entranceIndex", saveContext->entranceIndex); SaveManager::Instance->SaveData("linkAge", saveContext->linkAge); SaveManager::Instance->SaveData("cutsceneIndex", saveContext->cutsceneIndex); diff --git a/soh/soh/SaveManager.h b/soh/soh/SaveManager.h index 6d7ef3b33a5..2a30deb4247 100644 --- a/soh/soh/SaveManager.h +++ b/soh/soh/SaveManager.h @@ -32,6 +32,7 @@ typedef struct { s32 filenameLanguage; s32 gregFound; s32 hasWallet; + u8 level; } SaveFileMetaInfo; typedef enum { diff --git a/soh/soh/SohGui/SohMenu.cpp b/soh/soh/SohGui/SohMenu.cpp index 61967087bfb..a7d6a82f5ba 100644 --- a/soh/soh/SohGui/SohMenu.cpp +++ b/soh/soh/SohGui/SohMenu.cpp @@ -85,6 +85,7 @@ void SohMenu::InitElement() { AddMenuSettings(); AddMenuEnhancements(); AddMenuRandomizer(); + AddMenuLeveled(); #ifdef ENABLE_REMOTE_CONTROL AddMenuNetwork(); #endif diff --git a/soh/soh/SohGui/SohMenu.h b/soh/soh/SohGui/SohMenu.h index eb3b1f4f7e3..4a530b153fd 100644 --- a/soh/soh/SohGui/SohMenu.h +++ b/soh/soh/SohGui/SohMenu.h @@ -205,6 +205,7 @@ class SohMenu : public Ship::Menu { void AddMenuEnhancements(); void AddMenuDevTools(); void AddMenuRandomizer(); + void AddMenuLeveled(); void AddMenuNetwork(); static void UpdateLanguageMap(std::unordered_map& languageMap); diff --git a/soh/soh/SohGui/SohMenuLeveled.cpp b/soh/soh/SohGui/SohMenuLeveled.cpp new file mode 100644 index 00000000000..08f7d3ac822 --- /dev/null +++ b/soh/soh/SohGui/SohMenuLeveled.cpp @@ -0,0 +1,159 @@ +#include "SohMenu.h" +#include "SohGui.hpp" +#include + +namespace SohGui { + + extern std::shared_ptr mSohMenu; + using namespace UIWidgets; + + void SohMenu::AddMenuLeveled() { + // Add Leveled Menu + AddMenuEntry("Leveled", CVAR_SETTING("Menu.LeveledSidebarSection")); + + // UI Settings + WidgetPath path = { "Leveled", "UI Settings", SECTION_COLUMN_1 }; + AddSidebarEntry("Leveled", path.sidebarName, 1); + + // HUD + AddWidget(path, "HUD", WIDGET_SEPARATOR_TEXT); + // EXP Next Level + AddWidget(path, "EXP to NEXT Level", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.ExperienceNextLevel") + .Options(CheckboxOptions() + .Tooltip("Show experience required to level up popup in the HUD when gaining EXP.") + .DefaultValue(true)); + // Level Up + AddWidget(path, "Level Up", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.LevelUp") + .Options(CheckboxOptions().Tooltip("Show Level Up popup when leveling up.").DefaultValue(true)); + // Level Up Sound + AddWidget(path, "Level Up Sound", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.LevelUpSound") + .Options(CheckboxOptions().Tooltip("Plays sound when leveling up.").DefaultValue(true)); + + // Floating Numbers + AddWidget(path, "Floating Numbers", WIDGET_SEPARATOR_TEXT); + // Enemy Damage + AddWidget(path, "Enemy Damage", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.FloatingNumbers.EnemyDamage") + .Options(CheckboxOptions().Tooltip("Show floating damage numbers on enemies.").DefaultValue(true)); + // Player Damage + AddWidget(path, "Player Damage", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.FloatingNumbers.PlayerDamage") + .Options(CheckboxOptions().Tooltip("Show floating damage numbers on link.").DefaultValue(true)); + // EXP Gain + AddWidget(path, "EXP Gain", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.HUD.FloatingNumbers.ExpGain") + .Options(CheckboxOptions().Tooltip("Show floating EXP gain numbers.").DefaultValue(true)); + + // Navi Info + AddWidget(path, "Navi Enemy Info", WIDGET_SEPARATOR_TEXT); + // Navi Level + AddWidget(path, "Navi tells enemy level", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Navi.TellEnemyLevel") + .Options(CheckboxOptions().Tooltip("Navi will tell the enemy level.").DefaultValue(true)); + // Navi HP + AddWidget(path, "Navi tells enemy max HP", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Navi.TellEnemyMaxHP") + .Options(CheckboxOptions().Tooltip("Navi will tell the enemy's maximum HP.").DefaultValue(true)); + + // Entity Modifications + path.sidebarName = "Entity Modifications"; + AddSidebarEntry("Leveled", path.sidebarName, 1); + // Enemy / Link HP Mult + AddWidget(path, "Enemy HP and Link's Attack Multiplier: %dx", WIDGET_CVAR_SLIDER_INT) + .CVar("gLeveled.Enhancements.AttackAndHPMultiplier") + .Options(IntSliderOptions().Min(1).Max(32).Format("%d").DefaultValue(9).Tooltip( + "Changes Link's Attack and enemy HP multiplier.\nThis doesn't change balance, but rather the size of " + "numbers.")); + AddWidget(path, "Player Modifications", WIDGET_SEPARATOR_TEXT); + // Level Increases Life + AddWidget(path, "Level Gives Bonus Hearts", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Player.Enhancements.HeartsWithLevelUp") + .Options( + CheckboxOptions().Tooltip("Levels give health upgrades, up to 10 extra hearts.").DefaultValue(true)); + // Level Modifies Magic + AddWidget(path, "Level Affects Magic Capacity", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Player.Enhancements.MagicWithLevelUp") + .Options(CheckboxOptions() + .Tooltip("Levels alter magic capacity. Starts at 25% of the vanilla value, but will go up to " + "150% of vanilla. Double magic works the same as the original, doubling capacity.") + .DefaultValue(true)); + // Equipment Stats + AddWidget(path, "Equipment Affects Stats", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Player.Enhancements.EquipmentStats") + .Options(CheckboxOptions() + .Tooltip("Equipment will alter stats. Such as Goron Tunic providing +3 STR, but gives -3 " + "Defense, Bracelets give +1 STR, Shields, etc.") + .DefaultValue(true)); + AddWidget(path, "Enemy Modifications", WIDGET_SEPARATOR_TEXT); + // Enemy Level Affects Attack + AddWidget(path, "Enemy Level Affects Base Attack", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Enemy.Enhancements.AttackScalesWithLevel") + .Options( + CheckboxOptions() + .Tooltip("Enemies have a fixed attack value. This option scales this up the higher the enemy's " + "Strength stat. \nThis will increase difficulty a bit.") + .DefaultValue(true)); + + // Difficulty Options + path.sidebarName = "Difficulty Options"; + AddSidebarEntry("Leveled", path.sidebarName, 1); + + // Player Options + AddWidget(path, "Player Options", WIDGET_SEPARATOR_TEXT); + // Heart Value + AddWidget(path, ("Heart Container Value in Units: "), WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { + ImGui::Text("Heart Container Value in Units: %d (%.2fx)", CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2, (float)CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) / 4.0f); + UIWidgets::CVarSliderInt("Heart Container Value in Units", "gLeveled.Difficulty.HeartUnits", + IntSliderOptions().LabelPosition(Within).Min(1).Max(32).Format("").DefaultValue(4).Color(THEME_COLOR) + .Tooltip( + "Sets how many health units each completed heart container is worth.\n" + "One heart on the health meter is equal to 16 health units.\n" + "A lower setting will result in lower total health.\n" + "Change areas to update health capacity.")); + }); + // Damage Multiplier + AddWidget(path, ("Damage Multiplier: "), WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { + ImGui::Text("Damage Multiplier: %.2fx", (float)CVarGetInteger("gLeveled.Difficulty.Player.DamageMultiplier", 4) / 4.0f); + UIWidgets::CVarSliderInt("Leveled Damage Multiplier", "gLeveled.Difficulty.Player.DamageMultiplier", + IntSliderOptions().LabelPosition(Within).Min(1).Max(32).Format("").DefaultValue(4).Color(THEME_COLOR) + .Tooltip("Sets a multiplier for the damage the player takes. Includes ALL sources, even damage while being frozen or burned.\nDamage cannot be reduced below 1.")); + }); + + // EXP Options + AddWidget(path, "EXP Options", WIDGET_SEPARATOR_TEXT); + // EXP Multiplier + AddWidget(path, "EXP Rate: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gLeveled.Difficulty.EXP.Rate") + .Options(FloatSliderOptions().Min(0.0f).Max(10.0f).Format("%.2fx").DefaultValue(1.0f).Tooltip( + "Sets the EXP multiplier link gains, with the exception of gold skulltula tokens.")); + // Token EXP + AddWidget(path, "Skulltula Token EXP Rate: %.2f x", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gLeveled.Difficulty.EXP.TokenRate") + .Options(FloatSliderOptions().Min(0.0f).Max(10.0f).Format("%.2fx").DefaultValue(1.0f).Tooltip( + "Sets the EXP multiplier link gains from gold skulltula tokens.")); + + // Enemy Options + AddWidget(path, "Enemy Options", WIDGET_SEPARATOR_TEXT); + // Max GS Tokens make Ganon's Castle Enemies Lv99 + AddWidget(path, "Collecting all 100 tokens makes all enemies in Ganon's Castle level 99", WIDGET_CVAR_CHECKBOX) + .CVar("gLeveled.Difficulty.Enemy.MaxLevelInGanonCastle") + .Options(CheckboxOptions() + .Tooltip("If all tokens are collected, all enemies in Ganon's Casle are level 99.") + .DefaultValue(true)); + // Enemy HP + AddWidget(path, "Enemy HP: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gLeveled.Difficulty.Enemy.HPPercent") + .Options( + FloatSliderOptions().Min(0.0f).Max(10.0f).Format("%.2fx").DefaultValue(1.0f).Tooltip( + "Sets the HP multiplier for enemies.")); + // Level Scale + AddWidget(path, "Level Scale: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) + .CVar("gLeveled.Difficulty.Enemy.LevelScale") + .Options(FloatSliderOptions().Min(0.0f).Max(10.0f).Format("%.2fx").DefaultValue(1.0f).Tooltip( + "Sets the multiplier for enemy levels.\nFor example, if set to 150%, a level 20 enemy will be level 30.\nCaps at level 99.")); + } + +} // namespace SohGui diff --git a/soh/src/code/leveled_actor_level_table.c b/soh/src/code/leveled_actor_level_table.c new file mode 100644 index 00000000000..e059bab5371 --- /dev/null +++ b/soh/src/code/leveled_actor_level_table.c @@ -0,0 +1,970 @@ +#include "global.h" + +typedef struct { + s8 levelModifier; + u16 experienceRate; + u8 ignoreEntry; +} SceneLevelEntry; + +///////////////////////////////////////////////// +////////////////// BOSSES ///////////////////// +///////////////////////////////////////////////// +void Leveled_Gohma(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 7; + actor->exp = 120; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 3000; + + if (actor->category == ACTORCAT_ENEMY) { // Larva + actor->level = 5; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 0; + if (actor->params < 10) { + actor->exp = 5; + levelEntry->experienceRate = 100; + } + } +} + +void Leveled_Dodongo(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->category == ACTORCAT_BOSS) { // King Dodongo + actor->level = 15; + actor->exp = 500; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 4000; + } else { + actor->level = 14; + actor->exp = 18; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 160; + } +} + +void Leveled_Barinade(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 21; + levelEntry->levelModifier = 5; + if (actor->params == -1) { // Body + actor->exp = 850; + levelEntry->experienceRate = 5000; + } +} + +void Leveled_PhantomGanon(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 28; + levelEntry->levelModifier = 5; + if (actor->params == 1) + actor->exp = 1400; + levelEntry->experienceRate = 6000; +} + +void Leveled_Volvagia(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 35; + levelEntry->levelModifier = 5; + if (actor->id == ACTOR_BOSS_FD2) + actor->exp = 2475; + levelEntry->experienceRate = 6300; +} + +void Leveled_Morpha(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 40; + actor->exp = 3500; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 6600; +} + +void Leveled_BongoBongo(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 45; + actor->exp = 4666; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 7000; +} + +void Leveled_Twinrova(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 52; + levelEntry->levelModifier = 6; + if (actor->params == 2) + actor->exp = 6000; + levelEntry->experienceRate = 7000; +} + +void Leveled_Ganondorf(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 56; + actor->exp = 9999; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 40000; +} + +void Leveled_Ganon(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 60; + levelEntry->levelModifier = 8; +} + +///////////////////////////////////////////////// +/////////////// MINI-BOSSES /////////////////// +///////////////////////////////////////////////// +void Leveled_BigOctorock(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 20; + actor->exp = 320; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 2100; +} + +void Leveled_FlareDancer(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 34; + levelEntry->levelModifier = 4; + if (actor->id == ACTOR_EN_FW) { + actor->exp = 1000; + levelEntry->experienceRate = 3000; + } +} + +void Leveled_DarkLink(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = GET_PLAYER(play)->actor.level; + actor->exp = (u16)((f32)GetPlayerStat_NextLevelExpAtLevel(GET_PLAYER(play)->actor.level) * 0.50f); + levelEntry->ignoreEntry = true; +} + +void Leveled_DeadHand(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 44; + levelEntry->levelModifier = 4; + + if (actor->id == ACTOR_EN_DH) { + actor->exp = 2465; + levelEntry->experienceRate = 3666; + } +} + +void Leveled_IronKnuckle(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 50; + actor->exp = 2733; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 5000; + + if (actor->params == 0) { + actor->level = 52; + actor->exp = 3100; + levelEntry->levelModifier = 6; + } + if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 55; + actor->exp = 2850; + } +} +///////////////////////////////////////////////// +////////////// NORMAL ENEMIES ///////////////// +///////////////////////////////////////////////// +void Leveled_Dekubaba(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == 1) { // Big baba + actor->level = 24; + actor->exp = 32; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 116; + if (LINK_IS_CHILD) { + actor->level = 5; + actor->exp = 8; + } + } else { + actor->level = 2; + actor->exp = 3; + levelEntry->levelModifier = -2; + levelEntry->experienceRate = 60; + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 23; + actor->exp = 19; + } + } +} + +void Leveled_StickDekuBaba(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 1; + actor->exp = 1; + levelEntry->levelModifier = -99; + levelEntry->experienceRate = 1; +} + +void Leveled_Skulltula(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 3; + actor->exp = 3; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 80; + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 21; + actor->exp = 19; + } + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 38; + actor->exp = 30; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 44; + actor->exp = 34; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 50; + } + if (actor->params == 1) { // Big + actor->level += 3; + actor->exp += 2; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 90; + } +} + +void Leveled_Wall_Skulltula(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (((play, actor->params & 0xE000) >> 0xD) != 0) { // Gold Skulltula + actor->level = gSaveContext.inventory.gsTokens + 1; + actor->exp = 0; + levelEntry->ignoreEntry = true; + } else { + actor->level = 3; + actor->exp = 2; + levelEntry->levelModifier = -2; + levelEntry->experienceRate = 40; + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 24; + actor->exp = 18; + } + } +} + +void Leveled_DekuScrub(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 8; + actor->exp = 7; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 75; +} + +void Leveled_Tektite(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == -2) { // Blue + actor->level = 12; + actor->exp = 12; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 100; + if (LINK_IS_ADULT) { + actor->level = 25; + actor->exp = 21; + } + if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 36; + actor->exp = 44; + } + } else { + actor->level = 9; + actor->exp = 9; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 90; + if (LINK_IS_ADULT) { + actor->level = 27; + actor->exp = 24; + } + } +} + +void Leveled_Guay(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 8; + actor->exp = 4; + levelEntry->levelModifier = -1; + levelEntry->experienceRate = 50; + if (LINK_IS_ADULT) { + actor->level = 21; + actor->exp = 13; + } +} + +void Leveled_Octorock(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 13; + actor->exp = 12; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 70; + if (actor->params != 0) { + actor->level = 0; + actor->exp = 0; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 0; + } +} + +void Leveled_Armos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 12; + actor->exp = 17; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 135; + if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 45; + actor->exp = 60; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { // Ganon's Castle + actor->level = 50; + actor->exp = 70; + } +} + +void Leveled_BabyDodongo(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 11; + actor->exp = 7; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 75; +} + +void Leveled_Keese(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == 4) { // Ice Keese + actor->level = 34; + actor->exp = 33; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 60; + } else { + actor->level = 9; + actor->exp = 5; + levelEntry->levelModifier = -1; + levelEntry->experienceRate = 50; + + if (play->sceneNum == SCENE_FIRE_TEMPLE) { + actor->level = 28; + actor->exp = 22; + } else if (play->sceneNum == SCENE_ICE_CAVERN) { + actor->level = 31; + actor->exp = 26; + } else if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 34; + actor->exp = 29; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 42; + actor->exp = 36; + } else if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 38; + actor->exp = 33; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 38; + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 80; + } + } +} + +void Leveled_Peahat(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 18; + actor->exp = 47; + levelEntry->levelModifier = 10; + levelEntry->experienceRate = 275; + if (actor->params == 1) { // Larva + actor->level = 13; + levelEntry->levelModifier = 5; + levelEntry->experienceRate = 0; + } +} +void Leveled_Poh(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 10; + actor->exp = 14; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 130; + if (actor->params == 2 || actor->params == 3) { // Composer + actor->level = 15; + actor->exp = 25; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 130; + } +} + +void Leveled_Poh_Field(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 24; + actor->exp = 25; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 130; +} + +void Leveled_ReDead(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params >= -1) { + actor->level = 25; + actor->exp = 45; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 280; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 42; + actor->exp = 171; + } else if (play->sceneNum == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || play->sceneNum == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE) { + actor->level = 50; + actor->exp = 230; + } + } else { // Gibdo + actor->level = 43; + actor->exp = 183; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 305; + } +} + +void Leveled_Shabom(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 15; + levelEntry->levelModifier = -1; +} + +void Leveled_Bari(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 18; + actor->exp = 25; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 85; +} + +void Leveled_Biri(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 16; + actor->exp = 17; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 100; +} + +void Leveled_Stinger(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 17; + actor->exp = 19; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 125; + if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 37; + actor->exp = 85; + } +} + +void Leveled_JabuTentacle(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 19; + levelEntry->levelModifier = 3; + if (actor->params < 3) { + actor->exp = 61; + levelEntry->experienceRate = 400; + } +} + +void Leveled_Tailpasaran(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 19; + actor->exp = 22; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 125; +} + +void Leveled_Stalchild(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 6 + (u16)(play, actor->params / 2); + actor->exp = 3 + (u16)(play, actor->params / 2.5); + levelEntry->levelModifier = 1 + (u16)(play, actor->params / 2); + levelEntry->experienceRate = 45 + (u16)(play, actor->params * 5); +} + +void Leveled_Beamos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == 0) { // Big + actor->level = 48; + actor->exp = 90; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 150; + } else { + actor->level = 11; + actor->exp = 17; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 150; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 43; + actor->exp = 80; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 87; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 90; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 300; + } + } +} + +void Leveled_Wolfos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == 0) { + actor->level = 10; + actor->exp = 24; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 180; + if (LINK_IS_ADULT) { + actor->level = 20; + actor->exp = 29; + } + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->level = 26; + actor->exp = 43; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 158; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 165; + } + } else { // White + actor->level = 37; + actor->exp = 126; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 180; + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 420; + } +} + +void Leveled_Lizalfos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == -2) { // Dinolfos + actor->level = 52; + actor->exp = 250; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 320; + } else if (actor->params == -1) { // Lizalfos + actor->level = 47; + actor->exp = 210; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 290; + } else { // Miniboss Lizalfos + actor->level = 13; + actor->exp = 140; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 1150; + actor->ignoreExpReward = true; + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 570; + } +} + +void Leveled_Moblins(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == -1) { // Spear + actor->level = 24; + actor->exp = 32; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 140; + } else if (actor->params == 0) { // Club + actor->level = 25; + actor->exp = 47; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 240; + } else { // Spear patrol + actor->level = 24; + actor->exp = 24; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 140; + } +} + +void Leveled_Bubble(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + if (actor->params == -1) { // Blue + actor->level = 25; + actor->exp = 29; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 105; + } else if (actor->params == -2) { // Red + actor->level = 32; + actor->exp = 47; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 110; + if ( play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 40; + actor->exp = 75; + } + } else if (actor->params == -3) { // White + actor->level = 45; + actor->exp = 76; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 107; + } else if (actor->params == -4) { // Big Green + actor->level = 41; + actor->exp = 82; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 100; + } else { // Green + actor->level = 26; + actor->exp = 31; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 93; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 41; + actor->exp = 70; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 45; + actor->exp = 77; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 85; + } + } + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 220; + } +} + +void Leveled_Stalfos(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 27; + actor->exp = 64; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 345; + + if (play->sceneNum == SCENE_FOREST_TEMPLE) { + actor->ignoreExpReward = true; + actor->exp = 250; + levelEntry->experienceRate = 800; + }else if (play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 43; + actor->exp = 214; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 48; + actor->exp = 236; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER || + play->sceneNum == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || + play->sceneNum == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE) { + actor->level = 50; + actor->exp = 260; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 700; + } +} + +void Leveled_Floormaster(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 26; + actor->exp = 41; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 170; + actor->ignoreExpReward = true; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 42; + actor->exp = 140; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 160; + } +} + +void Leveled_Wallmaster(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 27; + actor->exp = 44; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 175; + if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 41; + actor->exp = 129; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 47; + actor->exp = 138; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 150; + } +} + +void Leveled_Poh_Sisters(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 28; + actor->exp = 127; + levelEntry->levelModifier = 4; + levelEntry->experienceRate = 450; +} + +void Leveled_LikeLike(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 32; + actor->exp = 99; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 245; + if (play->sceneNum == SCENE_WATER_TEMPLE) { + actor->level = 38; + actor->exp = 142; + } else if (play->sceneNum == SCENE_BOTTOM_OF_THE_WELL || + play->sceneNum == SCENE_SHADOW_TEMPLE) { + actor->level = 43; + actor->exp = 161; + } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 48; + actor->exp = 182; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 190; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 375; + } +} + +void Leveled_TorchSlug(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 33; + actor->exp = 67; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 135; + if (play->sceneNum == SCENE_SPIRIT_TEMPLE) { + actor->level = 48; + actor->exp = 163; + } else if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 170; + } else if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 350; + } +} +void Leveled_Freezer(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 35; + actor->exp = 101; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 205; + if (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE || play->sceneNum == SCENE_GANONS_TOWER) { + actor->level = 50; + actor->exp = 160; + } +} + +void Leveled_ShellBlade(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 38; + actor->exp = 129; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 200; + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 390; + } +} + +void Leveled_RollingSpike(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 37; + actor->exp = 100; + levelEntry->levelModifier = 1; + levelEntry->experienceRate = 180; + if (play->sceneNum == SCENE_GERUDO_TRAINING_GROUND) { + actor->level = 47; + actor->exp = 325; + } +} + +void Leveled_GerudoFighter(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 45; + actor->exp = 455; + levelEntry->levelModifier = 2; + levelEntry->experienceRate = 1250; +} + +void Leveled_Leever(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 45; + actor->exp = 15; + levelEntry->levelModifier = 0; + levelEntry->experienceRate = 20; +} + +void Leveled_Anubis(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 48; + levelEntry->levelModifier = 3; + if (actor->id == ACTOR_EN_ANUBICE) { + actor->exp = 120; + levelEntry->experienceRate = 155; + } +} + +void Leveled_Arwing(PlayState* play, Actor* actor, SceneLevelEntry* levelEntry) { + actor->level = 20; + actor->exp = 50; + levelEntry->levelModifier = 3; + levelEntry->experienceRate = 215; +} + + +void Actor_GetLevelAndExperience(PlayState* play, Actor* actor, u16 actorIdOverride) { + SceneLevelEntry levelEntry; + levelEntry.levelModifier = 0; + levelEntry.experienceRate = 0; + levelEntry.ignoreEntry = 0; + + u16 actorId = actor->id; + if (actorIdOverride != 0) { + actorId = actorIdOverride; + } + + switch (actorId) { + case ACTOR_BOSS_DODONGO: + case ACTOR_EN_DODONGO: + Leveled_Dodongo(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GOMA: + case ACTOR_EN_GOMA: + Leveled_Gohma(play, actor, &levelEntry); + break; + case ACTOR_BOSS_VA: + Leveled_Barinade(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GANONDROF: + Leveled_PhantomGanon(play, actor, &levelEntry); + break; + case ACTOR_BOSS_FD: + case ACTOR_BOSS_FD2: + Leveled_Volvagia(play, actor, &levelEntry); + break; + case ACTOR_BOSS_MO: + Leveled_Morpha(play, actor, &levelEntry); + break; + case ACTOR_BOSS_SST: + Leveled_BongoBongo(play, actor, &levelEntry); + break; + case ACTOR_BOSS_TW: + Leveled_Twinrova(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GANON: + Leveled_Ganondorf(play, actor, &levelEntry); + break; + case ACTOR_BOSS_GANON2: + Leveled_Ganon(play, actor, &levelEntry); + break; + case ACTOR_EN_DEKUBABA: + Leveled_Dekubaba(play, actor, &levelEntry); + break; + case ACTOR_EN_KAREBABA: + Leveled_StickDekuBaba(play, actor, &levelEntry); + break; + case ACTOR_EN_ST: + Leveled_Skulltula(play, actor, &levelEntry); + break; + case ACTOR_EN_DEKUNUTS: + Leveled_DekuScrub(play, actor, &levelEntry); + break; + case ACTOR_EN_TITE: + Leveled_Tektite(play, actor, &levelEntry); + break; + case ACTOR_EN_CROW: + Leveled_Guay(play, actor, &levelEntry); + break; + case ACTOR_EN_OKUTA: + Leveled_Octorock(play, actor, &levelEntry); + break; + case ACTOR_EN_AM: + Leveled_Armos(play, actor, &levelEntry); + break; + case ACTOR_EN_DODOJR: + Leveled_BabyDodongo(play, actor, &levelEntry); + break; + case ACTOR_EN_FIREFLY: + Leveled_Keese(play, actor, &levelEntry); + break; + case ACTOR_EN_PEEHAT: + Leveled_Peahat(play, actor, &levelEntry); + break; + case ACTOR_EN_POH: + Leveled_Poh(play, actor, &levelEntry); + case ACTOR_EN_PO_FIELD: + Leveled_Poh_Field(play, actor, &levelEntry); + break; + case ACTOR_EN_RD: + Leveled_ReDead(play, actor, &levelEntry); + break; + case ACTOR_EN_SKB: + Leveled_Stalchild(play, actor, &levelEntry); + break; + case ACTOR_EN_SW: + Leveled_Wall_Skulltula(play, actor, &levelEntry); + break; + case ACTOR_EN_VM: + Leveled_Beamos(play, actor, &levelEntry); + break; + case ACTOR_EN_WF: + Leveled_Wolfos(play, actor, &levelEntry); + break; + case ACTOR_EN_ZF: + Leveled_Lizalfos(play, actor, &levelEntry); + break; + case ACTOR_EN_BUBBLE: + Leveled_Shabom(play, actor, &levelEntry); + break; + case ACTOR_EN_BILI: + Leveled_Biri(play, actor, &levelEntry); + break; + case ACTOR_EN_VALI: + Leveled_Bari(play, actor, &levelEntry); + break; + case ACTOR_EN_TP: + Leveled_Tailpasaran(play, actor, &levelEntry); + break; + case ACTOR_EN_BA: + case ACTOR_EN_BX: + Leveled_JabuTentacle(play, actor, &levelEntry); + break; + case ACTOR_EN_EIYER: + case ACTOR_EN_WEIYER: + Leveled_Stinger(play, actor, &levelEntry); + break; + case ACTOR_EN_BIGOKUTA: + Leveled_BigOctorock(play, actor, &levelEntry); + break; + case ACTOR_EN_MB: + Leveled_Moblins(play, actor, &levelEntry); + break; + case ACTOR_EN_BB: + Leveled_Bubble(play, actor, &levelEntry); + break; + case ACTOR_EN_TEST: + Leveled_Stalfos(play, actor, &levelEntry); + break; + case ACTOR_EN_FLOORMAS: + Leveled_Floormaster(play, actor, &levelEntry); + break; + case ACTOR_EN_WALLMAS: + Leveled_Wallmaster(play, actor, &levelEntry); + break; + case ACTOR_EN_PO_SISTERS: + Leveled_Poh_Sisters(play, actor, &levelEntry); + break; + case ACTOR_EN_RR: + Leveled_LikeLike(play, actor, &levelEntry); + break; + case ACTOR_EN_BW: + Leveled_TorchSlug(play, actor, &levelEntry); + break; + case ACTOR_EN_FZ: + Leveled_Freezer(play, actor, &levelEntry); + break; + case ACTOR_EN_SB: + Leveled_ShellBlade(play, actor, &levelEntry); + break; + case ACTOR_EN_NY: + Leveled_RollingSpike(play, actor, &levelEntry); + break; + case ACTOR_EN_TORCH2: + Leveled_DarkLink(play, actor, &levelEntry); + break; + case ACTOR_EN_DH: + case ACTOR_EN_DHA: + Leveled_DeadHand(play, actor, &levelEntry); + break; + case ACTOR_EN_FD: + case ACTOR_EN_FW: + case ACTOR_EN_FD_FIRE: + Leveled_FlareDancer(play, actor, &levelEntry); + break; + case ACTOR_EN_IK: + Leveled_IronKnuckle(play, actor, &levelEntry); + break; + case ACTOR_EN_GELDB: + Leveled_GerudoFighter(play, actor, &levelEntry); + break; + case ACTOR_EN_REEBA: + Leveled_Leever(play, actor, &levelEntry); + break; + case ACTOR_EN_ANUBICE: + case ACTOR_EN_ANUBICE_FIRE: + Leveled_Anubis(play, actor, &levelEntry); + break; + case ACTOR_EN_CLEAR_TAG: + Leveled_Arwing(play, actor, &levelEntry); + break; + default: + actor->level = GET_PLAYER(play)->actor.level; + break; + } + + s8 sceneLevel = Leveled_GetSceneLevel(play->sceneNum); + if (!levelEntry.ignoreEntry && sceneLevel >= 0) { + actor->level = (u8)CLAMP((f32)(sceneLevel + levelEntry.levelModifier) * (f32)CVarGetFloat("gLeveled.Difficulty.Enemy.LevelScale", 1.0f), 1, 99); + actor->exp = GetEnemyExperienceReward(actor->level, levelEntry.experienceRate); + } +} \ No newline at end of file diff --git a/soh/src/code/leveled_map_levels.c b/soh/src/code/leveled_map_levels.c new file mode 100644 index 00000000000..ea1616b8043 --- /dev/null +++ b/soh/src/code/leveled_map_levels.c @@ -0,0 +1,181 @@ +#include "global.h" + +s8 Leveled_GetSceneLevel(s16 sceneId) { + switch (sceneId) { + case SCENE_DEKU_TREE: + case SCENE_DEKU_TREE_BOSS: + return 2; // Deku Tree + + case SCENE_DODONGOS_CAVERN: + case SCENE_DODONGOS_CAVERN_BOSS: + return 10; // Dodongo's + + case SCENE_JABU_JABU: + case SCENE_JABU_JABU_BOSS: + return 16; // Jabu Jabu + + case SCENE_FOREST_TEMPLE: + case SCENE_FOREST_TEMPLE_BOSS: + return 24; // Forest Temple + + case SCENE_FIRE_TEMPLE: + case SCENE_FIRE_TEMPLE_BOSS: + return 30; // Fire Temple + + case SCENE_WATER_TEMPLE: + case SCENE_WATER_TEMPLE_BOSS: + return 35; // Water Temple + + case SCENE_SPIRIT_TEMPLE: + case SCENE_SPIRIT_TEMPLE_BOSS: + return 45; // Spirit Temple + + case SCENE_SHADOW_TEMPLE: + case SCENE_SHADOW_TEMPLE_BOSS: + return 40; // Shadow Temple + + case SCENE_BOTTOM_OF_THE_WELL: + return 38; // Bottom of the Well + + case SCENE_ICE_CAVERN: + return 32; // Ice Cavern + + case SCENE_GANONS_TOWER: + case SCENE_GANONDORF_BOSS: + case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR: + case SCENE_GANON_BOSS: + if (CVarGetInteger("gLeveled.Difficulty.Enemy.MaxLevelInGanonCastle", 0) == 1 && gSaveContext.inventory.gsTokens == 100) + return 120; + else + return 50; + + case SCENE_GERUDO_TRAINING_GROUND: + return 43; + + case SCENE_THIEVES_HIDEOUT: + return 42; + + case SCENE_INSIDE_GANONS_CASTLE: + return 52; + + case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: + case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE: + return 53; + + case SCENE_MARKET_RUINS: + return 23; + + case SCENE_REDEAD_GRAVE: + return 10; + + case SCENE_ROYAL_FAMILYS_TOMB: + return 8; + + case SCENE_WINDMILL_AND_DAMPES_GRAVE: + return 22; + + case SCENE_HOUSE_OF_SKULLTULA: + return 99; + + case SCENE_HYRULE_FIELD: + if (LINK_IS_CHILD) + return 5; + else + return 20; + + case SCENE_GRAVEYARD: + if (LINK_IS_CHILD) + return 7; + else + return 20; + + case SCENE_ZORAS_RIVER: + if (LINK_IS_CHILD) + return 8; + else + return 24; + + case SCENE_KOKIRI_FOREST: + if (LINK_IS_CHILD) + return 2; + else + return 21; + + case SCENE_SACRED_FOREST_MEADOW: + if (LINK_IS_CHILD) + return 9; + else + return 23; + + case SCENE_LAKE_HYLIA: + if (LINK_IS_CHILD) + return 7; + else + return 22; + + case SCENE_ZORAS_FOUNTAIN: + if (LINK_IS_CHILD) + return 13; + else + return 30; + + case SCENE_GERUDO_VALLEY: + if (LINK_IS_CHILD) + return 10; + else + return 28; + + case SCENE_LOST_WOODS: + if (LINK_IS_CHILD) + return 7; + else + return 22; + + case SCENE_DESERT_COLOSSUS: + return 42; + + case SCENE_GERUDOS_FORTRESS: + return 40; + + case SCENE_DEATH_MOUNTAIN_TRAIL: + if (LINK_IS_CHILD) + return 8; + else + return 24; + + case SCENE_DEATH_MOUNTAIN_CRATER: + return 11; + + case SCENE_LON_LON_RANCH: + return 6; + + case SCENE_OUTSIDE_GANONS_CASTLE: + return 25; + + + // Debug only scenes + case SCENE_TEST01: + // Test Map + case SCENE_BESITU: + // Test Room + case SCENE_DEPTH_TEST: + // Depth Test + case SCENE_SYOTES: + // Stalfos Mini-Boss + case SCENE_SYOTES2: + // Stalfos Boss + case SCENE_SUTARU: + // Dark Link + case SCENE_HAIRAL_NIWA2: + // Castle Maze (Broken) + case SCENE_SASATEST: + // SRD Room + case SCENE_TESTROOM: + // Chest Room + return 45; + + default: + return -1; + } + return -1; +} \ No newline at end of file diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c new file mode 100644 index 00000000000..1003e49327e --- /dev/null +++ b/soh/src/code/leveled_overlays.c @@ -0,0 +1,776 @@ +#include "leveled_overlays.h" +#include "global.h" +#include "textures/icon_item_static/icon_item_static.h" +#include "textures/parameter_static/parameter_static.h" +#include "textures/do_action_static/do_action_static.h" +#include "textures/icon_item_24_static/icon_item_24_static.h" +#include "textures/message_static/message_static.h" +#include "textures/nes_font_static/nes_font_static.h" + + + +Gfx* Gfx_Texture32(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { + gDPLoadTextureBlock(displayListHead++, texture, G_IM_FMT_RGBA, G_IM_SIZ_32b, textureWidth, textureHeight, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPWideTextureRectangle(displayListHead++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, + (rectTop + rectHeight) << 2, G_TX_RENDERTILE, 0, 0, dsdx, (s32)((dtdy))); + + return displayListHead; +} + +Gfx* Gfx_Texture4b(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { + gDPLoadTextureBlock_4b(displayListHead++, texture, G_IM_FMT_IA, textureWidth, textureHeight, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPWideTextureRectangle(displayListHead++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, + (rectTop + rectHeight) << 2, G_TX_RENDERTILE, 0, 0, dsdx, dtdy); + + return displayListHead; +} + +void Leveled_DrawTexI8(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight, u8 r, u8 g, u8 b) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_TextureI8(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, + textureHeight, (s32)(1024 * (f32)textureWidth / rectWidth), + (s32)(1024 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_DrawTexIA8(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight, u8 r, u8 g, u8 b) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_TextureIA8(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, textureHeight, (s32)(1024 * (f32)textureWidth / rectWidth), + (s32)(1024 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_DrawTex32(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_Texture32(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, textureHeight, (s32)(2048 * (f32)textureWidth / rectWidth), + (s32)(2048 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_DrawTex4b(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_Texture4b(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, textureHeight, (s32)(2048 * (f32)textureWidth / rectWidth), + (s32)(2048 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_OverlayDrawTex4b(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, + s16 rectTop, s16 rectWidth, s16 rectHeight, u16 alpha) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(OVERLAY_DISP++); + gDPSetTextureFilter(OVERLAY_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, alpha); + + OVERLAY_DISP = Gfx_Texture4b(OVERLAY_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, rectWidth, + rectHeight, 1 << 10, 1 << 10); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void ActorDamageNumber_New(Actor* actor, u16 damage) { + if (damage == 0 || (actor->category == ACTORCAT_PLAYER && !CVarGetInteger("gLeveled.HUD.FloatingNumbers.PlayerDamage", 1)) || (actor->category != ACTORCAT_PLAYER && !CVarGetInteger("gLeveled.HUD.FloatingNumbers.EnemyDamage", 1))) + return; + + Vec2f position = { 0, 0 }; + Vec2f velocity = { 0, -8 }; + + actor->floatingNumber[0] = damage; + actor->floatingNumberLife[0] = 30; + actor->floatingNumberPosition[0] = position; + actor->floatingNumberVelocity[0] = velocity; +} + +void ActorExperienceNumber_New(Actor* actor, u16 experience) { + if (experience == 0 || !CVarGetInteger("gLeveled.HUD.FloatingNumbers.ExpGain", 1)) + return; + + + Vec2f position = { 0, 0 }; + Vec2f velocity = { 0, -6 }; + + actor->floatingNumber[1] = experience; + actor->floatingNumberLife[1] = 40; + actor->floatingNumberPosition[1] = position; + actor->floatingNumberVelocity[1] = velocity; +} + +void ActorLevelUp_New(Actor* actor, u8 powerDiff, u8 courageDiff, u16 healthDiff, u8 magicDiff) { + + Vec2f position = { 0, 0 }; + Vec2f velocity = { 0, -6 }; + + actor->floatingNumberLife[2] = 100; + if (powerDiff > 0) + actor->floatingNumberLife[3] = 100; + if (courageDiff > 0) + actor->floatingNumberLife[4] = 100; + if (healthDiff > 0) + actor->floatingNumberLife[5] = 100; + if (magicDiff > 0) + actor->floatingNumberLife[6] = 100; + + actor->floatingNumberPosition[2] = position; + actor->floatingNumberVelocity[2] = velocity; + position.x = 16; + position.y = 0; + velocity.x = -4; + velocity.y = -5; + actor->floatingNumberPosition[3] = position; + actor->floatingNumberVelocity[3] = velocity; + position.x = 16; + position.y = 0; + velocity.x = 4; + velocity.y = -5; + actor->floatingNumberPosition[4] = position; + actor->floatingNumberVelocity[4] = velocity; + position.x = 16; + position.y = 0; + velocity.x = -2; + velocity.y = -5; + actor->floatingNumberPosition[5] = position; + actor->floatingNumberVelocity[5] = velocity; + position.x = 16; + position.y = 0; + velocity.x = 2; + velocity.y = -5; + actor->floatingNumberPosition[6] = position; + actor->floatingNumberVelocity[6] = velocity; +} + +void ActorExperienceNumber_Draw(PlayState* play, Actor* actor) { + + extern const char* digitTextures[]; + s16 val = actor->floatingNumber[1]; + u8 digit[] = { 0, 0, 0, 0, 0 }; + u8 digits = 1; + u8 width = 8; + Vec3f spBC; + f32 spB4; + s32 j; + + if (actor->floatingNumberLife[1] <= 0) + return; + + OPEN_DISPS(play->state.gfxCtx); + + // Digits + if (val < 0) + val = 0; + + if (val > 32000) + val = 32000; + + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = (u8)val; + + // Velocity + actor->floatingNumberPosition[1].x += actor->floatingNumberVelocity[1].x; + actor->floatingNumberPosition[1].y += actor->floatingNumberVelocity[1].y; + + if (actor->floatingNumberVelocity[1].y < 0) { + actor->floatingNumberVelocity[1].y += 0.4f; + } + + // Life + actor->floatingNumberLife[1]--; + + // Position + func_8002BE04(play, &actor->world.pos, &spBC, &spB4); + + spBC.x = (160 * (spBC.x * spB4)) * 1.0f + 157 + actor->floatingNumberPosition[1].x + (digits - 1) * width / 2; + spBC.x = CLAMP(spBC.x, -320.0f, 320.0f); + + spBC.y = (120 * (spBC.y * spB4)) * -1.0f + 90 + actor->floatingNumberPosition[1].y; + spBC.y = CLAMP(spBC.y, -240.0f, 240.0f); + + if (spBC.z <= -0) { + spBC.x = 160 + actor->floatingNumberPosition[1].x + (digits - 1) * width / 2; + spBC.y = 200 + actor->floatingNumberPosition[1].y; + } + + spBC.z = spBC.z * 1.0f; + + // Color + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 190, 190, 0, 255); + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + // Draw + for (u8 i = 0; i < digits; i++) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, (s16)spBC.x - i * width, (s16)spBC.y, + 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void ActorDamageNumber_Draw(PlayState* play, Actor* actor) { + + extern const char* digitTextures[]; + s16 val = actor->floatingNumber[0]; + u8 digit[] = { 0, 0, 0, 0 }; + u8 digits = 1; + u8 width = 8; + Vec3f spBC; + f32 spB4; + s32 j; + + if (actor->floatingNumberLife[0] <= 0) + return; + + OPEN_DISPS(play->state.gfxCtx); + + // Digits + if (val < 0) + val = 0; + + if (val > 9999) + val = 9999; + + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = (u8)val; + + // Velocity + actor->floatingNumberPosition[0].x += actor->floatingNumberVelocity[0].x; + actor->floatingNumberPosition[0].y += actor->floatingNumberVelocity[0].y; + + actor->floatingNumberVelocity[0].y += 2.0f; + if (actor->floatingNumberPosition[0].y >= 0) { + actor->floatingNumberPosition[0].y = 0; + actor->floatingNumberVelocity[0].y = -actor->floatingNumberVelocity[0].y * 0.5f; + if (actor->floatingNumberVelocity[0].y >= -4.0f) + actor->floatingNumberVelocity[0].y = 0; + } + + // Life + actor->floatingNumberLife[0]--; + + // Position + func_8002BE04(play, &actor->world.pos, &spBC, &spB4); + + spBC.x = (160 * (spBC.x * spB4)) * 1.0f + 157 + actor->floatingNumberPosition[0].x + (digits - 1) * width / 2; + spBC.x = CLAMP(spBC.x, -320.0f, 320.0f); + + spBC.y = (120 * (spBC.y * spB4)) * -1.0f + 90 + actor->floatingNumberPosition[0].y; + spBC.y = CLAMP(spBC.y, -240.0f, 240.0f); + + spBC.z = spBC.z * 1.0f; + + // Color + if (actor->category == ACTORCAT_PLAYER) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 100, 0, 255); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255); + } + + // Draw + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + for (u8 i = 0; i < digits; i++) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, (s16)spBC.x - i * width - (digits - 1) * width * 0.5f, (s16)spBC.y, + 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Actor_LevelUpDraw(PlayState* play, Actor* actor) { + u8 width = 8; + Vec3f spBC; + f32 spB4; + for (s8 i = 2; i < 7; i++) { + if (actor->floatingNumberLife[i] <= 0) + continue; + + OPEN_DISPS(play->state.gfxCtx); + + // Velocity + actor->floatingNumberPosition[i].x += actor->floatingNumberVelocity[i].x; + actor->floatingNumberPosition[i].y += actor->floatingNumberVelocity[i].y; + + if (actor->floatingNumberVelocity[i].y < 0) { + actor->floatingNumberVelocity[i].y += 0.75f; + if (actor->floatingNumberVelocity[i].y > 0) + actor->floatingNumberVelocity[i].y = 0; + } + if (actor->floatingNumberVelocity[i].x < 0) { + actor->floatingNumberVelocity[i].x += 0.5f; + if (actor->floatingNumberVelocity[i].x > 0) + actor->floatingNumberVelocity[i].x = 0; + } + if (actor->floatingNumberVelocity[i].x > 0) { + actor->floatingNumberVelocity[i].x -= 0.5f; + if (actor->floatingNumberVelocity[i].x < 0) + actor->floatingNumberVelocity[i].x = 0; + } + + // Life + actor->floatingNumberLife[i]--; + + // Position + func_8002BE04(play, &actor->world.pos, &spBC, &spB4); + + spBC.x = (160 * (spBC.x * spB4)) * 1.0f + 165 + actor->floatingNumberPosition[i].x - 24; + spBC.x = CLAMP(spBC.x, -320.0f, 320.0f); + + spBC.y = (120 * (spBC.y * spB4)) * -1.0f + 90 + actor->floatingNumberPosition[i].y; + spBC.y = CLAMP(spBC.y, -240.0f, 240.0f); + + spBC.z = spBC.z * 1.0f; + + if (spBC.z < 0) { + spBC.x = 160 + actor->floatingNumberPosition[i].x - 24; + spBC.y = 200 + actor->floatingNumberPosition[i].y; + } + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + if (i == 2) { + + for (u8 j = 0; j < 3; j++) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar4CLatinCapitalLetterLTex, 8, 16, (s16)spBC.x, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar45LatinCapitalLetterETex, 8, 16, (s16)spBC.x + 5, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar56LatinCapitalLetterVTex, 8, 16, (s16)spBC.x + 10, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar45LatinCapitalLetterETex, 8, 16, (s16)spBC.x + 15, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar4CLatinCapitalLetterLTex, 8, 16, (s16)spBC.x + 20, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar55LatinCapitalLetterUTex, 8, 16, (s16)spBC.x + 30, (s16)spBC.y, 8, 16, 2048, 2048); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, dgMsgChar50LatinCapitalLetterPTex, 8, 16, (s16)spBC.x + 35, (s16)spBC.y, 8, 16, 2048, 2048); + } + } + + if (i == 3) { + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgItemIconSilverGauntletsTex, 32, 32, (s16)spBC.x, (s16)spBC.y, 32, 32, 4098, 4098); + } + if (i == 4) { + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgItemIconShieldHylianTex, 32, 32, (s16)spBC.x, (s16)spBC.y, 32, 32, 4098, 4098); + } + if (i == 5) { + + gDPPipeSync(OVERLAY_DISP++); + gDPSetTextureFilter(OVERLAY_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 70, 0, 255); + + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, dgHeartFullTex, 16, 16, (s16)spBC.x, (s16)spBC.y, 16, 16, 2048, 2048); + } + + if (i == 6) { + OVERLAY_DISP = Gfx_Texture32(OVERLAY_DISP, dgQuestIconMagicJarBigTex, 24, 24, (s16)spBC.x, (s16)spBC.y, 24, 24, 3072, 3072); + } + + CLOSE_DISPS(play->state.gfxCtx); + } +} + +extern const char* _gAmmoDigit0Tex[]; +void Leveled_ValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b) { + s32 val; + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + s16 separation = 5; + + u8 digits; + + OPEN_DISPS(play->state.gfxCtx); + + val = value; + + if (val > 999999) + val = 999999; + + if (val < 0) + val = 0; + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + digits = 1; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + s8 j; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = val; + + gDPPipeSync(POLY_OPA_DISP++); + + for (s8 i = 0; i < digits; i++) { + POLY_OPA_DISP = + Gfx_TextureIA8(POLY_OPA_DISP, (u8*)_gAmmoDigit0Tex[digit[i]], 8, 8, x - i * 6 + 6 * (digits - 1), y, 8, + 8, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_BigValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b, u8 a) { + s32 val; + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + s16 separation = 5; + u8 width = 7; + + extern const char* digitTextures[]; + + u8 digits; + + OPEN_DISPS(play->state.gfxCtx); + + val = value; + + if (val > 999999) + val = 999999; + + if (val < 0) + val = 0; + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, a); + + digits = 1; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + s8 j; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = val; + + gDPPipeSync(POLY_OPA_DISP++); + + for (u8 i = 0; i < digits; i++) { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 0, 0, 255); + + for (j = 0; j < 4; j++) { + POLY_OPA_DISP = Gfx_TextureI8(POLY_OPA_DISP, (u8*)digitTextures[digit[i]], 8, 16, x - i * width + width * (digits - 1) + (j % 2) * 2 - 1, y + (j / 2) * 2 - 1, 8, 16, 1 << 10, 1 << 10); + } + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + POLY_OPA_DISP = Gfx_TextureI8(POLY_OPA_DISP, (u8*)digitTextures[digit[i]], 8, 16, x - i * width + width * (digits - 1), y, 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Leveled_KaleidoEquip_Stats(PlayState* play) { + extern const char* digitTextures[]; + Player* player = GET_PLAYER(play); + u16 statX = 92; + u16 statY = 76; + u8 attack = 1; + Color_RGBA8 textColor = { 255, 255, 255, 255 }; + + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 1) + attack = Leveled_GetHealthAttackMultiplier(); + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 2) + attack = Leveled_GetHealthAttackMultiplier() << 1; + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == 3) { + attack = Leveled_GetHealthAttackMultiplier() << 2; + if (gBitFlags[3] & gSaveContext.inventory.equipment) + attack = Leveled_GetHealthAttackMultiplier(); + } + + // Values and Icons + // Level + Leveled_DrawTexI8(play, dgMsgChar4CLatinCapitalLetterLTex, 8, 16, statX + 2, statY - 2, 10, 11, 255, 255, 255); + Leveled_DrawTexI8(play, dgMsgChar76LatinSmallLetterVTex, 8, 16, statX + 5, statY - 2, 10, 11, 255, 255, 255); + + Leveled_BigValueNumberDraw(play, statX + 10, statY - 6, player->actor.level, 255, 255, 255, 255); + statY += 10; + // Health + Leveled_DrawTexIA8(play, dgHeartFullTex, 16, 16, statX + 2, statY, 8, 8, 255, 70, 0); + u8 healthValX = gSaveContext.healthCapacity2 >= 1000 ? 12 : gSaveContext.healthCapacity2 >= 100 ? 6 : 0; + if (HealthMeter_IsCritical()) { + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.health, 255, 127, 0); + } else { + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.health, 255, 255, 255); + } + Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 23 + healthValX, statY - 1, 8, 9, 255, 255, 255); + Leveled_ValueNumberDraw(play, statX + 28 + healthValX, statY, gSaveContext.healthCapacity2, 120, 255, 0); + statY += 8; + // Magic + if (gSaveContext.magicCapacity > 0) { + healthValX = gSaveContext.magicCapacity >= 100 ? 6 : 0; + Leveled_DrawTex32(play, dgQuestIconMagicJarBigTex, 24, 24, statX + 2, statY, 14, 14); + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.magic, 255, 255, 255); + Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 23 + healthValX, statY - 1, 8, 9, 255, 255, 255); + Leveled_ValueNumberDraw(play, statX + 28 + healthValX, statY, gSaveContext.magicCapacity, 120, 255, 0); + statY += 8; + } + // Attack + Leveled_DrawTex32(play, dgItemIconSwordKokiriTex, 32, 32, statX, statY - 1, 22, 20); + Leveled_ValueNumberDraw(play, statX + 10, statY, GetActorStat_Attack(attack, CLAMP(player->actor.power + player->actor.powerModifier, 0, 255)), 255, 255, 255); + statY += 8; + // Power + if (player->actor.powerModifier > 0) { + textColor.r = 120; + textColor.g = 255; + textColor.b = 0; + } else if (player->actor.powerModifier < 0) { + textColor.r = 255; + textColor.g = 0; + textColor.b = 0; + } + Leveled_DrawTex32(play, dgItemIconSilverGauntletsTex, 32, 32, statX + 2, statY, 16, 16); + Leveled_ValueNumberDraw(play, statX + 10, statY, CLAMP(player->actor.power + player->actor.powerModifier, 0, 255), textColor.r, textColor.g, textColor.b); + statY += 8; + // Courage + if (player->actor.courageModifier > 0) { + textColor.r = 120; + textColor.g = 255; + textColor.b = 0; + } else if (player->actor.courageModifier < 0) { + textColor.r = 255; + textColor.g = 0; + textColor.b = 0; + } else { + textColor.r = 255; + textColor.g = 255; + textColor.b = 255; + } + Leveled_DrawTex32(play, dgItemIconShieldHylianTex, 32, 32, statX + 2, statY, 16, 16); + Leveled_ValueNumberDraw(play, statX + 10, statY, CLAMP(player->actor.courage + player->actor.courageModifier, 0, 255), textColor.r, textColor.g, textColor.b); + statX = 90; + statY += 60; + if (gSaveContext.magicCapacity > 0) { + statY -= 8; + } + // EXP + Leveled_DrawTex32(play, dgItemIconGoronsBraceletTex, 32, 32, statX + 4, statY, 13, 16); + Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.experience, 255, 255, 255); + statY += 8; + // Next LV + Leveled_DrawTex4b(play, dgNextDoActionENGTex, 48, 16, statX - 4, statY, 68, 15); + Leveled_ValueNumberDraw(play, statX + 22, statY, + GetActorStat_NextLevelExp(player->actor.level, gSaveContext.experience), 255, 255, 255); +} + +void Leveled_Interface_DrawNextLevel(PlayState* play) { + if (gSaveContext.showNeededExpTimer > 0){ + gSaveContext.showNeededExpTimer--; + } else { + return; + } + if (play->pauseCtx.state != 0 || !CVarGetInteger("gLeveled.HUD.ExperienceNextLevel", 1)) + return; + + Player* player = GET_PLAYER(play); + u16 posX = OTRGetRectDimensionFromLeftEdge(10); + u16 posY = 58; + extern const char* digitTextures[]; + s32 val = GetActorStat_NextLevelExp(player->actor.level, gSaveContext.experience); + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + u8 digits = 1; + u8 width = 8; + s32 j; + + Leveled_OverlayDrawTex4b(play, dgNextDoActionENGTex, 48, 16, -192, -268, 48, 16, 255 - play->pauseCtx.alpha); // Load texture + Leveled_OverlayDrawTex4b(play, dgNextDoActionENGTex, 48, 16, posX, posY, 48, 16, 255 - play->pauseCtx.alpha); // Draw texture + + OPEN_DISPS(play->state.gfxCtx); + + // Digits + if (val < 0) + val = 0; + + if (val > 999999) + val = 999999; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = (u8)val; + + // Color + + // Draw + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + s16 numbersPosX = 38; + + for (u8 i = 0; i < digits; i++) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, 255 - play->pauseCtx.alpha); + + for (j = 0; j < 3; j++) { + for (u8 k = 0; k < 3; k++) { + if (j == 1 && k == 1) + continue; + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, + posX - i * width + width * (digits - 1) + numbersPosX - 1 + k, posY - 1 + j, 8, 16, 1024, 1024); + } + } + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255 - play->pauseCtx.alpha); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, posX - i * width + width * (digits - 1) + numbersPosX, posY, 8, 16, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} \ No newline at end of file diff --git a/soh/src/code/leveled_stat_math.c b/soh/src/code/leveled_stat_math.c new file mode 100644 index 00000000000..e1ae153d9bc --- /dev/null +++ b/soh/src/code/leveled_stat_math.c @@ -0,0 +1,267 @@ +#include "global.h" + +static u32 sExpTable[] = { 0, 30, 64, 105, 155, 217, 292, 385, 497, 632, 792, + 981, 1202, 1458, 1753, 2089, 2471, 2903, 3388, 3930, 4534, 5203, + 5942, 6755, 7648, 8625, 9691, 10851, 12110, 13474, 14949, 16540, 18253, + 20094, 22070, 24188, 26453, 28874, 31456, 34209, 37139, 40254, 43562, 47073, + 50794, 54734, 58904, 63312, 67968, 72883, 78067, 83531, 89286, 95345, 101719, + 108421, 115463, 122859, 130623, 138770, 147314, 156270, 165655, 175485, 185778, 196552, + 207824, 219614, 231943, 244831, 258300, 272372, 287071, 302422, 318450, 335182, 352645, + 370870, 389885, 409724, 430419, 452005, 474520, 498001, 522489, 548027, 574659, 602434, + 631400, 661611, 693122, 725993, 760287, 796070, 833414, 872395, 913094, 955597, 999999 }; +/* Old table +{ 0, 30, 65, 110, 167, 240, 334, 452, 598, 777, 993, + 1251, 1555, 1910, 2320, 2791, 3329, 3937, 4621, 5388, 6242, 7189, + 8235, 9386, 10647, 12026, 13528, 15160, 16929, 18840, 20902, 23120, 25501, + 28054, 30784, 33700, 36809, 40119, 43637, 47371, 51329, 55520, 59951, 64630, + 69567, 74770, 80248, 86008, 92061, 98416, 105080, 112065, 119379, 127032, 135033, + 143392, 152119, 161225, 170719, 180611, 190912, 201632, 212783, 224374, 236417, 248922, + 261901, 275365, 289325, 303792, 318779, 334297, 350358, 366973, 384155, 401916, 420268, + 439224, 458796, 478997, 499839, 521336, 543501, 566346, 589885, 614131, 639098, 664799, + 691249, 718461, 746448, 775226, 804807, 835208, 866441, 898523, 931466, 965287, 999999 };*/ + +u8 Leveled_GetHealthAttackMultiplier() { + return CLAMP(CVarGetInteger("gLeveled.Enhancements.AttackAndHPMultiplier", 9), 1, 32); +} + +u16 GetActorStat_DisplayAttack(u16 attack, u8 power) { + return GetActorStat_Attack(attack, power) / (1 + (float)power / 30.0f); +} + +u16 GetActorStat_Attack(u16 attack, u8 power) { + return (float)attack * (1 + (power - 2) * (0.14f + (power - 2) * 0.0006f)); +} + +f32 GetActorStat_EnemyAttack(u16 attack, u8 power) { + return (float)attack * (1 + (power - 2) * (0.007f + (power - 2) * 0.0002f)); +} + +u8 GetActorStat_Power(u8 level) { + return 3 + (u8)(84 * level / 99.0f); +} + +u8 GetActorStat_Courage(u8 level) { + return 2 + (u8)(75 * level / 99.0f); +} + +u8 GetActorStat_PlayerPower(u8 level) { + return 2 + (u8)(75 * level / 99.0f); +} + +u8 GetActorStat_PlayerCourage(u8 level) { + return 3 + (u8)(84 * level / 99.0f); +} + +u16 GetActorStat_EnemyMaxHealth(u16 baseHealth, u8 level){ + return (u16)(CLAMP((f32)GetActorStat_Attack(baseHealth * Leveled_GetHealthAttackMultiplier(), GetActorStat_PlayerPower(level)) * CVarGetFloat("gLeveled.Difficulty.Enemy.HPPercent", 1.0f), 1, 0xffff)); +} + +u8 GetPlayerStat_BonusHearts(u8 level){ + if (CVarGetInteger("gLeveled.Player.Enhancements.HeartsWithLevelUp", 1) == 0){ + return 0; + } + + u8 bonusHearts = (level + 1) / 8; + if (bonusHearts > 10){ + bonusHearts = 10; + } + return bonusHearts; +} + +u8 GetPlayerStat_MagicUnits(u8 level){ + if (CVarGetInteger("gLeveled.Player.Enhancements.MagicWithLevelUp", 1) == 0){ + return 48; + } + + u8 maximumMagic = 12 + (u8)((f32)level / 2.8f) * 2; + if (maximumMagic > 72){ + maximumMagic = 72; + } + return maximumMagic; +} + +u16 GetPlayerStat_GetModifiedHealthCapacity(u16 baseHealth, u8 level){ + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + u16 baseHearts = baseHealth / 16; + return (baseHearts + GetPlayerStat_BonusHearts(level)) * heartUnits; +} + +u16 GetPlayerStat_NextLevelExpAtLevel(u8 level) { + if (level == 99) + return 0; + + s32 nextLv = sExpTable[level] - sExpTable[level - 1]; +} + +u16 GetActorStat_NextLevelExp(u8 level, u32 currentExp) { + if (level == 99) + return 0; + + s32 nextLv = sExpTable[level] - currentExp; + + if (nextLv < 0) + nextLv = 0; + return nextLv; +} + +u16 GetEnemyExperienceReward(u8 level, u16 expRate) { + if (expRate == 0) + return 0; + + return CLAMP(round((3 + CLAMP_MAX(floor(level / 6) * 4, 4) + (level - 1) * ((0.1 + (level / 95.0)) + pow(CLAMP_MIN(level - 8, 0), 1.25) / 80.0)) * expRate / 100.0), 1, 9999); +} + +f32 Leveled_DamageFormula(f32 attack, u8 power, u8 courage) { + f32 damage = GetActorStat_Attack(attack, power); + if (power >= courage) { + for (u8 i = 0; i < power - courage; i++) { + damage *= 1.01f; + } + } else { + for (u8 i = 0; i < courage - power; i++) { + damage *= 0.97f; + } + } + return damage; +} + +f32 Leveled_DamageFormulaOnPlayer(f32 attack, u8 power, u8 courage) { + f32 damage = attack; + + if (CVarGetInteger("gLeveled.Enemy.Enhancements.AttackScalesWithLevel", 1) == 1){ + damage = GetActorStat_EnemyAttack(attack, power); + + if (power >= courage) { + for (u8 i = 0; i < power - courage; i++) { + f32 multAddition = CLAMP_MIN((0.07f - (power * 0.0005f)) - (f32)i / (100.0f - (power * 0.33f)), 0); + damage *= 1.04f + multAddition; + } + } else { + for (u8 i = 0; i < courage - power; i++) { + damage *= 0.96f; + } + } + } else { + if (power >= courage) { + for (u8 i = 0; i < power - courage; i++) { + damage *= 1.05f + CLAMP_MIN(0.05f - (f32)i / 100.0f, 0); + } + } else { + for (u8 i = 0; i < courage - power; i++) { + damage *= 0.96f; + } + } + } + return damage; +} + +f32 Leveled_DamageModify(Actor* actor, Actor* attackingActor, f32 attack) { + f32 damage; + if (actor->category == ACTORCAT_PLAYER) { + damage = Leveled_DamageFormulaOnPlayer(attack, CLAMP(attackingActor->power + attackingActor->powerModifier, 0, 255), CLAMP(actor->courage + actor->courageModifier, 0, 255)); + } else { + damage = Leveled_DamageFormula(attack, CLAMP(attackingActor->power + attackingActor->powerModifier, 0, 255), CLAMP(actor->courage + actor->courageModifier, 0, 255)); + } + + + if (damage >= 1.25f) + damage += Rand_ZeroOne() - 0.2f; + + if (damage >= 6) + damage += Rand_ZeroFloat(damage * 0.12f) - damage * 0.06f; + + if (damage > 9999) + damage = 9999; + + damage = (u16)(damage + 0.5f); + + return CLAMP_MIN(damage, attack > 0 ? 1 : 0); +} + +u16 Leveled_GoldSkulltulaExperience(u8 tokens) { + u16 experience = 5; + u8 i; + + for (i = 0; i < tokens; i++) { + experience += 5 + 5 * i / (f32)10.0; + } + return (u16)((f32)experience * CVarGetFloat("gLeveled.Difficulty.EXP.TokenRate", 1.0f)); +} + +void Leveled_SetPlayerModifiedStats(Player* player) { + s8 powerModifier = 0; + s8 courageModifier = 0; + + if (CVarGetInteger("gLeveled.Player.Enhancements.EquipmentStats", 1) == 1){ + switch (CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD)){ + case PLAYER_SWORD_MASTER: + courageModifier += 1; + break; + + case PLAYER_SWORD_BIGGORON: + if (gBitFlags[3] & gSaveContext.inventory.equipment){ + powerModifier -= 7; + courageModifier -= 12; + } else { + powerModifier += 2; + courageModifier -= 8; + } + break; + + default: + break; + } + + switch (CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC) - 1){ + case PLAYER_TUNIC_GORON: + powerModifier += 3; + courageModifier -= 3; + break; + + case PLAYER_TUNIC_ZORA: + powerModifier -= 3; + courageModifier += 3; + break; + + default: + break; + } + + switch (CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD)){ + case PLAYER_SHIELD_DEKU: + courageModifier += 1; + break; + + case PLAYER_SHIELD_HYLIAN: + courageModifier += 2; + break; + + case PLAYER_SHIELD_MIRROR: + powerModifier -= 2; + courageModifier += 3; + break; + + default: + break; + } + + switch (CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS) - 1){ + case PLAYER_BOOTS_IRON: + powerModifier += 2; + courageModifier += 1; + break; + + case PLAYER_BOOTS_HOVER: + courageModifier -= 1; + break; + + default: + break; + } + + powerModifier += Player_GetStrength(); + } + + player->actor.powerModifier = powerModifier; + player->actor.courageModifier = courageModifier; +} \ No newline at end of file diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index 3ca67ad3b53..e9c703fe1cc 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -1236,12 +1236,37 @@ void Actor_SetObjectDependency(PlayState* play, Actor* actor) { gSegments[6] = VIRTUAL_TO_PHYSICAL(play->objectCtx.status[actor->objBankIndex].segment); } +void Actor_RefreshLeveledStats(Actor* actor, Player* player) { + if (actor->category == ACTORCAT_PLAYER) { + actor->power = GetActorStat_PlayerPower(actor->level); + actor->courage = GetActorStat_PlayerCourage(actor->level); + gSaveContext.healthCapacity2 = + GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, actor->level); + if (gSaveContext.health > gSaveContext.healthCapacity2) + gSaveContext.health = gSaveContext.healthCapacity2; + gSaveContext.magicUnits = GetPlayerStat_MagicUnits(actor->level); + Leveled_SetPlayerModifiedStats(player); + } else { + actor->power = GetActorStat_Power(actor->level); + actor->powerModifier = 1; + actor->courage = GetActorStat_Courage(actor->level); + actor->courageModifier = 0; + } +} + void Actor_Init(Actor* actor, PlayState* play) { Actor_SetWorldToHome(actor); Actor_SetShapeRotToWorld(actor); Actor_SetFocus(actor, 0.0f); Math_Vec3f_Copy(&actor->prevPos, &actor->world.pos); Actor_SetScale(actor, 0.01f); + actor->level = 0; + actor->exp = 0; + actor->ignoreExpReward = false; + for (u8 i = 0; i < 5; i++) { + actor->floatingNumber[i] = 0; + actor->floatingNumberLife[i] = 0; + } actor->targetMode = 3; actor->minVelocityY = -20.0f; actor->xyzDistToPlayerSq = FLT_MAX; @@ -1257,12 +1282,19 @@ void Actor_Init(Actor* actor, PlayState* play) { actor->init(actor, play); actor->init = NULL; + if (actor->category != ACTORCAT_PLAYER) { + Actor_GetLevelAndExperience(play, actor, 0); + actor->colChkInfo.health = GetActorStat_EnemyMaxHealth(actor->colChkInfo.health, actor->level); + } + GameInteractor_ExecuteOnActorInit(actor); // For enemy health bar we need to know the max health during init - if (actor->category == ACTORCAT_ENEMY) { + if (actor->category != ACTORCAT_PLAYER) { actor->maximumHealth = actor->colChkInfo.health; } + + Actor_RefreshLeveledStats(actor, GET_PLAYER(play)); } } @@ -2201,8 +2233,12 @@ s32 Actor_NotMounted(PlayState* play, Actor* horse) { void func_8002F698(PlayState* play, Actor* actor, f32 arg2, s16 arg3, f32 arg4, u32 arg5, u32 arg6) { Player* player = GET_PLAYER(play); + u16 damage = arg6; + if (actor != NULL && damage != 0) { + damage = Leveled_DamageModify(&player->actor, actor, arg6); + } - player->knockbackDamage = arg6; + player->knockbackDamage = damage; player->knockbackType = arg5; player->knockbackRot = arg3; player->knockbackSpeed = arg2; @@ -2618,12 +2654,19 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { actor->init(actor, play); actor->init = NULL; + if (actor->category != ACTORCAT_PLAYER) { + Actor_GetLevelAndExperience(play, actor, 0); + actor->colChkInfo.health = GetActorStat_EnemyMaxHealth(actor->colChkInfo.health, actor->level); + } + GameInteractor_ExecuteOnActorInit(actor); // For enemy health bar we need to know the max health during init - if (actor->category == ACTORCAT_ENEMY) { + if (actor->category != ACTORCAT_PLAYER) { actor->maximumHealth = actor->colChkInfo.health; } + + Actor_RefreshLeveledStats(actor, GET_PLAYER(play)); } actor = actor->next; } else if (!Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) { @@ -3628,6 +3671,9 @@ Actor* Actor_Find(ActorContext* actorCtx, s32 actorId, s32 actorCategory) { */ void Enemy_StartFinishingBlow(PlayState* play, Actor* actor) { play->actorCtx.freezeFlashTimer = 5; + if (!actor->ignoreExpReward) { + Player_GainExperience(play, actor->exp); + } SoundSource_PlaySfxAtFixedWorldPos(play, &actor->world.pos, 20, NA_SE_EN_LAST_DAMAGE); } @@ -4740,12 +4786,15 @@ u8 func_800355E4(PlayState* play, Collider* collider) { } } -u8 Actor_ApplyDamage(Actor* actor) { +u16 Actor_ApplyDamage(Actor* actor) { if (actor->colChkInfo.damage >= actor->colChkInfo.health) { actor->colChkInfo.health = 0; } else { actor->colChkInfo.health -= actor->colChkInfo.damage; } + if (actor->colChkInfo.damage > 0) { + ActorDamageNumber_New(actor, actor->colChkInfo.damage); + } return actor->colChkInfo.health; } diff --git a/soh/src/code/z_collision_check.c b/soh/src/code/z_collision_check.c index aa466e412eb..0a63113ed04 100644 --- a/soh/src/code/z_collision_check.c +++ b/soh/src/code/z_collision_check.c @@ -3024,12 +3024,25 @@ void CollisionCheck_ApplyDamage(PlayState* play, CollisionCheckContext* colChkCt collider->actor->colChkInfo.damageEffect = tbl->table[i] >> 4 & 0xF; } if (!(collider->acFlags & AC_HARD)) { + Actor* attacker = collider->ac; + + if (collider->actor->category != ACTORCAT_PLAYER) { + damage *= Leveled_GetHealthAttackMultiplier(); + } else { + damage *= (1 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0)); + } + + if (info->acHit->atFlags & AT_TYPE_PLAYER) + attacker = &GET_PLAYER(play)->actor; + + damage = (u16)Leveled_DamageModify(collider->actor, attacker, damage); collider->actor->colChkInfo.damage += damage; } if (CVarGetInteger(CVAR_ENHANCEMENT("IvanCoopModeEnabled"), 0)) { collider->actor->colChkInfo.damage *= GET_PLAYER(play)->ivanDamageMultiplier; } + } /** diff --git a/soh/src/code/z_en_item00.c b/soh/src/code/z_en_item00.c index 0405494666f..0aa276a2d3e 100644 --- a/soh/src/code/z_en_item00.c +++ b/soh/src/code/z_en_item00.c @@ -1564,7 +1564,7 @@ s16 func_8001F404(s16 dropId) { } // clang-format on - if (dropId == ITEM00_HEART && gSaveContext.healthCapacity == gSaveContext.health) { + if (dropId == ITEM00_HEART && gSaveContext.healthCapacity2 == gSaveContext.health) { return ITEM00_RUPEE_GREEN; } diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index 451bdf9ab53..31f01650a39 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -390,12 +390,17 @@ void HealthMeter_Draw(PlayState* play) { f32 temp2; f32 temp3; f32 temp4; + u8 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + if (heartUnits < 4) { + heartUnits = 4; + CVarSetInteger("gLeveled.Difficulty.HeartUnits", 1); + } InterfaceContext* interfaceCtx = &play->interfaceCtx; GraphicsContext* gfxCtx = play->state.gfxCtx; Vtx* sp154 = interfaceCtx->beatingHeartVtx; - s32 curHeartFraction = gSaveContext.health % 0x10; - s16 totalHeartCount = gSaveContext.healthCapacity / 0x10; - s16 fullHeartCount = gSaveContext.health / 0x10; + s32 curHeartFraction = (s32)((f32)gSaveContext.health / heartUnits * 16) % 0x10; + s16 totalHeartCount = gSaveContext.healthCapacity2 / heartUnits; + s16 fullHeartCount = gSaveContext.health / heartUnits; s32 pad2; f32 sp144 = interfaceCtx->unk_22A * 0.1f; s32 curCombineModeSet = 0; @@ -410,7 +415,7 @@ void HealthMeter_Draw(PlayState* play) { OPEN_DISPS(gfxCtx); - if (!(gSaveContext.health % 0x10)) { + if (!(gSaveContext.health % heartUnits)) { fullHeartCount--; } @@ -669,15 +674,9 @@ void HealthMeter_HandleCriticalAlarm(PlayState* play) { u32 HealthMeter_IsCritical(void) { s32 var; - if (gSaveContext.healthCapacity <= 0x50) { - var = 0x10; - } else if (gSaveContext.healthCapacity <= 0xA0) { - var = 0x18; - } else if (gSaveContext.healthCapacity <= 0xF0) { - var = 0x20; - } else { - var = 0x2C; - } + s32 heartValue = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + + var = (s32)(heartValue + (f32)(((gSaveContext.healthCapacity2 / heartValue) - 3) * heartValue) * 0.103f); if (GameInteractor_Should(VB_HEALTH_METER_BE_CRITICAL, var >= gSaveContext.health && gSaveContext.health > 0)) { return true; diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index fc63f96c8a5..6684aa30b95 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -2823,6 +2823,12 @@ void Message_OpenText(PlayState* play, u16 textId) { } } msgCtx->msgLength = font->msgLength = GetEquipNowMessage(font->msgBuf, font->msgOffset, sizeof(font->msgBuf)); + } else if ((CVarGetInteger("gLeveled.Navi.TellEnemyLevel", 1) || + CVarGetInteger("gLeveled.Navi.TellEnemyMaxHP", 1)) && + (textId > 0x0600 && textId < 0x06FF) && play->actorCtx.targetCtx.targetedActor != NULL) { + Message_FindMessage(play, textId); + msgCtx->msgLength = font->msgLength = GetLeveledNaviEnemyInfo( + font->msgBuf, font->msgOffset, sizeof(font->msgBuf), play->actorCtx.targetCtx.targetedActor); } else { if (gSaveContext.language == LANGUAGE_JPN) { Message_FindMessageJPN(play, textId); @@ -4692,12 +4698,19 @@ void Message_Update(PlayState* play) { } if ((s32)(gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000) { gSaveContext.inventory.questItems ^= 0x40000000; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.health += heartUnits; + if (play != NULL) { + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); + } } else { gSaveContext.healthCapacity -= 0x10; - gSaveContext.health -= 0x10; + gSaveContext.health += heartUnits; + if (play != NULL) { + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); + } } } if (msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP_DONE) { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index ba9e1f70364..935af08b0a9 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2318,19 +2318,21 @@ u8 Item_Give(PlayState* play, u8 item) { gSaveContext.ship.stats.heartPieces++; return Return_Item(item, MOD_NONE, ITEM_NONE); } else if (item == ITEM_HEART_CONTAINER) { + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.health += heartUnits; } else { gSaveContext.healthCapacity -= 0x10; - gSaveContext.health -= 0x10; + gSaveContext.health -= heartUnits; } gSaveContext.ship.stats.heartContainers++; return Return_Item(item, MOD_NONE, ITEM_NONE); } else if (item == ITEM_HEART) { osSyncPrintf("回復ハート回復ハート回復ハート\n"); // "Recovery Heart" if (play != NULL) { - Health_ChangeBy(play, 0x10); + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + Health_ChangeBy(play, heartUnits); } return Return_Item(item, MOD_NONE, item); } else if (item == ITEM_MAGIC_SMALL) { @@ -2897,14 +2899,21 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { healthChange *= abs(giDefenseModifier); } } + + if (healthChange < 0) { + healthChange = (f32)healthChange * (f32)CVarGetInteger("gLeveled.Difficulty.Player.DamageMultiplier", 4) / 4.0f; + if (healthChange >= 0) + healthChange = -1; + ActorDamageNumber_New(GET_PLAYER(play), -healthChange); + } gSaveContext.health += healthChange; - if (gSaveContext.health > gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; + if (gSaveContext.health > gSaveContext.healthCapacity2) { + gSaveContext.health = gSaveContext.healthCapacity2; } - heartCount = gSaveContext.health % 0x10; + heartCount = gSaveContext.health / CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; healthLevel = heartCount; if (heartCount != 0) { @@ -3041,7 +3050,7 @@ void Inventory_ChangeAmmo(s16 item, s16 ammoChange) { void Magic_Fill(PlayState* play) { if (gSaveContext.isMagicAcquired) { gSaveContext.prevMagicState = gSaveContext.magicState; - gSaveContext.magicFillTarget = (gSaveContext.isDoubleMagicAcquired + 1) * MAGIC_NORMAL_METER; + gSaveContext.magicFillTarget = (gSaveContext.isDoubleMagicAcquired + 1) * gSaveContext.magicUnits; gSaveContext.magicState = MAGIC_STATE_FILL; } } @@ -3197,7 +3206,7 @@ void Interface_UpdateMagicBar(PlayState* play) { switch (gSaveContext.magicState) { case MAGIC_STATE_STEP_CAPACITY: - temp = gSaveContext.magicLevel * MAGIC_NORMAL_METER; + temp = gSaveContext.magicLevel * gSaveContext.magicUnits; if (gSaveContext.magicCapacity != temp) { if (gSaveContext.magicCapacity < temp) { gSaveContext.magicCapacity += 8; @@ -3225,6 +3234,9 @@ void Interface_UpdateMagicBar(PlayState* play) { // "Storage MAGIC_NOW=%d (%d)" osSyncPrintf("蓄電 MAGIC_NOW=%d (%d)\n", gSaveContext.magic, gSaveContext.magicFillTarget); + if (gSaveContext.magicFillTarget > gSaveContext.magicCapacity) { + gSaveContext.magicFillTarget = gSaveContext.magicCapacity; + } if (gSaveContext.magic >= gSaveContext.magicFillTarget) { gSaveContext.magic = gSaveContext.magicFillTarget; gSaveContext.magicState = gSaveContext.prevMagicState; @@ -3464,7 +3476,7 @@ void Interface_DrawMagicBar(PlayState* play) { s16 rMagicBarX; s16 PosX_MidEnd; s16 rMagicFillX; - s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 10); + s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 15); if (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) != ORIGINAL_LOCATION) { magicBarY = CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosY"), 0) + Y_Margins; if (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) == ANCHOR_LEFT) { @@ -3504,7 +3516,8 @@ void Interface_DrawMagicBar(PlayState* play) { } else if (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) == ANCHOR_TO_LIFE_METER) { magicBarY = R_MAGIC_BAR_SMALL_Y - 2 + - magicDrop * (lineLength == 0 ? 0 : (gSaveContext.healthCapacity - 1) / (0x10 * lineLength)) + + magicDrop * (lineLength == 0 ? 0 : (gSaveContext.healthCapacity2 - 1) / + ((CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) * lineLength)) + CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosY"), 0) + getHealthMeterYOffset(); s16 xPushover = CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosX"), 0) + getHealthMeterXOffset() + R_MAGIC_BAR_X - 1; @@ -3515,10 +3528,10 @@ void Interface_DrawMagicBar(PlayState* play) { R_MAGIC_FILL_X - 1; } } else { - if ((gSaveContext.healthCapacity - 1) / 0x10 >= lineLength && lineLength != 0) { + if ((gSaveContext.healthCapacity2 - 1) / (CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) >= lineLength && lineLength != 0) { magicBarY = magicBarY_original_l + - magicDrop * (lineLength == 0 ? 0 : ((gSaveContext.healthCapacity - 1) / (0x10 * lineLength) - 1)); + magicDrop * (lineLength == 0 ? 0 : ((gSaveContext.healthCapacity2 - 1) / ((CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) * lineLength) - 1)); } else { magicBarY = magicBarY_original_s; } @@ -5154,7 +5167,7 @@ void Interface_Draw(PlayState* play) { if (pauseCtx->debugState == 0) { Interface_InitVertices(play); func_8008A994(interfaceCtx); - if (fullUi || gSaveContext.health != gSaveContext.healthCapacity) { + if (fullUi || gSaveContext.health != gSaveContext.healthCapacity2) { HealthMeter_Draw(play); } @@ -5412,6 +5425,49 @@ void Interface_Draw(PlayState* play) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); } + // Draw Damage + Actor* currAct = play->actorCtx.actorLists[ACTORCAT_ENEMY].head; + if (currAct != NULL) { + while (currAct != NULL) { + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + currAct = play->actorCtx.actorLists[ACTORCAT_MISC].head; + if (currAct != NULL) { + while (currAct != NULL) { + if (currAct->id == ACTOR_EN_REEBA) + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + currAct = play->actorCtx.actorLists[ACTORCAT_NPC].head; + if (currAct != NULL) { + while (currAct != NULL) { + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + currAct = play->actorCtx.actorLists[ACTORCAT_BOSS].head; + if (currAct != NULL) { + while (currAct != NULL) { + ActorDamageNumber_Draw(play, currAct); + currAct = currAct->next; + } + } + + ActorDamageNumber_Draw(play, GET_PLAYER(play)); + + // Draw Experience Gain + + ActorExperienceNumber_Draw(play, GET_PLAYER(play)); + + // Draw Level Up + Actor_LevelUpDraw(play, GET_PLAYER(play)); + // Render enemy health bar after Z-target to leverage set variables if (CVarGetInteger(CVAR_ENHANCEMENT("EnemyHealthBar"), 0)) { Interface_DrawEnemyHealthBar(&play->actorCtx.targetCtx, play); @@ -5516,6 +5572,8 @@ void Interface_Draw(PlayState* play) { gDPPipeSync(OVERLAY_DISP++); + Leveled_Interface_DrawNextLevel(play); // Draw next level + // C-Left Button Icon & Ammo Count if (gSaveContext.equips.buttonItems[1] < 0xF0) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cLeftAlpha); @@ -5948,7 +6006,8 @@ void Interface_Draw(PlayState* play) { case 1: D_8015FFE2 = 20; D_8015FFE0 = 20; - gSaveContext.timerSeconds = gSaveContext.health >> 1; + u8 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + gSaveContext.timerSeconds = (s32)((f32)gSaveContext.health / heartUnits * 8); gSaveContext.timerState = 2; break; case 2: @@ -6628,18 +6687,19 @@ void Interface_Update(PlayState* play) { Map_Update(play); if (gSaveContext.healthAccumulator != 0) { - gSaveContext.healthAccumulator -= 4; - gSaveContext.health += 4; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + gSaveContext.healthAccumulator -= heartUnits >> 2; + gSaveContext.health += heartUnits >> 2; - if ((gSaveContext.health & 0xF) < 4) { + if ((gSaveContext.health % (heartUnits)) < heartUnits >> 2) { Audio_PlaySoundGeneral(NA_SE_SY_HP_RECOVER, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); } osSyncPrintf("now_life=%d max_life=%d\n", gSaveContext.health, gSaveContext.healthCapacity); - if (gSaveContext.health >= gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; + if (gSaveContext.health >= gSaveContext.healthCapacity2) { + gSaveContext.health = gSaveContext.healthCapacity2; osSyncPrintf("S_Private.now_life=%d S_Private.max_life=%d\n", gSaveContext.health, gSaveContext.healthCapacity); gSaveContext.healthAccumulator = 0; @@ -6786,7 +6846,8 @@ void Interface_Update(PlayState* play) { } if (gSaveContext.timerState == 0) { - if (((D_80125A58 == 1) || (D_80125A58 == 2) || (D_80125A58 == 4)) && ((gSaveContext.health >> 1) != 0)) { + u8 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + if (((D_80125A58 == 1) || (D_80125A58 == 2) || (D_80125A58 == 4)) && (((s32)((f32)gSaveContext.health / heartUnits * 8)) != 0)) { gSaveContext.timerState = 1; gSaveContext.timerX[0] = 140; gSaveContext.timerY[0] = 80; diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index c5c600459e7..9d36e7a33cb 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -681,6 +681,7 @@ void Player_SetEquipmentData(PlayState* play, Player* this) { this->currentSwordItemId = B_BTN_ITEM; Player_SetModelGroup(this, Player_ActionToModelGroup(this, this->heldItemAction)); Player_SetBootData(play, this); + Leveled_SetPlayerModifiedStats(this); } } @@ -787,6 +788,57 @@ s32 Player_GetStrength(void) { } } +void Player_GainExperience(PlayState* play, u16 experience) { + Player* player = GET_PLAYER(play); + + if (player == NULL) + return; + + experience = (u16)(CLAMP(round((f32)experience * CVarGetFloat("gLeveled.Difficulty.EXP.Rate", (f32)1.0)), 0, 0xffff)); + + bool levelUp = false; + u8 prevPower = player->actor.power; + u8 prevCourage = player->actor.courage; + u16 prevHealthCapacity = gSaveContext.healthCapacity2; + u16 prevMagicUnits = gSaveContext.magicUnits; + + if (player->actor.level == 99) + return; + + if (gSaveContext.experience < 999999) { + if (experience > 0) + gSaveContext.showNeededExpTimer = 60; + + gSaveContext.experience += experience; + ActorExperienceNumber_New(&player->actor, experience); + if (gSaveContext.experience > 999999) + gSaveContext.experience = 999999; + } + + while (GetActorStat_NextLevelExp(player->actor.level, gSaveContext.experience) <= 0 && player->actor.level < 99) { + player->actor.level += 1; + if (experience > 0) { + levelUp = true; + } + } + + Actor_RefreshLeveledStats(&player->actor, player); + + if (gSaveContext.magicLevel == 0) { + prevMagicUnits = gSaveContext.magicUnits; + } + + if (levelUp) { + gSaveContext.magicCapacity = gSaveContext.magicLevel * gSaveContext.magicUnits; + if (CVarGetInteger("gLeveled.HUD.LevelUp", 1) == 1) { + ActorLevelUp_New(&player->actor, player->actor.power - prevPower, player->actor.courage - prevCourage, gSaveContext.healthCapacity2 - prevHealthCapacity, gSaveContext.magicUnits - prevMagicUnits); + } + if (CVarGetInteger("gLeveled.HUD.LevelUpSound", 1) == 1) { + Audio_PlayFanfare(NA_BGM_ITEM_GET); + } + } +} + u8 Player_GetMask(PlayState* play) { Player* this = GET_PLAYER(play); diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c index 8fc6d1dde00..69928e11044 100644 --- a/soh/src/code/z_sram.c +++ b/soh/src/code/z_sram.c @@ -150,9 +150,8 @@ void Sram_OpenSave() { osSyncPrintf("scene_no = %d\n", gSaveContext.entranceIndex); osSyncPrintf(VT_RST); - if (gSaveContext.health < 0x30) { - gSaveContext.health = - CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity : 0x30; + if (gSaveContext.health < 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) { + gSaveContext.health = CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity2 : 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; } if (gSaveContext.scarecrowLongSongSet) { diff --git a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c index 2e3ad788843..86690fd6a3a 100644 --- a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c +++ b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c @@ -475,7 +475,7 @@ void BgDyYoseizo_HealPlayer_NoReward(BgDyYoseizo* this, PlayState* play) { } if (this->healingTimer == 110) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Magic_Fill(play); this->refillTimer = 200; } @@ -724,7 +724,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { switch (actionIndex) { case FAIRY_UPGRADE_MAGIC: gSaveContext.isMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_NORMAL_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits; Interface_ChangeAlpha(9); break; case FAIRY_UPGRADE_DOUBLE_MAGIC: @@ -732,7 +732,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { gSaveContext.isMagicAcquired = true; } gSaveContext.isDoubleMagicAcquired = true; - gSaveContext.magicFillTarget = MAGIC_DOUBLE_METER; + gSaveContext.magicFillTarget = gSaveContext.magicUnits * 2; gSaveContext.magicLevel = 0; Interface_ChangeAlpha(9); break; @@ -743,7 +743,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { } if (!this->healing) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; this->healing = true; if (actionIndex == 2) { Magic_Fill(play); @@ -771,7 +771,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { } this->itemSpawned = true; - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Interface_ChangeAlpha(9); gSaveContext.itemGetInf[1] |= sItemGetFlags[actionIndex]; Item_Give(play, sItemIds[actionIndex]); diff --git a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c index acba457abfe..d491cc0a990 100644 --- a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c +++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c @@ -329,7 +329,8 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { Animation_PlayLoop(&this->skelAnime, &object_kingdodongo_Anim_00F0D8); this->unk_1F8 = 1.0f; BossDodongo_SetupIntroCutscene(this, play); - this->health = 12; + Actor_GetLevelAndExperience(play, &this->actor, 0); + this->health = GetActorStat_EnemyMaxHealth(12, this->actor.level); this->colorFilterMin = 995.0f; this->actor.colChkInfo.mass = MASS_IMMOVABLE; this->colorFilterMax = 1000.0f; @@ -337,6 +338,8 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { this->unk_228 = 9200.0f; Collider_InitJntSph(play, &this->collider); Collider_SetJntSph(play, &this->collider, &this->actor, &sJntSphInit, this->items); + for (u8 i = 0; i < this->collider.count; i++) + this->collider.elements[i].info.toucher.damage <<= 1; // Double fire damage (to 1 heart) if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { // KD is dead // SOH [General] @@ -733,7 +736,9 @@ void BossDodongo_Explode(BossDodongo* this, PlayState* play) { Audio_PlayActorSound2(&this->actor, NA_SE_IT_BOMB_EXPLOSION); Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DAMAGE); func_80033E88(&this->actor, play, 4, 10); - this->health -= 2; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + this->health -= damage; + ActorDamageNumber_New(&this->actor, damage); // make sure not to die from the bomb explosion if (this->health <= 0) { @@ -1509,12 +1514,14 @@ void BossDodongo_UpdateDamage(BossDodongo* this, PlayState* play) { item1 = this->collider.elements[0].info.acHitInfo; if ((this->actionFunc == BossDodongo_Vulnerable) || (this->actionFunc == BossDodongo_LayDown)) { swordDamage = damage = CollisionCheck_GetSwordDamage(item1->toucher.dmgFlags, play); + swordDamage = Leveled_DamageModify(&this->actor, this->collider.base.actor, swordDamage * Leveled_GetHealthAttackMultiplier()); if (damage != 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DAMAGE); BossDodongo_SetupDamaged(this); this->unk_1C0 = 5; this->health -= swordDamage; + ActorDamageNumber_New(&this->actor, swordDamage); } } } diff --git a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c index 249b3afe965..807c4a05018 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c @@ -1295,12 +1295,14 @@ void BossFd_CollisionCheck(BossFd* this, PlayState* play) { if (headCollider->info.bumperFlags & BUMP_HIT) { headCollider->info.bumperFlags &= ~BUMP_HIT; hurtbox = headCollider->info.acHitInfo; - this->actor.colChkInfo.health -= 2; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + if (hurtbox->toucher.dmgFlags & 0x1000) { - this->actor.colChkInfo.health -= 2; - } - if ((s8)this->actor.colChkInfo.health <= 2) { - this->actor.colChkInfo.health = 2; + if (this->actor.colChkInfo.health + 1 >= damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 2; + } } this->work[BFD_DAMAGE_FLASH_TIMER] = 10; this->work[BFD_INVINC_TIMER] = 20; @@ -1486,7 +1488,8 @@ void BossFd_UpdateEffects(BossFd* this, PlayState* play) { diff.z = player->actor.world.pos.z - effect->pos.z; if ((this->timers[3] == 0) && (sqrtf(SQ(diff.x) + SQ(diff.y) + SQ(diff.z)) < 20.0f)) { this->timers[3] = 50; - func_8002F6D4(play, NULL, 5.0f, effect->kbAngle, 0.0f, 0x30); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 0x30); + func_8002F6D4(play, NULL, 5.0f, effect->kbAngle, 0.0f, damage); if (player->bodyIsBurning == false) { for (i2 = 0; i2 < ARRAY_COUNT(player->bodyFlameTimers); i2++) { player->bodyFlameTimers[i2] = Rand_S16Offset(0, 200); diff --git a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c index 0299016b9c5..0097b74927f 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c @@ -207,7 +207,7 @@ void BossFd2_Destroy(Actor* thisx, PlayState* play) { void BossFd2_SetupEmerge(BossFd2* this, PlayState* play) { BossFd* bossFd = (BossFd*)this->actor.parent; s16 temp_rand; - s8 health; + u16 health; osSyncPrintf("UP INIT 1\n"); Animation_PlayOnce(&this->skelAnime, &gHoleVolvagiaEmergeAnim); @@ -221,11 +221,11 @@ void BossFd2_SetupEmerge(BossFd2* this, PlayState* play) { this->timers[0] = 10; if (bossFd != NULL) { health = bossFd->actor.colChkInfo.health; - if (health >= 18) { + if (health >= GetActorStat_EnemyMaxHealth(18, this->actor.level)) { this->work[FD2_FAKEOUT_COUNT] = 0; - } else if (health >= 12) { + } else if (health >= GetActorStat_EnemyMaxHealth(12, this->actor.level)) { this->work[FD2_FAKEOUT_COUNT] = 1; - } else if (health >= 6) { + } else if (health >= GetActorStat_EnemyMaxHealth(6, this->actor.level)) { this->work[FD2_FAKEOUT_COUNT] = 2; } else { this->work[FD2_FAKEOUT_COUNT] = 3; @@ -234,7 +234,7 @@ void BossFd2_SetupEmerge(BossFd2* this, PlayState* play) { } void BossFd2_Emerge(BossFd2* this, PlayState* play) { - s8 health; + u16 health; BossFd* bossFd = (BossFd*)this->actor.parent; Player* player = GET_PLAYER(play); s16 i; @@ -258,13 +258,13 @@ void BossFd2_Emerge(BossFd2* this, PlayState* play) { this->work[FD2_HOLE_COUNTER]++; this->actor.world.pos.y = -200.0f; health = bossFd->actor.colChkInfo.health; - if (health == 24) { + if (health == GetActorStat_EnemyMaxHealth(24, this->actor.level)) { holeTime = 30; - } else if (health >= 18) { + } else if (health >= GetActorStat_EnemyMaxHealth(18, this->actor.level)) { holeTime = 25; - } else if (health >= 12) { + } else if (health >= GetActorStat_EnemyMaxHealth(12, this->actor.level)) { holeTime = 20; - } else if (health >= 6) { + } else if (health >= GetActorStat_EnemyMaxHealth(6, this->actor.level)) { holeTime = 10; } else { holeTime = 5; @@ -317,20 +317,20 @@ void BossFd2_Emerge(BossFd2* this, PlayState* play) { void BossFd2_SetupIdle(BossFd2* this, PlayState* play) { BossFd* bossFd = (BossFd*)this->actor.parent; - s8 health; + u16 health; s16 idleTime; osSyncPrintf("UP INIT 1\n"); Animation_PlayLoop(&this->skelAnime, &gHoleVolvagiaTurnAnim); this->actionFunc = BossFd2_Idle; health = bossFd->actor.colChkInfo.health; - if (health == 24) { + if (health == GetActorStat_EnemyMaxHealth(24, this->actor.level)) { idleTime = 50; - } else if (health >= 18) { + } else if (health >= GetActorStat_EnemyMaxHealth(18, this->actor.level)) { idleTime = 40; - } else if (health >= 12) { + } else if (health >= GetActorStat_EnemyMaxHealth(12, this->actor.level)) { idleTime = 40; - } else if (health >= 6) { + } else if (health >= GetActorStat_EnemyMaxHealth(6, this->actor.level)) { idleTime = 30; } else { idleTime = 20; @@ -384,7 +384,8 @@ void BossFd2_Burrow(BossFd2* this, PlayState* play) { } else { Math_ApproachF(&this->actor.world.pos.y, -100.0f, 1.0f, 10.0f); if (this->timers[0] == 0) { - if ((this->work[FD2_HOLE_COUNTER] >= 3) && ((s8)bossFd->actor.colChkInfo.health < 24)) { + if ((this->work[FD2_HOLE_COUNTER] >= 3) && + (bossFd->actor.colChkInfo.health < GetActorStat_EnemyMaxHealth(24, this->actor.level))) { this->work[FD2_HOLE_COUNTER] = 0; this->actionFunc = BossFd2_Wait; bossFd->handoffSignal = FD2_SIGNAL_FLY; @@ -844,8 +845,12 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { hurtbox = this->collider.elements[0].info.acHitInfo; if (!bossFd->faceExposed) { if (hurtbox->toucher.dmgFlags & 0x40000040) { - bossFd->actor.colChkInfo.health -= 2; - if ((s8)bossFd->actor.colChkInfo.health <= 2) { + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + + if (bossFd->actor.colChkInfo.health + 1 > damage) { + bossFd->actor.colChkInfo.health -= damage; + } else { bossFd->actor.colChkInfo.health = 1; } bossFd->faceExposed = true; @@ -872,7 +877,7 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { } } else { u8 canKill = false; - u8 damage; + u16 damage; if ((damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags, play)) == 0) { damage = (hurtbox->toucher.dmgFlags & 0x00001000) ? 4 : 2; @@ -882,15 +887,22 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) { if (hurtbox->toucher.dmgFlags & 0x80) { damage = 0; } - if (((s8)bossFd->actor.colChkInfo.health > 2) || canKill) { - bossFd->actor.colChkInfo.health -= damage; + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); + + if ((bossFd->actor.colChkInfo.health > 2) || canKill) { + if (bossFd->actor.colChkInfo.health >= damage) { + ActorDamageNumber_New(&this->actor, damage); + bossFd->actor.colChkInfo.health -= damage; + } else { + bossFd->actor.colChkInfo.health = 0; + } osSyncPrintf(VT_FGCOL(GREEN)); osSyncPrintf("damage %d\n", damage); } osSyncPrintf(VT_RST); osSyncPrintf("hp %d\n", bossFd->actor.colChkInfo.health); - if ((s8)bossFd->actor.colChkInfo.health <= 0) { + if (bossFd->actor.colChkInfo.health <= 0) { bossFd->actor.colChkInfo.health = 0; BossFd2_SetupDeath(this, play); this->work[FD2_DAMAGE_FLASH_TIMER] = 10; diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c index f845f323a20..4cfc1ec2d3a 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c @@ -584,7 +584,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) { this->unk_198 = 2; this->timers[2] = 110; if (GameInteractor_Should(VB_GANON_HEAL_BEFORE_FIGHT, true)) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } Audio_QueueSeqCmd(NA_BGM_STOP); } else { @@ -800,7 +800,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) { } if (this->csTimer == 25) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; } if (this->csTimer == 100) { @@ -1218,7 +1218,7 @@ void BossGanon_SetupTowerCutscene(BossGanon* this, PlayState* play) { this->csState = 100; this->unk_198 = 1; gSaveContext.magic = gSaveContext.magicCapacity; - gSaveContext.health = gSaveContext.healthCapacity; + gSaveContext.health = gSaveContext.healthCapacity2; } else { this->actionFunc = BossGanon_SetupTowerCutscene; } @@ -2277,7 +2277,7 @@ void BossGanon_Wait(BossGanon* this, PlayState* play) { } else if ((this->timers[0] == 0) && !(player->stateFlags1 & PLAYER_STATE1_HANGING_OFF_LEDGE)) { this->timers[0] = (s16)Rand_ZeroFloat(30.0f) + 30; - if ((s8)this->actor.colChkInfo.health >= 20) { + if (this->actor.colChkInfo.health >= GetActorStat_EnemyMaxHealth(20, this->actor.level)) { BossGanon_SetupChargeLightBall(this, play); } else if (Rand_ZeroOne() >= 0.5f) { if ((Rand_ZeroOne() >= 0.5f) || (this->actor.xzDistToPlayer > 350.0f)) { @@ -2769,7 +2769,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { } else if ((this->actionFunc == BossGanon_Vulnerable) && (this->unk_1C2 >= 3)) { if (!(acHitInfo->toucher.dmgFlags & 0x80)) { u8 hitWithSword = false; - u8 damage; + u16 damage; Vec3f sp50; u32 flags; @@ -2789,8 +2789,21 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { hitWithSword = true; } - if (((s8)this->actor.colChkInfo.health >= 3) || hitWithSword) { - this->actor.colChkInfo.health -= damage; + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + + if (hitWithSword) { + if (this->actor.colChkInfo.health >= damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } + } else { + if (this->actor.colChkInfo.health > damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 1; + } } for (i = 0; i < ARRAY_COUNT(sBossGanonCape->strands); i++) { @@ -2800,7 +2813,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) { } } - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { BossGanon_SetupDeathCutscene(this, play); Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DEAD); Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DD_THUNDER); diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c index 1799dafd0c4..a32ec8d7292 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c @@ -170,7 +170,7 @@ void BossGanon2_Init(Actor* thisx, PlayState* play) { } this->actor.colChkInfo.mass = MASS_IMMOVABLE; - this->actor.colChkInfo.health = 30; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(30, this->actor.level); Collider_InitJntSph(play, &this->unk_424); Collider_SetJntSph(play, &this->unk_424, &this->actor, &sJntSphInit1, this->unk_464); Collider_InitJntSph(play, &this->unk_444); @@ -1464,7 +1464,7 @@ void func_80900890(BossGanon2* this, PlayState* play) { if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { func_808FFDB0(this, play); if (this->unk_334 == 0) { - this->actor.colChkInfo.health = 25; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(25, this->actor.level); } this->unk_336 = 1; } @@ -1931,10 +1931,10 @@ void func_80902348(BossGanon2* this, PlayState* play) { } void func_80902524(BossGanon2* this, PlayState* play) { - s8 temp_v0_4; + u16 temp_v0_4; ColliderInfo* acHitInfo; s16 i; - u8 phi_v1_2; + u16 phi_v1_2; osSyncPrintf("this->no_hit_time %d\n", this->unk_316); if (this->unk_316 != 0 || ((this->unk_334 == 0) && (this->actionFunc == func_80900890))) { @@ -1959,9 +1959,15 @@ void func_80902524(BossGanon2* this, PlayState* play) { this->unk_342 = 5; Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_DAMAGE); Audio_StopSfxById(NA_SE_EN_MGANON_UNARI); - this->actor.colChkInfo.health -= 2; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + if (this->actor.colChkInfo.health >= damage) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 1; + } + ActorDamageNumber_New(&this->actor, damage); temp_v0_4 = this->actor.colChkInfo.health; - if (temp_v0_4 < 0x15 && this->unk_334 == 0) { + if (temp_v0_4 < GetActorStat_EnemyMaxHealth(21, this->actor.level) && this->unk_334 == 0) { func_80900818(this, play); } else { if (temp_v0_4 <= 0) { @@ -1993,11 +1999,20 @@ void func_80902524(BossGanon2* this, PlayState* play) { phi_v1_2 = 2; } } - this->actor.colChkInfo.health -= phi_v1_2; + u8 baseDamage = phi_v1_2; + + phi_v1_2 = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, phi_v1_2 * Leveled_GetHealthAttackMultiplier()); + if (phi_v1_2 <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= phi_v1_2; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, phi_v1_2); + temp_v0_4 = this->actor.colChkInfo.health; - if ((temp_v0_4 < 0x15) && (this->unk_334 == 0)) { + if ((temp_v0_4 < GetActorStat_EnemyMaxHealth(21, this->actor.level)) && (this->unk_334 == 0)) { func_80900818(this, play); - } else if ((temp_v0_4 <= 0) && (phi_v1_2 >= 2)) { + } else if ((temp_v0_4 <= 0) && (baseDamage >= 2)) { func_80901020(this, play); } else { if (temp_v0_4 <= 0) { diff --git a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c index 8f74d05a851..a18ef5409cc 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c @@ -458,7 +458,8 @@ void BossGanondrof_Neutral(BossGanondrof* this, PlayState* play) { if (this->timers[0] == 0) { this->timers[0] = (s16)(Rand_ZeroOne() * 64.0f) + 30; rand01 = Rand_ZeroOne(); - if (thisx->colChkInfo.health < 5) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(5, this->actor.level); + if (thisx->colChkInfo.health < healthCheck) { if (rand01 < 0.25f) { BossGanondrof_SetupThrow(this, play); } else if (rand01 >= 0.8f) { @@ -629,8 +630,10 @@ void BossGanondrof_Throw(BossGanondrof* this, PlayState* play) { if (Animation_OnFrame(&this->skelAnime, this->work[GND_THROW_FRAME])) { EnfHG* horseTemp = (EnfHG*)this->actor.child; - Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG_FIRE, this->spearTip.x, this->spearTip.y, + Actor* fireTemp = Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_FHG_FIRE, this->spearTip.x, this->spearTip.y, this->spearTip.z, this->work[GND_ACTION_STATE], 0, 0, FHGFIRE_ENERGY_BALL); + fireTemp->level = this->actor.level; + Actor_RefreshLeveledStats(fireTemp, GET_PLAYER(play)); this->actor.child = &horseTemp->actor; } @@ -1199,7 +1202,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { this->colliderBody.base.acFlags &= ~AC_HIT; } else { acHit = this->colliderBody.base.acFlags & AC_HIT; - if ((acHit && ((s8)this->actor.colChkInfo.health > 0)) || (this->returnCount != 0)) { + if ((acHit && (this->actor.colChkInfo.health > 0)) || (this->returnCount != 0)) { if (acHit) { this->colliderBody.base.acFlags &= ~AC_HIT; hurtbox = this->colliderBody.info.acHitInfo; @@ -1210,7 +1213,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { osSyncPrintf("hit != 0 \n"); } else if (this->actionFunc != BossGanondrof_Charge) { if (this->returnCount == 0) { - u8 dmg; + u16 dmg; u8 canKill = false; s32 dmgFlags = hurtbox->toucher.dmgFlags; @@ -1219,11 +1222,21 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { } dmg = CollisionCheck_GetSwordDamage(dmgFlags, play); (dmg == 0) ? (dmg = 2) : (canKill = true); - if (((s8)this->actor.colChkInfo.health > 2) || canKill) { - this->actor.colChkInfo.health -= dmg; + + dmg = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, dmg * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, dmg); + + if ((this->actor.colChkInfo.health > 2) || canKill) { + if (dmg < this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= dmg; + } else if (this->actor.colChkInfo.health > 2) { + this->actor.colChkInfo.health = 1; + } else { + this->actor.colChkInfo.health = 0; + } } - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { BossGanondrof_SetupDeath(this, play); Enemy_StartFinishingBlow(play, &this->actor); GameInteractor_ExecuteOnBossDefeat(&this->actor); @@ -1242,7 +1255,15 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) { } } else if (acHit && (hurtbox->toucher.dmgFlags & 0x0001F8A4)) { this->work[GND_INVINC_TIMER] = 10; - this->actor.colChkInfo.health -= 2; + + u16 dmg = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, 2 * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, dmg); + if (dmg < this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= dmg; + } else { + this->actor.colChkInfo.health = 0; + } + horse->hitTimer = 20; Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_DAMAGE); } diff --git a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c index 503e4147247..3caa68497a4 100644 --- a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c +++ b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c @@ -1837,9 +1837,16 @@ void BossGoma_UpdateHit(BossGoma* this, PlayState* play) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DAM2); } else if (this->actionFunc == BossGoma_FloorStunned && (damage = CollisionCheck_GetSwordDamage(acHitInfo->toucher.dmgFlags, play)) != 0) { - this->actor.colChkInfo.health -= damage; - if ((s8)this->actor.colChkInfo.health > 0) { + damage = Leveled_DamageModify(&this->actor, this->collider.elements[0].info.acHit->actor, damage * Leveled_GetHealthAttackMultiplier()); + if (damage <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, damage); + + if (this->actor.colChkInfo.health > 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DAM1); BossGoma_SetupFloorDamaged(this); EffectSsSibuki_SpawnBurst(play, &this->actor.focus.pos); diff --git a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c index b80c39f3b27..89207705990 100644 --- a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c +++ b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c @@ -1783,7 +1783,7 @@ void BossMo_CoreCollisionCheck(BossMo* this, PlayState* play) { // "hit 2 !!" osSyncPrintf("Core_Damage_check 当り 2 !!\n"); if ((this->work[MO_TENT_ACTION_STATE] != MO_CORE_UNDERWATER) && (this->work[MO_TENT_INVINC_TIMER] == 0)) { - u8 damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags, play); + u16 damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags, play); if ((damage != 0) && (this->work[MO_TENT_ACTION_STATE] < MO_CORE_ATTACK)) { // "sword hit !!" @@ -1791,14 +1791,21 @@ void BossMo_CoreCollisionCheck(BossMo* this, PlayState* play) { this->work[MO_TENT_ACTION_STATE] = MO_CORE_STUNNED; this->timers[0] = 25; + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + this->actor.speedXZ = 15.0f; this->actor.world.rot.y = this->actor.yawTowardsPlayer + 0x8000; this->work[MO_CORE_DMG_FLASH_TIMER] = 15; Audio_PlayActorSound2(&this->actor, NA_SE_EN_MOFER_CORE_DAMAGE); - this->actor.colChkInfo.health -= damage; + if (damage < this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } this->hitCount++; - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { if (((sMorphaTent1->csCamera == 0) && (sMorphaTent2 == NULL)) || ((sMorphaTent1->csCamera == 0) && (sMorphaTent2 != NULL) && (sMorphaTent2->csCamera == 0))) { Enemy_StartFinishingBlow(play, &this->actor); diff --git a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c index 6e1db9049a4..07da8364bd1 100644 --- a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c +++ b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c @@ -1895,8 +1895,9 @@ void BossSst_HandCrush(BossSst* this, PlayState* play) { } else { Player_PlaySfx(&player->actor, NA_SE_VO_LI_DAMAGE_S); } - - play->damagePlayer(play, -8); + + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); } if (Animation_OnFrame(&this->skelAnime, 0.0f)) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_CATCH); diff --git a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c index 1fee8de966f..cb7fb5af9d1 100644 --- a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c +++ b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c @@ -31,7 +31,7 @@ void BossTw_Update(Actor* thisx, PlayState* play); void BossTw_Draw(Actor* thisx, PlayState* play); void BossTw_Reset(void); -void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 arg2); +void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u16 arg2); void BossTw_TwinrovaSetupFly(BossTw* this, PlayState* play); void BossTw_DrawEffects(PlayState* play); void BossTw_TwinrovaLaugh(BossTw* this, PlayState* play); @@ -3091,7 +3091,7 @@ void BossTw_TwinrovaUpdate(Actor* thisx, PlayState* play2) { if (info->toucher.dmgFlags & (DMG_SLINGSHOT | DMG_ARROW)) {} } } else if (this->collider.base.acFlags & AC_HIT) { - u8 damage; + u16 damage; u8 swordDamage; ColliderInfo* info = this->collider.info.acHitInfo; @@ -3105,8 +3105,11 @@ void BossTw_TwinrovaUpdate(Actor* thisx, PlayState* play2) { swordDamage = true; } + damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, damage * Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + if (!(info->toucher.dmgFlags & DMG_HOOKSHOT)) { - if (((s8)this->actor.colChkInfo.health < 3) && !swordDamage) { + if ((this->actor.colChkInfo.health < GetActorStat_EnemyMaxHealth(3, this->actor.level)) && !swordDamage) { damage = 0; } @@ -5141,7 +5144,7 @@ void BossTw_TwinrovaChargeBlast(BossTw* this, PlayState* play) { Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000); if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { - if ((s8)this->actor.colChkInfo.health < 10) { + if (this->actor.colChkInfo.health < GetActorStat_EnemyMaxHealth(10, this->actor.level)) { sTwinrovaBlastType = Rand_ZeroFloat(1.99f); } else { if (++sFixedBlatSeq >= 4) { @@ -5241,7 +5244,7 @@ void BossTw_TwinrovaDoneBlastShoot(BossTw* this, PlayState* play) { Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000); } -void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) { +void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u16 damage) { if (this->actionFunc != BossTw_TwinrovaStun) { Animation_MorphToPlayOnce(&this->skelAnime, &gTwinrovaChargedAttackHitAnim, -15.0f); this->timers[0] = 150; @@ -5255,11 +5258,13 @@ void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) { this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&gTwinrovaDamageAnim); this->csState1 = 1; - if ((s8)(this->actor.colChkInfo.health -= damage) < 0) { + if (damage >= this->actor.colChkInfo.health) { this->actor.colChkInfo.health = 0; + } else { + this->actor.colChkInfo.health -= damage; } - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { BossTw_TwinrovaSetupDeathCS(this, play); Enemy_StartFinishingBlow(play, &this->actor); Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DEAD); diff --git a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c index 3e8f05776e6..7bde37a38ce 100644 --- a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c +++ b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c @@ -403,7 +403,7 @@ static s16 sDoorState; static u8 sPhase3StopMoving; static Vec3s sZapperRot; static u16 sPhase2Timer; -static s8 sPhase4HP; +static u16 sPhase4HP; void BossVa_SetupAction(BossVa* this, BossVaActionFunc func) { this->actionFunc = func; @@ -1362,7 +1362,7 @@ void BossVa_SetupBodyPhase4(BossVa* this, PlayState* play) { this->actor.world.rot.y = this->actor.yawTowardsPlayer; this->timer2 = (s16)(Rand_ZeroOne() * 150.0f) + 300; sBodyState = 1; - sPhase4HP = 4; + sPhase4HP = GetActorStat_EnemyMaxHealth(4, this->actor.level); if (this->actor.shape.yOffset != 0.0f) { this->timer = -30; } @@ -1400,11 +1400,21 @@ void BossVa_BodyPhase4(BossVa* this, PlayState* play) { this->actor.world.rot.y = this->actor.yawTowardsPlayer; Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_DAMAGE); Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 12); - sPhase4HP -= this->actor.colChkInfo.damage; + + u16 dmg = this->actor.colChkInfo.damage; + + if (dmg > sPhase4HP) { + sPhase4HP = 0; + } else { + sPhase4HP -= dmg; + } + + ActorDamageNumber_New(&this->actor, dmg); + if (sPhase4HP <= 0) { this->timer = 0; sFightPhase++; - sPhase4HP += 3; + sPhase4HP += GetActorStat_EnemyMaxHealth(3, this->actor.level); if (sFightPhase >= PHASE_DEATH) { BossVa_SetupBodyDeath(this, play); Enemy_StartFinishingBlow(play, &this->actor); diff --git a/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c index 4f16a06083d..2763cd168bd 100644 --- a/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c +++ b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c @@ -408,6 +408,7 @@ void func_809B75A0(EnBa* this, PlayState* play2) { Matrix_MultVec3f(&sp74, &this->unk_158[i + 1]); } this->unk_31A = 15; + Player_GainExperience(play, this->actor.exp); EnBa_SetupAction(this, EnBa_Die); } @@ -453,7 +454,14 @@ void EnBa_Update(Actor* thisx, PlayState* play) { if ((this->actor.params < EN_BA_DEAD_BLOB) && (this->collider.base.acFlags & 2)) { this->collider.base.acFlags &= ~2; - this->actor.colChkInfo.health--; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, Leveled_GetHealthAttackMultiplier()); + ActorDamageNumber_New(&this->actor, damage); + + if (damage > this->actor.colChkInfo.health) { + this->actor.colChkInfo.health = 0; + } else { + this->actor.colChkInfo.health -= damage; + } if (this->actor.colChkInfo.health == 0) { func_809B75A0(this, play); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); diff --git a/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c index 424f94a1c2d..c75d6061445 100644 --- a/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c +++ b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c @@ -533,7 +533,9 @@ void EnBb_Damage(EnBb* this, PlayState* play) { Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); if (this->actor.speedXZ == 0.0f) { this->actor.shape.yOffset = 200.0f; - EnBb_SetupDown(this); + if (this->actor.params > ENBB_GREEN) { + EnBb_SetupDown(this); + } } } @@ -1187,7 +1189,7 @@ void EnBb_CollisionCheck(EnBb* this, PlayState* play) { if ((this->action != BB_DOWN) || (this->timer < 190)) { Actor_ApplyDamage(&this->actor); } - if ((this->action != BB_DOWN) && (this->actor.params != ENBB_WHITE)) { + if ((this->action != BB_DOWN) && (this->actor.params != ENBB_WHITE) && (this->actor.params > ENBB_GREEN)) { EnBb_SetupDown(this); } } else { @@ -1204,13 +1206,13 @@ void EnBb_CollisionCheck(EnBb* this, PlayState* play) { EnBb_KillFlameTrail(this); } EnBb_SetupDeath(this, play); + Player_GainExperience(play, this->actor.exp); //! @bug //! Because Din's Fire kills the bubble in a single hit, Actor_SetColorFilter is never called and //! colorFilterParams is never set. And because Din's Fire halts updating during its cutscene, //! EnBb_Death doesn't kill the bubble on the next frame like it should. This combines with //! the bug in EnBb_Draw below to crash the game. - } else if ((this->actor.params == ENBB_WHITE) && - ((this->action == BB_WHITE) || (this->action == BB_STUNNED))) { + } else if ((this->actor.params == ENBB_WHITE) && ((this->action == BB_WHITE) || (this->action == BB_STUNNED)) || (this->actor.params <= ENBB_GREEN)) { Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0xC); this->actor.speedXZ = -8.0f; this->maxSpeed = 0.0f; diff --git a/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c b/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c index d06a234baa1..e85e2dbbfeb 100644 --- a/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c +++ b/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c @@ -181,7 +181,8 @@ void func_809BC598(EnBdfire* this, PlayState* play) { player->bodyFlameTimers[i] = Rand_S16Offset(0, 200); } player->bodyIsBurning = true; - func_8002F6D4(play, &this->actor, 20.0f, this->actor.world.rot.y, 0.0f, 8); + // Buffed damage from 8 to 16. + func_8002F6D4(play, &this->actor, 20.0f, this->actor.world.rot.y, 0.0f, 16); osSyncPrintf("POWER\n"); } } diff --git a/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c index 368bf865b25..d5f947971b2 100644 --- a/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c +++ b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c @@ -178,6 +178,7 @@ void EnBigokuta_Init(Actor* thisx, PlayState* play) { } CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, sColChkInfoInit); + Actor_GetLevelAndExperience(play, &this->actor, ACTOR_EN_BIGOKUTA); this->unk_194 = 1; diff --git a/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c index d8ba3877386..01756d1c7f3 100644 --- a/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c +++ b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c @@ -565,6 +565,10 @@ void EnBili_UpdateDamage(EnBili* this, PlayState* play) { this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; } + if (this->actor.colChkInfo.health > 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BARI_DAMAGE); + } + damageEffect = this->actor.colChkInfo.damageEffect; if (damageEffect == BIRI_DMGEFF_DEKUNUT) { @@ -580,7 +584,10 @@ void EnBili_UpdateDamage(EnBili* this, PlayState* play) { } EnBili_SetupDischargeLightning(this); } else { - EnBili_SetupBurnt(this); + if (this->actor.colChkInfo.health == 0) { + this->actor.params = EN_BILI_TYPE_DYING; + EnBili_SetupBurnt(this); + } } } else if (damageEffect == BIRI_DMGEFF_FIRE) { EnBili_SetupBurnt(this); @@ -590,7 +597,10 @@ void EnBili_UpdateDamage(EnBili* this, PlayState* play) { } else if (damageEffect == BIRI_DMGEFF_SLINGSHOT) { EnBili_SetupRecoil(this); } else { - EnBili_SetupBurnt(this); + if (this->actor.colChkInfo.health == 0) { + this->actor.params = EN_BILI_TYPE_DYING; + EnBili_SetupBurnt(this); + } } if (this->collider.info.acHitInfo->toucher.dmgFlags & 0x1F820) { // DMG_ARROW diff --git a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c index 712a9008d89..561c8f97dc2 100644 --- a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c +++ b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c @@ -206,7 +206,7 @@ void EnBomBowlPit_Reset(EnBomBowlPit* this, PlayState* play) { // "Normal termination"/"completion" osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST); if (this->getItemId == GI_HEART_PIECE) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; // "Ah recovery!" (?) osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ あぁ回復! ☆☆☆☆☆ \n" VT_RST); } diff --git a/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c index 6717b9788f8..4029ae466e6 100644 --- a/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c +++ b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c @@ -116,7 +116,7 @@ u32 func_809CBCEC(EnBubble* this) { } void EnBubble_DamagePlayer(EnBubble* this, PlayState* play) { - s32 damage = -this->colliderSphere.elements[0].info.toucher.damage; + s32 damage = -Leveled_DamageModify(&GET_PLAYER(play)->actor, &this->actor, this->colliderSphere.elements[0].info.toucher.damage); play->damagePlayer(play, damage); func_8002F7A0(play, &this->actor, 6.0f, this->actor.yawTowardsPlayer, 6.0f); diff --git a/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c index 38d3a9bf11c..1feb1aadd8f 100644 --- a/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c +++ b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c @@ -675,6 +675,7 @@ void func_809D0424(EnBw* this, PlayState* play) { this->unk_230 = 1; } Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); + Player_GainExperience(play, this->actor.exp); func_809D00F4(this); } } @@ -718,6 +719,7 @@ void func_809D0584(EnBw* this, PlayState* play) { this->unk_230 = 1; } Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); + Player_GainExperience(play, this->actor.exp); func_809D00F4(this); } } else if ((this->unk_220 != 1) && (this->unk_220 != 6)) { diff --git a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c index 0b4f3bf0a55..3bc12bddfb1 100644 --- a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c +++ b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c @@ -372,10 +372,19 @@ void EnClearTag_Update(Actor* thisx, PlayState* play2) { this->acceleration.z = Rand_CenteredFloat(15.0f); Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_THUNDER_GND); - this->actor.colChkInfo.health--; - if ((s8)this->actor.colChkInfo.health <= 0) { + + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, Leveled_GetHealthAttackMultiplier()); + if (damage <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= damage; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, damage); + + if (this->actor.colChkInfo.health <= 0) { this->state = CLEAR_TAG_STATE_CRASHING; this->actor.velocity.y = 0.0f; + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); goto state_crashing; } diff --git a/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c index 7ab5fbd8410..99008b32edd 100644 --- a/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c +++ b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c @@ -413,7 +413,7 @@ void EnCrow_Respawn(EnCrow* this, PlayState* play) { if (Math_StepToF(&this->actor.scale.x, target, target * 0.1f)) { this->actor.flags |= ACTOR_FLAG_ATTENTION_ENABLED; this->actor.flags &= ~ACTOR_FLAG_UPDATE_CULLING_DISABLED; - this->actor.colChkInfo.health = 1; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(1, this->actor.level); EnCrow_SetupFlyIdle(this); } this->actor.scale.z = this->actor.scale.y = this->actor.scale.x; @@ -428,10 +428,17 @@ void EnCrow_UpdateDamage(EnCrow* this, PlayState* play) { if (this->actor.colChkInfo.damageEffect == 1) { // Deku Nuts EnCrow_SetupTurnAway(this); } else { - Actor_ApplyDamage(&this->actor); - this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; - Enemy_StartFinishingBlow(play, &this->actor); - EnCrow_SetupDamaged(this, play); + if (Actor_ApplyDamage(&this->actor) == 0) { + this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; + Enemy_StartFinishingBlow(play, &this->actor); + EnCrow_SetupDamaged(this, play); + } else { + EnCrow_SetupFlyIdle(this); + this->timer = (s16)Rand_ZeroFloat(25) + 30; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_KAICHO_DAMAGE); + this->actor.flags |= ACTOR_FLAG_UPDATE_CULLING_DISABLED; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 6); + } } } } diff --git a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c index b84103a4a0b..f42176016ec 100644 --- a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c +++ b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c @@ -1047,6 +1047,7 @@ void EnDekubaba_UpdateDamage(EnDekubaba* this, PlayState* play) { ((this->actor.colChkInfo.damageEffect != DEKUBABA_DMGEFF_NONE) || (this->actor.colChkInfo.damage != 0))) { phi_s0 = this->actor.colChkInfo.health - this->actor.colChkInfo.damage; + ActorDamageNumber_New(&this->actor, this->actor.colChkInfo.damage); if (this->actionFunc != EnDekubaba_StunnedVertical) { if ((this->actor.colChkInfo.damageEffect == DEKUBABA_DMGEFF_BOOMERANG) || diff --git a/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c index ac2b5f7ee11..9acf20c5383 100644 --- a/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c +++ b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c @@ -407,7 +407,11 @@ void EnDekunuts_Gasp(EnDekunuts* this, PlayState* play) { void EnDekunuts_BeDamaged(EnDekunuts* this, PlayState* play) { Math_StepToF(&this->actor.speedXZ, 0.0f, 1.0f); if (SkelAnime_Update(&this->skelAnime)) { - EnDekunuts_SetupDie(this); + if (this->actor.colChkInfo.health == 0) { + EnDekunuts_SetupDie(this); + } else { + EnDekunuts_SetupRun(this); + } } } diff --git a/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c index 5f09f29875e..685fe347998 100644 --- a/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c +++ b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c @@ -493,9 +493,10 @@ void EnDh_CollisionCheck(EnDh* this, PlayState* play) { EnDh_SetupDeath(this); Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); } else { - if (((lastHealth >= 15) && (this->actor.colChkInfo.health < 15)) || - ((lastHealth >= 9) && (this->actor.colChkInfo.health < 9)) || - ((lastHealth >= 3) && (this->actor.colChkInfo.health < 3))) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(3, this->actor.level); + if (((lastHealth >= healthCheck * 5) && (this->actor.colChkInfo.health < healthCheck * 5)) || + ((lastHealth >= healthCheck * 3) && (this->actor.colChkInfo.health < healthCheck * 3)) || + ((lastHealth >= healthCheck) && (this->actor.colChkInfo.health < healthCheck))) { this->retreat++; } diff --git a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c index 07a244cd3cc..c336c8b8f06 100644 --- a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c +++ b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c @@ -665,6 +665,7 @@ void EnDodongo_SetupDeath(EnDodongo* this, PlayState* play) { Animation_MorphToPlayOnce(&this->skelAnime, &gDodongoDieAnim, -8.0f); this->timer = 0; Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_J_DEAD); + Player_GainExperience(play, this->actor.exp); this->actionState = DODONGO_DEATH; this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; this->actor.speedXZ = 0.0f; diff --git a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c index 96a9297b969..1632ffd3b72 100644 --- a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c +++ b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c @@ -636,7 +636,7 @@ void func_80A0329C(EnElf* this, PlayState* play) { if ((heightDiff > 0.0f) && (heightDiff < 60.0f)) { if (!func_80A01F90(&this->actor.world.pos, &refActor->actor.world.pos, 10.0f)) { if (GameInteractor_Should(VB_FAIRY_HEAL, true, this)) { - Health_ChangeBy(play, 128); + Health_ChangeBy(play, (CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2) << 3); } if (this->fairyFlags & FAIRY_FLAG_BIG) { Magic_Fill(play); diff --git a/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c index ced99da23e2..9333bb5dbd0 100644 --- a/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c +++ b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c @@ -630,20 +630,18 @@ void EnFirefly_UpdateDamage(EnFirefly* this, PlayState* play) { this->collider.base.acFlags &= ~AC_HIT; Actor_SetDropFlag(&this->actor, &this->collider.elements[0].info, 1); - if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { - if (Actor_ApplyDamage(&this->actor) == 0) { - Enemy_StartFinishingBlow(play, &this->actor); - this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; - } + if ((this->actor.colChkInfo.damageEffect != 0 || this->actor.colChkInfo.damage != 0) && + ((this->actor.colorFilterTimer == 0) || ((this->actor.colorFilterParams & 0x4000) == 0))) { damageEffect = this->actor.colChkInfo.damageEffect; if (damageEffect == 2) { // Din's Fire if (this->actor.params == KEESE_ICE_FLY) { - this->actor.colChkInfo.health = 0; - Enemy_StartFinishingBlow(play, &this->actor); - EnFirefly_Combust(this, play); - EnFirefly_SetupFall(this); + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); + EnFirefly_Combust(this, play); + EnFirefly_SetupFall(this); + } } else if (!this->onFire) { EnFirefly_Ignite(this); if (this->actionFunc == EnFirefly_Perch) { @@ -654,17 +652,38 @@ void EnFirefly_UpdateDamage(EnFirefly* this, PlayState* play) { if (this->actor.params == KEESE_ICE_FLY) { EnFirefly_SetupFall(this); } else { - EnFirefly_SetupFrozenFall(this, play); + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); + this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; + EnFirefly_SetupFrozenFall(this, play); + } } } else if (damageEffect == 1) { // Deku Nuts if (this->actionFunc != EnFirefly_Stunned) { EnFirefly_SetupStunned(this); } - } else { // Fire Arrows - if ((damageEffect == 0xF) && (this->actor.params == KEESE_ICE_FLY)) { + } else if ((damageEffect == 0xF) && (this->actor.params == KEESE_ICE_FLY)) { // Fire Arrows + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); EnFirefly_Combust(this, play); + } else { + EnFirefly_SetupFall(this); + } + } else { + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(play, &this->actor); + this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; + EnFirefly_SetupFall(this); + } + } + + if (this->actor.colChkInfo.health > 0 && damageEffect != 1) { + if (this->actionFunc == EnFirefly_Perch) { + EnFirefly_SetupFlyIdle(this); } - EnFirefly_SetupFall(this); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FFLY_DEAD); + this->actor.flags |= ACTOR_FLAG_UPDATE_CULLING_DISABLED; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 6); } } } diff --git a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c index b76f1fd9f09..5a667b05b97 100644 --- a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c +++ b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c @@ -740,6 +740,11 @@ void EnFloormas_SmDecideAction(EnFloormas* this, PlayState* play) { void EnFloormas_SmShrink(EnFloormas* this, PlayState* play) { if (Math_StepToF(&this->actor.scale.x, 0.0f, 0.0015f)) { EnFloormas_SetupSmWait(this); + EnFloormas* parent = (EnFloormas*)this->actor.parent; + EnFloormas* child = (EnFloormas*)this->actor.child; + if ((parent->actionFunc == EnFloormas_SmWait) && (child->actionFunc == EnFloormas_SmWait)) { + Player_GainExperience(play, this->actor.exp); + } } this->actor.scale.z = this->actor.scale.x; this->actor.scale.y = this->actor.scale.x; @@ -823,7 +828,8 @@ void EnFloormas_GrabLink(EnFloormas* this, PlayState* play) { } else { Player_PlaySfx(&player->actor, NA_SE_VO_LI_DAMAGE_S); } - play->damagePlayer(play, -8); + u16 damage = (u16)Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); } } diff --git a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c index 1cbe3bed2e7..d1f7e3b929e 100644 --- a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c +++ b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c @@ -153,7 +153,14 @@ s32 EnFw_CheckCollider(EnFw* this, PlayState* play) { this->lastDmgHook = false; } this->collider.base.acFlags &= ~AC_HIT; - if (Actor_ApplyDamage(&this->actor) <= 0) { + + if (this->actor.colChkInfo.damage >= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health = 0; + } else { + this->actor.colChkInfo.health -= this->actor.colChkInfo.damage; + } + + if (this->actor.colChkInfo.health <= 0) { if (this->actor.parent->colChkInfo.health <= 8) { Enemy_StartFinishingBlow(play, &this->actor); this->actor.parent->colChkInfo.health = 0; diff --git a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c index 217afe31f29..172b9e72676 100644 --- a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c +++ b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c @@ -365,6 +365,7 @@ void EnFz_ApplyDamage(EnFz* this, PlayState* play) { vec.z = this->actor.world.pos.z; EnFz_Damaged(this, play, &vec, 30, 10.0f); EnFz_SetupDespawn(this, play); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } } @@ -373,6 +374,7 @@ void EnFz_ApplyDamage(EnFz* this, PlayState* play) { Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 8); if (this->actor.colChkInfo.health == 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DEAD); + Player_GainExperience(play, this->actor.exp); EnFz_SetupMelt(this); } else { Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DAMAGE); @@ -718,17 +720,18 @@ void EnFz_Draw(Actor* thisx, PlayState* play) { EnFz* this = (EnFz*)thisx; s32 pad; s32 index; + + index = (6 - (u8)CLAMP(((f32)this->actor.colChkInfo.health / GetActorStat_EnemyMaxHealth(6, this->actor.level) * 5 + 0.99999f), 0, 6)) >> 1; - index = (6 - this->actor.colChkInfo.health) >> 1; - + // Leveled Mod - Already does this. // SOH [Enhancement] - With enemy health scaling, the Freezards health could cause an index out of bounds for the // displayLists, so we need to recompute the index based on the scaled health (using the maximum health value) and // clamp the final result for safety. - if (CVarGetInteger(CVAR_ENHANCEMENT("EnemySizeScalesHealth"), 0)) { + /* if (CVarGetInteger(CVAR_ENHANCEMENT("EnemySizeScalesHealth"), 0)) { u8 scaledHealth = (u8)(((f32)this->actor.colChkInfo.health / this->actor.maximumHealth) * 6); index = (6 - scaledHealth) >> 1; index = CLAMP(index, 0, 2); - } + }*/ OPEN_DISPS(play->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c index 277677360b4..87e22131507 100644 --- a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c +++ b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c @@ -724,7 +724,7 @@ s32 EnGirlA_CanBuy_ZoraTunic(PlayState* play, EnGirlA* this) { } s32 EnGirlA_CanBuy_Health(PlayState* play, EnGirlA* this) { - if (gSaveContext.healthCapacity == gSaveContext.health) { + if (gSaveContext.healthCapacity2 == gSaveContext.health) { return CANBUY_RESULT_CANT_GET_NOW; } if (gSaveContext.rupees < this->basePrice) { diff --git a/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c index fa95087bf71..b57666f3726 100644 --- a/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c +++ b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c @@ -329,7 +329,7 @@ void EnGoma_SetupHurt(EnGoma* this, PlayState* play) { Animation_GetLastFrame(&gObjectGolDamagedAnim), ANIMMODE_ONCE, -2.0f); this->actionFunc = EnGoma_Hurt; - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { this->actionTimer = 5; Enemy_StartFinishingBlow(play, &this->actor); } else { @@ -353,7 +353,7 @@ void EnGoma_Hurt(EnGoma* this, PlayState* play) { } if (this->actionTimer == 0) { - if ((s8)this->actor.colChkInfo.health <= 0) { + if (this->actor.colChkInfo.health <= 0) { EnGoma_SetupDie(this); } else { EnGoma_SetupFlee(this); @@ -620,7 +620,7 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { this->hurtTimer--; } else { ColliderInfo* acHitInfo; - u8 swordDamage; + u16 swordDamage; if ((this->colCyl1.base.atFlags & 2) && this->actionFunc == EnGoma_Jump) { EnGoma_SetupLand(this); @@ -628,7 +628,7 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { this->actor.velocity.y = 0.0f; } - if ((this->colCyl2.base.acFlags & AC_HIT) && (s8)this->actor.colChkInfo.health > 0) { + if ((this->colCyl2.base.acFlags & AC_HIT) && this->actor.colChkInfo.health > 0) { acHitInfo = this->colCyl2.info.acHitInfo; this->colCyl2.base.acFlags &= ~AC_HIT; @@ -659,7 +659,14 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) { swordDamage = 1; } - this->actor.colChkInfo.health -= swordDamage; + swordDamage = Leveled_DamageModify(&this->actor, &player->actor, swordDamage * Leveled_GetHealthAttackMultiplier()); + if (swordDamage <= this->actor.colChkInfo.health) { + this->actor.colChkInfo.health -= swordDamage; + } else { + this->actor.colChkInfo.health = 0; + } + ActorDamageNumber_New(&this->actor, swordDamage); + EnGoma_SetupHurt(this, play); Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 5); this->hurtTimer = 13; diff --git a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c index c4dbe3f3af0..0b1b2b824d4 100644 --- a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c +++ b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c @@ -686,7 +686,7 @@ void func_80A75C38(EnIk* this, PlayState* play) { f32 temp_f0; u8 pad; u8 pad2; - u8 prevHealth; + u16 prevHealth; s32 temp_v0_3; Vec3f sp38; @@ -720,20 +720,21 @@ void func_80A75C38(EnIk* this, PlayState* play) { } Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0xC); prevHealth = this->actor.colChkInfo.health; + u16 healthCheck = GetActorStat_EnemyMaxHealth(10, this->actor.level); Actor_ApplyDamage(&this->actor); if (this->actor.params != 0) { - if ((prevHealth > 10) && (this->actor.colChkInfo.health <= 10)) { + if ((prevHealth > healthCheck) && (this->actor.colChkInfo.health <= healthCheck)) { this->unk_2FB = 1; BodyBreak_Alloc(&this->bodyBreak, 3, play); } - } else if (this->actor.colChkInfo.health <= 10) { + } else if (this->actor.colChkInfo.health <= healthCheck) { Actor_ChangeCategory(play, &play->actorCtx, &this->actor, ACTORCAT_BOSS); SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 20, NA_SE_EN_LAST_DAMAGE); if (this->switchFlags != 0xFF) { Flags_SetSwitch(play, this->switchFlags); } return; - } else if (prevHealth == 50) { + } else if (prevHealth == GetActorStat_EnemyMaxHealth(50, this->actor.level)) { Actor_ChangeCategory(play, &play->actorCtx, &this->actor, ACTORCAT_ENEMY); } @@ -771,7 +772,8 @@ void func_80A75FA0(Actor* thisx, PlayState* play) { this->unk_2FA = this->unk_2FB; func_80A75C38(this, play); - if ((this->actor.params == 0) && (this->actor.colChkInfo.health <= 10)) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(10, this->actor.level); + if ((this->actor.params == 0) && (this->actor.colChkInfo.health <= healthCheck)) { func_80A781CC(&this->actor, play); return; } @@ -785,7 +787,8 @@ void func_80A75FA0(Actor* thisx, PlayState* play) { player->invincibilityTimer = 0; } else { player->invincibilityTimer = 0; - play->damagePlayer(play, -64); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 64); + play->damagePlayer(play, -damage); this->unk_2FE = 0; } } diff --git a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c index 78f75dabd5a..a2642de465d 100644 --- a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c +++ b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c @@ -625,6 +625,7 @@ void EnMb_Stunned(EnMb* this, PlayState* play) { if (this->actor.params == ENMB_TYPE_CLUB) { if (this->actor.colChkInfo.health == 0) { EnMb_SetupClubDead(this); + Player_GainExperience(play, this->actor.exp); } else if (this->state == ENMB_STATE_CLUB_KNEELING) { /* dead code: the setup for this action sets state to something else */ EnMb_SetupClubDamagedWhileKneeling(this); @@ -634,6 +635,7 @@ void EnMb_Stunned(EnMb* this, PlayState* play) { } else { if (this->actor.colChkInfo.health == 0) { EnMb_SetupSpearDead(this); + Player_GainExperience(play, this->actor.exp); } else { EnMb_SetupSpearDamaged(this); } @@ -1446,6 +1448,7 @@ void EnMb_CheckColliding(EnMb* this, PlayState* play) { if (this->actor.params == ENMB_TYPE_CLUB) { if (this->actor.colChkInfo.health == 0) { EnMb_SetupClubDead(this); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } else if (this->state != ENMB_STATE_CLUB_KNEELING) { EnMb_SetupClubDamaged(this); @@ -1453,6 +1456,7 @@ void EnMb_CheckColliding(EnMb* this, PlayState* play) { } else { if (this->actor.colChkInfo.health == 0) { EnMb_SetupSpearDead(this); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } else { EnMb_SetupSpearDamaged(this); diff --git a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c index 33133be7ba7..1a25238108c 100644 --- a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c +++ b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c @@ -871,6 +871,7 @@ void EnPeehat_StateExplode(EnPeehat* this, PlayState* play) { if (this->animTimer == 5) { bomb = (EnBom*)Actor_Spawn(&play->actorCtx, play, ACTOR_EN_BOM, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0x602, 0, true); + Player_GainExperience(play, this->actor.exp); if (bomb != NULL) { bomb->timer = 0; } diff --git a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c index e9b4ee2b6f9..7b6d6babbed 100644 --- a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c +++ b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c @@ -502,7 +502,8 @@ void func_80AE3454(EnRd* this, PlayState* play) { case 1: Animation_PlayLoop(&this->skelAnime, &gGibdoRedeadGrabAttackAnim); this->unk_304++; - play->damagePlayer(play, -8); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); func_800AA000(this->actor.xzDistToPlayer, 0xFF, 1, 0xC); this->unk_319 = 20; case 0: @@ -536,7 +537,8 @@ void func_80AE3454(EnRd* this, PlayState* play) { this->unk_319--; if (this->unk_319 == 0) { - play->damagePlayer(play, -8); + u16 damage = Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); func_800AA000(this->actor.xzDistToPlayer, 0xF0, 1, 0xC); this->unk_319 = 20; Player_PlaySfx(&player->actor, NA_SE_VO_LI_DAMAGE_S + player->ageProperties->unk_92); @@ -730,6 +732,7 @@ void func_80AE3ECC(EnRd* this, PlayState* play) { if (this->actor.colChkInfo.health == 0) { func_80AE2630(play, &this->actor, 1); func_80AE3C20(this); + Player_GainExperience(play, this->actor.exp); Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90); } else { func_80AE3A8C(this); @@ -801,6 +804,7 @@ void func_80AE4114(EnRd* this, PlayState* play) { if (this->actor.colChkInfo.health == 0) { func_80AE2630(play, &this->actor, 1); func_80AE3C20(this); + Player_GainExperience(play, this->actor.exp); Item_DropCollectibleRandom(play, 0, &this->actor.world.pos, 0x90); } else { func_80AE3A8C(this); diff --git a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c index 831e11b8c38..0e2672fe0fd 100644 --- a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c +++ b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c @@ -335,6 +335,7 @@ void EnRr_SetupReleasePlayer(EnRr* this, PlayState* play) { EnRr_SetupDamage(this); } else { EnRr_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); } } @@ -463,6 +464,7 @@ void EnRr_CollisionCheck(EnRr* this, PlayState* play) { } else { this->dropType = dropType; EnRr_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); } return; case RR_DMG_FIRE: // Fire Arrow and Din's Fire @@ -756,6 +758,7 @@ void EnRr_Stunned(EnRr* this, PlayState* play) { this->actionFunc = EnRr_Approach; } else { EnRr_SetupDeath(this); + Player_GainExperience(play, this->actor.exp); } } } diff --git a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c index bd926454504..90b766f714f 100644 --- a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c +++ b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c @@ -142,6 +142,8 @@ void func_80AFB950(EnSi* this, PlayState* play) { player->actor.freezeTimer = 10; } else { SET_GS_FLAGS((this->actor.params & 0x1F00) >> 8, this->actor.params & 0xFF); + u16 experience = Leveled_GoldSkulltulaExperience(gSaveContext.inventory.gsTokens); + Player_GainExperience(play, experience); GameInteractor_ExecuteOnFlagSet(FLAG_GS_TOKEN, this->actor.params); Actor_Kill(&this->actor); } diff --git a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c index b7612dd9179..0520e1ffdb4 100644 --- a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c +++ b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c @@ -420,6 +420,7 @@ void func_80AFD7B4(EnSkb* this, PlayState* play) { this->unk_283 |= 4; EffectSsDeadSound_SpawnStationary(play, &this->actor.projectedPos, NA_SE_EN_STALKID_DEAD, 1, 1, 0x28); EnSkb_SetupAction(this, func_80AFD880); + Player_GainExperience(play, this->actor.exp); GameInteractor_ExecuteOnEnemyDefeat(&this->actor); } diff --git a/soh/src/overlays/actors/ovl_En_St/z_en_st.c b/soh/src/overlays/actors/ovl_En_St/z_en_st.c index 99f4617bb9a..456e83b67fd 100644 --- a/soh/src/overlays/actors/ovl_En_St/z_en_st.c +++ b/soh/src/overlays/actors/ovl_En_St/z_en_st.c @@ -401,7 +401,8 @@ s32 EnSt_CheckHitLink(EnSt* this, PlayState* play) { } this->gaveDamageSpinTimer = 30; - play->damagePlayer(play, -8); + u16 damage = (u16)Leveled_DamageModify(&player->actor, &this->actor, 8); + play->damagePlayer(play, -damage); Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); func_8002F71C(play, &this->actor, 4.0f, this->actor.yawTowardsPlayer, 6.0f); return true; diff --git a/soh/src/overlays/actors/ovl_En_Test/z_en_test.c b/soh/src/overlays/actors/ovl_En_Test/z_en_test.c index 72969cc2e83..130c3329f93 100644 --- a/soh/src/overlays/actors/ovl_En_Test/z_en_test.c +++ b/soh/src/overlays/actors/ovl_En_Test/z_en_test.c @@ -317,6 +317,8 @@ void EnTest_Destroy(Actor* thisx, PlayState* play) { if ((this->actor.params != STALFOS_TYPE_2) && !Actor_FindNearby(play, &this->actor, ACTOR_EN_TEST, ACTORCAT_ENEMY, 8000.0f)) { func_800F5B58(); + if (this->actor.ignoreExpReward) + Player_GainExperience(play, this->actor.exp); } Effect_Delete(play, this->effectIndex); @@ -1510,7 +1512,7 @@ void func_80862E6C(EnTest* this, PlayState* play) { } } else { if (this->actor.home.rot.x == 0) { - this->actor.colChkInfo.health = 10; + this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(10, this->actor.level); if (this->actor.params == STALFOS_TYPE_4) { this->actor.params = -1; diff --git a/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c index a7574e17919..1cfc528f669 100644 --- a/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c +++ b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c @@ -726,6 +726,7 @@ void EnTite_Stunned(EnTite* this, PlayState* play) { this->actor.world.rot.y = this->actor.shape.rot.y; if (this->actor.colChkInfo.health == 0) { EnTite_SetupDeathCry(this); + Player_GainExperience(play, this->actor.exp); } else if (this->flipState == TEKTITE_FLIPPED) { EnTite_SetupFlipUpright(this); } else if (((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f) && @@ -867,6 +868,7 @@ void EnTite_CheckDamage(Actor* thisx, PlayState* play) { } if (thisx->colChkInfo.health == 0) { EnTite_SetupDeathCry(this); + Player_GainExperience(play, this->actor.exp); } else { // Flip tektite back up if it's on its back Audio_PlayActorSound2(thisx, NA_SE_EN_TEKU_DAMAGE); diff --git a/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c b/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c index 7d9861dae62..d28355d6978 100644 --- a/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c +++ b/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c @@ -601,6 +601,7 @@ void EnTp_UpdateDamage(EnTp* this, PlayState* play) { if (head->actor.params <= TAILPASARAN_HEAD) { EnTp_SetupDie(head); + Player_GainExperience(play, this->actor.exp); head->damageEffect = this->actor.colChkInfo.damageEffect; head->actor.params = TAILPASARAN_HEAD_DYING; } diff --git a/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c index a26e3eab97e..935f458dc7e 100644 --- a/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c +++ b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c @@ -388,6 +388,7 @@ void EnVm_Die(EnVm* this, PlayState* play) { bomb->timer = 0; } + Player_GainExperience(play, this->actor.exp); Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0xA0); Actor_Kill(&this->actor); } @@ -397,7 +398,10 @@ void EnVm_CheckHealth(EnVm* this, PlayState* play) { EnBom* bomb; if (Actor_GetCollidedExplosive(play, &this->colliderCylinder.base) != NULL) { - this->actor.colChkInfo.health--; + u16 damage = Leveled_DamageModify(&this->actor, &GET_PLAYER(play)->actor, Leveled_GetHealthAttackMultiplier()); + this->actor.colChkInfo.damage += damage; + ActorDamageNumber_New(&this->actor, damage); + Actor_ApplyDamage(&this->actor); osSyncPrintf("hp down %d\n", this->actor.colChkInfo.health); } else { if (!(this->colliderQuad2.base.acFlags & AC_HIT) || this->unk_21C == 2) { diff --git a/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c index 99d9fba48a6..942285d8461 100644 --- a/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c +++ b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c @@ -1945,6 +1945,7 @@ void EnZf_Die(EnZf* this, PlayState* play) { if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* miniboss */ && (D_80B4A1B4 == -1)) { Flags_SetSwitch(play, this->clearFlag); func_800F5B58(); + Player_GainExperience(play, this->actor.exp); } else { D_80B4A1B4 = -1; } diff --git a/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c index 6a38b626428..99de9ae436b 100644 --- a/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c +++ b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c @@ -2537,7 +2537,7 @@ void func_80B59828(EnZl3* this, PlayState* play) { if (func_80B59698(this, play) != 0) { func_80088AA0(180); func_80B53468(); - gSaveContext.healthAccumulator = 320; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Magic_Fill(play); if (Flags_GetSwitch(play, 0x20)) { Flags_UnsetSwitch(play, 0x20); @@ -2586,7 +2586,7 @@ void func_80B59AD0(EnZl3* this, PlayState* play) { func_80B53614(this, play); Flags_UnsetEventChkInf(EVENTCHKINF_WATCHED_GANONS_CASTLE_COLLAPSE_CAUGHT_BY_GERUDO); func_80B56F10(this, play); - gSaveContext.healthAccumulator = 320; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; Magic_Fill(play); this->action = 27; this->drawConfig = 1; diff --git a/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c b/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c index 3c83a1d51e0..4143a1cd755 100644 --- a/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c +++ b/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c @@ -617,7 +617,8 @@ void EnfHG_Damage(EnfHG* this, PlayState* play) { this->timers[0] = 140; this->actionFunc = EnfHG_Retreat; Animation_MorphToLoop(&this->skin.skelAnime, &gPhantomHorseRunningAnim, 0.0f); - if (bossGnd->actor.colChkInfo.health > 24) { + u16 healthCheck = GetActorStat_EnemyMaxHealth(24, bossGnd->actor.level); + if (bossGnd->actor.colChkInfo.health > healthCheck) { this->bossGndSignal = FHG_RIDE; } else { bossGnd->flyMode = GND_FLY_NEUTRAL; diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 777de3e5965..b6a2f7e1458 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -4545,7 +4545,7 @@ void func_80837C0C(PlayState* play, Player* this, s32 damageResponseType, f32 sp Player_PlaySfx(this, NA_SE_PL_DAMAGE); - if (!func_80837B18(play, this, 0 - this->actor.colChkInfo.damage)) { + if (!func_80837B18_modified(play, this, 0 - this->actor.colChkInfo.damage, false)) { this->stateFlags2 &= ~PLAYER_STATE2_GRABBED_BY_ENEMY; if (!(this->actor.bgCheckFlags & 1) && !(this->stateFlags1 & PLAYER_STATE1_IN_WATER)) { func_80837B9C(this, play); @@ -4786,7 +4786,7 @@ s32 func_808382DC(Player* this, PlayState* play) { this->bodyShockTimer = 40; } - this->actor.colChkInfo.damage += this->knockbackDamage; + this->actor.colChkInfo.damage += this->knockbackDamage << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, knockbackResponse[this->knockbackType - 1], this->knockbackSpeed, this->knockbackYVelocity, this->knockbackRot, 20); } else { @@ -4888,7 +4888,7 @@ s32 func_808382DC(Player* this, PlayState* play) { ((this->currentTunic != PLAYER_TUNIC_GORON && CVarGetInteger(CVAR_CHEAT("SuperTunic"), 0) == 0) || (this->floorTypeTimer >= D_808544F4[sp48])))) { this->floorTypeTimer = 0; - this->actor.colChkInfo.damage = 4; + this->actor.colChkInfo.damage = 4 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, PLAYER_HIT_RESPONSE_NONE, 4.0f, 5.0f, this->actor.shape.rot.y, 20); } else { return 0; @@ -9285,7 +9285,7 @@ s32 func_80842DF4(PlayState* play, Player* this) { func_80842B7C(play, this); if (this->actor.colChkInfo.atHitEffect == 1) { - this->actor.colChkInfo.damage = 8; + this->actor.colChkInfo.damage = 8 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, PLAYER_HIT_RESPONSE_ELECTRIC_SHOCK, 0.0f, 0.0f, this->actor.shape.rot.y, 20); return 1; @@ -9542,7 +9542,7 @@ void func_80843AE8(PlayState* play, Player* this) { LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_derth_rebirth, 1.0f, 99.0f, Animation_GetLastFrame(&gPlayerAnim_link_derth_rebirth), ANIMMODE_ONCE, 0.0f); } - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = gSaveContext.healthCapacity2; this->av2.actionVar2 = -1; } } else if (gSaveContext.healthAccumulator == 0) { @@ -9924,7 +9924,7 @@ void Player_Action_80844A44(Player* this, PlayState* play) { Math_StepToF(&this->linearVelocity, 0.0f, 0.05f); if (this->actor.bgCheckFlags & 1) { - this->actor.colChkInfo.damage = 0x10; + this->actor.colChkInfo.damage = 0x10 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0); func_80837C0C(play, this, PLAYER_HIT_RESPONSE_KNOCKBACK_LARGE, 4.0f, 5.0f, this->actor.shape.rot.y, 20); } } @@ -10810,6 +10810,7 @@ void Player_InitCommon(Player* this, PlayState* play, FlexSkeletonHeader* skelHe Collider_SetQuad(play, &this->meleeWeaponQuads[1], &this->actor, &D_80854650); Collider_InitQuad(play, &this->shieldQuad); Collider_SetQuad(play, &this->shieldQuad, &this->actor, &D_808546A0); + Player_GainExperience(play, 0); this->ivanDamageMultiplier = 1; } @@ -14616,25 +14617,27 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { if (this->itemAction == PLAYER_IA_BOTTLE_POE) { s32 rand = Rand_S16Offset(-1, 3); + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (rand == 0) { rand = 3; } - if ((rand < 0) && (gSaveContext.health <= 0x10)) { + if ((rand < 0) && (gSaveContext.health <= heartUnits)) { rand = 3; } if (rand < 0) { - Health_ChangeBy(play, -0x10); + Health_ChangeBy(play, -heartUnits); } else { - gSaveContext.healthAccumulator = rand * 0x10; + gSaveContext.healthAccumulator = rand * heartUnits; } } else { s32 sp28 = D_808549FC[this->itemAction - PLAYER_IA_BOTTLE_POTION_RED]; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (sp28 & 1) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = heartUnits * 20; } if (sp28 & 2) { @@ -14642,7 +14645,7 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { } if (sp28 & 4) { - gSaveContext.healthAccumulator = 0x50; + gSaveContext.healthAccumulator = heartUnits * 5; } } @@ -14778,7 +14781,8 @@ void Player_Action_8084EED8(Player* this, PlayState* play) { Player_PlaySfx(this, NA_SE_EV_BOTTLE_CAP_OPEN); Player_PlaySfx(this, NA_SE_EV_FIATY_HEAL - SFX_FLAG); } else if (LinkAnimation_OnFrame(&this->skelAnime, 47.0f)) { - gSaveContext.healthAccumulator = 0x140; + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + gSaveContext.healthAccumulator = heartUnits * 20; } } diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index d170beb95c7..23524ec3295 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -3,6 +3,7 @@ #include #include +#include "textures/nes_font_static/nes_font_static.h" #include "textures/title_static/title_static.h" #include "textures/parameter_static/parameter_static.h" #include @@ -1899,9 +1900,9 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { s16 phi_ra; s16 temp_t1; - this->windowContentVtx = Graph_Alloc(this->state.gfxCtx, 0x288 * sizeof(Vtx)); + this->windowContentVtx = Graph_Alloc(this->state.gfxCtx, 0x298 * sizeof(Vtx)); - for (phi_t2 = 0; phi_t2 < 0x288; phi_t2 += 4) { + for (phi_t2 = 0; phi_t2 < 0x298; phi_t2 += 4) { this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = 0x12C; this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = this->windowContentVtx[phi_t2].v.ob[0] + 0x10; @@ -2059,7 +2060,7 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { this->windowContentVtx[phi_t2].v.ob[1] - WREG(43); } - phi_t0 = this->windowPosX - 14; + phi_t0 = this->windowPosX - 26; temp_t1 -= 0x16; for (phi_a1 = 0; phi_a1 < 4; phi_a1++, phi_t2 += 4) { @@ -2069,7 +2070,7 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = this->windowContentVtx[phi_t2].v.ob[1] - D_80812828[phi_a1]; - phi_t0 += D_80812818[phi_a1]; + phi_t0 += D_80812818[phi_a1] - 1; } this->windowContentVtx[phi_t2 - 15].v.tc[0] = this->windowContentVtx[phi_t2 - 13].v.tc[0] = 0x400; @@ -2166,6 +2167,30 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { this->windowContentVtx[phi_t2 + 6].v.ob[1] = this->windowContentVtx[phi_t2 + 7].v.ob[1] = this->windowContentVtx[phi_t2 + 4].v.ob[1] - 0x10; this->windowContentVtx[phi_t2 + 5].v.tc[0] = this->windowContentVtx[phi_t2 + 7].v.tc[0] = 0x1000; + + phi_t2 += 8; + // Level count + phi_t0 = this->windowPosX + 30; + temp_t1 = 24; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 5; + } + phi_t0 += 3; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 9; + } } static u16 D_8081284C[] = { 0x007C, 0x0124, 0x01CC }; @@ -2278,6 +2303,31 @@ void FileChoose_DrawFileInfo(GameState* thisx, s16 fileIndex, s16 isActive) { } } + // draw level + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[648], 32, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, 255, 255, 100, this->fileInfoAlpha[fileIndex]); + + char lvText[] = { 21, 57 }; + + for (i = 0, vtxOffset = 0; i < 2; i++, vtxOffset += 4) { + FileChoose_DrawCharacter(this->state.gfxCtx, sp54->fontBuf + lvText[i] * FONT_CHAR_TEX_SIZE, vtxOffset); + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, TEXEL0, 0, + PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, 255, 255, 100, this->fileInfoAlpha[fileIndex]); + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[656], 12, 0); + + FileChoose_SplitNumber(Save_GetSaveMetaInfo(fileIndex)->level, &deathCountSplit[0], &deathCountSplit[1], + &deathCountSplit[2]); + + for (i = 1, vtxOffset = 0; i < 3; i++, vtxOffset += 4) { + FileChoose_DrawCharacter(this->state.gfxCtx, sp54->fontBuf + deathCountSplit[i] * FONT_CHAR_TEX_SIZE, + vtxOffset); + } + // end draw level + gDPPipeSync(POLY_OPA_DISP++); heartType = (Save_GetSaveMetaInfo(fileIndex)->defense == 0) ? 0 : 1; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c index 2eb5e15bbd3..1e636d7f2e5 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c @@ -372,12 +372,14 @@ void KaleidoScope_DrawDebugEditor(PlayState* play) { if (gSaveContext.healthCapacity < 0x30) { gSaveContext.healthCapacity = 0x30; } + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN) || CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { gSaveContext.healthCapacity += 0x10; if (gSaveContext.healthCapacity >= 0x140) { gSaveContext.healthCapacity = 0x140; } + Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); } break; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c index cec05af725c..eb1070447f1 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c @@ -528,6 +528,7 @@ void KaleidoScope_DrawEquipment(PlayState* play) { // Equip success sound Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + Leveled_SetPlayerModifiedStats(GET_PLAYER(play)); // Wait 10 frames before accepting input again pauseCtx->unk_1E4 = 7; sEquipTimer = 10; @@ -639,6 +640,7 @@ void KaleidoScope_DrawEquipment(PlayState* play) { Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + Leveled_SetPlayerModifiedStats(GET_PLAYER(play)); pauseCtx->unk_1E4 = 7; sEquipTimer = 10; } else if (CVarGetInteger(CVAR_ENHANCEMENT("AssignableTunicsAndBoots"), 0) != 0) { @@ -869,6 +871,10 @@ void KaleidoScope_DrawEquipment(PlayState* play) { Gfx_SetupDL_42Opa(play->state.gfxCtx); KaleidoScope_DrawEquipmentImage(play, pauseCtx->playerSegment, PAUSE_EQUIP_PLAYER_WIDTH, PAUSE_EQUIP_PLAYER_HEIGHT); + if (pauseCtx->pageIndex == PAUSE_EQUIP && (pauseCtx->unk_1E4 == 0 || sEquipTimer > 0) && pauseCtx->alpha == 255) { + Leveled_KaleidoEquip_Stats(play); + } + if (gUpgradeMasks[0]) {} CLOSE_DISPS(play->state.gfxCtx); diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index 14f749d43da..46ecd847def 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -4774,7 +4774,7 @@ void KaleidoScope_Update(PlayState* play) { play->gameplayFrames = 0; gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK; gSaveContext.health = - CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity : 0x30; + CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity2 : 3 * CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA); gSaveContext.healthAccumulator = 0; gSaveContext.magicState = MAGIC_STATE_IDLE; From 4139f230f1abfd82901b837d6b73468e180c9328 Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 7 Apr 2025 03:42:12 -0600 Subject: [PATCH 151/157] Some various fixes. Flare dancer core now takes a minimum of 9 hits to phase. Gold Skulltulas have their health depleted by half for each hit, after damage is dealt. Life meter consistancy fix when changing heart units. --- soh/include/z64actor.h | 2 +- .../game-interactor/GameInteractor_RawAction.cpp | 2 ++ soh/soh/Enhancements/mods.cpp | 6 ++++-- soh/soh/Enhancements/randomizer/hook_handlers.cpp | 7 +++++-- soh/soh/Enhancements/randomizer/logic.cpp | 2 ++ soh/src/code/z_lifemeter.c | 6 ++++-- soh/src/code/z_message_PAL.c | 8 ++------ soh/src/code/z_parameter.c | 2 ++ soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c | 13 ++++++++++--- soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c | 12 ++++++++---- soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c | 2 +- soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c | 5 +++++ 12 files changed, 46 insertions(+), 21 deletions(-) diff --git a/soh/include/z64actor.h b/soh/include/z64actor.h index 2c7922ee674..99a91a89ebf 100644 --- a/soh/include/z64actor.h +++ b/soh/include/z64actor.h @@ -267,7 +267,7 @@ typedef struct Actor { /* 0x138 */ ActorResetFunc reset; /* 0x13C */ char dbgPad[0x10]; // Padding that only exists in the debug rom // #region SOH [General] - /* */ u8 maximumHealth; // Max health value for use with health bars, set on actor init + /* */ u16 maximumHealth; // Max health value for use with health bars, set on actor init // #endregion u16 exp; // Experience u8 level; // Actor Level diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp index 2798e374b14..a090b96d7db 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -18,6 +18,8 @@ extern PlayState* gPlayState; void GameInteractor::RawAction::AddOrRemoveHealthContainers(int16_t amount) { gSaveContext.healthCapacity += amount * 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + } void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 5561bfa4de7..bfa9575a823 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -211,7 +211,8 @@ void UpdatePermanentHeartLossState() { 16 * (IS_RANDO ? (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_HEARTS) + 1) : 3); uint8_t newCapacity = startingHealth + (heartContainers * 16) + ((heartPieces - (heartPieces % 4)) * 4); - gSaveContext.healthCapacity = MAX(newCapacity, gSaveContext.healthCapacity2); + gSaveContext.healthCapacity = MAX(newCapacity, gSaveContext.healthCapacity); + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity2); hasAffectedHealth = false; } @@ -227,7 +228,8 @@ void RegisterPermanentHeartLoss() { if (!CVarGetInteger(CVAR_ENHANCEMENT("PermanentHeartLoss"), 0) || !GameInteractor::IsSaveLoaded()) return; - if (gSaveContext.healthCapacity > 16 && gSaveContext.healthCapacity - gSaveContext.health >= 16) { + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + if (gSaveContext.healthCapacity > 16 && gSaveContext.healthCapacity2 - gSaveContext.health >= heartUnits) { gSaveContext.healthCapacity -= 16; gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity2); diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 454a892a413..0b864c52ce8 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -377,14 +377,17 @@ void RandomizerOnItemReceiveHandler(GetItemEntry receivedItemEntry) { randomizerQueuedItemEntry = GET_ITEM_NONE; } + s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; + if (receivedItemEntry.modIndex == MOD_NONE && (receivedItemEntry.itemId == ITEM_HEART_PIECE || receivedItemEntry.itemId == ITEM_HEART_PIECE_2 || receivedItemEntry.itemId == ITEM_HEART_CONTAINER)) { - gSaveContext.healthAccumulator = 0x140; // Refill 20 hearts + gSaveContext.healthAccumulator = heartUnits * 20; // Refill 20 hearts if ((s32)(gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000) { gSaveContext.inventory.questItems ^= 0x40000000; gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + gSaveContext.health += heartUnits; } } diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index a9e7264a0ff..e998cec72e6 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -1771,9 +1771,11 @@ void Logic::ApplyItemEffect(Item& item, bool state) { } break; case RG_HEART_CONTAINER: mSaveContext->healthCapacity += (!state ? -16 : 16); + mSaveContext->healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); break; case RG_PIECE_OF_HEART: mSaveContext->healthCapacity += (!state ? -4 : 4); + mSaveContext->healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); break; case RG_BOOMERANG: case RG_LENS_OF_TRUTH: diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index 31f01650a39..c293ba0db5d 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -398,9 +398,11 @@ void HealthMeter_Draw(PlayState* play) { InterfaceContext* interfaceCtx = &play->interfaceCtx; GraphicsContext* gfxCtx = play->state.gfxCtx; Vtx* sp154 = interfaceCtx->beatingHeartVtx; - s32 curHeartFraction = (s32)((f32)gSaveContext.health / heartUnits * 16) % 0x10; s16 totalHeartCount = gSaveContext.healthCapacity2 / heartUnits; s16 fullHeartCount = gSaveContext.health / heartUnits; + f32 heartUnit = (f32)gSaveContext.health / heartUnits * 16 - fullHeartCount * 16; + heartUnit = heartUnit > 0.0f && heartUnit < 1.0f ? 1.0f : heartUnit; + s32 curHeartFraction = (s32)heartUnit % 0x10; s32 pad2; f32 sp144 = interfaceCtx->unk_22A * 0.1f; s32 curCombineModeSet = 0; @@ -636,7 +638,7 @@ void HealthMeter_Draw(PlayState* play) { } offsetX += 10.0f; - s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 10); + s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 15); if (lineLength != 0 && (i + 1) % lineLength == 0) { offsetX = PosX_anchor; offsetY += 10.0f; diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index 6684aa30b95..c4fbf5c99b5 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -4701,16 +4701,12 @@ void Message_Update(PlayState* play) { s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); gSaveContext.health += heartUnits; - if (play != NULL) { - Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); - } } else { gSaveContext.healthCapacity -= 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); gSaveContext.health += heartUnits; - if (play != NULL) { - Actor_RefreshLeveledStats(&GET_PLAYER(play)->actor, GET_PLAYER(play)); - } } } if (msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP_DONE) { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 935af08b0a9..2f445a991f4 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2321,9 +2321,11 @@ u8 Item_Give(PlayState* play, u8 item) { s32 heartUnits = CVarGetInteger("gLeveled.Difficulty.HeartUnits", 4) << 2; if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { gSaveContext.healthCapacity += 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); gSaveContext.health += heartUnits; } else { gSaveContext.healthCapacity -= 0x10; + gSaveContext.healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); gSaveContext.health -= heartUnits; } gSaveContext.ship.stats.heartContainers++; diff --git a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c index a59c736afe2..41b5462e15f 100644 --- a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c +++ b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c @@ -224,10 +224,17 @@ s32 EnFd_SpawnCore(EnFd* this, PlayState* play) { return false; } - this->actor.child->colChkInfo.health = this->actor.colChkInfo.health % 8; + u16 coreMaximumHealth = this->actor.maximumHealth / 3; + if (coreMaximumHealth * 3 != this->actor.maximumHealth) { + this->actor.colChkInfo.health = coreMaximumHealth * 3; + this->actor.maximumHealth = this->actor.colChkInfo.health; + } + + this->actor.child->colChkInfo.health = this->actor.colChkInfo.health % coreMaximumHealth; + this->actor.child->maximumHealth = coreMaximumHealth; if (this->actor.child->colChkInfo.health == 0) { - this->actor.child->colChkInfo.health = 8; + this->actor.child->colChkInfo.health = coreMaximumHealth; } if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) { @@ -781,7 +788,7 @@ void EnFd_Draw(Actor* thisx, PlayState* play) { Matrix_Pop(); if (this->actionFunc != EnFd_Reappear && !(this->fadeAlpha < 0.9f)) { Gfx_SetupDL_25Xlu(play->state.gfxCtx); - clampedHealth = CLAMP(thisx->colChkInfo.health - 1, 0, 23); + clampedHealth = CLAMP((s32)((f32)thisx->colChkInfo.health / thisx->maximumHealth * 23), 0, 23); gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, primColors[clampedHealth / 8].r, primColors[clampedHealth / 8].g, primColors[clampedHealth / 8].b, (u8)this->fadeAlpha); gDPSetEnvColor(POLY_XLU_DISP++, envColors[clampedHealth / 8].r, envColors[clampedHealth / 8].g, diff --git a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c index d1f7e3b929e..63b6b6437a4 100644 --- a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c +++ b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c @@ -154,18 +154,22 @@ s32 EnFw_CheckCollider(EnFw* this, PlayState* play) { } this->collider.base.acFlags &= ~AC_HIT; - if (this->actor.colChkInfo.damage >= this->actor.colChkInfo.health) { + u16 extraDamage = this->actor.maximumHealth / 9; + + if (this->actor.colChkInfo.damage + extraDamage >= this->actor.colChkInfo.health) { this->actor.colChkInfo.health = 0; } else { - this->actor.colChkInfo.health -= this->actor.colChkInfo.damage; + this->actor.colChkInfo.health -= this->actor.colChkInfo.damage + extraDamage; } + ActorDamageNumber_New(&this->actor, this->actor.colChkInfo.damage + extraDamage); + if (this->actor.colChkInfo.health <= 0) { - if (this->actor.parent->colChkInfo.health <= 8) { + if (this->actor.parent->colChkInfo.health <= this->actor.maximumHealth) { Enemy_StartFinishingBlow(play, &this->actor); this->actor.parent->colChkInfo.health = 0; } else { - this->actor.parent->colChkInfo.health -= 8; + this->actor.parent->colChkInfo.health -= this->actor.maximumHealth; } this->returnToParentTimer = 0; } diff --git a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c index 172b9e72676..1ebeb7999bb 100644 --- a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c +++ b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c @@ -721,7 +721,7 @@ void EnFz_Draw(Actor* thisx, PlayState* play) { s32 pad; s32 index; - index = (6 - (u8)CLAMP(((f32)this->actor.colChkInfo.health / GetActorStat_EnemyMaxHealth(6, this->actor.level) * 5 + 0.99999f), 0, 6)) >> 1; + index = (6 - (u8)CLAMP(((f32)this->actor.colChkInfo.health / this->actor.maximumHealth * 5 + 0.99999f), 0, 6)) >> 1; // Leveled Mod - Already does this. // SOH [Enhancement] - With enemy health scaling, the Freezards health could cause an index out of bounds for the diff --git a/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c index c5c8e6167fa..13d960365d8 100644 --- a/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c +++ b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c @@ -334,6 +334,11 @@ s32 func_80B0C9F0(EnSw* this, PlayState* play) { Actor_SetColorFilter(&this->actor, 0x4000, 0xC8, 0, this->unk_392); if (Actor_ApplyDamage(&this->actor) != 0) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_DAMAGE); + if (((this->actor.params & 0xE000) >> 0xD) != 0) { + if (this->actor.colChkInfo.health > 1) { + this->actor.colChkInfo.health >>= 1; + } + } return true; } Enemy_StartFinishingBlow(play, &this->actor); From a5f446087561162e536c7a838a4d49a23ad836c1 Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 7 Apr 2025 21:58:57 -0600 Subject: [PATCH 152/157] Some Ganon health check fixes. --- soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c index a32ec8d7292..18a0e6fd53c 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c @@ -1464,7 +1464,7 @@ void func_80900890(BossGanon2* this, PlayState* play) { if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { func_808FFDB0(this, play); if (this->unk_334 == 0) { - this->actor.colChkInfo.health = GetActorStat_EnemyMaxHealth(25, this->actor.level); + this->actor.colChkInfo.health = (u16)(this->actor.maximumHealth * (25.0f / 30.0f)); } this->unk_336 = 1; } @@ -1967,7 +1967,7 @@ void func_80902524(BossGanon2* this, PlayState* play) { } ActorDamageNumber_New(&this->actor, damage); temp_v0_4 = this->actor.colChkInfo.health; - if (temp_v0_4 < GetActorStat_EnemyMaxHealth(21, this->actor.level) && this->unk_334 == 0) { + if (temp_v0_4 < (u16)(this->actor.maximumHealth * (21.0f / 30.0f)) && this->unk_334 == 0) { func_80900818(this, play); } else { if (temp_v0_4 <= 0) { @@ -2010,7 +2010,7 @@ void func_80902524(BossGanon2* this, PlayState* play) { ActorDamageNumber_New(&this->actor, phi_v1_2); temp_v0_4 = this->actor.colChkInfo.health; - if ((temp_v0_4 < GetActorStat_EnemyMaxHealth(21, this->actor.level)) && (this->unk_334 == 0)) { + if ((temp_v0_4 < (u16)(this->actor.maximumHealth * (21.0f / 30.0f))) && (this->unk_334 == 0)) { func_80900818(this, play); } else if ((temp_v0_4 <= 0) && (baseDamage >= 2)) { func_80901020(this, play); From 1982944b269e031ad6ddd4c97417d538002a6180 Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 30 Apr 2025 02:33:43 -0600 Subject: [PATCH 153/157] Fix Ganon's Castle Max Level setting. --- soh/soh/SohGui/SohMenuLeveled.cpp | 2 +- soh/src/code/leveled_map_levels.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/soh/soh/SohGui/SohMenuLeveled.cpp b/soh/soh/SohGui/SohMenuLeveled.cpp index 08f7d3ac822..feadbe3eac9 100644 --- a/soh/soh/SohGui/SohMenuLeveled.cpp +++ b/soh/soh/SohGui/SohMenuLeveled.cpp @@ -142,7 +142,7 @@ namespace SohGui { .CVar("gLeveled.Difficulty.Enemy.MaxLevelInGanonCastle") .Options(CheckboxOptions() .Tooltip("If all tokens are collected, all enemies in Ganon's Casle are level 99.") - .DefaultValue(true)); + .DefaultValue(false)); // Enemy HP AddWidget(path, "Enemy HP: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) .CVar("gLeveled.Difficulty.Enemy.HPPercent") diff --git a/soh/src/code/leveled_map_levels.c b/soh/src/code/leveled_map_levels.c index ea1616b8043..97191a3e687 100644 --- a/soh/src/code/leveled_map_levels.c +++ b/soh/src/code/leveled_map_levels.c @@ -40,6 +40,7 @@ s8 Leveled_GetSceneLevel(s16 sceneId) { case SCENE_ICE_CAVERN: return 32; // Ice Cavern + case SCENE_INSIDE_GANONS_CASTLE: case SCENE_GANONS_TOWER: case SCENE_GANONDORF_BOSS: case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR: @@ -47,7 +48,7 @@ s8 Leveled_GetSceneLevel(s16 sceneId) { if (CVarGetInteger("gLeveled.Difficulty.Enemy.MaxLevelInGanonCastle", 0) == 1 && gSaveContext.inventory.gsTokens == 100) return 120; else - return 50; + return 52; case SCENE_GERUDO_TRAINING_GROUND: return 43; @@ -55,9 +56,6 @@ s8 Leveled_GetSceneLevel(s16 sceneId) { case SCENE_THIEVES_HIDEOUT: return 42; - case SCENE_INSIDE_GANONS_CASTLE: - return 52; - case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE: return 53; From 09f6c5ef0b3e3bbb01be051efc9c07fed3cea43c Mon Sep 17 00:00:00 2001 From: Gotest Date: Thu, 14 Aug 2025 20:13:39 -0600 Subject: [PATCH 154/157] Update gameplaystats.cpp Trigger rebuild of artifacts --- soh/soh/Enhancements/gameplaystats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp index c7cf007e357..836dd41de67 100644 --- a/soh/soh/Enhancements/gameplaystats.cpp +++ b/soh/soh/Enhancements/gameplaystats.cpp @@ -445,7 +445,7 @@ void DrawGameplayStatsHeader() { ImGui::TableSetupColumn("stat", ImGuiTableColumnFlags_WidthStretch); // if tag is empty (not a release build) if (gGitCommitTag[0] == 0) { - GameplayStatsRow("Leveled Mod Version:", "1.1.3"); + GameplayStatsRow("Leveled Mod Version:", "1.1.3.1"); GameplayStatsRow("A Mod By Gotest, AKA Arrenton", ""); GameplayStatsRow("Git Branch:", (char*)gGitBranch); GameplayStatsRow("Git Commit Hash:", (char*)gGitCommitHash); From e7a0ef29e2d529e088b21e156ca36ac9736de41e Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 19 Sep 2025 01:32:54 -0600 Subject: [PATCH 155/157] Fixed Rando crash when generating in the file select. Added options to show HP and MP numbers on HUD. --- soh/include/leveled_overlays.h | 1 + soh/soh/Enhancements/gameplaystats.cpp | 2 +- soh/soh/Enhancements/randomizer/logic.cpp | 6 +- soh/soh/SohGui/SohMenuLeveled.cpp | 45 +++++++++-- soh/src/code/leveled_overlays.c | 76 +++++++++++++++++++ soh/src/code/z_lifemeter.c | 37 +++++++++ soh/src/code/z_parameter.c | 26 +++++++ .../ovl_file_choose/z_file_choose.c | 65 +++++++++++----- 8 files changed, 228 insertions(+), 30 deletions(-) diff --git a/soh/include/leveled_overlays.h b/soh/include/leveled_overlays.h index 26f01abe4fc..ba52a59ea16 100644 --- a/soh/include/leveled_overlays.h +++ b/soh/include/leveled_overlays.h @@ -10,6 +10,7 @@ void ActorDamageNumber_Draw(PlayState* play, Actor* actor); void ActorExperienceNumber_Draw(PlayState* play, Actor* actor); void Actor_LevelUpDraw(PlayState* play, Actor* actor); void Leveled_ValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b); +void Leveled_OverlayValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 direction, u8 r, u8 g, u8 b, u8 a); void Leveled_BigValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b, u8 a); void Leveled_KaleidoEquip_Stats(PlayState* play); void Leveled_Interface_DrawNextLevel(PlayState* play); \ No newline at end of file diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp index 836dd41de67..4413de998fc 100644 --- a/soh/soh/Enhancements/gameplaystats.cpp +++ b/soh/soh/Enhancements/gameplaystats.cpp @@ -445,7 +445,7 @@ void DrawGameplayStatsHeader() { ImGui::TableSetupColumn("stat", ImGuiTableColumnFlags_WidthStretch); // if tag is empty (not a release build) if (gGitCommitTag[0] == 0) { - GameplayStatsRow("Leveled Mod Version:", "1.1.3.1"); + GameplayStatsRow("Leveled Mod Version:", "1.1.4"); GameplayStatsRow("A Mod By Gotest, AKA Arrenton", ""); GameplayStatsRow("Git Branch:", (char*)gGitBranch); GameplayStatsRow("Git Commit Hash:", (char*)gGitCommitHash); diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index e998cec72e6..93d4093d428 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -1771,11 +1771,13 @@ void Logic::ApplyItemEffect(Item& item, bool state) { } break; case RG_HEART_CONTAINER: mSaveContext->healthCapacity += (!state ? -16 : 16); - mSaveContext->healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + if (gPlayState != nullptr) + mSaveContext->healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); break; case RG_PIECE_OF_HEART: mSaveContext->healthCapacity += (!state ? -4 : 4); - mSaveContext->healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); + if (gPlayState != nullptr) + mSaveContext->healthCapacity2 = GetPlayerStat_GetModifiedHealthCapacity(gSaveContext.healthCapacity, GET_PLAYER(gPlayState)->actor.level); break; case RG_BOOMERANG: case RG_LENS_OF_TRUTH: diff --git a/soh/soh/SohGui/SohMenuLeveled.cpp b/soh/soh/SohGui/SohMenuLeveled.cpp index feadbe3eac9..0636f37644a 100644 --- a/soh/soh/SohGui/SohMenuLeveled.cpp +++ b/soh/soh/SohGui/SohMenuLeveled.cpp @@ -3,6 +3,16 @@ #include namespace SohGui { + enum LeveledUIMagicNumberType { + LEVELED_MAGIC_NUMBER_UI_FULL, + LEVELED_MAGIC_NUMBER_UI_CURRENT, + LEVELED_MAGIC_NUMBER_UI_NONE + }; + static const std::unordered_map magicNumberTypeList = { + { LEVELED_MAGIC_NUMBER_UI_FULL, "Full" }, + { LEVELED_MAGIC_NUMBER_UI_CURRENT, "Current" }, + { LEVELED_MAGIC_NUMBER_UI_NONE, "None" } + }; extern std::shared_ptr mSohMenu; using namespace UIWidgets; @@ -11,10 +21,10 @@ namespace SohGui { // Add Leveled Menu AddMenuEntry("Leveled", CVAR_SETTING("Menu.LeveledSidebarSection")); + ////////////////////////////////////////////////////////////////////////////////////////// // UI Settings WidgetPath path = { "Leveled", "UI Settings", SECTION_COLUMN_1 }; AddSidebarEntry("Leveled", path.sidebarName, 1); - // HUD AddWidget(path, "HUD", WIDGET_SEPARATOR_TEXT); // EXP Next Level @@ -31,7 +41,26 @@ namespace SohGui { AddWidget(path, "Level Up Sound", WIDGET_CVAR_CHECKBOX) .CVar("gLeveled.HUD.LevelUpSound") .Options(CheckboxOptions().Tooltip("Plays sound when leveling up.").DefaultValue(true)); - + + // Health Meter Numbers + AddWidget(path, "Health Numbers", WIDGET_CVAR_COMBOBOX) + .CVar("gLeveled.HUD.HealthNumbersType") + .Options(ComboboxOptions() + .ComboMap(magicNumberTypeList) + .DefaultIndex(LEVELED_MAGIC_NUMBER_UI_FULL) + .Tooltip("Sets the numbers displayed on the health meter.") + .Color(THEME_COLOR)); + + // Magic Meter Numbers + AddWidget(path, "Magic Meter Numbers", WIDGET_CVAR_COMBOBOX) + .CVar("gLeveled.HUD.MagicMeterNumbersType") + .SameLine(true) + .Options(ComboboxOptions() + .ComboMap(magicNumberTypeList) + .DefaultIndex(LEVELED_MAGIC_NUMBER_UI_FULL) + .Tooltip("Sets the numbers displayed on the magic meter.") + .Color(THEME_COLOR)); + //---------------------------------------------------------------------------------------- // Floating Numbers AddWidget(path, "Floating Numbers", WIDGET_SEPARATOR_TEXT); // Enemy Damage @@ -46,7 +75,7 @@ namespace SohGui { AddWidget(path, "EXP Gain", WIDGET_CVAR_CHECKBOX) .CVar("gLeveled.HUD.FloatingNumbers.ExpGain") .Options(CheckboxOptions().Tooltip("Show floating EXP gain numbers.").DefaultValue(true)); - + //---------------------------------------------------------------------------------------- // Navi Info AddWidget(path, "Navi Enemy Info", WIDGET_SEPARATOR_TEXT); // Navi Level @@ -57,7 +86,7 @@ namespace SohGui { AddWidget(path, "Navi tells enemy max HP", WIDGET_CVAR_CHECKBOX) .CVar("gLeveled.Navi.TellEnemyMaxHP") .Options(CheckboxOptions().Tooltip("Navi will tell the enemy's maximum HP.").DefaultValue(true)); - + ////////////////////////////////////////////////////////////////////////////////////////// // Entity Modifications path.sidebarName = "Entity Modifications"; AddSidebarEntry("Leveled", path.sidebarName, 1); @@ -67,6 +96,7 @@ namespace SohGui { .Options(IntSliderOptions().Min(1).Max(32).Format("%d").DefaultValue(9).Tooltip( "Changes Link's Attack and enemy HP multiplier.\nThis doesn't change balance, but rather the size of " "numbers.")); + //---------------------------------------------------------------------------------------- AddWidget(path, "Player Modifications", WIDGET_SEPARATOR_TEXT); // Level Increases Life AddWidget(path, "Level Gives Bonus Hearts", WIDGET_CVAR_CHECKBOX) @@ -87,6 +117,7 @@ namespace SohGui { .Tooltip("Equipment will alter stats. Such as Goron Tunic providing +3 STR, but gives -3 " "Defense, Bracelets give +1 STR, Shields, etc.") .DefaultValue(true)); + //---------------------------------------------------------------------------------------- AddWidget(path, "Enemy Modifications", WIDGET_SEPARATOR_TEXT); // Enemy Level Affects Attack AddWidget(path, "Enemy Level Affects Base Attack", WIDGET_CVAR_CHECKBOX) @@ -96,7 +127,7 @@ namespace SohGui { .Tooltip("Enemies have a fixed attack value. This option scales this up the higher the enemy's " "Strength stat. \nThis will increase difficulty a bit.") .DefaultValue(true)); - + ////////////////////////////////////////////////////////////////////////////////////////// // Difficulty Options path.sidebarName = "Difficulty Options"; AddSidebarEntry("Leveled", path.sidebarName, 1); @@ -121,7 +152,7 @@ namespace SohGui { IntSliderOptions().LabelPosition(Within).Min(1).Max(32).Format("").DefaultValue(4).Color(THEME_COLOR) .Tooltip("Sets a multiplier for the damage the player takes. Includes ALL sources, even damage while being frozen or burned.\nDamage cannot be reduced below 1.")); }); - + //---------------------------------------------------------------------------------------- // EXP Options AddWidget(path, "EXP Options", WIDGET_SEPARATOR_TEXT); // EXP Multiplier @@ -134,7 +165,7 @@ namespace SohGui { .CVar("gLeveled.Difficulty.EXP.TokenRate") .Options(FloatSliderOptions().Min(0.0f).Max(10.0f).Format("%.2fx").DefaultValue(1.0f).Tooltip( "Sets the EXP multiplier link gains from gold skulltula tokens.")); - + //---------------------------------------------------------------------------------------- // Enemy Options AddWidget(path, "Enemy Options", WIDGET_SEPARATOR_TEXT); // Max GS Tokens make Ganon's Castle Enemies Lv99 diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c index 1003e49327e..c2c9efd9830 100644 --- a/soh/src/code/leveled_overlays.c +++ b/soh/src/code/leveled_overlays.c @@ -513,6 +513,82 @@ void Leveled_ValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 CLOSE_DISPS(play->state.gfxCtx); } +void Leveled_OverlayValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 direction, u8 r, u8 g, u8 b, u8 a) { + s32 val; + u8 digit[] = { 0, 0, 0, 0, 0, 0 }; + s16 separation = 5; + + u8 digits; + + OPEN_DISPS(play->state.gfxCtx); + + val = value; + + if (val > 999999) + val = 999999; + + if (val < 0) + val = 0; + + gDPPipeSync(OVERLAY_DISP++); + gDPSetTextureFilter(OVERLAY_DISP++, G_TF_AVERAGE); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, r, g, b, a); + + digits = 1; + + if (val >= 100000) + digits += 1; + if (val >= 10000) + digits += 1; + if (val >= 1000) + digits += 1; + if (val >= 100) + digits += 1; + if (val >= 10) + digits += 1; + + if (direction == 2) { + x -= 6 * (digits); + } + if (direction == 1) { + x -= 3 * (digits); + } + + s8 j; + + for (j = 0; val >= 100000; j++) { + val -= 100000; + digit[5] += 1; + } + for (j = 0; val >= 10000; j++) { + val -= 10000; + digit[4] += 1; + } + for (j = 0; val >= 1000; j++) { + val -= 1000; + digit[3] += 1; + } + for (j = 0; val >= 100; j++) { + val -= 100; + digit[2] += 1; + } + for (j = 0; val >= 10; j++) { + val -= 10; + digit[1] += 1; + } + digit[0] = val; + + gDPPipeSync(OVERLAY_DISP++); + + for (s8 i = 0; i < digits; i++) { + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, (u8*)_gAmmoDigit0Tex[digit[i]], 8, 8, + x - i * 6 + 6 * (digits - 1), y, 8, 8, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + void Leveled_BigValueNumberDraw(PlayState* play, u16 x, u16 y, u32 value, u8 r, u8 g, u8 b, u8 a) { s32 val; u8 digit[] = { 0, 0, 0, 0, 0, 0 }; diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index c293ba0db5d..db4016e286f 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -646,6 +646,43 @@ void HealthMeter_Draw(PlayState* play) { FrameInterpolation_RecordCloseChild(); } + + // Draw Health Numbers + u32 healthNumbersType = CVarGetInteger("gLeveled.HUD.HealthNumbersType", 0); + s32 lineLength = CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.LineLength"), 15); + s32 numberOffsetX = CLAMP(totalHeartCount, 0, lineLength) * 5; + s32 numberPosX = PosX_anchor + numberOffsetX; + s32 numberPosY = getHealthMeterYOffset() + 13; + + if (healthNumbersType == 0) { + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + if (HealthMeter_IsCritical()) { + Leveled_OverlayValueNumberDraw(play, numberPosX + 21, numberPosY, gSaveContext.health, 2, (u16)(255.0 * (1 - sp144 * 0.5)), (u16)(127.0 * (1 - sp144 * 0.5)), 0, (u8)interfaceCtx->magicAlpha); + } else { + Leveled_OverlayValueNumberDraw(play, numberPosX + 21, numberPosY, gSaveContext.health, 2, 255, 255, 255, (u8)interfaceCtx->magicAlpha); + } + + Leveled_OverlayValueNumberDraw(play, numberPosX + 28, numberPosY, gSaveContext.healthCapacity2, 0, 255, 255, 255, (u8)interfaceCtx->magicAlpha); + + extern const char* fontTbl[]; + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, interfaceCtx->magicAlpha); + + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, fontTbl[15], 8, 16, numberPosX + 22, numberPosY, 8, 16, 8 << 7, 16 << 7); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->magicAlpha); + + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, fontTbl[15], 8, 16, numberPosX + 22, numberPosY, 8, 16, 8 << 7, 16 << 7); + } else if (healthNumbersType == 1) { + + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + if (HealthMeter_IsCritical()) { + Leveled_OverlayValueNumberDraw(play, numberPosX + 24, numberPosY, gSaveContext.health, 1, (u16)(255.0 * (1 - sp144 * 0.5)), (u16)(127.0 * (1 - sp144 * 0.5)), 0, (u8)interfaceCtx->magicAlpha); + } else { + Leveled_OverlayValueNumberDraw(play, numberPosX + 24, numberPosY, gSaveContext.health, 1, 255, 255, 255, (u8)interfaceCtx->magicAlpha); + } + } CLOSE_DISPS(gfxCtx); } diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 2f445a991f4..d1b767c89b9 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -3614,6 +3614,32 @@ void Interface_DrawMagicBar(PlayState* play) { (rMagicFillX + gSaveContext.magic) << 2, (magicBarY + 10) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); } + + // Draw Magic Numbers + u32 magicNumbersType = CVarGetInteger("gLeveled.HUD.MagicMeterNumbersType", 0); + + if (magicNumbersType == 0) { + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + Leveled_OverlayValueNumberDraw(play, rMagicFillX + (gSaveContext.magicCapacity >> 1) - 3, magicBarY + 1, gSaveContext.magic, 2, sMagicBorder.r, sMagicBorder.g, sMagicBorder.b, (u8)interfaceCtx->magicAlpha); + Leveled_OverlayValueNumberDraw(play, rMagicFillX + (gSaveContext.magicCapacity >> 1) + 3, magicBarY + 1, gSaveContext.magicCapacity, 0, sMagicBorder.r, sMagicBorder.g, sMagicBorder.b, (u8)interfaceCtx->magicAlpha); + + extern const char* fontTbl[]; + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, interfaceCtx->magicAlpha); + + OVERLAY_DISP = + Gfx_TextureI8(OVERLAY_DISP, fontTbl[15], 8, 16, rMagicFillX + (gSaveContext.magicCapacity >> 1) - 2, magicBarY + 1, 8, 16, 8 << 7, 16 << 7); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->magicAlpha); + + OVERLAY_DISP = + Gfx_TextureI8(OVERLAY_DISP, fontTbl[15], 8, 16, rMagicFillX + (gSaveContext.magicCapacity >> 1) - 2, magicBarY + 1, 8, 16, 8 << 7, 16 << 7); + } else if (magicNumbersType == 1) { + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + Leveled_OverlayValueNumberDraw(play, rMagicFillX + (gSaveContext.magicCapacity >> 1), magicBarY + 1, gSaveContext.magic, 1, sMagicBorder.r, sMagicBorder.g, sMagicBorder.b, (u8)interfaceCtx->magicAlpha); + } } CLOSE_DISPS(play->state.gfxCtx); diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 23524ec3295..e55c9d76a49 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -2170,26 +2170,51 @@ void FileChoose_SetWindowContentVtx(GameState* thisx) { phi_t2 += 8; // Level count - phi_t0 = this->windowPosX + 30; - temp_t1 = 24; - for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { - this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; - this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = - this->windowContentVtx[phi_t2].v.ob[0] + 12; - this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; - this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = - this->windowContentVtx[phi_t2].v.ob[1] - 12; - phi_t0 += 5; - } - phi_t0 += 3; - for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { - this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; - this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = - this->windowContentVtx[phi_t2].v.ob[0] + 12; - this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; - this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = - this->windowContentVtx[phi_t2].v.ob[1] - 12; - phi_t0 += 9; + if (CVarGetInteger(CVAR_ENHANCEMENT("FileSelectMoreInfo"), 0) == 0) { + phi_t0 = this->windowPosX + 30; + temp_t1 = 24; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 5; + } + phi_t0 += 3; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 9; + } + } else { + phi_t0 = this->windowPosX + 33; + temp_t1 = 14; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 5; + } + phi_t0 -= 13; + temp_t1 -= 10; + for (phi_a1 = 0; phi_a1 < 2; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 12; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 12; + phi_t0 += 9; + } } } From 197115d1ed570acd0cbd34e4d6eac42e227cecd9 Mon Sep 17 00:00:00 2001 From: Arrenton Date: Wed, 8 Oct 2025 00:21:47 -0600 Subject: [PATCH 156/157] Put damage numbers in a Vertex and Matrix. Makes them smooth animated. Fixed some text rendering issues. --- soh/include/z64actor.h | 1 + soh/src/code/leveled_overlays.c | 102 ++++++++++++++++++++++++-------- soh/src/code/z_lifemeter.c | 11 +++- 3 files changed, 86 insertions(+), 28 deletions(-) diff --git a/soh/include/z64actor.h b/soh/include/z64actor.h index 99a91a89ebf..5e911ed6619 100644 --- a/soh/include/z64actor.h +++ b/soh/include/z64actor.h @@ -279,6 +279,7 @@ typedef struct Actor { u8 floatingNumberLife[7]; Vec2f floatingNumberPosition[7]; Vec2f floatingNumberVelocity[7]; + Mtx floatingDamageNumberMtx; bool ignoreExpReward; // Actor handles exp reward differently } Actor; // size = 0x14C diff --git a/soh/src/code/leveled_overlays.c b/soh/src/code/leveled_overlays.c index c2c9efd9830..b8a352ca06e 100644 --- a/soh/src/code/leveled_overlays.c +++ b/soh/src/code/leveled_overlays.c @@ -6,7 +6,9 @@ #include "textures/icon_item_24_static/icon_item_24_static.h" #include "textures/message_static/message_static.h" #include "textures/nes_font_static/nes_font_static.h" +#include "soh/frame_interpolation.h" +#include "soh/ShipUtils.h" Gfx* Gfx_Texture32(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, @@ -21,7 +23,7 @@ Gfx* Gfx_Texture32(Gfx* displayListHead, void* texture, s16 textureWidth, s16 te return displayListHead; } -Gfx* Gfx_Texture4b(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, +Gfx* Gfx_TextureIA4(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { gDPLoadTextureBlock_4b(displayListHead++, texture, G_IM_FMT_IA, textureWidth, textureHeight, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, @@ -33,6 +35,18 @@ Gfx* Gfx_Texture4b(Gfx* displayListHead, void* texture, s16 textureWidth, s16 te return displayListHead; } +Gfx* Gfx_TextureI4(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { + gDPLoadTextureBlock_4b(displayListHead++, texture, G_IM_FMT_I, textureWidth, textureHeight, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPWideTextureRectangle(displayListHead++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, + (rectTop + rectHeight) << 2, G_TX_RENDERTILE, 0, 0, dsdx, dtdy); + + return displayListHead; +} + void Leveled_DrawTexI8(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, s16 rectWidth, s16 rectHeight, u8 r, u8 g, u8 b) { @@ -62,6 +76,21 @@ void Leveled_DrawTexIA8(PlayState* play, void* texture, s16 textureWidth, s16 te CLOSE_DISPS(play->state.gfxCtx); } +void Leveled_DrawTexI4(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u8 r, u8 g, u8 b) { + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, r, g, b, play->pauseCtx.alpha); + + POLY_OPA_DISP = Gfx_TextureI4(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, + textureHeight, (s32)(1024 * (f32)textureWidth / rectWidth), + (s32)(1024 * (f32)textureHeight / rectHeight)); + + CLOSE_DISPS(play->state.gfxCtx); +} + void Leveled_DrawTex32(PlayState* play, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, s16 rectWidth, s16 rectHeight) { @@ -84,7 +113,7 @@ void Leveled_DrawTex4b(PlayState* play, void* texture, s16 textureWidth, s16 tex gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_AVERAGE); gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, play->pauseCtx.alpha); - POLY_OPA_DISP = Gfx_Texture4b(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, textureHeight, (s32)(2048 * (f32)textureWidth / rectWidth), + POLY_OPA_DISP = Gfx_TextureIA4(POLY_OPA_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, textureWidth, textureHeight, (s32)(2048 * (f32)textureWidth / rectWidth), (s32)(2048 * (f32)textureHeight / rectHeight)); CLOSE_DISPS(play->state.gfxCtx); @@ -98,7 +127,7 @@ void Leveled_OverlayDrawTex4b(PlayState* play, void* texture, s16 textureWidth, gDPSetTextureFilter(OVERLAY_DISP++, G_TF_AVERAGE); gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, alpha); - OVERLAY_DISP = Gfx_Texture4b(OVERLAY_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, rectWidth, + OVERLAY_DISP = Gfx_TextureIA4(OVERLAY_DISP, texture, textureWidth, textureHeight, rectLeft, rectTop, rectWidth, rectHeight, 1 << 10, 1 << 10); CLOSE_DISPS(play->state.gfxCtx); @@ -109,10 +138,10 @@ void ActorDamageNumber_New(Actor* actor, u16 damage) { return; Vec2f position = { 0, 0 }; - Vec2f velocity = { 0, -8 }; + Vec2f velocity = { 0, -11 }; actor->floatingNumber[0] = damage; - actor->floatingNumberLife[0] = 30; + actor->floatingNumberLife[0] = 18; actor->floatingNumberPosition[0] = position; actor->floatingNumberVelocity[0] = velocity; } @@ -264,16 +293,20 @@ void ActorExperienceNumber_Draw(PlayState* play, Actor* actor) { CLOSE_DISPS(play->state.gfxCtx); } +// Vertex / Matrix code taken mostly from the Health Bar code. +static Vtx sLeveledDamageNumberVtx[16]; void ActorDamageNumber_Draw(PlayState* play, Actor* actor) { extern const char* digitTextures[]; + u8 textureWidth = 8; + u8 textureHeight = 16; s16 val = actor->floatingNumber[0]; u8 digit[] = { 0, 0, 0, 0 }; u8 digits = 1; - u8 width = 8; - Vec3f spBC; - f32 spB4; + Vec3f projActorCenter; + f32 projActorCappedInvW; + s16 numbers_actorOffset = -20; s32 j; if (actor->floatingNumberLife[0] <= 0) @@ -313,7 +346,7 @@ void ActorDamageNumber_Draw(PlayState* play, Actor* actor) { actor->floatingNumberPosition[0].x += actor->floatingNumberVelocity[0].x; actor->floatingNumberPosition[0].y += actor->floatingNumberVelocity[0].y; - actor->floatingNumberVelocity[0].y += 2.0f; + actor->floatingNumberVelocity[0].y += 1.15f; if (actor->floatingNumberPosition[0].y >= 0) { actor->floatingNumberPosition[0].y = 0; actor->floatingNumberVelocity[0].y = -actor->floatingNumberVelocity[0].y * 0.5f; @@ -325,30 +358,47 @@ void ActorDamageNumber_Draw(PlayState* play, Actor* actor) { actor->floatingNumberLife[0]--; // Position - func_8002BE04(play, &actor->world.pos, &spBC, &spB4); + func_8002BE04(play, &actor->focus.pos, &projActorCenter, &projActorCappedInvW); - spBC.x = (160 * (spBC.x * spB4)) * 1.0f + 157 + actor->floatingNumberPosition[0].x + (digits - 1) * width / 2; - spBC.x = CLAMP(spBC.x, -320.0f, 320.0f); + projActorCenter.x = (SCREEN_WIDTH / 2) * (projActorCenter.x * projActorCappedInvW) + actor->floatingNumberPosition[0].x - (digits - 1) * textureWidth / 2; + projActorCenter.x = projActorCenter.x * (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0) ? -1 : 1); + projActorCenter.x = CLAMP(projActorCenter.x, (-SCREEN_WIDTH / 2), (SCREEN_WIDTH / 2)); - spBC.y = (120 * (spBC.y * spB4)) * -1.0f + 90 + actor->floatingNumberPosition[0].y; - spBC.y = CLAMP(spBC.y, -240.0f, 240.0f); + projActorCenter.y = (SCREEN_HEIGHT / 2) * (projActorCenter.y * projActorCappedInvW); + projActorCenter.y = projActorCenter.y + numbers_actorOffset - actor->floatingNumberPosition[0].y; + projActorCenter.y = CLAMP(projActorCenter.y, (-SCREEN_HEIGHT / 2), (SCREEN_HEIGHT / 2)); - spBC.z = spBC.z * 1.0f; + // Setup DL for overlay disp + Gfx_SetupDL_39Overlay(play->state.gfxCtx); + + Matrix_Translate(projActorCenter.x, projActorCenter.y, 0, MTXMODE_NEW); + Matrix_Scale(1.0f, -1.0, 1.0f, MTXMODE_APPLY); + Matrix_ToMtx(&actor->floatingDamageNumberMtx, __FILE__, __LINE__); + gSPMatrix(OVERLAY_DISP++, &actor->floatingDamageNumberMtx, G_MTX_MODELVIEW | G_MTX_LOAD); // Color + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 20, 255); if (actor->category == ACTORCAT_PLAYER) { - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 100, 0, 255); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 100, 0, CLAMP(actor->floatingNumberLife[0] / 8.0 * 255, 0, 255)); } else { - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, CLAMP(actor->floatingNumberLife[0] / 8.0 * 255, 0, 255)); } - // Draw - gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, - PRIMITIVE, 0); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); for (u8 i = 0; i < digits; i++) { - OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, (u8*)digitTextures[digit[i]], 8, 16, (s16)spBC.x - i * width - (digits - 1) * width * 0.5f, (s16)spBC.y, - 8, 16, 1 << 10, 1 << 10); + Ship_CreateQuadVertexGroup(&sLeveledDamageNumberVtx[4 * i], -textureWidth * i, 0, textureWidth, textureHeight, 0); + + gDPPipeSync(OVERLAY_DISP++); + + gSPVertex(OVERLAY_DISP++, sLeveledDamageNumberVtx, 16, 0); + + gDPLoadTextureBlock(OVERLAY_DISP++, digitTextures[digit[i]], G_IM_FMT_I, G_IM_SIZ_8b, textureWidth, + textureHeight, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(OVERLAY_DISP++, 0 + 4 * i, 2 + 4 * i, 3 + 4 * i, 1 + 4 * i, 0); } CLOSE_DISPS(play->state.gfxCtx); @@ -687,8 +737,8 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { // Values and Icons // Level - Leveled_DrawTexI8(play, dgMsgChar4CLatinCapitalLetterLTex, 8, 16, statX + 2, statY - 2, 10, 11, 255, 255, 255); - Leveled_DrawTexI8(play, dgMsgChar76LatinSmallLetterVTex, 8, 16, statX + 5, statY - 2, 10, 11, 255, 255, 255); + Leveled_DrawTexI4(play, dgMsgChar4CLatinCapitalLetterLTex, 16, 16, statX + 2, statY - 2, 10, 11, 255, 255, 255); + Leveled_DrawTexI4(play, dgMsgChar76LatinSmallLetterVTex, 16, 16, statX + 5, statY - 2, 10, 11, 255, 255, 255); Leveled_BigValueNumberDraw(play, statX + 10, statY - 6, player->actor.level, 255, 255, 255, 255); statY += 10; @@ -700,7 +750,7 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { } else { Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.health, 255, 255, 255); } - Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 23 + healthValX, statY - 1, 8, 9, 255, 255, 255); + Leveled_DrawTexI4(play, dgMsgChar2FSolidusTex, 16, 16, statX + 22 + healthValX, statY - 1, 12, 9, 255, 255, 255); Leveled_ValueNumberDraw(play, statX + 28 + healthValX, statY, gSaveContext.healthCapacity2, 120, 255, 0); statY += 8; // Magic @@ -708,7 +758,7 @@ void Leveled_KaleidoEquip_Stats(PlayState* play) { healthValX = gSaveContext.magicCapacity >= 100 ? 6 : 0; Leveled_DrawTex32(play, dgQuestIconMagicJarBigTex, 24, 24, statX + 2, statY, 14, 14); Leveled_ValueNumberDraw(play, statX + 10, statY, gSaveContext.magic, 255, 255, 255); - Leveled_DrawTexI8(play, dgMsgChar2FSolidusTex, 8, 16, statX + 23 + healthValX, statY - 1, 8, 9, 255, 255, 255); + Leveled_DrawTexI4(play, dgMsgChar2FSolidusTex, 16, 16, statX + 22 + healthValX, statY - 1, 12, 9, 255, 255, 255); Leveled_ValueNumberDraw(play, statX + 28 + healthValX, statY, gSaveContext.magicCapacity, 120, 255, 0); statY += 8; } diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index db4016e286f..e92190e3269 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -668,11 +668,18 @@ void HealthMeter_Draw(PlayState* play) { extern const char* fontTbl[]; gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, interfaceCtx->magicAlpha); - OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, fontTbl[15], 8, 16, numberPosX + 22, numberPosY, 8, 16, 8 << 7, 16 << 7); + gDPLoadTextureBlock_4b(OVERLAY_DISP++, fontTbl[15], 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, G_TX_NOLOD, G_TX_NOLOD); + gSPWideTextureRectangle(OVERLAY_DISP++, numberPosX + 22 << 2, numberPosY << 2, (numberPosX + 22 + 8) << 2, + (numberPosY + 16) << 2, G_TX_RENDERTILE, 0, 0, 16 << 7, 16 << 7); gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->magicAlpha); - OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, fontTbl[15], 8, 16, numberPosX + 22, numberPosY, 8, 16, 8 << 7, 16 << 7); + gDPLoadTextureBlock_4b(OVERLAY_DISP++, fontTbl[15], 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, G_TX_NOLOD, + G_TX_NOLOD); + gSPWideTextureRectangle(OVERLAY_DISP++, numberPosX + 22 << 2, numberPosY << 2, (numberPosX + 22 + 8) << 2, + (numberPosY + 16) << 2, G_TX_RENDERTILE, 0, 0, 16 << 7, 16 << 7); } else if (healthNumbersType == 1) { gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, From 4f6f5f786586ace6c1bddde84f1585d5fac9307e Mon Sep 17 00:00:00 2001 From: Arrenton Date: Fri, 16 Jan 2026 18:55:31 -0700 Subject: [PATCH 157/157] Moved where the mod version is shown --- soh/soh/Enhancements/gameplaystats.cpp | 2 -- soh/soh/SohGui/SohMenuSettings.cpp | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp index 4413de998fc..ca360543fa4 100644 --- a/soh/soh/Enhancements/gameplaystats.cpp +++ b/soh/soh/Enhancements/gameplaystats.cpp @@ -445,8 +445,6 @@ void DrawGameplayStatsHeader() { ImGui::TableSetupColumn("stat", ImGuiTableColumnFlags_WidthStretch); // if tag is empty (not a release build) if (gGitCommitTag[0] == 0) { - GameplayStatsRow("Leveled Mod Version:", "1.1.4"); - GameplayStatsRow("A Mod By Gotest, AKA Arrenton", ""); GameplayStatsRow("Git Branch:", (char*)gGitBranch); GameplayStatsRow("Git Commit Hash:", (char*)gGitCommitHash); } else { diff --git a/soh/soh/SohGui/SohMenuSettings.cpp b/soh/soh/SohGui/SohMenuSettings.cpp index 6fb9e466c4d..04452f1954c 100644 --- a/soh/soh/SohGui/SohMenuSettings.cpp +++ b/soh/soh/SohGui/SohMenuSettings.cpp @@ -215,6 +215,9 @@ void SohMenu::AddMenuSettings() { AddWidget(path, "About", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Ship Of Harkinian", WIDGET_TEXT); + + AddWidget(path, "Leveled Mod Version: 1.1.4", WIDGET_TEXT); + AddWidget(path, "A Mod By Gotest, AKA Arrenton", WIDGET_TEXT); if (gGitCommitTag[0] != 0) { AddWidget(path, gBuildVersion, WIDGET_TEXT); } else {