diff --git a/assets/xml/objects/object_bal.xml b/assets/xml/objects/object_bal.xml index a31c0e29f3..c98ffb43df 100644 --- a/assets/xml/objects/object_bal.xml +++ b/assets/xml/objects/object_bal.xml @@ -1,81 +1,94 @@  + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/include/z64save.h b/include/z64save.h index 01739975d3..5abf105e92 100644 --- a/include/z64save.h +++ b/include/z64save.h @@ -622,7 +622,7 @@ typedef enum { #define WEEKEVENTREG_09_20 PACK_WEEKEVENTREG_FLAG(9, 0x20) #define WEEKEVENTREG_09_40 PACK_WEEKEVENTREG_FLAG(9, 0x40) #define WEEKEVENTREG_09_80 PACK_WEEKEVENTREG_FLAG(9, 0x80) -#define WEEKEVENTREG_10_01 PACK_WEEKEVENTREG_FLAG(10, 0x01) +#define WEEKEVENTREG_TALKED_TINGLE PACK_WEEKEVENTREG_FLAG(10, 0x01) #define WEEKEVENTREG_10_02 PACK_WEEKEVENTREG_FLAG(10, 0x02) #define WEEKEVENTREG_10_04 PACK_WEEKEVENTREG_FLAG(10, 0x04) #define WEEKEVENTREG_10_08 PACK_WEEKEVENTREG_FLAG(10, 0x08) @@ -888,17 +888,20 @@ typedef enum { // Cremia did Milk Run alone. Player didn't interact or didn't accept the ride #define WEEKEVENTREG_34_80 PACK_WEEKEVENTREG_FLAG(34, 0x80) -#define WEEKEVENTREG_35_01 PACK_WEEKEVENTREG_FLAG(35, 0x01) -#define WEEKEVENTREG_35_02 PACK_WEEKEVENTREG_FLAG(35, 0x02) -#define WEEKEVENTREG_35_04 PACK_WEEKEVENTREG_FLAG(35, 0x04) -#define WEEKEVENTREG_35_08 PACK_WEEKEVENTREG_FLAG(35, 0x08) -#define WEEKEVENTREG_35_10 PACK_WEEKEVENTREG_FLAG(35, 0x10) -#define WEEKEVENTREG_35_20 PACK_WEEKEVENTREG_FLAG(35, 0x20) +// Bought each possible map from Tingle +#define WEEKEVENTREG_TINGLE_MAP_BOUGHT_CLOCK_TOWN PACK_WEEKEVENTREG_FLAG(35, 0x01) +#define WEEKEVENTREG_TINGLE_MAP_BOUGHT_WOODFALL PACK_WEEKEVENTREG_FLAG(35, 0x02) +#define WEEKEVENTREG_TINGLE_MAP_BOUGHT_SNOWHEAD PACK_WEEKEVENTREG_FLAG(35, 0x04) +#define WEEKEVENTREG_TINGLE_MAP_BOUGHT_ROMANI_RANCH PACK_WEEKEVENTREG_FLAG(35, 0x08) +#define WEEKEVENTREG_TINGLE_MAP_BOUGHT_GREAT_BAY PACK_WEEKEVENTREG_FLAG(35, 0x10) +#define WEEKEVENTREG_TINGLE_MAP_BOUGHT_STONE_TOWER PACK_WEEKEVENTREG_FLAG(35, 0x20) + #define WEEKEVENTREG_35_40 PACK_WEEKEVENTREG_FLAG(35, 0x40) // Obtained Heart Piece from Five Frogs of the Frog Choir #define WEEKEVENTREG_35_80 PACK_WEEKEVENTREG_FLAG(35, 0x80) +// Player has spoken to certain shrine gorons in the winter #define WEEKEVENTREG_36_01 PACK_WEEKEVENTREG_FLAG(36, 0x01) #define WEEKEVENTREG_36_02 PACK_WEEKEVENTREG_FLAG(36, 0x02) #define WEEKEVENTREG_36_04 PACK_WEEKEVENTREG_FLAG(36, 0x04) @@ -910,6 +913,7 @@ typedef enum { #define WEEKEVENTREG_37_01 PACK_WEEKEVENTREG_FLAG(37, 0x01) #define WEEKEVENTREG_37_02 PACK_WEEKEVENTREG_FLAG(37, 0x02) #define WEEKEVENTREG_37_04 PACK_WEEKEVENTREG_FLAG(37, 0x04) + #define WEEKEVENTREG_37_08 PACK_WEEKEVENTREG_FLAG(37, 0x08) #define WEEKEVENTREG_37_10 PACK_WEEKEVENTREG_FLAG(37, 0x10) #define WEEKEVENTREG_37_20 PACK_WEEKEVENTREG_FLAG(37, 0x20) @@ -1095,8 +1099,12 @@ typedef enum { #define WEEKEVENTREG_64_01 PACK_WEEKEVENTREG_FLAG(64, 0x01) #define WEEKEVENTREG_64_02 PACK_WEEKEVENTREG_FLAG(64, 0x02) #define WEEKEVENTREG_64_04 PACK_WEEKEVENTREG_FLAG(64, 0x04) -#define WEEKEVENTREG_64_08 PACK_WEEKEVENTREG_FLAG(64, 0x08) -#define WEEKEVENTREG_64_10 PACK_WEEKEVENTREG_FLAG(64, 0x10) + +// Two-bit field storing player form when first talked to Tingle that cycle +// 0 - Zora, 1 - Deku, 2 - Goron, 3 - Human +#define WEEKEVENTREG_TINGLE_RECOGNIZED_PLAYER_FORM_LOW_BIT PACK_WEEKEVENTREG_FLAG(64, 0x08) +#define WEEKEVENTREG_TINGLE_RECOGNIZED_PLAYER_FORM_HIGH_BIT PACK_WEEKEVENTREG_FLAG(64, 0x10) + #define WEEKEVENTREG_64_20 PACK_WEEKEVENTREG_FLAG(64, 0x20) #define WEEKEVENTREG_64_40 PACK_WEEKEVENTREG_FLAG(64, 0x40) #define WEEKEVENTREG_TALKED_DOGGY_RACETRACK_OWNER_DAY_1 PACK_WEEKEVENTREG_FLAG(64, 0x80) diff --git a/spec b/spec index d102c0160e..17b041fe1e 100644 --- a/spec +++ b/spec @@ -2810,8 +2810,7 @@ beginseg name "ovl_En_Bal" compress include "build/src/overlays/actors/ovl_En_Bal/z_en_bal.o" - include "build/data/ovl_En_Bal/ovl_En_Bal.data.o" - include "build/data/ovl_En_Bal/ovl_En_Bal.reloc.o" + include "build/src/overlays/actors/ovl_En_Bal/ovl_En_Bal_reloc.o" endseg beginseg diff --git a/src/code/z_sram_NES.c b/src/code/z_sram_NES.c index 247e19d333..7683144909 100644 --- a/src/code/z_sram_NES.c +++ b/src/code/z_sram_NES.c @@ -117,9 +117,12 @@ u16 sPersistentCycleWeekEventRegs[ARRAY_COUNT(gSaveContext.save.saveInfo.weekEve /* 33 */ 0, /* 34 */ 0, /* 35 */ - PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_35_01) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_35_02) | - PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_35_04) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_35_08) | - PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_35_10) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_35_20) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_CLOCK_TOWN) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_WOODFALL) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_SNOWHEAD) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_ROMANI_RANCH) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_GREAT_BAY) | + PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_STONE_TOWER) | PERSISTENT_WEEKEVENTREG(WEEKEVENTREG_35_80), /* 36 */ 0, /* 37 */ 0, diff --git a/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.c b/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.c index 98d827868f..baeba3ce53 100644 --- a/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.c +++ b/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.c @@ -30,40 +30,41 @@ ActorInit Dm_Bal_InitVars = { (ActorFunc)DmBal_Draw, }; +// These animations are the same and in the same order as in the main NPC actor EnBal typedef enum { - /* -1 */ DMBAL_ANIM_NONE = -1, - /* 0x0 */ DMBAL_ANIM_0, - /* 0x1 */ DMBAL_ANIM_1, - /* 0x2 */ DMBAL_ANIM_2, - /* 0x3 */ DMBAL_ANIM_3, - /* 0x4 */ DMBAL_ANIM_4, - /* 0x5 */ DMBAL_ANIM_5, - /* 0x6 */ DMBAL_ANIM_6, - /* 0x7 */ DMBAL_ANIM_7, - /* 0x8 */ DMBAL_ANIM_8, - /* 0x9 */ DMBAL_ANIM_9, - /* 0xA */ DMBAL_ANIM_10, - /* 0xB */ DMBAL_ANIM_11, - /* 0xC */ DMBAL_ANIM_12, - /* 0xD */ DMBAL_ANIM_13, - /* 0xE */ DMBAL_ANIM_MAX -} DmBalAnimation; + /* -1 */ TINGLE_CS_ANIM_NONE = -1, + /* 0x0 */ TINGLE_CS_ANIM_FLOAT_IDLE, + /* 0x1 */ TINGLE_CS_ANIM_FALL_LOOP, + /* 0x2 */ TINGLE_CS_ANIM_FALL_ONCE, + /* 0x3 */ TINGLE_CS_ANIM_LAND, + /* 0x4 */ TINGLE_CS_ANIM_TWIST, + /* 0x5 */ TINGLE_CS_ANIM_TALK, + /* 0x6 */ TINGLE_CS_ANIM_MAGIC, + /* 0x7 */ TINGLE_CS_ANIM_HAPPY_DANCE_LOOP, + /* 0x8 */ TINGLE_CS_ANIM_HAPPY_DANCE_ONCE, + /* 0x9 */ TINGLE_CS_ANIM_MAGIC_REVERSE, + /* 0xA */ TINGLE_CS_ANIM_IDLE, + /* 0xB */ TINGLE_CS_ANIM_SPIN, + /* 0xC */ TINGLE_CS_ANIM_HIDE_FACE, + /* 0xD */ TINGLE_CS_ANIM_CONFETTI, + /* 0xE */ TINGLE_CS_ANIM_MAX +} TingleCsAnimation; -static AnimationInfo sAnimationInfo[DMBAL_ANIM_MAX] = { - { &object_bal_Anim_0005FC, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // DMBAL_ANIM_0 - { &object_bal_Anim_000840, 1.5f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // DMBAL_ANIM_1 - { &object_bal_Anim_000840, 1.5f, 0.0f, 0.0f, ANIMMODE_ONCE, -4.0f }, // DMBAL_ANIM_2 - { &object_bal_Anim_00A7DC, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, -4.0f }, // DMBAL_ANIM_3 - { &object_bal_Anim_00B1E8, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // DMBAL_ANIM_4 - { &object_bal_Anim_00B604, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // DMBAL_ANIM_5 - { &object_bal_Anim_00C498, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, -8.0f }, // DMBAL_ANIM_6 - { &object_bal_Anim_00C8D8, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // DMBAL_ANIM_7 - { &object_bal_Anim_00C8D8, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, -8.0f }, // DMBAL_ANIM_8 - { &object_bal_Anim_00C498, 1.0f, 23.0f, 0.0f, ANIMMODE_ONCE, -8.0f }, // DMBAL_ANIM_9 - { &object_bal_Anim_00D530, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // DMBAL_ANIM_10 - { &object_bal_Anim_000C78, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -2.0f }, // DMBAL_ANIM_11 - { &object_bal_Anim_00CB78, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -2.0f }, // DMBAL_ANIM_12 - { &object_bal_Anim_001804, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -2.0f }, // DMBAL_ANIM_13 +static AnimationInfo sAnimationInfo[TINGLE_CS_ANIM_MAX] = { + { &gTingleFloatIdleAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // TINGLE_CS_ANIM_FLOAT_IDLE + { &gTingleFallAnim, 1.5f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // TINGLE_CS_ANIM_FALL_LOOP + { &gTingleFallAnim, 1.5f, 0.0f, 0.0f, ANIMMODE_ONCE, -4.0f }, // TINGLE_CS_ANIM_FALL_ONCE + { &gTingleLandAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, -4.0f }, // TINGLE_CS_ANIM_LAND + { &gTingleTwistAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // TINGLE_CS_ANIM_TWIST + { &gTingleTalkAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // TINGLE_CS_ANIM_TALK + { &gTingleThrowConfettiAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, -8.0f }, // TINGLE_CS_ANIM_MAGIC + { &gTingleHappyDanceAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // TINGLE_CS_ANIM_HAPPY_DANCE_LOOP + { &gTingleHappyDanceAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, -8.0f }, // TINGLE_CS_ANIM_HAPPY_DANCE_ONCE + { &gTingleThrowConfettiAnim, 1.0f, 23.0f, 0.0f, ANIMMODE_ONCE, -8.0f }, // TINGLE_CS_ANIM_MAGIC_REVERSE + { &gTingleIdleAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // TINGLE_CS_ANIM_IDLE + { &gTingleSpinAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -2.0f }, // TINGLE_CS_ANIM_SPIN + { &gTingleFloatHideFaceAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -2.0f }, // TINGLE_CS_ANIM_HIDE_FACE + { &gTingleFloatThrowConfettiAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -2.0f }, // TINGLE_CS_ANIM_CONFETTI }; void DmBal_Init(Actor* thisx, PlayState* play) { @@ -73,8 +74,8 @@ void DmBal_Init(Actor* thisx, PlayState* play) { this->actor.uncullZoneForward = 3000.0f; Actor_SetScale(&this->actor, 0.02f); ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 36.0f); - SkelAnime_InitFlex(play, &this->skelAnime, &object_bal_Skel_00A6D0, &object_bal_Anim_0005FC, this->jointTable, - this->morphTable, OBJECT_BAL_LIMB_MAX); + SkelAnime_InitFlex(play, &this->skelAnime, &gTingleSkel, &gTingleFloatIdleAnim, this->jointTable, this->morphTable, + TINGLE_LIMB_MAX); Actor_UpdateBgCheckInfo(play, &this->actor, 0.0f, 0.0f, 0.0f, UPDBGCHECKINFO_FLAG_4); this->timer = 60; this->eyeIndex = 0; @@ -105,16 +106,16 @@ void DmBal_HandleCutscene(DmBal* this, PlayState* play) { case 1: this->keepEyesShut = false; this->eyeIndex = 0; - Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, DMBAL_ANIM_0); + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_CS_ANIM_FLOAT_IDLE); break; case 2: this->keepEyesShut = true; - Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, DMBAL_ANIM_12); + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_CS_ANIM_HIDE_FACE); break; case 3: - Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, DMBAL_ANIM_13); + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_CS_ANIM_CONFETTI); break; default: @@ -176,7 +177,7 @@ void DmBal_Update(Actor* thisx, PlayState* play) { DmBal* this = THIS; // Throw confetti - if (Animation_OnFrame(&this->skelAnime, 29.0f) && (this->skelAnime.animation == &object_bal_Anim_001804)) { + if (Animation_OnFrame(&this->skelAnime, 29.0f) && (this->skelAnime.animation == &gTingleFloatThrowConfettiAnim)) { Vec3f pos = this->actor.world.pos; Vec3f vel = { 0.0f, 9.0f, 0.0f }; @@ -199,7 +200,7 @@ s32 DmBal_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* p DmBal* this = THIS; Vec3s rots; - if (limbIndex == OBJECT_BAL_LIMB_06) { + if (limbIndex == TINGLE_LIMB_BALLOON) { rots.x = Math_SinS(this->unk_33A) * (0x10000 / 18); rots.z = Math_CosS(this->unk_33A) * (0x10000 / 18); Matrix_RotateZYX(rots.x, 0, rots.z, MTXMODE_APPLY); @@ -213,7 +214,7 @@ s32 DmBal_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* p void DmBal_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, Actor* thisx) { } -static TexturePtr sEyeTextures[] = { object_bal_Tex_006050, object_bal_Tex_0094D0 }; +static TexturePtr sEyeTextures[] = { gTingleEyeOpenTex, gTingleEyeClosedTex }; void DmBal_Draw(Actor* thisx, PlayState* play) { DmBal* this = THIS; diff --git a/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.h b/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.h index 01f10d4c92..fa2e856393 100644 --- a/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.h +++ b/src/overlays/actors/ovl_Dm_Bal/z_dm_bal.h @@ -14,8 +14,8 @@ typedef struct DmBal { /* 0x188 */ DmBalActionFunc actionFunc; /* 0x18C */ Vec3f scale; /* 0x198 */ s32 eyeIndex; - /* 0x262 */ Vec3s jointTable[OBJECT_BAL_LIMB_MAX]; - /* 0x19C */ Vec3s morphTable[OBJECT_BAL_LIMB_MAX]; + /* 0x262 */ Vec3s jointTable[TINGLE_LIMB_MAX]; + /* 0x19C */ Vec3s morphTable[TINGLE_LIMB_MAX]; /* 0x328 */ UNK_TYPE1 pad_328[12]; /* 0x334 */ s16 timer; /* 0x336 */ s16 keepEyesShut; diff --git a/src/overlays/actors/ovl_En_Bal/z_en_bal.c b/src/overlays/actors/ovl_En_Bal/z_en_bal.c index 779373a9dc..303baccf6b 100644 --- a/src/overlays/actors/ovl_En_Bal/z_en_bal.c +++ b/src/overlays/actors/ovl_En_Bal/z_en_bal.c @@ -5,6 +5,7 @@ */ #include "z_en_bal.h" +#include "overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h" #define FLAGS (ACTOR_FLAG_1 | ACTOR_FLAG_8 | ACTOR_FLAG_10) @@ -15,7 +16,80 @@ void EnBal_Destroy(Actor* thisx, PlayState* play); void EnBal_Update(Actor* thisx, PlayState* play); void EnBal_Draw(Actor* thisx, PlayState* play); -#if 0 +typedef enum { + /* 0 */ TINGLE_EYETEX_OPEN, + /* 1 */ TINGLE_EYETEX_CLOSED +} TingleEyeTexture; + +typedef enum { + /* -1 */ TINGLE_ANIM_NONE = -1, + /* 0 */ TINGLE_ANIM_FLOAT_IDLE, + /* 1 */ TINGLE_ANIM_FALL_LOOP, + /* 2 */ TINGLE_ANIM_FALL_ONCE, + /* 3 */ TINGLE_ANIM_LAND, + /* 4 */ TINGLE_ANIM_TWIST, + /* 5 */ TINGLE_ANIM_TALK, + /* 6 */ TINGLE_ANIM_MAGIC, + /* 7 */ TINGLE_ANIM_HAPPY_DANCE_LOOP, + /* 8 */ TINGLE_ANIM_HAPPY_DANCE_ONCE, + /* 9 */ TINGLE_ANIM_MAGIC_REVERSE, + /* 10 */ TINGLE_ANIM_IDLE, + /* 11 */ TINGLE_ANIM_SPIN, + /* 12 */ TINGLE_ANIM_HIDE_FACE, + /* 13 */ TINGLE_ANIM_CONFETTI, + /* 14 */ TINGLE_ANIM_MAX +} TingleAnimation; + +typedef enum { + /* 0 */ TINGLE_MAPCHOICE_PROXIMAL, + /* 1 */ TINGLE_MAPCHOICE_DISTAL, + /* 2 */ TINGLE_MAPCHOICE_CANCEL +} TingleBuyMapChoice; + +typedef enum { + /* 0 */ TINGLE_WATCH_TARGET_NONE, + /* 1 */ TINGLE_WATCH_TARGET_PLAYER, + /* 2 */ TINGLE_WATCH_TARGET_FAIRY +} TingleWatchTarget; + +typedef enum { + /* 0 */ TINGLE_BALLOON_ACTION_NONE, + /* 1 */ TINGLE_BALLOON_ACTION_POP, + /* 2 */ TINGLE_BALLOON_ACTION_FALL, + /* 4 */ TINGLE_BALLOON_ACTION_INFLATE = 4, + /* 5 */ TINGLE_BALLOON_ACTION_RISE +} TingleBalloonAction; + +typedef enum { + /* 0 */ TINGLE_IDLESTAGE_ACTIVITY, + /* 2 */ TINGLE_IDLESTAGE_PREP_WAIT = 2, + /* 3 */ TINGLE_IDLESTAGE_WAIT +} TingleIdleAnimStage; + +void EnBal_SetMainColliderToHead(EnBal* this); +s32 EnBal_ValidatePictograph(PlayState* play, Actor* thisx); +void EnBal_SetupFloatIdle(EnBal* this); +void EnBal_FloatIdle(EnBal* this, PlayState* play); +void EnBal_PopBalloon(EnBal* this, PlayState* play); +void EnBal_SetupFall(EnBal* this); +void EnBal_Fall(EnBal* this, PlayState* play); +void EnBal_InflateBalloon(EnBal* this, PlayState* play); +void EnBal_SetupFloatUp(EnBal* this); +void EnBal_FloatUp(EnBal* this, PlayState* play); +void EnBal_SetupGroundIdle(EnBal* this); +void EnBal_GroundIdle(EnBal* this, PlayState* play); +void EnBal_SetupTalk(EnBal* this); +void EnBal_Talk(EnBal* this, PlayState* play); +PlayerTransformation EnBal_GetRecognizedPlayerForm(void); +void EnBal_ThrowMagicSparkles(EnBal* this, PlayState* play); +void EnBal_EmitDustPuff(EnBal* this, PlayState* play); +void EnBal_TryPurchaseMap(EnBal* this, PlayState* play); +void EnBal_HandleConversation(EnBal* this, PlayState* play); +void EnBal_SetupOfferGetItem(EnBal* this); +void EnBal_OfferGetItem(EnBal* this, PlayState* play); +void EnBal_SetupThankYou(EnBal* this); +void EnBal_ThankYou(EnBal* this, PlayState* play); + ActorInit En_Bal_InitVars = { ACTOR_EN_BAL, ACTORCAT_NPC, @@ -28,8 +102,7 @@ ActorInit En_Bal_InitVars = { (ActorFunc)EnBal_Draw, }; -// static DamageTable sDamageTable = { -static DamageTable D_80A63CE0 = { +static DamageTable sDamageTable = { /* Deku Nut */ DMG_ENTRY(0, 0x0), /* Deku Stick */ DMG_ENTRY(0, 0x0), /* Horse trample */ DMG_ENTRY(0, 0x0), @@ -64,108 +137,1038 @@ static DamageTable D_80A63CE0 = { /* Powder Keg */ DMG_ENTRY(0, 0x0), }; -// sColChkInfoInit -static CollisionCheckInfoInit2 D_80A63D00 = { 1, 0, 0, 0, MASS_IMMOVABLE }; +static CollisionCheckInfoInit2 sColChkInfoInit = { 1, 0, 0, 0, MASS_IMMOVABLE }; -// static ColliderJntSphElementInit sJntSphElementsInit[1] = { -static ColliderJntSphElementInit D_80A63D0C[1] = { +static ColliderJntSphElementInit sJntSphElementsInit[1] = { { - { ELEMTYPE_UNK0, { 0xF7CFFFFF, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, }, - { 6, { { 2400, 0, 0 }, 50 }, 100 }, + { + ELEMTYPE_UNK0, + { 0xF7CFFFFF, 0x00, 0x00 }, + { 0xF7CFFFFF, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { TINGLE_LIMB_BALLOON, { { 2400, 0, 0 }, 50 }, 100 }, }, }; -// static ColliderJntSphInit sJntSphInit = { -static ColliderJntSphInit D_80A63D30 = { - { COLTYPE_NONE, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_1, COLSHAPE_JNTSPH, }, - ARRAY_COUNT(sJntSphElementsInit), D_80A63D0C, // sJntSphElementsInit, +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sJntSphElementsInit), + sJntSphElementsInit, }; -#endif +static s16 sBuyMapOptions[TINGLE_MAP_MAX][2] = { + { TINGLE_MAP_CLOCK_TOWN, TINGLE_MAP_WOODFALL }, // TINGLE_MAP_CLOCK_TOWN + { TINGLE_MAP_WOODFALL, TINGLE_MAP_SNOWHEAD }, // TINGLE_MAP_WOODFALL + { TINGLE_MAP_SNOWHEAD, TINGLE_MAP_ROMANI_RANCH }, // TINGLE_MAP_SNOWHEAD + { TINGLE_MAP_ROMANI_RANCH, TINGLE_MAP_GREAT_BAY }, // TINGLE_MAP_ROMANI_RANCH + { TINGLE_MAP_GREAT_BAY, TINGLE_MAP_STONE_TOWER }, // TINGLE_MAP_GREAT_BAY + { TINGLE_MAP_STONE_TOWER, TINGLE_MAP_CLOCK_TOWN }, // TINGLE_MAP_STONE_TOWER +}; -extern DamageTable D_80A63CE0; -extern CollisionCheckInfoInit2 D_80A63D00; -extern ColliderJntSphElementInit D_80A63D0C[1]; -extern ColliderJntSphInit D_80A63D30; +static AnimationInfo sAnimationInfo[TINGLE_ANIM_MAX] = { + { &gTingleFloatIdleAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // TINGLE_ANIM_FLOAT_IDLE + { &gTingleFallAnim, 1.5f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // TINGLE_ANIM_FALL_LOOP + { &gTingleFallAnim, 1.5f, 0.0f, 0.0f, ANIMMODE_ONCE, -4.0f }, // TINGLE_ANIM_FALL_ONCE + { &gTingleLandAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, -4.0f }, // TINGLE_ANIM_LAND + { &gTingleTwistAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // TINGLE_ANIM_TWIST + { &gTingleTalkAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // TINGLE_ANIM_TALK + { &gTingleThrowConfettiAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, -8.0f }, // TINGLE_ANIM_MAGIC + { &gTingleHappyDanceAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // TINGLE_ANIM_HAPPY_DANCE_LOOP + { &gTingleHappyDanceAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, -8.0f }, // TINGLE_ANIM_HAPPY_DANCE_ONCE + { &gTingleThrowConfettiAnim, 1.0f, 23.0f, 0.0f, ANIMMODE_ONCE, -8.0f }, // TINGLE_ANIM_MAGIC_REVERSE + { &gTingleIdleAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f }, // TINGLE_ANIM_IDLE + { &gTingleSpinAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -2.0f }, // TINGLE_ANIM_SPIN + { &gTingleFloatHideFaceAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -2.0f }, // TINGLE_ANIM_HIDE_FACE + { &gTingleFloatThrowConfettiAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -2.0f }, // TINGLE_ANIM_CONFETTI +}; -extern UNK_TYPE D_060005FC; -extern UNK_TYPE D_0600CB78; -extern UNK_TYPE D_0600D530; +void EnBal_Init(Actor* thisx, PlayState* play) { + EnBal* this = THIS; + s32 pad; + f32 endFrame = Animation_GetLastFrame(&gTingleFloatIdleAnim); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/EnBal_Init.s") + this->locationMapId = TINGLE_GET_MAP_ID(&this->picto.actor); + this->picto.actor.targetMode = 1; + this->picto.actor.uncullZoneForward = 3000.0f; + Actor_SetScale(&this->picto.actor, 0.02f); + SkelAnime_InitFlex(play, &this->skelAnime, &gTingleSkel, &gTingleFloatIdleAnim, this->jointTable, this->morphTable, + TINGLE_LIMB_MAX); + if (gSaveContext.save.saveInfo.playerData.isMagicAcquired) { + Animation_Change(&this->skelAnime, &gTingleTalkAnim, 1.0f, 0.0f, endFrame, ANIMMODE_LOOP, -10.0f); + } + ActorShape_Init(&this->picto.actor.shape, 0.0f, ActorShadow_DrawCircle, 0.0f); + this->skyFloatPhase = 0; + this->textId = 0; + this->balloonAction = TINGLE_BALLOON_ACTION_NONE; + this->timer = 0; + this->watchTarget = TINGLE_WATCH_TARGET_NONE; + this->inflateEarly = false; + this->isTalking = false; + this->eyeTexIndex = TINGLE_EYETEX_OPEN; + this->eyeTimer = 60; + this->forceEyesShut = false; + Collider_InitJntSph(play, &this->collider); + Collider_SetJntSph(play, &this->collider, &this->picto.actor, &sJntSphInit, this->colliderElements); + CollisionCheck_SetInfo2(&this->picto.actor.colChkInfo, &sDamageTable, &sColChkInfoInit); + Actor_UpdateBgCheckInfo(play, &this->picto.actor, 0.0f, 0.0f, 0.0f, UPDBGCHECKINFO_FLAG_4); + this->picto.validationFunc = EnBal_ValidatePictograph; + if (!gSaveContext.save.saveInfo.playerData.isMagicAcquired) { + this->picto.actor.world.pos.y = this->picto.actor.floorHeight; + EnBal_SetMainColliderToHead(this); + EnBal_SetupGroundIdle(this); + } else { + EnBal_SetupFloatIdle(this); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/EnBal_Destroy.s") +void EnBal_Destroy(Actor* thisx, PlayState* play) { + EnBal* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A61A18.s") + Collider_InitJntSph(play, &this->collider); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A61A44.s") +void EnBal_SetMainColliderToBalloon(EnBal* this) { + this->collider.elements->dim.limb = TINGLE_LIMB_BALLOON; + this->collider.elements->dim.modelSphere.radius = 40; + this->collider.elements->dim.modelSphere.center.x = 2200; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/EnBal_ValidatePictograph.s") +void EnBal_SetMainColliderToHead(EnBal* this) { + this->collider.elements->dim.limb = TINGLE_LIMB_HEAD; + this->collider.elements->dim.modelSphere.radius = 25; + this->collider.elements->dim.modelSphere.center.x = 0; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A61ADC.s") +s32 EnBal_ValidatePictograph(PlayState* play, Actor* thisx) { + s32 pictoValid; + EnBal* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A61B5C.s") + pictoValid = Snap_ValidatePictograph(play, &this->picto.actor, PICTO_VALID_TINGLE, &this->picto.actor.focus.pos, + &this->picto.actor.shape.rot, 10.0f, 400.0f, 0x4000); + if (!pictoValid) { + this->forceEyesShut = true; + this->idleAnimStage = TINGLE_IDLESTAGE_WAIT; + } + return pictoValid; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A61C9C.s") +void EnBal_SetupFloatIdle(EnBal* this) { + this->timer = 0; + EnBal_SetMainColliderToBalloon(this); + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_FLOAT_IDLE); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A61CE4.s") + this->picto.actor.focus.pos = this->picto.actor.world.pos; + this->picto.actor.focus.pos.y = this->picto.actor.world.pos.y + 100.0f; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A61DF8.s") + this->actionFunc = EnBal_FloatIdle; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A61E5C.s") +void EnBal_FloatIdle(EnBal* this, PlayState* play) { + f32 scaleFactor; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A62128.s") + this->skyFloatPhase += 0x320; + this->picto.actor.focus.pos.y = this->picto.actor.world.pos.y + 100.0f; + this->timer += 1000; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A621C4.s") + scaleFactor = (Math_CosS(this->skyFloatPhase) * 0.1f) + 1.0f; + this->balloonScale.z = scaleFactor; + this->balloonScale.y = scaleFactor; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A62260.s") + scaleFactor = (Math_SinS(this->skyFloatPhase) * 0.1f) + 1.0f; + this->balloonScale.x = SQ(scaleFactor); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A622A0.s") + this->picto.actor.world.pos.y = (Math_SinS(this->skyFloatPhase) * 50.0f) + this->picto.actor.home.pos.y; + if (this->forceEyesShut == true) { + if (this->skelAnime.animation != &gTingleFloatHideFaceAnim) { + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_HIDE_FACE); + } else if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + if (this->idleAnimStage != TINGLE_IDLESTAGE_ACTIVITY) { + this->idleAnimStage--; + } else { + this->forceEyesShut = false; + this->eyeTexIndex = TINGLE_EYETEX_OPEN; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_FLOAT_IDLE); + } + } + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A623CC.s") +void EnBal_SetupPopBalloon(EnBal* this) { + EnBal_SetMainColliderToHead(this); + this->timer = 0; + Actor_PlaySfx(&this->picto.actor, NA_SE_EV_MUJURA_BALLOON_BROKEN); + this->balloonAction = TINGLE_BALLOON_ACTION_POP; + this->actionFunc = EnBal_PopBalloon; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A624DC.s") +void EnBal_PopBalloon(EnBal* this, PlayState* play) { + this->picto.actor.focus.pos.x = this->picto.actor.world.pos.x; + this->picto.actor.focus.pos.y = this->picto.actor.world.pos.y + 20.0f; + this->picto.actor.focus.pos.z = this->picto.actor.world.pos.z + 30.0f; + if (this->picto.actor.csId != CS_ID_NONE) { + if (CutsceneManager_IsNext(this->picto.actor.csId)) { + CutsceneManager_StartWithPlayerCs(this->picto.actor.csId, &this->picto.actor); + Camera_SetFocalActor(Play_GetCamera(play, CutsceneManager_GetCurrentSubCamId(this->picto.actor.csId)), + &this->picto.actor); + } else { + CutsceneManager_Queue(this->picto.actor.csId); + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A62818.s") + if (this->timer > 30) { + this->timer = 0; + EnBal_SetupFall(this); + } else if (this->timer > 10) { + // Freeze animation and induce blink + this->skelAnime.playSpeed = 0.0f; + if (this->eyeTimer > 6) { + this->eyeTimer = 6; + } + this->timer++; + } else { + this->timer++; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A62830.s") +void EnBal_SetupFall(EnBal* this) { + Actor_PlaySfx(&this->picto.actor, NA_SE_VO_TIVO00); + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_FALL_LOOP); + this->forceEyesShut = true; + this->balloonAction = TINGLE_BALLOON_ACTION_FALL; + this->actionFunc = EnBal_Fall; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A629C4.s") +void EnBal_Fall(EnBal* this, PlayState* play) { + Vec3f worldPos = this->picto.actor.world.pos; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A62AC4.s") + this->picto.actor.focus.pos.x = this->picto.actor.world.pos.x; + this->picto.actor.focus.pos.y = this->picto.actor.world.pos.y + 20.0f; + this->picto.actor.focus.pos.z = this->picto.actor.world.pos.z + 30.0f; + if (this->timer == 30) { + this->picto.actor.gravity = -1.5f; + Math_SmoothStepToS(&this->picto.actor.shape.rot.y, this->picto.actor.world.rot.y, 5, 0x3000, 0x100); + Math_SmoothStepToS(&this->picto.actor.shape.rot.z, this->picto.actor.world.rot.z, 5, 0x3000, 0x100); + } else if (this->timer < 30) { + this->timer++; + this->picto.actor.shape.rot.y = (s32)(Math_CosS(this->skyFloatPhase) * 2500.0f) + this->picto.actor.world.rot.y; + this->picto.actor.shape.rot.z = (s32)(Math_SinS(this->skyFloatPhase) * 2500.0f) + this->picto.actor.world.rot.z; + if (this->timer == 25) { + this->forceEyesShut = false; + this->eyeTexIndex = TINGLE_EYETEX_OPEN; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_FALL_ONCE); + } else if (this->timer == 29) { + this->picto.actor.velocity.y = -12.0f; + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A62B30.s") + if (this->picto.actor.bgCheckFlags & BGCHECKFLAG_GROUND_TOUCH) { + Actor_PlaySfx(&this->picto.actor, NA_SE_VO_TIVO01); + Actor_PlaySfx(&this->picto.actor, NA_SE_EN_GERUDOFT_DOWN); + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A62B78.s") + if (this->picto.actor.bgCheckFlags & BGCHECKFLAG_GROUND) { + this->picto.actor.colChkInfo.health = 0; + if (this->timer > 80) { + this->picto.actor.shape.yOffset = 0.0f; + EnBal_SetupGroundIdle(this); + } else if (this->timer == 30) { + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_LAND); + this->picto.actor.shape.rot = this->picto.actor.world.rot; + Actor_SpawnFloorDustRing(play, &this->picto.actor, &worldPos, 10.0f, 30, 5.0f, 0, 0, 0); + this->timer++; + } else { + if ((play->gameplayFrames % 2) != 0) { + this->picto.actor.shape.yOffset = 20.0f; + } else { + this->picto.actor.shape.yOffset = 0.0f; + } + this->timer++; + } + } else if (this->picto.actor.velocity.y < 0.0f) { + Actor_PlaySfx_Flagged(&this->picto.actor, NA_SE_EV_HONEYCOMB_FALL - SFX_FLAG); + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A62CF0.s") + this->skyFloatPhase += 0xBB8; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A62DCC.s") +void EnBal_SetupInflateBalloon(EnBal* this) { + Vec3f scale = { 0.0f, 0.0f, 0.0f }; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A62ED0.s") + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_FLOAT_IDLE); + this->balloonAction = TINGLE_BALLOON_ACTION_INFLATE; + this->watchTarget = TINGLE_WATCH_TARGET_NONE; + this->forceEyesShut = false; + this->eyeTexIndex = TINGLE_EYETEX_OPEN; + this->balloonScale = scale; + this->skyFloatPhase = 0; + this->timer = 0; + this->actionFunc = EnBal_InflateBalloon; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A62FAC.s") +void EnBal_InflateBalloon(EnBal* this, PlayState* play) { + f32 scale; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A63158.s") + if (Math_SmoothStepToF(&this->balloonScale.x, 1.0f, 0.05f, 0.03f, 0.01f) == 0.0f) { + EnBal_SetupFloatUp(this); + } + EnBal_EmitDustPuff(this, play); + Actor_PlaySfx_Flagged(&this->picto.actor, NA_SE_EV_BALLOON_SWELL - SFX_FLAG); + scale = this->balloonScale.x * 1.1f; + this->balloonScale.z = scale; + this->balloonScale.y = scale; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A634B4.s") +void EnBal_SetupFloatUp(EnBal* this) { + this->balloonAction = TINGLE_BALLOON_ACTION_RISE; + EnBal_SetMainColliderToBalloon(this); + this->actionFunc = EnBal_FloatUp; + this->picto.actor.gravity = 0.0f; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A634C8.s") +void EnBal_FloatUp(EnBal* this, PlayState* play) { + f32 cosFactor; + f32 sinFactor; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A635DC.s") + if (this->timer < 500) { + cosFactor = (Math_CosS(this->skyFloatPhase) * 0.1f) + 1.0f; + this->balloonScale.z = cosFactor; + this->balloonScale.y = cosFactor; + sinFactor = (Math_SinS(this->skyFloatPhase) * 0.1f) + 1.0f; + this->balloonScale.x = SQ(sinFactor); + this->picto.actor.world.pos.y *= 500 - this->timer; + this->picto.actor.world.pos.y += + this->timer * (this->picto.actor.home.pos.y + (50.0f * Math_SinS(this->skyFloatPhase))); + this->picto.actor.world.pos.y *= 0.002f; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A635F0.s") + if (this->timer < 100) { + this->timer++; + } else { + this->timer += 10; + } + this->skyFloatPhase += 0x320; + } else { + EnBal_SetupFloatIdle(this); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A636AC.s") +void EnBal_SetupGroundIdle(EnBal* this) { + if (this->actionFunc == EnBal_Fall) { + this->forceEyesShut = false; + this->eyeTexIndex = TINGLE_EYETEX_OPEN; + this->idleAnimStage = TINGLE_IDLESTAGE_WAIT; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_IDLE); + } else if (Rand_Next() & 1) { + this->idleAnimStage = TINGLE_IDLESTAGE_ACTIVITY; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_TALK); + } else { + this->idleAnimStage = TINGLE_IDLESTAGE_ACTIVITY; + this->forceEyesShut = true; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_SPIN); + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A637FC.s") + this->watchTarget = TINGLE_WATCH_TARGET_NONE; + if (this->locationMapId == TINGLE_MAP_CLOCK_TOWN) { + if (!gSaveContext.save.saveInfo.playerData.isMagicAcquired) { + // Effectively turn off reinflation timer by setting above 300 + this->timer = 301; + } else if (this->inflateEarly == true) { + // If this field is set, reduce time to balloon reinflation. + this->inflateEarly = false; + this->timer = 290; + } else { + this->timer = 0; + } + } else { + this->timer = 0; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A63884.s") + this->actionFunc = EnBal_GroundIdle; +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/EnBal_Update.s") +void EnBal_GroundIdle(EnBal* this, PlayState* play) { + Player* player = GET_PLAYER(play); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A63A10.s") + if (this->timer == 300) { + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + EnBal_SetupInflateBalloon(this); + return; + } + } else if (this->timer < 300) { + this->timer++; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/func_80A63B94.s") + if (Actor_ProcessTalkRequest(&this->picto.actor, &play->state)) { + this->forceEyesShut = false; + this->eyeTexIndex = TINGLE_EYETEX_OPEN; + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_TALKED_TINGLE)) { + if (GET_PLAYER_FORM == EnBal_GetRecognizedPlayerForm()) { + this->watchTarget = TINGLE_WATCH_TARGET_PLAYER; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_TALK); + Message_StartTextbox(play, 0x1D0C, &this->picto.actor); + this->textId = 0x1D0C; + } else { + this->watchTarget = TINGLE_WATCH_TARGET_PLAYER; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_TALK); + Message_StartTextbox(play, 0x1D05, &this->picto.actor); + this->textId = 0x1D05; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Bal/EnBal_Draw.s") + if (!gSaveContext.save.saveInfo.playerData.isMagicAcquired) { + // Reinflation should be unreachable while player does not have magic + this->inflateEarly = true; + } + } else { + this->watchTarget = TINGLE_WATCH_TARGET_FAIRY; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_LAND); + Message_StartTextbox(play, 0x1D00, &this->picto.actor); + this->textId = 0x1D00; + + if (this->locationMapId == TINGLE_MAP_CLOCK_TOWN) { + this->inflateEarly = true; + } + } + EnBal_SetupTalk(this); + } else { + if ((this->picto.actor.xzDistToPlayer < 100.0f) && (this->actionFunc != EnBal_InflateBalloon)) { + if (this->idleAnimStage != TINGLE_IDLESTAGE_WAIT) { + this->watchTarget = TINGLE_WATCH_TARGET_PLAYER; + } else { + this->watchTarget = TINGLE_WATCH_TARGET_NONE; + } + + if (!(player->stateFlags1 & PLAYER_STATE1_800000) && !(player->actor.bgCheckFlags & BGCHECKFLAG_WATER) && + ((this->timer < 300) || (this->timer == 301))) { + Actor_OfferTalk(&this->picto.actor, play, 100.0f); + } + } else { + this->watchTarget = TINGLE_WATCH_TARGET_NONE; + } + + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + if (this->idleAnimStage == TINGLE_IDLESTAGE_PREP_WAIT) { + this->forceEyesShut = false; + this->eyeTexIndex = TINGLE_EYETEX_OPEN; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_IDLE); + this->idleAnimStage++; + } else if (this->idleAnimStage == TINGLE_IDLESTAGE_WAIT) { + if (Rand_Next() & 1) { + this->forceEyesShut = false; + this->eyeTexIndex = TINGLE_EYETEX_OPEN; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_TALK); + } else { + this->forceEyesShut = true; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_SPIN); + } + this->idleAnimStage = TINGLE_IDLESTAGE_ACTIVITY; + } else { + this->idleAnimStage++; + } + } else if ((this->idleAnimStage == TINGLE_IDLESTAGE_WAIT) && (Animation_OnFrame(&this->skelAnime, 20.0f))) { + this->forceEyesShut = true; + } + } +} + +void EnBal_SetupTalk(EnBal* this) { + this->timer = 0; + this->actionFunc = EnBal_Talk; +} + +void EnBal_Talk(EnBal* this, PlayState* play) { + Player* player = GET_PLAYER(play); + + player->stateFlags2 |= PLAYER_STATE2_100000; + this->isTalking = false; + + switch (Message_GetState(&play->msgCtx)) { + case TEXT_STATE_NONE: + case TEXT_STATE_1: + break; + + case TEXT_STATE_CLOSING: + break; + + case TEXT_STATE_3: + if (this->textId != 0x1D10) { + this->isTalking = true; + } + break; + + case TEXT_STATE_CHOICE: + EnBal_TryPurchaseMap(this, play); + break; + + case TEXT_STATE_5: + EnBal_HandleConversation(this, play); + break; + + case TEXT_STATE_DONE: + if (Message_ShouldAdvance(play)) { + EnBal_SetupGroundIdle(this); + } + break; + + case TEXT_STATE_10: + if (Message_ShouldAdvance(play) && (this->textId == 0x1D08)) { + this->forceEyesShut = false; + this->eyeTexIndex = TINGLE_EYETEX_OPEN; + } + break; + + default: + break; + } + + if (this->textId == 0x1D07) { + if ((this->skelAnime.curFrame > 29.0f) && (this->skelAnime.curFrame < 33.0f)) { + if (Animation_OnFrame(&this->skelAnime, 30.0f)) { + Actor_PlaySfx(&this->picto.actor, NA_SE_EV_CHINCLE_SPELL_EFFECT); + } + EnBal_ThrowMagicSparkles(this, play); + } + if (Animation_OnFrame(&this->skelAnime, 35.0f)) { + this->forceEyesShut = false; + this->eyeTexIndex = TINGLE_EYETEX_OPEN; + } + } +} + +void EnBal_UpdateShadow(EnBal* this) { + f32 homeY; + f32 worldY; + f32 shadowScale; + + if ((this->actionFunc != EnBal_GroundIdle) && (this->actionFunc != EnBal_FloatIdle)) { + homeY = this->picto.actor.home.pos.y - this->picto.actor.floorHeight; + worldY = this->picto.actor.world.pos.y - this->picto.actor.floorHeight; + shadowScale = (homeY - worldY) / homeY; + shadowScale = CLAMP_MIN(shadowScale, 0.0f); + this->picto.actor.shape.shadowScale = 12.0f * shadowScale; + this->picto.actor.shape.shadowAlpha = 255.0f * shadowScale; + } +} + +void EnBal_SetRecognizedPlayerForm(void) { + switch (gSaveContext.save.playerForm) { + case PLAYER_FORM_HUMAN: + SET_WEEKEVENTREG(WEEKEVENTREG_TINGLE_RECOGNIZED_PLAYER_FORM_LOW_BIT); + SET_WEEKEVENTREG(WEEKEVENTREG_TINGLE_RECOGNIZED_PLAYER_FORM_HIGH_BIT); + break; + + case PLAYER_FORM_DEKU: + SET_WEEKEVENTREG(WEEKEVENTREG_TINGLE_RECOGNIZED_PLAYER_FORM_LOW_BIT); + break; + + case PLAYER_FORM_GORON: + SET_WEEKEVENTREG(WEEKEVENTREG_TINGLE_RECOGNIZED_PLAYER_FORM_HIGH_BIT); + break; + + case PLAYER_FORM_ZORA: + break; + + default: + break; + } +} + +PlayerTransformation EnBal_GetRecognizedPlayerForm(void) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_RECOGNIZED_PLAYER_FORM_LOW_BIT)) { + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_RECOGNIZED_PLAYER_FORM_HIGH_BIT)) { + return PLAYER_FORM_HUMAN; + } + + return PLAYER_FORM_DEKU; + } + + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_RECOGNIZED_PLAYER_FORM_HIGH_BIT)) { + return PLAYER_FORM_GORON; + } + + return PLAYER_FORM_ZORA; +} + +void EnBal_ThrowMagicSparkles(EnBal* this, PlayState* play) { + static Vec3f sSparkleVelocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f sSparkleAccel = { 0.0f, 0.0f, 0.0f }; + static Color_RGBA8 sSparklePrimColor = { 255, 255, 255, 255 }; + static Color_RGBA8 sSparkleEnvColor = { 100, 200, 0, 255 }; + s32 i; + Vec3f pos = this->picto.actor.world.pos; + + pos.y += 30.0f; + pos.x += 20.0f * Math_SinS(this->picto.actor.shape.rot.y); + pos.z += 20.0f * Math_CosS(this->picto.actor.shape.rot.y); + + sSparkleAccel.y = -0.4f; + sSparkleVelocity.y = 5.5f; + + for (i = 0; i < 20; i++) { + sSparkleVelocity.x = Rand_Centered() * 3.0f; + sSparkleVelocity.z = Rand_Centered() * 3.0f; + EffectSsKirakira_SpawnDispersed(play, &pos, &sSparkleVelocity, &sSparkleAccel, &sSparklePrimColor, + &sSparkleEnvColor, 2500, 40); + } +} + +void EnBal_EmitDustPuff(EnBal* this, PlayState* play) { + static Vec3f sDustVelocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f sDustAccel = { 0.0f, 0.0f, 0.0f }; + static Color_RGBA8 sDustPrimColor = { 255, 255, 255, 255 }; + static Color_RGBA8 sDustEnvColor = { 198, 198, 198, 255 }; + Vec3f pos = this->picto.actor.world.pos; + + pos.y += 40.0f; + sDustVelocity.x = Rand_CenteredFloat(10.0f); + sDustVelocity.z = Rand_CenteredFloat(10.0f); + sDustVelocity.y = Rand_ZeroFloat(5.0f); + func_800B0EB0(play, &pos, &sDustVelocity, &sDustAccel, &sDustPrimColor, &sDustEnvColor, 150, 40, 15); +} + +static s32 sGetItemPending = false; +static Vec3f sFocusPosMultiplier = { 1000.0f, 0.0f, 0.0f }; +static TexturePtr sEyeTextures[] = { gTingleEyeOpenTex, gTingleEyeClosedTex }; + +s32 EnBal_CheckIfMapUnlocked(EnBal* this, PlayState* play) { + this->purchaseMapId = sBuyMapOptions[this->locationMapId][play->msgCtx.choiceIndex]; + switch (this->purchaseMapId) { + case TINGLE_MAP_CLOCK_TOWN: + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_CLOCK_TOWN)) { + return true; + } + break; + + case TINGLE_MAP_WOODFALL: + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_WOODFALL)) { + return true; + } + break; + + case TINGLE_MAP_SNOWHEAD: + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_SNOWHEAD)) { + return true; + } + break; + + case TINGLE_MAP_ROMANI_RANCH: + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_ROMANI_RANCH)) { + return true; + } + break; + + case TINGLE_MAP_GREAT_BAY: + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_GREAT_BAY)) { + return true; + } + break; + + case TINGLE_MAP_STONE_TOWER: + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_STONE_TOWER)) { + return true; + } + break; + + default: + break; + } + return false; +} + +void EnBal_UnlockSelectedAreaMap(EnBal* this) { + Inventory_SetWorldMapCloudVisibility(this->purchaseMapId); + switch (this->purchaseMapId) { + case TINGLE_MAP_CLOCK_TOWN: + SET_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_CLOCK_TOWN); + break; + + case TINGLE_MAP_WOODFALL: + SET_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_WOODFALL); + break; + + case TINGLE_MAP_SNOWHEAD: + SET_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_SNOWHEAD); + break; + + case TINGLE_MAP_ROMANI_RANCH: + SET_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_ROMANI_RANCH); + break; + + case TINGLE_MAP_GREAT_BAY: + SET_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_GREAT_BAY); + break; + + case TINGLE_MAP_STONE_TOWER: + SET_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_STONE_TOWER); + break; + + default: + break; + } +} + +void EnBal_TryPurchaseMap(EnBal* this, PlayState* play) { + Player* player = GET_PLAYER(play); + s32 price; + + if (Message_ShouldAdvance(play)) { + if (play->msgCtx.choiceIndex != TINGLE_MAPCHOICE_CANCEL) { + // Get price depending on which map player wants to buy + if (play->msgCtx.choiceIndex == TINGLE_MAPCHOICE_PROXIMAL) { + price = play->msgCtx.unk1206C; + } else { + price = play->msgCtx.unk12070; + } + + if (gSaveContext.save.saveInfo.playerData.rupees < price) { + // Can't buy map because player doesn't have the money + Audio_PlaySfx(NA_SE_SY_ERROR); + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_TALK); + Message_StartTextbox(play, 0x1D0A, &this->picto.actor); + this->textId = 0x1D0A; + } else if (EnBal_CheckIfMapUnlocked(this, play)) { + // Can't buy map because player already has it + Audio_PlaySfx(NA_SE_SY_ERROR); + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_TALK); + Message_StartTextbox(play, 0x1D09, &this->picto.actor); + this->textId = 0x1D09; + } else { + // Proceed with map purchase + Audio_PlaySfx_MessageDecide(); + Rupees_ChangeBy(-price); + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_MAGIC_REVERSE); + this->forceEyesShut = true; + Message_StartTextbox(play, 0x1D0B, &this->picto.actor); + this->textId = 0x1D0B; + EnBal_UnlockSelectedAreaMap(this); + player->stateFlags1 |= PLAYER_STATE1_20; + EnBal_SetupOfferGetItem(this); + } + } else { + // Cancel + Audio_PlaySfx_MessageCancel(); + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_TALK); + Message_StartTextbox(play, 0x1D06, &this->picto.actor); + this->textId = 0x1D06; + } + } +} + +void EnBal_HandleConversation(EnBal* this, PlayState* play) { + if (((this->textId != 0x1D07) || Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) && + (Message_ShouldAdvance(play))) { + switch (this->textId) { + case 0x1D00: + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_TALK); + Message_StartTextbox(play, 0x1D01, &this->picto.actor); + this->textId = 0x1D01; + break; + + case 0x1D01: + this->watchTarget = TINGLE_WATCH_TARGET_PLAYER; + Message_StartTextbox(play, 0x1D02, &this->picto.actor); + this->textId = 0x1D02; + break; + + case 0x1D02: + this->forceEyesShut = true; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_HAPPY_DANCE_LOOP); + Message_StartTextbox(play, 0x1D03, &this->picto.actor); + this->textId = 0x1D03; + break; + + case 0x1D03: + case 0x1D0D: + this->forceEyesShut = false; + this->eyeTexIndex = TINGLE_EYETEX_OPEN; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_TALK); + Message_StartTextbox(play, 0x1D04, &this->picto.actor); + this->textId = 0x1D04; + break; + + case 0x1D04: + this->watchTarget = TINGLE_WATCH_TARGET_PLAYER; + switch (this->locationMapId) { + case TINGLE_MAP_CLOCK_TOWN: + Message_StartTextbox(play, 0x1D11, &this->picto.actor); + this->textId = 0x1D11; + break; + + case TINGLE_MAP_WOODFALL: + Message_StartTextbox(play, 0x1D12, &this->picto.actor); + this->textId = 0x1D12; + break; + + case TINGLE_MAP_SNOWHEAD: + Message_StartTextbox(play, 0x1D13, &this->picto.actor); + this->textId = 0x1D13; + break; + + case TINGLE_MAP_ROMANI_RANCH: + Message_StartTextbox(play, 0x1D14, &this->picto.actor); + this->textId = 0x1D14; + break; + + case TINGLE_MAP_GREAT_BAY: + Message_StartTextbox(play, 0x1D15, &this->picto.actor); + this->textId = 0x1D15; + break; + + case TINGLE_MAP_STONE_TOWER: + Message_StartTextbox(play, 0x1D16, &this->picto.actor); + this->textId = 0x1D16; + break; + + default: + Message_StartTextbox(play, 0x1D11, &this->picto.actor); + this->textId = 0x1D11; + break; + } + break; + + case 0x1D05: + case 0x1D0C: + this->watchTarget = TINGLE_WATCH_TARGET_FAIRY; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_HAPPY_DANCE_LOOP); + Message_StartTextbox(play, 0x1D0D, &this->picto.actor); + this->textId = 0x1D0D; + break; + + case 0x1D06: + case 0x1D17: + this->watchTarget = TINGLE_WATCH_TARGET_NONE; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_MAGIC); + this->forceEyesShut = true; + Message_StartTextbox(play, 0x1D07, &this->picto.actor); + this->textId = 0x1D07; + break; + + case 0x1D07: + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_TALKED_TINGLE)) { + Message_CloseTextbox(play); + EnBal_SetupGroundIdle(this); + } else { + this->forceEyesShut = true; + this->watchTarget = TINGLE_WATCH_TARGET_PLAYER; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_TALK); + Message_StartTextbox(play, 0x1D08, &this->picto.actor); + this->textId = 0x1D08; + SET_WEEKEVENTREG(WEEKEVENTREG_TALKED_TINGLE); + EnBal_SetRecognizedPlayerForm(); + } + break; + + case 0x1D08: + case 0x1D09: + case 0x1D0A: + Message_CloseTextbox(play); + EnBal_SetupGroundIdle(this); + break; + + default: + break; + } + } +} + +void EnBal_SetupOfferGetItem(EnBal* this) { + this->actionFunc = EnBal_OfferGetItem; +} + +void EnBal_OfferGetItem(EnBal* this, PlayState* play) { + GetItemId mapGetItemId; + + if ((Message_GetState(&play->msgCtx) == TEXT_STATE_5) && Message_ShouldAdvance(play)) { + Message_CloseTextbox(play); + sGetItemPending = true; + } + + if ((sGetItemPending == true) && Actor_HasParent(&this->picto.actor, play)) { + this->picto.actor.parent = NULL; + EnBal_SetupThankYou(this); + sGetItemPending = false; + } else { + switch (this->purchaseMapId) { + case TINGLE_MAP_CLOCK_TOWN: + mapGetItemId = GI_TINGLE_MAP_CLOCK_TOWN; + break; + + case TINGLE_MAP_WOODFALL: + mapGetItemId = GI_TINGLE_MAP_WOODFALL; + break; + + case TINGLE_MAP_SNOWHEAD: + mapGetItemId = GI_TINGLE_MAP_SNOWHEAD; + break; + + case TINGLE_MAP_ROMANI_RANCH: + mapGetItemId = GI_TINGLE_MAP_ROMANI_RANCH; + break; + + case TINGLE_MAP_GREAT_BAY: + mapGetItemId = GI_TINGLE_MAP_GREAT_BAY; + break; + + case TINGLE_MAP_STONE_TOWER: + mapGetItemId = GI_TINGLE_MAP_STONE_TOWER; + break; + + default: + mapGetItemId = GI_TINGLE_MAP_CLOCK_TOWN; + break; + } + Actor_OfferGetItem(&this->picto.actor, play, mapGetItemId, 500.0f, 100.0f); + } +} + +void EnBal_SetupThankYou(EnBal* this) { + this->actionFunc = EnBal_ThankYou; +} + +void EnBal_ThankYou(EnBal* this, PlayState* play) { + Player* player = GET_PLAYER(play); + + if (Actor_ProcessTalkRequest(&this->picto.actor, &play->state)) { + player->stateFlags1 &= ~PLAYER_STATE1_20; + Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, TINGLE_ANIM_TWIST); + this->forceEyesShut = false; + this->eyeTexIndex = TINGLE_EYETEX_OPEN; + Message_StartTextbox(play, 0x1D17, &this->picto.actor); + this->textId = 0x1D17; + this->picto.actor.flags &= ~ACTOR_FLAG_10000; + EnBal_SetupTalk(this); + } else { + Actor_OfferTalkExchangeEquiCylinder(&this->picto.actor, play, 200.0f, PLAYER_IA_MINUS1); + } +} + +void EnBal_UpdateHead(EnBal* this, PlayState* play) { + Player* player; + + if (this->watchTarget == TINGLE_WATCH_TARGET_PLAYER) { + if ((this->skelAnime.animation != &gTingleIdleAnim) && (this->skelAnime.animation != &gTingleSpinAnim)) { + Actor_TrackPlayer(play, &this->picto.actor, &this->headRot, &this->torsoRot, this->picto.actor.focus.pos); + } else { + Actor_TrackNone(&this->headRot, &this->torsoRot); + } + } else if (this->watchTarget == TINGLE_WATCH_TARGET_FAIRY) { + Vec3f tatlPos; + + player = GET_PLAYER(play); + if (player->tatlActor != NULL) { + tatlPos = player->tatlActor->world.pos; + Actor_TrackPoint(&this->picto.actor, &tatlPos, &this->headRot, &this->torsoRot); + } + } else { + Actor_TrackNone(&this->headRot, &this->torsoRot); + } + + if (this->forceEyesShut == true) { + this->eyeTexIndex = TINGLE_EYETEX_CLOSED; + } else if (this->eyeTimer > 3) { + this->eyeTimer--; + } else if (this->eyeTimer != 0) { + this->eyeTexIndex = TINGLE_EYETEX_CLOSED; + this->eyeTimer--; + } else { + this->eyeTexIndex = TINGLE_EYETEX_OPEN; + this->eyeTimer = 60; + } +} + +void EnBal_UpdateCollision(EnBal* this, PlayState* play) { + if ((this->actionFunc == EnBal_FloatIdle) || ((this->actionFunc == EnBal_FloatUp) && (this->timer > 50))) { + CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); + } + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); +} + +void EnBal_TryBalloonPopped(EnBal* this, PlayState* play) { + if (this->collider.base.acFlags & AC_HIT) { + Actor_Spawn(&play->actorCtx, play, ACTOR_EN_CLEAR_TAG, this->picto.actor.world.pos.x, + this->picto.actor.world.pos.y + 100.0f, this->picto.actor.world.pos.z, 0xFF, 0xFF, 0xC8, + CLEAR_TAG_PARAMS(CLEAR_TAG_POP)); + this->collider.base.acFlags &= ~AC_HIT; + EnBal_SetupPopBalloon(this); + } +} + +void EnBal_Update(Actor* thisx, PlayState* play) { + EnBal* this = THIS; + + this->actionFunc(this, play); + EnBal_TryBalloonPopped(this, play); + EnBal_UpdateCollision(this, play); + SkelAnime_Update(&this->skelAnime); + if (this->actionFunc != EnBal_FloatIdle) { + Actor_MoveWithGravity(&this->picto.actor); + Actor_UpdateBgCheckInfo(play, &this->picto.actor, 32.0f, 30.0f, 60.0f, + UPDBGCHECKINFO_FLAG_2 | UPDBGCHECKINFO_FLAG_4); + } + EnBal_UpdateHead(this, play); + if ((this->actionFunc == EnBal_Talk) && (this->textId != 0x1D10)) { + Math_SmoothStepToS(&this->picto.actor.shape.rot.y, this->picto.actor.yawTowardsPlayer, 5, 0x1000, 0x100); + this->picto.actor.world.rot.y = this->picto.actor.shape.rot.y; + } + EnBal_UpdateShadow(this); +} + +s32 EnBal_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, Actor* thisx) { + EnBal* this = THIS; + Vec3s balloonRot; + + if (limbIndex == TINGLE_LIMB_BALLOON) { + if ((this->balloonAction == TINGLE_BALLOON_ACTION_NONE) || + (this->balloonAction == TINGLE_BALLOON_ACTION_INFLATE) || + (this->balloonAction == TINGLE_BALLOON_ACTION_RISE)) { + balloonRot.x = Math_SinS(this->timer) * 0xE38; + balloonRot.z = Math_CosS(this->timer) * 0xE38; + Matrix_RotateZYX(balloonRot.x, 0, balloonRot.z, MTXMODE_APPLY); + Matrix_Scale(this->balloonScale.x, this->balloonScale.y, this->balloonScale.z, MTXMODE_APPLY); + Matrix_RotateZS(-balloonRot.z, MTXMODE_APPLY); + Matrix_RotateXS(-balloonRot.x, MTXMODE_APPLY); + } else { + *dList = NULL; + } + } + + if (limbIndex == TINGLE_LIMB_HEAD) { + rot->x += this->headRot.y; + rot->y -= this->headRot.x; + if ((this->isTalking == true) && ((play->state.frames & 2) == 0)) { + Matrix_Translate(20.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + } + + return false; +} + +void EnBal_PostLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot, Actor* thisx) { + EnBal* this = THIS; + + Collider_UpdateSpheres(limbIndex, &this->collider); + if (limbIndex == TINGLE_LIMB_HEAD) { + Matrix_MultVec3f(&sFocusPosMultiplier, &this->picto.actor.focus.pos); + } +} + +void EnBal_Draw(Actor* thisx, PlayState* play) { + EnBal* this = THIS; + + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL25_Opa(play->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 8, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeTexIndex])); + SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnBal_OverrideLimbDraw, EnBal_PostLimbDraw, &this->picto.actor); + + CLOSE_DISPS(play->state.gfxCtx); +} diff --git a/src/overlays/actors/ovl_En_Bal/z_en_bal.h b/src/overlays/actors/ovl_En_Bal/z_en_bal.h index a64ef195d4..b8c6a9f496 100644 --- a/src/overlays/actors/ovl_En_Bal/z_en_bal.h +++ b/src/overlays/actors/ovl_En_Bal/z_en_bal.h @@ -3,6 +3,9 @@ #include "global.h" #include "z64snap.h" +#include "objects/object_bal/object_bal.h" + +#define TINGLE_GET_MAP_ID(thisx) ((thisx)->params & 0xFF) struct EnBal; @@ -10,9 +13,30 @@ typedef void (*EnBalActionFunc)(struct EnBal*, PlayState*); typedef struct EnBal { /* 0x000 */ PictoActor picto; - /* 0x148 */ char unk_148[0x44]; + /* 0x148 */ SkelAnime skelAnime; /* 0x18C */ EnBalActionFunc actionFunc; - /* 0x190 */ char unk_190[0x224]; -} EnBal; // size = 0x3B4 + /* 0x190 */ ColliderJntSph collider; + /* 0x1B0 */ ColliderJntSphElement colliderElements[1]; + /* 0x1F0 */ Vec3f balloonScale; + /* 0x1FC */ s32 eyeTexIndex; + /* 0x200 */ Vec3s jointTable[TINGLE_LIMB_MAX]; + /* 0x2C6 */ Vec3s morphTable[TINGLE_LIMB_MAX]; + /* 0x38C */ Vec3s headRot; + /* 0x392 */ Vec3s torsoRot; + /* 0x398 */ s16 eyeTimer; + /* 0x39A */ s16 forceEyesShut; + /* 0x39C */ s16 watchTarget; + /* 0x39E */ s16 skyFloatPhase; + /* 0x3A0 */ s16 textId; + /* 0x3A2 */ UNK_TYPE1 unk3A2[0x2]; + /* 0x3A4 */ s16 balloonAction; + /* 0x3A6 */ s16 idleAnimStage; + /* 0x3A8 */ s16 timer; + /* 0x3AA */ UNK_TYPE1 unk3AA[0x2]; + /* 0x3AC */ s16 purchaseMapId; + /* 0x3AE */ s16 locationMapId; + /* 0x3B0 */ s16 inflateEarly; + /* 0x3B2 */ u8 isTalking; +} EnBal; /* size = 0x3B4 */ #endif // Z_EN_BAL_H diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index cf6a9a7af2..1b8b9880a8 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -10758,42 +10758,42 @@ 0x80A614C4:("EnDyExtra_Draw",), 0x80A61810:("EnBal_Init",), 0x80A619EC:("EnBal_Destroy",), - 0x80A61A18:("func_80A61A18",), - 0x80A61A44:("func_80A61A44",), + 0x80A61A18:("EnBal_SetMainColliderToBalloon",), + 0x80A61A44:("EnBal_SetMainColliderToHead",), 0x80A61A6C:("EnBal_ValidatePictograph",), - 0x80A61ADC:("func_80A61ADC",), - 0x80A61B5C:("func_80A61B5C",), - 0x80A61C9C:("func_80A61C9C",), - 0x80A61CE4:("func_80A61CE4",), - 0x80A61DF8:("func_80A61DF8",), - 0x80A61E5C:("func_80A61E5C",), - 0x80A62128:("func_80A62128",), - 0x80A621C4:("func_80A621C4",), - 0x80A62260:("func_80A62260",), - 0x80A622A0:("func_80A622A0",), - 0x80A623CC:("func_80A623CC",), - 0x80A624DC:("func_80A624DC",), - 0x80A62818:("func_80A62818",), - 0x80A62830:("func_80A62830",), - 0x80A629C4:("func_80A629C4",), - 0x80A62AC4:("func_80A62AC4",), - 0x80A62B30:("func_80A62B30",), - 0x80A62B78:("func_80A62B78",), - 0x80A62CF0:("func_80A62CF0",), - 0x80A62DCC:("func_80A62DCC",), - 0x80A62ED0:("func_80A62ED0",), - 0x80A62FAC:("func_80A62FAC",), - 0x80A63158:("func_80A63158",), - 0x80A634B4:("func_80A634B4",), - 0x80A634C8:("func_80A634C8",), - 0x80A635DC:("func_80A635DC",), - 0x80A635F0:("func_80A635F0",), - 0x80A636AC:("func_80A636AC",), - 0x80A637FC:("func_80A637FC",), - 0x80A63884:("func_80A63884",), + 0x80A61ADC:("EnBal_SetupFloatIdle",), + 0x80A61B5C:("EnBal_FloatIdle",), + 0x80A61C9C:("EnBal_SetupPopBalloon",), + 0x80A61CE4:("EnBal_PopBalloon",), + 0x80A61DF8:("EnBal_SetupFall",), + 0x80A61E5C:("EnBal_Fall",), + 0x80A62128:("EnBal_SetupInflateBalloon",), + 0x80A621C4:("EnBal_InflateBalloon",), + 0x80A62260:("EnBal_SetupFloatUp",), + 0x80A622A0:("EnBal_FloatUp",), + 0x80A623CC:("EnBal_SetupGroundIdle",), + 0x80A624DC:("EnBal_GroundIdle",), + 0x80A62818:("EnBal_SetupTalk",), + 0x80A62830:("EnBal_Talk",), + 0x80A629C4:("EnBal_UpdateShadow",), + 0x80A62AC4:("EnBal_SetRecognizedPlayerForm",), + 0x80A62B30:("EnBal_GetRecognizedPlayerForm",), + 0x80A62B78:("EnBal_ThrowMagicSparkles",), + 0x80A62CF0:("EnBal_EmitDustPuff",), + 0x80A62DCC:("EnBal_CheckIfMapUnlocked",), + 0x80A62ED0:("EnBal_UnlockSelectedAreaMap",), + 0x80A62FAC:("EnBal_TryPurchaseMap",), + 0x80A63158:("EnBal_HandleConversation",), + 0x80A634B4:("EnBal_SetupOfferGetItem",), + 0x80A634C8:("EnBal_OfferGetItem",), + 0x80A635DC:("EnBal_SetupThankYou",), + 0x80A635F0:("EnBal_ThankYou",), + 0x80A636AC:("EnBal_UpdateHead",), + 0x80A637FC:("EnBal_UpdateCollision",), + 0x80A63884:("EnBal_TryBalloonPopped",), 0x80A63914:("EnBal_Update",), - 0x80A63A10:("func_80A63A10",), - 0x80A63B94:("func_80A63B94",), + 0x80A63A10:("EnBal_OverrideLimbDraw",), + 0x80A63B94:("EnBal_PostLimbDraw",), 0x80A63BEC:("EnBal_Draw",), 0x80A644A0:("EnGinkoMan_Init",), 0x80A64544:("EnGinkoMan_Destroy",), diff --git a/tools/disasm/variables.txt b/tools/disasm/variables.txt index 7cce846612..482947b96e 100644 --- a/tools/disasm/variables.txt +++ b/tools/disasm/variables.txt @@ -11779,30 +11779,25 @@ 0x80A61794:("D_80A61794","f32","",0x4), 0x80A61798:("D_80A61798","f32","",0x4), 0x80A6179C:("D_80A6179C","f32","",0x4), - 0x80A63CC0:("En_Bal_InitVars","UNK_TYPE1","",0x1), - 0x80A63CE0:("D_80A63CE0","UNK_TYPE1","",0x1), - 0x80A63D00:("D_80A63D00","UNK_PTR","",0x4), - 0x80A63D0C:("D_80A63D0C","UNK_TYPE1","",0x1), - 0x80A63D30:("D_80A63D30","UNK_TYPE1","",0x1), - 0x80A63D40:("D_80A63D40","UNK_TYPE1","",0x1), - 0x80A63D58:("D_80A63D58","UNK_PTR","",0x4), - 0x80A63EA8:("D_80A63EA8","UNK_TYPE4","",0x4), - 0x80A63EB4:("D_80A63EB4","f32","",0x4), - 0x80A63EB8:("D_80A63EB8","f32","",0x4), - 0x80A63EBC:("D_80A63EBC","f32","",0x4), - 0x80A63EC0:("D_80A63EC0","UNK_TYPE1","",0x1), - 0x80A63EC4:("D_80A63EC4","f32","",0x4), - 0x80A63ECC:("D_80A63ECC","UNK_TYPE1","",0x1), - 0x80A63ED0:("D_80A63ED0","UNK_TYPE1","",0x1), - 0x80A63ED4:("D_80A63ED4","f32","",0x4), - 0x80A63ED8:("D_80A63ED8","f32","",0x4), - 0x80A63EDC:("D_80A63EDC","f32","",0x4), - 0x80A63EE0:("D_80A63EE0","UNK_TYPE1","",0x1), - 0x80A63EEC:("D_80A63EEC","UNK_TYPE1","",0x1), - 0x80A63EF0:("D_80A63EF0","UNK_TYPE1","",0x1), - 0x80A63EF4:("D_80A63EF4","UNK_TYPE4","",0x4), - 0x80A63EF8:("D_80A63EF8","UNK_TYPE1","",0x1), - 0x80A63F04:("D_80A63F04","UNK_TYPE1","",0x1), + 0x80A63CC0:("En_Bal_InitVars","ActorInit","",0x1), + 0x80A63CE0:("sDamageTable","DamageTable","",0x1), + 0x80A63D00:("sColChkInfoInit","CollisionCheckInfoInit2","",0x1), + 0x80A63D0C:("sJntSphElementsInit","ColliderJntSphElementInit","",0x1), + 0x80A63D30:("sJntSphInit","ColliderJntSphInit","",0x1), + 0x80A63D40:("sBuyMapOptions","s16","",0xC), + 0x80A63D58:("sAnimationInfo","AnimationInfo","",0xE), + 0x80A63EA8:("sInitBalloonScale","Vec3f","",0x1), + 0x80A63EB4:("sSparkleVelocity","Vec3f","",0x1), + 0x80A63EC0:("sSparkleAccel","Vec3f","",0x1), + 0x80A63ECC:("sSparklePrimColor","Color_RGBA8","",0x1), + 0x80A63ED0:("sSparkleEnvColor","Color_RGBA8","",0x1), + 0x80A63ED4:("sDustVelocity","Vec3f","",0x1), + 0x80A63EE0:("sDustAccel","Vec3f","",0x1), + 0x80A63EEC:("sDustPrimColor","Color_RGBA8","",0x1), + 0x80A63EF0:("sDustEnvColor","Color_RGBA8","",0x1), + 0x80A63EF4:("sGetItemPending","s32","",0x1), + 0x80A63EF8:("sFocusPosMultiplier","Vec3f","",0x1), + 0x80A63F04:("sEyeTextures","TexturePtr","",0x2), 0x80A63F10:("D_80A63F10","f32","",0x4), 0x80A63F14:("D_80A63F14","f32","",0x4), 0x80A63F18:("D_80A63F18","f32","",0x4), diff --git a/tools/weekeventregconvert.py b/tools/weekeventregconvert.py index 7e8c183a1c..0bde4a5ce9 100755 --- a/tools/weekeventregconvert.py +++ b/tools/weekeventregconvert.py @@ -84,7 +84,7 @@ weekEventReg = { ( 9 << 8) | 0x20: "WEEKEVENTREG_09_20", ( 9 << 8) | 0x40: "WEEKEVENTREG_09_40", ( 9 << 8) | 0x80: "WEEKEVENTREG_09_80", - (10 << 8) | 0x01: "WEEKEVENTREG_10_01", + (10 << 8) | 0x01: "WEEKEVENTREG_TALKED_TINGLE", (10 << 8) | 0x02: "WEEKEVENTREG_10_02", (10 << 8) | 0x04: "WEEKEVENTREG_10_04", (10 << 8) | 0x08: "WEEKEVENTREG_10_08", @@ -284,12 +284,12 @@ weekEventReg = { (34 << 8) | 0x20: "WEEKEVENTREG_34_20", (34 << 8) | 0x40: "WEEKEVENTREG_RECEIVED_MASK_OF_TRUTH", (34 << 8) | 0x80: "WEEKEVENTREG_34_80", - (35 << 8) | 0x01: "WEEKEVENTREG_35_01", - (35 << 8) | 0x02: "WEEKEVENTREG_35_02", - (35 << 8) | 0x04: "WEEKEVENTREG_35_04", - (35 << 8) | 0x08: "WEEKEVENTREG_35_08", - (35 << 8) | 0x10: "WEEKEVENTREG_35_10", - (35 << 8) | 0x20: "WEEKEVENTREG_35_20", + (35 << 8) | 0x01: "WEEKEVENTREG_TINGLE_MAP_BOUGHT_CLOCK_TOWN", + (35 << 8) | 0x02: "WEEKEVENTREG_TINGLE_MAP_BOUGHT_WOODFALL", + (35 << 8) | 0x04: "WEEKEVENTREG_TINGLE_MAP_BOUGHT_SNOWHEAD", + (35 << 8) | 0x08: "WEEKEVENTREG_TINGLE_MAP_BOUGHT_ROMANI_RANCH", + (35 << 8) | 0x10: "WEEKEVENTREG_TINGLE_MAP_BOUGHT_GREAT_BAY", + (35 << 8) | 0x20: "WEEKEVENTREG_TINGLE_MAP_BOUGHT_STONE_TOWER", (35 << 8) | 0x40: "WEEKEVENTREG_35_40", (35 << 8) | 0x80: "WEEKEVENTREG_35_80", (36 << 8) | 0x01: "WEEKEVENTREG_36_01", @@ -519,8 +519,8 @@ weekEventReg = { (64 << 8) | 0x01: "WEEKEVENTREG_64_01", (64 << 8) | 0x02: "WEEKEVENTREG_64_02", (64 << 8) | 0x04: "WEEKEVENTREG_64_04", - (64 << 8) | 0x08: "WEEKEVENTREG_64_08", - (64 << 8) | 0x10: "WEEKEVENTREG_64_10", + (64 << 8) | 0x08: "WEEKEVENTREG_TINGLE_RECOGNIZED_PLAYER_FORM_LOW_BIT", + (64 << 8) | 0x10: "WEEKEVENTREG_TINGLE_RECOGNIZED_PLAYER_FORM_HIGH_BIT", (64 << 8) | 0x20: "WEEKEVENTREG_64_20", (64 << 8) | 0x40: "WEEKEVENTREG_64_40", (64 << 8) | 0x80: "WEEKEVENTREG_TALKED_DOGGY_RACETRACK_OWNER_DAY_1",