1 //----------------------------------------------------------------------------
2 //  EDGE Automap Functions
3 //----------------------------------------------------------------------------
4 //
5 //  Copyright (c) 1999-2010  The EDGE Team.
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 //  Based on the DOOM source code, released by Id Software under the
20 //  following copyright:
21 //
22 //    Copyright (C) 1993-1996 by id Software, Inc.
23 //
24 //----------------------------------------------------------------------------
25 
26 #include "i_defs.h"
27 #include "i_defs_gl.h"
28 
29 #include "con_main.h"
30 #include "e_input.h"
31 #include "hu_draw.h"
32 #include "hu_style.h"
33 #include "m_argv.h"
34 #include "m_bbox.h"
35 #include "m_cheat.h"
36 #include "m_misc.h"
37 #include "n_network.h"
38 #include "p_local.h"
39 #include "am_map.h"
40 #include "r_draw.h"
41 #include "r_colormap.h"
42 #include "r_modes.h"
43 
44 #include <stdio.h>
45 #include <float.h>
46 #include <math.h>
47 
48 #define DEBUG_TRUEBSP  0
49 #define DEBUG_COLLIDE  0
50 
51 // Automap colors
52 
53 // NOTE: this order must match the one in the COAL API script
54 static rgbcol_t am_colors[AM_NUM_COLORS] =
55 {
56 	RGB_MAKE( 40, 40,112),  // AMCOL_Grid
57     RGB_MAKE(112,112,112),  // AMCOL_Allmap
58     RGB_MAKE(255,  0,  0),  // AMCOL_Wall
59     RGB_MAKE(192,128, 80),  // AMCOL_Step
60     RGB_MAKE(192,128, 80),  // AMCOL_Ledge
61     RGB_MAKE(220,220,  0),  // AMCOL_Ceil
62     RGB_MAKE(  0,200,200),  // AMCOL_Secret
63 
64     RGB_MAKE(255,255,255),  // AMCOL_Player
65     RGB_MAKE(  0,255,  0),  // AMCOL_Monster
66     RGB_MAKE(220,  0,  0),  // AMCOL_Corpse
67     RGB_MAKE(  0,  0,255),  // AMCOL_Item
68     RGB_MAKE(255,188,  0),  // AMCOL_Missile
69     RGB_MAKE(120, 60, 30)   // AMCOL_Scenery
70 };
71 
72 
73 // Automap keys
74 // Ideally these would be configurable...
75 
76 int key_am_up;
77 int key_am_down;
78 int key_am_left;
79 int key_am_right;
80 
81 int key_am_zoomin;
82 int key_am_zoomout;
83 
84 int key_am_follow;
85 int key_am_grid;
86 int key_am_mark;
87 int key_am_clear;
88 
89 #define AM_NUMMARKPOINTS  9
90 
91 //
92 // NOTE:
93 //   `F' in the names here means `Framebuffer', i.e. on-screen coords.
94 //   `M' in the names means `Map', i.e. coordinates in the level.
95 //
96 
97 // scale on entry
98 #define INIT_MSCALE (4.0f)
99 #define  MAX_MSCALE (200.0f)
100 
101 // how much the automap moves window per tic in frame-buffer coordinates
102 // moves a whole screen-width in 1.5 seconds
103 #define F_PANINC 6.1
104 
105 // how much zoom-in per tic
106 // goes to 3x in 1 second
107 #define M_ZOOMIN  1.03f
108 
109 // how much zoom-in for each mouse-wheel click
110 // goes to 3x in 4 clicks
111 #define WHEEL_ZOOMIN  1.32f
112 
113 
114 bool automapactive = false;
115 
116 cvar_c am_smoothing;
117 cvar_c am_gridsize;
118 
119 static int cheating = 0;
120 static int grid = 0;
121 
122 static bool show_things = false;
123 static bool show_walls  = false;
124 static bool show_allmap = false;
125 
126 
127 // location and size of window on screen
128 static float f_x, f_y;
129 static float f_w, f_h;
130 
131 // scale value which makes the whole map fit into the on-screen area
132 // (multiplying map coords by this value).
133 static float f_scale;
134 
135 static mobj_t *f_focus;
136 
137 
138 // location on map which the map is centred on
139 static float m_cx, m_cy;
140 
141 // relative scaling: 1.0 = map fits the on-screen area,
142 //                   2.0 = map is twice as big
143 //                   8.0 = map is eight times as big
144 static float m_scale;
145 
146 
147 // translates between frame-buffer and map distances
148 #define MTOF(xx) (  (int)((xx) * m_scale * f_scale))
149 #define FTOM(xx) ((float)((xx) / m_scale / f_scale))
150 
151 // translates between frame-buffer and map coordinates
152 #define CXMTOF(xx)  (f_x + f_w*0.5 + MTOF((xx) - m_cx))
153 #define CYMTOF(yy)  (f_y + f_h*0.5 - MTOF((yy) - m_cy))
154 
155 
156 // largest size of map along X or Y axis
157 static float map_size;
158 
159 static float map_min_x;
160 static float map_min_y;
161 static float map_max_x;
162 static float map_max_y;
163 
164 
165 // how far the window pans each tic (map coords)
166 static float panning_x = 0;
167 static float panning_y = 0;
168 
169 // how far the window zooms in each tic (map coords)
170 static float zooming = -1;
171 
172 
173 // where the points are
174 static mpoint_t markpoints[AM_NUMMARKPOINTS];
175 
176 #define NO_MARK_X  (-777)
177 
178 // next point to be assigned
179 static int markpointnum = 0;
180 
181 // specifies whether to follow the player around
182 static bool followplayer = true;
183 
184 
185 cheatseq_t cheat_amap = {0, 0};
186 
187 static bool stopped = true;
188 
189 bool rotatemap = false;
190 
191 extern style_c *automap_style;  // FIXME: put in header
192 
193 
194 //
195 // adds a marker at the current location
196 //
AddMark(void)197 static void AddMark(void)
198 {
199 	markpoints[markpointnum].x = m_cx;
200 	markpoints[markpointnum].y = m_cy;
201 
202 	markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS;
203 }
204 
205 //
206 // Determines bounding box of all vertices,
207 // sets global variables controlling zoom range.
208 //
FindMinMaxBoundaries(void)209 static void FindMinMaxBoundaries(void)
210 {
211 	map_min_x = +9e9;
212 	map_min_y = +9e9;
213 
214 	map_max_x = -9e9;
215 	map_max_y = -9e9;
216 
217 	for (int i = 0; i < numvertexes; i++)
218 	{
219 		map_min_x = MIN(map_min_x, vertexes[i].x);
220 		map_max_x = MAX(map_max_x, vertexes[i].x);
221 
222 		map_min_y = MIN(map_min_y, vertexes[i].y);
223 		map_max_y = MAX(map_max_y, vertexes[i].y);
224 	}
225 
226 	float map_w = map_max_x - map_min_x;
227 	float map_h = map_max_y - map_min_y;
228 
229 	map_size = MAX(map_w, map_h);
230 
231 	m_cx = (map_min_x + map_max_x) / 2.0;
232 	m_cy = (map_min_y + map_max_y) / 2.0;
233 }
234 
235 
ClearMarks(void)236 static void ClearMarks(void)
237 {
238 	for (int i = 0; i < AM_NUMMARKPOINTS; i++)
239 		markpoints[i].x = NO_MARK_X;
240 
241 	markpointnum = 0;
242 }
243 
244 
AM_InitLevel(void)245 void AM_InitLevel(void)
246 {
247 	if (!cheat_amap.sequence)
248 	{
249 		cheat_amap.sequence = language["iddt"];
250 	}
251 
252 	ClearMarks();
253 
254 	FindMinMaxBoundaries();
255 
256 	m_scale = INIT_MSCALE;
257 }
258 
259 
AM_Stop(void)260 void AM_Stop(void)
261 {
262 	automapactive = false;
263 	stopped = true;
264 
265 	panning_x = 0;
266 	panning_y = 0;
267 	zooming   = -1;
268 }
269 
AM_Hide(void)270 static void AM_Hide(void)
271 {
272 	automapactive = false;
273 
274 	panning_x = 0;
275 	panning_y = 0;
276 	zooming   = -1;
277 }
278 
AM_Show(void)279 static void AM_Show(void)
280 {
281 	automapactive = true;
282 
283 	if (! stopped)
284 	///	AM_Stop();
285 		return;
286 
287 	AM_InitLevel();
288 
289 	stopped  = false;
290 
291 	panning_x = 0;
292 	panning_y = 0;
293 	zooming   = -1;
294 }
295 
296 
297 //
298 // Zooming
299 //
ChangeWindowScale(float factor)300 static void ChangeWindowScale(float factor)
301 {
302 	m_scale *= factor;
303 
304 	m_scale = MAX(m_scale, 1.0);
305 	m_scale = MIN(m_scale, MAX_MSCALE);
306 }
307 
308 
309 //
310 // Handle events (user inputs) in automap mode
311 //
AM_Responder(event_t * ev)312 bool AM_Responder(event_t * ev)
313 {
314 	int sym = ev->value.key.sym;
315 
316 	// check the enable/disable key
317 	if (ev->type == ev_keydown && E_MatchesKey(key_map, sym))
318 	{
319 		if (automapactive)
320 			AM_Hide();
321 		else
322 			AM_Show();
323 		return true;
324 	}
325 
326 	if (! automapactive)
327 		return false;
328 
329     // --- handle key releases ---
330 
331 	if (ev->type == ev_keyup)
332 	{
333 		if (E_MatchesKey(key_am_left, sym) || E_MatchesKey(key_am_right, sym))
334 			panning_x = 0;
335 
336 		if (E_MatchesKey(key_am_up, sym) || E_MatchesKey(key_am_down, sym))
337 			panning_y = 0;
338 
339 		if (E_MatchesKey(key_am_zoomin, sym) || E_MatchesKey(key_am_zoomout, sym))
340 			zooming = -1;
341 
342 		return false;
343 	}
344 
345     // --- handle key presses ---
346 
347 	if (ev->type != ev_keydown)
348 		return false;
349 
350 	if (! followplayer)
351 	{
352 		if (E_MatchesKey(key_am_left, sym))
353 		{
354 			panning_x = -FTOM(F_PANINC);
355 			return true;
356 		}
357 		else if (E_MatchesKey(key_am_right, sym))
358 		{
359 			panning_x = FTOM(F_PANINC);
360 			return true;
361 		}
362 		else if (E_MatchesKey(key_am_up, sym))
363 		{
364 			panning_y = FTOM(F_PANINC);
365 			return true;
366 		}
367 		else if (E_MatchesKey(key_am_down, sym))
368 		{
369 			panning_y = -FTOM(F_PANINC);
370 			return true;
371 		}
372 	}
373 
374 	if (E_MatchesKey(key_am_zoomin, sym))
375 	{
376 		zooming = M_ZOOMIN;
377 		return true;
378 	}
379 	else if (E_MatchesKey(key_am_zoomout, sym))
380 	{
381 		zooming = 1.0 / M_ZOOMIN;
382 		return true;
383 	}
384 
385 	if (E_MatchesKey(key_am_follow, sym))
386 	{
387 		followplayer = !followplayer;
388 
389 		// -ACB- 1998/08/10 Use DDF Lang Reference
390 		if (followplayer)
391 			CON_PlayerMessageLDF(consoleplayer, "AutoMapFollowOn");
392 		else
393 			CON_PlayerMessageLDF(consoleplayer, "AutoMapFollowOff");
394 
395 		return true;
396 	}
397 
398 	if (E_MatchesKey(key_am_grid, sym))
399 	{
400 		grid = !grid;
401 		// -ACB- 1998/08/10 Use DDF Lang Reference
402 		if (grid)
403 			CON_PlayerMessageLDF(consoleplayer, "AutoMapGridOn");
404 		else
405 			CON_PlayerMessageLDF(consoleplayer, "AutoMapGridOff");
406 
407 		return true;
408 	}
409 
410 	if (E_MatchesKey(key_am_mark, sym))
411 	{
412 		// -ACB- 1998/08/10 Use DDF Lang Reference
413 		CON_PlayerMessage(consoleplayer, "%s %d",
414 			language["AutoMapMarkedSpot"], markpointnum);
415 		AddMark();
416 		return true;
417 	}
418 
419 	if (E_MatchesKey(key_am_clear, sym))
420 	{
421 		// -ACB- 1998/08/10 Use DDF Lang Reference
422 		CON_PlayerMessageLDF(consoleplayer, "AutoMapMarksClear");
423 		ClearMarks();
424 		return true;
425 	}
426 
427 	// -AJA- 2007/04/18: mouse-wheel support
428 	if (sym == KEYD_WHEEL_DN)
429 	{
430 		ChangeWindowScale(1.0 / WHEEL_ZOOMIN);
431 		return true;
432 	}
433 	else if (sym == KEYD_WHEEL_UP)
434 	{
435 		ChangeWindowScale(WHEEL_ZOOMIN);
436 		return true;
437 	}
438 
439 	// -ACB- 1999/09/28 Proper casting
440 	if (!DEATHMATCH() && M_CheckCheat(&cheat_amap, (char)sym))
441 	{
442 		cheating = (cheating + 1) % 3;
443 
444 		show_things = (cheating == 2) ? true : false;
445 		show_walls  = (cheating >= 1) ? true : false;
446 	}
447 
448 	return false;
449 }
450 
451 
452 //
453 // Updates on game tick
454 //
AM_Ticker(void)455 void AM_Ticker(void)
456 {
457 	if (! automapactive)
458 		return;
459 
460 	// Change x,y location
461 	if (! followplayer)
462 	{
463 		m_cx += panning_x;
464 		m_cy += panning_y;
465 
466 		// limit position, don't go outside of the map
467 		m_cx = MIN(m_cx, map_max_x);
468 		m_cx = MAX(m_cx, map_min_x);
469 
470 		m_cy = MIN(m_cy, map_max_y);
471 		m_cy = MAX(m_cy, map_min_y);
472 	}
473 
474 	// Change the zoom if necessary
475 	if (zooming > 0)
476 		ChangeWindowScale(zooming);
477 }
478 
479 //
480 // Rotation in 2D.
481 // Used to rotate player arrow line character.
482 //
Rotate(float * x,float * y,angle_t a)483 static inline void Rotate(float * x, float * y, angle_t a)
484 {
485 	float new_x = *x * M_Cos(a) - *y * M_Sin(a);
486 	float new_y = *x * M_Sin(a) + *y * M_Cos(a);
487 
488 	*x = new_x;
489 	*y = new_y;
490 }
491 
GetRotatedCoords(float sx,float sy,float * dx,float * dy)492 static inline void GetRotatedCoords(float sx, float sy, float *dx, float *dy)
493 {
494 	*dx = sx;
495 	*dy = sy;
496 
497 	if (rotatemap)
498 	{
499 		// rotate coordinates so they are on the map correctly
500 		*dx -= f_focus->x;
501 		*dy -= f_focus->y;
502 
503 		Rotate(dx, dy, ANG90 - f_focus->angle);
504 
505 		*dx += f_focus->x;
506 		*dy += f_focus->y;
507 	}
508 }
509 
GetRotatedAngle(angle_t src)510 static inline angle_t GetRotatedAngle(angle_t src)
511 {
512 	if (rotatemap)
513 		return src + ANG90 - f_focus->angle;
514 
515 	return src;
516 }
517 
518 
519 //
520 // Draw visible parts of lines.
521 //
DrawMLine(mline_t * ml,rgbcol_t rgb,bool thick=true)522 static void DrawMLine(mline_t * ml, rgbcol_t rgb, bool thick = true)
523 {
524 	if (! am_smoothing.d)
525 		thick = false;
526 
527 	float x1 = f_x + f_w*0.5 + MTOF(ml->a.x);
528 	float y1 = f_y + f_h*0.5 - MTOF(ml->a.y);
529 
530 	float x2 = f_x + f_w*0.5 + MTOF(ml->b.x);
531 	float y2 = f_y + f_h*0.5 - MTOF(ml->b.y);
532 
533 	float dx = MTOF(- m_cx);
534 	float dy = MTOF(- m_cy);
535 
536 	HUD_SolidLine(x1, y1, x2, y2, rgb, thick, thick, dx, dy);
537 }
538 
539 
540 //
541 // Draws flat (floor/ceiling tile) aligned grid lines.
542 //
DrawGrid()543 static void DrawGrid()
544 {
545 	mline_t ml;
546 
547 	int grid_size = MAX(4, am_gridsize.d);
548 
549 	int mx0 = int(m_cx);
550 	int my0 = int(m_cy);
551 
552 	if (mx0 < 0) mx0 -= -(-mx0 % grid_size); else mx0 -= mx0 % grid_size;
553 	if (my0 < 0) my0 -= -(-my0 % grid_size); else my0 -= my0 % grid_size;
554 
555 	for (int j = 1; j < 1024; j++)
556 	{
557 		int jx = ((j & ~1) >> 1);
558 
559 		// stop when both lines are off the screen
560 		float x1 = CXMTOF(mx0 - jx * grid_size);
561 		float x2 = CXMTOF(mx0 + jx * grid_size);
562 
563 		if (x1 < f_x && x2 >= f_x + f_w)
564 			break;
565 
566 		ml.a.x = mx0 + jx * ((j & 1) ? -grid_size : grid_size);
567 		ml.b.x = ml.a.x;
568 
569 		ml.a.y = -40000;
570 		ml.b.y = +40000;
571 
572 		DrawMLine(&ml, am_colors[AMCOL_Grid], false);
573 	}
574 
575 	for (int k = 1; k < 1024; k++)
576 	{
577 		int ky = ((k & ~1) >> 1);
578 
579 		// stop when both lines are off the screen
580 		float y1 = CYMTOF(my0 + ky * grid_size);
581 		float y2 = CYMTOF(my0 - ky * grid_size);
582 
583 		if (y1 < f_y && y2 >= f_y + f_h)
584 			break;
585 
586 		ml.a.x = -40000;
587 		ml.b.x = +40000;
588 
589 		ml.a.y = my0 + ky * ((k & 1) ? -grid_size : grid_size);
590 		ml.b.y = ml.a.y;
591 
592 		DrawMLine(&ml, am_colors[AMCOL_Grid], false);
593 	}
594 }
595 
596 
597 //
598 // Checks whether the two sectors' regions are similiar.  If they are
599 // different enough, a line will be drawn on the automap.
600 //
601 // -AJA- 1999/12/07: written.
602 //
CheckSimiliarRegions(sector_t * front,sector_t * back)603 static bool CheckSimiliarRegions(sector_t *front, sector_t *back)
604 {
605 	extrafloor_t *F, *B;
606 
607 	if (front->tag == back->tag)
608 		return true;
609 
610 	// Note: doesn't worry about liquids
611 
612 	F = front->bottom_ef;
613 	B = back->bottom_ef;
614 
615 	while (F && B)
616 	{
617 		if (F->top_h != B->top_h)
618 			return false;
619 
620 		if (F->bottom_h != B->bottom_h)
621 			return false;
622 
623 		F = F->higher;
624 		B = B->higher;
625 	}
626 
627 	return (F || B) ? false : true;
628 }
629 
630 
631 //
632 // Determines visible lines, draws them.
633 //
634 // -AJA- This is now *lineseg* based, not linedef.
635 //
AM_WalkSeg(seg_t * seg)636 static void AM_WalkSeg(seg_t *seg)
637 {
638 	mline_t l;
639 	line_t *line;
640 
641 	sector_t *front = seg->frontsector;
642 	sector_t *back  = seg->backsector;
643 
644 	if (seg->miniseg)
645 	{
646 #if (DEBUG_TRUEBSP == 1)
647 		if (seg->partner && seg > seg->partner)
648 			return;
649 
650 		GetRotatedCoords(seg->v1->x, seg->v1->y, &l.a.x, &l.a.y);
651 		GetRotatedCoords(seg->v2->x, seg->v2->y, &l.b.x, &l.b.y);
652 
653 		DrawMLine(&l, RGB_MAKE(0,0,128), false);
654 #endif
655 		return;
656 	}
657 
658 	line = seg->linedef;
659 	SYS_ASSERT(line);
660 
661 	// only draw segs on the _right_ side of linedefs
662 	if (line->side[1] == seg->sidedef)
663 		return;
664 
665 	GetRotatedCoords(seg->v1->x, seg->v1->y, &l.a.x, &l.a.y);
666 	GetRotatedCoords(seg->v2->x, seg->v2->y, &l.b.x, &l.b.y);
667 
668 	if ((line->flags & MLF_Mapped) || show_walls)
669 	{
670 		if ((line->flags & MLF_DontDraw) && !show_walls)
671 			return;
672 
673 		if (!front || !back)
674 		{
675 			DrawMLine(&l, am_colors[AMCOL_Wall]);
676 		}
677 		else
678 		{
679 			if (line->flags & MLF_Secret)
680 			{
681 				// secret door
682 				if (show_walls)
683 					DrawMLine(&l, am_colors[AMCOL_Secret]);
684 				else
685 					DrawMLine(&l, am_colors[AMCOL_Wall]);
686 			}
687 			else if (back->f_h != front->f_h)
688 			{
689 				float diff = fabs(back->f_h - front->f_h);
690 
691 				// floor level change
692 				if (diff > 24)
693 					DrawMLine(&l, am_colors[AMCOL_Ledge]);
694 				else
695 					DrawMLine(&l, am_colors[AMCOL_Step]);
696 			}
697 			else if (back->c_h != front->c_h)
698 			{
699 				// ceiling level change
700 				DrawMLine(&l, am_colors[AMCOL_Ceil]);
701 			}
702 			else if ((front->exfloor_used > 0 || back->exfloor_used > 0) &&
703 				(front->exfloor_used != back->exfloor_used ||
704 				! CheckSimiliarRegions(front, back)))
705 			{
706 				// -AJA- 1999/10/09: extra floor change.
707 				DrawMLine(&l, am_colors[AMCOL_Ledge]);
708 			}
709 			else if (show_walls)
710 			{
711 				DrawMLine(&l, am_colors[AMCOL_Allmap]);
712 			}
713 		}
714 	}
715 	else if (f_focus->player && (show_allmap || f_focus->player->powers[PW_AllMap] != 0))
716 	{
717 		if (! (line->flags & MLF_DontDraw))
718 			DrawMLine(&l, am_colors[AMCOL_Allmap]);
719 	}
720 }
721 
722 
DrawLineCharacter(mline_t * lineguy,int lineguylines,float radius,angle_t angle,rgbcol_t rgb,float x,float y)723 static void DrawLineCharacter(mline_t *lineguy, int lineguylines,
724 							  float radius, angle_t angle,
725 							  rgbcol_t rgb, float x, float y)
726 {
727 	float cx, cy;
728 
729 	GetRotatedCoords(x, y, &cx, &cy);
730 
731 	cx = CXMTOF(cx);
732 	cy = CYMTOF(cy);
733 
734 	radius = MTOF(radius);
735 
736 	if (radius < 2)
737 		radius = 2;
738 
739 	angle = GetRotatedAngle(angle);
740 
741 	for (int i = 0; i < lineguylines; i++)
742 	{
743 		float ax = lineguy[i].a.x * radius;
744 		float ay = lineguy[i].a.y * radius;
745 
746 		if (angle)
747 			Rotate(&ax, &ay, angle);
748 
749 		float bx = lineguy[i].b.x * radius;
750 		float by = lineguy[i].b.y * radius;
751 
752 		if (angle)
753 			Rotate(&bx, &by, angle);
754 
755 		HUD_SolidLine(cx+ax, cy-ay, cx+bx, cy-by, rgb);
756 	}
757 }
758 
759 
760 #if (DEBUG_COLLIDE == 1)
DrawObjectBounds(mobj_t * mo,rgbcol_t rgb)761 static void DrawObjectBounds(mobj_t *mo, rgbcol_t rgb)
762 {
763 	float R = mo->radius;
764 
765 	if (R < 2)
766 		R = 2;
767 
768 	float lx = mo->x - R;
769 	float ly = mo->y - R;
770 	float hx = mo->x + R;
771 	float hy = mo->y + R;
772 
773 	mline_t ml;
774 
775 	GetRotatedCoords(lx, ly, &ml.a.x, &ml.a.y);
776 	GetRotatedCoords(lx, hy, &ml.b.x, &ml.b.y);
777 	DrawMLine(&ml, rgb);
778 
779 	GetRotatedCoords(lx, hy, &ml.a.x, &ml.a.y);
780 	GetRotatedCoords(hx, hy, &ml.b.x, &ml.b.y);
781 	DrawMLine(&ml, rgb);
782 
783 	GetRotatedCoords(hx, hy, &ml.a.x, &ml.a.y);
784 	GetRotatedCoords(hx, ly, &ml.b.x, &ml.b.y);
785 	DrawMLine(&ml, rgb);
786 
787 	GetRotatedCoords(hx, ly, &ml.a.x, &ml.a.y);
788 	GetRotatedCoords(lx, ly, &ml.b.x, &ml.b.y);
789 	DrawMLine(&ml, rgb);
790 }
791 #endif
792 
793 
794 static rgbcol_t player_colors[8] =
795 {
796 	RGB_MAKE(  5,255,  5),  // GREEN,
797 	RGB_MAKE( 80, 80, 80),  // GRAY + GRAY_LEN*2/3,
798 	RGB_MAKE(160,100, 50),  // BROWN,
799 	RGB_MAKE(255,255,255),  // RED + RED_LEN/2,
800 	RGB_MAKE(255,176,  5),  // ORANGE,
801 	RGB_MAKE(170,170,170),  // GRAY + GRAY_LEN*1/3,
802 	RGB_MAKE(255,  5,  5),  // RED,
803 	RGB_MAKE(255,185,225),  // PINK
804 };
805 
806 //
807 // The vector graphics for the automap.
808 //
809 // A line drawing of the player pointing right, starting from the
810 // middle.
811 
812 static mline_t player_arrow[] =
813 {
814 	{{-0.875f, 0.0f}, {1.0f, 0.0f}},   // -----
815 
816 	{{1.0f, 0.0f}, {0.5f,  0.25f}},  // ----->
817 	{{1.0f, 0.0f}, {0.5f, -0.25f}},
818 
819 	{{-0.875f, 0.0f}, {-1.125f,  0.25f}},  // >---->
820 	{{-0.875f, 0.0f}, {-1.125f, -0.25f}},
821 
822 	{{-0.625f, 0.0f}, {-0.875f,  0.25f}},  // >>--->
823 	{{-0.625f, 0.0f}, {-0.875f, -0.25f}}
824 };
825 
826 #define NUMPLYRLINES (sizeof(player_arrow)/sizeof(mline_t))
827 
828 static mline_t cheat_player_arrow[] =
829 {
830 	{{-0.875f, 0.0f}, {1.0f, 0.0f}},    // -----
831 
832 	{{1.0f, 0.0f}, {0.5f,  0.167f}},  // ----->
833 	{{1.0f, 0.0f}, {0.5f, -0.167f}},
834 
835 	{{-0.875f, 0.0f}, {-1.125f,  0.167f}},  // >----->
836 	{{-0.875f, 0.0f}, {-1.125f, -0.167f}},
837 
838 	{{-0.625f, 0.0f}, {-0.875f,  0.167f}},  // >>----->
839 	{{-0.625f, 0.0f}, {-0.875f, -0.167f}},
840 
841 	{{-0.5f, 0.0f}, {-0.5f, -0.167f}},      // >>-d--->
842 	{{-0.5f, -0.167f}, {-0.5f + 0.167f, -0.167f}},
843 	{{-0.5f + 0.167f, -0.167f}, {-0.5f + 0.167f, 0.25f}},
844 
845 	{{-0.167f, 0.0f}, {-0.167f, -0.167f}},  // >>-dd-->
846 	{{-0.167f, -0.167f}, {0.0f, -0.167f}},
847 	{{0.0f, -0.167f}, {0.0f, 0.25f}},
848 
849 	{{0.167f, 0.25f}, {0.167f, -0.143f}},  // >>-ddt->
850 	{{0.167f, -0.143f}, {0.167f + 0.031f, -0.143f - 0.031f}},
851 	{{0.167f + 0.031f, -0.143f - 0.031f}, {0.167f + 0.1f, -0.143f}}
852 };
853 
854 #define NUMCHEATPLYRLINES (sizeof(cheat_player_arrow)/sizeof(mline_t))
855 
856 static mline_t thin_triangle_guy[] =
857 {
858 	{{-0.5f, -0.7f}, {1.0f, 0.0f}},
859 	{{1.0f, 0.0f}, {-0.5f, 0.7f}},
860 	{{-0.5f, 0.7f}, {-0.5f, -0.7f}}
861 };
862 
863 #define NUMTHINTRIANGLEGUYLINES (sizeof(thin_triangle_guy)/sizeof(mline_t))
864 
AM_DrawPlayer(mobj_t * mo)865 static void AM_DrawPlayer(mobj_t *mo)
866 {
867 #if (DEBUG_COLLIDE == 1)
868 	DrawObjectBounds(mo, am_colors[AMCOL_Player]);
869 #endif
870 
871 	if (!netgame)
872 	{
873 		if (cheating)
874 			DrawLineCharacter(cheat_player_arrow, NUMCHEATPLYRLINES,
875 				mo->radius, mo->angle,
876 				am_colors[AMCOL_Player], mo->x, mo->y);
877 		else
878 			DrawLineCharacter(player_arrow, NUMPLYRLINES,
879 				mo->radius, mo->angle,
880 				am_colors[AMCOL_Player], mo->x, mo->y);
881 
882 		return;
883 	}
884 
885 #if 0 //!!!! TEMP DISABLED, NETWORK DEBUGGING
886 	if ((DEATHMATCH() && !singledemo) && mo->player != p)
887 		return;
888 #endif
889 
890 	DrawLineCharacter(player_arrow, NUMPLYRLINES,
891 		mo->radius, mo->angle,
892 		player_colors[mo->player->pnum & 0x07],
893 		mo->x, mo->y);
894 }
895 
896 
AM_WalkThing(mobj_t * mo)897 static void AM_WalkThing(mobj_t *mo)
898 {
899 	int index = AMCOL_Scenery;
900 
901 	if (mo->player && mo->player->mo == mo)
902 	{
903 		AM_DrawPlayer(mo);
904 		return;
905 	}
906 
907 	if (! show_things)
908 		return;
909 
910 	// -AJA- more colourful things
911 	if (mo->flags & MF_SPECIAL)
912 		index = AMCOL_Item;
913 	else if (mo->flags & MF_MISSILE)
914 		index = AMCOL_Missile;
915 	else if (mo->extendedflags & EF_MONSTER && mo->health <= 0)
916 		index = AMCOL_Corpse;
917 	else if (mo->extendedflags & EF_MONSTER)
918 		index = AMCOL_Monster;
919 
920 #if (DEBUG_COLLIDE == 1)
921 	DrawObjectBounds(mo, am_colors[index]);
922 	return;
923 #endif
924 
925 	DrawLineCharacter(
926 		thin_triangle_guy, NUMTHINTRIANGLEGUYLINES,
927 		mo->radius, mo->angle, am_colors[index], mo->x, mo->y);
928 }
929 
930 
931 //
932 // Visit a subsector and draw everything.
933 //
AM_WalkSubsector(unsigned int num)934 static void AM_WalkSubsector(unsigned int num)
935 {
936 	subsector_t *sub = &subsectors[num];
937 
938 	// handle each seg
939 	for (seg_t *seg = sub->segs; seg; seg = seg->sub_next)
940 	{
941 		AM_WalkSeg(seg);
942 	}
943 
944 	// handle each thing
945 	for (mobj_t *mo = sub->thinglist; mo; mo = mo->snext)
946 	{
947 		AM_WalkThing(mo);
948 	}
949 }
950 
951 
952 //
953 // Checks BSP node/subtree bounding box.
954 // Returns true if some part of the bbox might be visible.
955 //
AM_CheckBBox(float * bspcoord)956 static bool AM_CheckBBox(float *bspcoord)
957 {
958 	float xl = bspcoord[BOXLEFT];
959 	float yt = bspcoord[BOXTOP];
960 	float xr = bspcoord[BOXRIGHT];
961 	float yb = bspcoord[BOXBOTTOM];
962 
963 	// TODO: improve this quick'n'dirty hack
964 	if (rotatemap)
965 		return true;
966 
967 	float x1 = CXMTOF(xl);
968 	float x2 = CXMTOF(xr);
969 
970 	float y1 = CYMTOF(yt);
971 	float y2 = CYMTOF(yb);
972 
973 	// some part of bbox is visible?
974 	return HUD_ScissorTest(x1, y1, x2, y2);
975 }
976 
977 
978 //
979 // Walks all subsectors below a given node, traversing subtree
980 // recursively.  Just call with BSP root.
981 //
AM_WalkBSPNode(unsigned int bspnum)982 static void AM_WalkBSPNode(unsigned int bspnum)
983 {
984 	node_t *node;
985 	int side;
986 
987 	// Found a subsector?
988 	if (bspnum & NF_V5_SUBSECTOR)
989 	{
990 		AM_WalkSubsector(bspnum & (~NF_V5_SUBSECTOR));
991 		return;
992 	}
993 
994 	node = &nodes[bspnum];
995 	side = 0;
996 
997 	// Recursively divide right space
998 	if (AM_CheckBBox(node->bbox[0]))
999 		AM_WalkBSPNode(node->children[side]);
1000 
1001 	// Recursively divide back space
1002 	if (AM_CheckBBox(node->bbox[side ^ 1]))
1003 		AM_WalkBSPNode(node->children[side ^ 1]);
1004 }
1005 
1006 
DrawMarks(void)1007 static void DrawMarks(void)
1008 {
1009 	font_c *am_font = automap_style->fonts[0];
1010 
1011 	HUD_SetFont(am_font);
1012 	HUD_SetAlignment(0, 0); // centre the characters
1013 
1014 	char buffer[4];
1015 
1016 	for (int i = 0; i < AM_NUMMARKPOINTS; i++)
1017 	{
1018 		if (markpoints[i].x == NO_MARK_X)
1019 			continue;
1020 
1021 		float mx, my;
1022 
1023 		GetRotatedCoords(markpoints[i].x, markpoints[i].y, &mx, &my);
1024 
1025 		buffer[0] = ('1' + i);
1026 		buffer[1] = 0;
1027 
1028 		HUD_DrawText(CXMTOF(mx), CYMTOF(my), buffer);
1029 	}
1030 
1031 	HUD_SetFont();
1032 	HUD_SetAlignment();
1033 }
1034 
1035 
AM_RenderScene(void)1036 static void AM_RenderScene(void)
1037 {
1038 	HUD_PushScissor(f_x, f_y, f_x+f_w, f_y+f_h, true);
1039 
1040 	// walk the bsp tree
1041 	AM_WalkBSPNode(root_node);
1042 
1043 	HUD_PopScissor();
1044 }
1045 
1046 
AM_Drawer(float x,float y,float w,float h,mobj_t * focus)1047 void AM_Drawer(float x, float y, float w, float h, mobj_t *focus)
1048 {
1049 	f_x = x;
1050 	f_y = y;
1051 	f_w = w;
1052 	f_h = h;
1053 
1054 	f_scale = MAX(f_w, f_h) / map_size / 2.0f;
1055 	f_focus = focus;
1056 
1057 	if (followplayer)
1058 	{
1059 		m_cx = f_focus->x;
1060 		m_cy = f_focus->y;
1061 	}
1062 
1063 	SYS_ASSERT(automap_style);
1064 
1065 	if (grid && !rotatemap)
1066 		DrawGrid();
1067 
1068 	AM_RenderScene();
1069 
1070 	DrawMarks();
1071 }
1072 
1073 
AM_SetColor(int which,rgbcol_t color)1074 void AM_SetColor(int which, rgbcol_t color)
1075 {
1076 	SYS_ASSERT(0 <= which && which < AM_NUM_COLORS);
1077 
1078 	am_colors[which] = color;
1079 }
1080 
1081 
AM_GetState(int * state,float * zoom)1082 void AM_GetState(int *state, float *zoom)
1083 {
1084 	*state = 0;
1085 
1086 	if (grid)
1087 		*state |= AMST_Grid;
1088 
1089 	if (followplayer)
1090 		*state |= AMST_Follow;
1091 
1092 	if (rotatemap)
1093 		*state |= AMST_Rotate;
1094 
1095 	if (show_things)
1096 		*state |= AMST_Things;
1097 
1098 	if (show_walls)
1099 		*state |= AMST_Walls;
1100 
1101 	// nothing required for AMST_Allmap flag (no actual state)
1102 
1103 	*zoom = m_scale;
1104 }
1105 
1106 
AM_SetState(int state,float zoom)1107 void AM_SetState(int state, float zoom)
1108 {
1109 	grid         = (state & AMST_Grid)   ? true : false;
1110 	followplayer = (state & AMST_Follow) ? true : false;
1111 	rotatemap    = (state & AMST_Rotate) ? true : false;
1112 
1113 	show_things  = (state & AMST_Things) ? true : false;
1114 	show_walls   = (state & AMST_Walls)  ? true : false;
1115 	show_allmap  = (state & AMST_Allmap) ? true : false;
1116 
1117 	m_scale = zoom;
1118 }
1119 
1120 //--- editor settings ---
1121 // vi:ts=4:sw=4:noexpandtab
1122