1 /*
2 This file is part of Warzone 2100.
3 Copyright (C) 1999-2004 Eidos Interactive
4 Copyright (C) 2005-2020 Warzone 2100 Project
5
6 Warzone 2100 is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 Warzone 2100 is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Warzone 2100; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 #include <string.h>
21
22 #include "lib/framework/frame.h"
23 #include "lib/framework/fixedpoint.h"
24 #include "lib/framework/math_ext.h"
25 #include "lib/ivis_opengl/pieblitfunc.h"
26 // FIXME Direct iVis implementation include!
27 #include "lib/ivis_opengl/piematrix.h"
28 #include "lib/ivis_opengl/piepalette.h"
29 #include "lib/ivis_opengl/piestate.h"
30 #include "lib/ivis_opengl/piefunc.h"
31 #include "lib/ivis_opengl/bitimage.h"
32 #include "lib/gamelib/gtime.h"
33 #include "advvis.h"
34 #include "objects.h"
35 #include "display3d.h"
36 #include "map.h"
37 #include "component.h"
38 #include "console.h"
39 #include "radar.h"
40 #include "mapdisplay.h"
41 #include "hci.h"
42 #include "geometry.h"
43 #include "intimage.h"
44 #include "loop.h"
45 #include "warcam.h"
46 #include "display.h"
47 #include "mission.h"
48 #include "multiplay.h"
49 #include "intdisplay.h"
50 #include "texture.h"
51 #include "warzoneconfig.h"
52 #ifndef GLM_ENABLE_EXPERIMENTAL
53 #define GLM_ENABLE_EXPERIMENTAL
54 #endif
55 #include <glm/gtx/transform.hpp>
56
57 #define HIT_NOTIFICATION (GAME_TICKS_PER_SEC * 2)
58 #define RADAR_FRAME_SKIP 10
59
60 bool bEnemyAllyRadarColor = false; /**< Enemy/ally radar color. */
61 RADAR_DRAW_MODE radarDrawMode = RADAR_MODE_DEFAULT; /**< Current mini-map mode. */
62 bool rotateRadar = true; ///< Rotate the radar?
63 bool radarRotationArrow = true; ///< display arrow when radar rotation enabled?
64
65 static PIELIGHT colRadarAlly, colRadarMe, colRadarEnemy;
66 static PIELIGHT tileColours[MAX_TILES];
67 static UDWORD *radarBuffer = nullptr;
68 static Vector3i playerpos = {0, 0, 0};
69
70 PIELIGHT clanColours[] =
71 {
72 // see frontend2.png for team color order.
73 // [r,g,b,a]
74 {{0, 255, 0, 255}}, // green Player 0
75 {{255, 192, 40, 255}}, // orange Player 1
76 {{255, 255, 255, 255}}, // grey Player 2
77 {{0, 0, 0, 255}}, // black Player 3
78 {{255, 0, 0, 255}}, // red Player 4
79 {{20, 20, 255, 255}}, // blue Player 5
80 {{255, 0, 192, 255}}, // pink Player 6
81 {{0, 255, 255, 255}}, // cyan Player 7
82 {{255, 255, 0, 255}}, // yellow Player 8
83 {{144, 0, 255, 255}}, // purple Player 9
84 {{200, 255, 255, 255}}, // white Player A (Should be brighter than grey, but grey is already maximum.)
85 {{128, 128, 255, 255}}, // bright blue Player B
86 {{128, 255, 128, 255}}, // neon green Player C
87 {{128, 0, 0, 255}}, // infrared Player D
88 {{64, 0, 128, 255}}, // ultraviolet Player E
89 {{128, 128, 0, 255}}, // brown Player F
90 };
91
92 static PIELIGHT flashColours[] =
93 {
94 //right now the flash color is all bright red
95 {{254, 37, 37, 200}}, // Player 0
96 {{254, 37, 37, 200}}, // Player 1
97 {{254, 37, 37, 200}}, // Player 2
98 {{254, 37, 37, 200}}, // Player 3
99 {{254, 37, 37, 200}}, // Player 4 (notice, brighter red)
100 {{254, 37, 37, 200}}, // Player 5
101 {{254, 37, 37, 200}}, // Player 6
102 {{254, 37, 37, 200}}, // Player 7
103 {{254, 37, 37, 200}}, // Player 8
104 {{254, 37, 37, 200}}, // Player 9
105 {{254, 37, 37, 200}}, // Player A
106 {{254, 37, 37, 200}}, // Player B
107 {{254, 37, 37, 200}}, // Player C
108 {{254, 37, 37, 200}}, // Player D
109 {{254, 37, 37, 200}}, // Player E
110 {{254, 37, 37, 200}}, // Player F
111 };
112
113 static size_t radarWidth, radarHeight, radarTexWidth, radarTexHeight;
114 static SDWORD radarCenterX, radarCenterY;
115 static uint8_t RadarZoom;
116 static float RadarZoomMultiplier = 1.0f;
117 static size_t radarBufferSize = 0;
118 static int frameSkip = 0;
119
120 static void DrawRadarTiles();
121 static void DrawRadarObjects();
122 static void DrawRadarExtras(const glm::mat4 &modelViewProjectionMatrix);
123 static void DrawNorth(const glm::mat4 &modelViewProjectionMatrix);
124 static void setViewingWindow();
125
radarSize(int ZoomLevel)126 static void radarSize(int ZoomLevel)
127 {
128 float zoom = (float)ZoomLevel * RadarZoomMultiplier / 16.0;
129 radarWidth = radarTexWidth * zoom;
130 radarHeight = radarTexHeight * zoom;
131 if (rotateRadar)
132 {
133 radarCenterX = pie_GetVideoBufferWidth() - BASE_GAP * 4 - static_cast<int>(MAX(radarHeight, radarWidth)) / 2;
134 radarCenterY = pie_GetVideoBufferHeight() - BASE_GAP * 4 - static_cast<int>(MAX(radarWidth, radarHeight)) / 2;
135 }
136 else
137 {
138 radarCenterX = pie_GetVideoBufferWidth() - BASE_GAP * 4 - static_cast<int>(radarWidth) / 2;
139 radarCenterY = pie_GetVideoBufferHeight() - BASE_GAP * 4 - static_cast<int>(radarHeight) / 2;
140 }
141 debug(LOG_WZ, "radar=(%u,%u) tex=(%zu,%zu) size=(%zu,%zu)", radarCenterX, radarCenterY, radarTexWidth, radarTexHeight, radarWidth, radarHeight);
142 }
143
radarInitVars()144 void radarInitVars()
145 {
146 radarTexWidth = 0;
147 radarTexHeight = 0;
148 RadarZoom = war_GetRadarZoom();
149 debug(LOG_WZ, "Resetting radar zoom to %u", RadarZoom);
150 radarSize(RadarZoom);
151 playerpos = Vector3i(-1, -1, -1);
152 frameSkip = 0;
153 }
154
InitRadar()155 bool InitRadar()
156 {
157 // Ally/enemy/me colors
158 colRadarAlly = WZCOL_YELLOW;
159 colRadarEnemy = WZCOL_RED;
160 colRadarMe = WZCOL_WHITE;
161 return true;
162 }
163
resizeRadar()164 bool resizeRadar()
165 {
166 if (radarBuffer)
167 {
168 free(radarBuffer);
169 }
170 radarTexWidth = scrollMaxX - scrollMinX;
171 radarTexHeight = scrollMaxY - scrollMinY;
172 radarBufferSize = radarTexWidth * radarTexHeight * sizeof(UDWORD);
173 radarBuffer = (uint32_t *)malloc(radarBufferSize);
174 memset(radarBuffer, 0, radarBufferSize);
175 frameSkip = 0;
176 if (rotateRadar)
177 {
178 RadarZoomMultiplier = (float)std::max(RADWIDTH, RADHEIGHT) / std::max<size_t>({radarTexWidth, radarTexHeight, 1});
179 }
180 else
181 {
182 RadarZoomMultiplier = 1.0f;
183 }
184 debug(LOG_WZ, "Setting radar zoom to %u", RadarZoom);
185 radarSize(RadarZoom);
186 pie_SetRadar(-static_cast<float>(radarWidth) / 2.0 - 1, -static_cast<float>(radarHeight) / 2.0 - 1, static_cast<float>(radarWidth), static_cast<float>(radarHeight), radarTexWidth, radarTexHeight);
187 setViewingWindow();
188
189 return true;
190 }
191
ShutdownRadar()192 bool ShutdownRadar()
193 {
194 free(radarBuffer);
195 radarBuffer = nullptr;
196 frameSkip = 0;
197 return true;
198 }
199
SetRadarZoom(uint8_t ZoomLevel)200 void SetRadarZoom(uint8_t ZoomLevel)
201 {
202 if (ZoomLevel > MAX_RADARZOOM)
203 {
204 ZoomLevel = MAX_RADARZOOM;
205 }
206 if (ZoomLevel < MIN_RADARZOOM)
207 {
208 ZoomLevel = MIN_RADARZOOM;
209 }
210 debug(LOG_WZ, "Setting radar zoom to %u from %u", ZoomLevel, RadarZoom);
211 RadarZoom = ZoomLevel;
212 radarSize(RadarZoom);
213 frameSkip = 0;
214 resizeRadar();
215 }
216
GetRadarZoom()217 uint8_t GetRadarZoom()
218 {
219 return RadarZoom;
220 }
221
222 /** Calculate the radar pixel sizes. Returns pixels per tile. */
CalcRadarPixelSize(float * SizeH,float * SizeV)223 static void CalcRadarPixelSize(float *SizeH, float *SizeV)
224 {
225 *SizeH = (float)radarHeight / std::max<size_t>(radarTexHeight, 1);
226 *SizeV = (float)radarWidth / std::max<size_t>(radarTexWidth, 1);
227 }
228
229 /** Given a position within the radar, return a world coordinate. */
CalcRadarPosition(int mX,int mY,int * PosX,int * PosY)230 void CalcRadarPosition(int mX, int mY, int *PosX, int *PosY)
231 {
232 int sPosX, sPosY;
233 float pixSizeH, pixSizeV;
234
235 Vector2f pos;
236 pos.x = mX - radarCenterX;
237 pos.y = mY - radarCenterY;
238 if (rotateRadar)
239 {
240 pos = Vector2f_Rotate2f(pos, -playerPos.r.y);
241 }
242 pos.x += radarWidth / 2.0;
243 pos.y += radarHeight / 2.0;
244
245 CalcRadarPixelSize(&pixSizeH, &pixSizeV);
246 sPosX = pos.x / pixSizeH; // adjust for pixel size
247 sPosY = pos.y / pixSizeV;
248 sPosX += scrollMinX; // adjust for scroll limits
249 sPosY += scrollMinY;
250
251 #if REALLY_DEBUG_RADAR
252 debug(LOG_ERROR, "m=(%d,%d) radar=(%d,%d) pos(%d,%d), scroll=(%u-%u,%u-%u) sPos=(%d,%d), pixSize=(%f,%f)",
253 mX, mY, radarX, radarY, posX, posY, scrollMinX, scrollMaxX, scrollMinY, scrollMaxY, sPosX, sPosY, pixSizeH, pixSizeV);
254 #endif
255
256 // old safety code -- still necessary?
257 sPosX = clip<int>(sPosX, scrollMinX, scrollMaxX -1);
258 sPosY = clip<int>(sPosY, scrollMinY, scrollMaxY -1);
259
260 *PosX = sPosX;
261 *PosY = sPosY;
262 }
263
drawRadar()264 void drawRadar()
265 {
266 float pixSizeH, pixSizeV;
267
268 CalcRadarPixelSize(&pixSizeH, &pixSizeV);
269
270 ASSERT_OR_RETURN(, radarBuffer, "No radar buffer allocated");
271
272 setViewingWindow();
273 playerpos = playerPos.p; // cache position
274
275 if (frameSkip <= 0)
276 {
277 DrawRadarTiles();
278 DrawRadarObjects();
279 pie_DownLoadRadar(radarBuffer);
280 frameSkip = RADAR_FRAME_SKIP;
281 }
282 frameSkip--;
283 glm::mat4 radarMatrix = glm::translate(glm::vec3(radarCenterX, radarCenterY, 0));
284 glm::mat4 orthoMatrix = glm::ortho(0.f, static_cast<float>(pie_GetVideoBufferWidth()), static_cast<float>(pie_GetVideoBufferHeight()), 0.f);
285 if (rotateRadar)
286 {
287 // rotate the map
288 radarMatrix *= glm::rotate(UNDEG(playerPos.r.y), glm::vec3(0.f, 0.f, 1.f));
289 if (radarRotationArrow)
290 {
291 DrawNorth(orthoMatrix * radarMatrix);
292 }
293 }
294
295 pie_RenderRadar(orthoMatrix * radarMatrix);
296 DrawRadarExtras(orthoMatrix * radarMatrix * glm::translate(glm::vec3(-static_cast<float>(radarWidth) / 2.f - 1.f, -static_cast<float>(radarHeight) / 2.f - 1.f, 0.f)));
297 drawRadarBlips(-static_cast<int>(radarWidth) / 2.0 - 1, -static_cast<int>(radarHeight) / 2.0 - 1, pixSizeH, pixSizeV, orthoMatrix * radarMatrix);
298 }
299
DrawNorth(const glm::mat4 & modelViewProjectionMatrix)300 static void DrawNorth(const glm::mat4 &modelViewProjectionMatrix)
301 {
302 iV_DrawImage(IntImages, RADAR_NORTH, -((radarWidth / 2.0) + iV_GetImageWidth(IntImages, RADAR_NORTH) + 1), -(radarHeight / 2.0), modelViewProjectionMatrix);
303 }
304
appliedRadarColour(RADAR_DRAW_MODE drawMode,MAPTILE * WTile)305 static PIELIGHT appliedRadarColour(RADAR_DRAW_MODE drawMode, MAPTILE *WTile)
306 {
307 PIELIGHT WScr = WZCOL_BLACK; // squelch warning
308
309 // draw radar on/off feature
310 if (!getRevealStatus() && !TEST_TILE_VISIBLE(selectedPlayer, WTile))
311 {
312 return WZCOL_TRANSPARENT_BOX;
313 }
314
315 switch (drawMode)
316 {
317 case RADAR_MODE_TERRAIN:
318 {
319 // draw radar terrain on/off feature
320 PIELIGHT col = tileColours[TileNumber_tile(WTile->texture)];
321
322 col.byte.r = sqrtf(col.byte.r * WTile->illumination);
323 col.byte.b = sqrtf(col.byte.b * WTile->illumination);
324 col.byte.g = sqrtf(col.byte.g * WTile->illumination);
325 if (terrainType(WTile) == TER_CLIFFFACE)
326 {
327 col.byte.r /= 2;
328 col.byte.g /= 2;
329 col.byte.b /= 2;
330 }
331 if (!hasSensorOnTile(WTile, selectedPlayer))
332 {
333 col.byte.r = col.byte.r * 2 / 3;
334 col.byte.g = col.byte.g * 2 / 3;
335 col.byte.b = col.byte.b * 2 / 3;
336 }
337 if (!TEST_TILE_VISIBLE(selectedPlayer, WTile))
338 {
339 col.byte.r /= 2;
340 col.byte.g /= 2;
341 col.byte.b /= 2;
342 }
343 WScr = col;
344 }
345 break;
346 case RADAR_MODE_COMBINED:
347 {
348 // draw radar terrain on/off feature
349 PIELIGHT col = tileColours[TileNumber_tile(WTile->texture)];
350
351 col.byte.r = sqrtf(col.byte.r * (WTile->illumination + WTile->height / ELEVATION_SCALE) / 2);
352 col.byte.b = sqrtf(col.byte.b * (WTile->illumination + WTile->height / ELEVATION_SCALE) / 2);
353 col.byte.g = sqrtf(col.byte.g * (WTile->illumination + WTile->height / ELEVATION_SCALE) / 2);
354 if (terrainType(WTile) == TER_CLIFFFACE)
355 {
356 col.byte.r /= 2;
357 col.byte.g /= 2;
358 col.byte.b /= 2;
359 }
360 if (!hasSensorOnTile(WTile, selectedPlayer))
361 {
362 col.byte.r = col.byte.r * 2 / 3;
363 col.byte.g = col.byte.g * 2 / 3;
364 col.byte.b = col.byte.b * 2 / 3;
365 }
366 if (!TEST_TILE_VISIBLE(selectedPlayer, WTile))
367 {
368 col.byte.r /= 2;
369 col.byte.g /= 2;
370 col.byte.b /= 2;
371 }
372 WScr = col;
373 }
374 break;
375 case RADAR_MODE_HEIGHT_MAP:
376 {
377 WScr.byte.r = WScr.byte.g = WScr.byte.b = WTile->height / ELEVATION_SCALE;
378 }
379 break;
380 case RADAR_MODE_NO_TERRAIN:
381 {
382 WScr = WZCOL_RADAR_BACKGROUND;
383 }
384 break;
385 case NUM_RADAR_MODES:
386 {
387 assert(false);
388 }
389 break;
390 }
391 return WScr;
392 }
393
394 /** Draw the map tiles on the radar. */
DrawRadarTiles()395 static void DrawRadarTiles()
396 {
397 SDWORD x, y;
398
399 for (x = scrollMinX; x < scrollMaxX; x++)
400 {
401 for (y = scrollMinY; y < scrollMaxY; y++)
402 {
403 MAPTILE *psTile = mapTile(x, y);
404 size_t pos = radarTexWidth * (y - scrollMinY) + (x - scrollMinX);
405
406 ASSERT(pos * sizeof(*radarBuffer) < radarBufferSize, "Buffer overrun");
407 if (y == scrollMinY || x == scrollMinX || y == scrollMaxY - 1 || x == scrollMaxX - 1)
408 {
409 radarBuffer[pos] = WZCOL_BLACK.rgba;
410 continue;
411 }
412 radarBuffer[pos] = appliedRadarColour(radarDrawMode, psTile).rgba;
413 }
414 }
415 }
416
417 /** Draw the droids and structure positions on the radar. */
DrawRadarObjects()418 static void DrawRadarObjects()
419 {
420 UBYTE clan;
421 PIELIGHT playerCol;
422 PIELIGHT flashCol;
423
424 /* Show droids on map - go through all players */
425 for (clan = 0; clan < MAX_PLAYERS; clan++)
426 {
427 DROID *psDroid;
428
429 //see if have to draw enemy/ally color
430 if (bEnemyAllyRadarColor)
431 {
432 if (clan == selectedPlayer)
433 {
434 playerCol = colRadarMe;
435 }
436 else
437 {
438 playerCol = (aiCheckAlliances(selectedPlayer, clan) ? colRadarAlly : colRadarEnemy);
439 }
440 }
441 else
442 {
443 //original 8-color mode
444 STATIC_ASSERT(MAX_PLAYERS <= ARRAY_SIZE(clanColours));
445 playerCol = clanColours[getPlayerColour(clan)];
446 }
447
448 STATIC_ASSERT(MAX_PLAYERS <= ARRAY_SIZE(flashColours));
449 flashCol = flashColours[getPlayerColour(clan)];
450
451 /* Go through all droids */
452 for (psDroid = apsDroidLists[clan]; psDroid != nullptr; psDroid = psDroid->psNext)
453 {
454 if (psDroid->pos.x < world_coord(scrollMinX) || psDroid->pos.y < world_coord(scrollMinY)
455 || psDroid->pos.x >= world_coord(scrollMaxX) || psDroid->pos.y >= world_coord(scrollMaxY))
456 {
457 continue;
458 }
459 if (psDroid->visible[selectedPlayer]
460 || (bMultiPlayer && alliancesSharedVision(game.alliance)
461 && aiCheckAlliances(selectedPlayer, psDroid->player)))
462 {
463 int x = psDroid->pos.x / TILE_UNITS;
464 int y = psDroid->pos.y / TILE_UNITS;
465 size_t pos = (x - scrollMinX) + (y - scrollMinY) * radarTexWidth;
466
467 ASSERT(pos * sizeof(*radarBuffer) < radarBufferSize, "Buffer overrun");
468 if (clan == selectedPlayer && gameTime > HIT_NOTIFICATION && gameTime - psDroid->timeLastHit < HIT_NOTIFICATION)
469 {
470 radarBuffer[pos] = flashCol.rgba;
471 }
472 else
473 {
474 radarBuffer[pos] = playerCol.rgba;
475 }
476 }
477 }
478 }
479
480 /* Do the same for structures */
481 for (SDWORD x = scrollMinX; x < scrollMaxX; x++)
482 {
483 for (SDWORD y = scrollMinY; y < scrollMaxY; y++)
484 {
485 MAPTILE *psTile = mapTile(x, y);
486 STRUCTURE *psStruct;
487 size_t pos = (x - scrollMinX) + (y - scrollMinY) * radarTexWidth;
488
489 ASSERT(pos * sizeof(*radarBuffer) < radarBufferSize, "Buffer overrun");
490 if (!TileHasStructure(psTile))
491 {
492 continue;
493 }
494 psStruct = (STRUCTURE *)psTile->psObject;
495 clan = psStruct->player;
496
497 //see if have to draw enemy/ally color
498 if (bEnemyAllyRadarColor)
499 {
500 if (clan == selectedPlayer)
501 {
502 playerCol = colRadarMe;
503 }
504 else
505 {
506 playerCol = (aiCheckAlliances(selectedPlayer, clan) ? colRadarAlly : colRadarEnemy);
507 }
508 }
509 else
510 {
511 //original 8-color mode
512 playerCol = clanColours[getPlayerColour(clan)];
513 }
514 flashCol = flashColours[getPlayerColour(clan)];
515
516 if (psStruct->visible[selectedPlayer]
517 || (bMultiPlayer && alliancesSharedVision(game.alliance)
518 && aiCheckAlliances(selectedPlayer, psStruct->player)))
519 {
520 if (clan == selectedPlayer && gameTime > HIT_NOTIFICATION && gameTime - psStruct->timeLastHit < HIT_NOTIFICATION)
521 {
522 radarBuffer[pos] = flashCol.rgba;
523 }
524 else
525 {
526 radarBuffer[pos] = playerCol.rgba;
527 }
528 }
529 }
530 }
531 }
532
533 /** Rotate an array of 2d vectors about a given angle, also translates them after rotating. */
RotateVector2D(Vector3i * Vector,Vector3i * TVector,Vector3i * Pos,int Angle,int Count)534 static void RotateVector2D(Vector3i *Vector, Vector3i *TVector, Vector3i *Pos, int Angle, int Count)
535 {
536 int64_t Cos = iCos(Angle);
537 int64_t Sin = iSin(Angle);
538 int ox = 0;
539 int oy = 0;
540 int i;
541 Vector3i *Vec = Vector;
542 Vector3i *TVec = TVector;
543
544 if (Pos)
545 {
546 ox = Pos->x;
547 oy = Pos->y;
548 }
549
550 for (i = 0; i < Count; i++)
551 {
552 TVec->x = ((Vec->x * Cos + Vec->y * Sin) >> 16) + ox;
553 TVec->y = ((Vec->y * Cos - Vec->x * Sin) >> 16) + oy;
554 Vec++;
555 TVec++;
556 }
557 }
558
getDistanceAdjust()559 static SDWORD getDistanceAdjust()
560 {
561 int dif = std::max<int>(MAXDISTANCE - getViewDistance(), 0);
562
563 return dif / 100;
564 }
565
getLengthAdjust()566 static SDWORD getLengthAdjust()
567 {
568 const int pitch = 360 - (playerPos.r.x / DEG_1);
569
570 // Max at
571 const int lookingDown = (0 - MIN_PLAYER_X_ANGLE);
572 const int lookingFar = (0 - MAX_PLAYER_X_ANGLE);
573
574 int dif = MAX(pitch - lookingFar, 0);
575 if (dif > (lookingDown - lookingFar))
576 {
577 dif = (lookingDown - lookingFar);
578 }
579
580 return dif / 2;
581 }
582
583 /** Draws a Myth/FF7 style viewing window */
setViewingWindow()584 static void setViewingWindow()
585 {
586 float pixSizeH, pixSizeV;
587 Vector3i v[4] = {{0, 0, 0}}, tv[4] = {{0, 0, 0}}, centre = {0, 0, 0};
588 int shortX, longX, yDrop, yDropVar;
589 int dif = getDistanceAdjust();
590 int dif2 = getLengthAdjust();
591 PIELIGHT colour;
592 CalcRadarPixelSize(&pixSizeH, &pixSizeV);
593 int x = playerPos.p.x * pixSizeH / TILE_UNITS;
594 int y = playerPos.p.z * pixSizeV / TILE_UNITS;
595
596 shortX = ((visibleTiles.x / 4) - (dif / 6)) * pixSizeH;
597 longX = ((visibleTiles.x / 2) - (dif / 4)) * pixSizeH;
598 yDropVar = ((visibleTiles.y / 2) - (dif2 / 3)) * pixSizeV;
599 yDrop = ((visibleTiles.y / 2) - dif2 / 3) * pixSizeV;
600
601 v[0].x = longX;
602 v[0].y = -yDropVar;
603
604 v[1].x = -longX;
605 v[1].y = -yDropVar;
606
607 v[2].x = shortX;
608 v[2].y = yDrop;
609
610 v[3].x = -shortX;
611 v[3].y = yDrop;
612
613 centre.x = x - scrollMinX * pixSizeH;
614 centre.y = y - scrollMinY * pixSizeV;
615
616 RotateVector2D(v, tv, ¢re, playerPos.r.y, 4);
617
618 switch (getCampaignNumber())
619 {
620 case 1:
621 case 2:
622 // white
623 colour.byte.r = UBYTE_MAX;
624 colour.byte.g = UBYTE_MAX;
625 colour.byte.b = UBYTE_MAX;
626 colour.byte.a = 0x3f;
627 break;
628 case 3:
629 // greenish
630 colour.byte.r = 0x3f;
631 colour.byte.g = UBYTE_MAX;
632 colour.byte.b = 0x3f;
633 colour.byte.a = 0x3f;
634 break;
635 default:
636 // black
637 colour.rgba = 0;
638 colour.byte.a = 0x3f;
639 break;
640 }
641
642 /* Send the four points to the draw routine and the clip box params */
643 pie_SetViewingWindow(tv, colour);
644 }
645
DrawRadarExtras(const glm::mat4 & modelViewProjectionMatrix)646 static void DrawRadarExtras(const glm::mat4 &modelViewProjectionMatrix)
647 {
648 pie_DrawViewingWindow(modelViewProjectionMatrix);
649 RenderWindowFrame(FRAME_RADAR, -1, -1, radarWidth + 2, radarHeight + 2, modelViewProjectionMatrix);
650 }
651
652 /** Does a screen coordinate lie within the radar area? */
CoordInRadar(int x,int y)653 bool CoordInRadar(int x, int y)
654 {
655 Vector2f pos;
656 pos.x = x - radarCenterX;
657 pos.y = y - radarCenterY;
658 if (rotateRadar)
659 {
660 pos = Vector2f_Rotate2f(pos, -playerPos.r.y);
661 }
662 pos.x += radarWidth / 2.0;
663 pos.y += radarHeight / 2.0;
664
665 if (pos.x < 0 || pos.y < 0 || pos.x >= radarWidth || pos.y >= radarHeight)
666 {
667 return false;
668 }
669 return true;
670 }
671
radarColour(UDWORD tileNumber,uint8_t r,uint8_t g,uint8_t b)672 void radarColour(UDWORD tileNumber, uint8_t r, uint8_t g, uint8_t b)
673 {
674 tileColours[tileNumber].byte.r = r;
675 tileColours[tileNumber].byte.g = g;
676 tileColours[tileNumber].byte.b = b;
677 tileColours[tileNumber].byte.a = 255;
678 }
679