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