mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-08-13 17:03:47 -04:00
39cc86c260
subrepo: subdir: "soh" merged: "ba904bbd0" upstream: origin: "https://github.com/HarbourMasters/soh.git" branch: "master" commit: "ba904bbd0" git-subrepo: version: "0.4.1" origin: "???" commit: "???"
1664 lines
58 KiB
C
1664 lines
58 KiB
C
#include "z_en_skj.h"
|
|
#include "overlays/actors/ovl_En_Skjneedle/z_en_skjneedle.h"
|
|
#include "objects/object_skj/object_skj.h"
|
|
|
|
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_25)
|
|
|
|
void EnSkj_Init(Actor* thisx, GlobalContext* globalCtx);
|
|
void EnSkj_Destroy(Actor* thisx, GlobalContext* globalCtx);
|
|
void EnSkj_Update(Actor* thisx, GlobalContext* globalCtx);
|
|
void EnSkj_Draw(Actor* thisx, GlobalContext* globalCtx);
|
|
|
|
void EnSkj_SariasSongShortStumpUpdate(Actor* thisx, GlobalContext* globalCtx);
|
|
void EnSkj_OcarinaMinigameShortStumpUpdate(Actor* thisx, GlobalContext* globalCtx);
|
|
|
|
void func_80AFF2A0(EnSkj* this);
|
|
void func_80AFF334(EnSkj* this);
|
|
|
|
void EnSkj_CalculateCenter(EnSkj* this);
|
|
void EnSkj_OcarinaGameSetupWaitForPlayer(EnSkj* this);
|
|
void EnSkj_SetupResetFight(EnSkj* this);
|
|
void EnSkj_SetupLeaveOcarinaGame(EnSkj* this);
|
|
void EnSkj_SetupPlayOcarinaGame(EnSkj* this);
|
|
void EnSkj_Backflip(EnSkj* this);
|
|
void EnSkj_SetupNeedleRecover(EnSkj* this);
|
|
void EnSkj_SetupSpawnDeathEffect(EnSkj* this);
|
|
void EnSkj_SetupStand(EnSkj* this);
|
|
void EnSkj_SetupWaitForSong(EnSkj* this);
|
|
void EnSkj_SetupTalk(EnSkj* this);
|
|
void EnSkj_SetupMaskTrade(EnSkj* this);
|
|
void EnSkj_SetupWrongSong(EnSkj* this);
|
|
void EnSkj_SetupAfterSong(EnSkj* this);
|
|
void func_80AFFE24(EnSkj* this);
|
|
void EnSkj_SetupPostSariasSong(EnSkj* this);
|
|
void EnSkj_JumpFromStump(EnSkj* this);
|
|
void EnSkj_SetupWaitForLandAnimFinish(EnSkj* this);
|
|
void EnSkj_SetupWalkToPlayer(EnSkj* this);
|
|
void EnSkj_SetupWaitForMaskTextClear(EnSkj* this);
|
|
void EnSkj_SetupWaitForTextClear(EnSkj* this);
|
|
void EnSkj_SetupDie(EnSkj* this);
|
|
void func_80AFF1F0(EnSkj* this);
|
|
void EnSkj_OfferNextRound(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_SetupAskForMask(EnSkj* this, GlobalContext* globalCtx);
|
|
f32 EnSkj_GetItemXzRange(EnSkj* this);
|
|
s32 EnSkj_CollisionCheck(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_SetupTakeMask(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_TurnPlayer(EnSkj* this, Player* player);
|
|
|
|
void EnSkj_SetupWaitForOcarina(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_StartOcarinaMinigame(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WaitForOcarina(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WaitForPlayback(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_FailedMiniGame(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WonOcarinaMiniGame(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WaitToGiveReward(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_GiveOcarinaGameReward(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_FinishOcarinaGameRound(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WaitForNextRound(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WaitForOfferResponse(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_SetupWaitForOcarina(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_CleanupOcarinaGame(EnSkj* this, GlobalContext* globalCtx);
|
|
|
|
void EnSkj_Fade(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WaitToShootNeedle(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_SariasSongKidIdle(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WaitForDeathAnim(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_PickNextFightAction(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WaitForLandAnim(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_ResetFight(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_Fight(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_NeedleRecover(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_SpawnDeathEffect(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WaitInRange(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WaitForSong(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_AfterSong(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_SariaSongTalk(EnSkj* this, GlobalContext* globalCtx);
|
|
void func_80AFFE44(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_ChangeModeAfterSong(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_StartMaskTrade(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WaitForLanding(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WaitForLandAnimFinish(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WalkToPlayer(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_AskForMask(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_TakeMask(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WaitForMaskTextClear(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_WrongSong(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_SariasSongWaitForTextClear(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_OcarinaGameWaitForPlayer(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_OcarinaGameIdle(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_PlayOcarinaGame(EnSkj* this, GlobalContext* globalCtx);
|
|
void EnSkj_LeaveOcarinaGame(EnSkj* this, GlobalContext* globalCtx);
|
|
|
|
void EnSkj_SpawnBlood(GlobalContext* globalCtx, Vec3f* pos);
|
|
|
|
void EnSkj_SetupWaitInRange(EnSkj* this);
|
|
|
|
#define songFailTimer multiuseTimer
|
|
#define battleExitTimer multiuseTimer
|
|
|
|
typedef enum {
|
|
/* 0 */ SKJ_ANIM_BACKFLIP,
|
|
/* 1 */ SKJ_ANIM_SHOOT_NEEDLE,
|
|
/* 2 */ SKJ_ANIM_PLAY_FLUTE,
|
|
/* 3 */ SKJ_ANIM_DIE,
|
|
/* 4 */ SKJ_ANIM_HIT,
|
|
/* 5 */ SKJ_ANIM_LAND,
|
|
/* 6 */ SKJ_ANIM_LOOK_LEFT_RIGHT,
|
|
/* 7 */ SKJ_ANIM_FIGHTING_STANCE,
|
|
/* 8 */ SKJ_ANIM_WALK_TO_PLAYER,
|
|
/* 9 */ SKJ_ANIM_WAIT
|
|
} SkullKidAnim;
|
|
|
|
typedef enum {
|
|
/* 0 */ SKULL_KID_LEFT,
|
|
/* 1 */ SKULL_KID_RIGHT
|
|
} SkullKidStumpSide;
|
|
|
|
typedef enum {
|
|
/* 0 */ SKULL_KID_OCARINA_WAIT,
|
|
/* 1 */ SKULL_KID_OCARINA_PLAY_NOTES,
|
|
/* 2 */ SKULL_KID_OCARINA_LEAVE_GAME
|
|
} SkullKidOcarinaGameState;
|
|
|
|
typedef enum {
|
|
/* 00 */ SKJ_ACTION_FADE,
|
|
/* 01 */ SKJ_ACTION_WAIT_TO_SHOOT_NEEDLE,
|
|
/* 02 */ SKJ_ACTION_SARIA_SONG_IDLE,
|
|
/* 03 */ SKJ_ACTION_WAIT_FOR_DEATH_ANIM,
|
|
/* 04 */ SKJ_ACTION_PICK_NEXT_FIHGT_ACTION,
|
|
/* 05 */ SKJ_ACTION_WAIT_FOR_LAND_ANIM,
|
|
/* 06 */ SKJ_ACTION_RESET_FIGHT,
|
|
/* 07 */ SKJ_ACTION_FIGHT,
|
|
/* 08 */ SKJ_ACTION_NEEDLE_RECOVER,
|
|
/* 09 */ SKJ_ACTION_SPAWN_DEATH_EFFECT,
|
|
/* 10 */ SKJ_ACTION_SARIA_SONG_WAIT_IN_RANGE,
|
|
/* 11 */ SKJ_ACTION_SARIA_SONG_WAIT_FOR_SONG,
|
|
/* 12 */ SKJ_ACTION_SARIA_SONG_AFTER_SONG,
|
|
/* 13 */ SKJ_ACTION_SARIA_TALK,
|
|
/* 14 */ SKJ_ACTION_UNK14,
|
|
/* 15 */ SKJ_ACTION_SARIA_SONG_CHANGE_MODE,
|
|
/* 16 */ SKJ_ACTION_SARIA_SONG_START_TRADE,
|
|
/* 17 */ SKJ_ACTION_SARIA_SONG_WAIT_FOR_LANDING,
|
|
/* 18 */ SKJ_ACTION_SARIA_SONG_WAIT_FOR_LANDING_ANIM,
|
|
/* 19 */ SKJ_ACTION_SARIA_SONG_WALK_TO_PLAYER,
|
|
/* 20 */ SKJ_ACTION_SARIA_SONG_ASK_FOR_MASK,
|
|
/* 21 */ SKJ_ACTION_SARIA_SONG_TAKE_MASK,
|
|
/* 22 */ SKJ_ACTION_SARIA_SONG_WAIT_MASK_TEXT,
|
|
/* 23 */ SKJ_ACTION_SARIA_SONG_WRONG_SONG,
|
|
/* 24 */ SKJ_ACTION_SARIA_SONG_WAIT_FOR_TEXT,
|
|
/* 25 */ SKJ_ACTION_OCARINA_GAME_WAIT_FOR_PLAYER,
|
|
/* 26 */ SKJ_ACTION_OCARINA_GAME_IDLE,
|
|
/* 27 */ SKJ_ACTION_OCARINA_GAME_PLAY,
|
|
/* 28 */ SKJ_ACTION_OCARINA_GAME_LEAVE
|
|
} SkullKidAction;
|
|
|
|
typedef struct {
|
|
u8 unk0;
|
|
EnSkj* skullkid;
|
|
} EnSkjUnkStruct;
|
|
|
|
static EnSkjUnkStruct sSmallStumpSkullKid = { 0, NULL };
|
|
static EnSkjUnkStruct sOcarinaMinigameSkullKids[] = { { 0, NULL }, { 0, NULL } };
|
|
|
|
const ActorInit En_Skj_InitVars = {
|
|
ACTOR_EN_SKJ,
|
|
ACTORCAT_ENEMY,
|
|
FLAGS,
|
|
OBJECT_SKJ,
|
|
sizeof(EnSkj),
|
|
(ActorFunc)EnSkj_Init,
|
|
(ActorFunc)EnSkj_Destroy,
|
|
(ActorFunc)EnSkj_Update,
|
|
(ActorFunc)EnSkj_Draw,
|
|
NULL,
|
|
};
|
|
|
|
static ColliderCylinderInitType1 D_80B01678 = {
|
|
{
|
|
COLTYPE_NONE,
|
|
AT_ON | AT_TYPE_ENEMY,
|
|
AC_ON | AC_TYPE_PLAYER,
|
|
OC1_NONE,
|
|
COLSHAPE_CYLINDER,
|
|
},
|
|
{
|
|
ELEMTYPE_UNK0,
|
|
{ 0xFFCFFFFF, 0x0, 0x08 },
|
|
{ 0xFFCFFFFF, 0x0, 0x0 },
|
|
TOUCH_ON | TOUCH_SFX_NORMAL,
|
|
BUMP_ON,
|
|
OCELEM_ON,
|
|
},
|
|
{ 8, 48, 0, { 0, 0, 0 } },
|
|
};
|
|
|
|
static DamageTable sDamageTable = {
|
|
/* Deku nut */ DMG_ENTRY(0, 0x0),
|
|
/* Deku stick */ DMG_ENTRY(0, 0x0),
|
|
/* Slingshot */ DMG_ENTRY(0, 0x0),
|
|
/* Explosive */ DMG_ENTRY(0, 0x0),
|
|
/* Boomerang */ DMG_ENTRY(0, 0x0),
|
|
/* Normal arrow */ DMG_ENTRY(0, 0x0),
|
|
/* Hammer swing */ DMG_ENTRY(2, 0x0),
|
|
/* Hookshot */ DMG_ENTRY(0, 0x0),
|
|
/* Kokiri sword */ DMG_ENTRY(1, 0xF),
|
|
/* Master sword */ DMG_ENTRY(2, 0xF),
|
|
/* Giant's Knife */ DMG_ENTRY(4, 0xF),
|
|
/* Fire arrow */ DMG_ENTRY(0, 0x0),
|
|
/* Ice arrow */ DMG_ENTRY(0, 0x0),
|
|
/* Light arrow */ DMG_ENTRY(0, 0x0),
|
|
/* Unk arrow 1 */ DMG_ENTRY(0, 0x0),
|
|
/* Unk arrow 2 */ DMG_ENTRY(0, 0x0),
|
|
/* Unk arrow 3 */ DMG_ENTRY(0, 0x0),
|
|
/* Fire magic */ DMG_ENTRY(0, 0x0),
|
|
/* Ice magic */ DMG_ENTRY(0, 0x0),
|
|
/* Light magic */ DMG_ENTRY(0, 0x0),
|
|
/* Shield */ DMG_ENTRY(0, 0x0),
|
|
/* Mirror Ray */ DMG_ENTRY(0, 0x0),
|
|
/* Kokiri spin */ DMG_ENTRY(1, 0x0),
|
|
/* Giant spin */ DMG_ENTRY(4, 0x0),
|
|
/* Master spin */ DMG_ENTRY(2, 0x0),
|
|
/* Kokiri jump */ DMG_ENTRY(2, 0x0),
|
|
/* Giant jump */ DMG_ENTRY(8, 0x0),
|
|
/* Master jump */ DMG_ENTRY(4, 0x0),
|
|
/* Unknown 1 */ DMG_ENTRY(0, 0x0),
|
|
/* Unblockable */ DMG_ENTRY(0, 0x0),
|
|
/* Hammer jump */ DMG_ENTRY(0, 0x0),
|
|
/* Unknown 2 */ DMG_ENTRY(0, 0x0),
|
|
};
|
|
|
|
static s32 sOcarinaGameRewards[] = {
|
|
GI_RUPEE_GREEN,
|
|
GI_RUPEE_BLUE,
|
|
GI_HEART_PIECE,
|
|
GI_RUPEE_RED,
|
|
};
|
|
|
|
static AnimationMinimalInfo sAnimationInfo[] = {
|
|
{ &gSkullKidBackflipAnim, ANIMMODE_ONCE, 0.0f },
|
|
{ &gSkullKidShootNeedleAnim, ANIMMODE_ONCE, 0.0f },
|
|
{ &gSkullKidPlayFluteAnim, ANIMMODE_LOOP, 0.0f },
|
|
{ &gSkullKidDieAnim, ANIMMODE_ONCE, 0.0f },
|
|
{ &gSkullKidHitAnim, ANIMMODE_ONCE, 0.0f },
|
|
{ &gSkullKidLandAnim, ANIMMODE_ONCE, 0.0f },
|
|
{ &gSkullKidLookLeftAndRightAnim, ANIMMODE_LOOP, 0.0f },
|
|
{ &gSkullKidFightingStanceAnim, ANIMMODE_LOOP, 0.0f },
|
|
{ &gSkullKidWalkToPlayerAnim, ANIMMODE_LOOP, 0.0f },
|
|
{ &gSkullKidWaitAnim, ANIMMODE_LOOP, 0.0f },
|
|
};
|
|
|
|
static EnSkjActionFunc sActionFuncs[] = {
|
|
EnSkj_Fade,
|
|
EnSkj_WaitToShootNeedle,
|
|
EnSkj_SariasSongKidIdle,
|
|
EnSkj_WaitForDeathAnim,
|
|
EnSkj_PickNextFightAction,
|
|
EnSkj_WaitForLandAnim,
|
|
EnSkj_ResetFight,
|
|
EnSkj_Fight,
|
|
EnSkj_NeedleRecover,
|
|
EnSkj_SpawnDeathEffect,
|
|
EnSkj_WaitInRange,
|
|
EnSkj_WaitForSong,
|
|
EnSkj_AfterSong,
|
|
EnSkj_SariaSongTalk,
|
|
func_80AFFE44,
|
|
EnSkj_ChangeModeAfterSong,
|
|
EnSkj_StartMaskTrade,
|
|
EnSkj_WaitForLanding,
|
|
EnSkj_WaitForLandAnimFinish,
|
|
EnSkj_WalkToPlayer,
|
|
EnSkj_AskForMask,
|
|
EnSkj_TakeMask,
|
|
EnSkj_WaitForMaskTextClear,
|
|
EnSkj_WrongSong,
|
|
EnSkj_SariasSongWaitForTextClear,
|
|
EnSkj_OcarinaGameWaitForPlayer,
|
|
EnSkj_OcarinaGameIdle,
|
|
EnSkj_PlayOcarinaGame,
|
|
EnSkj_LeaveOcarinaGame,
|
|
};
|
|
|
|
static InitChainEntry sInitChain[] = {
|
|
ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE),
|
|
ICHAIN_F32(targetArrowOffset, 30, ICHAIN_STOP),
|
|
};
|
|
|
|
static s32 D_80B01EA0; // gets set if ACTOR_FLAG_8 is set
|
|
|
|
void EnSkj_ChangeAnim(EnSkj* this, u8 index) {
|
|
f32 endFrame = Animation_GetLastFrame(sAnimationInfo[index].animation);
|
|
|
|
this->animIndex = index;
|
|
Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, 1.0f, 0.0f, endFrame,
|
|
sAnimationInfo[index].mode, sAnimationInfo[index].morphFrames);
|
|
}
|
|
|
|
void EnSkj_SetupAction(EnSkj* this, u8 action) {
|
|
this->action = action;
|
|
this->actionFunc = sActionFuncs[action];
|
|
|
|
switch (action) {
|
|
case SKJ_ACTION_FADE:
|
|
case SKJ_ACTION_WAIT_FOR_DEATH_ANIM:
|
|
case SKJ_ACTION_PICK_NEXT_FIHGT_ACTION:
|
|
case SKJ_ACTION_SPAWN_DEATH_EFFECT:
|
|
case SKJ_ACTION_SARIA_SONG_START_TRADE:
|
|
case SKJ_ACTION_SARIA_SONG_WAIT_FOR_LANDING:
|
|
case SKJ_ACTION_SARIA_SONG_WAIT_FOR_LANDING_ANIM:
|
|
case SKJ_ACTION_SARIA_SONG_WALK_TO_PLAYER:
|
|
case SKJ_ACTION_SARIA_SONG_ASK_FOR_MASK:
|
|
case SKJ_ACTION_SARIA_SONG_TAKE_MASK:
|
|
case SKJ_ACTION_SARIA_SONG_WAIT_MASK_TEXT:
|
|
case SKJ_ACTION_SARIA_SONG_WRONG_SONG:
|
|
case SKJ_ACTION_SARIA_SONG_WAIT_FOR_TEXT:
|
|
case SKJ_ACTION_OCARINA_GAME_WAIT_FOR_PLAYER:
|
|
case SKJ_ACTION_OCARINA_GAME_IDLE:
|
|
case SKJ_ACTION_OCARINA_GAME_PLAY:
|
|
case SKJ_ACTION_OCARINA_GAME_LEAVE:
|
|
this->unk_2D3 = 0;
|
|
break;
|
|
default:
|
|
this->unk_2D3 = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void EnSkj_CalculateCenter(EnSkj* this) {
|
|
Vec3f mult;
|
|
|
|
mult.x = 0.0f;
|
|
mult.y = 0.0f;
|
|
mult.z = 120.0f;
|
|
|
|
Matrix_RotateY((this->actor.shape.rot.y / 32768.0f) * M_PI, MTXMODE_NEW);
|
|
Matrix_MultVec3f(&mult, &this->center);
|
|
|
|
this->center.x += this->actor.world.pos.x;
|
|
this->center.z += this->actor.world.pos.z;
|
|
}
|
|
|
|
void EnSkj_SetNaviId(EnSkj* this) {
|
|
switch (this->actor.params) {
|
|
case 0:
|
|
if (gSaveContext.itemGetInf[3] & 0x200) {
|
|
this->actor.naviEnemyId = 0x41; // Skull kid with skull mask
|
|
} else if (gSaveContext.itemGetInf[1] & 0x40) {
|
|
this->actor.naviEnemyId = 0x40; // Skull kid after Saria's song but no mask
|
|
} else {
|
|
this->actor.naviEnemyId = 0x3F; // No Sarias song no skull mask
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
case 2:
|
|
this->actor.naviEnemyId = 0x3F;
|
|
break;
|
|
|
|
default:
|
|
this->actor.naviEnemyId = 0x36; // Skull kid as adult
|
|
break;
|
|
}
|
|
}
|
|
|
|
void EnSkj_Init(Actor* thisx, GlobalContext* globalCtx2) {
|
|
s16 type = (thisx->params >> 0xA) & 0x3F;
|
|
EnSkj* this = (EnSkj*)thisx;
|
|
GlobalContext* globalCtx = globalCtx2;
|
|
s32 pad;
|
|
Player* player;
|
|
|
|
Actor_ProcessInitChain(thisx, sInitChain);
|
|
switch (type) {
|
|
case 5: // Invisible on the small stump (sarias song))
|
|
sSmallStumpSkullKid.unk0 = 1;
|
|
sSmallStumpSkullKid.skullkid = (EnSkj*)thisx;
|
|
this->actor.destroy = NULL;
|
|
this->actor.draw = NULL;
|
|
this->actor.update = EnSkj_SariasSongShortStumpUpdate;
|
|
this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2);
|
|
this->actor.flags |= 0;
|
|
Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_PROP);
|
|
break;
|
|
|
|
case 6: // Invisible on the short stump (ocarina game)
|
|
sSmallStumpSkullKid.unk0 = 1;
|
|
sSmallStumpSkullKid.skullkid = (EnSkj*)thisx;
|
|
this->actor.destroy = NULL;
|
|
this->actor.draw = NULL;
|
|
this->actor.update = EnSkj_OcarinaMinigameShortStumpUpdate;
|
|
this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2);
|
|
this->actor.flags |= 0;
|
|
Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_PROP);
|
|
this->actor.focus.pos.x = 1230.0f;
|
|
this->actor.focus.pos.y = -90.0f;
|
|
this->actor.focus.pos.z = 450.0f;
|
|
this->actionFunc = EnSkj_SetupWaitForOcarina;
|
|
break;
|
|
|
|
default:
|
|
this->actor.params = type;
|
|
if (((this->actor.params != 0) && (this->actor.params != 1)) && (this->actor.params != 2)) {
|
|
if (INV_CONTENT(ITEM_TRADE_ADULT) < ITEM_SAW) {
|
|
Actor_Kill(&this->actor);
|
|
return;
|
|
}
|
|
}
|
|
|
|
EnSkj_SetNaviId(this);
|
|
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gSkullKidSkel, &gSkullKidPlayFluteAnim, this->jointTable,
|
|
this->morphTable, 19);
|
|
if ((type >= 0) && (type < 3)) {
|
|
this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2);
|
|
this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3;
|
|
Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_NPC);
|
|
}
|
|
|
|
if ((type < 0) || (type >= 7)) {
|
|
this->actor.flags &= ~ACTOR_FLAG_25;
|
|
}
|
|
|
|
if ((type > 0) && (type < 3)) {
|
|
this->actor.targetMode = 7;
|
|
this->posCopy = this->actor.world.pos;
|
|
sOcarinaMinigameSkullKids[type - 1].unk0 = 1;
|
|
sOcarinaMinigameSkullKids[type - 1].skullkid = this;
|
|
this->minigameState = 0;
|
|
this->alpha = 0;
|
|
EnSkj_OcarinaGameSetupWaitForPlayer(this);
|
|
} else {
|
|
this->alpha = 255;
|
|
EnSkj_SetupResetFight(this);
|
|
}
|
|
|
|
this->actor.colChkInfo.damageTable = &sDamageTable;
|
|
this->actor.colChkInfo.health = 10;
|
|
Collider_InitCylinder(globalCtx, &this->collider);
|
|
Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &D_80B01678);
|
|
ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 40.0f);
|
|
Actor_SetScale(thisx, 0.01f);
|
|
this->actor.textId = this->textId = 0;
|
|
this->multiuseTimer = 0;
|
|
this->backflipFlag = 0;
|
|
this->needlesToShoot = 3;
|
|
this->hitsUntilDodge = 3;
|
|
this->actor.speedXZ = 0.0f;
|
|
this->actor.velocity.y = 0.0f;
|
|
this->actor.gravity = -1.0f;
|
|
EnSkj_CalculateCenter(this);
|
|
|
|
player = GET_PLAYER(globalCtx);
|
|
osSyncPrintf("Player_X : %f\n", player->actor.world.pos.x);
|
|
osSyncPrintf("Player_Z : %f\n", player->actor.world.pos.z);
|
|
osSyncPrintf("World_X : %f\n", this->actor.world.pos.x);
|
|
osSyncPrintf("World_Z : %f\n", this->actor.world.pos.z);
|
|
osSyncPrintf("Center_X : %f\n", this->center.x);
|
|
osSyncPrintf("Center_Z : %f\n\n", this->center.z);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
void EnSkj_Destroy(Actor* thisx, GlobalContext* globalCtx) {
|
|
s32 pad;
|
|
EnSkj* this = (EnSkj*)thisx;
|
|
|
|
Collider_DestroyCylinder(globalCtx, &this->collider);
|
|
}
|
|
|
|
s32 EnSkj_RangeCheck(Player* player, EnSkj* this) {
|
|
f32 xDiff = player->actor.world.pos.x - this->actor.world.pos.x;
|
|
f32 zDiff = player->actor.world.pos.z - this->actor.world.pos.z;
|
|
f32 yDiff = player->actor.world.pos.y - this->actor.world.pos.y;
|
|
|
|
return (SQ(xDiff) + SQ(zDiff) <= 676.0f) && (yDiff >= 0.0f);
|
|
}
|
|
|
|
f32 EnSkj_GetItemXzRange(EnSkj* this) {
|
|
EnSkj* temp_v0;
|
|
f32 zDiff;
|
|
f32 xDiff;
|
|
|
|
temp_v0 = sSmallStumpSkullKid.skullkid;
|
|
xDiff = temp_v0->actor.world.pos.x - this->actor.world.pos.x;
|
|
zDiff = temp_v0->actor.world.pos.z - this->actor.world.pos.z;
|
|
return sqrtf(SQ(xDiff) + SQ(zDiff)) + 26.0f;
|
|
}
|
|
|
|
f32 EnSkj_GetItemYRange(EnSkj* this) {
|
|
return fabsf(sSmallStumpSkullKid.skullkid->actor.world.pos.y - this->actor.world.pos.y) + 10.0f;
|
|
}
|
|
|
|
s32 EnSkj_ShootNeedle(EnSkj* this, GlobalContext* globalCtx) {
|
|
s32 pad;
|
|
Vec3f pos;
|
|
Vec3f pos2;
|
|
EnSkjneedle* needle;
|
|
|
|
pos.x = 1.5f;
|
|
pos.y = 0.0f;
|
|
pos.z = 40.0f;
|
|
|
|
Matrix_RotateY((this->actor.shape.rot.y / 32768.0f) * M_PI, MTXMODE_NEW);
|
|
Matrix_MultVec3f(&pos, &pos2);
|
|
|
|
pos2.x += this->actor.world.pos.x;
|
|
pos2.z += this->actor.world.pos.z;
|
|
pos2.y = this->actor.world.pos.y + 27.0f;
|
|
|
|
needle = (EnSkjneedle*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_SKJNEEDLE, pos2.x, pos2.y, pos2.z,
|
|
this->actor.shape.rot.x, this->actor.shape.rot.y, this->actor.shape.rot.z, 0);
|
|
if (needle != NULL) {
|
|
needle->killTimer = 100;
|
|
needle->actor.speedXZ = 24.0f;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void EnSkj_SpawnBlood(GlobalContext* globalCtx, Vec3f* pos) {
|
|
EffectSparkInit effect;
|
|
s32 sp20;
|
|
|
|
effect.position.x = pos->x;
|
|
effect.position.y = pos->y;
|
|
effect.position.z = pos->z;
|
|
effect.uDiv = 5;
|
|
effect.vDiv = 5;
|
|
|
|
effect.colorStart[0].r = 0;
|
|
effect.colorStart[0].g = 0;
|
|
effect.colorStart[0].b = 128;
|
|
effect.colorStart[0].a = 255;
|
|
|
|
effect.colorStart[1].r = 0;
|
|
effect.colorStart[1].g = 0;
|
|
effect.colorStart[1].b = 128;
|
|
effect.colorStart[1].a = 255;
|
|
|
|
effect.colorStart[2].r = 0;
|
|
effect.colorStart[2].g = 0;
|
|
effect.colorStart[2].b = 128;
|
|
effect.colorStart[2].a = 255;
|
|
|
|
effect.colorStart[3].r = 0;
|
|
effect.colorStart[3].g = 0;
|
|
effect.colorStart[3].b = 128;
|
|
effect.colorStart[3].a = 255;
|
|
|
|
effect.colorEnd[0].r = 0;
|
|
effect.colorEnd[0].g = 0;
|
|
effect.colorEnd[0].b = 32;
|
|
effect.colorEnd[0].a = 0;
|
|
|
|
effect.colorEnd[1].r = 0;
|
|
effect.colorEnd[1].g = 0;
|
|
effect.colorEnd[1].b = 32;
|
|
effect.colorEnd[1].a = 0;
|
|
|
|
effect.colorEnd[2].r = 0;
|
|
effect.colorEnd[2].g = 0;
|
|
effect.colorEnd[2].b = 64;
|
|
effect.colorEnd[2].a = 0;
|
|
|
|
effect.colorEnd[3].r = 0;
|
|
effect.colorEnd[3].g = 0;
|
|
effect.colorEnd[3].b = 64;
|
|
effect.colorEnd[3].a = 0;
|
|
|
|
effect.speed = 8.0f;
|
|
effect.gravity = -1.0f;
|
|
|
|
effect.timer = 0;
|
|
effect.duration = 8;
|
|
|
|
Effect_Add(globalCtx, &sp20, EFFECT_SPARK, 0, 1, &effect);
|
|
}
|
|
|
|
s32 EnSkj_CollisionCheck(EnSkj* this, GlobalContext* globalCtx) {
|
|
s16 yawDiff;
|
|
Vec3f effectPos;
|
|
|
|
if (!((this->unk_2D3 == 0) || (D_80B01EA0 != 0) || !(this->collider.base.acFlags & AC_HIT))) {
|
|
this->collider.base.acFlags &= ~AC_HIT;
|
|
if (this->actor.colChkInfo.damageEffect != 0) {
|
|
if (this->actor.colChkInfo.damageEffect == 0xF) {
|
|
effectPos.x = this->collider.info.bumper.hitPos.x;
|
|
effectPos.y = this->collider.info.bumper.hitPos.y;
|
|
effectPos.z = this->collider.info.bumper.hitPos.z;
|
|
|
|
EnSkj_SpawnBlood(globalCtx, &effectPos);
|
|
EffectSsHitMark_SpawnFixedScale(globalCtx, 1, &effectPos);
|
|
|
|
yawDiff = this->actor.yawTowardsPlayer - this->actor.world.rot.y;
|
|
if ((this->action == 2) || (this->action == 6)) {
|
|
if ((yawDiff > 0x6000) || (yawDiff < -0x6000)) {
|
|
Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8);
|
|
EnSkj_SetupDie(this);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
Actor_ApplyDamage(&this->actor);
|
|
Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8);
|
|
|
|
if (this->actor.colChkInfo.health != 0) {
|
|
if (this->hitsUntilDodge != 0) {
|
|
this->hitsUntilDodge--;
|
|
}
|
|
if (this->dodgeResetTimer == 0) {
|
|
this->dodgeResetTimer = 60;
|
|
}
|
|
func_80AFF1F0(this);
|
|
return 1;
|
|
}
|
|
EnSkj_SetupDie(this);
|
|
return 1;
|
|
}
|
|
} else {
|
|
this->backflipFlag = 1;
|
|
EnSkj_Backflip(this);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
s32 func_80AFEDF8(EnSkj* this, GlobalContext* globalCtx) {
|
|
s16 yawDiff;
|
|
|
|
if (this->actor.xzDistToPlayer < this->unk_2EC) {
|
|
this = this;
|
|
if (func_8002DDE4(globalCtx) != 0) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
yawDiff = this->actor.yawTowardsPlayer - this->actor.world.rot.y;
|
|
|
|
if ((yawDiff < this->unk_2C8) && (-this->unk_2C8 < yawDiff)) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void EnSkj_Backflip(EnSkj* this) {
|
|
this->actor.velocity.y = 8.0f;
|
|
this->actor.speedXZ = -8.0f;
|
|
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_BACKFLIP);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_FADE);
|
|
}
|
|
|
|
void EnSkj_Fade(EnSkj* this, GlobalContext* globalCtx) {
|
|
u32 alpha = this->alpha;
|
|
|
|
if (this->unk_2D6 == 2) {
|
|
globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_00;
|
|
this->unk_2D6 = 0;
|
|
}
|
|
|
|
alpha -= 20;
|
|
|
|
if (this->backflipFlag != 0) {
|
|
if (alpha > 255) {
|
|
alpha = 0;
|
|
}
|
|
|
|
this->alpha = alpha;
|
|
this->actor.shape.shadowAlpha = alpha;
|
|
}
|
|
|
|
if (this->actor.velocity.y <= 0.0f) {
|
|
if (this->actor.bgCheckFlags & 2) {
|
|
this->actor.bgCheckFlags &= ~2;
|
|
func_80AFF2A0(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupWaitToShootNeedle(EnSkj* this) {
|
|
this->needlesToShoot = 3;
|
|
this->needleShootTimer = 0;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_SHOOT_NEEDLE);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_WAIT_TO_SHOOT_NEEDLE);
|
|
}
|
|
|
|
void EnSkj_WaitToShootNeedle(EnSkj* this, GlobalContext* globalCtx) {
|
|
u8 val;
|
|
s16 lastFrame = Animation_GetLastFrame(&gSkullKidShootNeedleAnim);
|
|
|
|
if ((this->skelAnime.curFrame == lastFrame) && (this->needleShootTimer == 0)) {
|
|
val = this->needlesToShoot;
|
|
if (this->needlesToShoot != 0) {
|
|
EnSkj_ShootNeedle(this, globalCtx);
|
|
this->needleShootTimer = 4;
|
|
val--;
|
|
this->needlesToShoot = val;
|
|
|
|
} else {
|
|
EnSkj_SetupNeedleRecover(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupResetFight(EnSkj* this) {
|
|
this->unk_2C8 = 0xAAA;
|
|
this->unk_2EC = 200.0f;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_PLAY_FLUTE);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_IDLE);
|
|
}
|
|
|
|
void EnSkj_SariasSongKidIdle(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (this->actor.params == 0) {
|
|
if (!(gSaveContext.itemGetInf[1] & 0x40) && (this->actor.xzDistToPlayer < 200.0f)) {
|
|
this->backflipFlag = 1;
|
|
EnSkj_Backflip(this);
|
|
} else if (sSmallStumpSkullKid.unk0 != 0) {
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
if (EnSkj_RangeCheck(player, sSmallStumpSkullKid.skullkid)) {
|
|
EnSkj_SetupWaitInRange(this);
|
|
player->stateFlags2 |= 0x800000;
|
|
player->unk_6A8 = &sSmallStumpSkullKid.skullkid->actor;
|
|
}
|
|
}
|
|
} else {
|
|
if (func_80AFEDF8(this, globalCtx) != 0) {
|
|
func_80AFF334(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupDie(EnSkj* this) {
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_DIE);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_WAIT_FOR_DEATH_ANIM);
|
|
}
|
|
|
|
void EnSkj_WaitForDeathAnim(EnSkj* this, GlobalContext* globalCtx) {
|
|
s16 lastFrame = Animation_GetLastFrame(&gSkullKidDieAnim);
|
|
|
|
if (this->skelAnime.curFrame == lastFrame) {
|
|
EnSkj_SetupSpawnDeathEffect(this);
|
|
}
|
|
}
|
|
|
|
void func_80AFF1F0(EnSkj* this) {
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_HIT);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_PICK_NEXT_FIHGT_ACTION);
|
|
}
|
|
|
|
void EnSkj_PickNextFightAction(EnSkj* this, GlobalContext* globalCtx) {
|
|
s16 lastFrame = Animation_GetLastFrame(&gSkullKidHitAnim);
|
|
|
|
if (this->skelAnime.curFrame == lastFrame) {
|
|
if (this->hitsUntilDodge == 0) {
|
|
this->hitsUntilDodge = 3;
|
|
EnSkj_Backflip(this);
|
|
} else {
|
|
EnSkj_SetupStand(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void func_80AFF2A0(EnSkj* this) {
|
|
EnSkj_CalculateCenter(this);
|
|
this->actor.speedXZ = 0.0f;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_LAND);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_WAIT_FOR_LAND_ANIM);
|
|
}
|
|
|
|
void EnSkj_WaitForLandAnim(EnSkj* this, GlobalContext* globalCtx) {
|
|
s16 lastFrame = Animation_GetLastFrame(&gSkullKidLandAnim);
|
|
|
|
if (this->skelAnime.curFrame == lastFrame) {
|
|
EnSkj_SetupStand(this);
|
|
}
|
|
}
|
|
|
|
void func_80AFF334(EnSkj* this) {
|
|
this->unk_2C8 = 0x2000;
|
|
this->battleExitTimer = 400;
|
|
this->unk_2EC = 600.0f;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_LOOK_LEFT_RIGHT);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_RESET_FIGHT);
|
|
}
|
|
|
|
void EnSkj_ResetFight(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (this->battleExitTimer == 0) {
|
|
EnSkj_SetupResetFight(this);
|
|
} else if (func_80AFEDF8(this, globalCtx) != 0) {
|
|
this->battleExitTimer = 600;
|
|
EnSkj_SetupStand(this);
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupStand(EnSkj* this) {
|
|
this->needleShootTimer = 60;
|
|
this->unk_2C8 = 0x2000;
|
|
this->unk_2F0 = 0.0f;
|
|
this->unk_2EC = 600.0f;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_FIGHTING_STANCE);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_FIGHT);
|
|
}
|
|
|
|
void EnSkj_Fight(EnSkj* this, GlobalContext* globalCtx) {
|
|
Vec3f pos1;
|
|
Vec3f pos2;
|
|
s32 pad[3];
|
|
f32 prevPosX;
|
|
f32 prevPosZ;
|
|
f32 phi_f14;
|
|
s16 yawDistToPlayer;
|
|
|
|
if (this->needleShootTimer == 0) {
|
|
EnSkj_SetupWaitToShootNeedle(this);
|
|
} else if (this->battleExitTimer != 0) {
|
|
yawDistToPlayer =
|
|
Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, this->unk_2F0, 0);
|
|
this->actor.world.rot.y = this->actor.shape.rot.y;
|
|
Math_ApproachF(&this->unk_2F0, 2000.0f, 1.0f, 200.0f);
|
|
|
|
pos1.x = 0.0f;
|
|
pos1.y = 0.0f;
|
|
pos1.z = -120.0f;
|
|
|
|
Matrix_RotateY((this->actor.shape.rot.y / 32768.0f) * M_PI, MTXMODE_NEW);
|
|
Matrix_MultVec3f(&pos1, &pos2);
|
|
prevPosX = this->actor.world.pos.x;
|
|
prevPosZ = this->actor.world.pos.z;
|
|
if (1) {}
|
|
this->actor.world.pos.x = this->center.x + pos2.x;
|
|
this->actor.world.pos.z = this->center.z + pos2.z;
|
|
|
|
phi_f14 = sqrtf(SQ(this->actor.world.pos.x - prevPosX) + SQ(this->actor.world.pos.z - prevPosZ));
|
|
phi_f14 = CLAMP_MAX(phi_f14, 10.0f);
|
|
phi_f14 /= 10.0f;
|
|
|
|
this->skelAnime.playSpeed = (yawDistToPlayer < 0) ? -(1.0f + phi_f14) : (1.0f + phi_f14);
|
|
|
|
} else if (func_80AFEDF8(this, globalCtx) != 0) {
|
|
this->backflipFlag = 1;
|
|
EnSkj_Backflip(this);
|
|
} else {
|
|
EnSkj_SetupResetFight(this);
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupNeedleRecover(EnSkj* this) {
|
|
Animation_Reverse(&this->skelAnime);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_NEEDLE_RECOVER);
|
|
}
|
|
|
|
void EnSkj_NeedleRecover(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (this->skelAnime.curFrame == 0.0f) {
|
|
EnSkj_SetupStand(this);
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupSpawnDeathEffect(EnSkj* this) {
|
|
this->backflipFlag = 1;
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SPAWN_DEATH_EFFECT);
|
|
}
|
|
|
|
void EnSkj_SpawnDeathEffect(EnSkj* this, GlobalContext* globalCtx) {
|
|
Vec3f effectPos;
|
|
Vec3f effectVel;
|
|
Vec3f effectAccel;
|
|
u32 phi_v0;
|
|
|
|
phi_v0 = this->alpha - 4;
|
|
|
|
if (phi_v0 > 255) {
|
|
phi_v0 = 0;
|
|
}
|
|
this->alpha = phi_v0;
|
|
this->actor.shape.shadowAlpha = phi_v0;
|
|
|
|
effectPos.x = Rand_CenteredFloat(30.0f) + this->actor.world.pos.x;
|
|
effectPos.y = Rand_CenteredFloat(30.0f) + this->actor.world.pos.y;
|
|
effectPos.z = Rand_CenteredFloat(30.0f) + this->actor.world.pos.z;
|
|
|
|
effectAccel.z = 0.0f;
|
|
effectAccel.y = 0.0f;
|
|
effectAccel.x = 0.0f;
|
|
|
|
effectVel.z = 0.0f;
|
|
effectVel.y = 0.0f;
|
|
effectVel.x = 0.0f;
|
|
|
|
EffectSsDeadDb_Spawn(globalCtx, &effectPos, &effectVel, &effectAccel, 100, 10, 255, 255, 255, 255, 0, 0, 255, 1, 9,
|
|
1);
|
|
}
|
|
|
|
void EnSkj_SetupWaitInRange(EnSkj* this) {
|
|
this->textId = 0x10BC;
|
|
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_IN_RANGE);
|
|
}
|
|
|
|
void EnSkj_WaitInRange(EnSkj* this, GlobalContext* globalCtx) {
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
|
|
// When link pulls out the Ocarina center him on the stump
|
|
// Link was probably supposed to be pointed towards skull kid as well
|
|
if (player->stateFlags2 & 0x1000000) {
|
|
player->stateFlags2 |= 0x2000000;
|
|
player->unk_6A8 = &sSmallStumpSkullKid.skullkid->actor;
|
|
player->actor.world.pos.x = sSmallStumpSkullKid.skullkid->actor.world.pos.x;
|
|
player->actor.world.pos.y = sSmallStumpSkullKid.skullkid->actor.world.pos.y;
|
|
player->actor.world.pos.z = sSmallStumpSkullKid.skullkid->actor.world.pos.z;
|
|
EnSkj_TurnPlayer(sSmallStumpSkullKid.skullkid, player);
|
|
func_8010BD88(globalCtx, OCARINA_ACTION_CHECK_SARIA);
|
|
EnSkj_SetupWaitForSong(this);
|
|
} else if (D_80B01EA0 != 0) {
|
|
player->actor.world.pos.x = sSmallStumpSkullKid.skullkid->actor.world.pos.x;
|
|
player->actor.world.pos.y = sSmallStumpSkullKid.skullkid->actor.world.pos.y;
|
|
player->actor.world.pos.z = sSmallStumpSkullKid.skullkid->actor.world.pos.z;
|
|
if ((Player_GetMask(globalCtx) == PLAYER_MASK_SKULL) && !(gSaveContext.itemGetInf[3] & 0x200)) {
|
|
func_80078884(NA_SE_SY_TRE_BOX_APPEAR);
|
|
EnSkj_SetupMaskTrade(this);
|
|
} else {
|
|
EnSkj_SetupTalk(this);
|
|
}
|
|
} else if (!EnSkj_RangeCheck(player, sSmallStumpSkullKid.skullkid)) {
|
|
EnSkj_SetupResetFight(this);
|
|
} else {
|
|
player->stateFlags2 |= 0x800000;
|
|
if (gSaveContext.itemGetInf[1] & 0x40) {
|
|
if (gSaveContext.itemGetInf[3] & 0x200) {
|
|
this->textId = Text_GetFaceReaction(globalCtx, 0x15);
|
|
if (this->textId == 0) {
|
|
this->textId = 0x1020;
|
|
}
|
|
} else if (Player_GetMask(globalCtx) == PLAYER_MASK_NONE) {
|
|
this->textId = 0x10BC;
|
|
} else if (Player_GetMask(globalCtx) == PLAYER_MASK_SKULL) {
|
|
this->textId = 0x101B;
|
|
} else {
|
|
this->textId = Text_GetFaceReaction(globalCtx, 0x15);
|
|
}
|
|
func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this));
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupWaitForSong(EnSkj* this) {
|
|
this->unk_2D6 = 0;
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_FOR_SONG);
|
|
}
|
|
|
|
void EnSkj_WaitForSong(EnSkj* this, GlobalContext* globalCtx) {
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
|
|
// Played a song thats not Saria's song
|
|
if (!(gSaveContext.itemGetInf[1] & 0x40) && ((globalCtx->msgCtx.msgMode == MSGMODE_OCARINA_FAIL) ||
|
|
(globalCtx->msgCtx.msgMode == MSGMODE_OCARINA_FAIL_NO_TEXT))) {
|
|
globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04;
|
|
Message_CloseTextbox(globalCtx);
|
|
player->unk_6A8 = &this->actor;
|
|
func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this));
|
|
EnSkj_SetupWrongSong(this);
|
|
} else {
|
|
if ((globalCtx->msgCtx.msgMode == MSGMODE_OCARINA_CORRECT_PLAYBACK) && (this->unk_2D6 == 0)) {
|
|
this->unk_2D6 = 1;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_PLAY_FLUTE);
|
|
} else if ((this->unk_2D6 != 0) && (globalCtx->msgCtx.msgMode == MSGMODE_SONG_DEMONSTRATION_DONE)) {
|
|
this->unk_2D6 = 0;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT);
|
|
}
|
|
if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04) {
|
|
globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_00;
|
|
this->unk_2D6 = 0;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_IN_RANGE);
|
|
} else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_03) {
|
|
if (!(gSaveContext.itemGetInf[1] & 0x40)) {
|
|
// Saria's song has been played for the first titme
|
|
globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04;
|
|
func_80078884(NA_SE_SY_CORRECT_CHIME);
|
|
player->unk_6A8 = &this->actor;
|
|
func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this));
|
|
this->textId = 0x10BB;
|
|
EnSkj_SetupAfterSong(this);
|
|
} else {
|
|
globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_05;
|
|
}
|
|
} else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_02) {
|
|
player->stateFlags2 &= ~0x1000000;
|
|
Actor_Kill(&this->actor);
|
|
} else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_01) {
|
|
player->stateFlags2 |= 0x800000;
|
|
} else {
|
|
if (globalCtx->msgCtx.ocarinaMode >= OCARINA_MODE_05) {
|
|
gSaveContext.sunsSongState = 0;
|
|
if (gSaveContext.itemGetInf[1] & 0x40) {
|
|
globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04;
|
|
player->unk_6A8 = &this->actor;
|
|
func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this));
|
|
this->textId = 0x10BD;
|
|
EnSkj_SetupAfterSong(this);
|
|
} else {
|
|
globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04;
|
|
player->unk_6A8 = &this->actor;
|
|
func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this));
|
|
EnSkj_SetupWrongSong(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupAfterSong(EnSkj* this) {
|
|
this->unk_2D6 = 0;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_AFTER_SONG);
|
|
}
|
|
|
|
void EnSkj_AfterSong(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (D_80B01EA0 != 0) {
|
|
EnSkj_SetupTalk(this);
|
|
} else {
|
|
func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this));
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupTalk(EnSkj* this) {
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_TALK);
|
|
}
|
|
|
|
void EnSkj_SariaSongTalk(EnSkj* this, GlobalContext* globalCtx) {
|
|
s32 pad;
|
|
|
|
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) {
|
|
if (gSaveContext.itemGetInf[1] & 0x40) {
|
|
EnSkj_SetupWaitInRange(this);
|
|
} else {
|
|
func_80AFFE24(this);
|
|
func_8002F434(&this->actor, globalCtx, GI_HEART_PIECE, EnSkj_GetItemXzRange(this),
|
|
EnSkj_GetItemYRange(this));
|
|
}
|
|
}
|
|
}
|
|
|
|
void func_80AFFE24(EnSkj* this) {
|
|
EnSkj_SetupAction(this, SKJ_ACTION_UNK14);
|
|
}
|
|
|
|
void func_80AFFE44(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (Actor_HasParent(&this->actor, globalCtx)) {
|
|
this->actor.parent = NULL;
|
|
EnSkj_SetupPostSariasSong(this);
|
|
} else {
|
|
func_8002F434(&this->actor, globalCtx, GI_HEART_PIECE, EnSkj_GetItemXzRange(this), EnSkj_GetItemYRange(this));
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupPostSariasSong(EnSkj* this) {
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_CHANGE_MODE);
|
|
}
|
|
|
|
void EnSkj_ChangeModeAfterSong(EnSkj* this, GlobalContext* globalCtx) {
|
|
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) {
|
|
gSaveContext.itemGetInf[1] |= 0x40;
|
|
EnSkj_SetNaviId(this);
|
|
EnSkj_SetupWaitInRange(this);
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupMaskTrade(EnSkj* this) {
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_START_TRADE);
|
|
}
|
|
|
|
void EnSkj_StartMaskTrade(EnSkj* this, GlobalContext* globalCtx) {
|
|
u8 sp1F = Message_GetState(&globalCtx->msgCtx);
|
|
|
|
func_8002DF54(globalCtx, &this->actor, 1);
|
|
if ((sp1F == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) {
|
|
EnSkj_JumpFromStump(this);
|
|
}
|
|
}
|
|
|
|
void EnSkj_JumpFromStump(EnSkj* this) {
|
|
this->actor.velocity.y = 8.0f;
|
|
this->actor.speedXZ = 2.0f;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_BACKFLIP);
|
|
Animation_Reverse(&this->skelAnime);
|
|
this->skelAnime.curFrame = this->skelAnime.startFrame;
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_FOR_LANDING);
|
|
}
|
|
|
|
void EnSkj_WaitForLanding(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (this->actor.velocity.y <= 0.0f) {
|
|
if (this->actor.bgCheckFlags & 2) {
|
|
this->actor.bgCheckFlags &= ~2;
|
|
this->actor.speedXZ = 0.0f;
|
|
EnSkj_SetupWaitForLandAnimFinish(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupWaitForLandAnimFinish(EnSkj* this) {
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_LAND);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_FOR_LANDING_ANIM);
|
|
}
|
|
|
|
void EnSkj_WaitForLandAnimFinish(EnSkj* this, GlobalContext* globalCtx) {
|
|
s16 lastFrame = Animation_GetLastFrame(&gSkullKidLandAnim);
|
|
|
|
if (this->skelAnime.curFrame == lastFrame) {
|
|
EnSkj_SetupWalkToPlayer(this);
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupWalkToPlayer(EnSkj* this) {
|
|
this->unk_2F0 = 0.0f;
|
|
this->actor.speedXZ = 2.0f;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_WALK_TO_PLAYER);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WALK_TO_PLAYER);
|
|
}
|
|
|
|
void EnSkj_WalkToPlayer(EnSkj* this, GlobalContext* globalCtx) {
|
|
Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0xA, this->unk_2F0, 0);
|
|
Math_ApproachF(&this->unk_2F0, 2000.0f, 1.0f, 100.0f);
|
|
this->actor.world.rot.y = this->actor.shape.rot.y;
|
|
if (this->actor.xzDistToPlayer < 120.0f) {
|
|
this->actor.speedXZ = 0.0f;
|
|
EnSkj_SetupAskForMask(this, globalCtx);
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupAskForMask(EnSkj* this, GlobalContext* globalCtx) {
|
|
Message_StartTextbox(globalCtx, 0x101C, &this->actor);
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_ASK_FOR_MASK);
|
|
}
|
|
|
|
void EnSkj_AskForMask(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) {
|
|
switch (globalCtx->msgCtx.choiceIndex) {
|
|
case 0: // Yes
|
|
EnSkj_SetupTakeMask(this, globalCtx);
|
|
break;
|
|
case 1: // No
|
|
Message_ContinueTextbox(globalCtx, 0x101D);
|
|
EnSkj_SetupWaitForMaskTextClear(this);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupTakeMask(EnSkj* this, GlobalContext* globalCtx) {
|
|
Message_ContinueTextbox(globalCtx, 0x101E);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_TAKE_MASK);
|
|
}
|
|
|
|
void EnSkj_TakeMask(EnSkj* this, GlobalContext* globalCtx) {
|
|
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) {
|
|
Rupees_ChangeBy(10);
|
|
gSaveContext.itemGetInf[3] |= 0x200;
|
|
EnSkj_SetNaviId(this);
|
|
Player_UnsetMask(globalCtx);
|
|
Item_Give(globalCtx, ITEM_SOLD_OUT);
|
|
Message_ContinueTextbox(globalCtx, 0x101F);
|
|
EnSkj_SetupWaitForMaskTextClear(this);
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupWaitForMaskTextClear(EnSkj* this) {
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_MASK_TEXT);
|
|
}
|
|
|
|
void EnSkj_WaitForMaskTextClear(EnSkj* this, GlobalContext* globalCtx) {
|
|
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) {
|
|
func_8002DF54(globalCtx, &this->actor, 7);
|
|
this->backflipFlag = 1;
|
|
EnSkj_Backflip(this);
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupWrongSong(EnSkj* this) {
|
|
this->textId = 0x1041;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WRONG_SONG);
|
|
}
|
|
|
|
void EnSkj_WrongSong(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (D_80B01EA0 != 0) {
|
|
EnSkj_SetupWaitForTextClear(this);
|
|
} else {
|
|
func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this));
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupWaitForTextClear(EnSkj* this) {
|
|
EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_FOR_TEXT);
|
|
}
|
|
|
|
void EnSkj_SariasSongWaitForTextClear(EnSkj* this, GlobalContext* globalCtx) {
|
|
u8 state = Message_GetState(&globalCtx->msgCtx);
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
|
|
if (state == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) {
|
|
EnSkj_SetupWaitInRange(this);
|
|
player->stateFlags2 |= 0x800000;
|
|
player->unk_6A8 = (Actor*)sSmallStumpSkullKid.skullkid;
|
|
}
|
|
}
|
|
|
|
void EnSkj_OcarinaGameSetupWaitForPlayer(EnSkj* this) {
|
|
this->actor.flags &= ~ACTOR_FLAG_0;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_OCARINA_GAME_WAIT_FOR_PLAYER);
|
|
}
|
|
|
|
void EnSkj_OcarinaGameWaitForPlayer(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (this->playerInRange) {
|
|
this->actor.flags |= ACTOR_FLAG_0;
|
|
EnSkj_SetupAction(this, SKJ_ACTION_OCARINA_GAME_IDLE);
|
|
}
|
|
}
|
|
|
|
s32 EnSkj_IsLeavingGame(EnSkj* this) {
|
|
s32 paramDecr = this->actor.params - 1;
|
|
|
|
if (sOcarinaMinigameSkullKids[paramDecr].unk0 == 2) {
|
|
EnSkj_SetupLeaveOcarinaGame(this);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void EnSkj_SetupIdle(EnSkj* this) {
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_OCARINA_GAME_IDLE);
|
|
}
|
|
|
|
void EnSkj_Appear(EnSkj* this) {
|
|
if (this->alpha != 255) {
|
|
this->alpha += 20;
|
|
|
|
if (this->alpha > 255) {
|
|
this->alpha = 255;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnSkj_OcarinaGameIdle(EnSkj* this, GlobalContext* globalCtx) {
|
|
EnSkj_Appear(this);
|
|
|
|
if ((EnSkj_IsLeavingGame(this) == false) && (this->minigameState != 0)) {
|
|
EnSkj_SetupPlayOcarinaGame(this);
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupPlayOcarinaGame(EnSkj* this) {
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_PLAY_FLUTE);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_OCARINA_GAME_PLAY);
|
|
}
|
|
|
|
void EnSkj_PlayOcarinaGame(EnSkj* this, GlobalContext* globalCtx) {
|
|
EnSkj_Appear(this);
|
|
|
|
if (!EnSkj_IsLeavingGame(this) && (this->minigameState == 0)) {
|
|
EnSkj_SetupIdle(this);
|
|
}
|
|
}
|
|
|
|
void EnSkj_SetupLeaveOcarinaGame(EnSkj* this) {
|
|
this->actor.velocity.y = 8.0f;
|
|
this->actor.speedXZ = -8.0f;
|
|
EnSkj_ChangeAnim(this, SKJ_ANIM_BACKFLIP);
|
|
EnSkj_SetupAction(this, SKJ_ACTION_OCARINA_GAME_LEAVE);
|
|
}
|
|
|
|
void EnSkj_LeaveOcarinaGame(EnSkj* this, GlobalContext* globalCtx) {
|
|
s32 paramsDecr = this->actor.params - 1;
|
|
|
|
sOcarinaMinigameSkullKids[paramsDecr].unk0 = 0;
|
|
sOcarinaMinigameSkullKids[paramsDecr].skullkid = NULL;
|
|
this->backflipFlag = 1;
|
|
EnSkj_Backflip(this);
|
|
}
|
|
|
|
void EnSkj_Update(Actor* thisx, GlobalContext* globalCtx) {
|
|
Vec3f dropPos;
|
|
s32 pad;
|
|
EnSkj* this = (EnSkj*)thisx;
|
|
|
|
D_80B01EA0 = Actor_ProcessTalkRequest(&this->actor, globalCtx);
|
|
|
|
this->timer++;
|
|
|
|
if (this->multiuseTimer != 0) {
|
|
this->multiuseTimer--;
|
|
}
|
|
|
|
if (this->needleShootTimer != 0) {
|
|
this->needleShootTimer--;
|
|
}
|
|
|
|
if (this->dodgeResetTimer != 0) {
|
|
this->dodgeResetTimer--;
|
|
}
|
|
|
|
if (this->dodgeResetTimer == 0) {
|
|
this->hitsUntilDodge = 3;
|
|
}
|
|
|
|
if ((this->backflipFlag != 0) && (this->alpha == 0)) {
|
|
if (this->action == 9) {
|
|
dropPos.x = this->actor.world.pos.x;
|
|
dropPos.y = this->actor.world.pos.y;
|
|
dropPos.z = this->actor.world.pos.z;
|
|
|
|
Item_DropCollectible(globalCtx, &dropPos, ITEM00_RUPEE_ORANGE);
|
|
}
|
|
Actor_Kill(&this->actor);
|
|
return;
|
|
}
|
|
|
|
Actor_SetFocus(&this->actor, 30.0f);
|
|
Actor_SetScale(&this->actor, 0.01f);
|
|
this->actionFunc(this, globalCtx);
|
|
this->actor.textId = this->textId;
|
|
EnSkj_CollisionCheck(this, globalCtx);
|
|
Collider_UpdateCylinder(&this->actor, &this->collider);
|
|
|
|
if ((this->unk_2D3 != 0) && (D_80B01EA0 == 0)) {
|
|
CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
|
|
|
|
if (this->actor.colorFilterTimer == 0) {
|
|
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
|
|
}
|
|
}
|
|
|
|
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
|
|
SkelAnime_Update(&this->skelAnime);
|
|
Actor_MoveForward(&this->actor);
|
|
Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 20.0f, 7);
|
|
}
|
|
|
|
void EnSkj_SariasSongShortStumpUpdate(Actor* thisx, GlobalContext* globalCtx) {
|
|
EnSkj* this = (EnSkj*)thisx;
|
|
|
|
D_80B01EA0 = Actor_ProcessTalkRequest(&this->actor, globalCtx);
|
|
|
|
if (BREG(0) != 0) {
|
|
DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z,
|
|
this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f,
|
|
1.0f, 255, 0, 0, 255, 4, globalCtx->state.gfxCtx);
|
|
}
|
|
}
|
|
|
|
void EnSkj_TurnPlayer(EnSkj* this, Player* player) {
|
|
Math_SmoothStepToS(&player->actor.shape.rot.y, this->actor.world.rot.y, 5, 2000, 0);
|
|
player->actor.world.rot.y = player->actor.shape.rot.y;
|
|
player->currentYaw = player->actor.shape.rot.y;
|
|
}
|
|
|
|
void EnSkj_SetupWaitForOcarina(EnSkj* this, GlobalContext* globalCtx) {
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
|
|
if (EnSkj_RangeCheck(player, this)) {
|
|
sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid->playerInRange = true;
|
|
sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].skullkid->playerInRange = true;
|
|
|
|
if (player->stateFlags2 & 0x1000000) {
|
|
player->stateFlags2 |= 0x2000000;
|
|
func_800F5BF0(NATURE_ID_KOKIRI_REGION);
|
|
EnSkj_TurnPlayer(this, player);
|
|
player->unk_6A8 = &this->actor;
|
|
Message_StartTextbox(globalCtx, 0x10BE, &this->actor);
|
|
this->actionFunc = EnSkj_StartOcarinaMinigame;
|
|
} else {
|
|
this->actionFunc = EnSkj_WaitForOcarina;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnSkj_WaitForOcarina(EnSkj* this, GlobalContext* globalCtx) {
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
|
|
if (player->stateFlags2 & 0x1000000) {
|
|
player->stateFlags2 |= 0x2000000;
|
|
func_800F5BF0(NATURE_ID_KOKIRI_REGION);
|
|
EnSkj_TurnPlayer(this, player);
|
|
player->unk_6A8 = &this->actor;
|
|
Message_StartTextbox(globalCtx, 0x10BE, &this->actor);
|
|
this->actionFunc = EnSkj_StartOcarinaMinigame;
|
|
} else if (EnSkj_RangeCheck(player, this)) {
|
|
player->stateFlags2 |= 0x800000;
|
|
}
|
|
}
|
|
|
|
void EnSkj_StartOcarinaMinigame(EnSkj* this, GlobalContext* globalCtx) {
|
|
u8 dialogState = Message_GetState(&globalCtx->msgCtx);
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
|
|
EnSkj_TurnPlayer(this, player);
|
|
|
|
if (dialogState == TEXT_STATE_CLOSING) {
|
|
func_8010BD58(globalCtx, OCARINA_ACTION_MEMORY_GAME);
|
|
if (sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid != NULL) {
|
|
sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid->minigameState = SKULL_KID_OCARINA_PLAY_NOTES;
|
|
}
|
|
this->songFailTimer = 160;
|
|
this->actionFunc = EnSkj_WaitForPlayback;
|
|
}
|
|
}
|
|
|
|
void EnSkj_WaitForPlayback(EnSkj* this, GlobalContext* globalCtx) {
|
|
Player* player = GET_PLAYER(globalCtx);
|
|
|
|
EnSkj_TurnPlayer(this, player);
|
|
|
|
if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_03) { // failed the game
|
|
Message_CloseTextbox(globalCtx);
|
|
globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04;
|
|
player->unk_6A8 = &this->actor;
|
|
func_8002F2CC(&this->actor, globalCtx, 26.0f);
|
|
this->textId = 0x102D;
|
|
this->actionFunc = EnSkj_FailedMiniGame;
|
|
} else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_0F) { // completed the game
|
|
func_80078884(NA_SE_SY_CORRECT_CHIME);
|
|
Message_CloseTextbox(globalCtx);
|
|
globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04;
|
|
player->unk_6A8 = &this->actor;
|
|
func_8002F2CC(&this->actor, globalCtx, 26.0f);
|
|
this->textId = 0x10BF;
|
|
this->actionFunc = EnSkj_WonOcarinaMiniGame;
|
|
} else { // playing the game
|
|
switch (globalCtx->msgCtx.msgMode) {
|
|
case MSGMODE_MEMORY_GAME_LEFT_SKULLKID_WAIT:
|
|
if (sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid != NULL) {
|
|
sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid->minigameState = SKULL_KID_OCARINA_WAIT;
|
|
}
|
|
if (!Audio_IsSfxPlaying(NA_SE_SY_METRONOME)) {
|
|
if (sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].skullkid != NULL) {
|
|
sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].skullkid->minigameState =
|
|
SKULL_KID_OCARINA_PLAY_NOTES;
|
|
}
|
|
Message_UpdateOcarinaGame(globalCtx);
|
|
}
|
|
break;
|
|
case MSGMODE_MEMORY_GAME_RIGHT_SKULLKID_WAIT:
|
|
if (sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].skullkid != NULL) {
|
|
sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].skullkid->minigameState = SKULL_KID_OCARINA_WAIT;
|
|
}
|
|
if (!Audio_IsSfxPlaying(NA_SE_SY_METRONOME)) {
|
|
Message_UpdateOcarinaGame(globalCtx);
|
|
this->songFailTimer = 160;
|
|
}
|
|
break;
|
|
case MSGMODE_MEMORY_GAME_PLAYER_PLAYING:
|
|
if (this->songFailTimer != 0) {
|
|
this->songFailTimer--;
|
|
} else { // took too long, game failed
|
|
func_80078884(NA_SE_SY_OCARINA_ERROR);
|
|
Message_CloseTextbox(globalCtx);
|
|
globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04;
|
|
player->unk_6A8 = &this->actor;
|
|
func_8002F2CC(&this->actor, globalCtx, 26.0f);
|
|
this->textId = 0x102D;
|
|
this->actionFunc = EnSkj_FailedMiniGame;
|
|
}
|
|
break;
|
|
case MSGMODE_MEMORY_GAME_START_NEXT_ROUND:
|
|
if (!Audio_IsSfxPlaying(NA_SE_SY_METRONOME)) {
|
|
if (sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid != NULL) {
|
|
sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid->minigameState =
|
|
SKULL_KID_OCARINA_PLAY_NOTES;
|
|
}
|
|
this->songFailTimer = 160;
|
|
Audio_OcaSetInstrument(6); // related instrument sound (flute?)
|
|
Audio_OcaSetSongPlayback(OCARINA_SONG_MEMORY_GAME + 1, 1);
|
|
globalCtx->msgCtx.msgMode = MSGMODE_MEMORY_GAME_LEFT_SKULLKID_PLAYING;
|
|
globalCtx->msgCtx.stateTimer = 2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnSkj_FailedMiniGame(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (D_80B01EA0) {
|
|
this->actionFunc = EnSkj_WaitForNextRound;
|
|
} else {
|
|
func_8002F2CC(&this->actor, globalCtx, 26.0f);
|
|
}
|
|
}
|
|
|
|
void EnSkj_WaitForNextRound(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) {
|
|
EnSkj_OfferNextRound(this, globalCtx);
|
|
}
|
|
}
|
|
|
|
void EnSkj_OfferNextRound(EnSkj* this, GlobalContext* globalCtx) {
|
|
Message_ContinueTextbox(globalCtx, 0x102E);
|
|
this->actionFunc = EnSkj_WaitForOfferResponse;
|
|
}
|
|
|
|
void EnSkj_WaitForOfferResponse(EnSkj* this, GlobalContext* globalCtx) {
|
|
Player* player;
|
|
|
|
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) {
|
|
switch (globalCtx->msgCtx.choiceIndex) {
|
|
case 0: // yes
|
|
player = GET_PLAYER(globalCtx);
|
|
player->stateFlags3 |= 0x20; // makes player take ocarina out right away after closing box
|
|
this->actionFunc = EnSkj_SetupWaitForOcarina;
|
|
break;
|
|
case 1: // no
|
|
this->actionFunc = EnSkj_CleanupOcarinaGame;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnSkj_WonOcarinaMiniGame(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (D_80B01EA0) {
|
|
this->actionFunc = EnSkj_WaitToGiveReward;
|
|
} else {
|
|
func_8002F2CC(&this->actor, globalCtx, 26.0f);
|
|
}
|
|
}
|
|
|
|
void EnSkj_WaitToGiveReward(EnSkj* this, GlobalContext* globalCtx) {
|
|
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) {
|
|
func_8002F434(&this->actor, globalCtx, sOcarinaGameRewards[gSaveContext.ocarinaGameRoundNum], 26.0f, 26.0f);
|
|
this->actionFunc = EnSkj_GiveOcarinaGameReward;
|
|
}
|
|
}
|
|
|
|
void EnSkj_GiveOcarinaGameReward(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (Actor_HasParent(&this->actor, globalCtx)) {
|
|
this->actor.parent = NULL;
|
|
this->actionFunc = EnSkj_FinishOcarinaGameRound;
|
|
} else {
|
|
func_8002F434(&this->actor, globalCtx, sOcarinaGameRewards[gSaveContext.ocarinaGameRoundNum], 26.0f, 26.0f);
|
|
}
|
|
}
|
|
|
|
void EnSkj_FinishOcarinaGameRound(EnSkj* this, GlobalContext* globalCtx) {
|
|
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) {
|
|
s32 ocarinaGameRoundNum = gSaveContext.ocarinaGameRoundNum;
|
|
|
|
if (gSaveContext.ocarinaGameRoundNum < 3) {
|
|
gSaveContext.ocarinaGameRoundNum++;
|
|
}
|
|
|
|
if (ocarinaGameRoundNum == 2) {
|
|
gSaveContext.itemGetInf[1] |= 0x80;
|
|
this->actionFunc = EnSkj_CleanupOcarinaGame;
|
|
} else {
|
|
EnSkj_OfferNextRound(this, globalCtx);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnSkj_CleanupOcarinaGame(EnSkj* this, GlobalContext* globalCtx) {
|
|
if (sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid != NULL) {
|
|
sOcarinaMinigameSkullKids[SKULL_KID_LEFT].unk0 = 2;
|
|
}
|
|
|
|
if (sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].skullkid != NULL) {
|
|
sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].unk0 = 2;
|
|
}
|
|
|
|
if ((sOcarinaMinigameSkullKids[SKULL_KID_LEFT].unk0 == 2) &&
|
|
(sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].unk0 == 2)) {
|
|
func_800F5C2C();
|
|
Actor_Kill(&this->actor);
|
|
}
|
|
}
|
|
|
|
void EnSkj_OcarinaMinigameShortStumpUpdate(Actor* thisx, GlobalContext* globalCtx) {
|
|
EnSkj* this = (EnSkj*)thisx;
|
|
|
|
D_80B01EA0 = Actor_ProcessTalkRequest(&this->actor, globalCtx);
|
|
this->timer++;
|
|
|
|
this->actor.focus.pos.x = 1230.0f;
|
|
this->actor.focus.pos.y = -90.0f;
|
|
this->actor.focus.pos.z = 450.0f;
|
|
|
|
if (BREG(0) != 0) {
|
|
DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z,
|
|
this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f,
|
|
1.0f, 255, 0, 0, 255, 4, globalCtx->state.gfxCtx);
|
|
}
|
|
|
|
this->actionFunc(this, globalCtx);
|
|
|
|
this->actor.textId = this->textId;
|
|
this->actor.xzDistToPlayer = 50.0;
|
|
}
|
|
|
|
s32 EnSkj_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) {
|
|
return 0;
|
|
}
|
|
|
|
void EnSkj_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) {
|
|
OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_skj.c", 2417);
|
|
|
|
if ((limbIndex == 11) && (gSaveContext.itemGetInf[3] & 0x200)) {
|
|
func_80093D18(globalCtx->state.gfxCtx);
|
|
Matrix_Push();
|
|
Matrix_RotateZYX(-0x4000, 0, 0, MTXMODE_APPLY);
|
|
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_skj.c", 2430),
|
|
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
|
gSPDisplayList(POLY_OPA_DISP++, gSKJskullMaskDL);
|
|
Matrix_Pop();
|
|
}
|
|
|
|
CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_skj.c", 2437);
|
|
}
|
|
|
|
Gfx* EnSkj_TranslucentDL(GraphicsContext* gfxCtx, u32 alpha) {
|
|
Gfx* dList;
|
|
Gfx* dListHead;
|
|
|
|
//! @bug This only allocates space for 1 command but uses 3
|
|
dList = dListHead = Graph_Alloc(gfxCtx, sizeof(Gfx));
|
|
gDPSetRenderMode(dListHead++, G_RM_FOG_SHADE_A, G_RM_AA_ZB_XLU_SURF2);
|
|
gDPSetEnvColor(dListHead++, 0, 0, 0, alpha);
|
|
gSPEndDisplayList(dListHead++);
|
|
|
|
return dList;
|
|
}
|
|
|
|
Gfx* EnSkj_OpaqueDL(GraphicsContext* gfxCtx, u32 alpha) {
|
|
Gfx* dList;
|
|
Gfx* dListHead;
|
|
|
|
//! @bug This only allocates space for 1 command but uses 2
|
|
dList = dListHead = Graph_Alloc(gfxCtx, sizeof(Gfx));
|
|
gDPSetEnvColor(dListHead++, 0, 0, 0, alpha);
|
|
gSPEndDisplayList(dListHead++);
|
|
|
|
return dList;
|
|
}
|
|
|
|
void EnSkj_Draw(Actor* thisx, GlobalContext* globalCtx) {
|
|
s32 pad;
|
|
EnSkj* this = (EnSkj*)thisx;
|
|
|
|
OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_skj.c", 2475);
|
|
|
|
func_80093D18(globalCtx->state.gfxCtx);
|
|
|
|
if (this->alpha < 255) {
|
|
gSPSegment(POLY_OPA_DISP++, 0x0C, EnSkj_TranslucentDL(globalCtx->state.gfxCtx, this->alpha));
|
|
} else {
|
|
gSPSegment(POLY_OPA_DISP++, 0x0C, EnSkj_OpaqueDL(globalCtx->state.gfxCtx, this->alpha));
|
|
}
|
|
|
|
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
|
|
EnSkj_OverrideLimbDraw, EnSkj_PostLimbDraw, this);
|
|
|
|
CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_skj.c", 2495);
|
|
}
|