z_horse: document horse-spawn helpers

Continuing the naming pass in `z_horse.c` for the decompilation.

| old name      | new name                  | purpose                           |
|---------------|---------------------------|-----------------------------------|
| func_8006CFC0 | Horse_IsValidScene        | “is this scene horse-compatible?” |
| func_8006D074 | Horse_SetDefaultSpawn     | restore default save-file coords  |
| func_8006D0AC | Horse_PatchLakeHyliaSpawn | one-off Lake Hylia fixer          |
| func_8006D0EC | Horse_SetNormal           | non-cutscene spawn path           |
| func_8006D684 | Horse_SetCutsceneMount    | scripted mounts / races           |
| func_8006DC68 | Horse_InitForScene        | init horse for a scene            |
| func_8006DD9C | Actor_RotateToPoint       | generic actor rotation helper     |

Misc tidy-ups
=============

* Added concise comments explaining each helper’s contract and quirks.
* Updated declarations in `include/functions.h`.

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin 2025-07-02 11:56:18 -07:00
commit 76b7b756bb
8 changed files with 46 additions and 30 deletions

View file

@ -889,13 +889,13 @@ void SkelCurve_SetAnim(SkelAnimeCurve* skelCurve, TransformUpdateIndex* transUpd
s32 SkelCurve_Update(PlayState* play, SkelAnimeCurve* skelCurve);
void SkelCurve_Draw(Actor* actor, PlayState* play, SkelAnimeCurve* skelCurve,
OverrideCurveLimbDraw overrideLimbDraw, PostCurveLimbDraw postLimbDraw, s32 lod, void* data);
s32 func_8006CFC0(s32 scene);
void func_8006D074(PlayState* play);
void func_8006D0AC(PlayState* play);
void func_8006D0EC(PlayState* play, Player* player);
void func_8006D684(PlayState* play, Player* player);
void func_8006DC68(PlayState* play, Player* player);
void func_8006DD9C(Actor* actor, Vec3f* arg1, s16 arg2);
s32 Horse_IsValidScene(s32 scene);
void Horse_SetDefaultSpawn(PlayState* play);
void Horse_PatchLakeHyliaSpawn(PlayState* play);
void Horse_SetNormal(PlayState* play, Player* player);
void Horse_SetCutsceneMount(PlayState* play, Player* player);
void Horse_InitForScene(PlayState* play, Player* player);
void Actor_RotateToPoint(Actor* actor, Vec3f* target, s16 speed);
s32 Jpeg_Decode(void* data, void* zbuffer, void* workBuff, u32 workSize);
void KaleidoSetup_Update(PlayState* play);
void KaleidoSetup_Init(PlayState* play);

View file

@ -1467,7 +1467,7 @@ s32 func_8002DEEC(Player* player) {
}
void func_8002DF18(PlayState* play, Player* player) {
func_8006DC68(play, player);
Horse_InitForScene(play, player);
}
s32 func_8002DF38(PlayState* play, Actor* actor, u8 csAction) {

View file

@ -4,7 +4,9 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
s32 func_8006CFC0(s32 scene) {
// Checks whether the current scene is one of the five “horse-compatible”
// outdoor maps (field, lake, two Gerudo areas, ranch).
s32 Horse_IsValidScene(s32 scene) {
s32 validScenes[] = { SCENE_HYRULE_FIELD, SCENE_LAKE_HYLIA, SCENE_GERUDO_VALLEY, SCENE_GERUDOS_FORTRESS,
SCENE_LON_LON_RANCH };
s32 i;
@ -18,7 +20,9 @@ s32 func_8006CFC0(s32 scene) {
return 0;
}
void func_8006D074(PlayState* play) {
// Hard-codes Eponas default spawn information (Hyrule Field near the entrance
// to Gerudo Valley)
void Horse_SetDefaultSpawn(PlayState* play) {
gSaveContext.horseData.scene = SCENE_HYRULE_FIELD;
gSaveContext.horseData.pos.x = -1840;
gSaveContext.horseData.pos.y = 72;
@ -26,7 +30,10 @@ void func_8006D074(PlayState* play) {
gSaveContext.horseData.angle = -27353;
}
void func_8006D0AC(PlayState* play) {
// Only when the save already says “Lake Hylia”, overwrite the coords to the
// proper Lake Hylia shoreline start point. A one-off “oh, youre at Lake, let me
// correct the numbers” fixer.
void Horse_PatchLakeHyliaSpawn(PlayState* play) {
if (gSaveContext.horseData.scene == SCENE_LAKE_HYLIA) {
gSaveContext.horseData.scene = SCENE_LAKE_HYLIA;
gSaveContext.horseData.pos.x = -2065;
@ -43,7 +50,9 @@ typedef struct {
/* 0x0A */ s16 type;
} HorseSpawn;
void func_8006D0EC(PlayState* play, Player* player) {
// Decides whether to actually drop a horse actor into the world or instantly
// mount Link on one.
void Horse_SetNormal(PlayState* play, Player* player) {
s32 i;
HorseSpawn horseSpawns[] = {
{ SCENE_HYRULE_FIELD, -460, 100, 6640, 0, 2 }, { SCENE_LAKE_HYLIA, -1929, -1025, 768, 0, 2 },
@ -88,7 +97,7 @@ void func_8006D0EC(PlayState* play, Player* player) {
osSyncPrintf("馬存在によるセット %d %d %d\n", gSaveContext.horseData.scene,
Flags_GetEventChkInf(EVENTCHKINF_EPONA_OBTAINED), DREG(1));
if (func_8006CFC0(gSaveContext.horseData.scene)) {
if (Horse_IsValidScene(gSaveContext.horseData.scene)) {
Actor* horseActor = Actor_Spawn(&play->actorCtx, play, ACTOR_EN_HORSE, gSaveContext.horseData.pos.x,
gSaveContext.horseData.pos.y, gSaveContext.horseData.pos.z, 0,
gSaveContext.horseData.angle, 0, 1, true);
@ -101,7 +110,7 @@ void func_8006D0EC(PlayState* play, Player* player) {
// "Horse_SetNormal():%d set spot is no good."
osSyncPrintf("Horse_SetNormal():%d セットスポットまずいです。\n", gSaveContext.horseData.scene);
osSyncPrintf(VT_RST);
func_8006D074(play);
Horse_SetDefaultSpawn(play);
}
} else if ((play->sceneNum == SCENE_LON_LON_RANCH) && !Flags_GetEventChkInf(EVENTCHKINF_EPONA_OBTAINED) &&
(DREG(1) == 0)) {
@ -137,7 +146,10 @@ typedef struct {
/* 0x10 */ s16 type;
} struct_8011F9B8;
void func_8006D684(PlayState* play, Player* player) {
// Handles “special” scripted mounts: field reload points, Lon Lon race, Gerudo
// Fortress archery, etc. It mounts Link, positions the camera, or spawns
// passive horses depending on the cutscene index in that lookup table.
void Horse_SetCutsceneMount(PlayState* play, Player* player) {
s32 pad;
s32 i;
Vec3s spawnPos;
@ -248,17 +260,19 @@ void func_8006D684(PlayState* play, Player* player) {
}
}
void func_8006DC68(PlayState* play, Player* player) {
// Top-level dispatcher for adult-Link scenes. Validates the saved spawn, then
// calls either Horse_SetCutsceneMount or Horse_SetNormal as appropriate.
void Horse_InitForScene(PlayState* play, Player* player) {
if (LINK_IS_ADULT) {
if (!func_8006CFC0(gSaveContext.horseData.scene)) {
if (!Horse_IsValidScene(gSaveContext.horseData.scene)) {
osSyncPrintf(VT_COL(RED, WHITE));
// "Horse_Set_Check():%d set spot is no good."
osSyncPrintf("Horse_Set_Check():%d セットスポットまずいです。\n", gSaveContext.horseData.scene);
osSyncPrintf(VT_RST);
func_8006D074(play);
Horse_SetDefaultSpawn(play);
}
if (func_8006CFC0(play->sceneNum)) {
if (Horse_IsValidScene(play->sceneNum)) {
if ((gSaveContext.sceneSetupIndex > 3) ||
((gSaveContext.entranceIndex == ENTR_HYRULE_FIELD_11 ||
gSaveContext.entranceIndex == ENTR_HYRULE_FIELD_12 ||
@ -267,15 +281,17 @@ void func_8006DC68(PlayState* play, Player* player) {
(gSaveContext.respawnFlag == 0)) ||
((play->sceneNum == SCENE_LON_LON_RANCH) && ((gSaveContext.eventInf[0] & 0xF) == 6) &&
!Flags_GetEventChkInf(EVENTCHKINF_EPONA_OBTAINED) && (DREG(1) == 0))) {
func_8006D684(play, player);
Horse_SetCutsceneMount(play, player);
} else {
func_8006D0EC(play, player);
Horse_SetNormal(play, player);
}
}
}
}
void func_8006DD9C(Actor* actor, Vec3f* arg1, s16 arg2) {
// Generic helper: smoothly yaw an actor toward a target point, clamping the
// turn speed. Its used by horse AI but isnt horse-specific.
void Actor_RotateToPoint(Actor* actor, Vec3f* arg1, s16 arg2) {
s16 x = Math_Vec3f_Yaw(&actor->world.pos, arg1) - actor->world.rot.y;
if (x > arg2) {

View file

@ -1913,7 +1913,7 @@ u8 Item_Give(PlayState* play, u8 item) {
osSyncPrintf(VT_RST);
if (item == ITEM_MEDALLION_WATER) {
func_8006D0AC(play);
Horse_PatchLakeHyliaSpawn(play);
}
return Return_Item(item, MOD_NONE, ITEM_NONE);

View file

@ -235,7 +235,7 @@ void EnGe1_KickPlayer(EnGe1* this, PlayState* play) {
if (this->cutsceneTimer > 0) {
this->cutsceneTimer--;
} else {
func_8006D074(play);
Horse_SetDefaultSpawn(play);
if ((INV_CONTENT(ITEM_HOOKSHOT) == ITEM_NONE) || (INV_CONTENT(ITEM_LONGSHOT) == ITEM_NONE)) {
play->nextEntranceIndex = ENTR_GERUDO_VALLEY_1;

View file

@ -244,7 +244,7 @@ void EnGe2_CaptureClose(EnGe2* this, PlayState* play) {
if (this->timer > 0) {
this->timer--;
} else {
func_8006D074(play);
Horse_SetDefaultSpawn(play);
if ((INV_CONTENT(ITEM_HOOKSHOT) == ITEM_NONE) || (INV_CONTENT(ITEM_LONGSHOT) == ITEM_NONE)) {
play->nextEntranceIndex = ENTR_GERUDO_VALLEY_1;
@ -274,7 +274,7 @@ void EnGe2_CaptureCharge(EnGe2* this, PlayState* play) {
if (this->timer > 0) {
this->timer--;
} else {
func_8006D074(play);
Horse_SetDefaultSpawn(play);
if ((INV_CONTENT(ITEM_HOOKSHOT) == ITEM_NONE) || (INV_CONTENT(ITEM_LONGSHOT) == ITEM_NONE)) {
play->nextEntranceIndex = ENTR_GERUDO_VALLEY_1;

View file

@ -532,7 +532,7 @@ void EnHorse_RaceWaypointPos(RaceWaypoint* waypoints, s32 idx, Vec3f* pos) {
}
void EnHorse_RotateToPoint(EnHorse* this, PlayState* play, Vec3f* pos, s16 turnAmount) {
func_8006DD9C(&this->actor, pos, turnAmount);
Actor_RotateToPoint(&this->actor, pos, turnAmount);
}
void EnHorse_UpdateIngoRaceInfo(EnHorse* this, PlayState* play, RaceInfo* raceInfo) {

View file

@ -453,7 +453,7 @@ void func_80A6A5A4(EnHorseLinkChild* this, PlayState* play) {
yawDiff = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(play)->actor) - this->actor.world.rot.y;
// 0.7071 = cos(pi/4)
if ((Math_CosS(yawDiff) < 0.7071f) && (this->animationIdx == 2)) {
func_8006DD9C(&this->actor, &GET_PLAYER(play)->actor.world.pos, 300);
Actor_RotateToPoint(&this->actor, &GET_PLAYER(play)->actor.world.pos, 300);
}
if (SkelAnime_Update(&this->skin.skelAnime)) {
@ -490,9 +490,9 @@ void func_80A6A7D0(EnHorseLinkChild* this, PlayState* play) {
if ((this->animationIdx == 4) || (this->animationIdx == 3) || (this->animationIdx == 2)) {
if (!this->unk_1E8) {
func_8006DD9C(&this->actor, &player->actor.world.pos, 300);
Actor_RotateToPoint(&this->actor, &player->actor.world.pos, 300);
} else {
func_8006DD9C(&this->actor, &this->actor.home.pos, 300);
Actor_RotateToPoint(&this->actor, &this->actor.home.pos, 300);
}
}