Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b6f2279
Restore sub-100% values for speed modifier slider. (#6465)
Malkierian Apr 6, 2026
978a219
add check for market balcony camera (#6464)
OtherBlue Apr 6, 2026
876d512
Fix Skip Planting Beans crash (#6463)
Malkierian Apr 7, 2026
77f2883
use category adj for gi (#6466)
A-Green-Spoon Apr 7, 2026
695c05d
Fix cooldown behavior for crystal and light switches, water gate (#6461)
f1ana Apr 7, 2026
40425c8
Fix seeded warp traps to be deterministic (#6469)
serprex Apr 7, 2026
4122d80
Faster bean planting (#6473)
JordanLongstaff Apr 8, 2026
69ef35d
Fix malon text and make the DMC jump unintuitive (#6479)
Pepper0ni Apr 10, 2026
88099e6
Fix generation failure for Ganon's Castle MQ Fire Trial (#6487)
serprex Apr 12, 2026
7b7b799
Make Lake Hylia water level switch in rando 10x faster (#6492)
serprex Apr 12, 2026
568813a
Remove tooltip text referencing Let It Snow chests (#6488)
serprex Apr 12, 2026
8513fd8
fix cosmetic/audio randomizing options being seeded when they shouldn…
serprex Apr 12, 2026
d855742
Avoid over-allocating with texture interpolation (#6489)
serprex Apr 12, 2026
25eb091
Revise Arrow Cycling (#6490)
serprex Apr 12, 2026
ca38dba
Add missing Hint clarities back (#6477)
jeromkiller Apr 13, 2026
13ebc31
9.2.2 Ackbar Charlie (#6494)
serprex Apr 13, 2026
adb1e46
fix bad args handling in extractor (#6501)
serprex Apr 14, 2026
feb489c
Fix shopsanity soft lock (#6502)
serprex Apr 14, 2026
cb71e22
9.2.3 Ackbar Delta (#6503)
serprex Apr 14, 2026
c75993d
Coop Mode of ArthurExtreme7
skijer Aug 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
"-T",
"v143",
"-A",
"x64"
"x64",
"-DBUILD_REMOTE_CONTROL=ON",
"-DCMAKE_PREFIX_PATH=C:/vcpkg/installed/x64-windows"
]
},
"group": "build",
Expand All @@ -45,6 +47,8 @@
"args": [
"--build",
"./build/x64",
"--config",
"Release",
"--target",
"GenerateSohOtr"
]
Expand All @@ -63,7 +67,10 @@
"windows": {
"args": [
"--build",
"./build/x64"
"./build/x64",
"--config",
"Release",
"--parallel"
]
},
"group": {
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set(CMAKE_C_STANDARD 23 CACHE STRING "The C standard to use")

set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")

project(Ship VERSION 9.2.1 LANGUAGES C CXX)
project(Ship VERSION 9.2.3 LANGUAGES C CXX)
include(CMake/soh-cvars.cmake)
include(CMake/lus-cvars.cmake)
set(SPDLOG_LEVEL_TRACE 0)
Expand Down
38 changes: 36 additions & 2 deletions soh/include/functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,8 @@ s32 func_8002DF38(PlayState* play, Actor* actor, u8 csMode);
s32 Player_SetCsActionWithHaltedActors(PlayState* play, Actor* actor, u8 arg2);
void func_8002DF90(DynaPolyActor* dynaActor);
void func_8002DFA4(DynaPolyActor* dynaActor, f32 arg1, s16 arg2);
Player* Actor_GetClosestPlayer(PlayState* play, Actor* actor);
Player* Actor_GetClosestPlayerFromPos(PlayState* play, const Vec3f* pos);
s32 Player_IsFacingActor(Actor* actor, s16 angle, PlayState* play);
s32 Actor_ActorBIsFacingActorA(Actor* actorA, Actor* actorB, s16 angle);
s32 Actor_IsFacingPlayer(Actor* actor, s16 angle);
Expand Down Expand Up @@ -487,6 +489,8 @@ void func_800304DC(PlayState* play, ActorContext* actorCtx, ActorEntry* actorEnt
void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx);
s32 func_800314D4(PlayState* play, Actor* actorB, Vec3f* arg2, f32 arg3);
void func_800315AC(PlayState* play, ActorContext* actorCtx);
void Actor_PlaySfx(Actor* actor);
void Actor_SetSfxUpdatesSuppressed(s32 suppressed);
void func_80031A28(PlayState* play, ActorContext* actorCtx);
void func_80031B14(PlayState* play, ActorContext* actorCtx);
void func_80031C3C(ActorContext* actorCtx, PlayState* play);
Expand Down Expand Up @@ -988,7 +992,7 @@ void HealthMeter_Init(PlayState* play);
void HealthMeter_Update(PlayState* play);
void HealthMeter_Draw(PlayState* play);
void HealthMeter_HandleCriticalAlarm(PlayState* play);
u32 HealthMeter_IsCritical(void);
u32 HealthMeter_IsCritical(Player* player);
void Lights_PointSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius, s32 type);
void Lights_PointNoGlowSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius);
void Lights_PointGlowSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius);
Expand Down Expand Up @@ -1066,11 +1070,27 @@ void Interface_SetSceneRestrictions(PlayState* play);
void Inventory_SwapAgeEquipment(void);
void Interface_InitHorsebackArchery(PlayState* play);
void func_800849EC(PlayState* play);
void Interface_LoadItemIconForItem(PlayState* play, u16 button, u8 item);
void Interface_LoadItemIcon1(PlayState* play, u16 button);
void Interface_LoadItemIcon2(PlayState* play, u16 button);
void Interface_ResetExtraItemData(void);
s16 Interface_GetQuickItemButtonIndex(u8 controllerPort);
s16 Interface_GetQuickItemInventorySlotForPortAndSlot(u8 controllerPort, s16 slotIndex);
s16 Interface_GetQuickItemInventorySlotForPort(u8 controllerPort);
u8 Interface_GetQuickItemForPortAndSlot(u8 controllerPort, s16 slotIndex);
u8 Interface_GetQuickItemForPort(u8 controllerPort);
void Interface_SetQuickItemForPortAndSlot(PlayState* play, u8 controllerPort, s16 slotIndex, u8 item,
s16 inventorySlot);
void Interface_SetQuickItemForPort(PlayState* play, u8 controllerPort, u8 item, s16 inventorySlot);
s16 Interface_GetQuickItemSelectedSlotForPort(u8 controllerPort);
void Interface_SetQuickItemSelectedSlotForPort(u8 controllerPort, s16 slotIndex);
s16 Interface_GetQuickItemUseSlotForPort(u8 controllerPort);
void Interface_SetQuickItemUseSlotForPort(u8 controllerPort, s16 slotIndex);
void func_80084BF4(PlayState* play, u16 flag);
uint16_t Interface_DrawTextLine(GraphicsContext* gfx, char text[], int16_t x, int16_t y, uint16_t colorR, uint16_t colorG, uint16_t colorB, uint16_t colorA, float textScale, uint8_t textShadow);
u8 Item_Give(PlayState* play, u8 item);
void Item_Give_SetPlayer(Player* player);
void Item_Give_ClearPlayer(void);
u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry);
u8 Item_CheckObtainability(u8 item);
void Inventory_DeleteItem(u16 item, u16 invSlot);
Expand All @@ -1084,12 +1104,21 @@ bool Inventory_HatchPocketCucco(PlayState* play);
void Interface_SetDoAction(PlayState* play, u16 action);
void Interface_SetNaviCall(PlayState* play, u16 naviCallState);
void Interface_LoadActionLabelB(PlayState* play, u16 action);
s32 Health_ChangeBy(PlayState* play, s16 healthChange);
s32 Health_ChangeBy(PlayState* play, s16 healthChange, Player* player);
void Rupees_ChangeBy(s16 rupeeChange);
void Inventory_ChangeAmmo(s16 item, s16 ammoChange);
void Magic_Fill(PlayState* play);
void Magic_Reset(PlayState* play);
s32 Magic_RequestChange(PlayState* play, s16 amount, s16 type);
s16 Magic_GetLevelForPort(u8 controllerPort);
s16 Magic_GetAmountForPort(u8 controllerPort);
void Magic_InitForPort(u8 controllerPort);
void Magic_ResetExtraData(void);
s16 Magic_GetStateForPlayer(Player* player);
void Magic_SetStateForPlayer(Player* player, s16 state);
void Magic_FillForPlayer(PlayState* play, Player* player);
void Magic_ResetForPlayer(PlayState* play, Player* player);
s32 Magic_RequestChangeForPlayer(PlayState* play, Player* player, s16 amount, s16 type);
void Interface_SetSubTimer(s16 seconds);
void Interface_SetSubTimerToFinalSecond(PlayState* play);
void Interface_SetTimer(s16 arg0);
Expand Down Expand Up @@ -1242,6 +1271,11 @@ s32 func_8009728C(PlayState* play, RoomContext* roomCtx, s32 roomNum);
s32 func_800973FC(PlayState* play, RoomContext* roomCtx);
void Room_Draw(PlayState* play, Room* room, u32 flags);
void func_80097534(PlayState* play, RoomContext* roomCtx);
s32 OTRRoom_AreaPersistenceEnabled(void);
void OTRRoom_ResetAreaPersistence(void);
s32 OTRRoom_IsRetainedRoom(s32 roomNum);
s32 OTRRoom_GetRetainedRoomCount(void);
Room* OTRRoom_GetRetainedRoom(s32 index);
void Sample_Destroy(GameState* thisx);
void Sample_Init(GameState* thisx);
void Inventory_ChangeEquipment(s16 equipment, u16 value);
Expand Down
3 changes: 2 additions & 1 deletion soh/include/macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@

#define RGBA8(r, g, b, a) ((((r) & 0xFF) << 24) | (((g) & 0xFF) << 16) | (((b) & 0xFF) << 8) | (((a) & 0xFF) << 0))

#define GET_PLAYER(play) ((Player*)(play)->actorCtx.actorLists[ACTORCAT_PLAYER].head)
#define GET_PLAYER1(play) ((Player*)(play)->actorCtx.actorLists[ACTORCAT_PLAYER].head)
#define GET_PLAYER(play) GET_PLAYER1(play)

#define GET_ACTIVE_CAM(play) ((play)->cameraPtrs[(play)->activeCamera]) // Upstream TODO: Camera

Expand Down
2 changes: 1 addition & 1 deletion soh/include/variables.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ extern "C"
extern u64 gGfxSPTaskOutputBuffer[0x3000]; // 0x18000 bytes
extern u8 gGfxSPTaskYieldBuffer[OS_YIELD_DATA_SIZE]; // 0xC00 bytes
extern u8 gGfxSPTaskStack[0x400]; // 0x400 bytes
extern GfxPool gGfxPools[2]; // 0x24820 bytes
extern GfxPool gGfxPools[4];
extern u8* gAudioHeap;
extern u8* gSystemHeap;
extern GameState* gGameState;
Expand Down
4 changes: 2 additions & 2 deletions soh/include/z64.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#endif

#define AUDIO_HEAP_SIZE 0x380000
#define SYSTEM_HEAP_SIZE (1024 * 1024 * 4)
#define SYSTEM_HEAP_SIZE (1024 * 1024 * 8)

#ifdef __cplusplus
namespace LUS
Expand Down Expand Up @@ -1454,7 +1454,7 @@ typedef struct PlayState {
/* 0x11D4C */ s32 (*grabPlayer)(struct PlayState* play, Player* player);
/* 0x11D50 */ s32 (*startPlayerCutscene)(struct PlayState* play, Actor* actor, s32 mode);
/* 0x11D54 */ void (*func_11D54)(Player* player, struct PlayState* play);
/* 0x11D58 */ s32 (*damagePlayer)(struct PlayState* play, s32 damage);
/* 0x11D58 */ s32 (*damagePlayer)(struct PlayState* play, Player* player, s32 damage);
/* 0x11D5C */ void (*talkWithPlayer)(struct PlayState* play, Actor* actor);
/* 0x11D60 */ MtxF viewProjectionMtxF;
/* 0x11DA0 */ MtxF billboardMtxF;
Expand Down
13 changes: 13 additions & 0 deletions soh/include/z64player.h
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,16 @@ typedef struct Player {
/* 0x0A86 */ s8 unk_A86;
/* 0x0A87 */ u8 unk_A87;
/* 0x0A88 */ Vec3f unk_A88; // previous body part 0 position
// #region Multiplayer groundwork
// 1-based controller assignment (1-4). Default is 1 for main player.
/* */ u8 controllerPort;
// If false, the actor ignores controller input and behaves as if no buttons are pressed.
/* */ u8 inputEnabled;
// Marks actors spawned as additional local players.
/* */ u8 isSecondPlayer;
// Optional link to the main player actor for follow/sync behaviors.
/* */ struct Player* linkedPlayer;
// #endregion
// #region SOH [General]
// Upstream TODO: Rename these to be more obviously SoH specific
/* */ PendingFlag pendingFlag;
Expand All @@ -953,6 +963,9 @@ typedef struct Player {
/* */ u8 boomerangQuickRecall; // Has the player pressed the boomerang button while it's in the air still?
/* */ u8 ivanFloating;
/* */ u8 ivanDamageMultiplier;
/* */ u8 bootsAreActive;
/* */ u8 bootsToggleRequest;
/* */ u8 equippedBoots;
// #endregion
} Player; // size = 0xA94

Expand Down
13 changes: 11 additions & 2 deletions soh/include/z64save.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,17 @@ typedef struct {
/* 0x003F */ u8 ocarinaGameRoundNum;
/* 0x0040 */ ItemEquips childEquips;
/* 0x004A */ ItemEquips adultEquips;
/* 0x0054 */ u32 unk_54; // this may be incorrect, currently used for alignment
/* 0x0058 */ char unk_58[0x0E];
/* 0x0054 */ s16 healthCapacity4; // "max_life" for P4
/* 0x0056 */ s16 health4; // "now_life" for P4
/* 0x0058 */ s16 healthCapacity2; // "max_life" for P2
/* 0x005A */ s16 health2; // "now_life" for P2
/* 0x005C */ s8 magicLevel2; // 0 for no magic/new load, 1 for magic, 2 for double magic (P2)
/* 0x005D */ s8 magic2; // current magic available for use (P2)
/* 0x005E */ u8 isMagicAcquired2;
/* 0x005F */ u8 isDoubleMagicAcquired2;
/* 0x0060 */ s16 healthCapacity3; // "max_life" for P3
/* 0x0062 */ s16 health3; // "now_life" for P3
/* 0x0064 */ char unk_64[0x02];
/* 0x0066 */ s16 savedSceneNum; // Upstream TODO: sceneId
/* 0x0068 */ ItemEquips equips;
/* 0x0074 */ Inventory inventory;
Expand Down
55 changes: 55 additions & 0 deletions soh/soh/Enhancements/BlueFireArrows.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/Enhancements/randomizer/SeedContext.h"
#include "soh/ShipInit.hpp"

extern "C" {
#include "overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.h"
#include "overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.h"

extern PlayState* gPlayState;
}

static void UpdateBlueFireCollidersBgBreakwall(void* actorPtr) {
BgBreakwall* thisx = (BgBreakwall*)actorPtr;
thisx->collider.info.bumper.dmgFlags |= DMG_ARROW_ICE;
}

static void UpdateBlueFireCollidersBgIceShelter(void* actorPtr) {
BgIceShelter* thisx = (BgIceShelter*)actorPtr;
thisx->cylinder1.base.acFlags |= AC_TYPE_PLAYER;
thisx->cylinder1.info.bumper.dmgFlags |= DMG_ARROW_ICE;
thisx->cylinder2.base.acFlags |= AC_TYPE_PLAYER;
thisx->cylinder2.info.bumper.dmgFlags |= DMG_ARROW_ICE;
}

static bool CheckAC(Actor* ac) {
return ac != NULL && ac->id == ACTOR_EN_ARROW && ac->child != NULL && ac->child->id == ACTOR_ARROW_ICE;
}

void RegisterBlueFireArrowsHooks() {
bool shouldRegister =
CVarGetInteger(CVAR_ENHANCEMENT("BlueFireArrows"), 0) || (IS_RANDO && RAND_GET_OPTION(RSK_BLUE_FIRE_ARROWS));

COND_ID_HOOK(OnActorInit, ACTOR_BG_BREAKWALL, shouldRegister, UpdateBlueFireCollidersBgBreakwall);
COND_ID_HOOK(OnActorInit, ACTOR_BG_ICE_SHELTER, shouldRegister, UpdateBlueFireCollidersBgIceShelter);

// fix bug where cylinder2 never checks acFlags
COND_VB_SHOULD(VB_BG_ICE_SHELTER_HIT, shouldRegister, {
BgIceShelter* thisx = va_arg(args, BgIceShelter*);

if (thisx->cylinder2.base.acFlags & AC_HIT) {
thisx->cylinder2.base.acFlags &= ~AC_HIT;
*should = true;
}
});

COND_VB_SHOULD(VB_BG_ICE_SHELTER_MELT, shouldRegister, {
BgIceShelter* thisx = va_arg(args, BgIceShelter*);

if (CheckAC(thisx->cylinder1.base.ac) || CheckAC(thisx->cylinder2.base.ac)) {
*should = true;
}
});
}

static RegisterShipInitFunc initFunc(RegisterBlueFireArrowsHooks, { "IS_RANDO", CVAR_ENHANCEMENT("BlueFireArrows") });
2 changes: 1 addition & 1 deletion soh/soh/Enhancements/Difficulty/BonkDamage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ static void RegisterBonkDamage() {
break;
}

Health_ChangeBy(gPlayState, -bonkDamage);
Health_ChangeBy(gPlayState, -bonkDamage, GET_PLAYER(gPlayState));
// Set invincibility to make Link flash red as a visual damage indicator.
Player* player = GET_PLAYER(gPlayState);
player->invincibilityTimer = 28;
Expand Down
4 changes: 3 additions & 1 deletion soh/soh/Enhancements/ExtraModes/EnemyRandomizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,9 +392,11 @@ uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, s16* posX, s16* po
// This should probably be handled on OTR generation in the future when object dependency is fully removed.
// Remove bats and Skulltulas from graveyard.
// Remove Octorok in Lost Woods.
// Remove signs in Gerudo Fortress as child
if (((*actorId == ACTOR_EN_FIREFLY || (*actorId == ACTOR_EN_SW && *params == 0)) &&
play->sceneNum == SCENE_GRAVEYARD) ||
(*actorId == ACTOR_EN_OKUTA && play->sceneNum == SCENE_LOST_WOODS)) {
(*actorId == ACTOR_EN_OKUTA && play->sceneNum == SCENE_LOST_WOODS) ||
(*actorId == ACTOR_EN_KANBAN && play->sceneNum == SCENE_GERUDOS_FORTRESS && LINK_IS_CHILD)) {
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion soh/soh/Enhancements/ExtraModes/RupeeDash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ static void UpdateRupeeDash() {
uint16_t walletSize = (CUR_UPG_VALUE(UPG_WALLET) + 1) * -1;
Rupees_ChangeBy(walletSize);
} else {
Health_ChangeBy(gPlayState, -16);
Health_ChangeBy(gPlayState, -16, GET_PLAYER(gPlayState));
}
}

Expand Down
Loading
Loading