1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: drawminimap.cpp
5 	Desc: contains drawMinimap()
6 
7 	Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 	See LICENSE for details.
9 
10 -------------------------------------------------------------------------------*/
11 
12 #include "../main.hpp"
13 #include "../draw.hpp"
14 #include "../game.hpp"
15 #include "../stat.hpp"
16 #include "../items.hpp"
17 #include "../sound.hpp"
18 #include "../menu.hpp"
19 #include "../player.hpp"
20 #include "interface.hpp"
21 #include "../collision.hpp"
22 #include "../mod_tools.hpp"
23 
24 /*-------------------------------------------------------------------------------
25 
26 	drawMinimap
27 
28 	Draws the game's minimap in the lower right corner of the screen
29 
30 -------------------------------------------------------------------------------*/
31 
32 Uint32 minotaur_timer = 0;
33 std::vector<MinimapPing> minimapPings;
34 int minimapPingGimpTimer = -1;
35 Uint32 lastMapTick = 0;
36 
minimapColorFunc(Uint8 r,Uint8 g,Uint8 b,Uint8 a)37 Uint32 minimapColorFunc(Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
38 	Uint32 result = 0u;
39 	result |= (Uint32)a << 24;
40 	result |= (Uint32)b << 16;
41 	result |= (Uint32)g <<  8;
42 	result |= (Uint32)r;
43 	return result;
44 }
45 
drawMinimap()46 void drawMinimap()
47 {
48 	if ( gameplayCustomManager.inUse() )
49 	{
50 		if ( CustomHelpers::isLevelPartOfSet(currentlevel, secretlevel, gameplayCustomManager.minimapDisableFloors) )
51 		{
52 			return;
53 		}
54 	}
55 
56 	node_t* node;
57 	Uint32 color;
58 	int x, y, i;
59 	int minimapTotalScale = minimapScaleQuickToggle + minimapScale;
60 	// handle toggling scale hotkey.
61 	if ( !command && *inputPressed(impulses[IN_MINIMAPSCALE]) || (shootmode && *inputPressed(joyimpulses[INJOY_GAME_MINIMAPSCALE])) )
62 	{
63 		if ( minimapScaleQuickToggle == 3 )
64 		{
65 			minimapScaleQuickToggle = 0;
66 		}
67 		else
68 		{
69 			++minimapScaleQuickToggle;
70 		}
71 		*inputPressed(impulses[IN_MINIMAPSCALE]) = 0;
72 		*inputPressed(joyimpulses[INJOY_GAME_MINIMAPSCALE]) = 0;
73 		playSound(139, 32);
74 	}
75 
76 	if ( map.height > 64 || map.width > 64 )
77 	{
78 		int maxDimension = std::max(map.height, map.width);
79 		maxDimension -= 64;
80 		int numMinimapSizesToReduce = 0;
81 		while ( maxDimension > 0 )
82 		{
83 			maxDimension -= 32;
84 			++numMinimapSizesToReduce;
85 		}
86 		minimapTotalScale = std::max(1, minimapScale - numMinimapSizesToReduce) + minimapScaleQuickToggle;
87 	}
88 
89 	// create a new minimap texture
90 	SDL_Surface* minimapSurface = SDL_CreateRGBSurface(0, map.width, map.height, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
91 	TempTexture* minimapTexture = new TempTexture();
92 	SDL_LockSurface(minimapSurface);
93 
94 	// draw level
95 	glDisable(GL_DEPTH_TEST);
96 	glDisable(GL_LIGHTING);
97 	glMatrixMode(GL_PROJECTION);
98 	glViewport(0, 0, xres, yres);
99 	glLoadIdentity();
100 	glOrtho(0, xres, 0, yres, -1, 1);
101 	glMatrixMode(GL_MODELVIEW);
102 	for ( x = 0; x < map.width; x++ )
103 	{
104 		for ( y = 0; y < map.height; y++ )
105 		{
106 			Uint32 color = 0;
107 			if ( minimap[y][x] == 0 )
108 			{
109 				color = minimapColorFunc(32, 12, 0, 255 * ((100 - minimapTransparencyBackground) / 100.f));
110 			}
111 			else if ( minimap[y][x] == 1 )
112 			{
113 				color = minimapColorFunc(96, 24, 0, 255 * ((100 - minimapTransparencyForeground) / 100.f));
114 			}
115 			else if ( minimap[y][x] == 2 )
116 			{
117 				color = minimapColorFunc(192, 64, 0, 255 * ((100 - minimapTransparencyForeground) / 100.f));
118 			}
119 			else if ( minimap[y][x] == 3 )
120 			{
121 				color = minimapColorFunc(32, 32, 32, 255 * ((100 - minimapTransparencyForeground) / 100.f));
122 			}
123 			else if ( minimap[y][x] == 4 )
124 			{
125 				color = minimapColorFunc(64, 64, 64, 255 * ((100 - minimapTransparencyForeground) / 100.f));
126 			}
127 			putPixel(minimapSurface, x, y, color);
128 		}
129 	}
130 	SDL_UnlockSurface(minimapSurface);
131 	minimapTexture->load(minimapSurface, false, true);
132 	minimapTexture->bind();
133 	glColor4f(1, 1, 1, 1);
134 	glBegin(GL_QUADS);
135 	glTexCoord2f(0, 0);
136 	glVertex2f(xres - map.width * minimapTotalScale, map.height * minimapTotalScale);
137 	glTexCoord2f(0, 1);
138 	glVertex2f(xres - map.width * minimapTotalScale, 0);
139 	glTexCoord2f(1, 1);
140 	glVertex2f(xres, 0);
141 	glTexCoord2f(1, 0);
142 	glVertex2f(xres, map.height * minimapTotalScale);
143 	glEnd();
144 	glBindTexture(GL_TEXTURE_2D, 0);
145 	if (minimapTexture) {
146 		delete minimapTexture;
147 		minimapTexture = nullptr;
148 	}
149 	if (minimapSurface) {
150 		SDL_FreeSurface(minimapSurface);
151 		minimapSurface = nullptr;
152 	}
153 	glDisable(GL_DEPTH_TEST);
154 	glDisable(GL_LIGHTING);
155 	glMatrixMode(GL_PROJECTION);
156 	glViewport(0, 0, xres, yres);
157 	glLoadIdentity();
158 	glOrtho(0, xres, 0, yres, -1, 1);
159 	glMatrixMode(GL_MODELVIEW);
160 	glBindTexture(GL_TEXTURE_2D, 0);
161 
162 	// draw exits/monsters
163 	glBegin(GL_QUADS);
164 	for ( node = map.entities->first; node != NULL; node = node->next )
165 	{
166 		Entity* entity = (Entity*)node->element;
167 		if ( entity->sprite == 161 || (entity->sprite >= 254 && entity->sprite < 258)
168 			|| entity->behavior == &actCustomPortal )   // ladder or portal models
169 		{
170 			if ( entity->x >= 0 && entity->y >= 0 && entity->x < map.width << 4 && entity->y < map.height << 4 )
171 			{
172 				x = floor(entity->x / 16);
173 				y = floor(entity->y / 16);
174 				if ( minimap[y][x] || (entity->entityShowOnMap > 0 && !(entity->behavior == &actCustomPortal)) )
175 				{
176 					if ( ticks % 40 - ticks % 20 )
177 					{
178 						glColor4f( 0, 1, 1, 1 );
179 						//glBegin(GL_QUADS);
180 						glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
181 						glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
182 						glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
183 						glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
184 						//glEnd();
185 					}
186 				}
187 			}
188 		}
189 		else
190 		{
191 			if ( entity->skill[28] > 0 ) // mechanism
192 			{
193 				if ( entity->behavior == &actCustomPortal
194 					|| entity->behavior == &actTextSource
195 					|| entity->behavior == &actFloorDecoration )
196 				{
197 					continue;
198 				}
199 			}
200 			if ( entity->behavior == &actMonster && entity->monsterAllyIndex < 0 )
201 			{
202 				bool warningEffect = false;
203 				if ( (players[clientnum] && players[clientnum]->entity
204 					&& players[clientnum]->entity->creatureShadowTaggedThisUid == entity->getUID())
205 					|| (entity->getStats() && entity->getStats()->EFFECTS[EFF_SHADOW_TAGGED]) )
206 				{
207 					warningEffect = true;
208 					x = floor(entity->x / 16);
209 					y = floor(entity->y / 16);
210 					glColor4f(.75, .75, .75, 1);
211 					//glBegin(GL_QUADS);
212 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
213 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
214 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
215 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
216 					//glEnd();
217 				}
218 				if ( !warningEffect
219 					&& ((stats[clientnum]->ring && stats[clientnum]->ring->type == RING_WARNING)
220 						|| (entity->entityShowOnMap > 0)) )
221 				{
222 					int beatitude = 0;
223 					if ( stats[clientnum]->ring && stats[clientnum]->ring->type == RING_WARNING )
224 					{
225 						beatitude = stats[clientnum]->ring->beatitude;
226 						// invert for succ/incubus
227 						if ( beatitude < 0 && shouldInvertEquipmentBeatitude(stats[clientnum]) )
228 						{
229 							beatitude = abs(stats[clientnum]->ring->beatitude);
230 						}
231 					}
232 
233 					bool doEffect = false;
234 					if ( entity->entityShowOnMap > 0 )
235 					{
236 						doEffect = true;
237 					}
238 					else if ( stats[clientnum]->ring && players[clientnum] && players[clientnum]->entity
239 						&& entityDist(players[clientnum]->entity, entity) < 16.0 * std::max(3, (11 + 5 * beatitude)) )
240 					{
241 						doEffect = true;
242 					}
243 					if ( doEffect )
244 					{
245 						x = floor(entity->x / 16);
246 						y = floor(entity->y / 16);
247 						glColor4f(0.75, 0.5, 0.75, 1);
248 						//glBegin(GL_QUADS);
249 						glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
250 						glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
251 						glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
252 						glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
253 						//glEnd();
254 						warningEffect = true;
255 					}
256 				}
257 				if ( !warningEffect && stats[clientnum]->shoes != NULL )
258 				{
259 					if ( stats[clientnum]->shoes->type == ARTIFACT_BOOTS )
260 					{
261 						if ( (abs(entity->vel_x) > 0.1 || abs(entity->vel_y) > 0.1)
262 							&& players[clientnum] && players[clientnum]->entity
263 							&& entityDist(players[clientnum]->entity, entity) < 16.0 * 20 )
264 						{
265 							entity->entityShowOnMap = std::max(entity->entityShowOnMap, TICKS_PER_SECOND * 5);
266 							x = floor(entity->x / 16);
267 							y = floor(entity->y / 16);
268 							glColor4f(0.75, 0.5, 0.75, 1);
269 							//glBegin(GL_QUADS);
270 							glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
271 							glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
272 							glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
273 							glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
274 							//glEnd();
275 						}
276 					}
277 				}
278 			}
279 			else if ( entity->isBoulderSprite() )     // boulder.vox
280 			{
281 				x = std::min<int>(std::max<int>(0, entity->x / 16), map.width - 1);
282 				y = std::min<int>(std::max<int>(0, entity->y / 16), map.height - 1);
283 				if ( minimap[y][x] == 1 || minimap[y][x] == 2 )
284 				{
285 					glColor4f( 192 / 255.f, 64 / 255.f, 0 / 255.f, 1 );
286 					//glBegin(GL_QUADS);
287 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
288 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
289 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
290 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
291 					//glEnd();
292 				}
293 			}
294 			else if ( entity->behavior == &actItem && entity->itemShowOnMap == 1 )
295 			{
296 				x = floor(entity->x / 16);
297 				y = floor(entity->y / 16);
298 				if ( ticks % 40 - ticks % 20 )
299 				{
300 					glColor4f(240 / 255.f, 228 / 255.f, 66 / 255.f, 1); // yellow
301 					//glBegin(GL_QUADS);
302 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
303 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
304 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
305 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
306 				}
307 			}
308 			else if ( entity->entityShowOnMap > 0 )
309 			{
310 				x = floor(entity->x / 16);
311 				y = floor(entity->y / 16);
312 				if ( ticks % 40 - ticks % 20 )
313 				{
314 					glColor4f(255 / 255.f, 168 / 255.f, 200 / 255.f, 1); // pink
315 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
316 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale - minimapTotalScale);
317 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale + minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
318 					glVertex2f(x * minimapTotalScale + xres - map.width * minimapTotalScale, map.height * minimapTotalScale - y * minimapTotalScale);
319 				}
320 			}
321 		}
322 		if ( entity->entityShowOnMap > 0 && lastMapTick != ticks )
323 		{
324 			// only decrease the entities' shown duration when the global game timer passes a tick
325 			// (drawMinimap doesn't follow game tick intervals)
326 			--entity->entityShowOnMap;
327 		}
328 	}
329 	lastMapTick = ticks;
330 	glEnd();
331 
332 	// draw player pings
333 	if ( !minimapPings.empty() )
334 	{
335 		for ( std::vector<MinimapPing>::iterator it = minimapPings.begin(); it != minimapPings.end();)
336 		{
337 			MinimapPing ping = *it;
338 			switch ( ping.player )
339 			{
340 				case 0:
341 					color = SDL_MapRGB(mainsurface->format, 64, 255, 64); // green
342 					break;
343 				case 1:
344 					color = SDL_MapRGB(mainsurface->format, 86, 180, 233); // sky blue
345 					break;
346 				case 2:
347 					color = SDL_MapRGB(mainsurface->format, 240, 228, 66); // yellow
348 					break;
349 				case 3:
350 					color = SDL_MapRGB(mainsurface->format, 204, 121, 167); // pink
351 					break;
352 				default:
353 					color = SDL_MapRGB(mainsurface->format, 192, 192, 192); // grey
354 					break;
355 			}
356 
357 			int aliveTime = ticks - ping.tickStart;
358 			if ( aliveTime < TICKS_PER_SECOND * 2.5 ) // 2.5 second duration.
359 			{
360 				if ( (aliveTime < TICKS_PER_SECOND && (aliveTime % 10 < 5)) || aliveTime >= TICKS_PER_SECOND || ping.radiusPing )
361 				{
362 					// draw the ping blinking every 5 ticks if less than 1 second lifetime, otherwise constantly draw.
363 					x = xres - map.width * minimapTotalScale + ping.x * minimapTotalScale;
364 					y = yres - map.height * minimapTotalScale + ping.y * minimapTotalScale;
365 					int alpha = 255;
366 					if ( ping.radiusPing )
367 					{
368 						alpha = 50;
369 					}
370 					if ( aliveTime >= TICKS_PER_SECOND * 2 )
371 					{
372 						// start fading ping after 2 seconds, lasting 0.5 seconds.
373 						real_t alphafade = 1 - (aliveTime - TICKS_PER_SECOND * 2) / static_cast<real_t>(TICKS_PER_SECOND * 0.5);
374 						alpha = std::max(static_cast<int>(alphafade * alpha), 0);
375 					}
376 					// draw a circle
377 					if ( ping.radiusPing )
378 					{
379 						int radius = 3 + std::min(30, aliveTime);
380 						radius = std::min(minimapTotalScale * 6, radius);
381 						drawCircle(x - 1, y - 1, std::max(radius + minimapObjectZoom, 0), color, alpha);
382 					}
383 					else
384 					{
385 						drawCircle(x - 1, y - 1, std::max(3 + minimapObjectZoom, 0), color, alpha);
386 					}
387 				}
388 			}
389 
390 
391 			// prune old pings > 2.5 seconds
392 			if ( aliveTime > TICKS_PER_SECOND * 2.5 )
393 			{
394 				if ( ping.player == clientnum )
395 				{
396 					if ( minimapPingGimpTimer > TICKS_PER_SECOND / 4 )
397 					{
398 						minimapPingGimpTimer = TICKS_PER_SECOND / 4; // reduce the gimp timer when one of the player's own pings fades.
399 					}
400 				}
401 				it = minimapPings.erase(it);
402 			}
403 			else
404 			{
405 				++it;
406 			}
407 		}
408 	}
409 
410 	// draw players and allies
411 
412 	for ( node = map.creatures->first; node != nullptr; node = node->next )
413 	{
414 		Entity* entity = (Entity*)node->element;
415 		int drawMonsterAlly = -1;
416 		int foundplayer = -1;
417 		if ( entity->behavior == &actPlayer )
418 		{
419 			foundplayer = entity->skill[2];
420 		}
421 		else if ( entity->behavior == &actMonster )
422 		{
423 			if ( entity->monsterAllyIndex >= 0 )
424 			{
425 				drawMonsterAlly = entity->monsterAllyIndex;
426 			}
427 		}
428 		if ( drawMonsterAlly >= 0 || foundplayer >= 0 )
429 		{
430 			// my player = green, other players = blue
431 			if ( foundplayer >= 0 )
432 			{
433 				switch ( foundplayer )
434 				{
435 					case 0:
436 						color = SDL_MapRGB(mainsurface->format, 64, 255, 64); // green
437 						break;
438 					case 1:
439 						color = SDL_MapRGB(mainsurface->format, 86, 180, 233); // sky blue
440 						break;
441 					case 2:
442 						color = SDL_MapRGB(mainsurface->format, 240, 228, 66); // yellow
443 						break;
444 					case 3:
445 						color = SDL_MapRGB(mainsurface->format, 204, 121, 167); // pink
446 						break;
447 					default:
448 						color = SDL_MapRGB(mainsurface->format, 192, 192, 192); // grey
449 						break;
450 				}
451 				if ( players[clientnum] && players[clientnum]->entity
452 					&& players[clientnum]->entity->creatureShadowTaggedThisUid == entity->getUID() )
453 				{
454 					color = SDL_MapRGB(mainsurface->format, 192, 192, 192); // grey
455 				}
456 				//color = SDL_MapRGB(mainsurface->format, 0, 192, 0);
457 			}
458 			else
459 			{
460 				switch ( drawMonsterAlly )
461 				{
462 					case 0:
463 						color = SDL_MapRGB(mainsurface->format, 64, 255, 64); // green
464 						break;
465 					case 1:
466 						color = SDL_MapRGB(mainsurface->format, 86, 180, 233); // sky blue
467 						break;
468 					case 2:
469 						color = SDL_MapRGB(mainsurface->format, 240, 228, 66); // yellow
470 						break;
471 					case 3:
472 						color = SDL_MapRGB(mainsurface->format, 204, 121, 167); // pink
473 						break;
474 					default:
475 						color = SDL_MapRGB(mainsurface->format, 192, 192, 192); // grey
476 						break;
477 				}
478 				if ( players[clientnum] && players[clientnum]->entity
479 					&& players[clientnum]->entity->creatureShadowTaggedThisUid == entity->getUID() )
480 				{
481 					color = SDL_MapRGB(mainsurface->format, 192, 192, 192); // grey
482 				}
483 			}
484 
485 			// draw the first pixel
486 			x = xres - map.width * minimapTotalScale + (int)(entity->x / (16.f / minimapTotalScale));
487 			y = map.height * minimapTotalScale - (int)(entity->y / (16.f / minimapTotalScale));
488 			if ( foundplayer >= 0 )
489 			{
490 				if ( softwaremode )
491 				{
492 					//SPG_Pixel(screen,(int)(players[c]->x/16)+564+x+xres/2-(status_bmp->w/2),(int)(players[c]->y/16)+yres-71+y,color); //TODO: NOT a PLAYERSWAP
493 				}
494 				else
495 				{
496 					glColor4f(((Uint8)(color >> mainsurface->format->Rshift)) / 255.f, ((Uint8)(color >> mainsurface->format->Gshift)) / 255.f, ((Uint8)(color >> mainsurface->format->Bshift)) / 255.f, 1);
497 					glBegin(GL_POINTS);
498 					glVertex2f( x, y );
499 					glEnd();
500 				}
501 			}
502 
503 			// draw a circle
504 			if ( foundplayer >= 0 )
505 			{
506 				drawCircle(x - 1, yres - y - 1, std::max(3 + minimapObjectZoom, 0), color, 255);
507 			}
508 			else
509 			{
510 				drawCircle(x - 1, yres - y - 1, std::max(2 + minimapObjectZoom, 0), color, 128);
511 			}
512 
513 			x = 0;
514 			y = 0;
515 			if ( foundplayer >= 0 )
516 			{
517 				for ( i = 0; i < 4 + minimapObjectZoom; ++i )
518 				{
519 					// move forward
520 					if ( cos(entity->yaw) > .4 )
521 					{
522 						x++;
523 					}
524 					else if ( cos(entity->yaw) < -.4 )
525 					{
526 						x--;
527 					}
528 					if ( sin(entity->yaw) > .4 )
529 					{
530 						y++;
531 					}
532 					else if ( sin(entity->yaw) < -.4 )
533 					{
534 						y--;
535 					}
536 
537 					// get brighter color shade
538 					/*if ( foundplayer )
539 					{
540 						color = SDL_MapRGB(mainsurface->format, 64, 255, 64);
541 					}
542 					else
543 					{
544 						color = SDL_MapRGB(mainsurface->format, 64, 64, 255);
545 					}*/
546 
547 					// draw the pixel
548 					if ( softwaremode )
549 					{
550 						//SPG_Pixel(screen,(int)(players[c]->x/16)+564+x+xres/2-(status_bmp->w/2),(int)(players[c]->y/16)+yres-71+y,color); //TODO: NOT a PLAYERSWAP
551 					}
552 					else
553 					{
554 						glColor4f(((Uint8)(color >> mainsurface->format->Rshift)) / 255.f, ((Uint8)(color >> mainsurface->format->Gshift)) / 255.f, ((Uint8)(color >> mainsurface->format->Bshift)) / 255.f, 1);
555 						glBegin(GL_POINTS);
556 						glVertex2f( xres - map.width * minimapTotalScale + (int)(entity->x / (16.f / minimapTotalScale)) + x, map.height * minimapTotalScale - (int)(entity->y / (16.f / minimapTotalScale)) - y );
557 						glEnd();
558 					}
559 				}
560 			}
561 		}
562 	}
563 
564 	// draw minotaur
565 	if (players[clientnum] == nullptr)
566 	{
567 		return;
568 	}
569 	for ( node = map.creatures->first; node != nullptr; node = node->next )
570 	{
571 		Entity* entity = (Entity*)node->element;
572 		if ( entity->sprite == 239 )
573 		{
574 			if ( ticks % 120 - ticks % 60 )
575 			{
576 				if ( !minotaur_timer )
577 				{
578 					playSound(116, 64);
579 				}
580 				minotaur_timer = 1;
581 				if ( !colorblind )
582 				{
583 					color = SDL_MapRGB(mainsurface->format, 192, 0, 0);
584 				}
585 				else
586 				{
587 					color = SDL_MapRGB(mainsurface->format, 0, 192, 192);
588 				}
589 
590 				// draw the first pixel
591 				x = xres - map.width * minimapTotalScale + (int)(entity->x / (16.f / minimapTotalScale));
592 				y = map.height * minimapTotalScale - (int)(entity->y / (16.f / minimapTotalScale));
593 				if ( softwaremode )
594 				{
595 					//SPG_Pixel(screen,(int)(players[c]->x/16)+564+x+xres/2-(status_bmp->w/2),(int)(players[c]->y/16)+yres-71+y,color); //TODO: NOT a PLAYERSWAP
596 				}
597 				else
598 				{
599 					glColor4f(((Uint8)(color >> 16)) / 255.f, ((Uint8)(color >> 8)) / 255.f, ((Uint8)(color)) / 255.f, 1);
600 					glBegin(GL_POINTS);
601 					glVertex2f( x, y );
602 					glEnd();
603 				}
604 
605 				// draw a circle
606 				drawCircle(x - 1, yres - y - 1, std::max(3 + minimapObjectZoom, 0), color, 255);
607 
608 				x = 0;
609 				y = 0;
610 				for ( i = 0; i < 4 + minimapObjectZoom; ++i )
611 				{
612 					// move forward
613 					if ( cos(entity->yaw) > .4 )
614 					{
615 						x++;
616 					}
617 					else if ( cos(entity->yaw) < -.4 )
618 					{
619 						x--;
620 					}
621 					if ( sin(entity->yaw) > .4 )
622 					{
623 						y++;
624 					}
625 					else if ( sin(entity->yaw) < -.4 )
626 					{
627 						y--;
628 					}
629 
630 					// get brighter color shade
631 					if ( !colorblind )
632 					{
633 						color = SDL_MapRGB(mainsurface->format, 255, 64, 64);
634 					}
635 					else
636 					{
637 						color = SDL_MapRGB(mainsurface->format, 64, 255, 255);
638 					}
639 
640 					// draw the pixel
641 					if ( softwaremode )
642 					{
643 						//SPG_Pixel(screen,(int)(players[c]->x/16)+564+x+xres/2-(status_bmp->w/2),(int)(players[c]->y/16)+yres-71+y,color); //TODO: NOT a PLAYERSWAR
644 					}
645 					else
646 					{
647 						glColor4f(((Uint8)(color >> 16)) / 255.f, ((Uint8)(color >> 8)) / 255.f, ((Uint8)(color)) / 255.f, 1);
648 						glBegin(GL_POINTS);
649 						glVertex2f( xres - map.width * minimapTotalScale + (int)(entity->x / (16.f / minimapTotalScale)) + x, map.height * minimapTotalScale - (int)(entity->y / (16.f / minimapTotalScale)) - y );
650 						glEnd();
651 					}
652 				}
653 			}
654 			else
655 			{
656 				minotaur_timer = 0;
657 			}
658 		}
659 	}
660 }
661 
minimapPingAdd(MinimapPing newPing)662 void minimapPingAdd(MinimapPing newPing)
663 {
664 	int numPlayerPings = 0;
665 	if ( !minimapPings.empty() )
666 	{
667 		for ( std::vector<MinimapPing>::iterator it = minimapPings.begin(); it != minimapPings.end();)
668 		{
669 			MinimapPing ping = *it;
670 			if ( ping.player == newPing.player && !newPing.radiusPing )
671 			{
672 				++numPlayerPings;
673 				// prune pings if too many by same player.
674 				if ( numPlayerPings > 3 )
675 				{
676 					if ( ping.player == clientnum )
677 					{
678 						// this is the player creating the sound source.
679 						minimapPingGimpTimer = TICKS_PER_SECOND * 3; // 3 second penalty for spam.
680 					}
681 					it = minimapPings.erase(it);
682 					continue;
683 				}
684 			}
685 			++it;
686 		}
687 	}
688 	if ( !minimapPingMute && !newPing.radiusPing )
689 	{
690 		playSound(399, 64);
691 	}
692 	minimapPings.insert(minimapPings.begin(), newPing);
693 }
694