1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 //
18 // $Log:$
19 //
20 // DESCRIPTION: the automap code
21 //
22 //-----------------------------------------------------------------------------
23
24 #include <stdio.h>
25
26 #include "doomdef.h"
27 #include "templates.h"
28 #include "g_level.h"
29 #include "doomdef.h"
30 #include "st_stuff.h"
31 #include "p_local.h"
32 #include "p_lnspec.h"
33 #include "w_wad.h"
34 #include "a_sharedglobal.h"
35 #include "statnums.h"
36 #include "r_data/r_translate.h"
37 #include "d_event.h"
38 #include "gi.h"
39 #include "p_setup.h"
40 #include "c_bind.h"
41 #include "farchive.h"
42 #include "r_renderer.h"
43 #include "r_sky.h"
44 #include "sbar.h"
45
46 #include "m_cheat.h"
47 #include "i_system.h"
48 #include "c_dispatch.h"
49 #include "colormatcher.h"
50 #include "d_netinf.h"
51
52 // Needs access to LFB.
53 #include "v_video.h"
54 #include "v_palette.h"
55
56 #include "v_text.h"
57
58 // State.
59 #include "doomstat.h"
60 #include "r_state.h"
61
62 // Data.
63 #include "gstrings.h"
64
65 #include "am_map.h"
66 #include "a_artifacts.h"
67 #include "po_man.h"
68 #include "a_keys.h"
69 #include "r_data/colormaps.h"
70
71
72 //=============================================================================
73 //
74 // CVARs
75 //
76 //=============================================================================
77
78 CVAR (Int, am_rotate, 0, CVAR_ARCHIVE);
79 CVAR (Int, am_overlay, 0, CVAR_ARCHIVE);
80 CVAR (Bool, am_showsecrets, true, CVAR_ARCHIVE);
81 CVAR (Bool, am_showmonsters, true, CVAR_ARCHIVE);
82 CVAR (Bool, am_showitems, false, CVAR_ARCHIVE);
83 CVAR (Bool, am_showtime, true, CVAR_ARCHIVE);
84 CVAR (Bool, am_showtotaltime, false, CVAR_ARCHIVE);
85 CVAR (Int, am_colorset, 0, CVAR_ARCHIVE);
86 CVAR (Bool, am_customcolors, true, CVAR_ARCHIVE);
87 CVAR (Int, am_map_secrets, 1, CVAR_ARCHIVE);
88 CVAR (Int, am_drawmapback, 1, CVAR_ARCHIVE);
89 CVAR (Bool, am_showkeys, true, CVAR_ARCHIVE);
90 CVAR (Bool, am_showtriggerlines, false, CVAR_ARCHIVE);
91 CVAR (Int, am_showthingsprites, 0, CVAR_ARCHIVE);
92
93 //=============================================================================
94 //
95 // Automap colors
96 //
97 //=============================================================================
98
99 CVAR (Color, am_backcolor, 0x6c5440, CVAR_ARCHIVE);
100 CVAR (Color, am_yourcolor, 0xfce8d8, CVAR_ARCHIVE);
101 CVAR (Color, am_wallcolor, 0x2c1808, CVAR_ARCHIVE);
102 CVAR (Color, am_secretwallcolor, 0x000000, CVAR_ARCHIVE);
103 CVAR (Color, am_specialwallcolor, 0xffffff, CVAR_ARCHIVE);
104 CVAR (Color, am_tswallcolor, 0x888888, CVAR_ARCHIVE);
105 CVAR (Color, am_fdwallcolor, 0x887058, CVAR_ARCHIVE);
106 CVAR (Color, am_cdwallcolor, 0x4c3820, CVAR_ARCHIVE);
107 CVAR (Color, am_efwallcolor, 0x665555, CVAR_ARCHIVE);
108 CVAR (Color, am_thingcolor, 0xfcfcfc, CVAR_ARCHIVE);
109 CVAR (Color, am_gridcolor, 0x8b5a2b, CVAR_ARCHIVE);
110 CVAR (Color, am_xhaircolor, 0x808080, CVAR_ARCHIVE);
111 CVAR (Color, am_notseencolor, 0x6c6c6c, CVAR_ARCHIVE);
112 CVAR (Color, am_lockedcolor, 0x007800, CVAR_ARCHIVE);
113 CVAR (Color, am_intralevelcolor, 0x0000ff, CVAR_ARCHIVE);
114 CVAR (Color, am_interlevelcolor, 0xff0000, CVAR_ARCHIVE);
115 CVAR (Color, am_secretsectorcolor, 0xff00ff, CVAR_ARCHIVE);
116 CVAR (Color, am_thingcolor_friend, 0xfcfcfc, CVAR_ARCHIVE);
117 CVAR (Color, am_thingcolor_monster, 0xfcfcfc, CVAR_ARCHIVE);
118 CVAR (Color, am_thingcolor_ncmonster, 0xfcfcfc, CVAR_ARCHIVE);
119 CVAR (Color, am_thingcolor_item, 0xfcfcfc, CVAR_ARCHIVE);
120 CVAR (Color, am_thingcolor_citem, 0xfcfcfc, CVAR_ARCHIVE);
121
122 CVAR (Color, am_ovyourcolor, 0xfce8d8, CVAR_ARCHIVE);
123 CVAR (Color, am_ovwallcolor, 0x00ff00, CVAR_ARCHIVE);
124 CVAR (Color, am_ovsecretwallcolor, 0x008844, CVAR_ARCHIVE);
125 CVAR (Color, am_ovspecialwallcolor, 0xffffff, CVAR_ARCHIVE);
126 CVAR (Color, am_ovotherwallscolor, 0x008844, CVAR_ARCHIVE);
127 CVAR (Color, am_ovlockedcolor, 0x008844, CVAR_ARCHIVE);
128 CVAR (Color, am_ovefwallcolor, 0x008844, CVAR_ARCHIVE);
129 CVAR (Color, am_ovfdwallcolor, 0x008844, CVAR_ARCHIVE);
130 CVAR (Color, am_ovcdwallcolor, 0x008844, CVAR_ARCHIVE);
131 CVAR (Color, am_ovunseencolor, 0x00226e, CVAR_ARCHIVE);
132 CVAR (Color, am_ovtelecolor, 0xffff00, CVAR_ARCHIVE);
133 CVAR (Color, am_ovinterlevelcolor, 0xffff00, CVAR_ARCHIVE);
134 CVAR (Color, am_ovsecretsectorcolor,0x00ffff, CVAR_ARCHIVE);
135 CVAR (Color, am_ovthingcolor, 0xe88800, CVAR_ARCHIVE);
136 CVAR (Color, am_ovthingcolor_friend, 0xe88800, CVAR_ARCHIVE);
137 CVAR (Color, am_ovthingcolor_monster, 0xe88800, CVAR_ARCHIVE);
138 CVAR (Color, am_ovthingcolor_ncmonster, 0xe88800, CVAR_ARCHIVE);
139 CVAR (Color, am_ovthingcolor_item, 0xe88800, CVAR_ARCHIVE);
140 CVAR (Color, am_ovthingcolor_citem, 0xe88800, CVAR_ARCHIVE);
141
142 //=============================================================================
143 //
144 // internal representation of a single color
145 //
146 //=============================================================================
147
148 struct AMColor
149 {
150 int Index;
151 uint32 RGB;
152
FromCVarAMColor153 void FromCVar(FColorCVar & cv)
154 {
155 Index = cv.GetIndex();
156 RGB = uint32(cv) | MAKEARGB(255, 0, 0, 0);
157 }
158
FromRGBAMColor159 void FromRGB(int r,int g, int b)
160 {
161 RGB = MAKEARGB(255, r, g, b);
162 Index = ColorMatcher.Pick(r, g, b);
163 }
164
setInvalidAMColor165 void setInvalid()
166 {
167 Index = -1;
168 RGB = -1;
169 }
170
isValidAMColor171 bool isValid() const
172 {
173 return Index > -1;
174 }
175 };
176
177 //=============================================================================
178 //
179 // a complete color set
180 //
181 //=============================================================================
182
183 static const char *ColorNames[] = {
184 "Background",
185 "YourColor",
186 "WallColor",
187 "TwoSidedWallColor",
188 "FloorDiffWallColor",
189 "CeilingDiffWallColor",
190 "ExtraFloorWallColor",
191 "ThingColor",
192 "ThingColor_Item",
193 "ThingColor_CountItem",
194 "ThingColor_Monster",
195 "ThingColor_NocountMonster",
196 "ThingColor_Friend",
197 "SpecialWallColor",
198 "SecretWallColor",
199 "GridColor",
200 "XHairColor",
201 "NotSeenColor",
202 "LockedColor",
203 "IntraTeleportColor",
204 "InterTeleportColor",
205 "SecretSectorColor",
206 "AlmostBackgroundColor",
207 NULL
208 };
209
210 struct AMColorset
211 {
212 enum
213 {
214 Background,
215 YourColor,
216 WallColor,
217 TSWallColor,
218 FDWallColor,
219 CDWallColor,
220 EFWallColor,
221 ThingColor,
222 ThingColor_Item,
223 ThingColor_CountItem,
224 ThingColor_Monster,
225 ThingColor_NocountMonster,
226 ThingColor_Friend,
227 SpecialWallColor,
228 SecretWallColor,
229 GridColor,
230 XHairColor,
231 NotSeenColor,
232 LockedColor,
233 IntraTeleportColor,
234 InterTeleportColor,
235 SecretSectorColor,
236 AlmostBackgroundColor,
237 AM_NUM_COLORS
238 };
239
240 AMColor c[AM_NUM_COLORS];
241 bool displayLocks;
242 bool forcebackground;
243 bool defined; // only for mod specific colorsets: must be true to be usable
244
initFromCVarsAMColorset245 void initFromCVars(FColorCVar **values)
246 {
247 for(int i=0;i<AlmostBackgroundColor; i++)
248 {
249 c[i].FromCVar(*values[i]);
250 }
251
252 DWORD ba = *(values[0]);
253
254 int r = RPART(ba) - 16;
255 int g = GPART(ba) - 16;
256 int b = BPART(ba) - 16;
257
258 if (r < 0)
259 r += 32;
260 if (g < 0)
261 g += 32;
262 if (b < 0)
263 b += 32;
264
265 c[AlmostBackgroundColor].FromRGB(r, g, b);
266 displayLocks = true;
267 forcebackground = false;
268 }
269
initFromColorsAMColorset270 void initFromColors(const unsigned char *colors, bool showlocks)
271 {
272 for(int i=0, j=0; i<AM_NUM_COLORS; i++, j+=3)
273 {
274 if (colors[j] == 1 && colors[j+1] == 0 && colors[j+2] == 0)
275 {
276 c[i].setInvalid();
277 }
278 else
279 {
280 c[i].FromRGB(colors[j], colors[j+1], colors[j+2]);
281 }
282 }
283 displayLocks = showlocks;
284 forcebackground = false;
285 }
286
setWhiteAMColorset287 void setWhite()
288 {
289 c[0].FromRGB(0,0,0);
290 for(int i=1; i<AM_NUM_COLORS; i++)
291 {
292 c[i].FromRGB(255,255,255);
293 }
294 }
295
operator []AMColorset296 const AMColor &operator[](int index) const
297 {
298 return c[index];
299 }
300
isValidAMColorset301 bool isValid(int index) const
302 {
303 return c[index].isValid();
304 }
305 };
306
307 //=============================================================================
308 //
309 // predefined colorsets
310 //
311 //=============================================================================
312
313 static FColorCVar *cv_standard[] = {
314 &am_backcolor,
315 &am_yourcolor,
316 &am_wallcolor,
317 &am_tswallcolor,
318 &am_fdwallcolor,
319 &am_cdwallcolor,
320 &am_efwallcolor,
321 &am_thingcolor,
322 &am_thingcolor_item,
323 &am_thingcolor_citem,
324 &am_thingcolor_monster,
325 &am_thingcolor_ncmonster,
326 &am_thingcolor_friend,
327 &am_specialwallcolor,
328 &am_secretwallcolor,
329 &am_gridcolor,
330 &am_xhaircolor,
331 &am_notseencolor,
332 &am_lockedcolor,
333 &am_intralevelcolor,
334 &am_interlevelcolor,
335 &am_secretsectorcolor
336 };
337
338 static FColorCVar *cv_overlay[] = {
339 &am_backcolor, // this will not be used in overlay mode
340 &am_ovyourcolor,
341 &am_ovwallcolor,
342 &am_ovotherwallscolor,
343 &am_ovfdwallcolor,
344 &am_ovcdwallcolor,
345 &am_ovefwallcolor,
346 &am_ovthingcolor,
347 &am_ovthingcolor_item,
348 &am_ovthingcolor_citem,
349 &am_ovthingcolor_monster,
350 &am_ovthingcolor_ncmonster,
351 &am_ovthingcolor_friend,
352 &am_ovspecialwallcolor,
353 &am_ovsecretwallcolor,
354 &am_gridcolor, // this will not be used in overlay mode
355 &am_xhaircolor, // this will not be used in overlay mode
356 &am_ovunseencolor,
357 &am_ovlockedcolor,
358 &am_ovtelecolor,
359 &am_ovinterlevelcolor,
360 &am_ovsecretsectorcolor
361 };
362
CCMD(am_restorecolors)363 CCMD(am_restorecolors)
364 {
365 for (unsigned i = 0; i < countof(cv_standard); i++)
366 {
367 cv_standard[i]->ResetToDefault();
368 }
369 for (unsigned i = 0; i < countof(cv_overlay); i++)
370 {
371 cv_overlay[i]->ResetToDefault();
372 }
373 }
374
375
376
377 #define NOT_USED 1,0,0 // use almost black as indicator for an unused color
378
379 static unsigned char DoomColors[]= {
380 0x00,0x00,0x00, // background
381 0xff,0xff,0xff, // yourcolor
382 0xfc,0x00,0x00, // wallcolor
383 0x80,0x80,0x80, // tswallcolor
384 0xbc,0x78,0x48, // fdwallcolor
385 0xfc,0xfc,0x00, // cdwallcolor
386 0xbc,0x78,0x48, // efwallcolor
387 0x74,0xfc,0x6c, // thingcolor
388 0x74,0xfc,0x6c, // thingcolor_item
389 0x74,0xfc,0x6c, // thingcolor_citem
390 0x74,0xfc,0x6c, // thingcolor_monster
391 0x74,0xfc,0x6c, // thingcolor_ncmonster
392 0x74,0xfc,0x6c, // thingcolor_friend
393 NOT_USED, // specialwallcolor
394 NOT_USED, // secretwallcolor
395 0x4c,0x4c,0x4c, // gridcolor
396 0x80,0x80,0x80, // xhaircolor
397 0x6c,0x6c,0x6c, // notseencolor
398 0xfc,0xfc,0x00, // lockedcolor
399 NOT_USED, // intrateleport
400 NOT_USED, // interteleport
401 NOT_USED, // secretsector
402 0x10,0x10,0x10, // almostbackground
403 };
404
405 static unsigned char StrifeColors[]= {
406 0x00,0x00,0x00, // background
407 239, 239, 0, // yourcolor
408 199, 195, 195, // wallcolor
409 119, 115, 115, // tswallcolor
410 55, 59, 91, // fdwallcolor
411 119, 115, 115, // cdwallcolor
412 55, 59, 91, // efwallcolor
413 187, 59, 0, // thingcolor
414 219, 171, 0, // thingcolor_item
415 219, 171, 0, // thingcolor_citem
416 0xfc,0x00,0x00, // thingcolor_monster
417 0xfc,0x00,0x00, // thingcolor_ncmonster
418 0xfc,0x00,0x00, // thingcolor_friend
419 NOT_USED, // specialwallcolor
420 NOT_USED, // secretwallcolor
421 0x4c,0x4c,0x4c, // gridcolor
422 0x80,0x80,0x80, // xhaircolor
423 0x6c,0x6c,0x6c, // notseencolor
424 119, 115, 115, // lockedcolor
425 NOT_USED, // intrateleport
426 NOT_USED, // interteleport
427 NOT_USED, // secretsector
428 0x10,0x10,0x10, // almostbackground
429 };
430
431 static unsigned char RavenColors[]= {
432 0x6c,0x54,0x40, // background
433 0xff,0xff,0xff, // yourcolor
434 75, 50, 16, // wallcolor
435 88, 93, 86, // tswallcolor
436 208, 176, 133, // fdwallcolor
437 103, 59, 31, // cdwallcolor
438 208, 176, 133, // efwallcolor
439 236, 236, 236, // thingcolor
440 236, 236, 236, // thingcolor_item
441 236, 236, 236, // thingcolor_citem
442 236, 236, 236, // thingcolor_monster
443 236, 236, 236, // thingcolor_ncmonster
444 236, 236, 236, // thingcolor_friend
445 NOT_USED, // specialwallcolor
446 NOT_USED, // secretwallcolor
447 75, 50, 16, // gridcolor
448 0x00,0x00,0x00, // xhaircolor
449 0x00,0x00,0x00, // notseencolor
450 103, 59, 31, // lockedcolor
451 NOT_USED, // intrateleport
452 NOT_USED, // interteleport
453 NOT_USED, // secretsector
454 0x10,0x10,0x10, // almostbackground
455 };
456
457 #undef NOT_USED
458
459 static AMColorset AMColors;
460 static AMColorset AMMod;
461 static AMColorset AMModOverlay;
462
463
464 //=============================================================================
465 //
466 //
467 //
468 //=============================================================================
469
ParseAMColors(bool overlay)470 void FMapInfoParser::ParseAMColors(bool overlay)
471 {
472 bool colorset = false;
473
474 AMColorset &cset = overlay? AMModOverlay : AMMod;
475
476 cset.setWhite();
477 cset.defined = true;
478 sc.MustGetToken('{');
479 while(sc.GetToken())
480 {
481 if (sc.TokenType == '}') return;
482
483 sc.TokenMustBe(TK_Identifier);
484 FString nextKey = sc.String;
485 sc.MustGetToken('=');
486
487 if (nextKey.CompareNoCase("base") == 0)
488 {
489 if (colorset) sc.ScriptError("'base' must be specified before the first color");
490 sc.MustGetToken(TK_StringConst);
491 if (sc.Compare("doom"))
492 {
493 cset.initFromColors(DoomColors, false);
494 }
495 else if (sc.Compare("raven"))
496 {
497 cset.initFromColors(RavenColors, true);
498 }
499 else if (sc.Compare("strife"))
500 {
501 cset.initFromColors(StrifeColors, false);
502 }
503 else
504 {
505 sc.ScriptError("Unknown value for 'base'. Must be 'Doom', 'Strife' or 'Raven'.");
506 }
507 }
508 else if (nextKey.CompareNoCase("showlocks") == 0)
509 {
510 if(sc.CheckToken(TK_False))
511 cset.displayLocks = false;
512 else
513 {
514 sc.MustGetToken(TK_True);
515 cset.displayLocks = true;
516 }
517 }
518 else
519 {
520 int i;
521 for (i = 0; ColorNames[i] != NULL; i++)
522 {
523 if (nextKey.CompareNoCase(ColorNames[i]) == 0)
524 {
525 sc.MustGetToken(TK_StringConst);
526 FString color = sc.String;
527 FString colorName = V_GetColorStringByName(color);
528 if(!colorName.IsEmpty()) color = colorName;
529 int colorval = V_GetColorFromString(NULL, color);
530 cset.c[i].FromRGB(RPART(colorval), GPART(colorval), BPART(colorval));
531 colorset = true;
532 break;
533 }
534 }
535 if (ColorNames[i]== NULL)
536 {
537 sc.ScriptError("Unknown key '%s'", nextKey.GetChars());
538 }
539 }
540 }
541 }
542
543 //=============================================================================
544 //
545 //
546 //
547 //=============================================================================
548
549 #define MAPBITS 12
550 #define MapDiv SafeDivScale12
551 #define MapMul MulScale12
552 #define MAPUNIT (1<<MAPBITS)
553 #define FRACTOMAPBITS (FRACBITS-MAPBITS)
554
555 // scale on entry
556 #define INITSCALEMTOF (.2*MAPUNIT)
557 // used by MTOF to scale from map-to-frame-buffer coords
558 static fixed_t scale_mtof = (fixed_t)INITSCALEMTOF;
559 // used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof)
560 static fixed_t scale_ftom;
561
562 // translates between frame-buffer and map distances
FTOM(fixed_t x)563 inline fixed_t FTOM(fixed_t x)
564 {
565 return x * scale_ftom;
566 }
567
MTOF(fixed_t x)568 inline fixed_t MTOF(fixed_t x)
569 {
570 return MulScale24 (x, scale_mtof);
571 }
572
573
574
575 static int bigstate = 0;
576 static bool textured = 1; // internal toggle for texture mode
577
CUSTOM_CVAR(Bool,am_textured,false,CVAR_ARCHIVE)578 CUSTOM_CVAR(Bool, am_textured, false, CVAR_ARCHIVE)
579 {
580 textured |= self;
581 }
582
583 CVAR(Int, am_showsubsector, -1, 0);
584
585
586 // Disable the ML_DONTDRAW line flag if x% of all lines in a map are flagged with it
587 // (To counter annoying mappers who think they are smart by making the automap unusable)
588 bool am_showallenabled;
589
590 CUSTOM_CVAR (Int, am_showalllines, -1, 0) // This is a cheat so don't save it.
591 {
592 int flagged = 0;
593 int total = 0;
594 if (self > 0 && numlines > 0)
595 {
596 for(int i=0;i<numlines;i++)
597 {
598 line_t *line = &lines[i];
599
600 // disregard intra-sector lines
601 if (line->frontsector == line->backsector) continue;
602
603 // disregard control sectors for deep water
604 if (line->frontsector->e->FakeFloor.Sectors.Size() > 0) continue;
605
606 // disregard control sectors for 3D-floors
607 if (line->frontsector->e->XFloor.attached.Size() > 0) continue;
608
609 total++;
610 if (line->flags & ML_DONTDRAW) flagged++;
611 }
612 am_showallenabled = (flagged * 100 / total >= self);
613 }
614 else if (self == 0)
615 {
616 am_showallenabled = true;
617 }
618 else
619 {
620 am_showallenabled = false;
621 }
622 }
623
EXTERN_CVAR(Bool,sv_cheats)624 EXTERN_CVAR (Bool, sv_cheats)
625 CUSTOM_CVAR (Int, am_cheat, 0, 0)
626 {
627 // No automap cheat in net games when cheats are disabled!
628 if (netgame && !sv_cheats && self != 0)
629 {
630 self = 0;
631 }
632 }
633
634
635
636 #define AM_NUMMARKPOINTS 10
637
638 // player radius for automap checking
639 #define PLAYERRADIUS 16*MAPUNIT
640
641 // how much the automap moves window per tic in frame-buffer coordinates
642 // moves 140 pixels at 320x200 in 1 second
643 #define F_PANINC (140/TICRATE)
644 // how much zoom-in per tic
645 // goes to 2x in 1 second
646 #define M_ZOOMIN (1.02*MAPUNIT)
647 // how much zoom-out per tic
648 // pulls out to 0.5x in 1 second
649 #define M_ZOOMOUT (MAPUNIT/1.02)
650
651 // translates between frame-buffer and map coordinates
652 #define CXMTOF(x) (MTOF((x)-m_x)/* - f_x*/)
653 #define CYMTOF(y) (f_h - MTOF((y)-m_y)/* + f_y*/)
654
655 struct fpoint_t
656 {
657 int x, y;
658 };
659
660 struct fline_t
661 {
662 fpoint_t a, b;
663 };
664
665 struct mpoint_t
666 {
667 fixed_t x,y;
668 };
669
670 struct mline_t
671 {
672 mpoint_t a, b;
673 };
674
675 struct islope_t
676 {
677 fixed_t slp, islp;
678 };
679
680
681
682 //=============================================================================
683 //
684 // The vector graphics for the automap.
685 // A line drawing of the player pointing right,
686 // starting from the middle.
687 //
688 //=============================================================================
689
690 static TArray<mline_t> MapArrow;
691 static TArray<mline_t> CheatMapArrow;
692 static TArray<mline_t> CheatKey;
693 static TArray<mline_t> EasyKey;
694
695 #define R (MAPUNIT)
696 // [RH] Avoid lots of warnings without compiler-specific #pragmas
697 #define L(a,b,c,d) { {(fixed_t)((a)*R),(fixed_t)((b)*R)}, {(fixed_t)((c)*R),(fixed_t)((d)*R)} }
698 static mline_t triangle_guy[] = {
699 L (-.867,-.5, .867,-.5),
700 L (.867,-.5, 0,1),
701 L (0,1, -.867,-.5)
702 };
703 #define NUMTRIANGLEGUYLINES (sizeof(triangle_guy)/sizeof(mline_t))
704
705 static mline_t thintriangle_guy[] = {
706 L (-.5,-.7, 1,0),
707 L (1,0, -.5,.7),
708 L (-.5,.7, -.5,-.7)
709 };
710 #define NUMTHINTRIANGLEGUYLINES (sizeof(thintriangle_guy)/sizeof(mline_t))
711
712 static mline_t square_guy[] = {
713 L (0,1,1,0),
714 L (1,0,0,-1),
715 L (0,-1,-1,0),
716 L (-1,0,0,1)
717 };
718 #define NUMSQUAREGUYLINES (sizeof(square_guy)/sizeof(mline_t))
719
720 #undef R
721
722
723
724 //=============================================================================
725 //
726 //
727 //
728 //=============================================================================
729
730 static int grid = 0;
731
732 bool automapactive = false;
733
734 // location of window on screen
735 static int f_x;
736 static int f_y;
737
738 // size of window on screen
739 static int f_w;
740 static int f_h;
741 static int f_p; // [RH] # of bytes from start of a line to start of next
742
743 static int amclock;
744
745 static mpoint_t m_paninc; // how far the window pans each tic (map coords)
746 static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords)
747 static float am_zoomdir;
748
749 static fixed_t m_x, m_y; // LL x,y where the window is on the map (map coords)
750 static fixed_t m_x2, m_y2; // UR x,y where the window is on the map (map coords)
751
752 //
753 // width/height of window on map (map coords)
754 //
755 static fixed_t m_w;
756 static fixed_t m_h;
757
758 // based on level size
759 static fixed_t min_x, min_y, max_x, max_y;
760
761 static fixed_t max_w; // max_x-min_x,
762 static fixed_t max_h; // max_y-min_y
763
764 // based on player size
765 static fixed_t min_w;
766 static fixed_t min_h;
767
768
769 static fixed_t min_scale_mtof; // used to tell when to stop zooming out
770 static fixed_t max_scale_mtof; // used to tell when to stop zooming in
771
772 // old stuff for recovery later
773 static fixed_t old_m_w, old_m_h;
774 static fixed_t old_m_x, old_m_y;
775
776 // old location used by the Follower routine
777 static mpoint_t f_oldloc;
778
779 static FTextureID marknums[10]; // numbers used for marking by the automap
780 static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are
781 static int markpointnum = 0; // next point to be assigned
782
783 static FTextureID mapback; // the automap background
784 static fixed_t mapystart=0; // y-value for the start of the map bitmap...used in the parallax stuff.
785 static fixed_t mapxstart=0; //x-value for the bitmap.
786
787 static bool stopped = true;
788
789 static void AM_calcMinMaxMtoF();
790
791 static void DrawMarker (FTexture *tex, fixed_t x, fixed_t y, int yadjust,
792 INTBOOL flip, fixed_t xscale, fixed_t yscale, int translation, fixed_t alpha, DWORD fillcolor, FRenderStyle renderstyle);
793
794 void AM_rotatePoint (fixed_t *x, fixed_t *y);
795 void AM_rotate (fixed_t *x, fixed_t *y, angle_t an);
796 void AM_doFollowPlayer ();
797
798
799 //=============================================================================
800 //
801 // map functions
802 //
803 //=============================================================================
804 bool AM_addMark ();
805 bool AM_clearMarks ();
806 void AM_saveScaleAndLoc ();
807 void AM_restoreScaleAndLoc ();
808 void AM_minOutWindowScale ();
809
810
CVAR(Bool,am_followplayer,true,CVAR_ARCHIVE)811 CVAR(Bool, am_followplayer, true, CVAR_ARCHIVE)
812
813
814 CCMD(am_togglefollow)
815 {
816 am_followplayer = !am_followplayer;
817 f_oldloc.x = FIXED_MAX;
818 Printf ("%s\n", GStrings(am_followplayer ? "AMSTR_FOLLOWON" : "AMSTR_FOLLOWOFF"));
819 }
820
CCMD(am_togglegrid)821 CCMD(am_togglegrid)
822 {
823 grid = !grid;
824 Printf ("%s\n", GStrings(grid ? "AMSTR_GRIDON" : "AMSTR_GRIDOFF"));
825 }
826
CCMD(am_toggletexture)827 CCMD(am_toggletexture)
828 {
829 if (am_textured && hasglnodes)
830 {
831 textured = !textured;
832 Printf ("%s\n", GStrings(textured ? "AMSTR_TEXON" : "AMSTR_TEXOFF"));
833 }
834 }
835
CCMD(am_setmark)836 CCMD(am_setmark)
837 {
838 if (AM_addMark())
839 {
840 Printf ("%s %d\n", GStrings("AMSTR_MARKEDSPOT"), markpointnum);
841 }
842 }
843
CCMD(am_clearmarks)844 CCMD(am_clearmarks)
845 {
846 if (AM_clearMarks())
847 {
848 Printf ("%s\n", GStrings("AMSTR_MARKSCLEARED"));
849 }
850 }
851
CCMD(am_gobig)852 CCMD(am_gobig)
853 {
854 bigstate = !bigstate;
855 if (bigstate)
856 {
857 AM_saveScaleAndLoc();
858 AM_minOutWindowScale();
859 }
860 else
861 AM_restoreScaleAndLoc();
862 }
863
864 // Calculates the slope and slope according to the x-axis of a line
865 // segment in map coordinates (with the upright y-axis n' all) so
866 // that it can be used with the brain-dead drawing stuff.
867
868 // Ripped out for Heretic
869 /*
870 void AM_getIslope (mline_t *ml, islope_t *is)
871 {
872 int dx, dy;
873
874 dy = ml->a.y - ml->b.y;
875 dx = ml->b.x - ml->a.x;
876 if (!dy) is->islp = (dx<0?-MAXINT:MAXINT);
877 else is->islp = FixedDiv(dx, dy);
878 if (!dx) is->slp = (dy<0?-MAXINT:MAXINT);
879 else is->slp = FixedDiv(dy, dx);
880 }
881 */
882
883
AM_ParseArrow(TArray<mline_t> & Arrow,const char * lumpname)884 void AM_ParseArrow(TArray<mline_t> &Arrow, const char *lumpname)
885 {
886 const int R = ((8*PLAYERRADIUS)/7);
887 FScanner sc;
888 int lump = Wads.CheckNumForFullName(lumpname, true);
889 if (lump >= 0)
890 {
891 sc.OpenLumpNum(lump);
892 sc.SetCMode(true);
893 while (sc.GetToken())
894 {
895 mline_t line;
896 sc.TokenMustBe('(');
897 sc.MustGetFloat();
898 line.a.x = xs_RoundToInt(sc.Float*R);
899 sc.MustGetToken(',');
900 sc.MustGetFloat();
901 line.a.y = xs_RoundToInt(sc.Float*R);
902 sc.MustGetToken(')');
903 sc.MustGetToken(',');
904 sc.MustGetToken('(');
905 sc.MustGetFloat();
906 line.b.x = xs_RoundToInt(sc.Float*R);
907 sc.MustGetToken(',');
908 sc.MustGetFloat();
909 line.b.y = xs_RoundToInt(sc.Float*R);
910 sc.MustGetToken(')');
911 Arrow.Push(line);
912 }
913 }
914 }
915
AM_StaticInit()916 void AM_StaticInit()
917 {
918 MapArrow.Clear();
919 CheatMapArrow.Clear();
920 CheatKey.Clear();
921 EasyKey.Clear();
922
923 if (gameinfo.mMapArrow.IsNotEmpty()) AM_ParseArrow(MapArrow, gameinfo.mMapArrow);
924 if (gameinfo.mCheatMapArrow.IsNotEmpty()) AM_ParseArrow(CheatMapArrow, gameinfo.mCheatMapArrow);
925 AM_ParseArrow(CheatKey, gameinfo.mCheatKey);
926 AM_ParseArrow(EasyKey, gameinfo.mEasyKey);
927 if (MapArrow.Size() == 0) I_FatalError("No automap arrow defined");
928
929 char namebuf[9];
930
931 for (int i = 0; i < 10; i++)
932 {
933 mysnprintf (namebuf, countof(namebuf), "AMMNUM%d", i);
934 marknums[i] = TexMan.CheckForTexture (namebuf, FTexture::TEX_MiscPatch);
935 }
936 markpointnum = 0;
937 mapback.SetInvalid();
938 }
939
940 //=============================================================================
941 //
942 // called by the coordinate drawer
943 //
944 //=============================================================================
945
AM_GetPosition(fixed_t & x,fixed_t & y)946 void AM_GetPosition(fixed_t &x, fixed_t &y)
947 {
948 x = (m_x + m_w/2) << FRACTOMAPBITS;
949 y = (m_y + m_h/2) << FRACTOMAPBITS;
950 }
951
952 //=============================================================================
953 //
954 //
955 //
956 //=============================================================================
957
AM_activateNewScale()958 void AM_activateNewScale ()
959 {
960 m_x += m_w/2;
961 m_y += m_h/2;
962 m_w = FTOM(f_w);
963 m_h = FTOM(f_h);
964 m_x -= m_w/2;
965 m_y -= m_h/2;
966 m_x2 = m_x + m_w;
967 m_y2 = m_y + m_h;
968 }
969
970 //=============================================================================
971 //
972 //
973 //
974 //=============================================================================
975
AM_saveScaleAndLoc()976 void AM_saveScaleAndLoc ()
977 {
978 old_m_x = m_x;
979 old_m_y = m_y;
980 old_m_w = m_w;
981 old_m_h = m_h;
982 }
983
984 //=============================================================================
985 //
986 //
987 //
988 //=============================================================================
989
AM_restoreScaleAndLoc()990 void AM_restoreScaleAndLoc ()
991 {
992 m_w = old_m_w;
993 m_h = old_m_h;
994 if (!am_followplayer)
995 {
996 m_x = old_m_x;
997 m_y = old_m_y;
998 }
999 else
1000 {
1001 m_x = (players[consoleplayer].camera->X() >> FRACTOMAPBITS) - m_w/2;
1002 m_y = (players[consoleplayer].camera->Y() >> FRACTOMAPBITS)- m_h/2;
1003 }
1004 m_x2 = m_x + m_w;
1005 m_y2 = m_y + m_h;
1006
1007 // Change the scaling multipliers
1008 scale_mtof = MapDiv(f_w<<MAPBITS, m_w);
1009 scale_ftom = MapDiv(MAPUNIT, scale_mtof);
1010 }
1011
1012 //=============================================================================
1013 //
1014 // adds a marker at the current location
1015 //
1016 //=============================================================================
1017
AM_addMark()1018 bool AM_addMark ()
1019 {
1020 if (marknums[0].isValid())
1021 {
1022 markpoints[markpointnum].x = m_x + m_w/2;
1023 markpoints[markpointnum].y = m_y + m_h/2;
1024 markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS;
1025 return true;
1026 }
1027 return false;
1028 }
1029
1030 //=============================================================================
1031 //
1032 // Determines bounding box of all vertices,
1033 // sets global variables controlling zoom range.
1034 //
1035 //=============================================================================
1036
AM_findMinMaxBoundaries()1037 static void AM_findMinMaxBoundaries ()
1038 {
1039 min_x = min_y = FIXED_MAX;
1040 max_x = max_y = FIXED_MIN;
1041
1042 for (int i = 0; i < numvertexes; i++)
1043 {
1044 if (vertexes[i].x < min_x)
1045 min_x = vertexes[i].x;
1046 else if (vertexes[i].x > max_x)
1047 max_x = vertexes[i].x;
1048
1049 if (vertexes[i].y < min_y)
1050 min_y = vertexes[i].y;
1051 else if (vertexes[i].y > max_y)
1052 max_y = vertexes[i].y;
1053 }
1054
1055 max_w = (max_x >>= FRACTOMAPBITS) - (min_x >>= FRACTOMAPBITS);
1056 max_h = (max_y >>= FRACTOMAPBITS) - (min_y >>= FRACTOMAPBITS);
1057
1058 min_w = 2*PLAYERRADIUS; // const? never changed?
1059 min_h = 2*PLAYERRADIUS;
1060
1061 AM_calcMinMaxMtoF();
1062 }
1063
1064 //=============================================================================
1065 //
1066 //
1067 //
1068 //=============================================================================
1069
AM_calcMinMaxMtoF()1070 static void AM_calcMinMaxMtoF()
1071 {
1072 fixed_t a = MapDiv (SCREENWIDTH << MAPBITS, max_w);
1073 fixed_t b = MapDiv (::ST_Y << MAPBITS, max_h);
1074
1075 min_scale_mtof = a < b ? a : b;
1076 max_scale_mtof = MapDiv (SCREENHEIGHT << MAPBITS, 2*PLAYERRADIUS);
1077 }
1078
1079 //=============================================================================
1080 //
1081 //
1082 //
1083 //=============================================================================
1084
AM_ClipRotatedExtents(fixed_t pivotx,fixed_t pivoty)1085 static void AM_ClipRotatedExtents (fixed_t pivotx, fixed_t pivoty)
1086 {
1087 if (am_rotate == 0 || (am_rotate == 2 && !viewactive))
1088 {
1089 if (m_x + m_w/2 > max_x)
1090 m_x = max_x - m_w/2;
1091 else if (m_x + m_w/2 < min_x)
1092 m_x = min_x - m_w/2;
1093
1094 if (m_y + m_h/2 > max_y)
1095 m_y = max_y - m_h/2;
1096 else if (m_y + m_h/2 < min_y)
1097 m_y = min_y - m_h/2;
1098 }
1099 else
1100 {
1101 #if 0
1102 fixed_t rmin_x, rmin_y, rmax_x, rmax_y;
1103 fixed_t xs[5], ys[5];
1104 int i;
1105
1106 xs[0] = min_x; ys[0] = min_y;
1107 xs[1] = max_x; ys[1] = min_y;
1108 xs[2] = max_x; ys[2] = max_y;
1109 xs[3] = min_x; ys[3] = max_y;
1110 xs[4] = m_x + m_w/2; ys[4] = m_y + m_h/2;
1111 rmin_x = rmin_y = FIXED_MAX;
1112 rmax_x = rmax_y = FIXED_MIN;
1113
1114 for (i = 0; i < 5; ++i)
1115 {
1116 xs[i] -= pivotx;
1117 ys[i] -= pivoty;
1118 AM_rotate (&xs[i], &ys[i], ANG90 - players[consoleplayer].camera->angle);
1119
1120 if (i == 5)
1121 break;
1122 // xs[i] += pivotx;
1123 // ys[i] += pivoty;
1124
1125 if (xs[i] < rmin_x) rmin_x = xs[i];
1126 if (xs[i] > rmax_x) rmax_x = xs[i];
1127 if (ys[i] < rmin_y) rmin_y = ys[i];
1128 if (ys[i] > rmax_y) rmax_y = ys[i];
1129 }
1130 if (rmax_x < 0)
1131 xs[4] = -rmax_x;
1132 else if (rmin_x > 0)
1133 xs[4] = -rmin_x;
1134
1135 // if (ys[4] > rmax_y)
1136 // ys[4] = rmax_y;
1137 // else if (ys[4] < rmin_y)
1138 // ys[4] = rmin_y;
1139 AM_rotate (&xs[4], &ys[4], ANG270 - players[consoleplayer].camera->angle);
1140 m_x = xs[4] + pivotx - m_w/2;
1141 m_y = ys[4] + pivoty - m_h/2;
1142 #endif
1143 }
1144
1145 m_x2 = m_x + m_w;
1146 m_y2 = m_y + m_h;
1147 }
1148
1149 //=============================================================================
1150 //
1151 //
1152 //
1153 //=============================================================================
1154
AM_ScrollParchment(fixed_t dmapx,fixed_t dmapy)1155 static void AM_ScrollParchment (fixed_t dmapx, fixed_t dmapy)
1156 {
1157 mapxstart -= MulScale12 (dmapx, scale_mtof);
1158 mapystart -= MulScale12 (dmapy, scale_mtof);
1159
1160 if (mapback.isValid())
1161 {
1162 FTexture *backtex = TexMan[mapback];
1163
1164 if (backtex != NULL)
1165 {
1166 int pwidth = backtex->GetWidth() << MAPBITS;
1167 int pheight = backtex->GetHeight() << MAPBITS;
1168
1169 while(mapxstart > 0)
1170 mapxstart -= pwidth;
1171 while(mapxstart <= -pwidth)
1172 mapxstart += pwidth;
1173 while(mapystart > 0)
1174 mapystart -= pheight;
1175 while(mapystart <= -pheight)
1176 mapystart += pheight;
1177 }
1178 }
1179 }
1180
1181 //=============================================================================
1182 //
1183 //
1184 //
1185 //=============================================================================
1186
AM_changeWindowLoc()1187 void AM_changeWindowLoc ()
1188 {
1189 if (0 != (m_paninc.x | m_paninc.y))
1190 {
1191 am_followplayer = false;
1192 f_oldloc.x = FIXED_MAX;
1193 }
1194
1195 int oldmx = m_x, oldmy = m_y;
1196 fixed_t incx, incy, oincx, oincy;
1197
1198 incx = m_paninc.x;
1199 incy = m_paninc.y;
1200
1201 oincx = incx = Scale(m_paninc.x, SCREENWIDTH, 320);
1202 oincy = incy = Scale(m_paninc.y, SCREENHEIGHT, 200);
1203 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
1204 {
1205 AM_rotate(&incx, &incy, players[consoleplayer].camera->angle - ANG90);
1206 }
1207
1208 m_x += incx;
1209 m_y += incy;
1210
1211 AM_ClipRotatedExtents (oldmx + m_w/2, oldmy + m_h/2);
1212 AM_ScrollParchment (m_x != oldmx ? oincx : 0, m_y != oldmy ? -oincy : 0);
1213 }
1214
1215
1216 //=============================================================================
1217 //
1218 //
1219 //
1220 //=============================================================================
1221
AM_initVariables()1222 void AM_initVariables ()
1223 {
1224 int pnum;
1225
1226 automapactive = true;
1227
1228 // Reset AM buttons
1229 Button_AM_PanLeft.Reset();
1230 Button_AM_PanRight.Reset();
1231 Button_AM_PanUp.Reset();
1232 Button_AM_PanDown.Reset();
1233 Button_AM_ZoomIn.Reset();
1234 Button_AM_ZoomOut.Reset();
1235
1236
1237 f_oldloc.x = FIXED_MAX;
1238 amclock = 0;
1239
1240 m_paninc.x = m_paninc.y = 0;
1241 mtof_zoommul = MAPUNIT;
1242
1243 m_w = FTOM(SCREENWIDTH);
1244 m_h = FTOM(SCREENHEIGHT);
1245
1246 // find player to center on initially
1247 if (!playeringame[pnum = consoleplayer])
1248 for (pnum=0;pnum<MAXPLAYERS;pnum++)
1249 if (playeringame[pnum])
1250 break;
1251 assert(pnum >= 0 && pnum < MAXPLAYERS);
1252 m_x = (players[pnum].camera->X() >> FRACTOMAPBITS) - m_w/2;
1253 m_y = (players[pnum].camera->Y() >> FRACTOMAPBITS) - m_h/2;
1254 AM_changeWindowLoc();
1255
1256 // for saving & restoring
1257 old_m_x = m_x;
1258 old_m_y = m_y;
1259 old_m_w = m_w;
1260 old_m_h = m_h;
1261 }
1262
1263 //=============================================================================
1264 //
1265 //
1266 //
1267 //=============================================================================
1268
AM_initColors(bool overlayed)1269 static void AM_initColors (bool overlayed)
1270 {
1271 if (overlayed)
1272 {
1273 if (am_customcolors && AMModOverlay.defined)
1274 {
1275 AMColors = AMModOverlay;
1276 }
1277 else
1278 {
1279 AMColors.initFromCVars(cv_overlay);
1280 }
1281 }
1282 else if (am_customcolors && AMMod.defined)
1283 {
1284 AMColors = AMMod;
1285 }
1286 else switch(am_colorset)
1287 {
1288 default:
1289 /* Use the custom colors in the am_* cvars */
1290 AMColors.initFromCVars(cv_standard);
1291 break;
1292
1293 case 1: // Doom
1294 // Use colors corresponding to the original Doom's
1295 AMColors.initFromColors(DoomColors, false);
1296 break;
1297
1298 case 2: // Strife
1299 // Use colors corresponding to the original Strife's
1300 AMColors.initFromColors(StrifeColors, false);
1301 break;
1302
1303 case 3: // Raven
1304 // Use colors corresponding to the original Raven's
1305 AMColors.initFromColors(RavenColors, true);
1306 break;
1307
1308 }
1309 }
1310
1311 //=============================================================================
1312 //
1313 //
1314 //
1315 //=============================================================================
1316
AM_clearMarks()1317 bool AM_clearMarks ()
1318 {
1319 for (int i = AM_NUMMARKPOINTS-1; i >= 0; i--)
1320 markpoints[i].x = -1; // means empty
1321 markpointnum = 0;
1322 return marknums[0].isValid();
1323 }
1324
1325 //=============================================================================
1326 //
1327 // called right after the level has been loaded
1328 //
1329 //=============================================================================
1330
AM_LevelInit()1331 void AM_LevelInit ()
1332 {
1333 if (level.info->MapBackground.Len() == 0)
1334 {
1335 mapback = TexMan.CheckForTexture("AUTOPAGE", FTexture::TEX_MiscPatch);
1336 }
1337 else
1338 {
1339 mapback = TexMan.CheckForTexture(level.info->MapBackground, FTexture::TEX_MiscPatch);
1340 }
1341
1342 AM_clearMarks();
1343
1344 AM_findMinMaxBoundaries();
1345 scale_mtof = MapDiv(min_scale_mtof, (int) (0.7*MAPUNIT));
1346 if (scale_mtof > max_scale_mtof)
1347 scale_mtof = min_scale_mtof;
1348 scale_ftom = MapDiv(MAPUNIT, scale_mtof);
1349
1350 am_showalllines.Callback();
1351 }
1352
1353 //=============================================================================
1354 //
1355 //
1356 //
1357 //=============================================================================
1358
AM_Stop()1359 void AM_Stop ()
1360 {
1361 automapactive = false;
1362 stopped = true;
1363 V_SetBorderNeedRefresh();
1364 viewactive = true;
1365 }
1366
1367 //=============================================================================
1368 //
1369 //
1370 //
1371 //=============================================================================
1372
AM_Start()1373 void AM_Start ()
1374 {
1375 if (!stopped) AM_Stop();
1376 stopped = false;
1377 AM_initVariables();
1378 }
1379
1380
1381
1382 //=============================================================================
1383 //
1384 // set the window scale to the maximum size
1385 //
1386 //=============================================================================
1387
AM_minOutWindowScale()1388 void AM_minOutWindowScale ()
1389 {
1390 scale_mtof = min_scale_mtof;
1391 scale_ftom = MapDiv(MAPUNIT, scale_mtof);
1392 }
1393
1394 //=============================================================================
1395 //
1396 // set the window scale to the minimum size
1397 //
1398 //=============================================================================
1399
AM_maxOutWindowScale()1400 void AM_maxOutWindowScale ()
1401 {
1402 scale_mtof = max_scale_mtof;
1403 scale_ftom = MapDiv(MAPUNIT, scale_mtof);
1404 }
1405
1406 //=============================================================================
1407 //
1408 // Called right after the resolution has changed
1409 //
1410 //=============================================================================
1411
AM_NewResolution()1412 void AM_NewResolution()
1413 {
1414 fixed_t oldmin = min_scale_mtof;
1415
1416 if ( oldmin == 0 )
1417 {
1418 return; // [SP] Not in a game, exit!
1419 }
1420 AM_calcMinMaxMtoF();
1421 scale_mtof = Scale(scale_mtof, min_scale_mtof, oldmin);
1422 scale_ftom = MapDiv(MAPUNIT, scale_mtof);
1423 if (scale_mtof < min_scale_mtof)
1424 AM_minOutWindowScale();
1425 else if (scale_mtof > max_scale_mtof)
1426 AM_maxOutWindowScale();
1427 f_w = screen->GetWidth();
1428 f_h = ST_Y;
1429 AM_activateNewScale();
1430 }
1431
1432
1433 //=============================================================================
1434 //
1435 //
1436 //
1437 //=============================================================================
1438
CCMD(togglemap)1439 CCMD (togglemap)
1440 {
1441 if (gameaction == ga_nothing)
1442 {
1443 gameaction = ga_togglemap;
1444 }
1445 }
1446
1447 //=============================================================================
1448 //
1449 //
1450 //
1451 //=============================================================================
1452
AM_ToggleMap()1453 void AM_ToggleMap ()
1454 {
1455 if (gamestate != GS_LEVEL)
1456 return;
1457
1458 // Don't activate the automap if we're not allowed to use it.
1459 if (dmflags2 & DF2_NO_AUTOMAP)
1460 return;
1461
1462 ST_SetNeedRefresh();
1463 if (!automapactive)
1464 {
1465 AM_Start ();
1466 viewactive = (am_overlay != 0.f);
1467 }
1468 else
1469 {
1470 if (am_overlay==1 && viewactive)
1471 {
1472 viewactive = false;
1473 ST_SetNeedRefresh();
1474 }
1475 else
1476 {
1477 AM_Stop ();
1478 }
1479 }
1480 }
1481
1482 //=============================================================================
1483 //
1484 // Handle events (user inputs) in automap mode
1485 //
1486 //=============================================================================
1487
AM_Responder(event_t * ev,bool last)1488 bool AM_Responder (event_t *ev, bool last)
1489 {
1490 if (automapactive && (ev->type == EV_KeyDown || ev->type == EV_KeyUp))
1491 {
1492 if (am_followplayer)
1493 {
1494 // check for am_pan* and ignore in follow mode
1495 const char *defbind = AutomapBindings.GetBind(ev->data1);
1496 if (!strnicmp(defbind, "+am_pan", 7)) return false;
1497 }
1498
1499 bool res = C_DoKey(ev, &AutomapBindings, NULL);
1500 if (res && ev->type == EV_KeyUp && !last)
1501 {
1502 // If this is a release event we also need to check if it released a button in the main Bindings
1503 // so that that button does not get stuck.
1504 const char *defbind = Bindings.GetBind(ev->data1);
1505 return (defbind[0] != '+'); // Let G_Responder handle button releases
1506 }
1507 return res;
1508 }
1509 return false;
1510 }
1511
1512
1513 //=============================================================================
1514 //
1515 // Zooming
1516 //
1517 //=============================================================================
1518
AM_changeWindowScale()1519 void AM_changeWindowScale ()
1520 {
1521 int mtof_zoommul;
1522
1523 if (am_zoomdir > 0)
1524 {
1525 mtof_zoommul = int(M_ZOOMIN * am_zoomdir);
1526 }
1527 else if (am_zoomdir < 0)
1528 {
1529 mtof_zoommul = int(M_ZOOMOUT / -am_zoomdir);
1530 }
1531 else if (Button_AM_ZoomIn.bDown)
1532 {
1533 mtof_zoommul = int(M_ZOOMIN);
1534 }
1535 else if (Button_AM_ZoomOut.bDown)
1536 {
1537 mtof_zoommul = int(M_ZOOMOUT);
1538 }
1539 else
1540 {
1541 mtof_zoommul = MAPUNIT;
1542 }
1543 am_zoomdir = 0;
1544
1545 // Change the scaling multipliers
1546 scale_mtof = MapMul(scale_mtof, mtof_zoommul);
1547 scale_ftom = MapDiv(MAPUNIT, scale_mtof);
1548
1549 if (scale_mtof < min_scale_mtof)
1550 AM_minOutWindowScale();
1551 else if (scale_mtof > max_scale_mtof)
1552 AM_maxOutWindowScale();
1553 }
1554
CCMD(am_zoom)1555 CCMD(am_zoom)
1556 {
1557 if (argv.argc() >= 2)
1558 {
1559 am_zoomdir = (float)atof(argv[1]);
1560 }
1561 }
1562
1563 //=============================================================================
1564 //
1565 //
1566 //
1567 //=============================================================================
1568
AM_doFollowPlayer()1569 void AM_doFollowPlayer ()
1570 {
1571 fixed_t sx, sy;
1572
1573 if (players[consoleplayer].camera != NULL &&
1574 (f_oldloc.x != players[consoleplayer].camera->X() ||
1575 f_oldloc.y != players[consoleplayer].camera->Y()))
1576 {
1577 m_x = (players[consoleplayer].camera->X() >> FRACTOMAPBITS) - m_w/2;
1578 m_y = (players[consoleplayer].camera->Y() >> FRACTOMAPBITS) - m_h/2;
1579 m_x2 = m_x + m_w;
1580 m_y2 = m_y + m_h;
1581
1582 // do the parallax parchment scrolling.
1583 sx = (players[consoleplayer].camera->X() - f_oldloc.x) >> FRACTOMAPBITS;
1584 sy = (f_oldloc.y - players[consoleplayer].camera->Y()) >> FRACTOMAPBITS;
1585 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
1586 {
1587 AM_rotate (&sx, &sy, players[consoleplayer].camera->angle - ANG90);
1588 }
1589 AM_ScrollParchment (sx, sy);
1590
1591 f_oldloc.x = players[consoleplayer].camera->X();
1592 f_oldloc.y = players[consoleplayer].camera->Y();
1593 }
1594 }
1595
1596 //=============================================================================
1597 //
1598 // Updates on Game Tick
1599 //
1600 //=============================================================================
1601
AM_Ticker()1602 void AM_Ticker ()
1603 {
1604 if (!automapactive)
1605 return;
1606
1607 amclock++;
1608
1609 if (am_followplayer)
1610 {
1611 AM_doFollowPlayer();
1612 }
1613 else
1614 {
1615 m_paninc.x = m_paninc.y = 0;
1616 if (Button_AM_PanLeft.bDown) m_paninc.x -= FTOM(F_PANINC);
1617 if (Button_AM_PanRight.bDown) m_paninc.x += FTOM(F_PANINC);
1618 if (Button_AM_PanUp.bDown) m_paninc.y += FTOM(F_PANINC);
1619 if (Button_AM_PanDown.bDown) m_paninc.y -= FTOM(F_PANINC);
1620 }
1621
1622 // Change the zoom if necessary
1623 if (Button_AM_ZoomIn.bDown || Button_AM_ZoomOut.bDown || am_zoomdir != 0)
1624 AM_changeWindowScale();
1625
1626 // Change x,y location
1627 //if (m_paninc.x || m_paninc.y)
1628 AM_changeWindowLoc();
1629 }
1630
1631
1632 //=============================================================================
1633 //
1634 // Clear automap frame buffer.
1635 //
1636 //=============================================================================
1637
AM_clearFB(const AMColor & color)1638 void AM_clearFB (const AMColor &color)
1639 {
1640 bool drawback = mapback.isValid() && am_drawmapback != 0;
1641 if (am_drawmapback == 2)
1642 {
1643 // only draw background when using a mod defined custom color set or Raven colors, if am_drawmapback is 2.
1644 if (!am_customcolors || !AMMod.defined)
1645 {
1646 drawback &= (am_colorset == 3);
1647 }
1648 }
1649
1650 if (!drawback)
1651 {
1652 screen->Clear (0, 0, f_w, f_h, color.Index, color.RGB);
1653 }
1654 else
1655 {
1656 FTexture *backtex = TexMan[mapback];
1657 if (backtex != NULL)
1658 {
1659 int pwidth = backtex->GetWidth();
1660 int pheight = backtex->GetHeight();
1661 int x, y;
1662
1663 //blit the automap background to the screen.
1664 for (y = mapystart >> MAPBITS; y < f_h; y += pheight)
1665 {
1666 for (x = mapxstart >> MAPBITS; x < f_w; x += pwidth)
1667 {
1668 screen->DrawTexture (backtex, x, y, DTA_ClipBottom, f_h, DTA_TopOffset, 0, DTA_LeftOffset, 0, TAG_DONE);
1669 }
1670 }
1671 }
1672 }
1673 }
1674
1675
1676 //=============================================================================
1677 //
1678 // Automap clipping of lines.
1679 //
1680 // Based on Cohen-Sutherland clipping algorithm but with a slightly
1681 // faster reject and precalculated slopes. If the speed is needed,
1682 // use a hash algorithm to handle the common cases.
1683 //
1684 //=============================================================================
1685
AM_clipMline(mline_t * ml,fline_t * fl)1686 bool AM_clipMline (mline_t *ml, fline_t *fl)
1687 {
1688 enum {
1689 LEFT =1,
1690 RIGHT =2,
1691 BOTTOM =4,
1692 TOP =8
1693 };
1694
1695 register int outcode1 = 0;
1696 register int outcode2 = 0;
1697 register int outside;
1698
1699 fpoint_t tmp = { 0, 0 };
1700 int dx;
1701 int dy;
1702
1703 #define DOOUTCODE(oc, mx, my) \
1704 (oc) = 0; \
1705 if ((my) < 0) (oc) |= TOP; \
1706 else if ((my) >= f_h) (oc) |= BOTTOM; \
1707 if ((mx) < 0) (oc) |= LEFT; \
1708 else if ((mx) >= f_w) (oc) |= RIGHT;
1709
1710 // do trivial rejects and outcodes
1711 if (ml->a.y > m_y2)
1712 outcode1 = TOP;
1713 else if (ml->a.y < m_y)
1714 outcode1 = BOTTOM;
1715
1716 if (ml->b.y > m_y2)
1717 outcode2 = TOP;
1718 else if (ml->b.y < m_y)
1719 outcode2 = BOTTOM;
1720
1721 if (outcode1 & outcode2)
1722 return false; // trivially outside
1723
1724 if (ml->a.x < m_x)
1725 outcode1 |= LEFT;
1726 else if (ml->a.x > m_x2)
1727 outcode1 |= RIGHT;
1728
1729 if (ml->b.x < m_x)
1730 outcode2 |= LEFT;
1731 else if (ml->b.x > m_x2)
1732 outcode2 |= RIGHT;
1733
1734 if (outcode1 & outcode2)
1735 return false; // trivially outside
1736
1737 // transform to frame-buffer coordinates.
1738 fl->a.x = CXMTOF(ml->a.x);
1739 fl->a.y = CYMTOF(ml->a.y);
1740 fl->b.x = CXMTOF(ml->b.x);
1741 fl->b.y = CYMTOF(ml->b.y);
1742
1743 DOOUTCODE(outcode1, fl->a.x, fl->a.y);
1744 DOOUTCODE(outcode2, fl->b.x, fl->b.y);
1745
1746 if (outcode1 & outcode2)
1747 return false;
1748
1749 while (outcode1 | outcode2) {
1750 // may be partially inside box
1751 // find an outside point
1752 if (outcode1)
1753 outside = outcode1;
1754 else
1755 outside = outcode2;
1756
1757 // clip to each side
1758 if (outside & TOP)
1759 {
1760 dy = fl->a.y - fl->b.y;
1761 dx = fl->b.x - fl->a.x;
1762 tmp.x = fl->a.x + Scale(dx, fl->a.y, dy);
1763 tmp.y = 0;
1764 }
1765 else if (outside & BOTTOM)
1766 {
1767 dy = fl->a.y - fl->b.y;
1768 dx = fl->b.x - fl->a.x;
1769 tmp.x = fl->a.x + Scale(dx, fl->a.y - f_h, dy);
1770 tmp.y = f_h-1;
1771 }
1772 else if (outside & RIGHT)
1773 {
1774 dy = fl->b.y - fl->a.y;
1775 dx = fl->b.x - fl->a.x;
1776 tmp.y = fl->a.y + Scale(dy, f_w-1 - fl->a.x, dx);
1777 tmp.x = f_w-1;
1778 }
1779 else if (outside & LEFT)
1780 {
1781 dy = fl->b.y - fl->a.y;
1782 dx = fl->b.x - fl->a.x;
1783 tmp.y = fl->a.y + Scale(dy, -fl->a.x, dx);
1784 tmp.x = 0;
1785 }
1786
1787 if (outside == outcode1)
1788 {
1789 fl->a = tmp;
1790 DOOUTCODE(outcode1, fl->a.x, fl->a.y);
1791 }
1792 else
1793 {
1794 fl->b = tmp;
1795 DOOUTCODE(outcode2, fl->b.x, fl->b.y);
1796 }
1797
1798 if (outcode1 & outcode2)
1799 return false; // trivially outside
1800 }
1801
1802 return true;
1803 }
1804 #undef DOOUTCODE
1805
1806
1807 //=============================================================================
1808 //
1809 // Clip lines, draw visible parts of lines.
1810 //
1811 //=============================================================================
1812
AM_drawMline(mline_t * ml,const AMColor & color)1813 void AM_drawMline (mline_t *ml, const AMColor &color)
1814 {
1815 fline_t fl;
1816
1817 if (AM_clipMline (ml, &fl))
1818 {
1819 screen->DrawLine (f_x + fl.a.x, f_y + fl.a.y, f_x + fl.b.x, f_y + fl.b.y, color.Index, color.RGB);
1820 }
1821 }
1822
AM_drawMline(mline_t * ml,int colorindex)1823 inline void AM_drawMline (mline_t *ml, int colorindex)
1824 {
1825 AM_drawMline(ml, AMColors[colorindex]);
1826 }
1827
1828 //=============================================================================
1829 //
1830 // Draws flat (floor/ceiling tile) aligned grid lines.
1831 //
1832 //=============================================================================
1833
AM_drawGrid(int color)1834 void AM_drawGrid (int color)
1835 {
1836 fixed_t x, y;
1837 fixed_t start, end;
1838 mline_t ml;
1839 fixed_t minlen, extx, exty;
1840 fixed_t minx, miny;
1841
1842 // [RH] Calculate a minimum for how long the grid lines should be so that
1843 // they cover the screen at any rotation.
1844 minlen = (fixed_t)sqrt ((double)m_w*(double)m_w + (double)m_h*(double)m_h);
1845 extx = (minlen - m_w) / 2;
1846 exty = (minlen - m_h) / 2;
1847
1848 minx = m_x;
1849 miny = m_y;
1850
1851 // Figure out start of vertical gridlines
1852 start = minx - extx;
1853 if ((start-bmaporgx)%(MAPBLOCKUNITS<<MAPBITS))
1854 start += (MAPBLOCKUNITS<<MAPBITS)
1855 - ((start-bmaporgx)%(MAPBLOCKUNITS<<MAPBITS));
1856 end = minx + minlen - extx;
1857
1858 // draw vertical gridlines
1859 for (x = start; x < end; x += (MAPBLOCKUNITS<<MAPBITS))
1860 {
1861 ml.a.x = x;
1862 ml.b.x = x;
1863 ml.a.y = miny - exty;
1864 ml.b.y = ml.a.y + minlen;
1865 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
1866 {
1867 AM_rotatePoint (&ml.a.x, &ml.a.y);
1868 AM_rotatePoint (&ml.b.x, &ml.b.y);
1869 }
1870 AM_drawMline(&ml, color);
1871 }
1872
1873 // Figure out start of horizontal gridlines
1874 start = miny - exty;
1875 if ((start-bmaporgy)%(MAPBLOCKUNITS<<MAPBITS))
1876 start += (MAPBLOCKUNITS<<MAPBITS)
1877 - ((start-bmaporgy)%(MAPBLOCKUNITS<<MAPBITS));
1878 end = miny + minlen - exty;
1879
1880 // draw horizontal gridlines
1881 for (y=start; y<end; y+=(MAPBLOCKUNITS<<MAPBITS))
1882 {
1883 ml.a.x = minx - extx;
1884 ml.b.x = ml.a.x + minlen;
1885 ml.a.y = y;
1886 ml.b.y = y;
1887 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
1888 {
1889 AM_rotatePoint (&ml.a.x, &ml.a.y);
1890 AM_rotatePoint (&ml.b.x, &ml.b.y);
1891 }
1892 AM_drawMline (&ml, color);
1893 }
1894 }
1895
1896 //=============================================================================
1897 //
1898 // AM_drawSubsectors
1899 //
1900 //=============================================================================
1901
AM_drawSubsectors()1902 void AM_drawSubsectors()
1903 {
1904 static TArray<FVector2> points;
1905 float scale = float(scale_mtof);
1906 angle_t rotation;
1907 sector_t tempsec;
1908 int floorlight, ceilinglight;
1909 fixed_t scalex, scaley;
1910 double originx, originy;
1911 FDynamicColormap *colormap;
1912 mpoint_t originpt;
1913
1914 for (int i = 0; i < numsubsectors; ++i)
1915 {
1916 if (subsectors[i].flags & SSECF_POLYORG)
1917 {
1918 continue;
1919 }
1920
1921 if ((!(subsectors[i].flags & SSECF_DRAWN) || (subsectors[i].render_sector->MoreFlags & SECF_HIDDEN)) && am_cheat == 0)
1922 {
1923 continue;
1924 }
1925 // Fill the points array from the subsector.
1926 points.Resize(subsectors[i].numlines);
1927 for (DWORD j = 0; j < subsectors[i].numlines; ++j)
1928 {
1929 mpoint_t pt = { subsectors[i].firstline[j].v1->x >> FRACTOMAPBITS,
1930 subsectors[i].firstline[j].v1->y >> FRACTOMAPBITS };
1931 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
1932 {
1933 AM_rotatePoint(&pt.x, &pt.y);
1934 }
1935 points[j].X = f_x + ((pt.x - m_x) * scale / float(1 << 24));
1936 points[j].Y = f_y + (f_h - (pt.y - m_y) * scale / float(1 << 24));
1937 }
1938 // For lighting and texture determination
1939 sector_t *sec = Renderer->FakeFlat (subsectors[i].render_sector, &tempsec, &floorlight, &ceilinglight, false);
1940 // Find texture origin.
1941 originpt.x = -sec->GetXOffset(sector_t::floor) >> FRACTOMAPBITS;
1942 originpt.y = sec->GetYOffset(sector_t::floor) >> FRACTOMAPBITS;
1943 rotation = 0 - sec->GetAngle(sector_t::floor);
1944 // Coloring for the polygon
1945 colormap = sec->ColorMap;
1946
1947 FTextureID maptex = sec->GetTexture(sector_t::floor);
1948
1949 scalex = sec->GetXScale(sector_t::floor);
1950 scaley = sec->GetYScale(sector_t::floor);
1951
1952 if (sec->e->XFloor.ffloors.Size())
1953 {
1954 secplane_t *floorplane = &sec->floorplane;
1955
1956 // Look for the highest floor below the camera viewpoint.
1957 // Check the center of the subsector's sector. Do not check each
1958 // subsector separately because that might result in different planes for
1959 // different subsectors of the same sector which is not wanted here.
1960 // (Make the comparison in floating point to avoid overflows and improve performance.)
1961 double secx;
1962 double secy;
1963 double seczb, seczt;
1964 double cmpz = FIXED2DBL(viewz);
1965
1966 if (players[consoleplayer].camera && sec == players[consoleplayer].camera->Sector)
1967 {
1968 // For the actual camera sector use the current viewpoint as reference.
1969 secx = FIXED2DBL(viewx);
1970 secy = FIXED2DBL(viewy);
1971 }
1972 else
1973 {
1974 secx = FIXED2DBL(sec->soundorg[0]);
1975 secy = FIXED2DBL(sec->soundorg[1]);
1976 }
1977 seczb = floorplane->ZatPoint(secx, secy);
1978 seczt = sec->ceilingplane.ZatPoint(secx, secy);
1979
1980 for (unsigned int i = 0; i < sec->e->XFloor.ffloors.Size(); ++i)
1981 {
1982 F3DFloor *rover = sec->e->XFloor.ffloors[i];
1983 if (!(rover->flags & FF_EXISTS)) continue;
1984 if (rover->flags & FF_FOG) continue;
1985 if (!(rover->flags & FF_RENDERPLANES)) continue;
1986 if (rover->alpha == 0) continue;
1987 double roverz = rover->top.plane->ZatPoint(secx, secy);
1988 // Ignore 3D floors that are above or below the sector itself:
1989 // they are hidden. Since 3D floors are sorted top to bottom,
1990 // if we get below the sector floor, we can stop.
1991 if (roverz > seczt) continue;
1992 if (roverz < seczb) break;
1993 if (roverz < cmpz)
1994 {
1995 maptex = *(rover->top.texture);
1996 floorplane = rover->top.plane;
1997 sector_t *model = rover->top.model;
1998 int selector = (rover->flags & FF_INVERTPLANES) ? sector_t::floor : sector_t::ceiling;
1999 rotation = 0 - model->GetAngle(selector);
2000 scalex = model->GetXScale(selector);
2001 scaley = model->GetYScale(selector);
2002 originpt.x = -model->GetXOffset(selector) >> FRACTOMAPBITS;
2003 originpt.y = model->GetYOffset(selector) >> FRACTOMAPBITS;
2004 break;
2005 }
2006 }
2007
2008 lightlist_t *light = P_GetPlaneLight(sec, floorplane, false);
2009 floorlight = *light->p_lightlevel;
2010 colormap = light->extra_colormap;
2011 }
2012 if (maptex == skyflatnum)
2013 {
2014 continue;
2015 }
2016
2017 // Apply the floor's rotation to the texture origin.
2018 if (rotation != 0)
2019 {
2020 AM_rotate(&originpt.x, &originpt.y, rotation);
2021 }
2022 // Apply the automap's rotation to the texture origin.
2023 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
2024 {
2025 rotation += ANG90 - players[consoleplayer].camera->angle;
2026 AM_rotatePoint(&originpt.x, &originpt.y);
2027 }
2028 originx = f_x + ((originpt.x - m_x) * scale / float(1 << 24));
2029 originy = f_y + (f_h - (originpt.y - m_y) * scale / float(1 << 24));
2030
2031 // If this subsector has not actually been seen yet (because you are cheating
2032 // to see it on the map), tint and desaturate it.
2033 if (!(subsectors[i].flags & SSECF_DRAWN))
2034 {
2035 colormap = GetSpecialLights(
2036 MAKERGB(
2037 (colormap->Color.r + 255) / 2,
2038 (colormap->Color.g + 200) / 2,
2039 (colormap->Color.b + 160) / 2),
2040 colormap->Fade,
2041 255 - (255 - colormap->Desaturate) / 4);
2042 floorlight = (floorlight + 200*15) / 16;
2043 }
2044
2045 // Draw the polygon.
2046 FTexture *pic = TexMan(maptex);
2047 if (pic != NULL && pic->UseType != FTexture::TEX_Null)
2048 {
2049 screen->FillSimplePoly(TexMan(maptex),
2050 &points[0], points.Size(),
2051 originx, originy,
2052 scale / (FIXED2DBL(scalex) * float(1 << MAPBITS)),
2053 scale / (FIXED2DBL(scaley) * float(1 << MAPBITS)),
2054 rotation,
2055 colormap,
2056 floorlight
2057 );
2058 }
2059 }
2060 }
2061
2062 //=============================================================================
2063 //
2064 //
2065 //
2066 //=============================================================================
2067
AM_CheckSecret(line_t * line)2068 static bool AM_CheckSecret(line_t *line)
2069 {
2070 if (AMColors.isValid(AMColors.SecretSectorColor))
2071 {
2072 if (line->frontsector != NULL)
2073 {
2074 if (line->frontsector->wasSecret())
2075 {
2076 if (am_map_secrets!=0 && !line->frontsector->isSecret()) return true;
2077 if (am_map_secrets==2 && !(line->flags & ML_SECRET)) return true;
2078 }
2079 }
2080 if (line->backsector != NULL)
2081 {
2082 if (line->backsector->wasSecret())
2083 {
2084 if (am_map_secrets!=0 && !line->backsector->isSecret()) return true;
2085 if (am_map_secrets==2 && !(line->flags & ML_SECRET)) return true;
2086 }
2087 }
2088 }
2089 return false;
2090 }
2091
2092
2093 //=============================================================================
2094 //
2095 // Polyobject debug stuff
2096 //
2097 //=============================================================================
2098
AM_drawSeg(seg_t * seg,const AMColor & color)2099 void AM_drawSeg(seg_t *seg, const AMColor &color)
2100 {
2101 mline_t l;
2102 l.a.x = seg->v1->x >> FRACTOMAPBITS;
2103 l.a.y = seg->v1->y >> FRACTOMAPBITS;
2104 l.b.x = seg->v2->x >> FRACTOMAPBITS;
2105 l.b.y = seg->v2->y >> FRACTOMAPBITS;
2106
2107 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
2108 {
2109 AM_rotatePoint (&l.a.x, &l.a.y);
2110 AM_rotatePoint (&l.b.x, &l.b.y);
2111 }
2112 AM_drawMline(&l, color);
2113 }
2114
AM_drawPolySeg(FPolySeg * seg,const AMColor & color)2115 void AM_drawPolySeg(FPolySeg *seg, const AMColor &color)
2116 {
2117 mline_t l;
2118 l.a.x = seg->v1.x >> FRACTOMAPBITS;
2119 l.a.y = seg->v1.y >> FRACTOMAPBITS;
2120 l.b.x = seg->v2.x >> FRACTOMAPBITS;
2121 l.b.y = seg->v2.y >> FRACTOMAPBITS;
2122
2123 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
2124 {
2125 AM_rotatePoint (&l.a.x, &l.a.y);
2126 AM_rotatePoint (&l.b.x, &l.b.y);
2127 }
2128 AM_drawMline(&l, color);
2129 }
2130
AM_showSS()2131 void AM_showSS()
2132 {
2133 if (am_showsubsector >= 0 && am_showsubsector < numsubsectors)
2134 {
2135 AMColor yellow;
2136 yellow.FromRGB(255,255,0);
2137 AMColor red;
2138 red.FromRGB(255,0,0);
2139
2140 subsector_t *sub = &subsectors[am_showsubsector];
2141 for (unsigned int i = 0; i < sub->numlines; i++)
2142 {
2143 AM_drawSeg(sub->firstline + i, yellow);
2144 }
2145 PO_LinkToSubsectors();
2146
2147 for (int i = 0; i <po_NumPolyobjs; i++)
2148 {
2149 FPolyObj *po = &polyobjs[i];
2150 FPolyNode *pnode = po->subsectorlinks;
2151
2152 while (pnode != NULL)
2153 {
2154 if (pnode->subsector == sub)
2155 {
2156 for (unsigned j = 0; j < pnode->segs.Size(); j++)
2157 {
2158 AM_drawPolySeg(&pnode->segs[j], red);
2159 }
2160 }
2161 pnode = pnode->snext;
2162 }
2163 }
2164 }
2165 }
2166
2167 //=============================================================================
2168 //
2169 // Determines if a 3D floor boundary should be drawn
2170 //
2171 //=============================================================================
2172
AM_Check3DFloors(line_t * line)2173 bool AM_Check3DFloors(line_t *line)
2174 {
2175 TArray<F3DFloor*> &ff_front = line->frontsector->e->XFloor.ffloors;
2176 TArray<F3DFloor*> &ff_back = line->backsector->e->XFloor.ffloors;
2177
2178 // No 3D floors so there's no boundary
2179 if (ff_back.Size() == 0 && ff_front.Size() == 0) return false;
2180
2181 int realfrontcount = 0;
2182 int realbackcount = 0;
2183
2184 for(unsigned i=0;i<ff_front.Size();i++)
2185 {
2186 F3DFloor *rover = ff_front[i];
2187 if (!(rover->flags & FF_EXISTS)) continue;
2188 if (rover->alpha == 0) continue;
2189 realfrontcount++;
2190 }
2191
2192 for(unsigned i=0;i<ff_back.Size();i++)
2193 {
2194 F3DFloor *rover = ff_back[i];
2195 if (!(rover->flags & FF_EXISTS)) continue;
2196 if (rover->alpha == 0) continue;
2197 realbackcount++;
2198 }
2199 // if the amount of 3D floors does not match there is a boundary
2200 if (realfrontcount != realbackcount) return true;
2201
2202 for(unsigned i=0;i<ff_front.Size();i++)
2203 {
2204 F3DFloor *rover = ff_front[i];
2205 if (!(rover->flags & FF_EXISTS)) continue;
2206 if (rover->alpha == 0) continue;
2207
2208 bool found = false;
2209 for(unsigned j=0;j<ff_back.Size();j++)
2210 {
2211 F3DFloor *rover2 = ff_back[j];
2212 if (!(rover2->flags & FF_EXISTS)) continue;
2213 if (rover2->alpha == 0) continue;
2214 if (rover->model == rover2->model && rover->flags == rover2->flags)
2215 {
2216 found = true;
2217 break;
2218 }
2219 }
2220 // At least one 3D floor in the front sector didn't have a match in the back sector so there is a boundary.
2221 if (!found) return true;
2222 }
2223 // All 3D floors could be matched so let's not draw a boundary.
2224 return false;
2225 }
2226
2227 // [TP] Check whether a sector can trigger a special that satisfies the provided function.
2228 // If found, specialptr and argsptr will be filled by the special and the arguments
2229 // If needUseActivated is true, the special must be activated by use.
AM_checkSectorActions(sector_t * sector,bool (* function)(int,int *),int * specialptr,int ** argsptr,bool needUseActivated)2230 bool AM_checkSectorActions (sector_t *sector, bool (*function)(int, int *), int *specialptr, int **argsptr, bool needUseActivated)
2231 {
2232 for (ASectorAction* action = sector->SecActTarget; action; action = barrier_cast<ASectorAction *>(action->tracer))
2233 {
2234 if ((action->IsActivatedByUse() || false == needUseActivated)
2235 && (*function)(action->special, action->args)
2236 && action->CanTrigger (players[consoleplayer].mo))
2237 {
2238 *specialptr = action->special;
2239 *argsptr = action->args;
2240 return true;
2241 }
2242 }
2243
2244 return false;
2245 }
2246
2247 // [TP] Check whether there's a boundary on the provided line for a special that satisfies the provided function.
2248 // It's a boundary if the line can activate the special or the line's bordering sectors can activate it.
2249 // If found, specialptr and argsptr will be filled with special and args if given.
AM_checkSpecialBoundary(line_t & line,bool (* function)(int,int *),int * specialptr=NULL,int ** argsptr=NULL)2250 bool AM_checkSpecialBoundary (line_t &line, bool (*function)(int, int *), int *specialptr = NULL, int **argsptr = NULL)
2251 {
2252 if (specialptr == NULL)
2253 {
2254 static int sink;
2255 specialptr = &sink;
2256 }
2257
2258 if (argsptr == NULL)
2259 {
2260 static int *sink;
2261 argsptr = &sink;
2262 }
2263
2264 // Check if the line special qualifies for this
2265 if ((line.activation & SPAC_PlayerActivate) && (*function)(line.special, line.args))
2266 {
2267 *specialptr = line.special;
2268 *argsptr = line.args;
2269 return true;
2270 }
2271
2272 // Check sector actions in the line's front sector -- the action has to be use-activated in order to
2273 // show up if this is a one-sided line, because the player cannot trigger sector actions by crossing
2274 // a one-sided line (since that's impossible, duh).
2275 if (AM_checkSectorActions(line.frontsector, function, specialptr, argsptr, line.backsector == NULL))
2276 return true;
2277
2278 // If it has a back sector, check sector actions in that.
2279 return (line.backsector && AM_checkSectorActions(line.backsector, function, specialptr, argsptr, false));
2280 }
2281
AM_isTeleportSpecial(int special,int *)2282 bool AM_isTeleportSpecial (int special, int *)
2283 {
2284 return (special == Teleport ||
2285 special == Teleport_NoFog ||
2286 special == Teleport_ZombieChanger ||
2287 special == Teleport_Line);
2288 }
2289
AM_isTeleportBoundary(line_t & line)2290 bool AM_isTeleportBoundary (line_t &line)
2291 {
2292 return AM_checkSpecialBoundary(line, &AM_isTeleportSpecial);
2293 }
2294
AM_isExitSpecial(int special,int *)2295 bool AM_isExitSpecial (int special, int *)
2296 {
2297 return (special == Teleport_NewMap ||
2298 special == Teleport_EndGame ||
2299 special == Exit_Normal ||
2300 special == Exit_Secret);
2301 }
2302
AM_isExitBoundary(line_t & line)2303 bool AM_isExitBoundary (line_t& line)
2304 {
2305 return AM_checkSpecialBoundary(line, &AM_isExitSpecial);
2306 }
2307
AM_isTriggerSpecial(int special,int *)2308 bool AM_isTriggerSpecial (int special, int *)
2309 {
2310 return LineSpecialsInfo[special] != NULL
2311 && LineSpecialsInfo[special]->max_args >= 0
2312 && special != Door_Open
2313 && special != Door_Close
2314 && special != Door_CloseWaitOpen
2315 && special != Door_Raise
2316 && special != Door_Animated
2317 && special != Generic_Door;
2318 }
2319
AM_isTriggerBoundary(line_t & line)2320 bool AM_isTriggerBoundary (line_t &line)
2321 {
2322 return AM_checkSpecialBoundary(line, &AM_isTriggerSpecial);
2323 }
2324
AM_isLockSpecial(int special,int * args)2325 bool AM_isLockSpecial (int special, int* args)
2326 {
2327 return special == Door_LockedRaise
2328 || special == ACS_LockedExecute
2329 || special == ACS_LockedExecuteDoor
2330 || (special == Door_Animated && args[3] != 0)
2331 || (special == Generic_Door && args[4] != 0)
2332 || (special == FS_Execute && args[2] != 0);
2333 }
2334
AM_isLockBoundary(line_t & line,int * lockptr=NULL)2335 bool AM_isLockBoundary (line_t &line, int *lockptr = NULL)
2336 {
2337 if (lockptr == NULL)
2338 {
2339 static int sink;
2340 lockptr = &sink;
2341 }
2342
2343 if (line.locknumber)
2344 {
2345 *lockptr = line.locknumber;
2346 return true;
2347 }
2348
2349 int special;
2350 int *args;
2351 bool result = AM_checkSpecialBoundary(line, &AM_isLockSpecial, &special, &args);
2352
2353 if (result)
2354 {
2355 switch (special)
2356 {
2357 case FS_Execute:
2358 *lockptr = args[2];
2359 break;
2360
2361 case Door_Animated:
2362 case Door_LockedRaise:
2363 *lockptr = args[3];
2364 break;
2365
2366 default:
2367 *lockptr = args[4];
2368 break;
2369 }
2370 }
2371
2372 return result;
2373 }
2374
2375 //=============================================================================
2376 //
2377 // Determines visible lines, draws them.
2378 // This is LineDef based, not LineSeg based.
2379 //
2380 //=============================================================================
2381
AM_drawWalls(bool allmap)2382 void AM_drawWalls (bool allmap)
2383 {
2384 int i;
2385 static mline_t l;
2386 int lock, color;
2387
2388 for (i = 0; i < numlines; i++)
2389 {
2390 l.a.x = lines[i].v1->x >> FRACTOMAPBITS;
2391 l.a.y = lines[i].v1->y >> FRACTOMAPBITS;
2392 l.b.x = lines[i].v2->x >> FRACTOMAPBITS;
2393 l.b.y = lines[i].v2->y >> FRACTOMAPBITS;
2394
2395 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
2396 {
2397 AM_rotatePoint (&l.a.x, &l.a.y);
2398 AM_rotatePoint (&l.b.x, &l.b.y);
2399 }
2400
2401 if (am_cheat != 0 || (lines[i].flags & ML_MAPPED))
2402 {
2403 if ((lines[i].flags & ML_DONTDRAW) && (am_cheat == 0 || am_cheat >= 4))
2404 {
2405 if (!am_showallenabled || CheckCheatmode(false))
2406 {
2407 continue;
2408 }
2409 }
2410
2411 if (AM_CheckSecret(&lines[i]))
2412 {
2413 // map secret sectors like Boom
2414 AM_drawMline(&l, AMColors.SecretSectorColor);
2415 }
2416 else if (lines[i].flags & ML_SECRET)
2417 { // secret door
2418 if (am_cheat != 0 && lines[i].backsector != NULL)
2419 AM_drawMline(&l, AMColors.SecretWallColor);
2420 else
2421 AM_drawMline(&l, AMColors.WallColor);
2422 }
2423 else if (AM_isTeleportBoundary(lines[i]) && AMColors.isValid(AMColors.IntraTeleportColor))
2424 { // intra-level teleporters
2425 AM_drawMline(&l, AMColors.IntraTeleportColor);
2426 }
2427 else if (AM_isExitBoundary(lines[i]) && AMColors.isValid(AMColors.InterTeleportColor))
2428 { // inter-level/game-ending teleporters
2429 AM_drawMline(&l, AMColors.InterTeleportColor);
2430 }
2431 else if (AM_isLockBoundary(lines[i], &lock))
2432 {
2433 if (AMColors.displayLocks)
2434 {
2435 color = P_GetMapColorForLock(lock);
2436
2437 AMColor c;
2438
2439 if (color >= 0) c.FromRGB(RPART(color), GPART(color), BPART(color));
2440 else c = AMColors[AMColors.LockedColor];
2441
2442 AM_drawMline (&l, c);
2443 }
2444 else
2445 {
2446 AM_drawMline (&l, AMColors.LockedColor); // locked special
2447 }
2448 }
2449 else if (am_showtriggerlines
2450 && AMColors.isValid(AMColors.SpecialWallColor)
2451 && AM_isTriggerBoundary(lines[i]))
2452 {
2453 AM_drawMline(&l, AMColors.SpecialWallColor); // wall with special non-door action the player can do
2454 }
2455 else if (lines[i].backsector == NULL)
2456 {
2457 AM_drawMline(&l, AMColors.WallColor); // one-sided wall
2458 }
2459 else if (lines[i].backsector->floorplane
2460 != lines[i].frontsector->floorplane)
2461 {
2462 AM_drawMline(&l, AMColors.FDWallColor); // floor level change
2463 }
2464 else if (lines[i].backsector->ceilingplane
2465 != lines[i].frontsector->ceilingplane)
2466 {
2467 AM_drawMline(&l, AMColors.CDWallColor); // ceiling level change
2468 }
2469 else if (AM_Check3DFloors(&lines[i]))
2470 {
2471 AM_drawMline(&l, AMColors.EFWallColor); // Extra floor border
2472 }
2473 else if (am_cheat > 0 && am_cheat < 4)
2474 {
2475 AM_drawMline(&l, AMColors.TSWallColor);
2476 }
2477 }
2478 else if (allmap)
2479 {
2480 if ((lines[i].flags & ML_DONTDRAW) && (am_cheat == 0 || am_cheat >= 4))
2481 {
2482 if (!am_showallenabled || CheckCheatmode(false))
2483 {
2484 continue;
2485 }
2486 }
2487 AM_drawMline(&l, AMColors.NotSeenColor);
2488 }
2489 }
2490 }
2491
2492
2493 //=============================================================================
2494 //
2495 // Rotation in 2D.
2496 // Used to rotate player arrow line character.
2497 //
2498 //=============================================================================
2499
AM_rotate(fixed_t * xp,fixed_t * yp,angle_t a)2500 void AM_rotate(fixed_t *xp, fixed_t *yp, angle_t a)
2501 {
2502 static angle_t angle_saved = 0;
2503 static double sinrot = 0;
2504 static double cosrot = 1;
2505
2506 if (angle_saved != a)
2507 {
2508 angle_saved = a;
2509 double rot = (double)a / (double)(1u << 31) * (double)M_PI;
2510 sinrot = sin(rot);
2511 cosrot = cos(rot);
2512 }
2513
2514 double x = FIXED2FLOAT(*xp);
2515 double y = FIXED2FLOAT(*yp);
2516 double tmpx = (x * cosrot) - (y * sinrot);
2517 y = (x * sinrot) + (y * cosrot);
2518 x = tmpx;
2519 *xp = FLOAT2FIXED(x);
2520 *yp = FLOAT2FIXED(y);
2521 }
2522
2523 //=============================================================================
2524 //
2525 //
2526 //
2527 //=============================================================================
2528
AM_rotatePoint(fixed_t * x,fixed_t * y)2529 void AM_rotatePoint (fixed_t *x, fixed_t *y)
2530 {
2531 fixed_t pivotx = m_x + m_w/2;
2532 fixed_t pivoty = m_y + m_h/2;
2533 *x -= pivotx;
2534 *y -= pivoty;
2535 AM_rotate (x, y, ANG90 - players[consoleplayer].camera->angle);
2536 *x += pivotx;
2537 *y += pivoty;
2538 }
2539
2540 //=============================================================================
2541 //
2542 //
2543 //
2544 //=============================================================================
2545
2546 void
AM_drawLineCharacter(const mline_t * lineguy,int lineguylines,fixed_t scale,angle_t angle,const AMColor & color,fixed_t x,fixed_t y)2547 AM_drawLineCharacter
2548 ( const mline_t *lineguy,
2549 int lineguylines,
2550 fixed_t scale,
2551 angle_t angle,
2552 const AMColor &color,
2553 fixed_t x,
2554 fixed_t y )
2555 {
2556 int i;
2557 mline_t l;
2558
2559 for (i=0;i<lineguylines;i++) {
2560 l.a.x = lineguy[i].a.x;
2561 l.a.y = lineguy[i].a.y;
2562
2563 if (scale) {
2564 l.a.x = MapMul(scale, l.a.x);
2565 l.a.y = MapMul(scale, l.a.y);
2566 }
2567
2568 if (angle)
2569 AM_rotate(&l.a.x, &l.a.y, angle);
2570
2571 l.a.x += x;
2572 l.a.y += y;
2573
2574 l.b.x = lineguy[i].b.x;
2575 l.b.y = lineguy[i].b.y;
2576
2577 if (scale) {
2578 l.b.x = MapMul(scale, l.b.x);
2579 l.b.y = MapMul(scale, l.b.y);
2580 }
2581
2582 if (angle)
2583 AM_rotate(&l.b.x, &l.b.y, angle);
2584
2585 l.b.x += x;
2586 l.b.y += y;
2587
2588 AM_drawMline(&l, color);
2589 }
2590 }
2591
2592 //=============================================================================
2593 //
2594 //
2595 //
2596 //=============================================================================
2597
AM_drawPlayers()2598 void AM_drawPlayers ()
2599 {
2600 if (am_cheat >= 2 && am_cheat != 4 && am_showthingsprites > 0)
2601 {
2602 // Player sprites are drawn with the others
2603 return;
2604 }
2605
2606 mpoint_t pt;
2607 angle_t angle;
2608 int i;
2609
2610 if (!multiplayer)
2611 {
2612 mline_t *arrow;
2613 int numarrowlines;
2614
2615 pt.x = players[consoleplayer].camera->X() >> FRACTOMAPBITS;
2616 pt.y = players[consoleplayer].camera->Y() >> FRACTOMAPBITS;
2617 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
2618 {
2619 angle = ANG90;
2620 AM_rotatePoint (&pt.x, &pt.y);
2621 }
2622 else
2623 {
2624 angle = players[consoleplayer].camera->angle;
2625 }
2626
2627 if (am_cheat != 0 && CheatMapArrow.Size() > 0)
2628 {
2629 arrow = &CheatMapArrow[0];
2630 numarrowlines = CheatMapArrow.Size();
2631 }
2632 else
2633 {
2634 arrow = &MapArrow[0];
2635 numarrowlines = MapArrow.Size();
2636 }
2637 AM_drawLineCharacter(arrow, numarrowlines, 0, angle, AMColors[AMColors.YourColor], pt.x, pt.y);
2638 return;
2639 }
2640
2641 for (i = 0; i < MAXPLAYERS; i++)
2642 {
2643 player_t *p = &players[i];
2644 AMColor color;
2645
2646 if (!playeringame[i] || p->mo == NULL)
2647 {
2648 continue;
2649 }
2650
2651 // We don't always want to show allies on the automap.
2652 if (dmflags2 & DF2_NO_AUTOMAP_ALLIES && i != consoleplayer)
2653 continue;
2654
2655 if (deathmatch && !demoplayback &&
2656 !p->mo->IsTeammate (players[consoleplayer].mo) &&
2657 p != players[consoleplayer].camera->player)
2658 {
2659 continue;
2660 }
2661
2662 if (p->mo->alpha < OPAQUE)
2663 {
2664 color = AMColors[AMColors.AlmostBackgroundColor];
2665 }
2666 else
2667 {
2668 float h, s, v, r, g, b;
2669
2670 D_GetPlayerColor (i, &h, &s, &v, NULL);
2671 HSVtoRGB (&r, &g, &b, h, s, v);
2672
2673 color.FromRGB(clamp (int(r*255.f),0,255), clamp (int(g*255.f),0,255), clamp (int(b*255.f),0,255));
2674 }
2675
2676 if (p->mo != NULL)
2677 {
2678 pt.x = p->mo->X() >> FRACTOMAPBITS;
2679 pt.y = p->mo->Y() >> FRACTOMAPBITS;
2680 angle = p->mo->angle;
2681
2682 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
2683 {
2684 AM_rotatePoint (&pt.x, &pt.y);
2685 angle -= players[consoleplayer].camera->angle - ANG90;
2686 }
2687
2688 AM_drawLineCharacter(&MapArrow[0], MapArrow.Size(), 0, angle, color, pt.x, pt.y);
2689 }
2690 }
2691 }
2692
2693 //=============================================================================
2694 //
2695 //
2696 //
2697 //=============================================================================
2698
AM_drawKeys()2699 void AM_drawKeys ()
2700 {
2701 AMColor color;
2702 mpoint_t p;
2703 angle_t angle;
2704
2705 TThinkerIterator<AKey> it;
2706 AKey *key;
2707
2708 while ((key = it.Next()) != NULL)
2709 {
2710 p.x = key->X() >> FRACTOMAPBITS;
2711 p.y = key->Y() >> FRACTOMAPBITS;
2712 angle = key->angle;
2713
2714 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
2715 {
2716 AM_rotatePoint (&p.x, &p.y);
2717 angle += ANG90 - players[consoleplayer].camera->angle;
2718 }
2719
2720 if (key->flags & MF_SPECIAL)
2721 {
2722 // Find the key's own color.
2723 // Only works correctly if single-key locks have lower numbers than any-key locks.
2724 // That is the case for all default keys, however.
2725 int P_GetMapColorForKey (AInventory * key);
2726 int c = P_GetMapColorForKey(key);
2727
2728 if (c >= 0) color.FromRGB(RPART(c), GPART(c), BPART(c));
2729 else color = AMColors[AMColors.ThingColor_CountItem];
2730 AM_drawLineCharacter(&EasyKey[0], EasyKey.Size(), 0, 0, color, p.x, p.y);
2731 }
2732 }
2733 }
2734
2735 //=============================================================================
2736 //
2737 //
2738 //
2739 //=============================================================================
AM_drawThings()2740 void AM_drawThings ()
2741 {
2742 AMColor color;
2743 int i;
2744 AActor* t;
2745 mpoint_t p;
2746 angle_t angle;
2747
2748 for (i=0;i<numsectors;i++)
2749 {
2750 t = sectors[i].thinglist;
2751 while (t)
2752 {
2753 if (am_cheat > 0 || !(t->flags6 & MF6_NOTONAUTOMAP))
2754 {
2755 p.x = t->X() >> FRACTOMAPBITS;
2756 p.y = t->Y() >> FRACTOMAPBITS;
2757
2758 if (am_showthingsprites > 0 && t->sprite > 0)
2759 {
2760 FTexture *texture = NULL;
2761 spriteframe_t *frame;
2762 angle_t rotation = 0;
2763
2764 // try all modes backwards until a valid texture has been found.
2765 for(int show = am_showthingsprites; show > 0 && texture == NULL; show--)
2766 {
2767 const spritedef_t& sprite = sprites[t->sprite];
2768 const size_t spriteIndex = sprite.spriteframes + (show > 1 ? t->frame : 0);
2769
2770 frame = &SpriteFrames[spriteIndex];
2771 angle_t angle = ANGLE_270 - t->angle;
2772 if (frame->Texture[0] != frame->Texture[1]) angle += (ANGLE_180 / 16);
2773 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
2774 {
2775 angle += players[consoleplayer].camera->angle - ANGLE_90;
2776 }
2777 rotation = angle >> 28;
2778
2779 const FTextureID textureID = frame->Texture[show > 2 ? rotation : 0];
2780 texture = TexMan(textureID);
2781 }
2782
2783 if (texture == NULL) goto drawTriangle; // fall back to standard display if no sprite can be found.
2784
2785 const fixed_t spriteXScale = FixedMul(t->scaleX, 10 * scale_mtof);
2786 const fixed_t spriteYScale = FixedMul(t->scaleY, 10 * scale_mtof);
2787
2788 DrawMarker (texture, p.x, p.y, 0, !!(frame->Flip & (1 << rotation)),
2789 spriteXScale, spriteYScale, t->Translation, FRACUNIT, 0, LegacyRenderStyles[STYLE_Normal]);
2790 }
2791 else
2792 {
2793 drawTriangle:
2794 angle = t->angle;
2795
2796 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
2797 {
2798 AM_rotatePoint (&p.x, &p.y);
2799 angle += ANG90 - players[consoleplayer].camera->angle;
2800 }
2801
2802 color = AMColors[AMColors.ThingColor];
2803
2804 // use separate colors for special thing types
2805 if (t->flags3&MF3_ISMONSTER && !(t->flags&MF_CORPSE))
2806 {
2807 if (t->flags & MF_FRIENDLY) color = AMColors[AMColors.ThingColor_Friend];
2808 else if (!(t->flags & MF_COUNTKILL)) color = AMColors[AMColors.ThingColor_NocountMonster];
2809 else color = AMColors[AMColors.ThingColor_Monster];
2810 }
2811 else if (t->flags&MF_SPECIAL)
2812 {
2813 // Find the key's own color.
2814 // Only works correctly if single-key locks have lower numbers than any-key locks.
2815 // That is the case for all default keys, however.
2816 if (t->IsKindOf(RUNTIME_CLASS(AKey)))
2817 {
2818 if (G_SkillProperty(SKILLP_EasyKey))
2819 {
2820 // Already drawn by AM_drawKeys(), so don't draw again
2821 color.Index = -1;
2822 }
2823 else if (am_showkeys)
2824 {
2825 int P_GetMapColorForKey (AInventory * key);
2826 int c = P_GetMapColorForKey(static_cast<AKey *>(t));
2827
2828 if (c >= 0) color.FromRGB(RPART(c), GPART(c), BPART(c));
2829 else color = AMColors[AMColors.ThingColor_CountItem];
2830 AM_drawLineCharacter(&CheatKey[0], CheatKey.Size(), 0, 0, color, p.x, p.y);
2831 color.Index = -1;
2832 }
2833 else
2834 {
2835 color = AMColors[AMColors.ThingColor_Item];
2836 }
2837 }
2838 else if (t->flags&MF_COUNTITEM)
2839 color = AMColors[AMColors.ThingColor_CountItem];
2840 else
2841 color = AMColors[AMColors.ThingColor_Item];
2842 }
2843
2844 if (color.Index != -1)
2845 {
2846 AM_drawLineCharacter
2847 (thintriangle_guy, NUMTHINTRIANGLEGUYLINES,
2848 16<<MAPBITS, angle, color, p.x, p.y);
2849 }
2850
2851 if (am_cheat == 3 || am_cheat == 6)
2852 {
2853 static const mline_t box[4] =
2854 {
2855 { { -MAPUNIT, -MAPUNIT }, { MAPUNIT, -MAPUNIT } },
2856 { { MAPUNIT, -MAPUNIT }, { MAPUNIT, MAPUNIT } },
2857 { { MAPUNIT, MAPUNIT }, { -MAPUNIT, MAPUNIT } },
2858 { { -MAPUNIT, MAPUNIT }, { -MAPUNIT, -MAPUNIT } },
2859 };
2860
2861 AM_drawLineCharacter (box, 4, t->radius >> FRACTOMAPBITS, angle - t->angle, color, p.x, p.y);
2862 }
2863 }
2864 }
2865 t = t->snext;
2866 }
2867 }
2868 }
2869
2870 //=============================================================================
2871 //
2872 //
2873 //
2874 //=============================================================================
2875
DrawMarker(FTexture * tex,fixed_t x,fixed_t y,int yadjust,INTBOOL flip,fixed_t xscale,fixed_t yscale,int translation,fixed_t alpha,DWORD fillcolor,FRenderStyle renderstyle)2876 static void DrawMarker (FTexture *tex, fixed_t x, fixed_t y, int yadjust,
2877 INTBOOL flip, fixed_t xscale, fixed_t yscale, int translation, fixed_t alpha, DWORD fillcolor, FRenderStyle renderstyle)
2878 {
2879 if (tex == NULL || tex->UseType == FTexture::TEX_Null)
2880 {
2881 return;
2882 }
2883 if (am_rotate == 1 || (am_rotate == 2 && viewactive))
2884 {
2885 AM_rotatePoint (&x, &y);
2886 }
2887 screen->DrawTexture (tex, CXMTOF(x) + f_x, CYMTOF(y) + yadjust + f_y,
2888 DTA_DestWidth, MulScale16 (tex->GetScaledWidth() * CleanXfac, xscale),
2889 DTA_DestHeight, MulScale16 (tex->GetScaledHeight() * CleanYfac, yscale),
2890 DTA_ClipTop, f_y,
2891 DTA_ClipBottom, f_y + f_h,
2892 DTA_ClipLeft, f_x,
2893 DTA_ClipRight, f_x + f_w,
2894 DTA_FlipX, flip,
2895 DTA_Translation, TranslationToTable(translation),
2896 DTA_Alpha, alpha,
2897 DTA_FillColor, fillcolor,
2898 DTA_RenderStyle, DWORD(renderstyle),
2899 TAG_DONE);
2900 }
2901
2902 //=============================================================================
2903 //
2904 //
2905 //
2906 //=============================================================================
2907
AM_drawMarks()2908 void AM_drawMarks ()
2909 {
2910 for (int i = 0; i < AM_NUMMARKPOINTS; i++)
2911 {
2912 if (markpoints[i].x != -1)
2913 {
2914 DrawMarker (TexMan(marknums[i]), markpoints[i].x, markpoints[i].y, -3, 0,
2915 FRACUNIT, FRACUNIT, 0, FRACUNIT, 0, LegacyRenderStyles[STYLE_Normal]);
2916 }
2917 }
2918 }
2919
2920 //=============================================================================
2921 //
2922 //
2923 //
2924 //=============================================================================
2925
AM_drawAuthorMarkers()2926 void AM_drawAuthorMarkers ()
2927 {
2928 // [RH] Draw any actors derived from AMapMarker on the automap.
2929 // If args[0] is 0, then the actor's sprite is drawn at its own location.
2930 // Otherwise, its sprite is drawn at the location of any actors whose TIDs match args[0].
2931 TThinkerIterator<AMapMarker> it (STAT_MAPMARKER);
2932 AMapMarker *mark;
2933
2934 while ((mark = it.Next()) != NULL)
2935 {
2936 if (mark->flags2 & MF2_DORMANT)
2937 {
2938 continue;
2939 }
2940
2941 FTextureID picnum;
2942 FTexture *tex;
2943 WORD flip = 0;
2944
2945 if (mark->picnum.isValid())
2946 {
2947 tex = TexMan(mark->picnum);
2948 if (tex->Rotations != 0xFFFF)
2949 {
2950 spriteframe_t *sprframe = &SpriteFrames[tex->Rotations];
2951 picnum = sprframe->Texture[0];
2952 flip = sprframe->Flip & 1;
2953 tex = TexMan[picnum];
2954 }
2955 }
2956 else
2957 {
2958 spritedef_t *sprdef = &sprites[mark->sprite];
2959 if (mark->frame >= sprdef->numframes)
2960 {
2961 continue;
2962 }
2963 else
2964 {
2965 spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + mark->frame];
2966 picnum = sprframe->Texture[0];
2967 flip = sprframe->Flip & 1;
2968 tex = TexMan[picnum];
2969 }
2970 }
2971 FActorIterator it (mark->args[0]);
2972 AActor *marked = mark->args[0] == 0 ? mark : it.Next();
2973
2974 while (marked != NULL)
2975 {
2976 // Use more correct info if we have GL nodes available
2977 if (mark->args[1] == 0 ||
2978 (mark->args[1] == 1 && (hasglnodes ?
2979 marked->subsector->flags & SSECF_DRAWN :
2980 marked->Sector->MoreFlags & SECF_DRAWN)))
2981 {
2982 DrawMarker (tex, marked->X() >> FRACTOMAPBITS, marked->Y() >> FRACTOMAPBITS, 0,
2983 flip, mark->scaleX, mark->scaleY, mark->Translation,
2984 mark->alpha, mark->fillcolor, mark->RenderStyle);
2985 }
2986 marked = mark->args[0] != 0 ? it.Next() : NULL;
2987 }
2988 }
2989 }
2990
2991 //=============================================================================
2992 //
2993 //
2994 //
2995 //=============================================================================
2996
AM_drawCrosshair(const AMColor & color)2997 void AM_drawCrosshair (const AMColor &color)
2998 {
2999 screen->DrawPixel(f_w/2, (f_h+1)/2, color.Index, color.RGB);
3000 }
3001
3002 //=============================================================================
3003 //
3004 //
3005 //
3006 //=============================================================================
3007
AM_Drawer()3008 void AM_Drawer ()
3009 {
3010 if (!automapactive)
3011 return;
3012
3013 bool allmap = (level.flags2 & LEVEL2_ALLMAP) != 0;
3014 bool allthings = allmap && players[consoleplayer].mo->FindInventory(RUNTIME_CLASS(APowerScanner), true) != NULL;
3015
3016 AM_initColors (viewactive);
3017
3018 if (!viewactive)
3019 {
3020 // [RH] Set f_? here now to handle automap overlaying
3021 // and view size adjustments.
3022 f_x = f_y = 0;
3023 f_w = screen->GetWidth ();
3024 f_h = ST_Y;
3025 f_p = screen->GetPitch ();
3026
3027 AM_clearFB(AMColors[AMColors.Background]);
3028 }
3029 else
3030 {
3031 f_x = viewwindowx;
3032 f_y = viewwindowy;
3033 f_w = viewwidth;
3034 f_h = viewheight;
3035 f_p = screen->GetPitch ();
3036 }
3037 AM_activateNewScale();
3038
3039 if (am_textured && hasglnodes && textured && !viewactive)
3040 AM_drawSubsectors();
3041
3042 if (grid)
3043 AM_drawGrid(AMColors.GridColor);
3044
3045 AM_drawWalls(allmap);
3046 AM_drawPlayers();
3047 if (G_SkillProperty(SKILLP_EasyKey))
3048 AM_drawKeys();
3049 if ((am_cheat >= 2 && am_cheat != 4) || allthings)
3050 AM_drawThings();
3051
3052 AM_drawAuthorMarkers();
3053
3054 if (!viewactive)
3055 AM_drawCrosshair(AMColors[AMColors.XHairColor]);
3056
3057 AM_drawMarks();
3058
3059 AM_showSS();
3060 }
3061
3062 //=============================================================================
3063 //
3064 //
3065 //
3066 //=============================================================================
3067
AM_SerializeMarkers(FArchive & arc)3068 void AM_SerializeMarkers(FArchive &arc)
3069 {
3070 arc << markpointnum;
3071 for (int i=0; i<AM_NUMMARKPOINTS; i++)
3072 {
3073 arc << markpoints[i].x << markpoints[i].y;
3074 }
3075 arc << scale_mtof;
3076 arc << scale_ftom;
3077 }
3078