1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 2005-2014 Simon Howard
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 //
16 // DESCRIPTION:  the automap code
17 //
18 
19 
20 #include <stdio.h>
21 
22 #include "deh_main.h"
23 
24 #include "z_zone.h"
25 #include "doomkeys.h"
26 #include "doomdef.h"
27 #include "st_stuff.h"
28 #include "p_local.h"
29 #include "w_wad.h"
30 
31 #include "m_cheat.h"
32 #include "m_controls.h"
33 #include "m_misc.h"
34 #include "i_system.h"
35 #include "i_timer.h"
36 #include "i_video.h"
37 
38 // Needs access to LFB.
39 #include "v_video.h"
40 
41 // State.
42 #include "doomstat.h"
43 #include "r_state.h"
44 
45 // Data.
46 #include "dstrings.h"
47 
48 #include "am_map.h"
49 extern boolean inhelpscreens; // [crispy]
50 
51 
52 // For use if I do walls with outsides/insides
53 #define REDS		(256-5*16)
54 #define REDRANGE	16
55 #define BLUES		(256-4*16+8)
56 #define BLUERANGE	8
57 #define GREENS		(7*16)
58 #define GREENRANGE	16
59 #define GRAYS		(6*16)
60 #define GRAYSRANGE	16
61 #define BROWNS		(4*16)
62 #define BROWNRANGE	16
63 #define YELLOWS		(256-32+7)
64 #define YELLOWRANGE	1
65 #define BLACK		0
66 #define WHITE		(256-47)
67 
68 // Automap colors
69 #define BACKGROUND	BLACK
70 #define YOURCOLORS	WHITE
71 #define YOURRANGE	0
72 #define WALLCOLORS	(crispy->extautomap ? REDS+4 : REDS) // [crispy] slightly darker red
73 #define WALLRANGE	REDRANGE
74 #define TSWALLCOLORS	GRAYS
75 #define TSWALLRANGE	GRAYSRANGE
76 #define FDWALLCOLORS	(crispy->extautomap ? BROWNS+6 : BROWNS) // [crispy] darker brown
77 #define FDWALLRANGE	BROWNRANGE
78 #define CDWALLCOLORS	(crispy->extautomap ? 163 : YELLOWS) // [crispy] golden yellow
79 #define CDWALLRANGE	YELLOWRANGE
80 #define THINGCOLORS	GREENS
81 #define THINGRANGE	GREENRANGE
82 #define SECRETWALLCOLORS	252 // [crispy] purple
83 #define CRISPY_HIGHLIGHT_REVEALED_SECRETS
84 #define REVEALEDSECRETWALLCOLORS	112 // [crispy] green
85 #define SECRETWALLRANGE WALLRANGE
86 #define GRIDCOLORS	(GRAYS + GRAYSRANGE/2)
87 #define GRIDRANGE	0
88 #define XHAIRCOLORS	GRAYS
89 
90 // drawing stuff
91 
92 #define AM_NUMMARKPOINTS 10
93 
94 // scale on entry
95 #define INITSCALEMTOF (.2*FRACUNIT)
96 // how much the automap moves window per tic in frame-buffer coordinates
97 // moves 140 pixels in 1 second
98 #define F_PANINC	4
99 // how much zoom-in per tic
100 // goes to 2x in 1 second
101 #define M_ZOOMIN        ((int) (1.02*FRACUNIT))
102 // how much zoom-out per tic
103 // pulls out to 0.5x in 1 second
104 #define M_ZOOMOUT       ((int) (FRACUNIT/1.02))
105 // [crispy] zoom faster with the mouse wheel
106 #define M2_ZOOMIN       ((int) (1.08*FRACUNIT))
107 #define M2_ZOOMOUT      ((int) (FRACUNIT/1.08))
108 
109 // translates between frame-buffer and map distances
110 // [crispy] fix int overflow that causes map and grid lines to disappear
111 #define FTOM(x) (((int64_t)((x)<<FRACBITS) * scale_ftom) >> FRACBITS)
112 #define MTOF(x) ((((int64_t)(x) * scale_mtof) >> FRACBITS)>>FRACBITS)
113 // translates between frame-buffer and map coordinates
114 #define CXMTOF(x)  (f_x + MTOF((x)-m_x))
115 #define CYMTOF(y)  (f_y + (f_h - MTOF((y)-m_y)))
116 
117 // the following is crap
118 #define LINE_NEVERSEE ML_DONTDRAW
119 
120 typedef struct
121 {
122     int x, y;
123 } fpoint_t;
124 
125 typedef struct
126 {
127     fpoint_t a, b;
128 } fline_t;
129 
130 typedef struct
131 {
132     int64_t		x,y;
133 } mpoint_t;
134 
135 typedef struct
136 {
137     mpoint_t a, b;
138 } mline_t;
139 
140 typedef struct
141 {
142     fixed_t slp, islp;
143 } islope_t;
144 
145 typedef enum
146 {
147     no_key,
148     red_key,
149     yellow_key,
150     blue_key
151 } keycolor_t;
152 
153 
154 //
155 // The vector graphics for the automap.
156 //  A line drawing of the player pointing right,
157 //   starting from the middle.
158 //
159 #define R ((8*PLAYERRADIUS)/7)
160 mline_t player_arrow[] = {
161     { { -R+R/8, 0 }, { R, 0 } }, // -----
162     { { R, 0 }, { R-R/2, R/4 } },  // ----->
163     { { R, 0 }, { R-R/2, -R/4 } },
164     { { -R+R/8, 0 }, { -R-R/8, R/4 } }, // >---->
165     { { -R+R/8, 0 }, { -R-R/8, -R/4 } },
166     { { -R+3*R/8, 0 }, { -R+R/8, R/4 } }, // >>--->
167     { { -R+3*R/8, 0 }, { -R+R/8, -R/4 } }
168 };
169 #undef R
170 
171 #define R ((8*PLAYERRADIUS)/7)
172 mline_t cheat_player_arrow[] = {
173     { { -R+R/8, 0 }, { R, 0 } }, // -----
174     { { R, 0 }, { R-R/2, R/6 } },  // ----->
175     { { R, 0 }, { R-R/2, -R/6 } },
176     { { -R+R/8, 0 }, { -R-R/8, R/6 } }, // >----->
177     { { -R+R/8, 0 }, { -R-R/8, -R/6 } },
178     { { -R+3*R/8, 0 }, { -R+R/8, R/6 } }, // >>----->
179     { { -R+3*R/8, 0 }, { -R+R/8, -R/6 } },
180     { { -R/2, 0 }, { -R/2, -R/6 } }, // >>-d--->
181     { { -R/2, -R/6 }, { -R/2+R/6, -R/6 } },
182     { { -R/2+R/6, -R/6 }, { -R/2+R/6, R/4 } },
183     { { -R/6, 0 }, { -R/6, -R/6 } }, // >>-dd-->
184     { { -R/6, -R/6 }, { 0, -R/6 } },
185     { { 0, -R/6 }, { 0, R/4 } },
186     { { R/6, R/4 }, { R/6, -R/7 } }, // >>-ddt->
187     { { R/6, -R/7 }, { R/6+R/32, -R/7-R/32 } },
188     { { R/6+R/32, -R/7-R/32 }, { R/6+R/10, -R/7 } }
189 };
190 #undef R
191 
192 #define R (FRACUNIT)
193 mline_t triangle_guy[] = {
194     { { (fixed_t)(-.867*R), (fixed_t)(-.5*R) }, { (fixed_t)(.867*R ), (fixed_t)(-.5*R) } },
195     { { (fixed_t)(.867*R ), (fixed_t)(-.5*R) }, { (fixed_t)(0      ), (fixed_t)(R    ) } },
196     { { (fixed_t)(0      ), (fixed_t)(R    ) }, { (fixed_t)(-.867*R), (fixed_t)(-.5*R) } }
197 };
198 #undef R
199 
200 #define R (FRACUNIT)
201 mline_t thintriangle_guy[] = {
202     { { (fixed_t)(-.5*R), (fixed_t)(-.7*R) }, { (fixed_t)(R    ), (fixed_t)(0    ) } },
203     { { (fixed_t)(R    ), (fixed_t)(0    ) }, { (fixed_t)(-.5*R), (fixed_t)(.7*R ) } },
204     { { (fixed_t)(-.5*R), (fixed_t)(.7*R ) }, { (fixed_t)(-.5*R), (fixed_t)(-.7*R) } }
205 };
206 // [crispy] print keys as crosses
207 static mline_t cross_mark[] = {
208     { { -R, 0 }, { R, 0 } },
209     { { 0, -R }, { 0, R } },
210 };
211 static mline_t square_mark[] = {
212     { { -R,  0 }, {  0,  R } },
213     { {  0,  R }, {  R,  0 } },
214     { {  R,  0 }, {  0, -R } },
215     { {  0, -R }, { -R,  0 } },
216 };
217 #undef R
218 
219 
220 
221 
222 static int 	cheating = 0;
223 static int 	grid = 0;
224 
225 static int 	leveljuststarted = 1; 	// kluge until AM_LevelInit() is called
226 
227 boolean    	automapactive = false;
228 //static int 	finit_width = SCREENWIDTH;
229 //static int 	finit_height = SCREENHEIGHT - (ST_HEIGHT << crispy->hires);
230 
231 // location of window on screen
232 static int 	f_x;
233 static int	f_y;
234 
235 // size of window on screen
236 static int 	f_w;
237 static int	f_h;
238 
239 static int 	lightlev; 		// used for funky strobing effect
240 #define fb I_VideoBuffer // [crispy] simplify
241 //static pixel_t*	fb; 			// pseudo-frame buffer
242 static int 	amclock;
243 
244 static mpoint_t m_paninc, m_paninc2; // how far the window pans each tic (map coords)
245 static fixed_t 	mtof_zoommul; // how far the window zooms in each tic (map coords)
246 static fixed_t 	ftom_zoommul; // how far the window zooms in each tic (fb coords)
247 
248 static int64_t 	m_x, m_y;   // LL x,y where the window is on the map (map coords)
249 static int64_t 	m_x2, m_y2; // UR x,y where the window is on the map (map coords)
250 
251 //
252 // width/height of window on map (map coords)
253 //
254 static int64_t 	m_w;
255 static int64_t 	m_h;
256 
257 // based on level size
258 static fixed_t 	min_x;
259 static fixed_t	min_y;
260 static fixed_t 	max_x;
261 static fixed_t  max_y;
262 
263 static fixed_t 	max_w; // max_x-min_x,
264 static fixed_t  max_h; // max_y-min_y
265 
266 // based on player size
267 static fixed_t 	min_w;
268 static fixed_t  min_h;
269 
270 
271 static fixed_t 	min_scale_mtof; // used to tell when to stop zooming out
272 static fixed_t 	max_scale_mtof; // used to tell when to stop zooming in
273 
274 // old stuff for recovery later
275 static int64_t old_m_w, old_m_h;
276 static int64_t old_m_x, old_m_y;
277 
278 // old location used by the Follower routine
279 static mpoint_t f_oldloc;
280 
281 // used by MTOF to scale from map-to-frame-buffer coords
282 static fixed_t scale_mtof = (fixed_t)INITSCALEMTOF;
283 // used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof)
284 static fixed_t scale_ftom;
285 
286 static player_t *plr; // the player represented by an arrow
287 
288 static patch_t *marknums[10]; // numbers used for marking by the automap
289 static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are
290 static int markpointnum = 0; // next point to be assigned
291 
292 static int followplayer = 1; // specifies whether to follow the player around
293 
294 cheatseq_t cheat_amap = CHEAT("iddt", 0);
295 
296 static boolean stopped = true;
297 
298 // [crispy] Antialiased lines from Heretic with more colors
299 #define NUMSHADES 8
300 #define NUMSHADES_BITS 3 // log2(NUMSHADES)
301 static pixel_t color_shades[NUMSHADES * 256];
302 
303 // Forward declare for AM_LevelInit
304 static void AM_drawFline_Vanilla(fline_t* fl, int color);
305 static void AM_drawFline_Smooth(fline_t* fl, int color);
306 // Indirect through this to avoid having to test crispy->smoothmap for every line
307 void (*AM_drawFline)(fline_t*, int) = AM_drawFline_Vanilla;
308 
309 // [crispy] automap rotate mode needs these early on
310 void AM_rotate (int64_t *x, int64_t *y, angle_t a);
311 static void AM_rotatePoint (mpoint_t *pt);
312 static mpoint_t mapcenter;
313 static angle_t mapangle;
314 
315 // Calculates the slope and slope according to the x-axis of a line
316 // segment in map coordinates (with the upright y-axis n' all) so
317 // that it can be used with the brain-dead drawing stuff.
318 
319 void
AM_getIslope(mline_t * ml,islope_t * is)320 AM_getIslope
321 ( mline_t*	ml,
322   islope_t*	is )
323 {
324     int dx, dy;
325 
326     dy = ml->a.y - ml->b.y;
327     dx = ml->b.x - ml->a.x;
328     if (!dy) is->islp = (dx<0?-INT_MAX:INT_MAX);
329     else is->islp = FixedDiv(dx, dy);
330     if (!dx) is->slp = (dy<0?-INT_MAX:INT_MAX);
331     else is->slp = FixedDiv(dy, dx);
332 
333 }
334 
335 //
336 //
337 //
AM_activateNewScale(void)338 void AM_activateNewScale(void)
339 {
340     m_x += m_w/2;
341     m_y += m_h/2;
342     m_w = FTOM(f_w);
343     m_h = FTOM(f_h);
344     m_x -= m_w/2;
345     m_y -= m_h/2;
346     m_x2 = m_x + m_w;
347     m_y2 = m_y + m_h;
348 }
349 
350 //
351 //
352 //
AM_saveScaleAndLoc(void)353 void AM_saveScaleAndLoc(void)
354 {
355     old_m_x = m_x;
356     old_m_y = m_y;
357     old_m_w = m_w;
358     old_m_h = m_h;
359 }
360 
361 //
362 //
363 //
AM_restoreScaleAndLoc(void)364 void AM_restoreScaleAndLoc(void)
365 {
366 
367     m_w = old_m_w;
368     m_h = old_m_h;
369     if (!followplayer)
370     {
371 	m_x = old_m_x;
372 	m_y = old_m_y;
373     } else {
374 	m_x = plr->mo->x - m_w/2;
375 	m_y = plr->mo->y - m_h/2;
376     }
377     m_x2 = m_x + m_w;
378     m_y2 = m_y + m_h;
379 
380     // Change the scaling multipliers
381     scale_mtof = FixedDiv(f_w<<FRACBITS, m_w);
382     scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
383 }
384 
385 //
386 // adds a marker at the current location
387 //
AM_addMark(void)388 void AM_addMark(void)
389 {
390     // [crispy] keep the map static in overlay mode
391     // if not following the player
392     if (!(!followplayer && crispy->automapoverlay))
393     {
394     markpoints[markpointnum].x = m_x + m_w/2;
395     markpoints[markpointnum].y = m_y + m_h/2;
396     }
397     else
398     {
399 	markpoints[markpointnum].x = plr->mo->x;
400 	markpoints[markpointnum].y = plr->mo->y;
401     }
402     markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS;
403 
404 }
405 
406 //
407 // Determines bounding box of all vertices,
408 // sets global variables controlling zoom range.
409 //
AM_findMinMaxBoundaries(void)410 void AM_findMinMaxBoundaries(void)
411 {
412     int i;
413     fixed_t a;
414     fixed_t b;
415 
416     min_x = min_y =  INT_MAX;
417     max_x = max_y = -INT_MAX;
418 
419     for (i=0;i<numvertexes;i++)
420     {
421 	if (vertexes[i].x < min_x)
422 	    min_x = vertexes[i].x;
423 	else if (vertexes[i].x > max_x)
424 	    max_x = vertexes[i].x;
425 
426 	if (vertexes[i].y < min_y)
427 	    min_y = vertexes[i].y;
428 	else if (vertexes[i].y > max_y)
429 	    max_y = vertexes[i].y;
430     }
431 
432     // [crispy] cope with huge level dimensions which span the entire INT range
433     max_w = max_x/2 - min_x/2;
434     max_h = max_y/2 - min_y/2;
435 
436     min_w = 2*PLAYERRADIUS; // const? never changed?
437     min_h = 2*PLAYERRADIUS;
438 
439     a = FixedDiv(f_w<<FRACBITS, max_w);
440     b = FixedDiv(f_h<<FRACBITS, max_h);
441 
442     min_scale_mtof = a < b ? a/2 : b/2;
443     max_scale_mtof = FixedDiv(f_h<<FRACBITS, 2*PLAYERRADIUS);
444 
445 }
446 
447 
448 //
449 //
450 //
AM_changeWindowLoc(void)451 void AM_changeWindowLoc(void)
452 {
453     int64_t incx, incy;
454 
455     if (m_paninc.x || m_paninc.y || m_paninc2.x || m_paninc2.y)
456     {
457 	followplayer = 0;
458 	f_oldloc.x = INT_MAX;
459     }
460 
461     // [crispy] accumulate automap panning by keyboard and mouse
462     incx = m_paninc.x + m_paninc2.x;
463     incy = m_paninc.y + m_paninc2.y;
464     if (crispy->automaprotate)
465     {
466 	AM_rotate(&incx, &incy, -mapangle);
467     }
468     m_x += incx;
469     m_y += incy;
470 
471     if (m_x + m_w/2 > max_x)
472 	m_x = max_x - m_w/2;
473     else if (m_x + m_w/2 < min_x)
474 	m_x = min_x - m_w/2;
475 
476     if (m_y + m_h/2 > max_y)
477 	m_y = max_y - m_h/2;
478     else if (m_y + m_h/2 < min_y)
479 	m_y = min_y - m_h/2;
480 
481     m_x2 = m_x + m_w;
482     m_y2 = m_y + m_h;
483 
484     // [crispy] reset after moving with the mouse
485     m_paninc2.x = m_paninc2.y = 0;
486 }
487 
488 
489 //
490 //
491 //
AM_initVariables(void)492 void AM_initVariables(void)
493 {
494     int pnum;
495     static event_t st_notify = { ev_keyup, AM_MSGENTERED, 0, 0 };
496 
497     automapactive = true;
498 //  fb = I_VideoBuffer; // [crispy] simplify
499 
500     f_oldloc.x = INT_MAX;
501     amclock = 0;
502     lightlev = 0;
503 
504     m_paninc.x = m_paninc.y = m_paninc2.x = m_paninc2.y = 0;
505     ftom_zoommul = FRACUNIT;
506     mtof_zoommul = FRACUNIT;
507 
508     m_w = FTOM(f_w);
509     m_h = FTOM(f_h);
510 
511     // find player to center on initially
512     if (playeringame[consoleplayer])
513     {
514         plr = &players[consoleplayer];
515     }
516     else
517     {
518         plr = &players[0];
519 
520 	for (pnum=0;pnum<MAXPLAYERS;pnum++)
521         {
522 	    if (playeringame[pnum])
523             {
524                 plr = &players[pnum];
525 		break;
526             }
527         }
528     }
529 
530     m_x = plr->mo->x - m_w/2;
531     m_y = plr->mo->y - m_h/2;
532 
533     // [JN] Predefine rotation variables for AM_rotatePoint.
534     mapcenter.x = m_x + m_w / 2;
535     mapcenter.y = m_y + m_h / 2;
536     if (!(!followplayer && crispy->automapoverlay))
537     {
538         mapangle = ANG90 - plr->mo->angle;
539     }
540 
541     AM_changeWindowLoc();
542 
543     // for saving & restoring
544     old_m_x = m_x;
545     old_m_y = m_y;
546     old_m_w = m_w;
547     old_m_h = m_h;
548 
549     // inform the status bar of the change
550     ST_Responder(&st_notify);
551 
552 }
553 
554 //
555 //
556 //
AM_loadPics(void)557 void AM_loadPics(void)
558 {
559     int i;
560     char namebuf[9];
561 
562     for (i=0;i<10;i++)
563     {
564 	DEH_snprintf(namebuf, 9, "AMMNUM%d", i);
565 	marknums[i] = W_CacheLumpName(namebuf, PU_STATIC);
566     }
567 
568 }
569 
AM_unloadPics(void)570 void AM_unloadPics(void)
571 {
572     int i;
573     char namebuf[9];
574 
575     for (i=0;i<10;i++)
576     {
577 	DEH_snprintf(namebuf, 9, "AMMNUM%d", i);
578 	W_ReleaseLumpName(namebuf);
579     }
580 }
581 
AM_clearMarks(void)582 void AM_clearMarks(void)
583 {
584     int i;
585 
586     for (i=0;i<AM_NUMMARKPOINTS;i++)
587 	markpoints[i].x = -1; // means empty
588     markpointnum = 0;
589 }
590 
591 //
592 // should be called at the start of every level
593 // right now, i figure it out myself
594 //
AM_LevelInit(boolean reinit)595 void AM_LevelInit(boolean reinit)
596 {
597     fixed_t a, b;
598     static int f_h_old;
599     // [crispy] Only need to precalculate color lookup tables once
600     static int precalc_once;
601 
602     leveljuststarted = 0;
603 
604     f_x = f_y = 0;
605     f_w = SCREENWIDTH;
606     f_h = SCREENHEIGHT - (ST_HEIGHT << crispy->hires);
607 
608     AM_drawFline = crispy->smoothmap ? AM_drawFline_Smooth : AM_drawFline_Vanilla;
609 
610     if (!reinit)
611     AM_clearMarks();
612 
613     AM_findMinMaxBoundaries();
614     // [crispy] preserve map scale when re-initializing
615     if (reinit && f_h_old)
616     {
617 	scale_mtof = scale_mtof * f_h / f_h_old;
618     }
619     else
620     {
621     // [crispy] initialize zoomlevel on all maps so that a 4096 units
622     // square map would just fit in (MAP01 is 3376x3648 units)
623     a = FixedDiv(f_w, (max_w>>FRACBITS < 2048) ? 2*(max_w>>FRACBITS) : 4096);
624     b = FixedDiv(f_h, (max_h>>FRACBITS < 2048) ? 2*(max_h>>FRACBITS) : 4096);
625     scale_mtof = FixedDiv(a < b ? a : b, (int) (0.7*FRACUNIT));
626     }
627     if (scale_mtof > max_scale_mtof)
628 	scale_mtof = min_scale_mtof;
629     scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
630 
631     f_h_old = f_h;
632 
633     // [crispy] Precalculate color lookup tables for antialised line drawing using COLORMAP
634     if (!precalc_once)
635     {
636         precalc_once = 1;
637         for (int color = 0; color < 256; ++color)
638         {
639 #define REINDEX(I) (color + I * 256)
640             // Pick a range of shades for a steep gradient to keep lines thin
641             int shade_index[NUMSHADES] =
642             {
643                 REINDEX(0), REINDEX(1), REINDEX(2), REINDEX(3), REINDEX(7), REINDEX(15), REINDEX(23), REINDEX(31),
644             };
645 #undef REINDEX
646             for (int shade = 0; shade < NUMSHADES; ++shade)
647             {
648                 color_shades[color * NUMSHADES + shade] = colormaps[shade_index[shade]];
649             }
650         }
651     }
652 }
653 
654 
655 
656 
657 //
658 //
659 //
AM_Stop(void)660 void AM_Stop (void)
661 {
662     static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED, 0 };
663 
664     AM_unloadPics();
665     automapactive = false;
666     ST_Responder(&st_notify);
667     stopped = true;
668 }
669 
670 //
671 //
672 //
673 // [crispy] moved here for extended savegames
674 static int lastlevel = -1, lastepisode = -1;
AM_Start(void)675 void AM_Start (void)
676 {
677     if (!stopped) AM_Stop();
678     stopped = false;
679     if (lastlevel != gamemap || lastepisode != gameepisode)
680     {
681 	AM_LevelInit(false);
682 	lastlevel = gamemap;
683 	lastepisode = gameepisode;
684     }
685     AM_initVariables();
686     AM_loadPics();
687 }
688 
689 // [crispy] reset IDDT cheat when re-starting map during demo recording
AM_ResetIDDTcheat(void)690 void AM_ResetIDDTcheat (void)
691 {
692 	cheating = 0;
693 }
694 
695 //
696 // set the window scale to the maximum size
697 //
AM_minOutWindowScale(void)698 void AM_minOutWindowScale(void)
699 {
700     scale_mtof = min_scale_mtof;
701     scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
702     AM_activateNewScale();
703 }
704 
705 //
706 // set the window scale to the minimum size
707 //
AM_maxOutWindowScale(void)708 void AM_maxOutWindowScale(void)
709 {
710     scale_mtof = max_scale_mtof;
711     scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
712     AM_activateNewScale();
713 }
714 
715 
716 //
717 // Handle events (user inputs) in automap mode
718 //
719 boolean
AM_Responder(event_t * ev)720 AM_Responder
721 ( event_t*	ev )
722 {
723 
724     int rc;
725     static int bigstate=0;
726     static char buffer[20];
727     int key;
728 
729     rc = false;
730 
731     if (ev->type == ev_joystick && joybautomap >= 0
732         && (ev->data1 & (1 << joybautomap)) != 0)
733     {
734         joywait = I_GetTime() + 5;
735 
736         if (!automapactive)
737         {
738             AM_Start ();
739             viewactive = false;
740         }
741         else
742         {
743             bigstate = 0;
744             viewactive = true;
745             AM_Stop ();
746         }
747 
748         return true;
749     }
750 
751     if (!automapactive)
752     {
753 	if (ev->type == ev_keydown && ev->data1 == key_map_toggle)
754 	{
755 	    AM_Start ();
756 	    viewactive = false;
757 	    rc = true;
758 	}
759     }
760     // [crispy] zoom and move Automap with the mouse (wheel)
761     else if (ev->type == ev_mouse && !crispy->automapoverlay && !menuactive && !inhelpscreens)
762     {
763 	if (mousebprevweapon >= 0 && ev->data1 & (1 << mousebprevweapon))
764 	{
765 		mtof_zoommul = M2_ZOOMOUT;
766 		ftom_zoommul = M2_ZOOMIN;
767 		rc = true;
768 	}
769 	else
770 	if (mousebnextweapon >= 0 && ev->data1 & (1 << mousebnextweapon))
771 	{
772 		mtof_zoommul = M2_ZOOMIN;
773 		ftom_zoommul = M2_ZOOMOUT;
774 		rc = true;
775 	}
776 	else
777 	if (!followplayer && (ev->data2 || ev->data3))
778 	{
779 		// [crispy] mouse sensitivity for strafe
780 		m_paninc2.x = FTOM(ev->data2*(mouseSensitivity_x2+5)/80);
781 		m_paninc2.y = FTOM(ev->data3*(mouseSensitivity_x2+5)/80);
782 		rc = true;
783 	}
784     }
785     else if (ev->type == ev_keydown)
786     {
787 	rc = true;
788         key = ev->data1;
789 
790         if (key == key_map_east)          // pan right
791         {
792             // [crispy] keep the map static in overlay mode
793             // if not following the player
794             if (!followplayer && !crispy->automapoverlay) m_paninc.x = crispy->fliplevels ? -FTOM(F_PANINC) : FTOM(F_PANINC);
795             else rc = false;
796         }
797         else if (key == key_map_west)     // pan left
798         {
799             if (!followplayer && !crispy->automapoverlay) m_paninc.x = crispy->fliplevels ? FTOM(F_PANINC) : -FTOM(F_PANINC);
800             else rc = false;
801         }
802         else if (key == key_map_north)    // pan up
803         {
804             if (!followplayer && !crispy->automapoverlay) m_paninc.y = FTOM(F_PANINC);
805             else rc = false;
806         }
807         else if (key == key_map_south)    // pan down
808         {
809             if (!followplayer && !crispy->automapoverlay) m_paninc.y = -FTOM(F_PANINC);
810             else rc = false;
811         }
812         else if (key == key_map_zoomout)  // zoom out
813         {
814             mtof_zoommul = M_ZOOMOUT;
815             ftom_zoommul = M_ZOOMIN;
816         }
817         else if (key == key_map_zoomin)   // zoom in
818         {
819             mtof_zoommul = M_ZOOMIN;
820             ftom_zoommul = M_ZOOMOUT;
821         }
822         else if (key == key_map_toggle)
823         {
824             bigstate = 0;
825             viewactive = true;
826             AM_Stop ();
827         }
828         else if (key == key_map_maxzoom)
829         {
830             bigstate = !bigstate;
831             if (bigstate)
832             {
833                 AM_saveScaleAndLoc();
834                 AM_minOutWindowScale();
835             }
836             else AM_restoreScaleAndLoc();
837         }
838         else if (key == key_map_follow)
839         {
840             followplayer = !followplayer;
841             f_oldloc.x = INT_MAX;
842             if (followplayer)
843                 plr->message = DEH_String(AMSTR_FOLLOWON);
844             else
845                 plr->message = DEH_String(AMSTR_FOLLOWOFF);
846         }
847         else if (key == key_map_grid)
848         {
849             grid = !grid;
850             if (grid)
851                 plr->message = DEH_String(AMSTR_GRIDON);
852             else
853                 plr->message = DEH_String(AMSTR_GRIDOFF);
854         }
855         else if (key == key_map_mark)
856         {
857             M_snprintf(buffer, sizeof(buffer), "%s %d",
858                        DEH_String(AMSTR_MARKEDSPOT), markpointnum);
859             plr->message = buffer;
860             AM_addMark();
861         }
862         else if (key == key_map_clearmark)
863         {
864             AM_clearMarks();
865             plr->message = DEH_String(AMSTR_MARKSCLEARED);
866         }
867         else if (key == key_map_overlay)
868         {
869             // [crispy] force redraw status bar
870             inhelpscreens = true;
871 
872             crispy->automapoverlay = !crispy->automapoverlay;
873             if (crispy->automapoverlay)
874                 plr->message = DEH_String(AMSTR_OVERLAYON);
875             else
876                 plr->message = DEH_String(AMSTR_OVERLAYOFF);
877         }
878         else if (key == key_map_rotate)
879         {
880             crispy->automaprotate = !crispy->automaprotate;
881             if (crispy->automaprotate)
882                 plr->message = DEH_String(AMSTR_ROTATEON);
883             else
884                 plr->message = DEH_String(AMSTR_ROTATEOFF);
885         }
886         else
887         {
888             rc = false;
889         }
890 
891         if ((!deathmatch || gameversion <= exe_doom_1_8)
892          && cht_CheckCheat(&cheat_amap, ev->data2))
893         {
894             rc = false;
895             cheating = (cheating + 1) % 3;
896         }
897     }
898     else if (ev->type == ev_keyup)
899     {
900         rc = false;
901         key = ev->data1;
902 
903         if (key == key_map_east)
904         {
905             if (!followplayer) m_paninc.x = 0;
906         }
907         else if (key == key_map_west)
908         {
909             if (!followplayer) m_paninc.x = 0;
910         }
911         else if (key == key_map_north)
912         {
913             if (!followplayer) m_paninc.y = 0;
914         }
915         else if (key == key_map_south)
916         {
917             if (!followplayer) m_paninc.y = 0;
918         }
919         else if (key == key_map_zoomout || key == key_map_zoomin)
920         {
921             mtof_zoommul = FRACUNIT;
922             ftom_zoommul = FRACUNIT;
923         }
924     }
925 
926     return rc;
927 
928 }
929 
930 
931 //
932 // Zooming
933 //
AM_changeWindowScale(void)934 void AM_changeWindowScale(void)
935 {
936 
937     // Change the scaling multipliers
938     scale_mtof = FixedMul(scale_mtof, mtof_zoommul);
939     scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
940 
941     // [crispy] reset after zooming with the mouse wheel
942     if (ftom_zoommul == M2_ZOOMIN || ftom_zoommul == M2_ZOOMOUT)
943     {
944 	mtof_zoommul = FRACUNIT;
945 	ftom_zoommul = FRACUNIT;
946     }
947 
948     if (scale_mtof < min_scale_mtof)
949 	AM_minOutWindowScale();
950     else if (scale_mtof > max_scale_mtof)
951 	AM_maxOutWindowScale();
952     else
953 	AM_activateNewScale();
954 }
955 
956 
957 //
958 //
959 //
AM_doFollowPlayer(void)960 void AM_doFollowPlayer(void)
961 {
962 
963     if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y)
964     {
965 	// [JN] Use interpolated player coords for smooth
966 	// scrolling and static player arrow position.
967 	m_x = plr->mo->x - m_w/2;
968 	m_y = plr->mo->y - m_h/2;
969 	m_x2 = m_x + m_w;
970 	m_y2 = m_y + m_h;
971 	f_oldloc.x = plr->mo->x;
972 	f_oldloc.y = plr->mo->y;
973 
974 	//  m_x = FTOM(MTOF(plr->mo->x - m_w/2));
975 	//  m_y = FTOM(MTOF(plr->mo->y - m_h/2));
976 	//  m_x = plr->mo->x - m_w/2;
977 	//  m_y = plr->mo->y - m_h/2;
978 
979     }
980 
981 }
982 
983 //
984 //
985 //
AM_updateLightLev(void)986 void AM_updateLightLev(void)
987 {
988     static int nexttic = 0;
989     //static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 };
990     static int litelevels[] = { 0, 4, 7, 10, 12, 14, 15, 15 };
991     static int litelevelscnt = 0;
992 
993     // Change light level
994     if (amclock>nexttic)
995     {
996 	lightlev = litelevels[litelevelscnt++];
997 	if (litelevelscnt == arrlen(litelevels)) litelevelscnt = 0;
998 	nexttic = amclock + 6 - (amclock % 6);
999     }
1000 
1001 }
1002 
1003 
1004 //
1005 // Updates on Game Tick
1006 //
AM_Ticker(void)1007 void AM_Ticker (void)
1008 {
1009 
1010     if (!automapactive)
1011 	return;
1012 
1013     amclock++;
1014 
1015     if (followplayer)
1016 	AM_doFollowPlayer();
1017 
1018     // Change the zoom if necessary
1019     if (ftom_zoommul != FRACUNIT)
1020 	AM_changeWindowScale();
1021 
1022     // Change x,y location
1023     if (m_paninc.x || m_paninc.y || m_paninc2.x || m_paninc2.y)
1024 	AM_changeWindowLoc();
1025 
1026     // Update light level
1027     // AM_updateLightLev();
1028 
1029     // [crispy] required for AM_rotatePoint()
1030     if (crispy->automaprotate)
1031     {
1032 	mapcenter.x = m_x + m_w / 2;
1033 	mapcenter.y = m_y + m_h / 2;
1034 	// [crispy] keep the map static in overlay mode
1035 	// if not following the player
1036 	if (!(!followplayer && crispy->automapoverlay))
1037 	mapangle = ANG90 - plr->mo->angle;
1038     }
1039 }
1040 
1041 
1042 //
1043 // Clear automap frame buffer.
1044 //
AM_clearFB(int color)1045 void AM_clearFB(int color)
1046 {
1047     memset(fb, color, f_w*f_h*sizeof(*fb));
1048 }
1049 
1050 
1051 //
1052 // Automap clipping of lines.
1053 //
1054 // Based on Cohen-Sutherland clipping algorithm but with a slightly
1055 // faster reject and precalculated slopes.  If the speed is needed,
1056 // use a hash algorithm to handle  the common cases.
1057 //
1058 boolean
AM_clipMline(mline_t * ml,fline_t * fl)1059 AM_clipMline
1060 ( mline_t*	ml,
1061   fline_t*	fl )
1062 {
1063     enum
1064     {
1065 	LEFT	=1,
1066 	RIGHT	=2,
1067 	BOTTOM	=4,
1068 	TOP	=8
1069     };
1070 
1071     register int	outcode1 = 0;
1072     register int	outcode2 = 0;
1073     register int	outside;
1074 
1075     fpoint_t	tmp;
1076     int		dx;
1077     int		dy;
1078 
1079 
1080 #define DOOUTCODE(oc, mx, my) \
1081     (oc) = 0; \
1082     if ((my) < 0) (oc) |= TOP; \
1083     else if ((my) >= f_h) (oc) |= BOTTOM; \
1084     if ((mx) < 0) (oc) |= LEFT; \
1085     else if ((mx) >= f_w) (oc) |= RIGHT;
1086 
1087 
1088     // do trivial rejects and outcodes
1089     if (ml->a.y > m_y2)
1090 	outcode1 = TOP;
1091     else if (ml->a.y < m_y)
1092 	outcode1 = BOTTOM;
1093 
1094     if (ml->b.y > m_y2)
1095 	outcode2 = TOP;
1096     else if (ml->b.y < m_y)
1097 	outcode2 = BOTTOM;
1098 
1099     if (outcode1 & outcode2)
1100 	return false; // trivially outside
1101 
1102     if (ml->a.x < m_x)
1103 	outcode1 |= LEFT;
1104     else if (ml->a.x > m_x2)
1105 	outcode1 |= RIGHT;
1106 
1107     if (ml->b.x < m_x)
1108 	outcode2 |= LEFT;
1109     else if (ml->b.x > m_x2)
1110 	outcode2 |= RIGHT;
1111 
1112     if (outcode1 & outcode2)
1113 	return false; // trivially outside
1114 
1115     // transform to frame-buffer coordinates.
1116     fl->a.x = CXMTOF(ml->a.x);
1117     fl->a.y = CYMTOF(ml->a.y);
1118     fl->b.x = CXMTOF(ml->b.x);
1119     fl->b.y = CYMTOF(ml->b.y);
1120 
1121     DOOUTCODE(outcode1, fl->a.x, fl->a.y);
1122     DOOUTCODE(outcode2, fl->b.x, fl->b.y);
1123 
1124     if (outcode1 & outcode2)
1125 	return false;
1126 
1127     while (outcode1 | outcode2)
1128     {
1129 	// may be partially inside box
1130 	// find an outside point
1131 	if (outcode1)
1132 	    outside = outcode1;
1133 	else
1134 	    outside = outcode2;
1135 
1136 	// clip to each side
1137 	if (outside & TOP)
1138 	{
1139 	    dy = fl->a.y - fl->b.y;
1140 	    dx = fl->b.x - fl->a.x;
1141 	    tmp.x = fl->a.x + (dx*(fl->a.y))/dy;
1142 	    tmp.y = 0;
1143 	}
1144 	else if (outside & BOTTOM)
1145 	{
1146 	    dy = fl->a.y - fl->b.y;
1147 	    dx = fl->b.x - fl->a.x;
1148 	    tmp.x = fl->a.x + (dx*(fl->a.y-f_h))/dy;
1149 	    tmp.y = f_h-1;
1150 	}
1151 	else if (outside & RIGHT)
1152 	{
1153 	    dy = fl->b.y - fl->a.y;
1154 	    dx = fl->b.x - fl->a.x;
1155 	    tmp.y = fl->a.y + (dy*(f_w-1 - fl->a.x))/dx;
1156 	    tmp.x = f_w-1;
1157 	}
1158 	else if (outside & LEFT)
1159 	{
1160 	    dy = fl->b.y - fl->a.y;
1161 	    dx = fl->b.x - fl->a.x;
1162 	    tmp.y = fl->a.y + (dy*(-fl->a.x))/dx;
1163 	    tmp.x = 0;
1164 	}
1165         else
1166         {
1167             tmp.x = 0;
1168             tmp.y = 0;
1169         }
1170 
1171 	if (outside == outcode1)
1172 	{
1173 	    fl->a = tmp;
1174 	    DOOUTCODE(outcode1, fl->a.x, fl->a.y);
1175 	}
1176 	else
1177 	{
1178 	    fl->b = tmp;
1179 	    DOOUTCODE(outcode2, fl->b.x, fl->b.y);
1180 	}
1181 
1182 	if (outcode1 & outcode2)
1183 	    return false; // trivially outside
1184     }
1185 
1186     return true;
1187 }
1188 #undef DOOUTCODE
1189 
1190 
1191 //
1192 // Classic Bresenham w/ whatever optimizations needed for speed
1193 //
1194 static void
AM_drawFline_Vanilla(fline_t * fl,int color)1195 AM_drawFline_Vanilla
1196 ( fline_t*	fl,
1197   int		color )
1198 {
1199     register int x;
1200     register int y;
1201     register int dx;
1202     register int dy;
1203     register int sx;
1204     register int sy;
1205     register int ax;
1206     register int ay;
1207     register int d;
1208 
1209     static int fuck = 0;
1210 
1211     // For debugging only
1212     if (      fl->a.x < 0 || fl->a.x >= f_w
1213 	   || fl->a.y < 0 || fl->a.y >= f_h
1214 	   || fl->b.x < 0 || fl->b.x >= f_w
1215 	   || fl->b.y < 0 || fl->b.y >= f_h)
1216     {
1217         DEH_fprintf(stderr, "fuck %d \r", fuck++);
1218 	return;
1219     }
1220 
1221 #define PUTDOT_RAW(xx,yy,cc) fb[(yy)*f_w+(flipscreenwidth[xx])]=(cc)
1222 #ifndef CRISPY_TRUECOLOR
1223 #define PUTDOT(xx,yy,cc) PUTDOT_RAW(xx,yy,cc)
1224 #else
1225 #define PUTDOT(xx,yy,cc) PUTDOT_RAW(xx,yy,(colormaps[(cc)]))
1226 #endif
1227 
1228     dx = fl->b.x - fl->a.x;
1229     ax = 2 * (dx<0 ? -dx : dx);
1230     sx = dx<0 ? -1 : 1;
1231 
1232     dy = fl->b.y - fl->a.y;
1233     ay = 2 * (dy<0 ? -dy : dy);
1234     sy = dy<0 ? -1 : 1;
1235 
1236     x = fl->a.x;
1237     y = fl->a.y;
1238 
1239     if (ax > ay)
1240     {
1241 	d = ay - ax/2;
1242 	while (1)
1243 	{
1244 	    PUTDOT(x,y,color);
1245 	    if (x == fl->b.x) return;
1246 	    if (d>=0)
1247 	    {
1248 		y += sy;
1249 		d -= ax;
1250 	    }
1251 	    x += sx;
1252 	    d += ay;
1253 	}
1254     }
1255     else
1256     {
1257 	d = ax - ay/2;
1258 	while (1)
1259 	{
1260 	    PUTDOT(x, y, color);
1261 	    if (y == fl->b.y) return;
1262 	    if (d >= 0)
1263 	    {
1264 		x += sx;
1265 		d -= ay;
1266 	    }
1267 	    y += sy;
1268 	    d += ax;
1269 	}
1270     }
1271 }
1272 
1273 // [crispy] Adapted from Heretic's DrawWuLine
AM_drawFline_Smooth(fline_t * fl,int color)1274 static void AM_drawFline_Smooth(fline_t* fl, int color)
1275 {
1276     int X0 = fl->a.x, Y0 = fl->a.y, X1 = fl->b.x, Y1 = fl->b.y;
1277     pixel_t* BaseColor = &color_shades[color * NUMSHADES];
1278 
1279     unsigned short IntensityShift, ErrorAdj, ErrorAcc;
1280     unsigned short ErrorAccTemp, Weighting, WeightingComplementMask;
1281     short DeltaX, DeltaY, Temp, XDir;
1282 
1283     /* Make sure the line runs top to bottom */
1284     if (Y0 > Y1)
1285     {
1286         Temp = Y0;
1287         Y0 = Y1;
1288         Y1 = Temp;
1289         Temp = X0;
1290         X0 = X1;
1291         X1 = Temp;
1292     }
1293 
1294     /* Draw the initial pixel, which is always exactly intersected by
1295        the line and so needs no weighting */
1296     /* Always write the raw color value because we've already performed the necessary lookup
1297      * into colormap */
1298     PUTDOT_RAW(X0, Y0, BaseColor[0]);
1299 
1300     if ((DeltaX = X1 - X0) >= 0)
1301     {
1302         XDir = 1;
1303     }
1304     else
1305     {
1306         XDir = -1;
1307         DeltaX = -DeltaX;       /* make DeltaX positive */
1308     }
1309     /* Special-case horizontal, vertical, and diagonal lines, which
1310        require no weighting because they go right through the center of
1311        every pixel */
1312     if ((DeltaY = Y1 - Y0) == 0)
1313     {
1314         /* Horizontal line */
1315         while (DeltaX-- != 0)
1316         {
1317             X0 += XDir;
1318             PUTDOT_RAW(X0, Y0, BaseColor[0]);
1319         }
1320         return;
1321     }
1322     if (DeltaX == 0)
1323     {
1324         /* Vertical line */
1325         do
1326         {
1327             Y0++;
1328             PUTDOT_RAW(X0, Y0, BaseColor[0]);
1329         }
1330         while (--DeltaY != 0);
1331         return;
1332     }
1333     //diagonal line.
1334     if (DeltaX == DeltaY)
1335     {
1336         do
1337         {
1338             X0 += XDir;
1339             Y0++;
1340             PUTDOT_RAW(X0, Y0, BaseColor[0]);
1341         }
1342         while (--DeltaY != 0);
1343         return;
1344     }
1345     /* Line is not horizontal, diagonal, or vertical */
1346     ErrorAcc = 0;               /* initialize the line error accumulator to 0 */
1347     /* # of bits by which to shift ErrorAcc to get intensity level */
1348     IntensityShift = 16 - NUMSHADES_BITS;
1349     /* Mask used to flip all bits in an intensity weighting, producing the
1350        result (1 - intensity weighting) */
1351     WeightingComplementMask = NUMSHADES - 1;
1352     /* Is this an X-major or Y-major line? */
1353     if (DeltaY > DeltaX)
1354     {
1355         /* Y-major line; calculate 16-bit fixed-point fractional part of a
1356            pixel that X advances each time Y advances 1 pixel, truncating the
1357            result so that we won't overrun the endpoint along the X axis */
1358         ErrorAdj = ((unsigned int) DeltaX << 16) / (unsigned int) DeltaY;
1359         /* Draw all pixels other than the first and last */
1360         while (--DeltaY)
1361         {
1362             ErrorAccTemp = ErrorAcc;    /* remember currrent accumulated error */
1363             ErrorAcc += ErrorAdj;       /* calculate error for next pixel */
1364             if (ErrorAcc <= ErrorAccTemp)
1365             {
1366                 /* The error accumulator turned over, so advance the X coord */
1367                 X0 += XDir;
1368             }
1369             Y0++;               /* Y-major, so always advance Y */
1370             /* The IntensityBits most significant bits of ErrorAcc give us the
1371                intensity weighting for this pixel, and the complement of the
1372                weighting for the paired pixel */
1373             Weighting = ErrorAcc >> IntensityShift;
1374             PUTDOT_RAW(X0, Y0, BaseColor[Weighting]);
1375             PUTDOT_RAW(X0 + XDir, Y0, BaseColor[(Weighting ^ WeightingComplementMask)]);
1376         }
1377         /* Draw the final pixel, which is always exactly intersected by the line
1378            and so needs no weighting */
1379         PUTDOT_RAW(X1, Y1, BaseColor[0]);
1380         return;
1381     }
1382     /* It's an X-major line; calculate 16-bit fixed-point fractional part of a
1383        pixel that Y advances each time X advances 1 pixel, truncating the
1384        result to avoid overrunning the endpoint along the X axis */
1385     ErrorAdj = ((unsigned int) DeltaY << 16) / (unsigned int) DeltaX;
1386     /* Draw all pixels other than the first and last */
1387     while (--DeltaX)
1388     {
1389         ErrorAccTemp = ErrorAcc;        /* remember currrent accumulated error */
1390         ErrorAcc += ErrorAdj;   /* calculate error for next pixel */
1391         if (ErrorAcc <= ErrorAccTemp)
1392         {
1393             /* The error accumulator turned over, so advance the Y coord */
1394             Y0++;
1395         }
1396         X0 += XDir;             /* X-major, so always advance X */
1397         /* The IntensityBits most significant bits of ErrorAcc give us the
1398            intensity weighting for this pixel, and the complement of the
1399            weighting for the paired pixel */
1400         Weighting = ErrorAcc >> IntensityShift;
1401         PUTDOT_RAW(X0, Y0, BaseColor[Weighting]);
1402         PUTDOT_RAW(X0, Y0 + 1, BaseColor[(Weighting ^ WeightingComplementMask)]);
1403 
1404     }
1405     /* Draw the final pixel, which is always exactly intersected by the line
1406        and so needs no weighting */
1407     PUTDOT_RAW(X1, Y1, BaseColor[0]);
1408 }
1409 
1410 //
1411 // Clip lines, draw visible part sof lines.
1412 //
1413 void
AM_drawMline(mline_t * ml,int color)1414 AM_drawMline
1415 ( mline_t*	ml,
1416   int		color )
1417 {
1418     static fline_t fl;
1419 
1420     if (AM_clipMline(ml, &fl))
1421 	AM_drawFline(&fl, color); // draws it on frame buffer using fb coords
1422 }
1423 
1424 
1425 
1426 //
1427 // Draws flat (floor/ceiling tile) aligned grid lines.
1428 //
AM_drawGrid(int color)1429 void AM_drawGrid(int color)
1430 {
1431     int64_t x, y;
1432     int64_t start, end;
1433     mline_t ml;
1434 
1435     // Figure out start of vertical gridlines
1436     start = m_x;
1437     if (crispy->automaprotate)
1438     {
1439 	start -= m_h / 2;
1440     }
1441     // [crispy] fix losing grid lines near the automap boundary
1442     if ((start-bmaporgx)%(MAPBLOCKUNITS<<FRACBITS))
1443 	start += // (MAPBLOCKUNITS<<FRACBITS)
1444 	    - ((start-bmaporgx)%(MAPBLOCKUNITS<<FRACBITS));
1445     end = m_x + m_w;
1446     if (crispy->automaprotate)
1447     {
1448 	end += m_h / 2;
1449     }
1450 
1451     // draw vertical gridlines
1452     for (x=start; x<end; x+=(MAPBLOCKUNITS<<FRACBITS))
1453     {
1454 	ml.a.x = x;
1455 	ml.b.x = x;
1456 	// [crispy] moved here
1457 	ml.a.y = m_y;
1458 	ml.b.y = m_y+m_h;
1459 	if (crispy->automaprotate)
1460 	{
1461 	    ml.a.y -= m_w / 2;
1462 	    ml.b.y += m_w / 2;
1463 	    AM_rotatePoint(&ml.a);
1464 	    AM_rotatePoint(&ml.b);
1465 	}
1466 	AM_drawMline(&ml, color);
1467     }
1468 
1469     // Figure out start of horizontal gridlines
1470     start = m_y;
1471     if (crispy->automaprotate)
1472     {
1473 	start -= m_w / 2;
1474     }
1475     // [crispy] fix losing grid lines near the automap boundary
1476     if ((start-bmaporgy)%(MAPBLOCKUNITS<<FRACBITS))
1477 	start += // (MAPBLOCKUNITS<<FRACBITS)
1478 	    - ((start-bmaporgy)%(MAPBLOCKUNITS<<FRACBITS));
1479     end = m_y + m_h;
1480     if (crispy->automaprotate)
1481     {
1482 	end += m_w / 2;
1483     }
1484 
1485     // draw horizontal gridlines
1486     for (y=start; y<end; y+=(MAPBLOCKUNITS<<FRACBITS))
1487     {
1488 	ml.a.y = y;
1489 	ml.b.y = y;
1490 	// [crispy] moved here
1491 	ml.a.x = m_x;
1492 	ml.b.x = m_x + m_w;
1493 	if (crispy->automaprotate)
1494 	{
1495 	    ml.a.x -= m_h / 2;
1496 	    ml.b.x += m_h / 2;
1497 	    AM_rotatePoint(&ml.a);
1498 	    AM_rotatePoint(&ml.b);
1499 	}
1500 	AM_drawMline(&ml, color);
1501     }
1502 
1503 }
1504 
1505 //
1506 // Determines visible lines, draws them.
1507 // This is LineDef based, not LineSeg based.
1508 //
1509 
1510 // [crispy] keyed linedefs (PR, P1, SR, S1)
AM_DoorColor(int type)1511 static keycolor_t AM_DoorColor(int type)
1512 {
1513   if (crispy->extautomap)
1514   {
1515     switch (type)
1516     {
1517 	case 26:
1518 	case 32:
1519 	case 99:
1520 	case 133:
1521 	    return blue_key;
1522 	case 27:
1523 	case 34:
1524 	case 136:
1525 	case 137:
1526 	    return yellow_key;
1527 	case 28:
1528 	case 33:
1529 	case 134:
1530 	case 135:
1531 	    return red_key;
1532     }
1533   }
1534   return no_key;
1535 }
1536 
AM_drawWalls(void)1537 void AM_drawWalls(void)
1538 {
1539     int i;
1540     static mline_t l;
1541 
1542     for (i=0;i<numlines;i++)
1543     {
1544 	l.a.x = lines[i].v1->x;
1545 	l.a.y = lines[i].v1->y;
1546 	l.b.x = lines[i].v2->x;
1547 	l.b.y = lines[i].v2->y;
1548 	if (crispy->automaprotate)
1549 	{
1550 	    AM_rotatePoint(&l.a);
1551 	    AM_rotatePoint(&l.b);
1552 	}
1553 	if (cheating || (lines[i].flags & ML_MAPPED))
1554 	{
1555 	    if ((lines[i].flags & LINE_NEVERSEE) && !cheating)
1556 		continue;
1557 	    {
1558 		// [crispy] draw keyed doors in their respective colors
1559 		// (no Boom multiple keys)
1560 		// make keyed doors flash for easier visibility
1561 		keycolor_t amd;
1562 		if (!(lines[i].flags & ML_SECRET) &&
1563 		    (amd = AM_DoorColor(lines[i].special)) > no_key)
1564 		{
1565 		    switch (amd)
1566 		    {
1567 			case blue_key:
1568 			    AM_drawMline(&l, ((leveltime & 16) ? BLUES : GRIDCOLORS));
1569 			    continue;
1570 			case yellow_key:
1571 			    AM_drawMline(&l, ((leveltime & 16) ? (YELLOWS-2) : GRIDCOLORS));
1572 			    continue;
1573 			case red_key:
1574 			    AM_drawMline(&l, ((leveltime & 16) ? (REDS-2) : GRIDCOLORS));
1575 			    continue;
1576 			default:
1577 			    // [crispy] it should be impossible to reach here
1578 			    break;
1579 		    }
1580 		}
1581 	    }
1582 	    // [crispy] draw exit lines in white (no Boom exit lines 197, 198)
1583 	    // NB: Choco does not have this at all, Boom/PrBoom+ have this disabled by default
1584 	    if (crispy->extautomap && (
1585 	        lines[i].special == 11 ||
1586 	        lines[i].special == 51 ||
1587 	        lines[i].special == 52 ||
1588 	        lines[i].special == 124))
1589 	    {
1590 		AM_drawMline(&l, WHITE);
1591 		continue;
1592 	    }
1593 	    if (!lines[i].backsector)
1594 	    {
1595 		// [crispy] draw 1S secret sector boundaries in purple
1596 		if (crispy->extautomap &&
1597 		    cheating && (lines[i].frontsector->special == 9))
1598 		    AM_drawMline(&l, SECRETWALLCOLORS);
1599 #if defined CRISPY_HIGHLIGHT_REVEALED_SECRETS
1600 		// [crispy] draw revealed secret sector boundaries in green
1601 		else
1602 		if (crispy->extautomap &&
1603 		    crispy->secretmessage && (lines[i].frontsector->oldspecial == 9))
1604 		    AM_drawMline(&l, REVEALEDSECRETWALLCOLORS);
1605 #endif
1606 		else
1607 		AM_drawMline(&l, WALLCOLORS+lightlev);
1608 	    }
1609 	    else
1610 	    {
1611 		// [crispy] draw teleporters in green
1612 		// and also WR teleporters 97 if they are not secret
1613 		// (no monsters-only teleporters 125, 126; no Boom teleporters)
1614 		if (lines[i].special == 39 ||
1615 		    (crispy->extautomap && !(lines[i].flags & ML_SECRET) && lines[i].special == 97))
1616 		{ // teleporters
1617 		    AM_drawMline(&l, crispy->extautomap ? (GREENS+GREENRANGE/2) : (WALLCOLORS+WALLRANGE/2));
1618 		}
1619 		else if (lines[i].flags & ML_SECRET) // secret door
1620 		{
1621 		    // [crispy] NB: Choco has this check, but (SECRETWALLCOLORS == WALLCOLORS)
1622 		    // Boom/PrBoom+ does not have this check at all
1623 		    if (false && cheating) AM_drawMline(&l, SECRETWALLCOLORS + lightlev);
1624 		    else AM_drawMline(&l, WALLCOLORS+lightlev);
1625 		}
1626 #if defined CRISPY_HIGHLIGHT_REVEALED_SECRETS
1627 		// [crispy] draw revealed secret sector boundaries in green
1628 		else if (crispy->extautomap && crispy->secretmessage &&
1629 		    (lines[i].backsector->oldspecial == 9 ||
1630 		    lines[i].frontsector->oldspecial == 9))
1631 		{
1632 		    AM_drawMline(&l, REVEALEDSECRETWALLCOLORS);
1633 		}
1634 #endif
1635 		// [crispy] draw 2S secret sector boundaries in purple
1636 		else if (crispy->extautomap && cheating &&
1637 		    (lines[i].backsector->special == 9 ||
1638 		    lines[i].frontsector->special == 9))
1639 		{
1640 		    AM_drawMline(&l, SECRETWALLCOLORS);
1641 		}
1642 		else if (lines[i].backsector->floorheight
1643 			   != lines[i].frontsector->floorheight) {
1644 		    AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change
1645 		}
1646 		else if (lines[i].backsector->ceilingheight
1647 			   != lines[i].frontsector->ceilingheight) {
1648 		    AM_drawMline(&l, CDWALLCOLORS+lightlev); // ceiling level change
1649 		}
1650 		else if (cheating) {
1651 		    AM_drawMline(&l, TSWALLCOLORS+lightlev);
1652 		}
1653 	    }
1654 	}
1655 	else if (plr->powers[pw_allmap])
1656 	{
1657 	    if (!(lines[i].flags & LINE_NEVERSEE)) AM_drawMline(&l, GRAYS+3);
1658 	}
1659     }
1660 }
1661 
1662 
1663 //
1664 // Rotation in 2D.
1665 // Used to rotate player arrow line character.
1666 //
1667 void
AM_rotate(int64_t * x,int64_t * y,angle_t a)1668 AM_rotate
1669 ( int64_t*	x,
1670   int64_t*	y,
1671   angle_t	a )
1672 {
1673     int64_t tmpx;
1674 
1675     tmpx =
1676 	FixedMul(*x,finecosine[a>>ANGLETOFINESHIFT])
1677 	- FixedMul(*y,finesine[a>>ANGLETOFINESHIFT]);
1678 
1679     *y   =
1680 	FixedMul(*x,finesine[a>>ANGLETOFINESHIFT])
1681 	+ FixedMul(*y,finecosine[a>>ANGLETOFINESHIFT]);
1682 
1683     *x = tmpx;
1684 }
1685 
1686 // [crispy] rotate point around map center
1687 // adapted from prboom-plus/src/am_map.c:898-920
AM_rotatePoint(mpoint_t * pt)1688 static void AM_rotatePoint (mpoint_t *pt)
1689 {
1690     int64_t tmpx;
1691     const angle_t actualangle = ANG90 - viewangle;
1692 
1693     pt->x -= mapcenter.x;
1694     pt->y -= mapcenter.y;
1695 
1696     tmpx = (int64_t)FixedMul(pt->x, finecosine[actualangle>>ANGLETOFINESHIFT])
1697          - (int64_t)FixedMul(pt->y, finesine[actualangle>>ANGLETOFINESHIFT])
1698          + mapcenter.x;
1699 
1700     pt->y = (int64_t)FixedMul(pt->x, finesine[actualangle>>ANGLETOFINESHIFT])
1701           + (int64_t)FixedMul(pt->y, finecosine[actualangle>>ANGLETOFINESHIFT])
1702           + mapcenter.y;
1703 
1704     pt->x = tmpx;
1705 }
1706 
1707 void
AM_drawLineCharacter(mline_t * lineguy,int lineguylines,fixed_t scale,angle_t angle,int color,fixed_t x,fixed_t y)1708 AM_drawLineCharacter
1709 ( mline_t*	lineguy,
1710   int		lineguylines,
1711   fixed_t	scale,
1712   angle_t	angle,
1713   int		color,
1714   fixed_t	x,
1715   fixed_t	y )
1716 {
1717     int		i;
1718     mline_t	l;
1719 
1720     if (crispy->automaprotate)
1721     {
1722 	angle += mapangle;
1723     }
1724 
1725     for (i=0;i<lineguylines;i++)
1726     {
1727 	l.a.x = lineguy[i].a.x;
1728 	l.a.y = lineguy[i].a.y;
1729 
1730 	if (scale)
1731 	{
1732 	    l.a.x = FixedMul(scale, l.a.x);
1733 	    l.a.y = FixedMul(scale, l.a.y);
1734 	}
1735 
1736 	if (angle)
1737 	    AM_rotate(&l.a.x, &l.a.y, angle);
1738 
1739 	l.a.x += x;
1740 	l.a.y += y;
1741 
1742 	l.b.x = lineguy[i].b.x;
1743 	l.b.y = lineguy[i].b.y;
1744 
1745 	if (scale)
1746 	{
1747 	    l.b.x = FixedMul(scale, l.b.x);
1748 	    l.b.y = FixedMul(scale, l.b.y);
1749 	}
1750 
1751 	if (angle)
1752 	    AM_rotate(&l.b.x, &l.b.y, angle);
1753 
1754 	l.b.x += x;
1755 	l.b.y += y;
1756 
1757 	AM_drawMline(&l, color);
1758     }
1759 }
1760 
AM_drawPlayers(void)1761 void AM_drawPlayers(void)
1762 {
1763     int		i;
1764     player_t*	p;
1765     static int 	their_colors[] = { GREENS, GRAYS, BROWNS, REDS };
1766     int		their_color = -1;
1767     int		color;
1768     mpoint_t	pt;
1769 
1770     if (!netgame)
1771     {
1772 	pt.x = plr->mo->x;
1773 	pt.y = plr->mo->y;
1774 	if (crispy->automaprotate)
1775 	{
1776 	    AM_rotatePoint(&pt);
1777 	}
1778 
1779 	if (cheating)
1780 	    AM_drawLineCharacter
1781 		(cheat_player_arrow, arrlen(cheat_player_arrow), 0,
1782 		 plr->mo->angle, WHITE, pt.x, pt.y);
1783 	else
1784 	    AM_drawLineCharacter
1785 		(player_arrow, arrlen(player_arrow), 0, plr->mo->angle,
1786 		 WHITE, pt.x, pt.y);
1787 	return;
1788     }
1789 
1790     for (i=0;i<MAXPLAYERS;i++)
1791     {
1792 	their_color++;
1793 	p = &players[i];
1794 
1795 	if ( (deathmatch && !singledemo) && p != plr)
1796 	    continue;
1797 
1798 	if (!playeringame[i])
1799 	    continue;
1800 
1801 	if (p->powers[pw_invisibility])
1802 	    color = 246; // *close* to black
1803 	else
1804 	    color = their_colors[their_color];
1805 
1806 	pt.x = p->mo->x;
1807 	pt.y = p->mo->y;
1808 	if (crispy->automaprotate)
1809 	{
1810 	    AM_rotatePoint(&pt);
1811 	}
1812 
1813 	AM_drawLineCharacter
1814 	    (player_arrow, arrlen(player_arrow), 0, p->mo->angle,
1815 	     color, pt.x, pt.y);
1816     }
1817 
1818 }
1819 
1820 void
AM_drawThings(int colors,int colorrange)1821 AM_drawThings
1822 ( int	colors,
1823   int 	colorrange)
1824 {
1825     int		i;
1826     mobj_t*	t;
1827     keycolor_t	key;
1828     mpoint_t	pt;
1829 
1830     for (i=0;i<numsectors;i++)
1831     {
1832 	t = sectors[i].thinglist;
1833 	while (t)
1834 	{
1835 	    // [crispy] do not draw an extra triangle for the player
1836 	    if (t == plr->mo)
1837 	    {
1838 		t = t->snext;
1839 		continue;
1840 	    }
1841 
1842 	    pt.x = t->x;
1843 	    pt.y = t->y;
1844 	    if (crispy->automaprotate)
1845 	    {
1846 		AM_rotatePoint(&pt);
1847 	    }
1848 
1849 	  if (crispy->extautomap)
1850 	  {
1851 	    // [crispy] skull keys and key cards
1852 	    switch (t->info->doomednum)
1853 	    {
1854 		case 38:
1855 		case 13:
1856 		    key = red_key;
1857 		    break;
1858 		case 39:
1859 		case 6:
1860 		    key = yellow_key;
1861 		    break;
1862 		case 40:
1863 		case 5:
1864 		    key = blue_key;
1865 		    break;
1866 		default:
1867 		    key = no_key;
1868 		    break;
1869 	    }
1870 
1871 	    // [crispy] draw keys as crosses in their respective colors
1872 	    if (key > no_key)
1873 	    {
1874 	    AM_drawLineCharacter
1875 		(cross_mark, arrlen(cross_mark),
1876 		 16<<FRACBITS, t->angle,
1877 		 (key == red_key) ? REDS :
1878 		 (key == yellow_key) ? YELLOWS :
1879 		 (key == blue_key) ? BLUES :
1880 		 colors+lightlev,
1881 		 pt.x, pt.y);
1882 	    }
1883 	    else
1884 	    // [crispy] draw blood splats and puffs as small squares
1885 	    if (t->type == MT_BLOOD || t->type == MT_PUFF)
1886 	    {
1887 	    AM_drawLineCharacter
1888 		(square_mark, arrlen(square_mark),
1889 		 t->radius >> 2, t->angle,
1890 		 (t->type == MT_BLOOD) ? REDS : GRAYS,
1891 		 pt.x, pt.y);
1892 	    }
1893 	    else
1894 	    {
1895 	    AM_drawLineCharacter
1896 		(thintriangle_guy, arrlen(thintriangle_guy),
1897 		// [crispy] triangle size represents actual thing size
1898 		 t->radius, t->angle,
1899 		// [crispy] show countable kills in red ...
1900 		 ((t->flags & (MF_COUNTKILL | MF_CORPSE)) == MF_COUNTKILL) ? REDS :
1901 		// [crispy] ... show Lost Souls and missiles in orange ...
1902                 (t->flags & (MF_FLOAT | MF_MISSILE)) ? 216 :
1903 		// [crispy] ... show other shootable items in dark gold ...
1904 		 (t->flags & MF_SHOOTABLE) ? 164 :
1905 		// [crispy] ... corpses in gray ...
1906 		 (t->flags & MF_CORPSE) ? GRAYS :
1907 		// [crispy] ... and countable items in yellow
1908 		 (t->flags & MF_COUNTITEM) ? YELLOWS :
1909 		 colors+lightlev,
1910 		 pt.x, pt.y);
1911 	    }
1912 	  }
1913 	  else
1914 	  {
1915 	    AM_drawLineCharacter
1916 		(thintriangle_guy, arrlen(thintriangle_guy),
1917 		 16<<FRACBITS, t->angle, colors+lightlev, pt.x, pt.y);
1918 	  }
1919 	    t = t->snext;
1920 	}
1921     }
1922 }
1923 
AM_drawMarks(void)1924 void AM_drawMarks(void)
1925 {
1926     int i, fx, fy, w, h;
1927     mpoint_t pt;
1928 
1929     for (i=0;i<AM_NUMMARKPOINTS;i++)
1930     {
1931 	if (markpoints[i].x != -1)
1932 	{
1933 	    //      w = SHORT(marknums[i]->width);
1934 	    //      h = SHORT(marknums[i]->height);
1935 	    w = 5; // because something's wrong with the wad, i guess
1936 	    h = 6; // because something's wrong with the wad, i guess
1937 	    // [crispy] center marks around player
1938 	    pt.x = markpoints[i].x;
1939 	    pt.y = markpoints[i].y;
1940 	    if (crispy->automaprotate)
1941 	    {
1942 		AM_rotatePoint(&pt);
1943 	    }
1944 	    fx = (flipscreenwidth[CXMTOF(pt.x)] >> crispy->hires) - 1 - WIDESCREENDELTA;
1945 	    fy = (CYMTOF(pt.y) >> crispy->hires) - 2;
1946 	    if (fx >= f_x && fx <= (f_w >> crispy->hires) - w && fy >= f_y && fy <= (f_h >> crispy->hires) - h)
1947 		V_DrawPatch(fx, fy, marknums[i]);
1948 	}
1949     }
1950 
1951 }
1952 
AM_drawCrosshair(int color)1953 void AM_drawCrosshair(int color)
1954 {
1955     // [crispy] draw an actual crosshair
1956     if (!followplayer)
1957     {
1958 	static fline_t h, v;
1959 
1960 	if (!h.a.x)
1961 	{
1962 	    h.a.x = h.b.x = v.a.x = v.b.x = f_x + f_w / 2;
1963 	    h.a.y = h.b.y = v.a.y = v.b.y = f_y + f_h / 2;
1964 	    h.a.x -= 2; h.b.x += 2;
1965 	    v.a.y -= 2; v.b.y += 2;
1966 	}
1967 
1968 	AM_drawFline(&h, color);
1969 	AM_drawFline(&v, color);
1970     }
1971 // [crispy] do not draw the useless dot on the player arrow
1972 /*
1973     else
1974 #ifndef CRISPY_TRUECOLOR
1975     fb[(f_w*(f_h+1))/2] = color; // single point for now
1976 #else
1977     fb[(f_w*(f_h+1))/2] = colormaps[color]; // single point for now
1978 #endif
1979 */
1980 
1981 }
1982 
AM_Drawer(void)1983 void AM_Drawer (void)
1984 {
1985     if (!automapactive) return;
1986 
1987     if (!crispy->automapoverlay)
1988     AM_clearFB(BACKGROUND);
1989     if (grid)
1990 	AM_drawGrid(GRIDCOLORS);
1991     AM_drawWalls();
1992     AM_drawPlayers();
1993     if (cheating==2)
1994 	AM_drawThings(THINGCOLORS, THINGRANGE);
1995     AM_drawCrosshair(XHAIRCOLORS);
1996 
1997     AM_drawMarks();
1998 
1999     V_MarkRect(f_x, f_y, f_w, f_h);
2000 
2001 }
2002 
2003 // [crispy] extended savegames
AM_GetMarkPoints(int * n,long * p)2004 void AM_GetMarkPoints (int *n, long *p)
2005 {
2006 	int i;
2007 
2008 	*n = markpointnum;
2009 	*p = -1L;
2010 
2011 	// [crispy] prevent saving markpoints from previous map
2012 	if (lastlevel == gamemap && lastepisode == gameepisode)
2013 	{
2014 		for (i = 0; i < AM_NUMMARKPOINTS; i++)
2015 		{
2016 			*p++ = (long)markpoints[i].x;
2017 			*p++ = (markpoints[i].x == -1) ? 0L : (long)markpoints[i].y;
2018 		}
2019 	}
2020 }
2021 
AM_SetMarkPoints(int n,long * p)2022 void AM_SetMarkPoints (int n, long *p)
2023 {
2024 	int i;
2025 
2026 	AM_LevelInit(false);
2027 	lastlevel = gamemap;
2028 	lastepisode = gameepisode;
2029 
2030 	markpointnum = n;
2031 
2032 	for (i = 0; i < AM_NUMMARKPOINTS; i++)
2033 	{
2034 		markpoints[i].x = (int64_t)*p++;
2035 		markpoints[i].y = (int64_t)*p++;
2036 	}
2037 }
2038