1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2025-02-18 04:45:24 +00:00

En_Cow Doc (#1492)

* Add Documentation for Cows

* Better naming for cow unlock flag

* Update with MM solutions and PR suggestions

* Readable conditions

* Decimal cycle counter

* Replace params with cow type

* Clean up type usage and function names

* Clean up cylinders

* Fix collider enum

* Rename EnCow rear collider

* Fix spacing

* PR fixes:
COLL -> COLLIDER enum
static prefix
Missed MM function naming
Specific actor flags naming

* Rename of healRot and breathTimer

* Document Epona's song reset behavior

* anon review

* fig review

* whitespace

* rework milk interaction stuff

* changes to milk comment

---------

Co-authored-by: fig02 <fig02srl@gmail.com>
This commit is contained in:
Billy 2023-08-19 15:34:48 +00:00 committed by GitHub
parent d6207b17c2
commit fc3e0f080b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 206 additions and 147 deletions

View file

@ -1,14 +1,14 @@
<Root> <Root>
<File Name="object_cow" Segment="6"> <File Name="object_cow" Segment="6">
<!-- Cow Body Skeleton --> <!-- Cow Body Skeleton -->
<Skeleton Name="gCowBodySkel" Type="Flex" LimbType="Standard" Offset="0x4010"/> <Skeleton Name="gCowBodySkel" Type="Flex" LimbType="Standard" LimbNone="COW_LIMB_NONE" LimbMax="COW_LIMB_MAX" EnumName="CowLimb" Offset="0x4010"/>
<!-- Cow Body Limbs --> <!-- Cow Body Limbs -->
<Limb Name="gCowRootLimb" LimbType="Standard" Offset="0x3FC0"/> <Limb Name="gCowRootLimb" LimbType="Standard" EnumName="COW_LIMB_ROOT" Offset="0x3FC0"/>
<Limb Name="gCowHeadLimb" LimbType="Standard" Offset="0x3FCC"/> <Limb Name="gCowHeadLimb" LimbType="Standard" EnumName="COW_LIMB_HEAD" Offset="0x3FCC"/>
<Limb Name="gCowJawLimb" LimbType="Standard" Offset="0x3FD8"/> <Limb Name="gCowJawLimb" LimbType="Standard" EnumName="COW_LIMB_JAW" Offset="0x3FD8"/>
<Limb Name="gCowNoseLimb" LimbType="Standard" Offset="0x3FE4"/> <Limb Name="gCowNoseLimb" LimbType="Standard" EnumName="COW_LIMB_NOSE" Offset="0x3FE4"/>
<Limb Name="gCowNoseRingLimb" LimbType="Standard" Offset="0x3FF0"/> <Limb Name="gCowNoseRingLimb" LimbType="Standard" EnumName="COW_LIMB_NOSE_RING" Offset="0x3FF0"/>
<!-- Cow Body Limb DisplayLists --> <!-- Cow Body Limb DisplayLists -->
<DList Name="gCowTorsoDL" Offset="0x1A80"/> <DList Name="gCowTorsoDL" Offset="0x1A80"/>
@ -18,14 +18,14 @@
<DList Name="gCowNoseRingDL" Offset="0x2628"/> <DList Name="gCowNoseRingDL" Offset="0x2628"/>
<!-- Cow Tail Skeleton --> <!-- Cow Tail Skeleton -->
<Skeleton Name="gCowTailSkel" Type="Flex" LimbType="Standard" Offset="0x4C30"/> <Skeleton Name="gCowTailSkel" Type="Flex" LimbType="Standard" LimbNone="COW_TAIL_LIMB_NONE" LimbMax="COW_TAIL_LIMB_MAX" EnumName="CowTailLimb" Offset="0x4C30"/>
<!-- Cow Tail Limbs --> <!-- Cow Tail Limbs -->
<Limb Name="gCowTailRootLimb" LimbType="Standard" Offset="0x4BE0"/> <Limb Name="gCowTailRootLimb" LimbType="Standard" EnumName="COW_TAIL_LIMB_ROOT" Offset="0x4BE0"/>
<Limb Name="gCowTailUpperLimb" LimbType="Standard" Offset="0x4BEC"/> <Limb Name="gCowTailUpperLimb" LimbType="Standard" EnumName="COW_TAIL_LIMB_UPPER" Offset="0x4BEC"/>
<Limb Name="gCowTailMiddleLimb" LimbType="Standard" Offset="0x4BF8"/> <Limb Name="gCowTailMiddleLimb" LimbType="Standard" EnumName="COW_TAIL_LIMB_MIDDLE" Offset="0x4BF8"/>
<Limb Name="gCowTailLowerLimb" LimbType="Standard" Offset="0x4C04"/> <Limb Name="gCowTailLowerLimb" LimbType="Standard" EnumName="COW_TAIL_LIMB_LOWER" Offset="0x4C04"/>
<Limb Name="gCowTailEndLimb" LimbType="Standard" Offset="0x4C10"/> <Limb Name="gCowTailEndLimb" LimbType="Standard" EnumName="COW_TAIL_LIMB_END" Offset="0x4C10"/>
<!-- Cow Tail Limb DisplayLists --> <!-- Cow Tail Limb DisplayLists -->
<DList Name="gCowTailConnectionDL" Offset="0x46F0"/> <DList Name="gCowTailConnectionDL" Offset="0x46F0"/>

View file

@ -106,6 +106,7 @@
#define R_MESSAGE_DEBUGGER_TEXTID YREG(79) #define R_MESSAGE_DEBUGGER_TEXTID YREG(79)
#define R_C_UP_ICON_X YREG(88) #define R_C_UP_ICON_X YREG(88)
#define R_C_UP_ICON_Y YREG(89) #define R_C_UP_ICON_Y YREG(89)
#define R_EPONAS_SONG_PLAYED DREG(53)
#define R_MAGIC_FILL_COLOR(i) ZREG(0 + (i)) #define R_MAGIC_FILL_COLOR(i) ZREG(0 + (i))
#define R_C_BTN_COLOR(i) ZREG(39 + (i)) #define R_C_BTN_COLOR(i) ZREG(39 + (i))
#define R_B_BTN_COLOR(i) ZREG(43 + (i)) #define R_B_BTN_COLOR(i) ZREG(43 + (i))

View file

@ -387,7 +387,7 @@ typedef enum {
#define EVENTCHKINF_1B 0x1B #define EVENTCHKINF_1B 0x1B
#define EVENTCHKINF_1C 0x1C #define EVENTCHKINF_1C 0x1C
#define EVENTCHKINF_1D 0x1D #define EVENTCHKINF_1D 0x1D
#define EVENTCHKINF_1E 0x1E #define EVENTCHKINF_HORSE_RACE_COW_UNLOCK 0x1E
#define EVENTCHKINF_20 0x20 #define EVENTCHKINF_20 0x20
#define EVENTCHKINF_21 0x21 #define EVENTCHKINF_21 0x21
#define EVENTCHKINF_22 0x22 #define EVENTCHKINF_22 0x22

View file

@ -2486,7 +2486,7 @@ void Message_DrawMain(PlayState* play, Gfx** p) {
} else { } else {
Message_CloseTextbox(play); Message_CloseTextbox(play);
if (msgCtx->lastPlayedSong == OCARINA_SONG_EPONAS) { if (msgCtx->lastPlayedSong == OCARINA_SONG_EPONAS) {
DREG(53) = 1; R_EPONAS_SONG_PLAYED = true;
} }
osSyncPrintf(VT_FGCOL(YELLOW)); osSyncPrintf(VT_FGCOL(YELLOW));
osSyncPrintf("☆☆☆ocarina=%d message->ocarina_no=%d ", msgCtx->lastPlayedSong, osSyncPrintf("☆☆☆ocarina=%d message->ocarina_no=%d ", msgCtx->lastPlayedSong,

View file

@ -5,7 +5,6 @@
*/ */
#include "z_en_cow.h" #include "z_en_cow.h"
#include "assets/objects/object_cow/object_cow.h"
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) #define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3)
@ -13,17 +12,19 @@ void EnCow_Init(Actor* thisx, PlayState* play);
void EnCow_Destroy(Actor* thisx, PlayState* play); void EnCow_Destroy(Actor* thisx, PlayState* play);
void EnCow_Update(Actor* thisx, PlayState* play2); void EnCow_Update(Actor* thisx, PlayState* play2);
void EnCow_Draw(Actor* thisx, PlayState* play); void EnCow_Draw(Actor* thisx, PlayState* play);
void func_809DFE98(Actor* thisx, PlayState* play);
void func_809E0070(Actor* thisx, PlayState* play);
void func_809DF494(EnCow* this, PlayState* play); void EnCow_TalkEnd(EnCow* this, PlayState* play);
void func_809DF6BC(EnCow* this, PlayState* play); void EnCow_GiveMilkEnd(EnCow* this, PlayState* play);
void func_809DF778(EnCow* this, PlayState* play); void EnCow_GiveMilkWait(EnCow* this, PlayState* play);
void func_809DF7D8(EnCow* this, PlayState* play); void EnCow_GiveMilk(EnCow* this, PlayState* play);
void func_809DF870(EnCow* this, PlayState* play); void EnCow_CheckForEmptyBottle(EnCow* this, PlayState* play);
void func_809DF8FC(EnCow* this, PlayState* play); void EnCow_UpdateAnimation(EnCow* this, PlayState* play);
void func_809DF96C(EnCow* this, PlayState* play); void EnCow_Talk(EnCow* this, PlayState* play);
void func_809DFA84(EnCow* this, PlayState* play); void EnCow_Idle(EnCow* this, PlayState* play);
void EnCow_DrawTail(Actor* thisx, PlayState* play);
void EnCow_UpdateTail(Actor* thisx, PlayState* play);
void EnCow_IdleTail(EnCow* this, PlayState* play);
ActorInit En_Cow_InitVars = { ActorInit En_Cow_InitVars = {
ACTOR_EN_COW, ACTOR_EN_COW,
@ -57,45 +58,50 @@ static ColliderCylinderInit sCylinderInit = {
{ 30, 40, 0, { 0, 0, 0 } }, { 30, 40, 0, { 0, 0, 0 } },
}; };
static Vec3f D_809E010C = { 0.0f, -1300.0f, 1100.0f }; static Vec3f sHeadFocusOffset = { 0.0f, -1300.0f, 1100.0f };
void func_809DEE00(Vec3f* vec, s16 rotY) { void EnCow_RotateY(Vec3f* vec, s16 rotY) {
f32 xCalc; f32 xCalc;
f32 rotCalcTemp; f32 rotCalcTemp;
rotCalcTemp = Math_CosS(rotY); rotCalcTemp = Math_CosS(rotY);
xCalc = (Math_SinS(rotY) * vec->z) + (rotCalcTemp * vec->x); xCalc = (Math_SinS(rotY) * vec->z) + (rotCalcTemp * vec->x);
rotCalcTemp = Math_SinS(rotY); rotCalcTemp = Math_SinS(rotY);
vec->z = (Math_CosS(rotY) * vec->z) + (-rotCalcTemp * vec->x); vec->z = (Math_CosS(rotY) * vec->z) + (-rotCalcTemp * vec->x);
vec->x = xCalc; vec->x = xCalc;
} }
void func_809DEE9C(EnCow* this) { void EnCow_SetColliderPos(EnCow* this) {
Vec3f vec; Vec3f vec;
vec.y = 0.0f; vec.y = 0.0f;
vec.x = 0.0f; vec.x = 0.0f;
vec.z = 30.0f; vec.z = 30.0f;
func_809DEE00(&vec, this->actor.shape.rot.y);
this->colliders[0].dim.pos.x = this->actor.world.pos.x + vec.x; EnCow_RotateY(&vec, this->actor.shape.rot.y);
this->colliders[0].dim.pos.y = this->actor.world.pos.y;
this->colliders[0].dim.pos.z = this->actor.world.pos.z + vec.z; this->colliders[COW_COLLIDER_FRONT].dim.pos.x = this->actor.world.pos.x + vec.x;
this->colliders[COW_COLLIDER_FRONT].dim.pos.y = this->actor.world.pos.y;
this->colliders[COW_COLLIDER_FRONT].dim.pos.z = this->actor.world.pos.z + vec.z;
vec.x = 0.0f; vec.x = 0.0f;
vec.y = 0.0f; vec.y = 0.0f;
vec.z = -20.0f; vec.z = -20.0f;
func_809DEE00(&vec, this->actor.shape.rot.y);
this->colliders[1].dim.pos.x = this->actor.world.pos.x + vec.x; EnCow_RotateY(&vec, this->actor.shape.rot.y);
this->colliders[1].dim.pos.y = this->actor.world.pos.y;
this->colliders[1].dim.pos.z = this->actor.world.pos.z + vec.z; this->colliders[COW_COLLIDER_REAR].dim.pos.x = this->actor.world.pos.x + vec.x;
this->colliders[COW_COLLIDER_REAR].dim.pos.y = this->actor.world.pos.y;
this->colliders[COW_COLLIDER_REAR].dim.pos.z = this->actor.world.pos.z + vec.z;
} }
void func_809DEF94(EnCow* this) { void EnCow_SetTailPos(EnCow* this) {
Vec3f vec; Vec3f vec;
VEC_SET(vec, 0.0f, 57.0f, -36.0f); VEC_SET(vec, 0.0f, 57.0f, -36.0f);
EnCow_RotateY(&vec, this->actor.shape.rot.y);
func_809DEE00(&vec, this->actor.shape.rot.y);
this->actor.world.pos.x += vec.x; this->actor.world.pos.x += vec.x;
this->actor.world.pos.y += vec.y; this->actor.world.pos.y += vec.y;
this->actor.world.pos.z += vec.z; this->actor.world.pos.z += vec.z;
@ -106,188 +112,216 @@ void EnCow_Init(Actor* thisx, PlayState* play) {
s32 pad; s32 pad;
ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 72.0f); ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 72.0f);
switch (this->actor.params) {
case 0: switch (COW_GET_TYPE(this)) {
SkelAnime_InitFlex(play, &this->skelAnime, &gCowBodySkel, NULL, this->jointTable, this->morphTable, 6); case COW_TYPE_BODY:
SkelAnime_InitFlex(play, &this->skelAnime, &gCowBodySkel, NULL, this->jointTable, this->morphTable,
COW_LIMB_MAX);
Animation_PlayLoop(&this->skelAnime, &gCowBodyChewAnim); Animation_PlayLoop(&this->skelAnime, &gCowBodyChewAnim);
Collider_InitCylinder(play, &this->colliders[0]);
Collider_SetCylinder(play, &this->colliders[0], &this->actor, &sCylinderInit); Collider_InitCylinder(play, &this->colliders[COW_COLLIDER_FRONT]);
Collider_InitCylinder(play, &this->colliders[1]); Collider_SetCylinder(play, &this->colliders[COW_COLLIDER_FRONT], &this->actor, &sCylinderInit);
Collider_SetCylinder(play, &this->colliders[1], &this->actor, &sCylinderInit);
func_809DEE9C(this); Collider_InitCylinder(play, &this->colliders[COW_COLLIDER_REAR]);
this->actionFunc = func_809DF96C; Collider_SetCylinder(play, &this->colliders[COW_COLLIDER_REAR], &this->actor, &sCylinderInit);
EnCow_SetColliderPos(this);
this->actionFunc = EnCow_Idle;
if (play->sceneId == SCENE_LINKS_HOUSE) { if (play->sceneId == SCENE_LINKS_HOUSE) {
if (!LINK_IS_ADULT) { if (!LINK_IS_ADULT) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
return; return;
} }
if (!GET_EVENTCHKINF(EVENTCHKINF_1E)) {
if (!GET_EVENTCHKINF(EVENTCHKINF_HORSE_RACE_COW_UNLOCK)) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
return; return;
} }
} }
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_COW, this->actor.world.pos.x, Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_COW, this->actor.world.pos.x,
this->actor.world.pos.y, this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0, 1); this->actor.world.pos.y, this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0,
this->unk_278 = Rand_ZeroFloat(1000.0f) + 40.0f; COW_TYPE_TAIL);
this->unk_27A = 0; this->animationTimer = Rand_ZeroFloat(1000.0f) + 40.0f;
this->breathTimer = 0;
this->actor.targetMode = 6; this->actor.targetMode = 6;
DREG(53) = 0; R_EPONAS_SONG_PLAYED = false;
break; break;
case 1:
SkelAnime_InitFlex(play, &this->skelAnime, &gCowTailSkel, NULL, this->jointTable, this->morphTable, 6); case COW_TYPE_TAIL:
SkelAnime_InitFlex(play, &this->skelAnime, &gCowTailSkel, NULL, this->jointTable, this->morphTable,
COW_TAIL_LIMB_MAX);
Animation_PlayLoop(&this->skelAnime, &gCowTailIdleAnim); Animation_PlayLoop(&this->skelAnime, &gCowTailIdleAnim);
this->actor.update = func_809DFE98; this->actor.update = EnCow_UpdateTail;
this->actor.draw = func_809E0070; this->actor.draw = EnCow_DrawTail;
this->actionFunc = func_809DFA84; this->actionFunc = EnCow_IdleTail;
func_809DEF94(this); EnCow_SetTailPos(this);
this->actor.flags &= ~ACTOR_FLAG_0; this->actor.flags &= ~ACTOR_FLAG_0;
this->unk_278 = ((u32)(Rand_ZeroFloat(1000.0f)) & 0xFFFF) + 40.0f; this->animationTimer = (u16)Rand_ZeroFloat(1000.0f) + 40.0f;
break; break;
} }
this->actor.colChkInfo.mass = MASS_IMMOVABLE; this->actor.colChkInfo.mass = MASS_IMMOVABLE;
Actor_SetScale(&this->actor, 0.01f); Actor_SetScale(&this->actor, 0.01f);
this->unk_276 = 0; this->cowFlags = 0;
} }
void EnCow_Destroy(Actor* thisx, PlayState* play) { void EnCow_Destroy(Actor* thisx, PlayState* play) {
EnCow* this = (EnCow*)thisx; EnCow* this = (EnCow*)thisx;
if (this->actor.params == 0) { if (COW_GET_TYPE(this) == COW_TYPE_BODY) {
Collider_DestroyCylinder(play, &this->colliders[0]); Collider_DestroyCylinder(play, &this->colliders[COW_COLLIDER_FRONT]);
Collider_DestroyCylinder(play, &this->colliders[1]); Collider_DestroyCylinder(play, &this->colliders[COW_COLLIDER_REAR]);
} }
} }
void func_809DF494(EnCow* this, PlayState* play) { void EnCow_UpdateAnimation(EnCow* this, PlayState* play) {
if (this->unk_278 > 0) { if (this->animationTimer > 0) {
this->unk_278--; this->animationTimer--;
} else { } else {
this->unk_278 = Rand_ZeroFloat(500.0f) + 40.0f; this->animationTimer = Rand_ZeroFloat(500.0f) + 40.0f;
Animation_Change(&this->skelAnime, &gCowBodyChewAnim, 1.0f, this->skelAnime.curFrame, Animation_Change(&this->skelAnime, &gCowBodyChewAnim, 1.0f, this->skelAnime.curFrame,
Animation_GetLastFrame(&gCowBodyChewAnim), ANIMMODE_ONCE, 1.0f); Animation_GetLastFrame(&gCowBodyChewAnim), ANIMMODE_ONCE, 1.0f);
} }
if ((this->actor.xzDistToPlayer < 150.0f) && !(this->unk_276 & 2)) { if (this->actor.xzDistToPlayer < 150.0f) {
this->unk_276 |= 2; if (!(this->cowFlags & COW_FLAG_PLAYER_NEARBY)) {
if (this->skelAnime.animation == &gCowBodyChewAnim) { this->cowFlags |= COW_FLAG_PLAYER_NEARBY;
this->unk_278 = 0;
if (this->skelAnime.animation == &gCowBodyChewAnim) {
this->animationTimer = 0;
}
} }
} }
this->unk_27A++; this->breathTimer++;
if (this->unk_27A > 48) {
this->unk_27A = 0; if (this->breathTimer > 48) {
this->breathTimer = 0;
} }
// (1.0f / 100.0f) instead of 0.01f below is necessary so 0.01f doesn't get reused mistakenly if (this->breathTimer < 32) {
if (this->unk_27A < 0x20) { this->actor.scale.x = ((Math_SinS(this->breathTimer * 0x0400) * (1.0f / 100.0f)) + 1.0f) * 0.01f;
this->actor.scale.x = ((Math_SinS(this->unk_27A << 0xA) * (1.0f / 100.0f)) + 1.0f) * 0.01f;
} else { } else {
this->actor.scale.x = 0.01f; this->actor.scale.x = 0.01f;
} }
if (this->unk_27A >= 0x11) { if (this->breathTimer > 16) {
this->actor.scale.y = ((Math_SinS((this->unk_27A << 0xA) - 0x4000) * (1.0f / 100.0f)) + 1.0f) * 0.01f; this->actor.scale.y = ((Math_SinS((this->breathTimer * 0x0400) - 0x4000) * (1.0f / 100.0f)) + 1.0f) * 0.01f;
} else { } else {
this->actor.scale.y = 0.01f; this->actor.scale.y = 0.01f;
} }
} }
void func_809DF6BC(EnCow* this, PlayState* play) { void EnCow_TalkEnd(EnCow* this, PlayState* play) {
if ((Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) { if ((Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) {
this->actor.flags &= ~ACTOR_FLAG_16; this->actor.flags &= ~ACTOR_FLAG_16;
Message_CloseTextbox(play); Message_CloseTextbox(play);
this->actionFunc = func_809DF96C; this->actionFunc = EnCow_Idle;
} }
} }
void func_809DF730(EnCow* this, PlayState* play) { void EnCow_GiveMilkEnd(EnCow* this, PlayState* play) {
if (Actor_TextboxIsClosing(&this->actor, play)) { if (Actor_TextboxIsClosing(&this->actor, play)) {
this->actor.flags &= ~ACTOR_FLAG_16; this->actor.flags &= ~ACTOR_FLAG_16;
this->actionFunc = func_809DF96C; this->actionFunc = EnCow_Idle;
} }
} }
void func_809DF778(EnCow* this, PlayState* play) { void EnCow_GiveMilkWait(EnCow* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play)) {
this->actor.parent = NULL; this->actor.parent = NULL;
this->actionFunc = func_809DF730; this->actionFunc = EnCow_GiveMilkEnd;
} else { } else {
Actor_OfferGetItem(&this->actor, play, GI_MILK, 10000.0f, 100.0f); Actor_OfferGetItem(&this->actor, play, GI_MILK, 10000.0f, 100.0f);
} }
} }
void func_809DF7D8(EnCow* this, PlayState* play) { void EnCow_GiveMilk(EnCow* this, PlayState* play) {
if ((Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) { if ((Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) {
this->actor.flags &= ~ACTOR_FLAG_16; this->actor.flags &= ~ACTOR_FLAG_16;
Message_CloseTextbox(play); Message_CloseTextbox(play);
this->actionFunc = func_809DF778; this->actionFunc = EnCow_GiveMilkWait;
Actor_OfferGetItem(&this->actor, play, GI_MILK, 10000.0f, 100.0f); Actor_OfferGetItem(&this->actor, play, GI_MILK, 10000.0f, 100.0f);
} }
} }
void func_809DF870(EnCow* this, PlayState* play) { void EnCow_CheckForEmptyBottle(EnCow* this, PlayState* play) {
if ((Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) { if ((Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) {
if (Inventory_HasEmptyBottle()) { if (Inventory_HasEmptyBottle()) {
Message_ContinueTextbox(play, 0x2007); Message_ContinueTextbox(play, 0x2007);
this->actionFunc = func_809DF7D8; this->actionFunc = EnCow_GiveMilk;
} else { } else {
Message_ContinueTextbox(play, 0x2013); Message_ContinueTextbox(play, 0x2013);
this->actionFunc = func_809DF6BC; this->actionFunc = EnCow_TalkEnd;
} }
} }
} }
void func_809DF8FC(EnCow* this, PlayState* play) { void EnCow_Talk(EnCow* this, PlayState* play) {
if (Actor_ProcessTalkRequest(&this->actor, play)) { if (Actor_ProcessTalkRequest(&this->actor, play)) {
this->actionFunc = func_809DF870; this->actionFunc = EnCow_CheckForEmptyBottle;
} else { } else {
this->actor.flags |= ACTOR_FLAG_16; this->actor.flags |= ACTOR_FLAG_16;
func_8002F2CC(&this->actor, play, 170.0f); func_8002F2CC(&this->actor, play, 170.0f);
this->actor.textId = 0x2006; this->actor.textId = 0x2006;
} }
func_809DF494(this, play);
EnCow_UpdateAnimation(this, play);
} }
void func_809DF96C(EnCow* this, PlayState* play) { void EnCow_Idle(EnCow* this, PlayState* play) {
if ((play->msgCtx.ocarinaMode == OCARINA_MODE_00) || (play->msgCtx.ocarinaMode == OCARINA_MODE_04)) { if ((play->msgCtx.ocarinaMode == OCARINA_MODE_00) || (play->msgCtx.ocarinaMode == OCARINA_MODE_04)) {
if (DREG(53) != 0) { // There is a complex interaction between `R_EPONAS_SONG_PLAYED` and `COW_FLAG_FAILED_TO_GIVE_MILK` to allow
if (this->unk_276 & 4) { // multiple cows to try and give milk on the same frame.
this->unk_276 &= ~0x4; // `COW_FLAG_FAILED_TO_GIVE_MILK` gets set if this cow is not in range with the player to interact.
DREG(53) = 0; // In the case of a failure, `R_EPONAS_SONG_PLAYED` is not set to false in case another cow can succeed.
// On the following frame, if both `R_EPONAS_SONG_PLAYED` and `COW_FLAG_FAILED_TO_GIVE_MILK` are set, the
// first cow that updates can assume all other cows also failed and can safely unset `R_EPONAS_SONG_PLAYED`.
// All cows also unset their own `COW_FLAG_FAILED_TO_GIVE_MILK` flag.
if (R_EPONAS_SONG_PLAYED) {
if (this->cowFlags & COW_FLAG_FAILED_TO_GIVE_MILK) {
this->cowFlags &= ~COW_FLAG_FAILED_TO_GIVE_MILK;
R_EPONAS_SONG_PLAYED = false;
} else { } else {
if ((this->actor.xzDistToPlayer < 150.0f) && if ((this->actor.xzDistToPlayer < 150.0f) &&
(ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) < 0x61A8)) { (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) < 25000)) {
DREG(53) = 0; R_EPONAS_SONG_PLAYED = false;
this->actionFunc = func_809DF8FC; this->actionFunc = EnCow_Talk;
this->actor.flags |= ACTOR_FLAG_16; this->actor.flags |= ACTOR_FLAG_16;
func_8002F2CC(&this->actor, play, 170.0f); func_8002F2CC(&this->actor, play, 170.0f);
this->actor.textId = 0x2006; this->actor.textId = 0x2006;
} else { } else {
this->unk_276 |= 4; this->cowFlags |= COW_FLAG_FAILED_TO_GIVE_MILK;
} }
} }
} else { } else {
this->unk_276 &= ~0x4; this->cowFlags &= ~COW_FLAG_FAILED_TO_GIVE_MILK;
} }
} }
func_809DF494(this, play);
EnCow_UpdateAnimation(this, play);
} }
void func_809DFA84(EnCow* this, PlayState* play) { void EnCow_IdleTail(EnCow* this, PlayState* play) {
if (this->unk_278 > 0) { if (this->animationTimer > 0) {
this->unk_278--; this->animationTimer--;
} else { } else {
this->unk_278 = Rand_ZeroFloat(200.0f) + 40.0f; this->animationTimer = Rand_ZeroFloat(200.0f) + 40.0f;
Animation_Change(&this->skelAnime, &gCowTailIdleAnim, 1.0f, this->skelAnime.curFrame, Animation_Change(&this->skelAnime, &gCowTailIdleAnim, 1.0f, this->skelAnime.curFrame,
Animation_GetLastFrame(&gCowTailIdleAnim), ANIMMODE_ONCE, 1.0f); Animation_GetLastFrame(&gCowTailIdleAnim), ANIMMODE_ONCE, 1.0f);
} }
if ((this->actor.xzDistToPlayer < 150.0f) && if ((this->actor.xzDistToPlayer < 150.0f) &&
(ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) >= 0x61A9) && !(this->unk_276 & 2)) { (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) > 25000)) {
this->unk_276 |= 2; if (!(this->cowFlags & COW_FLAG_PLAYER_NEARBY)) {
if (this->skelAnime.animation == &gCowTailIdleAnim) { this->cowFlags |= COW_FLAG_PLAYER_NEARBY;
this->unk_278 = 0;
if (this->skelAnime.animation == &gCowTailIdleAnim) {
this->animationTimer = 0;
}
} }
} }
} }
@ -299,10 +333,12 @@ void EnCow_Update(Actor* thisx, PlayState* play2) {
s16 targetY; s16 targetY;
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
CollisionCheck_SetOC(play, &play->colChkCtx, &this->colliders[0].base); CollisionCheck_SetOC(play, &play->colChkCtx, &this->colliders[COW_COLLIDER_FRONT].base);
CollisionCheck_SetOC(play, &play->colChkCtx, &this->colliders[1].base); CollisionCheck_SetOC(play, &play->colChkCtx, &this->colliders[COW_COLLIDER_REAR].base);
Actor_MoveXZGravity(thisx); Actor_MoveXZGravity(thisx);
Actor_UpdateBgCheckInfo(play, thisx, 0.0f, 0.0f, 0.0f, UPDBGCHECKINFO_FLAG_2); Actor_UpdateBgCheckInfo(play, thisx, 0.0f, 0.0f, 0.0f, UPDBGCHECKINFO_FLAG_2);
if (SkelAnime_Update(&this->skelAnime)) { if (SkelAnime_Update(&this->skelAnime)) {
if (this->skelAnime.animation == &gCowBodyChewAnim) { if (this->skelAnime.animation == &gCowBodyChewAnim) {
Actor_PlaySfx(thisx, NA_SE_EV_COW_CRY); Actor_PlaySfx(thisx, NA_SE_EV_COW_CRY);
@ -313,7 +349,9 @@ void EnCow_Update(Actor* thisx, PlayState* play2) {
ANIMMODE_LOOP, 1.0f); ANIMMODE_LOOP, 1.0f);
} }
} }
this->actionFunc(this, play); this->actionFunc(this, play);
if ((thisx->xzDistToPlayer < 150.0f) && if ((thisx->xzDistToPlayer < 150.0f) &&
(ABS(Math_Vec3f_Yaw(&thisx->world.pos, &player->actor.world.pos)) < 0xC000)) { (ABS(Math_Vec3f_Yaw(&thisx->world.pos, &player->actor.world.pos)) < 0xC000)) {
targetX = Math_Vec3f_Pitch(&thisx->focus.pos, &player->actor.focus.pos); targetX = Math_Vec3f_Pitch(&thisx->focus.pos, &player->actor.focus.pos);
@ -330,16 +368,16 @@ void EnCow_Update(Actor* thisx, PlayState* play2) {
} else if (targetY < -0x2500) { } else if (targetY < -0x2500) {
targetY = -0x2500; targetY = -0x2500;
} }
} else { } else {
targetY = 0; targetY = 0;
targetX = 0; targetX = 0;
} }
Math_SmoothStepToS(&this->someRot.x, targetX, 0xA, 0xC8, 0xA);
Math_SmoothStepToS(&this->someRot.y, targetY, 0xA, 0xC8, 0xA); Math_SmoothStepToS(&this->headRot.x, targetX, 10, 200, 10);
Math_SmoothStepToS(&this->headRot.y, targetY, 10, 200, 10);
} }
void func_809DFE98(Actor* thisx, PlayState* play) { void EnCow_UpdateTail(Actor* thisx, PlayState* play) {
EnCow* this = (EnCow*)thisx; EnCow* this = (EnCow*)thisx;
s32 pad; s32 pad;
@ -352,27 +390,30 @@ void func_809DFE98(Actor* thisx, PlayState* play) {
ANIMMODE_LOOP, 1.0f); ANIMMODE_LOOP, 1.0f);
} }
} }
this->actionFunc(this, play); this->actionFunc(this, play);
} }
s32 EnCow_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { s32 EnCow_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) {
EnCow* this = (EnCow*)thisx; EnCow* this = (EnCow*)thisx;
if (limbIndex == 2) { if (limbIndex == COW_LIMB_HEAD) {
rot->y += this->someRot.y; rot->y += this->headRot.y;
rot->x += this->someRot.x; rot->x += this->headRot.x;
} }
if (limbIndex == 5) {
if (limbIndex == COW_LIMB_NOSE_RING) {
*dList = NULL; *dList = NULL;
} }
return false; return false;
} }
void EnCow_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { void EnCow_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) {
EnCow* this = (EnCow*)thisx; EnCow* this = (EnCow*)thisx;
if (limbIndex == 2) { if (limbIndex == COW_LIMB_HEAD) {
Matrix_MultVec3f(&D_809E010C, &this->actor.focus.pos); Matrix_MultVec3f(&sHeadFocusOffset, &this->actor.focus.pos);
} }
} }
@ -384,7 +425,7 @@ void EnCow_Draw(Actor* thisx, PlayState* play) {
EnCow_OverrideLimbDraw, EnCow_PostLimbDraw, this); EnCow_OverrideLimbDraw, EnCow_PostLimbDraw, this);
} }
void func_809E0070(Actor* thisx, PlayState* play) { void EnCow_DrawTail(Actor* thisx, PlayState* play) {
EnCow* this = (EnCow*)thisx; EnCow* this = (EnCow*)thisx;
Gfx_SetupDL_37Opa(play->state.gfxCtx); Gfx_SetupDL_37Opa(play->state.gfxCtx);

View file

@ -3,6 +3,23 @@
#include "ultra64.h" #include "ultra64.h"
#include "global.h" #include "global.h"
#include "assets/objects/object_cow/object_cow.h"
#define COW_FLAG_PLAYER_NEARBY (1 << 1)
#define COW_FLAG_FAILED_TO_GIVE_MILK (1 << 2)
#define COW_GET_TYPE(thisx) ((thisx)->actor.params)
typedef enum {
/* 0 */ COW_TYPE_BODY,
/* 1 */ COW_TYPE_TAIL
} CowType;
typedef enum {
/* 0 */ COW_COLLIDER_FRONT,
/* 1 */ COW_COLLIDER_REAR,
/* 2 */ COW_COLLIDER_MAX
} CowCollider;
struct EnCow; struct EnCow;
@ -10,14 +27,14 @@ typedef void (*EnCowActionFunc)(struct EnCow*, PlayState*);
typedef struct EnCow { typedef struct EnCow {
/* 0x0000 */ Actor actor; /* 0x0000 */ Actor actor;
/* 0x014C */ ColliderCylinder colliders[2]; /* 0x014C */ ColliderCylinder colliders[COW_COLLIDER_MAX];
/* 0x01E4 */ SkelAnime skelAnime; /* 0x01E4 */ SkelAnime skelAnime;
/* 0x0228 */ Vec3s jointTable[6]; /* 0x0228 */ Vec3s jointTable[COW_LIMB_MAX];
/* 0x024C */ Vec3s morphTable[6]; /* 0x024C */ Vec3s morphTable[COW_LIMB_MAX];
/* 0x0270 */ Vec3s someRot; /* 0x0270 */ Vec3s headRot;
/* 0x0276 */ u16 unk_276; /* 0x0276 */ u16 cowFlags;
/* 0x0278 */ u16 unk_278; /* 0x0278 */ u16 animationTimer;
/* 0x027A */ u16 unk_27A; /* 0x027A */ u16 breathTimer;
/* 0x027C */ EnCowActionFunc actionFunc; /* 0x027C */ EnCowActionFunc actionFunc;
} EnCow; // size = 0x0280 } EnCow; // size = 0x0280

View file

@ -743,7 +743,7 @@ void EnHorse_Init(Actor* thisx, PlayState* play2) {
AREG(6) = 0; AREG(6) = 0;
Actor_ProcessInitChain(&this->actor, sInitChain); Actor_ProcessInitChain(&this->actor, sInitChain);
EnHorse_ClearDustFlags(&this->dustFlags); EnHorse_ClearDustFlags(&this->dustFlags);
DREG(53) = 0; R_EPONAS_SONG_PLAYED = false;
this->riderPos = this->actor.world.pos; this->riderPos = this->actor.world.pos;
this->noInputTimer = 0; this->noInputTimer = 0;
this->noInputTimerMax = 0; this->noInputTimerMax = 0;
@ -1735,8 +1735,8 @@ void EnHorse_SetFollowAnimation(EnHorse* this, PlayState* play);
void EnHorse_Inactive(EnHorse* this, PlayState* play2) { void EnHorse_Inactive(EnHorse* this, PlayState* play2) {
PlayState* play = play2; PlayState* play = play2;
if (DREG(53) != 0 && this->type == HORSE_EPONA) { if (R_EPONAS_SONG_PLAYED && this->type == HORSE_EPONA) {
DREG(53) = 0; R_EPONAS_SONG_PLAYED = false;
if (EnHorse_Spawn(this, play) != 0) { if (EnHorse_Spawn(this, play) != 0) {
Audio_PlaySfxGeneral(NA_SE_EV_HORSE_NEIGH, &this->actor.projectedPos, 4, &gSfxDefaultFreqAndVolScale, Audio_PlaySfxGeneral(NA_SE_EV_HORSE_NEIGH, &this->actor.projectedPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
@ -1810,8 +1810,8 @@ void EnHorse_Idle(EnHorse* this, PlayState* play) {
this->actor.speed = 0.0f; this->actor.speed = 0.0f;
EnHorse_IdleAnimSounds(this, play); EnHorse_IdleAnimSounds(this, play);
if (DREG(53) && this->type == HORSE_EPONA) { if (R_EPONAS_SONG_PLAYED && this->type == HORSE_EPONA) {
DREG(53) = 0; R_EPONAS_SONG_PLAYED = false;
if (!func_80A5BBBC(play, this, &this->actor.world.pos)) { if (!func_80A5BBBC(play, this, &this->actor.world.pos)) {
if (EnHorse_Spawn(this, play)) { if (EnHorse_Spawn(this, play)) {
Audio_PlaySfxGeneral(NA_SE_EV_HORSE_NEIGH, &this->actor.projectedPos, 4, &gSfxDefaultFreqAndVolScale, Audio_PlaySfxGeneral(NA_SE_EV_HORSE_NEIGH, &this->actor.projectedPos, 4, &gSfxDefaultFreqAndVolScale,
@ -1905,7 +1905,7 @@ void EnHorse_FollowPlayer(EnHorse* this, PlayState* play) {
f32 distToPlayer; f32 distToPlayer;
f32 angleDiff; f32 angleDiff;
DREG(53) = 0; R_EPONAS_SONG_PLAYED = false;
distToPlayer = Actor_WorldDistXZToActor(&this->actor, &GET_PLAYER(play)->actor); distToPlayer = Actor_WorldDistXZToActor(&this->actor, &GET_PLAYER(play)->actor);
// First rotate if the player is behind // First rotate if the player is behind
@ -2597,7 +2597,7 @@ void EnHorse_FleePlayer(EnHorse* this, PlayState* play) {
s32 animFinished; s32 animFinished;
s16 yaw; s16 yaw;
if (DREG(53) || this->type == HORSE_HNI) { if (R_EPONAS_SONG_PLAYED || this->type == HORSE_HNI) {
EnHorse_StartIdleRidable(this); EnHorse_StartIdleRidable(this);
Audio_PlaySfxGeneral(NA_SE_EV_HORSE_NEIGH, &this->actor.projectedPos, 4, &gSfxDefaultFreqAndVolScale, Audio_PlaySfxGeneral(NA_SE_EV_HORSE_NEIGH, &this->actor.projectedPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);

View file

@ -358,7 +358,7 @@ void func_80A6A068(EnHorseLinkChild* this, PlayState* play) {
return; return;
} }
if ((GET_EVENTCHKINF(EVENTCHKINF_16) && (DREG(53) != 0)) || if ((GET_EVENTCHKINF(EVENTCHKINF_16) && R_EPONAS_SONG_PLAYED) ||
((play->sceneId == SCENE_LON_LON_RANCH) && (gSaveContext.save.cutsceneIndex == 0xFFF1))) { ((play->sceneId == SCENE_LON_LON_RANCH) && (gSaveContext.save.cutsceneIndex == 0xFFF1))) {
func_80A6A4DC(this); func_80A6A4DC(this);
} else { } else {
@ -434,7 +434,7 @@ void func_80A6A068(EnHorseLinkChild* this, PlayState* play) {
void func_80A6A4DC(EnHorseLinkChild* this) { void func_80A6A4DC(EnHorseLinkChild* this) {
this->action = 5; this->action = 5;
this->animationIdx = Rand_ZeroOne() > 0.5f ? 0 : 1; this->animationIdx = Rand_ZeroOne() > 0.5f ? 0 : 1;
DREG(53) = 0; R_EPONAS_SONG_PLAYED = false;
Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f, Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f,
Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f); Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f);
} }
@ -442,8 +442,8 @@ void func_80A6A4DC(EnHorseLinkChild* this) {
void func_80A6A5A4(EnHorseLinkChild* this, PlayState* play) { void func_80A6A5A4(EnHorseLinkChild* this, PlayState* play) {
s16 yawDiff; s16 yawDiff;
if (DREG(53) != 0) { if (R_EPONAS_SONG_PLAYED) {
DREG(53) = 0; R_EPONAS_SONG_PLAYED = false;
Audio_PlaySfxGeneral(NA_SE_EV_KID_HORSE_NEIGH, &this->actor.projectedPos, 4, &gSfxDefaultFreqAndVolScale, Audio_PlaySfxGeneral(NA_SE_EV_KID_HORSE_NEIGH, &this->actor.projectedPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
func_80A6A724(this); func_80A6A724(this);

View file

@ -87,7 +87,7 @@ u16 EnMa3_GetTextId(PlayState* play, Actor* thisx) {
HIGH_SCORE(HS_HORSE_RACE) = 180; HIGH_SCORE(HS_HORSE_RACE) = 180;
} }
if (!GET_EVENTCHKINF(EVENTCHKINF_1E) && (((void)0, gSaveContext.timerSeconds) < 50)) { if (!GET_EVENTCHKINF(EVENTCHKINF_HORSE_RACE_COW_UNLOCK) && (((void)0, gSaveContext.timerSeconds) < 50)) {
return 0x208F; return 0x208F;
} }
@ -128,7 +128,7 @@ s16 EnMa3_UpdateTalkState(PlayState* play, Actor* thisx) {
if (Message_ShouldAdvance(play)) { if (Message_ShouldAdvance(play)) {
SET_INFTABLE(INFTABLE_B9); SET_INFTABLE(INFTABLE_B9);
if (play->msgCtx.choiceIndex == 0) { if (play->msgCtx.choiceIndex == 0) {
if (GET_EVENTCHKINF(EVENTCHKINF_1E)) { if (GET_EVENTCHKINF(EVENTCHKINF_HORSE_RACE_COW_UNLOCK)) {
Message_ContinueTextbox(play, 0x2091); Message_ContinueTextbox(play, 0x2091);
} else if (HIGH_SCORE(HS_HORSE_RACE) == 0) { } else if (HIGH_SCORE(HS_HORSE_RACE) == 0) {
Message_ContinueTextbox(play, 0x2092); Message_ContinueTextbox(play, 0x2092);
@ -145,7 +145,7 @@ s16 EnMa3_UpdateTalkState(PlayState* play, Actor* thisx) {
talkState = NPC_TALK_STATE_IDLE; talkState = NPC_TALK_STATE_IDLE;
break; break;
case 0x208F: case 0x208F:
SET_EVENTCHKINF(EVENTCHKINF_1E); SET_EVENTCHKINF(EVENTCHKINF_HORSE_RACE_COW_UNLOCK);
FALLTHROUGH; FALLTHROUGH;
case 0x2004: case 0x2004:
case 0x2012: case 0x2012:

View file

@ -229,7 +229,7 @@ void EnNiw_Init(Actor* thisx, PlayState* play) {
case 0xD: case 0xD:
case 0xE: case 0xE:
Collider_SetCylinder(play, &this->collider, &this->actor, &sCylinderInit2); Collider_SetCylinder(play, &this->collider, &this->actor, &sCylinderInit2);
if (play->sceneId == SCENE_LINKS_HOUSE && !GET_EVENTCHKINF(EVENTCHKINF_1E)) { if (play->sceneId == SCENE_LINKS_HOUSE && !GET_EVENTCHKINF(EVENTCHKINF_HORSE_RACE_COW_UNLOCK)) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
} }
break; break;