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 "Globe.h"
21 #include <cmath>
22 #include <fstream>
23 #include <algorithm>
24 #include "../fmath.h"
25 #include "../Engine/Action.h"
26 #include "../Engine/SurfaceSet.h"
27 #include "../Engine/Timer.h"
28 #include "../Resource/ResourcePack.h"
29 #include "Polygon.h"
30 #include "Polyline.h"
31 #include "../Engine/FastLineClip.h"
32 #include "../Engine/Palette.h"
33 #include "../Engine/Game.h"
34 #include "../Savegame/SavedGame.h"
35 #include "../Savegame/GameTime.h"
36 #include "../Savegame/Base.h"
37 #include "../Savegame/Country.h"
38 #include "../Ruleset/RuleCountry.h"
39 #include "../Interface/Text.h"
40 #include "../Engine/Font.h"
41 #include "../Engine/Language.h"
42 #include "../Engine/Exception.h"
43 #include "../Ruleset/RuleRegion.h"
44 #include "../Savegame/Region.h"
45 #include "../Ruleset/City.h"
46 #include "../Savegame/Target.h"
47 #include "../Savegame/Ufo.h"
48 #include "../Savegame/Craft.h"
49 #include "../Savegame/Waypoint.h"
50 #include "../Engine/ShaderMove.h"
51 #include "../Engine/ShaderRepeat.h"
52 #include "../Engine/Options.h"
53 #include "../Savegame/TerrorSite.h"
54 #include "../Savegame/AlienBase.h"
55 #include "../Engine/LocalizedText.h"
56 #include "../Savegame/BaseFacility.h"
57 #include "../Ruleset/RuleBaseFacility.h"
58 #include "../Ruleset/RuleCraft.h"
59 #include "../Ruleset/Ruleset.h"
60 #include "../Interface/Cursor.h"
61 #include "../Engine/Screen.h"
62 
63 namespace OpenXcom
64 {
65 
66 const double Globe::ROTATE_LONGITUDE = 0.10;
67 const double Globe::ROTATE_LATITUDE = 0.06;
68 
69 namespace
70 {
71 
72 ///helper class for `Globe` for drawing earth globe with shadows
73 struct GlobeStaticData
74 {
75 	///array of shading gradient
76 	Sint16 shade_gradient[240];
77 	///size of x & y of noise surface
78 	const int random_surf_size;
79 
80 	/**
81 	 * Function returning normal vector of sphere surface
82      * @param ox x cord of sphere center
83      * @param oy y cord of sphere center
84      * @param r radius of sphere
85      * @param x cord of point where we getting this vector
86      * @param y cord of point where we getting this vector
87      * @return normal vector of sphere surface
88      */
circle_normOpenXcom::__anon94ba77990111::GlobeStaticData89 	inline Cord circle_norm(double ox, double oy, double r, double x, double y)
90 	{
91 		const double limit = r*r;
92 		const double norm = 1./r;
93 		Cord ret;
94 		ret.x = (x-ox);
95 		ret.y = (y-oy);
96 		const double temp = (ret.x)*(ret.x) + (ret.y)*(ret.y);
97 		if(limit > temp)
98 		{
99 			ret.x *= norm;
100 			ret.y *= norm;
101 			ret.z = sqrt(limit - temp)*norm;
102 			return ret;
103 		}
104 		else
105 		{
106 			ret.x = 0.;
107 			ret.y = 0.;
108 			ret.z = 0.;
109 			return ret;
110 		}
111 	}
112 
113 	//initialization
GlobeStaticDataOpenXcom::__anon94ba77990111::GlobeStaticData114 	GlobeStaticData() : random_surf_size(60)
115 	{
116 		//filling terminator gradient LUT
117 		for (int i=0; i<240; ++i)
118 		{
119 			int j = i - 120;
120 
121 			if (j<-66) j=-16;
122 			else
123 			if (j<-48) j=-15;
124 			else
125 			if (j<-33) j=-14;
126 			else
127 			if (j<-22) j=-13;
128 			else
129 			if (j<-15) j=-12;
130 			else
131 			if (j<-11) j=-11;
132 			else
133 			if (j<-9) j=-10;
134 
135 			if (j>120) j=19;
136 			else
137 			if (j>98) j=18;
138 			else
139 			if (j>86) j=17;
140 			else
141 			if (j>74) j=16;
142 			else
143 			if (j>54) j=15;
144 			else
145 			if (j>38) j=14;
146 			else
147 			if (j>26) j=13;
148 			else
149 			if (j>18) j=12;
150 			else
151 			if (j>13) j=11;
152 			else
153 			if (j>10) j=10;
154 			else
155 			if (j>8) j=9;
156 
157 			shade_gradient[i]= j+16;
158 		}
159 
160 	}
161 };
162 
163 GlobeStaticData static_data;
164 
165 struct Ocean
166 {
funcOpenXcom::__anon94ba77990111::Ocean167 	static inline void func(Uint8& dest, const int&, const int&, const int&, const int&)
168 	{
169 		dest = Palette::blockOffset(12) + 0;
170 	}
171 };
172 
173 struct CreateShadow
174 {
getShadowValueOpenXcom::__anon94ba77990111::CreateShadow175 	static inline Uint8 getShadowValue(const Uint8& dest, const Cord& earth, const Cord& sun, const Sint16& noise)
176 	{
177 		Cord temp = earth;
178 		//diff
179 		temp -= sun;
180 		//norm
181 		temp.x *= temp.x;
182 		temp.y *= temp.y;
183 		temp.z *= temp.z;
184 		temp.x += temp.z + temp.y;
185 		//we have norm of distance between 2 vectors, now stored in `x`
186 
187 		temp.x -= 2;
188 		temp.x *= 125.;
189 
190 		if (temp.x < -110)
191 			temp.x = -31;
192 		else if (temp.x > 120)
193 			temp.x = 50;
194 		else
195 			temp.x = static_data.shade_gradient[(Sint16)temp.x + 120];
196 
197 		temp.x -= noise;
198 
199 		if(temp.x > 0.)
200 		{
201 			const Sint16 val = (temp.x> 31)? 31 : (Sint16)temp.x;
202 			const int d = dest & helper::ColorGroup;
203 			if(d ==  Palette::blockOffset(12) || d ==  Palette::blockOffset(13))
204 			{
205 				//this pixel is ocean
206 				return Palette::blockOffset(12) + val;
207 			}
208 			else
209 			{
210 				//this pixel is land
211 				if (dest==0) return val;
212 				const int s = val / 3;
213 				const int e = dest+s;
214 				if(e > d + helper::ColorShade)
215 					return d + helper::ColorShade;
216 				return e;
217 			}
218 		}
219 		else
220 		{
221 			const int d = dest & helper::ColorGroup;
222 			if(d ==  Palette::blockOffset(12) || d ==  Palette::blockOffset(13))
223 			{
224 				//this pixel is ocean
225 				return Palette::blockOffset(12);
226 			}
227 			else
228 			{
229 				//this pixel is land
230 				return dest;
231 			}
232 		}
233 	}
234 
funcOpenXcom::__anon94ba77990111::CreateShadow235 	static inline void func(Uint8& dest, const Cord& earth, const Cord& sun, const Sint16& noise, const int&)
236 	{
237 		if(dest && earth.z)
238 			dest = getShadowValue(dest, earth, sun, noise);
239 		else
240 			dest = 0;
241 	}
242 };
243 
244 }//namespace
245 
246 
247 /**
248  * Sets up a globe with the specified size and position.
249  * @param game Pointer to core game.
250  * @param cenX X position of the center of the globe.
251  * @param cenY Y position of the center of the globe.
252  * @param width Width in pixels.
253  * @param height Height in pixels.
254  * @param x X position in pixels.
255  * @param y Y position in pixels.
256  */
Globe(Game * game,int cenX,int cenY,int width,int height,int x,int y)257 Globe::Globe(Game *game, int cenX, int cenY, int width, int height, int x, int y):
258 	InteractiveSurface(width, height, x, y),
259 	_rotLon(0.0), _rotLat(0.0),
260 	_hoverLon(0.0), _hoverLat(0.0),
261 	_cenX(cenX), _cenY(cenY), _game(game),
262 	_blink(true), _hover(false), _cacheLand(),
263 	_isMouseScrolling(false), _isMouseScrolled(false),
264 	_xBeforeMouseScrolling(0), _yBeforeMouseScrolling(0),
265 	_lonBeforeMouseScrolling(0.0), _latBeforeMouseScrolling(0.0),
266 	_mouseScrollingStartTime(0), _totalMouseMoveX(0),
267 	_totalMouseMoveY(0), _mouseMovedOverThreshold(false)
268 {
269 	_texture = new SurfaceSet(*_game->getResourcePack()->getSurfaceSet("TEXTURE.DAT"));
270 
271 	_countries = new Surface(width, height, x, y);
272 	_markers = new Surface(width, height, x, y);
273 	_radars = new Surface(width, height, x, y);
274 	_clipper = new FastLineClip(x, x+width, y, y+height);
275 
276 	// Animation timers
277 	_blinkTimer = new Timer(100);
278 	_blinkTimer->onTimer((SurfaceHandler)&Globe::blink);
279 	_blinkTimer->start();
280 	_rotTimer = new Timer(10);
281 	_rotTimer->onTimer((SurfaceHandler)&Globe::rotate);
282 
283 	// Globe markers
284 	_mkXcomBase = new Surface(3, 3);
285 	_mkXcomBase->lock();
286 	_mkXcomBase->setPixel(0, 0, 9);
287 	_mkXcomBase->setPixel(1, 0, 9);
288 	_mkXcomBase->setPixel(2, 0, 9);
289 	_mkXcomBase->setPixel(0, 1, 9);
290 	_mkXcomBase->setPixel(2, 1, 9);
291 	_mkXcomBase->setPixel(0, 2, 9);
292 	_mkXcomBase->setPixel(1, 2, 9);
293 	_mkXcomBase->setPixel(2, 2, 9);
294 	_mkXcomBase->unlock();
295 
296 	_mkAlienBase = new Surface(3, 3);
297 	_mkAlienBase->lock();
298 	_mkAlienBase->setPixel(0, 0, 1);
299 	_mkAlienBase->setPixel(1, 0, 1);
300 	_mkAlienBase->setPixel(2, 0, 1);
301 	_mkAlienBase->setPixel(0, 1, 1);
302 	_mkAlienBase->setPixel(2, 1, 1);
303 	_mkAlienBase->setPixel(0, 2, 1);
304 	_mkAlienBase->setPixel(1, 2, 1);
305 	_mkAlienBase->setPixel(2, 2, 1);
306 	_mkAlienBase->unlock();
307 
308 	_mkCraft = new Surface(3, 3);
309 	_mkCraft->lock();
310 	_mkCraft->setPixel(1, 0, 11);
311 	_mkCraft->setPixel(0, 1, 11);
312 	_mkCraft->setPixel(2, 1, 11);
313 	_mkCraft->setPixel(1, 2, 11);
314 	_mkCraft->unlock();
315 
316 	_mkWaypoint = new Surface(3, 3);
317 	_mkWaypoint->lock();
318 	_mkWaypoint->setPixel(0, 0, 3);
319 	_mkWaypoint->setPixel(0, 2, 3);
320 	_mkWaypoint->setPixel(1, 1, 3);
321 	_mkWaypoint->setPixel(2, 0, 3);
322 	_mkWaypoint->setPixel(2, 2, 3);
323 	_mkWaypoint->unlock();
324 
325 	_mkCity = new Surface(3, 3);
326 	_mkCity->lock();
327 	_mkCity->setPixel(0, 0, 14);
328 	_mkCity->setPixel(1, 0, 14);
329 	_mkCity->setPixel(2, 0, 14);
330 	_mkCity->setPixel(0, 1, 14);
331 	_mkCity->setPixel(1, 1, 11);
332 	_mkCity->setPixel(2, 1, 14);
333 	_mkCity->setPixel(0, 2, 14);
334 	_mkCity->setPixel(1, 2, 14);
335 	_mkCity->setPixel(2, 2, 14);
336 	_mkCity->unlock();
337 
338 	_mkFlyingUfo = new Surface(3, 3);
339 	_mkFlyingUfo->lock();
340 	_mkFlyingUfo->setPixel(1, 0, 13);
341 	_mkFlyingUfo->setPixel(0, 1, 13);
342 	_mkFlyingUfo->setPixel(1, 1, 13);
343 	_mkFlyingUfo->setPixel(2, 1, 13);
344 	_mkFlyingUfo->setPixel(1, 2, 13);
345 	_mkFlyingUfo->unlock();
346 
347 	_mkLandedUfo = new Surface(3, 3);
348 	_mkLandedUfo->lock();
349 	_mkLandedUfo->setPixel(0, 0, 7);
350 	_mkLandedUfo->setPixel(0, 2, 7);
351 	_mkLandedUfo->setPixel(1, 1, 7);
352 	_mkLandedUfo->setPixel(2, 0, 7);
353 	_mkLandedUfo->setPixel(2, 2, 7);
354 	_mkLandedUfo->unlock();
355 
356 	_mkCrashedUfo = new Surface(3, 3);
357 	_mkCrashedUfo->lock();
358 	_mkCrashedUfo->setPixel(0, 0, 5);
359 	_mkCrashedUfo->setPixel(0, 2, 5);
360 	_mkCrashedUfo->setPixel(1, 1, 5);
361 	_mkCrashedUfo->setPixel(2, 0, 5);
362 	_mkCrashedUfo->setPixel(2, 2, 5);
363 	_mkCrashedUfo->unlock();
364 
365 	_mkAlienSite = new Surface(3, 3);
366 	_mkAlienSite->lock();
367 	_mkAlienSite->setPixel(1, 0, 1);
368 	_mkAlienSite->setPixel(0, 1, 1);
369 	_mkAlienSite->setPixel(1, 1, 1);
370 	_mkAlienSite->setPixel(2, 1, 1);
371 	_mkAlienSite->setPixel(1, 2, 1);
372 	_mkAlienSite->unlock();
373 
374 	_cenLon = _game->getSavedGame()->getGlobeLongitude();
375 	_cenLat = _game->getSavedGame()->getGlobeLatitude();
376 	_zoom = _game->getSavedGame()->getGlobeZoom();
377 	_zoomOld = _zoom;
378 
379 	setupRadii(width, height);
380 
381 	//filling random noise "texture"
382 	_randomNoiseData.resize(static_data.random_surf_size * static_data.random_surf_size);
383 	for(size_t i=0; i<_randomNoiseData.size(); ++i)
384 		_randomNoiseData[i] = rand()%4;
385 
386 	cachePolygons();
387 }
388 
389 /**
390  * Deletes the contained surfaces.
391  */
~Globe()392 Globe::~Globe()
393 {
394 	delete _texture;
395 
396 	delete _blinkTimer;
397 	delete _rotTimer;
398 	delete _countries;
399 	delete _markers;
400 	delete _mkXcomBase;
401 	delete _mkAlienBase;
402 	delete _mkCraft;
403 	delete _mkWaypoint;
404 	delete _mkCity;
405 	delete _mkFlyingUfo;
406 	delete _mkLandedUfo;
407 	delete _mkCrashedUfo;
408 	delete _mkAlienSite;
409 	delete _radars;
410 	delete _clipper;
411 
412 	for (std::list<Polygon*>::iterator i = _cacheLand.begin(); i != _cacheLand.end(); ++i)
413 	{
414 		delete *i;
415 	}
416 }
417 
418 /**
419  * Converts a polar point into a cartesian point for
420  * mapping a polygon onto the 3D-looking globe.
421  * @param lon Longitude of the polar point.
422  * @param lat Latitude of the polar point.
423  * @param x Pointer to the output X position.
424  * @param y Pointer to the output Y position.
425  */
polarToCart(double lon,double lat,Sint16 * x,Sint16 * y) const426 void Globe::polarToCart(double lon, double lat, Sint16 *x, Sint16 *y) const
427 {
428 	// Orthographic projection
429 	*x = _cenX + (Sint16)floor(_radius * cos(lat) * sin(lon - _cenLon));
430 	*y = _cenY + (Sint16)floor(_radius * (cos(_cenLat) * sin(lat) - sin(_cenLat) * cos(lat) * cos(lon - _cenLon)));
431 }
432 
polarToCart(double lon,double lat,double * x,double * y) const433 void Globe::polarToCart(double lon, double lat, double *x, double *y) const
434 {
435 	// Orthographic projection
436 	*x = _cenX + _radius * cos(lat) * sin(lon - _cenLon);
437 	*y = _cenY + _radius * (cos(_cenLat) * sin(lat) - sin(_cenLat) * cos(lat) * cos(lon - _cenLon));
438 }
439 
440 
441 /**
442  * Converts a cartesian point into a polar point for
443  * mapping a globe click onto the flat world map.
444  * @param x X position of the cartesian point.
445  * @param y Y position of the cartesian point.
446  * @param lon Pointer to the output longitude.
447  * @param lat Pointer to the output latitude.
448  */
cartToPolar(Sint16 x,Sint16 y,double * lon,double * lat) const449 void Globe::cartToPolar(Sint16 x, Sint16 y, double *lon, double *lat) const
450 {
451 	// Orthographic projection
452 	x -= _cenX;
453 	y -= _cenY;
454 
455 	double rho = sqrt((double)(x*x + y*y));
456 	double c = asin(rho / _radius);
457 	if ( AreSame(rho, 0.0) )
458 	{
459 		*lat = _cenLat;
460 		*lon = _cenLon;
461 
462 	}
463 	else
464 	{
465 		*lat = asin((y * sin(c) * cos(_cenLat)) / rho + cos(c) * sin(_cenLat));
466 		*lon = atan2(x * sin(c),(rho * cos(_cenLat) * cos(c) - y * sin(_cenLat) * sin(c))) + _cenLon;
467 	}
468 
469 	// Keep between 0 and 2xPI
470 	while (*lon < 0)
471 		*lon += 2 * M_PI;
472 	while (*lon >= 2 * M_PI)
473 		*lon -= 2 * M_PI;
474 }
475 
476 /**
477  * Checks if a polar point is on the back-half of the globe,
478  * invisible to the player.
479  * @param lon Longitude of the point.
480  * @param lat Latitude of the point.
481  * @return True if it's on the back, False if it's on the front.
482  */
pointBack(double lon,double lat) const483 bool Globe::pointBack(double lon, double lat) const
484 {
485 	double c = cos(_cenLat) * cos(lat) * cos(lon - _cenLon) + sin(_cenLat) * sin(lat);
486 
487 	return c < 0.0;
488 }
489 
490 
491 /** Return latitude of last visible to player point on given longitude.
492  * @param lon Longitude of the point.
493  * @return Longitude of last visible point.
494  */
lastVisibleLat(double lon) const495 double Globe::lastVisibleLat(double lon) const
496 {
497 //	double c = cos(_cenLat) * cos(lat) * cos(lon - _cenLon) + sin(_cenLat) * sin(lat);
498 //		tan(lat) = -cos(_cenLat) * cos(lon - _cenLon)/sin(_cenLat) ;
499 	return atan(-cos(_cenLat) * cos(lon - _cenLon)/sin(_cenLat));
500 }
501 
502 /**
503  * Checks if a polar point is inside a certain polygon.
504  * @param lon Longitude of the point.
505  * @param lat Latitude of the point.
506  * @param poly Pointer to the polygon.
507  * @return True if it's inside, False if it's outside.
508  */
insidePolygon(double lon,double lat,Polygon * poly) const509 bool Globe::insidePolygon(double lon, double lat, Polygon *poly) const
510 {
511 	bool backFace = true;
512 	for (int i = 0; i < poly->getPoints(); ++i)
513 	{
514 		backFace = backFace && pointBack(poly->getLongitude(i), poly->getLatitude(i));
515 	}
516 	if (backFace != pointBack(lon, lat))
517 		return false;
518 
519 	bool odd = false;
520 	for (int i = 0; i < poly->getPoints(); ++i)
521 	{
522 		int j = (i + 1) % poly->getPoints();
523 
524 		/*double x = lon, y = lat,
525 			   x_i = poly->getLongitude(i), y_i = poly->getLatitude(i),
526 			   x_j = poly->getLongitude(j), y_j = poly->getLatitude(j);*/
527 
528 		double x, y, x_i, x_j, y_i, y_j;
529 		polarToCart(poly->getLongitude(i), poly->getLatitude(i), &x_i, &y_i);
530 		polarToCart(poly->getLongitude(j), poly->getLatitude(j), &x_j, &y_j);
531 		polarToCart(lon, lat, &x, &y);
532 
533 		if (((y_i < y && y_j >= y) || (y_j < y && y_i >= y)) && (x_i <= x || x_j <= x))
534 		{
535 			odd ^= (x_i + (y - y_i) / (y_j - y_i) * (x_j - x_i) < x);
536 		}
537 	}
538 	return odd;
539 }
540 
541 /**
542  * Loads a series of map polar coordinates in X-Com format,
543  * converts them and stores them in a set of polygons.
544  * @param filename Filename of the DAT file.
545  * @param polygons Pointer to the polygon set.
546  * @sa http://www.ufopaedia.org/index.php?title=WORLD.DAT
547  */
loadDat(const std::string & filename,std::list<Polygon * > * polygons)548 void Globe::loadDat(const std::string &filename, std::list<Polygon*> *polygons)
549 {
550 	// Load file
551 	std::ifstream mapFile (filename.c_str(), std::ios::in | std::ios::binary);
552 	if (!mapFile)
553 	{
554 		throw Exception(filename + " not found");
555 	}
556 
557 	short value[10];
558 
559 	while (mapFile.read((char*)&value, sizeof(value)))
560 	{
561 		Polygon* poly;
562 		int points;
563 
564 		for (int i = 0; i < 10; ++i)
565 		{
566 			value[i] = SDL_SwapLE16(value[i]);
567 		}
568 
569 		if (value[6] != -1)
570 		{
571 			points = 4;
572 		}
573 		else
574 		{
575 			points = 3;
576 		}
577 		poly = new Polygon(points);
578 
579 		for (int i = 0, j = 0; i < points; ++i)
580 		{
581 			// Correct X-Com degrees and convert to radians
582 			double lonRad = value[j++] * 0.125f * M_PI / 180;
583 			double latRad = value[j++] * 0.125f * M_PI / 180;
584 
585 			poly->setLongitude(i, lonRad);
586 			poly->setLatitude(i, latRad);
587 		}
588 		poly->setTexture(value[8]);
589 
590 		polygons->push_back(poly);
591 	}
592 
593 	if (!mapFile.eof())
594 	{
595 		throw Exception("Invalid globe map");
596 	}
597 
598 	mapFile.close();
599 }
600 
601 /**
602  * Sets a leftwards rotation speed and starts the timer.
603  */
rotateLeft()604 void Globe::rotateLeft()
605 {
606 	_rotLon = -ROTATE_LONGITUDE;
607 	if (!_rotTimer->isRunning()) _rotTimer->start();
608 }
609 
610 /**
611  * Sets a rightwards rotation speed and starts the timer.
612  */
rotateRight()613 void Globe::rotateRight()
614 {
615 	_rotLon = ROTATE_LONGITUDE;
616 	if (!_rotTimer->isRunning()) _rotTimer->start();
617 }
618 
619 /**
620  * Sets a upwards rotation speed and starts the timer.
621  */
rotateUp()622 void Globe::rotateUp()
623 {
624 	_rotLat = -ROTATE_LATITUDE;
625 	if (!_rotTimer->isRunning()) _rotTimer->start();
626 }
627 
628 /**
629  * Sets a downwards rotation speed and starts the timer.
630  */
rotateDown()631 void Globe::rotateDown()
632 {
633 	_rotLat = ROTATE_LATITUDE;
634 	if (!_rotTimer->isRunning()) _rotTimer->start();
635 }
636 
637 /**
638  * Resets the rotation speed and timer.
639  */
rotateStop()640 void Globe::rotateStop()
641 {
642 	_rotLon = 0.0;
643 	_rotLat = 0.0;
644 	_rotTimer->stop();
645 }
646 
647 /**
648  * Resets longitude rotation speed and timer.
649  */
rotateStopLon()650 void Globe::rotateStopLon()
651 {
652 	_rotLon = 0.0;
653 	if (AreSame(_rotLat, 0.0))
654 	{
655 		_rotTimer->stop();
656 	}
657 }
658 
659 /**
660  * Resets latitude rotation speed and timer.
661  */
rotateStopLat()662 void Globe::rotateStopLat()
663 {
664 	_rotLat = 0.0;
665 	if (AreSame(_rotLon, 0.0))
666 	{
667 		_rotTimer->stop();
668 	}
669 }
670 
671 /**
672  * Changes the current globe zoom factor.
673  * @param zoom New zoom.
674  */
setZoom(size_t zoom)675 void Globe::setZoom(size_t zoom)
676 {
677 	_zoom = std::min(std::max(zoom, (size_t)0u), _zoomRadius.size() - 1);
678 	_radius = _zoomRadius[_zoom];
679 	_game->getSavedGame()->setGlobeZoom(_zoom);
680 	if (_isMouseScrolling)
681 	{
682 		_lonBeforeMouseScrolling = _cenLon;
683 		_latBeforeMouseScrolling = _cenLat;
684 		_totalMouseMoveX = 0; _totalMouseMoveY = 0;
685 	}
686 	invalidate();
687 }
688 
689 /**
690  * Increases the zoom level on the globe.
691  */
zoomIn()692 void Globe::zoomIn()
693 {
694 	if (_zoom < _zoomRadius.size() - 1)
695 	{
696 		setZoom(_zoom + 1);
697 	}
698 }
699 
700 /**
701  * Decreases the zoom level on the globe.
702  */
zoomOut()703 void Globe::zoomOut()
704 {
705 	if (_zoom > 0)
706 	{
707 		setZoom(_zoom - 1);
708 	}
709 }
710 
711 /**
712  * Zooms the globe out as far as possible.
713  */
zoomMin()714 void Globe::zoomMin()
715 {
716 	if (_zoom > 0)
717 	{
718 		setZoom(0);
719 	}
720 }
721 
722 /**
723  * Zooms the globe in as close as possible.
724  */
zoomMax()725 void Globe::zoomMax()
726 {
727 	if (_zoom < _zoomRadius.size() - 1)
728 	{
729 		setZoom(_zoomRadius.size() - 1);
730 	}
731 }
732 
733 /**
734  * Stores the zoom used before a dogfight.
735  */
saveZoomDogfight()736 void Globe::saveZoomDogfight()
737 {
738 	_zoomOld = _zoom;
739 }
740 
741 /**
742  * Zooms the globe smoothly into dogfight level.
743  * @return Is the globe already zoomed in?
744  */
zoomDogfightIn()745 bool Globe::zoomDogfightIn()
746 {
747 	if (_zoom < DOGFIGHT_ZOOM)
748 	{
749 		double radiusNow = _radius;
750 		if (radiusNow + _radiusStep >= _zoomRadius[DOGFIGHT_ZOOM])
751 		{
752 			setZoom(DOGFIGHT_ZOOM);
753 		}
754 		else
755 		{
756 			if (radiusNow + _radiusStep >= _zoomRadius[_zoom + 1])
757 				_zoom++;
758 			setZoom(_zoom);
759 			_radius = radiusNow + _radiusStep;
760 		}
761 		return false;
762 	}
763 	return true;
764 }
765 
766 /**
767  * Zooms the globe smoothly out of dogfight level.
768  * @return Is the globe already zoomed out?
769  */
zoomDogfightOut()770 bool Globe::zoomDogfightOut()
771 {
772 	if (_zoom > _zoomOld)
773 	{
774 		double radiusNow = _radius;
775 		if (radiusNow - _radiusStep <= _zoomRadius[_zoomOld])
776 		{
777 			setZoom(_zoomOld);
778 		}
779 		else
780 		{
781 			if (radiusNow - _radiusStep <= _zoomRadius[_zoom - 1])
782 				_zoom--;
783 			setZoom(_zoom);
784 			_radius = radiusNow - _radiusStep;
785 		}
786 		return false;
787 	}
788 	return true;
789 }
790 
791 /**
792  * Rotates the globe to center on a certain
793  * polar point on the world map.
794  * @param lon Longitude of the point.
795  * @param lat Latitude of the point.
796  */
center(double lon,double lat)797 void Globe::center(double lon, double lat)
798 {
799 	_cenLon = lon;
800 	_cenLat = lat;
801 	_game->getSavedGame()->setGlobeLongitude(_cenLon);
802 	_game->getSavedGame()->setGlobeLatitude(_cenLat);
803 	invalidate();
804 }
805 
806 /**
807  * Checks if a polar point is inside the globe's landmass.
808  * @param lon Longitude of the point.
809  * @param lat Latitude of the point.
810  * @return True if it's inside, False if it's outside.
811  */
insideLand(double lon,double lat) const812 bool Globe::insideLand(double lon, double lat) const
813 {
814 	bool inside = false;
815 	// We're only temporarily changing cenLon/cenLat so the "const" is actually preserved
816 	Globe* const globe = const_cast<Globe* const>(this); // WARNING: BAD CODING PRACTICE
817 	double oldLon = _cenLon, oldLat = _cenLat;
818 	globe->_cenLon = lon;
819 	globe->_cenLat = lat;
820 	for (std::list<Polygon*>::iterator i = _game->getResourcePack()->getPolygons()->begin(); i != _game->getResourcePack()->getPolygons()->end() && !inside; ++i)
821 	{
822 		inside = insidePolygon(lon, lat, *i);
823 	}
824 	globe->_cenLon = oldLon;
825 	globe->_cenLat = oldLat;
826 	return inside;
827 }
828 
829 /**
830  * Switches the amount of detail shown on the globe.
831  * With detail on, country and city details are shown when zoomed in.
832  */
toggleDetail()833 void Globe::toggleDetail()
834 {
835 	Options::globeDetail = !Options::globeDetail;
836 	drawDetail();
837 }
838 
839 /**
840  * Checks if a certain target is near a certain cartesian point
841  * (within a circled area around it) over the globe.
842  * @param target Pointer to target.
843  * @param x X coordinate of point.
844  * @param y Y coordinate of point.
845  * @return True if it's near, false otherwise.
846  */
targetNear(Target * target,int x,int y) const847 bool Globe::targetNear(Target* target, int x, int y) const
848 {
849 	Sint16 tx, ty;
850 	if (pointBack(target->getLongitude(), target->getLatitude()))
851 		return false;
852 	polarToCart(target->getLongitude(), target->getLatitude(), &tx, &ty);
853 
854 	int dx = x - tx;
855 	int dy = y - ty;
856 	return (dx * dx + dy * dy <= NEAR_RADIUS);
857 }
858 
859 /**
860  * Returns a list of all the targets currently near a certain
861  * cartesian point over the globe.
862  * @param x X coordinate of point.
863  * @param y Y coordinate of point.
864  * @param craft Only get craft targets.
865  * @return List of pointers to targets.
866  */
getTargets(int x,int y,bool craft) const867 std::vector<Target*> Globe::getTargets(int x, int y, bool craft) const
868 {
869 	std::vector<Target*> v;
870 	if (!craft)
871 	{
872 		for (std::vector<Base*>::iterator i = _game->getSavedGame()->getBases()->begin(); i != _game->getSavedGame()->getBases()->end(); ++i)
873 		{
874 			if ((*i)->getLongitude() == 0.0 && (*i)->getLatitude() == 0.0)
875 				continue;
876 
877 			if (targetNear((*i), x, y))
878 			{
879 				v.push_back(*i);
880 			}
881 
882 			for (std::vector<Craft*>::iterator j = (*i)->getCrafts()->begin(); j != (*i)->getCrafts()->end(); ++j)
883 			{
884 				if ((*j)->getLongitude() == (*i)->getLongitude() && (*j)->getLatitude() == (*i)->getLatitude() && (*j)->getDestination() == 0)
885 					continue;
886 
887 				if (targetNear((*j), x, y))
888 				{
889 					v.push_back(*j);
890 				}
891 			}
892 		}
893 	}
894 	for (std::vector<Ufo*>::iterator i = _game->getSavedGame()->getUfos()->begin(); i != _game->getSavedGame()->getUfos()->end(); ++i)
895 	{
896 		if (!(*i)->getDetected())
897 			continue;
898 
899 		if (targetNear((*i), x, y))
900 		{
901 			v.push_back(*i);
902 		}
903 	}
904 	for (std::vector<Waypoint*>::iterator i = _game->getSavedGame()->getWaypoints()->begin(); i != _game->getSavedGame()->getWaypoints()->end(); ++i)
905 	{
906 		if (targetNear((*i), x, y))
907 		{
908 			v.push_back(*i);
909 		}
910 	}
911 	for (std::vector<TerrorSite*>::iterator i = _game->getSavedGame()->getTerrorSites()->begin(); i != _game->getSavedGame()->getTerrorSites()->end(); ++i)
912 	{
913 		if (targetNear((*i), x, y))
914 		{
915 			v.push_back(*i);
916 		}
917 	}
918 	for (std::vector<AlienBase*>::iterator i = _game->getSavedGame()->getAlienBases()->begin(); i != _game->getSavedGame()->getAlienBases()->end(); ++i)
919  	{
920 		if (!(*i)->isDiscovered())
921 		{
922 			continue;
923 		}
924 		if (targetNear((*i), x, y))
925 		{
926 			v.push_back(*i);
927 		}
928 	}
929 	return v;
930 }
931 
932 /**
933  * Takes care of pre-calculating all the polygons currently visible
934  * on the globe and caching them so they only need to be recalculated
935  * when the globe is actually moved.
936  */
cachePolygons()937 void Globe::cachePolygons()
938 {
939 	cache(_game->getResourcePack()->getPolygons(), &_cacheLand);
940 }
941 
942 /**
943  * Caches a set of polygons.
944  * @param polygons Pointer to list of polygons.
945  * @param cache Pointer to cache.
946  */
cache(std::list<Polygon * > * polygons,std::list<Polygon * > * cache)947 void Globe::cache(std::list<Polygon*> *polygons, std::list<Polygon*> *cache)
948 {
949 	// Clear existing cache
950 	for (std::list<Polygon*>::iterator i = cache->begin(); i != cache->end(); ++i)
951 	{
952 		delete *i;
953 	}
954 	cache->clear();
955 
956 	// Pre-calculate values to cache
957 	for (std::list<Polygon*>::iterator i = polygons->begin(); i != polygons->end(); ++i)
958 	{
959 		// Is quad on the back face?
960 		double closest = 0.0;
961 		double z;
962 		double furthest = 0.0;
963 		for (int j = 0; j < (*i)->getPoints(); ++j)
964 		{
965 			z = cos(_cenLat) * cos((*i)->getLatitude(j)) * cos((*i)->getLongitude(j) - _cenLon) + sin(_cenLat) * sin((*i)->getLatitude(j));
966 			if (z > closest)
967 				closest = z;
968 			else if (z < furthest)
969 				furthest = z;
970 		}
971 		if (-furthest > closest)
972 			continue;
973 
974 		Polygon* p = new Polygon(**i);
975 
976 		// Convert coordinates
977 		for (int j = 0; j < p->getPoints(); ++j)
978 		{
979 			Sint16 x, y;
980 			polarToCart(p->getLongitude(j), p->getLatitude(j), &x, &y);
981 			p->setX(j, x);
982 			p->setY(j, y);
983 		}
984 
985 		cache->push_back(p);
986 	}
987 }
988 
989 /**
990  * Replaces a certain amount of colors in the palette of the globe.
991  * @param colors Pointer to the set of colors.
992  * @param firstcolor Offset of the first color to replace.
993  * @param ncolors Amount of colors to replace.
994  */
setPalette(SDL_Color * colors,int firstcolor,int ncolors)995 void Globe::setPalette(SDL_Color *colors, int firstcolor, int ncolors)
996 {
997 	Surface::setPalette(colors, firstcolor, ncolors);
998 
999 	_texture->setPalette(colors, firstcolor, ncolors);
1000 
1001 	_countries->setPalette(colors, firstcolor, ncolors);
1002 	_markers->setPalette(colors, firstcolor, ncolors);
1003 	_mkXcomBase->setPalette(colors, firstcolor, ncolors);
1004 	_mkAlienBase->setPalette(colors, firstcolor, ncolors);
1005 	_mkCraft->setPalette(colors, firstcolor, ncolors);
1006 	_mkWaypoint->setPalette(colors, firstcolor, ncolors);
1007 	_mkCity->setPalette(colors, firstcolor, ncolors);
1008 	_mkFlyingUfo->setPalette(colors, firstcolor, ncolors);
1009 	_mkLandedUfo->setPalette(colors, firstcolor, ncolors);
1010 	_mkCrashedUfo->setPalette(colors, firstcolor, ncolors);
1011 	_mkAlienSite->setPalette(colors, firstcolor, ncolors);
1012 	_radars->setPalette(colors, firstcolor, ncolors);
1013 }
1014 
1015 /**
1016  * Keeps the animation timers running.
1017  */
think()1018 void Globe::think()
1019 {
1020 	_blinkTimer->think(0, this);
1021 	_rotTimer->think(0, this);
1022 }
1023 
1024 /**
1025  * Makes the globe markers blink.
1026  */
blink()1027 void Globe::blink()
1028 {
1029 	_blink = !_blink;
1030 
1031 	int off = 0;
1032 	if (_blink)
1033 		off = -1;
1034 	else
1035 		off = 1;
1036 
1037 	_mkXcomBase->offset(off);
1038 	_mkAlienBase->offset(off);
1039 	_mkCraft->offset(off);
1040 	_mkWaypoint->offset(off);
1041 	_mkFlyingUfo->offset(off);
1042 	_mkLandedUfo->offset(off);
1043 	_mkCrashedUfo->offset(off);
1044 	_mkAlienSite->offset(off);
1045 
1046 	drawMarkers();
1047 }
1048 
1049 /**
1050  * Rotates the globe by a set amount. Necessary
1051  * since the globe keeps rotating while a button
1052  * is pressed down.
1053  */
rotate()1054 void Globe::rotate()
1055 {
1056 	_cenLon += _rotLon * ((110 - Options::geoScrollSpeed) / 100.0) / (_zoom+1);
1057 	_cenLat += _rotLat * ((110 - Options::geoScrollSpeed) / 100.0) / (_zoom+1);
1058 	_game->getSavedGame()->setGlobeLongitude(_cenLon);
1059 	_game->getSavedGame()->setGlobeLatitude(_cenLat);
1060 	invalidate();
1061 }
1062 
1063 /**
1064  * Draws the whole globe, part by part.
1065  */
draw()1066 void Globe::draw()
1067 {
1068 	if (_redraw)
1069 	{
1070 		cachePolygons();
1071 	}
1072 	Surface::draw();
1073 	drawOcean();
1074 	drawLand();
1075 	drawRadars();
1076 	drawShadow();
1077 	drawMarkers();
1078 	drawDetail();
1079 	drawFlights();
1080 }
1081 
1082 
1083 /**
1084  * Renders the ocean, shading it according to the time of day.
1085  */
drawOcean()1086 void Globe::drawOcean()
1087 {
1088 	lock();
1089 	drawCircle(_cenX+1, _cenY, _radius+20, Palette::blockOffset(12)+0);
1090 //	ShaderDraw<Ocean>(ShaderSurface(this));
1091 	unlock();
1092 }
1093 
1094 
1095 
1096 
1097 /**
1098  * Renders the land, taking all the visible world polygons
1099  * and texturing and shading them accordingly.
1100  */
drawLand()1101 void Globe::drawLand()
1102 {
1103 	Sint16 x[4], y[4];
1104 
1105 	for (std::list<Polygon*>::iterator i = _cacheLand.begin(); i != _cacheLand.end(); ++i)
1106 	{
1107 		// Convert coordinates
1108 		for (int j = 0; j < (*i)->getPoints(); ++j)
1109 		{
1110 			x[j] = (*i)->getX(j);
1111 			y[j] = (*i)->getY(j);
1112 		}
1113 
1114 		// Apply textures according to zoom and shade
1115 		int zoom = (2 - (int)floor(_zoom / 2.0)) * NUM_TEXTURES;
1116 		drawTexturedPolygon(x, y, (*i)->getPoints(), _texture->getFrame((*i)->getTexture() + zoom), 0, 0);
1117 	}
1118 }
1119 
1120 /**
1121  * Get position of sun from point on globe
1122  * @param lon longitude of position
1123  * @param lat latitude of position
1124  * @return position of sun
1125  */
getSunDirection(double lon,double lat) const1126 Cord Globe::getSunDirection(double lon, double lat) const
1127 {
1128 	const double curTime = _game->getSavedGame()->getTime()->getDaylight();
1129 	const double rot = curTime * 2*M_PI;
1130 	double sun;
1131 
1132 	if (Options::globeSeasons)
1133 	{
1134 		const int MonthDays1[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
1135 		const int MonthDays2[] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
1136 
1137 		int year=_game->getSavedGame()->getTime()->getYear();
1138 		int month=_game->getSavedGame()->getTime()->getMonth()-1;
1139 		int day=_game->getSavedGame()->getTime()->getDay()-1;
1140 
1141 		double tm = (double)(( _game->getSavedGame()->getTime()->getHour() * 60
1142 			+ _game->getSavedGame()->getTime()->getMinute() ) * 60
1143 			+ _game->getSavedGame()->getTime()->getSecond() ) / 86400; //day fraction is also taken into account
1144 
1145 		double CurDay;
1146 		if (year%4 == 0 && !(year%100 == 0 && year%400 != 0))
1147 			CurDay = (MonthDays2[month] + day + tm )/366 - 0.219; //spring equinox (start of astronomic year)
1148 		else
1149 			CurDay = (MonthDays1[month] + day + tm )/365 - 0.219;
1150 		if (CurDay<0) CurDay += 1.;
1151 
1152 		sun = -0.261 * sin(CurDay*2*M_PI);
1153 	}
1154 	else
1155 		sun = 0;
1156 
1157 	Cord sun_direction(cos(rot+lon), sin(rot+lon)*-sin(lat), sin(rot+lon)*cos(lat));
1158 
1159 	Cord pole(0, cos(lat), sin(lat));
1160 
1161 	if (sun>0)
1162 		 sun_direction *= 1. - sun;
1163 	else
1164 		 sun_direction *= 1. + sun;
1165 
1166 	pole *= sun;
1167 	sun_direction += pole;
1168 	double norm = sun_direction.norm();
1169 	//norm should be always greater than 0
1170 	norm = 1./norm;
1171 	sun_direction *= norm;
1172 	return sun_direction;
1173 }
1174 
1175 
drawShadow()1176 void Globe::drawShadow()
1177 {
1178 	ShaderMove<Cord> earth = ShaderMove<Cord>(_earthData[_zoom], getWidth(), getHeight());
1179 	ShaderRepeat<Sint16> noise = ShaderRepeat<Sint16>(_randomNoiseData, static_data.random_surf_size, static_data.random_surf_size);
1180 
1181 	earth.setMove(_cenX-getWidth()/2, _cenY-getHeight()/2);
1182 
1183 	lock();
1184 	ShaderDraw<CreateShadow>(ShaderSurface(this), earth, ShaderScalar(getSunDirection(_cenLon, _cenLat)), noise);
1185 	unlock();
1186 
1187 }
1188 
1189 
XuLine(Surface * surface,Surface * src,double x1,double y1,double x2,double y2,int shade)1190 void Globe::XuLine(Surface* surface, Surface* src, double x1, double y1, double x2, double y2, int shade)
1191 {
1192 	if (_clipper->LineClip(&x1,&y1,&x2,&y2) != 1) return; //empty line
1193 
1194 	double deltax = x2-x1, deltay = y2-y1;
1195 	bool inv;
1196 	Sint16 tcol;
1197 	double len,x0,y0,SX,SY;
1198 	if (abs((int)y2-(int)y1) > abs((int)x2-(int)x1))
1199 	{
1200 		len=abs((int)y2-(int)y1);
1201 		inv=false;
1202 	}
1203 	else
1204 	{
1205 		len=abs((int)x2-(int)x1);
1206 		inv=true;
1207 	}
1208 
1209 	if (y2<y1) {
1210     SY=-1;
1211   } else if ( AreSame(deltay, 0.0) ) {
1212     SY=0;
1213   } else {
1214     SY=1;
1215   }
1216 
1217 	if (x2<x1) {
1218     SX=-1;
1219   } else if ( AreSame(deltax, 0.0) ) {
1220     SX=0;
1221   } else {
1222     SX=1;
1223   }
1224 
1225 	x0=x1;  y0=y1;
1226 	if (inv)
1227 		SY=(deltay/len);
1228 	else
1229 		SX=(deltax/len);
1230 
1231 	while(len>0)
1232 	{
1233 		tcol=src->getPixel((int)x0,(int)y0);
1234 		if (tcol)
1235 		{
1236 			const int d = tcol & helper::ColorGroup;
1237 			if(d ==  Palette::blockOffset(12) || d ==  Palette::blockOffset(13))
1238 			{
1239 				//this pixel is ocean
1240 				tcol = Palette::blockOffset(12) + shade + 8;
1241 			}
1242 			else
1243 			{
1244 				const int e = tcol + shade;
1245 				if(e > d + helper::ColorShade)
1246 					tcol = d + helper::ColorShade;
1247 				else tcol = e;
1248 			}
1249 			surface->setPixel((int)x0,(int)y0,tcol);
1250 		}
1251 		x0+=SX;
1252 		y0+=SY;
1253 		len-=1.0;
1254 	}
1255 }
1256 
1257 /**
1258  * Draws the radar ranges of player bases on the globe.
1259  */
drawRadars()1260 void Globe::drawRadars()
1261 {
1262 	_radars->clear();
1263 	if (!Options::globeRadarLines)
1264 		return;
1265 
1266 	double x, y;
1267 	double tr, range;
1268 	double lat, lon;
1269 	std::vector<double> ranges;
1270 
1271 	_radars->lock();
1272 
1273 	if (_hover)
1274 	{
1275 		const std::vector<std::string> &facilities = _game->getRuleset()->getBaseFacilitiesList();
1276 		for (std::vector<std::string>::const_iterator i = facilities.begin(); i != facilities.end(); ++i)
1277 		{
1278 			range=_game->getRuleset()->getBaseFacility(*i)->getRadarRange();
1279 			range = range * (1 / 60.0) * (M_PI / 180);
1280 			drawGlobeCircle(_hoverLat,_hoverLon,range,48);
1281 			if (Options::globeAllRadarsOnBaseBuild) ranges.push_back(range);
1282 		}
1283 	}
1284 
1285 	// Draw radars around bases
1286 	for (std::vector<Base*>::iterator i = _game->getSavedGame()->getBases()->begin(); i != _game->getSavedGame()->getBases()->end(); ++i)
1287 	{
1288 		lat = (*i)->getLatitude();
1289 		lon = (*i)->getLongitude();
1290 		// Cheap hack to hide bases when they haven't been placed yet
1291 		if (( !(AreSame(lon, 0.0) && AreSame(lat, 0.0)) )/* &&
1292 			!pointBack((*i)->getLongitude(), (*i)->getLatitude())*/)
1293 		{
1294 			polarToCart(lon, lat, &x, &y);
1295 
1296 			if (_hover && Options::globeAllRadarsOnBaseBuild)
1297 			{
1298 				for (size_t j=0; j<ranges.size(); j++) drawGlobeCircle(lat,lon,ranges[j],48);
1299 			}
1300 			else
1301 			{
1302 				range = 0;
1303 				for (std::vector<BaseFacility*>::iterator j = (*i)->getFacilities()->begin(); j != (*i)->getFacilities()->end(); ++j)
1304 				{
1305 					if ((*j)->getBuildTime() == 0)
1306 					{
1307 						tr = (*j)->getRules()->getRadarRange();
1308 						if (tr > range) range = tr;
1309 					}
1310 				}
1311 				range = range * (1 / 60.0) * (M_PI / 180);
1312 
1313 				if (range>0) drawGlobeCircle(lat,lon,range,48);
1314 			}
1315 
1316 		}
1317 
1318 		for (std::vector<Craft*>::iterator j = (*i)->getCrafts()->begin(); j != (*i)->getCrafts()->end(); ++j)
1319 		{
1320 			lat=(*j)->getLatitude();
1321 			lon=(*j)->getLongitude();
1322 			if ((*j)->getStatus()!= "STR_OUT")
1323 				continue;
1324 			polarToCart(lon, lat, &x, &y);
1325 			range = (*j)->getRules()->getRadarRange();
1326 			range = range * (1 / 60.0) * (M_PI / 180);
1327 
1328 			if (range>0) drawGlobeCircle(lat,lon,range,24);
1329 		}
1330 	}
1331 
1332 	_radars->unlock();
1333 }
1334 
1335 /**
1336  *	Draw globe range circle
1337  */
drawGlobeCircle(double lat,double lon,double radius,int segments)1338 void Globe::drawGlobeCircle(double lat, double lon, double radius, int segments)
1339 {
1340 	double x, y, x2 = 0, y2 = 0;
1341 	double lat1, lon1;
1342 	double seg = M_PI / (static_cast<double>(segments) / 2);
1343 	for (double az = 0; az <= M_PI*2+0.01; az+=seg) //48 circle segments
1344 	{
1345 		//calculating sphere-projected circle
1346 		lat1 = asin(sin(lat) * cos(radius) + cos(lat) * sin(radius) * cos(az));
1347 		lon1 = lon + atan2(sin(az) * sin(radius) * cos(lat), cos(radius) - sin(lat) * sin(lat1));
1348 		polarToCart(lon1, lat1, &x, &y);
1349 		if ( AreSame(az, 0.0) ) //first vertex is for initialization only
1350 		{
1351 			x2=x;
1352 			y2=y;
1353 			continue;
1354 		}
1355 		if (!pointBack(lon1,lat1))
1356 			XuLine(_radars, this, x, y, x2, y2, 4);
1357 		x2=x; y2=y;
1358 	}
1359 }
1360 
1361 
setNewBaseHover(void)1362 void Globe::setNewBaseHover(void)
1363 {
1364 	_hover=true;
1365 }
unsetNewBaseHover(void)1366 void Globe::unsetNewBaseHover(void)
1367 {
1368 	_hover=false;
1369 }
getNewBaseHover(void)1370 bool Globe::getNewBaseHover(void)
1371 {
1372 	return _hover;
1373 }
setNewBaseHoverPos(double lon,double lat)1374 void Globe::setNewBaseHoverPos(double lon, double lat)
1375 {
1376 	_hoverLon=lon;
1377 	_hoverLat=lat;
1378 }
1379 
1380 
drawVHLine(Surface * surface,double lon1,double lat1,double lon2,double lat2,Uint8 color)1381 void Globe::drawVHLine(Surface *surface, double lon1, double lat1, double lon2, double lat2, Uint8 color)
1382 {
1383 	double sx = lon2 - lon1;
1384 	double sy = lat2 - lat1;
1385 	double ln1, lt1, ln2, lt2;
1386 	int seg;
1387 	Sint16 x1, y1, x2, y2;
1388 
1389 	if (sx<0) sx += 2*M_PI;
1390 
1391 	if (fabs(sx)<0.01)
1392 	{
1393 		seg = abs( sy/(2*M_PI)*48 );
1394 		if (seg == 0) ++seg;
1395 	}
1396 	else
1397 	{
1398 		seg = abs( sx/(2*M_PI)*96 );
1399 		if (seg == 0) ++seg;
1400 	}
1401 
1402 	sx /= seg;
1403 	sy /= seg;
1404 
1405 	for (int i = 0; i < seg; ++i)
1406 	{
1407 		ln1 = lon1 + sx*i;
1408 		lt1 = lat1 + sy*i;
1409 		ln2 = lon1 + sx*(i+1);
1410 		lt2 = lat1 + sy*(i+1);
1411 
1412 		if (!pointBack(ln2, lt2)&&!pointBack(ln1, lt1))
1413 		{
1414 			polarToCart(ln1,lt1,&x1,&y1);
1415 			polarToCart(ln2,lt2,&x2,&y2);
1416 			surface->drawLine(x1, y1, x2, y2, color);
1417 		}
1418 	}
1419 }
1420 
1421 
1422 /**
1423  * Draws the details of the countries on the globe,
1424  * based on the current zoom level.
1425  */
drawDetail()1426 void Globe::drawDetail()
1427 {
1428 	_countries->clear();
1429 
1430 	if (!Options::globeDetail)
1431 		return;
1432 
1433 	// Draw the country borders
1434 	if (_zoom >= 1)
1435 	{
1436 		// Lock the surface
1437 		_countries->lock();
1438 
1439 		for (std::list<Polyline*>::iterator i = _game->getResourcePack()->getPolylines()->begin(); i != _game->getResourcePack()->getPolylines()->end(); ++i)
1440 		{
1441 			Sint16 x[2], y[2];
1442 			for (int j = 0; j < (*i)->getPoints() - 1; ++j)
1443 			{
1444 				// Don't draw if polyline is facing back
1445 				if (pointBack((*i)->getLongitude(j), (*i)->getLatitude(j)) || pointBack((*i)->getLongitude(j + 1), (*i)->getLatitude(j + 1)))
1446 					continue;
1447 
1448 				// Convert coordinates
1449 				polarToCart((*i)->getLongitude(j), (*i)->getLatitude(j), &x[0], &y[0]);
1450 				polarToCart((*i)->getLongitude(j + 1), (*i)->getLatitude(j + 1), &x[1], &y[1]);
1451 
1452 				_countries->drawLine(x[0], y[0], x[1], y[1], Palette::blockOffset(10)+2);
1453 			}
1454 		}
1455 
1456 		// Unlock the surface
1457 		_countries->unlock();
1458 	}
1459 
1460 	// Draw the country names
1461 	if (_zoom >= 2)
1462 	{
1463 		Text *label = new Text(100, 9, 0, 0);
1464 		label->setPalette(getPalette());
1465 		label->initText(_game->getResourcePack()->getFont("FONT_BIG"), _game->getResourcePack()->getFont("FONT_SMALL"), _game->getLanguage());
1466 		label->setAlign(ALIGN_CENTER);
1467 		label->setColor(Palette::blockOffset(15)-1);
1468 
1469 		Sint16 x, y;
1470 		for (std::vector<Country*>::iterator i = _game->getSavedGame()->getCountries()->begin(); i != _game->getSavedGame()->getCountries()->end(); ++i)
1471 		{
1472 			// Don't draw if label is facing back
1473 			if (pointBack((*i)->getRules()->getLabelLongitude(), (*i)->getRules()->getLabelLatitude()))
1474 				continue;
1475 
1476 			// Convert coordinates
1477 			polarToCart((*i)->getRules()->getLabelLongitude(), (*i)->getRules()->getLabelLatitude(), &x, &y);
1478 
1479 			label->setX(x - 40);
1480 			label->setY(y);
1481 			label->setText(_game->getLanguage()->getString((*i)->getRules()->getType()));
1482 			label->blit(_countries);
1483 		}
1484 
1485 		delete label;
1486 	}
1487 
1488 	// Draw the city and base markers
1489 	if (_zoom >= 3)
1490 	{
1491 		Text *label = new Text(80, 9, 0, 0);
1492 		label->setPalette(getPalette());
1493 		label->initText(_game->getResourcePack()->getFont("FONT_BIG"), _game->getResourcePack()->getFont("FONT_SMALL"), _game->getLanguage());
1494 		label->setAlign(ALIGN_CENTER);
1495 		label->setColor(Palette::blockOffset(8)+10);
1496 
1497 		Sint16 x, y;
1498 		for (std::vector<Region*>::iterator i = _game->getSavedGame()->getRegions()->begin(); i != _game->getSavedGame()->getRegions()->end(); ++i)
1499 		{
1500 			for (std::vector<City*>::iterator j = (*i)->getRules()->getCities()->begin(); j != (*i)->getRules()->getCities()->end(); ++j)
1501 			{
1502 				// Don't draw if city is facing back
1503 				if (pointBack((*j)->getLongitude(), (*j)->getLatitude()))
1504 					continue;
1505 
1506 				// Convert coordinates
1507 				polarToCart((*j)->getLongitude(), (*j)->getLatitude(), &x, &y);
1508 
1509 				_mkCity->setX(x - 1);
1510 				_mkCity->setY(y - 1);
1511 				_mkCity->setPalette(getPalette());
1512 				_mkCity->blit(_countries);
1513 
1514 				label->setX(x - 40);
1515 				label->setY(y + 2);
1516 				label->setText(_game->getLanguage()->getString((*j)->getName()));
1517 				label->blit(_countries);
1518 			}
1519 		}
1520 		// Draw bases names
1521 		for (std::vector<Base*>::iterator j = _game->getSavedGame()->getBases()->begin(); j != _game->getSavedGame()->getBases()->end(); ++j)
1522 		{
1523 			if (pointBack((*j)->getLongitude(), (*j)->getLatitude()))
1524 				continue;
1525 			polarToCart((*j)->getLongitude(), (*j)->getLatitude(), &x, &y);
1526 			label->setX(x - 40);
1527 			label->setY(y + 2);
1528 			label->setColor(Palette::blockOffset(8)+5);
1529 			label->setText((*j)->getName());
1530 			label->blit(_countries);
1531 		}
1532 
1533 		delete label;
1534 	}
1535 
1536 	static int debugType = 0;
1537 	static bool canSwitchDebugType = false;
1538 	if (_game->getSavedGame()->getDebugMode())
1539 	{
1540 		int color;
1541 		canSwitchDebugType = true;
1542 		if (debugType == 0)
1543 		{
1544 			color = 0;
1545 			for (std::vector<Country*>::iterator i = _game->getSavedGame()->getCountries()->begin(); i != _game->getSavedGame()->getCountries()->end(); ++i)
1546 			{
1547 				color += 10;
1548 				for(size_t k = 0; k != (*i)->getRules()->getLatMax().size(); ++k)
1549 				{
1550 					double lon2 = (*i)->getRules()->getLonMax().at(k);
1551 					double lon1 = (*i)->getRules()->getLonMin().at(k);
1552 					double lat2 = (*i)->getRules()->getLatMax().at(k);
1553 					double lat1 = (*i)->getRules()->getLatMin().at(k);
1554 
1555 					drawVHLine(_countries, lon1, lat1, lon2, lat1, color);
1556 					drawVHLine(_countries, lon1, lat2, lon2, lat2, color);
1557 					drawVHLine(_countries, lon1, lat1, lon1, lat2, color);
1558 					drawVHLine(_countries, lon2, lat1, lon2, lat2, color);
1559 				}
1560 			}
1561 		}
1562 		else if (debugType == 1)
1563 		{
1564 			color = 0;
1565 			for (std::vector<Region*>::iterator i = _game->getSavedGame()->getRegions()->begin(); i != _game->getSavedGame()->getRegions()->end(); ++i)
1566 			{
1567 				color += 10;
1568 				for(size_t k = 0; k != (*i)->getRules()->getLatMax().size(); ++k)
1569 				{
1570 					double lon2 = (*i)->getRules()->getLonMax().at(k);
1571 					double lon1 = (*i)->getRules()->getLonMin().at(k);
1572 					double lat2 = (*i)->getRules()->getLatMax().at(k);
1573 					double lat1 = (*i)->getRules()->getLatMin().at(k);
1574 
1575 					drawVHLine(_countries, lon1, lat1, lon2, lat1, color);
1576 					drawVHLine(_countries, lon1, lat2, lon2, lat2, color);
1577 					drawVHLine(_countries, lon1, lat1, lon1, lat2, color);
1578 					drawVHLine(_countries, lon2, lat1, lon2, lat2, color);
1579 				}
1580 			}
1581 		}
1582 		else if (debugType == 2)
1583 		{
1584 			for (std::vector<Region*>::iterator i = _game->getSavedGame()->getRegions()->begin(); i != _game->getSavedGame()->getRegions()->end(); ++i)
1585 			{
1586 				color = -1;
1587 				for (std::vector<MissionZone>::const_iterator j = (*i)->getRules()->getMissionZones().begin(); j != (*i)->getRules()->getMissionZones().end(); ++j)
1588 				{
1589 					color += 2;
1590 					for(std::vector<MissionArea>::const_iterator k = (*j).areas.begin(); k != (*j).areas.end(); ++k)
1591 					{
1592 						double lon2 = (*k).lonMax * M_PI / 180;
1593 						double lon1 = (*k).lonMin * M_PI / 180;
1594 						double lat2 = (*k).latMax * M_PI / 180;
1595 						double lat1 = (*k).latMin * M_PI / 180;
1596 
1597 						drawVHLine(_countries, lon1, lat1, lon2, lat1, color);
1598 						drawVHLine(_countries, lon1, lat2, lon2, lat2, color);
1599 						drawVHLine(_countries, lon1, lat1, lon1, lat2, color);
1600 						drawVHLine(_countries, lon2, lat1, lon2, lat2, color);
1601 					}
1602 				}
1603 			}
1604 		}
1605 	}
1606 	else
1607 	{
1608 		if (canSwitchDebugType)
1609 		{
1610 			++debugType;
1611 			if (debugType > 2) debugType = 0;
1612 			canSwitchDebugType = false;
1613 		}
1614 	}
1615 }
1616 
drawPath(Surface * surface,double lon1,double lat1,double lon2,double lat2)1617 void Globe::drawPath(Surface *surface, double lon1, double lat1, double lon2, double lat2)
1618 {
1619 	double length;
1620 	Sint16 count;
1621 	Sint16 x1, y1, x2, y2;
1622 	CordPolar p1, p2;
1623 	Cord a(CordPolar(lon1, lat1));
1624 	Cord b(CordPolar(lon2, lat2));
1625 
1626 	if(-b == a)
1627 		return;
1628 
1629 	b -= a;
1630 
1631 	//longer path have more parts
1632 	length = b.norm();
1633 	length *= length*15;
1634 	count = length + 1;
1635 	b /= count;
1636 	p1 = CordPolar(a);
1637 	polarToCart(p1.lon, p1.lat, &x1, &y1);
1638 	for(int i = 0; i < count; ++i)
1639 	{
1640 		a += b;
1641 		p2 = CordPolar(a);
1642 		polarToCart(p2.lon, p2.lat, &x2, &y2);
1643 
1644 		if (!pointBack(p1.lon, p1.lat) && !pointBack(p2.lon, p2.lat))
1645 		{
1646 			XuLine(surface, this, x1, y1, x2, y2, 8);
1647 		}
1648 
1649 		p1 = p2;
1650 		x1 = x2;
1651 		y1 = y2;
1652 	}
1653 }
1654 
1655 /**
1656  * Draws the flight paths of player craft flying on the globe.
1657  */
drawFlights()1658 void Globe::drawFlights()
1659 {
1660 	//_radars->clear();
1661 
1662 	if (!Options::globeFlightPaths)
1663 		return;
1664 
1665 	// Lock the surface
1666 	_radars->lock();
1667 
1668 	// Draw the craft flight paths
1669 	for (std::vector<Base*>::iterator i = _game->getSavedGame()->getBases()->begin(); i != _game->getSavedGame()->getBases()->end(); ++i)
1670 	{
1671 		for (std::vector<Craft*>::iterator j = (*i)->getCrafts()->begin(); j != (*i)->getCrafts()->end(); ++j)
1672 		{
1673 			// Hide crafts docked at base
1674 			if ((*j)->getStatus() != "STR_OUT" || (*j)->getDestination() == 0 /*|| pointBack((*j)->getLongitude(), (*j)->getLatitude())*/)
1675 				continue;
1676 
1677 			double lon1 = (*j)->getLongitude();
1678 			double lon2 = (*j)->getDestination()->getLongitude();
1679 			double lat1 = (*j)->getLatitude();
1680 			double lat2 = (*j)->getDestination()->getLatitude();
1681 
1682 			drawPath(_radars, lon1, lat1, lon2, lat2);
1683 		}
1684 	}
1685 
1686 	// Unlock the surface
1687 	_radars->unlock();
1688 }
1689 
1690 /**
1691  * Draws the markers of all the various things going
1692  * on around the world on top of the globe.
1693  */
drawMarkers()1694 void Globe::drawMarkers()
1695 {
1696 	Sint16 x, y;
1697 	_markers->clear();
1698 
1699 	// Draw the base markers
1700 	for (std::vector<Base*>::iterator i = _game->getSavedGame()->getBases()->begin(); i != _game->getSavedGame()->getBases()->end(); ++i)
1701 	{
1702 		// Cheap hack to hide bases when they haven't been placed yet
1703 		if (((*i)->getLongitude() != 0.0 || (*i)->getLatitude() != 0.0) &&
1704 			!pointBack((*i)->getLongitude(), (*i)->getLatitude()))
1705 		{
1706 			polarToCart((*i)->getLongitude(), (*i)->getLatitude(), &x, &y);
1707 
1708 			_mkXcomBase->setX(x - 1);
1709 			_mkXcomBase->setY(y - 1);
1710 			_mkXcomBase->blit(_markers);
1711 		}
1712 	}
1713 
1714 	// Draw the waypoint markers
1715 	for (std::vector<Waypoint*>::iterator i = _game->getSavedGame()->getWaypoints()->begin(); i != _game->getSavedGame()->getWaypoints()->end(); ++i)
1716 	{
1717 		if (pointBack((*i)->getLongitude(), (*i)->getLatitude()))
1718 			continue;
1719 
1720 		polarToCart((*i)->getLongitude(), (*i)->getLatitude(), &x, &y);
1721 
1722 		_mkWaypoint->setX(x - 1);
1723 		_mkWaypoint->setY(y - 1);
1724 		_mkWaypoint->blit(_markers);
1725 	}
1726 
1727 	// Draw the terror site markers
1728 	for (std::vector<TerrorSite*>::iterator i = _game->getSavedGame()->getTerrorSites()->begin(); i != _game->getSavedGame()->getTerrorSites()->end(); ++i)
1729 	{
1730 		if (pointBack((*i)->getLongitude(), (*i)->getLatitude()))
1731 			continue;
1732 
1733 		polarToCart((*i)->getLongitude(), (*i)->getLatitude(), &x, &y);
1734 
1735 		_mkAlienSite->setX(x - 1);
1736 		_mkAlienSite->setY(y - 1);
1737 		_mkAlienSite->blit(_markers);
1738 	}
1739 
1740 	// Draw the Alien Base markers
1741 	for (std::vector<AlienBase*>::iterator i = _game->getSavedGame()->getAlienBases()->begin(); i != _game->getSavedGame()->getAlienBases()->end(); ++i)
1742 	{
1743 		if (pointBack((*i)->getLongitude(), (*i)->getLatitude()))
1744 			continue;
1745 
1746 		polarToCart((*i)->getLongitude(), (*i)->getLatitude(), &x, &y);
1747 
1748 		if ((*i)->isDiscovered())
1749 		{
1750 			_mkAlienBase->setX(x - 1);
1751 			_mkAlienBase->setY(y - 1);
1752 			_mkAlienBase->blit(_markers);
1753 		}
1754 	}
1755 
1756 	// Draw the UFO markers
1757 	for (std::vector<Ufo*>::iterator i = _game->getSavedGame()->getUfos()->begin(); i != _game->getSavedGame()->getUfos()->end(); ++i)
1758 	{
1759 		if (pointBack((*i)->getLongitude(), (*i)->getLatitude()))
1760 			continue;
1761 		Surface *marker = 0;
1762 		switch ((*i)->getStatus())
1763 		{
1764 		case Ufo::DESTROYED:
1765 			continue;
1766 		case Ufo::FLYING:
1767 			if (!(*i)->getDetected()) continue;
1768 			marker = _mkFlyingUfo;
1769 			break;
1770 		case Ufo::LANDED:
1771 			if (!(*i)->getDetected()) continue;
1772 			marker = _mkLandedUfo;
1773 			break;
1774 		case Ufo::CRASHED:
1775 			marker = _mkCrashedUfo;
1776 			break;
1777 		}
1778 		polarToCart((*i)->getLongitude(), (*i)->getLatitude(), &x, &y);
1779 		marker->setX(x - 1);
1780 		marker->setY(y - 1);
1781 		marker->blit(_markers);
1782 	}
1783 
1784 	// Draw the craft markers
1785 	for (std::vector<Base*>::iterator i = _game->getSavedGame()->getBases()->begin(); i != _game->getSavedGame()->getBases()->end(); ++i)
1786 	{
1787 		for (std::vector<Craft*>::iterator j = (*i)->getCrafts()->begin(); j != (*i)->getCrafts()->end(); ++j)
1788 		{
1789 			// Hide crafts docked at base
1790 			if ((*j)->getStatus() != "STR_OUT" || pointBack((*j)->getLongitude(), (*j)->getLatitude()))
1791 				continue;
1792 
1793 			polarToCart((*j)->getLongitude(), (*j)->getLatitude(), &x, &y);
1794 
1795 			_mkCraft->setX(x - 1);
1796 			_mkCraft->setY(y - 1);
1797 			_mkCraft->blit(_markers);
1798 		}
1799 	}
1800 }
1801 
1802 /**
1803  * Blits the globe onto another surface.
1804  * @param surface Pointer to another surface.
1805  */
blit(Surface * surface)1806 void Globe::blit(Surface *surface)
1807 {
1808 	Surface::blit(surface);
1809 	_radars->blit(surface);
1810 	_countries->blit(surface);
1811 	_markers->blit(surface);
1812 }
1813 
1814 /**
1815  * Ignores any mouse hovers that are outside the globe.
1816  * @param action Pointer to an action.
1817  * @param state State that the action handlers belong to.
1818  */
mouseOver(Action * action,State * state)1819 void Globe::mouseOver(Action *action, State *state)
1820 {
1821 	double lon, lat;
1822 	cartToPolar((Sint16)floor(action->getAbsoluteXMouse()), (Sint16)floor(action->getAbsoluteYMouse()), &lon, &lat);
1823 
1824 	if (_isMouseScrolling && action->getDetails()->type == SDL_MOUSEMOTION)
1825 	{
1826 		// The following is the workaround for a rare problem where sometimes
1827 		// the mouse-release event is missed for any reason.
1828 		// (checking: is the dragScroll-mouse-button still pressed?)
1829 		// However if the SDL is also missed the release event, then it is to no avail :(
1830 		if (0 == (SDL_GetMouseState(0, 0)&SDL_BUTTON(Options::geoDragScrollButton)))
1831 		{ // so we missed again the mouse-release :(
1832 			// Check if we have to revoke the scrolling, because it was too short in time, so it was a click
1833 			if ((!_mouseMovedOverThreshold) && (SDL_GetTicks() - _mouseScrollingStartTime <= (Options::dragScrollTimeTolerance)))
1834 			{
1835 				center(_lonBeforeMouseScrolling, _latBeforeMouseScrolling);
1836 			}
1837 			_isMouseScrolled = _isMouseScrolling = false;
1838 			stopScrolling(action);
1839 			return;
1840 		}
1841 
1842 		_isMouseScrolled = true;
1843 
1844 		// Set the mouse cursor back
1845 		SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
1846 		SDL_WarpMouse((_game->getScreen()->getWidth() - 100) / 2 , _game->getScreen()->getHeight() / 2);
1847 		SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
1848 
1849 		// Check the threshold
1850 		_totalMouseMoveX += action->getDetails()->motion.xrel;
1851 		_totalMouseMoveY += action->getDetails()->motion.yrel;
1852 
1853 		if (!_mouseMovedOverThreshold)
1854 			_mouseMovedOverThreshold = ((std::abs(_totalMouseMoveX) > Options::dragScrollPixelTolerance) || (std::abs(_totalMouseMoveY) > Options::dragScrollPixelTolerance));
1855 
1856 		// Scrolling
1857 		if (Options::geoDragScrollInvert)
1858 		{
1859 			double newLon = ((double)_totalMouseMoveX / action->getXScale()) * ROTATE_LONGITUDE/(_zoom+1)/2;
1860 			double newLat = ((double)_totalMouseMoveY / action->getYScale()) * ROTATE_LATITUDE/(_zoom+1)/2;
1861 			center(_lonBeforeMouseScrolling + newLon / (Options::geoScrollSpeed / 10), _latBeforeMouseScrolling + newLat / (Options::geoScrollSpeed / 10));
1862 		}
1863 		else
1864 		{
1865 			double newLon = -action->getDetails()->motion.xrel * ROTATE_LONGITUDE/(_zoom+1)/2;
1866 			double newLat = -action->getDetails()->motion.yrel * ROTATE_LATITUDE/(_zoom+1)/2;
1867 			center(_cenLon + newLon / (Options::geoScrollSpeed / 10), _cenLat + newLat / (Options::geoScrollSpeed / 10));
1868 		}
1869 
1870 		// We don't want to look the mouse-cursor jumping :)
1871 		action->setMouseAction(_xBeforeMouseScrolling, _yBeforeMouseScrolling, getX(), getY());
1872 		action->getDetails()->motion.x = _xBeforeMouseScrolling; action->getDetails()->motion.y = _yBeforeMouseScrolling;
1873 		_game->getCursor()->handle(action);
1874 	}
1875 
1876 	if (_isMouseScrolling &&
1877 		(action->getDetails()->motion.x != _xBeforeMouseScrolling ||
1878 		action->getDetails()->motion.y != _yBeforeMouseScrolling))
1879 	{
1880 		action->setMouseAction(_xBeforeMouseScrolling, _yBeforeMouseScrolling, getX(), getY());
1881 		action->getDetails()->motion.x = _xBeforeMouseScrolling; action->getDetails()->motion.y = _yBeforeMouseScrolling;
1882 	}
1883 	// Check for errors
1884 	if (lat == lat && lon == lon)
1885 	{
1886 		InteractiveSurface::mouseOver(action, state);
1887 	}
1888 }
1889 
1890 /**
1891  * Ignores any mouse clicks that are outside the globe.
1892  * @param action Pointer to an action.
1893  * @param state State that the action handlers belong to.
1894  */
mousePress(Action * action,State * state)1895 void Globe::mousePress(Action *action, State *state)
1896 {
1897 	double lon, lat;
1898 	cartToPolar((Sint16)floor(action->getAbsoluteXMouse()), (Sint16)floor(action->getAbsoluteYMouse()), &lon, &lat);
1899 
1900 	if (action->getDetails()->button.button == Options::geoDragScrollButton)
1901 	{
1902 		_isMouseScrolling = true;
1903 		_isMouseScrolled = false;
1904 		SDL_GetMouseState(&_xBeforeMouseScrolling, &_yBeforeMouseScrolling);
1905 		_lonBeforeMouseScrolling = _cenLon;
1906 		_latBeforeMouseScrolling = _cenLat;
1907 		_totalMouseMoveX = 0; _totalMouseMoveY = 0;
1908 		_mouseMovedOverThreshold = false;
1909 		_mouseScrollingStartTime = SDL_GetTicks();
1910 	}
1911 	// Check for errors
1912 	if (lat == lat && lon == lon)
1913 	{
1914 		InteractiveSurface::mousePress(action, state);
1915 	}
1916 }
1917 
1918 /**
1919  * Ignores any mouse clicks that are outside the globe.
1920  * @param action Pointer to an action.
1921  * @param state State that the action handlers belong to.
1922  */
mouseRelease(Action * action,State * state)1923 void Globe::mouseRelease(Action *action, State *state)
1924 {
1925 	double lon, lat;
1926 	cartToPolar((Sint16)floor(action->getAbsoluteXMouse()), (Sint16)floor(action->getAbsoluteYMouse()), &lon, &lat);
1927 	if (action->getDetails()->button.button == Options::geoDragScrollButton)
1928 	{
1929 		stopScrolling(action);
1930 	}
1931 	// Check for errors
1932 	if (lat == lat && lon == lon)
1933 	{
1934 		InteractiveSurface::mouseRelease(action, state);
1935 	}
1936 }
1937 
1938 /**
1939  * Ignores any mouse clicks that are outside the globe
1940  * and handles globe rotation and zooming.
1941  * @param action Pointer to an action.
1942  * @param state State that the action handlers belong to.
1943  */
mouseClick(Action * action,State * state)1944 void Globe::mouseClick(Action *action, State *state)
1945 {
1946 	if (action->getDetails()->button.button == SDL_BUTTON_WHEELUP)
1947 	{
1948 		zoomIn();
1949 	}
1950 	else if (action->getDetails()->button.button == SDL_BUTTON_WHEELDOWN)
1951 	{
1952 		zoomOut();
1953 	}
1954 
1955 	double lon, lat;
1956 	cartToPolar((Sint16)floor(action->getAbsoluteXMouse()), (Sint16)floor(action->getAbsoluteYMouse()), &lon, &lat);
1957 
1958 	// The following is the workaround for a rare problem where sometimes
1959 	// the mouse-release event is missed for any reason.
1960 	// However if the SDL is also missed the release event, then it is to no avail :(
1961 	// (this part handles the release if it is missed and now an other button is used)
1962 	if (_isMouseScrolling)
1963 	{
1964 		if (action->getDetails()->button.button != Options::geoDragScrollButton
1965 			&& 0 == (SDL_GetMouseState(0, 0)&SDL_BUTTON(Options::geoDragScrollButton)))
1966 		{ // so we missed again the mouse-release :(
1967 			// Check if we have to revoke the scrolling, because it was too short in time, so it was a click
1968 			if ((!_mouseMovedOverThreshold) && (SDL_GetTicks() - _mouseScrollingStartTime <= (Options::dragScrollTimeTolerance)))
1969 			{
1970 				center(_lonBeforeMouseScrolling, _latBeforeMouseScrolling);
1971 			}
1972 			_isMouseScrolled = _isMouseScrolling = false;
1973 			stopScrolling(action);
1974 		}
1975 	}
1976 
1977 	// DragScroll-Button release: release mouse-scroll-mode
1978 	if (_isMouseScrolling)
1979 	{
1980 		// While scrolling, other buttons are ineffective
1981 		if (action->getDetails()->button.button == Options::geoDragScrollButton)
1982 		{
1983 			_isMouseScrolling = false;
1984 			stopScrolling(action);
1985 		}
1986 		else
1987 		{
1988 			return;
1989 		}
1990 		// Check if we have to revoke the scrolling, because it was too short in time, so it was a click
1991 		if ((!_mouseMovedOverThreshold) && (SDL_GetTicks() - _mouseScrollingStartTime <= (Options::dragScrollTimeTolerance)))
1992 		{
1993 			_isMouseScrolled = false;
1994 			stopScrolling(action);
1995 			center(_lonBeforeMouseScrolling, _latBeforeMouseScrolling);
1996 		}
1997 		if (_isMouseScrolled) return;
1998 	}
1999 
2000 	// Check for errors
2001 	if (lat == lat && lon == lon)
2002 	{
2003 		InteractiveSurface::mouseClick(action, state);
2004 		if (action->getDetails()->button.button == SDL_BUTTON_RIGHT)
2005 		{
2006 			center(lon, lat);
2007 		}
2008 	}
2009 }
2010 
2011 /**
2012  * Handles globe keyboard shortcuts.
2013  * @param action Pointer to an action.
2014  * @param state State that the action handlers belong to.
2015  */
keyboardPress(Action * action,State * state)2016 void Globe::keyboardPress(Action *action, State *state)
2017 {
2018 	InteractiveSurface::keyboardPress(action, state);
2019 	if (action->getDetails()->key.keysym.sym == Options::keyGeoToggleDetail)
2020 	{
2021 		toggleDetail();
2022 	}
2023 	if (action->getDetails()->key.keysym.sym == Options::keyGeoToggleRadar)
2024 	{
2025 		toggleRadarLines();
2026 	}
2027 }
2028 
2029 /**
2030  * Get the polygons texture at a given point
2031  * @param lon Longitude of the point.
2032  * @param lat Latitude of the point.
2033  * @param texture pointer to texture ID returns -1 when polygon not found
2034  * @param shade pointer to shade
2035  */
getPolygonTextureAndShade(double lon,double lat,int * texture,int * shade) const2036 void Globe::getPolygonTextureAndShade(double lon, double lat, int *texture, int *shade) const
2037 {
2038 	///this is shade conversion from 0..31 levels of geoscape to battlescape levels 0..15
2039 	int worldshades[32] = {  0, 0, 0, 0, 1, 1, 2, 2,
2040 							 3, 3, 4, 4, 5, 5, 6, 6,
2041 							 7, 7, 8, 8, 9, 9,10,11,
2042 							11,12,12,13,13,14,15,15};
2043 
2044 	*texture = -1;
2045 	*shade = worldshades[ CreateShadow::getShadowValue(0, Cord(0.,0.,1.), getSunDirection(lon, lat), 0) ];
2046 
2047 	// We're only temporarily changing cenLon/cenLat so the "const" is actually preserved
2048 	Globe* const globe = const_cast<Globe* const>(this); // WARNING: BAD CODING PRACTICE
2049 	double oldLon = _cenLon, oldLat = _cenLat;
2050 	globe->_cenLon = lon;
2051 	globe->_cenLat = lat;
2052 	for (std::list<Polygon*>::iterator i = _game->getResourcePack()->getPolygons()->begin(); i != _game->getResourcePack()->getPolygons()->end(); ++i)
2053 	{
2054 		if (insidePolygon(lon, lat, *i))
2055 		{
2056 			*texture = (*i)->getTexture();
2057 			break;
2058 		}
2059 	}
2060 	globe->_cenLon = oldLon;
2061 	globe->_cenLat = oldLat;
2062 }
2063 
2064 /**
2065  * Checks if the globe is zoomed in to it's maximum.
2066  * @return Returns true if globe is at max zoom, otherwise returns false.
2067  */
getZoom() const2068 size_t Globe::getZoom() const
2069 {
2070 	return _zoom;
2071 }
2072 
2073 /*
2074  * Turns Radar lines on or off.
2075  */
toggleRadarLines()2076 void Globe::toggleRadarLines()
2077 {
2078 	Options::globeRadarLines = !Options::globeRadarLines;
2079 	drawRadars();
2080 }
2081 
2082 /*
2083  * Resizes the geoscape.
2084  */
resize()2085 void Globe::resize()
2086 {
2087 	Surface *surfaces[4] = {this, _markers, _countries, _radars};
2088 	int width = Options::baseXGeoscape - 64;
2089 	int height = Options::baseYGeoscape;
2090 
2091 	for (int i = 0; i < 4; ++i)
2092 	{
2093 		surfaces[i]->setWidth(width);
2094 		surfaces[i]->setHeight(height);
2095 		surfaces[i]->invalidate();
2096 	}
2097 	_clipper->Wxrig = width;
2098 	_clipper->Wybot = height;
2099 	_cenX = width / 2;
2100 	_cenY = height / 2;
2101 	setupRadii(width, height);
2102 	invalidate();
2103 }
2104 
2105 /*
2106  * Set up the Radius of earth at the various zoom levels.
2107  * @param width the new width of the globe.
2108  * @param height the new height of the globe.
2109  */
setupRadii(int width,int height)2110 void Globe::setupRadii(int width, int height)
2111 {
2112 	_zoomRadius.clear();
2113 
2114 	_zoomRadius.push_back(0.45*height);
2115 	_zoomRadius.push_back(0.60*height);
2116 	_zoomRadius.push_back(0.90*height);
2117 	_zoomRadius.push_back(1.40*height);
2118 	_zoomRadius.push_back(2.25*height);
2119 	_zoomRadius.push_back(3.60*height);
2120 
2121 	_radius = _zoomRadius[_zoom];
2122 	_radiusStep = (_zoomRadius[DOGFIGHT_ZOOM] - _zoomRadius[0]) / 10.0;
2123 
2124 	_earthData.resize(_zoomRadius.size());
2125 	//filling normal field for each radius
2126 
2127 	for(size_t r = 0; r<_zoomRadius.size(); ++r)
2128 	{
2129 		_earthData[r].resize(width * height);
2130 		for(int j=0; j<height; ++j)
2131 			for(int i=0; i<width; ++i)
2132 			{
2133 				_earthData[r][width*j + i] = static_data.circle_norm(width/2, height/2, _zoomRadius[r], i+.5, j+.5);
2134 			}
2135 	}
2136 }
2137 
2138 /**
2139  * Move the mouse back to where it started after we finish drag scrolling.
2140  * @param action Pointer to an action.
2141  */
stopScrolling(Action * action)2142 void Globe::stopScrolling(Action *action)
2143 {
2144 	SDL_WarpMouse(_xBeforeMouseScrolling, _yBeforeMouseScrolling);
2145 	action->setMouseAction(_xBeforeMouseScrolling, _yBeforeMouseScrolling, getX(), getY());
2146 }
2147 }
2148