/* * File: z_en_suttari.c * Overlay: ovl_En_Suttari * Description: Sakon */ #include "z_en_suttari.h" #include "overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h" #include "overlays/actors/ovl_En_Door/z_en_door.h" #include "overlays/actors/ovl_En_Elf/z_en_elf.h" #include "overlays/effects/ovl_Effect_Ss_Solder_Srch_Ball/z_eff_ss_solder_srch_ball.h" #define FLAGS (ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_CULLING_DISABLED) void EnSuttari_Init(Actor* thisx, PlayState* play); void EnSuttari_Destroy(Actor* thisx, PlayState* play); void EnSuttari_Update(Actor* thisx, PlayState* play); void EnSuttari_Draw(Actor* thisx, PlayState* play); void func_80BACA14(EnSuttari* this, PlayState* play); void func_80BACEE0(EnSuttari* this, PlayState* play); void func_80BAD004(EnSuttari* this, PlayState* play); void func_80BAD5F8(EnSuttari* this, PlayState* play); void func_80BAD7F8(EnSuttari* this, PlayState* play); void func_80BAD380(EnSuttari* this, PlayState* play); void func_80BACBB0(EnSuttari* this, PlayState* play); void func_80BADA9C(EnSuttari* this, PlayState* play); void func_80BADDB4(EnSuttari* this, PlayState* play); void func_80BAD130(EnSuttari* this, PlayState* play); void func_80BACD2C(EnSuttari* this, PlayState* play); void func_80BACE4C(EnSuttari* this, PlayState* play); void func_80BADD0C(EnSuttari* this, PlayState* play); void func_80BADA08(EnSuttari* this, PlayState* play); void func_80BAD2B4(EnSuttari* this, PlayState* play); void func_80BADE14(EnSuttari* this, PlayState* play); void func_80BADE8C(EnSuttari* this, PlayState* play); void func_80BADF3C(EnSuttari* this, PlayState* play); ActorProfile En_Suttari_Profile = { /**/ ACTOR_EN_SUTTARI, /**/ ACTORCAT_NPC, /**/ FLAGS, /**/ OBJECT_BOJ, /**/ sizeof(EnSuttari), /**/ EnSuttari_Init, /**/ EnSuttari_Destroy, /**/ EnSuttari_Update, /**/ EnSuttari_Draw, }; typedef enum SuttariAnimation { /* -1 */ SUTTARI_ANIM_NONE = -1, /* 0 */ SUTTARI_ANIM_0, /* 1 */ SUTTARI_ANIM_1, /* 2 */ SUTTARI_ANIM_2, /* 3 */ SUTTARI_ANIM_3, /* 4 */ SUTTARI_ANIM_4, /* 5 */ SUTTARI_ANIM_5, /* 6 */ SUTTARI_ANIM_6, /* 7 */ SUTTARI_ANIM_7, /* 8 */ SUTTARI_ANIM_8, /* 9 */ SUTTARI_ANIM_MAX } SuttariAnimation; static AnimationInfo sAnimationInfo[SUTTARI_ANIM_MAX] = { { &object_boj_Anim_00071C, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, 0.0f }, // SUTTARI_ANIM_0 { &object_boj_Anim_0128F4, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, 0.0f }, // SUTTARI_ANIM_1 { &object_boj_Anim_011F84, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, 0.0f }, // SUTTARI_ANIM_2 { &object_boj_Anim_012E84, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -6.0f }, // SUTTARI_ANIM_3 { &object_boj_Anim_01139C, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, 0.0f }, // SUTTARI_ANIM_4 { &object_boj_Anim_00071C, 2.0f, 0.0f, 0.0f, ANIMMODE_LOOP, 0.0f }, // SUTTARI_ANIM_5 { &object_boj_Anim_011F84, 2.0f, 0.0f, 0.0f, ANIMMODE_LOOP, 0.0f }, // SUTTARI_ANIM_6 { &object_boj_Anim_011C38, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, 0.0f }, // SUTTARI_ANIM_7 { &object_boj_Anim_010BDC, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, 0.0f }, // SUTTARI_ANIM_8 }; static ColliderCylinderInit sCylinderInit = { { COL_MATERIAL_NONE, AT_NONE, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_1, COLSHAPE_CYLINDER, }, { ELEM_MATERIAL_UNK1, { 0x00000000, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, ATELEM_NONE | ATELEM_SFX_NORMAL, ACELEM_ON, OCELEM_ON, }, { 18, 64, 0, { 0, 0, 0 } }, }; static CollisionCheckInfoInit2 sColChkInfoInit2 = { 0, 18, 64, 0, MASS_IMMOVABLE, }; static DamageTable sDamageTable = { /* Deku Nut */ DMG_ENTRY(1, 0x0), /* Deku Stick */ DMG_ENTRY(1, 0x0), /* Horse trample */ DMG_ENTRY(1, 0x0), /* Explosives */ DMG_ENTRY(1, 0xE), /* Zora boomerang */ DMG_ENTRY(1, 0xE), /* Normal arrow */ DMG_ENTRY(1, 0xE), /* UNK_DMG_0x06 */ DMG_ENTRY(1, 0x0), /* Hookshot */ DMG_ENTRY(1, 0xE), /* Goron punch */ DMG_ENTRY(1, 0xF), /* Sword */ DMG_ENTRY(1, 0xF), /* Goron pound */ DMG_ENTRY(1, 0x0), /* Fire arrow */ DMG_ENTRY(1, 0xE), /* Ice arrow */ DMG_ENTRY(1, 0xE), /* Light arrow */ DMG_ENTRY(1, 0xE), /* Goron spikes */ DMG_ENTRY(1, 0x0), /* Deku spin */ DMG_ENTRY(1, 0x0), /* Deku bubble */ DMG_ENTRY(1, 0x0), /* Deku launch */ DMG_ENTRY(1, 0x0), /* UNK_DMG_0x12 */ DMG_ENTRY(1, 0x0), /* Zora barrier */ DMG_ENTRY(1, 0x0), /* Normal shield */ DMG_ENTRY(1, 0x0), /* Light ray */ DMG_ENTRY(1, 0x0), /* Thrown object */ DMG_ENTRY(1, 0x0), /* Zora punch */ DMG_ENTRY(1, 0xF), /* Spin attack */ DMG_ENTRY(1, 0xF), /* Sword beam */ DMG_ENTRY(1, 0xE), /* Normal Roll */ DMG_ENTRY(1, 0x0), /* UNK_DMG_0x1B */ DMG_ENTRY(1, 0x0), /* UNK_DMG_0x1C */ DMG_ENTRY(1, 0x0), /* Unblockable */ DMG_ENTRY(1, 0x0), /* UNK_DMG_0x1E */ DMG_ENTRY(1, 0x0), /* Powder Keg */ DMG_ENTRY(1, 0xE), }; static TrackOptionsSet sTrackOptions = { { 0xFA0, 4, 1, 3 }, { 0x1770, 4, 1, 6 }, { 0xFA0, 4, 1, 3 }, { 0x1770, 4, 1, 6 }, }; #include "src/overlays/actors/ovl_En_Suttari/scheduleScripts.schl.inc" static s32 D_80BAE8F8[] = { -1, -1, -1, -1, -1, -1, 1, 2, 0, 3, 1, 2, 0, 0, 3, 0, }; void EnSuttari_UpdateCollider(EnSuttari* this, PlayState* play) { 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; if (this->flags1 & 0x40) { if (this->collider.base.acFlags & AC_HIT) { this->collider.base.acFlags &= ~AC_HIT; if (this->actor.colChkInfo.damageEffect == 0xF) { this->flags1 |= 0x100; this->flags1 &= ~0x40; Enemy_StartFinishingBlow(play, &this->actor); } else if (this->actor.colChkInfo.damageEffect == 0xE) { this->flags1 |= 0x200; this->flags1 &= ~0x40; } } } CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); if ((this->flags1 & 1) && (this->actionFunc != func_80BADE14)) { Actor_UpdateBgCheckInfo(play, &this->actor, 35.0f, 30.0f, 30.0f, UPDBGCHECKINFO_FLAG_1 | UPDBGCHECKINFO_FLAG_4); } else { Actor_UpdateBgCheckInfo(play, &this->actor, 35.0f, 30.0f, 30.0f, UPDBGCHECKINFO_FLAG_4); } } Actor* EnSuttari_GetActorById(PlayState* play, s16 actorId) { Actor* actor = play->actorCtx.actorLists[ACTORCAT_NPC].first; while (actor != NULL) { if (actor->id == actorId) { return actor; } actor = actor->next; } return NULL; } void EnSuttari_TriggerTransition(PlayState* play, u16 entrance) { play->nextEntrance = entrance; play->transitionType = TRANS_TYPE_64; gSaveContext.nextTransitionType = TRANS_TYPE_64; play->transitionTrigger = TRANS_TRIGGER_START; } void EnSuttari_AdvanceTime(void) { gSaveContext.save.time = CURRENT_TIME + (u16)R_TIME_SPEED; gSaveContext.save.time = CURRENT_TIME + (u16)((void)0, gSaveContext.save.timeSpeedOffset); } s32 func_80BAA904(EnSuttari* this, PlayState* play) { Vec3f pos; s32 pad; CollisionPoly* poly; pos.x = (Math_SinS(this->actor.world.rot.y) * 50.0f) + this->actor.world.pos.x; pos.y = this->actor.world.pos.y + 100.0f; pos.z = (Math_CosS(this->actor.world.rot.y) * 50.0f) + this->actor.world.pos.z; if (BgCheck_EntityRaycastFloor2(play, &play->colCtx, &poly, &pos) > -500.0f) { return false; } return true; } void func_80BAA9B4(EnSuttari* this) { s16 curFrame = this->skelAnime.curFrame; s16 endFrame = Animation_GetLastFrame(sAnimationInfo[this->animIndex].animation); switch (this->textId) { case 0x1454: case 0x1455: case 0x1456: case 0x1457: case 0x1458: case 0x1459: case 0x145A: case 0x145B: if ((this->animIndex != SUTTARI_ANIM_8) && (curFrame == endFrame)) { this->animIndex = SUTTARI_ANIM_8; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); } break; default: if ((this->animIndex != SUTTARI_ANIM_1) && (curFrame == endFrame)) { this->animIndex = SUTTARI_ANIM_1; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); } break; } } void func_80BAAA94(EnSuttari* this) { switch (this->textId) { case 0x29E5: this->animIndex = SUTTARI_ANIM_1; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); break; case 0x29E9: this->enFsn->flags |= ENFSN_ANGRY; break; case 0x29EA: this->enFsn->flags |= ENFSN_CALM_DOWN; break; case 0x29EB: this->enFsn->flags |= ENFSN_OFFER_FINAL_PRICE; break; case 0x29EC: this->animIndex = SUTTARI_ANIM_7; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); break; case 0x29ED: this->animIndex = SUTTARI_ANIM_1; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); break; default: break; } } void func_80BAAB78(EnSuttari* this, PlayState* play) { if (this->flags1 & 4) { switch (this->textId) { case 0: this->flags1 |= 0x20; this->csIdIndex = 0; this->textId = 0x29E5; break; case 0x29E5: CutsceneManager_Stop(this->csIdList[this->csIdIndex]); this->csIdIndex = 1; this->textId = 0x29E6; break; case 0x29E6: CutsceneManager_Stop(this->csIdList[this->csIdIndex]); this->csIdIndex = 0; this->textId = 0x29E7; break; case 0x29E7: CutsceneManager_Stop(this->csIdList[this->csIdIndex]); this->csIdIndex = 1; this->textId = 0x29E8; break; case 0x29E8: CutsceneManager_Stop(this->csIdList[this->csIdIndex]); this->csIdIndex = 0; this->textId = 0x29E9; break; case 0x29E9: CutsceneManager_Stop(this->csIdList[this->csIdIndex]); this->csIdIndex = 1; this->textId = 0x29EA; break; case 0x29EA: CutsceneManager_Stop(this->csIdList[this->csIdIndex]); this->csIdIndex = 0; this->textId = 0x29EB; break; case 0x29EB: CutsceneManager_Stop(this->csIdList[this->csIdIndex]); this->csIdIndex = 1; this->textId = 0x29EC; break; case 0x29EC: CutsceneManager_Stop(this->csIdList[this->csIdIndex]); this->csIdIndex = 0; this->textId = 0x29ED; break; case 0x29ED: CutsceneManager_Stop(this->csIdList[this->csIdIndex]); this->flags1 |= 0x400; this->csIdIndex = 1; this->textId = 0x29EE; break; default: break; } if (CutsceneManager_GetCurrentCsId() == CS_ID_GLOBAL_TALK) { CutsceneManager_Stop(CS_ID_GLOBAL_TALK); } CutsceneManager_Queue(this->csIdList[this->csIdIndex]); this->actionFunc = func_80BADD0C; } else if (this->flags1 & 1) { switch (this->textId) { case 0: if (CHECK_WEEKEVENTREG(WEEKEVENTREG_81_01)) { this->textId = 0x1455; ((EnElf*)GET_PLAYER(play)->tatlActor)->unk_264 |= 8; this->flags2 |= 1; } else { this->textId = 0x1450; } break; case 0x1450: this->textId = 0x1451; break; case 0x1451: if (this->flags1 & 0x800) { this->flags1 &= ~0x800; this->textId = 0x1453; } else { this->flags1 |= 0x400; this->textId = 0x1452; SET_WEEKEVENTREG(WEEKEVENTREG_81_01); } break; case 0x1453: this->flags1 |= 0x400; SET_WEEKEVENTREG(WEEKEVENTREG_81_01); ((EnElf*)GET_PLAYER(play)->tatlActor)->unk_264 |= 8; this->flags2 |= 1; this->textId = 0x1454; break; case 0x1455: this->textId = 0x1456; break; case 0x1456: this->textId = 0x1457; break; case 0x1457: this->textId = 0x1458; break; case 0x1458: this->textId = 0x1459; break; case 0x1459: this->textId = 0x145A; break; case 0x145A: this->flags1 |= 0x400; this->textId = 0x145B; break; default: break; } } Message_StartTextbox(play, this->textId, &this->actor); } void func_80BAAF1C(EnSuttari* this) { s16 curFrame = this->skelAnime.curFrame; s16 endFrame = Animation_GetLastFrame(sAnimationInfo[this->animIndex].animation); if (this->animIndex == SUTTARI_ANIM_5) { this->animIndex = SUTTARI_ANIM_3; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); } else if ((this->animIndex == SUTTARI_ANIM_3) && (curFrame == endFrame)) { this->animIndex = SUTTARI_ANIM_6; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); this->flags1 &= ~0x100; } } void func_80BAAFDC(EnSuttari* this, PlayState* play) { if ((this->actor.xzDistToPlayer < 500.0f) && (this->actor.playerHeightRel < 100.0f)) { Vec3f effectVelocityOffset = { 0.0f, 0.0f, 0.0f }; Vec3f effectPos; Vec3f effectVelocity; Math_Vec3f_Copy(&effectPos, &this->actor.world.pos); effectPos.x += Math_SinS(this->actor.world.rot.y + this->unk3F4) * 10.0f; effectPos.y += 60.0f; effectPos.z += Math_CosS(this->actor.world.rot.y + this->unk3F4) * 10.0f; Matrix_Push(); Matrix_RotateYS(this->actor.shape.rot.y, MTXMODE_NEW); effectVelocityOffset.z = 20.0f; Matrix_MultVec3f(&effectVelocityOffset, &effectVelocity); Matrix_Pop(); if (!this->playerDetected) { EffectSsSolderSrchBall_Spawn(play, &effectPos, &effectVelocity, &gZeroVec3f, 50, &this->playerDetected, SOLDERSRCHBALL_INVISIBLE); } if (this->playerDetected == true) { Audio_PlaySfx(NA_SE_SY_FOUND); this->playerDetected = false; this->actor.speed = 0.0f; if (this->unk1F4[0] != 0) { this->unk1F4[0]--; } SET_WEEKEVENTREG(WEEKEVENTREG_61_08); this->unk3F6 = 20; this->actionFunc = func_80BADE8C; } } } void func_80BAB1A0(EnSuttari* this, PlayState* play) { if ((this->actor.xzDistToPlayer < 500.0f) && (this->actor.playerHeightRel < 100.0f)) { Vec3f effectVelocityOffset = { 0.0f, 0.0f, 0.0f }; Vec3f effectPos; Vec3f effectVelocity; Math_Vec3f_Copy(&effectPos, &this->actor.world.pos); effectPos.x += Math_SinS(this->actor.world.rot.y + this->unk3F4) * 350.0f; effectPos.y += 60.0f; effectPos.z += Math_CosS(this->actor.world.rot.y + this->unk3F4) * 350.0f; Matrix_Push(); Matrix_RotateYS(this->actor.shape.rot.y, MTXMODE_NEW); effectVelocityOffset.z = 20.0f; Matrix_MultVec3f(&effectVelocityOffset, &effectVelocity); Matrix_Pop(); if (!this->playerDetected) { EffectSsSolderSrchBall_Spawn(play, &effectPos, &effectVelocity, &gZeroVec3f, 50, &this->playerDetected, SOLDERSRCHBALL_INVISIBLE); } if (this->playerDetected == true) { Audio_PlaySfx(NA_SE_SY_FOUND); this->playerDetected = false; this->actor.speed = 0.0f; if (this->unk1F4[0] != 0) { this->unk1F4[0]--; } SET_WEEKEVENTREG(WEEKEVENTREG_61_08); this->unk3F6 = 20; this->actionFunc = func_80BADE8C; } else { func_80BAAFDC(this, play); } } } void func_80BAB374(EnSuttari* this, PlayState* play) { s16 curFrame = this->skelAnime.curFrame; Vec3f sp38; if ((curFrame % 3) == 0) { sp38.x = Rand_CenteredFloat(15.0f) + this->actor.world.pos.x; sp38.y = this->actor.world.pos.y; sp38.z = Rand_CenteredFloat(15.0f) + this->actor.world.pos.z; Actor_SpawnFloorDustRing(play, &this->actor, &sp38, 10.0f, 0, 2.0f, 0, 0, false); } } void func_80BAB434(EnSuttari* this) { this->unk3F2 = Math_SinS(this->unk3F4) * 60.0f * (0x10000 / 360.0f); this->unk3F4 += 0x400; } void EnSuttari_GetPaths(EnSuttari* this, PlayState* play) { this->paths[0] = SubS_GetPathByIndex(play, ENSUTTARI_GET_PATH_INDEX(&this->actor), ENSUTTARI_PATH_INDEX_NONE); //! @bug: the additional path shouldn't use `ENSUTTARI_PATH_INDEX_NONE`, but `ADDITIONAL_PATH_INDEX_NONE` this->paths[1] = SubS_GetPathByIndex(play, this->paths[0]->additionalPathIndex, ENSUTTARI_PATH_INDEX_NONE); } void func_80BAB4F0(EnSuttari* this, PlayState* play) { Player* player = GET_PLAYER(play); Vec3f point; if (this->flags1 & 0x80) { EnSuttari_UpdateCollider(this, play); } SkelAnime_Update(&this->skelAnime); if (!(this->flags1 & 4) && (Player_GetMask(play) != PLAYER_MASK_STONE)) { if (SubS_AngleDiffLessEqual(this->actor.shape.rot.y, 0x36B0, this->actor.yawTowardsPlayer)) { point.x = player->actor.world.pos.x; point.y = player->bodyPartsPos[PLAYER_BODYPART_HEAD].y + 3.0f; point.z = player->actor.world.pos.z; SubS_TrackPoint(&point, &this->actor.focus.pos, &this->actor.shape.rot, &this->trackTarget, &this->headRot, &this->torsoRot, &sTrackOptions); } else { Math_SmoothStepToS(&this->trackTarget.x, 0, 4, 0x3E8, 1); Math_SmoothStepToS(&this->trackTarget.y, 0, 4, 0x3E8, 1); Math_SmoothStepToS(&this->headRot.x, 0, 4, 0x3E8, 1); Math_SmoothStepToS(&this->headRot.y, 0, 4, 0x3E8, 1); Math_SmoothStepToS(&this->torsoRot.x, 0, 4, 0x3E8, 1); Math_SmoothStepToS(&this->torsoRot.y, 0, 4, 0x3E8, 1); } } SubS_UpdateFidgetTables(play, this->fidgetTableY, this->fidgetTableZ, OBJECT_BOJ_LIMB_MAX); } s16 EnSuttari_GetDistSqAndOrient(Path* path, s32 index, Vec3f* pos, f32* distSq) { Vec3s* points; f32 diffX; f32 diffZ; if (path != NULL) { points = Lib_SegmentedToVirtual(path->points); points = &points[index]; diffX = points->x - pos->x; diffZ = points->z - pos->z; } else { diffX = 0.0f; diffZ = 0.0f; } *distSq = SQ(diffX) + SQ(diffZ); return RAD_TO_BINANG(Math_Atan2F_XY(diffZ, diffX)); } s32 EnSuttari_HasReachedPointForward(EnSuttari* this, Path* path, s32 pointIndex) { Vec3s* points = Lib_SegmentedToVirtual(path->points); s32 count = path->count; s32 index = pointIndex; s32 reached = false; f32 diffX; f32 diffZ; f32 px; f32 pz; f32 d; Vec3f point; Math_Vec3s_ToVec3f(&point, &points[index]); if (index == 0) { diffX = points[1].x - points[0].x; diffZ = points[1].z - points[0].z; } else if (index == (count - 1)) { diffX = points[count - 1].x - points[count - 2].x; diffZ = points[count - 1].z - points[count - 2].z; } else { diffX = points[index + 1].x - points[index - 1].x; diffZ = points[index + 1].z - points[index - 1].z; } Math3D_RotateXZPlane(&point, RAD_TO_BINANG(Math_FAtan2F(diffX, diffZ)), &px, &pz, &d); if (((px * this->actor.world.pos.x) + (pz * this->actor.world.pos.z) + d) > 0.0f) { reached = true; } return reached; } s32 EnSuttari_HasReachedPointReverse(EnSuttari* this, Path* path, s32 pointIndex) { Vec3s* points = Lib_SegmentedToVirtual(path->points); s32 count = path->count; s32 index = pointIndex; s32 reached = false; f32 diffX; f32 diffZ; f32 px; f32 pz; f32 d; Vec3f point; Math_Vec3s_ToVec3f(&point, &points[index]); if (index == 0) { diffX = points[0].x - points[1].x; diffZ = points[0].z - points[1].z; } else if (index == (count - 1)) { diffX = points[count - 2].x - points[count - 1].x; diffZ = points[count - 2].z - points[count - 1].z; } else { diffX = points[index - 1].x - points[index + 1].x; diffZ = points[index - 1].z - points[index + 1].z; } Math3D_RotateXZPlane(&point, RAD_TO_BINANG(Math_FAtan2F(diffX, diffZ)), &px, &pz, &d); if (((px * this->actor.world.pos.x) + (pz * this->actor.world.pos.z) + d) > 0.0f) { reached = true; } return reached; } void func_80BABA90(EnSuttari* this, s32 arg1, u8 arg2) { s16 target; f32 dist; if (this->paths[arg1] != NULL) { target = EnSuttari_GetDistSqAndOrient(this->paths[arg1], this->unk1F4[arg1], &this->actor.world.pos, &dist); if (this->actor.bgCheckFlags & BGCHECKFLAG_WALL) { if (arg2 == 2) { this->unk1F4[arg1] = -0x63; } else { target = this->actor.wallYaw; } } Math_SmoothStepToS(&this->actor.world.rot.y, target, 4, 0x3E8, 1); this->actor.shape.rot.y = this->actor.world.rot.y; if (EnSuttari_HasReachedPointForward(this, this->paths[arg1], this->unk1F4[arg1])) { if (this->unk1F4[arg1] >= (this->paths[arg1]->count - 1)) { if (arg2 == 0) { this->unk1F4[arg1] = 0; } else { this->unk1F4[arg1] = -0x63; } } else { this->unk1F4[arg1]++; } } } } void func_80BABB90(EnSuttari* this, s32 arg1) { s16 target; f32 sp30; if (this->paths[arg1] != NULL) { target = EnSuttari_GetDistSqAndOrient(this->paths[arg1], this->unk1F4[arg1], &this->actor.world.pos, &sp30); if (this->actor.bgCheckFlags & BGCHECKFLAG_WALL) { target = this->actor.wallYaw; } Math_SmoothStepToS(&this->actor.world.rot.y, target, 1, 0xBB8, 0); this->actor.shape.rot.y = this->actor.world.rot.y; if (EnSuttari_HasReachedPointReverse(this, this->paths[arg1], this->unk1F4[arg1])) { if (this->unk1F4[arg1] <= 0) { this->unk1F4[arg1] = -0x63; } else { this->unk1F4[arg1]--; } } } } s32 func_80BABC48(EnSuttari* this, PlayState* play, ScheduleOutput* scheduleOutput) { u16 sp26 = SCRIPT_TIME_NOW; u16 phi_a0; u8 pathIndex = ENSUTTARI_GET_PATH_INDEX(&this->actor); u16 tmp; if (D_80BAE8F8[scheduleOutput->result] >= 0) { this->timePath = SubS_GetAdditionalPath(play, pathIndex, D_80BAE8F8[scheduleOutput->result]); } if (this->timePath == NULL) { return 0; } if ((this->scheduleResult != 0) && (this->scheduleResult < 0xC) && (this->timePathTimeSpeed >= 0)) { phi_a0 = sp26; } else { phi_a0 = scheduleOutput->time0; } if (scheduleOutput->time1 < phi_a0) { this->timePathTotalTime = (phi_a0 - scheduleOutput->time1) + 0xFFFF; } else { this->timePathTotalTime = scheduleOutput->time1 - phi_a0; } this->timePathElapsedTime = sp26 - phi_a0; tmp = phi_a0 = this->timePath->count - (SUBS_TIME_PATHING_ORDER - 1); this->unk42C = 0; this->timePathWaypointTime = this->timePathTotalTime / tmp; this->timePathWaypoint = (this->timePathElapsedTime / this->timePathWaypointTime) + (SUBS_TIME_PATHING_ORDER - 1); this->unk430 = 0; return 1; } s32 func_80BABDD8(EnSuttari* this, PlayState* play, ScheduleOutput* scheduleOutput) { s32 pad; EnDoor* door; u8 pathIndex = ENSUTTARI_GET_PATH_INDEX(&this->actor); u16 sp44 = SCRIPT_TIME_NOW; Vec3f sp38; Vec3f sp2C; Vec3s* sp28; s32 sp24; if ((this->scheduleResult == 10) || (this->scheduleResult == 11) || (this->scheduleResult == 2)) { return false; } door = (EnDoor*)SubS_FindNearestActor(&this->actor, play, ACTORCAT_DOOR, ACTOR_EN_DOOR); sp24 = D_80BAE8F8[scheduleOutput->result]; if ((door != NULL) && (sp24 >= 0)) { this->timePath = SubS_GetAdditionalPath(play, pathIndex, sp24); } if ((door == NULL) || (this->timePath == NULL)) { return false; } sp28 = Lib_SegmentedToVirtual(this->timePath->points); Math_Vec3s_ToVec3f(&sp38, &sp28[0]); Math_Vec3s_ToVec3f(&sp2C, &sp28[1]); this->unk434 = sp44 - scheduleOutput->time0; this->unk436 = scheduleOutput->time1 - scheduleOutput->time0; if ((scheduleOutput->result != 10) && (scheduleOutput->result != 11)) { door->openTimer = 75; } Math_Vec3f_Copy(&this->unk438, &sp38); Math_Vec3f_Copy(&this->unk444, &sp2C); this->actor.world.rot.y = Math_Vec3f_Yaw(&sp38, &sp2C); this->actor.world.pos = sp38; return true; } s32 func_80BABF64(EnSuttari* this, PlayState* play, ScheduleOutput* scheduleOutput) { s32 ret; switch (scheduleOutput->result) { case 15: case 14: case 13: case 12: ret = func_80BABC48(this, play, scheduleOutput); break; case 11: case 10: case 9: case 8: case 7: case 6: ret = func_80BABDD8(this, play, scheduleOutput); break; case 5: case 4: case 3: case 2: case 1: ret = true; break; default: ret = false; break; } return ret; } s32 func_80BABFD4(EnSuttari* this, PlayState* play) { f32 knots[265]; Vec3f sp70; Vec3f sp64; Vec3f timePathTargetPos; s32 sp54 = 0; s32 sp50 = 0; s32 pad; SubS_TimePathing_FillKnots(knots, SUBS_TIME_PATHING_ORDER, this->timePath->count + SUBS_TIME_PATHING_ORDER); if (this->unk42C == 0) { timePathTargetPos = gZeroVec3f; SubS_TimePathing_Update(this->timePath, &this->timePathProgress, &this->timePathElapsedTime, this->timePathWaypointTime, this->timePathTotalTime, &this->timePathWaypoint, knots, &timePathTargetPos, this->timePathTimeSpeed); SubS_TimePathing_ComputeInitialY(play, this->timePath, this->timePathWaypoint, &timePathTargetPos); this->actor.world.pos.y = timePathTargetPos.y; this->unk42C = 1; } else { timePathTargetPos = this->timePathTargetPos; } this->actor.world.pos.x = timePathTargetPos.x; this->actor.world.pos.z = timePathTargetPos.z; if (SubS_InCsMode(play)) { sp54 = this->timePathElapsedTime; sp50 = this->timePathWaypoint; timePathTargetPos = this->actor.world.pos; } this->timePathTargetPos = gZeroVec3f; if (SubS_TimePathing_Update(this->timePath, &this->timePathProgress, &this->timePathElapsedTime, this->timePathWaypointTime, this->timePathTotalTime, &this->timePathWaypoint, knots, &this->timePathTargetPos, this->timePathTimeSpeed)) { this->unk430 = 1; } else { sp70 = this->actor.world.pos; sp64 = this->timePathTargetPos; this->actor.world.rot.y = Math_Vec3f_Yaw(&sp70, &sp64); } if (SubS_InCsMode(play)) { this->timePathElapsedTime = sp54; this->timePathWaypoint = sp50; this->timePathTargetPos = timePathTargetPos; } return 0; } s32 func_80BAC220(EnSuttari* this, PlayState* play) { Vec3f sp3C; Vec3f sp30; s32 tmp2; f32 tmp; if (this->unk434 < 0) { this->unk434 = 0; } else { tmp2 = (this->unk436 < this->unk434) ? this->unk436 : this->unk434; this->unk434 = tmp2; } tmp = Math_Vec3f_DistXZ(&this->unk438, &this->unk444) / this->unk436; sp3C.x = sp3C.y = 0.0f; sp3C.z = this->unk434 * tmp; Lib_Vec3f_TranslateAndRotateY(&this->unk438, this->actor.world.rot.y, &sp3C, &sp30); this->actor.world.pos = sp30; this->unk434 += this->timePathTimeSpeed; return 0; } void func_80BAC2FC(EnSuttari* this, PlayState* play) { Player* player = GET_PLAYER(play); s16 curFrame = this->skelAnime.curFrame; s16 endFrame = Animation_GetLastFrame(sAnimationInfo[this->animIndex].animation); switch (this->scheduleResult) { case 12: case 13: this->flags1 |= 0x80; func_80BABFD4(this, play); break; case 15: if ((this->animIndex == SUTTARI_ANIM_1) && (curFrame == endFrame)) { this->animIndex = SUTTARI_ANIM_2; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); } if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_83_04) && !(this->flags1 & 0x1000)) { if (CutsceneManager_IsNext(this->csIdList[0])) { CutsceneManager_Start(this->csIdList[0], &this->actor); if (!(player->stateFlags1 & PLAYER_STATE1_10000000)) { this->flags2 |= 0x10; player->stateFlags1 |= PLAYER_STATE1_10000000; } this->flags1 |= 0x1000; this->flags2 |= 2; } else { CutsceneManager_Queue(this->csIdList[0]); } } func_80BABFD4(this, play); break; case 6: case 8: this->flags1 |= 0x80; func_80BAC220(this, play); break; case 10: this->flags1 |= 0x80; func_80BAC220(this, play); break; case 9: case 11: this->flags1 |= 0x80; this->flags1 |= 0x20; this->enFsn->flags &= ~ENFSN_HAGGLE; if (this->animIndex != SUTTARI_ANIM_2) { this->animIndex = SUTTARI_ANIM_2; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); } func_80BAC220(this, play); break; case 7: this->flags1 |= 0x20; this->flags1 |= 0x80; func_80BAC220(this, play); break; case 14: this->flags1 |= 0x20; this->flags1 |= 0x80; func_80BABFD4(this, play); break; case 1: Actor_Kill(&this->actor); break; case 2: if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_81_04)) { this->flags1 |= 0x80; this->actor.world.pos.x = -16.0f; this->actor.world.pos.z = -16.0f; this->actor.world.pos.y = 12.0f; this->enFsn->flags |= ENFSN_HAGGLE; this->actionFunc = func_80BADA08; } break; case 4: if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_RECOVERED_STOLEN_BOMB_BAG)) { if ((this->animIndex == SUTTARI_ANIM_2) || (this->animIndex == SUTTARI_ANIM_1)) { this->animIndex = SUTTARI_ANIM_5; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); } this->flags1 |= 0x10; if (this->flags2 & 2) { this->flags2 &= ~2; } if (!(player->stateFlags1 & PLAYER_STATE1_10000000)) { this->flags2 |= 0x10; player->stateFlags1 |= PLAYER_STATE1_10000000; } this->textId = 0x2A30; Message_StartTextbox(play, this->textId, &this->actor); this->actionFunc = func_80BAD2B4; } break; default: break; } Math_ApproachS(&this->actor.shape.rot.y, this->actor.world.rot.y, 4, 0x1554); } void func_80BAC6E8(EnSuttari* this, PlayState* play) { ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); SkelAnime_InitFlex(play, &this->skelAnime, &object_boj_Skel_00C240, &object_boj_Anim_00071C, this->jointTable, this->morphTable, OBJECT_BOJ_LIMB_MAX); this->actor.draw = EnSuttari_Draw; this->actor.flags |= ACTOR_FLAG_ATTENTION_ENABLED; if (play->sceneId == SCENE_IKANA) { this->flags1 |= 1; if ((gSaveContext.save.day == 1) || (gSaveContext.save.day == 2)) { this->animIndex = SUTTARI_ANIM_2; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); this->flags1 |= 0x80; this->actionFunc = func_80BACA14; return; } else if ((gSaveContext.save.day == 3) && (CURRENT_TIME <= CLOCK_TIME(19, 0)) && !CHECK_WEEKEVENTREG(WEEKEVENTREG_61_08) && !CHECK_WEEKEVENTREG(WEEKEVENTREG_RECOVERED_STOLEN_BOMB_BAG) && CHECK_WEEKEVENTREG(WEEKEVENTREG_KAFEI_ENTRUSTED_LINK)) { this->animIndex = SUTTARI_ANIM_2; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); this->actionFunc = func_80BACEE0; return; } } else if (play->sceneId == SCENE_BACKTOWN) { if ((CURRENT_TIME >= CLOCK_TIME(0, 20)) && (CURRENT_TIME < CLOCK_TIME(6, 0))) { Actor_Kill(&this->actor); } if ((gSaveContext.save.entrance == ENTRANCE(NORTH_CLOCK_TOWN, 7)) || CHECK_WEEKEVENTREG(WEEKEVENTREG_58_40)) { Actor_Kill(&this->actor); } this->csIdList[0] = this->actor.csId; this->csIdList[1] = CutsceneManager_GetAdditionalCsId(this->csIdList[0]); this->flags1 |= 0x80; this->flags1 |= 8; this->animIndex = SUTTARI_ANIM_1; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); this->actionFunc = func_80BAD004; return; } else if (play->sceneId == SCENE_ICHIBA) { if (CHECK_WEEKEVENTREG(WEEKEVENTREG_RECOVERED_STOLEN_BOMB_BAG)) { Actor_Kill(&this->actor); return; } this->animIndex = SUTTARI_ANIM_0; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); this->flags1 |= 2; this->actionFunc = func_80BAD5F8; return; } else if (play->sceneId == SCENE_AYASHIISHOP) { if (CHECK_WEEKEVENTREG(WEEKEVENTREG_RECOVERED_STOLEN_BOMB_BAG)) { Actor_Kill(&this->actor); return; } this->animIndex = SUTTARI_ANIM_0; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); this->csIdList[0] = this->actor.csId; this->csIdList[1] = CutsceneManager_GetAdditionalCsId(this->csIdList[0]); this->flags1 |= 4; this->actionFunc = func_80BAD7F8; return; } else { this->flags1 |= 8; this->flags1 |= 0x10; this->actionFunc = func_80BACA14; return; } Actor_Kill(&this->actor); } void func_80BACA14(EnSuttari* this, PlayState* play) { Player* player = GET_PLAYER(play); if ((this->animIndex == SUTTARI_ANIM_1) || (this->animIndex == SUTTARI_ANIM_8)) { this->animIndex = SUTTARI_ANIM_2; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); } func_80BABA90(this, 0, 0); func_80BAB434(this); if ((player->transformation == PLAYER_FORM_GORON) || (player->transformation == PLAYER_FORM_ZORA)) { if ((this->actor.playerHeightRel < 60.0f) && (this->actor.xzDistToPlayer < 500.0f)) { this->unk3F2 = this->headRot.y; this->actionFunc = func_80BACBB0; } } else if ((player->transformation == PLAYER_FORM_HUMAN) && (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) != EQUIP_VALUE_SWORD_NONE)) { if (Actor_TalkOfferAccepted(&this->actor, &play->state)) { this->unk3F2 = this->headRot.y; func_80BAAB78(this, play); this->actionFunc = func_80BADA9C; } else if (this->actor.xzDistToPlayer < 200.0f) { Actor_OfferTalk(&this->actor, play, 200.0f); } } Math_ApproachF(&this->actor.speed, 5.0f, 0.2f, 0.1f); Actor_MoveWithGravity(&this->actor); } void func_80BACBB0(EnSuttari* this, PlayState* play) { Player* player = GET_PLAYER(play); s16 target; this->unk3F2 = this->headRot.y; if ((player->transformation == PLAYER_FORM_HUMAN) || (player->transformation == PLAYER_FORM_DEKU)) { this->actionFunc = func_80BACA14; } if ((this->actor.playerHeightRel < 60.0f) && (this->actor.xzDistToPlayer < 500.0f)) { if (this->actor.bgCheckFlags & BGCHECKFLAG_WALL) { target = this->actor.wallYaw; } else if (func_80BAA904(this, play)) { target = -this->actor.world.rot.y; } else { target = -this->actor.yawTowardsPlayer; } Math_SmoothStepToS(&this->actor.world.rot.y, target, 4, 0x3E8, 1); this->actor.shape.rot.y = this->actor.world.rot.y; Math_ApproachF(&this->actor.speed, 5.0f, 0.2f, 0.1f); } else { this->actionFunc = func_80BACD2C; this->actor.speed = 0.0f; } Actor_MoveWithGravity(&this->actor); if (!(this->actor.bgCheckFlags & BGCHECKFLAG_GROUND)) { this->actor.world.pos = this->actor.prevPos; this->actor.world.rot.y = -this->actor.world.rot.y; this->actionFunc = func_80BACE4C; } } void func_80BACD2C(EnSuttari* this, PlayState* play) { Player* player = GET_PLAYER(play); this->unk3F2 = this->headRot.y; if ((player->transformation == PLAYER_FORM_HUMAN) || (player->transformation == PLAYER_FORM_DEKU)) { this->actionFunc = func_80BACA14; } if ((this->actor.playerHeightRel < 60.0f) && (this->actor.xzDistToPlayer < 500.0f)) { this->actionFunc = func_80BACBB0; Math_ApproachF(&this->actor.speed, 5.0f, 0.2f, 0.1f); } else { Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 4, 0x3E8, 1); this->actor.shape.rot.y = this->actor.world.rot.y; } Actor_MoveWithGravity(&this->actor); if (!(this->actor.bgCheckFlags & BGCHECKFLAG_GROUND)) { this->actor.world.pos = this->actor.prevPos; this->actor.world.rot.y = -this->actor.world.rot.y; this->actionFunc = func_80BACE4C; } } void func_80BACE4C(EnSuttari* this, PlayState* play) { if (this->actor.xzDistToPlayer > 100.0f) { this->actionFunc = func_80BACBB0; } Actor_MoveWithGravity(&this->actor); if (this->actor.world.pos.y != this->actor.floorHeight) { this->actor.world.pos = this->actor.prevPos; this->actor.world.rot.y = -this->actor.world.rot.y; this->actor.shape.rot.y = this->actor.world.rot.y; } } void func_80BACEE0(EnSuttari* this, PlayState* play) { ScheduleOutput scheduleOutput; this->timePathTimeSpeed = R_TIME_SPEED + ((void)0, gSaveContext.save.timeSpeedOffset); if (!Schedule_RunScript(play, D_80BAE820, &scheduleOutput) || ((this->scheduleResult != scheduleOutput.result) && !func_80BABF64(this, play, &scheduleOutput))) { this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; scheduleOutput.result = 0; } else { this->actor.flags |= ACTOR_FLAG_ATTENTION_ENABLED; } this->scheduleResult = scheduleOutput.result; func_80BAC2FC(this, play); func_80BAB434(this); if (this->scheduleResult == 5) { SET_WEEKEVENTREG(WEEKEVENTREG_58_80); this->actionFunc = func_80BADDB4; this->actor.speed = 0.0f; } else if (Player_GetMask(play) != PLAYER_MASK_STONE) { func_80BAB1A0(this, play); } Actor_MoveWithGravity(&this->actor); } void func_80BAD004(EnSuttari* this, PlayState* play) { ScheduleOutput scheduleOutput; this->timePathTimeSpeed = R_TIME_SPEED + ((void)0, gSaveContext.save.timeSpeedOffset); if (!Schedule_RunScript(play, D_80BAE820, &scheduleOutput) || ((this->scheduleResult != scheduleOutput.result) && !func_80BABF64(this, play, &scheduleOutput))) { this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; scheduleOutput.result = 0; } else { this->actor.flags |= ACTOR_FLAG_ATTENTION_ENABLED; } this->scheduleResult = scheduleOutput.result; func_80BAC2FC(this, play); if (Actor_TalkOfferAccepted(&this->actor, &play->state)) { Message_StartTextbox(play, 0x2A3A, &this->actor); this->actionFunc = func_80BAD130; } else if ((this->actor.xzDistToPlayer < 200.0f) || this->actor.isLockedOn) { Actor_OfferTalkNearColChkInfoCylinder(&this->actor, play); } Actor_MoveWithGravity(&this->actor); } void func_80BAD130(EnSuttari* this, PlayState* play) { u8 talkState = Message_GetState(&play->msgCtx); if (((talkState == TEXT_STATE_EVENT) || (talkState == TEXT_STATE_DONE)) && Message_ShouldAdvance(play)) { play->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; play->msgCtx.stateTimer = 4; if (this->flags1 & 8) { this->actionFunc = func_80BAD004; } else if (this->flags1 & 4) { this->actionFunc = func_80BAD7F8; } else if (this->flags1 & 2) { this->actionFunc = func_80BAD5F8; } else if (this->flags1 & 1) { this->unk3F6 = 20; this->actionFunc = func_80BADF3C; } } Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000, 0x100); } void func_80BAD230(EnSuttari* this, PlayState* play) { if (CutsceneManager_IsNext(this->csIdList[1])) { CutsceneManager_Start(this->csIdList[1], &this->actor); this->textId = 0x2A31; Message_StartTextbox(play, this->textId, &this->actor); this->flags1 |= 0x4000; SEQCMD_PLAY_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 0, NA_BGM_CHASE | SEQ_FLAG_ASYNC); this->actionFunc = func_80BAD380; } else { CutsceneManager_Queue(this->csIdList[1]); } } void func_80BAD2B4(EnSuttari* this, PlayState* play) { if ((Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) { if (this->textId == 0x2A30) { CutsceneManager_Stop(this->csIdList[0]); CutsceneManager_Queue(this->csIdList[1]); this->actionFunc = func_80BAD230; } else { CutsceneManager_Stop(this->csIdList[1]); play->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; play->msgCtx.stateTimer = 4; this->flags1 |= 0x40; this->actionFunc = func_80BAD380; } } func_80BAB374(this, play); } void func_80BAD380(EnSuttari* this, PlayState* play) { u8 talkState = Message_GetState(&play->msgCtx); Player* player = GET_PLAYER(play); if ((player->stateFlags1 & PLAYER_STATE1_TALKING) && (play->msgCtx.currentTextId != 0x2A31)) { this->flags1 |= 0x8000; this->actor.speed = 0.0f; } else { this->flags1 &= ~0x8000; func_80BABA90(this, 1, 1); if ((this->flags1 & 0x4000) && (talkState == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) { this->flags2 &= ~0x10; player->stateFlags1 &= ~PLAYER_STATE1_10000000; this->flags1 &= ~0x4000; CutsceneManager_Stop(this->csIdList[1]); play->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; play->msgCtx.stateTimer = 4; this->flags1 |= 0x40; } if (this->flags1 & 0x100) { this->flags2 |= 8; func_80BAAF1C(this); } else if (this->flags1 & 0x200) { SET_WEEKEVENTREG(WEEKEVENTREG_SAKON_DEAD); this->flags2 |= 4; this->actor.speed = 0.0f; Actor_Spawn(&play->actorCtx, play, ACTOR_EN_CLEAR_TAG, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, CLEAR_TAG_PARAMS(CLEAR_TAG_SMALL_EXPLOSION)); SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 30, NA_SE_IT_BOMB_EXPLOSION); Actor_Kill(&this->actor); return; } if (this->unk1F4[1] == -0x63) { if (this->flags2 & 8) { SET_WEEKEVENTREG(WEEKEVENTREG_RECOVERED_STOLEN_BOMB_BAG); } this->actor.speed = 0.0f; SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 20); this->flags2 |= 4; EnSuttari_TriggerTransition(play, ENTRANCE(NORTH_CLOCK_TOWN, 7)); } else { this->unk3F2 = this->headRot.y; Math_ApproachF(&this->actor.speed, 4.0f, 0.2f, 0.5f); Actor_MoveWithGravity(&this->actor); func_80BAB374(this, play); } } } void func_80BAD5F8(EnSuttari* this, PlayState* play) { ScheduleOutput scheduleOutput; s16 curFrame = this->skelAnime.curFrame; s16 endFrame = Animation_GetLastFrame(sAnimationInfo[this->animIndex].animation); if ((curFrame == endFrame) && (this->animIndex == SUTTARI_ANIM_0) && (this->flags1 & 0x20)) { this->animIndex = SUTTARI_ANIM_2; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); } this->timePathTimeSpeed = R_TIME_SPEED + ((void)0, gSaveContext.save.timeSpeedOffset); if (!Schedule_RunScript(play, D_80BAE820, &scheduleOutput) || ((this->scheduleResult != scheduleOutput.result) && !func_80BABF64(this, play, &scheduleOutput))) { this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; scheduleOutput.result = 0; } else { this->actor.flags |= ACTOR_FLAG_ATTENTION_ENABLED; } this->scheduleResult = scheduleOutput.result; func_80BAC2FC(this, play); if ((this->unk430 == 1) && (this->timePath->additionalPathIndex == ADDITIONAL_PATH_INDEX_NONE)) { Actor_Kill(&this->actor); return; } func_80BAB434(this); if ((this->flags1 & 0x20) && (this->unk430 == 0) && (scheduleOutput.result != 7)) { if (Actor_TalkOfferAccepted(&this->actor, &play->state)) { Message_StartTextbox(play, 0x2A02, &this->actor); this->actionFunc = func_80BAD130; } else if ((this->actor.xzDistToPlayer < 100.0f) || this->actor.isLockedOn) { Actor_OfferTalkNearColChkInfoCylinder(&this->actor, play); } } Actor_MoveWithGravity(&this->actor); } void func_80BAD7F8(EnSuttari* this, PlayState* play) { ScheduleOutput scheduleOutput; s16 curFrame = this->skelAnime.curFrame; s16 endFrame = Animation_GetLastFrame(sAnimationInfo[this->animIndex].animation); if (this->enFsn == NULL) { this->enFsn = (EnFsn*)EnSuttari_GetActorById(play, ACTOR_EN_FSN); } else { if ((this->flags1 & 0x2000) && (this->animIndex == SUTTARI_ANIM_1) && (curFrame == endFrame)) { this->animIndex = SUTTARI_ANIM_2; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); } this->timePathTimeSpeed = R_TIME_SPEED + ((void)0, gSaveContext.save.timeSpeedOffset); if (!Schedule_RunScript(play, D_80BAE820, &scheduleOutput) || ((this->scheduleResult != scheduleOutput.result) && !func_80BABF64(this, play, &scheduleOutput))) { this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; scheduleOutput.result = 0; } else { this->actor.flags |= ACTOR_FLAG_ATTENTION_ENABLED; } this->scheduleResult = scheduleOutput.result; func_80BAC2FC(this, play); if ((this->unk430 == 1) && (this->timePath->additionalPathIndex == ADDITIONAL_PATH_INDEX_NONE)) { Actor_Kill(&this->actor); return; } if ((this->flags1 & 0x20) && (scheduleOutput.result != 9)) { if (Actor_TalkOfferAccepted(&this->actor, &play->state)) { Message_StartTextbox(play, 0x2A02, &this->actor); this->actionFunc = func_80BAD130; } else if ((this->actor.xzDistToPlayer < 100.0f) || this->actor.isLockedOn) { Actor_OfferTalkNearColChkInfoCylinder(&this->actor, play); } } Actor_MoveWithGravity(&this->actor); } } void func_80BADA08(EnSuttari* this, PlayState* play) { if (Actor_TalkOfferAccepted(&this->actor, &play->state)) { this->actor.flags &= ~ACTOR_FLAG_TALK_OFFER_AUTO_ACCEPTED; func_80BAAB78(this, play); SET_WEEKEVENTREG(WEEKEVENTREG_81_04); } else if (this->actor.xzDistToPlayer < 500.0f) { this->actor.flags |= ACTOR_FLAG_TALK_OFFER_AUTO_ACCEPTED; Actor_OfferTalk(&this->actor, play, 500.0f); } } void func_80BADA9C(EnSuttari* this, PlayState* play) { u8 talkstate = Message_GetState(&play->msgCtx); s16 curFrame = this->skelAnime.curFrame; s16 endFrame = Animation_GetLastFrame(sAnimationInfo[this->animIndex].animation); if (this->flags1 & 1) { this->unk3F2 = this->headRot.y; func_80BAA9B4(this); } else if ((this->animIndex == SUTTARI_ANIM_7) && (curFrame == endFrame)) { this->animIndex = SUTTARI_ANIM_1; Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex); } if (talkstate == TEXT_STATE_EVENT) { if (Message_ShouldAdvance(play)) { if (this->flags1 & 0x400) { if (this->textId == 0x29EE) { CutsceneManager_Stop(this->csIdList[this->csIdIndex]); } this->flags1 &= ~0x400; if (this->flags2 & 1) { ((EnElf*)GET_PLAYER(play)->tatlActor)->unk_264 |= 0x10; this->flags2 &= ~1; } play->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; play->msgCtx.stateTimer = 4; this->textId = 0; if (this->flags1 & 1) { this->actionFunc = func_80BACA14; } else { this->actionFunc = func_80BAD7F8; } return; } func_80BAAB78(this, play); } } else if ((talkstate == TEXT_STATE_CHOICE) && Message_ShouldAdvance(play)) { switch (play->msgCtx.choiceIndex) { case 0: Audio_PlaySfx_MessageDecide(); this->flags1 |= 0x800; func_80BAAB78(this, play); break; case 1: Audio_PlaySfx_MessageCancel(); func_80BAAB78(this, play); break; default: break; } } if (!(this->flags1 & 4)) { Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000, 0x100); this->actor.world.rot.y = this->actor.shape.rot.y; } } void func_80BADD0C(EnSuttari* this, PlayState* play) { if (CutsceneManager_IsNext(this->csIdList[this->csIdIndex])) { if (this->csIdIndex == 0) { CutsceneManager_Start(this->csIdList[this->csIdIndex], &this->enFsn->actor); } else { CutsceneManager_Start(this->csIdList[this->csIdIndex], &this->actor); } func_80BAAA94(this); this->actionFunc = func_80BADA9C; } else { CutsceneManager_Queue(this->csIdList[this->csIdIndex]); } } void func_80BADDB4(EnSuttari* this, PlayState* play) { func_80BABA90(this, 1, 1); func_80BAB434(this); if (CHECK_WEEKEVENTREG(WEEKEVENTREG_51_10)) { this->actionFunc = func_80BADE14; } Actor_MoveWithGravity(&this->actor); } void func_80BADE14(EnSuttari* this, PlayState* play) { func_80BABA90(this, 1, 2); if (this->unk1F4[1] == -0x63) { this->actor.speed = 0.0f; } else { this->unk3F2 = this->headRot.y; Math_ApproachF(&this->actor.speed, 6.0f, 0.2f, 0.5f); } Actor_MoveWithGravity(&this->actor); } void func_80BADE8C(EnSuttari* this, PlayState* play) { this->unk3F2 = this->headRot.y; Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xBB8, 0); if (Actor_TalkOfferAccepted(&this->actor, &play->state)) { this->actor.flags &= ~ACTOR_FLAG_TALK_OFFER_AUTO_ACCEPTED; Message_StartTextbox(play, 0x2A3A, &this->actor); this->actionFunc = func_80BAD130; } else { this->actor.flags |= ACTOR_FLAG_TALK_OFFER_AUTO_ACCEPTED; Actor_OfferTalk(&this->actor, play, 500.0f); } } void func_80BADF3C(EnSuttari* this, PlayState* play) { func_80BABB90(this, 0); if (this->unk1F4[0] == -0x63) { Actor_Kill(&this->actor); } this->unk3F2 = this->headRot.y; if (DECR(this->unk3F6) == 0) { Math_ApproachF(&this->actor.speed, 6.0f, 0.2f, 0.5f); } Actor_MoveWithGravity(&this->actor); } void EnSuttari_Init(Actor* thisx, PlayState* play) { EnSuttari* this = (EnSuttari*)thisx; s32 pad; if (CHECK_WEEKEVENTREG(WEEKEVENTREG_SAKON_DEAD)) { Actor_Kill(&this->actor); return; } Collider_InitCylinder(play, &this->collider); Collider_InitAndSetCylinder(play, &this->collider, &this->actor, &sCylinderInit); CollisionCheck_SetInfo2(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit2); this->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED; EnSuttari_GetPaths(this, play); Actor_SetScale(&this->actor, 0.01f); this->actionFunc = func_80BAC6E8; this->actor.gravity = -4.0f; } void EnSuttari_Destroy(Actor* thisx, PlayState* play) { EnSuttari* this = (EnSuttari*)thisx; if ((play->sceneId == SCENE_BACKTOWN) && !(this->flags2 & 4)) { SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 20); } Collider_DestroyCylinder(play, &this->collider); } void EnSuttari_Update(Actor* thisx, PlayState* play) { EnSuttari* this = (EnSuttari*)thisx; s32 pad; Player* player = GET_PLAYER(play); this->actionFunc(this, play); if ((this->flags1 & 8) && (this->flags2 & 0x10) && (player->stateFlags1 & PLAYER_STATE1_10000000)) { player->actor.freezeTimer = 3; } if (!(this->flags1 & 0x8000)) { func_80BAB4F0(this, play); } if (this->flags2 & 2) { EnSuttari_AdvanceTime(); } if (this->scheduleResult != 0) { if ((this->animIndex == SUTTARI_ANIM_2) || (this->animIndex == SUTTARI_ANIM_6)) { if (Animation_OnFrame(&this->skelAnime, 8.0f) || Animation_OnFrame(&this->skelAnime, 16.0f)) { Actor_PlaySfx(&this->actor, NA_SE_EV_PAMERA_WALK); } } else if ((this->animIndex == SUTTARI_ANIM_0) || (this->animIndex == SUTTARI_ANIM_5)) { if (Animation_OnFrame(&this->skelAnime, 8.0f) || Animation_OnFrame(&this->skelAnime, 17.0f)) { Actor_PlaySfx(&this->actor, NA_SE_EV_PAMERA_WALK); } } } } s32 EnSuttari_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) { EnSuttari* this = (EnSuttari*)thisx; if (limbIndex == OBJECT_BOJ_LIMB_0F) { *dList = object_boj_DL_00AF90; if (!(this->flags1 & 4)) { Matrix_Translate(1500.0f, 0.0f, 0.0f, MTXMODE_APPLY); Matrix_RotateXS(this->unk3F2, MTXMODE_APPLY); Matrix_RotateZS(-this->headRot.x, MTXMODE_APPLY); Matrix_Translate(-1500.0f, 0.0f, 0.0f, MTXMODE_APPLY); } } if (limbIndex == OBJECT_BOJ_LIMB_08) { Matrix_RotateXS(-this->torsoRot.y, MTXMODE_APPLY); Matrix_RotateZS(-this->torsoRot.x, MTXMODE_APPLY); } if ((limbIndex == OBJECT_BOJ_LIMB_08) || (limbIndex == OBJECT_BOJ_LIMB_09) || (limbIndex == OBJECT_BOJ_LIMB_0C)) { rot->y += TRUNCF_BINANG(Math_SinS(this->fidgetTableY[limbIndex]) * 200.0f); rot->z += TRUNCF_BINANG(Math_CosS(this->fidgetTableZ[limbIndex]) * 200.0f); } return false; } void EnSuttari_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, Actor* thisx) { static Vec3f D_80BAE950 = { 0.0f, 0.0f, 0.0f }; static Vec3f D_80BAE95C = { 2000.0f, -1000.0f, 0.0f }; EnSuttari* this = (EnSuttari*)thisx; s32 pad; MtxF* curState; Actor* bombBag; if (((this->flags1 & 8) && (this->flags1 & 0x10)) || ((this->flags1 & 2) && !(this->flags1 & 0x20)) || ((this->flags1 & 4) && !(this->flags1 & 0x20))) { if (limbIndex == OBJECT_BOJ_LIMB_08) { curState = Matrix_GetCurrent(); Matrix_MultVec3f(&D_80BAE95C, &this->unk3F8); if (this->actor.child == NULL) { if (this->flags1 & 0x100) { bombBag = Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_NIMOTSU, curState->mf[3][0], curState->mf[3][1], curState->mf[3][2], 0, 0, 0, -1); if (bombBag != NULL) { Matrix_MtxFToYXZRot(curState, &bombBag->shape.rot, false); } } else { Gfx_SetupDL25_Opa(play->state.gfxCtx); OPEN_DISPS(play->state.gfxCtx); gSPDisplayList(POLY_OPA_DISP++, gBombShopBagDL); CLOSE_DISPS(play->state.gfxCtx); } } } } if (limbIndex == OBJECT_BOJ_LIMB_0F) { Matrix_MultVec3f(&D_80BAE950, &this->actor.focus.pos); } } void EnSuttari_TransformLimbDraw(PlayState* play, s32 limbIndex, Actor* thisx) { } void EnSuttari_Draw(Actor* thisx, PlayState* play) { EnSuttari* this = (EnSuttari*)thisx; s32 pad; Vec3f pos; Vec3f scale; if (this->flags1 & 0x80) { OPEN_DISPS(play->state.gfxCtx); Gfx_SetupDL25_Opa(play->state.gfxCtx); gSPSegment(POLY_OPA_DISP++, 0x08, Gfx_EnvColor(play->state.gfxCtx, 255, 255, 255, 0)); gSPSegment(POLY_OPA_DISP++, 0x09, Gfx_EnvColor(play->state.gfxCtx, 55, 55, 255, 0)); gDPPipeSync(POLY_OPA_DISP++); SkelAnime_DrawTransformFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, EnSuttari_OverrideLimbDraw, EnSuttari_PostLimbDraw, EnSuttari_TransformLimbDraw, &this->actor); if (this->flags1 & 0x80) { Gfx_SetupDL25_Xlu(play->state.gfxCtx); pos = this->actor.world.pos; scale.x = 0.2f; scale.y = 0.2f; scale.z = 0.2f; func_800BC620(&pos, &scale, 255, play); } CLOSE_DISPS(play->state.gfxCtx); } }