#include "global.h" #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80165460.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80165608.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80165630.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80165658.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_8016566C.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80165690.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_801656A4.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80165DB8.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80165DCC.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80165DF0.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80165E04.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80165E1C.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80165E7C.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80165EC0.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/D_801DFA18.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80166060.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_801660B8.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/Play_Destroy.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_801663C4.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80166644.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_801668B4.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80166968.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80166B30.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/Play_Update.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80167DE4.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80167F0C.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/Play_Draw.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80168DAC.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/Play_Main.s") s32 Play_InCsMode(PlayState* this) { return (this->csCtx.state != 0) || Player_InCsMode(this); } #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_80169100.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_801691F0.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/Play_LoadScene.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_8016927C.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/func_801692C4.s") #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/Play_SceneInit.s") void Play_GetScreenPos(PlayState* this, Vec3f* worldPos, Vec3f* screenPos) { f32 invW; // screenPos temporarily stores the projectedPos Actor_GetProjectedPos(this, worldPos, screenPos, &invW); screenPos->x = (SCREEN_WIDTH / 2) + (screenPos->x * invW * (SCREEN_WIDTH / 2)); screenPos->y = (SCREEN_HEIGHT / 2) - (screenPos->y * invW * (SCREEN_HEIGHT / 2)); } s16 Play_CreateSubCamera(PlayState* this) { s16 subCamId; for (subCamId = CAM_ID_SUB_FIRST; subCamId < NUM_CAMS; subCamId++) { if (this->cameraPtrs[subCamId] == NULL) { break; } } // if no subCameras available if (subCamId == NUM_CAMS) { return CAM_ID_NONE; } this->cameraPtrs[subCamId] = &this->subCameras[subCamId - CAM_ID_SUB_FIRST]; Camera_Init(this->cameraPtrs[subCamId], &this->view, &this->colCtx, this); this->cameraPtrs[subCamId]->camId = subCamId; return subCamId; } s16 Play_GetActiveCamId(PlayState* this) { return this->activeCamId; } s32 Play_ChangeCameraStatus(PlayState* this, s16 camId, s16 status) { s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; if (status == CAM_STATUS_ACTIVE) { this->activeCamId = camIdx; } return Camera_ChangeStatus(this->cameraPtrs[camIdx], status); } void Play_ClearCamera(PlayState* this, s16 camId) { s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; if (this->cameraPtrs[camIdx] != NULL) { Camera_ChangeStatus(this->cameraPtrs[camIdx], CAM_STATUS_INACTIVE); this->cameraPtrs[camIdx] = NULL; } } void Play_ClearAllSubCameras(PlayState* this) { s16 subCamId; for (subCamId = CAM_ID_SUB_FIRST; subCamId < NUM_CAMS; subCamId++) { if (this->cameraPtrs[subCamId] != NULL) { Play_ClearCamera(this, subCamId); } } this->activeCamId = CAM_ID_MAIN; } Camera* Play_GetCamera(PlayState* this, s16 camId) { s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; return this->cameraPtrs[camIdx]; } /** * @return bit-packed success if each of the params were applied */ s32 Play_SetCameraAtEye(PlayState* this, s16 camId, Vec3f* at, Vec3f* eye) { s32 successfullySet = 0; s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; Camera* camera = this->cameraPtrs[camIdx]; successfullySet |= Camera_SetViewParam(camera, CAM_VIEW_AT, at); successfullySet <<= 1; successfullySet |= Camera_SetViewParam(camera, CAM_VIEW_EYE, eye); camera->dist = Math3D_Distance(at, eye); if (camera->focalActor != NULL) { camera->atActorOffset.x = at->x - camera->focalActor->world.pos.x; camera->atActorOffset.y = at->y - camera->focalActor->world.pos.y; camera->atActorOffset.z = at->z - camera->focalActor->world.pos.z; } else { camera->atActorOffset.x = camera->atActorOffset.y = camera->atActorOffset.z = 0.0f; } camera->atLerpStepScale = 0.01f; return successfullySet; } /** * @return bit-packed success if each of the params were applied */ s32 Play_SetCameraAtEyeUp(PlayState* this, s16 camId, Vec3f* at, Vec3f* eye, Vec3f* up) { s32 successfullySet = 0; s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; Camera* camera = this->cameraPtrs[camIdx]; successfullySet |= Camera_SetViewParam(camera, CAM_VIEW_AT, at); successfullySet <<= 1; successfullySet |= Camera_SetViewParam(camera, CAM_VIEW_EYE, eye); successfullySet <<= 1; successfullySet |= Camera_SetViewParam(camera, CAM_VIEW_UP, up); camera->dist = Math3D_Distance(at, eye); if (camera->focalActor != NULL) { camera->atActorOffset.x = at->x - camera->focalActor->world.pos.x; camera->atActorOffset.y = at->y - camera->focalActor->world.pos.y; camera->atActorOffset.z = at->z - camera->focalActor->world.pos.z; } else { camera->atActorOffset.x = camera->atActorOffset.y = camera->atActorOffset.z = 0.0f; } camera->atLerpStepScale = 0.01f; return successfullySet; } /** * @return true if the fov was successfully set */ s32 Play_SetCameraFov(PlayState* this, s16 camId, f32 fov) { s32 successfullySet = Camera_SetViewParam(this->cameraPtrs[camId], CAM_VIEW_FOV, &fov) & 1; if (1) {} return successfullySet; } s32 Play_SetCameraRoll(PlayState* this, s16 camId, s16 roll) { s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; Camera* camera = this->cameraPtrs[camIdx]; camera->roll = roll; return 1; } void Play_CopyCamera(PlayState* this, s16 destCamId, s16 srcCamId) { s16 srcCamId2 = (srcCamId == CAM_ID_NONE) ? this->activeCamId : srcCamId; s16 destCamId1 = (destCamId == CAM_ID_NONE) ? this->activeCamId : destCamId; Camera_Copy(this->cameraPtrs[destCamId1], this->cameraPtrs[srcCamId2]); } // Same as Play_ChangeCameraSetting but also calls Camera_InitPlayerSettings s32 func_80169A50(PlayState* this, s16 camId, Player* player, s16 setting) { Camera* camera; s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; camera = this->cameraPtrs[camIdx]; Camera_InitPlayerSettings(camera, player); return Camera_ChangeSetting(camera, setting); } s32 Play_ChangeCameraSetting(PlayState* this, s16 camId, s16 setting) { return Camera_ChangeSetting(Play_GetCamera(this, camId), setting); } // Related to bosses and fishing void func_80169AFC(PlayState* this, s16 camId, s16 timer) { s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; s16 i; Play_ClearCamera(this, camIdx); for (i = CAM_ID_SUB_FIRST; i < NUM_CAMS; i++) { if (this->cameraPtrs[i] != NULL) { Play_ClearCamera(this, i); } } if (timer <= 0) { Play_ChangeCameraStatus(this, CAM_ID_MAIN, CAM_STATUS_ACTIVE); this->cameraPtrs[CAM_ID_MAIN]->childCamId = this->cameraPtrs[CAM_ID_MAIN]->doorTimer2 = 0; } } s16 Play_GetCameraUID(PlayState* this, s16 camId) { Camera* camera = this->cameraPtrs[camId]; if (camera != NULL) { return camera->uid; } else { return -1; } } // Unused in both MM and OoT, purpose is very unclear s16 func_80169BF8(PlayState* this, s16 camId, s16 uid) { Camera* camera = this->cameraPtrs[camId]; if (camera != NULL) { return 0; } else if (camera->uid != uid) { return 0; } else if (camera->status != CAM_STATUS_ACTIVE) { return 2; } else { return 1; } } u16 Play_GetActorCsCamSetting(PlayState* this, s32 csCamDataIndex) { ActorCsCamInfo* actorCsCamList = &this->actorCsCamList[csCamDataIndex]; return actorCsCamList->setting; } Vec3s* Play_GetActorCsCamFuncData(PlayState* this, s32 csCamDataIndex) { ActorCsCamInfo* actorCsCamList = &this->actorCsCamList[csCamDataIndex]; return Lib_SegmentedToVirtual(actorCsCamList->actorCsCamFuncData); } /** * Converts the number of a scene to its "original" equivalent, the default version of the area which the player first * enters. */ s16 Play_GetOriginalSceneNumber(s16 sceneNum) { // Inverted Stone Tower Temple -> Stone Tower Temple if (sceneNum == SCENE_INISIE_R) { return SCENE_INISIE_N; } // Purified Southern Swamp -> Poisoned Sothern Swamp if (sceneNum == SCENE_20SICHITAI2) { return SCENE_20SICHITAI; } // Spring Mountain Village -> Winter Mountain Village if (sceneNum == SCENE_10YUKIYAMANOMURA2) { return SCENE_10YUKIYAMANOMURA; } // Spring Goron Village -> Winter Goron Village if (sceneNum == SCENE_11GORONNOSATO2) { return SCENE_11GORONNOSATO; } // Spring Path to Goron Village -> Winter Path to Goron Village if (sceneNum == SCENE_17SETUGEN2) { return SCENE_17SETUGEN; } // Inverted Stone Tower -> Stone Tower if (sceneNum == SCENE_F41) { return SCENE_F40; } return sceneNum; } /** * Copies the flags set in ActorContext over to the current scene's CycleSceneFlags, usually using the original scene * number. Exception for Inverted Stone Tower Temple, which uses its own. */ void Play_SaveCycleSceneFlags(GameState* thisx) { PlayState* this = (PlayState*)thisx; CycleSceneFlags* cycleSceneFlags; cycleSceneFlags = &gSaveContext.cycleSceneFlags[Play_GetOriginalSceneNumber(this->sceneNum)]; cycleSceneFlags->chest = this->actorCtx.flags.chest; cycleSceneFlags->switch0 = this->actorCtx.flags.switches[0]; cycleSceneFlags->switch1 = this->actorCtx.flags.switches[1]; if (this->sceneNum == SCENE_INISIE_R) { // Inverted Stone Tower Temple cycleSceneFlags = &gSaveContext.cycleSceneFlags[this->sceneNum]; } cycleSceneFlags->collectible = this->actorCtx.flags.collectible[0]; cycleSceneFlags->clearedRoom = this->actorCtx.flags.clearedRoom; } void Play_SetRespawnData(GameState* thisx, s32 respawnMode, u16 entranceIndex, s32 roomIndex, s32 playerParams, Vec3f* pos, s16 yaw) { PlayState* this = (PlayState*)thisx; gSaveContext.respawn[respawnMode].entranceIndex = Entrance_CreateIndex(entranceIndex >> 9, 0, entranceIndex & 0xF); gSaveContext.respawn[respawnMode].roomIndex = roomIndex; gSaveContext.respawn[respawnMode].pos = *pos; gSaveContext.respawn[respawnMode].yaw = yaw; gSaveContext.respawn[respawnMode].playerParams = playerParams; gSaveContext.respawn[respawnMode].tempSwitchFlags = this->actorCtx.flags.switches[2]; gSaveContext.respawn[respawnMode].unk_18 = this->actorCtx.flags.collectible[1]; gSaveContext.respawn[respawnMode].tempCollectFlags = this->actorCtx.flags.collectible[2]; } void Play_SetupRespawnPoint(GameState* thisx, s32 respawnMode, s32 playerParams) { PlayState* this = (PlayState*)thisx; Player* player = GET_PLAYER(this); if (this->sceneNum != SCENE_KAKUSIANA) { // Grottos Play_SetRespawnData(&this->state, respawnMode, (u16)((void)0, gSaveContext.save.entranceIndex), this->roomCtx.currRoom.num, playerParams, &player->actor.world.pos, player->actor.shape.rot.y); } } // Override respawn data in Sakon's Hideout void func_80169ECC(PlayState* this) { if (this->sceneNum == SCENE_SECOM) { this->nextEntranceIndex = 0x2060; gSaveContext.respawnFlag = -7; } } // Gameplay_TriggerVoidOut ? // Used by Player, Ikana_Rotaryroom, Bji01, Kakasi, LiftNuts, Test4, Warptag, WarpUzu, Roomtimer void func_80169EFC(GameState* thisx) { PlayState* this = (PlayState*)thisx; gSaveContext.respawn[RESPAWN_MODE_DOWN].tempSwitchFlags = this->actorCtx.flags.switches[2]; gSaveContext.respawn[RESPAWN_MODE_DOWN].unk_18 = this->actorCtx.flags.collectible[1]; gSaveContext.respawn[RESPAWN_MODE_DOWN].tempCollectFlags = this->actorCtx.flags.collectible[2]; this->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex; gSaveContext.respawnFlag = 1; func_80169ECC(this); this->transitionTrigger = TRANS_TRIGGER_START; this->transitionType = TRANS_TYPE_02; } // Gameplay_LoadToLastEntrance ? // Used by game_over and Test7 void func_80169F78(GameState* thisx) { PlayState* this = (PlayState*)thisx; this->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_TOP].entranceIndex; gSaveContext.respawnFlag = -1; func_80169ECC(this); this->transitionTrigger = TRANS_TRIGGER_START; this->transitionType = TRANS_TYPE_02; } // Gameplay_TriggerRespawn ? // Used for void by Wallmaster, Deku Shrine doors. Also used by Player, Kaleido, DoorWarp1 void func_80169FDC(GameState* thisx) { func_80169F78(thisx); } // Used by Kankyo to determine how to change the lighting, e.g. for game over. s32 func_80169FFC(GameState* thisx) { PlayState* this = (PlayState*)thisx; return this->roomCtx.currRoom.mesh->type0.type != 1; } s32 FrameAdvance_IsEnabled(GameState* thisx) { PlayState* this = (PlayState*)thisx; return this->frameAdvCtx.enabled != 0; } // Unused, unchanged from OoT, which uses it only in one Camera function. /** * @brief Tests if \p actor is a door and the sides are different rooms. * * @param[in] thisx GameState, promoted to play inside. * @param[in] actor Actor to test. * @param[out] yaw Facing angle of the actor, or reverse if in the back room. * @return true if \p actor is a door and the sides are in different rooms, false otherwise */ s32 func_8016A02C(GameState* thisx, Actor* actor, s16* yaw) { PlayState* this = (PlayState*)thisx; TransitionActorEntry* transitionActor; s8 frontRoom; if (actor->category != ACTORCAT_DOOR) { return false; } transitionActor = &this->doorCtx.transitionActorList[(u16)actor->params >> 10]; frontRoom = transitionActor->sides[0].room; if (frontRoom == transitionActor->sides[1].room) { return false; } if (frontRoom == actor->room) { *yaw = actor->shape.rot.y; } else { *yaw = actor->shape.rot.y + 0x8000; } return true; } // Unused /** * @brief Tests if \p pos is underwater. * * @param[in] play PlayState * @param[in] pos position to test * @return true if inside a waterbox and not above a void. */ s32 Play_IsUnderwater(PlayState* this, Vec3f* pos) { WaterBox* waterBox; CollisionPoly* poly; Vec3f waterSurfacePos; s32 bgId; waterSurfacePos = *pos; if ((WaterBox_GetSurface1(this, &this->colCtx, waterSurfacePos.x, waterSurfacePos.z, &waterSurfacePos.y, &waterBox) == true) && (pos->y < waterSurfacePos.y) && (BgCheck_EntityRaycastFloor3(&this->colCtx, &poly, &bgId, &waterSurfacePos) != BGCHECK_Y_MIN)) { return true; } else { return false; } } s32 Play_IsDebugCamEnabled(void) { return gDbgCamEnabled; } // A mapping from playerActorCsIds to sGlobalCamDataSettings indices. extern s16 D_801D0D64[]; // s16 D_801D0D64[] = { -3, -2, -4, -5, -7, -11, -8, -9, -6, -16 }; // Used by Player /** * Extract the common actor cutscene ids used by Player from the scene and set the actor cutscene ids in * this->playerActorCsIds. If a playerActorCsId is not present in the scene, then that particular id is set * to -1. Otherwise, if there is an ActorCutscene where csCamSceneDataId matches the appropriate element of D_801D0D64, * set the corresponding playerActorCsId (and possibly change its priority for the zeroth one) */ void Play_AssignPlayerActorCsIdsFromScene(GameState* thisx, s32 startActorCsId) { PlayState* this = (PlayState*)thisx; s32 i; s16* curPlayerActorCsId = this->playerActorCsIds; s16* phi_s1 = D_801D0D64; for (i = 0; i < ARRAY_COUNT(this->playerActorCsIds); i++, curPlayerActorCsId++, phi_s1++) { ActorCutscene* actorCutscene; s32 curActorCsId; *curPlayerActorCsId = -1; for (curActorCsId = startActorCsId; curActorCsId != -1; curActorCsId = actorCutscene->additionalCutscene) { actorCutscene = ActorCutscene_GetCutscene(curActorCsId); if (actorCutscene->csCamSceneDataId == *phi_s1) { if ((actorCutscene->csCamSceneDataId == -3) && (actorCutscene->priority == 700)) { // override ocarina cs priority actorCutscene->priority = 550; } *curPlayerActorCsId = curActorCsId; break; } } } } // These regs are used by Gameplay_Draw, and several actors, purpose as yet unclear. void func_8016A268(GameState* thisx, s16 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5) { MREG(64) = arg1; MREG(65) = arg2; MREG(66) = arg3; MREG(67) = arg4; MREG(68) = arg5; } #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/Play_Init.s")