gzdoom-gles/wadsrc/static/zscript/actors/shared/fastprojectile.zs

195 lines
4.1 KiB
Text

// Fast projectiles --------------------------------------------------------
class FastProjectile : Actor
{
Default
{
Projectile;
MissileHeight 0;
}
virtual void Effect()
{
class<Actor> trail = MissileName;
if (trail != null)
{
double hitz = pos.z - 8;
if (hitz < floorz)
{
hitz = floorz;
}
// Do not clip this offset to the floor.
hitz += MissileHeight;
Actor act = Spawn (trail, (pos.xy, hitz), ALLOW_REPLACE);
if (act != null)
{
if (bGetOwner && target != null)
act.target = target;
else
act.target = self;
act.angle = angle;
act.pitch = pitch;
}
}
}
//----------------------------------------------------------------------------
//
// AFastProjectile :: Tick
//
// Thinker for the ultra-fast projectiles used by Heretic and Hexen
//
//----------------------------------------------------------------------------
override void Tick ()
{
ClearInterpolation();
double oldz = pos.Z;
if (isFrozen())
return;
// [RH] Ripping is a little different than it was in Hexen
FCheckPosition tm;
tm.DoRipping = bRipper;
int count = 8;
if (radius > 0)
{
while (abs(Vel.X) >= radius * count || abs(Vel.Y) >= radius * count)
{
// we need to take smaller steps.
count += count;
}
}
if (height > 0)
{
while (abs(Vel.Z) >= height * count)
{
count += count;
}
}
// Handle movement
bool ismoved = Vel != (0, 0, 0)
// Check Z position set during previous tick.
// It should be strictly equal to the argument of SetZ() function.
|| ( (pos.Z != floorz ) /* Did it hit the floor? */
&& (pos.Z != ceilingz - Height) /* Did it hit the ceiling? */ );
if (ismoved)
{
// force some lateral movement so that collision detection works as intended.
if (bMissile && Vel.X == 0 && Vel.Y == 0 && !IsZeroDamage())
{
VelFromAngle(MinVel);
}
Vector3 frac = Vel / count;
int changexy = frac.X != 0 || frac.Y != 0;
int ripcount = count / 8;
for (int i = 0; i < count; i++)
{
if (changexy)
{
if (--ripcount <= 0)
{
tm.ClearLastRipped(); // [RH] Do rip damage each step, like Hexen
}
if (!TryMove (Pos.XY + frac.XY, true, NULL, tm))
{ // Blocked move
if (!bSkyExplode)
{
let l = tm.ceilingline;
if (l &&
l.backsector &&
l.backsector.GetTexture(sector.ceiling) == skyflatnum)
{
let posr = PosRelative(l.backsector);
if (pos.Z >= l.backsector.ceilingplane.ZatPoint(posr.XY))
{
// Hack to prevent missiles exploding against the sky.
// Does not handle sky floors.
Destroy ();
return;
}
}
// [RH] Don't explode on horizon lines.
if (BlockingLine != NULL && BlockingLine.special == Line_Horizon)
{
Destroy ();
return;
}
}
ExplodeMissile (BlockingLine, BlockingMobj);
return;
}
}
AddZ(frac.Z);
UpdateWaterLevel ();
oldz = pos.Z;
if (oldz <= floorz)
{ // Hit the floor
if (floorpic == skyflatnum && !bSkyExplode)
{
// [RH] Just remove the missile without exploding it
// if this is a sky floor.
Destroy ();
return;
}
SetZ(floorz);
HitFloor ();
Destructible.ProjectileHitPlane(self, SECPART_Floor);
ExplodeMissile (NULL, NULL);
return;
}
if (pos.Z + height > ceilingz)
{ // Hit the ceiling
if (ceilingpic == skyflatnum && !bSkyExplode)
{
Destroy ();
return;
}
SetZ(ceilingz - Height);
Destructible.ProjectileHitPlane(self, SECPART_Ceiling);
ExplodeMissile (NULL, NULL);
return;
}
CheckPortalTransition();
if (changexy && ripcount <= 0)
{
ripcount = count >> 3;
// call the 'Effect' method.
Effect();
}
}
}
if (!CheckNoDelay())
return; // freed itself
// Advance the state
if (tics != -1)
{
if (tics > 0) tics--;
while (!tics)
{
if (!SetState (CurState.NextState))
{ // mobj was removed
return;
}
}
}
}
}