1 /*
2 ** decallib.cpp
3 ** Parses DECALDEFs and creates a "library" of decals
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2006 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 #include "decallib.h"
36 #include "sc_man.h"
37 #include "w_wad.h"
38 #include "v_video.h"
39 #include "v_palette.h"
40 #include "cmdlib.h"
41 #include "m_random.h"
42 #include "weightedlist.h"
43 #include "statnums.h"
44 #include "templates.h"
45 #include "a_sharedglobal.h"
46 #include "r_data/r_translate.h"
47 #include "gi.h"
48 #include "g_level.h"
49 #include "colormatcher.h"
50 #include "b_bot.h"
51 #include "farchive.h"
52 
53 FDecalLib DecalLibrary;
54 
55 static fixed_t ReadScale (FScanner &sc);
56 static TArray<BYTE> DecalTranslations;
57 
58 // A decal group holds multiple decals and returns one randomly
59 // when GetDecal() is called.
60 
61 // Sometimes two machines in a game will disagree on the state of
62 // decals. I do not know why.
63 
64 static FRandom pr_decalchoice ("DecalChoice");
65 static FRandom pr_decal ("Decal");
66 
67 class FDecalGroup : public FDecalBase
68 {
69 	friend class FDecalLib;
70 
71 public:
FDecalGroup()72 	FDecalGroup () : Choices (pr_decalchoice) {}
73 	const FDecalTemplate *GetDecal () const;
ReplaceDecalRef(FDecalBase * from,FDecalBase * to)74 	void ReplaceDecalRef (FDecalBase *from, FDecalBase *to)
75 	{
76 		Choices.ReplaceValues(from, to);
77 	}
AddDecal(FDecalBase * decal,WORD weight)78 	void AddDecal (FDecalBase *decal, WORD weight)
79 	{
80 		Choices.AddEntry (decal, weight);
81 	}
82 
83 private:
84 	TWeightedList<FDecalBase *> Choices;
85 
operator =(const FDecalGroup &)86 	FDecalGroup &operator= (const FDecalGroup &) { return *this; }
87 };
88 
89 struct FDecalLib::FTranslation
90 {
91 	FTranslation (DWORD start, DWORD end);
92 	FTranslation *LocateTranslation (DWORD start, DWORD end);
93 
94 	DWORD StartColor, EndColor;
95 	FTranslation *Next;
96 	WORD Index;
97 };
98 
99 struct FDecalAnimator
100 {
101 	FDecalAnimator (const char *name);
102 	virtual ~FDecalAnimator ();
103 	virtual DThinker *CreateThinker (DBaseDecal *actor, side_t *wall) const = 0;
104 
105 	FName Name;
106 };
107 
108 TDeletingArray<FDecalAnimator *> Animators;
109 
110 struct DDecalThinker : public DThinker
111 {
112 	DECLARE_CLASS (DDecalThinker, DThinker)
113 	HAS_OBJECT_POINTERS
114 public:
DDecalThinkerDDecalThinker115 	DDecalThinker (DBaseDecal *decal) : DThinker (STAT_DECALTHINKER), TheDecal (decal) {}
116 	void Serialize (FArchive &arc);
117 	TObjPtr<DBaseDecal> TheDecal;
118 protected:
DDecalThinkerDDecalThinker119 	DDecalThinker () : DThinker (STAT_DECALTHINKER) {}
120 };
121 
122 IMPLEMENT_POINTY_CLASS (DDecalThinker)
DECLARE_POINTER(TheDecal)123  DECLARE_POINTER (TheDecal)
124 END_POINTERS
125 
126 void DDecalThinker::Serialize (FArchive &arc)
127 {
128 	Super::Serialize (arc);
129 	arc << TheDecal;
130 }
131 
132 struct FDecalFaderAnim : public FDecalAnimator
133 {
FDecalFaderAnimFDecalFaderAnim134 	FDecalFaderAnim (const char *name) : FDecalAnimator (name) {}
135 	DThinker *CreateThinker (DBaseDecal *actor, side_t *wall) const;
136 
137 	int DecayStart;
138 	int DecayTime;
139 };
140 
141 class DDecalFader : public DDecalThinker
142 {
143 	DECLARE_CLASS (DDecalFader, DDecalThinker)
144 public:
DDecalFader(DBaseDecal * decal)145 	DDecalFader (DBaseDecal *decal) : DDecalThinker (decal) {}
146 	void Serialize (FArchive &arc);
147 	void Tick ();
148 
149 	int TimeToStartDecay;
150 	int TimeToEndDecay;
151 	fixed_t StartTrans;
152 private:
DDecalFader()153 	DDecalFader () {}
154 };
155 
156 struct FDecalColorerAnim : public FDecalAnimator
157 {
FDecalColorerAnimFDecalColorerAnim158 	FDecalColorerAnim (const char *name) : FDecalAnimator (name) {}
159 	DThinker *CreateThinker (DBaseDecal *actor, side_t *wall) const;
160 
161 	int DecayStart;
162 	int DecayTime;
163 	PalEntry GoalColor;
164 };
165 
166 class DDecalColorer : public DDecalThinker
167 {
168 	DECLARE_CLASS (DDecalColorer, DDecalThinker)
169 public:
DDecalColorer(DBaseDecal * decal)170 	DDecalColorer (DBaseDecal *decal) : DDecalThinker (decal) {}
171 	void Serialize (FArchive &arc);
172 	void Tick ();
173 
174 	int TimeToStartDecay;
175 	int TimeToEndDecay;
176 	PalEntry StartColor;
177 	PalEntry GoalColor;
178 private:
DDecalColorer()179 	DDecalColorer () {}
180 };
181 
182 struct FDecalStretcherAnim : public FDecalAnimator
183 {
FDecalStretcherAnimFDecalStretcherAnim184 	FDecalStretcherAnim (const char *name) : FDecalAnimator (name) {}
185 	DThinker *CreateThinker (DBaseDecal *actor, side_t *wall) const;
186 
187 	int StretchStart;
188 	int StretchTime;
189 	fixed_t GoalX, GoalY;
190 };
191 
192 class DDecalStretcher : public DDecalThinker
193 {
194 	DECLARE_CLASS (DDecalStretcher, DDecalThinker)
195 public:
DDecalStretcher(DBaseDecal * decal)196 	DDecalStretcher (DBaseDecal *decal) : DDecalThinker (decal) {}
197 	void Serialize (FArchive &arc);
198 	void Tick ();
199 
200 	int TimeToStart;
201 	int TimeToStop;
202 	fixed_t GoalX;
203 	fixed_t StartX;
204 	fixed_t GoalY;
205 	fixed_t StartY;
206 	bool bStretchX;
207 	bool bStretchY;
208 	bool bStarted;
209 private:
DDecalStretcher()210 	DDecalStretcher () {}
211 };
212 
213 struct FDecalSliderAnim : public FDecalAnimator
214 {
FDecalSliderAnimFDecalSliderAnim215 	FDecalSliderAnim (const char *name) : FDecalAnimator (name) {}
216 	DThinker *CreateThinker (DBaseDecal *actor, side_t *wall) const;
217 
218 	int SlideStart;
219 	int SlideTime;
220 	fixed_t /*DistX,*/ DistY;
221 };
222 
223 class DDecalSlider : public DDecalThinker
224 {
225 	DECLARE_CLASS (DDecalSlider, DDecalThinker)
226 public:
DDecalSlider(DBaseDecal * decal)227 	DDecalSlider (DBaseDecal *decal) : DDecalThinker (decal) {}
228 	void Serialize (FArchive &arc);
229 	void Tick ();
230 
231 	int TimeToStart;
232 	int TimeToStop;
233 /*	fixed_t DistX; */
234 	fixed_t DistY;
235 	fixed_t StartX;
236 	fixed_t StartY;
237 	bool bStarted;
238 private:
DDecalSlider()239 	DDecalSlider () {}
240 };
241 
242 struct FDecalCombinerAnim : public FDecalAnimator
243 {
FDecalCombinerAnimFDecalCombinerAnim244 	FDecalCombinerAnim (const char *name) : FDecalAnimator (name) {}
245 	DThinker *CreateThinker (DBaseDecal *actor, side_t *wall) const;
246 
247 	int FirstAnimator;
248 	int NumAnimators;
249 
250 	static TArray<FDecalAnimator *> AnimatorList;
251 };
252 
253 TArray<FDecalAnimator *> FDecalCombinerAnim::AnimatorList;
254 
255 static const char *DecalKeywords[] =
256 {
257 	"x-scale",
258 	"y-scale",
259 	"pic",
260 	"solid",
261 	"add",
262 	"translucent",
263 	"flipx",
264 	"flipy",
265 	"randomflipx",
266 	"randomflipy",
267 	"fullbright",
268 	"fuzzy",
269 	"shade",
270 	"colors",
271 	"animator",
272 	"lowerdecal",
273 	NULL
274 };
275 
276 enum
277 {
278 	DECAL_XSCALE,
279 	DECAL_YSCALE,
280 	DECAL_PIC,
281 	DECAL_SOLID,
282 	DECAL_ADD,
283 	DECAL_TRANSLUCENT,
284 	DECAL_FLIPX,
285 	DECAL_FLIPY,
286 	DECAL_RANDOMFLIPX,
287 	DECAL_RANDOMFLIPY,
288 	DECAL_FULLBRIGHT,
289 	DECAL_FUZZY,
290 	DECAL_SHADE,
291 	DECAL_COLORS,
292 	DECAL_ANIMATOR,
293 	DECAL_LOWERDECAL
294 };
295 
GetDecal() const296 const FDecalTemplate *FDecalBase::GetDecal () const
297 {
298 	return NULL;
299 }
300 
ReplaceDecalRef(FDecalBase * from,FDecalBase * to)301 void FDecalTemplate::ReplaceDecalRef(FDecalBase *from, FDecalBase *to)
302 {
303 	if (LowerDecal == from)
304 	{
305 		LowerDecal = to;
306 	}
307 }
308 
FDecalLib()309 FDecalLib::FDecalLib ()
310 {
311 	Root = NULL;
312 	Translations = NULL;
313 }
314 
~FDecalLib()315 FDecalLib::~FDecalLib ()
316 {
317 	Clear ();
318 }
319 
Clear()320 void FDecalLib::Clear ()
321 {
322 	FTranslation *trans;
323 
324 	DelTree (Root);
325 	Root = NULL;
326 
327 	trans = Translations;
328 	while (trans != NULL)
329 	{
330 		FTranslation *next = trans->Next;
331 		delete trans;
332 		trans = next;
333 	}
334 }
335 
DelTree(FDecalBase * root)336 void FDecalLib::DelTree (FDecalBase *root)
337 {
338 	if (root != NULL)
339 	{
340 		DelTree (root->Left);
341 		DelTree (root->Right);
342 		delete root;
343 	}
344 }
345 
ReadAllDecals()346 void FDecalLib::ReadAllDecals ()
347 {
348 	int lump, lastlump = 0;
349 	unsigned int i;
350 
351 	for(unsigned i=0;i<Animators.Size(); i++)
352 	{
353 		delete Animators[i];
354 	}
355 	Animators.Clear();
356 	FDecalCombinerAnim::AnimatorList.Clear();
357 	DecalTranslations.Clear();
358 
359 	DecalLibrary.Clear();
360 
361 	while ((lump = Wads.FindLump ("DECALDEF", &lastlump)) != -1)
362 	{
363 		FScanner sc(lump);
364 		ReadDecals (sc);
365 	}
366 	// Supporting code to allow specifying decals directly in the DECORATE lump
367 	for (i = 0; i < PClass::m_RuntimeActors.Size(); i++)
368 	{
369 		AActor *def = (AActor*)GetDefaultByType (PClass::m_RuntimeActors[i]);
370 
371 		FName v = ENamedName(intptr_t(def->DecalGenerator));
372 		if (v.IsValidName())
373 		{
374 			def->DecalGenerator = ScanTreeForName (v, Root);
375 		}
376 	}
377 }
378 
ReadDecals(FScanner & sc)379 void FDecalLib::ReadDecals(FScanner &sc)
380 {
381 	while (sc.GetString())
382 	{
383 		if (sc.Compare("decal"))
384 		{
385 			ParseDecal(sc);
386 		}
387 		else if (sc.Compare("decalgroup"))
388 		{
389 			ParseDecalGroup(sc);
390 		}
391 		else if (sc.Compare("generator"))
392 		{
393 			ParseGenerator(sc);
394 		}
395 		else if (sc.Compare("fader"))
396 		{
397 			ParseFader(sc);
398 		}
399 		else if (sc.Compare("stretcher"))
400 		{
401 			ParseStretcher(sc);
402 		}
403 		else if (sc.Compare("slider"))
404 		{
405 			ParseSlider(sc);
406 		}
407 		else if (sc.Compare("combiner"))
408 		{
409 			ParseCombiner(sc);
410 		}
411 		else if (sc.Compare("colorchanger"))
412 		{
413 			ParseColorchanger(sc);
414 		}
415 		else
416 		{
417 			sc.ScriptError("Unknown decaldef keyword '%s'", sc.String);
418 		}
419 	}
420 }
421 
GetDecalID(FScanner & sc)422 WORD FDecalLib::GetDecalID (FScanner &sc)
423 {
424 	sc.MustGetString ();
425 	if (!IsNum (sc.String))
426 	{
427 		sc.UnGet ();
428 		return 0;
429 	}
430 	else
431 	{
432 		unsigned long num = strtoul (sc.String, NULL, 10);
433 		if (num < 1 || num > 65535)
434 		{
435 			sc.ScriptError ("Decal ID must be between 1 and 65535");
436 		}
437 		return (WORD)num;
438 	}
439 }
440 
ParseDecal(FScanner & sc)441 void FDecalLib::ParseDecal (FScanner &sc)
442 {
443 	FString decalName;
444 	WORD decalNum;
445 	FDecalTemplate newdecal;
446 	FTextureID picnum;
447 	int lumpnum;
448 
449 	sc.MustGetString ();
450 	decalName = sc.String;
451 	decalNum = GetDecalID (sc);
452 	sc.MustGetStringName ("{");
453 
454 	memset ((void *)&newdecal, 0, sizeof(newdecal));
455 	newdecal.PicNum.SetInvalid();
456 	newdecal.ScaleX = newdecal.ScaleY = FRACUNIT;
457 	newdecal.RenderFlags = RF_WALLSPRITE;
458 	newdecal.RenderStyle = STYLE_Normal;
459 	newdecal.Alpha = 0x8000;
460 
461 	for (;;)
462 	{
463 		sc.MustGetString ();
464 		if (sc.Compare ("}"))
465 		{
466 			AddDecal (decalName, decalNum, newdecal);
467 			break;
468 		}
469 		switch (sc.MustMatchString (DecalKeywords))
470 		{
471 		case DECAL_XSCALE:
472 			newdecal.ScaleX = ReadScale (sc);
473 			break;
474 
475 		case DECAL_YSCALE:
476 			newdecal.ScaleY = ReadScale (sc);
477 			break;
478 
479 		case DECAL_PIC:
480 			sc.MustGetString ();
481 			picnum = TexMan.CheckForTexture (sc.String, FTexture::TEX_Any);
482 			if (!picnum.Exists() && (lumpnum = Wads.CheckNumForName (sc.String, ns_graphics)) >= 0)
483 			{
484 				picnum = TexMan.CreateTexture (lumpnum, FTexture::TEX_Decal);
485 			}
486 			newdecal.PicNum = picnum;
487 			break;
488 
489 		case DECAL_SOLID:
490 			newdecal.RenderStyle = STYLE_Normal;
491 			break;
492 
493 		case DECAL_ADD:
494 			sc.MustGetFloat ();
495 			newdecal.Alpha = (WORD)(32768.f * sc.Float);
496 			newdecal.RenderStyle = STYLE_Add;
497 			break;
498 
499 		case DECAL_TRANSLUCENT:
500 			sc.MustGetFloat ();
501 			newdecal.Alpha = (WORD)(32768.f * sc.Float);
502 			newdecal.RenderStyle = STYLE_Translucent;
503 			break;
504 
505 		case DECAL_FLIPX:
506 			newdecal.RenderFlags |= RF_XFLIP;
507 			break;
508 
509 		case DECAL_FLIPY:
510 			newdecal.RenderFlags |= RF_YFLIP;
511 			break;
512 
513 		case DECAL_RANDOMFLIPX:
514 			newdecal.RenderFlags |= FDecalTemplate::DECAL_RandomFlipX;
515 			break;
516 
517 		case DECAL_RANDOMFLIPY:
518 			newdecal.RenderFlags |= FDecalTemplate::DECAL_RandomFlipY;
519 			break;
520 
521 		case DECAL_FULLBRIGHT:
522 			newdecal.RenderFlags |= RF_FULLBRIGHT;
523 			break;
524 
525 		case DECAL_FUZZY:
526 			newdecal.RenderStyle = STYLE_Fuzzy;
527 			break;
528 
529 		case DECAL_SHADE:
530 			sc.MustGetString ();
531 			if (!sc.Compare("BloodDefault"))
532 			{
533 				newdecal.ShadeColor = V_GetColor (NULL, sc.String);
534 			}
535 			else
536 			{
537 				newdecal.ShadeColor = gameinfo.defaultbloodcolor;
538 			}
539 			newdecal.RenderStyle = STYLE_Shaded;
540 			newdecal.ShadeColor |=
541 				ColorMatcher.Pick (RPART(newdecal.ShadeColor),
542 					GPART(newdecal.ShadeColor), BPART(newdecal.ShadeColor)) << 24;
543 			break;
544 
545 		case DECAL_COLORS:
546 			DWORD startcolor, endcolor;
547 
548 			sc.MustGetString (); startcolor = V_GetColor (NULL, sc.String);
549 			sc.MustGetString (); endcolor   = V_GetColor (NULL, sc.String);
550 			newdecal.Translation = GenerateTranslation (startcolor, endcolor)->Index;
551 			break;
552 
553 		case DECAL_ANIMATOR:
554 			sc.MustGetString ();
555 			newdecal.Animator = FindAnimator (sc.String);
556 			break;
557 
558 		case DECAL_LOWERDECAL:
559 			sc.MustGetString ();
560 			newdecal.LowerDecal = GetDecalByName (sc.String);
561 			break;
562 		}
563 	}
564 }
565 
ParseDecalGroup(FScanner & sc)566 void FDecalLib::ParseDecalGroup (FScanner &sc)
567 {
568 	FString groupName;
569 	WORD decalNum;
570 	FDecalBase *targetDecal;
571 	FDecalGroup *group;
572 
573 	sc.MustGetString ();
574 	groupName = sc.String;
575 	decalNum = GetDecalID (sc);
576 	sc.MustGetStringName ("{");
577 
578 	group = new FDecalGroup;
579 
580 	for (;;)
581 	{
582 		sc.MustGetString ();
583 		if (sc.Compare ("}"))
584 		{
585 			group->Name = groupName;
586 			group->SpawnID = decalNum;
587 			AddDecal (group);
588 			break;
589 		}
590 
591 		targetDecal = ScanTreeForName (sc.String, Root);
592 		if (targetDecal == NULL)
593 		{
594 			sc.ScriptError ("%s has not been defined", sc.String);
595 		}
596 		sc.MustGetNumber ();
597 
598 		group->AddDecal (targetDecal, sc.Number);
599 	}
600 }
601 
ParseGenerator(FScanner & sc)602 void FDecalLib::ParseGenerator (FScanner &sc)
603 {
604 	const PClass *type;
605 	FDecalBase *decal;
606 	bool optional = false;
607 
608 	// Get name of generator (actor)
609 	sc.MustGetString ();
610 	optional = sc.Compare("optional");
611 	if (optional) sc.MustGetString();
612 
613 	type = PClass::FindClass (sc.String);
614 	if (type == NULL || type->ActorInfo == NULL)
615 	{
616 		if (!optional) sc.ScriptError ("%s is not an actor.", sc.String);
617 	}
618 
619 	// Get name of generated decal
620 	sc.MustGetString ();
621 	if (stricmp (sc.String, "None") == 0)
622 	{
623 		decal = NULL;
624 	}
625 	else
626 	{
627 		decal = ScanTreeForName (sc.String, Root);
628 		if (decal == NULL)
629 		{
630 			if (!optional) sc.ScriptError ("%s has not been defined.", sc.String);
631 		}
632 	}
633 	if (type != NULL)
634 	{
635 		AActor *actor = (AActor *)type->Defaults;
636 		actor->DecalGenerator = decal;
637 		if (decal != NULL)
638 		{
639 			decal->Users.Push(type);
640 		}
641 	}
642 }
643 
ParseFader(FScanner & sc)644 void FDecalLib::ParseFader (FScanner &sc)
645 {
646 	FString faderName;
647 	int startTime = 0, decayTime = 0;
648 
649 	sc.MustGetString ();
650 	faderName = sc.String;
651 	sc.MustGetStringName ("{");
652 
653 	for (;;)
654 	{
655 		sc.MustGetString ();
656 		if (sc.Compare ("}"))
657 		{
658 			FDecalFaderAnim *fader = new FDecalFaderAnim (faderName);
659 			fader->DecayStart = startTime;
660 			fader->DecayTime = decayTime;
661 			Animators.Push (fader);
662 			break;
663 		}
664 		else if (sc.Compare ("DecayStart"))
665 		{
666 			sc.MustGetFloat ();
667 			startTime = (int)(sc.Float * TICRATE);
668 		}
669 		else if (sc.Compare ("DecayTime"))
670 		{
671 			sc.MustGetFloat ();
672 			decayTime = (int)(sc.Float * TICRATE);
673 		}
674 		else
675 		{
676 			sc.ScriptError ("Unknown fader parameter '%s'", sc.String);
677 		}
678 	}
679 }
680 
ParseStretcher(FScanner & sc)681 void FDecalLib::ParseStretcher (FScanner &sc)
682 {
683 	FString stretcherName;
684 	fixed_t goalX = -1, goalY = -1;
685 	int startTime = 0, takeTime = 0;
686 
687 	sc.MustGetString ();
688 	stretcherName = sc.String;
689 	sc.MustGetStringName ("{");
690 
691 	for (;;)
692 	{
693 		sc.MustGetString ();
694 		if (sc.Compare ("}"))
695 		{
696 			if (goalX >= 0 || goalY >= 0)
697 			{
698 				FDecalStretcherAnim *stretcher = new FDecalStretcherAnim (stretcherName);
699 				stretcher->StretchStart = startTime;
700 				stretcher->StretchTime = takeTime;
701 				stretcher->GoalX = goalX;
702 				stretcher->GoalY = goalY;
703 				Animators.Push (stretcher);
704 			}
705 			break;
706 		}
707 		else if (sc.Compare ("StretchStart"))
708 		{
709 			sc.MustGetFloat ();
710 			startTime = (int)(sc.Float * TICRATE);
711 		}
712 		else if (sc.Compare ("StretchTime"))
713 		{
714 			sc.MustGetFloat ();
715 			takeTime = (int)(sc.Float * TICRATE);
716 		}
717 		else if (sc.Compare ("GoalX"))
718 		{
719 			goalX = ReadScale (sc);
720 		}
721 		else if (sc.Compare ("GoalY"))
722 		{
723 			goalY = ReadScale (sc);
724 		}
725 		else
726 		{
727 			sc.ScriptError ("Unknown stretcher parameter '%s'", sc.String);
728 		}
729 	}
730 }
731 
ParseSlider(FScanner & sc)732 void FDecalLib::ParseSlider (FScanner &sc)
733 {
734 	FString sliderName;
735 	fixed_t distX = 0, distY = 0;
736 	int startTime = 0, takeTime = 0;
737 
738 	sc.MustGetString ();
739 	sliderName = sc.String;
740 	sc.MustGetStringName ("{");
741 
742 	for (;;)
743 	{
744 		sc.MustGetString ();
745 		if (sc.Compare ("}"))
746 		{
747 			if ((/*distX |*/ distY) != 0)
748 			{
749 				FDecalSliderAnim *slider = new FDecalSliderAnim (sliderName);
750 				slider->SlideStart = startTime;
751 				slider->SlideTime = takeTime;
752 				/*slider->DistX = distX;*/
753 				slider->DistY = distY;
754 				Animators.Push (slider);
755 			}
756 			break;
757 		}
758 		else if (sc.Compare ("SlideStart"))
759 		{
760 			sc.MustGetFloat ();
761 			startTime = (int)(sc.Float * TICRATE);
762 		}
763 		else if (sc.Compare ("SlideTime"))
764 		{
765 			sc.MustGetFloat ();
766 			takeTime = (int)(sc.Float * TICRATE);
767 		}
768 		else if (sc.Compare ("DistX"))
769 		{
770 			sc.MustGetFloat ();	// must remain to avoid breaking definitions that accidentally used DistX
771 			Printf ("DistX in slider decal %s is unsupported\n", sliderName.GetChars());
772 		}
773 		else if (sc.Compare ("DistY"))
774 		{
775 			sc.MustGetFloat ();
776 			distY = (fixed_t)(sc.Float * FRACUNIT);
777 		}
778 		else
779 		{
780 			sc.ScriptError ("Unknown slider parameter '%s'", sc.String);
781 		}
782 	}
783 }
784 
ParseColorchanger(FScanner & sc)785 void FDecalLib::ParseColorchanger (FScanner &sc)
786 {
787 	FString faderName;
788 	int startTime = 0, decayTime = 0;
789 	PalEntry goal = 0;
790 
791 	sc.MustGetString ();
792 	faderName = sc.String;
793 	sc.MustGetStringName ("{");
794 
795 	for (;;)
796 	{
797 		sc.MustGetString ();
798 		if (sc.Compare ("}"))
799 		{
800 			FDecalColorerAnim *fader = new FDecalColorerAnim (faderName);
801 			fader->DecayStart = startTime;
802 			fader->DecayTime = decayTime;
803 			fader->GoalColor = goal;
804 			Animators.Push (fader);
805 			break;
806 		}
807 		else if (sc.Compare ("FadeStart"))
808 		{
809 			sc.MustGetFloat ();
810 			startTime = (int)(sc.Float * TICRATE);
811 		}
812 		else if (sc.Compare ("FadeTime"))
813 		{
814 			sc.MustGetFloat ();
815 			decayTime = (int)(sc.Float * TICRATE);
816 		}
817 		else if (sc.Compare ("Color"))
818 		{
819 			sc.MustGetString ();
820 			goal = V_GetColor (NULL, sc.String);
821 		}
822 		else
823 		{
824 			sc.ScriptError ("Unknown color changer parameter '%s'", sc.String);
825 		}
826 	}
827 }
828 
ParseCombiner(FScanner & sc)829 void FDecalLib::ParseCombiner (FScanner &sc)
830 {
831 	FString combinerName;
832 	size_t first = FDecalCombinerAnim::AnimatorList.Size ();
833 
834 	sc.MustGetString ();
835 	combinerName = sc.String;
836 	sc.MustGetStringName ("{");
837 	sc.MustGetString ();
838 	while (!sc.Compare ("}"))
839 	{
840 		FDecalAnimator *anim = FindAnimator (sc.String);
841 		if (anim == NULL)
842 		{
843 			sc.ScriptError ("Undefined animator %s", sc.String);
844 		}
845 		FDecalCombinerAnim::AnimatorList.Push (anim);
846 		sc.MustGetString ();
847 	}
848 
849 	size_t last = FDecalCombinerAnim::AnimatorList.Size ();
850 
851 	if (last > first)
852 	{
853 		FDecalCombinerAnim *combiner = new FDecalCombinerAnim (combinerName);
854 		combiner->FirstAnimator = (int)first;
855 		combiner->NumAnimators = (int)(last - first);
856 		Animators.Push (combiner);
857 	}
858 }
859 
ReplaceDecalRef(FDecalBase * from,FDecalBase * to,FDecalBase * root)860 void FDecalLib::ReplaceDecalRef (FDecalBase *from, FDecalBase *to, FDecalBase *root)
861 {
862 	if (root == NULL)
863 	{
864 		return;
865 	}
866 	ReplaceDecalRef (from, to, root->Left);
867 	ReplaceDecalRef (from, to, root->Right);
868 	root->ReplaceDecalRef (from, to);
869 }
870 
AddDecal(const char * name,WORD num,const FDecalTemplate & decal)871 void FDecalLib::AddDecal (const char *name, WORD num, const FDecalTemplate &decal)
872 {
873 	FDecalTemplate *newDecal = new FDecalTemplate;
874 
875 	*newDecal = decal;
876 	newDecal->Name = name;
877 	newDecal->SpawnID = num;
878 	AddDecal (newDecal);
879 }
880 
AddDecal(FDecalBase * decal)881 void FDecalLib::AddDecal (FDecalBase *decal)
882 {
883 	FDecalBase *node = Root, **prev = &Root;
884 	int num = decal->SpawnID;
885 
886 	decal->SpawnID = 0;
887 
888 	// Check if this decal already exists.
889 	while (node != NULL)
890 	{
891 		int lexx = stricmp (decal->Name, node->Name);
892 		if (lexx == 0)
893 		{
894 			break;
895 		}
896 		else if (lexx < 0)
897 		{
898 			prev = &node->Left;
899 			node = node->Left;
900 		}
901 		else
902 		{
903 			prev = &node->Right;
904 			node = node->Right;
905 		}
906 	}
907 	if (node == NULL)
908 	{ // No, add it.
909 		decal->SpawnID = 0;
910 		*prev = decal;
911 		decal->Left = NULL;
912 		decal->Right = NULL;
913 	}
914 	else
915 	{ // Yes, replace the old one.
916 		// If this decal has been used as the lowerdecal for another decal,
917 		// be sure and update the lowerdecal to use the new decal.
918 		ReplaceDecalRef(node, decal, Root);
919 
920 		decal->Left = node->Left;
921 		decal->Right = node->Right;
922 		*prev = decal;
923 
924 		// Fix references to the old decal so that they use the new one instead.
925 		for (unsigned int i = 0; i < node->Users.Size(); ++i)
926 		{
927 			((AActor *)node->Users[i]->Defaults)->DecalGenerator = decal;
928 		}
929 		decal->Users = node->Users;
930 		delete node;
931 	}
932 	// If this decal has an ID, make sure no existing decals have the same ID.
933 	if (num != 0)
934 	{
935 		FDecalBase *spawner = ScanTreeForNum (num, Root);
936 		if (spawner != NULL)
937 		{
938 			spawner->SpawnID = 0;
939 		}
940 		decal->SpawnID = num;
941 	}
942 }
943 
GetDecalByNum(WORD num) const944 const FDecalTemplate *FDecalLib::GetDecalByNum (WORD num) const
945 {
946 	if (num == 0)
947 	{
948 		return NULL;
949 	}
950 	FDecalBase *base = ScanTreeForNum (num, Root);
951 	if (base != NULL)
952 	{
953 		return base->GetDecal ();
954 	}
955 	return NULL;
956 }
957 
GetDecalByName(const char * name) const958 const FDecalTemplate *FDecalLib::GetDecalByName (const char *name) const
959 {
960 	if (name == NULL)
961 	{
962 		return NULL;
963 	}
964 	FDecalBase *base = ScanTreeForName (name, Root);
965 	if (base != NULL)
966 	{
967 		return static_cast<FDecalTemplate *>(base);
968 	}
969 	return NULL;
970 }
971 
ScanTreeForNum(const WORD num,FDecalBase * root)972 FDecalBase *FDecalLib::ScanTreeForNum (const WORD num, FDecalBase *root)
973 {
974 	while (root != NULL)
975 	{
976 		if (root->SpawnID == num)
977 		{
978 			break;
979 		}
980 		FDecalBase *leftres = ScanTreeForNum (num, root->Left);
981 		if (leftres != NULL)
982 			return leftres;
983 		root = root->Right;		// Avoid tail-recursion
984 	}
985 	return root;
986 }
987 
ScanTreeForName(const char * name,FDecalBase * root)988 FDecalBase *FDecalLib::ScanTreeForName (const char *name, FDecalBase *root)
989 {
990 	while (root != NULL)
991 	{
992 		int lexx = stricmp (name, root->Name);
993 		if (lexx == 0)
994 		{
995 			break;
996 		}
997 		else if (lexx < 0)
998 		{
999 			root = root->Left;
1000 		}
1001 		else
1002 		{
1003 			root = root->Right;
1004 		}
1005 	}
1006 	return root;
1007 }
1008 
GenerateTranslation(DWORD start,DWORD end)1009 FDecalLib::FTranslation *FDecalLib::GenerateTranslation (DWORD start, DWORD end)
1010 {
1011 	FTranslation *trans;
1012 
1013 	if (Translations != NULL)
1014 	{
1015 		trans = Translations->LocateTranslation (start, end);
1016 	}
1017 	else
1018 	{
1019 		trans = NULL;
1020 	}
1021 	if (trans == NULL)
1022 	{
1023 		trans = new FTranslation (start, end);
1024 		trans->Next = Translations;
1025 		Translations = trans;
1026 	}
1027 	return trans;
1028 }
1029 
FDecalBase()1030 FDecalBase::FDecalBase ()
1031 {
1032 	Name = NAME_None;
1033 }
1034 
~FDecalBase()1035 FDecalBase::~FDecalBase ()
1036 {
1037 }
1038 
ApplyToDecal(DBaseDecal * decal,side_t * wall) const1039 void FDecalTemplate::ApplyToDecal (DBaseDecal *decal, side_t *wall) const
1040 {
1041 	if (RenderStyle.Flags & STYLEF_ColorIsFixed)
1042 	{
1043 		decal->SetShade (ShadeColor);
1044 	}
1045 	decal->Translation = Translation;
1046 	decal->ScaleX = ScaleX;
1047 	decal->ScaleY = ScaleY;
1048 	decal->PicNum = PicNum;
1049 	decal->Alpha = Alpha << 1;
1050 	decal->RenderStyle = RenderStyle;
1051 	decal->RenderFlags = (RenderFlags & ~(DECAL_RandomFlipX|DECAL_RandomFlipY)) |
1052 		(decal->RenderFlags & (RF_RELMASK|RF_CLIPMASK|RF_INVISIBLE|RF_ONESIDED));
1053 	if (RenderFlags & (DECAL_RandomFlipX|DECAL_RandomFlipY))
1054 	{
1055 		decal->RenderFlags ^= pr_decal() &
1056 			((RenderFlags & (DECAL_RandomFlipX|DECAL_RandomFlipY)) >> 8);
1057 	}
1058 	if (Animator != NULL)
1059 	{
1060 		Animator->CreateThinker (decal, wall);
1061 	}
1062 }
1063 
GetDecal() const1064 const FDecalTemplate *FDecalTemplate::GetDecal () const
1065 {
1066 	return this;
1067 }
1068 
FTranslation(DWORD start,DWORD end)1069 FDecalLib::FTranslation::FTranslation (DWORD start, DWORD end)
1070 {
1071 	DWORD ri, gi, bi, rs, gs, bs;
1072 	PalEntry *first, *last;
1073 	BYTE *table;
1074 	unsigned int i, tablei;
1075 
1076 	StartColor = start;
1077 	EndColor = end;
1078 	Next = NULL;
1079 
1080 	if (DecalTranslations.Size() == 256*256)
1081 	{
1082 		Printf ("Too many decal translations defined\n");
1083 		Index = 0;
1084 		return;
1085 	}
1086 
1087 	first = (PalEntry *)&StartColor;
1088 	last = (PalEntry *)&EndColor;
1089 
1090 	ri = first->r << 24;
1091 	gi = first->g << 24;
1092 	bi = first->b << 24;
1093 	rs = last->r << 24;
1094 	gs = last->g << 24;
1095 	bs = last->b << 24;
1096 
1097 	rs = (rs - ri) / 255;
1098 	gs = (gs - ri) / 255;
1099 	bs = (bs - bi) / 255;
1100 
1101 	tablei = DecalTranslations.Reserve(256);
1102 	table = &DecalTranslations[tablei];
1103 
1104 	for (i = 1; i < 256; i++, ri += rs, gi += gs, bi += bs)
1105 	{
1106 		table[i] = ColorMatcher.Pick (ri >> 24, gi >> 24, bi >> 24);
1107 	}
1108 	table[0] = table[1];
1109 	Index = (WORD)TRANSLATION(TRANSLATION_Decals, tablei >> 8);
1110 }
1111 
LocateTranslation(DWORD start,DWORD end)1112 FDecalLib::FTranslation *FDecalLib::FTranslation::LocateTranslation (DWORD start, DWORD end)
1113 {
1114 	FTranslation *trans = this;
1115 
1116 	do
1117 	{
1118 		if (start == trans->StartColor && end == trans->EndColor)
1119 		{
1120 			return trans;
1121 		}
1122 		trans = trans->Next;
1123 	} while (trans != NULL);
1124 	return trans;
1125 }
1126 
GetDecal() const1127 const FDecalTemplate *FDecalGroup::GetDecal () const
1128 {
1129 	const FDecalBase *decal = Choices.PickEntry ();
1130 	const FDecalBase *remember = decal;
1131 
1132 	// Repeatedly GetDecal() until the result is constant, since
1133 	// the choice might be another FDecalGroup.
1134 	if (decal != NULL)
1135 	{
1136 		do
1137 		{
1138 			remember = decal;
1139 			decal = decal->GetDecal ();
1140 		} while (decal != NULL && decal != remember);
1141 	}
1142 	return static_cast<const FDecalTemplate *>(remember);
1143 }
1144 
FDecalAnimator(const char * name)1145 FDecalAnimator::FDecalAnimator (const char *name)
1146 {
1147 	Name = name;
1148 }
1149 
~FDecalAnimator()1150 FDecalAnimator::~FDecalAnimator ()
1151 {
1152 }
1153 
IMPLEMENT_CLASS(DDecalFader)1154 IMPLEMENT_CLASS (DDecalFader)
1155 
1156 void DDecalFader::Serialize (FArchive &arc)
1157 {
1158 	Super::Serialize (arc);
1159 	arc << TimeToStartDecay << TimeToEndDecay << StartTrans;
1160 }
1161 
Tick()1162 void DDecalFader::Tick ()
1163 {
1164 	if (TheDecal == NULL)
1165 	{
1166 		Destroy ();
1167 	}
1168 	else
1169 	{
1170 		if (level.maptime < TimeToStartDecay || bglobal.freeze)
1171 		{
1172 			return;
1173 		}
1174 		else if (level.maptime >= TimeToEndDecay)
1175 		{
1176 			TheDecal->Destroy ();		// remove the decal
1177 			Destroy ();					// remove myself
1178 			return;
1179 		}
1180 		if (StartTrans == -1)
1181 		{
1182 			StartTrans = TheDecal->Alpha;
1183 		}
1184 
1185 		int distanceToEnd = TimeToEndDecay - level.maptime;
1186 		int fadeDistance = TimeToEndDecay - TimeToStartDecay;
1187 		TheDecal->Alpha = Scale (StartTrans, distanceToEnd, fadeDistance);
1188 	}
1189 }
1190 
CreateThinker(DBaseDecal * actor,side_t * wall) const1191 DThinker *FDecalFaderAnim::CreateThinker (DBaseDecal *actor, side_t *wall) const
1192 {
1193 	DDecalFader *fader = new DDecalFader (actor);
1194 
1195 	fader->TimeToStartDecay = level.maptime + DecayStart;
1196 	fader->TimeToEndDecay = fader->TimeToStartDecay + DecayTime;
1197 	fader->StartTrans = -1;
1198 	return fader;
1199 }
1200 
IMPLEMENT_CLASS(DDecalStretcher)1201 IMPLEMENT_CLASS (DDecalStretcher)
1202 
1203 void DDecalStretcher::Serialize (FArchive &arc)
1204 {
1205 	Super::Serialize (arc);
1206 	arc << TimeToStart
1207 		<< TimeToStop
1208 		<< GoalX
1209 		<< StartX
1210 		<< bStretchX
1211 		<< GoalY
1212 		<< StartY
1213 		<< bStretchY
1214 		<< bStarted;
1215 }
1216 
CreateThinker(DBaseDecal * actor,side_t * wall) const1217 DThinker *FDecalStretcherAnim::CreateThinker (DBaseDecal *actor, side_t *wall) const
1218 {
1219 	DDecalStretcher *thinker = new DDecalStretcher (actor);
1220 
1221 	thinker->TimeToStart = level.maptime + StretchStart;
1222 	thinker->TimeToStop = thinker->TimeToStart + StretchTime;
1223 
1224 	if (GoalX >= 0)
1225 	{
1226 		thinker->GoalX = GoalX;
1227 		thinker->bStretchX = true;
1228 	}
1229 	else
1230 	{
1231 		thinker->bStretchX = false;
1232 	}
1233 	if (GoalY >= 0)
1234 	{
1235 		thinker->GoalY = GoalY;
1236 		thinker->bStretchY = true;
1237 	}
1238 	else
1239 	{
1240 		thinker->bStretchY = false;
1241 	}
1242 	thinker->bStarted = false;
1243 	return thinker;
1244 }
1245 
Tick()1246 void DDecalStretcher::Tick ()
1247 {
1248 	if (TheDecal == NULL)
1249 	{
1250 		Destroy ();
1251 		return;
1252 	}
1253 	if (level.maptime < TimeToStart || bglobal.freeze)
1254 	{
1255 		return;
1256 	}
1257 	if (level.maptime >= TimeToStop)
1258 	{
1259 		if (bStretchX)
1260 		{
1261 			TheDecal->ScaleX = GoalX;
1262 		}
1263 		if (bStretchY)
1264 		{
1265 			TheDecal->ScaleY = GoalY;
1266 		}
1267 		Destroy ();
1268 		return;
1269 	}
1270 	if (!bStarted)
1271 	{
1272 		bStarted = true;
1273 		StartX = TheDecal->ScaleX;
1274 		StartY = TheDecal->ScaleY;
1275 	}
1276 
1277 	int distance = level.maptime - TimeToStart;
1278 	int maxDistance = TimeToStop - TimeToStart;
1279 	if (bStretchX)
1280 	{
1281 		TheDecal->ScaleX = StartX + Scale (GoalX - StartX, distance, maxDistance);
1282 	}
1283 	if (bStretchY)
1284 	{
1285 		TheDecal->ScaleY = StartY + Scale (GoalY - StartY, distance, maxDistance);
1286 	}
1287 }
1288 
IMPLEMENT_CLASS(DDecalSlider)1289 IMPLEMENT_CLASS (DDecalSlider)
1290 
1291 void DDecalSlider::Serialize (FArchive &arc)
1292 {
1293 	Super::Serialize (arc);
1294 	arc << TimeToStart
1295 		<< TimeToStop
1296 		/*<< DistX*/
1297 		<< DistY
1298 		/*<< StartX*/
1299 		<< StartY
1300 		<< bStarted;
1301 }
1302 
CreateThinker(DBaseDecal * actor,side_t * wall) const1303 DThinker *FDecalSliderAnim::CreateThinker (DBaseDecal *actor, side_t *wall) const
1304 {
1305 	DDecalSlider *thinker = new DDecalSlider (actor);
1306 
1307 	thinker->TimeToStart = level.maptime + SlideStart;
1308 	thinker->TimeToStop = thinker->TimeToStart + SlideTime;
1309 	/*thinker->DistX = DistX;*/
1310 	thinker->DistY = DistY;
1311 	thinker->bStarted = false;
1312 	return thinker;
1313 }
1314 
Tick()1315 void DDecalSlider::Tick ()
1316 {
1317 	if (TheDecal == NULL)
1318 	{
1319 		Destroy ();
1320 		return;
1321 	}
1322 	if (level.maptime < TimeToStart || bglobal.freeze)
1323 	{
1324 		return;
1325 	}
1326 	if (!bStarted)
1327 	{
1328 		bStarted = true;
1329 		/*StartX = TheDecal->LeftDistance;*/
1330 		StartY = TheDecal->Z;
1331 	}
1332 	if (level.maptime >= TimeToStop)
1333 	{
1334 		/*TheDecal->LeftDistance = StartX + DistX;*/
1335 		TheDecal->Z = StartY + DistY;
1336 		Destroy ();
1337 		return;
1338 	}
1339 
1340 	int distance = level.maptime - TimeToStart;
1341 	int maxDistance = TimeToStop - TimeToStart;
1342 	/*TheDecal->LeftDistance = StartX + Scale (DistX, distance, maxDistance);*/
1343 	TheDecal->Z = StartY + Scale (DistY, distance, maxDistance);
1344 }
1345 
CreateThinker(DBaseDecal * actor,side_t * wall) const1346 DThinker *FDecalCombinerAnim::CreateThinker (DBaseDecal *actor, side_t *wall) const
1347 {
1348 	DThinker *thinker = NULL;
1349 
1350 	for (int i = 0; i < NumAnimators; ++i)
1351 	{
1352 		thinker = AnimatorList[FirstAnimator+i]->CreateThinker (actor, wall);
1353 	}
1354 	return thinker;
1355 }
1356 
FindAnimator(const char * name)1357 FDecalAnimator *FDecalLib::FindAnimator (const char *name)
1358 {
1359 	int i;
1360 
1361 	for (i = (int)Animators.Size ()-1; i >= 0; --i)
1362 	{
1363 		if (stricmp (name, Animators[i]->Name) == 0)
1364 		{
1365 			return Animators[i];
1366 		}
1367 	}
1368 	return NULL;
1369 }
1370 
1371 
IMPLEMENT_CLASS(DDecalColorer)1372 IMPLEMENT_CLASS (DDecalColorer)
1373 
1374 void DDecalColorer::Serialize (FArchive &arc)
1375 {
1376 	Super::Serialize (arc);
1377 	arc << TimeToStartDecay << TimeToEndDecay << StartColor << GoalColor;
1378 }
1379 
Tick()1380 void DDecalColorer::Tick ()
1381 {
1382 	if (TheDecal == NULL || !(TheDecal->RenderStyle.Flags & STYLEF_ColorIsFixed))
1383 	{
1384 		Destroy ();
1385 	}
1386 	else
1387 	{
1388 		if (level.maptime < TimeToStartDecay || bglobal.freeze)
1389 		{
1390 			return;
1391 		}
1392 		else if (level.maptime >= TimeToEndDecay)
1393 		{
1394 			TheDecal->SetShade (GoalColor);
1395 			Destroy ();					// remove myself
1396 		}
1397 		if (StartColor.a == 255)
1398 		{
1399 			StartColor = TheDecal->AlphaColor & 0xffffff;
1400 			if (StartColor == GoalColor)
1401 			{
1402 				Destroy ();
1403 				return;
1404 			}
1405 		}
1406 		if (level.maptime & 0)
1407 		{ // Changing the shade can be expensive, so don't do it too often.
1408 			return;
1409 		}
1410 
1411 		int distance = level.maptime - TimeToStartDecay;
1412 		int maxDistance = TimeToEndDecay - TimeToStartDecay;
1413 		int r = StartColor.r + Scale (GoalColor.r - StartColor.r, distance, maxDistance);
1414 		int g = StartColor.g + Scale (GoalColor.g - StartColor.g, distance, maxDistance);
1415 		int b = StartColor.b + Scale (GoalColor.b - StartColor.b, distance, maxDistance);
1416 		TheDecal->SetShade (r, g, b);
1417 	}
1418 }
1419 
CreateThinker(DBaseDecal * actor,side_t * wall) const1420 DThinker *FDecalColorerAnim::CreateThinker (DBaseDecal *actor, side_t *wall) const
1421 {
1422 	DDecalColorer *Colorer = new DDecalColorer (actor);
1423 
1424 	Colorer->TimeToStartDecay = level.maptime + DecayStart;
1425 	Colorer->TimeToEndDecay = Colorer->TimeToStartDecay + DecayTime;
1426 	Colorer->StartColor = 0xff000000;
1427 	Colorer->GoalColor = GoalColor;
1428 	return Colorer;
1429 }
1430 
ReadScale(FScanner & sc)1431 static fixed_t ReadScale (FScanner &sc)
1432 {
1433 	sc.MustGetFloat ();
1434 	return fixed_t(clamp (sc.Float * FRACUNIT, 256.0, 256.0*FRACUNIT));
1435 }
1436