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