qzdoom/src/g_shared/a_decals.cpp

815 lines
18 KiB
C++
Raw Normal View History

/*
** a_decals.cpp
** Implements the actor that represents decals in the level
**
**---------------------------------------------------------------------------
2006-04-12 01:50:09 +00:00
** Copyright 1998-2006 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 "actor.h"
#include "a_sharedglobal.h"
#include "r_defs.h"
#include "p_local.h"
#include "v_video.h"
#include "p_trace.h"
#include "decallib.h"
#include "statnums.h"
#include "c_dispatch.h"
2006-04-12 01:50:09 +00:00
static fixed_t DecalWidth, DecalLeft, DecalRight;
static fixed_t SpreadZ;
static const DBaseDecal *SpreadSource;
static const FDecalTemplate *SpreadTemplate;
static TArray<side_t *> SpreadStack;
static int ImpactCount;
static DImpactDecal *FirstImpact; // (but not the Second or Third Impact :-)
static DImpactDecal *LastImpact;
CVAR (Bool, cl_spreaddecals, true, CVAR_ARCHIVE)
// They also overload floorclip to be the fractional distance from the
// left edge of the side. This distance is stored as a 2.30 fixed pt number.
2006-04-12 01:50:09 +00:00
IMPLEMENT_CLASS (DBaseDecal)
IMPLEMENT_CLASS (DImpactDecal)
2006-04-12 01:50:09 +00:00
DBaseDecal::DBaseDecal ()
: DThinker(STAT_DECAL),
WallNext(0), WallPrev(0), x(0), y(0), z(0), AlphaColor(0), Translation(0), PicNum(0xFFFF),
RenderFlags(0), XScale(8), YScale(8), RenderStyle(0), LeftDistance(0), Alpha(0)
{
}
DBaseDecal::DBaseDecal (fixed_t x, fixed_t y, fixed_t z)
: DThinker(STAT_DECAL),
WallNext(0), WallPrev(0), x(x), y(y), z(z), AlphaColor(0), Translation(0), PicNum(0xFFFF),
RenderFlags(0), XScale(8), YScale(8), RenderStyle(0), LeftDistance(0), Alpha(0)
{
}
DBaseDecal::DBaseDecal (const AActor *basis)
: DThinker(STAT_DECAL),
WallNext(0), WallPrev(0), x(basis->x), y(basis->y), z(basis->z), AlphaColor(basis->alphacolor),
Translation(basis->Translation), PicNum(basis->picnum), RenderFlags(basis->renderflags),
XScale(basis->xscale), YScale(basis->yscale), RenderStyle(basis->RenderStyle), LeftDistance(0),
Alpha(basis->alpha)
{
}
DBaseDecal::DBaseDecal (const DBaseDecal *basis)
: DThinker(STAT_DECAL),
WallNext(0), WallPrev(0), x(basis->x), y(basis->y), z(basis->z), AlphaColor(basis->AlphaColor),
Translation(basis->Translation), PicNum(basis->PicNum), RenderFlags(basis->RenderFlags),
XScale(basis->XScale), YScale(basis->YScale), RenderStyle(basis->RenderStyle), LeftDistance(0),
Alpha(basis->Alpha)
{
}
void DBaseDecal::Destroy ()
{
Remove ();
Super::Destroy ();
}
2006-04-12 01:50:09 +00:00
void DBaseDecal::Remove ()
{
2006-04-12 01:50:09 +00:00
DBaseDecal **prev = WallPrev;
DBaseDecal *next = WallNext;
if (prev && (*prev = next))
2006-04-12 01:50:09 +00:00
next->WallPrev = prev;
WallPrev = NULL;
WallNext = NULL;
}
2006-04-12 01:50:09 +00:00
void DBaseDecal::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << x << y << z
<< AlphaColor
<< Translation
<< PicNum
<< RenderFlags
<< XScale << YScale
<< RenderStyle
<< LeftDistance
<< Alpha;
}
void DBaseDecal::SerializeChain (FArchive &arc, DBaseDecal **first)
{
DWORD numInChain;
2006-04-12 01:50:09 +00:00
DBaseDecal *fresh;
DBaseDecal **firstptr = first;
if (arc.IsLoading ())
{
numInChain = arc.ReadCount ();
while (numInChain--)
{
arc << fresh;
*firstptr = fresh;
2006-04-12 01:50:09 +00:00
fresh->WallPrev = firstptr;
firstptr = &fresh->WallNext;
}
}
else
{
numInChain = 0;
fresh = *firstptr;
while (fresh != NULL)
{
2006-04-12 01:50:09 +00:00
fresh = fresh->WallNext;
++numInChain;
}
arc.WriteCount (numInChain);
fresh = *firstptr;
while (numInChain--)
{
arc << fresh;
2006-04-12 01:50:09 +00:00
fresh = fresh->WallNext;
}
}
}
2006-04-12 01:50:09 +00:00
void DBaseDecal::MoveChain (DBaseDecal *first, fixed_t x, fixed_t y)
{
while (first != NULL)
{
first->x += x;
first->y += y;
2006-04-12 01:50:09 +00:00
first = (DBaseDecal *)first->WallNext;
}
}
2006-04-12 01:50:09 +00:00
void DBaseDecal::FixForSide (side_t *wall)
{
2006-04-12 01:50:09 +00:00
DBaseDecal *decal = wall->AttachedDecals;
line_t *line = &lines[wall->linenum];
int wallnum = int(wall - sides);
vertex_t *v1, *v2;
if (line->sidenum[0] == wallnum)
{
v1 = line->v1;
v2 = line->v2;
}
else
{
v1 = line->v2;
v2 = line->v1;
}
fixed_t dx = v2->x - v1->x;
fixed_t dy = v2->y - v1->y;
while (decal != NULL)
{
2006-04-12 01:50:09 +00:00
decal->x = v1->x + MulScale2 (decal->LeftDistance, dx);
decal->y = v1->y + MulScale2 (decal->LeftDistance, dy);
decal = decal->WallNext;
}
}
2006-04-12 01:50:09 +00:00
void DBaseDecal::SetShade (DWORD rgb)
{
2006-04-12 01:50:09 +00:00
PalEntry *entry = (PalEntry *)&rgb;
AlphaColor = rgb | (ColorMatcher.Pick (entry->r, entry->g, entry->b) << 24);
}
2006-04-12 01:50:09 +00:00
void DBaseDecal::SetShade (int r, int g, int b)
{
2006-04-12 01:50:09 +00:00
AlphaColor = MAKEARGB(ColorMatcher.Pick (r, g, b), r, g, b);
}
// Returns the texture the decal stuck to.
2006-04-12 01:50:09 +00:00
int DBaseDecal::StickToWall (side_t *wall)
{
// Stick the decal at the end of the chain so it appears on top
2006-04-12 01:50:09 +00:00
DBaseDecal *next, **prev;
2006-04-12 01:50:09 +00:00
prev = &wall->AttachedDecals;
while (*prev != NULL)
{
next = *prev;
2006-04-12 01:50:09 +00:00
prev = &next->WallNext;
}
*prev = this;
2006-04-12 01:50:09 +00:00
WallNext = NULL;
WallPrev = prev;
/*
2006-04-12 01:50:09 +00:00
WallNext = wall->AttachedDecals;
WallPrev = &wall->AttachedDecals;
if (WallNext)
WallNext->WallPrev = &WallNext;
wall->AttachedDecals = this;
*/
sector_t *front, *back;
line_t *line;
int tex;
line = &lines[wall->linenum];
if (line->sidenum[0] == wall - sides)
{
front = line->frontsector;
back = line->backsector;
}
else
{
front = line->backsector;
back = line->frontsector;
}
if (back == NULL)
{
2006-04-12 01:50:09 +00:00
RenderFlags |= RF_RELMID;
if (line->flags & ML_DONTPEGBOTTOM)
z -= front->floortexz;
else
z -= front->ceilingtexz;
tex = wall->midtexture;
}
else if (back->floorplane.ZatPoint (x, y) >= z)
{
2006-04-12 01:50:09 +00:00
RenderFlags |= RF_RELLOWER|RF_CLIPLOWER;
if (line->flags & ML_DONTPEGBOTTOM)
z -= front->ceilingtexz;
else
z -= back->floortexz;
tex = wall->bottomtexture;
}
else
{
2006-04-12 01:50:09 +00:00
RenderFlags |= RF_RELUPPER|RF_CLIPUPPER;
if (line->flags & ML_DONTPEGTOP)
z -= front->ceilingtexz;
else
z -= back->ceilingtexz;
tex = wall->toptexture;
}
CalcFracPos (wall);
return tex;
}
2006-04-12 01:50:09 +00:00
fixed_t DBaseDecal::GetRealZ (const side_t *wall) const
{
const line_t *line = &lines[wall->linenum];
const sector_t *front, *back;
if (line->sidenum[0] == wall - sides)
{
front = line->frontsector;
back = line->backsector;
}
else
{
front = line->backsector;
back = line->frontsector;
}
if (back == NULL)
{
back = front;
}
2006-04-12 01:50:09 +00:00
switch (RenderFlags & RF_RELMASK)
{
default:
return z;
case RF_RELUPPER:
if (curline->linedef->flags & ML_DONTPEGTOP)
{
return z + front->ceilingtexz;
}
else
{
return z + back->ceilingtexz;
}
case RF_RELLOWER:
if (curline->linedef->flags & ML_DONTPEGBOTTOM)
{
return z + front->ceilingtexz;
}
else
{
return z + back->floortexz;
}
case RF_RELMID:
if (curline->linedef->flags & ML_DONTPEGBOTTOM)
{
return z + front->floortexz;
}
else
{
return z + front->ceilingtexz;
}
}
}
2006-04-12 01:50:09 +00:00
void DBaseDecal::CalcFracPos (side_t *wall)
{
line_t *line = &lines[wall->linenum];
int wallnum = int(wall - sides);
vertex_t *v1, *v2;
if (line->sidenum[0] == wallnum)
{
v1 = line->v1;
v2 = line->v2;
}
else
{
v1 = line->v2;
v2 = line->v1;
}
fixed_t dx = v2->x - v1->x;
fixed_t dy = v2->y - v1->y;
if (abs(dx) > abs(dy))
{
2006-04-12 01:50:09 +00:00
LeftDistance = SafeDivScale2 (x - v1->x, dx);
}
else if (dy != 0)
{
2006-04-12 01:50:09 +00:00
LeftDistance = SafeDivScale2 (y - v1->y, dy);
}
else
{
2006-04-12 01:50:09 +00:00
LeftDistance = 0;
}
}
static void GetWallStuff (side_t *wall, vertex_t *&v1, fixed_t &ldx, fixed_t &ldy)
{
line_t *line = &lines[wall->linenum];
if (line->sidenum[0] == wall - sides)
{
v1 = line->v1;
ldx = line->dx;
ldy = line->dy;
}
else
{
v1 = line->v2;
ldx = -line->dx;
ldy = -line->dy;
}
}
static fixed_t Length (fixed_t dx, fixed_t dy)
{
return (fixed_t)sqrtf ((float)dx*(float)dx+(float)dy*(float)dy);
}
static side_t *NextWall (const side_t *wall)
{
line_t *line = &lines[wall->linenum];
int wallnum = int(wall - sides);
if (line->sidenum[0] == wallnum)
{
if (line->sidenum[1] != NO_SIDE)
{
return sides + line->sidenum[1];
}
}
else if (line->sidenum[1] == wallnum)
{
return sides + line->sidenum[0];
}
return NULL;
}
2006-04-12 01:50:09 +00:00
void DBaseDecal::SpreadLeft (fixed_t r, vertex_t *v1, side_t *feelwall)
{
fixed_t ldx, ldy;
SpreadStack.Push (feelwall);
while (r < 0 && feelwall->LeftSide != NO_SIDE)
{
fixed_t startr = r;
fixed_t x = v1->x;
fixed_t y = v1->y;
feelwall = &sides[feelwall->LeftSide];
GetWallStuff (feelwall, v1, ldx, ldy);
fixed_t wallsize = Length (ldx, ldy);
r += DecalLeft;
x += Scale (r, ldx, wallsize);
y += Scale (r, ldy, wallsize);
r = wallsize + startr;
2006-04-12 01:50:09 +00:00
SpreadSource->CloneSelf (SpreadTemplate, x, y, SpreadZ, feelwall);
SpreadStack.Push (feelwall);
side_t *nextwall = NextWall (feelwall);
if (nextwall != NULL && nextwall->LeftSide != NO_SIDE)
{
int i;
for (i = SpreadStack.Size(); i-- > 0; )
{
if (SpreadStack[i] == nextwall)
break;
}
if (i == -1)
{
vertex_t *v2;
GetWallStuff (nextwall, v2, ldx, ldy);
SpreadLeft (startr, v2, nextwall);
}
}
}
}
2006-04-12 01:50:09 +00:00
void DBaseDecal::SpreadRight (fixed_t r, side_t *feelwall, fixed_t wallsize)
{
vertex_t *v1;
fixed_t x, y, ldx, ldy;
SpreadStack.Push (feelwall);
while (r > wallsize && feelwall->RightSide != NO_SIDE)
{
feelwall = &sides[feelwall->RightSide];
side_t *nextwall = NextWall (feelwall);
if (nextwall != NULL && nextwall->LeftSide != NO_SIDE)
{
int i;
for (i = SpreadStack.Size(); i-- > 0; )
{
if (SpreadStack[i] == nextwall)
break;
}
if (i == -1)
{
SpreadRight (r, nextwall, wallsize);
}
}
r = DecalWidth - r + wallsize - DecalLeft;
GetWallStuff (feelwall, v1, ldx, ldy);
x = v1->x;
y = v1->y;
wallsize = Length (ldx, ldy);
x -= Scale (r, ldx, wallsize);
y -= Scale (r, ldy, wallsize);
r = DecalRight - r;
2006-04-12 01:50:09 +00:00
SpreadSource->CloneSelf (SpreadTemplate, x, y, SpreadZ, feelwall);
SpreadStack.Push (feelwall);
}
}
2006-04-12 01:50:09 +00:00
void DBaseDecal::Spread (const FDecalTemplate *tpl, side_t *wall)
{
FTexture *tex;
vertex_t *v1;
fixed_t rorg, ldx, ldy;
int xscale, yscale;
GetWallStuff (wall, v1, ldx, ldy);
rorg = Length (x - v1->x, y - v1->y);
tex = TexMan[PicNum];
int dwidth = tex->GetWidth ();
xscale = (XScale + 1) << (FRACBITS - 6);
yscale = (YScale + 1) << (FRACBITS - 6);
DecalWidth = dwidth * xscale;
DecalLeft = tex->LeftOffset * xscale;
DecalRight = DecalWidth - DecalLeft;
SpreadSource = this;
SpreadTemplate = tpl;
SpreadZ = z;
// Try spreading left first
SpreadLeft (rorg - DecalLeft, v1, wall);
SpreadStack.Clear ();
// Then try spreading right
SpreadRight (rorg + DecalRight, wall,
Length (lines[wall->linenum].dx, lines[wall->linenum].dy));
SpreadStack.Clear ();
}
DBaseDecal *DBaseDecal::CloneSelf (const FDecalTemplate *tpl, fixed_t ix, fixed_t iy, fixed_t iz, side_t *wall) const
{
DBaseDecal *decal = new DBaseDecal(ix, iy, iz);
if (decal != NULL)
{
decal->StickToWall (wall);
tpl->ApplyToDecal (decal);
decal->AlphaColor = AlphaColor;
decal->RenderFlags = (decal->RenderFlags & RF_DECALMASK) |
(this->RenderFlags & ~RF_DECALMASK);
}
return decal;
}
CUSTOM_CVAR (Int, cl_maxdecals, 1024, CVAR_ARCHIVE)
{
if (self < 0)
{
self = 0;
}
else
{
while (ImpactCount > self)
{
FirstImpact->Destroy ();
}
}
}
// Uses: target points to previous impact decal
// tracer points to next impact decal
//
// Note that this means we can't simply serialize an impact decal as-is
// because doing so when many are present in a level could result in
// a lot of recursion and we would run out of stack. Not nice. So instead,
// the save game code calls DImpactDecal::SerializeAll to serialize a
// list of impact decals.
void DImpactDecal::SerializeTime (FArchive &arc)
{
if (arc.IsLoading ())
{
ImpactCount = 0;
FirstImpact = LastImpact = NULL;
}
}
void DImpactDecal::Serialize (FArchive &arc)
{
Super::Serialize (arc);
}
DImpactDecal::DImpactDecal ()
: ImpactNext(0), ImpactPrev(0)
{
Link();
}
DImpactDecal::DImpactDecal (fixed_t x, fixed_t y, fixed_t z)
: DBaseDecal (x, y, z),
ImpactNext(0), ImpactPrev(0)
{
Link();
}
void DImpactDecal::Link ()
{
ImpactCount++;
ImpactPrev = LastImpact;
if (ImpactPrev != NULL)
{
ImpactPrev->ImpactNext = this;
}
else
{
FirstImpact = this;
}
LastImpact = this;
}
DImpactDecal *DImpactDecal::StaticCreate (const char *name, fixed_t x, fixed_t y, fixed_t z, side_t *wall, PalEntry color)
{
2006-04-12 01:50:09 +00:00
if (cl_maxdecals > 0)
{
2006-04-12 01:50:09 +00:00
const FDecalTemplate *tpl = DecalLibrary.GetDecalByName (name);
if (tpl != NULL && (tpl = tpl->GetDecal()) != NULL)
{
2006-04-12 01:50:09 +00:00
return StaticCreate (tpl, x, y, z, wall, color);
}
}
return NULL;
}
DImpactDecal *DImpactDecal::StaticCreate (const FDecalTemplate *tpl, fixed_t x, fixed_t y, fixed_t z, side_t *wall, PalEntry color)
{
DImpactDecal *decal = NULL;
if (tpl != NULL && cl_maxdecals > 0 && !(wall->Flags & WALLF_NOAUTODECALS))
{
if (tpl->LowerDecal)
{
StaticCreate (tpl->LowerDecal->GetDecal(), x, y, z, wall);
}
if (ImpactCount >= cl_maxdecals)
{
FirstImpact->Destroy ();
}
2006-04-12 01:50:09 +00:00
decal = new DImpactDecal (x, y, z);
2006-04-12 01:50:09 +00:00
int stickypic = decal->StickToWall (wall);
FTexture *tex = TexMan[stickypic];
if (tex != NULL && tex->bNoDecals)
{
return NULL;
}
2006-04-12 01:50:09 +00:00
if (decal == NULL)
{
return NULL;
}
2006-04-12 01:50:09 +00:00
tpl->ApplyToDecal (decal);
if (color != 0)
{
decal->SetShade (color.r, color.g, color.b);
}
2006-04-12 01:50:09 +00:00
if (!cl_spreaddecals || decal->PicNum == 0xffff)
{
return decal;
}
2006-04-12 01:50:09 +00:00
// Spread decal to nearby walls if it does not all fit on this one
decal->Spread (tpl, wall);
}
2006-04-12 01:50:09 +00:00
return decal;
}
2006-04-12 01:50:09 +00:00
DBaseDecal *DImpactDecal::CloneSelf (const FDecalTemplate *tpl, fixed_t ix, fixed_t iy, fixed_t iz, side_t *wall) const
{
if (ImpactCount >= cl_maxdecals)
{
FirstImpact->Destroy ();
}
2006-04-12 01:50:09 +00:00
DImpactDecal *decal = new DImpactDecal(ix, iy, iz);
if (decal != NULL)
{
2006-04-12 01:50:09 +00:00
decal->StickToWall (wall);
tpl->ApplyToDecal (decal);
decal->AlphaColor = AlphaColor;
decal->RenderFlags = (decal->RenderFlags & RF_DECALMASK) |
(this->RenderFlags & ~RF_DECALMASK);
}
2006-04-12 01:50:09 +00:00
return decal;
}
2006-04-12 01:50:09 +00:00
void DImpactDecal::Destroy ()
{
2006-04-12 01:50:09 +00:00
if (ImpactPrev != NULL)
{
2006-04-12 01:50:09 +00:00
ImpactPrev->ImpactNext = ImpactNext;
}
2006-04-12 01:50:09 +00:00
if (ImpactNext != NULL)
{
2006-04-12 01:50:09 +00:00
ImpactNext->ImpactPrev = ImpactPrev;
}
if (LastImpact == this)
{
2006-04-12 01:50:09 +00:00
LastImpact = ImpactPrev;
}
if (FirstImpact == this)
{
2006-04-12 01:50:09 +00:00
FirstImpact = ImpactNext;
}
ImpactCount--;
Super::Destroy ();
}
CCMD (countdecals)
{
Printf ("%d impact decals\n", ImpactCount);
}
CCMD (countdecalsreal)
{
2006-04-12 01:50:09 +00:00
TThinkerIterator<DImpactDecal> iterator (STAT_DECAL);
int count = 0;
while (iterator.Next())
count++;
Printf ("Counted %d impact decals\n", count);
}
CCMD (spray)
{
if (argv.argc() < 2 || m_Instigator == NULL)
{
Printf ("Usage: spray <decal>\n");
return;
}
FTraceResults trace;
angle_t ang = m_Instigator->angle >> ANGLETOFINESHIFT;
angle_t pitch = (angle_t)(m_Instigator->pitch) >> ANGLETOFINESHIFT;
fixed_t vx = FixedMul (finecosine[pitch], finecosine[ang]);
fixed_t vy = FixedMul (finecosine[pitch], finesine[ang]);
fixed_t vz = -finesine[pitch];
if (Trace (m_Instigator->x, m_Instigator->y,
m_Instigator->z + m_Instigator->height - (m_Instigator->height>>2),
m_Instigator->Sector,
vx, vy, vz, 172*FRACUNIT, 0, ML_BLOCKEVERYTHING, m_Instigator,
trace, TRACE_NoSky))
{
if (trace.HitType == TRACE_HitWall)
{
2006-04-12 01:50:09 +00:00
DImpactDecal::StaticCreate (argv[1],
trace.X, trace.Y, trace.Z,
sides + trace.Line->sidenum[trace.Side]);
}
}
}
2006-04-12 01:50:09 +00:00
class ADecal : public AActor
{
DECLARE_STATELESS_ACTOR (ADecal, AActor);
public:
void BeginPlay ();
bool DoTrace (DBaseDecal *decal, angle_t angle, sector_t *sec);
};
IMPLEMENT_STATELESS_ACTOR (ADecal, Any, 9200, 0)
END_DEFAULTS
void ADecal::BeginPlay ()
{
Super::BeginPlay ();
// Find a wall to attach to, and set RenderFlags to keep
// the decal at its current height. If the decal cannot find a wall
// within 64 units, it destroys itself.
//
// Subclasses can set special1 if they don't want this sticky logic.
DBaseDecal *decal = new DBaseDecal (this);
if (!DoTrace (decal, angle, Sector))
{
decal->Destroy();
return;
}
if (args[0] != 0)
{
const FDecalTemplate *tpl = DecalLibrary.GetDecalByNum (args[0]);
if (tpl != NULL)
{
tpl->ApplyToDecal (decal);
}
}
}
bool ADecal::DoTrace (DBaseDecal *decal, angle_t angle, sector_t *sec)
{
FTraceResults trace;
Trace (x, y, z, sec,
finecosine[(angle+ANGLE_180)>>ANGLETOFINESHIFT],
finesine[(angle+ANGLE_180)>>ANGLETOFINESHIFT], 0,
64*FRACUNIT, 0, 0, NULL, trace, TRACE_NoSky);
if (trace.HitType == TRACE_HitWall)
{
decal->x = trace.X;
decal->y = trace.Y;
decal->StickToWall (sides + trace.Line->sidenum[trace.Side]);
return true;
}
return false;
}