2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
#include "a_sharedglobal.h"
|
|
|
|
#include "p_local.h"
|
|
|
|
#include "g_level.h"
|
|
|
|
#include "r_sky.h"
|
|
|
|
#include "p_lnspec.h"
|
|
|
|
#include "b_bot.h"
|
|
|
|
#include "p_checkposition.h"
|
2016-11-24 19:02:44 +00:00
|
|
|
#include "virtual.h"
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-11-24 20:36:02 +00:00
|
|
|
IMPLEMENT_CLASS(AFastProjectile, false, false)
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// AFastProjectile :: Tick
|
|
|
|
//
|
|
|
|
// Thinker for the ultra-fast projectiles used by Heretic and Hexen
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void AFastProjectile::Tick ()
|
|
|
|
{
|
|
|
|
int i;
|
2016-03-22 23:53:09 +00:00
|
|
|
DVector3 frac;
|
2016-03-01 15:47:10 +00:00
|
|
|
int changexy;
|
|
|
|
|
|
|
|
ClearInterpolation();
|
2016-03-22 23:53:09 +00:00
|
|
|
double oldz = Z();
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
if (!(flags5 & MF5_NOTIMEFREEZE))
|
|
|
|
{
|
|
|
|
//Added by MC: Freeze mode.
|
|
|
|
if (bglobal.freeze || level.flags2 & LEVEL2_FROZEN)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// [RH] Ripping is a little different than it was in Hexen
|
|
|
|
FCheckPosition tm(!!(flags2 & MF2_RIP));
|
|
|
|
|
|
|
|
int count = 8;
|
2016-03-22 23:53:09 +00:00
|
|
|
if (radius > 0)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-22 23:53:09 +00:00
|
|
|
while ( fabs(Vel.X) > radius * count || fabs(Vel.Y) > radius * count)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
// we need to take smaller steps.
|
2016-03-22 23:53:09 +00:00
|
|
|
count += count;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle movement
|
2016-03-20 18:52:35 +00:00
|
|
|
if (!Vel.isZero() || (Z() != floorz))
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-10-02 12:37:26 +00:00
|
|
|
// force some lateral movement so that collision detection works as intended.
|
|
|
|
if ((flags & MF_MISSILE) && Vel.X == 0 && Vel.Y == 0 && !IsZeroDamage())
|
|
|
|
{
|
|
|
|
Vel.X = MinVel;
|
|
|
|
}
|
|
|
|
|
2016-03-22 23:53:09 +00:00
|
|
|
frac = Vel / count;
|
|
|
|
changexy = frac.X != 0 || frac.Y != 0;
|
|
|
|
int ripcount = count / 8;
|
2016-03-01 15:47:10 +00:00
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
if (changexy)
|
|
|
|
{
|
|
|
|
if (--ripcount <= 0)
|
|
|
|
{
|
|
|
|
tm.LastRipped.Clear(); // [RH] Do rip damage each step, like Hexen
|
|
|
|
}
|
|
|
|
|
2016-03-23 09:42:41 +00:00
|
|
|
if (!P_TryMove (this, Pos() + frac, true, NULL, tm))
|
2016-03-01 15:47:10 +00:00
|
|
|
{ // Blocked move
|
|
|
|
if (!(flags3 & MF3_SKYEXPLODE))
|
|
|
|
{
|
|
|
|
if (tm.ceilingline &&
|
|
|
|
tm.ceilingline->backsector &&
|
|
|
|
tm.ceilingline->backsector->GetTexture(sector_t::ceiling) == skyflatnum &&
|
2016-03-25 17:19:54 +00:00
|
|
|
Z() >= tm.ceilingline->backsector->ceilingplane.ZatPoint(PosRelative(tm.ceilingline)))
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
P_ExplodeMissile (this, BlockingLine, BlockingMobj);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2016-03-22 23:53:09 +00:00
|
|
|
AddZ(frac.Z);
|
2016-03-26 00:13:36 +00:00
|
|
|
UpdateWaterLevel ();
|
2016-03-22 23:53:09 +00:00
|
|
|
oldz = Z();
|
|
|
|
if (oldz <= floorz)
|
2016-03-01 15:47:10 +00:00
|
|
|
{ // Hit the floor
|
|
|
|
|
|
|
|
if (floorpic == skyflatnum && !(flags3 & MF3_SKYEXPLODE))
|
|
|
|
{
|
|
|
|
// [RH] Just remove the missile without exploding it
|
|
|
|
// if this is a sky floor.
|
|
|
|
Destroy ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-03-20 18:52:35 +00:00
|
|
|
SetZ(floorz);
|
2016-03-01 15:47:10 +00:00
|
|
|
P_HitFloor (this);
|
|
|
|
P_ExplodeMissile (this, NULL, NULL);
|
|
|
|
return;
|
|
|
|
}
|
2016-03-20 12:32:53 +00:00
|
|
|
if (Top() > ceilingz)
|
2016-03-01 15:47:10 +00:00
|
|
|
{ // Hit the ceiling
|
|
|
|
|
|
|
|
if (ceilingpic == skyflatnum && !(flags3 & MF3_SKYEXPLODE))
|
|
|
|
{
|
|
|
|
Destroy ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-03-20 19:55:06 +00:00
|
|
|
SetZ(ceilingz - Height);
|
2016-03-01 15:47:10 +00:00
|
|
|
P_ExplodeMissile (this, NULL, NULL);
|
|
|
|
return;
|
|
|
|
}
|
2016-10-02 12:38:48 +00:00
|
|
|
if (!frac.isZero() && ripcount <= 0)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
ripcount = count >> 3;
|
2016-11-24 19:02:44 +00:00
|
|
|
|
|
|
|
// call the scripted 'Effect' method.
|
2016-11-24 20:36:02 +00:00
|
|
|
IFVIRTUAL(AFastProjectile, Effect)
|
|
|
|
{
|
|
|
|
// Without the type cast this picks the 'void *' assignment...
|
|
|
|
VMValue params[1] = { (DObject*)this };
|
2016-11-30 16:15:01 +00:00
|
|
|
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
|
2016-11-24 20:36:02 +00:00
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!CheckNoDelay())
|
|
|
|
return; // freed itself
|
|
|
|
// Advance the state
|
|
|
|
if (tics != -1)
|
|
|
|
{
|
|
|
|
if (tics > 0) tics--;
|
|
|
|
while (!tics)
|
|
|
|
{
|
|
|
|
if (!SetState (state->GetNextState ()))
|
|
|
|
{ // mobj was removed
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|