Disable Fixed Camera Enhancement (#6083)
Co-authored-by: PurpleHato <linkvssangoku.jr@gmail.com>
This commit is contained in:
229
soh/soh/Enhancements/Graphics/DisableFixedCamera.cpp
Normal file
229
soh/soh/Enhancements/Graphics/DisableFixedCamera.cpp
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||||
|
#include "soh/ShipInit.hpp"
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "functions.h"
|
||||||
|
#include "variables.h"
|
||||||
|
#include "z64bgcheck.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CVAR_DISABLE_FIXED_CAMERA_NAME CVAR_ENHANCEMENT("DisableFixedCamera")
|
||||||
|
#define CVAR_DISABLE_FIXED_CAMERA_VALUE CVarGetInteger(CVAR_DISABLE_FIXED_CAMERA_NAME, 0)
|
||||||
|
static const std::set<SceneID> fixedCameraSceneList = {
|
||||||
|
SCENE_MARKET_ENTRANCE_DAY,
|
||||||
|
SCENE_MARKET_ENTRANCE_NIGHT,
|
||||||
|
SCENE_MARKET_ENTRANCE_RUINS,
|
||||||
|
SCENE_BACK_ALLEY_DAY,
|
||||||
|
SCENE_BACK_ALLEY_NIGHT,
|
||||||
|
SCENE_MARKET_DAY,
|
||||||
|
SCENE_MARKET_NIGHT,
|
||||||
|
SCENE_MARKET_RUINS,
|
||||||
|
SCENE_CASTLE_COURTYARD_ZELDA,
|
||||||
|
SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY,
|
||||||
|
SCENE_TEMPLE_OF_TIME_EXTERIOR_NIGHT,
|
||||||
|
SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS,
|
||||||
|
SCENE_FOREST_TEMPLE,
|
||||||
|
SCENE_KNOW_IT_ALL_BROS_HOUSE,
|
||||||
|
SCENE_TWINS_HOUSE,
|
||||||
|
SCENE_MIDOS_HOUSE,
|
||||||
|
SCENE_SARIAS_HOUSE,
|
||||||
|
SCENE_BACK_ALLEY_HOUSE,
|
||||||
|
SCENE_POTION_SHOP_GRANNY,
|
||||||
|
SCENE_SHOOTING_GALLERY,
|
||||||
|
SCENE_LINKS_HOUSE,
|
||||||
|
SCENE_DOG_LADY_HOUSE,
|
||||||
|
SCENE_STABLE,
|
||||||
|
SCENE_IMPAS_HOUSE,
|
||||||
|
SCENE_KAKARIKO_CENTER_GUEST_HOUSE,
|
||||||
|
SCENE_CARPENTERS_TENT,
|
||||||
|
SCENE_GRAVEKEEPERS_HUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int16_t sSetNormalCam = -1;
|
||||||
|
static bool sIsCamApplied = false;
|
||||||
|
static int sCheckItemCamState = -1;
|
||||||
|
static s16 sStoreLastCamType = -1;
|
||||||
|
|
||||||
|
extern "C" void DisableFixedCamera_CheckCameraState(PlayState* play);
|
||||||
|
|
||||||
|
struct CamDataBackup {
|
||||||
|
CamData* original = nullptr;
|
||||||
|
CamData* copy = nullptr;
|
||||||
|
size_t len = 0;
|
||||||
|
bool active = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unordered_map<CollisionHeader*, CamDataBackup> sCamDataBackups;
|
||||||
|
|
||||||
|
static void DisableFixedCamera_ResetState() {
|
||||||
|
sSetNormalCam = -1;
|
||||||
|
sIsCamApplied = false;
|
||||||
|
sCheckItemCamState = -1;
|
||||||
|
sStoreLastCamType = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DisableFixedCamera_RestoreAllCameraData() {
|
||||||
|
for (auto& [colHeader, backup] : sCamDataBackups) {
|
||||||
|
if (colHeader != nullptr && backup.active) {
|
||||||
|
colHeader->cameraDataList = backup.original;
|
||||||
|
backup.active = false;
|
||||||
|
}
|
||||||
|
delete[] backup.copy;
|
||||||
|
backup.copy = nullptr;
|
||||||
|
backup.original = nullptr;
|
||||||
|
backup.len = 0;
|
||||||
|
}
|
||||||
|
sCamDataBackups.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to check if a camera type is a fixed camera
|
||||||
|
static bool IsFixedCameraType(s16 type) {
|
||||||
|
return type == CAM_SET_PREREND_FIXED || type == CAM_SET_PREREND_PIVOT || type == CAM_SET_PIVOT_FROM_SIDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RegisterDisableFixedCamera() {
|
||||||
|
const bool disableFixedCamEnabled = CVAR_DISABLE_FIXED_CAMERA_VALUE != 0;
|
||||||
|
|
||||||
|
COND_HOOK(OnCameraState, disableFixedCamEnabled,
|
||||||
|
[](PlayState* play) { DisableFixedCamera_CheckCameraState(play); });
|
||||||
|
|
||||||
|
if (!disableFixedCamEnabled) {
|
||||||
|
DisableFixedCamera_RestoreAllCameraData();
|
||||||
|
DisableFixedCamera_ResetState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static RegisterShipInitFunc initFunc(RegisterDisableFixedCamera, { CVAR_DISABLE_FIXED_CAMERA_NAME });
|
||||||
|
|
||||||
|
static void DisableFixedCamera_RestoreCameraData(CollisionHeader* colHeader) {
|
||||||
|
if (colHeader == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = sCamDataBackups.find(colHeader);
|
||||||
|
if (it == sCamDataBackups.end() || !it->second.active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
colHeader->cameraDataList = it->second.original;
|
||||||
|
it->second.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void DisableFixedCamera_SetNormalCamera(PlayState* play) {
|
||||||
|
CollisionHeader* colHeader = BgCheck_GetCollisionHeader(&play->colCtx, BGCHECK_SCENE);
|
||||||
|
if (colHeader != nullptr && colHeader->cameraDataList != nullptr && colHeader->cameraDataListLen > 0) {
|
||||||
|
CamDataBackup& backup = sCamDataBackups[colHeader];
|
||||||
|
if (backup.copy == nullptr) {
|
||||||
|
backup.original = colHeader->cameraDataList;
|
||||||
|
backup.len = colHeader->cameraDataListLen;
|
||||||
|
backup.copy = new CamData[backup.len];
|
||||||
|
memcpy(backup.copy, backup.original, sizeof(CamData) * backup.len);
|
||||||
|
}
|
||||||
|
if (colHeader->cameraDataList != backup.copy) {
|
||||||
|
colHeader->cameraDataList = backup.copy;
|
||||||
|
}
|
||||||
|
backup.active = true;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < colHeader->cameraDataListLen; i++) {
|
||||||
|
if (IsFixedCameraType(colHeader->cameraDataList[i].cameraSType)) {
|
||||||
|
colHeader->cameraDataList[i].cameraSType = CAM_SET_NORMAL0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
play->unk_1242B = 0;
|
||||||
|
if (IsFixedCameraType(play->mainCamera.setting)) {
|
||||||
|
play->mainCamera.setting = CAM_SET_NORMAL0;
|
||||||
|
play->mainCamera.prevSetting = CAM_SET_NORMAL0;
|
||||||
|
}
|
||||||
|
Camera_ChangeSetting(&play->mainCamera, CAM_SET_NORMAL0);
|
||||||
|
Camera_ChangeMode(&play->mainCamera, CAM_MODE_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void DisableFixedCamera_CheckCameraState(PlayState* play) {
|
||||||
|
const bool disableFixedCamEnabled = CVarGetInteger(CVAR_DISABLE_FIXED_CAMERA_NAME, 0) != 0;
|
||||||
|
if (!disableFixedCamEnabled) {
|
||||||
|
CollisionHeader* colHeader = BgCheck_GetCollisionHeader(&play->colCtx, BGCHECK_SCENE);
|
||||||
|
DisableFixedCamera_RestoreCameraData(colHeader);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// prevents normal cam from taking effect during open cutscene to avoid crash
|
||||||
|
if (play->sceneNum == SCENE_LINKS_HOUSE && gSaveContext.cutsceneIndex == 0xFFF1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Only compute player state if we're in a relevant scene
|
||||||
|
const bool isInFixedCameraScene = fixedCameraSceneList.contains(static_cast<SceneID>(play->sceneNum));
|
||||||
|
if (!isInFixedCameraScene) {
|
||||||
|
bool sceneChanged = play->sceneNum != sSetNormalCam;
|
||||||
|
if (sceneChanged) {
|
||||||
|
sSetNormalCam = play->sceneNum;
|
||||||
|
sIsCamApplied = false;
|
||||||
|
sStoreLastCamType = -1;
|
||||||
|
// Clean up backups when leaving fixed camera scenes
|
||||||
|
for (auto& [key, backup] : sCamDataBackups) {
|
||||||
|
delete[] backup.copy;
|
||||||
|
}
|
||||||
|
sCamDataBackups.clear();
|
||||||
|
}
|
||||||
|
DisableFixedCamera_RestoreCameraData(BgCheck_GetCollisionHeader(&play->colCtx, BGCHECK_SCENE));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sceneChanged = play->sceneNum != sSetNormalCam;
|
||||||
|
bool itemCamChanged = false;
|
||||||
|
|
||||||
|
if (sceneChanged) {
|
||||||
|
sSetNormalCam = play->sceneNum;
|
||||||
|
sIsCamApplied = false;
|
||||||
|
sStoreLastCamType = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* player = (Player*)play->actorCtx.actorLists[ACTORCAT_PLAYER].head;
|
||||||
|
|
||||||
|
bool ocarinaPulling = player && (player->stateFlags2 & PLAYER_STATE2_OCARINA_PLAYING);
|
||||||
|
bool bottleUsing = false;
|
||||||
|
if (player) {
|
||||||
|
bool inItemCs = (player->stateFlags1 & PLAYER_STATE1_IN_ITEM_CS) != 0;
|
||||||
|
bool isBottleAction =
|
||||||
|
(player->itemAction >= PLAYER_IA_BOTTLE) && (player->itemAction <= PLAYER_IA_BOTTLE_FAIRY);
|
||||||
|
bottleUsing = inItemCs && isBottleAction;
|
||||||
|
}
|
||||||
|
bool itemCamActive = ocarinaPulling || bottleUsing;
|
||||||
|
|
||||||
|
if (sCheckItemCamState == -1) {
|
||||||
|
sCheckItemCamState = itemCamActive;
|
||||||
|
} else if (sCheckItemCamState != static_cast<int>(itemCamActive)) {
|
||||||
|
sCheckItemCamState = itemCamActive;
|
||||||
|
itemCamChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sceneChanged && !itemCamChanged) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets cam when ocarina or bottle is used and sets it back to normal when done
|
||||||
|
if (itemCamChanged && itemCamActive) {
|
||||||
|
if (play->mainCamera.camDataIdx >= 0) {
|
||||||
|
sStoreLastCamType = play->mainCamera.camDataIdx;
|
||||||
|
}
|
||||||
|
Camera_ChangeSetting(&play->mainCamera, CAM_SET_TURN_AROUND);
|
||||||
|
Camera_ChangeMode(&play->mainCamera, CAM_MODE_NORMAL);
|
||||||
|
if (sStoreLastCamType >= 0) {
|
||||||
|
play->mainCamera.camDataIdx = sStoreLastCamType;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (itemCamChanged && !itemCamActive) {
|
||||||
|
DisableFixedCamera_SetNormalCamera(play);
|
||||||
|
if (sStoreLastCamType >= 0) {
|
||||||
|
play->mainCamera.camDataIdx = sStoreLastCamType;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sIsCamApplied) {
|
||||||
|
DisableFixedCamera_SetNormalCamera(play);
|
||||||
|
sIsCamApplied = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ DEFINE_HOOK(OnLoadGame, (int32_t fileNum));
|
|||||||
DEFINE_HOOK(OnExitGame, (int32_t fileNum));
|
DEFINE_HOOK(OnExitGame, (int32_t fileNum));
|
||||||
DEFINE_HOOK(OnGameStateMainStart, ());
|
DEFINE_HOOK(OnGameStateMainStart, ());
|
||||||
DEFINE_HOOK(OnGameFrameUpdate, ());
|
DEFINE_HOOK(OnGameFrameUpdate, ());
|
||||||
|
DEFINE_HOOK(OnCameraState, (PlayState * play));
|
||||||
DEFINE_HOOK(OnItemReceive, (GetItemEntry itemEntry));
|
DEFINE_HOOK(OnItemReceive, (GetItemEntry itemEntry));
|
||||||
DEFINE_HOOK(OnEquipmentDelete, (int16_t equipmentType, uint16_t equipValue));
|
DEFINE_HOOK(OnEquipmentDelete, (int16_t equipmentType, uint16_t equipValue));
|
||||||
DEFINE_HOOK(OnSaleEnd, (GetItemEntry itemEntry));
|
DEFINE_HOOK(OnSaleEnd, (GetItemEntry itemEntry));
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ void GameInteractor_ExecuteOnGameFrameUpdate() {
|
|||||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnGameFrameUpdate>();
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnGameFrameUpdate>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameInteractor_ExecuteOnCameraState(PlayState* play) {
|
||||||
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnCameraState>(play);
|
||||||
|
}
|
||||||
|
|
||||||
void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry) {
|
void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry) {
|
||||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnItemReceive>(itemEntry);
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnItemReceive>(itemEntry);
|
||||||
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnItemReceive>(itemEntry);
|
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnItemReceive>(itemEntry);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ void GameInteractor_ExecuteOnLoadGame(int32_t fileNum);
|
|||||||
void GameInteractor_ExecuteOnExitGame(int32_t fileNum);
|
void GameInteractor_ExecuteOnExitGame(int32_t fileNum);
|
||||||
void GameInteractor_ExecuteOnGameStateMainStart();
|
void GameInteractor_ExecuteOnGameStateMainStart();
|
||||||
void GameInteractor_ExecuteOnGameFrameUpdate();
|
void GameInteractor_ExecuteOnGameFrameUpdate();
|
||||||
|
void GameInteractor_ExecuteOnCameraState(PlayState* play);
|
||||||
void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry);
|
void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry);
|
||||||
void GameInteractor_ExecuteOnEquipmentDelete(int16_t equipmentType, uint16_t equipValue);
|
void GameInteractor_ExecuteOnEquipmentDelete(int16_t equipmentType, uint16_t equipValue);
|
||||||
void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry);
|
void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry);
|
||||||
|
|||||||
@@ -560,6 +560,22 @@ void SohMenu::AddMenuEnhancements() {
|
|||||||
.Options(CheckboxOptions().Tooltip("Disables 2D pre-rendered backgrounds. Enable this when using a mod that "
|
.Options(CheckboxOptions().Tooltip("Disables 2D pre-rendered backgrounds. Enable this when using a mod that "
|
||||||
"implements 3D backdrops for these areas.\n"
|
"implements 3D backdrops for these areas.\n"
|
||||||
"Requires Scene Change to alter."));
|
"Requires Scene Change to alter."));
|
||||||
|
AddWidget(path, "Disable Fixed Camera", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar(CVAR_ENHANCEMENT("DisableFixedCamera"))
|
||||||
|
.RaceDisable(false)
|
||||||
|
.PreFunc([](WidgetInfo& info) {
|
||||||
|
if (CVarGetInteger(CVAR_ENHANCEMENT("3DSceneRender"), 0) == 0) {
|
||||||
|
CVarSetInteger(CVAR_ENHANCEMENT("DisableFixedCamera"), 0);
|
||||||
|
info.options->disabled = true;
|
||||||
|
} else {
|
||||||
|
info.options->disabled = false;
|
||||||
|
}
|
||||||
|
info.options->disabledTooltip = "Requires \"Disable 2D Pre-Rendered Scenes\" to be enabled.";
|
||||||
|
})
|
||||||
|
.Options(CheckboxOptions().Tooltip(
|
||||||
|
"Disables the fixed camera in maps that use 2D pre-rendered backgrounds. Enable this when using a mod "
|
||||||
|
"that implements 3D backdrops for these areas.\n"
|
||||||
|
"Requires Scene Change to alter."));
|
||||||
AddWidget(path, "Ingame Text Spacing: %d", WIDGET_CVAR_SLIDER_INT)
|
AddWidget(path, "Ingame Text Spacing: %d", WIDGET_CVAR_SLIDER_INT)
|
||||||
.CVar(CVAR_ENHANCEMENT("TextSpacing"))
|
.CVar(CVAR_ENHANCEMENT("TextSpacing"))
|
||||||
.RaceDisable(false)
|
.RaceDisable(false)
|
||||||
|
|||||||
@@ -631,7 +631,6 @@ void Play_Init(GameState* thisx) {
|
|||||||
} else {
|
} else {
|
||||||
play->unk_1242B = 0;
|
play->unk_1242B = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Interface_SetSceneRestrictions(play);
|
Interface_SetSceneRestrictions(play);
|
||||||
Environment_PlaySceneSequence(play);
|
Environment_PlaySceneSequence(play);
|
||||||
gSaveContext.seqId = play->sequenceCtx.seqId;
|
gSaveContext.seqId = play->sequenceCtx.seqId;
|
||||||
@@ -1303,6 +1302,8 @@ void Play_Update(PlayState* play) {
|
|||||||
skip:
|
skip:
|
||||||
PLAY_LOG(3801);
|
PLAY_LOG(3801);
|
||||||
|
|
||||||
|
GameInteractor_ExecuteOnCameraState(play);
|
||||||
|
|
||||||
if (!isPaused || gDbgCamEnabled) {
|
if (!isPaused || gDbgCamEnabled) {
|
||||||
s32 i;
|
s32 i;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user