diff --git a/assets/xml/objects/gameplay_dangeon_keep.xml b/assets/xml/objects/gameplay_dangeon_keep.xml index f3ad6195a9..9d5a9901ad 100644 --- a/assets/xml/objects/gameplay_dangeon_keep.xml +++ b/assets/xml/objects/gameplay_dangeon_keep.xml @@ -75,15 +75,19 @@ + + - - - - + + + + + + diff --git a/include/functions.h b/include/functions.h index 0e641326d1..c4bd9eef77 100644 --- a/include/functions.h +++ b/include/functions.h @@ -3582,7 +3582,7 @@ void Audio_SetCutsceneFlag(u8 flag); // void func_801A3FB4(void); // void func_801A3FFC(UNK_TYPE1 param_1); void audio_setBGM(u32 bgmID); -// void func_801A4058(void); +void func_801A4058(UNK_TYPE arg0); // void func_801A41C8(void); // void func_801A41F8(void); // void func_801A429C(void); diff --git a/spec b/spec index 16fb78b81c..66ab043e0b 100644 --- a/spec +++ b/spec @@ -2346,8 +2346,7 @@ beginseg name "ovl_En_Warp_tag" compress include "build/src/overlays/actors/ovl_En_Warp_tag/z_en_warp_tag.o" - include "build/data/ovl_En_Warp_tag/ovl_En_Warp_tag.data.o" - include "build/data/ovl_En_Warp_tag/ovl_En_Warp_tag.reloc.o" + include "build/src/overlays/actors/ovl_En_Warp_tag/ovl_En_Warp_tag_reloc.o" endseg beginseg diff --git a/src/overlays/actors/ovl_En_Warp_tag/z_en_warp_tag.c b/src/overlays/actors/ovl_En_Warp_tag/z_en_warp_tag.c index ce6367b877..5759b0a9bd 100644 --- a/src/overlays/actors/ovl_En_Warp_tag/z_en_warp_tag.c +++ b/src/overlays/actors/ovl_En_Warp_tag/z_en_warp_tag.c @@ -2,9 +2,11 @@ * File: z_en_warp_tag.c * Overlay: ovl_En_Warp_tag * Description: Warp to Trial Entrance + * if GoronTrial, has model: Uses GAMEPLAY_DANGEON_KEEP object assigned in EnWarptag_Init */ #include "z_en_warp_tag.h" +#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" #define FLAGS (ACTOR_FLAG_1 | ACTOR_FLAG_10 | ACTOR_FLAG_2000000 | ACTOR_FLAG_8000000) @@ -13,15 +15,15 @@ void EnWarptag_Init(Actor* thisx, GlobalContext* globalCtx); void EnWarptag_Destroy(Actor* thisx, GlobalContext* globalCtx); void EnWarptag_Update(Actor* thisx, GlobalContext* globalCtx); +void EnWarpTag_Draw(Actor* thisx, GlobalContext* globalCtx); -void func_809C085C(EnWarptag* this, GlobalContext* globalCtx); -void func_809C08E0(EnWarptag* this, GlobalContext* globalCtx); -void func_809C09A0(EnWarptag* this, GlobalContext* globalCtx); -void func_809C0A20(EnWarptag* this, GlobalContext* globalCtx); -void func_809C0AB4(EnWarptag* this, GlobalContext* globalCtx); -void func_809C0E30(EnWarptag* this, GlobalContext* globalCtx); +void EnWarpTag_CheckDungeonKeepObject(EnWarptag* this, GlobalContext* globalCtx); +void EnWarpTag_WaitForPlayer(EnWarptag* this, GlobalContext* globalCtx); +void EnWarpTag_Unused809C09A0(EnWarptag* this, GlobalContext* globalCtx); +void EnWarpTag_Unused809C0A20(EnWarptag* this, GlobalContext* globalCtx); +void EnWarpTag_RespawnPlayer(EnWarptag* this, GlobalContext* globalCtx); +void EnWarpTag_GrottoReturn(EnWarptag* this, GlobalContext* globalCtx); -#if 0 const ActorInit En_Warp_tag_InitVars = { ACTOR_EN_WARP_TAG, ACTORCAT_ITEMACTION, @@ -34,32 +36,238 @@ const ActorInit En_Warp_tag_InitVars = { (ActorFunc)NULL, }; -// static InitChainEntry sInitChain[] = { -static InitChainEntry D_809C1008[] = { +// this appears to be unused, as the code never accesses it in known vanilla cases +// these unknown values get passed to a unknown z_message function +u8 D_809C1000[] = { 0x28, 0x29, 0x2A, 0x2B, 0x2D, 0x2C, 0, 0 }; + +static InitChainEntry sInitChain[] = { ICHAIN_VEC3F(scale, 1, ICHAIN_CONTINUE), ICHAIN_VEC3S(shape.rot, 0, ICHAIN_STOP), }; -#endif +void EnWarptag_Init(Actor* thisx, GlobalContext* globalCtx) { + EnWarptag* this = THIS; -extern InitChainEntry D_809C1008[]; + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + Actor_SetFocus(&this->dyna.actor, 0.0f); -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Warp_tag/EnWarptag_Init.s") + if (GET_WARPTAG_3C0_MAX(thisx) == WARPTAG_3C0_MAX) { + this->dyna.actor.flags &= ~ACTOR_FLAG_1; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Warp_tag/EnWarptag_Destroy.s") + if (GET_WARPTAG_INVISIBLE(&this->dyna.actor)) { + this->actionFunc = EnWarpTag_WaitForPlayer; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Warp_tag/func_809C085C.s") + } else { + if ((this->dangeonKeepObject = Object_GetIndex(&globalCtx->objectCtx, GAMEPLAY_DANGEON_KEEP)) < 0) { + Actor_MarkForDeath(&this->dyna.actor); + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Warp_tag/func_809C08E0.s") + this->actionFunc = EnWarpTag_CheckDungeonKeepObject; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Warp_tag/func_809C09A0.s") + } else { // not used by known variants + this->actionFunc = EnWarpTag_Unused809C09A0; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Warp_tag/func_809C0A20.s") +void EnWarptag_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnWarptag* this = THIS; + if (this->dyna.actor.draw != NULL) { + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Warp_tag/func_809C0AB4.s") +/** + * Loads DynaPoly from GAMEPLAY_DANGEON_KEEP. + */ +void EnWarpTag_CheckDungeonKeepObject(EnWarptag* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->dangeonKeepObject)) { + this->actionFunc = EnWarpTag_WaitForPlayer; + DynaPolyActor_Init(&this->dyna, 0x1); + DynaPolyActor_LoadMesh(globalCtx, &this->dyna, &gWarpTagGoronTrialBaseCollider); + this->dyna.actor.objBankIndex = this->dangeonKeepObject; + this->dyna.actor.draw = EnWarpTag_Draw; + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Warp_tag/func_809C0E30.s") +void EnWarpTag_WaitForPlayer(EnWarptag* this, GlobalContext* globalCtx) { + if (!Player_InCsMode(&globalCtx->state) && (this->dyna.actor.xzDistToPlayer <= 30.0f) && + (this->dyna.actor.playerHeightRel <= 10.0f)) { + if (GET_WARPTAG_INVISIBLE(&this->dyna.actor)) { + func_800B7298(globalCtx, NULL, 0x51); + this->actionFunc = EnWarpTag_GrottoReturn; + } else { + func_800B7298(globalCtx, NULL, 0xF); + this->actionFunc = EnWarpTag_RespawnPlayer; + } + } +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Warp_tag/EnWarptag_Update.s") +/** + * Unused ActionFunc: assigned in EnWarpTag_Init, no known variants use. + */ +void EnWarpTag_Unused809C09A0(EnWarptag* this, GlobalContext* globalCtx) { + if (func_800B8718(&this->dyna.actor, &globalCtx->state)) { + // func above: checks for ACTOR_FLAG_20000000, returns true and resets if set, else return false + // this actor doesnt have that flag set default, or in init, and this is called shortly after init + // and I doubt its set externally by another actor, so I believe this is unused + // might be a bug, they might have meant to set actor flag (0x2000 0000) up above but mistyped (0x200 0000) + // also GET_WARPTAG_3C0 should always return 2C0 -> 0xF for all known in-game uses, which is OOB + func_80152434(globalCtx, D_809C1000[GET_WARPTAG_3C0(&this->dyna.actor)]); // unk message function + this->actionFunc = EnWarpTag_Unused809C0A20; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Warp_tag/func_809C0F3C.s") + } else { + func_800B8804(&this->dyna.actor, globalCtx, 50.0f); // updates player->unk_A90 + } +} + +/** + * Unused ActionFunc: assigned by EnWarpTag_Unused809C09A0, no known variants use. + */ +void EnWarpTag_Unused809C0A20(EnWarptag* this, GlobalContext* globalCtx) { + if (globalCtx->msgCtx.ocarinaMode == 9) { + func_800B7298(globalCtx, NULL, 7); + this->actionFunc = EnWarpTag_RespawnPlayer; + ActorCutscene_Stop(ActorCutscene_GetCurrentIndex()); + + } else if (globalCtx->msgCtx.ocarinaMode >= 2) { + globalCtx->msgCtx.ocarinaMode = 4; + this->actionFunc = EnWarpTag_Unused809C09A0; + } +} + +/** + * ActionFunc: Goron Trial (Moon), respawn at the beginning of goron rolling track, try again. + */ +void EnWarpTag_RespawnPlayer(EnWarptag* this, GlobalContext* globalCtx) { + ActorEntry* playerActorEntry; + Player* player; + s32 playerSpawnIndex; + s32 new15E; + s32 entranceIndex; + u32 playerSpawnIndexPerForm[PLAYER_FORM_MAX]; + u8 playerForm; + s16 playerParams; + + player = GET_PLAYER(globalCtx); + if (globalCtx->playerActorCsIds[4] >= 0 && ActorCutscene_GetCurrentIndex() != globalCtx->playerActorCsIds[4]) { + if (ActorCutscene_GetCanPlayNext(globalCtx->playerActorCsIds[4]) == 0) { + ActorCutscene_SetIntentToPlay(globalCtx->playerActorCsIds[4]); + + } else { + ActorCutscene_StartAndSetUnkLinkFields(globalCtx->playerActorCsIds[4], &this->dyna.actor); + func_800B8E58(player, NA_SE_PL_WARP_PLATE); + func_8016566C(0); + } + + } else { + f32 diffX = player->actor.world.pos.x - this->dyna.actor.world.pos.x; + Vec3f newRespawnPos; + f32 diffZ = player->actor.world.pos.z - this->dyna.actor.world.pos.z; + f32 distance = sqrtf(SQ(diffX) + SQ(diffZ)); + + // some weird float behavior prevention? + if (distance != 0.0f) { + distance = (distance - 1.0f) / distance; + distance = CLAMP_MIN(distance, 0.0f); + } + + player->actor.world.pos.x = this->dyna.actor.world.pos.x + (diffX * distance); + player->actor.world.pos.z = this->dyna.actor.world.pos.z + (diffZ * distance); + + if (Math_StepToS(&this->unkValue15E, 0x2710, 0xC8)) { + player->stateFlags3 |= 0x1; + player->actor.gravity = -0.5f; + + if (this->dyna.actor.playerHeightRel < -80.0f) { + playerSpawnIndexPerForm[PLAYER_FORM_FIERCE_DEITY] = GET_WARPTAG_EXIT_INDEX(&this->dyna.actor); + playerSpawnIndexPerForm[PLAYER_FORM_HUMAN] = playerSpawnIndexPerForm[PLAYER_FORM_FIERCE_DEITY]; + playerSpawnIndexPerForm[PLAYER_FORM_GORON] = this->dyna.actor.world.rot.x; + playerSpawnIndexPerForm[PLAYER_FORM_ZORA] = this->dyna.actor.world.rot.y; + playerSpawnIndexPerForm[PLAYER_FORM_DEKU] = this->dyna.actor.world.rot.z; + + if (this->dyna.actor.draw != NULL) { + playerForm = PLAYER_BOOTS_FIERCE_DEITY; + } else { + playerForm = player->transformation; + } + + entranceIndex = gSaveContext.save.entranceIndex; + + playerSpawnIndex = playerSpawnIndexPerForm[playerForm]; + playerActorEntry = &globalCtx->linkActorEntry[playerSpawnIndex]; + newRespawnPos.x = playerActorEntry->pos.x; + newRespawnPos.y = playerActorEntry->pos.y; + newRespawnPos.z = playerActorEntry->pos.z; + + if (GET_WARPTAG_3C0_MAX(&this->dyna.actor) == WARPTAG_3C0_MAX) { + playerParams = 0x9FF; + } else { // not used by any known variant + playerParams = 0x8FF; + } + + // why are we getting player home rotation from the room data? doesnt player have home.rot.y? + // especially because we are converting from deg to binang, but isnt home.rot.y already in binang?? + Play_SetRespawnData( + &globalCtx->state, 0, entranceIndex, // parameter 3 is called "sceneSetup" + globalCtx->setupEntranceList[playerSpawnIndex].room, playerParams, &newRespawnPos, + ((((playerActorEntry->rot.y >> 7) & 0x1FF) / 180.0f) * 32768.0f)); // DEG_TO_BINANG ? + + func_80169EFC(&globalCtx->state); + gSaveContext.respawnFlag = ~0x4; + func_80165690(); + } + } + + player->actor.shape.rot.y += this->unkValue15E; + new15E = this->unkValue15E - 0xFA0; + if (new15E < 0) { + new15E = 0; + } + func_80165658(new15E * 0.04f); // unknown Play_ function + } +} + +/** + * ActionFunc: Deku Playground, return to North Clock Town. + */ +void EnWarpTag_GrottoReturn(EnWarptag* this, GlobalContext* globalCtx) { + if (ActorCutscene_GetCurrentIndex() != this->dyna.actor.cutscene) { + if (ActorCutscene_GetCanPlayNext(this->dyna.actor.cutscene)) { + ActorCutscene_StartAndSetUnkLinkFields(this->dyna.actor.cutscene, &this->dyna.actor); + } else { + ActorCutscene_SetIntentToPlay(this->dyna.actor.cutscene); + } + } + + if (this->grottoExitDelay++ == 10) { + globalCtx->nextEntranceIndex = globalCtx->setupExitList[GET_WARPTAG_EXIT_INDEX(&this->dyna.actor)]; + Scene_SetExitFade(globalCtx); + globalCtx->sceneLoadFlag = 0x14; + func_8019F128(NA_SE_OC_SECRET_HOLE_OUT); + func_801A4058(5); + if (1) {} + gSaveContext.seqIndex = 0xFF; + gSaveContext.nightSeqIndex = 0xFF; + } +} + +void EnWarptag_Update(Actor* thisx, GlobalContext* globalCtx) { + EnWarptag* this = THIS; + this->actionFunc(this, globalCtx); +} + +/** + * Only draws for Goron Trial (a rainblow animated target). + */ +void EnWarpTag_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx); + + func_8012C28C(globalCtx->state.gfxCtx); + AnimatedMat_Draw(globalCtx, Lib_SegmentedToVirtual(&gWarpTagRainbowAnimMat)); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, gWarpTagGoronTrialBaseDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx); +} diff --git a/src/overlays/actors/ovl_En_Warp_tag/z_en_warp_tag.h b/src/overlays/actors/ovl_En_Warp_tag/z_en_warp_tag.h index 1be6eb5ec7..925fda9305 100644 --- a/src/overlays/actors/ovl_En_Warp_tag/z_en_warp_tag.h +++ b/src/overlays/actors/ovl_En_Warp_tag/z_en_warp_tag.h @@ -8,11 +8,27 @@ struct EnWarptag; typedef void (*EnWarptagActionFunc)(struct EnWarptag*, GlobalContext*); typedef struct EnWarptag { - /* 0x0000 */ Actor actor; - /* 0x0144 */ char unk_144[0x1C]; - /* 0x0160 */ EnWarptagActionFunc actionFunc; + /* 0x000 */ DynaPolyActor dyna; + /* 0x15C */ s8 dangeonKeepObject; + /* 0x15E */ union { + s16 reusedValue; // default name + s16 unkValue15E; // passed to unk play func, mods player rotation, stepped to 0x2710 + s16 grottoExitDelay; // 10 frame delay before player can leave the grotto + }; + /* 0x160 */ EnWarptagActionFunc actionFunc; } EnWarptag; // size = 0x164 extern const ActorInit En_Warp_tag_InitVars; +// Only two known Variants: +// Goron Trial (MOON): 0x03C1 +// Deku Playground: 0x83C0 + +#define GET_WARPTAG_3C0_MAX(thisx) ((thisx)->params & 0x3C0) +#define GET_WARPTAG_3C0(thisx) (((thisx)->params >> 6) & 0xF) +#define GET_WARPTAG_EXIT_INDEX(thisx) ((thisx)->params & 0x3F) +#define GET_WARPTAG_INVISIBLE(thisx) ((thisx)->params < 0) // 0x8000 flag + +#define WARPTAG_3C0_MAX 0x3C0 + #endif // Z_EN_WARP_TAG_H diff --git a/tools/disasm/functions.txt b/tools/disasm/functions.txt index fd1b38a799..bd06baf023 100644 --- a/tools/disasm/functions.txt +++ b/tools/disasm/functions.txt @@ -8989,14 +8989,14 @@ 0x809BD858:("func_809BD858",), 0x809C0760:("EnWarptag_Init",), 0x809C0824:("EnWarptag_Destroy",), - 0x809C085C:("func_809C085C",), - 0x809C08E0:("func_809C08E0",), - 0x809C09A0:("func_809C09A0",), - 0x809C0A20:("func_809C0A20",), - 0x809C0AB4:("func_809C0AB4",), - 0x809C0E30:("func_809C0E30",), + 0x809C085C:("EnWarpTag_CheckDungeonKeepObject",), + 0x809C08E0:("EnWarpTag_WaitForPlayer",), + 0x809C09A0:("EnWarpTag_Unused809C09A0",), + 0x809C0A20:("EnWarpTag_Unused809C0A20",), + 0x809C0AB4:("EnWarpTag_RespawnPlayer",), + 0x809C0E30:("EnWarpTag_GrottoReturn",), 0x809C0F18:("EnWarptag_Update",), - 0x809C0F3C:("func_809C0F3C",), + 0x809C0F3C:("EnWarpTag_Draw",), 0x809C10B0:("func_809C10B0",), 0x809C1124:("func_809C1124",), 0x809C1158:("func_809C1158",),