diff --git a/src/overlays/actors/ovl_En_Aob_01/z_en_aob_01.c b/src/overlays/actors/ovl_En_Aob_01/z_en_aob_01.c index 0705d7f06..b06b57ebd 100644 --- a/src/overlays/actors/ovl_En_Aob_01/z_en_aob_01.c +++ b/src/overlays/actors/ovl_En_Aob_01/z_en_aob_01.c @@ -598,7 +598,7 @@ s32 func_809C2504(EnAob01* this, GlobalContext* globalCtx) { Actor* npc = globalCtx->actorCtx.actorLists[ACTORCAT_NPC].first; while (npc != NULL) { - if ((npc->id == ACTOR_EN_RACEDOG) && (func_800F2178(this->unk_430) == ((EnRacedog*)npc)->unk_1E8)) { + if ((npc->id == ACTOR_EN_RACEDOG) && (func_800F2178(this->unk_430) == ((EnRacedog*)npc)->currentPoint)) { ActorCutscene_Stop(this->unk_430); this->unk_3F4 = npc; this->unk_430 = ActorCutscene_GetAdditionalCutscene(this->unk_430); @@ -614,7 +614,7 @@ s32 func_809C2594(EnAob01* this, GlobalContext* globalCtx) { Actor* npc = globalCtx->actorCtx.actorLists[ACTORCAT_NPC].first; while (npc != NULL) { - if ((npc->id == ACTOR_EN_RACEDOG) && (((EnRacedog*)npc)->unk_290 == ((EnRacedog*)npc)->unk_292)) { + if ((npc->id == ACTOR_EN_RACEDOG) && (((EnRacedog*)npc)->index == ((EnRacedog*)npc)->selectedDogIndex)) { this->unk_3F4 = npc; return true; } @@ -629,7 +629,7 @@ s32 func_809C25E4(EnAob01* this, GlobalContext* globalCtx) { s16 count = 0; while (npc != NULL) { - if ((npc->id == ACTOR_EN_RACEDOG) && (((EnRacedog*)npc)->unk_29C == 3)) { + if ((npc->id == ACTOR_EN_RACEDOG) && (((EnRacedog*)npc)->raceStatus == RACEDOG_RACE_STATUS_FINISHED)) { count++; } npc = npc->next; @@ -853,12 +853,12 @@ s32 func_809C2EC4(EnAob01* this, GlobalContext* globalCtx) { while (dog != NULL) { if (dog->id == ACTOR_EN_DG) { - this->unk_432 = ((EnDg*)dog)->unk_288; + this->unk_432 = ((EnDg*)dog)->selectedDogIndex; if (this->unk_432 == -1) { return false; } - if (this->unk_432 == ENDG_GET_3E0(dog)) { + if (this->unk_432 == ENDG_GET_INDEX(dog)) { return true; } } diff --git a/src/overlays/actors/ovl_En_Dg/z_en_dg.c b/src/overlays/actors/ovl_En_Dg/z_en_dg.c index dce730c48..23bd7e183 100644 --- a/src/overlays/actors/ovl_En_Dg/z_en_dg.c +++ b/src/overlays/actors/ovl_En_Dg/z_en_dg.c @@ -57,19 +57,32 @@ typedef struct { static D_8098C2A4_s D_8098C2A4 = { 0x0063, 0x0000 }; +/** + * Stores the state for the dogs milling about at the Doggy Racetrack. + */ typedef struct { - s16 unk_00; - s16 unk_02; - s16 unk_04; -} D_8098C2A8_s; + s16 color; // The dog's color, which is used as an index into sBaseSpeeds + s16 index; // The dog's index within sDogInfo + s16 textId; // The ID of the text to display when the dog is picked up +} RacetrackDogInfo; -static D_8098C2A8_s D_8098C2A8[] = { - { 3, 0, 0x3539 }, { 1, 1, 0x353A }, { 5, 2, 0x353B }, { 2, 3, 0x353C }, { 4, 4, 0x3538 }, - { 2, 5, 0x353E }, { 3, 6, 0x353F }, { 1, 7, 0x3540 }, { 1, 8, 0x3541 }, { 6, 9, 0x3542 }, - { 2, 10, 0x3543 }, { 3, 11, 0x3544 }, { 1, 12, 0x3545 }, { 4, 13, 0x3546 }, +/** + * A table of RacetrackDogInfo for every dog at the Doggy Racetrack. Note that the textId values + * in this table are updated by functions within this actor. + */ +static RacetrackDogInfo sRacetrackDogInfo[] = { + { DOG_COLOR_BEIGE, 0, 0x3539 }, { DOG_COLOR_WHITE, 1, 0x353A }, { DOG_COLOR_BLUE, 2, 0x353B }, + { DOG_COLOR_GRAY, 3, 0x353C }, { DOG_COLOR_BROWN, 4, 0x3538 }, { DOG_COLOR_GRAY, 5, 0x353E }, + { DOG_COLOR_BEIGE, 6, 0x353F }, { DOG_COLOR_WHITE, 7, 0x3540 }, { DOG_COLOR_WHITE, 8, 0x3541 }, + { DOG_COLOR_GOLD, 9, 0x3542 }, { DOG_COLOR_GRAY, 10, 0x3543 }, { DOG_COLOR_BEIGE, 11, 0x3544 }, + { DOG_COLOR_WHITE, 12, 0x3545 }, { DOG_COLOR_BROWN, 13, 0x3546 }, }; -static D_8098C2A8_s D_8098C2FC = { 0, -1, 0x353E }; +/** + * Stores the RacetrackDogInfo for the dog that is selected by the player. These values are just + * placeholders, and the actual value gets grabbed from sRacetrackDogInfo in func_80989E18. + */ +static RacetrackDogInfo sSelectedRacetrackDogInfo = { DOG_COLOR_DEFAULT, -1, 0x353E }; static ColliderCylinderInit sCylinderInit = { { @@ -252,29 +265,29 @@ void func_80989674(EnDg* this, GlobalContext* globalCtx) { s16 phi_a1; f32 sp30; - if (this->unk_1DC != NULL) { - phi_a1 = func_809895B4(this->unk_1DC, this->unk_1E0, &this->actor.world.pos, &sp30); + if (this->path != NULL) { + phi_a1 = func_809895B4(this->path, this->unk_1E0, &this->actor.world.pos, &sp30); if (this->actor.bgCheckFlags & 8) { phi_a1 = this->actor.wallYaw; } Math_SmoothStepToS(&this->actor.world.rot.y, phi_a1, 4, 0x3E8, 1); this->actor.shape.rot.y = this->actor.world.rot.y; - if (func_80989418(this, this->unk_1DC, this->unk_1E0)) { - if (this->unk_1E0 >= (this->unk_1DC->count - 1)) { + if (func_80989418(this, this->path, this->unk_1E0)) { + if (this->unk_1E0 >= (this->path->count - 1)) { this->unk_1E0 = 0; } else { this->unk_1E0++; } } - if ((this->unk_286 == 21) || ((this->unk_286 == 20) && (globalCtx->sceneNum == SCENE_OMOYA))) { + if ((this->index == 21) || ((this->index == 20) && (globalCtx->sceneNum == SCENE_OMOYA))) { Math_ApproachF(&this->actor.speedXZ, 1.0f, 0.2f, 1.0f); - } else if (this->unk_286 == 20) { + } else if (this->index == 20) { Math_ApproachF(&this->actor.speedXZ, 3.5f, 0.2f, 1.0f); } else if (globalCtx->sceneNum == SCENE_CLOCKTOWER) { Math_ApproachF(&this->actor.speedXZ, 3.5f, 0.2f, 1.0f); - } else if (D_8098C2A8[this->unk_286].unk_04 & 0x11) { + } else if (sRacetrackDogInfo[this->index].textId & 0x11) { Math_ApproachF(&this->actor.speedXZ, 1.0f, 0.2f, 1.0f); } else { Math_ApproachF(&this->actor.speedXZ, 3.5f, 0.2f, 1.0f); @@ -289,7 +302,7 @@ void func_80989864(EnDg* this, GlobalContext* globalCtx) { s16 mod = (this->actor.speedXZ > 6.0f) ? 2 : 3; Vec3f sp38; - if (((this->unk_286 + frame) % mod) == 0) { + if (((this->index + frame) % mod) == 0) { sp38.x = randPlusMinusPoint5Scaled(15.0f) + this->actor.world.pos.x; sp38.y = this->actor.world.pos.y; sp38.z = randPlusMinusPoint5Scaled(15.0f) + this->actor.world.pos.z; @@ -329,13 +342,13 @@ void func_80989A9C(EnDg* this, f32 arg1) { void func_80989ADC(EnDg* this, GlobalContext* globalCtx) { if (!(this->actor.bgCheckFlags & 0x20)) { - if ((this->unk_286 == 21) || ((this->unk_286 == 20) && (globalCtx->sceneNum == SCENE_OMOYA))) { + if ((this->index == 21) || ((this->index == 20) && (globalCtx->sceneNum == SCENE_OMOYA))) { func_80989140(&this->skelAnime, sAnimations, 1); - } else if (this->unk_286 == 20) { + } else if (this->index == 20) { func_80989140(&this->skelAnime, sAnimations, 2); } else if (globalCtx->sceneNum == SCENE_CLOCKTOWER) { func_80989140(&this->skelAnime, sAnimations, 2); - } else if (D_8098C2A8[this->unk_286].unk_04 & 0x11) { + } else if (sRacetrackDogInfo[this->index].textId & 0x11) { func_80989140(&this->skelAnime, sAnimations, 1); } else { func_80989140(&this->skelAnime, sAnimations, 2); @@ -345,38 +358,37 @@ void func_80989ADC(EnDg* this, GlobalContext* globalCtx) { } void func_80989BF8(EnDg* this) { - if (this->unk_286 < 14) { - if (this->unk_286 % 2) { - D_8098C2A8[this->unk_286].unk_04 = - 0x3538 + - ((gSaveContext.save.weekEventReg[42 + (this->unk_286 / 2)] & (0x10 | 0x20 | 0x40 | 0x80)) >> 4); + if (this->index < 14) { + if (this->index % 2) { + sRacetrackDogInfo[this->index].textId = + 0x3538 + ((gSaveContext.save.weekEventReg[42 + (this->index / 2)] & (0x10 | 0x20 | 0x40 | 0x80)) >> 4); } else { - D_8098C2A8[this->unk_286].unk_04 = - 0x3538 + (gSaveContext.save.weekEventReg[42 + (this->unk_286 / 2)] & (1 | 2 | 4 | 8)); + sRacetrackDogInfo[this->index].textId = + 0x3538 + (gSaveContext.save.weekEventReg[42 + (this->index / 2)] & (1 | 2 | 4 | 8)); } } else { Actor_MarkForDeath(&this->actor); } - if ((D_8098C2A8[this->unk_286].unk_04 >= 0x3547) || (D_8098C2A8[this->unk_286].unk_04 < 0x3538)) { - D_8098C2A8[this->unk_286].unk_04 = 0x353E; + if ((sRacetrackDogInfo[this->index].textId >= 0x3547) || (sRacetrackDogInfo[this->index].textId < 0x3538)) { + sRacetrackDogInfo[this->index].textId = 0x353E; } - if (D_8098C2A8[this->unk_286].unk_04 == 0x353D) { - D_8098C2A8[this->unk_286].unk_04 = 0x3538; + if (sRacetrackDogInfo[this->index].textId == 0x353D) { + sRacetrackDogInfo[this->index].textId = 0x3538; } } void func_80989D38(EnDg* this, GlobalContext* globalCtx) { - if (this->unk_286 == 21) { + if (this->index == 21) { if (CURRENT_DAY == 1) { Message_StartTextbox(globalCtx, 0x91C, NULL); } else { Message_StartTextbox(globalCtx, 0x91E, NULL); } - } else if ((this->unk_286 >= 0) && (this->unk_286 < 14)) { - Message_StartTextbox(globalCtx, D_8098C2A8[this->unk_286].unk_04, NULL); - } else if (this->unk_286 == 20) { + } else if ((this->index >= 0) && (this->index < 14)) { + Message_StartTextbox(globalCtx, sRacetrackDogInfo[this->index].textId, NULL); + } else if (this->index == 20) { Message_StartTextbox(globalCtx, 0x353D, NULL); } else { Message_StartTextbox(globalCtx, 0x627, NULL); @@ -384,7 +396,7 @@ void func_80989D38(EnDg* this, GlobalContext* globalCtx) { } void func_80989E18(EnDg* this, GlobalContext* globalCtx) { - D_8098C2A8_s* temp; + RacetrackDogInfo* temp; if ((D_8098C2A0 != 0) && !(this->unk_280 & 1)) { this->actor.flags |= ACTOR_FLAG_8000000; @@ -397,7 +409,7 @@ void func_80989E18(EnDg* this, GlobalContext* globalCtx) { if (Actor_HasParent(&this->actor, globalCtx)) { Actor_PlaySfxAtPos(&this->actor, NA_SE_EV_SMALL_DOG_BARK); this->unk_290 = 1; - D_8098C2FC = D_8098C2A8[this->unk_286]; + sSelectedRacetrackDogInfo = sRacetrackDogInfo[this->index]; if (D_8098C2A0 == 0) { this->actor.flags |= ACTOR_FLAG_8000000; D_8098C2A0 = 1; @@ -427,13 +439,13 @@ s32 func_80989FC8(GlobalContext* globalCtx) { while (enemy != NULL) { if (enemy->actor.id == ACTOR_EN_DG) { if (enemy->actor.isTargeted) { - D_8098C2A4.unk_00 = enemy->unk_286; + D_8098C2A4.unk_00 = enemy->index; return true; } dist = enemy->actor.xzDistToPlayer; if (dist < minDist) { - D_8098C2A4.unk_00 = enemy->unk_286; + D_8098C2A4.unk_00 = enemy->index; minDist = dist; } } @@ -455,7 +467,7 @@ void func_8098A064(EnDg* this, GlobalContext* globalCtx) { func_80989FC8(globalCtx); } - if (this->unk_286 == D_8098C2A4.unk_00) { + if (this->index == D_8098C2A4.unk_00) { if (!(this->unk_280 & 0x20)) { this->unk_280 |= 0x20; func_80989140(&this->skelAnime, sAnimations, 1); @@ -467,7 +479,7 @@ void func_8098A064(EnDg* this, GlobalContext* globalCtx) { } } } - } else if (this->unk_286 == D_8098C2A4.unk_00) { + } else if (this->index == D_8098C2A4.unk_00) { this->unk_280 &= ~0x20; D_8098C2A4.unk_00 = 99; func_80989ADC(this, globalCtx); @@ -1053,8 +1065,8 @@ void func_8098BA64(EnDg* this, GlobalContext* globalCtx) { D_8098C2A0 = 0; this->unk_280 &= ~1; } - this->unk_288 = -1; - D_8098C2FC.unk_02 = this->unk_288; + this->selectedDogIndex = -1; + sSelectedRacetrackDogInfo.index = this->selectedDogIndex; this->unk_28A = 100; Actor_MoveWithGravity(&this->actor); this->unk_280 |= 0x10; @@ -1114,7 +1126,7 @@ void EnDg_Init(Actor* thisx, GlobalContext* globalCtx) { Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); CollisionCheck_SetInfo2(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); Actor_ProcessInitChain(&this->actor, sInitChain); - this->unk_1DC = SubS_GetPathByIndex(globalCtx, ENDG_GET_FC00(&this->actor), 0x3F); + this->path = SubS_GetPathByIndex(globalCtx, ENDG_GET_PATH(&this->actor), 0x3F); Actor_SetScale(&this->actor, 0.0075f); this->actor.targetMode = 1; this->actor.gravity = -3.0f; @@ -1122,7 +1134,7 @@ void EnDg_Init(Actor* thisx, GlobalContext* globalCtx) { this->unk_280 = 0; this->unk_28E = 20; this->unk_284 = 10; - this->unk_286 = ENDG_GET_3E0(&this->actor); + this->index = ENDG_GET_INDEX(&this->actor); this->unk_28C = 0; this->unk_290 = 0; if (globalCtx->sceneNum == SCENE_F01_B) { @@ -1144,7 +1156,7 @@ void EnDg_Update(Actor* thisx, GlobalContext* globalCtx) { s32 pad; Vec3f sp28 = { 0.0f, 0.0f, 0.0f }; - this->unk_288 = D_8098C2FC.unk_02; + this->selectedDogIndex = sSelectedRacetrackDogInfo.index; if (!(player->stateFlags1 & 0x20) || (globalCtx->sceneNum != SCENE_CLOCKTOWER)) { if (func_8098A1B4(this, globalCtx)) { func_8098A234(this, globalCtx); @@ -1194,25 +1206,31 @@ void EnDg_Draw(Actor* thisx, GlobalContext* globalCtx) { gDPPipeSync(POLY_OPA_DISP++); - switch (D_8098C2A8[this->unk_286].unk_00) { - case 3: + switch (sRacetrackDogInfo[this->index].color) { + case DOG_COLOR_BEIGE: gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 200, 0); break; - case 1: + + case DOG_COLOR_WHITE: gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 0); break; - case 5: + + case DOG_COLOR_BLUE: gDPSetEnvColor(POLY_OPA_DISP++, 79, 79, 143, 0); break; - case 6: + + case DOG_COLOR_GOLD: gDPSetEnvColor(POLY_OPA_DISP++, 255, 207, 47, 0); break; - case 4: + + case DOG_COLOR_BROWN: gDPSetEnvColor(POLY_OPA_DISP++, 143, 79, 47, 0); break; - case 2: + + case DOG_COLOR_GRAY: gDPSetEnvColor(POLY_OPA_DISP++, 143, 143, 143, 0); break; + default: gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 200, 0); break; diff --git a/src/overlays/actors/ovl_En_Dg/z_en_dg.h b/src/overlays/actors/ovl_En_Dg/z_en_dg.h index 09732ba81..ca85d4a4d 100644 --- a/src/overlays/actors/ovl_En_Dg/z_en_dg.h +++ b/src/overlays/actors/ovl_En_Dg/z_en_dg.h @@ -4,12 +4,22 @@ #include "global.h" #include "objects/object_dog/object_dog.h" +typedef enum { + /* 0 */ DOG_COLOR_DEFAULT, // ends up just being treated as beige + /* 1 */ DOG_COLOR_WHITE, + /* 2 */ DOG_COLOR_GRAY, + /* 3 */ DOG_COLOR_BEIGE, + /* 4 */ DOG_COLOR_BROWN, + /* 5 */ DOG_COLOR_BLUE, + /* 6 */ DOG_COLOR_GOLD, +} DogColors; + struct EnDg; typedef void (*EnDgActionFunc)(struct EnDg*, GlobalContext*); -#define ENDG_GET_FC00(thisx) (((thisx)->params & 0xFC00) >> 0xA) -#define ENDG_GET_3E0(thisx) (((thisx)->params & 0x3E0) >> 5) +#define ENDG_GET_INDEX(thisx) (((thisx)->params & 0x3E0) >> 5) +#define ENDG_GET_PATH(thisx) (((thisx)->params & 0xFC00) >> 0xA) typedef struct EnDg { /* 0x000 */ Actor actor; @@ -17,15 +27,15 @@ typedef struct EnDg { /* 0x148 */ UNK_TYPE1 unk_148[0x4]; /* 0x14C */ SkelAnime skelAnime; /* 0x190 */ ColliderCylinder collider; - /* 0x1DC */ Path* unk_1DC; + /* 0x1DC */ Path* path; /* 0x1E0 */ s32 unk_1E0; /* 0x1E4 */ Vec3s jointTable[DOG_LIMB_MAX]; /* 0x232 */ Vec3s morphTable[DOG_LIMB_MAX]; /* 0x280 */ u16 unk_280; /* 0x282 */ s16 unk_282; /* 0x284 */ s16 unk_284; - /* 0x286 */ s16 unk_286; - /* 0x288 */ s16 unk_288; + /* 0x286 */ s16 index; + /* 0x288 */ s16 selectedDogIndex; /* 0x28A */ s16 unk_28A; /* 0x28C */ s16 unk_28C; /* 0x28E */ s16 unk_28E; diff --git a/src/overlays/actors/ovl_En_Racedog/z_en_racedog.c b/src/overlays/actors/ovl_En_Racedog/z_en_racedog.c index 0166ed423..e6fb2535a 100644 --- a/src/overlays/actors/ovl_En_Racedog/z_en_racedog.c +++ b/src/overlays/actors/ovl_En_Racedog/z_en_racedog.c @@ -9,6 +9,7 @@ */ #include "z_en_racedog.h" +#include "overlays/actors/ovl_En_Dg/z_en_dg.h" #define FLAGS (ACTOR_FLAG_10 | ACTOR_FLAG_80000000) @@ -19,26 +20,63 @@ void EnRacedog_Destroy(Actor* thisx, GlobalContext* globalCtx); void EnRacedog_Update(Actor* thisx, GlobalContext* globalCtx); void EnRacedog_Draw(Actor* thisx, GlobalContext* globalCtx); -void func_80B24C14(EnRacedog* this, GlobalContext* globalCtx); -void func_80B24CB4(EnRacedog* this, GlobalContext* globalCtx); -void func_80B24E14(EnRacedog* this); -void func_80B24F08(EnRacedog* this); -void func_80B251EC(EnRacedog* this); -void func_80B252F8(EnRacedog* this); -void func_80B2538C(EnRacedog* this); -void func_80B25448(EnRacedog* this); -s32 func_80B25490(EnRacedog* this, Vec2f* arg1); -void func_80B255AC(EnRacedog* this, GlobalContext* globalCtx); -void func_80B256BC(EnRacedog* this); +void EnRacedog_RaceStart(EnRacedog* this, GlobalContext* globalCtx); +void EnRacedog_Race(EnRacedog* this, GlobalContext* globalCtx); +void EnRacedog_UpdateTextId(EnRacedog* this); +void EnRacedog_UpdateSpeed(EnRacedog* this); +void EnRacedog_CalculateFinalStretchTargetSpeed(EnRacedog* this); +void EnRacedog_UpdateRaceVariables(EnRacedog* this); +void EnRacedog_CheckForFinish(EnRacedog* this); +void EnRacedog_UpdateRunAnimationPlaySpeed(EnRacedog* this); +s32 EnRacedog_IsOverFinishLine(EnRacedog* this, Vec2f* arg1); +void EnRacedog_SpawnFloorDustRing(EnRacedog* this, GlobalContext* globalCtx); +void EnRacedog_PlayWalkSfx(EnRacedog* this); +/** + * Dogs can be in three conditions, which is indicated by the message it says when + * you pick it up prior to entering the race. + * If it starts with "Ruff!", it's in good condition. + * If it starts with "Rrr-Ruff!", it's in normal condition. + * If it starts with "Hoo-whine", it's in bad condition. + * These text boxes are grouped up like so: + * - 0x3538 - 0x353D: Good condition + * - 0x353E - 0x3541: Normal condition + * - 0x3542 - 0x3546: Bad condition + */ +#define DOG_IS_IN_GOOD_CONDITION(this) (sDogInfo[this->index].textId < 0x353E) +#define DOG_IS_IN_BAD_CONDITION(this) (sDogInfo[this->index].textId >= 0x3542) + +typedef enum { + /* 0 */ RACEDOG_ANIMATION_IDLE, + /* 1 */ RACEDOG_ANIMATION_WALK_1, + /* 2 */ RACEDOG_ANIMATION_RUN, + /* 3 */ RACEDOG_ANIMATION_BARK, + /* 4 */ RACEDOG_ANIMATION_SIT_DOWN_1, + /* 5 */ RACEDOG_ANIMATION_SIT_DOWN_2, + /* 6 */ RACEDOG_ANIMATION_LYING_DOWN_START_1, + /* 7 */ RACEDOG_ANIMATION_LYING_DOWN_LOOP, + /* 8 */ RACEDOG_ANIMATION_LYING_DOWN_START_2, + /* 9 */ RACEDOG_ANIMATION_LYING_DOWN_START_3, + /* 10 */ RACEDOG_ANIMATION_LYING_DOWN_START_4, + /* 11 */ RACEDOG_ANIMATION_WALK_2, + /* 12 */ RACEDOG_ANIMATION_JUMP, + /* 13 */ RACEDOG_ANIMATION_LONG_JUMP, + /* 14 */ RACEDOG_ANIMATION_JUMP_2, + /* 15 */ RACEDOG_ANIMATION_WALK_3, + /* 16 */ RACEDOG_ANIMATION_MAX +} RacedogAnimationIndex; + +/** + * Stores various information for each dog in the race, mostly related to speed. + */ typedef struct { - f32 unk_00; - f32 unk_04; - s16 unk_08; - s16 unk_0A; - s16 unk_0C; - s16 unk_0E; -} UnkRacedogStruct; + f32 sprintSpeedMultiplier; // Target speed is multiplied by this when the dog is in the last 1/4 of the race + f32 goodConditionSpeedMultiplier; // Target speed is multiplied by this if the dog is in good condition + s16 color; // The dog's color, which is used as an index into sBaseSpeeds + s16 index; // The dog's index within sDogInfo + s16 pointToUseSecondBaseSpeed; // When the dog is at or after this point, the second value in sBaseSpeeds is used + s16 textId; // Used to determine the dog's condition +} RaceDogInfo; const ActorInit En_Racedog_InitVars = { ACTOR_EN_RACEDOG, @@ -52,31 +90,65 @@ const ActorInit En_Racedog_InitVars = { (ActorFunc)EnRacedog_Draw, }; -static s16 D_80B25D40 = 0; +static s16 sNumberOfDogsFinished = 0; -static s16 D_80B25D44 = -1; +/** + * The furthest point along the race track path that any dog has reached. + */ +static s16 sFurthestPoint = -1; -static s16 D_80B25D48 = 0; +/** + * Starts counting up as soon as the first-place dog gets 3/4ths of the way through the race track, + * and stops counting up as soon as the first-place dog finishes. Used to scale up the sprint + * multiplier in EnRacedog_CalculateFinalStretchTargetSpeed. + */ +static s16 sSprintTimer = 0; -static s16 D_80B25D4C = -1; +/** + * The index of the dog currently in first place. It's determined by checking, for each dog on the track, + * if its current point along the race track path is greater than or equal to sFurthestPoint. + */ +static s16 sFirstPlaceIndex = -1; -static f32 D_80B25D50[][2] = { +/** + * The base speeds for each dog indexed by its color. The two values are used during different parts of the race. + * - At the very start of the race, all dogs will use the first value for their color, but only for a single point + * along the race track's path. After that, they will have a base speed of 5.0, regardless of what's in the table + * for their color. The sole exception to this is the blue dog, who will use its first value for the entire + * first 1/4th of the race. + * - For the last 3/4ths of the race, all dogs will check to see if their current point along the race track path is + * greater than or equal to the pointToUseSecondBaseSpeed for their RaceDogInfo. If it is, then they will use the + * second value for their color as their base speed; otherwise, they will use 5.0. + */ +static f32 sBaseSpeeds[][2] = { { 0.0f, 0.0f }, { 5.0f, 5.5f }, { 5.0f, 5.0f }, { 5.5f, 5.0f }, { 4.5f, 5.5f }, { 6.0f, 4.0f }, { 4.0f, 6.0f }, }; -static UnkRacedogStruct D_80B25D88[] = { - { -1.0f, 1.20000004768f, 3, 0, 9, 0x3539 }, { -1.0f, 1.20000004768f, 1, 1, 9, 0x353A }, - { -1.0f, 1.20000004768f, 5, 2, 10, 0x353B }, { -1.0f, 1.20000004768f, 2, 3, 9, 0x353C }, - { -1.0f, 1.20000004768f, 4, 4, 8, 0x353D }, { -1.0f, 1.20000004768f, 2, 5, 9, 0x353E }, - { -1.0f, 1.20000004768f, 3, 6, 9, 0x353F }, { -1.0f, 1.20000004768f, 1, 7, 9, 0x3540 }, - { -1.0f, 1.20000004768f, 1, 8, 9, 0x3541 }, { -1.0f, 1.20000004768f, 6, 9, 8, 0x3542 }, - { -1.0f, 1.20000004768f, 2, 10, 9, 0x3543 }, { -1.0f, 1.20000004768f, 3, 11, 9, 0x3544 }, - { -1.0f, 1.20000004768f, 1, 12, 9, 0x3545 }, { -1.0f, 1.20000004768f, 4, 13, 8, 0x3546 }, +/** + * A table of RaceDogInfo for every dog in the race. Note that the sprintSpeedMultiplier and + * textId values in this table are updated by functions within this actor. + */ +static RaceDogInfo sDogInfo[] = { + { -1.0f, 1.2f, DOG_COLOR_BEIGE, 0, 9, 0x3539 }, { -1.0f, 1.2f, DOG_COLOR_WHITE, 1, 9, 0x353A }, + { -1.0f, 1.2f, DOG_COLOR_BLUE, 2, 10, 0x353B }, { -1.0f, 1.2f, DOG_COLOR_GRAY, 3, 9, 0x353C }, + { -1.0f, 1.2f, DOG_COLOR_BROWN, 4, 8, 0x353D }, { -1.0f, 1.2f, DOG_COLOR_GRAY, 5, 9, 0x353E }, + { -1.0f, 1.2f, DOG_COLOR_BEIGE, 6, 9, 0x353F }, { -1.0f, 1.2f, DOG_COLOR_WHITE, 7, 9, 0x3540 }, + { -1.0f, 1.2f, DOG_COLOR_WHITE, 8, 9, 0x3541 }, { -1.0f, 1.2f, DOG_COLOR_GOLD, 9, 8, 0x3542 }, + { -1.0f, 1.2f, DOG_COLOR_GRAY, 10, 9, 0x3543 }, { -1.0f, 1.2f, DOG_COLOR_BEIGE, 11, 9, 0x3544 }, + { -1.0f, 1.2f, DOG_COLOR_WHITE, 12, 9, 0x3545 }, { -1.0f, 1.2f, DOG_COLOR_BROWN, 13, 8, 0x3546 }, }; -static UnkRacedogStruct D_80B25E68 = { -1.0f, 1.0, 0, -1, 0, 0x353E }; +/** + * Stores the RacedogInfo for the dog that is selected by the player. These values are just + * placeholders, and the actual value gets grabbed from sDogInfo in EnRacedog_Init. + */ +static RaceDogInfo sSelectedDogInfo = { -1.0f, 1.0, DOG_COLOR_DEFAULT, -1, 0, 0x353E }; -static Vec2f D_80B25E78[] = { +/** + * The XZ-coordinates used to determine if the dog is inside the finish line. + * They're a little bit bigger than the in-game visual. + */ +static Vec2f sFinishLineCoordinates[] = { { -3914.0f, 1283.0f }, { -3747.0f, 1104.0f }, { -3717.0f, 1169.0f }, @@ -140,7 +212,7 @@ static DamageTable sDamageTable = { /* Powder Keg */ DMG_ENTRY(0, 0x0), }; -static AnimationInfoS D_80B25EF0[] = { +static AnimationInfoS sAnimations[] = { { &gDogWalkAnim, 1.0f, 0, -1, ANIMMODE_LOOP, 0 }, { &gDogWalkAnim, 1.0f, 0, -1, ANIMMODE_LOOP, -6 }, { &gDogRunAnim, 1.0f, 0, -1, ANIMMODE_LOOP, 0 }, { &gDogBarkAnim, 1.0f, 0, -1, ANIMMODE_LOOP, -6 }, { &gDogSitAnim, 1.0f, 0, -1, ANIMMODE_ONCE, -6 }, { &gDogSitAnim, 1.0f, 0, -1, ANIMMODE_LOOP_PARTIAL, -6 }, @@ -155,21 +227,21 @@ static InitChainEntry sInitChain[] = { ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_STOP), }; -void func_80B24630(SkelAnime* skelAnime, AnimationInfoS arg1[], s32 arg2) { +void EnRacedog_ChangeAnimation(SkelAnime* skelAnime, AnimationInfoS* animationInfo, s32 index) { f32 frameCount; - arg1 += arg2; - if (arg1->frameCount < 0) { - frameCount = Animation_GetLastFrame(arg1->animation); + animationInfo += index; + if (animationInfo->frameCount < 0) { + frameCount = Animation_GetLastFrame(animationInfo->animation); } else { - frameCount = arg1->frameCount; + frameCount = animationInfo->frameCount; } - Animation_Change(skelAnime, arg1->animation, arg1->playSpeed + (BREG(88) * 0.1f), arg1->startFrame, frameCount, - arg1->mode, arg1->morphFrames); + Animation_Change(skelAnime, animationInfo->animation, animationInfo->playSpeed + (BREG(88) * 0.1f), + animationInfo->startFrame, frameCount, animationInfo->mode, animationInfo->morphFrames); } -void func_80B246F4(EnRacedog* this, GlobalContext* globalCtx) { +void EnRacedog_UpdateCollision(EnRacedog* this, GlobalContext* globalCtx) { this->collider.dim.pos.x = this->actor.world.pos.x; this->collider.dim.pos.y = this->actor.world.pos.y; this->collider.dim.pos.z = this->actor.world.pos.z; @@ -177,40 +249,44 @@ void func_80B246F4(EnRacedog* this, GlobalContext* globalCtx) { Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 26.0f, 10.0f, 0.0f, 5); } -s16 func_80B2478C(Path* path, s32 arg1, Vec3f* pos, f32* arg3) { +/** + * Returns the Y-rotation the dog should have to move to the next point along the race track path. + * There is a small degree of randomness incorporated into this angle. + */ +s16 EnRacedog_GetYRotation(Path* path, s32 pointIndex, Vec3f* pos, f32* distSQ) { Vec3s* point; - f32 phi_f14; - f32 sp1C; + f32 xDiffRand; + f32 zDiffRand; f32 xDiff; f32 zDiff; if (path != NULL) { point = Lib_SegmentedToVirtual(path->points); - point = &point[arg1]; - phi_f14 = (randPlusMinusPoint5Scaled(100.0f) + point->x) - pos->x; - sp1C = (randPlusMinusPoint5Scaled(100.0f) + point->z) - pos->z; + point = &point[pointIndex]; + xDiffRand = (randPlusMinusPoint5Scaled(100.0f) + point->x) - pos->x; + zDiffRand = (randPlusMinusPoint5Scaled(100.0f) + point->z) - pos->z; xDiff = point->x - pos->x; zDiff = point->z - pos->z; } else { - phi_f14 = 0.0f; - sp1C = 0.0f; + xDiffRand = 0.0f; + zDiffRand = 0.0f; xDiff = 0.0f; zDiff = 0.0f; } - *arg3 = SQ(xDiff) + SQ(zDiff); - return RADF_TO_BINANG(Math_Acot2F(sp1C, phi_f14)); + *distSQ = SQ(xDiff) + SQ(zDiff); + return RADF_TO_BINANG(Math_Acot2F(zDiffRand, xDiffRand)); } -void func_80B248B8(EnRacedog* this, Vec3f* arg1) { - f32 sp20; - f32 sp1C; +void EnRacedog_GetFloorRot(EnRacedog* this, Vec3f* floorRot) { + f32 ny; + f32 nz; if (this->actor.floorPoly != NULL) { - sp20 = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.y); - sp1C = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.z); + ny = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.y); + nz = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.z); - arg1->x = -Math_Acot2F(1.0f, -sp1C * sp20); + floorRot->x = -Math_Acot2F(1.0f, -nz * ny); } } @@ -225,43 +301,51 @@ void EnRacedog_Init(Actor* thisx, GlobalContext* globalCtx) { CollisionCheck_SetInfo2(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); Actor_ProcessInitChain(&this->actor, sInitChain); - this->unk_1E0 = SubS_GetPathByIndex(globalCtx, ENRACEDOG_GET_PATH(&this->actor), 0x3F); + this->path = SubS_GetPathByIndex(globalCtx, ENRACEDOG_GET_PATH(&this->actor), 0x3F); Actor_SetScale(&this->actor, 0.0075f); this->actor.gravity = -3.0f; - if (ENRACEDOG_GET_3E0(&this->actor) < 14) { - this->unk_290 = ENRACEDOG_GET_3E0(&this->actor); + if (ENRACEDOG_GET_INDEX(&this->actor) < 14) { + this->index = ENRACEDOG_GET_INDEX(&this->actor); } else { Actor_MarkForDeath(&this->actor); } - this->unk_2BC = 0xFF; - this->unk_2C0 = 0x32; - this->unk_288 = 0xC; - this->unk_2A0.x = 0.0f; - this->unk_2A0.y = 0.0f; - this->unk_2A0.z = 0.0f; - this->unk_2C4 = 1.0f; + this->selectionArrowGreenPrimColor = 255; + this->selectionArrowGreenEnvColor = 50; + this->selectionArrowTimer = 12; + this->prevRot.x = 0.0f; + this->prevRot.y = 0.0f; + this->prevRot.z = 0.0f; + this->selectionArrowScale = 1.0f; - if ((D_80B25D88[this->unk_290].unk_0E >= 0x353F) && (this->unk_290 == (s16)Rand_ZeroFloat(20.0f))) { - this->unk_28C = 5; + // The first part of this check is a bit strange. If they intended to check for dogs that were + // in good condition, they should've stopped at 0x353D instead of 0x353E. Additionally, this + // runs before EnRacedog_UpdateTextId is called to set the text IDs to their "correct" values, + // meaning that the IDs are just whatever their default values in sDogInfo are. As a result, + // the blue dog, one beige dog, one white dog, one brown dog, and two gray dogs never bother + // to do the random 1/20 check here, regardless of anything else. + if ((sDogInfo[this->index].textId > 0x353E) && (this->index == (s16)Rand_ZeroFloat(20.0f))) { + this->extraTimeBeforeRaceStart = 5; } else { - this->unk_28C = 0; + this->extraTimeBeforeRaceStart = 0; } - this->unk_28A = 60; - this->unk_28A += this->unk_28C; - this->unk_298 = D_80B25D50[D_80B25D88[this->unk_290].unk_08][0]; - this->unk_29C = 0; - this->unk_2B8 = -1; + this->raceStartTimer = 60; + this->raceStartTimer += this->extraTimeBeforeRaceStart; + this->targetSpeed = sBaseSpeeds[sDogInfo[this->index].color][0]; + this->raceStatus = RACEDOG_RACE_STATUS_BEFORE_POINT_9; + this->pointForCurrentTargetSpeed = -1; - func_80B24E14(this); + EnRacedog_UpdateTextId(this); this->actor.flags |= ACTOR_FLAG_10; this->actor.flags |= ACTOR_FLAG_20; - D_80B25E68 = D_80B25D88[(s16)((gSaveContext.eventInf[0] & 0xF8) >> 3)]; - this->unk_292 = D_80B25E68.unk_0A; - func_80B24630(&this->skelAnime, D_80B25EF0, 0); - D_80B25EF0->playSpeed = Rand_ZeroFloat(0.5f) + 1.0f; - this->actionFunc = func_80B24C14; + + sSelectedDogInfo = sDogInfo[(s16)((gSaveContext.eventInf[0] & 0xF8) >> 3)]; + this->selectedDogIndex = sSelectedDogInfo.index; + + EnRacedog_ChangeAnimation(&this->skelAnime, sAnimations, RACEDOG_ANIMATION_IDLE); + sAnimations[RACEDOG_ANIMATION_IDLE].playSpeed = Rand_ZeroFloat(0.5f) + 1.0f; + this->actionFunc = EnRacedog_RaceStart; } void EnRacedog_Destroy(Actor* thisx, GlobalContext* globalCtx) { @@ -270,125 +354,150 @@ void EnRacedog_Destroy(Actor* thisx, GlobalContext* globalCtx) { Collider_DestroyCylinder(globalCtx, &this->collider); } -void func_80B24C14(EnRacedog* this, GlobalContext* globalCtx) { - s16 phi_v1; - - if (this->unk_28A == 0) { - phi_v1 = 0; - } else { - this->unk_28A--; - phi_v1 = this->unk_28A; - } - - if (phi_v1 == 0) { - this->unk_28A = Rand_S16Offset(50, 50); - if (this->unk_28C == 0) { +/** + * This function makes the dog wait at the starting line until the race actually begins. + * It's also responsible for playing the starting shot sound once the race begins. + */ +void EnRacedog_RaceStart(EnRacedog* this, GlobalContext* globalCtx) { + if (DECR(this->raceStartTimer) == 0) { + this->raceStartTimer = Rand_S16Offset(50, 50); + if (this->extraTimeBeforeRaceStart == 0) { play_sound(NA_SE_SY_START_SHOT); } - func_80B24630(&this->skelAnime, D_80B25EF0, 2); - this->actionFunc = func_80B24CB4; + EnRacedog_ChangeAnimation(&this->skelAnime, sAnimations, RACEDOG_ANIMATION_RUN); + this->actionFunc = EnRacedog_Race; } } -void func_80B24CB4(EnRacedog* this, GlobalContext* globalCtx) { - s16 phi_a1; - f32 sp30; +/** + * This function handles the dog's behavior and state for the entire time the race is going. + */ +void EnRacedog_Race(EnRacedog* this, GlobalContext* globalCtx) { + s16 yRotation; + f32 distSq; this->collider.dim.radius = 15; - if (this->unk_1E0 != 0) { - phi_a1 = func_80B2478C(this->unk_1E0, this->unk_1E8, &this->actor.world.pos, &sp30); + if (this->path != NULL) { + yRotation = EnRacedog_GetYRotation(this->path, this->currentPoint, &this->actor.world.pos, &distSq); if (this->actor.bgCheckFlags & 8) { - phi_a1 = this->actor.wallYaw; + yRotation = this->actor.wallYaw; } - Math_SmoothStepToS(&this->actor.world.rot.y, phi_a1, 4, 0x3E8, 1); + Math_SmoothStepToS(&this->actor.world.rot.y, yRotation, 4, 0x3E8, 1); this->actor.shape.rot.y = this->actor.world.rot.y; - if (sp30 <= 2500.0f) { - this->unk_1E8++; - if (this->unk_1E8 >= (this->unk_1E0->count - 1)) { - this->unk_1E8 = 0; + if (distSq <= SQ(50.0f)) { + this->currentPoint++; + if (this->currentPoint >= (this->path->count - 1)) { + this->currentPoint = 0; } } - func_80B24F08(this); - if ((this->unk_1E8 >= ((this->unk_1E0->count / 4) * 3)) && (this->unk_290 == D_80B25D4C)) { - D_80B25D48++; + EnRacedog_UpdateSpeed(this); + + // Putting this after EnRacedog_UpdateSpeed will ensure that when EnRacedog_CalculateFinalStretchTargetSpeed + // is called for the first-place dog, the sprint timer will be 0, so the first-place dog will be guaranteed + // to have a sprint speed multiplier of 1. + if ((this->currentPoint >= ((this->path->count / 4) * 3)) && (this->index == sFirstPlaceIndex)) { + sSprintTimer++; } - func_80B252F8(this); - func_80B2538C(this); + EnRacedog_UpdateRaceVariables(this); + EnRacedog_CheckForFinish(this); Actor_MoveWithGravity(&this->actor); } - func_80B25448(this); - func_80B256BC(this); - func_80B255AC(this, globalCtx); + EnRacedog_UpdateRunAnimationPlaySpeed(this); + EnRacedog_PlayWalkSfx(this); + EnRacedog_SpawnFloorDustRing(this, globalCtx); } -void func_80B24E14(EnRacedog* this) { - if (this->unk_290 % 2) { - D_80B25D88[this->unk_290].unk_0E = - (((gSaveContext.save.weekEventReg[42 + (this->unk_290 / 2)]) & (0x10 | 0x20 | 0x40 | 0x80)) >> 4) + 0x3539; +/** + * Updates the text ID in sDogInfo based on what was set in the weekEventRegs by + * En_Aob_01. This makes it so sDogInfo can be used in other functions to determine + * the condition of the dog. + */ +void EnRacedog_UpdateTextId(EnRacedog* this) { + if (this->index % 2) { + sDogInfo[this->index].textId = + (((gSaveContext.save.weekEventReg[42 + (this->index / 2)]) & (0x10 | 0x20 | 0x40 | 0x80)) >> 4) + 0x3539; } else { - D_80B25D88[this->unk_290].unk_0E = - ((gSaveContext.save.weekEventReg[42 + (this->unk_290 / 2)]) & (1 | 2 | 4 | 8)) + 0x3539; + sDogInfo[this->index].textId = + ((gSaveContext.save.weekEventReg[42 + (this->index / 2)]) & (1 | 2 | 4 | 8)) + 0x3539; } - if ((D_80B25D88[this->unk_290].unk_0E >= 0x3547) || (D_80B25D88[this->unk_290].unk_0E < 0x3539)) { - D_80B25D88[this->unk_290].unk_0E = 0x353E; + // This makes sure the text ID is something in the range of 0x3539 to 0x3547. + if ((sDogInfo[this->index].textId >= 0x3547) || (sDogInfo[this->index].textId < 0x3539)) { + sDogInfo[this->index].textId = 0x353E; } - if (D_80B25D88[this->unk_290].unk_0E == 0x3547) { - D_80B25D88[this->unk_290].unk_0E = 0x3538; + + // Actual valid text IDs for the race dogs range between 0x3538 and 0x3546, so this + // code makes the two ranges match up. Perhaps the text got shifted? + if (sDogInfo[this->index].textId == 0x3547) { + sDogInfo[this->index].textId = 0x3538; } } -void func_80B24F08(EnRacedog* this) { - s32 temp_a0; - s32 temp_v1 = this->unk_1E0->count; +/** + * Responsible for calculating the dog's target speed and for making its actual speed + * approach the target speed. + */ +void EnRacedog_UpdateSpeed(EnRacedog* this) { + s32 quarterPathCount; + s32 pathCount = this->path->count; - if (this->unk_2B8 < this->unk_1E8) { - this->unk_2B8 = this->unk_1E8; - if (this->unk_1E8 == 0) { - this->unk_298 = D_80B25D50[D_80B25D88[this->unk_290].unk_08][0]; + // Only update the target speed if the dog is at a further point along the path + // than it was when the target speed was last updated. + if (this->pointForCurrentTargetSpeed < this->currentPoint) { + this->pointForCurrentTargetSpeed = this->currentPoint; + if (this->currentPoint == 0) { + // The dog is at the very start of the race track. + this->targetSpeed = sBaseSpeeds[sDogInfo[this->index].color][0]; } else { - temp_a0 = temp_v1 / 4; - if (this->unk_1E8 < temp_a0) { - if (D_80B25D88[this->unk_290].unk_08 == 5) { - this->unk_298 = D_80B25D50[D_80B25D88[this->unk_290].unk_08][0] + randPlusMinusPoint5Scaled(1.0f); + quarterPathCount = pathCount / 4; + if (this->currentPoint < quarterPathCount) { + // The dog is past the very start, but is still less than 1/4th of the way through the race track. + // This code will give the blue dog a higher base speed than any other dog (6.0 instead of 5.0). + if (sDogInfo[this->index].color == DOG_COLOR_BLUE) { + this->targetSpeed = sBaseSpeeds[sDogInfo[this->index].color][0] + randPlusMinusPoint5Scaled(1.0f); } else { - this->unk_298 = 5.0f + randPlusMinusPoint5Scaled(1.0f); + this->targetSpeed = 5.0f + randPlusMinusPoint5Scaled(1.0f); } - if ((D_80B25D88[this->unk_290].unk_0E < 0x353E) && (this->unk_290 != D_80B25D4C)) { - this->unk_298 *= D_80B25D88[this->unk_290].unk_04; + if (DOG_IS_IN_GOOD_CONDITION(this) && (this->index != sFirstPlaceIndex)) { + this->targetSpeed *= sDogInfo[this->index].goodConditionSpeedMultiplier; } - } else if (this->unk_1E8 < (temp_a0 * 3)) { - if (this->unk_1E8 < D_80B25D88[this->unk_290].unk_0C) { - this->unk_298 = 5.0f + randPlusMinusPoint5Scaled(1.0f); + } else if (this->currentPoint < (quarterPathCount * 3)) { + // The dog is between 1/4th and 3/4ths of the way through the race track. + if (this->currentPoint < sDogInfo[this->index].pointToUseSecondBaseSpeed) { + this->targetSpeed = 5.0f + randPlusMinusPoint5Scaled(1.0f); } else { - this->unk_298 = D_80B25D50[D_80B25D88[this->unk_290].unk_08][1] + randPlusMinusPoint5Scaled(1.0f); + this->targetSpeed = sBaseSpeeds[sDogInfo[this->index].color][1] + randPlusMinusPoint5Scaled(1.0f); - if ((D_80B25D88[this->unk_290].unk_0E < 0x353E) && (this->unk_290 != D_80B25D4C)) { - this->unk_298 *= D_80B25D88[this->unk_290].unk_04; + if (DOG_IS_IN_GOOD_CONDITION(this) && (this->index != sFirstPlaceIndex)) { + this->targetSpeed *= sDogInfo[this->index].goodConditionSpeedMultiplier; } } - } else if (this->unk_1E8 < temp_v1) { - func_80B251EC(this); + } else if (this->currentPoint < pathCount) { + // The dog is more than 3/4ths of the way through the race track. + EnRacedog_CalculateFinalStretchTargetSpeed(this); } else { - this->unk_298 = randPlusMinusPoint5Scaled(1.0f) + 5.0f; + this->targetSpeed = randPlusMinusPoint5Scaled(1.0f) + 5.0f; } } } - if ((this->unk_1E8 != 0) || (this->unk_29C != 0)) { + // Seemingly the only point of this raceStatus check is to ensure this code still runs when + // the dog has finished the race and is at point 0 along their second lap. + if ((this->currentPoint != 0) || (this->raceStatus != RACEDOG_RACE_STATUS_BEFORE_POINT_9)) { this->actor.shape.rot.y = this->actor.world.rot.y; } - Math_ApproachF(&this->actor.speedXZ, this->unk_298, 0.5f, 3.0f); + Math_ApproachF(&this->actor.speedXZ, this->targetSpeed, 0.5f, 3.0f); - if (this->unk_290 == this->unk_292) { + // The dog that the player has selected has a slightly higher max speed than the other dogs. + if (this->index == this->selectedDogIndex) { if (this->actor.speedXZ > 7.5f) { this->actor.speedXZ = 7.5f; } @@ -399,66 +508,90 @@ void func_80B24F08(EnRacedog* this) { } } -void func_80B251EC(EnRacedog* this) { - f32 temp; +/** + * This function handles updating targetSpeed for the final stretch of the race, + * where the dog starts sprinting towards the finish line. + */ +void EnRacedog_CalculateFinalStretchTargetSpeed(EnRacedog* this) { + f32 sprintSpeedMultiplier; - if (D_80B25D88[this->unk_290].unk_00 == -1.0f) { - if (D_80B25D48 < 100.0f) { - D_80B25D88[this->unk_290].unk_00 = 200.0f / (200.0f - D_80B25D48); + // Dogs are only allowed to update their sprintSpeedMultiplier once, so its value will + // depend on the value of sSprintTimer when they first hit the 3/4th mark on the track. + if (sDogInfo[this->index].sprintSpeedMultiplier == -1.0f) { + if (sSprintTimer < 100.0f) { + sDogInfo[this->index].sprintSpeedMultiplier = 200.0f / (200.0f - sSprintTimer); } else { - D_80B25D88[this->unk_290].unk_00 = 2.0f; + sDogInfo[this->index].sprintSpeedMultiplier = 2.0f; } } - if (D_80B25D88[this->unk_290].unk_0E < 0x3542) { - temp = D_80B25D88[this->unk_290].unk_00; - this->unk_298 = temp * D_80B25D50[D_80B25D88[this->unk_290].unk_08][1]; + if (!DOG_IS_IN_BAD_CONDITION(this)) { + sprintSpeedMultiplier = sDogInfo[this->index].sprintSpeedMultiplier; + this->targetSpeed = sprintSpeedMultiplier * sBaseSpeeds[sDogInfo[this->index].color][1]; } - if ((D_80B25D88[this->unk_290].unk_0E < 0x353E) && (this->unk_290 != D_80B25D4C)) { - this->unk_298 *= D_80B25D88[this->unk_290].unk_04; + if (DOG_IS_IN_GOOD_CONDITION(this) && (this->index != sFirstPlaceIndex)) { + this->targetSpeed *= sDogInfo[this->index].goodConditionSpeedMultiplier; } } -void func_80B252F8(EnRacedog* this) { - if ((this->unk_1E8 >= 9) && (this->unk_29C == 0)) { - this->unk_29C = 1; +/** + * Responsible for updating the raceStatus variable for this dog, as well as updating the + * sFurthestPoint and sFirstPlaceIndex variables. + */ +void EnRacedog_UpdateRaceVariables(EnRacedog* this) { + if ((this->currentPoint >= 9) && (this->raceStatus == RACEDOG_RACE_STATUS_BEFORE_POINT_9)) { + this->raceStatus = RACEDOG_RACE_STATUS_BETWEEN_POINTS_9_AND_11; } - if ((this->unk_1E8 >= 0xB) && (this->unk_29C == 1)) { - this->unk_29C = 2; + if ((this->currentPoint >= 11) && (this->raceStatus == RACEDOG_RACE_STATUS_BETWEEN_POINTS_9_AND_11)) { + this->raceStatus = RACEDOG_RACE_STATUS_AFTER_POINT_11; } - if (((this->unk_1E8 >= D_80B25D44) || (this->unk_29C <= 0)) && (this->unk_1E8 > D_80B25D44)) { - D_80B25D44 = this->unk_1E8; - D_80B25D4C = this->unk_290; + if (((this->currentPoint >= sFurthestPoint) || (this->raceStatus <= RACEDOG_RACE_STATUS_BEFORE_POINT_9)) && + (this->currentPoint > sFurthestPoint)) { + sFurthestPoint = this->currentPoint; + sFirstPlaceIndex = this->index; } } -void func_80B2538C(EnRacedog* this) { - if (func_80B25490(this, D_80B25E78) && this->unk_29C == 2) { - D_80B25D40++; - if (D_80B25D40 == 1) { +/** + * Checks to see if this dog has finished the race. This function is responsible for changing the + * music when the first dog finishes the race, and it is also responsible for updating the event + * flags with what position the player's selected dog finished in. + */ +void EnRacedog_CheckForFinish(EnRacedog* this) { + if (EnRacedog_IsOverFinishLine(this, sFinishLineCoordinates) && + this->raceStatus == RACEDOG_RACE_STATUS_AFTER_POINT_11) { + sNumberOfDogsFinished++; + if (sNumberOfDogsFinished == 1) { Audio_QueueSeqCmd(NA_BGM_HORSE_GOAL | 0x8000); play_sound(NA_SE_SY_START_SHOT); } - this->unk_29C = 3; - if (this->unk_290 == this->unk_292) { - gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & 7) | (D_80B25D40 * 8); + this->raceStatus = RACEDOG_RACE_STATUS_FINISHED; + if (this->index == this->selectedDogIndex) { + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & 7) | (sNumberOfDogsFinished * 8); } } } -void func_80B25448(EnRacedog* this) { +/** + * Slows the dog's running speed animation if its speed is less than 3.0, and sets it + * to normal speed otherwise. + */ +void EnRacedog_UpdateRunAnimationPlaySpeed(EnRacedog* this) { if (this->actor.speedXZ < 3.0f) { - D_80B25EF0[2].playSpeed = 0.9f; + sAnimations[RACEDOG_ANIMATION_RUN].playSpeed = 0.9f; } else { - D_80B25EF0[2].playSpeed = 1.0f; + sAnimations[RACEDOG_ANIMATION_RUN].playSpeed = 1.0f; } } -s32 func_80B25490(EnRacedog* this, Vec2f* arg1) { +/** + * Returns true if the dog's current position is in the finish line. + */ +s32 EnRacedog_IsOverFinishLine(EnRacedog* this, Vec2f* finishLineCoordinates) { f32 xDistToTopFront; f32 zDistToTopFront; f32 xDistToBottomFront; @@ -467,53 +600,57 @@ s32 func_80B25490(EnRacedog* this, Vec2f* arg1) { f32 zDistToBottomFront; f32 zDistToTopBack; f32 xDistToTopBack; - f32 temp_f0; - f32 temp; + f32 frontPointsCrossProduct; + f32 crossProductTemp; - xDistToTopFront = this->actor.world.pos.x - arg1[0].x; - zDistToTopFront = this->actor.world.pos.z - arg1[0].z; - xDistToBottomFront = this->actor.world.pos.x - arg1[1].x; - zDistToBottomFront = this->actor.world.pos.z - arg1[1].z; - xDistToBottomBack = this->actor.world.pos.x - arg1[2].x; - zDistToBottomBack = this->actor.world.pos.z - arg1[2].z; - xDistToTopBack = this->actor.world.pos.x - arg1[3].x; - zDistToTopBack = this->actor.world.pos.z - arg1[3].z; + xDistToTopFront = this->actor.world.pos.x - finishLineCoordinates[0].x; + zDistToTopFront = this->actor.world.pos.z - finishLineCoordinates[0].z; + xDistToBottomFront = this->actor.world.pos.x - finishLineCoordinates[1].x; + zDistToBottomFront = this->actor.world.pos.z - finishLineCoordinates[1].z; + xDistToBottomBack = this->actor.world.pos.x - finishLineCoordinates[2].x; + zDistToBottomBack = this->actor.world.pos.z - finishLineCoordinates[2].z; + xDistToTopBack = this->actor.world.pos.x - finishLineCoordinates[3].x; + zDistToTopBack = this->actor.world.pos.z - finishLineCoordinates[3].z; - temp_f0 = ((xDistToTopFront * zDistToBottomFront) - (xDistToBottomFront * zDistToTopFront)); - temp = (((xDistToBottomFront * zDistToBottomBack) - (xDistToBottomBack * zDistToBottomFront))); - if (temp_f0 * temp < 0.0f) { + // frontPointsCrossProduct is positive if the dog is to the left of the line formed by the front points + // crossProductTemp is positive if the dog is above the line formed by the bottom points + frontPointsCrossProduct = ((xDistToTopFront * zDistToBottomFront) - (xDistToBottomFront * zDistToTopFront)); + crossProductTemp = (((xDistToBottomFront * zDistToBottomBack) - (xDistToBottomBack * zDistToBottomFront))); + if (frontPointsCrossProduct * crossProductTemp < 0.0f) { return false; } - temp_f0 = ((xDistToTopFront * zDistToBottomFront) - (xDistToBottomFront * zDistToTopFront)); - temp = ((xDistToBottomBack * zDistToTopBack) - (xDistToTopBack * zDistToBottomBack)); - if (temp_f0 * temp < 0.0f) { + // crossProductTemp is positive if the dog is to the right of the line formed by the back points + frontPointsCrossProduct = ((xDistToTopFront * zDistToBottomFront) - (xDistToBottomFront * zDistToTopFront)); + crossProductTemp = ((xDistToBottomBack * zDistToTopBack) - (xDistToTopBack * zDistToBottomBack)); + if (frontPointsCrossProduct * crossProductTemp < 0.0f) { return false; } - temp_f0 = ((xDistToTopFront * zDistToBottomFront) - (xDistToBottomFront * zDistToTopFront)); - temp = ((xDistToTopBack * zDistToTopFront) - (xDistToTopFront * zDistToTopBack)); - if (temp_f0 * temp < 0.0f) { + // crossProductTemp is positive if the dog is below the line formed by the top points + frontPointsCrossProduct = ((xDistToTopFront * zDistToBottomFront) - (xDistToBottomFront * zDistToTopFront)); + crossProductTemp = ((xDistToTopBack * zDistToTopFront) - (xDistToTopFront * zDistToTopBack)); + if (frontPointsCrossProduct * crossProductTemp < 0.0f) { return false; } return true; } -void func_80B255AC(EnRacedog* this, GlobalContext* globalCtx) { +void EnRacedog_SpawnFloorDustRing(EnRacedog* this, GlobalContext* globalCtx) { s16 curFrame = this->skelAnime.curFrame; s16 mod = (this->actor.speedXZ > 6.0f) ? 2 : 3; - Vec3f sp38; + Vec3f pos; - if (((this->unk_290 + curFrame) % mod) == 0) { - sp38.x = this->actor.world.pos.x + randPlusMinusPoint5Scaled(15.0f); - sp38.y = this->actor.world.pos.y; - sp38.z = this->actor.world.pos.z + randPlusMinusPoint5Scaled(15.0f); - Actor_SpawnFloorDustRing(globalCtx, &this->actor, &sp38, 10.0f, 0, 2.0f, 300, 0, true); + if (((this->index + curFrame) % mod) == 0) { + pos.x = this->actor.world.pos.x + randPlusMinusPoint5Scaled(15.0f); + pos.y = this->actor.world.pos.y; + pos.z = this->actor.world.pos.z + randPlusMinusPoint5Scaled(15.0f); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &pos, 10.0f, 0, 2.0f, 300, 0, true); } } -void func_80B256BC(EnRacedog* this) { +void EnRacedog_PlayWalkSfx(EnRacedog* this) { s16 curFrame = this->skelAnime.curFrame; if ((curFrame == 1) || (curFrame == 7)) { @@ -524,18 +661,20 @@ void func_80B256BC(EnRacedog* this) { void EnRacedog_Update(Actor* thisx, GlobalContext* globalCtx) { s32 pad; EnRacedog* this = THIS; - Vec3f sp2C = { 0.0f, 0.0f, 0.0f }; + Vec3f floorRot = { 0.0f, 0.0f, 0.0f }; - this->unk_292 = D_80B25E68.unk_0A; + this->selectedDogIndex = sSelectedDogInfo.index; this->actionFunc(this, globalCtx); - func_80B246F4(this, globalCtx); - func_80B248B8(this, &sp2C); - Math_ApproachF(&this->unk_2AC.x, sp2C.x, 0.2f, 0.1f); + EnRacedog_UpdateCollision(this, globalCtx); + EnRacedog_GetFloorRot(this, &floorRot); + Math_ApproachF(&this->curRot.x, floorRot.x, 0.2f, 0.1f); - if (this->unk_2A0.x > 0.0f) { - if ((this->unk_2AC.x < 0.0f) && (this->unk_2AC.x > -0.1f)) { + if (this->prevRot.x > 0.0f) { + if ((this->curRot.x < 0.0f) && (this->curRot.x > -0.1f)) { + // Moves to the part of the running animation where the dog has four feet + // on the ground and is about to lift its feet off the ground. this->skelAnime.curFrame = 4.0f; this->actor.velocity.y = 5.5f; } @@ -545,51 +684,43 @@ void EnRacedog_Update(Actor* thisx, GlobalContext* globalCtx) { this->skelAnime.curFrame = 0.0f; } - this->unk_2A0 = this->unk_2AC; + this->prevRot = this->curRot; SkelAnime_Update(&this->skelAnime); } -void func_80B2583C(EnRacedog* this) { - s16 phi_v1; - - if (this->unk_288 >= 7) { - this->unk_2BC -= 0x10; - this->unk_2C0 += 8; - this->unk_2C4 += 0.05f; +void EnRacedog_UpdateSelectionArrow(EnRacedog* this) { + if (this->selectionArrowTimer > 6) { + this->selectionArrowGreenPrimColor -= 16; + this->selectionArrowGreenEnvColor += 8; + this->selectionArrowScale += 0.05f; } else { - this->unk_2BC += 0x10; - this->unk_2C0 -= 8; - this->unk_2C4 -= 0.05f; + this->selectionArrowGreenPrimColor += 16; + this->selectionArrowGreenEnvColor -= 8; + this->selectionArrowScale -= 0.05f; } - if (this->unk_288 == 0) { - phi_v1 = 0; - } else { - this->unk_288--; - phi_v1 = this->unk_288; - } - - if (phi_v1 == 0) { - this->unk_288 = 0xC; + if (DECR(this->selectionArrowTimer) == 0) { + this->selectionArrowTimer = 12; } } -void func_80B258D8(EnRacedog* this, GlobalContext* globalCtx) { - Vec3s sp48 = gZeroVec3s; - s32 phi_v0 = (this->unk_290 == this->unk_292) ? true : false; +void EnRacedog_DrawSelectionArrow(EnRacedog* this, GlobalContext* globalCtx) { + Vec3s rotation = gZeroVec3s; + s32 shouldDrawSelectionArrow = (this->index == this->selectedDogIndex) ? true : false; - if (phi_v0) { + if (shouldDrawSelectionArrow) { OPEN_DISPS(globalCtx->state.gfxCtx); func_8012C28C(globalCtx->state.gfxCtx); - func_80B2583C(this); + EnRacedog_UpdateSelectionArrow(this); Matrix_SetStateRotationAndTranslation(this->actor.world.pos.x, this->actor.world.pos.y + 40.0f, - this->actor.world.pos.z, &sp48); + this->actor.world.pos.z, &rotation); gDPPipeSync(POLY_OPA_DISP++); - gDPSetPrimColor(POLY_OPA_DISP++, 0, 255, 255, this->unk_2BC, 0, 255); - gDPSetEnvColor(POLY_OPA_DISP++, 255, this->unk_2C0, 0, 255); - Matrix_Scale(this->unk_2C4 * 2.0f, this->unk_2C4 * 2.0f, this->unk_2C4 * 2.0f, MTXMODE_APPLY); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 255, 255, this->selectionArrowGreenPrimColor, 0, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 255, this->selectionArrowGreenEnvColor, 0, 255); + Matrix_Scale(this->selectionArrowScale * 2.0f, this->selectionArrowScale * 2.0f, + this->selectionArrowScale * 2.0f, MTXMODE_APPLY); gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gDogSelectionArrowEmptyDL); gSPDisplayList(POLY_OPA_DISP++, gDogSelectionArrowDL); @@ -606,14 +737,14 @@ s32 EnRacedog_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dL void EnRacedog_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, Actor* thisx) { s32 pad; EnRacedog* this = THIS; - Vec3f sp1C = { 0.0f, 20.0f, 0.0f }; + Vec3f focusOffset = { 0.0f, 20.0f, 0.0f }; if (limbIndex == DOG_LIMB_HEAD) { - Matrix_MultiplyVector3fByState(&sp1C, &this->actor.focus.pos); + Matrix_MultiplyVector3fByState(&focusOffset, &this->actor.focus.pos); } if (limbIndex == DOG_LIMB_TAIL) { - func_80B258D8(this, globalCtx); + EnRacedog_DrawSelectionArrow(this, globalCtx); } } @@ -626,32 +757,38 @@ void EnRacedog_Draw(Actor* thisx, GlobalContext* globalCtx) { gDPPipeSync(POLY_OPA_DISP++); - switch (D_80B25D88[this->unk_290].unk_08) { - case 3: + switch (sDogInfo[this->index].color) { + case DOG_COLOR_BEIGE: gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 200, 0); break; - case 1: + + case DOG_COLOR_WHITE: gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 0); break; - case 5: + + case DOG_COLOR_BLUE: gDPSetEnvColor(POLY_OPA_DISP++, 79, 79, 143, 0); break; - case 6: + + case DOG_COLOR_GOLD: gDPSetEnvColor(POLY_OPA_DISP++, 255, 207, 47, 0); break; - case 4: + + case DOG_COLOR_BROWN: gDPSetEnvColor(POLY_OPA_DISP++, 143, 79, 47, 0); break; - case 2: + + case DOG_COLOR_GRAY: gDPSetEnvColor(POLY_OPA_DISP++, 143, 143, 143, 0); break; + default: gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 200, 0); break; } Matrix_InsertTranslation(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); - Matrix_RotateStateAroundXAxis(this->unk_2AC.x); + Matrix_RotateStateAroundXAxis(this->curRot.x); Matrix_InsertZRotation_s(this->actor.shape.rot.z, MTXMODE_APPLY); Matrix_RotateY(this->actor.shape.rot.y, MTXMODE_APPLY); Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); diff --git a/src/overlays/actors/ovl_En_Racedog/z_en_racedog.h b/src/overlays/actors/ovl_En_Racedog/z_en_racedog.h index 19f7af7a5..a4406b04c 100644 --- a/src/overlays/actors/ovl_En_Racedog/z_en_racedog.h +++ b/src/overlays/actors/ovl_En_Racedog/z_en_racedog.h @@ -4,9 +4,20 @@ #include "global.h" #include "objects/object_dog/object_dog.h" -#define ENRACEDOG_GET_3E0(thisx) (((thisx)->params & 0x3E0) >> 5) +#define ENRACEDOG_GET_INDEX(thisx) (((thisx)->params & 0x3E0) >> 5) #define ENRACEDOG_GET_PATH(thisx) (((thisx)->params & 0xFC00) >> 10) +/** + * The main point of this seems to be some very light anti-cheat detection. The dog + * must progress through these statuses in a linear order to finish the race. + */ +typedef enum { + /* 0 */ RACEDOG_RACE_STATUS_BEFORE_POINT_9, + /* 1 */ RACEDOG_RACE_STATUS_BETWEEN_POINTS_9_AND_11, + /* 2 */ RACEDOG_RACE_STATUS_AFTER_POINT_11, + /* 3 */ RACEDOG_RACE_STATUS_FINISHED +} RacedogRaceStatus; + struct EnRacedog; typedef void (*EnRacedogActionFunc)(struct EnRacedog*, GlobalContext*); @@ -18,26 +29,26 @@ typedef struct EnRacedog { /* 0x14C */ SkelAnime skelAnime; /* 0x190 */ ColliderCylinder collider; /* 0x1DC */ UNK_TYPE1 unk_1DC[0x4]; - /* 0x1E0 */ Path* unk_1E0; + /* 0x1E0 */ Path* path; /* 0x1E4 */ UNK_TYPE1 unk_1E4[0x4]; - /* 0x1E8 */ s32 unk_1E8; + /* 0x1E8 */ s32 currentPoint; /* 0x1EC */ Vec3s jointTable[DOG_LIMB_MAX]; /* 0x23A */ Vec3s morphTable[DOG_LIMB_MAX]; - /* 0x288 */ s16 unk_288; - /* 0x28A */ s16 unk_28A; - /* 0x28C */ s16 unk_28C; + /* 0x288 */ s16 selectionArrowTimer; + /* 0x28A */ s16 raceStartTimer; + /* 0x28C */ s16 extraTimeBeforeRaceStart; /* 0x28A */ UNK_TYPE1 unk_28E[0x2]; - /* 0x290 */ s16 unk_290; - /* 0x292 */ s16 unk_292; + /* 0x290 */ s16 index; + /* 0x292 */ s16 selectedDogIndex; /* 0x294 */ UNK_TYPE1 unk_294[0x4]; - /* 0x298 */ f32 unk_298; - /* 0x29C */ s16 unk_29C; - /* 0x2A0 */ Vec3f unk_2A0; - /* 0x2AC */ Vec3f unk_2AC; - /* 0x2B8 */ s32 unk_2B8; - /* 0x2BC */ s32 unk_2BC; - /* 0x2C0 */ s32 unk_2C0; - /* 0x2C4 */ f32 unk_2C4; + /* 0x298 */ f32 targetSpeed; + /* 0x29C */ s16 raceStatus; + /* 0x2A0 */ Vec3f prevRot; + /* 0x2AC */ Vec3f curRot; + /* 0x2B8 */ s32 pointForCurrentTargetSpeed; + /* 0x2BC */ s32 selectionArrowGreenPrimColor; + /* 0x2C0 */ s32 selectionArrowGreenEnvColor; + /* 0x2C4 */ f32 selectionArrowScale; } EnRacedog; // size = 0x2C8 extern const ActorInit En_Racedog_InitVars; diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index 161cabe5b..92f36da06 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -13277,26 +13277,26 @@ 0x80B23F50:("ObjWind_Destroy",), 0x80B23F60:("ObjWind_Update",), 0x80B243C0:("ObjWind_Draw",), - 0x80B24630:("func_80B24630",), - 0x80B246F4:("func_80B246F4",), - 0x80B2478C:("func_80B2478C",), - 0x80B248B8:("func_80B248B8",), + 0x80B24630:("EnRacedog_ChangeAnimation",), + 0x80B246F4:("EnRacedog_UpdateCollision",), + 0x80B2478C:("EnRacedog_GetYRotation",), + 0x80B248B8:("EnRacedog_GetFloorRot",), 0x80B24930:("EnRacedog_Init",), 0x80B24BE8:("EnRacedog_Destroy",), - 0x80B24C14:("func_80B24C14",), - 0x80B24CB4:("func_80B24CB4",), - 0x80B24E14:("func_80B24E14",), - 0x80B24F08:("func_80B24F08",), - 0x80B251EC:("func_80B251EC",), - 0x80B252F8:("func_80B252F8",), - 0x80B2538C:("func_80B2538C",), - 0x80B25448:("func_80B25448",), - 0x80B25490:("func_80B25490",), - 0x80B255AC:("func_80B255AC",), - 0x80B256BC:("func_80B256BC",), + 0x80B24C14:("EnRacedog_RaceStart",), + 0x80B24CB4:("EnRacedog_Race",), + 0x80B24E14:("EnRacedog_UpdateTextId",), + 0x80B24F08:("EnRacedog_UpdateSpeed",), + 0x80B251EC:("EnRacedog_CalculateFinalStretchTargetSpeed",), + 0x80B252F8:("EnRacedog_UpdateRaceVariables",), + 0x80B2538C:("EnRacedog_CheckForFinish",), + 0x80B25448:("EnRacedog_UpdateRunAnimationPlaySpeed",), + 0x80B25490:("EnRacedog_IsOverFinishLine",), + 0x80B255AC:("EnRacedog_SpawnFloorDustRing",), + 0x80B256BC:("EnRacedog_PlayWalkSfx",), 0x80B25708:("EnRacedog_Update",), - 0x80B2583C:("func_80B2583C",), - 0x80B258D8:("func_80B258D8",), + 0x80B2583C:("EnRacedog_UpdateSelectionArrow",), + 0x80B258D8:("EnRacedog_DrawSelectionArrow",), 0x80B25A74:("EnRacedog_OverrideLimbDraw",), 0x80B25A90:("EnRacedog_PostLimbDraw",), 0x80B25B14:("EnRacedog_Draw",),