1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2024-11-25 09:45:02 +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>
<File Name="object_cow" Segment="6">
<!-- 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 -->
<Limb Name="gCowRootLimb" LimbType="Standard" Offset="0x3FC0"/>
<Limb Name="gCowHeadLimb" LimbType="Standard" Offset="0x3FCC"/>
<Limb Name="gCowJawLimb" LimbType="Standard" Offset="0x3FD8"/>
<Limb Name="gCowNoseLimb" LimbType="Standard" Offset="0x3FE4"/>
<Limb Name="gCowNoseRingLimb" LimbType="Standard" Offset="0x3FF0"/>
<Limb Name="gCowRootLimb" LimbType="Standard" EnumName="COW_LIMB_ROOT" Offset="0x3FC0"/>
<Limb Name="gCowHeadLimb" LimbType="Standard" EnumName="COW_LIMB_HEAD" Offset="0x3FCC"/>
<Limb Name="gCowJawLimb" LimbType="Standard" EnumName="COW_LIMB_JAW" Offset="0x3FD8"/>
<Limb Name="gCowNoseLimb" LimbType="Standard" EnumName="COW_LIMB_NOSE" Offset="0x3FE4"/>
<Limb Name="gCowNoseRingLimb" LimbType="Standard" EnumName="COW_LIMB_NOSE_RING" Offset="0x3FF0"/>
<!-- Cow Body Limb DisplayLists -->
<DList Name="gCowTorsoDL" Offset="0x1A80"/>
@ -18,14 +18,14 @@
<DList Name="gCowNoseRingDL" Offset="0x2628"/>
<!-- 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 -->
<Limb Name="gCowTailRootLimb" LimbType="Standard" Offset="0x4BE0"/>
<Limb Name="gCowTailUpperLimb" LimbType="Standard" Offset="0x4BEC"/>
<Limb Name="gCowTailMiddleLimb" LimbType="Standard" Offset="0x4BF8"/>
<Limb Name="gCowTailLowerLimb" LimbType="Standard" Offset="0x4C04"/>
<Limb Name="gCowTailEndLimb" LimbType="Standard" Offset="0x4C10"/>
<Limb Name="gCowTailRootLimb" LimbType="Standard" EnumName="COW_TAIL_LIMB_ROOT" Offset="0x4BE0"/>
<Limb Name="gCowTailUpperLimb" LimbType="Standard" EnumName="COW_TAIL_LIMB_UPPER" Offset="0x4BEC"/>
<Limb Name="gCowTailMiddleLimb" LimbType="Standard" EnumName="COW_TAIL_LIMB_MIDDLE" Offset="0x4BF8"/>
<Limb Name="gCowTailLowerLimb" LimbType="Standard" EnumName="COW_TAIL_LIMB_LOWER" Offset="0x4C04"/>
<Limb Name="gCowTailEndLimb" LimbType="Standard" EnumName="COW_TAIL_LIMB_END" Offset="0x4C10"/>
<!-- Cow Tail Limb DisplayLists -->
<DList Name="gCowTailConnectionDL" Offset="0x46F0"/>

View file

@ -106,6 +106,7 @@
#define R_MESSAGE_DEBUGGER_TEXTID YREG(79)
#define R_C_UP_ICON_X YREG(88)
#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_C_BTN_COLOR(i) ZREG(39 + (i))
#define R_B_BTN_COLOR(i) ZREG(43 + (i))

View file

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

View file

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

View file

@ -5,7 +5,6 @@
*/
#include "z_en_cow.h"
#include "assets/objects/object_cow/object_cow.h"
#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_Update(Actor* thisx, PlayState* play2);
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 func_809DF6BC(EnCow* this, PlayState* play);
void func_809DF778(EnCow* this, PlayState* play);
void func_809DF7D8(EnCow* this, PlayState* play);
void func_809DF870(EnCow* this, PlayState* play);
void func_809DF8FC(EnCow* this, PlayState* play);
void func_809DF96C(EnCow* this, PlayState* play);
void func_809DFA84(EnCow* this, PlayState* play);
void EnCow_TalkEnd(EnCow* this, PlayState* play);
void EnCow_GiveMilkEnd(EnCow* this, PlayState* play);
void EnCow_GiveMilkWait(EnCow* this, PlayState* play);
void EnCow_GiveMilk(EnCow* this, PlayState* play);
void EnCow_CheckForEmptyBottle(EnCow* this, PlayState* play);
void EnCow_UpdateAnimation(EnCow* this, PlayState* play);
void EnCow_Talk(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 = {
ACTOR_EN_COW,
@ -57,45 +58,50 @@ static ColliderCylinderInit sCylinderInit = {
{ 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 rotCalcTemp;
rotCalcTemp = Math_CosS(rotY);
xCalc = (Math_SinS(rotY) * vec->z) + (rotCalcTemp * vec->x);
rotCalcTemp = Math_SinS(rotY);
vec->z = (Math_CosS(rotY) * vec->z) + (-rotCalcTemp * vec->x);
vec->x = xCalc;
}
void func_809DEE9C(EnCow* this) {
void EnCow_SetColliderPos(EnCow* this) {
Vec3f vec;
vec.y = 0.0f;
vec.x = 0.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;
this->colliders[0].dim.pos.y = this->actor.world.pos.y;
this->colliders[0].dim.pos.z = this->actor.world.pos.z + vec.z;
EnCow_RotateY(&vec, this->actor.shape.rot.y);
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.y = 0.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;
this->colliders[1].dim.pos.y = this->actor.world.pos.y;
this->colliders[1].dim.pos.z = this->actor.world.pos.z + vec.z;
EnCow_RotateY(&vec, this->actor.shape.rot.y);
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;
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.y += vec.y;
this->actor.world.pos.z += vec.z;
@ -106,188 +112,216 @@ void EnCow_Init(Actor* thisx, PlayState* play) {
s32 pad;
ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 72.0f);
switch (this->actor.params) {
case 0:
SkelAnime_InitFlex(play, &this->skelAnime, &gCowBodySkel, NULL, this->jointTable, this->morphTable, 6);
switch (COW_GET_TYPE(this)) {
case COW_TYPE_BODY:
SkelAnime_InitFlex(play, &this->skelAnime, &gCowBodySkel, NULL, this->jointTable, this->morphTable,
COW_LIMB_MAX);
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[1]);
Collider_SetCylinder(play, &this->colliders[1], &this->actor, &sCylinderInit);
func_809DEE9C(this);
this->actionFunc = func_809DF96C;
Collider_InitCylinder(play, &this->colliders[COW_COLLIDER_FRONT]);
Collider_SetCylinder(play, &this->colliders[COW_COLLIDER_FRONT], &this->actor, &sCylinderInit);
Collider_InitCylinder(play, &this->colliders[COW_COLLIDER_REAR]);
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 (!LINK_IS_ADULT) {
Actor_Kill(&this->actor);
return;
}
if (!GET_EVENTCHKINF(EVENTCHKINF_1E)) {
if (!GET_EVENTCHKINF(EVENTCHKINF_HORSE_RACE_COW_UNLOCK)) {
Actor_Kill(&this->actor);
return;
}
}
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->unk_278 = Rand_ZeroFloat(1000.0f) + 40.0f;
this->unk_27A = 0;
this->actor.world.pos.y, this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0,
COW_TYPE_TAIL);
this->animationTimer = Rand_ZeroFloat(1000.0f) + 40.0f;
this->breathTimer = 0;
this->actor.targetMode = 6;
DREG(53) = 0;
R_EPONAS_SONG_PLAYED = false;
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);
this->actor.update = func_809DFE98;
this->actor.draw = func_809E0070;
this->actionFunc = func_809DFA84;
func_809DEF94(this);
this->actor.update = EnCow_UpdateTail;
this->actor.draw = EnCow_DrawTail;
this->actionFunc = EnCow_IdleTail;
EnCow_SetTailPos(this);
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;
}
this->actor.colChkInfo.mass = MASS_IMMOVABLE;
Actor_SetScale(&this->actor, 0.01f);
this->unk_276 = 0;
this->cowFlags = 0;
}
void EnCow_Destroy(Actor* thisx, PlayState* play) {
EnCow* this = (EnCow*)thisx;
if (this->actor.params == 0) {
Collider_DestroyCylinder(play, &this->colliders[0]);
Collider_DestroyCylinder(play, &this->colliders[1]);
if (COW_GET_TYPE(this) == COW_TYPE_BODY) {
Collider_DestroyCylinder(play, &this->colliders[COW_COLLIDER_FRONT]);
Collider_DestroyCylinder(play, &this->colliders[COW_COLLIDER_REAR]);
}
}
void func_809DF494(EnCow* this, PlayState* play) {
if (this->unk_278 > 0) {
this->unk_278--;
void EnCow_UpdateAnimation(EnCow* this, PlayState* play) {
if (this->animationTimer > 0) {
this->animationTimer--;
} 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_GetLastFrame(&gCowBodyChewAnim), ANIMMODE_ONCE, 1.0f);
}
if ((this->actor.xzDistToPlayer < 150.0f) && !(this->unk_276 & 2)) {
this->unk_276 |= 2;
if (this->skelAnime.animation == &gCowBodyChewAnim) {
this->unk_278 = 0;
if (this->actor.xzDistToPlayer < 150.0f) {
if (!(this->cowFlags & COW_FLAG_PLAYER_NEARBY)) {
this->cowFlags |= COW_FLAG_PLAYER_NEARBY;
if (this->skelAnime.animation == &gCowBodyChewAnim) {
this->animationTimer = 0;
}
}
}
this->unk_27A++;
if (this->unk_27A > 48) {
this->unk_27A = 0;
this->breathTimer++;
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->unk_27A < 0x20) {
this->actor.scale.x = ((Math_SinS(this->unk_27A << 0xA) * (1.0f / 100.0f)) + 1.0f) * 0.01f;
if (this->breathTimer < 32) {
this->actor.scale.x = ((Math_SinS(this->breathTimer * 0x0400) * (1.0f / 100.0f)) + 1.0f) * 0.01f;
} else {
this->actor.scale.x = 0.01f;
}
if (this->unk_27A >= 0x11) {
this->actor.scale.y = ((Math_SinS((this->unk_27A << 0xA) - 0x4000) * (1.0f / 100.0f)) + 1.0f) * 0.01f;
if (this->breathTimer > 16) {
this->actor.scale.y = ((Math_SinS((this->breathTimer * 0x0400) - 0x4000) * (1.0f / 100.0f)) + 1.0f) * 0.01f;
} else {
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)) {
this->actor.flags &= ~ACTOR_FLAG_16;
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)) {
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)) {
this->actor.parent = NULL;
this->actionFunc = func_809DF730;
this->actionFunc = EnCow_GiveMilkEnd;
} else {
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)) {
this->actor.flags &= ~ACTOR_FLAG_16;
Message_CloseTextbox(play);
this->actionFunc = func_809DF778;
this->actionFunc = EnCow_GiveMilkWait;
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 (Inventory_HasEmptyBottle()) {
Message_ContinueTextbox(play, 0x2007);
this->actionFunc = func_809DF7D8;
this->actionFunc = EnCow_GiveMilk;
} else {
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)) {
this->actionFunc = func_809DF870;
this->actionFunc = EnCow_CheckForEmptyBottle;
} else {
this->actor.flags |= ACTOR_FLAG_16;
func_8002F2CC(&this->actor, play, 170.0f);
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 (DREG(53) != 0) {
if (this->unk_276 & 4) {
this->unk_276 &= ~0x4;
DREG(53) = 0;
// There is a complex interaction between `R_EPONAS_SONG_PLAYED` and `COW_FLAG_FAILED_TO_GIVE_MILK` to allow
// multiple cows to try and give milk on the same frame.
// `COW_FLAG_FAILED_TO_GIVE_MILK` gets set if this cow is not in range with the player to interact.
// 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 {
if ((this->actor.xzDistToPlayer < 150.0f) &&
(ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) < 0x61A8)) {
DREG(53) = 0;
this->actionFunc = func_809DF8FC;
(ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) < 25000)) {
R_EPONAS_SONG_PLAYED = false;
this->actionFunc = EnCow_Talk;
this->actor.flags |= ACTOR_FLAG_16;
func_8002F2CC(&this->actor, play, 170.0f);
this->actor.textId = 0x2006;
} else {
this->unk_276 |= 4;
this->cowFlags |= COW_FLAG_FAILED_TO_GIVE_MILK;
}
}
} 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) {
if (this->unk_278 > 0) {
this->unk_278--;
void EnCow_IdleTail(EnCow* this, PlayState* play) {
if (this->animationTimer > 0) {
this->animationTimer--;
} 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_GetLastFrame(&gCowTailIdleAnim), ANIMMODE_ONCE, 1.0f);
}
if ((this->actor.xzDistToPlayer < 150.0f) &&
(ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) >= 0x61A9) && !(this->unk_276 & 2)) {
this->unk_276 |= 2;
if (this->skelAnime.animation == &gCowTailIdleAnim) {
this->unk_278 = 0;
(ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) > 25000)) {
if (!(this->cowFlags & COW_FLAG_PLAYER_NEARBY)) {
this->cowFlags |= COW_FLAG_PLAYER_NEARBY;
if (this->skelAnime.animation == &gCowTailIdleAnim) {
this->animationTimer = 0;
}
}
}
}
@ -299,10 +333,12 @@ void EnCow_Update(Actor* thisx, PlayState* play2) {
s16 targetY;
Player* player = GET_PLAYER(play);
CollisionCheck_SetOC(play, &play->colChkCtx, &this->colliders[0].base);
CollisionCheck_SetOC(play, &play->colChkCtx, &this->colliders[1].base);
CollisionCheck_SetOC(play, &play->colChkCtx, &this->colliders[COW_COLLIDER_FRONT].base);
CollisionCheck_SetOC(play, &play->colChkCtx, &this->colliders[COW_COLLIDER_REAR].base);
Actor_MoveXZGravity(thisx);
Actor_UpdateBgCheckInfo(play, thisx, 0.0f, 0.0f, 0.0f, UPDBGCHECKINFO_FLAG_2);
if (SkelAnime_Update(&this->skelAnime)) {
if (this->skelAnime.animation == &gCowBodyChewAnim) {
Actor_PlaySfx(thisx, NA_SE_EV_COW_CRY);
@ -313,7 +349,9 @@ void EnCow_Update(Actor* thisx, PlayState* play2) {
ANIMMODE_LOOP, 1.0f);
}
}
this->actionFunc(this, play);
if ((thisx->xzDistToPlayer < 150.0f) &&
(ABS(Math_Vec3f_Yaw(&thisx->world.pos, &player->actor.world.pos)) < 0xC000)) {
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) {
targetY = -0x2500;
}
} else {
targetY = 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;
s32 pad;
@ -352,27 +390,30 @@ void func_809DFE98(Actor* thisx, PlayState* play) {
ANIMMODE_LOOP, 1.0f);
}
}
this->actionFunc(this, play);
}
s32 EnCow_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) {
EnCow* this = (EnCow*)thisx;
if (limbIndex == 2) {
rot->y += this->someRot.y;
rot->x += this->someRot.x;
if (limbIndex == COW_LIMB_HEAD) {
rot->y += this->headRot.y;
rot->x += this->headRot.x;
}
if (limbIndex == 5) {
if (limbIndex == COW_LIMB_NOSE_RING) {
*dList = NULL;
}
return false;
}
void EnCow_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) {
EnCow* this = (EnCow*)thisx;
if (limbIndex == 2) {
Matrix_MultVec3f(&D_809E010C, &this->actor.focus.pos);
if (limbIndex == COW_LIMB_HEAD) {
Matrix_MultVec3f(&sHeadFocusOffset, &this->actor.focus.pos);
}
}
@ -384,7 +425,7 @@ void EnCow_Draw(Actor* thisx, PlayState* play) {
EnCow_OverrideLimbDraw, EnCow_PostLimbDraw, this);
}
void func_809E0070(Actor* thisx, PlayState* play) {
void EnCow_DrawTail(Actor* thisx, PlayState* play) {
EnCow* this = (EnCow*)thisx;
Gfx_SetupDL_37Opa(play->state.gfxCtx);

View file

@ -3,6 +3,23 @@
#include "ultra64.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;
@ -10,14 +27,14 @@ typedef void (*EnCowActionFunc)(struct EnCow*, PlayState*);
typedef struct EnCow {
/* 0x0000 */ Actor actor;
/* 0x014C */ ColliderCylinder colliders[2];
/* 0x014C */ ColliderCylinder colliders[COW_COLLIDER_MAX];
/* 0x01E4 */ SkelAnime skelAnime;
/* 0x0228 */ Vec3s jointTable[6];
/* 0x024C */ Vec3s morphTable[6];
/* 0x0270 */ Vec3s someRot;
/* 0x0276 */ u16 unk_276;
/* 0x0278 */ u16 unk_278;
/* 0x027A */ u16 unk_27A;
/* 0x0228 */ Vec3s jointTable[COW_LIMB_MAX];
/* 0x024C */ Vec3s morphTable[COW_LIMB_MAX];
/* 0x0270 */ Vec3s headRot;
/* 0x0276 */ u16 cowFlags;
/* 0x0278 */ u16 animationTimer;
/* 0x027A */ u16 breathTimer;
/* 0x027C */ EnCowActionFunc actionFunc;
} EnCow; // size = 0x0280

View file

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

View file

@ -358,7 +358,7 @@ void func_80A6A068(EnHorseLinkChild* this, PlayState* play) {
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))) {
func_80A6A4DC(this);
} else {
@ -434,7 +434,7 @@ void func_80A6A068(EnHorseLinkChild* this, PlayState* play) {
void func_80A6A4DC(EnHorseLinkChild* this) {
this->action = 5;
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_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f);
}
@ -442,8 +442,8 @@ void func_80A6A4DC(EnHorseLinkChild* this) {
void func_80A6A5A4(EnHorseLinkChild* this, PlayState* play) {
s16 yawDiff;
if (DREG(53) != 0) {
DREG(53) = 0;
if (R_EPONAS_SONG_PLAYED) {
R_EPONAS_SONG_PLAYED = false;
Audio_PlaySfxGeneral(NA_SE_EV_KID_HORSE_NEIGH, &this->actor.projectedPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
func_80A6A724(this);

View file

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

View file

@ -229,7 +229,7 @@ void EnNiw_Init(Actor* thisx, PlayState* play) {
case 0xD:
case 0xE:
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);
}
break;