UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cp_geoscape.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 */
24 
25 #include "../../cl_shared.h"
26 #include "../../ui/ui_dataids.h"
27 #include "../../ui/node/ui_node_geoscape.h"
28 #include "cp_overlay.h"
29 #include "cp_campaign.h"
30 #include "cp_geoscape.h"
31 #include "cp_popup.h"
32 #include "cp_mapfightequip.h"
33 #include "cp_missions.h"
34 #include "cp_ufo.h"
35 #include "cp_time.h"
36 #include "cp_xvi.h"
37 
39 
40 #ifdef DEBUG
41 static cvar_t* debug_showInterest;
42 #endif
43 
44 #define GLOBE_ROTATE -90
45 #define ZOOM_LIMIT 2.5f
46 
47 /* Functions */
48 static bool GEO_IsPositionSelected(const uiNode_t* node, const vec2_t pos, int x, int y);
49 
50 /* static variables */
51 static char textStandard[2048];
52 static int centerOnEventIdx;
54 /* Colors */
55 static const vec4_t green = {0.0f, 1.0f, 0.0f, 0.8f};
56 static const vec4_t yellow = {1.0f, 0.874f, 0.294f, 1.0f};
57 static const vec4_t red = {1.0f, 0.0f, 0.0f, 0.8f};
58 
59 static const float defaultBaseAngle = -90.0f;
61 static byte* terrainPic;
65 static byte* culturePic;
73 static byte* nationsPic;
78 /*
79 ==============================================================
80 CLICK ON MAP and MULTI SELECTION FUNCTIONS
81 ==============================================================
82 */
83 
85 {
86  return cgi->Cvar_GetInteger("geo_overlay_radar");
87 }
88 
89 static inline bool GEO_IsNationOverlayActivated (void)
90 {
91  return cgi->Cvar_GetInteger("geo_overlay_nation");
92 }
93 
94 static inline bool GEO_IsXVIOverlayActivated (void)
95 {
96  return cgi->Cvar_GetInteger("geo_overlay_xvi");
97 }
98 
106 bool GEO_Click (const uiNode_t* node, int x, int y, const vec2_t pos)
107 {
108  switch (ccs.mapAction) {
109  case MA_NEWBASE:
110  /* new base construction */
112  if (!MapIsWater(GEO_GetColor(pos, MAPTYPE_TERRAIN, nullptr))) {
113  if (B_GetCount() < MAX_BASES) {
114  Vector2Copy(pos, ccs.newBasePos);
115  CP_GameTimeStop();
116  cgi->Cmd_ExecuteString("mn_set_base_title");
117  cgi->UI_PushWindow("popup_newbase");
118  return true;
119  }
120  return false;
121  }
122  break;
123  case MA_NEWINSTALLATION:
124  if (!MapIsWater(GEO_GetColor(pos, MAPTYPE_TERRAIN, nullptr))) {
125  Vector2Copy(pos, ccs.newBasePos);
126  CP_GameTimeStop();
127  cgi->UI_PushWindow("popup_newinstallation");
128  return true;
129  }
130  break;
131  default:
132  break;
133  }
134 
135  /* Init data for multi selection */
136  cgi->UI_ExecuteConfunc("ui_clear_geoscape_selection");
137  int selection_count = 0;
138  /* Get selected missions */
139  MIS_Foreach(tempMission) {
140  if (tempMission->stage == STAGE_NOT_ACTIVE || !tempMission->onGeoscape)
141  continue;
142  if (!tempMission->pos || !GEO_IsPositionSelected(node, tempMission->pos, x, y))
143  continue;
144  cgi->UI_ExecuteConfunc("ui_add_geoscape_selection \"%s\" \"%d\" \"%s\"",
145  "mission", MIS_GetIdx(tempMission), MIS_GetName(tempMission));
146  ++selection_count;
147  }
148 
149  /* Get selected aircraft which belong */
150  AIR_Foreach(aircraft) {
151  if (AIR_IsAircraftOnGeoscape(aircraft) && aircraft->fuel > 0 && GEO_IsPositionSelected(node, aircraft->pos, x, y)) {
152  cgi->UI_ExecuteConfunc("ui_add_geoscape_selection \"%s\" \"%d\" \"%s\"",
153  "aircraft", aircraft->idx, aircraft->name);
154  ++selection_count;
155  }
156  }
157 
158  /* Get selected bases */
159  base_t* base = nullptr;
160  while ((base = B_GetNext(base)) != nullptr) {
161  if (!GEO_IsPositionSelected(node, base->pos, x, y))
162  continue;
163  cgi->UI_ExecuteConfunc("ui_add_geoscape_selection \"%s\" \"%d\" \"%s\"",
164  "base", base->idx, base->name);
165  ++selection_count;
166  }
167 
168  /* Get selected installations */
169  INS_Foreach(installation) {
170  if (!GEO_IsPositionSelected(node, installation->pos, x, y))
171  continue;
172  cgi->UI_ExecuteConfunc("ui_add_geoscape_selection \"%s\" \"%d\" \"%s\"",
173  "installation", installation->idx, installation->name);
174  ++selection_count;
175  }
176 
177  /* Get selected ufos */
178  aircraft_t* ufo = nullptr;
179  while ((ufo = UFO_GetNextOnGeoscape(ufo)) != nullptr) {
180  if (AIR_IsAircraftOnGeoscape(ufo) && GEO_IsPositionSelected(node, ufo->pos, x, y)) {
181  cgi->UI_ExecuteConfunc("ui_add_geoscape_selection \"%s\" \"%lu\" \"%s\"",
182  "ufo", UFO_GetGeoscapeIDX(ufo), UFO_GetName(ufo));
183  ++selection_count;
184  }
185  }
186 
187  if (selection_count > 0) {
188  CP_GameTimeStop();
189  cgi->UI_PushWindow("popup_geoscape_selection");
190  return true;
191  } else {
192  aircraft_t* aircraft = GEO_GetSelectedAircraft();
193  /* Nothing selected */
194  if (!aircraft) {
195  GEO_ResetAction();
196  return false;
197  }
198 
199  if (AIR_IsAircraftOnGeoscape(aircraft) && AIR_AircraftHasEnoughFuel(aircraft, pos)) {
200  /* Move the selected aircraft to the position clicked */
201  GEO_CalcLine(aircraft->pos, pos, &aircraft->route);
202  aircraft->status = AIR_TRANSIT;
203  aircraft->aircraftTarget = nullptr;
204  aircraft->time = 0;
205  aircraft->point = 0;
206  return true;
207  }
208  }
209  return false;
210 }
211 
212 
213 /*
214 ==============================================================
215 GEOSCAPE DRAWING AND COORDINATES
216 ==============================================================
217 */
218 
228 static bool GEO_3DMapToScreen (const uiNode_t* node, const vec2_t pos, int* x, int* y, int* z)
229 {
230  vec2_t mid;
231  vec3_t v, v1, rotationAxis;
232  const float radius = GLOBE_RADIUS;
233 
234  PolarToVec(pos, v);
235 
236  /* rotate the vector to switch of reference frame.
237  * We switch from the static frame of the earth to the local frame of the player */
238  VectorSet(rotationAxis, 0, 0, 1);
240  RotatePointAroundVector(v1, rotationAxis, v, - data.angles[PITCH]);
241 
242  VectorSet(rotationAxis, 0, 1, 0);
243  RotatePointAroundVector(v, rotationAxis, v1, - data.angles[YAW]);
244 
245  /* set mid to the coordinates of the center of the globe */
246  Vector2Set(mid, data.mapPos[0] + data.mapSize[0] / 2.0f, data.mapPos[1] + data.mapSize[1] / 2.0f);
247 
248  /* We now convert those coordinates relative to the center of the globe to coordinates of the screen
249  * (which are relative to the upper left side of the screen) */
250  *x = (int) (mid[0] - radius * v[1]);
251  *y = (int) (mid[1] - radius * v[0]);
252 
253  if (z)
254  *z = (int) (radius * v[2]);
255 
256  /* if the point is on the wrong side of the earth, the player cannot see it */
257  if (v[2] > 0)
258  return false;
259 
260  /* if the point is outside the screen, the player cannot see it */
261  if (*x < data.mapPos[0] && *y < data.mapPos[1]
262  && *x > data.mapPos[0] + data.mapSize[0]
263  && *y > data.mapPos[1] + data.mapSize[1])
264  return false;
265 
266  return true;
267 }
268 
278 static bool GEO_MapToScreen (const uiNode_t* node, const vec2_t pos, int* x, int* y)
279 {
281  /* get "raw" position */
282  float sx = pos[0] / 360 + data.center[0] - 0.5;
283 
284  /* shift it on screen */
285  if (sx < -0.5f)
286  sx += 1.0f;
287  else if (sx > +0.5f)
288  sx -= 1.0f;
289 
290  *x = data.mapPos[0] + 0.5f * data.mapSize[0] - sx * data.mapSize[0] * data.zoom;
291  *y = data.mapPos[1] + 0.5f * data.mapSize[1] - (pos[1] / 180.0f + data.center[1] - 0.5f) * data.mapSize[1] * data.zoom;
292 
293  if (*x < data.mapPos[0] && *y < data.mapPos[1]
294  && *x > data.mapPos[0] + data.mapSize[0]
295  && *y > data.mapPos[1] + data.mapSize[1])
296  return false;
297  return true;
298 }
299 
309 static bool GEO_AllMapToScreen (const uiNode_t* node, const vec2_t pos, int* x, int* y, int* z)
310 {
312  if (!data.flatgeoscape)
313  return GEO_3DMapToScreen(node, pos, x, y, z);
314 
315  if (z)
316  *z = -10;
317  return GEO_MapToScreen(node, pos, x, y);
318 }
319 
324 #define UI_MAP_DIST_SELECTION 15
325 
328 static bool GEO_IsPositionSelected (const uiNode_t* node, const vec2_t pos, int x, int y)
329 {
330  int msx, msy;
331 
332  if (GEO_AllMapToScreen(node, pos, &msx, &msy, nullptr))
333  if (x >= msx - UI_MAP_DIST_SELECTION && x <= msx + UI_MAP_DIST_SELECTION
334  && y >= msy - UI_MAP_DIST_SELECTION && y <= msy + UI_MAP_DIST_SELECTION)
335  return true;
336 
337  return false;
338 }
339 
348 static void GEO_Draw3DMarkerIfVisible (const uiNode_t* node, const vec2_t pos, float theta, const char* model, int skin)
349 {
351  if (data.flatgeoscape) {
352  int x, y;
353  vec3_t screenPos;
354 
355  GEO_AllMapToScreen(node, pos, &x, &y, nullptr);
356  VectorSet(screenPos, x, y, 0);
357  /* models are used on 2D geoscape for aircraft */
358  cgi->R_Draw2DMapMarkers(screenPos, theta, model, skin);
359  } else {
360  cgi->R_Draw3DMapMarkers(data.mapPos, data.mapSize, data.angles, pos, theta, GLOBE_RADIUS, model, skin);
361  }
362 }
363 
371 void GEO_CalcLine (const vec2_t start, const vec2_t end, mapline_t* line)
372 {
373  vec3_t s, e, v;
374  vec3_t normal;
375  vec2_t trafo, sa, ea;
376  float cosTrafo, sinTrafo;
377  float phiStart, phiEnd, dPhi, phi;
378  float* p;
379  int i, n;
380 
381  /* get plane normal */
382  PolarToVec(start, s);
383  PolarToVec(end, e);
384  /* Procedure below won't work if start is the same like end */
385  if (VectorEqual(s, e)) {
386  line->distance = 0;
387  line->numPoints = 2;
388  Vector2Set(line->point[0], end[0], end[1]);
389  Vector2Set(line->point[1], end[0], end[1]);
390  return;
391  }
392 
393  CrossProduct(s, e, normal);
394  VectorNormalize(normal);
395 
396  /* get transformation */
397  VecToPolar(normal, trafo);
398  cosTrafo = cos(trafo[1] * torad);
399  sinTrafo = sin(trafo[1] * torad);
400 
401  sa[0] = start[0] - trafo[0];
402  sa[1] = start[1];
403  PolarToVec(sa, s);
404  ea[0] = end[0] - trafo[0];
405  ea[1] = end[1];
406  PolarToVec(ea, e);
407 
408  phiStart = atan2(s[1], cosTrafo * s[2] - sinTrafo * s[0]);
409  phiEnd = atan2(e[1], cosTrafo * e[2] - sinTrafo * e[0]);
410 
411  /* get waypoints */
412  if (phiEnd < phiStart - M_PI)
413  phiEnd += 2 * M_PI;
414  if (phiEnd > phiStart + M_PI)
415  phiEnd -= 2 * M_PI;
416 
417  n = (phiEnd - phiStart) / M_PI * LINE_MAXSEG;
418  if (n > 0)
419  n = n + 1;
420  else
421  n = -n + 1;
422 
423  line->distance = fabs(phiEnd - phiStart) / n * todeg;
424  line->numPoints = n + 1;
425  /* make sure we do not exceed route array size */
426  assert(line->numPoints <= LINE_MAXPTS);
427  dPhi = (phiEnd - phiStart) / n;
428  p = nullptr;
429  for (phi = phiStart, i = 0; i <= n; phi += dPhi, i++) {
430  const float* last = p;
431  p = line->point[i];
432  VectorSet(v, -sinTrafo * cos(phi), sin(phi), cosTrafo * cos(phi));
433  VecToPolar(v, p);
434  p[0] += trafo[0];
435 
436  if (!last) {
437  while (p[0] < -180.0)
438  p[0] += 360.0;
439  while (p[0] > +180.0)
440  p[0] -= 360.0;
441  } else {
442  while (p[0] - last[0] > +180.0)
443  p[0] -= 360.0;
444  while (p[0] - last[0] < -180.0)
445  p[0] += 360.0;
446  }
447  }
448 }
449 
457 static void GEO_MapDrawLine (const uiNode_t* node, const mapline_t* line)
458 {
459  const vec4_t color = {1, 0.5, 0.5, 1};
461  screenPoint_t* p;
462  int i, start, old;
463 
464  /* draw */
465  cgi->R_Color(color);
466  start = 0;
468  old = data.mapSize[0] / 2;
469  for (i = 0, p = pts; i < line->numPoints; i++, p++) {
470  GEO_MapToScreen(node, line->point[i], &p->x, &p->y);
471 
472  /* If we cross longitude 180 degree (right/left edge of the screen), draw the first part of the path */
473  if (i > start && abs(p->x - old) > data.mapSize[0] / 2) {
474  /* shift last point */
475  int diff;
476 
477  if (p->x - old > data.mapSize[0] / 2)
478  diff = -data.mapSize[0] * data.zoom;
479  else
480  diff = data.mapSize[0] * data.zoom;
481  p->x += diff;
482 
483  /* wrap around screen border */
484  cgi->R_DrawLineStrip(i - start, (int*)(&pts));
485 
486  /* first path of the path is drawn, now we begin the second part of the path */
487  /* shift first point, continue drawing */
488  start = i;
489  pts[0].x = p[-1].x - diff;
490  pts[0].y = p[-1].y;
491  p = pts;
492  }
493  old = p->x;
494  }
495 
496  cgi->R_DrawLineStrip(i - start, (int*)(&pts));
497  cgi->R_Color(nullptr);
498 }
499 
507 static void GEO_3DMapDrawLine (const uiNode_t* node, const mapline_t* line)
508 {
509  const vec4_t color = {1, 0.5, 0.5, 1};
511  int numPoints, start;
512 
513  start = 0;
514  numPoints = 0;
515 
516  /* draw only when the point of the path is visible */
517  cgi->R_Color(color);
518  for (int i = 0; i < line->numPoints; i++) {
519  if (GEO_3DMapToScreen(node, line->point[i], &pts[i].x, &pts[i].y, nullptr))
520  numPoints++;
521  else if (!numPoints)
522  /* the point which is not drawn is at the beginning of the path */
523  start++;
524  }
525 
526  cgi->R_DrawLineStrip(numPoints, (int*)(&pts[start]));
527  cgi->R_Color(nullptr);
528 }
529 
530 #define CIRCLE_DRAW_POINTS 60
531 
540 static void GEO_MapDrawEquidistantPoints (const uiNode_t* node, const vec2_t center, const float angle, const vec4_t color)
541 {
543  int numPoints = 0;
544  vec3_t initialVector, rotationAxis, currentPoint, centerPos;
545 
546  cgi->R_Color(color);
547 
548  /* Set centerPos corresponding to cartesian coordinates of the center point */
549  PolarToVec(center, centerPos);
550 
551  /* Find a perpendicular vector to centerPos, and rotate centerPos around it to obtain one point distant of angle from centerPos */
552  PerpendicularVector(rotationAxis, centerPos);
553  RotatePointAroundVector(initialVector, rotationAxis, centerPos, angle);
554 
555  bool draw = false;
556  bool oldDraw = false;
558  /* Now, each equidistant point is given by a rotation around centerPos */
559  for (int i = 0; i <= CIRCLE_DRAW_POINTS; i++) {
560  int xCircle, yCircle;
561  vec2_t posCircle;
562  const float degrees = i * 360.0f / (float)CIRCLE_DRAW_POINTS;
563  RotatePointAroundVector(currentPoint, centerPos, initialVector, degrees);
564  VecToPolar(currentPoint, posCircle);
565  if (GEO_AllMapToScreen(node, posCircle, &xCircle, &yCircle, nullptr)) {
566  draw = true;
567  if (data.flatgeoscape && numPoints != 0 && abs(pts[numPoints - 1].x - xCircle) > 512)
568  oldDraw = false;
569  }
570 
571  /* if moving from a point of the screen to a distant one, draw the path we already calculated, and begin a new path
572  * (to avoid unwanted lines) */
573  if (draw != oldDraw && i != 0) {
574  cgi->R_DrawLineStrip(numPoints, (int*)(&pts));
575  numPoints = 0;
576  }
577  /* if the current point is to be drawn, add it to the path */
578  if (draw) {
579  pts[numPoints].x = xCircle;
580  pts[numPoints].y = yCircle;
581  numPoints++;
582  }
583  /* update value of oldDraw */
584  oldDraw = draw;
585  }
586 
587  /* Draw the last path */
588  cgi->R_DrawLineStrip(numPoints, (int*)(&pts));
589  cgi->R_Color(nullptr);
590 }
591 
600 static float GEO_AngleOfPath3D (const vec2_t start, const vec2_t end, vec3_t direction, vec3_t ortVector)
601 {
602  vec3_t start3D, end3D, north3D, ortToDest, ortToPole, v;
603  const vec2_t northPole = {0.0f, 90.0f};
605  PolarToVec(start, start3D);
606  PolarToVec(end, end3D);
607  PolarToVec(northPole, north3D);
608 
609  /* calculate the vector othogonal to movement */
610  CrossProduct(start3D, end3D, ortToDest);
611  VectorNormalize(ortToDest);
612  if (ortVector) {
613  VectorCopy(ortToDest, ortVector);
614  }
615 
616  /* calculate the vector othogonal to north pole (from model location) */
617  CrossProduct(start3D, north3D, ortToPole);
618  VectorNormalize(ortToPole);
619 
624  /* smooth change of direction if the model is not idle */
625  if (direction) {
626  VectorSubtract(ortToDest, direction, v);
627  const float dist = VectorLength(v);
628  if (dist > 0.01) {
629  vec3_t rotationAxis;
630  CrossProduct(direction, ortToDest, rotationAxis);
631  VectorNormalize(rotationAxis);
632  RotatePointAroundVector(v, rotationAxis, direction, 5.0);
633  VectorCopy(v, direction);
634  VectorSubtract(ortToDest, direction, v);
635  if (VectorLength(v) < dist)
636  VectorCopy(direction, ortToDest);
637  else
638  VectorCopy(ortToDest, direction);
639  }
640  }
641 
642  /* calculate the angle the model is making at earth surface with north pole direction */
643  float angle = todeg * acos(DotProduct(ortToDest, ortToPole));
644  /* with arcos, you only get the absolute value of the angle: get the sign */
645  CrossProduct(ortToDest, ortToPole, v);
646  if (DotProduct(start3D, v) < 0)
647  angle = - angle;
648 
649  return angle;
650 }
651 
660 static float GEO_AngleOfPath2D (const vec2_t start, const vec2_t end, vec3_t direction, vec3_t ortVector)
661 {
662  vec3_t start3D, end3D, tangentVector, v, rotationAxis;
663 
664  /* calculate the vector tangent to movement */
665  PolarToVec(start, start3D);
666  PolarToVec(end, end3D);
667  if (ortVector) {
668  CrossProduct(start3D, end3D, ortVector);
669  VectorNormalize(ortVector);
670  CrossProduct(ortVector, start3D, tangentVector);
671  } else {
672  CrossProduct(start3D, end3D, v);
673  CrossProduct(v, start3D, tangentVector);
674  }
675  VectorNormalize(tangentVector);
676 
677  /* smooth change of direction if the model is not idle */
678  if (direction) {
679  VectorSubtract(tangentVector, direction, v);
680  const float dist = VectorLength(v);
681  if (dist > 0.01) {
682  CrossProduct(direction, tangentVector, rotationAxis);
683  VectorNormalize(rotationAxis);
684  RotatePointAroundVector(v, rotationAxis, direction, 5.0);
685  VectorSubtract(tangentVector, direction, v);
686  if (VectorLength(v) < dist)
687  VectorCopy(direction, tangentVector);
688  else
689  VectorCopy(tangentVector, direction);
690  }
691  }
692 
693  VectorSet(rotationAxis, 0, 0, 1);
694  RotatePointAroundVector(v, rotationAxis, tangentVector, - start[0]);
695  VectorSet(rotationAxis, 0, 1, 0);
696  RotatePointAroundVector(tangentVector, rotationAxis, v, start[1] + 90.0f);
697 
698  /* calculate the orientation angle of the model around axis perpendicular to the screen */
699  float angle = todeg * atan(tangentVector[0] / tangentVector[1]);
700  if (tangentVector[1] > 0)
701  angle -= 90.0f;
702  else
703  angle += 90.0f;
704 
705  return angle;
706 }
707 
716 float GEO_AngleOfPath (const vec2_t start, const vec2_t end, vec3_t direction, vec3_t ortVector)
717 {
718  uiNode_t* node = geoscapeNode;
719  if (!node)
720  return 0.0f;
721 
722  const mapExtraData_t& data = UI_MAPEXTRADATA(node);
723  if (!data.flatgeoscape)
724  return GEO_AngleOfPath3D(start, end, direction, ortVector);
725  return GEO_AngleOfPath2D(start, end, direction, ortVector);
726 }
727 
734 static void GEO_ConvertObjectPositionToGeoscapePosition (bool flatgeoscape, float* vector, const vec2_t objectPos)
735 {
736  if (flatgeoscape)
737  Vector2Set(vector, objectPos[0], objectPos[1]);
738  else
739  VectorSet(vector, objectPos[0], -objectPos[1], 0);
740 }
741 
745 static void GEO_GetMissionAngle (bool flatgeoscape, float* vector, int id)
746 {
747  mission_t* mission = MIS_GetByIdx(id);
748  if (mission == nullptr)
749  return;
750  GEO_ConvertObjectPositionToGeoscapePosition(flatgeoscape, vector, mission->pos);
751  GEO_SelectMission(mission);
752 }
753 
757 static void GEO_GetUFOAngle (bool flatgeoscape, float* vector, int idx)
758 {
759  aircraft_t* ufo;
760 
761  /* Cycle through UFOs (only those visible on geoscape) */
762  ufo = nullptr;
763  while ((ufo = UFO_GetNextOnGeoscape(ufo)) != nullptr) {
764  if (ufo->idx != idx)
765  continue;
766  GEO_ConvertObjectPositionToGeoscapePosition(flatgeoscape, vector, ufo->pos);
767  GEO_SelectUFO(ufo);
768  return;
769  }
770 }
771 
772 
776 static void GEO_StartCenter (uiNode_t* node)
777 {
779  if (data.flatgeoscape) {
780  /* case 2D geoscape */
781  vec2_t diff;
782 
784  0.5f - data.smoothFinal2DGeoscapeCenter[1] / 180.0f);
785  if (data.smoothFinal2DGeoscapeCenter[1] < 0.5 / ZOOM_LIMIT)
786  data.smoothFinal2DGeoscapeCenter[1] = 0.5 / ZOOM_LIMIT;
787  if (data.smoothFinal2DGeoscapeCenter[1] > 1.0 - 0.5 / ZOOM_LIMIT)
788  data.smoothFinal2DGeoscapeCenter[1] = 1.0 - 0.5 / ZOOM_LIMIT;
789  diff[0] = data.smoothFinal2DGeoscapeCenter[0] - data.center[0];
790  diff[1] = data.smoothFinal2DGeoscapeCenter[1] - data.center[1];
791  data.smoothDeltaLength = sqrt(diff[0] * diff[0] + diff[1] * diff[1]);
792  } else {
793  /* case 3D geoscape */
794  vec3_t diff;
795 
797  VectorSubtract(data.smoothFinalGlobeAngle, data.angles, diff);
798  data.smoothDeltaLength = VectorLength(diff);
799  }
800 
802  data.smoothDeltaZoom = fabs(data.smoothFinalZoom - data.zoom);
803  data.smoothRotation = true;
804 }
805 
810 void GEO_CenterPosition (const vec2_t pos)
811 {
812  uiNode_t* node = geoscapeNode;
813  if (!node)
814  return;
816  const bool flatgeoscape = data.flatgeoscape;
817  float* vector;
818  if (flatgeoscape)
819  vector = data.smoothFinal2DGeoscapeCenter;
820  else
821  vector = data.smoothFinalGlobeAngle;
822 
823  GEO_ConvertObjectPositionToGeoscapePosition(flatgeoscape, vector, pos);
824  GEO_StartCenter(node);
825 }
826 
830 static void GEO_SelectObject_f (void)
831 {
832  uiNode_t* node = geoscapeNode;
833  if (!node)
834  return;
835 
836  if (cgi->Cmd_Argc() != 3) {
837  cgi->Com_Printf("Usage: %s <mission|ufo> <id>\n", cgi->Cmd_Argv(0));
838  return;
839  }
840 
841  const char* type = cgi->Cmd_Argv(1);
842  const int idx = atoi(cgi->Cmd_Argv(2));
844  const bool flatgeoscape = data.flatgeoscape;
845 
846  float* vector;
847  if (flatgeoscape)
848  vector = data.smoothFinal2DGeoscapeCenter;
849  else
850  vector = data.smoothFinalGlobeAngle;
851 
852  if (Q_streq(type, "mission"))
853  GEO_GetMissionAngle(flatgeoscape, vector, idx);
854  else if (Q_streq(type, "ufo"))
855  GEO_GetUFOAngle(flatgeoscape, vector, idx);
856  else {
857  cgi->Com_Printf("GEO_SelectObject_f: type %s unsupported.", type);
858  return;
859  }
860  GEO_StartCenter(node);
861 }
862 
867 static void GEO_GetGeoscapeAngle (vec2_t pos)
868 {
869  int counter = 0;
870  int maxEventIdx;
871  const int numMissions = CP_CountMissionOnGeoscape();
872  aircraft_t* ufo;
873  base_t* base;
874  int numBases = B_GetCount();
875 
876  /* If the value of maxEventIdx is too big or to low, restart from beginning */
877  maxEventIdx = numMissions + numBases + INS_GetCount() - 1;
878  base = nullptr;
879  while ((base = B_GetNext(base)) != nullptr) {
880  AIR_ForeachFromBase(aircraft, base) {
881  if (AIR_IsAircraftOnGeoscape(aircraft))
882  maxEventIdx++;
883  }
884  }
885  ufo = nullptr;
886  while ((ufo = UFO_GetNextOnGeoscape(ufo)) != nullptr)
887  maxEventIdx++;
888 
889  /* if there's nothing to center the view on, just go to 0,0 pos */
890  if (maxEventIdx < 0) {
891  Vector2Copy(vec2_origin, pos);
892  return;
893  }
894 
895  /* check centerOnEventIdx is within the bounds */
896  if (centerOnEventIdx < 0)
897  centerOnEventIdx = maxEventIdx;
898  if (centerOnEventIdx > maxEventIdx)
899  centerOnEventIdx = 0;
900 
901  /* Cycle through missions */
902  if (centerOnEventIdx < numMissions) {
903  MIS_Foreach(mission) {
904  if (!mission->onGeoscape)
905  continue;
906  if (counter == centerOnEventIdx) {
907  Vector2Copy(mission->pos, pos);
908  GEO_SelectMission(mission);
909  return;
910  }
911  counter++;
912  }
913  }
914  counter = numMissions;
915 
916  /* Cycle through bases */
917  if (centerOnEventIdx < numBases + counter) {
918  base = nullptr;
919  while ((base = B_GetNext(base)) != nullptr) {
920  if (counter == centerOnEventIdx) {
921  Vector2Copy(base->pos, pos);
922  return;
923  }
924  counter++;
925  }
926  }
927  counter += numBases;
928 
929  /* Cycle through installations */
930  if (centerOnEventIdx < INS_GetCount() + counter) {
931  INS_Foreach(inst) {
932  if (counter == centerOnEventIdx) {
933  Vector2Copy(inst->pos, pos);
934  return;
935  }
936  counter++;
937  }
938  }
939  counter += INS_GetCount();
940 
941  /* Cycle through aircraft (only those present on geoscape) */
942  AIR_Foreach(aircraft) {
943  if (AIR_IsAircraftOnGeoscape(aircraft)) {
944  if (centerOnEventIdx == counter) {
945  Vector2Copy(aircraft->pos, pos);
946  GEO_SelectAircraft(aircraft);
947  return;
948  }
949  counter++;
950  }
951  }
952 
953  /* Cycle through UFOs (only those visible on geoscape) */
954  ufo = nullptr;
955  while ((ufo = UFO_GetNextOnGeoscape(ufo)) != nullptr) {
956  if (centerOnEventIdx == counter) {
957  Vector2Copy(ufo->pos, pos);
958  GEO_SelectUFO(ufo);
959  return;
960  }
961  counter++;
962  }
963 }
964 
972 {
973  if (!Q_streq(cgi->UI_GetActiveWindowName(), "geoscape"))
974  return;
975 
977 
978  uiNode_t* node = geoscapeNode;
979  if (!node)
980  return;
981 
982  vec2_t pos;
984  GEO_CenterPosition(pos);
985 }
986 
987 #define BULLET_SIZE 1
988 
994 static void GEO_DrawBullets (const uiNode_t* node, const vec3_t pos)
995 {
996  int x, y;
997 
998  if (GEO_AllMapToScreen(node, pos, &x, &y, nullptr))
999  cgi->R_DrawFill(x, y, BULLET_SIZE, BULLET_SIZE, yellow);
1000 }
1001 
1010 static void GEO_DrawBeam (const uiNode_t* node, const vec3_t start, const vec3_t end, const vec4_t color)
1011 {
1012  int points[4];
1013 
1014  if (!GEO_AllMapToScreen(node, start, &(points[0]), &(points[1]), nullptr))
1015  return;
1016  if (!GEO_AllMapToScreen(node, end, &(points[2]), &(points[3]), nullptr))
1017  return;
1018 
1019  cgi->R_Color(color);
1020  cgi->R_DrawLine(points, 2.0);
1021  cgi->R_Color(nullptr);
1022 }
1023 
1024 static inline void GEO_RenderImage (int x, int y, const char* image)
1025 {
1026  cgi->R_DrawImageCentered(x, y, image);
1027 }
1028 
1029 #define SELECT_CIRCLE_RADIUS 1.5f + 3.0f / UI_MAPEXTRADATACONST(node).zoom
1030 
1036 static void GEO_DrawMapOneMission (const uiNode_t* node, const mission_t* mission)
1037 {
1038  int x, y;
1039  const bool isCurrentSelectedMission = GEO_IsMissionSelected(mission);
1040 
1041  if (isCurrentSelectedMission)
1042  cgi->Cvar_Set("mn_mapdaytime", GEO_IsNight(mission->pos) ? _("Night") : _("Day"));
1043 
1044  if (!GEO_AllMapToScreen(node, mission->pos, &x, &y, nullptr))
1045  return;
1046 
1047  const mapExtraData_t& data = UI_MAPEXTRADATACONST(node);
1048  if (isCurrentSelectedMission) {
1049  /* Draw circle around the mission */
1050  if (data.flatgeoscape) {
1051  if (mission->active) {
1052  GEO_RenderImage(x, y, "pics/geoscape/circleactive");
1053  } else {
1054  GEO_RenderImage(x, y, "pics/geoscape/circle");
1055  }
1056  } else {
1057  if (!mission->active)
1059  }
1060  }
1061 
1062  /* Draw mission model (this must be called after drawing the selection circle so that the model is rendered on top of it)*/
1063  if (data.flatgeoscape) {
1064  GEO_RenderImage(x, y, "pics/geoscape/mission");
1065  } else {
1066  GEO_Draw3DMarkerIfVisible(node, mission->pos, defaultBaseAngle, MIS_GetModel(mission), 0);
1067  }
1068 
1069  cgi->UI_DrawString("f_verysmall", ALIGN_UL, x + 10, y, MIS_GetName(mission));
1070 }
1071 
1078 static void GEO_DrawRadarLineCoverage (const uiNode_t* node, const radar_t* radar, const vec2_t pos)
1079 {
1080  const vec4_t color = {1., 1., 1., .4};
1081  GEO_MapDrawEquidistantPoints(node, pos, radar->range, color);
1082  GEO_MapDrawEquidistantPoints(node, pos, radar->trackingRange, color);
1083 }
1084 
1091 static void GEO_DrawRadarInMap (const uiNode_t* node, const radar_t* radar, const vec2_t pos)
1092 {
1093  /* Show radar range zones */
1094  GEO_DrawRadarLineCoverage(node, radar, pos);
1095 
1096  /* everything below is drawn only if there is at least one detected UFO */
1097  if (!radar->numUFOs)
1098  return;
1099 
1100  /* Draw lines from radar to ufos sensored */
1101  int x, y;
1102  const bool display = GEO_AllMapToScreen(node, pos, &x, &y, nullptr);
1103  if (!display)
1104  return;
1105 
1106  screenPoint_t pts[2];
1107  pts[0].x = x;
1108  pts[0].y = y;
1109 
1110  /* Set color */
1111  const vec4_t color = {1., 1., 1., .3};
1112  cgi->R_Color(color);
1113  for (int i = 0; i < radar->numUFOs; i++) {
1114  const aircraft_t* ufo = radar->ufos[i];
1115  if (UFO_IsUFOSeenOnGeoscape(ufo) && GEO_AllMapToScreen(node, ufo->pos, &x, &y, nullptr)) {
1116  pts[1].x = x;
1117  pts[1].y = y;
1118  cgi->R_DrawLineStrip(2, (int*)pts);
1119  }
1120  }
1121  cgi->R_Color(nullptr);
1122 }
1123 
1132 static void GEO_DrawMapOneInstallation (const uiNode_t* node, const installation_t* installation,
1133  bool oneUFOVisible, const char* font)
1134 {
1135  const installationTemplate_t* tpl = installation->installationTemplate;
1136 
1137  /* Draw weapon range if at least one UFO is visible */
1138  if (oneUFOVisible && AII_InstallationCanShoot(installation)) {
1139  for (int i = 0; i < tpl->maxBatteries; i++) {
1140  const aircraftSlot_t* slot = &installation->batteries[i].slot;
1141  if (slot->item && slot->ammoLeft != 0 && slot->installationTime == 0) {
1142  GEO_MapDrawEquidistantPoints(node, installation->pos,
1144  }
1145  }
1146  }
1147 
1148  /* Draw installation radar (only the "wire" style part) */
1150  GEO_DrawRadarInMap(node, &installation->radar, installation->pos);
1151 
1152  int x, y;
1153  /* Draw installation */
1154  if (!UI_MAPEXTRADATACONST(node).flatgeoscape) {
1155  GEO_Draw3DMarkerIfVisible(node, installation->pos, defaultBaseAngle, tpl->model, 0);
1156  } else if (GEO_MapToScreen(node, installation->pos, &x, &y)) {
1157  GEO_RenderImage(x, y, tpl->image);
1158  }
1159 
1160  /* Draw installation names */
1161  if (GEO_AllMapToScreen(node, installation->pos, &x, &y, nullptr))
1162  cgi->UI_DrawString(font, ALIGN_UL, x, y + 10, installation->name);
1163 }
1164 
1172 static void GEO_DrawMapOneBase (const uiNode_t* node, const base_t* base,
1173  bool oneUFOVisible, const char* font)
1174 {
1175  /* Draw weapon range if at least one UFO is visible */
1176  if (oneUFOVisible && AII_BaseCanShoot(base)) {
1177  int i;
1178  for (i = 0; i < base->numBatteries; i++) {
1179  const aircraftSlot_t* slot = &base->batteries[i].slot;
1180  if (slot->item && slot->ammoLeft != 0 && slot->installationTime == 0) {
1181  GEO_MapDrawEquidistantPoints(node, base->pos,
1183  }
1184  }
1185  for (i = 0; i < base->numLasers; i++) {
1186  const aircraftSlot_t* slot = &base->lasers[i].slot;
1187  if (slot->item && slot->ammoLeft != 0 && slot->installationTime == 0) {
1188  GEO_MapDrawEquidistantPoints(node, base->pos,
1190  }
1191  }
1192  }
1193 
1194  /* Draw base radar (only the "wire" style part) */
1196  GEO_DrawRadarInMap(node, &base->radar, base->pos);
1197 
1198  int x, y;
1199  /* Draw base */
1200  if (!UI_MAPEXTRADATACONST(node).flatgeoscape) {
1201  if (B_IsUnderAttack(base))
1202  /* two skins - second skin is for baseattack */
1203  GEO_Draw3DMarkerIfVisible(node, base->pos, defaultBaseAngle, "geoscape/base", 1);
1204  else
1205  GEO_Draw3DMarkerIfVisible(node, base->pos, defaultBaseAngle, "geoscape/base", 0);
1206  } else if (GEO_MapToScreen(node, base->pos, &x, &y)) {
1207  if (B_IsUnderAttack(base))
1208  GEO_RenderImage(x, y, "pics/geoscape/baseattack");
1209  else
1210  GEO_RenderImage(x, y, "pics/geoscape/base");
1211  }
1212 
1213  /* Draw base names */
1214  if (GEO_AllMapToScreen(node, base->pos, &x, &y, nullptr))
1215  cgi->UI_DrawString(font, ALIGN_UL, x, y + 10, base->name);
1216 }
1217 
1224 static void GEO_DrawAircraftHealthBar (const uiNode_t* node, const aircraft_t* aircraft)
1225 {
1226  const mapExtraData_t& data = UI_MAPEXTRADATACONST(node);
1227  const int width = 8 * data.zoom;
1228  const int height = 1 * data.zoom * 0.9f;
1229  vec4_t color;
1230  int centerX;
1231  int centerY;
1232  bool visible;
1233 
1234  if (!aircraft)
1235  return;
1236  if (aircraft->stats[AIR_STATS_DAMAGE] <= 0)
1237  return;
1238 
1239  if (((float)aircraft->damage / aircraft->stats[AIR_STATS_DAMAGE]) <= .33f) {
1240  Vector4Copy(red, color);
1241  } else if (((float)aircraft->damage / aircraft->stats[AIR_STATS_DAMAGE]) <= .75f) {
1242  Vector4Copy(yellow, color);
1243  } else {
1244  Vector4Copy(green, color);
1245  }
1246 
1247  if (!data.flatgeoscape)
1248  visible = GEO_3DMapToScreen(node, aircraft->pos, &centerX, &centerY, nullptr);
1249  else
1250  visible = GEO_AllMapToScreen(node, aircraft->pos, &centerX, &centerY, nullptr);
1251 
1252  if (visible) {
1253  const vec4_t bordercolor = {1, 1, 1, 1};
1254  cgi->R_DrawFill(centerX - width / 2 , centerY - 5 * data.zoom, round(width * ((float)aircraft->damage / aircraft->stats[AIR_STATS_DAMAGE])), height, color);
1255  cgi->R_DrawRect(centerX - width / 2, centerY - 5 * data.zoom, width, height, bordercolor, 1.0, 1);
1256  }
1257 }
1258 
1265 static void GEO_DrawMapOnePhalanxAircraft (const uiNode_t* node, aircraft_t* aircraft, bool oneUFOVisible)
1266 {
1267  float angle;
1268 
1269  /* Draw aircraft radar (only the "wire" style part) */
1271  GEO_DrawRadarInMap(node, &aircraft->radar, aircraft->pos);
1272 
1273  /* Draw only the bigger weapon range on geoscape: more detail will be given on airfight map */
1274  if (oneUFOVisible)
1275  GEO_MapDrawEquidistantPoints(node, aircraft->pos, aircraft->stats[AIR_STATS_WRANGE] / 1000.0f, red);
1276 
1277  const mapExtraData_t& data = UI_MAPEXTRADATACONST(node);
1278  /* Draw aircraft route */
1279  if (aircraft->status >= AIR_TRANSIT) {
1280  /* aircraft is moving */
1281  mapline_t path;
1282 
1283  path.numPoints = aircraft->route.numPoints - aircraft->point;
1285  if (path.numPoints > 1) {
1286  memcpy(path.point, aircraft->pos, sizeof(vec2_t));
1287  memcpy(path.point + 1, aircraft->route.point + aircraft->point + 1, (path.numPoints - 1) * sizeof(vec2_t));
1288  if (!data.flatgeoscape)
1289  GEO_3DMapDrawLine(node, &path);
1290  else
1291  GEO_MapDrawLine(node, &path);
1292  }
1293  angle = GEO_AngleOfPath(aircraft->pos, aircraft->route.point[aircraft->route.numPoints - 1], aircraft->direction, nullptr);
1294  } else {
1295  /* aircraft is idle */
1296  angle = 0.0f;
1297  }
1298 
1299  /* Draw a circle around selected aircraft */
1300  if (GEO_IsAircraftSelected(aircraft)) {
1301  int x;
1302  int y;
1303 
1304  if (!data.flatgeoscape)
1306  else {
1307  GEO_AllMapToScreen(node, aircraft->pos, &x, &y, nullptr);
1308  GEO_RenderImage(x, y, "pics/geoscape/circleactive");
1309  }
1310 
1311  /* Draw a circle around the ufo pursued by selected aircraft */
1312  if (aircraft->status == AIR_UFO && GEO_AllMapToScreen(node, aircraft->aircraftTarget->pos, &x, &y, nullptr)) {
1313  if (!data.flatgeoscape)
1315  else
1316  GEO_RenderImage(x, y, "pics/geoscape/circleactive");
1317  }
1318  }
1319 
1320  /* Draw aircraft (this must be called after drawing the selection circle so that the aircraft is drawn on top of it)*/
1321  GEO_Draw3DMarkerIfVisible(node, aircraft->pos, angle, aircraft->model, 0);
1322 
1324  if (oneUFOVisible || cgi->Cvar_GetInteger("debug_showcrafthealth") >= 1)
1325  GEO_DrawAircraftHealthBar(node, aircraft);
1326 }
1327 
1335 static const char* GEO_GetMissionText (char* buffer, size_t size, const mission_t* mission)
1336 {
1337  assert(mission);
1338  Com_sprintf(buffer, size, _("Name: %s\nObjective: %s"),
1339  MIS_GetName(mission), (mission->mapDef) ? _(mission->mapDef->description) : _("Unknown"));
1340  return buffer;
1341 }
1342 
1350 static const char* GEO_GetAircraftText (char* buffer, size_t size, const aircraft_t* aircraft)
1351 {
1352  if (aircraft->status == AIR_UFO) {
1353  const float distance = GetDistanceOnGlobe(aircraft->pos, aircraft->aircraftTarget->pos);
1354  Com_sprintf(buffer, size, _("Name:\t%s (%i/%i)\n"), aircraft->name, AIR_GetTeamSize(aircraft), aircraft->maxTeamSize);
1355  Q_strcat(buffer, size, _("Status:\t%s\n"), AIR_AircraftStatusToName(aircraft));
1356  if (aircraft->stats[AIR_STATS_DAMAGE] > 0)
1357  Q_strcat(buffer, size, _("Health:\t%3.0f%%\n"), (double)aircraft->damage * 100 / aircraft->stats[AIR_STATS_DAMAGE]);
1358  Q_strcat(buffer, size, _("Distance to target:\t\t%.0f\n"), distance);
1359  Q_strcat(buffer, size, _("Speed:\t%i km/h\n"), AIR_AircraftMenuStatsValues(aircraft->stats[AIR_STATS_SPEED], AIR_STATS_SPEED));
1360  Q_strcat(buffer, size, _("Fuel:\t%i/%i\n"), AIR_AircraftMenuStatsValues(aircraft->fuel, AIR_STATS_FUELSIZE),
1362  Q_strcat(buffer, size, _("ETA:\t%sh\n"), CP_SecondConvert((float)SECONDS_PER_HOUR * distance / aircraft->stats[AIR_STATS_SPEED]));
1363  } else {
1364  Com_sprintf(buffer, size, _("Name:\t%s (%i/%i)\n"), aircraft->name, AIR_GetTeamSize(aircraft), aircraft->maxTeamSize);
1365  Q_strcat(buffer, size, _("Status:\t%s\n"), AIR_AircraftStatusToName(aircraft));
1366  if (aircraft->stats[AIR_STATS_DAMAGE] > 0)
1367  Q_strcat(buffer, size, _("Health:\t%3.0f%%\n"), (double)aircraft->damage * 100 / aircraft->stats[AIR_STATS_DAMAGE]);
1368  Q_strcat(buffer, size, _("Speed:\t%i km/h\n"), AIR_AircraftMenuStatsValues(aircraft->stats[AIR_STATS_SPEED], AIR_STATS_SPEED));
1369  Q_strcat(buffer, size, _("Fuel:\t%i/%i\n"), AIR_AircraftMenuStatsValues(aircraft->fuel, AIR_STATS_FUELSIZE),
1371  if (aircraft->status != AIR_IDLE) {
1372  const float distance = GetDistanceOnGlobe(aircraft->pos,
1373  aircraft->route.point[aircraft->route.numPoints - 1]);
1374  Q_strcat(buffer, size, _("ETA:\t%sh\n"), CP_SecondConvert((float)SECONDS_PER_HOUR * distance / aircraft->stats[AIR_STATS_SPEED]));
1375  }
1376  }
1377  return buffer;
1378 }
1379 
1387 static const char* GEO_GetUFOText (char* buffer, size_t size, const aircraft_t* ufo)
1388 {
1389  Com_sprintf(buffer, size, "%s\n", UFO_GetName(ufo));
1390  Q_strcat(buffer, size, _("Speed: %i km/h\n"), AIR_AircraftMenuStatsValues(ufo->stats[AIR_STATS_SPEED], AIR_STATS_SPEED));
1391  return buffer;
1392 }
1393 
1398 {
1399  char buf[512];
1400  aircraft_t* ufo;
1401 
1402  cgi->UI_ExecuteConfunc("clean_geoscape_object");
1403 
1404  /* draw mission pics */
1405  MIS_Foreach(mission) {
1406  if (!mission->onGeoscape)
1407  continue;
1408  cgi->UI_ExecuteConfunc("add_geoscape_object mission %i \"%s\" \"%s\n%s\"",
1409  mission->idx, MIS_GetModel(mission), MIS_GetName(mission),
1410  (mission->mapDef) ? _(mission->mapDef->description) : "");
1411  }
1412 
1413  /* draws ufos */
1414  ufo = nullptr;
1415  while ((ufo = UFO_GetNextOnGeoscape(ufo)) != nullptr) {
1416  const unsigned int ufoIDX = UFO_GetGeoscapeIDX(ufo);
1417  cgi->UI_ExecuteConfunc("add_geoscape_object ufo %i %s \"%s\"",
1418  ufoIDX, ufo->model, GEO_GetUFOText(buf, sizeof(buf), ufo));
1419  }
1420 }
1421 
1431 void GEO_DrawMarkers (const uiNode_t* node)
1432 {
1433  const char* font;
1434  aircraft_t* ufo;
1435  base_t* base;
1436 
1437  const vec4_t white = {1.f, 1.f, 1.f, 0.7f};
1438  int maxInterpolationPoints;
1439 
1440  assert(node);
1441 
1442  /* font color on geoscape */
1443  cgi->R_Color(node->color);
1444  /* default font */
1445  font = cgi->UI_GetFontFromNode(node);
1446 
1447  /* check if at least 1 UFO is visible */
1448  const bool oneUFOVisible = UFO_GetNextOnGeoscape(nullptr) != nullptr;
1449 
1450  /* draw mission pics */
1451  MIS_Foreach(mission) {
1452  if (!mission->onGeoscape)
1453  continue;
1454  GEO_DrawMapOneMission(node, mission);
1455  }
1456 
1457  /* draw installations */
1458  INS_Foreach(installation) {
1459  GEO_DrawMapOneInstallation(node, installation, oneUFOVisible, font);
1460  }
1461 
1462  /* draw bases */
1463  base = nullptr;
1464  while ((base = B_GetNext(base)) != nullptr)
1465  GEO_DrawMapOneBase(node, base, oneUFOVisible, font);
1466 
1467  /* draw all aircraft */
1468  AIR_Foreach(aircraft) {
1469  if (AIR_IsAircraftOnGeoscape(aircraft))
1470  GEO_DrawMapOnePhalanxAircraft(node, aircraft, oneUFOVisible);
1471  }
1472 
1473  /* draws ufos */
1474  ufo = nullptr;
1475  while ((ufo = UFO_GetNextOnGeoscape(ufo)) != nullptr) {
1476 #ifdef DEBUG
1477  /* in debug mode you execute set showufos 1 to see the ufos on geoscape */
1478  if (cgi->Cvar_GetInteger("debug_showufos")) {
1479  /* Draw ufo route */
1480  if (!UI_MAPEXTRADATACONST(node).flatgeoscape)
1481  GEO_3DMapDrawLine(node, &ufo->route);
1482  else
1483  GEO_MapDrawLine(node, &ufo->route);
1484  } else
1485 #endif
1486  {
1487  const float angle = GEO_AngleOfPath(ufo->pos, ufo->route.point[ufo->route.numPoints - 1], ufo->direction, nullptr);
1488  const mapExtraData_t& data = UI_MAPEXTRADATACONST(node);
1489 
1490  if (!data.flatgeoscape)
1492 
1493  if (GEO_IsUFOSelected(ufo)) {
1494  if (!data.flatgeoscape) {
1496  } else {
1497  int x, y;
1498  GEO_AllMapToScreen(node, ufo->pos, &x, &y, nullptr);
1499  GEO_RenderImage(x, y, "pics/geoscape/circleactive");
1500  }
1501  }
1502  GEO_Draw3DMarkerIfVisible(node, ufo->pos, angle, ufo->model, 0);
1503 
1505  if (RS_IsResearched_ptr(ufo->tech)
1506  || cgi->Cvar_GetInteger("debug_showcrafthealth") >= 1)
1507  GEO_DrawAircraftHealthBar(node, ufo);
1508  }
1509  }
1510 
1511  if (ccs.gameTimeScale > 0)
1512  maxInterpolationPoints = floor(1.0f / (ccs.frametime * (float)ccs.gameTimeScale));
1513  else
1514  maxInterpolationPoints = 0;
1515 
1516  /* draws projectiles */
1517  for (int i = 0; i < ccs.numProjectiles; i++) {
1518  aircraftProjectile_t* projectile = &ccs.projectiles[i];
1519  vec3_t drawPos = {0, 0, 0};
1520 
1521  if (projectile->hasMoved) {
1522  projectile->hasMoved = false;
1523  VectorCopy(projectile->pos[0], drawPos);
1524  } else {
1525  if (maxInterpolationPoints > 2 && projectile->numInterpolationPoints < maxInterpolationPoints) {
1526  /* If a new point hasn't been given and there is at least 3 points need to be filled in then
1527  * use linear interpolation to draw the points until a new projectile point is provided.
1528  * The reason you need at least 3 points is that acceptable results can be achieved with 2 or less
1529  * gaps in points so don't add the overhead of interpolation. */
1530  const float xInterpolStep = (projectile->projectedPos[0][0] - projectile->pos[0][0]) / (float)maxInterpolationPoints;
1531  projectile->numInterpolationPoints += 1;
1532  drawPos[0] = projectile->pos[0][0] + (xInterpolStep * projectile->numInterpolationPoints);
1533  LinearInterpolation(projectile->pos[0], projectile->projectedPos[0], drawPos[0], drawPos[1]);
1534  } else {
1535  VectorCopy(projectile->pos[0], drawPos);
1536  }
1537  }
1538 
1539  if (projectile->bullets) {
1540  GEO_DrawBullets(node, drawPos);
1541  } else if (projectile->beam) {
1542  vec3_t start;
1543  vec3_t end;
1544 
1545  if (projectile->attackingAircraft)
1546  VectorCopy(projectile->attackingAircraft->pos, start);
1547  else
1548  VectorCopy(projectile->attackerPos, start);
1549 
1550  if (projectile->aimedAircraft)
1551  VectorCopy(projectile->aimedAircraft->pos, end);
1552  else
1553  VectorCopy(projectile->idleTarget, end);
1554 
1555  GEO_DrawBeam(node, start, end, projectile->aircraftItem->craftitem.beamColor);
1556  } else {
1557  GEO_Draw3DMarkerIfVisible(node, drawPos, projectile->angle, projectile->aircraftItem->model, 0);
1558  }
1559  }
1560 
1561  const bool showXVI = CP_IsXVIVisible();
1562  static char buffer[512];
1563 
1564  /* Draw nation names */
1565  buffer[0] = 0;
1566  NAT_Foreach(nation) {
1567  int x, y;
1568  if (GEO_AllMapToScreen(node, nation->pos, &x, &y, nullptr))
1569  cgi->UI_DrawString("f_verysmall", ALIGN_UC, x , y, _(nation->name));
1570  if (showXVI) {
1571  const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation);
1572  Q_strcat(buffer, sizeof(buffer), _("%s\t%i%%\n"), _(nation->name), stats->xviInfection);
1573  }
1574  }
1575 
1576  if (showXVI)
1577  cgi->UI_RegisterText(TEXT_XVI, buffer);
1578  else
1579  cgi->UI_ResetData(TEXT_XVI);
1580 
1581  cgi->R_Color(nullptr);
1582 }
1583 
1589 {
1590  if (!CP_IsRunning()) {
1591  data->active = false;
1592  return;
1593  }
1594 
1595  data->active = true;
1596  data->map = ccs.curCampaign->map;
1600  data->date = ccs.date;
1601 
1602  geoscapeNode = static_cast<uiNode_t* >(data->geoscapeNode);
1603 
1604  mission_t* mission = GEO_GetSelectedMission();
1605  /* display text */
1606  cgi->UI_ResetData(TEXT_STANDARD);
1607  switch (ccs.mapAction) {
1608  case MA_NEWBASE:
1609  cgi->UI_RegisterText(TEXT_STANDARD, _("Select the desired location of the new base on the map.\n"));
1610  return;
1611  case MA_NEWINSTALLATION:
1612  cgi->UI_RegisterText(TEXT_STANDARD, _("Select the desired location of the new installation on the map.\n"));
1613  return;
1614  case MA_NONE:
1615  break;
1616  }
1617 
1618  /* Nothing is displayed yet */
1619  if (mission) {
1620  cgi->UI_RegisterText(TEXT_STANDARD, GEO_GetMissionText(textStandard, sizeof(textStandard), mission));
1621  } else if (GEO_GetSelectedAircraft() != nullptr) {
1622  const aircraft_t* aircraft = GEO_GetSelectedAircraft();
1623  if (AIR_IsAircraftInBase(aircraft)) {
1624  cgi->UI_RegisterText(TEXT_STANDARD, nullptr);
1625  GEO_ResetAction();
1626  return;
1627  }
1628  cgi->UI_RegisterText(TEXT_STANDARD, GEO_GetAircraftText(textStandard, sizeof(textStandard), aircraft));
1629  } else if (GEO_GetSelectedUFO() != nullptr) {
1631  } else {
1632 #ifdef DEBUG
1633  if (debug_showInterest->integer) {
1634  static char t[64];
1635  Com_sprintf(t, lengthof(t), "Interest level: %i\n", ccs.overallInterest);
1636  cgi->UI_RegisterText(TEXT_STANDARD, t);
1637  } else
1638 #endif
1639  cgi->UI_RegisterText(TEXT_STANDARD, "");
1640  }
1641 }
1642 
1646 void GEO_ResetAction (void)
1647 {
1648  /* don't allow a reset when no base is set up */
1649  if (B_AtLeastOneExists())
1650  ccs.mapAction = MA_NONE;
1651 
1652  GEO_SetInterceptorAircraft(nullptr);
1653  GEO_SetSelectedMission(nullptr);
1654  GEO_SetSelectedAircraft(nullptr);
1655  GEO_SetSelectedUFO(nullptr);
1656 
1657  if (!radarOverlayWasSet)
1658  GEO_SetOverlay("radar", 0);
1659 }
1660 
1665 {
1666  GEO_ResetAction();
1667  GEO_SetSelectedUFO(ufo);
1668 }
1669 
1674 {
1675  GEO_ResetAction();
1676  GEO_SetSelectedAircraft(aircraft);
1677 }
1678 
1685 {
1686  if (!mission || GEO_IsMissionSelected(mission))
1687  return GEO_GetSelectedMission();
1688  GEO_ResetAction();
1689  GEO_SetSelectedMission(mission);
1690  return GEO_GetSelectedMission();
1691 }
1692 
1697 {
1698  /* Unselect the current selected mission if it's the same */
1699  if (GEO_IsMissionSelected(mission))
1700  GEO_ResetAction();
1701 
1703 }
1704 
1710 void GEO_NotifyUFORemoved (const aircraft_t* ufo, bool destroyed)
1711 {
1713 
1714  if (GEO_GetSelectedUFO() == nullptr)
1715  return;
1716 
1717  /* Unselect the current selected ufo if it's the same */
1718  if (GEO_IsUFOSelected(ufo))
1719  GEO_ResetAction();
1720  else if (destroyed && ccs.geoscape.selectedUFO > ufo)
1723 }
1724 
1730 {
1731  /* Unselect the current selected ufo if its the same */
1732  if (GEO_IsAircraftSelected(aircraft) || GEO_IsInterceptorSelected(aircraft))
1733  GEO_ResetAction();
1734 }
1735 
1745 {
1746  const byte* color = GEO_GetColor(pos, MAPTYPE_NATIONS, nullptr);
1747  const vec3_t fcolor = {color[0] / 255.0f, color[1] / 255.0f, color[2] / 255.0f};
1748 #ifdef PARANOID
1749  cgi->Com_DPrintf(DEBUG_CLIENT, "GEO_GetNation: color value for %.0f:%.0f is r:%i, g:%i, b: %i\n", pos[0], pos[1], color[0], color[1], color[2]);
1750 #endif
1751  NAT_Foreach(nation) {
1752  /* compare the first three color values with color value at pos */
1753  /* 0.02 x 255 = 5.1, which allow a variation of +-5 for each color components */
1754  if (VectorEqualEpsilon(nation->color, fcolor, 0.02))
1755  return nation;
1756  }
1757  cgi->Com_DPrintf(DEBUG_CLIENT, "GEO_GetNation: No nation found at %.0f:%.0f - color: %i:%i:%i\n", pos[0], pos[1], color[0], color[1], color[2]);
1758  return nullptr;
1759 }
1760 
1768 static const char* GEO_GetCultureType (const byte* color)
1769 {
1770  if (MapIsWater(color))
1771  return "water";
1772  else if (MapIsEastern(color))
1773  return "eastern";
1774  else if (MapIsWestern(color))
1775  return "western";
1776  else if (MapIsOriental(color))
1777  return "oriental";
1778  else if (MapIsAfrican(color))
1779  return "african";
1780  return "western";
1781 }
1782 
1790 static const char* GEO_GetPopulationType (const byte* color)
1791 {
1792  if (MapIsWater(color))
1793  return "water";
1794  else if (MapIsUrban(color))
1795  return "urban";
1796  else if (MapIsSuburban(color))
1797  return "suburban";
1798  else if (MapIsVillage(color))
1799  return "village";
1800  else if (MapIsRural(color))
1801  return "rural";
1802  return "nopopulation";
1803 }
1804 
1812 static inline const char* GEO_GetTerrainTypeByPos (const vec2_t pos, bool* coast)
1813 {
1814  const byte* color = GEO_GetColor(pos, MAPTYPE_TERRAIN, coast);
1815  return cgi->csi->terrainDefs.getTerrainName(color);
1816 }
1817 
1824 static inline const char* GEO_GetCultureTypeByPos (const vec2_t pos)
1825 {
1826  const byte* color = GEO_GetColor(pos, MAPTYPE_CULTURE, nullptr);
1827  return GEO_GetCultureType(color);
1828 }
1829 
1836 static inline const char* GEO_GetPopulationTypeByPos (const vec2_t pos)
1837 {
1838  const byte* color = GEO_GetColor(pos, MAPTYPE_POPULATION, nullptr);
1839  return GEO_GetPopulationType(color);
1840 }
1841 
1849 {
1850  const byte* color = GEO_GetColor(pos, MAPTYPE_POPULATION, nullptr);
1851 
1852  if (MapIsWater(color))
1853  cgi->Com_Error(ERR_DROP, "GEO_GetPopulationType: Trying to get number of civilian in a position on water");
1854 
1855  if (MapIsUrban(color))
1856  return 10;
1857  else if (MapIsSuburban(color))
1858  return 8;
1859  else if (MapIsVillage(color))
1860  return 6;
1861  else if (MapIsRural(color))
1862  return 4;
1863  else if (MapIsNopopulation(color))
1864  return 2;
1865 
1866  return 0;
1867 }
1868 
1876 {
1877  bool coast = false;
1878  const char* terrainType = GEO_GetTerrainTypeByPos(pos, &coast);
1879  const char* cultureType = GEO_GetCultureTypeByPos(pos);
1880  const char* populationType = GEO_GetPopulationTypeByPos(pos);
1881 
1882  cgi->Com_Printf(" (Terrain: %s, Culture: %s, Population: %s, Coast: %s)\n",
1883  terrainType, cultureType, populationType, coast ? "true" : "false");
1884 }
1885 
1891 {
1892  while (pos[0] > 180.0)
1893  pos[0] -= 360.0;
1894  while (pos[0] < -180.0)
1895  pos[0] += 360.0;
1896  while (pos[1] > 90.0)
1897  pos[1] -= 180.0;
1898  while (pos[1] < -90.0)
1899  pos[1] += 180.0;
1900 }
1901 
1907 bool GEO_IsNight (const vec2_t pos)
1908 {
1909  float p, q, a, root, x;
1910 
1911  /* set p to hours (we don't use ccs.day here because we need a float value) */
1912  p = (float) ccs.date.sec / SECONDS_PER_DAY;
1913  /* convert current day to angle (-pi on 1st january, pi on 31 december) */
1914  q = (ccs.date.day + p) * (2 * M_PI / DAYS_PER_YEAR_AVG) - M_PI;
1915  p = (0.5 + pos[0] / 360 - p) * (2 * M_PI) - q;
1916  a = -sin(pos[1] * torad);
1917  root = sqrt(1.0 - a * a);
1918  x = sin(p) * root * sin(q) - (a * SIN_ALPHA + cos(p) * root * COS_ALPHA) * cos(q);
1919  return (x > 0);
1920 }
1921 
1934 const byte* GEO_GetColor (const vec2_t pos, mapType_t type, bool* coast)
1935 {
1936  int x, y;
1937  int width, height;
1938  const byte* mask;
1939  const byte* color;
1940 
1941  switch (type) {
1942  case MAPTYPE_TERRAIN:
1943  mask = terrainPic;
1944  width = terrainWidth;
1945  height = terrainHeight;
1946  break;
1947  case MAPTYPE_CULTURE:
1948  mask = culturePic;
1949  width = cultureWidth;
1950  height = cultureHeight;
1951  break;
1952  case MAPTYPE_POPULATION:
1953  mask = populationPic;
1954  width = populationWidth;
1955  height = populationHeight;
1956  break;
1957  case MAPTYPE_NATIONS:
1958  mask = nationsPic;
1959  width = nationsWidth;
1960  height = nationsHeight;
1961  break;
1962  default:
1963  cgi->Com_Error(ERR_DROP, "Unknown maptype %i\n", type);
1964  }
1965 
1966  assert(mask);
1967 
1969  assert(pos[0] >= -180);
1970  assert(pos[0] <= 180);
1971  assert(pos[1] >= -90);
1972  assert(pos[1] <= 90);
1973 
1974  /* get coordinates */
1975  x = (180 - pos[0]) / 360 * width;
1976  x--; /* we start from 0 */
1977  y = (90 - pos[1]) / 180 * height;
1978  y--; /* we start from 0 */
1979  if (x < 0)
1980  x = 0;
1981  if (y < 0)
1982  y = 0;
1983 
1984  /* 4 => RGBA */
1985  /* terrainWidth is the width of the image */
1986  /* this calculation returns the pixel in col x and in row y */
1987  assert(4 * (x + y * width) < width * height * 4);
1988  color = mask + 4 * (x + y * width);
1989  if (coast != nullptr) {
1990  if (MapIsWater(color)) {
1991  *coast = false;
1992  } else {
1993  /* only check four directions */
1994  const int gap = 4;
1995  if (x > gap) {
1996  const byte* coastCheck = mask + 4 * ((x - gap) + y * width);
1997  *coast = MapIsWater(coastCheck);
1998  }
1999  if (!*coast && x < width - 1 - gap) {
2000  const byte* coastCheck = mask + 4 * ((x + gap) + y * width);
2001  *coast = MapIsWater(coastCheck);
2002  }
2003 
2004  if (!*coast) {
2005  if (y > gap) {
2006  const byte* coastCheck = mask + 4 * (x + (y - gap) * width);
2007  *coast = MapIsWater(coastCheck);
2008  }
2009  if (!*coast && y < height - 1 - gap) {
2010  const byte* coastCheck = mask + 4 * (x + (y + gap) * width);
2011  *coast = MapIsWater(coastCheck);
2012  }
2013  }
2014  }
2015  }
2016 
2017  return color;
2018 }
2019 
2023 static const float MIN_DIST_BASE = 4.0f;
2024 
2030 {
2031  base_t* base = nullptr;
2032  while ((base = B_GetNext(base)) != nullptr)
2033  if (GetDistanceOnGlobe(pos, base->pos) < MIN_DIST_BASE)
2034  return base;
2035 
2036  return nullptr;
2037 }
2038 
2049 bool GEO_PositionFitsTCPNTypes (const vec2_t pos, const linkedList_t* terrainTypes, const linkedList_t* cultureTypes, const linkedList_t* populationTypes, const linkedList_t* nations)
2050 {
2051  bool coast = false;
2052  const char* terrainType = GEO_GetTerrainTypeByPos(pos, &coast);
2053  const char* cultureType = GEO_GetCultureTypeByPos(pos);
2054  const char* populationType = GEO_GetPopulationTypeByPos(pos);
2055 
2056  if (MapIsWater(GEO_GetColor(pos, MAPTYPE_TERRAIN, nullptr)))
2057  return false;
2058 
2059  if (!terrainTypes || cgi->LIST_ContainsString(terrainTypes, terrainType) || (coast && cgi->LIST_ContainsString(terrainTypes, "coast"))) {
2060  if (!cultureTypes || cgi->LIST_ContainsString(cultureTypes, cultureType)) {
2061  if (!populationTypes || cgi->LIST_ContainsString(populationTypes, populationType)) {
2062  const nation_t* nationAtPos = GEO_GetNation(pos);
2063  if (!nations)
2064  return true;
2065  if (nationAtPos && (!nations || cgi->LIST_ContainsString(nations, nationAtPos->id))) {
2066  return true;
2067  }
2068  }
2069  }
2070  }
2071 
2072  return false;
2073 }
2074 
2075 
2084 void CP_GetRandomPosOnGeoscape (vec2_t pos, bool noWater)
2085 {
2086  do {
2087  pos[0] = (frand() - 0.5f) * 360.0f;
2088  pos[1] = asin((frand() - 0.5f) * 2.0f) * todeg;
2089  } while (noWater && MapIsWater(GEO_GetColor(pos, MAPTYPE_TERRAIN, nullptr)));
2090 
2091  cgi->Com_DPrintf(DEBUG_CLIENT, "CP_GetRandomPosOnGeoscape: Get random position on geoscape %.2f:%.2f\n", pos[0], pos[1]);
2092 }
2093 
2108 bool CP_GetRandomPosOnGeoscapeWithParameters (vec2_t pos, const linkedList_t* terrainTypes, const linkedList_t* cultureTypes, const linkedList_t* populationTypes, const linkedList_t* nations)
2109 {
2110  float x, y;
2111  int num;
2112  int randomNum;
2113 
2114  /* RASTER might reduce amount of tested locations to get a better performance */
2115  /* Number of points in latitude and longitude that will be tested. Therefore, the total number of position tried
2116  * will be numPoints * numPoints */
2117  const float numPoints = 360.0 / RASTER;
2118  /* RASTER is minimizing the amount of locations, so an offset is introduced to enable access to all locations, depending on a random factor */
2119  const float offsetX = frand() * RASTER;
2120  const float offsetY = -1.0 + frand() * 2.0 / numPoints;
2121  vec2_t posT;
2122  int hits = 0;
2123 
2124  /* check all locations for suitability in 2 iterations */
2125  /* prepare 1st iteration */
2126 
2127  /* ITERATION 1 */
2128  for (y = 0; y < numPoints; y++) {
2129  const float posY = asin(2.0 * y / numPoints + offsetY) * todeg; /* Use non-uniform distribution otherwise we favour the poles */
2130  for (x = 0; x < numPoints; x++) {
2131  const float posX = x * RASTER - 180.0 + offsetX;
2132 
2133  Vector2Set(posT, posX, posY);
2134 
2135  if (GEO_PositionFitsTCPNTypes(posT, terrainTypes, cultureTypes, populationTypes, nations)) {
2136  /* the location given in pos belongs to the terrain, culture, population types and nations
2137  * that are acceptable, so count it */
2139  hits++;
2140  }
2141  }
2142  }
2143 
2144  /* if there have been no hits, the function failed to find a position */
2145  if (hits == 0)
2146  return false;
2147 
2148  /* the 2nd iteration goes through the locations again, but does so only until a random point */
2149  /* prepare 2nd iteration */
2150  randomNum = num = rand() % hits;
2151 
2152  /* ITERATION 2 */
2153  for (y = 0; y < numPoints; y++) {
2154  const float posY = asin(2.0 * y / numPoints + offsetY) * todeg;
2155  for (x = 0; x < numPoints; x++) {
2156  const float posX = x * RASTER - 180.0 + offsetX;
2157 
2158  Vector2Set(posT, posX, posY);
2159 
2160  if (GEO_PositionFitsTCPNTypes(posT, terrainTypes, cultureTypes, populationTypes, nations)) {
2161  num--;
2162 
2163  if (num < 1) {
2164  Vector2Set(pos, posX, posY);
2165  cgi->Com_DPrintf(DEBUG_CLIENT, "CP_GetRandomPosOnGeoscapeWithParameters: New random coords for a mission are %.0f:%.0f, chosen as #%i out of %i possible locations\n",
2166  pos[0], pos[1], randomNum, hits);
2167  return true;
2168  }
2169  }
2170  }
2171  }
2172 
2173  cgi->Com_DPrintf(DEBUG_CLIENT, "CP_GetRandomPosOnGeoscapeWithParameters: New random coordinates for a mission are %.0f:%.0f, chosen as #%i out of %i possible locations\n",
2174  pos[0], pos[1], num, hits);
2175 
2177  /* Make sure that position is within bounds */
2178  assert(pos[0] >= -180);
2179  assert(pos[0] <= 180);
2180  assert(pos[1] >= -90);
2181  assert(pos[1] <= 90);
2182 
2183  return true;
2184 }
2185 
2186 void GEO_Shutdown (void)
2187 {
2188  cgi->Free(terrainPic);
2189  terrainPic = nullptr;
2190 
2191  cgi->Free(culturePic);
2192  culturePic = nullptr;
2193 
2194  cgi->Free(populationPic);
2195  populationPic = nullptr;
2196 
2197  cgi->Free(nationsPic);
2198  nationsPic = nullptr;
2199 }
2200 
2201 void GEO_Init (const char* map)
2202 {
2203  /* load terrain mask */
2204  cgi->R_LoadImage(va("pics/geoscape/%s_terrain", map), &terrainPic, &terrainWidth, &terrainHeight);
2205  if (!terrainPic || !terrainWidth || !terrainHeight)
2206  cgi->Com_Error(ERR_DROP, "Couldn't load map mask %s_terrain in pics/geoscape", map);
2207 
2208  /* load culture mask */
2209  cgi->R_LoadImage(va("pics/geoscape/%s_culture", map), &culturePic, &cultureWidth, &cultureHeight);
2210  if (!culturePic || !cultureWidth || !cultureHeight)
2211  cgi->Com_Error(ERR_DROP, "Couldn't load map mask %s_culture in pics/geoscape", map);
2212 
2213  /* load population mask */
2214  cgi->R_LoadImage(va("pics/geoscape/%s_population", map), &populationPic, &populationWidth, &populationHeight);
2216  cgi->Com_Error(ERR_DROP, "Couldn't load map mask %s_population in pics/geoscape", map);
2217 
2218  /* load nations mask */
2219  cgi->R_LoadImage(va("pics/geoscape/%s_nations", map), &nationsPic, &nationsWidth, &nationsHeight);
2220  if (!nationsPic || !nationsWidth || !nationsHeight)
2221  cgi->Com_Error(ERR_DROP, "Couldn't load map mask %s_nations in pics/geoscape", map);
2222 }
2223 
2224 void GEO_Reset (const char* map)
2225 {
2226  GEO_Shutdown();
2227  GEO_Init(map);
2228  GEO_ResetAction();
2230 }
2231 
2236 {
2237  /* Unselect the currently selected ufo if it's the same */
2238  if (GEO_IsUFOSelected(ufo))
2239  GEO_ResetAction();
2240 
2242 }
2243 
2249 void GEO_SetOverlay (const char* overlayID, int status)
2250 {
2251  if (Q_streq(overlayID, "nation")) {
2252  cgi->Cvar_SetValue("geo_overlay_nation", status);
2253  return;
2254  }
2255 
2256  /* do nothing while the first base is not build */
2257  if (!B_AtLeastOneExists())
2258  return;
2259 
2260  if (Q_streq(overlayID, "xvi")) {
2261  cgi->Cvar_SetValue("geo_overlay_xvi", status);
2262  }
2263  if (Q_streq(overlayID, "radar")) {
2264  cgi->Cvar_SetValue("geo_overlay_radar", status);
2267  }
2268 }
2269 
2273 static void GEO_SetOverlay_f (void)
2274 {
2275  if (cgi->Cmd_Argc() != 3) {
2276  cgi->Com_Printf("Usage: %s <nation|xvi|radar> <1|0>\n", cgi->Cmd_Argv(0));
2277  return;
2278  }
2279 
2280  const char* overlay = cgi->Cmd_Argv(1);
2281  const int status = atoi(cgi->Cmd_Argv(2));
2282  const bool setRadar = Q_streq(overlay, "radar");
2283  GEO_SetOverlay(overlay, status);
2284 
2285  /* save last decision player took on radar display, in order to be able to restore it later */
2286  if (setRadar)
2288 }
2289 
2293 void GEO_InitStartup (void)
2294 {
2295  cgi->Cmd_AddCommand("geo_setoverlay", GEO_SetOverlay_f, "Set the geoscape overlay");
2296  cgi->Cmd_AddCommand("map_selectobject", GEO_SelectObject_f, "Select an object and center on it");
2297  cgi->Cmd_AddCommand("mn_mapaction_reset", GEO_ResetAction, nullptr);
2298 
2299 #ifdef DEBUG
2300  debug_showInterest = cgi->Cvar_Get("debug_showinterest", "0", CVAR_DEVELOPER, "Shows the global interest value on geoscape");
2301 #endif
2302 }
static void GEO_DrawRadarInMap(const uiNode_t *node, const radar_t *radar, const vec2_t pos)
Draw only the "wire" part of the radar coverage in geoscape.
float distance
Definition: cp_aircraft.h:40
static byte * culturePic
Definition: cp_geoscape.cpp:65
vec_t VectorLength(const vec3_t v)
Calculate the length of a vector.
Definition: mathlib.cpp:434
static char textStandard[2048]
Definition: cp_geoscape.cpp:51
void GEO_UpdateGeoscapeDock(void)
Will add missions and UFOs to the geoscape dock panel.
static float GEO_AngleOfPath2D(const vec2_t start, const vec2_t end, vec3_t direction, vec3_t ortVector)
Return the angle of a model given its position and destination, on 2D geoscape.
#define VectorCopy(src, dest)
Definition: vector.h:51
const nationInfo_t * NAT_GetCurrentMonthInfo(const nation_t *const nation)
Get the current month nation stats.
Definition: cp_nation.cpp:132
void GEO_NotifyAircraftRemoved(const aircraft_t *aircraft)
Notify that an aircraft has been removed from game.
aircraftSlot_t slot
Definition: cp_base.h:78
bool AIR_IsAircraftInBase(const aircraft_t *aircraft)
Checks whether given aircraft is in its homebase.
bool CP_IsRunning(void)
Checks whether a campaign mode game is running.
Definition: cp_campaign.cpp:79
#define LINE_MAXPTS
Definition: cp_aircraft.h:32
static const char * GEO_GetCultureTypeByPos(const vec2_t pos)
Determine the culture type under a given position.
static const char * GEO_GetAircraftText(char *buffer, size_t size, const aircraft_t *aircraft)
Assembles a string for an aircraft that is on the geoscape.
#define VectorSet(v, x, y, z)
Definition: vector.h:59
#define GEO_SetSelectedAircraft(aircraft)
Definition: cp_geoscape.h:62
#define GEO_SetInterceptorAircraft(interceptor)
Definition: cp_geoscape.h:63
int numUFOs
Definition: cp_radar.h:38
static void GEO_Draw3DMarkerIfVisible(const uiNode_t *node, const vec2_t pos, float theta, const char *model, int skin)
Draws a 3D marker on geoscape if the player can see it.
Nation definition.
Definition: cp_nation.h:44
bool RS_IsResearched_ptr(const technology_t *tech)
Checks whether an item is already researched.
A installation with all it's data.
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
static void GEO_GetMissionAngle(bool flatgeoscape, float *vector, int id)
center to a mission
float GEO_AngleOfPath(const vec2_t start, const vec2_t end, vec3_t direction, vec3_t ortVector)
Select which function should be used for calculating the direction of model on 2D or 3D geoscape...
#define MapIsSuburban(color)
Definition: cp_geoscape.h:42
static byte * populationPic
Definition: cp_geoscape.cpp:69
vec4_t beamColor
Definition: inv_shared.h:255
struct ccs_s::@1 geoscape
void GEO_Init(const char *map)
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition: shared.cpp:410
static bool GEO_MapToScreen(const uiNode_t *node, const vec2_t pos, int *x, int *y)
Transform a 2D position on the map to screen coordinates.
void VecToPolar(const vec3_t v, vec2_t a)
Converts vector coordinates into polar coordinates.
Definition: mathlib.cpp:922
static int populationWidth
Definition: cp_geoscape.cpp:71
#define MapIsWestern(color)
Definition: cp_geoscape.h:35
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition: cvar.h:71
int AIR_AircraftMenuStatsValues(const int value, const int stat)
Some of the aircraft values needs special calculations when they are shown in the menus...
static byte * terrainPic
Definition: cp_geoscape.cpp:61
#define MIS_Foreach(var)
iterates through missions
Definition: cp_missions.h:118
#define SIN_ALPHA
Definition: mathlib.h:65
const char * AIR_AircraftStatusToName(const aircraft_t *aircraft)
Translates the aircraft status id to a translatable string.
#define _(String)
Definition: cl_shared.h:43
static void GEO_RenderImage(int x, int y, const char *image)
static void GEO_DrawMapOneBase(const uiNode_t *node, const base_t *base, bool oneUFOVisible, const char *font)
Draws one base on the geoscape map (2D and 3D)
bool UFO_IsUFOSeenOnGeoscape(const aircraft_t *ufo)
Check if an aircraft should be seen on geoscape.
Definition: cp_ufo.cpp:989
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
#define LINE_MAXSEG
Definition: cp_aircraft.h:31
#define B_IsUnderAttack(base)
Definition: cp_base.h:53
void GEO_NotifyUFODisappear(const aircraft_t *ufo)
Notify that a UFO disappears on radars.
void GEO_CalcLine(const vec2_t start, const vec2_t end, mapline_t *line)
Calculate the shortest way to go from start to end on a sphere.
char name[MAX_VAR]
Definition: cp_base.h:86
int maxTeamSize
Definition: cp_aircraft.h:138
enum mapType_s mapType_t
static const char * GEO_GetUFOText(char *buffer, size_t size, const aircraft_t *ufo)
Assembles a string for a UFO that is on the geoscape.
int xviInfection
Definition: cp_nation.h:38
void GEO_Shutdown(void)
csi_t * csi
Definition: cgame.h:100
bool AII_InstallationCanShoot(const installation_t *installation)
Check if the installation has a weapon and ammo.
void GEO_InitStartup(void)
Initialise MAP/Geoscape.
const vec2_t vec2_origin
Definition: mathlib.cpp:34
void GEO_Draw(geoscapeData_t *data)
Draw the geoscape.
static void GEO_MapDrawLine(const uiNode_t *node, const mapline_t *line)
Draw a path on a menu node (usually the 2D geoscape map)
int day
Definition: common.h:291
const struct aircraft_s * ufos[MAX_UFOONGEOSCAPE]
Definition: cp_radar.h:37
date_t date
Definition: cp_campaign.h:245
#define B_AtLeastOneExists()
Definition: cp_base.h:55
#define MapIsOriental(color)
Definition: cp_geoscape.h:37
void CrossProduct(const vec3_t v1, const vec3_t v2, vec3_t cross)
binary operation on vectors in a three-dimensional space
Definition: mathlib.cpp:820
void GEO_NotifyMissionRemoved(const mission_t *mission)
Notify that a mission has been removed.
void GEO_CenterPosition(const vec2_t pos)
Start to rotate or shift the globe to the given position.
void RotatePointAroundVector(vec3_t dst, const vec3_t dir, const vec3_t point, float degrees)
Rotate a point around a given vector.
Definition: mathlib.cpp:849
const byte * GEO_GetColor(const vec2_t pos, mapType_t type, bool *coast)
Returns the color value from geoscape of a certain mask (terrain, culture or population) at a given p...
static const char * GEO_GetMissionText(char *buffer, size_t size, const mission_t *mission)
Assembles a string for a mission that is on the geoscape.
static void GEO_SetOverlay_f(void)
Console command to call GEO_SetOverlay.
#define GEO_GetSelectedAircraft()
Definition: cp_geoscape.h:56
int gameTimeScale
Definition: cp_campaign.h:264
int CP_CountMissionOnGeoscape(void)
Count the number of mission active and displayed on geoscape.
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
bool GEO_IsRadarOverlayActivated(void)
Definition: cp_geoscape.cpp:84
char name[MAX_VAR]
Definition: cp_aircraft.h:120
float stats[AIR_STATS_MAX]
Definition: inv_shared.h:248
#define UI_MAPEXTRADATACONST(node)
const char * id
Definition: cp_nation.h:45
const char * UFO_GetName(const aircraft_t *ufocraft)
Returns name of the UFO if UFO has been researched.
Definition: cp_ufo.cpp:243
aircraft_t * UFO_GetNextOnGeoscape(aircraft_t *lastUFO)
Definition: cp_ufo.cpp:66
static void GEO_StartCenter(uiNode_t *node)
Start center to the selected point.
const char *IMPORT * UI_GetFontFromNode(const uiNode_t *const node)
mission definition
Definition: cp_missions.h:85
static bool GEO_AllMapToScreen(const uiNode_t *node, const vec2_t pos, int *x, int *y, int *z)
Call either GEO_MapToScreen or GEO_3DMapToScreen depending on the geoscape you're using...
#define SECONDS_PER_HOUR
Definition: common.h:302
int numProjectiles
Definition: cp_campaign.h:304
mapline_t route
Definition: cp_aircraft.h:134
int integer
Definition: cvar.h:81
int INS_GetCount(void)
Get number of installations.
void CP_GameTimeStop(void)
Stop game time speed.
Definition: cp_time.cpp:126
voidpf void * buf
Definition: ioapi.h:42
A base with all it's data.
Definition: cp_base.h:84
static int centerOnEventIdx
Definition: cp_geoscape.cpp:52
void GEO_ResetAction(void)
No more special action on the geoscape.
int GEO_GetCivilianNumberByPosition(const vec2_t pos)
Get number of civilian on a map at given position.
bool radarOverlayWasSet
Definition: cp_radar.cpp:36
const linkedList_t *IMPORT * LIST_ContainsString(const linkedList_t *list, const char *string)
static void GEO_DrawAircraftHealthBar(const uiNode_t *node, const aircraft_t *aircraft)
Draws health bar for an aircraft (either phalanx or ufo)
#define BULLET_SIZE
#define YAW
Definition: mathlib.h:55
#define INS_Foreach(var)
Functions to generate and render overlay for geoscape.
static int nationsHeight
Definition: cp_geoscape.cpp:75
struct radar_s radar
Definition: cp_aircraft.h:158
static int nationsWidth
Definition: cp_geoscape.cpp:75
float frametime
Definition: cp_campaign.h:248
int B_GetCount(void)
Returns the count of founded bases.
Definition: cp_base.cpp:276
static int populationHeight
Definition: cp_geoscape.cpp:71
#define todeg
Definition: mathlib.h:51
struct technology_s * tech
Definition: cp_aircraft.h:162
Campaign XVI header.
cvar_t *IMPORT * Cvar_Set(const char *varName, const char *value,...) __attribute__((format(__printf__
static int cultureWidth
Definition: cp_geoscape.cpp:67
#define LinearInterpolation(a, b, x, y)
Definition: vector.h:78
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition: vector.h:44
#define ERR_DROP
Definition: common.h:211
bool AIR_AircraftHasEnoughFuel(const aircraft_t *aircraft, const vec2_t destination)
check if aircraft has enough fuel to go to destination, and then come back home
#define DEBUG_CLIENT
Definition: defines.h:59
#define NAT_Foreach(var)
iterates trough nations
Definition: cp_nation.h:80
GLsizei size
Definition: r_gl.h:152
static int cultureHeight
Definition: cp_geoscape.cpp:67
void GEO_NotifyUFORemoved(const aircraft_t *ufo, bool destroyed)
Notify that a UFO has been removed.
#define UFO_GetGeoscapeIDX(ufo)
Definition: cp_ufo.h:33
#define GEO_SetSelectedUFO(ufo)
Definition: cp_geoscape.h:64
#define GLOBE_ROTATE
Definition: cp_geoscape.cpp:44
vec3_t pos
Definition: cp_aircraft.h:131
static int terrainHeight
Definition: cp_geoscape.cpp:63
#define Vector2Set(v, x, y)
Definition: vector.h:61
date_t date
Definition: cl_shared.h:60
#define M_PI
Definition: mathlib.h:34
int MIS_GetIdx(const mission_t *mis)
Find idx corresponding to mission.
const char * model
Definition: inv_shared.h:269
base_t * GEO_PositionCloseToBase(const vec2_t pos)
Check if given pos is close to an existing base.
int numLasers
Definition: cp_base.h:120
static void GEO_DrawMapOneMission(const uiNode_t *node, const mission_t *mission)
Draws one mission on the geoscape map (2D and 3D)
Campaign missions headers.
const objDef_t * ammo
Definition: cp_aircraft.h:85
base_t * B_GetNext(base_t *lastBase)
Iterates through founded bases.
Definition: cp_base.cpp:285
#define UI_MAPEXTRADATA(node)
void PolarToVec(const vec2_t a, vec3_t v)
Converts longitude and latitude to a 3D vector in Euclidean coordinates.
Definition: mathlib.cpp:910
const cgame_import_t * cgi
struct radar_s radar
Definition: cp_base.h:106
bool GEO_Click(const uiNode_t *node, int x, int y, const vec2_t pos)
Click on the map/geoscape.
int idx
Definition: cp_base.h:85
const char * map
Definition: cl_shared.h:59
struct radar_s radar
bool GEO_PositionFitsTCPNTypes(const vec2_t pos, const linkedList_t *terrainTypes, const linkedList_t *cultureTypes, const linkedList_t *populationTypes, const linkedList_t *nations)
Checks for a given location, if it fulfills all criteria given via parameters (terrain, culture, population, nation type)
static void GEO_DrawBullets(const uiNode_t *node, const vec3_t pos)
Draws a bunch of bullets on the geoscape map.
#define GEO_IsMissionSelected(mission)
Definition: cp_geoscape.h:54
#define SELECT_CIRCLE_RADIUS
bool radarOverlay
Definition: cl_shared.h:58
bool CP_IsXVIVisible(void)
Returns true if the XVI effect should be visible to the player.
Definition: cp_xvi.cpp:196
static void GEO_GetGeoscapeAngle(vec2_t pos)
Returns position of the model corresponding to centerOnEventIdx.
struct aircraft_s * aircraftTarget
Definition: cp_aircraft.h:156
#define PITCH
Definition: mathlib.h:54
mapAction_t mapAction
Definition: cp_campaign.h:260
ccs_t ccs
Definition: cp_campaign.cpp:62
static void GEO_GetUFOAngle(bool flatgeoscape, float *vector, int idx)
center to a ufo
Campaign geoscape time header.
#define GEO_IsInterceptorSelected(aircraft)
Definition: cp_geoscape.h:52
static int terrainWidth
Definition: cp_geoscape.cpp:63
Header for Geoscape management.
static void GEO_DrawMapOnePhalanxAircraft(const uiNode_t *node, aircraft_t *aircraft, bool oneUFOVisible)
Draws one Phalanx aircraft on the geoscape map (2D and 3D)
Atomic structure used to define most of the UI.
Definition: ui_nodes.h:80
bool CP_GetRandomPosOnGeoscapeWithParameters(vec2_t pos, const linkedList_t *terrainTypes, const linkedList_t *cultureTypes, const linkedList_t *populationTypes, const linkedList_t *nations)
Determines a random position on geoscape that fulfills certain criteria given via parameters...
const installationTemplate_t * installationTemplate
cvar_t *IMPORT * Cvar_Get(const char *varName, const char *value, int flags, const char *desc)
void PerpendicularVector(vec3_t dst, const vec3_t src)
Finds a vector perpendicular to the source vector.
Definition: mathlib.cpp:780
char map[MAX_VAR]
Definition: cp_campaign.h:177
static byte * nationsPic
Definition: cp_geoscape.cpp:73
void * geoscapeNode
Definition: cl_shared.h:71
char name[MAX_VAR]
static const vec4_t yellow
Definition: cp_geoscape.cpp:56
vec2_t smoothFinal2DGeoscapeCenter
#define GLOBE_RADIUS
radius of the globe in screen coordinates
bool active
Definition: cp_missions.h:89
int sec
Definition: common.h:292
slot of aircraft
Definition: cp_aircraft.h:77
#define GEO_SetSelectedMission(mission)
Definition: cp_geoscape.h:65
bool AIR_IsAircraftOnGeoscape(const aircraft_t *aircraft)
Checks whether given aircraft is on geoscape.
const objDef_t * aircraftItem
Definition: cp_airfight.h:44
static const vec4_t green
Definition: cp_geoscape.cpp:55
bool GEO_IsNight(const vec2_t pos)
Check whether given position is Day or Night.
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
#define AIR_ForeachFromBase(var, base)
iterates trough all aircraft from a specific homebase
Definition: cp_aircraft.h:201
#define RASTER
Definition: cp_geoscape.h:49
int numPoints
Definition: cp_aircraft.h:39
#define MapIsNopopulation(color)
Definition: cp_geoscape.h:45
static const char * GEO_GetTerrainTypeByPos(const vec2_t pos, bool *coast)
Determine the terrain type under a given position.
baseWeapon_t batteries[MAX_BASE_SLOT]
Definition: cp_base.h:116
vec2_t point[LINE_MAXPTS]
Definition: cp_aircraft.h:42
#define CIRCLE_DRAW_POINTS
void GEO_DrawMarkers(const uiNode_t *node)
Draws all ufos, aircraft, bases and so on to the geoscape map (2D and 3D)
static const vec4_t red
Definition: cp_geoscape.cpp:57
static bool GEO_IsNationOverlayActivated(void)
Definition: cp_geoscape.cpp:89
#define DAYS_PER_YEAR_AVG
Definition: common.h:297
#define UI_MAP_DIST_SELECTION
maximum distance (in pixel) to get a valid mouse click
#define MapIsAfrican(color)
Definition: cp_geoscape.h:38
aircraft_t * attackingAircraft
Definition: cp_airfight.h:54
void CP_GetRandomPosOnGeoscape(vec2_t pos, bool noWater)
Determines a random position on geoscape.
float frand(void)
Return random values between 0 and 1.
Definition: mathlib.cpp:506
QGL_EXTERN GLint i
Definition: r_gl.h:113
aircraftProjectile_t projectiles[MAX_PROJECTILESONGEOSCAPE]
Definition: cp_campaign.h:303
static hudRadar_t radar
vec3_t projectedPos[MAX_MULTIPLE_PROJECTILES]
Definition: cp_airfight.h:47
int overallInterest
Definition: cp_campaign.h:238
#define MapIsEastern(color)
Definition: cp_geoscape.h:36
int AIR_GetTeamSize(const aircraft_t *aircraft)
Counts the number of soldiers in given aircraft.
#define COS_ALPHA
Definition: mathlib.h:66
Header for slot management related stuff.
const char *IMPORT * UI_GetActiveWindowName(void)
void GEO_CenterOnPoint_f(void)
Switch to next model on 2D and 3D geoscape.
vec_t VectorNormalize(vec3_t v)
Calculate unit vector for a given vec3_t.
Definition: mathlib.cpp:745
#define MapIsRural(color)
Definition: cp_geoscape.h:44
#define MapIsVillage(color)
Definition: cp_geoscape.h:43
static const float defaultBaseAngle
Definition: cp_geoscape.cpp:59
static void GEO_3DMapDrawLine(const uiNode_t *node, const mapline_t *line)
Draw a path on a menu node (usually the 3Dgeoscape map)
static uiNode_t * geoscapeNode
Definition: cp_geoscape.cpp:38
vec4_t color
Definition: ui_nodes.h:127
TerrainDefs terrainDefs
Definition: q_shared.h:574
#define CVAR_DEVELOPER
Definition: cvar.h:45
int range
Definition: cp_radar.h:35
void GEO_CheckPositionBoundaries(float *pos)
Check that a position (in latitude / longitude) is within boundaries.
aircraftStatus_t status
Definition: cp_aircraft.h:125
#define VectorEqualEpsilon(a, b, epsilon)
Definition: vector.h:64
#define GEO_IsUFOSelected(ufo)
Definition: cp_geoscape.h:53
static void GEO_DrawRadarLineCoverage(const uiNode_t *node, const radar_t *radar, const vec2_t pos)
Draw only the "wire" Radar coverage.
static void GEO_MapDrawEquidistantPoints(const uiNode_t *node, const vec2_t center, const float angle, const vec4_t color)
Draw equidistant points from a given point on a menu node.
void Q_strcat(char *dest, size_t destsize, const char *format,...)
Safely (without overflowing the destination buffer) concatenates two strings.
Definition: shared.cpp:475
#define SECONDS_PER_DAY
Definition: common.h:301
#define GEO_GetSelectedMission()
Definition: cp_geoscape.h:59
void GEO_PrintParameterStringByPos(const vec2_t pos)
Prints positions parameter in console.
#define MapIsWater(color)
Definition: cp_geoscape.h:32
void GEO_SelectUFO(aircraft_t *ufo)
Select the specified ufo on the geoscape.
vec_t vec3_t[3]
Definition: ufotypes.h:39
#define torad
Definition: mathlib.h:50
#define Vector2Copy(src, dest)
Definition: vector.h:52
vec_t vec2_t[2]
Definition: ufotypes.h:38
static void GEO_SelectObject_f(void)
Center the view and select an object from the geoscape.
Header file for single player campaign control.
void RADAR_UpdateWholeRadarOverlay(void)
Update radar overlay of base, installation and aircraft range.
Definition: cp_radar.cpp:89
#define GEO_GetSelectedUFO()
Definition: cp_geoscape.h:58
static void GEO_DrawMapOneInstallation(const uiNode_t *node, const installation_t *installation, bool oneUFOVisible, const char *font)
Draws one installation on the geoscape map (2D and 3D)
baseWeapon_t lasers[MAX_BASE_SLOT]
Definition: cp_base.h:119
#define MapIsUrban(color)
Definition: cp_geoscape.h:41
int AII_BaseCanShoot(const base_t *base)
Check if the base has weapon and ammo.
vec3_t direction
Definition: cp_aircraft.h:132
static bool GEO_IsXVIOverlayActivated(void)
Definition: cp_geoscape.cpp:94
#define lengthof(x)
Definition: shared.h:105
static void GEO_DrawBeam(const uiNode_t *node, const vec3_t start, const vec3_t end, const vec4_t color)
Draws a energy beam on the geoscape map (laser/particle)
char * description
Definition: q_shared.h:466
GLsizei const GLvoid * data
Definition: r_gl.h:152
aircraft_t * aimedAircraft
Definition: cp_airfight.h:57
#define Q_streq(a, b)
Definition: shared.h:136
vec3_t pos
Definition: cp_base.h:91
int stats[AIR_STATS_MAX]
Definition: cp_aircraft.h:159
static void GEO_ConvertObjectPositionToGeoscapePosition(bool flatgeoscape, float *vector, const vec2_t objectPos)
Will set the vector for the geoscape position.
#define AIR_Foreach(var)
iterates trough all aircraft
Definition: cp_aircraft.h:192
projectile used during fight between two or more aircraft
Definition: cp_airfight.h:43
static bool GEO_IsPositionSelected(const uiNode_t *node, const vec2_t pos, int x, int y)
Tell if the specified position is considered clicked.
const char * MIS_GetName(const mission_t *mission)
Returns a short translated name for a mission.
An aircraft with all it's data.
Definition: cp_aircraft.h:114
vec3_t pos[MAX_MULTIPLE_PROJECTILES]
Definition: cp_airfight.h:46
static const char * GEO_GetPopulationType(const byte *color)
Translate color value to population type.
#define MAX_BASES
Definition: cp_base.h:32
mapDef_t * mapDef
Definition: cp_missions.h:88
campaign_t * curCampaign
Definition: cp_campaign.h:377
void GEO_SetOverlay(const char *overlayID, int status)
Turn overlay on/off.
static const char * GEO_GetCultureType(const byte *color)
Translate color value to culture type.
static bool GEO_3DMapToScreen(const uiNode_t *node, const vec2_t pos, int *x, int *y, int *z)
Transform a 2D position on the map to screen coordinates.
#define ZOOM_LIMIT
Definition: cp_geoscape.cpp:45
aircraft_t * selectedUFO
Definition: cp_campaign.h:253
uint8_t byte
Definition: ufotypes.h:34
QGL_EXTERN int GLboolean GLfloat * v
Definition: r_gl.h:120
vec3_t smoothFinalGlobeAngle
#define VectorEqual(a, b)
Definition: vector.h:65
mission_t * MIS_GetByIdx(int id)
Find mission corresponding to idx.
craftItem craftitem
Definition: inv_shared.h:331
#define Vector4Copy(src, dest)
Definition: vector.h:53
static float GEO_AngleOfPath3D(const vec2_t start, const vec2_t end, vec3_t direction, vec3_t ortVector)
Return the angle of a model given its position and destination, on 3D geoscape.
bool xviOverlay
Definition: cl_shared.h:57
char * model
Definition: cp_aircraft.h:123
bool nationOverlay
Definition: cl_shared.h:56
vec2_t pos
Definition: cp_missions.h:104
nation_t * GEO_GetNation(const vec2_t pos)
Translate nation map color to nation.
A path on the map described by 2D points.
Definition: cp_aircraft.h:38
const char *IMPORT * Cmd_Argv(int n)
const char * CP_SecondConvert(int second)
Converts a number of second into a char to display.
Definition: cp_time.cpp:56
static const float MIN_DIST_BASE
Minimum distance between a new mission and an existing base.
#define VectorSubtract(a, b, dest)
Definition: vector.h:45
const objDef_t * item
Definition: cp_aircraft.h:84
int trackingRange
Definition: cp_radar.h:36
mission_t * GEO_SelectMission(mission_t *mission)
Select the specified mission.
baseWeapon_t batteries[MAX_INSTALLATION_BATTERIES]
const char * MIS_GetModel(const mission_t *mission)
Get mission model that should be shown on the geoscape.
int numBatteries
Definition: cp_base.h:117
int installationTime
Definition: cp_aircraft.h:89
void GEO_SelectAircraft(aircraft_t *aircraft)
Select the specified aircraft on the geoscape.
double GetDistanceOnGlobe(const vec2_t pos1, const vec2_t pos2)
Calculate distance on the geoscape.
Definition: mathlib.cpp:171
Detailed information about the nation relationship (currently per month, but could be used elsewhere)...
Definition: cp_nation.h:33
vec2_t newBasePos
Definition: cp_campaign.h:261
static const char * GEO_GetPopulationTypeByPos(const vec2_t pos)
Determine the population type under a given position.
#define GEO_IsAircraftSelected(aircraft)
Definition: cp_geoscape.h:51
vec_t vec4_t[4]
Definition: ufotypes.h:40
void GEO_Reset(const char *map)
const char * getTerrainName(const byte *const color) const
Translate color value to terrain type.
Definition: q_shared.h:455