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