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