diff --git a/spec b/spec index f39198bd9..fe108e63f 100644 --- a/spec +++ b/spec @@ -3747,9 +3747,7 @@ beginseg name "ovl_En_Invadepoh" compress include "build/src/overlays/actors/ovl_En_Invadepoh/z_en_invadepoh.o" - include "build/data/ovl_En_Invadepoh/ovl_En_Invadepoh.data.o" - include "build/data/ovl_En_Invadepoh/ovl_En_Invadepoh.bss.o" - include "build/data/ovl_En_Invadepoh/ovl_En_Invadepoh.reloc.o" + include "build/src/overlays/actors/ovl_En_Invadepoh/ovl_En_Invadepoh_reloc.o" endseg beginseg diff --git a/src/overlays/actors/ovl_En_Invadepoh/z_en_invadepoh.c b/src/overlays/actors/ovl_En_Invadepoh/z_en_invadepoh.c index 018820502..cbdea46fa 100644 --- a/src/overlays/actors/ovl_En_Invadepoh/z_en_invadepoh.c +++ b/src/overlays/actors/ovl_En_Invadepoh/z_en_invadepoh.c @@ -1,511 +1,4439 @@ #include "z_en_invadepoh.h" +#include "z64horse.h" +#include "overlays/actors/ovl_En_Door/z_en_door.h" +#include "assets/objects/gameplay_keep/gameplay_keep.h" +#include "overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h" +#include "sys_cfb.h" -#define FLAGS (ACTOR_FLAG_10) +#define FLAGS 0x00000010 #define THIS ((EnInvadepoh*)thisx) +#define ENINVADEPOH_INVADER_ACTIVE (1 << 0) +#define ENINVADEPOH_INVADER_CLOSEST (1 << 1) + +#define BARN_X_POSITION -409 +#define BARN_Y_POSITION 265 +#define BARN_Z_POSITION -1829 + +#define SPAWN_X_POSITION -193 +#define SPAWN_Y_POSITION 235 +#define SPAWN_Z_POSITION -434 + +typedef enum { + ENINVADEPOH_EVENT_UNSET, + ENINVADEPOH_EVENT_WAIT, + ENINVADEPOH_EVENT_ACTIVE, + ENINVADEPOH_EVENT_CLEAR, + ENINVADEPOH_EVENT_FAILED +} EnInvadepohEventState; + void EnInvadepoh_Init(Actor* thisx, PlayState* play); void EnInvadepoh_Destroy(Actor* thisx, PlayState* play); -void EnInvadepoh_Update(Actor* thisx, PlayState* play); +void EnInvadepoh_Event_Update(Actor* thisx, PlayState* play2); + +// Update functions +void EnInvadepoh_Invader_WaitForObject(Actor* thisx, PlayState* play2); +void EnInvadepoh_Invader_Update(Actor* thisx, PlayState* play2); +void EnInvadepoh_Cow_WaitForObject(Actor* thisx, PlayState* play2); +void EnInvadepoh_Cow_Update(Actor* thisx, PlayState* play2); +void EnInvadepoh_CowTail_WaitForObject(Actor* thisx, PlayState* play2); +void EnInvadepoh_CowTail_Update(Actor* thisx, PlayState* play2); +void EnInvadepoh_AbductedRomani_Update(Actor* thisx, PlayState* play2); +void EnInvadepoh_AbductedRomani_WaitForObject(Actor* thisx, PlayState* play2); +void EnInvadepoh_ConfusedRomani_WaitForObject(Actor* thisx, PlayState* play2); +void EnInvadepoh_ConfusedRomani_Update(Actor* thisx, PlayState* play2); +void EnInvadepoh_LightBall_Update(Actor* thisx, PlayState* play2); +void EnInvadepoh_Night1Romani_WaitForObject(Actor* thisx, PlayState* play2); +void EnInvadepoh_Night1Romani_WaitForEvent(Actor* thisx, PlayState* play2); +void EnInvadepoh_Night1Romani_Update(Actor* thisx, PlayState* play2); +void EnInvadepoh_BarnRomani_WaitForObject(Actor* thisx, PlayState* play2); +void EnInvadepoh_BarnRomani_WaitForEvent(Actor* thisx, PlayState* play2); +void EnInvadepoh_BarnRomani_Update(Actor* thisx, PlayState* play2); +void EnInvadepoh_RewardRomani_WaitForObject(Actor* thisx, PlayState* play2); +void EnInvadepoh_RewardRomani_Update(Actor* thisx, PlayState* play2); +void EnInvadepoh_Dog_WaitForObject(Actor* thisx, PlayState* play2); +void EnInvadepoh_Dog_WaitForEvent(Actor* thisx, PlayState* play2); +void EnInvadepoh_Dog_Update(Actor* thisx, PlayState* play2); +void EnInvadepoh_Cremia_WaitForObject(Actor* thisx, PlayState* play2); +void EnInvadepoh_Cremia_WaitForEvent(Actor* thisx, PlayState* play2); +void EnInvadepoh_Cremia_Update(Actor* thisx, PlayState* play2); +void EnInvadepoh_Night3Romani_WaitForObject(Actor* thisx, PlayState* play2); +void EnInvadepoh_Night3Romani_WaitForEvent(Actor* thisx, PlayState* play2); +void EnInvadepoh_Night3Romani_Update(Actor* thisx, PlayState* play2); +void EnInvadepoh_Abuductor_WaitForObject(Actor* thisx, PlayState* play2); + +// Draw functions +void EnInvadepoh_Abductor_Update(Actor* thisx, PlayState* play2); +void EnInvadepoh_Event_Draw(Actor* thisx, PlayState* play); +void EnInvadepoh_Alien_Draw(Actor* thisx, PlayState* play2); +void EnInvadepoh_Cow_Draw(Actor* thisx, PlayState* play2); +void EnInvadepoh_CowTail_Draw(Actor* thisx, PlayState* play2); +void EnInvadepoh_Romani_Draw(Actor* thisx, PlayState* play); +void EnInvadepoh_LightBall_Draw(Actor* thisx, PlayState* play2); +void EnInvadepoh_Dog_Draw(Actor* thisx, PlayState* play); +void EnInvadepoh_Cremia_Draw(Actor* thisx, PlayState* play); + +// Action functions +void EnInvadepoh_Event_SetupWait(EnInvadepoh* this); +void EnInvadepoh_Event_Wait(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Event_SetupQueueInvasionCs(EnInvadepoh* this); +void EnInvadepoh_Event_QueueInvasionCs(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Event_SetupInvasionCs(EnInvadepoh* this); +void EnInvadepoh_Event_InvasionCs(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Event_SetupInvasion(EnInvadepoh* this); +void EnInvadepoh_Event_Invasion(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Event_SetupQueueVictoryCs(EnInvadepoh* this); +void EnInvadepoh_Event_QueueVictoryCs(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Event_SetupVictoryCs(EnInvadepoh* this); +void EnInvadepoh_Event_VictoryCs(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Event_SetupRomaniReward(EnInvadepoh* this); +void EnInvadepoh_Event_RomaniReward(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Event_SetupGoodEnd(EnInvadepoh* this); +void EnInvadepoh_Event_GoodEnd(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Event_SetupFailure(EnInvadepoh* this); +void EnInvadepoh_Event_Failure(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Event_SetupBadEnd(EnInvadepoh* this); +void EnInvadepoh_Event_BadEnd(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Invader_WaitForEvent(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Invade_WaitToRespawn(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Invader_SetupWarpIn(EnInvadepoh* this); +void EnInvadepoh_Invader_WarpIn(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Invader_SetupAttack(EnInvadepoh* this); +void EnInvadepoh_Invader_Attack(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Invader_Hitstun(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Invader_SetupDeath(EnInvadepoh* this); +void EnInvadepoh_Invader_Death(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_AbductedRomani_Wait(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_AbductedRomani_SetupYell(EnInvadepoh* this); +void EnInvadepoh_AbductedRomani_Yell(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_AbductedRomani_SetupStruggle(EnInvadepoh* this); +void EnInvadepoh_AbductedRomani_Struggle(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_AbductedRomani_SetupEnd(EnInvadepoh* this); +void EnInvadepoh_AbductedRomani_End(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_ConfusedRomani_Walk(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_ConfusedRomani_SetupIdle(EnInvadepoh* this); +void EnInvadepoh_ConfusedRomani_Idle(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_ConfusedRomani_Talk(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_LightBall_SetupDescend(EnInvadepoh* this); +void EnInvadepoh_LightBall_Descend(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_LightBall_SetupHover(EnInvadepoh* this); +void EnInvadepoh_LightBall_Hover(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_LightBall_SetupSpawnInvaders(EnInvadepoh* this); +void EnInvadepoh_LightBall_SpawnInvaders(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_LightBall_SetupAboveBarn(EnInvadepoh* this); +void EnInvadepoh_LightBall_AboveBarn(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_LightBall_Circle(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_LightBall_SetupFlee(EnInvadepoh* this); +void EnInvadepoh_LightBall_Flee(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_LightBall_SetupLeaveBarn(EnInvadepoh* this); +void EnInvadepoh_LightBall_LeaveBarn(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Night1Romani_Walk(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Night1Romani_Talk(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_BarnRomani_Idle(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_BarnRomani_SetupLookAround(EnInvadepoh* this); +void EnInvadepoh_BarnRomani_LookAround(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_BarnRomani_Walk(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_BarnRomani_Talk(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_RewardRomani_ClearCheck(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_RewardRomani_SetupStartCs(EnInvadepoh* this); +void EnInvadepoh_RewardRomani_StartCs(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_RewardRomani_SetupTalk(EnInvadepoh* this); +void EnInvadepoh_RewardRomani_Talk(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_RewardRomani_SetupGiveBottle(EnInvadepoh* this); +void EnInvadepoh_RewardRomani_GiveBottle(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_RewardRomani_SetupAfterGiveBottle(EnInvadepoh* this); +void EnInvadepoh_RewardRomani_AfterGiveBottle(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_RewardRomani_SetupFinished(EnInvadepoh* this); +void EnInvadepoh_RewardRomani_Finished(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Dog_Walk(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Dog_SetupRun(EnInvadepoh* this); +void EnInvadepoh_Dog_Run(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Dog_SetupBark(EnInvadepoh* this); +void EnInvadepoh_Dog_Bark(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Dog_Jump(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Cremia_Walk(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Cremia_Talk(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Cremia_SetupIdle(EnInvadepoh* this); +void EnInvadepoh_Cremia_Idle(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Night3Romani_Walk(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Night3Romani_Talk(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Night3Romani_SetupIdle(EnInvadepoh* this); +void EnInvadepoh_Night3Romani_Idle(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Abductor_Cow(EnInvadepoh* this, PlayState* play); +void EnInvadepoh_Abductor_Romani(EnInvadepoh* this, PlayState* play); + +s32 EnInvadepoh_SnapToFloor(EnInvadepoh* this); +s32 EnInvadepoh_StepTowardXZ(f32* pxValue, f32* pzValue, f32 xTarget, f32 zTarget, f32 speed); -#if 0 ActorInit En_Invadepoh_InitVars = { - /**/ ACTOR_EN_INVADEPOH, - /**/ ACTORCAT_PROP, - /**/ FLAGS, - /**/ GAMEPLAY_KEEP, - /**/ sizeof(EnInvadepoh), - /**/ EnInvadepoh_Init, - /**/ EnInvadepoh_Destroy, - /**/ EnInvadepoh_Update, - /**/ NULL, + ACTOR_EN_INVADEPOH, + ACTORCAT_PROP, + FLAGS, + GAMEPLAY_KEEP, + sizeof(EnInvadepoh), + (ActorFunc)EnInvadepoh_Init, + (ActorFunc)EnInvadepoh_Destroy, + (ActorFunc)EnInvadepoh_Event_Update, + (ActorFunc)NULL, }; -#endif -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B439B0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B43A24.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B43A74.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B43A9C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B43AB0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B43AF0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B43B80.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B43BC8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B43DD4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B43E6C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B43F0C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B43F70.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44024.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4407C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B440B8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44234.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B442E4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B443A0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B444BC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B444F4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44514.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44540.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44570.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44620.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44640.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44664.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44690.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B446D0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44700.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B447C0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44A90.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44B78.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44B84.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44C24.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44C80.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44E90.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44EFC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44F58.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B44FEC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B45080.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B450C0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4516C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B451A0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B452EC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B453F4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B45460.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B454BC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B45518.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B45550.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4560C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B45648.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B456A8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B457A0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B458D8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B45980.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B459E8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B45A4C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B45A94.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B45B1C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B45BB8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B45C04.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B45CE0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B45EC8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46118.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46184.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B461DC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4627C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46414.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46520.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B465CC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46644.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B468B4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B469C4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46A80.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/EnInvadepoh_Init.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46BB0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46BC0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46C08.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46C34.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46C50.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46C7C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46C94.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46CC0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46CF4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46D28.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/EnInvadepoh_Destroy.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46DA8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46DC8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46E20.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46E44.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46EC0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46EE8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46F88.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B46FA8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47064.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47084.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B470E0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47108.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B471C0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B471E0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47248.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47268.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47278.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47298.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47304.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47324.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/EnInvadepoh_Update.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47380.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B473E4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47478.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B474DC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47568.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47600.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4770C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B477B4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47830.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B478F4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47938.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B479E8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47BAC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47D30.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B47FA8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B48060.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B481C4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4827C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B482D4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B48324.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B48374.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B483CC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4843C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B484EC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B48588.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B48610.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B48620.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4873C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B487B4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B48848.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B48948.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B48AD4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B48DE4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B48E4C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B48FB0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B490F0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B491EC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B49228.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B492FC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4934C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B49404.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B49454.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B49628.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B49670.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B497A4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B497EC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B49904.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4994C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B499BC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B49A00.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B49B1C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B49BD0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B49C38.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B49DA0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B49DFC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B49F88.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4A168.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4A1B8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4A2C0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4A350.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4A570.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4A5E4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4A614.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4A67C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4A7C0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4A81C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4A9C8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4AB8C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4ABDC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4ACDC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4ACF0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4AD3C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4AD60.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4ADB8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4ADCC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4AEC0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4AEDC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4AF80.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4AF94.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B024.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B048.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B0C4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B218.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B3DC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B430.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B484.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B510.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B564.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B724.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B768.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B820.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B864.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4B8BC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4BA30.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4BA84.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4BBE0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4BC4C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4BFFC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4C058.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4C1BC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4C218.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4C3A0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4C568.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4C5C0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4C6C8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4C730.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4CAB0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4CB0C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4CC70.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4CCCC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4CE54.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4CFFC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4D054.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4D15C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4D290.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4D3E4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4D480.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4D670.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4D760.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4D7B8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4D9B4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4D9D8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4D9F4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4DB14.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4E120.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4E158.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4E1B0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4E200.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4E2AC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4E324.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4E3F0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4E5B0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4E61C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4E660.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4E6E4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4E784.s") - -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Invadepoh/func_80B4E7BC.s") +ColliderCylinderInit sAlienCylinderInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK4, + { 0xF7CFFFFF, 0x00, 0x04 }, + { 0x00003820, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 40, 95, 10, { 0, 0, 0 } }, +}; + +ColliderCylinderInit sHumanCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 18, 46, 0, { 0, 0, 0 } }, +}; + +ColliderCylinderInit sDogCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 13, 19, 0, { 0, 0, 0 } }, +}; + +Vec3f sLightBallSpawnOffset = { 216.0f, -20.0f, 1395.0f }; + +s32 sEventState = ENINVADEPOH_EVENT_UNSET; + +TexturePtr sRomaniEyeTextures[5] = { + gRomaniEyeOpenTex, gRomaniEyeHalfTex, gRomaniEyeClosedTex, gRomaniEyeHappyTex, gRomaniEyeSadTex, +}; +TexturePtr sRomaniMouthTextures[4] = { + gRomaniMouthHappyTex, + gRomaniMouthFrownTex, + gRomaniMouthHangingOpenTex, + gRomaniMouthSmileTex, +}; +s8 sRomaniTexturesDesegmented = false; + +TexturePtr sCremiaEyeTextures[6] = { + gCremiaEyeOpenTex, gCremiaEyeHalfTex, gCremiaEyeClosedTex, gCremiaEyeHappyTex, gCremiaEyeAngryTex, gCremiaEyeSadTex, +}; +TexturePtr sCremiaMouthTextures[4] = { + gCremiaMouthNormalTex, + gCremiaMouthSlightSmileTex, + gCremiaMouthFrownTex, + gCremiaMouthHangingOpenTex, +}; +s8 sCremiaTexturesDesegmented = false; + +s8 sRewardFinished = false; + +MtxF sAlienLeftEyeBeamMtxF; // TODO: Conflict ughhhhhh +MtxF sAlienRightEyeBeamMtxF; +EnInvadepoh* sAlienInvaders[8]; +u8 sAlienStateFlags[8]; +s8 sAliensTooClose; +EnInvadepohWarpEffect sWarpEffects[10]; +EnInvadepoh* sLightBall; +EnInvadepoh* sRomani; +EnInvadepoh* sCremia; +AnimatedMaterial* sAlienEyeBeamTexAnim; +AnimatedMaterial* sAlienEmptyTexAnim; +s16 sCutsceneIds[3]; +EnInvadepoh* sClosestAlien; + +#define BITPACK(field, value, width, offset) \ + (((field) & ~(((1 << (width)) - 1) << (offset))) | (((value) & ((1 << (width)) - 1)) << (offset))) +#define BITUNPACK(field, width, offset) (((field) & (((1 << width) - 1) << (offset))) >> (offset)) + +#define PACK_U16_INTO_U32_ARRAY(u32array, index, value) \ + if (((index) % 2) == 0) { \ + u32array[(index) >> 1] = BITPACK(u32array[(index) >> 1], value, 16, 0); \ + } else { \ + u32array[(index) >> 1] = BITPACK(u32array[(index) >> 1], value, 16, 16); \ + } \ + (void)0 + +#define UNPACK_U16_FROM_U32_ARRAY(var, u32array, index) \ + if (((index) % 2) == 0) { \ + var = BITUNPACK(u32array[(index) >> 1], 16, 0); \ + } else { \ + var = BITUNPACK(u32array[(index) >> 1], 16, 16); \ + } \ + (void)0 + +void EnInvadepoh_Invader_SetSpawnTime(s32 invaderIndex, s32 warpTime) { + warpTime -= CLOCK_TIME(2, 30); + warpTime = CLAMP_MIN(warpTime, 0); + + if ((invaderIndex % 2) == 0) { + gSaveContext.save.saveInfo.unk_E64[invaderIndex >> 1] = + (gSaveContext.save.saveInfo.unk_E64[invaderIndex >> 1] & ~0xFFFF) | (warpTime & 0xFFFF); + } else { + gSaveContext.save.saveInfo.unk_E64[invaderIndex >> 1] = + (gSaveContext.save.saveInfo.unk_E64[invaderIndex >> 1] & 0xFFFF) | ((warpTime & 0xFFFF) << 0x10); + } +} + +s32 EnInvadepoh_Invader_GetSpawnTime(s32 invaderIndex) { + u32 warpTime; + + if ((invaderIndex % 2) == 0) { + warpTime = gSaveContext.save.saveInfo.unk_E64[invaderIndex >> 1] & 0xFFFF; + } else { + warpTime = (gSaveContext.save.saveInfo.unk_E64[invaderIndex >> 1] & ~0xFFFF) >> 0x10; + } + return warpTime + CLOCK_TIME(2, 30); +} + +void EnInvadepoh_Invader_SetKillCount(s32 count) { + gSaveContext.save.saveInfo.unk_E64[4] = (gSaveContext.save.saveInfo.unk_E64[4] & ~0xFF) | (count & 0xFF); +} + +s32 EnInvadepoh_Invader_GetKillCount(void) { + return gSaveContext.save.saveInfo.unk_E64[4] & 0xFF; +} + +s32 EnInvadepoh_Invader_AddKill(void) { + s32 killCount = EnInvadepoh_Invader_GetKillCount(); + + if (killCount < 12) { + killCount++; + EnInvadepoh_Invader_SetKillCount(killCount); + } + return killCount; +} + +void EnInvadepoh_Invader_SetRespawnTime(s32 invaderIndex) { + s32 currentTime = gSaveContext.save.time; + + if ((CURRENT_DAY == 1) && (CLOCK_TIME(2, 30) <= currentTime) && (currentTime < CLOCK_TIME(5, 15))) { + s32 spawnDelay = (12 - EnInvadepoh_Invader_GetKillCount()) * 25.0f; + + EnInvadepoh_Invader_SetSpawnTime(invaderIndex, currentTime + spawnDelay); + } +} + +s32 EnInvadepoh_Invader_GetCurrentPoint(EnInvadepoh* this) { + s32 i; + s32 lastPoint = this->endPoint - 1; + + for (i = 0; i < lastPoint; i++) { + if (this->progress < this->checkpoints[i]) { + break; + } + } + return i; +} + +void EnInvadepoh_Romani_ApplyProgress(EnInvadepoh* this, s8* currentPoint, Vec3f* pathPosition) { + f32 curPathLength = 0.0f; + f32 curCheckpoint = 0.0f; + f32 invPathLength = 1.0f / this->pathLength; + s32 endPoint = this->endPoint; + s32 i; + Vec3f curToNext; + Vec3s* curPathPoint = this->pathPoints; + Vec3s* nextPathPoint = curPathPoint + 1; + f32 nextCheckpoint; + f32 nextPathLength; + f32 segmentProgress; + f32 pathSegLength; + + for (i = 0; i < endPoint; i++) { + curToNext.x = nextPathPoint->x - curPathPoint->x; + curToNext.y = nextPathPoint->y - curPathPoint->y; + curToNext.z = nextPathPoint->z - curPathPoint->z; + pathSegLength = Math3D_Vec3fMagnitude(&curToNext); + nextPathLength = curPathLength + pathSegLength; + nextCheckpoint = nextPathLength * invPathLength; + if (this->progress <= nextCheckpoint) { + *currentPoint = i; + segmentProgress = (this->progress - curCheckpoint) / (nextCheckpoint - curCheckpoint); + pathPosition->x = (segmentProgress * curToNext.x) + curPathPoint->x; + pathPosition->y = (segmentProgress * curToNext.y) + curPathPoint->y; + pathPosition->z = (segmentProgress * curToNext.z) + curPathPoint->z; + return; + } + curPathPoint = nextPathPoint++; + curPathLength = nextPathLength; + curCheckpoint = nextCheckpoint; + } + *currentPoint = endPoint; + pathPosition->x = this->pathPoints[endPoint].x; + pathPosition->y = this->pathPoints[endPoint].y; + pathPosition->z = this->pathPoints[endPoint].z; +} + +void EnInvadepoh_Invader_TurnToPath(EnInvadepoh* this, s16 step, s16 offset) { + s32 pad; + Vec3s* curPathPoint = &this->pathPoints[this->currentPoint]; + Vec3s* nextPathPoint = curPathPoint + 1; + Vec3f curPathPointF; + Vec3f nextPathPointF; + + if (this->currentPoint != this->endPoint) { + Math_Vec3s_ToVec3f(&curPathPointF, curPathPoint); + Math_Vec3s_ToVec3f(&nextPathPointF, nextPathPoint); + Math_ScaledStepToS(&this->actor.shape.rot.y, Math_Vec3f_Yaw(&curPathPointF, &nextPathPointF) + offset, step); + } +} + +void EnInvadepoh_Romani_TurnToPath(EnInvadepoh* this, s16 scale, s16 step, s16 minStep) { + s32 pad; + Vec3s* curPathPoint = &this->pathPoints[this->currentPoint]; + Vec3s* nextPathPoint = curPathPoint + 1; + Vec3f curPathPointF; + Vec3f nextPathPointF; + + if (this->currentPoint != this->endPoint) { + Math_Vec3s_ToVec3f(&curPathPointF, curPathPoint); + Math_Vec3s_ToVec3f(&nextPathPointF, nextPathPoint); + Math_SmoothStepToS(&this->actor.shape.rot.y, Math_Vec3f_Yaw(&curPathPointF, &nextPathPointF), scale, step, minStep); + } +} + +void EnInvadepoh_SetYawAlongPath(EnInvadepoh* this) { + s32 pad; + Vec3s* curPathPoint = &this->pathPoints[this->currentPoint]; + Vec3s* nextPathPoint = curPathPoint + 1; + Vec3f curPathPointF; + Vec3f nextPathPointF; + + Math_Vec3s_ToVec3f(&curPathPointF, curPathPoint); + Math_Vec3s_ToVec3f(&nextPathPointF, nextPathPoint); + this->actor.shape.rot.y = Math_Vec3f_Yaw(&curPathPointF, &nextPathPointF); +} + +f32 EnInvadepoh_GetPathLength(EnInvadepoh* this) { + s32 pointCount = this->endPoint + 1; + s32 i; + Vec3f prevPathVec; + Vec3f curPathVec; + Vec3s* curPathPoint = this->pathPoints; + f32 pathLength = 0.0f; + + Math_Vec3s_ToVec3f(&curPathVec, curPathPoint); + + for (curPathPoint++, i = 1; i < pointCount; curPathPoint++, i++) { + Math_Vec3f_Copy(&prevPathVec, &curPathVec); + Math_Vec3s_ToVec3f(&curPathVec, curPathPoint); + pathLength += Math3D_Distance(&prevPathVec, &curPathVec); + } + return pathLength; +} + +void EnInvadepoh_SetPath(EnInvadepoh* this, PlayState* play) { + Path* path = &play->setupPathList[ENINVADEPOH_GET_PATH(&this->actor)]; + + this->endPoint = path->count - 1; + this->pathPoints = Lib_SegmentedToVirtual(path->points); +} + +void EnInvadepoh_SetPosToPathPoint(EnInvadepoh* this, s32 point) { + Math_Vec3s_ToVec3f(&this->actor.world.pos, &this->pathPoints[point]); +} + +s32 EnInvadepoh_Dog_IsOnPath(EnInvadepoh* thisx, f32 highValue, f32 lowValue) { + EnInvadepoh* this = thisx; // TODO: why recast? + Vec3s* currentPoint = &this->pathPoints[this->currentPoint]; + Vec3s* nextPoint = &this->pathPoints[this->currentPoint] + 1; + f32 pathSegmentX; + f32 pathSegmentZ; + f32 pathUnitVecZ; + f32 pathUnitVecX; + f32 travelSegmentX; + f32 travelSegmentZ; + f32 distAlongPath; + f32 pathSegmentLength; + f32 distOffPath; + s16 pathYaw; + + if (this->currentPoint >= this->endPoint) { + return false; + } + pathSegmentX = (nextPoint->x - currentPoint->x); + pathSegmentZ = (nextPoint->z - currentPoint->z); + pathYaw = Math_Atan2S_XY(pathSegmentZ, pathSegmentX); + pathUnitVecZ = Math_CosS(pathYaw); + pathUnitVecX = Math_SinS(pathYaw); + travelSegmentX = this->actor.world.pos.x - currentPoint->x; + travelSegmentZ = this->actor.world.pos.z - currentPoint->z; + + distOffPath = fabsf((travelSegmentX * pathUnitVecZ) - (travelSegmentZ * pathUnitVecX)); + if (distOffPath > (highValue - lowValue)) { + return false; + } + + pathSegmentLength = Math3D_XZLength(pathSegmentX, pathSegmentZ); + distAlongPath = (travelSegmentZ * pathUnitVecZ) + (travelSegmentX * pathUnitVecX); + if ((distAlongPath < 0.0f) || (pathSegmentLength < distAlongPath)) { + return false; + } + return true; +} + +s32 EnInvadepoh_Dog_FindClosestWaypoint(EnInvadepoh* this, Vec3f* pos) { + s32 waypoint; + s32 endPoint = this->endPoint; + Vec3s* pathPoint; + Vec3f pathPoint3f; + f32 minDist = FLT_MAX; + s32 closestPoint = 0; + f32 pointToPos; + + for (waypoint = 0, pathPoint = this->pathPoints; waypoint < endPoint; waypoint++, pathPoint++) { + Math_Vec3s_ToVec3f(&pathPoint3f, pathPoint); + pointToPos = Math3D_Vec3fDistSq(&pathPoint3f, pos); + if (pointToPos < minDist) { + minDist = pointToPos; + closestPoint = waypoint; + } + } + return closestPoint; +} + +void EnInvadepoh_Invader_SetProgress(EnInvadepoh* this) { + s32 pad; + s32 currentTime = gSaveContext.save.time; + s32 warpInTime = EnInvadepoh_Invader_GetSpawnTime(ENINVADEPOH_GET_INDEX(&this->actor)); + + if (sEventState == ENINVADEPOH_EVENT_WAIT) { + this->progress = 0.0f; + } else if (sEventState == ENINVADEPOH_EVENT_ACTIVE) { + if ((currentTime - warpInTime) < 0) { + this->progress = 0.0f; + } else { + this->progress = (currentTime - warpInTime) * (1.0f / 3600.0f); + if (this->progress > 1.0f) { + this->progress = 1.0f; + } + } + } + this->currentPoint = EnInvadepoh_Invader_GetCurrentPoint(this); +} + +void EnInvadepoh_Invader_SetCheckpoints(EnInvadepoh* this) { + f32 invPathLength = 1.0f / this->pathLength; + s32 endPoint = this->endPoint; + s32 i; + Vec3f prevPathPoint; + Vec3f pathPointF; + Vec3s* pathPoint = this->pathPoints; + f32 pathPointLength = 0.0f; + f32* checkpoints; + + Math_Vec3s_ToVec3f(&pathPointF, pathPoint); + pathPoint++; + checkpoints = this->checkpoints; + for (i = 1; i < endPoint; i++, pathPoint++, checkpoints++) { + Math_Vec3f_Copy(&prevPathPoint, &pathPointF); + Math_Vec3s_ToVec3f(&pathPointF, pathPoint); + pathPointLength += Math3D_Distance(&prevPathPoint, &pathPointF); + *checkpoints = pathPointLength * invPathLength; + if (*checkpoints < 0.0f) { + *checkpoints = 0.0f; + } else if (*checkpoints > 1.0f) { + *checkpoints = 1.0f; + } + } +} + +void EnInvadepoh_Invader_SetupPath(EnInvadepoh* this, PlayState* play) { + EnInvadepoh_SetPath(this, play); + this->pathLength = EnInvadepoh_GetPathLength(this); + EnInvadepoh_Invader_SetCheckpoints(this); +} + +void EnInvadepoh_ConfusedRomani_SetupPath(EnInvadepoh* this, PlayState* play) { + EnInvadepoh_SetPath(this, play); +} + +void EnInvadepoh_ConfusedRomani_SetNextPathPoint(EnInvadepoh* this) { + this->currentPoint++; + if (this->currentPoint >= this->endPoint) { + this->currentPoint = 0; + } +} + +void EnInvadepoh_Night1Romani_SetupPath(EnInvadepoh* this, PlayState* play) { + EnInvadepoh_SetPath(this, play); + this->pathLength = EnInvadepoh_GetPathLength(this); +} + +void EnInvadepoh_Night1Romani_SetProgress(EnInvadepoh* this) { + if ((gSaveContext.save.time < CLOCK_TIME(2, 00)) || (gSaveContext.save.time >= CLOCK_TIME(6, 00))) { + this->progress = 0.0f; + } else if ((gSaveContext.save.time >= CLOCK_TIME(2, 15)) && (gSaveContext.save.time < CLOCK_TIME(6, 00))) { + this->progress = 1.0f; + } else { + f32 progress = (gSaveContext.save.time - CLOCK_TIME(2, 00)) * (1.0f / (CLOCK_TIME(2, 15) - CLOCK_TIME(2, 00))); + + this->progress = progress; + this->progress = CLAMP(this->progress, 0.0f, 1.0f); + } +} + +void EnInvadepoh_BarnRomani_SetupPath(EnInvadepoh* this, PlayState* play) { + EnInvadepoh_SetPath(this, play); +} + +void EnInvadepoh_BarnRomani_SetNextPathPoint(EnInvadepoh* this) { + if (this->currentPoint < this->endPoint) { + this->currentPoint++; + } +} + +void EnInvadepoh_Dog_SetupPath(EnInvadepoh* this, PlayState* play) { + EnInvadepoh_SetPath(this, play); + this->pathStep = 1; +} + +void EnInvadepoh_Dog_SetNextPathPoint(EnInvadepoh* this) { + this->currentPoint += this->pathStep; + if (this->currentPoint >= this->endPoint) { + this->currentPoint = 0; + } else if (this->currentPoint < 0) { + this->currentPoint = this->endPoint - 1; + } +} + +void EnInvadepoh_Night3Romani_SetupPath(EnInvadepoh* this, PlayState* play) { + EnInvadepoh_SetPath(this, play); + this->pathLength = EnInvadepoh_GetPathLength(this); +} + +void EnInvadepoh_Night3Romani_SetProgress(EnInvadepoh* this) { + if ((gSaveContext.save.time < CLOCK_TIME(20, 00)) && (gSaveContext.save.time >= CLOCK_TIME(6, 00))) { + this->progress = 0.0f; + } else if ((gSaveContext.save.time >= CLOCK_TIME(20, 14) + 15) || (gSaveContext.save.time < CLOCK_TIME(6, 00))) { + this->progress = 1.0f; + } else { + f32 progress = + (gSaveContext.save.time - CLOCK_TIME(20, 00)) * (1.0f / (CLOCK_TIME(20, 14) + 15 - CLOCK_TIME(20, 00))); + + this->progress = progress; + this->progress = CLAMP(this->progress, 0.0f, 1.0f); + } +} + +void EnInvadepoh_Invader_ApplyProgress(EnInvadepoh* this, PlayState* play) { + s32 pad; + Vec3s* curPathPoint; + Vec3s* nextPathPoint; + Vec3f curPathPointF; + Vec3f nextPathPointF; + f32 yPosTemp = this->actor.world.pos.y; + f32 curCheckpoint; + f32 nextCheckpoint; + + curPathPoint = this->pathPoints + this->currentPoint; + nextPathPoint = curPathPoint + 1; + curCheckpoint = (this->currentPoint <= 0) ? 0.0f : this->checkpoints[this->currentPoint - 1]; + nextCheckpoint = (this->currentPoint < (this->endPoint - 1)) ? this->checkpoints[this->currentPoint] : 1.0f; + + if (nextCheckpoint - curCheckpoint < 0.001f) { + Math_Vec3s_ToVec3f(&this->unk_314, curPathPoint); + } else { + f32 nextWeight = this->progress - curCheckpoint; + f32 prevWeight = nextCheckpoint - this->progress; + f32 invPathLength = 1.0f / (nextCheckpoint - curCheckpoint); + s32 pad3; + + Math_Vec3s_ToVec3f(&curPathPointF, curPathPoint); + Math_Vec3s_ToVec3f(&nextPathPointF, nextPathPoint); + this->unk_314.x = ((curPathPointF.x * prevWeight) + (nextPathPointF.x * nextWeight)) * invPathLength; + this->unk_314.y = ((curPathPointF.y * prevWeight) + (nextPathPointF.y * nextWeight)) * invPathLength; + this->unk_314.z = ((curPathPointF.z * prevWeight) + (nextPathPointF.z * nextWeight)) * invPathLength; + } + Math_Vec3f_Copy(&this->actor.world.pos, &this->unk_314); + func_800B4AEC(play, &this->actor, 0.0f); + if (this->actor.floorHeight > BGCHECK_Y_MIN + 1.0f) { + if (yPosTemp < this->actor.floorHeight) { + if (this->actor.velocity.y < 0.0f) { + this->actor.velocity.y = 0.0f; + } else { + this->actor.velocity.y += 2.0f; + this->actor.velocity.y = CLAMP_MAX(this->actor.velocity.y, 30.0f); + } + this->actor.world.pos.y = this->actor.velocity.y + yPosTemp; + if (this->actor.floorHeight < this->actor.world.pos.y) { + this->actor.world.pos.y = this->actor.floorHeight; + } + } else { + if (this->actor.velocity.y > 0.0f) { + this->actor.velocity.y = 0.0f; + } else { + this->actor.velocity.y -= 2.0f; + } + this->actor.world.pos.y = this->actor.velocity.y + yPosTemp; + if (this->actor.world.pos.y < this->actor.floorHeight) { + this->actor.world.pos.y = this->actor.floorHeight; + this->actor.velocity.y = CLAMP_MIN(this->actor.velocity.y, -30.0f); + } + } + } else { + this->actor.world.pos.y = yPosTemp; + } +} + +void EnInvadepoh_Invader_Knockback(EnInvadepoh* this, PlayState* play) { + if (this->actor.bgCheckFlags & BGCHECKFLAG_GROUND) { + this->actor.velocity.y *= 0.3f; + this->actor.speed *= 0.8f; + } else if (this->actor.bgCheckFlags & BGCHECKFLAG_WALL) { + this->actor.velocity.y *= 0.8f; + this->actor.speed *= 0.3f; + } else { + this->actor.velocity.y *= 0.8f; + this->actor.speed *= 0.8f; + } + Actor_MoveWithGravity(&this->actor); + Actor_UpdateBgCheckInfo(play, &this->actor, 30.0f, 40.0f, 0.0f, 5); +} + +void EnInvadepoh_Invader_EmptyFunc(EnInvadepoh* this) { +} + +s32 EnInvadepoh_Romani_MoveAlongPath(EnInvadepoh* this, PlayState* play, f32 speed, f32 height) { + s32 pad; // TODO: recast? + Vec3s* nextPathPoint = &this->pathPoints[this->currentPoint + 1]; + s32 reachedNextPoint = EnInvadepoh_StepTowardXZ(&this->actor.world.pos.x, &this->actor.world.pos.z, + nextPathPoint->x, nextPathPoint->z, speed); + + func_800B4AEC(play, &this->actor, height); + EnInvadepoh_SnapToFloor(this); + return reachedNextPoint; +} + +void EnInvadepoh_Night1Romani_MoveAlongPathTimed(EnInvadepoh* this, PlayState* play) { + s32 pad; // TODO: recast? + f32 yPosTemp = this->actor.world.pos.y; + + EnInvadepoh_Romani_ApplyProgress(this, &this->currentPoint, &this->actor.world.pos); + this->actor.world.pos.y = yPosTemp; + func_800B4AEC(play, &this->actor, 50.0f); + EnInvadepoh_SnapToFloor(this); +} + +s32 EnInvadepoh_Dog_MoveAlongPath(EnInvadepoh* this, PlayState* play) { + s32 pad; + Vec3s* currentPathPoint = &this->pathPoints[this->currentPoint]; + s32 nextPoint; + Vec3s* nextPathPoint; + f32 nextPathPointX; + f32 nextPathPointZ; + f32 curPathPointX; + f32 curPathPointZ; + Vec3f currentToNext; + Vec3f worldToNext; + Vec3f worldToCurrent; + Vec3f worldToTarget; + f32 angleToNext; + s32 reachedNext = false; + + nextPoint = this->currentPoint + this->pathStep; + + if (nextPoint >= this->endPoint) { + nextPoint = 0; + } else if (nextPoint < 0) { + nextPoint = this->endPoint - 1; + } + + nextPathPoint = &this->pathPoints[nextPoint]; + + curPathPointX = currentPathPoint->x; + curPathPointZ = currentPathPoint->z; + nextPathPointX = nextPathPoint->x; + nextPathPointZ = nextPathPoint->z; + + currentToNext.x = nextPathPointX - curPathPointX; + currentToNext.y = 0.0f; + currentToNext.z = nextPathPointZ - curPathPointZ; + + worldToNext.x = nextPathPointX - this->actor.world.pos.x; + worldToNext.y = 0.0f; + worldToNext.z = nextPathPointZ - this->actor.world.pos.z; + + worldToCurrent.x = curPathPointX - this->actor.world.pos.x; + worldToCurrent.y = 0.0f; + worldToCurrent.z = curPathPointZ - this->actor.world.pos.z; + + if (this->actor.speed > 0.0f) { + if (Math3D_AngleBetweenVectors(¤tToNext, &worldToNext, &angleToNext)) { + reachedNext = true; + } else if (angleToNext <= 0.0f) { + reachedNext = true; + } + } + // Instead of targeting nextPathPoint, the target is about 5% beyond it along that path segment. + worldToTarget.x = ((worldToNext.x + worldToCurrent.x) * 0.9f) + currentToNext.x; + worldToTarget.y = 0.0f; + worldToTarget.z = ((worldToNext.z + worldToCurrent.z) * 0.9f) + currentToNext.z; + + Math_SmoothStepToS(&this->actor.world.rot.y, Math_Atan2S_XY(worldToTarget.z, worldToTarget.x), 4, 0xFA0, 0x64); + Actor_MoveWithGravity(&this->actor); + Actor_UpdateBgCheckInfo(play, &this->actor, 30.0f, 15.0f, 0.0f, + EnInvadepoh_Dog_IsOnPath(this, 50.0f, 15.0f) + ? UPDBGCHECKINFO_FLAG_4 + : UPDBGCHECKINFO_FLAG_4 | UPDBGCHECKINFO_FLAG_1); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.world.rot.y, 3, 0x1F40, 0x64); + + return reachedNext; +} + +void EnInvadepoh_Dog_Move(EnInvadepoh* this, PlayState* play) { + Actor_MoveWithGravity(&this->actor); + Actor_UpdateBgCheckInfo(play, &this->actor, 30.0f, 15.0f, 0.0f, 5); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.world.rot.y, 3, 0x1F40, 0x64); +} + +void EnInvadepoh_Night3Romani_MoveAlongPathTimed(EnInvadepoh* this, PlayState* play) { + s32 pad; // TODO: recast? + f32 yPosTemp = this->actor.world.pos.y; + + EnInvadepoh_Romani_ApplyProgress(this, &this->currentPoint, &this->actor.world.pos); + this->actor.world.pos.y = yPosTemp; + func_800B4AEC(play, &this->actor, 50.0f); + EnInvadepoh_SnapToFloor(this); +} + +void EnInvadepoh_Romani_DesegmentTextures(void) { + TexturePtr* texPtr; + s32 i; + + if (!sRomaniTexturesDesegmented) { + sRomaniTexturesDesegmented = true; + for (i = 0, texPtr = sRomaniEyeTextures; i < 5; i++, texPtr++) { + *texPtr = Lib_SegmentedToVirtual(*texPtr); + } + for (i = 0, texPtr = sRomaniMouthTextures; i < 4; i++, texPtr++) { + *texPtr = Lib_SegmentedToVirtual(*texPtr); + } + } +} + +void EnInvadepoh_Cremia_DesegmentTextures(void) { + TexturePtr* texture; + s32 i; + + if (!sCremiaTexturesDesegmented) { + sCremiaTexturesDesegmented = true; + for (i = 0, texture = sCremiaEyeTextures; i < 6; i++, texture++) { + *texture = Lib_SegmentedToVirtual(*texture); + } + for (i = 0, texture = sCremiaMouthTextures; i < 4; i++, texture++) { + *texture = Lib_SegmentedToVirtual(*texture); + } + } +} + +void EnInvadepoh_Alien_SetTexAnim(void) { + sAlienEmptyTexAnim = Lib_SegmentedToVirtual(gAlienEmptyTexAnim); + sAlienEyeBeamTexAnim = Lib_SegmentedToVirtual(gAlienEyeBeamTexAnim); +} + +s32 EnInvadepoh_StepTowardXZ(f32* pxValue, f32* pzValue, f32 xTarget, f32 zTarget, f32 speed) { + f32 xDiff = xTarget - *pxValue; + f32 zDiff = zTarget - *pzValue; + f32 distToTarget = Math3D_XZLength(xDiff, zDiff); + + if (speed < distToTarget) { + f32 progressStep = speed / distToTarget; + + *pxValue += progressStep * xDiff; + *pzValue += progressStep * zDiff; + return false; + } else { + *pxValue = xTarget; + *pzValue = zTarget; + return true; + } +} + +s32 EnInvadepoh_SnapToFloor(EnInvadepoh* this) { + if (this->actor.floorHeight > BGCHECK_Y_MIN + 1.0f) { + this->actor.world.pos.y = this->actor.floorHeight; + return true; + } + return false; +} + +void EnInvadepoh_Event_CheckState(EnInvadepoh* this, PlayState* play) { + if (sEventState == ENINVADEPOH_EVENT_UNSET) { + if (CURRENT_DAY < 1) { + sEventState = ENINVADEPOH_EVENT_WAIT; + } else if (CURRENT_DAY == 1) { + s32 currentTime = gSaveContext.save.time; + + if (!((CLOCK_TIME(2, 30) <= currentTime) && (currentTime < CLOCK_TIME(6, 00)))) { + sEventState = ENINVADEPOH_EVENT_WAIT; + } else if (currentTime < CLOCK_TIME(5, 15)) { + s32 i; + s32 firstSpawn = CLOCK_TIME(5, 15); + s32 pad; + + for (i = 0; i < this->spawnCount; i++) { + s32 spawnTime = EnInvadepoh_Invader_GetSpawnTime(i); + + firstSpawn = MIN(spawnTime, firstSpawn); + } + if (currentTime < firstSpawn + 3601) { // 79 in-game minutes + sEventState = ENINVADEPOH_EVENT_ACTIVE; + } + } + } + if (sEventState == ENINVADEPOH_EVENT_UNSET) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_DEFENDED_AGAINST_THEM)) { + sEventState = ENINVADEPOH_EVENT_CLEAR; + } else { + sEventState = ENINVADEPOH_EVENT_FAILED; + } + } + } +} + +void EnInvadepoh_Event_SpawnInvaders(EnInvadepoh* this, PlayState* play) { + s32 pathIndex = ENINVADEPOH_GET_PATH(&this->actor); + s32 i; + + for (i = 0; i < this->spawnCount; i++) { + sAlienInvaders[i] = (EnInvadepoh*)Actor_Spawn( + &play->actorCtx, play, ACTOR_EN_INVADEPOH, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, ENINVADEPOH_SET_PARAMS(pathIndex, ENINVADEPOH_ALIEN_INVADER, i)); + if (pathIndex != 0xFF) { + Path* path = &play->setupPathList[pathIndex]; + + pathIndex = path->additionalPathIndex; + } + } +} + +void EnInvadepoh_Abductor_SpawnCow(EnInvadepoh* this, PlayState* play, s32 index) { + Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_INVADEPOH, this->actor.home.pos.x, + this->actor.home.pos.y, this->actor.home.pos.z, 0, 0, 0, + ENINVADEPOH_SET_PARAMS(0, ENINVADEPOH_COW, index)); +} + +void EnInvadepoh_Abductor_SpawnRomani(EnInvadepoh* this, PlayState* play) { + Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_INVADEPOH, this->actor.home.pos.x, + this->actor.home.pos.y, this->actor.home.pos.z, 0, 0, 0, + ENINVADEPOH_SET_PARAMS(0, ENINVADEPOH_ROMANI_ABDUCTED, 0)); +} + +void EnInvadepoh_Event_SpawnLightBall(EnInvadepoh* this, PlayState* play) { + sLightBall = (EnInvadepoh*)Actor_Spawn(&play->actorCtx, play, ACTOR_EN_INVADEPOH, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, + ENINVADEPOH_SET_PARAMS(0, ENINVADEPOH_LIGHT_BALL, 0)); +} + +void EnInvadepoh_LightBall_ReplaceTranslation(Vec3f* translation) { + MtxF* curMtxF = Matrix_GetCurrent(); + + curMtxF->xw = translation->x; + curMtxF->yw = translation->y; + curMtxF->zw = translation->z; +} + +s32 EnInvadepoh_Romani_OpenDoor(EnInvadepoh* this, PlayState* play, f32 range, s32 doorTimer) { + Actor* doorActor = play->actorCtx.actorLists[ACTORCAT_DOOR].first; + EnDoor* door; + s32 doorOpened = false; + + while (doorActor != NULL) { + if ((doorActor->id == ACTOR_EN_DOOR) && (doorActor->update != NULL)) { + door = (EnDoor*)doorActor; + if ((door->knobDoor.dyna.actor.room == this->actor.room) && + (Math3D_Vec3fDistSq(&door->knobDoor.dyna.actor.world.pos, &this->actor.world.pos) < range)) { + + door->unk_1A7 = doorTimer; + doorOpened = true; + break; + } + } + doorActor = doorActor->next; + } + return doorOpened; +} + +void EnInvadepoh_Romani_StartTextBox(EnInvadepoh* this, PlayState* play, u16 textId) { + this->textId = textId; + Message_StartTextbox(play, textId, &this->actor); +} + +void EnInvadepoh_Event_SetCutscenes(EnInvadepoh* this) { + s16 csId = this->actor.csId; + s32 i; + + for (i = 0; i < 3; i++) { + sCutsceneIds[i] = csId; + csId = CutsceneManager_GetAdditionalCsId(csId); + } +} + +s32 EnInvadepoh_Alien_LensFlareDepthCheck(PlayState* play, Vec3f* pos) { + Vec3f projectedPos; + f32 invW; + + Actor_GetProjectedPos(play, pos, &projectedPos, &invW); + if ((projectedPos.z > 1.0f) && (fabsf(projectedPos.x * invW) < 1.0f) && (fabsf(projectedPos.y * invW) < 1.0f)) { + f32 screenPosX = PROJECTED_TO_SCREEN_X(projectedPos, invW); + f32 screenPosY = PROJECTED_TO_SCREEN_Y(projectedPos, invW); + s32 wZ = (s32)(projectedPos.z * invW * 16352.0f) + 16352; + + if (wZ < SysCfb_GetZBufferInt(screenPosX, screenPosY)) { + return true; + } + } + return false; +} + +void EnInvadepoh_Event_SetClosestAlien(EnInvadepoh* this) { + s32 i; + f32 minDistSqToBarn = FLT_MAX; + s32 closestAlienIndex = -1; + + for (i = 0; i < this->spawnCount; i++) { + if ((sAlienInvaders[i] != NULL) && sAlienInvaders[i]->present) { + f32 distSqToBarn = Math3D_Vec3fDistSq(&sAlienInvaders[i]->actor.world.pos, &this->actor.world.pos); + + if (distSqToBarn < minDistSqToBarn) { + minDistSqToBarn = distSqToBarn; + closestAlienIndex = i; + } + } + sAlienStateFlags[i] &= ~ENINVADEPOH_INVADER_CLOSEST; + } + sAliensTooClose = false; + if (minDistSqToBarn <= SQ(2000.0f)) { + sAlienStateFlags[closestAlienIndex] |= ENINVADEPOH_INVADER_CLOSEST; + if (minDistSqToBarn <= SQ(340.0f)) { + sAliensTooClose = true; + } + } +} + +EnInvadepoh* EnInvadepoh_Dog_GetClosestAlien(void) { + s32 i; + + for (i = 0; i < 8; i++) { + if (sAlienStateFlags[i] & ENINVADEPOH_INVADER_CLOSEST) { + return sAlienInvaders[i]; + } + } + return NULL; +} + +s8 EnInvadepoh_Interact_GetNextAnim(EnInvadepohFaceAnimNext* nextAnims, s32 nextAnimCount) { + f32 rand = Rand_ZeroOne(); + EnInvadepohFaceAnimNext* nextAnim = nextAnims; + s32 nextIndex; + + nextAnimCount--; + for (nextIndex = 0; nextIndex < nextAnimCount; nextIndex++, nextAnim++) { + if (rand <= nextAnim->chance) { + break; + } + } + return (nextAnims + nextIndex)->index; // TODO: fake? +} + +void EnInvadepoh_Interact_SetNextAnim(EnInvadePohFaceAnimInfo* faceInfo, EnInvadepohFaceAnim* faceAnim) { + faceInfo->curAnimType = faceAnim->type; + faceInfo->curAnim = faceAnim; + faceInfo->curFrame = 0; + faceInfo->curIndex = faceAnim->frames->texIndex[0]; + if (faceInfo->curAnimType == 2) { + EnInvadepohFaceAnimDelayedBranched* faceDelayedLoopAnim = (EnInvadepohFaceAnimDelayedBranched*)faceAnim; + + faceInfo->delayTimer = Rand_S16Offset(faceDelayedLoopAnim->minDelay, faceDelayedLoopAnim->maxDelay); + } +} + +void EnInvadepoh_Interact_UpdateFixedAnim(EnInvadePohFaceAnimInfo* faceInfo, EnInvadepohFaceAnim** animSet) { + EnInvadepohFaceAnim* faceAnim = faceInfo->curAnim; + EnInvadepohFaceFrames* faceFrames = faceAnim->frames; + + if (faceInfo->curFrame < (faceFrames->count - 1)) { + faceInfo->curFrame++; + faceInfo->curIndex = faceFrames->texIndex[faceInfo->curFrame]; + } +} + +void EnInvadepoh_Interact_UpdateBranchedAnim(EnInvadePohFaceAnimInfo* faceInfo, EnInvadepohFaceAnim** animSet) { + EnInvadepohFaceAnimBranched* faceAnimLoop = (EnInvadepohFaceAnimBranched*)faceInfo->curAnim; + EnInvadepohFaceFrames* faceFrames = faceAnimLoop->anim.frames; + + if (faceInfo->curFrame < (faceFrames->count - 1)) { + faceInfo->curFrame++; + faceInfo->curIndex = faceFrames->texIndex[faceInfo->curFrame]; + } else { + EnInvadepohFaceAnim* nextAnim = + animSet[EnInvadepoh_Interact_GetNextAnim(faceAnimLoop->nextAnims, faceAnimLoop->nextCount)]; + + EnInvadepoh_Interact_SetNextAnim(faceInfo, nextAnim); + } +} + +void EnInvadepoh_Interact_UpdateDelayedBranchedAnim(EnInvadePohFaceAnimInfo* faceInfo, EnInvadepohFaceAnim** animSet) { + EnInvadepohFaceAnimDelayedBranched* faceAnimLoopDelayed = (EnInvadepohFaceAnimDelayedBranched*)faceInfo->curAnim; + EnInvadepohFaceFrames* faceFrames = faceAnimLoopDelayed->loop.anim.frames; + + if (faceInfo->curFrame < (faceFrames->count - 1)) { + faceInfo->curFrame++; + faceInfo->curIndex = faceFrames->texIndex[faceInfo->curFrame]; + } else if (faceInfo->delayTimer > 0) { + faceInfo->delayTimer--; + } else { + EnInvadepohFaceAnim* nextAnim = animSet[EnInvadepoh_Interact_GetNextAnim( + faceAnimLoopDelayed->loop.nextAnims, faceAnimLoopDelayed->loop.nextCount)]; + + EnInvadepoh_Interact_SetNextAnim(faceInfo, nextAnim); + } +} + +s8 D_80B4E99C[1] = { 0 }; +s8 D_80B4E9A0[4] = { 0, 1, 2, 0 }; +s8 D_80B4E9A4[5] = { 0, 1, 2, 1, 0 }; +s8 D_80B4E9AC[6] = { 0, 1, 2, 2, 1, 0 }; +s8 D_80B4E9B4[8] = { 0, 1, 2, 1, 0, 1, 2, 0 }; +s8 D_80B4E9BC[1] = { 1 }; +s8 D_80B4E9C0[1] = { 3 }; +EnInvadepohFaceFrames D_80B4E9C4 = { D_80B4E99C, 1 }; +EnInvadepohFaceFrames D_80B4E9CC = { D_80B4E9A0, 4 }; +EnInvadepohFaceFrames D_80B4E9D4 = { D_80B4E9A4, 5 }; +EnInvadepohFaceFrames D_80B4E9DC = { D_80B4E9AC, 6 }; +EnInvadepohFaceFrames D_80B4E9E4 = { D_80B4E9B4, 8 }; +EnInvadepohFaceFrames D_80B4E9EC = { D_80B4E9BC, 1 }; +EnInvadepohFaceFrames D_80B4E9F4 = { D_80B4E9C0, 1 }; +EnInvadepohFaceAnim D_80B4E9FC = { 0, &D_80B4E9C4 }; +EnInvadepohFaceAnimNext D_80B4EA04[4] = { + { 2, 0.5f }, + { 3, 0.9f }, + { 4, 0.97f }, + { 5, 1.0f }, +}; +EnInvadepohFaceAnimNext D_80B4EA24[1] = { 1, 1.0f }; +EnInvadepohFaceAnimDelayedBranched D_80B4EA2C = { { { 2, &D_80B4E9C4 }, 4, D_80B4EA04 }, 40, 60 }; +EnInvadepohFaceAnimBranched D_80B4EA40 = { { 1, &D_80B4E9CC }, 1, D_80B4EA24 }; +EnInvadepohFaceAnimBranched D_80B4EA50 = { { 1, &D_80B4E9D4 }, 1, D_80B4EA24 }; +EnInvadepohFaceAnimBranched D_80B4EA60 = { { 1, &D_80B4E9DC }, 1, D_80B4EA24 }; +EnInvadepohFaceAnimBranched D_80B4EA70 = { { 1, &D_80B4E9E4 }, 1, D_80B4EA24 }; +EnInvadepohFaceAnim D_80B4EA80 = { 0, &D_80B4E9EC }; +EnInvadepohFaceAnim D_80B4EA88 = { 0, &D_80B4E9F4 }; +EnInvadepohFaceAnim* D_80B4EA90[8] = { + &D_80B4E9FC, &D_80B4EA2C.loop.anim, &D_80B4EA40.anim, &D_80B4EA50.anim, + &D_80B4EA60.anim, &D_80B4EA70.anim, &D_80B4EA80, &D_80B4EA88, +}; + +s8 D_80B4EAB0[1] = { 0 }; +s8 D_80B4EAB4[1] = { 1 }; +s8 D_80B4EAB8[1] = { 2 }; +s8 D_80B4EABC[1] = { 3 }; +EnInvadepohFaceFrames D_80B4EAC0 = { D_80B4EAB0, 1 }; +EnInvadepohFaceFrames D_80B4EAC8 = { D_80B4EAB4, 1 }; +EnInvadepohFaceFrames D_80B4EAD0 = { D_80B4EAB8, 1 }; +EnInvadepohFaceFrames D_80B4EAD8 = { D_80B4EABC, 1 }; +EnInvadepohFaceAnim D_80B4EAE0 = { 0, &D_80B4EAC0 }; +EnInvadepohFaceAnim D_80B4EAE8 = { 0, &D_80B4EAC8 }; +EnInvadepohFaceAnim D_80B4EAF0 = { 0, &D_80B4EAD0 }; +EnInvadepohFaceAnim D_80B4EAF8 = { 0, &D_80B4EAD8 }; +EnInvadepohFaceAnim* D_80B4EB00[4] = { + &D_80B4EAE0, + &D_80B4EAE8, + &D_80B4EAF0, + &D_80B4EAF8, +}; + +s8 D_80B4EB10[1] = { 0 }; +s8 D_80B4EB14[4] = { 0, 1, 2, 0 }; +s8 D_80B4EB18[5] = { 0, 1, 2, 1, 0 }; +s8 D_80B4EB20[6] = { 0, 1, 2, 2, 1, 0 }; +s8 D_80B4EB28[8] = { 0, 1, 2, 1, 0, 1, 2, 0 }; +EnInvadepohFaceFrames D_80B4EB30 = { D_80B4EB10, 1 }; +EnInvadepohFaceFrames D_80B4EB38 = { D_80B4EB14, 4 }; +EnInvadepohFaceFrames D_80B4EB40 = { D_80B4EB18, 5 }; +EnInvadepohFaceFrames D_80B4EB48 = { D_80B4EB20, 6 }; +EnInvadepohFaceFrames D_80B4EB50 = { D_80B4EB28, 8 }; +EnInvadepohFaceAnim D_80B4EB58 = { 0, &D_80B4EB30 }; +EnInvadepohFaceAnimNext D_80B4EB60[4] = { + { 2, 0.5f }, + { 3, 0.9f }, + { 4, 0.95f }, + { 5, 1.0f }, +}; +EnInvadepohFaceAnimNext D_80B4EB80[1] = { 1, 1.0f }; +EnInvadepohFaceAnimDelayedBranched D_80B4EB88 = { { { 2, &D_80B4EB30 }, 4, D_80B4EB60 }, 40, 60 }; +EnInvadepohFaceAnimBranched D_80B4EB9C = { { 1, &D_80B4EB38 }, 1, D_80B4EB80 }; +EnInvadepohFaceAnimBranched D_80B4EBAC = { { 1, &D_80B4EB40 }, 1, D_80B4EB80 }; +EnInvadepohFaceAnimBranched D_80B4EBBC = { { 1, &D_80B4EB48 }, 1, D_80B4EB80 }; +EnInvadepohFaceAnimBranched D_80B4EBCC = { { 1, &D_80B4EB50 }, 1, D_80B4EB80 }; +EnInvadepohFaceAnim* D_80B4EBDC[6] = { + &D_80B4EB58, &D_80B4EB88.loop.anim, &D_80B4EB9C.anim, &D_80B4EBAC.anim, &D_80B4EBBC.anim, &D_80B4EBCC.anim, +}; + +s8 D_80B4EBF4[1] = { 0 }; +EnInvadepohFaceFrames D_80B4EBF8 = { D_80B4EBF4, 1 }; +EnInvadepohFaceAnim D_80B4EC00 = { 0, &D_80B4EBF8 }; +EnInvadepohFaceAnim* D_80B4EC08[1] = { &D_80B4EC00 }; + +void EnInvadepoh_Interact_UpdateAnimation(EnInvadePohFaceAnimInfo* faceInfo) { + static EnInvadepohFaceFunc sFaceUpdateFuncs[3] = { + EnInvadepoh_Interact_UpdateFixedAnim, + EnInvadepoh_Interact_UpdateBranchedAnim, + EnInvadepoh_Interact_UpdateDelayedBranchedAnim, + }; + + if ((faceInfo->animSet != NULL) && (faceInfo->curAnim != NULL)) { + sFaceUpdateFuncs[faceInfo->curAnimType](faceInfo, faceInfo->animSet); + } +} + +void EnInvadepoh_Interact_Init(EnInvadePohInteractInfo* interactInfo, EnInvadepohFaceAnim** eyeAnimSet, + s32 curEyeAnim, EnInvadepohFaceAnim** mouthAnimSet, s32 curMouthAnim, Vec3s* headRotTarget, + s16 maxTurnRate, f32 scaledTurnRate, f32 torsoRotXModTarget, f32 torsoRotXModStep) { + Math_Vec3s_Copy(&interactInfo->headRotTarget, headRotTarget); + interactInfo->maxTurnRate = maxTurnRate; + interactInfo->scaledTurnRate = scaledTurnRate; + interactInfo->torsoRotXModTarget = torsoRotXModTarget; + interactInfo->torsoRotXModStep = torsoRotXModStep; + if (eyeAnimSet != NULL) { + EnInvadepohFaceAnim* faceAnim = eyeAnimSet[curEyeAnim]; + + interactInfo->eyeAnim.animSet = eyeAnimSet; + interactInfo->eyeAnim.curAnimType = faceAnim->type; + interactInfo->eyeAnim.curAnim = faceAnim; + interactInfo->eyeAnim.delayTimer = 0; + interactInfo->eyeAnim.curFrame = 0; + interactInfo->eyeAnim.curIndex = faceAnim->frames->texIndex[0]; + } + if (mouthAnimSet != NULL) { + EnInvadepohFaceAnim* faceAnim = mouthAnimSet[curMouthAnim]; + + interactInfo->mouthAnim.animSet = mouthAnimSet; + interactInfo->mouthAnim.curAnimType = faceAnim->type; + interactInfo->mouthAnim.curAnim = faceAnim; + interactInfo->mouthAnim.delayTimer = 0; + interactInfo->mouthAnim.curFrame = 0; + interactInfo->mouthAnim.curIndex = faceAnim->frames->texIndex[0]; + } +} + +void EnInvadepoh_Interact_Update(EnInvadePohInteractInfo* interactInfo) { + Vec3f scaledRotStep; + Vec3s rotStep; + + rotStep.x = interactInfo->headRotTarget.x - interactInfo->headRot.x; + rotStep.y = interactInfo->headRotTarget.y - interactInfo->headRot.y; + rotStep.z = interactInfo->headRotTarget.z - interactInfo->headRot.z; + Math_Vec3s_ToVec3f(&scaledRotStep, &rotStep); + Math_Vec3f_Scale(&scaledRotStep, interactInfo->scaledTurnRate); + Math_Vec3f_ToVec3s(&rotStep, &scaledRotStep); + rotStep.x = ABS(rotStep.x); + rotStep.y = ABS(rotStep.y); + rotStep.z = ABS(rotStep.z); + rotStep.x = CLAMP_MAX(rotStep.x, interactInfo->maxTurnRate); + rotStep.y = CLAMP_MAX(rotStep.y, interactInfo->maxTurnRate); + rotStep.z = CLAMP_MAX(rotStep.z, interactInfo->maxTurnRate); + + Math_ScaledStepToS(&interactInfo->headRot.x, interactInfo->headRotTarget.x, rotStep.x); + Math_ScaledStepToS(&interactInfo->headRot.y, interactInfo->headRotTarget.y, rotStep.y); + Math_ScaledStepToS(&interactInfo->headRot.z, interactInfo->headRotTarget.z, rotStep.z); + Math_StepToF(&interactInfo->torsoRotXMod, interactInfo->torsoRotXModTarget, interactInfo->torsoRotXModStep); + + if (interactInfo->torsoRotZ != interactInfo->torsoRotZTarget) { + s16 torsoRotStep = interactInfo->torsoRotZTarget - interactInfo->torsoRotZ; + + torsoRotStep *= interactInfo->torsoMaxTurnRate; + torsoRotStep = ABS(torsoRotStep); + torsoRotStep = CLAMP(torsoRotStep, 0x64, interactInfo->torsoMaxTurnStep); + Math_ScaledStepToS(&interactInfo->torsoRotZ, interactInfo->torsoRotZTarget, torsoRotStep); + } + EnInvadepoh_Interact_UpdateAnimation(&interactInfo->eyeAnim); + EnInvadepoh_Interact_UpdateAnimation(&interactInfo->mouthAnim); +} + +void EnInvadepoh_LightBall_SpawnSparkles(EnInvadepoh* this, PlayState* play, s32 spawnCount) { + static Color_RGBA8 sLightYellow = { 255, 255, 200, 255 }; + static Color_RGBA8 sGoldenYellow = { 255, 200, 0, 0 }; + s32 i; + Vec3f pos; + Vec3f velocity; + Vec3f accel; + f32 spawnOffset; + s16 spawnAngle = 0; + + for (i = 0; i < spawnCount; i++) { + spawnAngle += (s16)(0x10000 * 1.0f / spawnCount); + spawnOffset = (Rand_ZeroOne() * 0.5f) + 0.5f; + + pos.x = Math_SinS(spawnAngle) * spawnOffset; + pos.z = Math_CosS(spawnAngle) * spawnOffset; + + velocity.x = (Rand_ZeroOne() * 16.0f) + (pos.x * 30.0f) - 8.0f; + velocity.y = -8.0f; + velocity.z = (Rand_ZeroOne() * 16.0f) + (pos.z * 30.0f) - 8.0f; + + accel.x = velocity.x * -0.025f; + accel.y = velocity.y * -0.025f; + accel.z = velocity.z * -0.025f; + + pos.x = (pos.x * 100.0f) + this->actor.world.pos.x; + pos.y = (Rand_ZeroOne() * 180.0f) + this->actor.world.pos.y - 90.0f; + pos.z = (pos.z * 100.0f) + this->actor.world.pos.z; + EffectSsKirakira_SpawnDispersed(play, &pos, &velocity, &accel, &sLightYellow, &sGoldenYellow, 6000, -40); + } +} + +s32 EnInvadepoh_Invader_SpawnWarp(Vec3f* spawnPos) { + EnInvadepohWarpEffect* warpEffect = sWarpEffects; + s32 i; + + for (i = 0; i < ARRAY_COUNT(sWarpEffects); i++, warpEffect++) { + if (warpEffect->timer <= 0) { + warpEffect->type = 0; + warpEffect->timer = 40; + Math_Vec3f_Copy(&warpEffect->pos, spawnPos); + warpEffect->alpha = 0; + return true; + } + } + return false; +} + +void EnInvadepoh_Event_WarpFade(EnInvadepohWarpEffect* warpEffect) { + if (warpEffect->timer > 20) { + if (warpEffect->alpha < 125) { + warpEffect->alpha += 10; + } else { + warpEffect->alpha = 135; + } + } else { + if (warpEffect->alpha > 13) { + warpEffect->alpha -= 13; + } else { + warpEffect->alpha = 0; + } + } +} + +s32 EnInvadepoh_Event_UpdateWarps(void) { + static void (*sEnInvadepohWarpUpdateFuncs[1])(EnInvadepohWarpEffect*) = { EnInvadepoh_Event_WarpFade }; + s32 warpActive = false; + s32 i; + EnInvadepohWarpEffect* warpEffect; + + for (i = 0, warpEffect = sWarpEffects; i < 10; i++, warpEffect++) { + if (warpEffect->timer > 0) { + sEnInvadepohWarpUpdateFuncs[warpEffect->type](warpEffect); + warpActive = true; + warpEffect->timer--; + } + } + return warpActive; +} + +void EnInvadepoh_Event_Init(Actor* thisx, PlayState* play) { + // PlayState* play = play2; Massive problems + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 spawnCount; + s32 pathIndex; + + this->actor.flags |= ACTOR_FLAG_20; + pathIndex = ENINVADEPOH_GET_PATH(&this->actor); + + for (spawnCount = 1; spawnCount < 8; spawnCount++) { + Path* path = &play->setupPathList[pathIndex]; + + pathIndex = path->additionalPathIndex; + if (pathIndex == 0xFF) { + break; + } + } + this->spawnCount = spawnCount; + EnInvadepoh_Event_CheckState(this, play); + EnInvadepoh_Event_SetCutscenes(this); + func_800BC154(play, &play->actorCtx, &this->actor, ACTORCAT_SWITCH); + if (sEventState == ENINVADEPOH_EVENT_WAIT) { + EnInvadepoh_Event_SetupWait(this); + } else if (sEventState == ENINVADEPOH_EVENT_ACTIVE) { + if (gSaveContext.save.time < CLOCK_TIME(2, 31)) { + EnInvadepoh_Event_SetupWait(this); + } else { + EnInvadepoh_Event_SpawnLightBall(this, play); + EnInvadepoh_Event_SpawnInvaders(this, play); + AudioSeq_QueueSeqCmd(0x8000 | NA_BGM_ALIEN_INVASION); + EnInvadepoh_Event_SetupInvasion(this); + } + } else if (sEventState == ENINVADEPOH_EVENT_CLEAR) { + if (gSaveContext.save.entrance == ENTRANCE(ROMANI_RANCH, 6)) { + EnInvadepoh_Event_SetupRomaniReward(this); + } else if (gSaveContext.save.entrance == ENTRANCE(ROMANI_RANCH, 7)) { + EnInvadepoh_Event_SetupGoodEnd(this); + } else { + EnInvadepoh_Event_SetupGoodEnd(this); + } + } else if (sEventState == ENINVADEPOH_EVENT_FAILED) { + EnInvadepoh_Event_SetupBadEnd(this); + } +} + +InitChainEntry sAlienInitChain[4] = { + ICHAIN_F32(uncullZoneForward, 20000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 600, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_STOP), +}; +void EnInvadepoh_Alien_Init(Actor* thisx, PlayState* play) { + // PlayState* play = play2; Stack size problems + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Actor_ProcessInitChain(&this->actor, sAlienInitChain); + Collider_InitCylinder(play, &this->collider); + ActorShape_Init(&this->actor.shape, 6800.0f, ActorShadow_DrawWhiteCircle, 150.0f); + this->actor.shape.shadowAlpha = 140; + this->actor.flags = ACTOR_FLAG_80000000 | ACTOR_FLAG_IGNORE_QUAKE | ACTOR_FLAG_10; + if (ENINVADEPOH_GET_TYPE(&this->actor) == ENINVADEPOH_ALIEN_ABDUCTOR) { + this->actor.update = EnInvadepoh_Abuductor_WaitForObject; + this->actor.world.pos.y = this->actor.home.pos.y + 150.0f; + } else { + this->actor.update = EnInvadepoh_Invader_WaitForObject; + Collider_SetCylinder(play, &this->collider, &this->actor, &sAlienCylinderInit); + this->actor.colChkInfo.mass = 40; + } + this->bankIndex = Object_GetSlot(&play->objectCtx, OBJECT_UCH); + if (this->bankIndex < 0) { + Actor_Kill(&this->actor); + } +} + +InitChainEntry sCowInitChain[4] = { + ICHAIN_F32(uncullZoneForward, 20000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 300, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_STOP), +}; +void EnInvadepoh_Cow_Init(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Actor_ProcessInitChain(&this->actor, sCowInitChain); + this->actor.update = EnInvadepoh_Cow_WaitForObject; + Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_INVADEPOH, 0.0f, 0.0f, 0.0f, 0, 0, 0, + ENINVADEPOH_SET_PARAMS(0, ENINVADEPOH_COW_TAIL, 0)); + this->bankIndex = Object_GetSlot(&play->objectCtx, OBJECT_COW); + if (this->bankIndex < 0) { + Actor_Kill(&this->actor); + } +} + +InitChainEntry sCowTailInitChain[4] = { + ICHAIN_F32(uncullZoneForward, 20000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 100, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_STOP), +}; +void EnInvadepoh_CowTail_Init(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Actor_ProcessInitChain(&this->actor, sCowTailInitChain); + this->actor.update = EnInvadepoh_CowTail_WaitForObject; + this->bankIndex = Object_GetSlot(&play->objectCtx, OBJECT_COW); + if (this->bankIndex < 0) { + Actor_Kill(&this->actor); + } +} + +InitChainEntry sRomaniInitChain[5] = { + ICHAIN_F32(uncullZoneForward, 20000, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneScale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 100, ICHAIN_CONTINUE), ICHAIN_F32(targetArrowOffset, 1500, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_STOP), +}; +void EnInvadepoh_Romani_Init(Actor* thisx, PlayState* play) { + // PlayState* play = play2; Stack management problems + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 romaniType = ENINVADEPOH_GET_TYPE(&this->actor); + + Actor_ProcessInitChain(&this->actor, sRomaniInitChain); + if ((romaniType == ENINVADEPOH_ROMANI_NIGHT_1) || (romaniType == ENINVADEPOH_ROMANI_NIGHT_3)) { + this->actor.targetMode = 3; + } else { + this->actor.targetMode = 6; + } + func_800BC154(play, &play->actorCtx, &this->actor, ACTORCAT_NPC); + Collider_InitCylinder(play, &this->collider); + if (romaniType != ENINVADEPOH_ROMANI_ABDUCTED) { + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 18.0f); + Collider_SetCylinder(play, &this->collider, &this->actor, &sHumanCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + } + if (romaniType == ENINVADEPOH_ROMANI_ABDUCTED) { + this->actor.update = EnInvadepoh_AbductedRomani_WaitForObject; + } else if (romaniType == ENINVADEPOH_ROMANI_CONFUSED) { + this->actor.update = EnInvadepoh_ConfusedRomani_WaitForObject; + this->actor.flags = ACTOR_FLAG_10 | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_TARGETABLE; + } else if (romaniType == ENINVADEPOH_ROMANI_NIGHT_1) { + this->actor.update = EnInvadepoh_Night1Romani_WaitForObject; + } else if (romaniType == ENINVADEPOH_ROMANI_BARN) { + this->actor.update = EnInvadepoh_BarnRomani_WaitForObject; + } else if (romaniType == ENINVADEPOH_ROMANI_REWARD) { + this->actor.update = EnInvadepoh_RewardRomani_WaitForObject; + } else { + this->actor.update = EnInvadepoh_Night3Romani_WaitForObject; + } + this->bankIndex = Object_GetSlot(&play->objectCtx, OBJECT_MA1); + if (this->bankIndex < 0) { + Actor_Kill(&this->actor); + } + if (romaniType == ENINVADEPOH_ROMANI_CONFUSED) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_DEFENDED_AGAINST_THEM)) { + Actor_Kill(&this->actor); + } + } else if (romaniType == ENINVADEPOH_ROMANI_NIGHT_1) { + if ((CLOCK_TIME(6, 00) > gSaveContext.save.time) && (gSaveContext.save.time >= CLOCK_TIME(2, 15))) { + Actor_Kill(&this->actor); + } + } else if (romaniType == ENINVADEPOH_ROMANI_BARN) { + // nothing + } else if (romaniType == ENINVADEPOH_ROMANI_REWARD) { + if (gSaveContext.save.entrance != ENTRANCE(ROMANI_RANCH, 6)) { + Actor_Kill(&this->actor); + } + } else if (romaniType == ENINVADEPOH_ROMANI_NIGHT_3) { + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_DEFENDED_AGAINST_THEM)) { + Actor_Kill(&this->actor); + } + sRomani = this; + } +} + +InitChainEntry sLightBallInitChain[6] = { + ICHAIN_F32(uncullZoneForward, 20000, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneScale, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_CONTINUE), ICHAIN_VEC3S(shape, 0, ICHAIN_CONTINUE), + ICHAIN_F32(terminalVelocity, -100, ICHAIN_CONTINUE), ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; +void EnInvadepoh_LightBall_Init(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Actor_ProcessInitChain(&this->actor, sLightBallInitChain); + this->actor.update = EnInvadepoh_LightBall_Update; + this->actor.draw = EnInvadepoh_LightBall_Draw; + func_800BC154(play, &play->actorCtx, &this->actor, ACTORCAT_NPC); + if ((sEventState == ENINVADEPOH_EVENT_WAIT) || (gSaveContext.save.time < CLOCK_TIME(2, 31))) { + this->actor.world.pos.x += sLightBallSpawnOffset.x; + this->actor.world.pos.y += sLightBallSpawnOffset.y + 3000.0f; + this->actor.world.pos.z += sLightBallSpawnOffset.z; + EnInvadepoh_LightBall_SetupDescend(this); + } else if (sEventState == ENINVADEPOH_EVENT_ACTIVE) { + this->actor.world.pos.y += 1500.0f; + EnInvadepoh_LightBall_SetupAboveBarn(this); + } else { + Actor_Kill(&this->actor); + } +} + +InitChainEntry sDogInitChain[6] = { + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 50, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 50, ICHAIN_CONTINUE), + ICHAIN_F32(gravity, -3, ICHAIN_CONTINUE), + ICHAIN_U8(targetMode, 4, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 7, ICHAIN_STOP), +}; +void EnInvadepoh_Dog_Init(Actor* thisx, PlayState* play) { + // PlayState* play = play2; Stack size problems + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Actor_ProcessInitChain(&this->actor, sDogInitChain); + this->actor.update = EnInvadepoh_Dog_WaitForObject; + Collider_InitCylinder(play, &this->collider); + Collider_SetCylinder(play, &this->collider, &this->actor, &sDogCylinderInit); + this->actor.colChkInfo.mass = 80; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 24.0f); + this->bankIndex = Object_GetSlot(&play->objectCtx, OBJECT_DOG); + if (this->bankIndex < 0) { + Actor_Kill(&this->actor); + } +} + +InitChainEntry sCremiaInitChain[6] = { + ICHAIN_F32(uncullZoneForward, 20000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 150, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 1500, ICHAIN_CONTINUE), + ICHAIN_U8(targetMode, 3, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_STOP), +}; +void EnInvadepoh_Cremia_Init(Actor* thisx, PlayState* play) { + // PlayState* play = play2; Stack size problems + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Actor_ProcessInitChain(&this->actor, sCremiaInitChain); + this->actor.update = EnInvadepoh_Cremia_WaitForObject; + func_800BC154(play, &play->actorCtx, &this->actor, ACTORCAT_NPC); + Collider_InitCylinder(play, &this->collider); + Collider_SetCylinder(play, &this->collider, &this->actor, &sHumanCylinderInit); + this->actor.colChkInfo.mass = MASS_HEAVY; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 18.0f); + this->bankIndex = Object_GetSlot(&play->objectCtx, OBJECT_MA2); + if (this->bankIndex < 0) { + Actor_Kill(&this->actor); + } + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_DEFENDED_AGAINST_THEM)) { + Actor_Kill(&this->actor); + } + sCremia = this; +} + +void EnInvadepoh_Init(Actor* thisx, PlayState* play) { + static ActorFunc sInitFuncs[14] = { + /* 0x0 */ EnInvadepoh_Event_Init, // Event handler + /* 0x1 */ EnInvadepoh_Alien_Init, // Alien invader + /* 0x2 */ EnInvadepoh_Cow_Init, // Abducted cow + /* 0x3 */ EnInvadepoh_CowTail_Init, // Cow tail + /* 0x4 */ EnInvadepoh_Romani_Init, // Abducted Romani + /* 0x5 */ EnInvadepoh_Romani_Init, // Confused Romani + /* 0x6 */ EnInvadepoh_LightBall_Init, // Light ball + /* 0x7 */ EnInvadepoh_Romani_Init, // Night 1 Romani + /* 0x8 */ EnInvadepoh_Romani_Init, // Unknown Romani + /* 0x9 */ EnInvadepoh_Romani_Init, // Reward Romani + /* 0xA */ EnInvadepoh_Dog_Init, // Dog + /* 0xB */ EnInvadepoh_Cremia_Init, // Night 3 Cremia + /* 0xC */ EnInvadepoh_Romani_Init, // Night 3 Romani + /* 0xD */ EnInvadepoh_Alien_Init, // Alien abductor + }; + + sInitFuncs[ENINVADEPOH_GET_TYPE(thisx)](thisx, play); +} + +void EnInvadepoh_Event_Destroy(Actor* thisx, PlayState* play2) { +} + +void EnInvadepoh_Invader_Destroy(Actor* thisx, PlayState* play2) { + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 index = ENINVADEPOH_GET_INDEX(&this->actor); + PlayState* play = play2; + + Collider_DestroyCylinder(play, &this->collider); + sAlienInvaders[index] = NULL; +} + +void EnInvadepoh_Cow_Destroy(Actor* thisx, PlayState* play2) { + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if (this->actor.parent != NULL) { + this->actor.parent->child = NULL; + } + if (this->actor.child != NULL) { + this->actor.child->parent = NULL; + } +} + +void EnInvadepoh_CowTail_Destroy(Actor* thisx, PlayState* play2) { + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if (this->actor.parent != NULL) { + this->actor.parent->child = NULL; + } +} + +void EnInvadepoh_Romani_Destroy(Actor* thisx, PlayState* play2) { + PlayState* play = play2; // both or neither recast required + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Collider_DestroyCylinder(play, &this->collider); +} + +void EnInvadepoh_LightBall_Destroy(Actor* thisx, PlayState* play2) { + sLightBall = NULL; +} + +void EnInvadepoh_Dog_Destroy(Actor* thisx, PlayState* play2) { + PlayState* play = play2; // both or neither recast required + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Collider_DestroyCylinder(play, &this->collider); +} + +void EnInvadepoh_Cremia_Destroy(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Collider_DestroyCylinder(play, &this->collider); + sCremia = NULL; +} + +void EnInvadepoh_Romani_Destroy2(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Collider_DestroyCylinder(play, &this->collider); + sRomani = NULL; +} + +void EnInvadepoh_Abductor_Destroy(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; // this recast required + + Collider_DestroyCylinder(play, &this->collider); + if (this->actor.child != NULL) { + this->actor.child->parent = NULL; + } +} + +void EnInvadepoh_Destroy(Actor* thisx, PlayState* play) { + static ActorFunc sDestroyFuncs[14] = { + /* 0x0 */ EnInvadepoh_Event_Destroy, // Event handler + /* 0x1 */ EnInvadepoh_Invader_Destroy, // Alien invader + /* 0x2 */ EnInvadepoh_Cow_Destroy, // Abducted cow + /* 0x3 */ EnInvadepoh_CowTail_Destroy, // Cow tail + /* 0x4 */ EnInvadepoh_Romani_Destroy, // Abducted Romani + /* 0x5 */ EnInvadepoh_Romani_Destroy, // Confused Romani + /* 0x6 */ EnInvadepoh_LightBall_Destroy, // Light ball + /* 0x7 */ EnInvadepoh_Romani_Destroy, // Night 1 Romani + /* 0x8 */ EnInvadepoh_Romani_Destroy, // Unknown Romani + /* 0x9 */ EnInvadepoh_Romani_Destroy, // Reward Romani + /* 0xA */ EnInvadepoh_Dog_Destroy, // Dog + /* 0xB */ EnInvadepoh_Cremia_Destroy, // Night 3 Cremia + /* 0xC */ EnInvadepoh_Romani_Destroy2, // Night 3 Romani + /* 0xD */ EnInvadepoh_Abductor_Destroy, // Alien abductor + }; + + sDestroyFuncs[ENINVADEPOH_GET_TYPE(thisx)](thisx, play); +} + +void EnInvadepoh_Event_SetupWait(EnInvadepoh* this) { + sEventState = ENINVADEPOH_EVENT_WAIT; + this->actionFunc = EnInvadepoh_Event_Wait; +} + +void EnInvadepoh_Event_Wait(EnInvadepoh* this, PlayState* play) { + if ((CLOCK_TIME(6, 00) > gSaveContext.save.time) && (gSaveContext.save.time >= CLOCK_TIME(2, 30))) { + EnInvadepoh_Event_SpawnLightBall(this, play); + EnInvadepoh_Event_SpawnInvaders(this, play); + EnInvadepoh_Event_SetupQueueInvasionCs(this); + } +} + +void EnInvadepoh_Event_SetupQueueInvasionCs(EnInvadepoh* this) { + sEventState = ENINVADEPOH_EVENT_ACTIVE; + this->actionTimer = 2; + this->actionFunc = EnInvadepoh_Event_QueueInvasionCs; +} + +void EnInvadepoh_Event_QueueInvasionCs(EnInvadepoh* this, PlayState* play) { + if (this->actionTimer > 0) { + this->actionTimer--; + } else if (CutsceneManager_IsNext(sCutsceneIds[0])) { + CutsceneManager_StartWithPlayerCs(sCutsceneIds[0], &this->actor); + EnInvadepoh_Event_SetupInvasionCs(this); + } else { + CutsceneManager_Queue(sCutsceneIds[0]); + } +} + +void EnInvadepoh_Event_SetupInvasionCs(EnInvadepoh* this) { + sEventState = ENINVADEPOH_EVENT_ACTIVE; + this->actionTimer = 160; + this->actionFunc = EnInvadepoh_Event_InvasionCs; +} + +void EnInvadepoh_Event_InvasionCs(EnInvadepoh* this, PlayState* play) { + static s16 sAlienSpawnTimes[8] = { + 130, 125, 115, 100, 80, 78, 76, 74, + }; + s32 i; + + for (i = 0; i < 8; i++) { + if (this->actionTimer == sAlienSpawnTimes[i]) { + sAlienStateFlags[i] |= ENINVADEPOH_INVADER_ACTIVE; + } + } + this->actionTimer--; + if (this->actionTimer <= 0) { + CutsceneManager_Stop(sCutsceneIds[0]); + AudioSeq_QueueSeqCmd(0x8000 | NA_BGM_ALIEN_INVASION); + EnInvadepoh_Event_SetupInvasion(this); + } +} + +void EnInvadepoh_Event_SetupInvasion(EnInvadepoh* this) { + sEventState = ENINVADEPOH_EVENT_ACTIVE; + this->actionFunc = EnInvadepoh_Event_Invasion; +} + +void EnInvadepoh_Event_Invasion(EnInvadepoh* this, PlayState* play) { + if ((CLOCK_TIME(6, 00) > gSaveContext.save.time) && (gSaveContext.save.time >= CLOCK_TIME(5, 15))) { + SET_WEEKEVENTREG(WEEKEVENTREG_DEFENDED_AGAINST_THEM); + EnInvadepoh_Event_SetupQueueVictoryCs(this); + } else { + s32 i; + + EnInvadepoh_Event_SetClosestAlien(this); + for (i = 0; i < this->spawnCount; i++) { + if ((sAlienInvaders[i] != NULL) && (sAlienInvaders[i]->atBarn)) { + EnInvadepoh_Event_SetupFailure(this); + break; + } + } + } +} + +void EnInvadepoh_Event_SetupQueueVictoryCs(EnInvadepoh* this) { + sEventState = ENINVADEPOH_EVENT_CLEAR; + this->actionFunc = EnInvadepoh_Event_QueueVictoryCs; +} + +void EnInvadepoh_Event_QueueVictoryCs(EnInvadepoh* this, PlayState* play) { + if (CutsceneManager_IsNext(sCutsceneIds[1])) { + CutsceneManager_StartWithPlayerCs(sCutsceneIds[1], &this->actor); + EnInvadepoh_Event_SetupVictoryCs(this); + } else { + CutsceneManager_Queue(sCutsceneIds[1]); + } +} + +void EnInvadepoh_Event_SetupVictoryCs(EnInvadepoh* this) { + sEventState = ENINVADEPOH_EVENT_CLEAR; + this->actionTimer = 110; + this->actionFunc = EnInvadepoh_Event_VictoryCs; +} + +void EnInvadepoh_Event_VictoryCs(EnInvadepoh* this, PlayState* play) { + if (this->actionTimer == 100) { + Audio_PlayFanfare(NA_BGM_CLEAR_EVENT); + } + this->actionTimer--; + if (this->actionTimer <= 0) { + play->nextEntrance = ENTRANCE(ROMANI_RANCH, 6); + gSaveContext.nextCutsceneIndex = 0; + play->transitionTrigger = TRANS_TRIGGER_START; + play->transitionType = TRANS_TYPE_73; + gSaveContext.nextTransitionType = TRANS_TYPE_72; + D_801BDAA0 = 1; + gHorseIsMounted = 0; + EnInvadepoh_Event_SetupGoodEnd(this); + } +} + +void EnInvadepoh_Event_SetupRomaniReward(EnInvadepoh* this) { + sEventState = ENINVADEPOH_EVENT_CLEAR; + this->actionFunc = EnInvadepoh_Event_RomaniReward; +} + +void EnInvadepoh_Event_RomaniReward(EnInvadepoh* this, PlayState* play) { + if (sRewardFinished) { + play->nextEntrance = ENTRANCE(ROMANI_RANCH, 7); + gSaveContext.nextCutsceneIndex = 0; + play->transitionTrigger = TRANS_TRIGGER_START; + play->transitionType = TRANS_TYPE_72; + gSaveContext.nextTransitionType = TRANS_TYPE_72; + EnInvadepoh_Event_SetupGoodEnd(this); + } +} + +void EnInvadepoh_Event_SetupGoodEnd(EnInvadepoh* this) { + sEventState = ENINVADEPOH_EVENT_CLEAR; + this->actionFunc = EnInvadepoh_Event_GoodEnd; +} + +void EnInvadepoh_Event_GoodEnd(EnInvadepoh* this, PlayState* play) { +} + +void EnInvadepoh_Event_SetupFailure(EnInvadepoh* this) { + sEventState = ENINVADEPOH_EVENT_FAILED; + this->actionFunc = EnInvadepoh_Event_Failure; +} + +void EnInvadepoh_Event_Failure(EnInvadepoh* this, PlayState* play) { + play->nextEntrance = ENTRANCE(ROMANI_RANCH, 0); + gSaveContext.nextCutsceneIndex = 0xFFF3; + play->transitionTrigger = TRANS_TRIGGER_START; + play->transitionType = TRANS_TYPE_72; + gSaveContext.nextTransitionType = TRANS_TYPE_72; + SET_WEEKEVENTREG(WEEKEVENTREG_89_10); + EnInvadepoh_Event_SetupBadEnd(this); +} + +void EnInvadepoh_Event_SetupBadEnd(EnInvadepoh* this) { + sEventState = ENINVADEPOH_EVENT_FAILED; + this->actionFunc = EnInvadepoh_Event_BadEnd; +} + +void EnInvadepoh_Event_BadEnd(EnInvadepoh* this, PlayState* play) { +} + +void EnInvadepoh_Event_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + this->actionFunc(this, play); + + this->actor.draw = EnInvadepoh_Event_UpdateWarps() ? EnInvadepoh_Event_Draw : NULL; +} + +void EnInvadepoh_Invader_SetupWaitForEvent(EnInvadepoh* this) { + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.acFlags &= ~AC_ON; + this->collider.base.ocFlags1 &= ~OC1_ON; + this->actor.flags &= ~ACTOR_FLAG_80000000; + this->alpha = 0; + this->actor.draw = NULL; + this->present = false; + this->drawDeathFlash = false; + this->eyeBeamAlpha = 0; + this->actionFunc = EnInvadepoh_Invader_WaitForEvent; +} + +void EnInvadepoh_Invader_WaitForEvent(EnInvadepoh* this, PlayState* play) { + EnInvadepoh_Invader_SetProgress(this); + EnInvadepoh_Invader_ApplyProgress(this, play); + EnInvadepoh_Invader_TurnToPath(this, 0x320, 0); + if (sAlienStateFlags[ENINVADEPOH_GET_INDEX(&this->actor)] & ENINVADEPOH_INVADER_ACTIVE) { + Actor_SetScale(&this->actor, 0.01f); + EnInvadepoh_SnapToFloor(this); + EnInvadepoh_Invader_SpawnWarp(&this->actor.world.pos); + EnInvadepoh_Invader_SetupWarpIn(this); + } +} + +void EnInvadepoh_Invader_SetupWaitToRespawn(EnInvadepoh* this) { + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.acFlags &= ~AC_ON; + this->collider.base.ocFlags1 &= ~OC1_ON; + this->actor.flags &= ~ACTOR_FLAG_80000000; + this->alpha = 0; + this->actor.draw = NULL; + this->present = false; + this->drawDeathFlash = false; + this->eyeBeamAlpha = 0; + this->actionFunc = EnInvadepoh_Invade_WaitToRespawn; +} + +void EnInvadepoh_Invade_WaitToRespawn(EnInvadepoh* this, PlayState* play) { + EnInvadepoh_Invader_SetProgress(this); + EnInvadepoh_Invader_ApplyProgress(this, play); + EnInvadepoh_Invader_TurnToPath(this, 0x320, 0); + if (this->progress > 0.0f) { + Actor_SetScale(&this->actor, 0.01f); + EnInvadepoh_SnapToFloor(this); + EnInvadepoh_Invader_SpawnWarp(&this->actor.world.pos); + EnInvadepoh_Invader_SetupWarpIn(this); + } +} + +void EnInvadepoh_Invader_SetupWarpIn(EnInvadepoh* this) { + Animation_MorphToLoop(&this->skelAnime, &gAlienFloatAnim, -6.0f); + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.acFlags &= ~AC_ON; + this->collider.base.ocFlags1 &= ~OC1_ON; + this->alpha = 0; + this->actor.draw = EnInvadepoh_Alien_Draw; + this->present = true; + this->drawDeathFlash = false; + this->eyeBeamAlpha = 0; + this->actor.flags |= ACTOR_FLAG_80000000; + this->actionFunc = EnInvadepoh_Invader_WarpIn; +} + +void EnInvadepoh_Invader_WarpIn(EnInvadepoh* this, PlayState* play) { + EnInvadepoh_Invader_SetProgress(this); + EnInvadepoh_Invader_ApplyProgress(this, play); + EnInvadepoh_Invader_TurnToPath(this, 0x320, 0); + Actor_PlaySfx_Flagged(&this->actor, NA_SE_EN_FOLLOWERS_BEAM_PRE - SFX_FLAG); + if (this->progress >= 0.9999f) { + this->atBarn = true; + } + if (this->alpha >= 249) { + this->alpha = 255; + } else { + this->alpha += 6; + } + if (this->alpha > 128) { + this->collider.base.atFlags |= AT_ON; + this->collider.base.acFlags |= AC_ON; + this->collider.base.ocFlags1 |= OC1_ON; + } + if (this->alpha == 255) { + if (this->eyeBeamAlpha >= 245) { + this->eyeBeamAlpha = 255; + EnInvadepoh_Invader_SetupAttack(this); + } else { + this->eyeBeamAlpha += 10; + } + } +} + +void EnInvadepoh_Invader_SetupAttack(EnInvadepoh* this) { + if (this->skelAnime.animation != &gAlienFloatAnim) { + Animation_MorphToLoop(&this->skelAnime, &gAlienFloatAnim, -6.0f); + } + this->collider.base.atFlags |= AT_ON; + this->collider.base.acFlags |= AC_ON; + this->collider.base.ocFlags1 |= OC1_ON; + this->alpha = 255; + this->actor.draw = EnInvadepoh_Alien_Draw; + this->present = true; + this->drawDeathFlash = false; + this->eyeBeamAlpha = 255; + this->actor.flags |= ACTOR_FLAG_80000000; + this->actionFunc = EnInvadepoh_Invader_Attack; +} + +void EnInvadepoh_Invader_Attack(EnInvadepoh* this, PlayState* play) { + EnInvadepoh_Invader_SetProgress(this); + EnInvadepoh_Invader_ApplyProgress(this, play); + EnInvadepoh_Invader_TurnToPath(this, 0x320, 0); + Actor_PlaySfx_Flagged(&this->actor, NA_SE_EN_FOLLOWERS_BEAM_PRE - SFX_FLAG); + if (this->progress >= 0.9999f) { + this->atBarn = true; + } +} + +void EnInvadepoh_Invader_SetupHitstun(EnInvadepoh* this) { + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.acFlags &= ~AC_ON; + this->collider.base.ocFlags1 |= OC1_ON; + Animation_PlayLoop(&this->skelAnime, &gAlienJerkingAnim); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0x10); + this->alpha = 255; + this->actor.draw = EnInvadepoh_Alien_Draw; + this->present = true; + this->drawDeathFlash = false; + this->eyeBeamAlpha = 0; + this->actionTimer = 8; + this->stateTimer = 0; + this->actor.flags |= ACTOR_FLAG_80000000; + this->actionFunc = EnInvadepoh_Invader_Hitstun; +} + +void EnInvadepoh_Invader_Hitstun(EnInvadepoh* this, PlayState* play) { + EnInvadepoh_Invader_Knockback(this, play); + this->actionTimer--; + if (this->actionTimer <= 0) { + EnInvadepoh_Invader_SetupDeath(this); + } +} + +void EnInvadepoh_Invader_SetupDeath(EnInvadepoh* this) { + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.acFlags &= ~AC_ON; + this->collider.base.ocFlags1 &= ~OC1_ON; + Animation_PlayLoop(&this->skelAnime, &gAlienDeathAnim); + this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; + this->actionTimer = 10; + this->alpha = 255; + this->actor.draw = EnInvadepoh_Alien_Draw; + this->present = true; + this->drawDeathFlash = false; + this->eyeBeamAlpha = 255; + this->actor.flags |= ACTOR_FLAG_80000000; + this->actionFunc = EnInvadepoh_Invader_Death; +} + +void EnInvadepoh_Invader_Death(EnInvadepoh* this, PlayState* play) { + static Vec3f sDeathScales[5] = { + { 0.01f, 0.01f, 0.01f }, { 0.02f, 0.01f, 0.005f }, { -0.01f, 0.0f, 0.0f }, + { 0.01f, 0.01f, 0.01f }, { 0.005f, 0.01f, 0.02f }, + }; + static Vec3f sDeathFlashScales[7] = { + { 0.0005f, 0.027999999f, 0.01f }, { -0.01f, 0.0f, 0.0f }, { -0.01f, 0.0f, 0.0f }, + { 0.016f, 0.0004f, 0.01f }, { -0.01f, 0.0f, 0.0f }, { 0.0005f, 0.0005f, 0.0005f }, + { 0.0002f, 0.0002f, 0.0002f }, + }; + Vec3f* deathScale; + + EnInvadepoh_Invader_EmptyFunc(this); + if (this->stateTimer < 5) { + deathScale = &sDeathScales[this->stateTimer]; + if (deathScale->x > 0.0f) { + this->present = true; + this->eyeBeamAlpha = 255; + Math_Vec3f_Copy(&this->actor.scale, deathScale); + } else { + this->present = false; + this->eyeBeamAlpha = 0; + } + } else { + this->present = false; + this->eyeBeamAlpha = 0; + } + if ((this->stateTimer >= 2) && (this->stateTimer < 9)) { + deathScale = &sDeathFlashScales[this->stateTimer - 2]; + if (deathScale->x > 0.0f) { + this->drawDeathFlash = true; + Math_Vec3f_Copy(&this->deathFlashScale, deathScale); + } else { + this->drawDeathFlash = false; + } + } else { + this->drawDeathFlash = false; + } + this->stateTimer++; + if (this->actionTimer == 8) { + Actor_Spawn(&play->actorCtx, play, ACTOR_EN_CLEAR_TAG, this->actor.world.pos.x, this->actor.world.pos.y - 10.0f, + this->actor.world.pos.z, 0, 0, 3, CLEAR_TAG_SMOKE); + } + if (this->actionTimer == 8) { + Enemy_StartFinishingBlow(play, &this->actor); + } + this->actionTimer--; + if (this->actionTimer <= 0) { + EnInvadepoh_Invader_SetRespawnTime(ENINVADEPOH_GET_INDEX(&this->actor)); + EnInvadepoh_Invader_AddKill(); + Item_DropCollectible(play, &this->actor.world.pos, 8); + EnInvadepoh_Invader_SetupWaitToRespawn(this); + } +} + +void EnInvadepoh_Invader_WaitForObject(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if (Object_IsLoaded(&play->objectCtx, this->bankIndex)) { + this->actor.objectSlot = this->bankIndex; + Actor_SetObjectDependency(play, &this->actor); + EnInvadepoh_Alien_SetTexAnim(); + this->actor.update = EnInvadepoh_Invader_Update; + SkelAnime_InitFlex(play, &this->skelAnime, &gAlienSkel, &gAlienFloatAnim, this->jointTable, this->morphTable, + ALIEN_LIMB_MAX); + this->skelAnime.curFrame = (ENINVADEPOH_GET_INDEX(&this->actor)) * this->skelAnime.endFrame / 8.0f; + EnInvadepoh_Invader_SetupPath(this, play); + EnInvadepoh_Invader_SetProgress(this); + EnInvadepoh_Invader_ApplyProgress(this, play); + EnInvadepoh_SetYawAlongPath(this); + EnInvadepoh_SnapToFloor(this); + if ((sEventState == ENINVADEPOH_EVENT_WAIT) || (gSaveContext.save.time < CLOCK_TIME(2, 31))) { + EnInvadepoh_Invader_SetupWaitForEvent(this); + } else if (sEventState == ENINVADEPOH_EVENT_ACTIVE) { + if (this->progress >= 0.0001f) { + EnInvadepoh_Invader_SetupAttack(this); + } else { + EnInvadepoh_Invader_SetupWarpIn(this); + } + } else { + Actor_Kill(&this->actor); + } + } +} + +void EnInvadepoh_Invader_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if (sEventState == ENINVADEPOH_EVENT_CLEAR) { + if ((this->actionFunc == EnInvadepoh_Invader_Attack) || (this->actionFunc == EnInvadepoh_Invader_WarpIn)) { + thisx->speed = 0.0f; + thisx->velocity.y = 0.0f; + thisx->gravity = 0.0f; + EnInvadepoh_Invader_SetupHitstun(this); + } else if ((this->actionFunc == EnInvadepoh_Invade_WaitToRespawn) || + (this->actionFunc == EnInvadepoh_Invader_WaitForEvent)) { + Actor_Kill(thisx); + return; + } + } else if (this->collider.base.acFlags & AC_HIT) { + Actor* hitbox = this->collider.base.ac; + + thisx->speed = hitbox->speed * 0.5f; + thisx->speed = CLAMP(thisx->speed, -60.0f, 60.0f); + + thisx->world.rot.y = hitbox->world.rot.y; + thisx->gravity = 0.0f; + + thisx->velocity.y = hitbox->velocity.y * 0.5f; + thisx->velocity.y = CLAMP(thisx->velocity.y, -30.0f, 30.0f); + + SoundSource_PlaySfxAtFixedWorldPos(play, &thisx->world.pos, 50, NA_SE_EN_INVADER_DEAD); + EnInvadepoh_Invader_SetupHitstun(this); + } + + this->actionFunc(this, play); + + if (thisx->draw != NULL) { + this->finishedAnim = SkelAnime_Update(&this->skelAnime); + } + + Collider_UpdateCylinder(thisx, &this->collider); + + if (this->collider.base.atFlags & AT_ON) { + CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider.base); + } else { + this->collider.base.atFlags &= ~AT_HIT; + } + if (this->collider.base.acFlags & AC_ON) { + CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); + } else { + this->collider.base.acFlags &= ~AC_HIT; + } + if (this->collider.base.ocFlags1 & OC1_ON) { + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); + } else { + this->collider.base.ocFlags1 &= ~OC1_HIT; + } +} + +void EnInvadepoh_Cow_WaitForObject(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if (Object_IsLoaded(&play->objectCtx, this->bankIndex)) { + this->actor.objectSlot = this->bankIndex; + Actor_SetObjectDependency(play, &this->actor); + this->actor.update = EnInvadepoh_Cow_Update; + this->actor.draw = EnInvadepoh_Cow_Draw; + SkelAnime_InitFlex(play, &this->skelAnime, &gCowSkel, NULL, this->jointTable, this->morphTable, COW_LIMB_MAX); + Animation_PlayLoop(&this->skelAnime, &gCowMooAnim); + } +} + +void EnInvadepoh_Cow_Update(Actor* thisx, PlayState* play2) { + static s16 D_80B4EDC0[3] = { -0x1F40, -0x1770, -0x2AF8 }; + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 index; + + if ((sLightBall == NULL) || (this->actor.parent == NULL)) { + Actor_Kill(&this->actor); + return; + } + index = ENINVADEPOH_GET_INDEX(&this->actor); + SkelAnime_Update(&this->skelAnime); + Math_ScaledStepToS(&this->actor.shape.rot.x, D_80B4EDC0[index], 0x32); + if (this->actor.child != NULL) { + s32 pad; + MtxF tailMtxF; + + Matrix_Push(); + Matrix_SetTranslateRotateYXZ(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + &this->actor.shape.rot); + Matrix_Translate(0.0f, 57.0f, -36.0f, MTXMODE_APPLY); + Matrix_RotateXS(this->actor.shape.rot.x * -0.7f, MTXMODE_APPLY); + Matrix_RotateZS(this->actor.shape.rot.z * -0.7f, MTXMODE_APPLY); + Matrix_MultZero(&this->actor.child->world.pos); + Matrix_Get(&tailMtxF); + Matrix_MtxFToYXZRot(&tailMtxF, &this->actor.child->shape.rot, false); + Matrix_Pop(); + } +} + +void EnInvadepoh_CowTail_WaitForObject(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if (Object_IsLoaded(&play->objectCtx, this->bankIndex)) { + this->actor.objectSlot = this->bankIndex; + Actor_SetObjectDependency(play, &this->actor); + this->actor.update = EnInvadepoh_CowTail_Update; + this->actor.draw = EnInvadepoh_CowTail_Draw; + SkelAnime_InitFlex(play, &this->skelAnime, &gCowTailSkel, NULL, this->jointTable, this->morphTable, + COW_TAIL_LIMB_MAX); + Animation_PlayLoop(&this->skelAnime, &gCowTailSwishAnim); + } +} + +void EnInvadepoh_CowTail_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if ((sLightBall == NULL) || (this->actor.parent == NULL)) { + Actor_Kill(&this->actor); + } else { + SkelAnime_Update(&this->skelAnime); + } +} + +void EnInvadepoh_AbductedRomani_SetupWait(EnInvadepoh* this) { + this->actionTimer = 40; + Animation_MorphToLoop(&this->skelAnime, &gRomaniLookAroundAnim, -10.0f); + this->actor.draw = NULL; + this->actionFunc = EnInvadepoh_AbductedRomani_Wait; +} + +void EnInvadepoh_AbductedRomani_Wait(EnInvadepoh* this, PlayState* play) { + this->actionTimer--; + if (this->actionTimer <= 0) { + EnInvadepoh_Romani_StartTextBox(this, play, 0x332F); // Romani's scream when abducted + this->actor.draw = EnInvadepoh_Romani_Draw; + EnInvadepoh_AbductedRomani_SetupYell(this); + } +} + +void EnInvadepoh_AbductedRomani_SetupYell(EnInvadepoh* this) { + this->actionTimer = 60; + Animation_MorphToLoop(&this->skelAnime, &gRomaniLookAroundAnim, -10.0f); + this->actor.draw = EnInvadepoh_Romani_Draw; + this->actionFunc = EnInvadepoh_AbductedRomani_Yell; +} + +void EnInvadepoh_AbductedRomani_Yell(EnInvadepoh* this, PlayState* play) { + if (this->actionTimer == 20) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + + interactInfo->headRotTarget.x = 0x7D0; + interactInfo->headRotTarget.y = 0; + interactInfo->headRotTarget.z = 0; + interactInfo->scaledTurnRate = 0.1f; + interactInfo->maxTurnRate = 0x3E8; + } + + this->actionTimer--; + if (this->actionTimer <= 0) { + EnInvadepoh_AbductedRomani_SetupStruggle(this); + } +} + +void EnInvadepoh_AbductedRomani_SetupStruggle(EnInvadepoh* this) { + this->interactInfo.headRotTarget.x = 0xBB8; + this->interactInfo.headRotTarget.y = 0; + this->interactInfo.headRotTarget.z = 0; + this->interactInfo.scaledTurnRate = 0.24f; + this->interactInfo.maxTurnRate = 0xBB8; + this->interactInfo.torsoRotZTarget = 0x3A98; + this->interactInfo.torsoMaxTurnRate = 0.1f; + this->interactInfo.torsoMaxTurnStep = 0x7D0; + this->actionTimer = 50; + + Animation_Change(&this->skelAnime, &gRomaniRunAnim, 2.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -5.0f); + this->actor.draw = EnInvadepoh_Romani_Draw; + this->actionFunc = EnInvadepoh_AbductedRomani_Struggle; +} + +void EnInvadepoh_AbductedRomani_Struggle(EnInvadepoh* this, PlayState* play) { + if (this->actionTimer == 40) { + this->interactInfo.headRotTarget.y = 0x1B58; + } else if (this->actionTimer == 30) { + this->interactInfo.headRotTarget.y = -0x1B58; + } else if (this->actionTimer == 20) { + this->interactInfo.headRotTarget.y = 0x1B58; + } else if (this->actionTimer == 10) { + this->interactInfo.headRotTarget.y = 0; + } + this->actionTimer--; + if (this->actionTimer <= 0) { + EnInvadepoh_AbductedRomani_SetupEnd(this); + } +} + +void EnInvadepoh_AbductedRomani_SetupEnd(EnInvadepoh* this) { + this->interactInfo.headRotTarget.x = 0; + this->interactInfo.headRotTarget.y = 0; + this->interactInfo.headRotTarget.z = 0; + this->interactInfo.scaledTurnRate = 0.24f; + this->interactInfo.maxTurnRate = 0x1388; + this->interactInfo.torsoRotZTarget = 0; + this->interactInfo.torsoMaxTurnRate = 0.28f; + this->interactInfo.torsoMaxTurnStep = 0x1B58; + + Animation_MorphToPlayOnce(&this->skelAnime, &gRomaniIdleAnim, -10.0f); + this->actor.draw = EnInvadepoh_Romani_Draw; + this->actionFunc = EnInvadepoh_AbductedRomani_End; +} + +void EnInvadepoh_AbductedRomani_End(EnInvadepoh* this, PlayState* play) { +} + +void EnInvadepoh_AbductedRomani_WaitForObject(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 pad; + + if (Object_IsLoaded(&play->objectCtx, this->bankIndex)) { + this->actor.objectSlot = this->bankIndex; + Actor_SetObjectDependency(play, &this->actor); + EnInvadepoh_Romani_DesegmentTextures(); + this->actor.update = EnInvadepoh_AbductedRomani_Update; + SkelAnime_InitFlex(play, &this->skelAnime, &gRomaniSkel, &gRomaniIdleAnim, this->jointTable, this->morphTable, + ROMANI_LIMB_MAX); + EnInvadepoh_Interact_Init(&this->interactInfo, D_80B4EA90, 6, D_80B4EB00, 2, &gZeroVec3s, 0x1388, 0.05f, 0.3f, + 0.12f); + Animation_PlayLoop(&this->skelAnime, &gRomaniIdleAnim); + EnInvadepoh_AbductedRomani_SetupWait(this); + } +} + +void EnInvadepoh_AbductedRomani_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + + if (this->actor.parent == NULL) { + Actor_Kill(&this->actor); + return; + } + this->actionFunc(this, play); + SkelAnime_Update(&this->skelAnime); + EnInvadepoh_Interact_Update(&this->interactInfo); + if (interactInfo->torsoRotZ != 0) { + this->actor.shape.rot.x = -interactInfo->torsoRotZ; + } +} + +void EnInvadepoh_ConfusedRomani_SetupWalk(EnInvadepoh* this) { + static s16 D_80B4EDC8[4] = { -0x708, -0x3E8, 0, 0x7D0 }; + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + + this->actionTimer = Rand_S16Offset(150, 250); + interactInfo->headRotTarget.x = D_80B4EDC8[Rand_Next() >> 0x1E]; + interactInfo->headRotTarget.y = 0; + interactInfo->headRotTarget.z = 0; + interactInfo->scaledTurnRate = 0.1f; + interactInfo->maxTurnRate = 0x320; + Animation_MorphToLoop(&this->skelAnime, &gRomaniWalkAnim, -10.0f); + this->actionFunc = EnInvadepoh_ConfusedRomani_Walk; +} + +void EnInvadepoh_ConfusedRomani_Walk(EnInvadepoh* this, PlayState* play) { + s32 pad; + + Math_StepToF(&this->actor.speed, 1.6f, 0.1f); + if (EnInvadepoh_Romani_MoveAlongPath(this, play, this->actor.speed, 50.0f)) { + EnInvadepoh_ConfusedRomani_SetNextPathPoint(this); + this->unk_370 = 0xC8; + this->actor.speed *= 0.25f; + } else { + Math_StepToS(&this->unk_370, 0x7D0, 0x46); + } + EnInvadepoh_Romani_TurnToPath(this, 6, this->unk_370, 0x46); + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_40) && + (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 7.0f))) { + Actor_PlaySfx(&this->actor, NA_SE_EN_ROMANI_WALK); + } + if (this->actionTimer > 0) { + this->actionTimer--; + } else { + EnInvadepoh_ConfusedRomani_SetupIdle(this); + } +} + +void EnInvadepoh_ConfusedRomani_SetupIdle(EnInvadepoh* this) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + f32 rand = Rand_ZeroOne(); + + this->actionTimer = Rand_S16Offset(150, 150); + if (rand < 0.5f) { + this->unk_374 = 0; + Math_Vec3s_Copy(&interactInfo->headRotTarget, &gZeroVec3s); + interactInfo->scaledTurnRate = 0.1f; + interactInfo->maxTurnRate = 0x3E8; + } else if (rand < 0.75f) { + this->unk_374 = 1; + interactInfo->headRotTarget.x = Rand_S16Offset(0, 0x7D0); + interactInfo->headRotTarget.y = 0; + interactInfo->headRotTarget.z = 0; + interactInfo->scaledTurnRate = 0.06f; + interactInfo->maxTurnRate = 0x3E8; + } else if (rand < 0.8f) { + this->unk_374 = 2; + interactInfo->headRotTarget.x = Rand_S16Offset(-0x7D0, 0x7D0); + interactInfo->headRotTarget.y = 0; + interactInfo->headRotTarget.z = 0; + interactInfo->scaledTurnRate = 0.05f; + interactInfo->maxTurnRate = 0x3E8; + } else { + this->unk_374 = 3; + interactInfo->headRotTarget.x = 0; + interactInfo->headRotTarget.y = 0; + interactInfo->headRotTarget.z = Rand_S16Offset(-0x9C4, 0x1388); + interactInfo->scaledTurnRate = 0.04f; + interactInfo->maxTurnRate = 0x3E8; + } + Animation_MorphToLoop(&this->skelAnime, &gRomaniIdleAnim, -10.0f); + this->actionFunc = EnInvadepoh_ConfusedRomani_Idle; +} + +void EnInvadepoh_ConfusedRomani_Idle(EnInvadepoh* this, PlayState* play) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + + Math_StepToF(&this->actor.speed, 0.0f, 0.2f); + if (EnInvadepoh_Romani_MoveAlongPath(this, play, this->actor.speed, 50.0f)) { + EnInvadepoh_ConfusedRomani_SetNextPathPoint(this); + } + if (this->unk_374 == 0) { + if ((this->actor.xzDistToPlayer < 350.0f) && (play->gameplayFrames & 0x60)) { + Player* player = GET_PLAYER(play); + s16 angleToPlayer = Math_Vec3f_Pitch(&this->actor.focus.pos, &player->actor.focus.pos) * 0.85f; + + interactInfo->headRotTarget.x = CLAMP((s16)(angleToPlayer - this->actor.shape.rot.x), -0x9C4, 0x9C4); + + angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + interactInfo->headRotTarget.y = CLAMP((s16)(angleToPlayer * 0.7f), -0x1F40, 0x1F40); + } + } else { + interactInfo->headRotTarget.x = 0; + interactInfo->headRotTarget.y = 0; + } + if (this->actionTimer > 0) { + s32 timerMod32 = (u32)this->actionTimer % 0x20; + + if ((timerMod32 == 0) && (Rand_ZeroOne() < 0.3f)) { + s32 next_unk_374 = (s32)Rand_Next() % 4; + + if (next_unk_374 != this->unk_374) { + this->unk_374 = next_unk_374; + if (this->unk_374 == 0) { + Math_Vec3s_Copy(&interactInfo->headRotTarget, &gZeroVec3s); + interactInfo->scaledTurnRate = 0.07f; + } else if (this->unk_374 == 1) { + interactInfo->headRotTarget.x = Rand_S16Offset(0x3E8, 0x3E8); + interactInfo->headRotTarget.y = Rand_S16Offset(-0x3E8, 0x7D0); + interactInfo->headRotTarget.z = Rand_S16Offset(-0x320, 0x640); + interactInfo->scaledTurnRate = 0.06f; + } else if (this->unk_374 == 2) { + interactInfo->headRotTarget.x = Rand_S16Offset(-0x7D0, 0x3E8); + interactInfo->headRotTarget.y = Rand_S16Offset(-0x3E8, 0x7D0); + interactInfo->headRotTarget.z = Rand_S16Offset(-0x320, 0x640); + interactInfo->scaledTurnRate = 0.05f; + } else { + interactInfo->headRotTarget.x = Rand_S16Offset(-0x3E8, 0x7D0); + interactInfo->headRotTarget.y = Rand_S16Offset(-0x7D0, 0xFA0); + interactInfo->headRotTarget.z = Rand_S16Offset(-0x7D0, 0xFA0); + interactInfo->scaledTurnRate = 0.04f; + } + } + } + this->actionTimer--; + return; + } + EnInvadepoh_ConfusedRomani_SetupWalk(this); +} + +void EnInvadepoh_ConfusedRomani_SetupTalk(EnInvadepoh* this) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + + this->actor.speed = 0.0f; + Animation_MorphToLoop(&this->skelAnime, &gRomaniIdleAnim, -10.0f); + this->unk_370 = 0; + interactInfo->scaledTurnRate = 0.05f; + interactInfo->maxTurnRate = 0x4B0; + this->actionFunc = EnInvadepoh_ConfusedRomani_Talk; +} + +void EnInvadepoh_ConfusedRomani_Talk(EnInvadepoh* this, PlayState* play) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + Player* player = GET_PLAYER(play); + s16 angleToPlayer; + + Math_StepToS(&this->unk_370, 0x7D0, 0x32); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 6, this->unk_370, 0x23); + + angleToPlayer = Math_Vec3f_Pitch(&this->actor.focus.pos, &player->actor.focus.pos) * 0.85f; + interactInfo->headRotTarget.x = CLAMP((s16)(angleToPlayer - this->actor.shape.rot.x), -0xBB8, 0xBB8); + + angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + interactInfo->headRotTarget.y = CLAMP((s16)(angleToPlayer * 0.7f), -0x1F40, 0x1F40); + + if (Actor_TextboxIsClosing(&this->actor, play)) { + EnInvadepoh_ConfusedRomani_SetupIdle(this); + } +} + +void EnInvadepoh_ConfusedRomani_WaitForObject(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if (Object_IsLoaded(&play->objectCtx, this->bankIndex)) { + this->actor.objectSlot = this->bankIndex; + Actor_SetObjectDependency(play, &this->actor); + EnInvadepoh_Romani_DesegmentTextures(); + this->actor.update = EnInvadepoh_ConfusedRomani_Update; + this->actor.draw = EnInvadepoh_Romani_Draw; + this->actor.textId = 0x3330; // ........ + SkelAnime_InitFlex(play, &this->skelAnime, &gRomaniSkel, &gRomaniIdleAnim, this->jointTable, this->morphTable, + ROMANI_LIMB_MAX); + EnInvadepoh_Interact_Init(&this->interactInfo, D_80B4EA90, 6, D_80B4EB00, 2, &gZeroVec3s, 100, 0.03f, 0.3f, 0.03f); + EnInvadepoh_ConfusedRomani_SetupPath(this, play); + EnInvadepoh_SetPosToPathPoint(this, 0); + func_800B4AEC(play, &this->actor, 50.0f); + EnInvadepoh_SnapToFloor(this); + EnInvadepoh_ConfusedRomani_SetupWalk(this); + } +} + +void EnInvadepoh_ConfusedRomani_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 inUncullRange = CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_40); + s32 talkRequested = Actor_ProcessTalkRequest(&this->actor, &play->state); + + if (talkRequested) { + Message_BombersNotebookQueueEvent(play, 5); + EnInvadepoh_ConfusedRomani_SetupTalk(this); + } + this->actionFunc(this, play); + if (inUncullRange) { + SkelAnime_Update(&this->skelAnime); + EnInvadepoh_Interact_Update(&this->interactInfo); + if ((this->actionFunc != EnInvadepoh_ConfusedRomani_Talk) && !talkRequested && this->actor.isLockedOn) { + Actor_OfferTalk(&this->actor, play, 100.0f); + } + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); + } +} + +void EnInvadepoh_LightBall_SetupDescend(EnInvadepoh* this) { + this->actor.gravity = -15.0f; + this->pulseScale = 0.0f; + this->pulseScaleTarget = 1.0f; + this->pulseScaleRate = 0.0f; + this->pulseRate = 0; + this->actionFunc = EnInvadepoh_LightBall_Descend; +} + +void EnInvadepoh_LightBall_Descend(EnInvadepoh* this, PlayState* play) { + s32 pad; + f32 distToTargetY; + + Actor_UpdateVelocityWithGravity(&this->actor); + this->actor.velocity.y *= 0.97f; + distToTargetY = + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y + sLightBallSpawnOffset.y + 300.0f, 0.7f, + fabsf(this->actor.velocity.y), 1.0f); + Actor_PlaySfx_Flagged(&this->actor, NA_SE_EV_UFO_APPEAR - SFX_FLAG); + if (fabsf(distToTargetY) < 1.0f) { + EnInvadepoh_LightBall_SpawnSparkles(this, play, 50); + EnInvadepoh_LightBall_SetupHover(this); + } +} + +void EnInvadepoh_LightBall_SetupHover(EnInvadepoh* this) { + this->actor.velocity.y *= 0.1f; + this->actionTimer = 20; + this->pulseScaleTarget = 0.3f; + this->pulseScaleRate = 0.03f; + this->pulseRate = 0xBB8; + this->actionFunc = EnInvadepoh_LightBall_Hover; +} + +void EnInvadepoh_LightBall_Hover(EnInvadepoh* this, PlayState* play) { + f32 hoverY = this->actor.home.pos.y + sLightBallSpawnOffset.y + 300.0f; + + if (this->actor.world.pos.y < hoverY) { + this->actor.gravity = 3.0f; + } else { + this->actor.gravity = -2.0f; + } + this->actor.velocity.y *= 0.96f; + Actor_MoveWithGravity(&this->actor); + Actor_PlaySfx_Flagged(&this->actor, NA_SE_EV_UFO_APPEAR - SFX_FLAG); + this->actionTimer--; + if (this->actionTimer <= 0) { + EnInvadepoh_LightBall_SetupSpawnInvaders(this); + } +} + +void EnInvadepoh_LightBall_SetupSpawnInvaders(EnInvadepoh* this) { + this->actionTimer = 120; + this->pulseScaleTarget = 0.2f; + this->pulseScaleRate = 0.01f; + this->actor.gravity = 33.0f; + this->pulseRate = 0xBB8; + this->lightBallTarget = 0; + this->stateTimer = 0; + this->actionFunc = EnInvadepoh_LightBall_SpawnInvaders; +} + +void EnInvadepoh_LightBall_SpawnInvaders(EnInvadepoh* this, PlayState* play) { + static Vec3f sLightBallTargetOffsets[5] = { + { -1813.0f, 374.0f, 1900.0f }, { 2198.0f, 153.0f, 3365.0f }, { -1434.0f, 262.0f, 3365.0f }, + { -393.0f, 396.0f, 1084.0f }, { 0.0f, 1500.0f, 0.0f }, + }; + s32 pad; + Vec3f lightBallTargetPos; + s32 pad2; + + if (this->stateTimer < 25) { + this->stateTimer++; + } else { + this->stateTimer = 0; + this->lightBallTarget++; + this->lightBallTarget = CLAMP_MAX(this->lightBallTarget, ARRAY_COUNT(sLightBallTargetOffsets) - 1); + this->actor.gravity = 33.0f; + EnInvadepoh_LightBall_SpawnSparkles(this, play, 20); + } + Math_Vec3f_Sum(&sLightBallTargetOffsets[this->lightBallTarget], &this->actor.home.pos, &lightBallTargetPos); + if (Math3D_Vec3fDistSq(&this->actor.world.pos, &lightBallTargetPos) < SQ(400.0f)) { + this->actor.speed *= 0.8f; + } else { + Math_StepToF(&this->actor.speed, 170.0f, 21.0f); + this->actor.speed *= 0.98f; + } + if (EnInvadepoh_StepTowardXZ(&this->actor.world.pos.x, &this->actor.world.pos.z, lightBallTargetPos.x, lightBallTargetPos.z, + this->actor.speed)) { + this->actor.speed = 0.0f; + } + if (lightBallTargetPos.y < this->actor.world.pos.y) { + Math_StepToF(&this->actor.gravity, -12.0f, 7.0f); + } else { + Math_StepToF(&this->actor.gravity, 5.0f, 4.0f); + } + this->actor.velocity.y += this->actor.gravity; + this->actor.velocity.y *= 0.97f; + this->actor.world.pos.y += this->actor.velocity.y; + Actor_PlaySfx_Flagged(&this->actor, NA_SE_EV_UFO_APPEAR - SFX_FLAG); + if (this->actionTimer > 0) { + this->actionTimer--; + } else { + EnInvadepoh_LightBall_SetupAboveBarn(this); + } +} + +void EnInvadepoh_LightBall_SetupAboveBarn(EnInvadepoh* this) { + this->pulseScaleTarget = 0.2f; + this->pulseScaleRate = 0.01f; + this->pulseRate = 0xBB8; + this->actor.velocity.y *= 0.8f; + this->actionFunc = EnInvadepoh_LightBall_AboveBarn; +} + +void EnInvadepoh_LightBall_AboveBarn(EnInvadepoh* this, PlayState* play) { + s32 pad; + Vec3f lightBallTargetPos; + + lightBallTargetPos.x = this->actor.home.pos.x; + lightBallTargetPos.y = this->actor.home.pos.y + 1500.0f; + lightBallTargetPos.z = this->actor.home.pos.z; + Math_SmoothStepToS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &lightBallTargetPos), 0xA, 0xBB8, 0x64); + if ((play->gameplayFrames % 0x40) < 14) { + Math_StepToF(&this->actor.speed, 5.0f, 1.0f); + } else { + this->actor.speed *= 0.97f; + } + this->actor.gravity = (lightBallTargetPos.y < this->actor.world.pos.y) ? -0.5f : 2.0f; + this->actor.velocity.y *= 0.97f; + Actor_MoveWithGravity(&this->actor); + if (sEventState == ENINVADEPOH_EVENT_CLEAR) { + EnInvadepoh_LightBall_SetupLeaveBarn(this); + } +} + +void EnInvadepoh_LightBall_SetupCircle(EnInvadepoh* this) { + this->pulseScaleTarget = 0.2f; + this->pulseScaleRate = 0.01f; + this->actor.gravity = -1.5f; + this->pulseRate = 0xBB8; + this->actionTimer = 35; + this->actionFunc = EnInvadepoh_LightBall_Circle; +} + +void EnInvadepoh_LightBall_Circle(EnInvadepoh* this, PlayState* play) { + s32 pad; + Vec3f lightBallTargetPos; + + lightBallTargetPos.x = this->actor.home.pos.x + sLightBallSpawnOffset.x; + lightBallTargetPos.y = this->actor.home.pos.y + sLightBallSpawnOffset.y + 400.0f; + lightBallTargetPos.z = this->actor.home.pos.z + sLightBallSpawnOffset.z; + Math_SmoothStepToS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &lightBallTargetPos), 4, 0x1F40, 0x64); + Math_StepToF(&this->actor.speed, 70.0f, 3.0f); + this->actor.gravity = (lightBallTargetPos.y < this->actor.world.pos.y) ? -2.0f : 2.0f; + this->actor.velocity.y *= 0.97f; + Actor_MoveWithGravity(&this->actor); + if (this->actionTimer > 0) { + this->actionTimer--; + } else { + EnInvadepoh_LightBall_SetupFlee(this); + } +} + +void EnInvadepoh_LightBall_SetupFlee(EnInvadepoh* this) { + this->pulseScaleTarget = 0.2f; + this->pulseScaleRate = 0.01f; + this->actor.gravity = 1.0f; + this->pulseRate = 0xBB8; + this->actionTimer = 60; + this->actionFunc = EnInvadepoh_LightBall_Flee; +} + +void EnInvadepoh_LightBall_Flee(EnInvadepoh* this, PlayState* play) { + Math_StepToF(&this->actor.speed, 150.0f, 4.0f); + this->actor.velocity.y *= 0.95f; + Actor_MoveWithGravity(&this->actor); + if (this->actionTimer > 0) { + this->actionTimer--; + } else { + Actor_Kill(&this->actor); + } +} + +void EnInvadepoh_LightBall_SetupLeaveBarn(EnInvadepoh* this) { + this->actionTimer = 40; + this->pulseScaleTarget = 0.2f; + this->pulseScaleRate = 0.01f; + this->actor.speed = 0.0f; + this->pulseRate = 0xBB8; + this->actionFunc = EnInvadepoh_LightBall_LeaveBarn; +} + +void EnInvadepoh_LightBall_LeaveBarn(EnInvadepoh* this, PlayState* play) { + s32 pad; + Vec3f lightBallTargetPos; + + lightBallTargetPos.x = this->actor.home.pos.x; + lightBallTargetPos.y = this->actor.home.pos.y + 800.0f; + lightBallTargetPos.z = this->actor.home.pos.z; + Math_SmoothStepToS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &lightBallTargetPos), 4, 0x1F40, 0x64); + Math_StepToF(&this->actor.speed, 30.0f, 3.0f); + this->actor.velocity.y *= 0.98f; + this->actor.gravity = (lightBallTargetPos.y < this->actor.world.pos.y) ? -0.5f : 2.0f; + Actor_MoveWithGravity(&this->actor); + Actor_UpdateBgCheckInfo(play, &this->actor, 0.0f, 0.0f, 0.0f, 4); + + if (this->actionTimer > 0) { + this->actionTimer--; + } else { + EnInvadepoh_LightBall_SetupCircle(this); + } +} + +void EnInvadepoh_LightBall_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + f32 scaleMod; + + this->actionFunc(this, play); + + this->pulsePhase += this->pulseRate; + Math_StepToF(&this->pulseScale, this->pulseScaleTarget, this->pulseScaleRate); + + scaleMod = 1.0f + (Math_SinS(this->pulsePhase) * this->pulseScale); + Actor_SetScale(&this->actor, 0.27f * scaleMod); + Math_StepToS(&this->unk_306, 0x258, 8); + this->actor.world.rot.y += this->unk_306; + this->unk_304 += 0x258; +} + +void EnInvadepoh_Night1Romani_SetupWalk(EnInvadepoh* this) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + + Animation_MorphToLoop(&this->skelAnime, &gRomaniWalkAnim, -10.0f); + interactInfo->headRotTarget.x = 0; + interactInfo->headRotTarget.y = 0; + interactInfo->headRotTarget.z = 0; + interactInfo->scaledTurnRate = 0.1f; + interactInfo->maxTurnRate = 0x320; + this->actionFunc = EnInvadepoh_Night1Romani_Walk; +} + +void EnInvadepoh_Night1Romani_Walk(EnInvadepoh* this, PlayState* play) { + EnInvadepoh_Night1Romani_SetProgress(this); + EnInvadepoh_Night1Romani_MoveAlongPathTimed(this, play); + EnInvadepoh_Romani_TurnToPath(this, 6, 0x7D0, 0x64); + + if ((this->currentPoint == 0) || (this->currentPoint + 1 == this->endPoint)) { + if (this->unk_378 == 0) { + s32 trueTimeSpeed = Environment_GetTimeSpeed(play); + s32 doorTimer = trueTimeSpeed; + + if (trueTimeSpeed > 0) { + // This is really dividing by trueTimeSpeed, but matching requires writing like this + doorTimer = (R_TIME_SPEED * -16.0f / doorTimer) - 0.5f; + this->unk_378 = EnInvadepoh_Romani_OpenDoor(this, play, SQ(80.0f), doorTimer); + } + } + this->actor.flags &= ~(ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY); + } else { + this->unk_378 = 0; + this->actor.flags |= (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY); + } + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_40) && + (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 7.0f))) { + Actor_PlaySfx(&this->actor, NA_SE_EN_ROMANI_WALK); + } + if (this->progress >= 0.9999f) { + Actor_Kill(&this->actor); + } +} + +void EnInvadepoh_Night1Romani_SetupTalk(EnInvadepoh* this) { + this->interactInfo.scaledTurnRate = 0.08f; + this->interactInfo.maxTurnRate = 0x7D0; + this->unk_370 = 0; + Animation_MorphToLoop(&this->skelAnime, &gRomaniIdleAnim, -10.0f); + this->actionFunc = EnInvadepoh_Night1Romani_Talk; +} + +void EnInvadepoh_Night1Romani_Talk(EnInvadepoh* this, PlayState* play) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + Player* player = GET_PLAYER(play); + s16 angleToPlayer; + + Math_StepToS(&this->unk_370, 0x7D0, 0x1F4); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, this->unk_370, 0x28); + angleToPlayer = Math_Vec3f_Pitch(&this->actor.focus.pos, &player->actor.focus.pos) * 0.85f; + interactInfo->headRotTarget.x = CLAMP((s16)(angleToPlayer - this->actor.shape.rot.x), -0xBB8, 0xBB8); + + angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + interactInfo->headRotTarget.y = CLAMP((s16)(angleToPlayer * 0.7f), -0x1F40, 0x1F40); + + if (Actor_TextboxIsClosing(&this->actor, play)) { + if (this->actor.textId == 0x332D) { // It's almost time, Grasshopper... + SET_WEEKEVENTREG(WEEKEVENTREG_54_10); + this->actor.textId = 0x332E; // If you run out of arrows... + } + EnInvadepoh_Night1Romani_SetupWalk(this); + } +} + +void EnInvadepoh_Night1Romani_WaitForObject(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 pad; + s32 currentTime; + + if (Object_IsLoaded(&play->objectCtx, this->bankIndex)) { + currentTime = gSaveContext.save.time; + this->actor.objectSlot = this->bankIndex; + Actor_SetObjectDependency(play, &this->actor); + EnInvadepoh_Romani_DesegmentTextures(); + SkelAnime_InitFlex(play, &this->skelAnime, &gRomaniSkel, &gRomaniWalkAnim, this->jointTable, this->morphTable, + ROMANI_LIMB_MAX); + EnInvadepoh_Interact_Init(&this->interactInfo, D_80B4EA90, 1, D_80B4EB00, 1, &gZeroVec3s, 0x64, 0.03f, 0.3f, 0.03f); + EnInvadepoh_Night1Romani_SetupPath(this, play); + EnInvadepoh_Night1Romani_SetProgress(this); + EnInvadepoh_Night1Romani_MoveAlongPathTimed(this, play); + EnInvadepoh_SetYawAlongPath(this); + EnInvadepoh_SnapToFloor(this); + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_PROMISED_TO_HELP_WITH_THEM)) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_54_10)) { + this->actor.textId = 0x332E; // If you run out of arrows... + } else { + this->actor.textId = 0x332D; // It's almost time, Grasshopper... + } + } else { + this->actor.textId = 0x332C; // ...It's almost time. + } + if ((currentTime >= CLOCK_TIME(18, 00)) || (currentTime < CLOCK_TIME(2, 00))) { + this->actor.update = EnInvadepoh_Night1Romani_WaitForEvent; + this->actor.draw = NULL; + } else if ((currentTime < CLOCK_TIME(6, 00)) && (CLOCK_TIME(2, 00) <= currentTime) && + (currentTime < CLOCK_TIME(2, 15))) { + this->actor.update = EnInvadepoh_Night1Romani_Update; + this->actor.draw = EnInvadepoh_Romani_Draw; + EnInvadepoh_Night1Romani_SetupWalk(this); + } else { + Actor_Kill(&this->actor); + } + } +} + +void EnInvadepoh_Night1Romani_WaitForEvent(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if ((CLOCK_TIME(6, 00) > gSaveContext.save.time) && (gSaveContext.save.time >= CLOCK_TIME(2, 00))) { + this->actor.update = EnInvadepoh_Night1Romani_Update; + this->actor.draw = EnInvadepoh_Romani_Draw; + EnInvadepoh_Night1Romani_SetupWalk(this); + } +} + +void EnInvadepoh_Night1Romani_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 inUncullRange = CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_40); + s32 talkRequested = Actor_ProcessTalkRequest(&this->actor, &play->state); + + if (talkRequested) { + Message_BombersNotebookQueueEvent(play, 5); + EnInvadepoh_Night1Romani_SetupTalk(this); + } + this->actionFunc(this, play); + if (inUncullRange && (this->actor.update != NULL)) { + SkelAnime_Update(&this->skelAnime); + EnInvadepoh_Interact_Update(&this->interactInfo); + if ((this->actionFunc != EnInvadepoh_Night1Romani_Talk) && !talkRequested && this->actor.isLockedOn) { + Actor_OfferTalk(&this->actor, play, 350.0f); + } + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); + } +} + +void EnInvadepoh_BarnRomani_SetupIdle(EnInvadepoh* this) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + + Animation_MorphToLoop(&this->skelAnime, &gRomaniIdleAnim, -10.0f); + interactInfo->headRotTarget.x = 0; + interactInfo->headRotTarget.y = 0; + interactInfo->headRotTarget.z = 0; + interactInfo->scaledTurnRate = 0.1f; + interactInfo->maxTurnRate = 0x5DC; + + this->actionTimer = Rand_S16Offset(200, 200); + this->unk_304 = this->actor.shape.rot.y; + this->actor.flags |= (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY); + this->actionFunc = EnInvadepoh_BarnRomani_Idle; +} + +void EnInvadepoh_BarnRomani_Idle(EnInvadepoh* this, PlayState* play) { + s32 pad; + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + + if ((play->gameplayFrames % 0x100) == 0) { + Vec3f pathPointF; + s16 yawToPath; + + Math_Vec3s_ToVec3f(&pathPointF, this->pathPoints); + yawToPath = Math_Vec3f_Yaw(&this->actor.world.pos, &pathPointF); + this->unk_304 = Rand_S16Offset(-0x1F40, 0x3E80) + yawToPath; + this->unk_370 = 0; + } + Math_StepToS(&this->unk_370, 0x7D0, 0x28); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->unk_304, 6, this->unk_370, 0x28); + if (this->actor.xzDistToPlayer < 300.0f) { + Player* player = GET_PLAYER(play); + s16 angleToPlayer; + + angleToPlayer = Math_Vec3f_Pitch(&this->actor.focus.pos, &player->actor.focus.pos) * 0.85f; + interactInfo->headRotTarget.x = CLAMP((s16)(angleToPlayer - this->actor.shape.rot.x), -0x9C4, 0x9C4); + + angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + interactInfo->headRotTarget.y = CLAMP((s16)(angleToPlayer * 0.7f), -0x1F40, 0x1F40); + + if ((play->gameplayFrames % 0x100) == 0) { + interactInfo->headRotTarget.z = Rand_S16Offset(-0x5DC, 0xBB8); + } + } else { + interactInfo->headRotTarget.x = 0; + interactInfo->headRotTarget.y = 0; + interactInfo->headRotTarget.z = 0; + } + if (this->actionTimer > 0) { + this->actionTimer--; + } else { + EnInvadepoh_BarnRomani_SetupLookAround(this); + } +} + +void EnInvadepoh_BarnRomani_SetupLookAround(EnInvadepoh* this) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + + Animation_MorphToPlayOnce(&this->skelAnime, &gRomaniLookAroundAnim, -10.0f); + interactInfo->headRotTarget.x = 0; + interactInfo->headRotTarget.y = 0; + interactInfo->headRotTarget.z = 0; + interactInfo->scaledTurnRate = 0.1f; + interactInfo->maxTurnRate = 0x320; + this->actor.flags |= (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY); + this->actionFunc = EnInvadepoh_BarnRomani_LookAround; +} + +void EnInvadepoh_BarnRomani_LookAround(EnInvadepoh* this, PlayState* play) { + if (this->finishedAnim) { + EnInvadepoh_BarnRomani_SetupIdle(this); + } +} + +void EnInvadepoh_BarnRomani_SetupWalk(EnInvadepoh* this) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + + Animation_MorphToLoop(&this->skelAnime, &gRomaniWalkAnim, 0.0f); + interactInfo->headRotTarget.x = 0; + interactInfo->headRotTarget.y = 0; + interactInfo->headRotTarget.z = 0; + interactInfo->scaledTurnRate = 0.1f; + interactInfo->maxTurnRate = 0x320; + this->actionFunc = EnInvadepoh_BarnRomani_Walk; +} + +void EnInvadepoh_BarnRomani_Walk(EnInvadepoh* this, PlayState* play) { + s32 pad; + + Math_StepToF(&this->actor.speed, 5.0f, 1.0f); + if (EnInvadepoh_Romani_MoveAlongPath(this, play, this->actor.speed, 50.0f)) { + EnInvadepoh_BarnRomani_SetNextPathPoint(this); + this->unk_370 = 0x5DC; + this->actor.speed *= 0.5f; + } else { + Math_StepToS(&this->unk_370, 0x190, 0x32); + } + EnInvadepoh_Romani_TurnToPath(this, 6, this->unk_370, 0x32); + if (this->currentPoint == 0) { + if (this->unk_378 == 0) { + this->unk_378 = EnInvadepoh_Romani_OpenDoor(this, play, SQ(80.0f), -15); + } + this->actor.flags &= ~(ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY); + } else { + this->unk_378 = 0; + this->actor.flags |= (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY); + } + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_40) && + (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 7.0f))) { + Actor_PlaySfx(&this->actor, NA_SE_EN_ROMANI_WALK); + } + if (this->currentPoint == this->endPoint) { + EnInvadepoh_BarnRomani_SetupIdle(this); + } +} + +void EnInvadepoh_BarnRomani_SetupTalk(EnInvadepoh* this) { + this->interactInfo.scaledTurnRate = 0.08f; + this->interactInfo.maxTurnRate = 0x7D0; + this->unk_370 = 0; + Animation_MorphToLoop(&this->skelAnime, &gRomaniIdleAnim, 0.0f); + this->actionFunc = EnInvadepoh_BarnRomani_Talk; +} + +void EnInvadepoh_BarnRomani_Talk(EnInvadepoh* this, PlayState* play) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + Player* player = GET_PLAYER(play); + s16 angleToPlayer; + + Math_StepToS(&this->unk_370, 0x7D0, 0x1F4); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, this->unk_370, (s16)0x28); + angleToPlayer = Math_Vec3f_Pitch(&this->actor.focus.pos, &player->actor.focus.pos) * 0.85f; + interactInfo->headRotTarget.x = CLAMP((s16)(angleToPlayer - this->actor.shape.rot.x), -0xBB8, 0xBB8); + + angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + interactInfo->headRotTarget.y = CLAMP((s16)(angleToPlayer * 0.7f), -0x1F40, 0x1F40); + if (Actor_TextboxIsClosing(&this->actor, play)) { + if (this->actor.textId == 0x332D) { // It's almost time, Grasshopper + SET_WEEKEVENTREG(WEEKEVENTREG_54_10); + this->actor.textId = 0x332E; // If you run out of arrows... + } + if (this->currentPoint == this->endPoint) { + EnInvadepoh_BarnRomani_SetupIdle(this); + } else { + EnInvadepoh_BarnRomani_SetupWalk(this); + } + } +} + +void EnInvadepoh_BarnRomani_WaitForObject(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 pad; + + if (Object_IsLoaded(&play->objectCtx, this->bankIndex)) { + s32 currentTime = gSaveContext.save.time; + + this->actor.objectSlot = this->bankIndex; + Actor_SetObjectDependency(play, &this->actor); + EnInvadepoh_Romani_DesegmentTextures(); + SkelAnime_InitFlex(play, &this->skelAnime, &gRomaniSkel, &gRomaniWalkAnim, this->jointTable, this->morphTable, + ROMANI_LIMB_MAX); + EnInvadepoh_Interact_Init(&this->interactInfo, D_80B4EA90, 1, D_80B4EB00, 1, &gZeroVec3s, 100, 0.03f, 0.3f, 0.03f); + EnInvadepoh_BarnRomani_SetupPath(this, play2); + if ((currentTime < CLOCK_TIME(2, 15)) || (currentTime >= CLOCK_TIME(6, 00))) { + this->currentPoint = 0; + this->actor.update = EnInvadepoh_BarnRomani_WaitForEvent; + } else { + this->currentPoint = this->endPoint; + this->actor.update = EnInvadepoh_BarnRomani_Update; + this->actor.draw = EnInvadepoh_Romani_Draw; + EnInvadepoh_BarnRomani_SetupIdle(this); + } + EnInvadepoh_SetPosToPathPoint(this, this->currentPoint); + EnInvadepoh_SetYawAlongPath(this); + func_800B4AEC(play, &this->actor, 50.0f); + EnInvadepoh_SnapToFloor(this); + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_PROMISED_TO_HELP_WITH_THEM)) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_54_10)) { + this->actor.textId = 0x332E; // If you run out of arrows... + } else { + this->actor.textId = 0x332D; // It's almost time, Grasshopper... + } + } else { + this->actor.textId = 0x332C; // ...It's almost time. + } + } +} + +void EnInvadepoh_BarnRomani_WaitForEvent(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if ((gSaveContext.save.time < CLOCK_TIME(6, 00)) && (gSaveContext.save.time >= CLOCK_TIME(2, 15))) { + this->actor.update = EnInvadepoh_BarnRomani_Update; + this->actor.draw = EnInvadepoh_Romani_Draw; + EnInvadepoh_BarnRomani_SetupWalk(this); + } +} + +void EnInvadepoh_BarnRomani_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 inUncullRange = CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_40); + s32 talkRequested = Actor_ProcessTalkRequest(&this->actor, &play->state); + + if (talkRequested) { + Message_BombersNotebookQueueEvent(play, 5); + EnInvadepoh_BarnRomani_SetupTalk(this); + } + this->actionFunc(this, play); + if (inUncullRange) { + this->finishedAnim = SkelAnime_Update(&this->skelAnime); + EnInvadepoh_Interact_Update(&this->interactInfo); + if ((this->actionFunc != EnInvadepoh_BarnRomani_Talk) && !talkRequested && this->actor.isLockedOn) { + Actor_OfferTalk(&this->actor, play, 100.0f); + } + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); + } +} + +void EnInvadepoh_RewardRomani_SetupClearCheck(EnInvadepoh* this) { + this->actionFunc = EnInvadepoh_RewardRomani_ClearCheck; +} + +void EnInvadepoh_RewardRomani_ClearCheck(EnInvadepoh* this, PlayState* play) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_DEFENDED_AGAINST_THEM)) { + this->actor.draw = EnInvadepoh_Romani_Draw; + this->actor.flags |= (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY); + EnInvadepoh_RewardRomani_SetupStartCs(this); + } +} + +void EnInvadepoh_RewardRomani_SetupStartCs(EnInvadepoh* this) { + this->actor.flags |= ACTOR_FLAG_10000; + this->actionFunc = EnInvadepoh_RewardRomani_StartCs; +} + +void EnInvadepoh_RewardRomani_StartCs(EnInvadepoh* this, PlayState* play) { + if (Actor_ProcessTalkRequest(&this->actor, &play->state)) { + EnInvadepoh_Romani_StartTextBox(this, play, 0x3331); // We did it...We won. + EnInvadepoh_RewardRomani_SetupTalk(this); + } else { + Actor_OfferTalk(&this->actor, play, 2000.0f); + } +} + +void EnInvadepoh_RewardRomani_SetupTalk(EnInvadepoh* this) { + this->actionFunc = EnInvadepoh_RewardRomani_Talk; +} + +void EnInvadepoh_RewardRomani_Talk(EnInvadepoh* this, PlayState* play) { + if ((Message_GetState(&play->msgCtx) == TEXT_STATE_5) && Message_ShouldAdvance(play)) { + if (this->textId == 0x3331) { // We did it...We won. + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_22_02)) { + EnInvadepoh_Romani_StartTextBox(this, play, 0x3334); // I have to get back to bed... + Message_BombersNotebookQueueEvent(play, 0x1D); + Message_BombersNotebookQueueEvent(play, 5); + } else { + EnInvadepoh_Romani_StartTextBox(this, play, 0x3333); // Here's Romani's thanks + } + } else if (this->textId == 0x3333) { // Here's Romani's thanks + EnInvadepoh_RewardRomani_SetupGiveBottle(this); + } else if (this->textId == 0x3334) { // I have to get back to bed... + Message_CloseTextbox(play); + EnInvadepoh_RewardRomani_SetupFinished(this); + } + } +} + +void EnInvadepoh_RewardRomani_SetupGiveBottle(EnInvadepoh* this) { + this->actionTimer = 2; + this->actionFunc = EnInvadepoh_RewardRomani_GiveBottle; +} + +void EnInvadepoh_RewardRomani_GiveBottle(EnInvadepoh* this, PlayState* play) { + if (this->actionTimer > 0) { + this->actionTimer--; + if (this->actionTimer == 0) { + Message_CloseTextbox(play); + } + } + if (Actor_HasParent(&this->actor, play)) { + this->actor.parent = NULL; + SET_WEEKEVENTREG(WEEKEVENTREG_22_02); + EnInvadepoh_RewardRomani_SetupAfterGiveBottle(this); + } else { + Actor_OfferGetItem(&this->actor, play, GI_MILK_BOTTLE, 2000.0f, 2000.0f); + } +} + +void EnInvadepoh_RewardRomani_SetupAfterGiveBottle(EnInvadepoh* this) { + this->actionFunc = EnInvadepoh_RewardRomani_AfterGiveBottle; +} + +void EnInvadepoh_RewardRomani_AfterGiveBottle(EnInvadepoh* this, PlayState* play) { + if (Actor_ProcessTalkRequest(&this->actor, &play->state)) { + EnInvadepoh_Romani_StartTextBox(this, play, 0x3334); // I have to get back to bed.. + Message_BombersNotebookQueueEvent(play, 0x1E); + Message_BombersNotebookQueueEvent(play, 0x1D); + Message_BombersNotebookQueueEvent(play, 5); + EnInvadepoh_RewardRomani_SetupTalk(this); + } else { + Actor_OfferTalkExchangeEquiCylinder(&this->actor, play, 2000.0f, PLAYER_IA_MINUS1); + } +} + +void EnInvadepoh_RewardRomani_SetupFinished(EnInvadepoh* this) { + this->actor.flags &= ~(ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY); + this->actionFunc = EnInvadepoh_RewardRomani_Finished; +} + +void EnInvadepoh_RewardRomani_Finished(EnInvadepoh* this, PlayState* play) { + if (play->msgCtx.bombersNotebookEventQueueCount == 0) { + if (play->msgCtx.msgMode == 0) { + sRewardFinished = true; + } else if ((Message_GetState(&play->msgCtx) == TEXT_STATE_DONE) || + (Message_GetState(&play->msgCtx) == TEXT_STATE_5)) { + sRewardFinished = true; + } + } +} + +void EnInvadepoh_RewardRomani_WaitForObject(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + + if (Object_IsLoaded(&play2->objectCtx, this->bankIndex)) { + this->actor.objectSlot = this->bankIndex; + Actor_SetObjectDependency(play, &this->actor); + EnInvadepoh_Romani_DesegmentTextures(); + this->actor.update = EnInvadepoh_RewardRomani_Update; + SkelAnime_InitFlex(play, &this->skelAnime, &gRomaniSkel, &gRomaniWalkAnim, this->jointTable, this->morphTable, + ROMANI_LIMB_MAX); + Animation_MorphToLoop(&this->skelAnime, &gRomaniIdleAnim, 0.0f); + EnInvadepoh_Interact_Init(&this->interactInfo, D_80B4EA90, 1, D_80B4EB00, 3, &gZeroVec3s, 0x7D0, 0.08f, 0.3f, 0.03f); + interactInfo->scaledTurnRate = 0.08f; + interactInfo->maxTurnRate = 0x7D0; + func_800B4AEC(play, &this->actor, 50.0f); + EnInvadepoh_SnapToFloor(this); + EnInvadepoh_RewardRomani_SetupClearCheck(this); + } +} + +void EnInvadepoh_RewardRomani_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + s32 inUncullRange = CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_40); + + this->actionFunc(this, play); + + if (inUncullRange && (this->actor.update != NULL)) { + Player* player; + s16 angleToPlayer; + + SkelAnime_Update(&this->skelAnime); + player = GET_PLAYER(play); + Math_StepToS(&this->unk_370, 0x7D0, 0x28); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 6, this->unk_370, 40); + angleToPlayer = Math_Vec3f_Pitch(&this->actor.focus.pos, &player->actor.focus.pos) * 0.9f; + interactInfo->headRotTarget.x = CLAMP((s16)(angleToPlayer - this->actor.shape.rot.x), -0xBB8, 0xBB8); + + angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + interactInfo->headRotTarget.y = CLAMP((s16)(angleToPlayer * 0.7f), -0x1F40, 0x1F40); + EnInvadepoh_Interact_Update(&this->interactInfo); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); + } +} + +void EnInvadepoh_Dog_PlayWalkSfx(EnInvadepoh* this) { + s32 pad; + + if (Animation_OnFrame(&this->skelAnime, 1.0f) || Animation_OnFrame(&this->skelAnime, 7.0f)) { + Actor_PlaySfx(&this->actor, NA_SE_EV_MONKEY_WALK); + } +} + +void EnInvadepoh_Dog_SetupWalk(EnInvadepoh* this) { + Animation_MorphToLoop(&this->skelAnime, &gDogWalkAnim, -6.0f); + this->actionTimer = Rand_S16Offset(50, 80); + this->actionFunc = EnInvadepoh_Dog_Walk; +} + +void EnInvadepoh_Dog_Walk(EnInvadepoh* this, PlayState* play) { + Math_StepToF(&this->actor.speed, 1.1f, 0.5f); + if (EnInvadepoh_Dog_MoveAlongPath(this, play)) { + EnInvadepoh_Dog_SetNextPathPoint(this); + } + EnInvadepoh_Dog_PlayWalkSfx(this); + if (this->dogTargetPoint >= 0) { + this->actionTimer = 0; + } + if (this->actionTimer > 0) { + this->actionTimer--; + } else { + EnInvadepoh_Dog_SetupRun(this); + } +} + +void EnInvadepoh_Dog_SetupRun(EnInvadepoh* this) { + Animation_MorphToLoop(&this->skelAnime, &gDogRunAnim, -6.0f); + this->actionTimer = Rand_S16Offset(50, 200); + this->actionFunc = EnInvadepoh_Dog_Run; +} + +void EnInvadepoh_Dog_Run(EnInvadepoh* this, PlayState* play) { + Math_StepToF(&this->actor.speed, 3.8f, 0.45f); + if (this->dogTargetPoint >= 0) { + f32 distToTarget; + Vec3f targetPathPoint; + + Math_Vec3s_ToVec3f(&targetPathPoint, &this->pathPoints[this->dogTargetPoint]); + distToTarget = Math3D_Vec3fDistSq(&this->actor.world.pos, &targetPathPoint); + if (distToTarget < SQ(80.0f)) { + this->actor.speed *= 0.85f; + } else if (distToTarget < SQ(150.0f)) { + this->actor.speed *= 0.93f; + } else if (distToTarget < SQ(250.0f)) { + this->actor.speed *= 0.96f; + } + if ((this->currentPoint == this->dogTargetPoint) || (distToTarget < SQ(50.0f))) { + this->actionTimer = 0; + } + } + EnInvadepoh_Dog_PlayWalkSfx(this); + if (EnInvadepoh_Dog_MoveAlongPath(this, play)) { + EnInvadepoh_Dog_SetNextPathPoint(this); + } + if (this->actionTimer > 0) { + this->actionTimer--; + } else if (this->dogTargetPoint >= 0) { + if (!sAliensTooClose && (Rand_ZeroOne() < 0.4f)) { + Actor_PlaySfx(&this->actor, NA_SE_EV_SMALL_DOG_GROAN); + } + EnInvadepoh_Dog_SetupBark(this); + } else { + EnInvadepoh_Dog_SetupWalk(this); + } +} + +void EnInvadepoh_Dog_SetupBark(EnInvadepoh* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gDogBarkAnim, -6.0f); + this->actionFunc = EnInvadepoh_Dog_Bark; +} + +void EnInvadepoh_Dog_Bark(EnInvadepoh* this, PlayState* play) { + s32 pad; + + Math_StepToF(&this->actor.speed, 0.0f, 1.0f); + Math_SmoothStepToS(&this->actor.world.rot.y, Actor_WorldYawTowardActor(&this->actor, &sClosestAlien->actor), 5, + 0x1388, 0x64); + EnInvadepoh_Dog_Move(this, play); + if (Animation_OnFrame(&this->skelAnime, 13.0f) || Animation_OnFrame(&this->skelAnime, 19.0f)) { + Actor_PlaySfx(&this->actor, NA_SE_EV_SMALL_DOG_ANG_BARK); + } + if (this->finishedAnim) { + EnInvadepoh_Dog_SetupRun(this); + } +} + +void EnInvadepoh_Dog_SetupJump(EnInvadepoh* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gDogJumpAnim, -6.0f); + this->actionFunc = EnInvadepoh_Dog_Jump; +} + +void EnInvadepoh_Dog_Jump(EnInvadepoh* this, PlayState* play) { + Math_StepToF(&this->actor.speed, 0.5f, 1.0f); + EnInvadepoh_Dog_Move(this, play); + if (this->finishedAnim) { + EnInvadepoh_Dog_SetupRun(this); + } +} + +void EnInvadepoh_Dog_WaitForObject(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if (Object_IsLoaded(&play->objectCtx, this->bankIndex)) { + if (1) {} + this->actor.objectSlot = this->bankIndex; + Actor_SetObjectDependency(play, &this->actor); + SkelAnime_InitFlex(play, &this->skelAnime, &gDogSkel, &gDogWalkAnim, this->jointTable, this->morphTable, + DOG_LIMB_MAX); + EnInvadepoh_Interact_Init(&this->interactInfo, NULL, 0, NULL, 0, &gZeroVec3s, 0xBB8, 0.1f, 0.0f, 0.0f); + EnInvadepoh_Dog_SetupPath(this, play); + EnInvadepoh_SetPosToPathPoint(this, 0); + func_800B4AEC(play, &this->actor, 50.0f); + EnInvadepoh_SnapToFloor(this); + Math_Vec3f_Copy(&this->unk_314, &this->actor.world.pos); + if (sEventState == ENINVADEPOH_EVENT_ACTIVE) { + this->actor.update = EnInvadepoh_Dog_Update; + this->actor.draw = EnInvadepoh_Dog_Draw; + this->actor.flags |= ACTOR_FLAG_TARGETABLE; + EnInvadepoh_Dog_SetupWalk(this); + } else if (sEventState == ENINVADEPOH_EVENT_WAIT) { + this->actor.update = EnInvadepoh_Dog_WaitForEvent; + } else { + Actor_Kill(&this->actor); + } + } +} + +void EnInvadepoh_Dog_WaitForEvent(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if (sEventState == ENINVADEPOH_EVENT_ACTIVE) { + this->actor.update = EnInvadepoh_Dog_Update; + this->actor.draw = EnInvadepoh_Dog_Draw; + this->actor.flags |= ACTOR_FLAG_TARGETABLE; + EnInvadepoh_Dog_SetupWalk(this); + } +} + +void EnInvadepoh_Dog_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 inUncullRange = CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_40); + + sClosestAlien = EnInvadepoh_Dog_GetClosestAlien(); + if (sClosestAlien == NULL) { + s32 hadTarget = (this->dogTargetPoint >= 0); + + this->dogTargetPoint = -1; + if (hadTarget) { + EnInvadepoh_Dog_SetupJump(this); + } + } else { + s32 lastTargetPoint = this->dogTargetPoint; + s32 pointsToTarget; + s32 halfwayPoint; + + this->dogTargetPoint = EnInvadepoh_Dog_FindClosestWaypoint(this, &sClosestAlien->actor.world.pos); + if (lastTargetPoint != this->dogTargetPoint) { + pointsToTarget = this->dogTargetPoint - this->currentPoint; + if (pointsToTarget < 0) { + pointsToTarget += this->endPoint; + } + + halfwayPoint = this->endPoint >> 1; + if (halfwayPoint < pointsToTarget) { + this->pathStep = -1; + } else if (pointsToTarget < halfwayPoint) { + this->pathStep = 1; + } + } + } + this->actionFunc(this, play); + this->finishedAnim = SkelAnime_Update(&this->skelAnime); + if (inUncullRange && (this->actor.update != NULL)) { + EnInvadepoh_Interact_Update(&this->interactInfo); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); + } +} + +void EnInvadepoh_Cremia_SetupWalk(EnInvadepoh* this) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + + Animation_MorphToLoop(&this->skelAnime, &gCremiaWalkAnim, -6.0f); + interactInfo->headRotTarget.x = 0; + interactInfo->headRotTarget.y = 0; + interactInfo->headRotTarget.z = 0; + interactInfo->scaledTurnRate = 0.1f; + interactInfo->maxTurnRate = 0x320; + + this->unk_370 = 0; + this->actionFunc = EnInvadepoh_Cremia_Walk; +} + +void EnInvadepoh_Cremia_Walk(EnInvadepoh* this, PlayState* play) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + EnInvadepoh* romani = sRomani; + s32 pad; + s32 framesMod0x80; + s32 pad2; + s16 angleToRomani; + s16 targetYaw; + s16 angleToPlayer; + + if (romani != NULL) { + if (romani->currentPoint == 0) { + this->unk_2F8 = 40.0f; + this->unk_304 = -0x8000; + this->actor.flags &= ~(ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY); + } else if (romani->currentPoint < (romani->endPoint - 1)) { + this->unk_2F8 = 40.0f; + Math_ScaledStepToS(&this->unk_304, -0x4800, 0xC8); + this->actor.flags |= (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY); + } else { + Math_StepToF(&this->unk_2F8, 5.0f, 3.0f); + Math_ScaledStepToS(&this->unk_304, -0x8000, 0x12C); + this->actor.flags &= ~(ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY); + } + angleToRomani = this->unk_304 + romani->actor.world.rot.y; + this->actor.world.pos.x = (Math_SinS(angleToRomani) * this->unk_2F8) + romani->actor.world.pos.x; + this->actor.world.pos.y = romani->actor.world.pos.y; + this->actor.world.pos.z = (Math_CosS(angleToRomani) * this->unk_2F8) + romani->actor.world.pos.z; + func_800B4AEC(play, &this->actor, 50.0f); + EnInvadepoh_SnapToFloor(this); + Math_StepToS(&this->unk_370, 0xBB8, 0x1F5); + if (Math3D_Vec3fDistSq(&this->actor.prevPos, &this->actor.world.pos) > SQ(0.01f)) { + Math_SmoothStepToS(&this->actor.shape.rot.y, Math_Vec3f_Yaw(&this->actor.prevPos, &this->actor.world.pos), + 3, this->unk_370, 0x1F4); + } + framesMod0x80 = (play->gameplayFrames + 20) % 0x80; + if (framesMod0x80 & 0x40) { + targetYaw = Math_Vec3f_Yaw(&this->actor.world.pos, &romani->actor.world.pos); + if (framesMod0x80 == 0x40) { + this->unk_370 = 0; + } + Math_StepToS(&this->unk_370, 0x7D0, 0x28); + Math_SmoothStepToS(&this->actor.shape.rot.y, targetYaw, 6, this->unk_370, 0x28); + angleToPlayer = Math_Vec3f_Pitch(&this->actor.focus.pos, &romani->actor.focus.pos) * 0.85f; + interactInfo->headRotTarget.x = CLAMP((s16)(angleToPlayer - this->actor.shape.rot.x), -0xBB8, 0xBB8); + + angleToPlayer = targetYaw - this->actor.shape.rot.y; + interactInfo->headRotTarget.y = CLAMP((s16)(angleToPlayer * 0.7f), -0x1F40, 0x1F40); + } + } + + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_40) && + (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 12.0f))) { + Actor_PlaySfx(&this->actor, NA_SE_EN_ROMANI_WALK); + } + if (gSaveContext.save.time > CLOCK_TIME(20, 15)) { + Actor_Kill(&this->actor); + } else if ((romani != NULL) && (romani->actionFunc == EnInvadepoh_Night3Romani_Talk)) { + EnInvadepoh_Cremia_SetupIdle(this); + } +} + +void EnInvadepoh_Cremia_SetupTalk(EnInvadepoh* this) { + this->interactInfo.scaledTurnRate = 0.08f; + this->interactInfo.maxTurnRate = 0x7D0; + Animation_MorphToLoop(&this->skelAnime, &gCremiaIdleAnim, -6.0f); + this->unk_370 = 0; + this->actionFunc = EnInvadepoh_Cremia_Talk; +} + +void EnInvadepoh_Cremia_Talk(EnInvadepoh* this, PlayState* play) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + Player* player = GET_PLAYER(play); + s16 angleToPlayer; + + Math_StepToS(&this->unk_370, 0x9C4, 0x1C2); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, this->unk_370, 0x28); + angleToPlayer = Math_Vec3f_Pitch(&this->actor.focus.pos, &player->actor.focus.pos) * 0.85f; + interactInfo->headRotTarget.x = CLAMP((s16)(angleToPlayer - this->actor.shape.rot.x), -0xBB8, 0xBB8); + + angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + interactInfo->headRotTarget.y = CLAMP((s16)(angleToPlayer * 0.7f), -0x1F40, 0x1F40); + if (Actor_TextboxIsClosing(&this->actor, play)) { + EnInvadepoh_Cremia_SetupWalk(this); + } +} + +void EnInvadepoh_Cremia_SetupIdle(EnInvadepoh* this) { + this->interactInfo.scaledTurnRate = 0.08f; + this->interactInfo.maxTurnRate = 0x7D0; + Animation_MorphToLoop(&this->skelAnime, &gCremiaIdleAnim, -6.0f); + this->unk_370 = 0; + this->actionFunc = EnInvadepoh_Cremia_Idle; +} + +void EnInvadepoh_Cremia_Idle(EnInvadepoh* this, PlayState* play) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + Player* player = GET_PLAYER(play); + s16 angleToPlayer; + + Math_StepToS(&this->unk_370, 0x7D0, 0xC8); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 6, this->unk_370, 0x28); + angleToPlayer = Math_Vec3f_Pitch(&this->actor.focus.pos, &player->actor.focus.pos) * 0.85f; + interactInfo->headRotTarget.x = CLAMP((s16)(angleToPlayer - this->actor.shape.rot.x), -0xBB8, 0xBB8); + + angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + interactInfo->headRotTarget.y = CLAMP((s16)(angleToPlayer * 0.7f), -0x1F40, 0x1F40); + if (sRomani == NULL) { + EnInvadepoh_Cremia_SetupWalk(this); + } else if ((sRomani != NULL) && (sRomani->actionFunc != EnInvadepoh_Night3Romani_Talk)) { + EnInvadepoh_Cremia_SetupWalk(this); + } +} + +void EnInvadepoh_Cremia_WaitForObject(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 pad; + + if (Object_IsLoaded(&play2->objectCtx, this->bankIndex)) { + s32 currentTime = gSaveContext.save.time; + + this->actor.objectSlot = this->bankIndex; + Actor_SetObjectDependency(play, &this->actor); + EnInvadepoh_Cremia_DesegmentTextures(); + SkelAnime_InitFlex(play, &this->skelAnime, &gCremiaSkel, &gCremiaWalkAnim, this->jointTable, this->morphTable, + CREMIA_LIMB_MAX); + EnInvadepoh_Interact_Init(&this->interactInfo, D_80B4EBDC, 1, D_80B4EC08, 0, &gZeroVec3s, 100, 0.03f, 0.3f, 0.03f); + this->actor.textId = 0x33CD; // Good night... See you...tomorrow...OK? + if (currentTime < CLOCK_TIME(20, 01) + 30) { + this->unk_304 = -0x8000; + this->unk_2F8 = 40.0f; + } else if (currentTime > CLOCK_TIME(20, 14) + 1) { + this->unk_304 = -0x4800; + this->unk_2F8 = 20.0f; + } else { + this->unk_304 = -0x8000; + this->unk_2F8 = 40.0f; + } + if ((CLOCK_TIME(6, 00) <= currentTime) && (currentTime < CLOCK_TIME(20, 00) + 30)) { + this->actor.update = EnInvadepoh_Cremia_WaitForEvent; + this->actor.draw = NULL; + } else if ((CLOCK_TIME(20, 00) + 30 <= currentTime) && (currentTime < CLOCK_TIME(20, 15))) { + this->actor.update = EnInvadepoh_Cremia_Update; + this->actor.draw = EnInvadepoh_Cremia_Draw; + EnInvadepoh_Cremia_SetupWalk(this); + } else { + Actor_Kill(&this->actor); + } + } +} + +void EnInvadepoh_Cremia_WaitForEvent(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if ((gSaveContext.save.time >= CLOCK_TIME(20, 00) + 30) && (gSaveContext.save.time < CLOCK_TIME(20, 15))) { + this->actor.update = EnInvadepoh_Cremia_Update; + this->actor.draw = EnInvadepoh_Cremia_Draw; + EnInvadepoh_Cremia_SetupWalk(this); + } +} + +void EnInvadepoh_Cremia_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 inUncullRange = CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_40); + s32 talkRequested = Actor_ProcessTalkRequest(&this->actor, &play->state); + + if (talkRequested) { + Message_BombersNotebookQueueEvent(play, 6); + EnInvadepoh_Cremia_SetupTalk(this); + } + this->actionFunc(this, play2); + if (inUncullRange && (this->actor.update != NULL)) { + SkelAnime_Update(&this->skelAnime); + EnInvadepoh_Interact_Update(&this->interactInfo); + if ((this->actionFunc != EnInvadepoh_Cremia_Talk) && !talkRequested && this->actor.isLockedOn) { + Actor_OfferTalk(&this->actor, play, 350.0f); + } + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); + } +} + +void EnInvadepoh_Night3Romani_SetupWalk(EnInvadepoh* this) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + + Animation_MorphToLoop(&this->skelAnime, &gRomaniWalkAnim, -10.0f); + interactInfo->headRotTarget.x = 0; + interactInfo->headRotTarget.y = 0; + interactInfo->headRotTarget.z = 0; + interactInfo->scaledTurnRate = 0.1f; + interactInfo->maxTurnRate = 0x320; + this->actionFunc = EnInvadepoh_Night3Romani_Walk; +} + +void EnInvadepoh_Night3Romani_Walk(EnInvadepoh* this, PlayState* play) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + EnInvadepoh* cremia = sCremia; + s32 curPoint; + s32 framesMod0x80; + s32 doorTimer; + s32 pad; + Vec3f curPathPointF; + Vec3f nextPathPointF; + + EnInvadepoh_Night3Romani_SetProgress(this); + EnInvadepoh_Night3Romani_MoveAlongPathTimed(this, play); + EnInvadepoh_Romani_TurnToPath(this, 6, 0x7D0, 0x64); + + curPoint = (this->currentPoint < this->endPoint) ? this->currentPoint : this->endPoint - 1; + + Math_Vec3s_ToVec3f(&curPathPointF, &this->pathPoints[curPoint]); + Math_Vec3s_ToVec3f(&nextPathPointF, &this->pathPoints[curPoint + 1]); + Math_SmoothStepToS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&curPathPointF, &nextPathPointF) + 0, 5, 0x7D0, 100); + if ((this->currentPoint == 0) || (this->currentPoint == this->endPoint - 1)) { + if (this->unk_378 == 0) { + s32 trueTimeSpeed = Environment_GetTimeSpeed(play); + doorTimer = trueTimeSpeed; + + if (trueTimeSpeed > 0) { + // This is really dividing by trueTimeSpeed, but matching requires writing like this + doorTimer = (R_TIME_SPEED * -23.0f / doorTimer) - 0.5f; + this->unk_378 = EnInvadepoh_Romani_OpenDoor(this, play, SQ(80.0f), doorTimer); + } + } + this->actor.flags &= ~(ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY); + } else { + this->unk_378 = 0; + this->actor.flags |= (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY); + } + framesMod0x80 = play->gameplayFrames % 0x80; + if (framesMod0x80 & 0x40) { + s16 targetYaw = Math_Vec3f_Yaw(&this->actor.world.pos, &cremia->actor.world.pos); + s16 angleToPlayer; + + if (framesMod0x80 == 0x40) { + this->unk_370 = 0; + } + Math_StepToS(&this->unk_370, 0x7D0, 0x28); + Math_SmoothStepToS(&this->actor.shape.rot.y, targetYaw, 6, this->unk_370, 40); + angleToPlayer = Math_Vec3f_Pitch(&this->actor.focus.pos, &cremia->actor.focus.pos) * 0.85f; + interactInfo->headRotTarget.x = CLAMP((s16)(angleToPlayer - this->actor.shape.rot.x), -0xBB8, 0xBB8); + + angleToPlayer = targetYaw - this->actor.shape.rot.y; + interactInfo->headRotTarget.y = CLAMP((s16)(angleToPlayer * 0.7f), -0x1F40, 0x1F40); + } + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_40) && + (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 7.0f))) { + Actor_PlaySfx(&this->actor, NA_SE_EN_ROMANI_WALK); + } + if (this->progress >= 0.9999f) { + Actor_Kill(&this->actor); + } else if ((cremia != NULL) && (cremia->actionFunc == EnInvadepoh_Cremia_Talk)) { + EnInvadepoh_Night3Romani_SetupIdle(this); + } +} + +void EnInvadepoh_Night3Romani_SetupTalk(EnInvadepoh* this) { + this->interactInfo.scaledTurnRate = 0.08f; + this->interactInfo.maxTurnRate = 0xFA0; + Animation_MorphToLoop(&this->skelAnime, &gRomaniIdleAnim, -10.0f); + this->unk_370 = 0; + this->actionFunc = EnInvadepoh_Night3Romani_Talk; +} + +void EnInvadepoh_Night3Romani_Talk(EnInvadepoh* this, PlayState* play) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + Player* player = GET_PLAYER(play); + s16 angleToPlayer; + + Math_StepToS(&this->unk_370, 0xBB8, 0x1F4); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 6, this->unk_370, 40); + angleToPlayer = Math_Vec3f_Pitch(&this->actor.focus.pos, &player->actor.focus.pos) * 0.85f; + interactInfo->headRotTarget.x = CLAMP((s16)(angleToPlayer - this->actor.shape.rot.x), -0xBB8, 0xBB8); + + angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + interactInfo->headRotTarget.y = CLAMP((s16)(angleToPlayer * 0.7f), -0x1F40, 0x1F40); + if (Actor_TextboxIsClosing(&this->actor, play)) { + EnInvadepoh_Night3Romani_SetupWalk(this); + } +} + +void EnInvadepoh_Night3Romani_SetupIdle(EnInvadepoh* this) { + this->interactInfo.scaledTurnRate = 0.08f; + this->interactInfo.maxTurnRate = 0x7D0; + Animation_MorphToLoop(&this->skelAnime, &gRomaniIdleAnim, -10.0f); + this->unk_370 = 0; + this->actionFunc = EnInvadepoh_Night3Romani_Idle; +} + +void EnInvadepoh_Night3Romani_Idle(EnInvadepoh* this, PlayState* play) { + EnInvadePohInteractInfo* interactInfo = &this->interactInfo; + Player* player = GET_PLAYER(play); + s16 angleToPlayer; + + Math_StepToS(&this->unk_370, 0x7D0, 0xC8); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 6, this->unk_370, 40); + angleToPlayer = Math_Vec3f_Pitch(&this->actor.focus.pos, &player->actor.focus.pos) * 0.85f; + interactInfo->headRotTarget.x = CLAMP((s16)(angleToPlayer - this->actor.shape.rot.x), -0xBB8, 0xBB8); + + angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + interactInfo->headRotTarget.y = CLAMP((s16)(angleToPlayer * 0.7f), -0x1F40, 0x1F40); + if (sCremia == NULL) { + EnInvadepoh_Night3Romani_SetupWalk(this); + } else if ((sCremia != NULL) && (sCremia->actionFunc != EnInvadepoh_Cremia_Talk)) { + EnInvadepoh_Night3Romani_SetupWalk(this); + } +} + +void EnInvadepoh_Night3Romani_WaitForObject(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 pad; + + if (Object_IsLoaded(&play2->objectCtx, this->bankIndex)) { + s32 currentTime = gSaveContext.save.time; + + this->actor.objectSlot = this->bankIndex; + Actor_SetObjectDependency(play, &this->actor); + EnInvadepoh_Romani_DesegmentTextures(); + SkelAnime_InitFlex(play, &this->skelAnime, &gRomaniSkel, &gRomaniWalkAnim, this->jointTable, this->morphTable, + ROMANI_LIMB_MAX); + EnInvadepoh_Interact_Init(&this->interactInfo, D_80B4EA90, 1, D_80B4EB00, 3, &gZeroVec3s, 100, 0.03f, 0.3f, 0.03f); + EnInvadepoh_Night3Romani_SetupPath(this, play); + this->actor.world.rot.y = this->actor.shape.rot.y; + EnInvadepoh_Night3Romani_SetProgress(this); + EnInvadepoh_Night3Romani_MoveAlongPathTimed(this, play); + EnInvadepoh_SetYawAlongPath(this); + EnInvadepoh_SnapToFloor(this); + this->actor.textId = 0x33CE; // See you tomorrow! + if ((currentTime >= CLOCK_TIME(6, 00)) && (currentTime < CLOCK_TIME(20, 00))) { + this->actor.update = EnInvadepoh_Night3Romani_WaitForEvent; + this->actor.draw = NULL; + } else if ((currentTime >= CLOCK_TIME(20, 00)) && (currentTime < CLOCK_TIME(20, 14) + 15)) { + this->actor.update = EnInvadepoh_Night3Romani_Update; + this->actor.draw = EnInvadepoh_Romani_Draw; + EnInvadepoh_Night3Romani_SetupWalk(this); + } else { + Actor_Kill(&this->actor); + } + } +} + +void EnInvadepoh_Night3Romani_WaitForEvent(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if ((gSaveContext.save.time >= CLOCK_TIME(20, 00)) && (gSaveContext.save.time < CLOCK_TIME(20, 14) + 15)) { + this->actor.update = EnInvadepoh_Night3Romani_Update; + this->actor.draw = EnInvadepoh_Romani_Draw; + EnInvadepoh_Night3Romani_SetupWalk(this); + } +} + +void EnInvadepoh_Night3Romani_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 inUncullRange = CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_40); + s32 talkRequested = Actor_ProcessTalkRequest(&this->actor, &play->state); + + if (talkRequested) { + Message_BombersNotebookQueueEvent(play, 5); + EnInvadepoh_Night3Romani_SetupTalk(this); + } + this->actionFunc(this, play); + if (inUncullRange && (this->actor.update != NULL)) { + SkelAnime_Update(&this->skelAnime); + EnInvadepoh_Interact_Update(&this->interactInfo); + if ((this->actionFunc != EnInvadepoh_Night3Romani_Talk) && !talkRequested && this->actor.isLockedOn) { + Actor_OfferTalk(&this->actor, play, 350.0f); + } + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); + } +} + +void EnInvadepoh_Abductor_SetupCow(EnInvadepoh* this) { + static EnInvadepohUnkStruct1 D_80B4EE0C[3] = { + { 0.08f, 0x2BC, -0xA }, + { 0.09f, 0x12C, -5 }, + { 0.05f, 0x190, 0 }, + }; + s32 pad; + s32 index = ENINVADEPOH_GET_INDEX(&this->actor); + s32 pad2; + + Animation_PlayLoop(&this->skelAnime, &gAlienHoldingCowAnim); + this->skelAnime.curFrame = (ENINVADEPOH_GET_INDEX(&this->actor)) * this->skelAnime.endFrame * 0.25f; + this->alpha = 255; + this->actor.draw = EnInvadepoh_Alien_Draw; + this->present = true; + this->drawDeathFlash = false; + this->eyeBeamAlpha = 255; + this->unk_300 = D_80B4EE0C[index].unk0; + this->unk_304 = index * 0x5555; + this->actor.world.pos.x = Math_SinS(this->unk_304) * 80.0f + this->actor.home.pos.x; + this->actor.world.pos.y = this->actor.home.pos.y; + this->actor.world.pos.z = Math_CosS(this->unk_304) * 80.0f + this->actor.home.pos.z; + this->actor.shape.rot.y = this->unk_304 + 0x4000; + this->unk_370 = D_80B4EE0C[index].unk4; + this->unk_372 = D_80B4EE0C[index].unk6; + this->actionFunc = EnInvadepoh_Abductor_Cow; + this->actor.velocity.y = 0.0f; +} + +void EnInvadepoh_Abductor_Cow(EnInvadepoh* this, PlayState* play) { + Actor* cow; + f32 distToTarget; + + if (sLightBall == NULL) { + Actor_Kill(&this->actor); + return; + } + Math_StepToF(&this->actor.velocity.y, 15.0f, this->unk_300); + distToTarget = Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y + 850.0f, 0.2f, + this->actor.velocity.y, 0.01f); + this->unk_304 += 0x2BC; + this->actor.world.pos.x = Math_SinS(this->unk_304) * 80.0f + this->actor.home.pos.x; + this->actor.world.pos.z = Math_CosS(this->unk_304) * 80.0f + this->actor.home.pos.z; + this->unk_370 += this->unk_372; + this->actor.shape.rot.y += this->unk_370; + + if (this->actor.child != NULL) { + cow = this->actor.child; + cow->world.pos.x = this->actor.world.pos.x; + cow->world.pos.y = this->actor.world.pos.y - 38.0f; + cow->world.pos.z = this->actor.world.pos.z; + cow->shape.rot.y = this->actor.shape.rot.y; + } + if (distToTarget < 5.0f) { + Actor_Kill(&this->actor); + } +} + +void EnInvadepoh_Abductor_SetupRomani(EnInvadepoh* this) { + Animation_PlayLoop(&this->skelAnime, &gAlienHoldingCowAnim); + this->skelAnime.curFrame = (ENINVADEPOH_GET_INDEX(&this->actor)) * this->skelAnime.endFrame * 0.25f; + this->alpha = 255; + this->actor.draw = NULL; + this->present = true; + this->drawDeathFlash = false; + this->eyeBeamAlpha = 255; + this->unk_306 = 0x190; + this->unk_304 = 0; + this->actionTimer = 200; + this->actionFunc = EnInvadepoh_Abductor_Romani; + this->actor.velocity.y = 0.0f; +} + +void EnInvadepoh_Abductor_Romani(EnInvadepoh* this, PlayState* play) { + s32 actionTimer; + f32 tempUnk_306; + s32 reachedTargetY = 0; + + if (this->actionTimer > 0) { + this->actionTimer--; + } + if (this->actionTimer > 160) { + this->actor.draw = NULL; + } else { + this->actor.draw = EnInvadepoh_Alien_Draw; + actionTimer = (reachedTargetY ? 0 : this->actionTimer); + if ((105 > actionTimer) && (actionTimer >= 100)) { + this->actor.gravity = -1.0f; + Math_SmoothStepToS(&this->actor.shape.rot.x, 0x2000, 8, 0x320, 0x28); + } else { + this->actor.gravity = 0.7f; + Math_SmoothStepToS(&this->actor.shape.rot.x, 0, 8, 0x320, 0x28); + } + + this->actor.velocity.y += this->actor.gravity; + this->actor.velocity.y *= 0.92f; + if (this->actionTimer > 80) { + this->actor.world.pos.y += this->actor.velocity.y; + } else { + f32 targetY = this->actor.home.pos.y + 850.0f; + + reachedTargetY = Math_StepToF(&this->actor.world.pos.y, targetY, fabsf(this->actor.velocity.y)); + } + tempUnk_306 = (this->unk_304 * -0.06f) + this->unk_306; + tempUnk_306 *= 0.98f; + this->unk_306 = tempUnk_306; + this->actor.shape.rot.y += this->unk_306; + if (this->actor.child != NULL) { + Actor* romani = this->actor.child; + + romani->world.pos.x = this->actor.world.pos.x; + romani->world.pos.y = this->actor.world.pos.y - 30.0f; + romani->world.pos.z = this->actor.world.pos.z; + romani->shape.rot.y = this->actor.shape.rot.y; + } + } + if ((this->actionTimer <= 0) || reachedTargetY) { + Actor_Kill(&this->actor); + } +} + +void EnInvadepoh_Abuductor_WaitForObject(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + s32 index; + + if (Object_IsLoaded(&play->objectCtx, this->bankIndex)) { + index = ENINVADEPOH_GET_INDEX(&this->actor); + this->actor.objectSlot = this->bankIndex; + Actor_SetObjectDependency(play, &this->actor); + EnInvadepoh_Alien_SetTexAnim(); + this->actor.update = EnInvadepoh_Abductor_Update; + SkelAnime_InitFlex(play, &this->skelAnime, &gAlienSkel, &gAlienHoldingCowAnim, this->jointTable, + this->morphTable, ALIEN_LIMB_MAX); + if (index < 3) { + EnInvadepoh_Abductor_SpawnCow(this, play, index); + EnInvadepoh_Abductor_SetupCow(this); + } else { + EnInvadepoh_Abductor_SpawnRomani(this, play); + EnInvadepoh_Abductor_SetupRomani(this); + } + } +} + +void EnInvadepoh_Abductor_Update(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + this->actionFunc(this, play); + + if (this->actor.update != NULL) { + SkelAnime_Update(&this->skelAnime); + Actor_PlaySfx_Flagged(&this->actor, NA_SE_EN_FOLLOWERS_BEAM_PRE - SFX_FLAG); + } +} + +void EnInvadepoh_Event_DrawWarps(PlayState* play) { + s32 i; + EnInvadepohWarpEffect* warpEffect; + + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL25_Xlu(play->state.gfxCtx); + + for (i = 0, warpEffect = sWarpEffects; i < 10; i++, warpEffect++) { + if (warpEffect->timer > 0) { + u32 warpScrollX1 = (play->gameplayFrames + (u8)(i * 0x10)) % 0x80; + u32 warpScrollY2 = (play->gameplayFrames * -0xF) % 0x100; + + Matrix_Translate(warpEffect->pos.x, warpEffect->pos.y, warpEffect->pos.z, MTXMODE_NEW); + Matrix_Scale(0.1f, 0.1f, 0.1f, MTXMODE_APPLY); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 170, warpEffect->alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 50, 0, 0); + gSPSegment(POLY_XLU_DISP++, 0x8, + Gfx_TwoTexScroll(play->state.gfxCtx, 0, warpScrollX1, 0, 0x20, 0x40, 1, 0, warpScrollY2, 0x20, 0x40)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gameplay_keep_DL_02E510); + } + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void EnInvadepoh_Event_Draw(Actor* thisx, PlayState* play) { + EnInvadepoh_Event_DrawWarps(play); +} + +s32 EnInvadepoh_Alien_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + Actor* thisx, Gfx** gfx) { + return false; +} + +void EnInvadepoh_Alien_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* pos, Actor* thisx, Gfx** gfx) { + static Vec3f sFocusOffset = { 2000.0f, 1000.0f, 0.0f }; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if ((limbIndex == ALIEN_LIMB_LEFT_EYE) && (this->eyeBeamAlpha != 0)) { + Matrix_Push(); + Matrix_RotateZS(-0x53ED, MTXMODE_APPLY); + Matrix_RotateYS(-0x3830, MTXMODE_APPLY); + Matrix_Scale(1.0f, 1.0f, 1.5f, MTXMODE_APPLY); + Matrix_Get(&sAlienLeftEyeBeamMtxF); + Matrix_Pop(); + } else if ((limbIndex == ALIEN_LIMB_RIGHT_EYE) && (this->eyeBeamAlpha != 0)) { + Matrix_Push(); + Matrix_RotateZS(-0x53ED, MTXMODE_APPLY); + Matrix_RotateYS(-0x47D0, MTXMODE_APPLY); + Matrix_Scale(1.0f, 1.0f, 1.5f, MTXMODE_APPLY); + Matrix_Get(&sAlienRightEyeBeamMtxF); + Matrix_Pop(); + } + if (limbIndex == ALIEN_LIMB_HEAD) { + Matrix_MultVec3f(&sFocusOffset, &this->actor.focus.pos); + } +} + +void EnInvadepoh_Alien_Draw(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL25_Xlu(play->state.gfxCtx); + Matrix_Push(); + if (this->present) { + if (this->alpha == 255) { + Gfx_SetupDL25_Opa(play->state.gfxCtx); + AnimatedMat_Draw(play, sAlienEmptyTexAnim); + Scene_SetRenderModeXlu(play, 0, 1); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + POLY_OPA_DISP = SkelAnime_DrawFlex(play, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnInvadepoh_Alien_OverrideLimbDraw, + EnInvadepoh_Alien_PostLimbDraw, &this->actor, POLY_OPA_DISP); + } else { + AnimatedMat_Draw(play, sAlienEmptyTexAnim); + Scene_SetRenderModeXlu(play, 1, 2); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha); + POLY_XLU_DISP = SkelAnime_DrawFlex(play, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnInvadepoh_Alien_OverrideLimbDraw, + EnInvadepoh_Alien_PostLimbDraw, &this->actor, POLY_XLU_DISP); + } + if (this->eyeBeamAlpha != 0) { + Gfx* ptr; + + AnimatedMat_Draw(play, sAlienEyeBeamTexAnim); + + OPEN_DISPS(play->state.gfxCtx); + ptr = POLY_XLU_DISP; + + gDPPipeSync(ptr++); + gDPSetPrimColor(ptr++, 0, 0xFF, 240, 180, 100, 60); + gDPSetEnvColor(ptr++, 255, 255, 255, 100.0f / 170.0f * this->eyeBeamAlpha); + + Matrix_Mult(&sAlienLeftEyeBeamMtxF, MTXMODE_NEW); + gSPMatrix(ptr++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(ptr++, gAlienEyeBeamDL); + + Matrix_Mult(&sAlienRightEyeBeamMtxF, MTXMODE_NEW); + gSPMatrix(ptr++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(ptr++, gAlienEyeBeamDL); + + POLY_XLU_DISP = ptr; + CLOSE_DISPS(play->state.gfxCtx); + } + } + if (this->drawDeathFlash) { + Matrix_SetTranslateRotateYXZ(this->actor.world.pos.x, this->actor.world.pos.y + 68.0f, this->actor.world.pos.z, + &this->actor.shape.rot); + Matrix_Scale(this->deathFlashScale.x, this->deathFlashScale.y, this->deathFlashScale.z, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gAlienDeathFlashDL); + } + if (this->present) { + Gfx* ptr; + Vec3f glowOffset; + Vec3f glowPos; + s32 glowAlpha; + + OPEN_DISPS(play->state.gfxCtx); + ptr = POLY_XLU_DISP; + + ptr = Gfx_SetupDL20_NoCD(ptr); + + gDPSetDither(ptr++, G_CD_NOISE); + gDPSetCombineLERP(ptr++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, + 0); + + Matrix_Mult(&play->billboardMtxF, MTXMODE_NEW); + Matrix_MultVecZ(60.0f, &glowOffset); + + glowPos.x = this->actor.world.pos.x + glowOffset.x; + glowPos.y = this->actor.world.pos.y + glowOffset.y + 68.0f; + glowPos.z = this->actor.world.pos.z + glowOffset.z; + + Matrix_Translate(glowPos.x, glowPos.y, glowPos.z, MTXMODE_NEW); + Matrix_Scale(0.25f, 0.25f, 0.25f, MTXMODE_APPLY); + + glowAlpha = 100.0f / 255.0f * this->alpha; + + gSPDisplayList(ptr++, gameplay_keep_DL_029CB0); + gDPSetPrimColor(ptr++, 0, 0, 240, 180, 100, glowAlpha); + gSPMatrix(ptr++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(ptr++, gameplay_keep_DL_029CF0); + + POLY_XLU_DISP = ptr; + + if ((this->alpha > 128) && EnInvadepoh_Alien_LensFlareDepthCheck(play, &glowPos)) { + Environment_DrawLensFlare(play, &play->envCtx, &play->view, play->state.gfxCtx, glowPos, 10.0f, 9.0f, 0, 0); + } + + CLOSE_DISPS(play->state.gfxCtx); + } + Matrix_Pop(); + + CLOSE_DISPS(play->state.gfxCtx); +} + +s32 EnInvadepoh_Cow_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + Actor* thisx) { + if (limbIndex == COW_LIMB_NOSE_RING) { + EnInvadepoh* this = (EnInvadepoh*)thisx; + + rot->x -= this->actor.shape.rot.x; + } + return false; +} + +void EnInvadepoh_Cow_Draw(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Gfx_SetupDL37_Opa(play->state.gfxCtx); + SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, (s32)this->skelAnime.dListCount, + EnInvadepoh_Cow_OverrideLimbDraw, NULL, &this->actor); +} + +void EnInvadepoh_CowTail_Draw(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Gfx_SetupDL37_Opa(play->state.gfxCtx); + SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, NULL, + NULL, &this->actor); +} + +s32 EnInvadepoh_Romani_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + Actor* thisx) { + if (limbIndex == ROMANI_LIMB_HEAD) { + EnInvadepoh* this = ((EnInvadepoh*)thisx); + + rot->x += this->interactInfo.headRot.y; + rot->y += this->interactInfo.headRot.z; + rot->z += this->interactInfo.headRot.x; + } else if (limbIndex == ROMANI_LIMB_TORSO) { + EnInvadepoh* this = ((EnInvadepoh*)thisx); + + rot->x += (s16)(this->interactInfo.torsoRotXMod * this->interactInfo.headRot.y); + rot->z += this->interactInfo.torsoRotZ; + } + return false; +} + +void EnInvadepoh_Romani_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* pos, Actor* thisx) { + static Vec3f sFocusOffset = { 400.0f, 270.0f, 0.0f }; + EnInvadepoh* this = (EnInvadepoh*)thisx; + + if (limbIndex == ROMANI_LIMB_LEFT_HAND) { + OPEN_DISPS(play->state.gfxCtx); + + gSPDisplayList(POLY_OPA_DISP++, gRomaniBowDL); + + CLOSE_DISPS(play->state.gfxCtx); + } else if (limbIndex == ROMANI_LIMB_HEAD) { + Matrix_MultVec3f(&sFocusOffset, &this->actor.focus.pos); + } +} + +void EnInvadepoh_Romani_Draw(Actor* thisx, PlayState* play) { + EnInvadepoh* this = (EnInvadepoh*)thisx; + + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL25_Opa(play->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x09, sRomaniMouthTextures[this->interactInfo.mouthAnim.curIndex]); + gSPSegment(POLY_OPA_DISP++, 0x08, sRomaniEyeTextures[this->interactInfo.eyeAnim.curIndex]); + SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnInvadepoh_Romani_OverrideLimbDraw, EnInvadepoh_Romani_PostLimbDraw, &this->actor); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void EnInvadepoh_LightBall_Draw(Actor* thisx, PlayState* play2) { + PlayState* play = play2; + EnInvadepoh* this = (EnInvadepoh*)thisx; + Vec3f flashPos; + + Matrix_Push(); + Matrix_Mult(&play->billboardMtxF, MTXMODE_NEW); + Matrix_MultVecZ(200.0f, &flashPos); + Matrix_Pop(); + flashPos.x += this->actor.world.pos.x; + flashPos.y += this->actor.world.pos.y; + flashPos.z += this->actor.world.pos.z; + EnInvadepoh_LightBall_ReplaceTranslation(&flashPos); + Matrix_ReplaceRotation(&play->billboardMtxF); + Matrix_RotateZS(this->unk_304, MTXMODE_APPLY); + + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL25_Xlu(play->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_XLU_DISP++, 0xFF, 0x80, 255, 255, 0, 180); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 50, 0, 0); + gSPDisplayList(POLY_XLU_DISP++, gEffFlash1DL); + + if (EnInvadepoh_Alien_LensFlareDepthCheck(play, &flashPos)) { + Environment_DrawLensFlare(play, &play->envCtx, &play->view, play->state.gfxCtx, flashPos, 20.0f, 9.0f, 0, 0); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +s32 EnInvadepoh_Dog_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + Actor* thisx) { + if ((limbIndex == DOG_LIMB_HEAD) || (limbIndex == DOG_LIMB_RIGHT_FACE_HAIR) || + (limbIndex == DOG_LIMB_LEFT_FACE_HAIR)) { + EnInvadepoh* this = (EnInvadepoh*)thisx; + + rot->x += this->interactInfo.headRot.x; + rot->y += this->interactInfo.headRot.y; + rot->z += this->interactInfo.headRot.z; + } + return false; +} + +void EnInvadepoh_Dog_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* pos, Actor* thisx) { + if (limbIndex == DOG_LIMB_HEAD) { + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Matrix_MultVecY(20.0f, &this->actor.focus.pos); + } +} + +void EnInvadepoh_Dog_Draw(Actor* thisx, PlayState* play) { + EnInvadepoh* this = (EnInvadepoh*)thisx; + + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL25_Opa(play->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 200, 0); + SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnInvadepoh_Dog_OverrideLimbDraw, EnInvadepoh_Dog_PostLimbDraw, &this->actor); + + CLOSE_DISPS(play->state.gfxCtx); +} + +s32 EnInvadepoh_Cremia_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + Actor* thisx) { + if (limbIndex == CREMIA_LIMB_HEAD) { + EnInvadepoh* this = (EnInvadepoh*)thisx; + + rot->x += this->interactInfo.headRot.y; + rot->y += this->interactInfo.headRot.z; + rot->z += this->interactInfo.headRot.x; + } else if (limbIndex == CREMIA_LIMB_TORSO) { + EnInvadepoh* this = (EnInvadepoh*)thisx; + + rot->x += (s16)(this->interactInfo.torsoRotXMod * this->interactInfo.headRot.y); + } + return false; +} + +void EnInvadepoh_Cremia_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* pos, Actor* thisx) { + if (limbIndex == CREMIA_LIMB_HEAD) { + EnInvadepoh* this = (EnInvadepoh*)thisx; + + Matrix_MultZero(&this->actor.focus.pos); + } +} + +void EnInvadepoh_Cremia_Draw(Actor* thisx, PlayState* play) { + EnInvadepoh* this = (EnInvadepoh*)thisx; + + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL25_Opa(play->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x09, sCremiaMouthTextures[this->interactInfo.mouthAnim.curIndex]); + gSPSegment(POLY_OPA_DISP++, 0x08, sCremiaEyeTextures[this->interactInfo.eyeAnim.curIndex]); + SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnInvadepoh_Cremia_OverrideLimbDraw, EnInvadepoh_Cremia_PostLimbDraw, &this->actor); + + CLOSE_DISPS(play->state.gfxCtx); +} diff --git a/src/overlays/actors/ovl_En_Invadepoh/z_en_invadepoh.h b/src/overlays/actors/ovl_En_Invadepoh/z_en_invadepoh.h index 54253ad03..29b63d870 100644 --- a/src/overlays/actors/ovl_En_Invadepoh/z_en_invadepoh.h +++ b/src/overlays/actors/ovl_En_Invadepoh/z_en_invadepoh.h @@ -2,12 +2,189 @@ #define Z_EN_INVADEPOH_H #include "global.h" +#include "assets/objects/object_uch/object_uch.h" +#include "assets/objects/object_ma1/object_ma1.h" +#include "assets/objects/object_ma2/object_ma2.h" +#include "assets/objects/object_cow/object_cow.h" +#include "assets/objects/object_dog/object_dog.h" struct EnInvadepoh; +typedef void (*EnInvadepohActionFunc)(struct EnInvadepoh* this, PlayState* play); +typedef void (*EnInvadepohMainFunc)(Actor* thisx, PlayState* play); // PlayState, not GameState + +typedef struct { + /* 0x0 */ f32 unk0; + /* 0x4 */ s16 unk4; + /* 0x6 */ s16 unk6; +} EnInvadepohUnkStruct1; // size = 0x8 + +typedef struct { + /* 0x0 */ s8 type; + /* 0x1 */ s8 timer; + /* 0x2 */ u8 alpha; + /* 0x4 */ Vec3f pos; +} EnInvadepohWarpEffect; // size = 0x10 + +typedef struct { + /* 0x0 */ s8* texIndex; + /* 0x1 */ s8 count; +} EnInvadepohFaceFrames; // size = 0x2 + +typedef struct { + /* 0x0 */ s8 index; + /* 0x4 */ f32 chance; +} EnInvadepohFaceAnimNext; // size = 0x8 + +#if 1 +typedef struct { + /* 0x0 */ s8 type; + /* 0x4 */ EnInvadepohFaceFrames* frames; +} EnInvadepohFaceAnim; // size = 0x8 + +typedef struct { + /* 0x0 */ EnInvadepohFaceAnim anim; + /* 0x8 */ s8 nextCount; + /* 0xC */ EnInvadepohFaceAnimNext* nextAnims; +} EnInvadepohFaceAnimBranched; // size = 0x10 + +typedef struct { + /* 0x00 */ EnInvadepohFaceAnimBranched loop; + /* 0x10 */ s16 minDelay; + /* 0x12 */ s16 maxDelay; +} EnInvadepohFaceAnimDelayedBranched; // size = 0x14 + +#else + +typedef struct { + /* 0x0 */ s8 type; + /* 0x4 */ EnInvadepohFaceFrames* frames; +} EnInvadepohFaceAnimOnce; // size = 0x8 + +typedef struct { + /* 0x0 */ s8 type; + /* 0x4 */ EnInvadepohFaceFrames* frames; + /* 0x8 */ s8 nextCount; + /* 0xC */ EnInvadepohFaceAnimNext* nextAnims; +} EnInvadepohFaceAnimBranched; // size = 0x10 + +typedef struct { + /* 0x0 */ s8 type; + /* 0x4 */ EnInvadepohFaceFrames* frames; + /* 0x8 */ s8 nextCount; + /* 0xC */ EnInvadepohFaceAnimNext* nextAnims; + /* 0x10 */ s16 minDelay; + /* 0x12 */ s16 maxDelay; +} EnInvadepohFaceAnimDelayedBranched; // size = 0x14 + +typedef union { + EnInvadepohFaceAnimOnce once; + EnInvadepohFaceAnimBranched branch; + EnInvadepohFaceAnimDelayedBranched delay; +} EnInvadepohFaceAnim; + +#endif + +typedef struct { + /* 0x0 */ EnInvadepohFaceAnim** animSet; + /* 0x4 */ s8 curAnimType; + /* 0x8 */ EnInvadepohFaceAnim* curAnim; + /* 0xC */ s16 delayTimer; + /* 0xE */ s8 curFrame; + /* 0xF */ s8 curIndex; +} EnInvadePohFaceAnimInfo; // size = 0x10 + +typedef struct { + /* 0x00 */ EnInvadePohFaceAnimInfo eyeAnim; + /* 0x10 */ EnInvadePohFaceAnimInfo mouthAnim; + /* 0x20 */ Vec3s headRot; + /* 0x26 */ Vec3s headRotTarget; + /* 0x2C */ s16 maxTurnRate; + /* 0x30 */ f32 scaledTurnRate; + /* 0x34 */ f32 torsoRotXMod; + /* 0x38 */ f32 torsoRotXModTarget; + /* 0x3c */ f32 torsoRotXModStep; + /* 0x40 */ s16 torsoRotZ; + /* 0x42 */ s16 torsoRotZTarget; + /* 0x44 */ f32 torsoMaxTurnRate; + /* 0x48 */ s16 torsoMaxTurnStep; +} EnInvadePohInteractInfo; // size = 0x4C + +typedef void (*EnInvadepohFaceFunc) (EnInvadePohFaceAnimInfo*, EnInvadepohFaceAnim**); + +#define EN_INVADEPOH_LIMB_MAX 23 +// #define EN_INVADEPOH_LIMB_MAX MAX(MAX(MAX(MAX(MAX(ALIEN_LIMB_MAX, ROMANI_LIMB_MAX), CREMIA_LIMB_MAX), DOG_LIMB_MAX), COW_LIMB_MAX), COW_TAIL_LIMB_MAX) + typedef struct EnInvadepoh { /* 0x000 */ Actor actor; - /* 0x144 */ char unk_144[0x27C]; + /* 0x144 */ SkelAnime skelAnime; + /* 0x188 */ Vec3s jointTable[EN_INVADEPOH_LIMB_MAX]; + /* 0x212 */ Vec3s morphTable[EN_INVADEPOH_LIMB_MAX]; + /* 0x29C */ s8 finishedAnim; + /* 0x2A0 */ ColliderCylinder collider; + /* 0x2EC */ EnInvadepohActionFunc actionFunc; + /* 0x2F0 */ s16 actionTimer; + /* 0x2F2 */ s16 stateTimer; + /* 0x2F4 */ s8 bankIndex; + /* 0x2F8 */ f32 unk_2F8; + /* 0x2FC */ char unk_2FC[4]; // unused? f32? + /* 0x300 */ f32 unk_300; + /* 0x304 */ s16 unk_304; + /* 0x306 */ s16 unk_306; + /* 0x308 */ s8 endPoint; + /* 0x309 */ s8 currentPoint; + /* 0x30A */ s8 pathStep; + /* 0x30C */ Vec3s *pathPoints; + /* 0x310 */ f32 pathLength; + /* 0x314 */ Vec3f unk_314; + /* 0x320 */ f32 progress; + /* 0x324 */ EnInvadePohInteractInfo interactInfo; + /* 0x370 */ s16 unk_370; + /* 0x372 */ s16 unk_372; + /* 0x374 */ s8 unk_374; + /* 0x376 */ u16 textId; + /* 0x378 */ s8 unk_378; + /* 0x379 */ s8 spawnCount; + /* 0x37C */ f32 checkpoints[3]; + /* 0x388 */ char unk_388; // unused? + /* 0x389 */ u8 alpha; + /* 0x38A */ s8 atBarn; + /* 0x38B */ s8 present; + /* 0x38C */ s8 drawDeathFlash; + /* 0x38D */ u8 eyeBeamAlpha; + /* 0x390 */ Vec3f deathFlashScale; + /* 0x39C */ f32 pulseScale; + /* 0x3A0 */ f32 pulseScaleTarget; + /* 0x3A4 */ f32 pulseScaleRate; + /* 0x3A8 */ s16 pulsePhase; + /* 0x3AA */ s16 pulseRate; + /* 0x3AC */ s8 lightBallTarget; + /* 0x3AD */ char pad3AD[3]; + /* 0x3B0 */ char pad3B0[0xC]; // unused? Vec3f? + /* 0x3BC */ s8 dogTargetPoint; } EnInvadepoh; // size = 0x3C0 +typedef enum { + /* 0x0 */ ENINVADEPOH_HANDLER, // Invasion event handler + /* 0x1 */ ENINVADEPOH_ALIEN_INVADER, // Alien invader + /* 0x2 */ ENINVADEPOH_COW, // Abudcted cow, spawned by unused alien abductor + /* 0x3 */ ENINVADEPOH_COW_TAIL, // Abducted cow tail, spawned by abducted cow + /* 0x4 */ ENINVADEPOH_ROMANI_ABDUCTED, // Abducted Romani, spawned by unused alien abductor + /* 0x5 */ ENINVADEPOH_ROMANI_CONFUSED, // Returned, confused Romani + /* 0x6 */ ENINVADEPOH_LIGHT_BALL, // Light ball spaceship + /* 0x7 */ ENINVADEPOH_ROMANI_NIGHT_1, // Romani running to barn on night 1 + /* 0x8 */ ENINVADEPOH_ROMANI_BARN, // Romani in the barn on night 1 + /* 0x9 */ ENINVADEPOH_ROMANI_REWARD, // Romani rewarding you for defeating invasion + /* 0xA */ ENINVADEPOH_DOG, // Dog during alien invasion + /* 0xB */ ENINVADEPOH_CREMIA_NIGHT_3, // Cremia walking from barn to house on night 3 + /* 0xC */ ENINVADEPOH_ROMANI_NIGHT_3, // Romnani walking from barn to house on night 3 + /* 0xD */ ENINVADEPOH_ALIEN_ABDUCTOR, // Alien abductor, unused, carries cow or Romani +} EnInvadepohParamsTypes; + +#define ENINVADEPOH_GET_INDEX(thisx) ((thisx)->params & 7) +#define ENINVADEPOH_GET_TYPE(thisx) ((thisx)->params >> 4 & 0xF) +#define ENINVADEPOH_GET_PATH(thisx) ((thisx)->params >> 8 & 0x7F) + +#define ENINVADEPOH_SET_PARAMS(path, type, index) (((index) & 7) | (((path) << 8) & 0x7F00) | (((type) << 4) & 0xF0)) + #endif // Z_EN_INVADEPOH_H diff --git a/src/overlays/actors/ovl_En_Invadepoh_Demo/z_en_invadepoh_demo.c b/src/overlays/actors/ovl_En_Invadepoh_Demo/z_en_invadepoh_demo.c index 87164778e..29f5414af 100644 --- a/src/overlays/actors/ovl_En_Invadepoh_Demo/z_en_invadepoh_demo.c +++ b/src/overlays/actors/ovl_En_Invadepoh_Demo/z_en_invadepoh_demo.c @@ -200,8 +200,8 @@ static EnInvadepohDemoFunc sDrawFuncs[EN_INVADEPOH_DEMO_TYPE_MAX] = { EnInvadepohDemo_CowTail_Draw, // EN_INVADEPOH_DEMO_TYPE_COW_TAIL }; -MtxF sAlienLeftEyeBeamMtxF; -MtxF sAlienRightEyeBeamMtxF; +MtxF sAlienLeftEyeBeamMtxF_2; +MtxF sAlienRightEyeBeamMtxF_2; void EnInvadepohDemo_DoNothing(EnInvadepohDemo* this, PlayState* play) { } @@ -589,14 +589,14 @@ void EnInvadepohDemo_Alien_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dL Matrix_RotateZS(-0x53ED, MTXMODE_APPLY); Matrix_RotateYS(-0x3830, MTXMODE_APPLY); Matrix_Scale(1.0f, 1.0f, 1.5f, MTXMODE_APPLY); - Matrix_Get(&sAlienLeftEyeBeamMtxF); + Matrix_Get(&sAlienLeftEyeBeamMtxF_2); Matrix_Pop(); } else if (limbIndex == ALIEN_LIMB_RIGHT_EYE) { Matrix_Push(); Matrix_RotateZS(-0x53ED, MTXMODE_APPLY); Matrix_RotateYS(-0x47D0, MTXMODE_APPLY); Matrix_Scale(1.0f, 1.0f, 1.5f, MTXMODE_APPLY); - Matrix_Get(&sAlienRightEyeBeamMtxF); + Matrix_Get(&sAlienRightEyeBeamMtxF_2); Matrix_Pop(); } } @@ -631,14 +631,14 @@ void EnInvadepohDemo_Alien_Draw(EnInvadepohDemo* this, PlayState* play) { gDPPipeSync(gfx++); gDPSetPrimColor(gfx++, 0, 0xFF, 240, 180, 100, 60); gDPSetEnvColor(gfx++, 255, 255, 255, 150); - Matrix_Mult(&sAlienLeftEyeBeamMtxF, MTXMODE_NEW); + Matrix_Mult(&sAlienLeftEyeBeamMtxF_2, MTXMODE_NEW); mtx = Matrix_NewMtx(play->state.gfxCtx); if (mtx != NULL) { gSPMatrix(gfx++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(gfx++, gAlienEyeBeamDL); - Matrix_Mult(&sAlienRightEyeBeamMtxF, MTXMODE_NEW); + Matrix_Mult(&sAlienRightEyeBeamMtxF_2, MTXMODE_NEW); mtx = Matrix_NewMtx(play->state.gfxCtx); diff --git a/undefined_syms.txt b/undefined_syms.txt index c3b6862a0..594fa644e 100644 --- a/undefined_syms.txt +++ b/undefined_syms.txt @@ -411,6 +411,11 @@ D_06015C28 = 0x06015C28; D_060156D8 = 0x060156D8; D_06016720 = 0x06016720; +// ovl_En_Jso + +D_060081F4 = 0x060081F4; +D_0600AA00 = 0x0600AA00; + // ovl_En_Jso2 D_06002ED8 = 0x06002ED8;