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