1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3 #include <SDL_keycode.h>
4 #include <boost/cstdint.hpp>
5
6 #include "OverheadController.h"
7
8 #include "System/Config/ConfigHandler.h"
9 #include "Game/Camera.h"
10 #include "Game/CameraHandler.h"
11 #include "Game/UI/MouseHandler.h"
12 #include "Map/Ground.h"
13 #include "Rendering/GlobalRendering.h"
14 #include "System/Log/ILog.h"
15 #include "System/myMath.h"
16 #include "System/Input/KeyInput.h"
17
18 CONFIG(float, MiddleClickScrollSpeed).defaultValue(0.01f);
19 CONFIG(int, OverheadScrollSpeed).defaultValue(10);
20 CONFIG(float, OverheadTiltSpeed).defaultValue(1.0f);
21 CONFIG(bool, OverheadEnabled).defaultValue(true);
22 CONFIG(float, OverheadFOV).defaultValue(45.0f);
23
COverheadController()24 COverheadController::COverheadController()
25 : flipped(false)
26 , zscale(0.5f)
27 , height(1500)
28 , oldAltHeight(1500)
29 , changeAltHeight(true)
30 , maxHeight(10000)
31 {
32 middleClickScrollSpeed = configHandler->GetFloat("MiddleClickScrollSpeed");
33 scrollSpeed = configHandler->GetInt("OverheadScrollSpeed") * 0.1f;
34 tiltSpeed = configHandler->GetFloat("OverheadTiltSpeed");
35 enabled = configHandler->GetBool("OverheadEnabled");
36 fov = configHandler->GetFloat("OverheadFOV");
37
38 if (globalRendering) {
39 // make whole map visible
40 const float h = std::max(pos.x / globalRendering->aspectRatio, pos.z);
41 height = CGround::GetHeightAboveWater(pos.x, pos.z, false) + (2.5f * h);
42 }
43
44 maxHeight = 9.5f * std::max(gs->mapx, gs->mapy);
45 UpdateVectors();
46 }
47
KeyMove(float3 move)48 void COverheadController::KeyMove(float3 move)
49 {
50 if (flipped) {
51 move.x = -move.x;
52 move.y = -move.y;
53 }
54 move *= math::sqrt(move.z) * 200;
55
56 pos.x += move.x * pixelSize * 2.0f * scrollSpeed;
57 pos.z -= move.y * pixelSize * 2.0f * scrollSpeed;
58 UpdateVectors();
59 }
60
MouseMove(float3 move)61 void COverheadController::MouseMove(float3 move)
62 {
63 if (flipped) {
64 move.x = -move.x;
65 move.y = -move.y;
66 }
67 move *= 100 * middleClickScrollSpeed;
68
69 pos.x += move.x * pixelSize * (1 + KeyInput::GetKeyModState(KMOD_SHIFT) * 3) * scrollSpeed;
70 pos.z += move.y * pixelSize * (1 + KeyInput::GetKeyModState(KMOD_SHIFT) * 3) * scrollSpeed;
71 UpdateVectors();
72 }
73
ScreenEdgeMove(float3 move)74 void COverheadController::ScreenEdgeMove(float3 move)
75 {
76 KeyMove(move);
77 }
78
79 // FIXME: 100% identical to SmoothController::MouseWheelMove
MouseWheelMove(float move)80 void COverheadController::MouseWheelMove(float move)
81 {
82 if (move == 0.0f)
83 return;
84
85 camHandler->CameraTransition(0.05f);
86
87 const float shiftSpeed = (KeyInput::GetKeyModState(KMOD_SHIFT) ? 3.0f : 1.0f);
88 const float altZoomDist = height * move * 0.007f * shiftSpeed;
89
90 // tilt the camera if LCTRL is pressed
91 //
92 // otherwise holding down LALT uses 'instant-zoom'
93 // from here to the end of the function (smoothed)
94 if (KeyInput::GetKeyModState(KMOD_CTRL)) {
95 zscale *= (1.0f + (0.01f * move * tiltSpeed * shiftSpeed));
96 zscale = Clamp(zscale, 0.05f, 10.0f);
97 } else {
98 if (move < 0.0f) {
99 // ZOOM IN to mouse cursor instead of mid screen
100 float3 cpos = pos - dir * height;
101 float dif = -altZoomDist;
102
103 if ((height - dif) < 60.0f) {
104 dif = height - 60.0f;
105 }
106 if (KeyInput::GetKeyModState(KMOD_ALT)) {
107 // instazoom in to standard view
108 dif = (height - oldAltHeight) / mouse->dir.y * dir.y;
109 }
110
111 float3 wantedPos = cpos + mouse->dir * dif;
112 float newHeight = CGround::LineGroundCol(wantedPos, wantedPos + dir * 15000, false);
113
114 if (newHeight < 0.0f) {
115 newHeight = height * (1.0f + move * 0.007f * shiftSpeed);
116 }
117 if ((wantedPos.y + (dir.y * newHeight)) < 0.0f) {
118 newHeight = -wantedPos.y / dir.y;
119 }
120 if (newHeight < maxHeight) {
121 height = newHeight;
122 pos = wantedPos + dir * height;
123 }
124 } else {
125 // ZOOM OUT from mid screen
126 if (KeyInput::GetKeyModState(KMOD_ALT)) {
127 // instazoom out to maximum height
128 if (height < maxHeight*0.5f && changeAltHeight) {
129 oldAltHeight = height;
130 changeAltHeight = false;
131 }
132 height = maxHeight;
133 pos.x = gs->mapx * 4;
134 pos.z = gs->mapy * 4.8f; // somewhat longer toward bottom
135 } else {
136 height *= (1.0f + (altZoomDist / height));
137 }
138 }
139
140 // instant-zoom: turn on the smooth transition and reset the camera tilt
141 if (KeyInput::GetKeyModState(KMOD_ALT)) {
142 zscale = 0.5f;
143 camHandler->CameraTransition(1.0f);
144 } else {
145 changeAltHeight = true;
146 }
147 }
148
149 UpdateVectors();
150 }
151
UpdateVectors()152 void COverheadController::UpdateVectors()
153 {
154 pos.x = Clamp(pos.x, 0.01f, gs->mapx * SQUARE_SIZE - 0.01f);
155 pos.z = Clamp(pos.z, 0.01f, gs->mapy * SQUARE_SIZE - 0.01f);
156 pos.y = CGround::GetHeightAboveWater(pos.x, pos.z, false);
157 height = Clamp(height, 60.0f, maxHeight);
158 dir = float3(0.0f, -1.0f, flipped ? zscale : -zscale).ANormalize();
159 }
160
Update()161 void COverheadController::Update()
162 {
163 pixelSize = (camera->GetTanHalfFov() * 2.0f) / globalRendering->viewSizeY * height * 2.0f;
164 }
165
GetPos() const166 float3 COverheadController::GetPos() const
167 {
168 float3 cpos = pos - dir * height;
169 return cpos;
170 }
171
SetPos(const float3 & newPos)172 void COverheadController::SetPos(const float3& newPos)
173 {
174 pos = newPos;
175 UpdateVectors();
176 }
177
SwitchFrom() const178 float3 COverheadController::SwitchFrom() const
179 {
180 return pos;
181 }
182
SwitchTo(bool showText)183 void COverheadController::SwitchTo(bool showText)
184 {
185 if (showText) {
186 LOG("Switching to Overhead (TA) style camera");
187 }
188 }
189
GetState(StateMap & sm) const190 void COverheadController::GetState(StateMap& sm) const
191 {
192 CCameraController::GetState(sm);
193
194 sm["height"] = height;
195 sm["zscale"] = zscale;
196 sm["flipped"] = flipped ? +1.0f : -1.0f;
197 }
198
SetState(const StateMap & sm)199 bool COverheadController::SetState(const StateMap& sm)
200 {
201 CCameraController::SetState(sm);
202
203 SetStateFloat(sm, "height", height);
204 SetStateFloat(sm, "zscale", zscale);
205 SetStateBool (sm, "flipped", flipped);
206 UpdateVectors();
207
208 return true;
209 }
210