diff --git a/Core/GameEngine/Include/GameClient/View.h b/Core/GameEngine/Include/GameClient/View.h index 1ff378d5c01..e660f2178fb 100644 --- a/Core/GameEngine/Include/GameClient/View.h +++ b/Core/GameEngine/Include/GameClient/View.h @@ -167,7 +167,8 @@ class View : public Snapshot virtual Bool isTimeFrozen(){ return false;} ///< Freezes time during the next camera movement. virtual Int getTimeMultiplier() {return 1;}; ///< Get the time multiplier. virtual void setTimeMultiplier(Int multiple) {}; ///< Set the time multiplier. - virtual void setDefaultView(Real pitch, Real angle, Real maxHeight) {}; // TheSuperHackers @todo Replace with setDefaultPitch(), setMaxHeightScale() + virtual void setCameraHeightAboveGroundLimitsToDefault(Real heightScale = 1.0f) {}; + virtual void setDefaultView(Real pitch, Real angle, Real maxHeight) {}; virtual void zoomCamera( Real finalZoom, Int milliseconds, Real easeIn=0.0f, Real easeOut=0.0f ) {}; virtual void pitchCamera( Real finalPitch, Int milliseconds, Real easeIn=0.0f, Real easeOut=0.0f ) {}; @@ -190,6 +191,7 @@ class View : public Snapshot virtual Real getHeightAboveGround() { return m_heightAboveGround; } virtual void setHeightAboveGround(Real z); virtual void zoom( Real height ); ///< Zoom in/out, closer to the ground, limit to min, or farther away from the ground, limit to max + virtual void setZoomToMax(); virtual void setZoomToDefault() { m_zoom = 1.0f; } ///< Set zoom to default value virtual void setOkToAdjustHeight( Bool val ) { m_okToAdjustHeight = val; } ///< Set this to adjust camera height diff --git a/Core/GameEngine/Source/GameClient/View.cpp b/Core/GameEngine/Source/GameClient/View.cpp index 5b36c14ea15..35d2b2829ea 100644 --- a/Core/GameEngine/Source/GameClient/View.cpp +++ b/Core/GameEngine/Source/GameClient/View.cpp @@ -129,6 +129,11 @@ void View::zoom( Real height ) setHeightAboveGround(getHeightAboveGround() + height); } +void View::setZoomToMax() +{ + setHeightAboveGround(getHeightAboveGround() + m_maxHeightAboveGround); +} + /** * Center the view on the given coordinate. */ diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h index 4c8a6872d5a..97a794ab110 100644 --- a/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h @@ -204,12 +204,14 @@ class W3DView : public View, public SubsystemInterface virtual Int getTimeMultiplier() {return m_timeMultiplier;};///< Get the time multiplier. virtual void setTimeMultiplier(Int multiple) {m_timeMultiplier = multiple;}; ///< Set the time multiplier. virtual void setDefaultView(Real pitch, Real angle, Real maxHeight); + virtual void setCameraHeightAboveGroundLimitsToDefault(Real heightScale = 1.0f); virtual void zoomCamera( Real finalZoom, Int milliseconds, Real easeIn, Real easeOut ); virtual void pitchCamera( Real finalPitch, Int milliseconds, Real easeIn, Real easeOut ); virtual void setHeightAboveGround(Real z); virtual void setZoom(Real z); - virtual void setZoomToDefault(); ///< Set zoom to default value + virtual void setZoomToMax(); + virtual void setZoomToDefault(); ///< Set zoom to default value - TheSuperHackers @info This function resets the camera so will cause scripted cameras to halt virtual void setFieldOfView( Real angle ); ///< Set the horizontal field of view angle diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp index a66094ab5f5..5e6a6697a80 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp @@ -1895,6 +1895,31 @@ void W3DView::setPitchToDefault() m_recalcCamera = true; } +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +void W3DView::setCameraHeightAboveGroundLimitsToDefault(Real heightScale) +{ + // TheSuperHackers @fix Mauller Adjust the camera height to compensate for the screen aspect ratio + Real baseAspectRatio = (Real)DEFAULT_DISPLAY_WIDTH / (Real)DEFAULT_DISPLAY_HEIGHT; + Real currentAspectRatio = (Real)TheTacticalView->getWidth() / (Real)TheTacticalView->getHeight(); + Real aspectRatioScale = 0.0f; + + if (currentAspectRatio > baseAspectRatio) + { + aspectRatioScale = fabs(( 1 + ( currentAspectRatio - baseAspectRatio) )); + } + else + { + aspectRatioScale = fabs(( 1 - ( baseAspectRatio - currentAspectRatio) )); + } + + m_maxHeightAboveGround = TheGlobalData->m_maxCameraHeight * aspectRatioScale * heightScale; + m_minHeightAboveGround = TheGlobalData->m_minCameraHeight * aspectRatioScale; + + if (m_minHeightAboveGround > m_maxHeightAboveGround) + m_maxHeightAboveGround = m_minHeightAboveGround; +} + //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void W3DView::setDefaultView(Real pitch, Real angle, Real maxHeight) @@ -1938,10 +1963,8 @@ void W3DView::setZoom(Real z) //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- -void W3DView::setZoomToDefault() +void W3DView::setZoomToMax() { - // default zoom has to be max, otherwise players will just zoom to max always - // terrain height + desired height offset == cameraOffset * actual zoom // find best approximation of max terrain height we can see Real terrainHeightMax = getHeightAroundPos(m_pos.x, m_pos.y); @@ -1954,6 +1977,15 @@ void W3DView::setZoomToDefault() m_zoom = desiredZoom; m_heightAboveGround = m_maxHeightAboveGround; + m_cameraAreaConstraintsValid = false; // recalc it. + m_recalcCamera = true; +} + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +void W3DView::setZoomToDefault() +{ + // default zoom has to be max, otherwise players will just zoom to max always stopDoingScriptedCamera(); m_CameraArrivedAtWaypointOnPathFlag = false; m_cameraAreaConstraintsValid = false; diff --git a/Generals/Code/GameEngine/Include/Common/GlobalData.h b/Generals/Code/GameEngine/Include/Common/GlobalData.h index 7974dd6486f..e86251f81ff 100644 --- a/Generals/Code/GameEngine/Include/Common/GlobalData.h +++ b/Generals/Code/GameEngine/Include/Common/GlobalData.h @@ -185,8 +185,11 @@ class GlobalData : public SubsystemInterface Real m_cameraPitch; Real m_cameraYaw; Real m_cameraHeight; + + // GeneralsX @tweak Copilot 23/03/2026 Mirror ZH note: these are 4:3 camera limits, scaled for other aspect ratios. Real m_maxCameraHeight; Real m_minCameraHeight; + Real m_terrainHeightAtEdgeOfMap; Real m_unitDamagedThresh; Real m_unitReallyDamagedThresh; diff --git a/Generals/Code/GameEngine/Include/GameLogic/ScriptActions.h b/Generals/Code/GameEngine/Include/GameLogic/ScriptActions.h index c291fe70c75..003358e923d 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/ScriptActions.h +++ b/Generals/Code/GameEngine/Include/GameLogic/ScriptActions.h @@ -111,7 +111,7 @@ class ScriptActions : public ScriptActionsInterface void doCameraTetherNamed(const AsciiString& unit, Bool snapToUnit, Real play); void doCameraStopTetherNamed(); - void doCameraSetDefault(Real pitch, Real angle, Real maxHeight); + void doCameraSetDefault(Real pitch, Real angle, Real heighScale); void doOversizeTheTerrain(Int amount); void doMoveCameraAlongWaypointPath(const AsciiString& waypoint, Real sec, Real cameraStutterSec); diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp index c9688a102fb..2e1ea0faa4f 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp @@ -823,6 +823,10 @@ static void saveOptions() TheInGameUI->recreateControlBar(); TheInGameUI->refreshCustomUiResources(); + + // GeneralsX @tweak Copilot 23/03/2026 Update shell camera limits and zoom after display mode changes. + TheTacticalView->setCameraHeightAboveGroundLimitsToDefault(); + TheTacticalView->setZoomToMax(); } } } diff --git a/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp b/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp index 4ba15c09991..b6b51a92e7f 100644 --- a/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -1346,8 +1346,9 @@ void InGameUI::init() // make the tactical display the full screen width and height TheTacticalView->setWidth( TheDisplay->getWidth() ); TheTacticalView->setHeight( TheDisplay->getHeight() ); + // GeneralsX @tweak Copilot 23/03/2026 Mirror ZH camera defaults for aspect-ratio-aware limits. + TheTacticalView->setCameraHeightAboveGroundLimitsToDefault(); } - TheTacticalView->setDefaultView(0.0f, 0.0f, 1.0f); /** @todo this may be the wrong place to create the sidebar, but for now this is where it lives */ @@ -2091,7 +2092,8 @@ void InGameUI::reset() // reset the command bar TheControlBar->reset(); - TheTacticalView->setDefaultView(0.0f, 0.0f, 1.0f); + // GeneralsX @tweak Copilot 23/03/2026 Keep reset aligned with aspect-ratio-aware camera defaults. + TheTacticalView->setCameraHeightAboveGroundLimitsToDefault(); ResetInGameChat(); diff --git a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp index 227cc57dcec..3efce164438 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp @@ -4270,9 +4270,12 @@ void ScriptActions::doCameraStopTetherNamed() //------------------------------------------------------------------------------------------------- /** doCameraSetDefault */ //------------------------------------------------------------------------------------------------- -void ScriptActions::doCameraSetDefault(Real pitch, Real angle, Real maxHeight) +void ScriptActions::doCameraSetDefault(Real pitch, Real angle, Real heighScale) { - TheTacticalView->setDefaultView(pitch, angle, maxHeight); + // GeneralsX @tweak Copilot 23/03/2026 Mirror ZH scripted camera defaults for aspect-ratio-aware limits. + TheTacticalView->setCameraHeightAboveGroundLimitsToDefault(heighScale); + TheTacticalView->setPitch(pitch); + TheTacticalView->setAngle(angle); } //------------------------------------------------------------------------------------------------- diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 2c11fd6bce8..50d28bfc945 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -1815,9 +1815,12 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) // update the loadscreen updateLoadProgress(LOAD_PROGRESS_POST_PRELOAD_ASSETS); + // GeneralsX @tweak Copilot 23/03/2026 Keep camera defaults aligned after resolution/aspect changes. + TheTacticalView->setCameraHeightAboveGroundLimitsToDefault(); TheTacticalView->setAngleToDefault(); TheTacticalView->setPitchToDefault(); TheTacticalView->setZoomToDefault(); + TheTacticalView->setZoomToMax(); if( TheRecorder ) TheRecorder->initControls(); diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h index e826ca7c357..b280fb99718 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h @@ -186,8 +186,11 @@ class GlobalData : public SubsystemInterface Real m_cameraPitch; Real m_cameraYaw; Real m_cameraHeight; + + // TheSuperHackers @info Max and Min camera height for the original 4:3 view, these are then scaled for other aspect ratios. Real m_maxCameraHeight; Real m_minCameraHeight; + Real m_terrainHeightAtEdgeOfMap; Real m_unitDamagedThresh; Real m_unitReallyDamagedThresh; diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/ScriptActions.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/ScriptActions.h index abdd2989c5d..ab0f469eb17 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/ScriptActions.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/ScriptActions.h @@ -111,7 +111,7 @@ class ScriptActions : public ScriptActionsInterface void doCameraTetherNamed(const AsciiString& unit, Bool snapToUnit, Real play); void doCameraStopTetherNamed(); - void doCameraSetDefault(Real pitch, Real angle, Real maxHeight); + void doCameraSetDefault(Real pitch, Real angle, Real heighScale); void doOversizeTheTerrain(Int amount); void doMoveCameraAlongWaypointPath(const AsciiString& waypoint, Real sec, Real cameraStutterSec, Real easeIn, Real easeOut); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp index 4f86db754a6..809a2b7c63b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp @@ -850,6 +850,11 @@ static void saveOptions() TheInGameUI->recreateControlBar(); TheInGameUI->refreshCustomUiResources(); + + // TheSuperHackers @info Only update the camera limits and set the zoom to max to not interfere with the scripted camera on the shellmap + // The tactical view gets reset at game start, this is here so the shell map looks correct once the resolution is adjusted + TheTacticalView->setCameraHeightAboveGroundLimitsToDefault(); + TheTacticalView->setZoomToMax(); } } } diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index ef1fc3161ad..718dfead9bc 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -1376,8 +1376,8 @@ void InGameUI::init() // make the tactical display the full screen width and height TheTacticalView->setWidth( TheDisplay->getWidth() ); TheTacticalView->setHeight( TheDisplay->getHeight() ); + TheTacticalView->setCameraHeightAboveGroundLimitsToDefault(); } - TheTacticalView->setDefaultView(0.0f, 0.0f, 1.0f); /** @todo this may be the wrong place to create the sidebar, but for now this is where it lives */ @@ -2147,7 +2147,7 @@ void InGameUI::reset() // reset the command bar TheControlBar->reset(); - TheTacticalView->setDefaultView(0.0f, 0.0f, 1.0f); + TheTacticalView->setCameraHeightAboveGroundLimitsToDefault(); ResetInGameChat(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp index 7b331e03710..ba8260667cc 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp @@ -4601,9 +4601,11 @@ void ScriptActions::doCameraStopTetherNamed() //------------------------------------------------------------------------------------------------- /** doCameraSetDefault */ //------------------------------------------------------------------------------------------------- -void ScriptActions::doCameraSetDefault(Real pitch, Real angle, Real maxHeight) +void ScriptActions::doCameraSetDefault(Real pitch, Real angle, Real heighScale) { - TheTacticalView->setDefaultView(pitch, angle, maxHeight); + TheTacticalView->setCameraHeightAboveGroundLimitsToDefault(heighScale); + TheTacticalView->setPitch(pitch); + TheTacticalView->setAngle(angle); } //------------------------------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index cd6d1a57766..65ad01ce8a6 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -2079,9 +2079,12 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) // update the loadscreen updateLoadProgress(LOAD_PROGRESS_POST_PRELOAD_ASSETS); + // TheSuperHackers @info Initialize the camera height limits to default if the resolution was changed + TheTacticalView->setCameraHeightAboveGroundLimitsToDefault(); TheTacticalView->setAngleToDefault(); TheTacticalView->setPitchToDefault(); TheTacticalView->setZoomToDefault(); + TheTacticalView->setZoomToMax(); if( TheRecorder ) TheRecorder->initControls(); diff --git a/docs/DEV_BLOG/2026-03-DIARY.md b/docs/DEV_BLOG/2026-03-DIARY.md index a5a1d023665..c1c5708036f 100644 --- a/docs/DEV_BLOG/2026-03-DIARY.md +++ b/docs/DEV_BLOG/2026-03-DIARY.md @@ -2,6 +2,22 @@ --- +## 2026-03-23 (SESSION 91): Backport Camera Aspect-Ratio Default Fixes to Generals Base + +Backported the camera default-height integration from ZH to Generals base game paths after validating file-by-file equivalence. + +What was changed in Generals: +- Replaced direct `setDefaultView(0.0f, 0.0f, 1.0f)` usage in `InGameUI::init()` and `InGameUI::reset()` with `setCameraHeightAboveGroundLimitsToDefault()`. +- Updated scripted camera default action to use height-scale-aware flow (`setCameraHeightAboveGroundLimitsToDefault`, `setPitch`, `setAngle`) and synced header signature. +- Updated `GameLogic::startNewGame()` to initialize camera height limits before default angle/pitch/zoom and force `setZoomToMax()`. +- Updated `OptionsMenu::saveOptions()` to refresh shell-map camera limits and zoom after runtime resolution changes. +- Added contextual note in `GlobalData.h` documenting that max/min camera height are treated as 4:3 baseline values and scaled for other aspect ratios. + +Validation: +- Confirmed pending patch scope is limited to 6 Generals files. +- Checked for accidental literal `\\n` artifacts in edited files; none found. +- Language service reports no syntax errors on changed files. + ## 2026-03-23 (SESSION 90): Issue #16 Skirmish Instant-End + Neutral/White Enemy on Linux **Problem observed**: