1 /*
2  * Copyright 2010-2014 OpenXcom Developers.
3  *
4  * This file is part of OpenXcom.
5  *
6  * OpenXcom is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * OpenXcom is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with OpenXcom.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #define _USE_MATH_DEFINES
20 #include "GeoscapeState.h"
21 #include <cmath>
22 #include <sstream>
23 #include <iomanip>
24 #include <ctime>
25 #include <algorithm>
26 #include <functional>
27 #include <assert.h>
28 #include "../Engine/RNG.h"
29 #include "../Engine/Game.h"
30 #include "../Engine/Action.h"
31 #include "../Resource/ResourcePack.h"
32 #include "../Engine/Language.h"
33 #include "../Engine/Palette.h"
34 #include "../Engine/Screen.h"
35 #include "../Engine/Surface.h"
36 #include "../Engine/Options.h"
37 #include "Globe.h"
38 #include "../Interface/Text.h"
39 #include "../Interface/TextButton.h"
40 #include "../Interface/Cursor.h"
41 #include "../Interface/FpsCounter.h"
42 #include "../Engine/Timer.h"
43 #include "../Savegame/GameTime.h"
44 #include "../Engine/Music.h"
45 #include "../Savegame/SavedGame.h"
46 #include "../Ruleset/Ruleset.h"
47 #include "../Savegame/Base.h"
48 #include "../Savegame/BaseFacility.h"
49 #include "../Ruleset/RuleBaseFacility.h"
50 #include "../Savegame/Craft.h"
51 #include "../Ruleset/RuleCraft.h"
52 #include "../Savegame/Ufo.h"
53 #include "../Ruleset/RuleUfo.h"
54 #include "../Savegame/Waypoint.h"
55 #include "../Savegame/Transfer.h"
56 #include "../Savegame/Soldier.h"
57 #include "../Savegame/SoldierDeath.h"
58 #include "../Menu/PauseState.h"
59 #include "InterceptState.h"
60 #include "../Basescape/BasescapeState.h"
61 #include "../Basescape/SellState.h"
62 #include "../Menu/ErrorMessageState.h"
63 #include "GraphsState.h"
64 #include "FundingState.h"
65 #include "MonthlyReportState.h"
66 #include "ProductionCompleteState.h"
67 #include "UfoDetectedState.h"
68 #include "GeoscapeCraftState.h"
69 #include "DogfightState.h"
70 #include "UfoLostState.h"
71 #include "CraftPatrolState.h"
72 #include "LowFuelState.h"
73 #include "MultipleTargetsState.h"
74 #include "ConfirmLandingState.h"
75 #include "ItemsArrivingState.h"
76 #include "CraftErrorState.h"
77 #include "../Ufopaedia/Ufopaedia.h"
78 #include "../Savegame/ResearchProject.h"
79 #include "ResearchCompleteState.h"
80 #include "../Ruleset/RuleResearch.h"
81 #include "ResearchRequiredState.h"
82 #include "NewPossibleResearchState.h"
83 #include "NewPossibleManufactureState.h"
84 #include "../Savegame/Production.h"
85 #include "../Ruleset/RuleManufacture.h"
86 #include "../Savegame/ItemContainer.h"
87 #include "../Savegame/TerrorSite.h"
88 #include "../Savegame/AlienBase.h"
89 #include "../Ruleset/RuleRegion.h"
90 #include "../Ruleset/City.h"
91 #include "AlienTerrorState.h"
92 #include "AlienBaseState.h"
93 #include "../Savegame/Region.h"
94 #include "../Savegame/Country.h"
95 #include "../Ruleset/RuleCountry.h"
96 #include "../Ruleset/RuleAlienMission.h"
97 #include "../Savegame/AlienStrategy.h"
98 #include "../Savegame/AlienMission.h"
99 #include "../Savegame/SavedBattleGame.h"
100 #include "../Battlescape/BattlescapeGenerator.h"
101 #include "../Battlescape/BriefingState.h"
102 #include "../Ruleset/UfoTrajectory.h"
103 #include "../Ruleset/Armor.h"
104 #include "BaseDefenseState.h"
105 #include "BaseDestroyedState.h"
106 #include "DefeatState.h"
107 #include "../Menu/LoadGameState.h"
108 #include "../Menu/SaveGameState.h"
109 #include "../Menu/ListSaveState.h"
110 #include "../Ruleset/AlienRace.h"
111 
112 namespace OpenXcom
113 {
114 
115 /**
116  * Initializes all the elements in the Geoscape screen.
117  * @param game Pointer to the core game.
118  */
GeoscapeState(Game * game)119 GeoscapeState::GeoscapeState(Game *game) : State(game), _pause(false), _zoomInEffectDone(false), _zoomOutEffectDone(false), _popups(), _dogfights(), _dogfightsToBeStarted(), _minimizedDogfights(0)
120 {
121 	int screenWidth = Options::baseXGeoscape;
122 	int screenHeight = Options::baseYGeoscape;
123 
124 	// Create objects
125 	Surface *hd = _game->getResourcePack()->getSurface("ALTGEOBORD.SCR");
126 	_bg = new Surface(hd->getWidth(), hd->getHeight(), 0, 0);
127 	_sideLine = new Surface(64, screenHeight, screenWidth - 64, 0);
128 	_sidebar = new Surface(64, 200, screenWidth - 64, screenHeight / 2 - 100);
129 
130 	_globe = new Globe(_game, (screenWidth-64)/2, screenHeight/2, screenWidth-64, screenHeight, 0, 0);
131 	_bg->setX((_globe->getWidth() - _bg->getWidth()) / 2);
132 	_bg->setY((_globe->getHeight() - _bg->getHeight()) / 2);
133 
134 	_btnIntercept = new TextButton(63, 11, screenWidth-63, screenHeight/2-100);
135 	_btnBases = new TextButton(63, 11, screenWidth-63, screenHeight/2-88);
136 	_btnGraphs = new TextButton(63, 11, screenWidth-63, screenHeight/2-76);
137 	_btnUfopaedia = new TextButton(63, 11, screenWidth-63, screenHeight/2-64);
138 	_btnOptions = new TextButton(63, 11, screenWidth-63, screenHeight/2-52);
139 	_btnFunding = new TextButton(63, 11, screenWidth-63, screenHeight/2-40);
140 
141 	_btn5Secs = new TextButton(31, 13, screenWidth-63, screenHeight/2+12);
142 	_btn1Min = new TextButton(31, 13, screenWidth-31, screenHeight/2+12);
143 	_btn5Mins = new TextButton(31, 13, screenWidth-63, screenHeight/2+26);
144 	_btn30Mins = new TextButton(31, 13, screenWidth-31, screenHeight/2+26);
145 	_btn1Hour = new TextButton(31, 13, screenWidth-63, screenHeight/2+40);
146 	_btn1Day = new TextButton(31, 13, screenWidth-31, screenHeight/2+40);
147 
148 	_btnRotateLeft = new InteractiveSurface(12, 10, screenWidth-61, screenHeight/2+76);
149 	_btnRotateRight = new InteractiveSurface(12, 10, screenWidth-37, screenHeight/2+76);
150 	_btnRotateUp = new InteractiveSurface(13, 12, screenWidth-49, screenHeight/2+62);
151 	_btnRotateDown = new InteractiveSurface(13, 12, screenWidth-49, screenHeight/2+87);
152 	_btnZoomIn = new InteractiveSurface(23, 23, screenWidth-25, screenHeight/2+56);
153 	_btnZoomOut = new InteractiveSurface(13, 17, screenWidth-20, screenHeight/2+82);
154 
155 	int height = (screenHeight - Screen::ORIGINAL_HEIGHT) / 2 + 10;
156 	_sideTop = new TextButton(63, height, screenWidth-63, _sidebar->getY() - height - 1);
157 	_sideBottom = new TextButton(63, height, screenWidth-63, _sidebar->getY() + _sidebar->getHeight() + 1);
158 
159 	_txtHour = new Text(20, 16, screenWidth-61, screenHeight/2-26);
160 	_txtHourSep = new Text(4, 16, screenWidth-41, screenHeight/2-26);
161 	_txtMin = new Text(20, 16, screenWidth-37, screenHeight/2-26);
162 	_txtMinSep = new Text(4, 16, screenWidth-17, screenHeight/2-26);
163 	_txtSec = new Text(11, 8, screenWidth-13, screenHeight/2-20);
164 	_txtWeekday = new Text(59, 8, screenWidth-61, screenHeight/2-13);
165 	_txtDay = new Text(29, 8, screenWidth-61, screenHeight/2-6);
166 	_txtMonth = new Text(29, 8, screenWidth-32, screenHeight/2-6);
167 	_txtYear = new Text(59, 8, screenWidth-61, screenHeight/2+1);
168 	_txtFunds = new Text(59, 8, screenWidth-61, screenHeight/2-27);
169 
170 	_timeSpeed = _btn5Secs;
171 	_gameTimer = new Timer(Options::geoClockSpeed);
172 
173 	_zoomInEffectTimer = new Timer(Options::dogfightSpeed+10);
174 	_zoomOutEffectTimer = new Timer(Options::dogfightSpeed+10);
175 	_dogfightStartTimer = new Timer(Options::dogfightSpeed+10);
176 
177 	_txtDebug = new Text(200, 18, 0, 0);
178 
179 	// Set palette
180 	setPalette("PAL_GEOSCAPE");
181 
182 	// Fix system colors
183 	_game->getCursor()->setColor(Palette::blockOffset(15)+12);
184 	_game->getFpsCounter()->setColor(Palette::blockOffset(15)+12);
185 
186 	add(_bg);
187 	add(_sideLine);
188 	add(_sidebar);
189 	add(_globe);
190 
191 	add(_btnIntercept);
192 	add(_btnBases);
193 	add(_btnGraphs);
194 	add(_btnUfopaedia);
195 	add(_btnOptions);
196 	add(_btnFunding);
197 
198 	add(_btn5Secs);
199 	add(_btn1Min);
200 	add(_btn5Mins);
201 	add(_btn30Mins);
202 	add(_btn1Hour);
203 	add(_btn1Day);
204 
205 	add(_btnRotateLeft);
206 	add(_btnRotateRight);
207 	add(_btnRotateUp);
208 	add(_btnRotateDown);
209 	add(_btnZoomIn);
210 	add(_btnZoomOut);
211 
212 	add(_sideTop);
213 	add(_sideBottom);
214 
215 	add(_txtFunds);
216 	add(_txtHour);
217 	add(_txtHourSep);
218 	add(_txtMin);
219 	add(_txtMinSep);
220 	add(_txtSec);
221 	add(_txtWeekday);
222 	add(_txtDay);
223 	add(_txtMonth);
224 	add(_txtYear);
225 
226 	add(_txtDebug);
227 
228 	// Set up objects
229 	Surface *geobord = _game->getResourcePack()->getSurface("GEOBORD.SCR");
230 	geobord->setX(_sidebar->getX() - geobord->getWidth() + _sidebar->getWidth());
231 	geobord->setY(_sidebar->getY());
232 	_sidebar->copy(geobord);
233 	_game->getResourcePack()->getSurface("ALTGEOBORD.SCR")->blit(_bg);
234 
235 	_sideLine->drawRect(0, 0, _sideLine->getWidth(), _sideLine->getHeight(), 15);
236 
237 	_btnIntercept->initText(_game->getResourcePack()->getFont("FONT_GEO_BIG"), _game->getResourcePack()->getFont("FONT_GEO_SMALL"), _game->getLanguage());
238 	_btnIntercept->setColor(Palette::blockOffset(15)+6);
239 	_btnIntercept->setTextColor(Palette::blockOffset(15)+5);
240 	_btnIntercept->setText(tr("STR_INTERCEPT"));
241 	_btnIntercept->onMouseClick((ActionHandler)&GeoscapeState::btnInterceptClick);
242 	_btnIntercept->onKeyboardPress((ActionHandler)&GeoscapeState::btnInterceptClick, Options::keyGeoIntercept);
243 
244 	_btnBases->initText(_game->getResourcePack()->getFont("FONT_GEO_BIG"), _game->getResourcePack()->getFont("FONT_GEO_SMALL"), _game->getLanguage());
245 	_btnBases->setColor(Palette::blockOffset(15)+6);
246 	_btnBases->setTextColor(Palette::blockOffset(15)+5);
247 	_btnBases->setText(tr("STR_BASES"));
248 	_btnBases->onMouseClick((ActionHandler)&GeoscapeState::btnBasesClick);
249 	_btnBases->onKeyboardPress((ActionHandler)&GeoscapeState::btnBasesClick, Options::keyGeoBases);
250 
251 	_btnGraphs->initText(_game->getResourcePack()->getFont("FONT_GEO_BIG"), _game->getResourcePack()->getFont("FONT_GEO_SMALL"), _game->getLanguage());
252 	_btnGraphs->setColor(Palette::blockOffset(15)+6);
253 	_btnGraphs->setTextColor(Palette::blockOffset(15)+5);
254 	_btnGraphs->setText(tr("STR_GRAPHS"));
255 	_btnGraphs->onMouseClick((ActionHandler)&GeoscapeState::btnGraphsClick);
256 	_btnGraphs->onKeyboardPress((ActionHandler)&GeoscapeState::btnGraphsClick, Options::keyGeoGraphs);
257 
258 	_btnUfopaedia->initText(_game->getResourcePack()->getFont("FONT_GEO_BIG"), _game->getResourcePack()->getFont("FONT_GEO_SMALL"), _game->getLanguage());
259 	_btnUfopaedia->setColor(Palette::blockOffset(15)+6);
260 	_btnUfopaedia->setTextColor(Palette::blockOffset(15)+5);
261 	_btnUfopaedia->setText(tr("STR_UFOPAEDIA_UC"));
262 	_btnUfopaedia->onMouseClick((ActionHandler)&GeoscapeState::btnUfopaediaClick);
263 	_btnUfopaedia->onKeyboardPress((ActionHandler)&GeoscapeState::btnUfopaediaClick, Options::keyGeoUfopedia);
264 
265 	_btnOptions->initText(_game->getResourcePack()->getFont("FONT_GEO_BIG"), _game->getResourcePack()->getFont("FONT_GEO_SMALL"), _game->getLanguage());
266 	_btnOptions->setColor(Palette::blockOffset(15)+6);
267 	_btnOptions->setTextColor(Palette::blockOffset(15)+5);
268 	_btnOptions->setText(tr("STR_OPTIONS_UC"));
269 	_btnOptions->onMouseClick((ActionHandler)&GeoscapeState::btnOptionsClick);
270 	_btnOptions->onKeyboardPress((ActionHandler)&GeoscapeState::btnOptionsClick, Options::keyGeoOptions);
271 
272 	_btnFunding->initText(_game->getResourcePack()->getFont("FONT_GEO_BIG"), _game->getResourcePack()->getFont("FONT_GEO_SMALL"), _game->getLanguage());
273 	_btnFunding->setColor(Palette::blockOffset(15)+6);
274 	_btnFunding->setTextColor(Palette::blockOffset(15)+5);
275 	_btnFunding->setText(tr("STR_FUNDING_UC"));
276 	_btnFunding->onMouseClick((ActionHandler)&GeoscapeState::btnFundingClick);
277 	_btnFunding->onKeyboardPress((ActionHandler)&GeoscapeState::btnFundingClick, Options::keyGeoFunding);
278 
279 	_btn5Secs->initText(_game->getResourcePack()->getFont("FONT_GEO_BIG"), _game->getResourcePack()->getFont("FONT_GEO_SMALL"), _game->getLanguage());
280 	_btn5Secs->setBig();
281 	_btn5Secs->setColor(Palette::blockOffset(15)+6);
282 	_btn5Secs->setTextColor(Palette::blockOffset(15)+5);
283 	_btn5Secs->setText(tr("STR_5_SECONDS"));
284 	_btn5Secs->setGroup(&_timeSpeed);
285 	_btn5Secs->onKeyboardPress((ActionHandler)&GeoscapeState::btnTimerClick, Options::keyGeoSpeed1);
286 
287 	_btn1Min->initText(_game->getResourcePack()->getFont("FONT_GEO_BIG"), _game->getResourcePack()->getFont("FONT_GEO_SMALL"), _game->getLanguage());
288 	_btn1Min->setBig();
289 	_btn1Min->setColor(Palette::blockOffset(15)+6);
290 	_btn1Min->setTextColor(Palette::blockOffset(15)+5);
291 	_btn1Min->setText(tr("STR_1_MINUTE"));
292 	_btn1Min->setGroup(&_timeSpeed);
293 	_btn1Min->onKeyboardPress((ActionHandler)&GeoscapeState::btnTimerClick, Options::keyGeoSpeed2);
294 
295 	_btn5Mins->initText(_game->getResourcePack()->getFont("FONT_GEO_BIG"), _game->getResourcePack()->getFont("FONT_GEO_SMALL"), _game->getLanguage());
296 	_btn5Mins->setBig();
297 	_btn5Mins->setColor(Palette::blockOffset(15)+6);
298 	_btn5Mins->setTextColor(Palette::blockOffset(15)+5);
299 	_btn5Mins->setText(tr("STR_5_MINUTES"));
300 	_btn5Mins->setGroup(&_timeSpeed);
301 	_btn5Mins->onKeyboardPress((ActionHandler)&GeoscapeState::btnTimerClick, Options::keyGeoSpeed3);
302 
303 	_btn30Mins->initText(_game->getResourcePack()->getFont("FONT_GEO_BIG"), _game->getResourcePack()->getFont("FONT_GEO_SMALL"), _game->getLanguage());
304 	_btn30Mins->setBig();
305 	_btn30Mins->setColor(Palette::blockOffset(15)+6);
306 	_btn30Mins->setTextColor(Palette::blockOffset(15)+5);
307 	_btn30Mins->setText(tr("STR_30_MINUTES"));
308 	_btn30Mins->setGroup(&_timeSpeed);
309 	_btn30Mins->onKeyboardPress((ActionHandler)&GeoscapeState::btnTimerClick, Options::keyGeoSpeed4);
310 
311 	_btn1Hour->initText(_game->getResourcePack()->getFont("FONT_GEO_BIG"), _game->getResourcePack()->getFont("FONT_GEO_SMALL"), _game->getLanguage());
312 	_btn1Hour->setBig();
313 	_btn1Hour->setColor(Palette::blockOffset(15)+6);
314 	_btn1Hour->setTextColor(Palette::blockOffset(15)+5);
315 	_btn1Hour->setText(tr("STR_1_HOUR"));
316 	_btn1Hour->setGroup(&_timeSpeed);
317 	_btn1Hour->onKeyboardPress((ActionHandler)&GeoscapeState::btnTimerClick, Options::keyGeoSpeed5);
318 
319 	_btn1Day->initText(_game->getResourcePack()->getFont("FONT_GEO_BIG"), _game->getResourcePack()->getFont("FONT_GEO_SMALL"), _game->getLanguage());
320 	_btn1Day->setBig();
321 	_btn1Day->setColor(Palette::blockOffset(15)+6);
322 	_btn1Day->setTextColor(Palette::blockOffset(15)+5);
323 	_btn1Day->setText(tr("STR_1_DAY"));
324 	_btn1Day->setGroup(&_timeSpeed);
325 	_btn1Day->onKeyboardPress((ActionHandler)&GeoscapeState::btnTimerClick, Options::keyGeoSpeed6);
326 
327 	_btnRotateLeft->onMousePress((ActionHandler)&GeoscapeState::btnRotateLeftPress);
328 	_btnRotateLeft->onMouseRelease((ActionHandler)&GeoscapeState::btnRotateLeftRelease);
329 	_btnRotateLeft->onKeyboardPress((ActionHandler)&GeoscapeState::btnRotateLeftPress, Options::keyGeoLeft);
330 	_btnRotateLeft->onKeyboardRelease((ActionHandler)&GeoscapeState::btnRotateLeftRelease, Options::keyGeoLeft);
331 
332 	_btnRotateRight->onMousePress((ActionHandler)&GeoscapeState::btnRotateRightPress);
333 	_btnRotateRight->onMouseRelease((ActionHandler)&GeoscapeState::btnRotateRightRelease);
334 	_btnRotateRight->onKeyboardPress((ActionHandler)&GeoscapeState::btnRotateRightPress, Options::keyGeoRight);
335 	_btnRotateRight->onKeyboardRelease((ActionHandler)&GeoscapeState::btnRotateRightRelease, Options::keyGeoRight);
336 
337 	_btnRotateUp->onMousePress((ActionHandler)&GeoscapeState::btnRotateUpPress);
338 	_btnRotateUp->onMouseRelease((ActionHandler)&GeoscapeState::btnRotateUpRelease);
339 	_btnRotateUp->onKeyboardPress((ActionHandler)&GeoscapeState::btnRotateUpPress, Options::keyGeoUp);
340 	_btnRotateUp->onKeyboardRelease((ActionHandler)&GeoscapeState::btnRotateUpRelease, Options::keyGeoUp);
341 
342 	_btnRotateDown->onMousePress((ActionHandler)&GeoscapeState::btnRotateDownPress);
343 	_btnRotateDown->onMouseRelease((ActionHandler)&GeoscapeState::btnRotateDownRelease);
344 	_btnRotateDown->onKeyboardPress((ActionHandler)&GeoscapeState::btnRotateDownPress, Options::keyGeoDown);
345 	_btnRotateDown->onKeyboardRelease((ActionHandler)&GeoscapeState::btnRotateDownRelease, Options::keyGeoDown);
346 
347 	_btnZoomIn->onMouseClick((ActionHandler)&GeoscapeState::btnZoomInLeftClick, SDL_BUTTON_LEFT);
348 	_btnZoomIn->onMouseClick((ActionHandler)&GeoscapeState::btnZoomInRightClick, SDL_BUTTON_RIGHT);
349 	_btnZoomIn->onKeyboardPress((ActionHandler)&GeoscapeState::btnZoomInLeftClick, Options::keyGeoZoomIn);
350 
351 	_btnZoomOut->onMouseClick((ActionHandler)&GeoscapeState::btnZoomOutLeftClick, SDL_BUTTON_LEFT);
352 	_btnZoomOut->onMouseClick((ActionHandler)&GeoscapeState::btnZoomOutRightClick, SDL_BUTTON_RIGHT);
353 	_btnZoomOut->onKeyboardPress((ActionHandler)&GeoscapeState::btnZoomOutLeftClick, Options::keyGeoZoomOut);
354 
355 	_sideTop->setColor(Palette::blockOffset(15)+6);
356 	_sideBottom->setColor(Palette::blockOffset(15)+6);
357 
358 	_txtFunds->setColor(Palette::blockOffset(15)+4);
359 	_txtFunds->setAlign(ALIGN_CENTER);
360 	_txtFunds->setVisible(Options::showFundsOnGeoscape);
361 
362 	_txtHour->setBig();
363 	_txtHour->setColor(Palette::blockOffset(15)+4);
364 	_txtHour->setAlign(ALIGN_RIGHT);
365 
366 	_txtHourSep->setBig();
367 	_txtHourSep->setColor(Palette::blockOffset(15)+4);
368 	_txtHourSep->setText(L":");
369 
370 	_txtMin->setBig();
371 	_txtMin->setColor(Palette::blockOffset(15)+4);
372 
373 	_txtMinSep->setBig();
374 	_txtMinSep->setColor(Palette::blockOffset(15)+4);
375 	_txtMinSep->setText(L":");
376 
377 	_txtSec->setColor(Palette::blockOffset(15)+4);
378 
379 	_txtWeekday->setColor(Palette::blockOffset(15)+4);
380 	_txtWeekday->setAlign(ALIGN_CENTER);
381 
382 	_txtDay->setColor(Palette::blockOffset(15)+4);
383 	_txtDay->setAlign(ALIGN_CENTER);
384 
385 	_txtMonth->setColor(Palette::blockOffset(15)+4);
386 	_txtMonth->setAlign(ALIGN_CENTER);
387 
388 	_txtYear->setColor(Palette::blockOffset(15)+4);
389 	_txtYear->setAlign(ALIGN_CENTER);
390 
391 	_txtDebug->setColor(Palette::blockOffset(15)+4);
392 
393 	if (Options::showFundsOnGeoscape)
394 	{
395 		_txtHour->setY(_txtHour->getY()+6);
396 		_txtHour->setSmall();
397 		_txtHourSep->setY(_txtHourSep->getY()+6);
398 		_txtHourSep->setSmall();
399 		_txtMin->setY(_txtMin->getY()+6);
400 		_txtMin->setSmall();
401 		_txtMinSep->setX(_txtMinSep->getX()-10);
402 		_txtMinSep->setY(_txtMinSep->getY()+6);
403 		_txtMinSep->setSmall();
404 		_txtSec->setX(_txtSec->getX()-10);
405 	}
406 
407 	_gameTimer->onTimer((StateHandler)&GeoscapeState::timeAdvance);
408 	_gameTimer->start();
409 
410 	_zoomInEffectTimer->onTimer((StateHandler)&GeoscapeState::zoomInEffect);
411 	_zoomOutEffectTimer->onTimer((StateHandler)&GeoscapeState::zoomOutEffect);
412 	_dogfightStartTimer->onTimer((StateHandler)&GeoscapeState::startDogfight);
413 
414 	timeDisplay();
415 }
416 
417 /**
418  * Deletes timers.
419  */
~GeoscapeState()420 GeoscapeState::~GeoscapeState()
421 {
422 	delete _gameTimer;
423 	delete _zoomInEffectTimer;
424 	delete _zoomOutEffectTimer;
425 	delete _dogfightStartTimer;
426 
427 	std::list<DogfightState*>::iterator it = _dogfights.begin();
428 	for(; it != _dogfights.end();)
429 	{
430 		delete *it;
431 		it = _dogfights.erase(it);
432 	}
433 	for(it = _dogfightsToBeStarted.begin(); it != _dogfightsToBeStarted.end();)
434 	{
435 		delete *it;
436 		it = _dogfightsToBeStarted.erase(it);
437 	}
438 }
439 
440 /**
441  * Handle blitting of Geoscape and Dogfights.
442  */
blit()443 void GeoscapeState::blit()
444 {
445 	State::blit();
446 	for(std::list<DogfightState*>::iterator it = _dogfights.begin(); it != _dogfights.end(); ++it)
447 	{
448 		(*it)->blit();
449 	}
450 }
451 /**
452  * Handle key shortcuts.
453  * @param action Pointer to an action.
454  */
handle(Action * action)455 void GeoscapeState::handle(Action *action)
456 {
457 	if (_dogfights.size() == _minimizedDogfights)
458 	{
459 		State::handle(action);
460 	}
461 
462 	if (action->getDetails()->type == SDL_KEYDOWN)
463 	{
464 		// "ctrl-d" - enable debug mode
465 		if (Options::debug && action->getDetails()->key.keysym.sym == SDLK_d && (SDL_GetModState() & KMOD_CTRL) != 0)
466 		{
467 			_game->getSavedGame()->setDebugMode();
468 			if (_game->getSavedGame()->getDebugMode())
469 			{
470 				_txtDebug->setText(L"DEBUG MODE");
471 			}
472 			else
473 			{
474 				_txtDebug->setText(L"");
475 			}
476 		}
477 		// quick save and quick load
478 		else if (!_game->getSavedGame()->isIronman())
479 		{
480 			if (action->getDetails()->key.keysym.sym == Options::keyQuickSave)
481 			{
482 				popup(new SaveGameState(_game, OPT_GEOSCAPE, SAVE_QUICK));
483 			}
484 			else if (action->getDetails()->key.keysym.sym == Options::keyQuickLoad)
485 			{
486 				popup(new LoadGameState(_game, OPT_GEOSCAPE, SAVE_QUICK));
487 			}
488 		}
489 	}
490 	if (!_dogfights.empty())
491 	{
492 		for(std::list<DogfightState*>::iterator it = _dogfights.begin(); it != _dogfights.end(); ++it)
493 		{
494 			(*it)->handle(action);
495 		}
496 		_minimizedDogfights = minimizedDogfightsCount();
497 	}
498 }
499 
500 /**
501  * Updates the timer display and resets the palette
502  * since it's bound to change on other screens.
503  */
init()504 void GeoscapeState::init()
505 {
506 	State::init();
507 	timeDisplay();
508 
509 	_globe->onMouseClick((ActionHandler)&GeoscapeState::globeClick);
510 	_globe->onMouseOver(0);
511 	_globe->rotateStop();
512 	_globe->setFocus(true);
513 	_globe->draw();
514 
515 	// Pop up save screen if it's a new ironman game
516 	if (_game->getSavedGame()->isIronman() && _game->getSavedGame()->getName().empty())
517 	{
518 		popup(new ListSaveState(_game, OPT_GEOSCAPE));
519 	}
520 
521 	// Set music if it's not already playing
522 	if (_dogfights.empty() && !_dogfightStartTimer->isRunning())
523 	{
524 		if (_game->getSavedGame()->getMonthsPassed() == -1)
525 		{
526 			_game->getResourcePack()->playMusic("GMGEO1");
527 		}
528 		else
529 		{
530 			_game->getResourcePack()->playMusic("GMGEO", true);
531 		}
532 	}
533 	_globe->unsetNewBaseHover();
534 }
535 
536 /**
537  * Runs the game timer and handles popups.
538  */
think()539 void GeoscapeState::think()
540 {
541 	State::think();
542 
543 	_zoomInEffectTimer->think(this, 0);
544 	_zoomOutEffectTimer->think(this, 0);
545 	_dogfightStartTimer->think(this, 0);
546 
547 	if (_game->getSavedGame()->getMonthsPassed() == -1)
548 	{
549 		_game->getSavedGame()->addMonth();
550 		determineAlienMissions(true);
551 		setupTerrorMission();
552 		_game->getSavedGame()->setFunds(_game->getSavedGame()->getFunds() - (_game->getSavedGame()->getBaseMaintenance() - _game->getSavedGame()->getBases()->front()->getPersonnelMaintenance()));
553 	}
554 	if (_popups.empty() && _dogfights.empty() && (!_zoomInEffectTimer->isRunning() || _zoomInEffectDone) && (!_zoomOutEffectTimer->isRunning() || _zoomOutEffectDone))
555 	{
556 		// Handle timers
557 		_gameTimer->think(this, 0);
558 	}
559 	else
560 	{
561 		if (!_dogfights.empty() || _minimizedDogfights != 0)
562 		{
563 			handleDogfights();
564 		}
565 		if (!_popups.empty())
566 		{
567 			// Handle popups
568 			_globe->rotateStop();
569 			_game->pushState(_popups.front());
570 			_popups.erase(_popups.begin());
571 		}
572 	}
573 }
574 
575 /**
576  * Updates the Geoscape clock with the latest
577  * game time and date in human-readable format. (+Funds)
578  */
timeDisplay()579 void GeoscapeState::timeDisplay()
580 {
581 	if (Options::showFundsOnGeoscape)
582 	{
583 		_txtFunds->setText(Text::formatFunding(_game->getSavedGame()->getFunds()));
584 	}
585 
586 	std::wostringstream ss;
587 	ss << std::setfill(L'0') << std::setw(2) << _game->getSavedGame()->getTime()->getSecond();
588 	_txtSec->setText(ss.str());
589 
590 	std::wostringstream ss2;
591 	ss2 << std::setfill(L'0') << std::setw(2) << _game->getSavedGame()->getTime()->getMinute();
592 	_txtMin->setText(ss2.str());
593 
594 	std::wostringstream ss3;
595 	ss3 << _game->getSavedGame()->getTime()->getHour();
596 	_txtHour->setText(ss3.str());
597 
598 	std::wostringstream ss4;
599 	ss4 << _game->getSavedGame()->getTime()->getDayString(_game->getLanguage());
600 	_txtDay->setText(ss4.str());
601 
602 	_txtWeekday->setText(tr(_game->getSavedGame()->getTime()->getWeekdayString()));
603 
604 	_txtMonth->setText(tr(_game->getSavedGame()->getTime()->getMonthString()));
605 
606 	std::wostringstream ss5;
607 	ss5 << _game->getSavedGame()->getTime()->getYear();
608 	_txtYear->setText(ss5.str());
609 }
610 
611 /**
612  * Advances the game timer according to
613  * the timer speed set, and calls the respective
614  * triggers. The timer always advances in "5 secs"
615  * cycles, regardless of the speed, otherwise it might
616  * skip important steps. Instead, it just keeps advancing
617  * the timer until the next speed step (eg. the next day
618  * on 1 Day speed) or until an event occurs, since updating
619  * the screen on each step would become cumbersomely slow.
620  */
timeAdvance()621 void GeoscapeState::timeAdvance()
622 {
623 	int timeSpan = 0;
624 	if (_timeSpeed == _btn5Secs)
625 	{
626 		timeSpan = 1;
627 	}
628 	else if (_timeSpeed == _btn1Min)
629 	{
630 		timeSpan = 12;
631 	}
632 	else if (_timeSpeed == _btn5Mins)
633 	{
634 		timeSpan = 12 * 5;
635 	}
636 	else if (_timeSpeed == _btn30Mins)
637 	{
638 		timeSpan = 12 * 5 * 6;
639 	}
640 	else if (_timeSpeed == _btn1Hour)
641 	{
642 		timeSpan = 12 * 5 * 6 * 2;
643 	}
644 	else if (_timeSpeed == _btn1Day)
645 	{
646 		timeSpan = 12 * 5 * 6 * 2 * 24;
647 	}
648 
649 	for (int i = 0; i < timeSpan && !_pause; ++i)
650 	{
651 		TimeTrigger trigger;
652 		trigger = _game->getSavedGame()->getTime()->advance();
653 		switch (trigger)
654 		{
655 		case TIME_1MONTH:
656 			time1Month();
657 		case TIME_1DAY:
658 			time1Day();
659 		case TIME_1HOUR:
660 			time1Hour();
661 		case TIME_30MIN:
662 			time30Minutes();
663 		case TIME_10MIN:
664 			time10Minutes();
665 		case TIME_5SEC:
666 			time5Seconds();
667 		}
668 	}
669 
670 	_pause = !_dogfightsToBeStarted.empty();
671 
672 	timeDisplay();
673 	_globe->draw();
674 }
675 
676 /**
677  * Takes care of any game logic that has to
678  * run every game second, like craft movement.
679  */
time5Seconds()680 void GeoscapeState::time5Seconds()
681 {
682 	// Game over if there are no more bases.
683 	if (_game->getSavedGame()->getBases()->empty())
684 	{
685 		popup(new DefeatState(_game));
686 		return;
687 	}
688 
689 	// Handle UFO logic
690 	for (std::vector<Ufo*>::iterator i = _game->getSavedGame()->getUfos()->begin(); i != _game->getSavedGame()->getUfos()->end(); ++i)
691 	{
692 		switch ((*i)->getStatus())
693 		{
694 		case Ufo::FLYING:
695 			if (!_zoomInEffectTimer->isRunning() && !_zoomOutEffectTimer->isRunning())
696 			{
697 				(*i)->think();
698 				if ((*i)->reachedDestination())
699 				{
700 					size_t terrorSiteCount = _game->getSavedGame()->getTerrorSites()->size();
701 					AlienMission *mission = (*i)->getMission();
702 					bool detected = (*i)->getDetected();
703 					mission->ufoReachedWaypoint(**i, *_game, *_globe);
704 					if (detected != (*i)->getDetected() && !(*i)->getFollowers()->empty())
705 					{
706 						if (!((*i)->getTrajectory().getID() == "__RETALIATION_ASSAULT_RUN" && (*i)->getStatus() ==  Ufo::LANDED))
707 							popup(new UfoLostState(_game, (*i)->getName(_game->getLanguage())));
708 					}
709 					if (terrorSiteCount < _game->getSavedGame()->getTerrorSites()->size())
710 					{
711 						TerrorSite *ts = _game->getSavedGame()->getTerrorSites()->back();
712 						const City *city = _game->getRuleset()->locateCity(ts->getLongitude(), ts->getLatitude());
713 						assert(city);
714 						popup(new AlienTerrorState(_game, ts, city->getName(), this));
715 					}
716 					// If UFO was destroyed, don't spawn missions
717 					if ((*i)->getStatus() == Ufo::DESTROYED)
718 						return;
719 					if (Base *base = dynamic_cast<Base*>((*i)->getDestination()))
720 					{
721 						mission->setWaveCountdown(30 * (RNG::generate(0, 48) + 400));
722 						(*i)->setDestination(0);
723 						base->setupDefenses();
724 						timerReset();
725 						if (!base->getDefenses()->empty())
726 						{
727 							popup(new BaseDefenseState(_game, base, *i, this));
728 						}
729 						else
730 						{
731                             handleBaseDefense(base, *i);
732                             return;
733 						}
734 					}
735 				}
736 			}
737 			break;
738 		case Ufo::LANDED:
739 			(*i)->think();
740 			if ((*i)->getSecondsRemaining() == 0)
741 			{
742 				AlienMission *mission = (*i)->getMission();
743 				bool detected = (*i)->getDetected();
744 				mission->ufoLifting(**i, *_game, *_globe);
745 				if (detected != (*i)->getDetected() && !(*i)->getFollowers()->empty())
746 				{
747 					popup(new UfoLostState(_game, (*i)->getName(_game->getLanguage())));
748 				}
749 			}
750 			break;
751 		case Ufo::CRASHED:
752 			(*i)->think();
753 			if ((*i)->getSecondsRemaining() == 0)
754 			{
755 				(*i)->setDetected(false);
756 				(*i)->setStatus(Ufo::DESTROYED);
757 			}
758 			break;
759 		case Ufo::DESTROYED:
760 			// Nothing to do
761 			break;
762 		}
763 	}
764 
765 	// Handle craft logic
766 	for (std::vector<Base*>::iterator i = _game->getSavedGame()->getBases()->begin(); i != _game->getSavedGame()->getBases()->end(); ++i)
767 	{
768 		for (std::vector<Craft*>::iterator j = (*i)->getCrafts()->begin(); j != (*i)->getCrafts()->end();)
769 		{
770 			if ((*j)->isDestroyed())
771 			{
772 				for(std::vector<Country*>::iterator country = _game->getSavedGame()->getCountries()->begin(); country != _game->getSavedGame()->getCountries()->end(); ++country)
773 				{
774 					if ((*country)->getRules()->insideCountry((*j)->getLongitude(), (*j)->getLatitude()))
775 					{
776 						(*country)->addActivityXcom(-(*j)->getRules()->getScore());
777 						break;
778 					}
779 				}
780 				for(std::vector<Region*>::iterator region = _game->getSavedGame()->getRegions()->begin(); region != _game->getSavedGame()->getRegions()->end(); ++region)
781 				{
782 					if ((*region)->getRules()->insideRegion((*j)->getLongitude(), (*j)->getLatitude()))
783 					{
784 						(*region)->addActivityXcom(-(*j)->getRules()->getScore());
785 						break;
786 					}
787 				}
788 				// if a transport craft has been shot down, kill all the soldiers on board.
789 				if ((*j)->getRules()->getSoldiers() > 0)
790 				{
791 					for (std::vector<Soldier*>::iterator k = (*i)->getSoldiers()->begin(); k != (*i)->getSoldiers()->end();)
792 					{
793 						if ((*k)->getCraft() == (*j))
794 						{
795 							SoldierDeath *death = new SoldierDeath();
796 							death->setTime(*_game->getSavedGame()->getTime());
797 							(*k)->die(death);
798 							_game->getSavedGame()->getDeadSoldiers()->push_back((*k));
799 							k = (*i)->getSoldiers()->erase(k);
800 						}
801 						else
802 						{
803 							++k;
804 						}
805 					}
806 				}
807 				delete *j;
808 				j = (*i)->getCrafts()->erase(j);
809 				continue;
810 			}
811 			if ((*j)->getDestination() != 0)
812 			{
813 				Ufo* u = dynamic_cast<Ufo*>((*j)->getDestination());
814 				if (u != 0 && !u->getDetected())
815 				{
816 					if (u->getTrajectory().getID() == "__RETALIATION_ASSAULT_RUN" && (u->getStatus() == Ufo::LANDED || u->getStatus() == Ufo::DESTROYED))
817 					{
818 						(*j)->returnToBase();
819 					}
820 					else
821 					{
822 						(*j)->setDestination(0);
823 						Waypoint *w = new Waypoint();
824 						w->setLongitude(u->getLongitude());
825 						w->setLatitude(u->getLatitude());
826 						w->setId(u->getId());
827 						popup(new GeoscapeCraftState(_game, (*j), _globe, w));
828 					}
829 				}
830 				if (u != 0 && u->getStatus() == Ufo::DESTROYED)
831 				{
832 					(*j)->returnToBase();
833 				}
834 			}
835 			if (!_zoomInEffectTimer->isRunning() && !_zoomOutEffectTimer->isRunning())
836 			{
837 				(*j)->think();
838 			}
839 			if ((*j)->reachedDestination())
840 			{
841 				Ufo* u = dynamic_cast<Ufo*>((*j)->getDestination());
842 				Waypoint *w = dynamic_cast<Waypoint*>((*j)->getDestination());
843 				TerrorSite* t = dynamic_cast<TerrorSite*>((*j)->getDestination());
844 				AlienBase* b = dynamic_cast<AlienBase*>((*j)->getDestination());
845 				if (u != 0)
846 				{
847 					switch (u->getStatus())
848 					{
849 					case Ufo::FLYING:
850 						// Not more than 4 interceptions at a time.
851 						if (_dogfights.size() + _dogfightsToBeStarted.size() >= 4)
852 						{
853 							++j;
854 							continue;
855 						}
856 						if (!(*j)->isInDogfight() && !(*j)->getDistance(u))
857 						{
858 							_dogfightsToBeStarted.push_back(new DogfightState(_game, _globe, (*j), u));
859 
860 							if (!_dogfightStartTimer->isRunning())
861 							{
862 								_pause = true;
863 								timerReset();
864 								_globe->center((*j)->getLongitude(), (*j)->getLatitude());
865 								startDogfight();
866 								_dogfightStartTimer->start();
867 							}
868 							_game->getResourcePack()->playMusic("GMINTER");
869 						}
870 						break;
871 					case Ufo::LANDED:
872 					case Ufo::CRASHED:
873 					case Ufo::DESTROYED: // Just before expiration
874 						if ((*j)->getNumSoldiers() > 0 || (*j)->getNumVehicles() > 0)
875 						{
876 							if (!(*j)->isInDogfight())
877 							{
878 								// look up polygons texture
879 								int texture, shade;
880 								_globe->getPolygonTextureAndShade(u->getLongitude(), u->getLatitude(), &texture, &shade);
881 								timerReset();
882 								popup(new ConfirmLandingState(_game, *j, texture, shade));
883 							}
884 						}
885 						else if (u->getStatus() != Ufo::LANDED)
886 						{
887 							(*j)->returnToBase();
888 						}
889 						break;
890 					}
891 				}
892 				else if (w != 0)
893 				{
894 					popup(new CraftPatrolState(_game, (*j), _globe));
895 					(*j)->setDestination(0);
896 				}
897 				else if (t != 0)
898 				{
899 					if ((*j)->getNumSoldiers() > 0)
900 					{
901 						// look up polygons texture
902 						int texture, shade;
903 						_globe->getPolygonTextureAndShade(t->getLongitude(), t->getLatitude(), &texture, &shade);
904 						timerReset();
905 						popup(new ConfirmLandingState(_game, *j, texture, shade));
906 					}
907 					else
908 					{
909 						(*j)->returnToBase();
910 					}
911 				}
912 				else if (b != 0)
913 				{
914 					if (b->isDiscovered())
915 					{
916 						if ((*j)->getNumSoldiers() > 0)
917 						{
918 							int texture, shade;
919 							_globe->getPolygonTextureAndShade(b->getLongitude(), b->getLatitude(), &texture, &shade);
920 							timerReset();
921 							popup(new ConfirmLandingState(_game, *j, texture, shade));
922 						}
923 						else
924 						{
925 							(*j)->returnToBase();
926 						}
927 					}
928 				}
929 			}
930 			 ++j;
931 		}
932 	}
933 
934 	// Clean up dead UFOs and end dogfights which were minimized.
935 	for (std::vector<Ufo*>::iterator i = _game->getSavedGame()->getUfos()->begin(); i != _game->getSavedGame()->getUfos()->end();)
936 	{
937 		if ((*i)->getStatus() == Ufo::DESTROYED)
938 		{
939 			if (!(*i)->getFollowers()->empty())
940 			{
941 				// Remove all dogfights with this UFO.
942 				for(std::list<DogfightState*>::iterator d = _dogfights.begin(); d != _dogfights.end();)
943 				{
944 					if ((*d)->getUfo() == (*i))
945 					{
946 						delete *d;
947 						d = _dogfights.erase(d);
948 					}
949 					else
950 					{
951 						++d;
952 					}
953 				}
954 			}
955 			delete *i;
956 			i = _game->getSavedGame()->getUfos()->erase(i);
957 		}
958 		else
959 		{
960 			++i;
961 		}
962 	}
963 
964 	// Clean up unused waypoints
965 	for (std::vector<Waypoint*>::iterator i = _game->getSavedGame()->getWaypoints()->begin(); i != _game->getSavedGame()->getWaypoints()->end();)
966 	{
967 		if ((*i)->getFollowers()->empty())
968 		{
969 			delete *i;
970 			i = _game->getSavedGame()->getWaypoints()->erase(i);
971 		}
972 		else
973 		{
974 			++i;
975 		}
976 	}
977 }
978 
979 /**
980  * Functor that attempt to detect an XCOM base.
981  */
982 class DetectXCOMBase: public std::unary_function<Ufo *, bool>
983 {
984 public:
985 	/// Create a detector for the given base.
DetectXCOMBase(const Base & base,int difficulty)986 	DetectXCOMBase(const Base &base, int difficulty) : _base(base), _difficulty(difficulty) { /* Empty by design.  */ }
987 	/// Attempt detection
988 	bool operator()(const Ufo *ufo) const;
989 private:
990 	const Base &_base;	//!< The target base.
991 	const int _difficulty;
992 };
993 
994 /**
995  * Only UFOs within detection range of the base have a chance to detect it.
996  * @param ufo Pointer to the UFO attempting detection.
997  * @return If the base is detected by @a ufo.
998  */
operator ()(const Ufo * ufo) const999 bool DetectXCOMBase::operator()(const Ufo *ufo) const
1000 {
1001 	if ((ufo->getMissionType() != "STR_ALIEN_RETALIATION" && !Options::aggressiveRetaliation) || // only UFOs on retaliation missions actively scan for bases
1002 		ufo->getTrajectory().getID() == "__RETALIATION_ASSAULT_RUN" || 	                                    // UFOs attacking a base don't detect!
1003 		ufo->isCrashed() ||                                                                                 // Crashed UFOs don't detect!
1004 		_base.getDistance(ufo) >= 80 * (1 / 60.0) * (M_PI / 180.0))                                         // UFOs have a detection range of 80 XCOM units.
1005 	{
1006 		return false;
1007 	}
1008 	return RNG::percent(_base.getDetectionChance(_difficulty));
1009 }
1010 
1011 /**
1012  * Functor that marks an XCOM base for retaliation.
1013  * This is required because of the iterator type.
1014  */
1015 struct SetRetaliationTarget: public std::unary_function<std::map<const Region *, Base *>::value_type, void>
1016 {
1017 	/// Mark as a valid retaliation target.
operator ()OpenXcom::SetRetaliationTarget1018 	void operator()(const argument_type &iter) const { iter.second->setRetaliationTarget(true); }
1019 };
1020 
1021 /**
1022  * Takes care of any game logic that has to
1023  * run every game ten minutes, like fuel consumption.
1024  */
time10Minutes()1025 void GeoscapeState::time10Minutes()
1026 {
1027 	for (std::vector<Base*>::iterator i = _game->getSavedGame()->getBases()->begin(); i != _game->getSavedGame()->getBases()->end(); ++i)
1028 	{
1029 		// Fuel consumption for XCOM craft.
1030 		for (std::vector<Craft*>::iterator j = (*i)->getCrafts()->begin(); j != (*i)->getCrafts()->end(); ++j)
1031 		{
1032 			if ((*j)->getStatus() == "STR_OUT")
1033 			{
1034 				(*j)->consumeFuel();
1035 				if (!(*j)->getLowFuel() && (*j)->getFuel() <= (*j)->getFuelLimit())
1036 				{
1037 					(*j)->setLowFuel(true);
1038 					(*j)->returnToBase();
1039 					popup(new LowFuelState(_game, (*j), this));
1040 				}
1041 
1042 				if ((*j)->getDestination() == 0)
1043 				{
1044 					for(std::vector<AlienBase*>::iterator b = _game->getSavedGame()->getAlienBases()->begin(); b != _game->getSavedGame()->getAlienBases()->end(); b++)
1045 					{
1046 						double range = (1696 * (1 / 60.0) * (M_PI / 180));
1047 						if ((*j)->getDistance(*b) <= range)
1048 						{
1049 							// TODO: move the detection range to the ruleset, or use the pre-defined one (which is 600, but detection range should be 500).
1050 							if (RNG::percent(50-((*j)->getDistance(*b) / range) * 50) && !(*b)->isDiscovered())
1051 							{
1052 								(*b)->setDiscovered(true);
1053 							}
1054 						}
1055 					}
1056 				}
1057 			}
1058 		}
1059 	}
1060 	int diff = (int)(_game->getSavedGame()->getDifficulty());
1061 	if (Options::aggressiveRetaliation)
1062 	{
1063 		// Detect as many bases as possible.
1064 		for (std::vector<Base*>::iterator iBase = _game->getSavedGame()->getBases()->begin(); iBase != _game->getSavedGame()->getBases()->end(); ++iBase)
1065 		{
1066 			// Find a UFO that detected this base, if any.
1067 			std::vector<Ufo*>::const_iterator uu = std::find_if (_game->getSavedGame()->getUfos()->begin(), _game->getSavedGame()->getUfos()->end(), DetectXCOMBase(**iBase, diff));
1068 			if (uu != _game->getSavedGame()->getUfos()->end())
1069 			{
1070 				// Base found
1071 				(*iBase)->setRetaliationTarget(true);
1072 			}
1073 		}
1074 	}
1075 	else
1076 	{
1077 		// Only remember last base in each region.
1078 		std::map<const Region *, Base *> discovered;
1079 		for (std::vector<Base*>::iterator iBase = _game->getSavedGame()->getBases()->begin(); iBase != _game->getSavedGame()->getBases()->end(); ++iBase)
1080 		{
1081 			// Find a UFO that detected this base, if any.
1082 			std::vector<Ufo*>::const_iterator uu = std::find_if (_game->getSavedGame()->getUfos()->begin(), _game->getSavedGame()->getUfos()->end(), DetectXCOMBase(**iBase, diff));
1083 			if (uu != _game->getSavedGame()->getUfos()->end())
1084 			{
1085 				discovered[_game->getSavedGame()->locateRegion(**iBase)] = *iBase;
1086 			}
1087 		}
1088 		// Now mark the bases as discovered.
1089 		std::for_each(discovered.begin(), discovered.end(), SetRetaliationTarget());
1090 	}
1091 }
1092 
1093 /** @brief Call AlienMission::think() with proper parameters.
1094  * This function object calls AlienMission::think() with the proper parameters.
1095  */
1096 class callThink: public std::unary_function<AlienMission*, void>
1097 {
1098 public:
1099 	/// Store the parameters.
1100 	/**
1101 	 * @param game The game engine.
1102 	 * @param globe The globe object.
1103 	 */
callThink(Game & game,const Globe & globe)1104 	callThink(Game &game, const Globe &globe) : _game(game), _globe(globe) { /* Empty by design. */ }
1105 	/// Call AlienMission::think() with stored parameters.
operator ()(AlienMission * am) const1106 	void operator()(AlienMission *am) const { am->think(_game, _globe); }
1107 private:
1108 	Game &_game;
1109 	const Globe &_globe;
1110 };
1111 
1112 /** @brief Process a TerrorSite.
1113  * This function object will count down towards expiring a TerrorSite, and handle expired TerrorSites.
1114  * @param ts Pointer to terror site.
1115  * @return Has terror site expired?
1116  */
processTerrorSite(TerrorSite * ts) const1117 bool GeoscapeState::processTerrorSite(TerrorSite *ts) const
1118 {
1119 	if (ts->getSecondsRemaining() >= 30 * 60)
1120 	{
1121 		ts->setSecondsRemaining(ts->getSecondsRemaining() - 30 * 60);
1122 		return false;
1123 	}
1124 	if (!ts->getFollowers()->empty()) // CHEEKY EXPLOIT
1125 	{
1126 		return false;
1127 	}
1128 	// Score and delete it.
1129 	Region *region = _game->getSavedGame()->locateRegion(*ts);
1130 	if (region)
1131 	{
1132 		region->addActivityAlien(_game->getRuleset()->getAlienMission("STR_ALIEN_TERROR")->getPoints() * 100);
1133 		//kids, tell your folks... don't ignore terror sites.
1134 	}
1135 	for (std::vector<Country*>::iterator k = _game->getSavedGame()->getCountries()->begin(); k != _game->getSavedGame()->getCountries()->end(); ++k)
1136 	{
1137 		if ((*k)->getRules()->insideCountry(ts->getLongitude(), ts->getLatitude()))
1138 		{
1139 			(*k)->addActivityAlien(_game->getRuleset()->getAlienMission("STR_ALIEN_TERROR")->getPoints() * 100);
1140 			break;
1141 		}
1142 	}
1143 	delete ts;
1144 	return true;
1145 }
1146 
1147 /** @brief Advance time for crashed UFOs.
1148  * This function object will decrease the expiration timer for crashed UFOs.
1149  */
1150 struct expireCrashedUfo: public std::unary_function<Ufo*, void>
1151 {
1152 	/// Decrease UFO expiration timer.
operator ()OpenXcom::expireCrashedUfo1153 	void operator()(Ufo *ufo) const
1154 	{
1155 		if (ufo->getStatus() == Ufo::CRASHED)
1156 		{
1157 			if (ufo->getSecondsRemaining() >= 30 * 60)
1158 			{
1159 				ufo->setSecondsRemaining(ufo->getSecondsRemaining() - 30 * 60);
1160 				return;
1161 			}
1162 			// Marked expired UFOs for removal.
1163 			ufo->setStatus(Ufo::DESTROYED);
1164 		}
1165 	}
1166 };
1167 
1168 /**
1169  * Takes care of any game logic that has to
1170  * run every game half hour, like UFO detection.
1171  */
time30Minutes()1172 void GeoscapeState::time30Minutes()
1173 {
1174 	// Decrease mission countdowns
1175 	std::for_each(_game->getSavedGame()->getAlienMissions().begin(),
1176 		      _game->getSavedGame()->getAlienMissions().end(),
1177 		      callThink(*_game, *_globe));
1178 	// Remove finished missions
1179 	for (std::vector<AlienMission*>::iterator am = _game->getSavedGame()->getAlienMissions().begin();
1180 		am != _game->getSavedGame()->getAlienMissions().end();)
1181 	{
1182 		if ((*am)->isOver())
1183 		{
1184 			delete *am;
1185 			am = _game->getSavedGame()->getAlienMissions().erase(am);
1186 		}
1187 		else
1188 		{
1189 			++am;
1190 		}
1191 	}
1192 
1193 	// Handle crashed UFOs expiration
1194 	std::for_each(_game->getSavedGame()->getUfos()->begin(),
1195 		      _game->getSavedGame()->getUfos()->end(),
1196 		      expireCrashedUfo());
1197 
1198 
1199 	// Handle craft maintenance and alien base detection
1200 	for (std::vector<Base*>::iterator i = _game->getSavedGame()->getBases()->begin(); i != _game->getSavedGame()->getBases()->end(); ++i)
1201 	{
1202 		for (std::vector<Craft*>::iterator j = (*i)->getCrafts()->begin(); j != (*i)->getCrafts()->end(); ++j)
1203 		{
1204 			if ((*j)->getStatus() == "STR_REFUELLING")
1205 			{
1206 				std::string item = (*j)->getRules()->getRefuelItem();
1207 				if (item == "")
1208 				{
1209 					(*j)->refuel();
1210 				}
1211 				else
1212 				{
1213 					if ((*i)->getItems()->getItem(item) > 0)
1214 					{
1215 						(*i)->getItems()->removeItem(item);
1216 						(*j)->refuel();
1217 						(*j)->setLowFuel(false);
1218 					}
1219 					else if (!(*j)->getLowFuel())
1220 					{
1221 						std::wstring msg = tr("STR_NOT_ENOUGH_ITEM_TO_REFUEL_CRAFT_AT_BASE")
1222 										   .arg(tr(item))
1223 										   .arg((*j)->getName(_game->getLanguage()))
1224 										   .arg((*i)->getName());
1225 						popup(new CraftErrorState(_game, this, msg));
1226 						if ((*j)->getFuel() > 0)
1227 							(*j)->setStatus("STR_READY");
1228 						else
1229 							(*j)->setLowFuel(true);
1230 					}
1231 				}
1232 			}
1233 		}
1234 	}
1235 
1236 	// Handle UFO detection and give aliens points
1237 	for (std::vector<Ufo*>::iterator u = _game->getSavedGame()->getUfos()->begin(); u != _game->getSavedGame()->getUfos()->end(); ++u)
1238 	{
1239 		int points = 0;
1240 		switch ((*u)->getStatus())
1241 		{
1242 		case Ufo::LANDED:
1243 			points++;
1244 		case Ufo::FLYING:
1245 			points++;
1246 			// Get area
1247 			for (std::vector<Region*>::iterator k = _game->getSavedGame()->getRegions()->begin(); k != _game->getSavedGame()->getRegions()->end(); ++k)
1248 			{
1249 				if ((*k)->getRules()->insideRegion((*u)->getLongitude(), (*u)->getLatitude()))
1250 				{
1251 					//one point per UFO in-flight per half hour
1252 					(*k)->addActivityAlien(points);
1253 					break;
1254 				}
1255 			}
1256 			// Get country
1257 			for (std::vector<Country*>::iterator k = _game->getSavedGame()->getCountries()->begin(); k != _game->getSavedGame()->getCountries()->end(); ++k)
1258 			{
1259 				if ((*k)->getRules()->insideCountry((*u)->getLongitude(), (*u)->getLatitude()))
1260 				{
1261 					//one point per UFO in-flight per half hour
1262 					(*k)->addActivityAlien(points);
1263 					break;
1264 				}
1265 			}
1266 			if (!(*u)->getDetected())
1267 			{
1268 				bool detected = false, hyperdetected = false;
1269 				for (std::vector<Base*>::iterator b = _game->getSavedGame()->getBases()->begin(); !hyperdetected && b != _game->getSavedGame()->getBases()->end(); ++b)
1270 				{
1271 					switch ((*b)->detect(*u))
1272 					{
1273 					case 2:	// hyper-wave decoder
1274 						(*u)->setHyperDetected(true);
1275 						hyperdetected = true;
1276 					case 1: // conventional radar
1277 						detected = true;
1278 					}
1279 					for (std::vector<Craft*>::iterator c = (*b)->getCrafts()->begin(); !detected && c != (*b)->getCrafts()->end(); ++c)
1280 					{
1281 						if ((*c)->getStatus() == "STR_OUT" && (*c)->detect(*u))
1282 						{
1283 							detected = true;
1284 							break;
1285 						}
1286 					}
1287 				}
1288 				if (detected)
1289 				{
1290 					(*u)->setDetected(true);
1291 					popup(new UfoDetectedState(_game, (*u), this, true, (*u)->getHyperDetected()));
1292 				}
1293 			}
1294 			else
1295 			{
1296 				bool detected = false, hyperdetected = false;
1297 				for (std::vector<Base*>::iterator b = _game->getSavedGame()->getBases()->begin(); !hyperdetected && b != _game->getSavedGame()->getBases()->end(); ++b)
1298 				{
1299 					switch ((*b)->insideRadarRange(*u))
1300 					{
1301 					case 2:	// hyper-wave decoder
1302 						detected = true;
1303 						hyperdetected = true;
1304 						(*u)->setHyperDetected(true);
1305 						break;
1306 					case 1: // conventional radar
1307 						detected = true;
1308 						hyperdetected = (*u)->getHyperDetected();
1309 					}
1310 					for (std::vector<Craft*>::iterator c = (*b)->getCrafts()->begin(); !detected && c != (*b)->getCrafts()->end(); ++c)
1311 					{
1312 						if ((*c)->getStatus() == "STR_OUT" && (*c)->detect(*u))
1313 						{
1314 							detected = true;
1315 							hyperdetected = (*u)->getHyperDetected();
1316 							break;
1317 						}
1318 					}
1319 				}
1320 				if (!detected)
1321 				{
1322 					(*u)->setDetected(false);
1323 					(*u)->setHyperDetected(false);
1324 					if (!(*u)->getFollowers()->empty())
1325 					{
1326 						popup(new UfoLostState(_game, (*u)->getName(_game->getLanguage())));
1327 					}
1328 				}
1329 			}
1330 			break;
1331 		case Ufo::CRASHED:
1332 		case Ufo::DESTROYED:
1333 			break;
1334 		}
1335 	}
1336 
1337 	// Processes TerrorSites
1338 	for (std::vector<TerrorSite*>::iterator ts = _game->getSavedGame()->getTerrorSites()->begin();
1339 		ts != _game->getSavedGame()->getTerrorSites()->end();)
1340 	{
1341 		if (processTerrorSite(*ts))
1342 		{
1343 			ts = _game->getSavedGame()->getTerrorSites()->erase(ts);
1344 		}
1345 		else
1346 		{
1347 			++ts;
1348 		}
1349 	}
1350 }
1351 
1352 /**
1353  * Takes care of any game logic that has to
1354  * run every game hour, like transfers.
1355  */
time1Hour()1356 void GeoscapeState::time1Hour()
1357 {
1358 	// Handle craft maintenance
1359 	for (std::vector<Base*>::iterator i = _game->getSavedGame()->getBases()->begin(); i != _game->getSavedGame()->getBases()->end(); ++i)
1360 	{
1361 		for (std::vector<Craft*>::iterator j = (*i)->getCrafts()->begin(); j != (*i)->getCrafts()->end(); ++j)
1362 		{
1363 			if ((*j)->getStatus() == "STR_REPAIRS")
1364 			{
1365 				(*j)->repair();
1366 			}
1367 			else if ((*j)->getStatus() == "STR_REARMING")
1368 			{
1369 				std::string s = (*j)->rearm(_game->getRuleset());
1370 				if (s != "")
1371 				{
1372 					std::wstring msg = tr("STR_NOT_ENOUGH_ITEM_TO_REARM_CRAFT_AT_BASE")
1373 									   .arg(tr(s))
1374 									   .arg((*j)->getName(_game->getLanguage()))
1375 									   .arg((*i)->getName());
1376 					popup(new CraftErrorState(_game, this, msg));
1377 				}
1378 			}
1379 		}
1380 	}
1381 
1382 	// Handle transfers
1383 	bool window = false;
1384 	for (std::vector<Base*>::iterator i = _game->getSavedGame()->getBases()->begin(); i != _game->getSavedGame()->getBases()->end(); ++i)
1385 	{
1386 		for (std::vector<Transfer*>::iterator j = (*i)->getTransfers()->begin(); j != (*i)->getTransfers()->end(); ++j)
1387 		{
1388 			(*j)->advance(*i);
1389 			if (!window && (*j)->getHours() == 0)
1390 			{
1391 				window = true;
1392 			}
1393 		}
1394 	}
1395 	if (window)
1396 	{
1397 		popup(new ItemsArrivingState(_game, this));
1398 	}
1399 	// Handle Production
1400 	for (std::vector<Base*>::iterator i = _game->getSavedGame()->getBases()->begin(); i != _game->getSavedGame()->getBases()->end(); ++i)
1401 	{
1402 		std::map<Production*, productionProgress_e> toRemove;
1403 		for (std::vector<Production*>::const_iterator j = (*i)->getProductions().begin(); j != (*i)->getProductions().end(); ++j)
1404 		{
1405 			toRemove[(*j)] = (*j)->step((*i), _game->getSavedGame(), _game->getRuleset());
1406 		}
1407 		for (std::map<Production*, productionProgress_e>::iterator j = toRemove.begin(); j != toRemove.end(); ++j)
1408 		{
1409 			if (j->second > PROGRESS_NOT_COMPLETE)
1410 			{
1411 				(*i)->removeProduction (j->first);
1412 				popup(new ProductionCompleteState(_game, (*i),  tr(j->first->getRules()->getName()), this, j->second));
1413 			}
1414 		}
1415 
1416 		if (Options::storageLimitsEnforced && (*i)->storesOverfull())
1417 		{
1418 			popup(new ErrorMessageState(_game, tr("STR_STORAGE_EXCEEDED").arg((*i)->getName()).c_str(), _palette, Palette::blockOffset(15) + 1, "BACK13.SCR", 6));
1419 			popup(new SellState(_game, (*i)));
1420 		}
1421 	}
1422 }
1423 
1424 /**
1425  * This class will attempt to generate a supply mission for a base.
1426  * Each alien base has a 6/101 chance to generate a supply mission.
1427  */
1428 class GenerateSupplyMission: public std::unary_function<const AlienBase *, void>
1429 {
1430 public:
1431 	/// Store rules and game data references for later use.
GenerateSupplyMission(const Ruleset & ruleset,SavedGame & save)1432 	GenerateSupplyMission(const Ruleset &ruleset, SavedGame &save) : _ruleset(ruleset), _save(save) { /* Empty by design */ }
1433 	/// Check and spawn mission.
1434 	void operator()(const AlienBase *base) const;
1435 private:
1436 	const Ruleset &_ruleset;
1437 	SavedGame &_save;
1438 };
1439 
1440 /**
1441  * Check and create supply mission for the given base.
1442  * There is a 6/101 chance of the mission spawning.
1443  * @param base A pointer to the alien base.
1444  */
operator ()(const AlienBase * base) const1445 void GenerateSupplyMission::operator()(const AlienBase *base) const
1446 {
1447 	if (RNG::percent(6))
1448 	{
1449 		//Spawn supply mission for this base.
1450 		const RuleAlienMission &rule = *_ruleset.getAlienMission("STR_ALIEN_SUPPLY");
1451 		AlienMission *mission = new AlienMission(rule);
1452 		mission->setRegion(_save.locateRegion(*base)->getRules()->getType(), _ruleset);
1453 		mission->setId(_save.getId("ALIEN_MISSIONS"));
1454 		mission->setRace(base->getAlienRace());
1455 		mission->setAlienBase(base);
1456 		mission->start();
1457 		_save.getAlienMissions().push_back(mission);
1458 	}
1459 }
1460 
1461 /**
1462  * Takes care of any game logic that has to
1463  * run every game day, like constructions.
1464  */
time1Day()1465 void GeoscapeState::time1Day()
1466 {
1467 	for (std::vector<Base*>::iterator i = _game->getSavedGame()->getBases()->begin(); i != _game->getSavedGame()->getBases()->end(); ++i)
1468 	{
1469 		// Handle facility construction
1470 		for (std::vector<BaseFacility*>::iterator j = (*i)->getFacilities()->begin(); j != (*i)->getFacilities()->end(); ++j)
1471 		{
1472 			if ((*j)->getBuildTime() > 0)
1473 			{
1474 				(*j)->build();
1475 				if ((*j)->getBuildTime() == 0)
1476 				{
1477 					popup(new ProductionCompleteState(_game, (*i),  tr((*j)->getRules()->getType()), this, PROGRESS_CONSTRUCTION));
1478 				}
1479 			}
1480 		}
1481 		// Handle science project
1482 		std::vector<ResearchProject*> finished;
1483 		for(std::vector<ResearchProject*>::const_iterator iter = (*i)->getResearch().begin (); iter != (*i)->getResearch().end (); ++iter)
1484 		{
1485 			if ((*iter)->step())
1486 			{
1487 				finished.push_back(*iter);
1488 			}
1489 		}
1490 		for(std::vector<ResearchProject*>::const_iterator iter = finished.begin (); iter != finished.end (); ++iter)
1491 		{
1492 			(*i)->removeResearch(*iter);
1493 			RuleResearch * bonus = 0;
1494 			const RuleResearch * research = (*iter)->getRules ();
1495 			// If "researched" the live alien, his body sent to the stores.
1496 			if (Options::spendResearchedItems && research->needItem() && _game->getRuleset()->getUnit(research->getName()))
1497 			{
1498 				(*i)->getItems()->addItem(
1499 					_game->getRuleset()->getArmor(
1500 						_game->getRuleset()->getUnit(
1501 							research->getName()
1502 						)->getArmor()
1503 					)->getCorpseGeoscape()
1504 				); // ;)
1505 			}
1506 			if ((*iter)->getRules()->getGetOneFree().size() != 0)
1507 			{
1508 				std::vector<std::string> possibilities;
1509 				for(std::vector<std::string>::const_iterator f = (*iter)->getRules()->getGetOneFree().begin(); f != (*iter)->getRules()->getGetOneFree().end(); ++f)
1510 				{
1511 					bool newFound = true;
1512 					for(std::vector<const RuleResearch*>::const_iterator discovered = _game->getSavedGame()->getDiscoveredResearch().begin(); discovered != _game->getSavedGame()->getDiscoveredResearch().end(); ++discovered)
1513 					{
1514 						if (*f == (*discovered)->getName())
1515 						{
1516 							newFound = false;
1517 						}
1518 					}
1519 					if (newFound)
1520 					{
1521 						possibilities.push_back(*f);
1522 					}
1523 				}
1524 				if (possibilities.size() != 0)
1525 				{
1526 					size_t pick = RNG::generate(0, possibilities.size()-1);
1527 					std::string sel = possibilities.at(pick);
1528 					bonus = _game->getRuleset()->getResearch(sel);
1529 					_game->getSavedGame()->addFinishedResearch(bonus, _game->getRuleset ());
1530 					if (bonus->getLookup() != "")
1531 					{
1532 						_game->getSavedGame()->addFinishedResearch(_game->getRuleset()->getResearch(bonus->getLookup()), _game->getRuleset ());
1533 					}
1534 				}
1535 			}
1536 			const RuleResearch * newResearch = research;
1537 			std::string name = research->getLookup() == "" ? research->getName() : research->getLookup();
1538 			if (_game->getSavedGame()->isResearched(name))
1539 			{
1540 				newResearch = 0;
1541 			}
1542 			_game->getSavedGame()->addFinishedResearch(research, _game->getRuleset ());
1543 			if (research->getLookup() != "")
1544 			{
1545 				_game->getSavedGame()->addFinishedResearch(_game->getRuleset()->getResearch(research->getLookup()), _game->getRuleset ());
1546 			}
1547 			popup(new ResearchCompleteState(_game, newResearch, bonus));
1548 			std::vector<RuleResearch *> newPossibleResearch;
1549 			_game->getSavedGame()->getDependableResearch (newPossibleResearch, (*iter)->getRules(), _game->getRuleset(), *i);
1550 			std::vector<RuleManufacture *> newPossibleManufacture;
1551 			_game->getSavedGame()->getDependableManufacture (newPossibleManufacture, (*iter)->getRules(), _game->getRuleset(), *i);
1552 			timerReset();
1553 			// check for possible researching weapon before clip
1554 			if (newResearch)
1555 			{
1556 				RuleItem *item = _game->getRuleset()->getItem(newResearch->getName());
1557 				if (item && item->getBattleType() == BT_FIREARM && !item->getCompatibleAmmo()->empty())
1558 				{
1559 					RuleManufacture *man = _game->getRuleset()->getManufacture(item->getType());
1560 					if (man && !man->getRequirements().empty())
1561 					{
1562 						const std::vector<std::string> &req = man->getRequirements();
1563 						RuleItem *ammo = _game->getRuleset()->getItem(item->getCompatibleAmmo()->front());
1564 						if (ammo && std::find(req.begin(), req.end(), ammo->getType()) != req.end() && !_game->getSavedGame()->isResearched(req))
1565 						{
1566 							popup(new ResearchRequiredState(_game, item));
1567 						}
1568 					}
1569 				}
1570 			}
1571 
1572 			popup(new NewPossibleResearchState(_game, *i, newPossibleResearch));
1573 			if (!newPossibleManufacture.empty())
1574 			{
1575 				popup(new NewPossibleManufactureState(_game, *i, newPossibleManufacture));
1576 			}
1577 			// now iterate through all the bases and remove this project from their labs
1578 			for (std::vector<Base*>::iterator j = _game->getSavedGame()->getBases()->begin(); j != _game->getSavedGame()->getBases()->end(); ++j)
1579 			{
1580 				for (std::vector<ResearchProject*>::const_iterator iter2 = (*j)->getResearch().begin(); iter2 != (*j)->getResearch().end(); ++iter2)
1581 				{
1582 					if ((*iter)->getRules()->getName() == (*iter2)->getRules()->getName() &&
1583 						_game->getRuleset()->getUnit((*iter2)->getRules()->getName()) == 0)
1584 					{
1585 						(*j)->removeResearch(*iter2);
1586 						break;
1587 					}
1588 				}
1589 			}
1590 			delete(*iter);
1591 		}
1592 		// Handle soldier wounds
1593 		for (std::vector<Soldier*>::iterator j = (*i)->getSoldiers()->begin(); j != (*i)->getSoldiers()->end(); ++j)
1594 		{
1595 			if ((*j)->getWoundRecovery() > 0)
1596 			{
1597 				(*j)->heal();
1598 			}
1599 		}
1600 		// Handle psionic training
1601 		if ((*i)->getAvailablePsiLabs() > 0 && Options::anytimePsiTraining)
1602 		{
1603 			for (std::vector<Soldier*>::const_iterator s = (*i)->getSoldiers()->begin(); s != (*i)->getSoldiers()->end(); ++s)
1604 			{
1605 				(*s)->trainPsi1Day();
1606 				(*s)->calcStatString(_game->getRuleset()->getStatStrings(), (Options::psiStrengthEval && _game->getSavedGame()->isResearched(_game->getRuleset()->getPsiRequirements())));
1607 			}
1608 		}
1609 	}
1610 	// handle regional and country points for alien bases
1611 	for(std::vector<AlienBase*>::const_iterator b = _game->getSavedGame()->getAlienBases()->begin(); b != _game->getSavedGame()->getAlienBases()->end(); ++b)
1612 	{
1613 		for (std::vector<Region*>::iterator k = _game->getSavedGame()->getRegions()->begin(); k != _game->getSavedGame()->getRegions()->end(); ++k)
1614 		{
1615 			if ((*k)->getRules()->insideRegion((*b)->getLongitude(), (*b)->getLatitude()))
1616 			{
1617 				(*k)->addActivityAlien(_game->getRuleset()->getAlienMission("STR_ALIEN_BASE")->getPoints() / 10);
1618 				break;
1619 			}
1620 		}
1621 		for (std::vector<Country*>::iterator k = _game->getSavedGame()->getCountries()->begin(); k != _game->getSavedGame()->getCountries()->end(); ++k)
1622 		{
1623 			if ((*k)->getRules()->insideCountry((*b)->getLongitude(), (*b)->getLatitude()))
1624 			{
1625 				(*k)->addActivityAlien(_game->getRuleset()->getAlienMission("STR_ALIEN_BASE")->getPoints() / 10);
1626 				break;
1627 			}
1628 		}
1629 	}
1630 
1631 	// Handle resupply of alien bases.
1632 	std::for_each(_game->getSavedGame()->getAlienBases()->begin(), _game->getSavedGame()->getAlienBases()->end(),
1633 		      GenerateSupplyMission(*_game->getRuleset(), *_game->getSavedGame()));
1634 
1635 	// Autosave 3 times a month
1636 	int day = _game->getSavedGame()->getTime()->getDay();
1637 	if (day == 10 || day == 20)
1638 	{
1639 		if (_game->getSavedGame()->isIronman())
1640 		{
1641 			popup(new SaveGameState(_game, OPT_GEOSCAPE, SAVE_IRONMAN));
1642 		}
1643 		else if (Options::autosave)
1644 		{
1645 			popup(new SaveGameState(_game, OPT_GEOSCAPE, SAVE_AUTO_GEOSCAPE));
1646 		}
1647 	}
1648 }
1649 
1650 /**
1651  * Takes care of any game logic that has to
1652  * run every game month, like funding.
1653  */
time1Month()1654 void GeoscapeState::time1Month()
1655 {
1656 	_game->getSavedGame()->addMonth();
1657 
1658 	int monthsPassed = _game->getSavedGame()->getMonthsPassed();
1659 	bool newRetaliation = false;
1660 
1661 	// Determine alien mission for this month.
1662 	determineAlienMissions();
1663 	if (monthsPassed > 5)
1664 		determineAlienMissions();
1665 
1666 	setupTerrorMission();
1667 
1668 	if (monthsPassed >= 14 - (int)(_game->getSavedGame()->getDifficulty())
1669 		|| _game->getSavedGame()->isResearched("STR_THE_MARTIAN_SOLUTION"))
1670 	{
1671 		newRetaliation = true;
1672 	}
1673 
1674 	// Handle Psi-Training and initiate a new retaliation mission, if applicable
1675 	bool psi = false;
1676 	for(std::vector<Base*>::const_iterator b = _game->getSavedGame()->getBases()->begin(); b != _game->getSavedGame()->getBases()->end(); ++b)
1677 	{
1678 		if (newRetaliation)
1679 		{
1680 			for (std::vector<Region*>::iterator i = _game->getSavedGame()->getRegions()->begin(); i != _game->getSavedGame()->getRegions()->end(); ++i)
1681 			{
1682 				if ((*i)->getRules()->insideRegion((*b)->getLongitude(), (*b)->getLatitude()))
1683 				{
1684 					if (!_game->getSavedGame()->getAlienMission((*i)->getRules()->getType(), "STR_ALIEN_RETALIATION"))
1685 					{
1686 						const RuleAlienMission &rule = *_game->getRuleset()->getAlienMission("STR_ALIEN_RETALIATION");
1687 						AlienMission *mission = new AlienMission(rule);
1688 						mission->setId(_game->getSavedGame()->getId("ALIEN_MISSIONS"));
1689 						mission->setRegion((*i)->getRules()->getType(), *_game->getRuleset());
1690 						// get races for retaliation missions
1691 						std::vector<std::string> races = _game->getRuleset()->getAlienRacesList();
1692 						for (std::vector<std::string>::iterator i = races.begin(); i != races.end();)
1693 						{
1694 							if (_game->getRuleset()->getAlienRace(*i)->canRetaliate())
1695 							{
1696 								i++;
1697 							}
1698 							else
1699 							{
1700 								i = races.erase(i);
1701 							}
1702 						}
1703 						size_t race = RNG::generate(0, races.size()-1);
1704 						mission->setRace(races[race]);
1705 						mission->start(150);
1706 						_game->getSavedGame()->getAlienMissions().push_back(mission);
1707 						newRetaliation = false;
1708 					}
1709 					break;
1710 				}
1711 			}
1712 		}
1713 		if ((*b)->getAvailablePsiLabs() > 0 && !Options::anytimePsiTraining)
1714 		{
1715 			psi = true;
1716 			for(std::vector<Soldier*>::const_iterator s = (*b)->getSoldiers()->begin(); s != (*b)->getSoldiers()->end(); ++s)
1717 			{
1718 				if ((*s)->isInPsiTraining())
1719 				{
1720 					(*s)->trainPsi();
1721 					(*s)->calcStatString(_game->getRuleset()->getStatStrings(), (Options::psiStrengthEval && _game->getSavedGame()->isResearched(_game->getRuleset()->getPsiRequirements())));
1722 				}
1723 			}
1724 		}
1725 	}
1726 
1727 	// Handle funding
1728 	timerReset();
1729 	_game->getSavedGame()->monthlyFunding();
1730 	popup(new MonthlyReportState(_game, psi, _globe));
1731 
1732 	// Handle Xcom Operatives discovering bases
1733 	if (!_game->getSavedGame()->getAlienBases()->empty() && RNG::percent(20))
1734 	{
1735 		for(std::vector<AlienBase*>::const_iterator b = _game->getSavedGame()->getAlienBases()->begin(); b != _game->getSavedGame()->getAlienBases()->end(); ++b)
1736 		{
1737 			if (!(*b)->isDiscovered())
1738 			{
1739 				(*b)->setDiscovered(true);
1740 				popup(new AlienBaseState(_game, *b, this));
1741 				break;
1742 			}
1743 		}
1744 	}
1745 }
1746 
1747 /**
1748  * Slows down the timer back to minimum speed,
1749  * for when important events occur.
1750  */
timerReset()1751 void GeoscapeState::timerReset()
1752 {
1753 	SDL_Event ev;
1754 	ev.button.button = SDL_BUTTON_LEFT;
1755 	Action act(&ev, _game->getScreen()->getXScale(), _game->getScreen()->getYScale(), _game->getScreen()->getCursorTopBlackBand(), _game->getScreen()->getCursorLeftBlackBand());
1756 	_btn5Secs->mousePress(&act, this);
1757 }
1758 
1759 /**
1760  * Adds a new popup window to the queue
1761  * (this prevents popups from overlapping)
1762  * and pauses the game timer respectively.
1763  * @param state Pointer to popup state.
1764  */
popup(State * state)1765 void GeoscapeState::popup(State *state)
1766 {
1767 	_pause = true;
1768 	_popups.push_back(state);
1769 }
1770 
1771 /**
1772  * Returns a pointer to the Geoscape globe for
1773  * access by other substates.
1774  * @return Pointer to globe.
1775  */
getGlobe() const1776 Globe *GeoscapeState::getGlobe() const
1777 {
1778 	return _globe;
1779 }
1780 
1781 /**
1782  * Processes any left-clicks on globe markers,
1783  * or right-clicks to scroll the globe.
1784  * @param action Pointer to an action.
1785  */
1786 
globeClick(Action * action)1787 void GeoscapeState::globeClick(Action *action)
1788 {
1789 	int mouseX = (int)floor(action->getAbsoluteXMouse()), mouseY = (int)floor(action->getAbsoluteYMouse());
1790 
1791 	// Clicking markers on the globe
1792 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
1793 	{
1794 		std::vector<Target*> v = _globe->getTargets(mouseX, mouseY, false);
1795 		if (!v.empty())
1796 		{
1797 			_game->pushState(new MultipleTargetsState(_game, v, 0, this));
1798 		}
1799 	}
1800 
1801 	if (_game->getSavedGame()->getDebugMode())
1802 	{
1803 		double lon, lat;
1804 		_globe->cartToPolar(mouseX, mouseY, &lon, &lat);
1805 		double lonDeg = lon / M_PI * 180, latDeg = lat / M_PI * 180;
1806 		std::wostringstream ss;
1807 		ss << "rad: " << lon << " , " << lat << std::endl;
1808 		ss << "deg: " << lonDeg << " , " << latDeg << std::endl;
1809 		_txtDebug->setText(ss.str());
1810 	}
1811 }
1812 
1813 /**
1814  * Opens the Intercept window.
1815  * @param action Pointer to an action.
1816  */
btnInterceptClick(Action *)1817 void GeoscapeState::btnInterceptClick(Action *)
1818 {
1819 	_game->pushState(new InterceptState(_game, _globe));
1820 }
1821 
1822 /**
1823  * Goes to the Basescape screen.
1824  * @param action Pointer to an action.
1825  */
btnBasesClick(Action *)1826 void GeoscapeState::btnBasesClick(Action *)
1827 {
1828 	timerReset();
1829 	if (!_game->getSavedGame()->getBases()->empty())
1830 	{
1831 		_game->pushState(new BasescapeState(_game, _game->getSavedGame()->getSelectedBase(), _globe));
1832 	}
1833 	else
1834 	{
1835 		_game->pushState(new BasescapeState(_game, 0, _globe));
1836 	}
1837 }
1838 
1839 /**
1840  * Goes to the Graphs screen.
1841  * @param action Pointer to an action.
1842  */
btnGraphsClick(Action *)1843 void GeoscapeState::btnGraphsClick(Action *)
1844 {
1845 	_game->pushState(new GraphsState(_game));
1846 }
1847 
1848 /**
1849  * Goes to the Ufopaedia window.
1850  * @param action Pointer to an action.
1851  */
btnUfopaediaClick(Action *)1852 void GeoscapeState::btnUfopaediaClick(Action *)
1853 {
1854 	Ufopaedia::open(_game);
1855 }
1856 
1857 /**
1858  * Opens the Options window.
1859  * @param action Pointer to an action.
1860  */
btnOptionsClick(Action *)1861 void GeoscapeState::btnOptionsClick(Action *)
1862 {
1863 	_game->pushState(new PauseState(_game, OPT_GEOSCAPE));
1864 }
1865 
1866 /**
1867  * Goes to the Funding screen.
1868  * @param action Pointer to an action.
1869  */
btnFundingClick(Action *)1870 void GeoscapeState::btnFundingClick(Action *)
1871 {
1872 	_game->pushState(new FundingState(_game));
1873 }
1874 
1875 /**
1876  * Starts rotating the globe to the left.
1877  * @param action Pointer to an action.
1878  */
btnRotateLeftPress(Action *)1879 void GeoscapeState::btnRotateLeftPress(Action *)
1880 {
1881 	_globe->rotateLeft();
1882 }
1883 
1884 /**
1885  * Stops rotating the globe to the left.
1886  * @param action Pointer to an action.
1887  */
btnRotateLeftRelease(Action *)1888 void GeoscapeState::btnRotateLeftRelease(Action *)
1889 {
1890 	_globe->rotateStopLon();
1891 }
1892 
1893 /**
1894  * Starts rotating the globe to the right.
1895  * @param action Pointer to an action.
1896  */
btnRotateRightPress(Action *)1897 void GeoscapeState::btnRotateRightPress(Action *)
1898 {
1899 	_globe->rotateRight();
1900 }
1901 
1902 /**
1903  * Stops rotating the globe to the right.
1904  * @param action Pointer to an action.
1905  */
btnRotateRightRelease(Action *)1906 void GeoscapeState::btnRotateRightRelease(Action *)
1907 {
1908 	_globe->rotateStopLon();
1909 }
1910 
1911 /**
1912  * Starts rotating the globe upwards.
1913  * @param action Pointer to an action.
1914  */
btnRotateUpPress(Action *)1915 void GeoscapeState::btnRotateUpPress(Action *)
1916 {
1917 	_globe->rotateUp();
1918 }
1919 
1920 /**
1921  * Stops rotating the globe upwards.
1922  * @param action Pointer to an action.
1923  */
btnRotateUpRelease(Action *)1924 void GeoscapeState::btnRotateUpRelease(Action *)
1925 {
1926 	_globe->rotateStopLat();
1927 }
1928 
1929 /**
1930  * Starts rotating the globe downwards.
1931  * @param action Pointer to an action.
1932  */
btnRotateDownPress(Action *)1933 void GeoscapeState::btnRotateDownPress(Action *)
1934 {
1935 	_globe->rotateDown();
1936 }
1937 
1938 /**
1939  * Stops rotating the globe downwards.
1940  * @param action Pointer to an action.
1941  */
btnRotateDownRelease(Action *)1942 void GeoscapeState::btnRotateDownRelease(Action *)
1943 {
1944 	_globe->rotateStopLat();
1945 }
1946 
1947 /**
1948  * Zooms into the globe.
1949  * @param action Pointer to an action.
1950  */
btnZoomInLeftClick(Action *)1951 void GeoscapeState::btnZoomInLeftClick(Action *)
1952 {
1953 	_globe->zoomIn();
1954 }
1955 
1956 /**
1957  * Zooms the globe maximum.
1958  * @param action Pointer to an action.
1959  */
btnZoomInRightClick(Action *)1960 void GeoscapeState::btnZoomInRightClick(Action *)
1961 {
1962 	_globe->zoomMax();
1963 }
1964 
1965 /**
1966  * Zooms out of the globe.
1967  * @param action Pointer to an action.
1968  */
btnZoomOutLeftClick(Action *)1969 void GeoscapeState::btnZoomOutLeftClick(Action *)
1970 {
1971 	_globe->zoomOut();
1972 }
1973 
1974 /**
1975  * Zooms the globe minimum.
1976  * @param action Pointer to an action.
1977  */
btnZoomOutRightClick(Action *)1978 void GeoscapeState::btnZoomOutRightClick(Action *)
1979 {
1980 	_globe->zoomMin();
1981 }
1982 
1983 /**
1984  * Zoom in effect for dogfights.
1985  */
zoomInEffect()1986 void GeoscapeState::zoomInEffect()
1987 {
1988 	if (_globe->zoomDogfightIn())
1989 	{
1990 		_zoomInEffectDone = true;
1991 		_zoomInEffectTimer->stop();
1992 	}
1993 }
1994 
1995 /**
1996  * Zoom out effect for dogfights.
1997  */
zoomOutEffect()1998 void GeoscapeState::zoomOutEffect()
1999 {
2000 	if (_globe->zoomDogfightOut())
2001 	{
2002 		_zoomOutEffectDone = true;
2003 		_zoomOutEffectTimer->stop();
2004 		init();
2005 	}
2006 }
2007 
2008 /**
2009  * Dogfight logic. Moved here to have the code clean.
2010  */
handleDogfights()2011 void GeoscapeState::handleDogfights()
2012 {
2013 	// If all dogfights are minimized rotate the globe, etc.
2014 	if (_dogfights.size() == _minimizedDogfights)
2015 	{
2016 		_pause = false;
2017 		_gameTimer->think(this, 0);
2018 	}
2019 	// Handle dogfights logic.
2020 	_minimizedDogfights = 0;
2021 	std::list<DogfightState*>::iterator d = _dogfights.begin();
2022 	while(d != _dogfights.end())
2023 	{
2024 		if ((*d)->isMinimized())
2025 		{
2026 			_minimizedDogfights++;
2027 		}
2028 		else
2029 		{
2030 			_globe->rotateStop();
2031 		}
2032 		(*d)->think();
2033 		if ((*d)->dogfightEnded())
2034 		{
2035 			if ((*d)->isMinimized())
2036 			{
2037 				_minimizedDogfights--;
2038 			}
2039 			delete *d;
2040 			d = _dogfights.erase(d);
2041 		}
2042 		else
2043 		{
2044 			++d;
2045 		}
2046 	}
2047 	if (_dogfights.empty())
2048 	{
2049 		_zoomOutEffectTimer->start();
2050 	}
2051 }
2052 
2053 /**
2054  * Gets the number of minimized dogfights.
2055  * @return Number of minimized dogfights.
2056  */
minimizedDogfightsCount()2057 int GeoscapeState::minimizedDogfightsCount()
2058 {
2059 	int minimizedDogfights = 0;
2060 	for(std::list<DogfightState*>::iterator d = _dogfights.begin(); d != _dogfights.end(); ++d)
2061 	{
2062 		if ((*d)->isMinimized())
2063 		{
2064 			++minimizedDogfights;
2065 		}
2066 	}
2067 	return minimizedDogfights;
2068 }
2069 
2070 /**
2071  * Starts a new dogfight.
2072  */
startDogfight()2073 void GeoscapeState::startDogfight()
2074 {
2075 	if (_globe->getZoom() < 3)
2076 	{
2077 		if (!_zoomInEffectTimer->isRunning())
2078 		{
2079 			_globe->saveZoomDogfight();
2080 			_globe->rotateStop();
2081 			_zoomInEffectTimer->start();
2082 		}
2083 	}
2084 	else
2085 	{
2086 		_dogfightStartTimer->stop();
2087 		_zoomInEffectTimer->stop();
2088 		timerReset();
2089 		while(!_dogfightsToBeStarted.empty())
2090 		{
2091 			_dogfights.push_back(_dogfightsToBeStarted.back());
2092 			_dogfightsToBeStarted.pop_back();
2093 			_dogfights.back()->setInterceptionNumber(getFirstFreeDogfightSlot());
2094 			_dogfights.back()->setInterceptionsCount(_dogfights.size() + _dogfightsToBeStarted.size());
2095 		}
2096 		// Set correct number of interceptions for every dogfight.
2097 		for(std::list<DogfightState*>::iterator d = _dogfights.begin(); d != _dogfights.end(); ++d)
2098 		{
2099 			(*d)->setInterceptionsCount(_dogfights.size());
2100 		}
2101 	}
2102 }
2103 
2104 /**
2105  * Returns the first free dogfight slot.
2106  * @return free slot
2107  */
getFirstFreeDogfightSlot()2108 int GeoscapeState::getFirstFreeDogfightSlot()
2109 {
2110 	int slotNo = 1;
2111 	for(std::list<DogfightState*>::iterator d = _dogfights.begin(); d != _dogfights.end(); ++d)
2112 	{
2113 		if ((*d)->getInterceptionNumber() == slotNo)
2114 		{
2115 			++slotNo;
2116 		}
2117 	}
2118 	return slotNo;
2119 }
2120 
2121 /**
2122  * Handle base defense
2123  * @param base Base to defend.
2124  * @param ufo Ufo attacking base.
2125  */
handleBaseDefense(Base * base,Ufo * ufo)2126 void GeoscapeState::handleBaseDefense(Base *base, Ufo *ufo)
2127 {
2128     // Whatever happens in the base defense, the UFO has finished its duty
2129 	ufo->setStatus(Ufo::DESTROYED);
2130 
2131 	if (base->getAvailableSoldiers(true) > 0)
2132 	{
2133 		SavedBattleGame *bgame = new SavedBattleGame();
2134 		_game->getSavedGame()->setBattleGame(bgame);
2135 		bgame->setMissionType("STR_BASE_DEFENSE");
2136 		BattlescapeGenerator bgen = BattlescapeGenerator(_game);
2137 		bgen.setBase(base);
2138 		bgen.setAlienRace(ufo->getAlienRace());
2139 		bgen.run();
2140 		_pause = true;
2141 		_game->pushState(new BriefingState(_game, 0, base));
2142 	}
2143 	else
2144 	{
2145 	    // Please garrison your bases in future
2146 		popup(new BaseDestroyedState(_game, base));
2147 	}
2148 }
2149 
2150 /**
2151  * Determine the alien missions to start this month.
2152  * In the vanilla game each month a terror mission and one other are started in
2153  * random regions.
2154  */
determineAlienMissions(bool atGameStart)2155 void GeoscapeState::determineAlienMissions(bool atGameStart)
2156 {
2157 	if (!atGameStart)
2158 	{
2159 		//
2160 		// One randomly selected mission.
2161 		//
2162 		AlienStrategy &strategy = _game->getSavedGame()->getAlienStrategy();
2163 		const std::string &targetRegion = strategy.chooseRandomRegion(_game->getRuleset());
2164 		const std::string &targetMission = strategy.chooseRandomMission(targetRegion);
2165 		// Choose race for this mission.
2166 		const RuleAlienMission &missionRules = *_game->getRuleset()->getAlienMission(targetMission);
2167 		const std::string &missionRace = missionRules.generateRace(_game->getSavedGame()->getMonthsPassed());
2168 		AlienMission *otherMission = new AlienMission(missionRules);
2169 		otherMission->setId(_game->getSavedGame()->getId("ALIEN_MISSIONS"));
2170 		otherMission->setRegion(targetRegion, *_game->getRuleset());
2171 		otherMission->setRace(missionRace);
2172 		otherMission->start();
2173 		_game->getSavedGame()->getAlienMissions().push_back(otherMission);
2174 		// Make sure this combination never comes up again.
2175 		strategy.removeMission(targetRegion, targetMission);
2176 	}
2177 	else
2178 	{
2179 		//
2180 		// Sectoid Research at base's region.
2181 		//
2182 		AlienStrategy &strategy = _game->getSavedGame()->getAlienStrategy();
2183 		std::string targetRegion =
2184 		_game->getSavedGame()->locateRegion(*_game->getSavedGame()->getBases()->front())->getRules()->getType();
2185 		// Choose race for this mission.
2186 		std::string research = _game->getRuleset()->getAlienMissionList().front();
2187 		const RuleAlienMission &missionRules = *_game->getRuleset()->getAlienMission(research);
2188 		AlienMission *otherMission = new AlienMission(missionRules);
2189 		otherMission->setId(_game->getSavedGame()->getId("ALIEN_MISSIONS"));
2190 		otherMission->setRegion(targetRegion, *_game->getRuleset());
2191 		std::string sectoid = missionRules.getTopRace(_game->getSavedGame()->getMonthsPassed());
2192 		otherMission->setRace(sectoid);
2193 		otherMission->start(150);
2194 		_game->getSavedGame()->getAlienMissions().push_back(otherMission);
2195 		// Make sure this combination never comes up again.
2196 		strategy.removeMission(targetRegion, research);
2197 	}
2198 }
2199 
setupTerrorMission()2200 void GeoscapeState::setupTerrorMission()
2201 {
2202 	//Determine a random region with at least one city and no currently running terror mission.
2203 	RuleRegion* region = 0;
2204 	int counter = 0;
2205 	std::vector<std::string> regions = _game->getRuleset()->getRegionsList();
2206 	do
2207 	{
2208 		// we try 40 times to pick a valid zone for a terror mission
2209 		if (counter == 40) return;
2210 		region = _game->getRuleset()->getRegion(regions[RNG::generate(0, regions.size()-1)]);
2211 		counter++;
2212 	}
2213 	while (region->getCities()->empty() || _game->getSavedGame()->getAlienMission(region->getType(), "STR_ALIEN_TERROR") != 0);
2214 	// Choose race for terror mission.
2215 	const RuleAlienMission &terrorRules = *_game->getRuleset()->getAlienMission("STR_ALIEN_TERROR");
2216 	const std::string &terrorRace = terrorRules.generateRace(_game->getSavedGame()->getMonthsPassed());
2217 	AlienMission *terrorMission = new AlienMission(terrorRules);
2218 	terrorMission->setId(_game->getSavedGame()->getId("ALIEN_MISSIONS"));
2219 	terrorMission->setRegion(region->getType(), *_game->getRuleset());
2220 	terrorMission->setRace(terrorRace);
2221 	terrorMission->start(150);
2222 	_game->getSavedGame()->getAlienMissions().push_back(terrorMission);
2223 }
2224 /**
2225  * Handler for clicking on a timer button.
2226  * @param action pointer to the mouse action.
2227  */
btnTimerClick(Action * action)2228 void GeoscapeState::btnTimerClick(Action *action)
2229 {
2230 	SDL_Event ev;
2231 	ev.type = SDL_MOUSEBUTTONDOWN;
2232 	ev.button.button = SDL_BUTTON_LEFT;
2233 	Action a = Action(&ev, 0.0, 0.0, 0, 0);
2234 	action->getSender()->mousePress(&a, this);
2235 }
2236 
2237 /**
2238  * Updates the scale.
2239  * @param dX delta of X;
2240  * @param dY delta of Y;
2241  */
resize(int & dX,int & dY)2242 void GeoscapeState::resize(int &dX, int &dY)
2243 {
2244 	if (_game->getSavedGame()->getSavedBattle())
2245 		return;
2246 	dX = Options::baseXResolution;
2247 	dY = Options::baseYResolution;
2248 	int divisor = 1;
2249 	switch (Options::geoscapeScale)
2250 	{
2251 	case SCALE_SCREEN_DIV_3:
2252 		divisor = 3;
2253 		break;
2254 	case SCALE_SCREEN_DIV_2:
2255 		divisor = 2;
2256 		break;
2257 	case SCALE_SCREEN:
2258 		break;
2259 	default:
2260 		dX = 0;
2261 		dY = 0;
2262 		return;
2263 	}
2264 
2265 	Options::baseXResolution = std::max(Screen::ORIGINAL_WIDTH, Options::displayWidth / divisor);
2266 	Options::baseYResolution = std::max(Screen::ORIGINAL_HEIGHT, Options::displayHeight / divisor);
2267 
2268 	dX = Options::baseXResolution - dX;
2269 	dY = Options::baseYResolution - dY;
2270 
2271 	_globe->resize();
2272 
2273 	for (std::vector<Surface*>::const_iterator i = _surfaces.begin(); i != _surfaces.end(); ++i)
2274 	{
2275 		if (*i != _globe)
2276 		{
2277 			(*i)->setX((*i)->getX() + dX);
2278 			(*i)->setY((*i)->getY() + dY/2);
2279 		}
2280 	}
2281 
2282 	_bg->setX((_globe->getWidth() - _bg->getWidth()) / 2);
2283 	_bg->setY((_globe->getHeight() - _bg->getHeight()) / 2);
2284 
2285 	int height = (Options::baseYResolution - Screen::ORIGINAL_HEIGHT) / 2 + 10;
2286 	_sideTop->setHeight(height);
2287 	_sideTop->setY(_sidebar->getY() - height - 1);
2288 	_sideBottom->setHeight(height);
2289 	_sideBottom->setY(_sidebar->getY() + _sidebar->getHeight() + 1);
2290 
2291 	_sideLine->setHeight(Options::baseYResolution);
2292 	_sideLine->setY(0);
2293 	_sideLine->drawRect(0, 0, _sideLine->getWidth(), _sideLine->getHeight(), 15);
2294 }
2295 
2296 }
2297