diff --git a/Core/GameEngine/Include/Common/OptionPreferences.h b/Core/GameEngine/Include/Common/OptionPreferences.h index d41945256a2..63108897941 100644 --- a/Core/GameEngine/Include/Common/OptionPreferences.h +++ b/Core/GameEngine/Include/Common/OptionPreferences.h @@ -132,6 +132,8 @@ class OptionPreferences : public UserPreferences Bool getShowMoneyPerMinute() const; Real getGameWindowTransitionSpeedMultiplier() const; + Real getCameraRotateSpeedMultiplier() const; + Real getCameraPitchSpeedMultiplier() const; Int getObserverStatsFontSize(void); Int getObserverNotificationFontSize(void); diff --git a/Core/GameEngine/Source/Common/OptionPreferences.cpp b/Core/GameEngine/Source/Common/OptionPreferences.cpp index 40b417abb4b..4a48ae6b84e 100644 --- a/Core/GameEngine/Source/Common/OptionPreferences.cpp +++ b/Core/GameEngine/Source/Common/OptionPreferences.cpp @@ -977,3 +977,23 @@ Real OptionPreferences::getGameWindowTransitionSpeedMultiplier() const Real speed = (Real) atof(it->second.str()); return clamp(1.0f, speed, 1000.0f); } + +Real OptionPreferences::getCameraRotateSpeedMultiplier() const +{ + OptionPreferences::const_iterator it = find("CameraRotateSpeedMultiplier"); + if (it == end()) + return 1.0f; + + Real multiplier = (Real) atof(it->second.str()); + return clamp(0.1f, multiplier, 10.0f); +} + +Real OptionPreferences::getCameraPitchSpeedMultiplier() const +{ + OptionPreferences::const_iterator it = find("CameraPitchSpeedMultiplier"); + if (it == end()) + return 1.0f; + + Real multiplier = (Real) atof(it->second.str()); + return clamp(0.1f, multiplier, 10.0f); +} diff --git a/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index ab8a8c5380b..231a6f355b4 100644 --- a/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -3552,9 +3552,29 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage DEBUG_ASSERTCRASH(TheInGameUI->isCameraZoomingOut(), ("Clearing zoom camera out, but it's already clear!")); TheInGameUI->setCameraZoomOut( false ); break; + case GameMessage::MSG_META_BEGIN_CAMERA_PITCH_UP: + case GameMessage::MSG_META_ALT_BEGIN_CAMERA_PITCH_UP: + TheInGameUI->setCameraPitchUp( true ); + break; + case GameMessage::MSG_META_END_CAMERA_PITCH_UP: + case GameMessage::MSG_META_ALT_END_CAMERA_PITCH_UP: + TheInGameUI->setCameraPitchUp( false ); + break; + case GameMessage::MSG_META_BEGIN_CAMERA_PITCH_DOWN: + case GameMessage::MSG_META_ALT_BEGIN_CAMERA_PITCH_DOWN: + TheInGameUI->setCameraPitchDown( true ); + break; + case GameMessage::MSG_META_END_CAMERA_PITCH_DOWN: + case GameMessage::MSG_META_ALT_END_CAMERA_PITCH_DOWN: + TheInGameUI->setCameraPitchDown( false ); + break; case GameMessage::MSG_META_CAMERA_RESET: TheInGameUI->resetCamera(); break; + case GameMessage::MSG_META_CAMERA_PITCH_RESET: + case GameMessage::MSG_META_ALT_CAMERA_PITCH_RESET: + TheTacticalView->userSetPitchToDefault(); + break; case GameMessage::MSG_META_TOGGLE_CAMERA_TRACKING_DRAWABLE: TheInGameUI->setCameraTrackingDrawable( true ); break; diff --git a/Core/GameEngine/Source/GameClient/MessageStream/MetaEvent.cpp b/Core/GameEngine/Source/GameClient/MessageStream/MetaEvent.cpp index ed504001712..8925298ec7c 100644 --- a/Core/GameEngine/Source/GameClient/MessageStream/MetaEvent.cpp +++ b/Core/GameEngine/Source/GameClient/MessageStream/MetaEvent.cpp @@ -186,7 +186,17 @@ static const LookupListRec GameMessageMetaTypeNames[] = { "END_CAMERA_ZOOM_IN", GameMessage::MSG_META_END_CAMERA_ZOOM_IN }, { "BEGIN_CAMERA_ZOOM_OUT", GameMessage::MSG_META_BEGIN_CAMERA_ZOOM_OUT }, { "END_CAMERA_ZOOM_OUT", GameMessage::MSG_META_END_CAMERA_ZOOM_OUT }, + { "BEGIN_CAMERA_PITCH_UP", GameMessage::MSG_META_BEGIN_CAMERA_PITCH_UP }, + { "END_CAMERA_PITCH_UP", GameMessage::MSG_META_END_CAMERA_PITCH_UP }, + { "ALT_BEGIN_CAMERA_PITCH_UP", GameMessage::MSG_META_ALT_BEGIN_CAMERA_PITCH_UP }, + { "ALT_END_CAMERA_PITCH_UP", GameMessage::MSG_META_ALT_END_CAMERA_PITCH_UP }, + { "BEGIN_CAMERA_PITCH_DOWN", GameMessage::MSG_META_BEGIN_CAMERA_PITCH_DOWN }, + { "END_CAMERA_PITCH_DOWN", GameMessage::MSG_META_END_CAMERA_PITCH_DOWN }, + { "ALT_BEGIN_CAMERA_PITCH_DOWN", GameMessage::MSG_META_ALT_BEGIN_CAMERA_PITCH_DOWN }, + { "ALT_END_CAMERA_PITCH_DOWN", GameMessage::MSG_META_ALT_END_CAMERA_PITCH_DOWN }, { "CAMERA_RESET", GameMessage::MSG_META_CAMERA_RESET }, + { "CAMERA_PITCH_RESET", GameMessage::MSG_META_CAMERA_PITCH_RESET }, + { "ALT_CAMERA_PITCH_RESET", GameMessage::MSG_META_ALT_CAMERA_PITCH_RESET }, { "TOGGLE_CAMERA_TRACKING_DRAWABLE", GameMessage::MSG_META_TOGGLE_CAMERA_TRACKING_DRAWABLE }, { "TOGGLE_FAST_FORWARD_REPLAY", GameMessage::MSG_META_TOGGLE_FAST_FORWARD_REPLAY }, { "TOGGLE_PAUSE", GameMessage::MSG_META_TOGGLE_PAUSE }, @@ -992,6 +1002,96 @@ void MetaMap::generateMetaMap() map->m_usableIn = COMMANDUSABLE_GAME; } } + { + MetaMapRec *map = getMetaMapRec(GameMessage::MSG_META_BEGIN_CAMERA_PITCH_UP); + if (map->m_key == MK_NONE) { + map->m_key = MK_KP9; + map->m_transition = DOWN; + map->m_modState = NONE; + map->m_usableIn = COMMANDUSABLE_GAME; + } + } + { + MetaMapRec *map = getMetaMapRec(GameMessage::MSG_META_END_CAMERA_PITCH_UP); + if (map->m_key == MK_NONE) { + map->m_key = MK_KP9; + map->m_transition = UP; + map->m_modState = NONE; + map->m_usableIn = COMMANDUSABLE_GAME; + } + } + { + MetaMapRec *map = getMetaMapRec(GameMessage::MSG_META_ALT_BEGIN_CAMERA_PITCH_UP); + if (map->m_key == MK_NONE) { + map->m_key = MK_PGUP; + map->m_transition = DOWN; + map->m_modState = NONE; + map->m_usableIn = COMMANDUSABLE_GAME; + } + } + { + MetaMapRec *map = getMetaMapRec(GameMessage::MSG_META_ALT_END_CAMERA_PITCH_UP); + if (map->m_key == MK_NONE) { + map->m_key = MK_PGUP; + map->m_transition = UP; + map->m_modState = NONE; + map->m_usableIn = COMMANDUSABLE_GAME; + } + } + { + MetaMapRec *map = getMetaMapRec(GameMessage::MSG_META_BEGIN_CAMERA_PITCH_DOWN); + if (map->m_key == MK_NONE) { + map->m_key = MK_KP3; + map->m_transition = DOWN; + map->m_modState = NONE; + map->m_usableIn = COMMANDUSABLE_GAME; + } + } + { + MetaMapRec *map = getMetaMapRec(GameMessage::MSG_META_END_CAMERA_PITCH_DOWN); + if (map->m_key == MK_NONE) { + map->m_key = MK_KP3; + map->m_transition = UP; + map->m_modState = NONE; + map->m_usableIn = COMMANDUSABLE_GAME; + } + } + { + MetaMapRec *map = getMetaMapRec(GameMessage::MSG_META_ALT_BEGIN_CAMERA_PITCH_DOWN); + if (map->m_key == MK_NONE) { + map->m_key = MK_PGDN; + map->m_transition = DOWN; + map->m_modState = NONE; + map->m_usableIn = COMMANDUSABLE_GAME; + } + } + { + MetaMapRec *map = getMetaMapRec(GameMessage::MSG_META_ALT_END_CAMERA_PITCH_DOWN); + if (map->m_key == MK_NONE) { + map->m_key = MK_PGDN; + map->m_transition = UP; + map->m_modState = NONE; + map->m_usableIn = COMMANDUSABLE_GAME; + } + } + { + MetaMapRec *map = getMetaMapRec(GameMessage::MSG_META_CAMERA_PITCH_RESET); + if (map->m_key == MK_NONE) { + map->m_key = MK_KP7; + map->m_transition = DOWN; + map->m_modState = NONE; + map->m_usableIn = COMMANDUSABLE_GAME; + } + } + { + MetaMapRec *map = getMetaMapRec(GameMessage::MSG_META_ALT_CAMERA_PITCH_RESET); + if (map->m_key == MK_NONE) { + map->m_key = MK_HOME; + map->m_transition = DOWN; + map->m_modState = NONE; + map->m_usableIn = COMMANDUSABLE_GAME; + } + } { MetaMapRec *map = TheMetaMap->getMetaMapRec(GameMessage::MSG_META_TAKE_SCREENSHOT); if (map->m_key == MK_NONE) diff --git a/Core/GameEngine/Source/GameClient/View.cpp b/Core/GameEngine/Source/GameClient/View.cpp index 0a56047cb00..73c9d8d5330 100644 --- a/Core/GameEngine/Source/GameClient/View.cpp +++ b/Core/GameEngine/Source/GameClient/View.cpp @@ -33,6 +33,7 @@ #include "GameClient/Drawable.h" #include "GameClient/GameClient.h" #include "GameClient/View.h" +#include "GameLogic/GameLogic.h" UnsignedInt View::m_idNext = 1; @@ -166,7 +167,10 @@ void View::setAngle(Real radians) void View::setPitch(Real radians) { #if CLAMP_VIEW_PITCH - m_pitch = clamp(DEG_TO_RADF(0.1f), radians, DEG_TO_RADF(89.9f)); + // Raised floor in matches prevents flat-camera sightlines; relaxed in replays. + const Bool inReplay = TheGameLogic != nullptr && TheGameLogic->isInReplayGame(); + const Real minPitchDegrees = inReplay ? 16.5f : 37.5f; + m_pitch = clamp(DEG_TO_RADF(minPitchDegrees), radians, DEG_TO_RADF(89.9f)); #else m_pitch = WWMath::Normalize_Angle(radians); #endif diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h index 5b87552ada6..953ede917c8 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h @@ -521,6 +521,8 @@ class GlobalData : public SubsystemInterface UnsignedInt m_networkDisconnectScreenNotifyTime; ///< The number of milliseconds between when the disconnect screen comes up and when the other players are notified that we are on the disconnect screen. Real m_keyboardCameraRotateSpeed; ///< How fast the camera rotates when rotated via keyboard controls. + Real m_cameraRotateSpeedMultiplier; ///< User scale for keyboard camera rotate speed, from Options.ini. + Real m_cameraPitchSpeedMultiplier; ///< User scale for keyboard camera pitch speed, from Options.ini. Int m_playStats; ///< Int whether we want to log play stats or not, if <= 0 then we don't log #if defined(RTS_DEBUG) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h b/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h index 4d8ea08f2f3..bcecd9e7003 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h @@ -276,7 +276,17 @@ class GameMessage : public MemoryPoolObject MSG_META_END_CAMERA_ZOOM_IN, MSG_META_BEGIN_CAMERA_ZOOM_OUT, MSG_META_END_CAMERA_ZOOM_OUT, + MSG_META_BEGIN_CAMERA_PITCH_UP, + MSG_META_END_CAMERA_PITCH_UP, + MSG_META_ALT_BEGIN_CAMERA_PITCH_UP, + MSG_META_ALT_END_CAMERA_PITCH_UP, + MSG_META_BEGIN_CAMERA_PITCH_DOWN, + MSG_META_END_CAMERA_PITCH_DOWN, + MSG_META_ALT_BEGIN_CAMERA_PITCH_DOWN, + MSG_META_ALT_END_CAMERA_PITCH_DOWN, MSG_META_CAMERA_RESET, + MSG_META_CAMERA_PITCH_RESET, + MSG_META_ALT_CAMERA_PITCH_RESET, MSG_META_TOGGLE_CAMERA_TRACKING_DRAWABLE, MSG_META_TOGGLE_FAST_FORWARD_REPLAY, ///< Toggle the fast forward feature MSG_META_TOGGLE_PAUSE, ///< TheSuperHackers @feature Toggle game pause diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h b/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h index af61c9b0355..d5798a48ec7 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h @@ -564,11 +564,15 @@ class InGameUI : public SubsystemInterface, public Snapshot void setCameraRotateRight(Bool set) { m_cameraRotatingRight = set; } void setCameraZoomIn(Bool set) { m_cameraZoomingIn = set; } void setCameraZoomOut(Bool set) { m_cameraZoomingOut = set; } + void setCameraPitchUp(Bool set) { m_cameraPitchingUp = set; } + void setCameraPitchDown(Bool set) { m_cameraPitchingDown = set; } void setCameraTrackingDrawable(Bool set) { m_cameraTrackingDrawable = set; } Bool isCameraRotatingLeft() const { return m_cameraRotatingLeft; } Bool isCameraRotatingRight() const { return m_cameraRotatingRight; } Bool isCameraZoomingIn() const { return m_cameraZoomingIn; } Bool isCameraZoomingOut() const { return m_cameraZoomingOut; } + Bool isCameraPitchingUp() const { return m_cameraPitchingUp; } + Bool isCameraPitchingDown() const { return m_cameraPitchingDown; } Bool isCameraTrackingDrawable() const { return m_cameraTrackingDrawable; } void resetCamera(); @@ -1041,6 +1045,8 @@ class InGameUI : public SubsystemInterface, public Snapshot Bool m_cameraZoomingIn; Bool m_cameraTrackingDrawable; Bool m_cameraZoomingOut; + Bool m_cameraPitchingUp; + Bool m_cameraPitchingDown; Bool m_drawRMBScrollAnchor; Bool m_moveRMBScrollAnchor; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp index e82213c0644..5a0d746d251 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp @@ -1064,6 +1064,8 @@ GlobalData::GlobalData() m_forceBenchmark = FALSE; ///m_playerInfoListFontSize = optionPref.getPlayerInfoListFontSize(); TheWritableGlobalData->m_showMoneyPerMinute = optionPref.getShowMoneyPerMinute(); TheWritableGlobalData->m_gameWindowTransitionSpeedMultiplier = optionPref.getGameWindowTransitionSpeedMultiplier(); + TheWritableGlobalData->m_cameraRotateSpeedMultiplier = optionPref.getCameraRotateSpeedMultiplier(); + TheWritableGlobalData->m_cameraPitchSpeedMultiplier = optionPref.getCameraPitchSpeedMultiplier(); TheWritableGlobalData->m_observerStatsFontSize = optionPref.getObserverStatsFontSize(); TheWritableGlobalData->m_observerNotificationFontSize = optionPref.getObserverNotificationFontSize(); TheWritableGlobalData->m_observerNotificationSpecialPowerUsage = optionPref.getObserverNotificationSpecialPowerUsage(); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp b/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp index a24859262f8..f46fe9d73ca 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp @@ -354,7 +354,17 @@ const char *GameMessage::getCommandTypeAsString(GameMessage::Type t) CASE_LABEL(MSG_META_END_CAMERA_ZOOM_IN) CASE_LABEL(MSG_META_BEGIN_CAMERA_ZOOM_OUT) CASE_LABEL(MSG_META_END_CAMERA_ZOOM_OUT) + CASE_LABEL(MSG_META_BEGIN_CAMERA_PITCH_UP) + CASE_LABEL(MSG_META_END_CAMERA_PITCH_UP) + CASE_LABEL(MSG_META_ALT_BEGIN_CAMERA_PITCH_UP) + CASE_LABEL(MSG_META_ALT_END_CAMERA_PITCH_UP) + CASE_LABEL(MSG_META_BEGIN_CAMERA_PITCH_DOWN) + CASE_LABEL(MSG_META_END_CAMERA_PITCH_DOWN) + CASE_LABEL(MSG_META_ALT_BEGIN_CAMERA_PITCH_DOWN) + CASE_LABEL(MSG_META_ALT_END_CAMERA_PITCH_DOWN) CASE_LABEL(MSG_META_CAMERA_RESET) + CASE_LABEL(MSG_META_CAMERA_PITCH_RESET) + CASE_LABEL(MSG_META_ALT_CAMERA_PITCH_RESET) CASE_LABEL(MSG_META_TOGGLE_CAMERA_TRACKING_DRAWABLE) CASE_LABEL(MSG_META_TOGGLE_FAST_FORWARD_REPLAY) CASE_LABEL(MSG_META_TOGGLE_PAUSE) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index 97c72099ebd..b833f4c59d1 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -2158,11 +2158,15 @@ void InGameUI::update() layout->runUpdate(); } - if (m_cameraRotatingLeft || m_cameraRotatingRight || m_cameraZoomingIn || m_cameraZoomingOut) + if (m_cameraRotatingLeft || m_cameraRotatingRight || m_cameraZoomingIn || m_cameraZoomingOut + || m_cameraPitchingUp || m_cameraPitchingDown) { // TheSuperHackers @tweak The camera rotation and zoom are now decoupled from the render update. const Real fpsRatio = TheFramePacer->getBaseOverUpdateFpsRatio(); - const Real rotateAngle = TheGlobalData->m_keyboardCameraRotateSpeed * fpsRatio; + // Pitch at half speed feels alike: shorter range, larger view change per degree. + const Real turnAngle = TheGlobalData->m_keyboardCameraRotateSpeed * 0.6f * fpsRatio; + const Real rotateAngle = turnAngle * TheGlobalData->m_cameraRotateSpeedMultiplier; + const Real pitchAngle = turnAngle * 0.5f * TheGlobalData->m_cameraPitchSpeedMultiplier; const Real zoomHeight = (Real)View::ZoomHeightPerSecond * fpsRatio; if (m_cameraRotatingLeft && !m_cameraRotatingRight) @@ -2182,6 +2186,15 @@ void InGameUI::update() { TheTacticalView->userZoom(+zoomHeight); } + + if (m_cameraPitchingUp && !m_cameraPitchingDown) + { + TheTacticalView->userSetPitch(TheTacticalView->getPitch() + pitchAngle); + } + else if (m_cameraPitchingDown && !m_cameraPitchingUp) + { + TheTacticalView->userSetPitch(TheTacticalView->getPitch() - pitchAngle); + } } @@ -2298,6 +2311,8 @@ void InGameUI::reset() setCameraRotateRight(false); setCameraZoomIn(false); setCameraZoomOut(false); + setCameraPitchUp(false); + setCameraPitchDown(false); setCameraTrackingDrawable(false); m_windowLayouts.clear(); @@ -3825,6 +3840,8 @@ void InGameUI::setInputEnabled(Bool enable) setCameraRotateRight(false); // KP6 setCameraZoomIn(false); // KP8 setCameraZoomOut(false); // KP2 + setCameraPitchUp(false); // KP9 + setCameraPitchDown(false); // KP3 } }