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, &centre, 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