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