From a9bf86d073c1753edb560750b6ecb727771548c5 Mon Sep 17 00:00:00 2001 From: djevangelia Date: Mon, 6 Apr 2026 08:50:46 +0200 Subject: [PATCH 1/4] Gold Skulltula/Skullwalltula (En_Sw, En_Si, Obj_Makekinsuta) --- include/actor.h | 4 +- include/save.h | 8 +- src/code/z_actor.c | 4 +- .../actors/ovl_En_Insect/z_en_insect.c | 2 +- src/overlays/actors/ovl_En_Si/z_en_si.c | 49 +- src/overlays/actors/ovl_En_Si/z_en_si.h | 2 +- src/overlays/actors/ovl_En_Sw/z_en_sw.c | 780 +++++++++++------- src/overlays/actors/ovl_En_Sw/z_en_sw.h | 54 +- .../ovl_Obj_Makekinsuta/z_obj_makekinsuta.c | 20 +- .../ovl_Obj_Makekinsuta/z_obj_makekinsuta.h | 2 +- 10 files changed, 555 insertions(+), 370 deletions(-) diff --git a/include/actor.h b/include/actor.h index 985f32abb33..d0b1db5e7a9 100644 --- a/include/actor.h +++ b/include/actor.h @@ -660,11 +660,11 @@ f32 func_8002DCE4(struct Player* player); int func_8002DD6C(struct Player* player); int func_8002DD78(struct Player* player); s32 func_8002DDE4(struct PlayState* play); -s32 func_8002DDF4(struct PlayState* play); +s32 Player_IsClimbingStill(struct PlayState* play); void Actor_SwapHookshotAttachment(struct PlayState* play, Actor* srcActor, Actor* destActor); void Actor_RequestHorseCameraSetting(struct PlayState* play, struct Player* player); void Actor_MountHorse(struct PlayState* play, struct Player* player, Actor* horse); -int func_8002DEEC(struct Player* player); +int Player_IsDeadOrCutscene(struct Player* player); void Actor_InitPlayerHorse(struct PlayState* play, struct Player* player); s32 Player_SetCsAction(struct PlayState* play, Actor* csActor, u8 csAction); s32 Player_SetCsActionWithHaltedActors(struct PlayState* play, Actor* csActor, u8 csAction); diff --git a/include/save.h b/include/save.h index 31d7491f650..8b66bfa12d0 100644 --- a/include/save.h +++ b/include/save.h @@ -492,10 +492,10 @@ typedef enum LinkAge { #define CHECK_QUEST_ITEM(item) (gSaveContext.save.info.inventory.questItems & gBitFlags[item]) #define CHECK_DUNGEON_ITEM(item, dungeonIndex) (gSaveContext.save.info.inventory.dungeonItems[dungeonIndex] & gBitFlags[item]) -#define GET_GS_FLAGS(index) \ - ((gSaveContext.save.info.gsFlags[(index) >> 2] & gGsFlagsMasks[(index) & 3]) >> gGsFlagsShifts[(index) & 3]) -#define SET_GS_FLAGS(index, value) \ - (gSaveContext.save.info.gsFlags[(index) >> 2] |= (value) << gGsFlagsShifts[(index) & 3]) +#define GET_GS_FLAGS(mapIndex) \ + ((gSaveContext.save.info.gsFlags[(mapIndex) >> 2] & gGsFlagsMasks[(mapIndex) & 3]) >> gGsFlagsShifts[(mapIndex) & 3]) +#define SET_GS_FLAGS(mapIndex, id) \ + (gSaveContext.save.info.gsFlags[(mapIndex) >> 2] |= (id) << gGsFlagsShifts[(mapIndex) & 3]) #define HIGH_SCORE(score) (gSaveContext.save.info.highScores[score]) diff --git a/src/code/z_actor.c b/src/code/z_actor.c index ce2b883b036..154e260d8e9 100644 --- a/src/code/z_actor.c +++ b/src/code/z_actor.c @@ -1168,7 +1168,7 @@ s32 func_8002DDE4(PlayState* play) { return player->stateFlags2 & PLAYER_STATE2_3; } -s32 func_8002DDF4(PlayState* play) { +s32 Player_IsClimbingStill(PlayState* play) { Player* player = GET_PLAYER(play); return player->stateFlags2 & PLAYER_STATE2_12; @@ -1204,7 +1204,7 @@ void Actor_MountHorse(PlayState* play, Player* player, Actor* horse) { horse->child = &player->actor; } -int func_8002DEEC(Player* player) { +int Player_IsDeadOrCutscene(Player* player) { return (player->stateFlags1 & (PLAYER_STATE1_DEAD | PLAYER_STATE1_29)) || (player->csAction != PLAYER_CSACTION_NONE); } diff --git a/src/overlays/actors/ovl_En_Insect/z_en_insect.c b/src/overlays/actors/ovl_En_Insect/z_en_insect.c index dff41dd86df..b91a5e55be3 100644 --- a/src/overlays/actors/ovl_En_Insect/z_en_insect.c +++ b/src/overlays/actors/ovl_En_Insect/z_en_insect.c @@ -466,7 +466,7 @@ void EnInsect_Dig(EnInsect* this, PlayState* play) { if (this->actionTimer <= 0) { if ((this->insectFlags & INSECT_FLAG_FOUND_SOIL) && this->soilActor != NULL && Math3D_Vec3fDistSq(&this->soilActor->actor.world.pos, &this->actor.world.pos) < SQ(8.0f)) { - this->soilActor->unk_152 = 1; + this->soilActor->bugDig = 1; } Actor_Kill(&this->actor); } diff --git a/src/overlays/actors/ovl_En_Si/z_en_si.c b/src/overlays/actors/ovl_En_Si/z_en_si.c index 5ee82311b25..a1c22b5b22b 100644 --- a/src/overlays/actors/ovl_En_Si/z_en_si.c +++ b/src/overlays/actors/ovl_En_Si/z_en_si.c @@ -16,15 +16,19 @@ #define FLAGS (ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_HOOKSHOT_PULLS_ACTOR) +// Params are set by spawning Gold Skulltula +#define ENSI_GS_AREA(this) PARAMS_GET_S(this->actor.params, 8, 5) // mapIndex for current area +#define ENSI_GS_ID(this) PARAMS_GET_S(this->actor.params, 0, 8) // ID flag for this Gold Skulltula + void EnSi_Init(Actor* thisx, PlayState* play); void EnSi_Destroy(Actor* thisx, PlayState* play); void EnSi_Update(Actor* thisx, PlayState* play); void EnSi_Draw(Actor* thisx, PlayState* play); -s32 func_80AFB748(EnSi* this, PlayState* play); -void func_80AFB768(EnSi* this, PlayState* play); -void func_80AFB89C(EnSi* this, PlayState* play); -void func_80AFB950(EnSi* this, PlayState* play); +s32 EnSi_RemoveAC(EnSi* this, PlayState* play); +void EnSi_Action_Idle(EnSi* this, PlayState* play); +void EnSi_Action_HookshotPull(EnSi* this, PlayState* play); +void EnSi_Action_WaitTextbox(EnSi* this, PlayState* play); static ColliderCylinderInit sCylinderInit = { { @@ -46,7 +50,7 @@ static ColliderCylinderInit sCylinderInit = { { 20, 18, 2, { 0, 0, 0 } }, }; -static CollisionCheckInfoInit2 D_80AFBADC = { 0, 0, 0, 0, MASS_IMMOVABLE }; +static CollisionCheckInfoInit2 sEnSiColliderInfo = { 0, 0, 0, 0, MASS_IMMOVABLE }; ActorProfile En_Si_Profile = { /**/ ACTOR_EN_SI, @@ -65,10 +69,10 @@ void EnSi_Init(Actor* thisx, PlayState* play) { Collider_InitCylinder(play, &this->collider); Collider_SetCylinder(play, &this->collider, &this->actor, &sCylinderInit); - CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &D_80AFBADC); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sEnSiColliderInfo); Actor_SetScale(&this->actor, 0.025f); - this->unk_19C = 0; - this->actionFunc = func_80AFB768; + this->unused_19C = 0; + this->actionFunc = EnSi_Action_Idle; this->actor.shape.yOffset = 42.0f; } @@ -78,25 +82,28 @@ void EnSi_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyCylinder(play, &this->collider); } -s32 func_80AFB748(EnSi* this, PlayState* play) { +s32 EnSi_RemoveAC(EnSi* this, PlayState* play) { if (this->collider.base.acFlags & AC_HIT) { this->collider.base.acFlags &= ~AC_HIT; } return 0; } -void func_80AFB768(EnSi* this, PlayState* play) { +/** + * Rotate, wait for player to Hookshot or pick up + */ +void EnSi_Action_Idle(EnSi* this, PlayState* play) { Player* player = GET_PLAYER(play); if (ACTOR_FLAGS_CHECK_ALL(&this->actor, ACTOR_FLAG_HOOKSHOT_ATTACHED)) { - this->actionFunc = func_80AFB89C; + this->actionFunc = EnSi_Action_HookshotPull; } else { Math_SmoothStepToF(&this->actor.scale.x, 0.25f, 0.4f, 1.0f, 0.0f); Actor_SetScale(&this->actor, this->actor.scale.x); this->actor.shape.rot.y += 0x400; if (!Player_InCsMode(play)) { - func_80AFB748(this, play); + EnSi_RemoveAC(this, play); if (this->collider.base.ocFlags2 & OC2_HIT_PLAYER) { this->collider.base.ocFlags2 &= ~OC2_HIT_PLAYER; @@ -104,7 +111,7 @@ void func_80AFB768(EnSi* this, PlayState* play) { player->actor.freezeTimer = 10; Message_StartTextbox(play, 0xB4, NULL); Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); - this->actionFunc = func_80AFB950; + this->actionFunc = EnSi_Action_WaitTextbox; } else { Collider_UpdateCylinder(&this->actor, &this->collider); CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); @@ -114,7 +121,10 @@ void func_80AFB768(EnSi* this, PlayState* play) { } } -void func_80AFB89C(EnSi* this, PlayState* play) { +/** + * Pulled by Hookshot towards player, give item when detached + */ +void EnSi_Action_HookshotPull(EnSi* this, PlayState* play) { Player* player = GET_PLAYER(play); Math_SmoothStepToF(&this->actor.scale.x, 0.25f, 0.4f, 1.0f, 0.0f); @@ -126,17 +136,20 @@ void func_80AFB89C(EnSi* this, PlayState* play) { player->actor.freezeTimer = 10; Message_StartTextbox(play, 0xB4, NULL); Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); - this->actionFunc = func_80AFB950; + this->actionFunc = EnSi_Action_WaitTextbox; } } -void func_80AFB950(EnSi* this, PlayState* play) { +/** + * Wait for textbox to close, then set token as collected and kill actor + */ +void EnSi_Action_WaitTextbox(EnSi* this, PlayState* play) { Player* player = GET_PLAYER(play); if (Message_GetState(&play->msgCtx) != TEXT_STATE_CLOSING) { player->actor.freezeTimer = 10; } else { - SET_GS_FLAGS(PARAMS_GET_S(this->actor.params, 8, 5), PARAMS_GET_S(this->actor.params, 0, 8)); + SET_GS_FLAGS(ENSI_GS_AREA(this), ENSI_GS_ID(this)); Actor_Kill(&this->actor); } } @@ -153,7 +166,7 @@ void EnSi_Update(Actor* thisx, PlayState* play) { void EnSi_Draw(Actor* thisx, PlayState* play) { EnSi* this = (EnSi*)thisx; - if (this->actionFunc != func_80AFB950) { + if (this->actionFunc != EnSi_Action_WaitTextbox) { func_8002ED80(&this->actor, play, 0); func_8002EBCC(&this->actor, play, 0); GetItem_Draw(play, GID_SKULL_TOKEN_2); diff --git a/src/overlays/actors/ovl_En_Si/z_en_si.h b/src/overlays/actors/ovl_En_Si/z_en_si.h index 6aa91c0f30f..dd25d2d42f9 100644 --- a/src/overlays/actors/ovl_En_Si/z_en_si.h +++ b/src/overlays/actors/ovl_En_Si/z_en_si.h @@ -12,7 +12,7 @@ typedef struct EnSi { /* 0x0000 */ Actor actor; /* 0x014C */ EnSiActionFunc actionFunc; /* 0x0150 */ ColliderCylinder collider; - /* 0x019C */ u8 unk_19C; + /* 0x019C */ u8 unused_19C; } EnSi; // size = 0x01A0 #endif diff --git a/src/overlays/actors/ovl_En_Sw/z_en_sw.c b/src/overlays/actors/ovl_En_Sw/z_en_sw.c index b745742836c..9950c24f82c 100644 --- a/src/overlays/actors/ovl_En_Sw/z_en_sw.c +++ b/src/overlays/actors/ovl_En_Sw/z_en_sw.c @@ -1,3 +1,12 @@ +/* + * File: z_en_sw.c + * Overlay: En_Sw + * Description: Gold Skulltula and Skullwalltula actors + * + * EnSw_GS_*, EnSw_ActionGS_* and *GS are functions, actions and variables only used by Gold Skulltula + * EnSw_S_*, EnSw_ActionS_*, *S for Skullwalltula + */ + #include "z_en_sw.h" #include "libc64/math64.h" @@ -20,26 +29,38 @@ #define FLAGS (ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_CULLING_DISABLED) +// Parameter and values for type of skulltula +#define ACTOR_ENSW_TYPE(thisx) PARAMS_GET_S(thisx->params, 13, 3) +#define ENSW_TYPE(this) PARAMS_GET_S(this->actor.params, 13, 3) +#define ENSW_FLY_GS 4 +#define ENSW_SOIL_GS 3 +#define ENSW_NIGHT_GS 2 +#define ENSW_REGULAR_GS 1 +#define ENSW_SKULLWALLTULA 0 + +#define ENSW_GS_AREA(this) PARAMS_GET_S(this->params, 8, 5) // mapIndex for current area +#define ENSW_GS_ID(this) PARAMS_GET_S(this->params, 0, 8) // ID flag for this Gold Skulltula + void EnSw_Init(Actor* thisx, PlayState* play); void EnSw_Destroy(Actor* thisx, PlayState* play); void EnSw_Update(Actor* thisx, PlayState* play); void EnSw_Draw(Actor* thisx, PlayState* play); -s32 func_80B0DFFC(EnSw* this, PlayState* play); -void func_80B0D364(EnSw* this, PlayState* play); -void func_80B0E5E0(EnSw* this, PlayState* play); -void func_80B0D590(EnSw* this, PlayState* play); -void func_80B0E90C(EnSw* this, PlayState* play); -void func_80B0E9BC(EnSw* this, PlayState* play); -void func_80B0E728(EnSw* this, PlayState* play); -void func_80B0DC7C(EnSw* this, PlayState* play); -s32 func_80B0C0CC(EnSw* this, PlayState* play, s32); -void func_80B0D3AC(EnSw* this, PlayState* play); -void func_80B0DB00(EnSw* this, PlayState* play); -void func_80B0D878(EnSw* this, PlayState* play); +void EnSw_ActionGS_Idle(EnSw* this, PlayState* play); +void EnSw_ActionGS_Fly(EnSw* this, PlayState* play); +void EnSw_ActionGS_SetupFly(EnSw* this, PlayState* play); +void EnSw_ActionGS_Death(EnSw* this, PlayState* play); +void EnSw_ActionS_Idle(EnSw* this, PlayState* play); +void EnSw_ActionS_TargetAttack(EnSw* this, PlayState* play); +void EnSw_ActionS_StopMovement(EnSw* this, PlayState* play); +void EnSw_ActionS_Retreat(EnSw* this, PlayState* play); +void EnSw_ActionS_Fall(EnSw* this, PlayState* play); +void EnSw_ActionS_Death(EnSw* this, PlayState* play); +s32 EnSw_GS_FindFloorPoly(EnSw* this, PlayState* play, s32); +s32 EnSw_S_SightCheck(EnSw* this, PlayState* play); ActorProfile En_Sw_Profile = { /**/ ACTOR_EN_SW, - /**/ ACTORCAT_NPC, + /**/ ACTORCAT_NPC, // If Skullwalltula, set to ACTORCAT_ENEMY on init /**/ FLAGS, /**/ OBJECT_ST, /**/ sizeof(EnSw), @@ -76,7 +97,7 @@ static ColliderJntSphInit sJntSphInit = { sJntSphElementsInit, }; -static CollisionCheckInfoInit2 D_80B0F074 = { 1, 2, 25, 25, MASS_IMMOVABLE }; +static CollisionCheckInfoInit2 sEnSwColliderInfo = { 1, 2, 25, 25, MASS_IMMOVABLE }; typedef enum EnSwAnimation { /* 0 */ ENSW_ANIM_0, @@ -100,7 +121,10 @@ void EnSw_CrossProduct(Vec3f* a, Vec3f* b, Vec3f* dst) { dst->z = (a->x * b->y) - (a->y * b->x); } -s32 func_80B0BE20(EnSw* this, CollisionPoly* poly) { +/** + * Gold Skulltula: Attach the actor to the found suitable poly + */ +s32 EnSw_GS_SetFloorPoly(EnSw* this, CollisionPoly* poly) { Vec3f polyNormal; Vec3f sp38; f32 sp34; @@ -145,40 +169,53 @@ s32 func_80B0BE20(EnSw* this, CollisionPoly* poly) { //! @bug Does not return, but the return value is not used by any caller so it doesn't matter. } -CollisionPoly* func_80B0C020(PlayState* play, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, s32* arg4) { - CollisionPoly* sp3C; +/** + * Gold Skulltula: By line test, find a polygon that it can attach to as its position. + * Ensure it's not a crawlspace, and is not ignored by projectiles + * @return CollisionPoly* found by line test if fulfills above criteria, else NULL + */ +CollisionPoly* EnSw_GS_FindSuitablePoly(PlayState* play, Vec3f* pos1, Vec3f* pos2, Vec3f* posResult, s32* bgId) { + CollisionPoly* poly; s32 pad; - if (!BgCheck_EntityLineTest1(&play->colCtx, arg1, arg2, arg3, &sp3C, true, true, true, false, arg4)) { + // Find poly + if (!BgCheck_EntityLineTest1(&play->colCtx, pos1, pos2, posResult, &poly, true, true, true, false, bgId)) { return NULL; } - if (SurfaceType_GetWallFlags(&play->colCtx, sp3C, *arg4) & WALL_FLAG_CRAWLSPACE) { + // Check suitability + if (SurfaceType_GetWallFlags(&play->colCtx, poly, *bgId) & WALL_FLAG_CRAWLSPACE) { return NULL; } - if (SurfaceType_IsIgnoredByProjectiles(&play->colCtx, sp3C, *arg4)) { + if (SurfaceType_IsIgnoredByProjectiles(&play->colCtx, poly, *bgId)) { return NULL; } - return sp3C; + return poly; } -s32 func_80B0C0CC(EnSw* this, PlayState* play, s32 arg2) { - CollisionPoly* temp_v0_2; - CollisionPoly* temp_s1; - Vec3f sp9C; - Vec3f sp90; +/** + * Gold Skulltula: Find a poly for the actor to attach to as its floor. + * If found, set it as floorPoly. + * @param trueArg always 1 + * @return true if found a suitable poly, else false + */ +s32 EnSw_GS_FindFloorPoly(EnSw* this, PlayState* play, s32 trueArg) { + CollisionPoly* poly2; + CollisionPoly* poly1; + Vec3f pos2; + Vec3f pos1; Vec3f sp84; Vec3f sp78; s32 pad; - s32 sp70; - s32 sp6C; - s32 phi_s1; - s32 sp64; + s32 bgId1; + s32 bgId2; + s32 i; + s32 foundPoly; - sp64 = 0; - this->unk_42C = 1; + foundPoly = false; + this->unused_42C = 1; sp84 = sp78 = this->actor.world.pos; sp84.x += this->unk_364.x * 18.0f; sp84.y += this->unk_364.y * 18.0f; @@ -186,35 +223,39 @@ s32 func_80B0C0CC(EnSw* this, PlayState* play, s32 arg2) { sp78.x -= this->unk_364.x * 18.0f; sp78.y -= this->unk_364.y * 18.0f; sp78.z -= this->unk_364.z * 18.0f; - temp_s1 = func_80B0C020(play, &sp84, &sp78, &sp90, &sp70); + poly1 = EnSw_GS_FindSuitablePoly(play, &sp84, &sp78, &pos1, &bgId1); - if ((temp_s1 != NULL) && (this->unk_360 == 0)) { + // Found one poly, check if can find another + if ((poly1 != NULL) && (this->isAscendingGS == 0)) { sp78.x = sp84.x + (this->unk_37C.x * 24); sp78.y = sp84.y + (this->unk_37C.y * 24); sp78.z = sp84.z + (this->unk_37C.z * 24); - temp_v0_2 = func_80B0C020(play, &sp84, &sp78, &sp9C, &sp6C); - if (temp_v0_2 != NULL) { - if (arg2 == 1) { - func_80B0BE20(this, temp_v0_2); - this->actor.world.pos = sp9C; - this->actor.floorBgId = sp6C; + poly2 = EnSw_GS_FindSuitablePoly(play, &sp84, &sp78, &pos2, &bgId2); + if (poly2 != NULL) { + // Set found poly 2 to floor poly + if (trueArg == 1) { + EnSw_GS_SetFloorPoly(this, poly2); + this->actor.world.pos = pos2; + this->actor.floorBgId = bgId2; } } else { - if (this->actor.floorPoly != temp_s1) { - func_80B0BE20(this, temp_s1); + // Only found one poly + if (this->actor.floorPoly != poly1) { + EnSw_GS_SetFloorPoly(this, poly1); } - this->actor.world.pos = sp90; - this->actor.floorBgId = sp70; + this->actor.world.pos = pos1; + this->actor.floorBgId = bgId1; } - sp64 = 1; + foundPoly = true; + // Didn't find poly, test another method } else { sp84 = sp78; - for (phi_s1 = 0; phi_s1 < 3; phi_s1++) { - if (phi_s1 == 0) { + for (i = 0; i < 3; i++) { + if (i == 0) { sp78.x = sp84.x - (this->unk_37C.x * 24.0f); sp78.y = sp84.y - (this->unk_37C.y * 24.0f); sp78.z = sp84.z - (this->unk_37C.z * 24.0f); - } else if (phi_s1 == 1) { + } else if (i == 1) { sp78.x = sp84.x + (this->unk_370.x * 24.0f); sp78.y = sp84.y + (this->unk_370.y * 24.0f); sp78.z = sp84.z + (this->unk_370.z * 24.0f); @@ -223,17 +264,18 @@ s32 func_80B0C0CC(EnSw* this, PlayState* play, s32 arg2) { sp78.y = sp84.y - (this->unk_370.y * 24.0f); sp78.z = sp84.z - (this->unk_370.z * 24.0f); } - temp_v0_2 = func_80B0C020(play, &sp84, &sp78, &sp9C, &sp6C); - if (temp_v0_2 == NULL) { + poly2 = EnSw_GS_FindSuitablePoly(play, &sp84, &sp78, &pos2, &bgId2); + if (poly2 == NULL) { continue; } - if (arg2 == 1) { - func_80B0BE20(this, temp_v0_2); - this->actor.world.pos = sp9C; - this->actor.floorBgId = sp6C; + // Found a poly + if (trueArg == 1) { + EnSw_GS_SetFloorPoly(this, poly2); + this->actor.world.pos = pos2; + this->actor.floorBgId = bgId2; } - sp64 = 1; + foundPoly = true; break; } } @@ -242,27 +284,27 @@ s32 func_80B0C0CC(EnSw* this, PlayState* play, s32 arg2) { Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.world.rot.y, 8, 0xFA0, 1); Math_SmoothStepToS(&this->actor.shape.rot.z, this->actor.world.rot.z, 8, 0xFA0, 1); - return sp64; + return foundPoly; } void EnSw_Init(Actor* thisx, PlayState* play) { EnSw* this = (EnSw*)thisx; s32 phi_v0; - Vec3f sp4C = { 0.0f, 0.0f, 0.0f }; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; // unused s32 pad; if (PARAMS_GET_NOSHIFT(thisx->params, 15, 1)) { phi_v0 = PARAMS_GET_S(thisx->params - 0x8000, 13, 3) + 1; - thisx->params = PARAMS_GET_S(thisx->params, 0, 13) | (phi_v0 << 0xD); + thisx->params = PARAMS_GET_S(thisx->params, 0, 13) | (phi_v0 << 13); } - if (PARAMS_GET_S(thisx->params, 13, 3) > 0) { + if (ACTOR_ENSW_TYPE(thisx) > ENSW_SKULLWALLTULA) { phi_v0 = PARAMS_GET_S(thisx->params, 8, 5) - 1; - thisx->params = (thisx->params & ~(0x1F << 8)) | (phi_v0 << 8); + thisx->params = (thisx->params & ~(31 << 8)) | (phi_v0 << 8); } - // Check to see if this gold skull token has already been retrieved. - if (GET_GS_FLAGS(PARAMS_GET_S(thisx->params, 8, 5)) & PARAMS_GET_S(thisx->params, 0, 8)) { + // Check to see if this Gold Skulltula token has already been retrieved. + if (GET_GS_FLAGS(ENSW_GS_AREA(thisx)) & ENSW_GS_ID(thisx)) { Actor_Kill(&this->actor); return; } @@ -272,17 +314,17 @@ void EnSw_Init(Actor* thisx, PlayState* play) { ActorShape_Init(&thisx->shape, 0.0f, NULL, 0.0f); Collider_InitJntSph(play, &this->collider); Collider_SetJntSph(play, &this->collider, &this->actor, &sJntSphInit, this->colliderElements); - CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(0xE), &D_80B0F074); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(14), &sEnSwColliderInfo); this->actor.scale.x = 0.02f; - if (PARAMS_GET_S(thisx->params, 13, 3) == 0) { + if (ACTOR_ENSW_TYPE(thisx) == ENSW_SKULLWALLTULA) { this->actor.world.rot.x = 0; this->actor.world.rot.z = 0; thisx->shape.rot = this->actor.world.rot; - this->unk_484.y = this->actor.world.pos.y; - this->unk_484.x = this->actor.world.pos.x + (Math_SinS(this->actor.world.rot.y) * -60.0f); - this->unk_484.z = this->actor.world.pos.z + (Math_CosS(this->actor.world.rot.y) * -60.0f); - func_80B0DFFC(this, play); + this->bodyLineS.y = this->actor.world.pos.y; + this->bodyLineS.x = this->actor.world.pos.x + (Math_SinS(this->actor.world.rot.y) * -60.0f); + this->bodyLineS.z = this->actor.world.pos.z + (Math_CosS(this->actor.world.rot.y) * -60.0f); + EnSw_S_SightCheck(this, play); this->actor.home.pos = this->actor.world.pos; } else { this->unk_370.x = Math_SinS(thisx->shape.rot.y + 0x4000); @@ -294,49 +336,52 @@ void EnSw_Init(Actor* thisx, PlayState* play) { this->unk_37C.x = Math_SinS(thisx->shape.rot.y); this->unk_37C.y = 0.0f; this->unk_37C.z = Math_CosS(thisx->shape.rot.y); - func_80B0C0CC(this, play, 1); + EnSw_GS_FindFloorPoly(this, play, true); } - if (PARAMS_GET_S(thisx->params, 13, 3) >= 3) { + // Spawned Gold Skulltula, not spawned on scene init (soil patch, crate etc) + if (ACTOR_ENSW_TYPE(thisx) >= ENSW_SOIL_GS) { SFX_PLAY_CENTERED(NA_SE_SY_CORRECT_CHIME); } - switch (PARAMS_GET_S(thisx->params, 13, 3)) { - case 3: - case 4: - this->unk_360 = 1; + switch (ACTOR_ENSW_TYPE(thisx)) { + case ENSW_SOIL_GS: // Spawn from soil patch + case ENSW_FLY_GS: // Spawn from crate etc + this->isAscendingGS = 1; this->actor.velocity.y = 8.0f; this->actor.speed = 4.0f; this->actor.gravity = -1.0f; FALLTHROUGH; - case 2: + case ENSW_NIGHT_GS: // Night only this->actor.scale.x = 0.0f; FALLTHROUGH; - case 1: + case ENSW_REGULAR_GS: // Regular this->collider.elements[0].base.atDmgInfo.damage *= 2; this->actor.naviEnemyId = NAVI_ENEMY_GOLD_SKULLTULA; this->actor.colChkInfo.health *= 2; this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; break; - default: + default: // Skullwalltula Actor_ChangeCategory(play, &play->actorCtx, &this->actor, ACTORCAT_ENEMY); this->actor.naviEnemyId = NAVI_ENEMY_SKULLWALLTULA; break; } - this->unk_38E = Rand_S16Offset(0xF, 0x1E); + this->breakTimerGS = Rand_S16Offset(15, 30); // Set for both Gold Skulltula and Skullwalltula, but only used by GS Actor_SetScale(&this->actor, this->actor.scale.x); this->actor.home.pos = this->actor.world.pos; thisx->shape.rot = this->actor.world.rot; - if (PARAMS_GET_S(thisx->params, 13, 3) >= 3) { - this->unk_38C = 0x28; - this->unk_394 = 1; - this->actionFunc = func_80B0D364; - } else if (PARAMS_GET_S(thisx->params, 13, 3) == 0) { - this->actionFunc = func_80B0E5E0; + // Gold Skulltula from soil or crate + if (ACTOR_ENSW_TYPE(thisx) >= ENSW_SOIL_GS) { + this->rotateTimerGS = 40; + this->deathTimer = 1; + this->actionFunc = EnSw_ActionGS_SetupFly; + } else if (ACTOR_ENSW_TYPE(thisx) == ENSW_SKULLWALLTULA) { + this->actionFunc = EnSw_ActionS_Idle; + // Regular and night Gold Skulltulas } else { - this->actionFunc = func_80B0D590; + this->actionFunc = EnSw_ActionGS_Idle; } } @@ -346,45 +391,54 @@ void EnSw_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyJntSph(play, &this->collider); } -s32 func_80B0C9F0(EnSw* this, PlayState* play) { - s32 phi_v1 = false; +/** + * Gold Skulltula and Skullwalltula: Handle being AC hit and doing AT hit to player. + * Start death process if fatal blow. + * @return 1 if took damage/dead, else 0 + */ +s32 EnSw_HandleDamage(EnSw* this, PlayState* play) { + s32 hammerWave = false; - if (this->actor.xyzDistToPlayerSq < SQ(400.0f) && PARAMS_GET_S(this->actor.params, 13, 3) == 0 && + // Hammer shockwave instakills Skullwalltula + if (this->actor.xyzDistToPlayerSq < SQ(400.0f) && ENSW_TYPE(this) == ENSW_SKULLWALLTULA && play->actorCtx.unk_02 != 0) { - this->actor.colChkInfo.damage = this->actor.colChkInfo.health; - phi_v1 = true; + hammerWave = true; } - if (this->unk_392 == 0) { - if ((this->collider.base.acFlags & AC_HIT) || phi_v1) { + // Handle AC hit and hammer, if is able to take AC hit + if (this->ACColliderTimer == 0) { + if ((this->collider.base.acFlags & AC_HIT) || hammerWave) { this->collider.base.acFlags &= ~AC_HIT; - this->unk_392 = 0x10; - Actor_SetColorFilter(&this->actor, COLORFILTER_COLORFLAG_RED, 200, COLORFILTER_BUFFLAG_OPA, this->unk_392); + this->ACColliderTimer = 16; + Actor_SetColorFilter(&this->actor, COLORFILTER_COLORFLAG_RED, 200, COLORFILTER_BUFFLAG_OPA, + this->ACColliderTimer); if (Actor_ApplyDamage(&this->actor) != 0) { Actor_PlaySfx(&this->actor, NA_SE_EN_STALTU_DAMAGE); return true; } Enemy_StartFinishingBlow(play, &this->actor); - if (PARAMS_GET_S(this->actor.params, 13, 3) != 0) { + // Start Gold Skulltula death + if (ENSW_TYPE(this) != ENSW_SKULLWALLTULA) { this->skelAnime.playSpeed = 8.0f; if ((play->state.frames & 1) == 0) { - this->unk_420 = 0.1f; + this->rotationSpeedGS = 0.1f; } else { - this->unk_420 = -0.1f; + this->rotationSpeedGS = -0.1f; } - this->unk_394 = 0xA; - this->unk_38A = 1; - this->unk_420 *= 4.0f; - this->actionFunc = func_80B0D878; + this->deathTimer = 10; + this->staggerFallTimer = 1; + this->rotationSpeedGS *= 4.0f; // Faster rotation when dying + this->actionFunc = EnSw_ActionGS_Death; + // Start Skullwalltula death } else { this->actor.shape.shadowDraw = ActorShadow_DrawCircle; - this->actor.shape.shadowAlpha = 0xFF; - this->unk_38A = 2; + this->actor.shape.shadowAlpha = 255; + this->staggerFallTimer = 2; this->actor.shape.shadowScale = 16.0f; this->actor.gravity = -1.0f; this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; - this->actionFunc = func_80B0DB00; + this->actionFunc = EnSw_ActionS_Fall; } Actor_PlaySfx(&this->actor, NA_SE_EN_STALWALL_DEAD); @@ -392,24 +446,32 @@ s32 func_80B0C9F0(EnSw* this, PlayState* play) { } } - if ((this->unk_390 == 0) && (this->collider.base.atFlags & AT_HIT)) { - this->unk_390 = 30; + // If hurt player, set timer for setting AT collider + if ((this->ATColliderTimer == 0) && (this->collider.base.atFlags & AT_HIT)) { + this->ATColliderTimer = 30; } return false; } -void func_80B0CBE8(EnSw* this, PlayState* play) { - if ((PARAMS_GET_S(this->actor.params, 13, 3) > 0) && (this->actionFunc != func_80B0D590)) { - if (this->unk_392 != 0) { - this->unk_392--; +/** + * Gold Skulltula and Skullwalltula: Set AT, AC and OC colliders. + * AT and AC colliders have timers to prevent taking/dealing damage too rapidly. OC always active. + */ +void EnSw_SetColliders(EnSw* this, PlayState* play) { + // No collider for Gold Skulltula that is not in idle action (i.e fly/death) + if ((ENSW_TYPE(this) > ENSW_SKULLWALLTULA) && (this->actionFunc != EnSw_ActionGS_Idle)) { + if (this->ACColliderTimer != 0) { + this->ACColliderTimer--; } } else { - if ((DECR(this->unk_390) == 0) && (this->actor.colChkInfo.health != 0)) { + // AT timer gets set to 30 on hitting player + if ((DECR(this->ATColliderTimer) == 0) && (this->actor.colChkInfo.health != 0)) { CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider.base); } - if ((DECR(this->unk_392) == 0) && (this->actor.colChkInfo.health != 0)) { + // AC timer gets set to 16 on hit by player + if ((DECR(this->ACColliderTimer) == 0) && (this->actor.colChkInfo.health != 0)) { CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); } @@ -417,12 +479,16 @@ void func_80B0CBE8(EnSw* this, PlayState* play) { } } -s32 func_80B0CCF4(EnSw* this, f32* arg1) { +/** + * Gold Skulltula: Rotate the Gold Skulltula + */ +s32 EnSw_GS_Rotate(EnSw* this, f32* rotation) { CollisionPoly* floorPoly; f32 temp_f0; Vec3f floorPolyNormal; MtxF sp2C; + // Not attached to ground/wall if (this->actor.floorPoly == NULL) { return false; } @@ -431,7 +497,7 @@ s32 func_80B0CCF4(EnSw* this, f32* arg1) { floorPolyNormal.x = COLPOLY_GET_NORMAL(floorPoly->normal.x); floorPolyNormal.y = COLPOLY_GET_NORMAL(floorPoly->normal.y); floorPolyNormal.z = COLPOLY_GET_NORMAL(floorPoly->normal.z); - Matrix_RotateAxis(*arg1, &floorPolyNormal, MTXMODE_NEW); + Matrix_RotateAxis(*rotation, &floorPolyNormal, MTXMODE_NEW); Matrix_MultVec3f(&this->unk_370, &floorPolyNormal); this->unk_370 = floorPolyNormal; EnSw_CrossProduct(&this->unk_370, &this->unk_364, &this->unk_37C); @@ -463,18 +529,25 @@ s32 func_80B0CCF4(EnSw* this, f32* arg1) { return true; } -void func_80B0CEA8(EnSw* this, PlayState* play) { +/** + * Gold Skulltula: Play rotation sound. + * (Only called by Gold Skulltula despite the type check) + */ +void EnSw_GS_RotateSound(EnSw* this, PlayState* play) { if (!(this->actor.scale.x < 0.0139999995f)) { Camera* activeCam = GET_ACTIVE_CAM(play); if (!(Math_Vec3f_DistXYZ(&this->actor.world.pos, &activeCam->eye) >= 380.0f)) { - Actor_PlaySfx(&this->actor, (PARAMS_GET_S(this->actor.params, 13, 3) > 0) ? NA_SE_EN_STALGOLD_ROLL - : NA_SE_EN_STALWALL_ROLL); + Actor_PlaySfx(&this->actor, + (ENSW_TYPE(this) > ENSW_SKULLWALLTULA) ? NA_SE_EN_STALGOLD_ROLL : NA_SE_EN_STALWALL_ROLL); } } } -void func_80B0CF44(EnSw* this, PlayState* play, s32 cnt) { +/** + * Gold Skulltula: Dust when digging up from soil + */ +void EnSw_GS_SpawnDigDust(EnSw* this, PlayState* play, s32 cnt) { Color_RGBA8 primColor = { 80, 80, 50, 255 }; Color_RGBA8 envColor = { 100, 100, 80, 0 }; Vec3f velocity = { 0.0f, 0.0f, 0.0f }; @@ -483,7 +556,7 @@ void func_80B0CF44(EnSw* this, PlayState* play, s32 cnt) { s16 angle = (Rand_ZeroOne() - 0.5f) * 65536.0f; s32 i; - for (i = cnt; i >= 0; i--, angle += (s16)(0x10000 / cnt)) { + for (i = cnt; i >= 0; i--, angle += (s16)(65536 / cnt)) { accel.x = (Rand_ZeroOne() - 0.5f) * 2.0f; accel.z = (Rand_ZeroOne() - 0.5f) * 2.0f; pos.x = this->actor.world.pos.x + (Math_SinS(angle) * 2.0f); @@ -493,7 +566,10 @@ void func_80B0CF44(EnSw* this, PlayState* play, s32 cnt) { } } -void func_80B0D14C(EnSw* this, PlayState* play, s32 cnt) { +/** + * Gold Skulltula: Dust when landing after flying after spawn + */ +void EnSw_GS_SpawnLandDust(EnSw* this, PlayState* play, s32 cnt) { Color_RGBA8 primColor = { 80, 80, 50, 255 }; Color_RGBA8 envColor = { 100, 100, 80, 0 }; Vec3f velocity = { 0.0f, 0.0f, 0.0f }; @@ -502,7 +578,7 @@ void func_80B0D14C(EnSw* this, PlayState* play, s32 cnt) { s16 angle = (Rand_ZeroOne() - 0.5f) * 65536.0f; s32 i; - for (i = cnt; i >= 0; i--, angle += (s16)(0x10000 / cnt)) { + for (i = cnt; i >= 0; i--, angle += (s16)(65536 / cnt)) { accel.x = (Rand_ZeroOne() - 0.5f) * 2.0f; accel.z = (Rand_ZeroOne() - 0.5f) * 2.0f; pos.x = this->actor.world.pos.x + (Math_SinS(angle) * 14.0f); @@ -512,23 +588,34 @@ void func_80B0D14C(EnSw* this, PlayState* play, s32 cnt) { } } -void func_80B0D364(EnSw* this, PlayState* play) { - if (PARAMS_GET_S(this->actor.params, 13, 3) == 4) { - this->unk_38C = 0; - this->actionFunc = func_80B0D3AC; +/** + * Gold Skulltula: For newly spawned Gold Skulltula, prepare to fly. + */ +void EnSw_ActionGS_SetupFly(EnSw* this, PlayState* play) { + // Crate etc + if (ENSW_TYPE(this) == ENSW_FLY_GS) { + this->rotateTimerGS = 0; + this->actionFunc = EnSw_ActionGS_Fly; + // Soil } else { - this->unk_38C = 10; - this->actionFunc = func_80B0D3AC; + this->rotateTimerGS = 10; + this->actionFunc = EnSw_ActionGS_Fly; } } -void func_80B0D3AC(EnSw* this, PlayState* play) { - if (this->unk_38C != 0) { - if ((this->unk_38C & 4) != 0) { - func_80B0CF44(this, play, 5); +/** + * Gold Skulltula: Flying newly spawned Gold Skulltula. Movement, setup idle when landed. + * If soil: Play digging up effects before emerging. + */ +void EnSw_ActionGS_Fly(EnSw* this, PlayState* play) { + // Soil skull has rotateTimerGS set to 10 - first, play digging up effects + if (this->rotateTimerGS != 0) { + if ((this->rotateTimerGS & 4) != 0) { + EnSw_GS_SpawnDigDust(this, play, 5); // Spawn digging up dust } - this->unk_38C--; - if (this->unk_38C == 0) { + this->rotateTimerGS--; + if (this->rotateTimerGS == 0) { + // Digging itself up sounds SfxSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 40, NA_SE_EN_STALGOLD_UP_CRY); SfxSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 40, NA_SE_EN_DODO_M_UP); } else { @@ -536,6 +623,7 @@ void func_80B0D3AC(EnSw* this, PlayState* play) { } } + // Flying movement Math_ApproachF(&this->actor.scale.x, 0.02f, 0.2f, 0.01f); Actor_SetScale(&this->actor, this->actor.scale.x); this->actor.world.pos.x += this->unk_364.x * this->actor.velocity.y; @@ -547,26 +635,33 @@ void func_80B0D3AC(EnSw* this, PlayState* play) { this->actor.velocity.y += this->actor.gravity; this->actor.velocity.y = CLAMP_MIN(this->actor.velocity.y, this->actor.minVelocityY); + // Coming down if (this->actor.velocity.y < 0.0f) { - this->unk_360 = 0; + this->isAscendingGS = 0; } - if (func_80B0C0CC(this, play, 1) == 1) { - Actor_PlaySfx(&this->actor, NA_SE_EN_DODO_M_GND); - func_80B0D14C(this, play, 8); + // Landing + if (EnSw_GS_FindFloorPoly(this, play, true) == 1) { + Actor_PlaySfx(&this->actor, NA_SE_EN_DODO_M_GND); // Hit ground sound + EnSw_GS_SpawnLandDust(this, play, 8); this->actor.scale.x = 0.02f; Actor_SetScale(&this->actor, 0.02f); - this->actionFunc = func_80B0D590; - this->actor.velocity.y = 0.0f; + this->actionFunc = EnSw_ActionGS_Idle; + this->actor.velocity.y = 0.0f; // Can't move once poly found this->actor.speed = 0.0f; this->actor.gravity = 0.0f; } } -void func_80B0D590(EnSw* this, PlayState* play) { - f32 sp2C; +/** + * Gold Skulltula: Idle. Manage rotation, play sound. + * Handle day-night for night Gold Skulltulas. + */ +void EnSw_ActionGS_Idle(EnSw* this, PlayState* play) { + f32 rotation; - if (PARAMS_GET_S(this->actor.params, 13, 3) == 2) { + // If night Gold Skulltula, set scale and colliders depending on time of day + if (ENSW_TYPE(this) == ENSW_NIGHT_GS) { if (this->actor.scale.x < 0.0139999995f) { this->collider.elements[0].base.atElemFlags = ATELEM_NONE; this->collider.elements[0].base.acElemFlags = ACELEM_NONE; @@ -583,81 +678,92 @@ void func_80B0D590(EnSw* this, PlayState* play) { Actor_SetScale(&this->actor, this->actor.scale.x); } - if (this->unk_38E != 0) { - this->unk_38E--; - if (this->unk_38E == 0) { - func_80B0CEA8(this, play); - this->unk_420 = ((play->state.frames % 2) == 0) ? 0.1f : -0.1f; - this->unk_38A = 1; - this->unk_38C = Rand_S16Offset(30, 60); - if (PARAMS_GET_S(this->actor.params, 13, 3) != 0) { - this->unk_38C *= 2; - this->unk_420 *= 2.0f; + // Gold Skulltula rotation break + if (this->breakTimerGS != 0) { + this->breakTimerGS--; + if (this->breakTimerGS == 0) { + // Restart rotation + EnSw_GS_RotateSound(this, play); + this->rotationSpeedGS = ((play->state.frames % 2) == 0) ? 0.1f : -0.1f; + this->staggerFallTimer = 1; + this->rotateTimerGS = Rand_S16Offset(30, 60); + if (ENSW_TYPE(this) != ENSW_SKULLWALLTULA) { + this->rotateTimerGS *= 2; + this->rotationSpeedGS *= 2.0f; } } + // Rotating } else { - this->unk_38C--; - if (this->unk_38C == 0) { - this->unk_38E = Rand_S16Offset(15, 30); - this->unk_38A = 0; + this->rotateTimerGS--; + if (this->rotateTimerGS == 0) { + // Take random length break from rotation + this->breakTimerGS = Rand_S16Offset(15, 30); + this->staggerFallTimer = 0; this->skelAnime.playSpeed = 0.0f; - if (PARAMS_GET_S(this->actor.params, 13, 3) != 0) { - this->unk_38E /= 2; + if (ENSW_TYPE(this) != ENSW_SKULLWALLTULA) { + this->breakTimerGS /= 2; } - } else if (this->unk_38A != 0) { - this->unk_38A--; - this->skelAnime.playSpeed = (this->unk_38A == 0) ? 4.0f : 0.0f; + // Stagger the rotation and play sound + } else if (this->staggerFallTimer != 0) { + this->staggerFallTimer--; + this->skelAnime.playSpeed = (this->staggerFallTimer == 0) ? 4.0f : 0.0f; if (this->skelAnime.playSpeed > 0.0f) { - func_80B0CEA8(this, play); + EnSw_GS_RotateSound(this, play); } - if (PARAMS_GET_S(this->actor.params, 13, 3) != 0) { + if (ENSW_TYPE(this) != ENSW_SKULLWALLTULA) { this->skelAnime.playSpeed *= 2.0f; } } else { if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame) == 1) { - this->unk_38A = 2; + this->staggerFallTimer = 2; } - sp2C = 32768.0f / this->skelAnime.endFrame; - sp2C *= this->skelAnime.curFrame; - sp2C = Math_SinS(sp2C) * this->unk_420; - func_80B0CCF4(this, &sp2C); + rotation = 32768.0f / this->skelAnime.endFrame; + rotation *= this->skelAnime.curFrame; + rotation = Math_SinS(rotation) * this->rotationSpeedGS; + EnSw_GS_Rotate(this, &rotation); this->actor.shape.rot = this->actor.world.rot; } } } -void func_80B0D878(EnSw* this, PlayState* play) { - Actor* temp_v0; +/** + * Gold Skulltula: Spawn death flame effects, then token, and finally kill the EnSw actor + */ +void EnSw_ActionGS_Death(EnSw* this, PlayState* play) { + Actor* token; Vec3f pos; Vec3f velAndAccel = { 0.0f, 0.5f, 0.0f }; f32 x; f32 y; f32 z; + // Keep rotating and playing sound when dying if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame) == 1) { - func_80B0CEA8(this, play); + EnSw_GS_RotateSound(this, play); } - func_80B0CCF4(this, &this->unk_420); + EnSw_GS_Rotate(this, &this->rotationSpeedGS); this->actor.shape.rot = this->actor.world.rot; - if ((this->unk_394 == 0) && (this->unk_392 == 0)) { + // Spawn token and kill this actor + if ((this->deathTimer == 0) && (this->ACColliderTimer == 0)) { SFX_PLAY_CENTERED(NA_SE_SY_KINSTA_MARK_APPEAR); x = (this->unk_364.x * 10.0f); y = (this->unk_364.y * 10.0f); z = (this->unk_364.z * 10.0f); - temp_v0 = + token = Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_SI, this->actor.world.pos.x + x, this->actor.world.pos.y + y, this->actor.world.pos.z + z, 0, 0, 0, this->actor.params); - if (temp_v0 != NULL) { - temp_v0->parent = NULL; + if (token != NULL) { + token->parent = NULL; } Actor_Kill(&this->actor); return; } - if ((this->unk_392 == 0) && (DECR(this->unk_394) != 0)) { + // Spawn Gold Skulltula death flame effects + if ((this->ACColliderTimer == 0) && (DECR(this->deathTimer) != 0)) { pos = this->actor.world.pos; pos.y += 10.0f + ((Rand_ZeroOne() - 0.5f) * 6.0f); pos.x += (Rand_ZeroOne() - 0.5f) * 32.0f; @@ -666,37 +772,45 @@ void func_80B0D878(EnSw* this, PlayState* play) { } } -void func_80B0DB00(EnSw* this, PlayState* play) { +/** + * Skullwalltula: Falling before dying + */ +void EnSw_ActionS_Fall(EnSw* this, PlayState* play) { Actor_MoveXZGravity(&this->actor); this->actor.shape.rot.x += 0x1000; this->actor.shape.rot.z += 0x1000; Actor_UpdateBgCheckInfo(play, &this->actor, 20.0f, 20.0f, 0.0f, UPDBGCHECKINFO_FLAG_0 | UPDBGCHECKINFO_FLAG_2); if ((this->actor.bgCheckFlags & BGCHECKFLAG_GROUND) && !(0.0f <= this->actor.velocity.y)) { + // Handle extreme Y if (this->actor.floorHeight <= BGCHECK_Y_MIN || this->actor.floorHeight >= 32000.0f) { Actor_Kill(&this->actor); return; } + // Bounce this->actor.bgCheckFlags &= ~BGCHECKFLAG_GROUND; - if (this->unk_38A == 0) { - this->actionFunc = func_80B0DC7C; - this->unk_394 = 10; + if (this->staggerFallTimer == 0) { + this->actionFunc = EnSw_ActionS_Death; + this->deathTimer = 10; } else { - this->actor.velocity.y = ((this->unk_38A--) * 8.0f) * 0.5f; + this->actor.velocity.y = ((this->staggerFallTimer--) * 8.0f) * 0.5f; } - Actor_PlaySfx(&this->actor, NA_SE_EN_DODO_M_GND); + Actor_PlaySfx(&this->actor, NA_SE_EN_DODO_M_GND); // Hit ground sound Actor_SpawnFloorDustRing(play, &this->actor, &this->actor.world.pos, 16.0f, 12, 2.0f, 120, 10, false); } } -void func_80B0DC7C(EnSw* this, PlayState* play) { +/** + * Skullwalltula: Spawn death flames and die + */ +void EnSw_ActionS_Death(EnSw* this, PlayState* play) { Vec3f velAndAccel = { 0.0f, 0.5f, 0.0f }; Vec3f pos = { 0.0f, 0.0f, 0.0f }; - if (DECR(this->unk_394) != 0) { + if (DECR(this->deathTimer) != 0) { pos.y = ((Rand_ZeroOne() - 0.5f) * 6.0f) + (this->actor.world.pos.y + 10.0f); pos.x = ((Rand_ZeroOne() - 0.5f) * 32.0f) + this->actor.world.pos.x; pos.z = ((Rand_ZeroOne() - 0.5f) * 32.0f) + this->actor.world.pos.z; @@ -709,81 +823,97 @@ void func_80B0DC7C(EnSw* this, PlayState* play) { } } -s16 func_80B0DE34(EnSw* this, Vec3f* arg1) { +s16 EnSw_S_GetTargetAngle(EnSw* this, Vec3f* targetPos) { s16 pitch; s16 yaw; - yaw = Math_Vec3f_Yaw(&this->actor.world.pos, arg1) - this->actor.wallYaw; - pitch = Math_Vec3f_Pitch(&this->actor.world.pos, arg1) - 0x4000; + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, targetPos) - this->actor.wallYaw; + pitch = Math_Vec3f_Pitch(&this->actor.world.pos, targetPos) - 0x4000; return pitch * (yaw >= 0 ? -1 : 1); } -s32 func_80B0DEA8(EnSw* this, PlayState* play, s32 arg2) { +/** + * Skullwalltula: Check if can target player based on distance etc criteria + * @param trueArg always 1 + * @return true if Skullwalltula can target player + */ +s32 EnSw_S_ShouldTarget(EnSw* this, PlayState* play, s32 trueArg) { Player* player = GET_PLAYER(play); - CollisionPoly* sp58; - s32 sp54; - Vec3f sp48; + CollisionPoly* outPoly; + s32 bgId; + Vec3f posResult; - if (!(player->stateFlags1 & PLAYER_STATE1_21) && arg2) { + // Don't engage if player isn't climbing or is still, angle difference or distance too big + if (!(player->stateFlags1 & PLAYER_STATE1_21) && trueArg) { return false; - } else if (func_8002DDF4(play) && arg2) { + } else if (Player_IsClimbingStill(play) && trueArg) { return false; - } else if (ABS(func_80B0DE34(this, &player->actor.world.pos) - this->actor.shape.rot.z) >= 0x1FC2) { + } else if (ABS(EnSw_S_GetTargetAngle(this, &player->actor.world.pos) - this->actor.shape.rot.z) >= 0x1FC2) { return false; } else if (Math_Vec3f_DistXYZ(&this->actor.world.pos, &player->actor.world.pos) >= 130.0f) { return false; - } else if (!BgCheck_EntityLineTest1(&play->colCtx, &this->actor.world.pos, &player->actor.world.pos, &sp48, &sp58, - true, false, false, true, &sp54)) { + // Otherwise, if nothing between player and Skullwalltula - target + } else if (!BgCheck_EntityLineTest1(&play->colCtx, &this->actor.world.pos, &player->actor.world.pos, &posResult, + &outPoly, true, false, false, true, &bgId)) { return true; } else { return false; } } -s32 func_80B0DFFC(EnSw* this, PlayState* play) { +/** + * Skullwalltula: Check if player is within sight using lines. Check one line per frame. + * Also adjust position against wall. + * @return true if in sight + */ +s32 EnSw_S_SightCheck(EnSw* this, PlayState* play) { s32 pad; - CollisionPoly* sp60; - s32 sp5C; - Vec3f sp50; - s32 sp4C = true; + CollisionPoly* outPoly; + s32 bgId; + Vec3f posResult; + s32 result = true; if (this->collider.base.ocFlags1 & OC1_HIT) { this->collider.base.acFlags &= ~AC_HIT; - sp4C = false; + result = false; } else if (((play->state.frames % 4) == 0) && - !BgCheck_EntityLineTest1(&play->colCtx, &this->actor.world.pos, &this->unk_454, &sp50, &sp60, true, - false, false, true, &sp5C)) { - sp4C = false; + !BgCheck_EntityLineTest1(&play->colCtx, &this->actor.world.pos, &this->sightLine1S, &posResult, &outPoly, + true, false, false, true, &bgId)) { + result = false; } else if (((play->state.frames % 4) == 1) && - BgCheck_EntityLineTest1(&play->colCtx, &this->actor.world.pos, &this->unk_460, &sp50, &sp60, true, false, - false, true, &sp5C)) { - sp4C = false; + BgCheck_EntityLineTest1(&play->colCtx, &this->actor.world.pos, &this->sightLine2S, &posResult, &outPoly, + true, false, false, true, &bgId)) { + result = false; } else if (((play->state.frames % 4) == 2) && - !BgCheck_EntityLineTest1(&play->colCtx, &this->actor.world.pos, &this->unk_46C, &sp50, &sp60, true, - false, false, true, &sp5C)) { + !BgCheck_EntityLineTest1(&play->colCtx, &this->actor.world.pos, &this->sightLine3S, &posResult, &outPoly, + true, false, false, true, &bgId)) { if (0) {} - sp4C = false; + result = false; } else if (((play->state.frames % 4) == 3) && - BgCheck_EntityLineTest1(&play->colCtx, &this->actor.world.pos, &this->unk_478, &sp50, &sp60, true, false, - false, true, &sp5C)) { - sp4C = false; + BgCheck_EntityLineTest1(&play->colCtx, &this->actor.world.pos, &this->sightLine4S, &posResult, &outPoly, + true, false, false, true, &bgId)) { + result = false; } - if (BgCheck_EntityLineTest1(&play->colCtx, &this->actor.world.pos, &this->unk_484, &sp50, &this->unk_430, true, - false, false, true, &sp5C)) { - this->actor.wallYaw = RAD_TO_BINANG(Math_FAtan2F(this->unk_430->normal.x, this->unk_430->normal.z)); - this->actor.world.pos = sp50; + // Adjust position against wall + if (BgCheck_EntityLineTest1(&play->colCtx, &this->actor.world.pos, &this->bodyLineS, &posResult, &this->wallPolyS, + true, false, false, true, &bgId)) { + this->actor.wallYaw = RAD_TO_BINANG(Math_FAtan2F(this->wallPolyS->normal.x, this->wallPolyS->normal.z)); + this->actor.world.pos = posResult; this->actor.world.pos.x += 6.0f * Math_SinS(this->actor.world.rot.y); this->actor.world.pos.z += 6.0f * Math_CosS(this->actor.world.rot.y); - this->unk_434 = sp50; - this->unk_434.x += Math_SinS(this->actor.world.rot.y); - this->unk_434.z += Math_CosS(this->actor.world.rot.y); + this->unused_434 = posResult; + this->unused_434.x += Math_SinS(this->actor.world.rot.y); + this->unused_434.z += Math_CosS(this->actor.world.rot.y); } - return sp4C; + return result; } -void func_80B0E314(EnSw* this, Vec3f arg1, f32 arg4) { +/** + * Skullwalltula: Move towards position (both attacking and retreating) + */ +void EnSw_S_Move(EnSw* this, Vec3f targetPos, f32 targetSpeed) { f32 xDist; f32 yDist; f32 zDist; @@ -792,10 +922,10 @@ void func_80B0E314(EnSw* this, Vec3f arg1, f32 arg4) { f32 yDiff; f32 zDiff; - Math_SmoothStepToF(&this->actor.speed, arg4, 0.3f, 100.0f, 0.1f); - xDiff = arg1.x - this->actor.world.pos.x; - yDiff = arg1.y - this->actor.world.pos.y; - zDiff = arg1.z - this->actor.world.pos.z; + Math_SmoothStepToF(&this->actor.speed, targetSpeed, 0.3f, 100.0f, 0.1f); + xDiff = targetPos.x - this->actor.world.pos.x; + yDiff = targetPos.y - this->actor.world.pos.y; + zDiff = targetPos.z - this->actor.world.pos.z; dist = sqrtf(SQ(xDiff) + SQ(yDiff) + SQ(zDiff)); if (dist == 0.0f) { xDist = yDist = zDist = 0.0f; @@ -812,109 +942,140 @@ void func_80B0E314(EnSw* this, Vec3f arg1, f32 arg4) { this->actor.world.pos.z += zDist; } -s32 func_80B0E430(EnSw* this, f32 arg1, s16 arg2, s32 arg3, PlayState* play) { +/** + * Skullwalltula: Rotate and play sound. Used both in idle and as part of target/attack and retreat. + * @param rotationSpeed always 6.0f + * @return 1 if reached target angle + */ +s32 EnSw_S_Rotate(EnSw* this, f32 rotationSpeed, s16 angleStep, s32 idleRotate, PlayState* play) { Camera* activeCam; f32 lastFrame = Animation_GetLastFrame(&object_st_Anim_000304); - if (DECR(this->unk_388) != 0) { + // If in rotation break, reduce rotation and stop until break is over + if (DECR(this->breakTimerS) != 0) { Math_SmoothStepToF(&this->skelAnime.playSpeed, 0.0f, 0.6f, 1000.0f, 0.01f); return 0; } - Math_SmoothStepToF(&this->skelAnime.playSpeed, arg1, 0.6f, 1000.0f, 0.01f); + Math_SmoothStepToF(&this->skelAnime.playSpeed, rotationSpeed, 0.6f, 1000.0f, 0.01f); - if ((arg3 == 1) && (lastFrame < (this->skelAnime.curFrame + this->skelAnime.playSpeed))) { + // Only return early here if not rotating as part of targeting/attacking or retreating home + if ((idleRotate == 1) && (lastFrame < (this->skelAnime.curFrame + this->skelAnime.playSpeed))) { return 0; } + // Rotate activeCam = GET_ACTIVE_CAM(play); if (Math_Vec3f_DistXYZ(&this->actor.world.pos, &activeCam->eye) < 380.0f) { - if (DECR(this->unk_440) == 0) { - Actor_PlaySfx(&this->actor, NA_SE_EN_STALWALL_ROLL); - this->unk_440 = 4; + if (DECR(this->soundTimerS) == 0) { + Actor_PlaySfx(&this->actor, NA_SE_EN_STALWALL_ROLL); // Rotation sound + this->soundTimerS = 4; } } else { - this->unk_440 = 0; + this->soundTimerS = 0; } - Math_SmoothStepToS(&this->actor.shape.rot.z, this->unk_444, 4, arg2, arg2); + + Math_SmoothStepToS(&this->actor.shape.rot.z, this->targetAngleS, 4, angleStep, angleStep); this->actor.world.rot = this->actor.shape.rot; - if (this->actor.shape.rot.z == this->unk_444) { + if (this->actor.shape.rot.z == this->targetAngleS) { return 1; } return 0; } -void func_80B0E5E0(EnSw* this, PlayState* play) { +/** + * Skullwalltula: Idle. Manage rotation and check targeting. + */ +void EnSw_ActionS_Idle(EnSw* this, PlayState* play) { s32 pad[2]; f32 rand; - if (func_80B0E430(this, 6.0f, 0x3E8, 1, play)) { + // Rotate. If reached target angle, set new target + rotation break timer + if (EnSw_S_Rotate(this, 6.0f, 0x3E8, true, play)) { rand = Rand_ZeroOne(); - this->unk_444 = - ((s16)(20000.0f * rand) + 0x2EE0) * (Rand_ZeroOne() >= 0.5f ? 1.0f : -1.0f) + this->actor.world.rot.z; - this->unk_388 = Rand_S16Offset(10, 30); + this->targetAngleS = + ((s16)(20000.0f * rand) + 12000) * (Rand_ZeroOne() >= 0.5f ? 1.0f : -1.0f) + this->actor.world.rot.z; + this->breakTimerS = Rand_S16Offset(10, 30); } - if ((DECR(this->unk_442) == 0) && (func_80B0DEA8(this, play, 1))) { - Actor_PlaySfx(&this->actor, NA_SE_EN_STALWALL_LAUGH); - this->unk_442 = 20; - this->actionFunc = func_80B0E728; + // If not in engagement cooldown and player fulfills criteria for targeting, target + if ((DECR(this->engageTimerS) == 0) && (EnSw_S_ShouldTarget(this, play, true))) { + Actor_PlaySfx(&this->actor, NA_SE_EN_STALWALL_LAUGH); // The targeting sound + this->engageTimerS = 20; + this->actionFunc = EnSw_ActionS_TargetAttack; } } -void func_80B0E728(EnSw* this, PlayState* play) { +/** + * Skullwalltula: Targeting player (colored state), as well as attacking + */ +void EnSw_ActionS_TargetAttack(EnSw* this, PlayState* play) { Player* player = GET_PLAYER(play); s32 pad; - if (DECR(this->unk_442) != 0) { - if (func_80B0DEA8(this, play, 1)) { - this->unk_448 = player->actor.world.pos; - this->unk_448.y += 30.0f; - this->unk_444 = func_80B0DE34(this, &this->unk_448); - func_80B0E430(this, 6.0f, (u16)0xFA0, 0, play); + // If not time for attack yet, ensure player is still available for targeting + // and adjust target position and angle + if (DECR(this->engageTimerS) != 0) { + if (EnSw_S_ShouldTarget(this, play, true)) { + this->targetPosS = player->actor.world.pos; + this->targetPosS.y += 30.0f; + this->targetAngleS = EnSw_S_GetTargetAngle(this, &this->targetPosS); + EnSw_S_Rotate(this, 6.0f, (u16)0xFA0, false, play); } else { - this->actionFunc = func_80B0E5E0; + this->actionFunc = EnSw_ActionS_Idle; } + // Attacking } else { - if (!func_80B0DFFC(this, play)) { - this->unk_442 = Rand_S16Offset(20, 10); - this->unk_444 = func_80B0DE34(this, &this->actor.home.pos); - this->unk_448 = this->actor.home.pos; - this->actionFunc = func_80B0E9BC; + // If loses sight of player, stop pursuit and retreat + if (!EnSw_S_SightCheck(this, play)) { + this->engageTimerS = Rand_S16Offset(20, 10); + this->targetAngleS = EnSw_S_GetTargetAngle(this, &this->actor.home.pos); + this->targetPosS = this->actor.home.pos; + this->actionFunc = EnSw_ActionS_Retreat; + // Move quickly to attack } else { - func_80B0E314(this, this->unk_448, 8.0f); + EnSw_S_Move(this, this->targetPosS, 8.0f); - if (DECR(this->unk_440) == 0) { - Actor_PlaySfx(&this->actor, NA_SE_EN_STALWALL_DASH); - this->unk_440 = 4; + if (DECR(this->soundTimerS) == 0) { + Actor_PlaySfx(&this->actor, NA_SE_EN_STALWALL_DASH); // Attack sound + this->soundTimerS = 4; } - if (!(Math_Vec3f_DistXYZ(&this->actor.world.pos, &this->unk_448) > 13.0f) || func_8002DDF4(play)) { - this->actionFunc = func_80B0E90C; + // Reached last set player target position, or player is not moving - stop + if (!(Math_Vec3f_DistXYZ(&this->actor.world.pos, &this->targetPosS) > 13.0f) || + Player_IsClimbingStill(play)) { + this->actionFunc = EnSw_ActionS_StopMovement; } } } } -void func_80B0E90C(EnSw* this, PlayState* play) { +/** + * Skullwalltula: Decelerate and stop movement, prepare to return home (set new target position and angle) + */ +void EnSw_ActionS_StopMovement(EnSw* this, PlayState* play) { s32 pad; - func_80B0E314(this, this->unk_448, 0.0f); + EnSw_S_Move(this, this->targetPosS, 0.0f); if (this->actor.speed == 0.0f) { - this->unk_444 = func_80B0DE34(this, &this->actor.home.pos); - this->unk_448 = this->actor.home.pos; - this->actionFunc = func_80B0E9BC; + this->targetAngleS = EnSw_S_GetTargetAngle(this, &this->actor.home.pos); + this->targetPosS = this->actor.home.pos; + this->actionFunc = EnSw_ActionS_Retreat; } } -void func_80B0E9BC(EnSw* this, PlayState* play) { +/** + * Skullwalltula: Move to home position + */ +void EnSw_ActionS_Retreat(EnSw* this, PlayState* play) { s32 pad; - if (func_80B0E430(this, 6.0f, 0x3E8, 0, play)) { - func_80B0E314(this, this->unk_448, 2.0f); - if (!(Math_Vec3f_DistXYZ(&this->actor.world.pos, &this->unk_448) > 4.0f)) { - this->actionFunc = func_80B0E5E0; + if (EnSw_S_Rotate(this, 6.0f, 0x3E8, false, play)) { + EnSw_S_Move(this, this->targetPosS, 2.0f); + // Reached home + if (!(Math_Vec3f_DistXYZ(&this->actor.world.pos, &this->targetPosS) > 4.0f)) { + this->actionFunc = EnSw_ActionS_Idle; } } } @@ -923,23 +1084,23 @@ void EnSw_Update(Actor* thisx, PlayState* play) { EnSw* this = (EnSw*)thisx; SkelAnime_Update(&this->skelAnime); - func_80B0C9F0(this, play); + EnSw_HandleDamage(this, play); this->actionFunc(this, play); - func_80B0CBE8(this, play); + EnSw_SetColliders(this, play); } s32 EnSw_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { - Vec3f sp7C = { 1400.0f, -2600.0f, -800.0f }; - Vec3f sp70 = { 1400.0f, -1600.0f, 0.0f }; - Vec3f sp64 = { -1400.0f, -2600.0f, -800.0f }; - Vec3f sp58 = { -1400.0f, -1600.0f, 0.0f }; - Vec3f sp4C = { 0.0, 0.0f, -600.0f }; + Vec3f sightLineVec1 = { 1400.0f, -2600.0f, -800.0f }; + Vec3f sightLineVec2 = { 1400.0f, -1600.0f, 0.0f }; + Vec3f sightLineVec3 = { -1400.0f, -2600.0f, -800.0f }; + Vec3f sightLineVec4 = { -1400.0f, -1600.0f, 0.0f }; + Vec3f bodyLineVec = { 0.0, 0.0f, -600.0f }; EnSw* this = (EnSw*)thisx; - Vec3f sp3C = { 0.0f, 0.0f, 0.0f }; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; OPEN_DISPS(play->state.gfxCtx, "../z_en_sw.c", 2084); - if (PARAMS_GET_S(this->actor.params, 13, 3) != 0) { + if (ENSW_TYPE(this) != ENSW_SKULLWALLTULA) { switch (limbIndex) { case 23: *dList = object_st_DL_004788; @@ -974,20 +1135,22 @@ s32 EnSw_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* po } } + // Update sightlines for Skullwalltula if (limbIndex == 1) { - Matrix_MultVec3f(&sp7C, &this->unk_454); - Matrix_MultVec3f(&sp70, &this->unk_460); - Matrix_MultVec3f(&sp64, &this->unk_46C); - Matrix_MultVec3f(&sp58, &this->unk_478); - Matrix_MultVec3f(&sp4C, &this->unk_484); + Matrix_MultVec3f(&sightLineVec1, &this->sightLine1S); + Matrix_MultVec3f(&sightLineVec2, &this->sightLine2S); + Matrix_MultVec3f(&sightLineVec3, &this->sightLine3S); + Matrix_MultVec3f(&sightLineVec4, &this->sightLine4S); + Matrix_MultVec3f(&bodyLineVec, &this->bodyLineS); } if (limbIndex == 5) { - Matrix_MultVec3f(&sp3C, &this->actor.focus.pos); + Matrix_MultVec3f(&zeroVec, &this->actor.focus.pos); } + // The color is not set, but works fine if set. if (limbIndex == 4) { - gDPSetEnvColor(POLY_OPA_DISP++, this->unk_1F4.r, this->unk_1F4.g, this->unk_1F4.b, 0); + gDPSetEnvColor(POLY_OPA_DISP++, this->teethColor.r, this->teethColor.g, this->teethColor.b, 0); } Collider_UpdateSpheres(limbIndex, &this->collider); @@ -1000,23 +1163,30 @@ s32 EnSw_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* po void EnSw_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { } -void func_80B0EDB8(PlayState* play, Color_RGBA8* arg1, s16 arg2, s16 arg3) { - f32 temp_f2; +/** + * Skullwalltula: Set angry color (targeting and attack state) + * arg2 = 20, arg3 = 30 + */ +void EnSw_S_SetColor(PlayState* play, Color_RGBA8* color, s16 arg2, s16 arg3) { + f32 far; OPEN_DISPS(play->state.gfxCtx, "../z_en_sw.c", 2181); - temp_f2 = (11500.0f / arg3) * (arg3 - arg2); + far = (11500.0f / arg3) * (arg3 - arg2); - if (0.0f == temp_f2) { - temp_f2 = 11500; + if (0.0f == far) { + far = 11500; } - POLY_OPA_DISP = Gfx_SetFog2(POLY_OPA_DISP, arg1->r, arg1->g, arg1->b, arg1->a, 0, (s16)temp_f2); + POLY_OPA_DISP = Gfx_SetFog2(POLY_OPA_DISP, color->r, color->g, color->b, color->a, 0, (s16)far); CLOSE_DISPS(play->state.gfxCtx, "../z_en_sw.c", 2197); } -void func_80B0EEA4(PlayState* play) { +/** + * Skullwalltula: Set angry fog effect (targeting and attack state) + */ +void EnSw_S_SetFog(PlayState* play) { s32 pad; OPEN_DISPS(play->state.gfxCtx, "../z_en_sw.c", 2205); @@ -1028,22 +1198,22 @@ void func_80B0EEA4(PlayState* play) { void EnSw_Draw(Actor* thisx, PlayState* play) { EnSw* this = (EnSw*)thisx; - Color_RGBA8 sp30 = { 184, 0, 228, 255 }; + Color_RGBA8 angryColor = { 184, 0, 228, 255 }; - if (PARAMS_GET_S(this->actor.params, 13, 3) != 0) { + if (ENSW_TYPE(this) != ENSW_SKULLWALLTULA) { Matrix_RotateX(DEG_TO_RAD(-80), MTXMODE_APPLY); if (this->actor.colChkInfo.health != 0) { Matrix_Translate(0.0f, 0.0f, 200.0f, MTXMODE_APPLY); } func_8002EBCC(&this->actor, play, 0); - } else if (this->actionFunc == func_80B0E728) { - func_80B0EDB8(play, &sp30, 0x14, 0x1E); + } else if (this->actionFunc == EnSw_ActionS_TargetAttack) { + EnSw_S_SetColor(play, &angryColor, 20, 30); } Gfx_SetupDL_25Opa(play->state.gfxCtx); SkelAnime_DrawOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, EnSw_OverrideLimbDraw, EnSw_PostLimbDraw, this); - if (this->actionFunc == func_80B0E728) { - func_80B0EEA4(play); + if (this->actionFunc == EnSw_ActionS_TargetAttack) { + EnSw_S_SetFog(play); } } diff --git a/src/overlays/actors/ovl_En_Sw/z_en_sw.h b/src/overlays/actors/ovl_En_Sw/z_en_sw.h index 6a22eb7ba16..0dbe86d5eae 100644 --- a/src/overlays/actors/ovl_En_Sw/z_en_sw.h +++ b/src/overlays/actors/ovl_En_Sw/z_en_sw.h @@ -14,39 +14,39 @@ typedef struct EnSw { /* 0x0190 */ EnSwActionFunc actionFunc; /* 0x0194 */ ColliderJntSph collider; /* 0x01B4 */ ColliderJntSphElement colliderElements[1]; - /* 0x01F4 */ Color_RGBA8 unk_1F4; + /* 0x01F4 */ Color_RGBA8 teethColor; /* 0x01F8 */ Vec3s jointTable[30]; /* 0x02AC */ Vec3s morphTable[30]; - /* 0x0360 */ u8 unk_360; + /* 0x0360 */ u8 isAscendingGS; /* 0x0364 */ Vec3f unk_364; /* 0x0370 */ Vec3f unk_370; /* 0x037C */ Vec3f unk_37C; - /* 0x0388 */ s16 unk_388; - /* 0x038A */ s16 unk_38A; - /* 0x038C */ s16 unk_38C; - /* 0x038E */ s16 unk_38E; - /* 0x0390 */ s16 unk_390; - /* 0x0392 */ s16 unk_392; - /* 0x0394 */ s16 unk_394; - /* 0x0396 */ char unk_396[0x42]; + /* 0x0388 */ s16 breakTimerS; + /* 0x038A */ s16 staggerFallTimer; + /* 0x038C */ s16 rotateTimerGS; // Also timer for soil digging up effects for soil spawn Gold Skulltulas + /* 0x038E */ s16 breakTimerGS; + /* 0x0390 */ s16 ATColliderTimer; + /* 0x0392 */ s16 ACColliderTimer; + /* 0x0394 */ s16 deathTimer; + /* 0x0396 */ char unused_396[0x42]; /* 0x03D8 */ MtxF unk_3D8; - /* 0x0418 */ char unk_418[8]; - /* 0x0420 */ f32 unk_420; - /* 0x0424 */ char unk_424[0x8]; - /* 0x042C */ u8 unk_42C; - /* 0x0430 */ struct CollisionPoly* unk_430; - /* 0x0434 */ Vec3f unk_434; - /* 0x0440 */ s16 unk_440; - /* 0x0442 */ s16 unk_442; - /* 0x0444 */ s16 unk_444; - /* 0x0446 */ s16 unk_446; - /* 0x0448 */ Vec3f unk_448; - /* 0x0454 */ Vec3f unk_454; - /* 0x0460 */ Vec3f unk_460; - /* 0x046C */ Vec3f unk_46C; - /* 0x0478 */ Vec3f unk_478; - /* 0x0484 */ Vec3f unk_484; - /* 0x0490 */ char unk_490[0x48]; + /* 0x0418 */ char unused_418[8]; + /* 0x0420 */ f32 rotationSpeedGS; + /* 0x0424 */ char unused_424[0x8]; + /* 0x042C */ u8 unused_42C; + /* 0x0430 */ struct CollisionPoly* wallPolyS; + /* 0x0434 */ Vec3f unused_434; + /* 0x0440 */ s16 soundTimerS; + /* 0x0442 */ s16 engageTimerS; + /* 0x0444 */ s16 targetAngleS; + /* 0x0446 */ s16 unused_446; + /* 0x0448 */ Vec3f targetPosS; + /* 0x0454 */ Vec3f sightLine1S; + /* 0x0460 */ Vec3f sightLine2S; + /* 0x046C */ Vec3f sightLine3S; + /* 0x0478 */ Vec3f sightLine4S; + /* 0x0484 */ Vec3f bodyLineS; + /* 0x0490 */ char unused_490[0x48]; } EnSw; // size = 0x04D8 #endif diff --git a/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.c b/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.c index ca902bce1bd..78c12afc712 100644 --- a/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.c +++ b/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.c @@ -1,7 +1,7 @@ /* * File: z_obj_makekinsuta.c * Overlay: ovl_Obj_Makekinsuta - * Description: Skulltula Sprouting from Bean Spot + * Description: Spawns a Gold Skulltula Sprouting from Bean Spot */ #include "z_obj_makekinsuta.h" @@ -17,8 +17,8 @@ void ObjMakekinsuta_Init(Actor* thisx, PlayState* play); void ObjMakekinsuta_Update(Actor* thisx, PlayState* play); -void func_80B98320(ObjMakekinsuta* this, PlayState* play); -void ObjMakekinsuta_DoNothing(ObjMakekinsuta* this, PlayState* play); +void ObjMakekinsuta_Action_Wait(ObjMakekinsuta* this, PlayState* play); +void ObjMakekinsuta_Action_DoNothing(ObjMakekinsuta* this, PlayState* play); ActorProfile Obj_Makekinsuta_Profile = { /**/ ACTOR_OBJ_MAKEKINSUTA, @@ -35,6 +35,7 @@ ActorProfile Obj_Makekinsuta_Profile = { void ObjMakekinsuta_Init(Actor* thisx, PlayState* play) { ObjMakekinsuta* this = (ObjMakekinsuta*)thisx; + // Make sure params are set for soil Gold Skulltula if (PARAMS_GET_NOSHIFT(this->actor.params, 13, 2) == 0x4000) { PRINTF_COLOR_BLUE(); PRINTF(T("金スタ発生敵(arg_data %x)\n", "Gold Star Enemy(arg_data %x)\n"), this->actor.params); @@ -45,15 +46,16 @@ void ObjMakekinsuta_Init(Actor* thisx, PlayState* play) { "../z_obj_makekinsuta.c", 119); PRINTF_RST(); } - this->actionFunc = func_80B98320; + this->actionFunc = ObjMakekinsuta_Action_Wait; } -void func_80B98320(ObjMakekinsuta* this, PlayState* play) { - if (this->unk_152 != 0) { - if (this->timer >= 60 && !func_8002DEEC(GET_PLAYER(play))) { +void ObjMakekinsuta_Action_Wait(ObjMakekinsuta* this, PlayState* play) { + // bugDig is set to 1 by the digging bugs + if (this->bugDig != 0) { + if (this->timer >= 60 && !Player_IsDeadOrCutscene(GET_PLAYER(play))) { Actor_Spawn(&play->actorCtx, play, ACTOR_EN_SW, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0, (this->actor.params | 0x8000)); - this->actionFunc = ObjMakekinsuta_DoNothing; + this->actionFunc = ObjMakekinsuta_Action_DoNothing; } else { this->timer++; } @@ -62,7 +64,7 @@ void func_80B98320(ObjMakekinsuta* this, PlayState* play) { } } -void ObjMakekinsuta_DoNothing(ObjMakekinsuta* this, PlayState* play) { +void ObjMakekinsuta_Action_DoNothing(ObjMakekinsuta* this, PlayState* play) { } void ObjMakekinsuta_Update(Actor* thisx, PlayState* play) { diff --git a/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.h b/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.h index c6134808c52..41aebdf65d3 100644 --- a/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.h +++ b/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.h @@ -12,7 +12,7 @@ typedef struct ObjMakekinsuta { /* 0x000 */ Actor actor; /* 0x14C */ ObjMakekinsutaActionFunc actionFunc; /* 0x150 */ s16 timer; - /* 0x152 */ s16 unk_152; + /* 0x152 */ s16 bugDig; } ObjMakekinsuta; // size = 0x154 #endif From 3e14b818dec880394315862500aaf650dea803b9 Mon Sep 17 00:00:00 2001 From: djevangelia <263709373+djevangelia@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:28:03 +0200 Subject: [PATCH 2/4] Update src/overlays/actors/ovl_En_Sw/z_en_sw.c Co-authored-by: mzxrules --- src/overlays/actors/ovl_En_Sw/z_en_sw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overlays/actors/ovl_En_Sw/z_en_sw.c b/src/overlays/actors/ovl_En_Sw/z_en_sw.c index 9950c24f82c..ba39dc79a6a 100644 --- a/src/overlays/actors/ovl_En_Sw/z_en_sw.c +++ b/src/overlays/actors/ovl_En_Sw/z_en_sw.c @@ -641,7 +641,7 @@ void EnSw_ActionGS_Fly(EnSw* this, PlayState* play) { } // Landing - if (EnSw_GS_FindFloorPoly(this, play, true) == 1) { + if (EnSw_GS_FindFloorPoly(this, play, true) == true) { Actor_PlaySfx(&this->actor, NA_SE_EN_DODO_M_GND); // Hit ground sound EnSw_GS_SpawnLandDust(this, play, 8); this->actor.scale.x = 0.02f; From 9ce45cdd250b5a0b0d5ce941cc3c6c95cf5d84d3 Mon Sep 17 00:00:00 2001 From: djevangelia <263709373+djevangelia@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:31:14 +0200 Subject: [PATCH 3/4] Update include/save.h Co-authored-by: mzxrules --- include/save.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/save.h b/include/save.h index 8b66bfa12d0..afb48041058 100644 --- a/include/save.h +++ b/include/save.h @@ -494,8 +494,8 @@ typedef enum LinkAge { #define GET_GS_FLAGS(mapIndex) \ ((gSaveContext.save.info.gsFlags[(mapIndex) >> 2] & gGsFlagsMasks[(mapIndex) & 3]) >> gGsFlagsShifts[(mapIndex) & 3]) -#define SET_GS_FLAGS(mapIndex, id) \ - (gSaveContext.save.info.gsFlags[(mapIndex) >> 2] |= (id) << gGsFlagsShifts[(mapIndex) & 3]) +#define SET_GS_FLAGS(mapIndex, flags) \ + (gSaveContext.save.info.gsFlags[(mapIndex) >> 2] |= (flags) << gGsFlagsShifts[(mapIndex) & 3]) #define HIGH_SCORE(score) (gSaveContext.save.info.highScores[score]) From d7138efbd959a21261fca5734b09cec89a1463e3 Mon Sep 17 00:00:00 2001 From: djevangelia <263709373+djevangelia@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:39:24 +0200 Subject: [PATCH 4/4] Update src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.c Co-authored-by: mzxrules --- src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.c b/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.c index 78c12afc712..6c52b3d6119 100644 --- a/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.c +++ b/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.c @@ -17,8 +17,8 @@ void ObjMakekinsuta_Init(Actor* thisx, PlayState* play); void ObjMakekinsuta_Update(Actor* thisx, PlayState* play); -void ObjMakekinsuta_Action_Wait(ObjMakekinsuta* this, PlayState* play); -void ObjMakekinsuta_Action_DoNothing(ObjMakekinsuta* this, PlayState* play); +void ObjMakekinsuta_Wait(ObjMakekinsuta* this, PlayState* play); +void ObjMakekinsuta_DoNothing(ObjMakekinsuta* this, PlayState* play); ActorProfile Obj_Makekinsuta_Profile = { /**/ ACTOR_OBJ_MAKEKINSUTA,