gzdoom/src/decallib.cpp

1399 lines
29 KiB
C++
Raw Normal View History

/*
** decallib.cpp
** Parses DECALDEFs and creates a "library" of decals
**
**---------------------------------------------------------------------------
** Copyright 1998-2005 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "decallib.h"
#include "sc_man.h"
#include "w_wad.h"
#include "v_video.h"
#include "v_palette.h"
#include "cmdlib.h"
#include "m_random.h"
#include "weightedlist.h"
#include "statnums.h"
#include "templates.h"
#include "r_draw.h"
2006-04-12 01:50:09 +00:00
#include "a_sharedglobal.h"
FDecalLib DecalLibrary;
static int ReadScale ();
static TArray<BYTE> DecalTranslations;
extern TArray<char*> DecalNames;
// A decal group holds multiple decals and returns one randomly
// when GetDecal() is called.
// Sometimes two machines in a game will disagree on the state of
// decals. I do not know why.
static FRandom pr_decalchoice ("DecalChoice");
static FRandom pr_decal ("Decal");
class FDecalGroup : public FDecalBase
{
friend class FDecalLib;
public:
FDecalGroup () : Choices (pr_decalchoice) {}
2006-04-12 01:50:09 +00:00
const FDecalTemplate *GetDecal () const;
void AddDecal (FDecalBase *decal, WORD weight)
{
Choices.AddEntry (decal, weight);
}
private:
TWeightedList<FDecalBase *> Choices;
};
struct FDecalLib::FTranslation
{
FTranslation (DWORD start, DWORD end);
FTranslation *LocateTranslation (DWORD start, DWORD end);
DWORD StartColor, EndColor;
FTranslation *Next;
WORD Index;
};
struct FDecalAnimator
{
FDecalAnimator (const char *name);
virtual ~FDecalAnimator ();
2006-04-12 01:50:09 +00:00
virtual DThinker *CreateThinker (DBaseDecal *actor) const = 0;
char *Name;
};
static TArray<FDecalAnimator *> Animators;
struct DDecalThinker : public DThinker
{
DECLARE_CLASS (DDecalThinker, DThinker)
HAS_OBJECT_POINTERS
public:
2006-04-12 01:50:09 +00:00
DDecalThinker (DBaseDecal *decal) : DThinker (STAT_DECALTHINKER), TheDecal (decal) {}
void Serialize (FArchive &arc);
2006-04-12 01:50:09 +00:00
DBaseDecal *TheDecal;
protected:
DDecalThinker () : DThinker (STAT_DECALTHINKER) {}
};
IMPLEMENT_POINTY_CLASS (DDecalThinker)
DECLARE_POINTER (TheDecal)
END_POINTERS
void DDecalThinker::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << TheDecal;
}
struct FDecalFaderAnim : public FDecalAnimator
{
FDecalFaderAnim (const char *name) : FDecalAnimator (name) {}
2006-04-12 01:50:09 +00:00
DThinker *CreateThinker (DBaseDecal *actor) const;
int DecayStart;
int DecayTime;
};
class DDecalFader : public DDecalThinker
{
DECLARE_CLASS (DDecalFader, DDecalThinker)
public:
2006-04-12 01:50:09 +00:00
DDecalFader (DBaseDecal *decal) : DDecalThinker (decal) {}
void Serialize (FArchive &arc);
void Tick ();
int TimeToStartDecay;
int TimeToEndDecay;
fixed_t StartTrans;
private:
DDecalFader () {}
};
struct FDecalColorerAnim : public FDecalAnimator
{
FDecalColorerAnim (const char *name) : FDecalAnimator (name) {}
2006-04-12 01:50:09 +00:00
DThinker *CreateThinker (DBaseDecal *actor) const;
int DecayStart;
int DecayTime;
PalEntry GoalColor;
};
class DDecalColorer : public DDecalThinker
{
DECLARE_CLASS (DDecalColorer, DDecalThinker)
public:
2006-04-12 01:50:09 +00:00
DDecalColorer (DBaseDecal *decal) : DDecalThinker (decal) {}
void Serialize (FArchive &arc);
void Tick ();
int TimeToStartDecay;
int TimeToEndDecay;
PalEntry StartColor;
PalEntry GoalColor;
private:
DDecalColorer () {}
};
struct FDecalStretcherAnim : public FDecalAnimator
{
FDecalStretcherAnim (const char *name) : FDecalAnimator (name) {}
2006-04-12 01:50:09 +00:00
DThinker *CreateThinker (DBaseDecal *actor) const;
int StretchStart;
int StretchTime;
int GoalX, GoalY;
};
class DDecalStretcher : public DDecalThinker
{
DECLARE_CLASS (DDecalStretcher, DDecalThinker)
public:
2006-04-12 01:50:09 +00:00
DDecalStretcher (DBaseDecal *decal) : DDecalThinker (decal) {}
void Serialize (FArchive &arc);
void Tick ();
int TimeToStart;
int TimeToStop;
BYTE GoalX;
BYTE StartX;
BYTE GoalY;
BYTE StartY;
bool bStretchX;
bool bStretchY;
bool bStarted;
private:
DDecalStretcher () {}
};
struct FDecalSliderAnim : public FDecalAnimator
{
FDecalSliderAnim (const char *name) : FDecalAnimator (name) {}
2006-04-12 01:50:09 +00:00
DThinker *CreateThinker (DBaseDecal *actor) const;
int SlideStart;
int SlideTime;
fixed_t DistX, DistY;
};
class DDecalSlider : public DDecalThinker
{
DECLARE_CLASS (DDecalSlider, DDecalThinker)
public:
2006-04-12 01:50:09 +00:00
DDecalSlider (DBaseDecal *decal) : DDecalThinker (decal) {}
void Serialize (FArchive &arc);
void Tick ();
int TimeToStart;
int TimeToStop;
fixed_t DistX;
fixed_t DistY;
fixed_t StartX;
fixed_t StartY;
bool bStarted;
private:
DDecalSlider () {}
};
struct FDecalCombinerAnim : public FDecalAnimator
{
FDecalCombinerAnim (const char *name) : FDecalAnimator (name) {}
2006-04-12 01:50:09 +00:00
DThinker *CreateThinker (DBaseDecal *actor) const;
int FirstAnimator;
int NumAnimators;
static TArray<FDecalAnimator *> AnimatorList;
};
TArray<FDecalAnimator *> FDecalCombinerAnim::AnimatorList;
static const char *DecalKeywords[] =
{
"x-scale",
"y-scale",
"pic",
"solid",
"add",
"translucent",
"flipx",
"flipy",
"randomflipx",
"randomflipy",
"fullbright",
"fuzzy",
"shade",
"colors",
"animator",
"lowerdecal",
NULL
};
enum
{
DECAL_XSCALE,
DECAL_YSCALE,
DECAL_PIC,
DECAL_SOLID,
DECAL_ADD,
DECAL_TRANSLUCENT,
DECAL_FLIPX,
DECAL_FLIPY,
DECAL_RANDOMFLIPX,
DECAL_RANDOMFLIPY,
DECAL_FULLBRIGHT,
DECAL_FUZZY,
DECAL_SHADE,
DECAL_COLORS,
DECAL_ANIMATOR,
DECAL_LOWERDECAL
};
2006-04-12 01:50:09 +00:00
const FDecalTemplate *FDecalBase::GetDecal () const
{
return NULL;
}
FDecalLib::FDecalLib ()
{
Root = NULL;
Translations = NULL;
}
FDecalLib::~FDecalLib ()
{
Clear ();
}
void FDecalLib::Clear ()
{
FTranslation *trans;
DelTree (Root);
Root = NULL;
trans = Translations;
while (trans != NULL)
{
FTranslation *next = trans->Next;
delete trans;
trans = next;
}
}
void FDecalLib::DelTree (FDecalBase *root)
{
if (root != NULL)
{
DelTree (root->Left);
DelTree (root->Right);
delete root;
}
}
void FDecalLib::ReadAllDecals ()
{
int lump, lastlump = 0;
size_t i;
while ((lump = Wads.FindLump ("DECALDEF", &lastlump)) != -1)
{
SC_OpenLumpNum (lump, "DECALDEF");
ReadDecals ();
SC_Close ();
}
// Supporting code to allow specifying decals directly in the DECORATE lump
for (i = 0; i < TypeInfo::m_RuntimeActors.Size(); i++)
{
AActor *def = (AActor*)GetDefaultByType (TypeInfo::m_RuntimeActors[i]);
intptr_t v = (intptr_t)def->DecalGenerator;
if (v > 0 && v <= (intptr_t)DecalNames.Size())
{
def->DecalGenerator = ScanTreeForName (DecalNames[v-1], Root);
}
}
// Free the array which is no longer needed!
for (i = 0; i < DecalNames.Size(); i++)
{
delete[] DecalNames[i];
}
DecalNames.Clear();
DecalNames.ShrinkToFit();
}
void FDecalLib::ReadDecals ()
{
while (SC_GetString ())
{
if (SC_Compare ("decal"))
{
ParseDecal ();
}
else if (SC_Compare ("decalgroup"))
{
ParseDecalGroup ();
}
else if (SC_Compare ("generator"))
{
ParseGenerator ();
}
else if (SC_Compare ("fader"))
{
ParseFader ();
}
else if (SC_Compare ("stretcher"))
{
ParseStretcher ();
}
else if (SC_Compare ("slider"))
{
ParseSlider ();
}
else if (SC_Compare ("combiner"))
{
ParseCombiner ();
}
else if (SC_Compare ("colorchanger"))
{
ParseColorchanger ();
}
else
{
SC_ScriptError (NULL);
}
}
}
BYTE FDecalLib::GetDecalID ()
{
SC_MustGetString ();
if (!IsNum (sc_String))
{
SC_UnGet ();
return 0;
}
else
{
unsigned long num = strtoul (sc_String, NULL, 10);
if (num < 1 || num > 255)
{
SC_ScriptError ("Decal ID must be between 1 and 255");
}
return (BYTE)num;
}
}
void FDecalLib::ParseDecal ()
{
char decalName[64];
byte decalNum;
2006-04-12 01:50:09 +00:00
FDecalTemplate newdecal;
int code, picnum;
SC_MustGetString ();
strcpy (decalName, sc_String);
decalNum = GetDecalID ();
SC_MustGetStringName ("{");
memset (&newdecal, 0, sizeof(newdecal));
newdecal.PicNum = 0xffff;
newdecal.ScaleX = newdecal.ScaleY = 63;
newdecal.RenderFlags = RF_WALLSPRITE;
newdecal.RenderStyle = STYLE_Normal;
newdecal.Alpha = 0x8000;
for (;;)
{
SC_MustGetString ();
if (SC_Compare ("}"))
{
AddDecal (decalName, decalNum, newdecal);
break;
}
switch ((code = SC_MustMatchString (DecalKeywords)))
{
case DECAL_XSCALE:
newdecal.ScaleX = ReadScale ();
break;
case DECAL_YSCALE:
newdecal.ScaleY = ReadScale ();
break;
case DECAL_PIC:
SC_MustGetString ();
picnum = TexMan.CheckForTexture (sc_String, FTexture::TEX_Any);
if (picnum < 0 && (picnum = Wads.CheckNumForName (sc_String)) >= 0)
{
picnum = TexMan.CreateTexture (picnum, FTexture::TEX_Decal);
}
newdecal.PicNum = picnum;
break;
case DECAL_SOLID:
newdecal.RenderStyle = STYLE_Normal;
break;
case DECAL_ADD:
SC_MustGetFloat ();
newdecal.Alpha = (WORD)(32768.f * sc_Float);
newdecal.RenderStyle = STYLE_Add;
break;
case DECAL_TRANSLUCENT:
SC_MustGetFloat ();
newdecal.Alpha = (WORD)(32768.f * sc_Float);
newdecal.RenderStyle = STYLE_Translucent;
break;
case DECAL_FLIPX:
newdecal.RenderFlags |= RF_XFLIP;
break;
case DECAL_FLIPY:
newdecal.RenderFlags |= RF_YFLIP;
break;
case DECAL_RANDOMFLIPX:
2006-04-12 01:50:09 +00:00
newdecal.RenderFlags |= FDecalTemplate::DECAL_RandomFlipX;
break;
case DECAL_RANDOMFLIPY:
2006-04-12 01:50:09 +00:00
newdecal.RenderFlags |= FDecalTemplate::DECAL_RandomFlipY;
break;
case DECAL_FULLBRIGHT:
newdecal.RenderFlags |= RF_FULLBRIGHT;
break;
case DECAL_FUZZY:
newdecal.RenderStyle = STYLE_Fuzzy;
break;
case DECAL_SHADE:
SC_MustGetString ();
newdecal.RenderStyle = STYLE_Shaded;
newdecal.ShadeColor = V_GetColor (NULL, sc_String);
newdecal.ShadeColor |=
ColorMatcher.Pick (RPART(newdecal.ShadeColor),
GPART(newdecal.ShadeColor), BPART(newdecal.ShadeColor)) << 24;
break;
case DECAL_COLORS:
DWORD startcolor, endcolor;
SC_MustGetString (); startcolor = V_GetColor (NULL, sc_String);
SC_MustGetString (); endcolor = V_GetColor (NULL, sc_String);
newdecal.Translation = GenerateTranslation (startcolor, endcolor)->Index;
break;
case DECAL_ANIMATOR:
SC_MustGetString ();
newdecal.Animator = FindAnimator (sc_String);
break;
case DECAL_LOWERDECAL:
SC_MustGetString ();
newdecal.LowerDecal = GetDecalByName (sc_String);
break;
}
}
}
void FDecalLib::ParseDecalGroup ()
{
char groupName[64];
BYTE decalNum;
FDecalBase *targetDecal;
FDecalGroup *group;
SC_MustGetString ();
strcpy (groupName, sc_String);
decalNum = GetDecalID ();
SC_MustGetStringName ("{");
group = new FDecalGroup;
for (;;)
{
SC_MustGetString ();
if (SC_Compare ("}"))
{
group->Name = copystring (groupName);
AddDecal (group);
break;
}
targetDecal = ScanTreeForName (sc_String, Root);
if (targetDecal == NULL)
{
SC_ScriptError ("%s has not been defined", sc_String);
}
SC_MustGetNumber ();
group->AddDecal (targetDecal, sc_Number);
}
}
void FDecalLib::ParseGenerator ()
{
const TypeInfo *type;
FDecalBase *decal;
AActor *actor;
// Get name of generator (actor)
SC_MustGetString ();
type = TypeInfo::FindType (sc_String);
if (type == NULL || type->ActorInfo == NULL)
{
if (type == NULL)
{
type = TypeInfo::IFindType (sc_String);
if (type != NULL)
{
SC_ScriptError ("%s is not an actor. Did you mean %s?", sc_String, type->Name + 1);
}
}
SC_ScriptError ("%s is not an actor.", sc_String);
}
actor = (AActor *)type->ActorInfo->Defaults;
// Get name of generated decal
SC_MustGetString ();
if (stricmp (sc_String, "None") == 0)
{
decal = NULL;
}
else
{
decal = ScanTreeForName (sc_String, Root);
if (decal == NULL)
{
SC_ScriptError ("%s has not been defined.", sc_String);
}
}
actor->DecalGenerator = decal;
decal->Users.Push (type);
}
void FDecalLib::ParseFader ()
{
char faderName[64];
int startTime = 0, decayTime = 0;
SC_MustGetString ();
strcpy (faderName, sc_String);
SC_MustGetStringName ("{");
for (;;)
{
SC_MustGetString ();
if (SC_Compare ("}"))
{
FDecalFaderAnim *fader = new FDecalFaderAnim (faderName);
fader->DecayStart = startTime;
fader->DecayTime = decayTime;
Animators.Push (fader);
break;
}
else if (SC_Compare ("DecayStart"))
{
SC_MustGetFloat ();
startTime = (int)(sc_Float * TICRATE);
}
else if (SC_Compare ("DecayTime"))
{
SC_MustGetFloat ();
decayTime = (int)(sc_Float * TICRATE);
}
else
{
SC_ScriptError ("Unknown fader parameter '%s'", sc_String);
}
}
}
void FDecalLib::ParseStretcher ()
{
char stretcherName[64];
int goalX = -1, goalY = -1;
int startTime = 0, takeTime = 0;
SC_MustGetString ();
strcpy (stretcherName, sc_String);
SC_MustGetStringName ("{");
for (;;)
{
SC_MustGetString ();
if (SC_Compare ("}"))
{
if (goalX >= 0 || goalY >= 0)
{
FDecalStretcherAnim *stretcher = new FDecalStretcherAnim (stretcherName);
stretcher->StretchStart = startTime;
stretcher->StretchTime = takeTime;
stretcher->GoalX = goalX;
stretcher->GoalY = goalY;
Animators.Push (stretcher);
}
break;
}
else if (SC_Compare ("StretchStart"))
{
SC_MustGetFloat ();
startTime = (int)(sc_Float * TICRATE);
}
else if (SC_Compare ("StretchTime"))
{
SC_MustGetFloat ();
takeTime = (int)(sc_Float * TICRATE);
}
else if (SC_Compare ("GoalX"))
{
goalX = ReadScale ();
}
else if (SC_Compare ("GoalY"))
{
goalY = ReadScale ();
}
else
{
SC_ScriptError ("Unknown stretcher parameter '%s'", sc_String);
}
}
}
void FDecalLib::ParseSlider ()
{
char sliderName[64];
fixed_t distX = 0, distY = 0;
int startTime = 0, takeTime = 0;
SC_MustGetString ();
strcpy (sliderName, sc_String);
SC_MustGetStringName ("{");
for (;;)
{
SC_MustGetString ();
if (SC_Compare ("}"))
{
if ((distX | distY) != 0)
{
FDecalSliderAnim *slider = new FDecalSliderAnim (sliderName);
slider->SlideStart = startTime;
slider->SlideTime = takeTime;
slider->DistX = distX;
slider->DistY = distY;
Animators.Push (slider);
}
break;
}
else if (SC_Compare ("SlideStart"))
{
SC_MustGetFloat ();
startTime = (int)(sc_Float * TICRATE);
}
else if (SC_Compare ("SlideTime"))
{
SC_MustGetFloat ();
takeTime = (int)(sc_Float * TICRATE);
}
else if (SC_Compare ("DistX"))
{
SC_MustGetFloat ();
distX = (fixed_t)(sc_Float * FRACUNIT);
}
else if (SC_Compare ("DistY"))
{
SC_MustGetFloat ();
distY = (fixed_t)(sc_Float * FRACUNIT);
}
else
{
SC_ScriptError ("Unknown slider parameter '%s'", sc_String);
}
}
}
void FDecalLib::ParseColorchanger ()
{
char faderName[64];
int startTime = 0, decayTime = 0;
PalEntry goal = 0;
SC_MustGetString ();
strcpy (faderName, sc_String);
SC_MustGetStringName ("{");
for (;;)
{
SC_MustGetString ();
if (SC_Compare ("}"))
{
FDecalColorerAnim *fader = new FDecalColorerAnim (faderName);
fader->DecayStart = startTime;
fader->DecayTime = decayTime;
fader->GoalColor = goal;
Animators.Push (fader);
break;
}
else if (SC_Compare ("FadeStart"))
{
SC_MustGetFloat ();
startTime = (int)(sc_Float * TICRATE);
}
else if (SC_Compare ("FadeTime"))
{
SC_MustGetFloat ();
decayTime = (int)(sc_Float * TICRATE);
}
else if (SC_Compare ("Color"))
{
SC_MustGetString ();
goal = V_GetColor (NULL, sc_String);
}
else
{
SC_ScriptError ("Unknown color changer parameter '%s'", sc_String);
}
}
}
void FDecalLib::ParseCombiner ()
{
char combinerName[64];
size_t first = FDecalCombinerAnim::AnimatorList.Size ();
SC_MustGetString ();
strcpy (combinerName, sc_String);
SC_MustGetStringName ("{");
SC_MustGetString ();
while (!SC_Compare ("}"))
{
FDecalAnimator *anim = FindAnimator (sc_String);
if (anim == NULL)
{
SC_ScriptError ("Undefined animator %s", sc_String);
}
FDecalCombinerAnim::AnimatorList.Push (anim);
SC_MustGetString ();
}
size_t last = FDecalCombinerAnim::AnimatorList.Size ();
if (last > first)
{
FDecalCombinerAnim *combiner = new FDecalCombinerAnim (combinerName);
combiner->FirstAnimator = (int)first;
combiner->NumAnimators = (int)(last - first);
Animators.Push (combiner);
}
}
2006-04-12 01:50:09 +00:00
void FDecalLib::AddDecal (const char *name, byte num, const FDecalTemplate &decal)
{
2006-04-12 01:50:09 +00:00
FDecalTemplate *newDecal = new FDecalTemplate;
*newDecal = decal;
newDecal->Name = copystring (name);
newDecal->SpawnID = num;
AddDecal (newDecal);
}
void FDecalLib::AddDecal (FDecalBase *decal)
{
FDecalBase *node = Root, **prev = &Root;
int num = decal->SpawnID;
decal->SpawnID = 0;
// Check if this decal already exists.
while (node != NULL)
{
int lexx = stricmp (decal->Name, node->Name);
if (lexx == 0)
{
break;
}
else if (lexx < 0)
{
prev = &node->Left;
node = node->Left;
}
else
{
prev = &node->Right;
node = node->Right;
}
}
if (node == NULL)
{ // No, add it.
decal->SpawnID = 0;
*prev = decal;
decal->Left = NULL;
decal->Right = NULL;
}
else
{ // Yes, replace the old one.
decal->Left = node->Left;
decal->Right = node->Right;
*prev = decal;
// Fix references to the old decal so that they use the new one instead.
for (unsigned int i = 0; i < node->Users.Size(); ++i)
{
((AActor *)node->Users[i]->ActorInfo->Defaults)->DecalGenerator = decal;
}
decal->Users = node->Users;
delete node;
}
// If this decal has an ID, make sure no existing decals have the same ID.
if (num != 0)
{
FDecalBase *spawner = ScanTreeForNum (num, Root);
if (spawner != NULL)
{
spawner->SpawnID = 0;
}
decal->SpawnID = num;
}
}
2006-04-12 01:50:09 +00:00
const FDecalTemplate *FDecalLib::GetDecalByNum (byte num) const
{
if (num == 0)
{
return NULL;
}
FDecalBase *base = ScanTreeForNum (num, Root);
if (base != NULL)
{
return base->GetDecal ();
}
return NULL;
}
2006-04-12 01:50:09 +00:00
const FDecalTemplate *FDecalLib::GetDecalByName (const char *name) const
{
if (name == NULL)
{
return NULL;
}
FDecalBase *base = ScanTreeForName (name, Root);
if (base != NULL)
{
2006-04-12 01:50:09 +00:00
return static_cast<FDecalTemplate *>(base);
}
return NULL;
}
FDecalBase *FDecalLib::ScanTreeForNum (const BYTE num, FDecalBase *root)
{
while (root != NULL)
{
if (root->SpawnID == num)
{
break;
}
FDecalBase *leftres = ScanTreeForNum (num, root->Left);
if (leftres != NULL)
return leftres;
root = root->Right; // Avoid tail-recursion
}
return root;
}
FDecalBase *FDecalLib::ScanTreeForName (const char *name, FDecalBase *root)
{
while (root != NULL)
{
int lexx = stricmp (name, root->Name);
if (lexx == 0)
{
break;
}
else if (lexx < 0)
{
root = root->Left;
}
else
{
root = root->Right;
}
}
return root;
}
FDecalLib::FTranslation *FDecalLib::GenerateTranslation (DWORD start, DWORD end)
{
FTranslation *trans;
if (Translations != NULL)
{
trans = Translations->LocateTranslation (start, end);
}
else
{
trans = NULL;
}
if (trans == NULL)
{
trans = new FTranslation (start, end);
trans->Next = Translations;
Translations = trans;
}
return trans;
}
FDecalBase::FDecalBase ()
{
Name = NULL;
}
FDecalBase::~FDecalBase ()
{
if (Name != NULL)
delete[] Name;
}
2006-04-12 01:50:09 +00:00
void FDecalTemplate::ApplyToDecal (DBaseDecal *actor) const
{
if (RenderStyle == STYLE_Shaded)
{
actor->SetShade (ShadeColor);
}
actor->Translation = Translation;
2006-04-12 01:50:09 +00:00
actor->XScale = ScaleX;
actor->YScale = ScaleY;
actor->PicNum = PicNum;
actor->Alpha = Alpha << 1;
actor->RenderStyle = RenderStyle;
2006-04-12 01:50:09 +00:00
actor->RenderFlags = (RenderFlags & ~(DECAL_RandomFlipX|DECAL_RandomFlipY)) |
(actor->RenderFlags & (RF_RELMASK|RF_CLIPMASK|RF_INVISIBLE|RF_ONESIDED));
if (RenderFlags & (DECAL_RandomFlipX|DECAL_RandomFlipY))
{
2006-04-12 01:50:09 +00:00
actor->RenderFlags ^= pr_decal() &
((RenderFlags & (DECAL_RandomFlipX|DECAL_RandomFlipY)) >> 8);
}
if (Animator != NULL)
{
Animator->CreateThinker (actor);
}
}
2006-04-12 01:50:09 +00:00
const FDecalTemplate *FDecalTemplate::GetDecal () const
{
return this;
}
FDecalLib::FTranslation::FTranslation (DWORD start, DWORD end)
{
DWORD ri, gi, bi, rs, gs, bs;
PalEntry *first, *last;
BYTE *table;
unsigned int i, tablei;
StartColor = start;
EndColor = end;
Next = NULL;
if (DecalTranslations.Size() == 256*256)
{
Printf ("Too many decal translations defined\n");
Index = 0;
return;
}
first = (PalEntry *)&StartColor;
last = (PalEntry *)&EndColor;
ri = first->r << 24;
gi = first->g << 24;
bi = first->b << 24;
rs = last->r << 24;
gs = last->g << 24;
bs = last->b << 24;
rs = (rs - ri) / 255;
gs = (gs - ri) / 255;
bs = (bs - bi) / 255;
tablei = DecalTranslations.Reserve(256);
table = &DecalTranslations[tablei];
for (i = 1; i < 256; i++, ri += rs, gi += gs, bi += bs)
{
table[i] = ColorMatcher.Pick (ri >> 24, gi >> 24, bi >> 24);
}
table[0] = table[1];
Index = (WORD)TRANSLATION(TRANSLATION_Decals, tablei >> 8);
}
FDecalLib::FTranslation *FDecalLib::FTranslation::LocateTranslation (DWORD start, DWORD end)
{
FTranslation *trans = this;
do
{
if (start == trans->StartColor && end == trans->EndColor)
{
return trans;
}
trans = trans->Next;
} while (trans != NULL);
return trans;
}
2006-04-12 01:50:09 +00:00
const FDecalTemplate *FDecalGroup::GetDecal () const
{
const FDecalBase *decal = Choices.PickEntry ();
const FDecalBase *remember;
// Repeatedly GetDecal() until the result is constant, since
// the choice might be another FDecalGroup.
do
{
remember = decal;
decal = decal->GetDecal ();
} while (decal != remember);
2006-04-12 01:50:09 +00:00
return static_cast<const FDecalTemplate *>(decal);
}
FDecalAnimator::FDecalAnimator (const char *name)
{
Name = copystring (name);
}
FDecalAnimator::~FDecalAnimator ()
{
if (Name != NULL)
{
delete[] Name;
Name = NULL;
}
}
IMPLEMENT_CLASS (DDecalFader)
void DDecalFader::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << TimeToStartDecay << TimeToEndDecay << StartTrans;
}
void DDecalFader::Tick ()
{
if (TheDecal == NULL)
{
Destroy ();
}
else
{
if (level.maptime < TimeToStartDecay || bglobal.freeze)
{
return;
}
else if (level.maptime >= TimeToEndDecay)
{
TheDecal->Destroy (); // remove the decal
Destroy (); // remove myself
}
if (StartTrans == -1)
{
2006-04-12 01:50:09 +00:00
StartTrans = TheDecal->Alpha;
}
int distanceToEnd = TimeToEndDecay - level.maptime;
int fadeDistance = TimeToEndDecay - TimeToStartDecay;
2006-04-12 01:50:09 +00:00
TheDecal->Alpha = Scale (StartTrans, distanceToEnd, fadeDistance);
if (TheDecal->RenderStyle < STYLE_Translucent)
{
TheDecal->RenderStyle = STYLE_Translucent;
}
}
}
2006-04-12 01:50:09 +00:00
DThinker *FDecalFaderAnim::CreateThinker (DBaseDecal *actor) const
{
DDecalFader *fader = new DDecalFader (actor);
fader->TimeToStartDecay = level.maptime + DecayStart;
fader->TimeToEndDecay = fader->TimeToStartDecay + DecayTime;
fader->StartTrans = -1;
return fader;
}
IMPLEMENT_CLASS (DDecalStretcher)
void DDecalStretcher::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << TimeToStart
<< TimeToStop
<< GoalX
<< StartX
<< bStretchX
<< GoalY
<< StartY
<< bStretchY
<< bStarted;
}
2006-04-12 01:50:09 +00:00
DThinker *FDecalStretcherAnim::CreateThinker (DBaseDecal *actor) const
{
DDecalStretcher *thinker = new DDecalStretcher (actor);
thinker->TimeToStart = level.maptime + StretchStart;
thinker->TimeToStop = thinker->TimeToStart + StretchTime;
if (GoalX >= 0)
{
thinker->GoalX = GoalX;
thinker->bStretchX = true;
}
else
{
thinker->bStretchX = false;
}
if (GoalY >= 0)
{
thinker->GoalY = GoalY;
thinker->bStretchY = true;
}
else
{
thinker->bStretchY = false;
}
thinker->bStarted = false;
return thinker;
}
void DDecalStretcher::Tick ()
{
if (TheDecal == NULL)
{
Destroy ();
return;
}
if (level.maptime < TimeToStart || bglobal.freeze)
{
return;
}
if (level.maptime >= TimeToStop)
{
if (bStretchX)
{
2006-04-12 01:50:09 +00:00
TheDecal->XScale = GoalX;
}
if (bStretchY)
{
2006-04-12 01:50:09 +00:00
TheDecal->YScale = GoalY;
}
Destroy ();
return;
}
if (!bStarted)
{
bStarted = true;
2006-04-12 01:50:09 +00:00
StartX = TheDecal->XScale;
StartY = TheDecal->YScale;
}
int distance = level.maptime - TimeToStart;
int maxDistance = TimeToStop - TimeToStart;
if (bStretchX)
{
2006-04-12 01:50:09 +00:00
TheDecal->XScale = StartX + Scale (GoalX - StartX, distance, maxDistance);
}
if (bStretchY)
{
2006-04-12 01:50:09 +00:00
TheDecal->YScale = StartY + Scale (GoalY - StartY, distance, maxDistance);
}
}
IMPLEMENT_CLASS (DDecalSlider)
void DDecalSlider::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << TimeToStart
<< TimeToStop
<< DistX
<< DistY
<< StartX
<< StartY
<< bStarted;
}
2006-04-12 01:50:09 +00:00
DThinker *FDecalSliderAnim::CreateThinker (DBaseDecal *actor) const
{
DDecalSlider *thinker = new DDecalSlider (actor);
thinker->TimeToStart = level.maptime + SlideStart;
thinker->TimeToStop = thinker->TimeToStart + SlideTime;
thinker->DistX = DistX;
thinker->DistY = DistY;
thinker->bStarted = false;
return thinker;
}
void DDecalSlider::Tick ()
{
if (TheDecal == NULL)
{
Destroy ();
return;
}
if (level.maptime < TimeToStart || bglobal.freeze)
{
return;
}
if (!bStarted)
{
bStarted = true;
StartX = TheDecal->x;
StartY = TheDecal->z;
}
if (level.maptime >= TimeToStop)
{
TheDecal->x = StartX + DistX;
TheDecal->z = StartY + DistY;
Destroy ();
return;
}
int distance = level.maptime - TimeToStart;
int maxDistance = TimeToStop - TimeToStart;
TheDecal->x = StartX + Scale (DistX, distance, maxDistance);
TheDecal->z = StartY + Scale (DistY, distance, maxDistance);
}
2006-04-12 01:50:09 +00:00
DThinker *FDecalCombinerAnim::CreateThinker (DBaseDecal *actor) const
{
DThinker *thinker = NULL;
for (int i = 0; i < NumAnimators; ++i)
{
thinker = AnimatorList[FirstAnimator+i]->CreateThinker (actor);
}
return thinker;
}
FDecalAnimator *FDecalLib::FindAnimator (const char *name)
{
unsigned int i;
for (i = Animators.Size ()-1; i >= 0; --i)
{
if (stricmp (name, Animators[i]->Name) == 0)
{
return Animators[i];
}
}
return NULL;
}
IMPLEMENT_CLASS (DDecalColorer)
void DDecalColorer::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << TimeToStartDecay << TimeToEndDecay << StartColor << GoalColor;
}
void DDecalColorer::Tick ()
{
if (TheDecal == NULL || TheDecal->RenderStyle != STYLE_Shaded)
{
Destroy ();
}
else
{
if (level.maptime < TimeToStartDecay || bglobal.freeze)
{
return;
}
else if (level.maptime >= TimeToEndDecay)
{
TheDecal->SetShade (GoalColor);
Destroy (); // remove myself
}
if (StartColor.a == 255)
{
2006-04-12 01:50:09 +00:00
StartColor = TheDecal->AlphaColor & 0xffffff;
if (StartColor == GoalColor)
{
Destroy ();
return;
}
}
if (level.maptime & 0)
{ // Changing the shade can be expensive, so don't do it too often.
return;
}
int distance = level.maptime - TimeToStartDecay;
int maxDistance = TimeToEndDecay - TimeToStartDecay;
int r = StartColor.r + Scale (GoalColor.r - StartColor.r, distance, maxDistance);
int g = StartColor.g + Scale (GoalColor.g - StartColor.g, distance, maxDistance);
int b = StartColor.b + Scale (GoalColor.b - StartColor.b, distance, maxDistance);
TheDecal->SetShade (r, g, b);
}
}
2006-04-12 01:50:09 +00:00
DThinker *FDecalColorerAnim::CreateThinker (DBaseDecal *actor) const
{
DDecalColorer *Colorer = new DDecalColorer (actor);
Colorer->TimeToStartDecay = level.maptime + DecayStart;
Colorer->TimeToEndDecay = Colorer->TimeToStartDecay + DecayTime;
Colorer->StartColor = 0xff000000;
Colorer->GoalColor = GoalColor;
return Colorer;
}
static int ReadScale ()
{
SC_MustGetFloat ();
return clamp ((int)(sc_Float * 64.f), 1, 256) - 1;
}