qzdoom/src/g_doom/a_painelemental.cpp
2013-06-07 03:31:30 +00:00

204 lines
5.2 KiB
C++

/*
#include "actor.h"
#include "info.h"
#include "p_enemy.h"
#include "p_local.h"
#include "a_action.h"
#include "templates.h"
#include "m_bbox.h"
#include "thingdef/thingdef.h"
#include "doomstat.h"
*/
DECLARE_ACTION(A_SkullAttack)
static PClassActor *GetSpawnType(VMValue *param)
{
PClassActor *spawntype;
if (param == NULL || param->Type == REGT_NIL)
{
spawntype = NULL;
}
else
{
assert(param->Type == REGT_POINTER);
assert(param->atag == ATAG_OBJECT || param->a == NULL);
spawntype = (PClassActor *)param->a;
}
return (spawntype != NULL) ? spawntype : PClass::FindActor("LostSoul");
}
enum PA_Flags
{
PAF_NOSKULLATTACK = 1,
PAF_AIMFACING = 2,
PAF_NOTARGET = 4,
};
//
// A_PainShootSkull
// Spawn a lost soul and launch it at the target
//
void A_PainShootSkull (AActor *self, angle_t angle, PClassActor *spawntype, int flags = 0, int limit = -1)
{
fixed_t x, y, z;
AActor *other;
angle_t an;
int prestep;
if (spawntype == NULL) return;
if (self->DamageType == NAME_Massacre) return;
// [RH] check to make sure it's not too close to the ceiling
if (self->z + self->height + 8*FRACUNIT > self->ceilingz)
{
if (self->flags & MF_FLOAT)
{
self->velz -= 2*FRACUNIT;
self->flags |= MF_INFLOAT;
self->flags4 |= MF4_VFRICTION;
}
return;
}
// [RH] make this optional
if (limit == -1 && (i_compatflags & COMPATF_LIMITPAIN))
limit = 21;
if (limit)
{
// count total number of skulls currently on the level
// if there are already 21 skulls on the level, don't spit another one
int count = limit;
FThinkerIterator iterator (spawntype);
DThinker *othink;
while ( (othink = iterator.Next ()) )
{
if (--count == 0)
return;
}
}
// okay, there's room for another one
an = angle >> ANGLETOFINESHIFT;
prestep = 4*FRACUNIT +
3*(self->radius + GetDefaultByType(spawntype)->radius)/2;
x = self->x + FixedMul (prestep, finecosine[an]);
y = self->y + FixedMul (prestep, finesine[an]);
z = self->z + 8*FRACUNIT;
// Check whether the Lost Soul is being fired through a 1-sided // phares
// wall or an impassible line, or a "monsters can't cross" line.// |
// If it is, then we don't allow the spawn. // V
FBoundingBox box(MIN(self->x, x), MIN(self->y, y), MAX(self->x, x), MAX(self->y, y));
FBlockLinesIterator it(box);
line_t *ld;
while ((ld = it.Next()))
{
if (!(ld->flags & ML_TWOSIDED) ||
(ld->flags & (ML_BLOCKING|ML_BLOCKMONSTERS|ML_BLOCKEVERYTHING)))
{
if (!(box.Left() > ld->bbox[BOXRIGHT] ||
box.Right() < ld->bbox[BOXLEFT] ||
box.Top() < ld->bbox[BOXBOTTOM] ||
box.Bottom() > ld->bbox[BOXTOP]))
{
if (P_PointOnLineSide(self->x,self->y,ld) != P_PointOnLineSide(x,y,ld))
return; // line blocks trajectory // ^
}
}
}
other = Spawn (spawntype, x, y, z, ALLOW_REPLACE);
// Check to see if the new Lost Soul's z value is above the
// ceiling of its new sector, or below the floor. If so, kill it.
if ((other->z >
(other->Sector->ceilingplane.ZatPoint (other->x, other->y) - other->height)) ||
(other->z < other->Sector->floorplane.ZatPoint (other->x, other->y)))
{
// kill it immediately
P_DamageMobj (other, self, self, TELEFRAG_DAMAGE, NAME_None);// ^
return; // |
} // phares
// Check for movements.
if (!P_CheckPosition (other, other->x, other->y))
{
// kill it immediately
P_DamageMobj (other, self, self, TELEFRAG_DAMAGE, NAME_None);
return;
}
// [RH] Lost souls hate the same things as their pain elementals
other->CopyFriendliness (self, !(flags & PAF_NOTARGET));
if (!(flags & PAF_NOSKULLATTACK))
A_SkullAttack(other, SKULLSPEED);
}
//
// A_PainAttack
// Spawn a lost soul and launch it at the target
//
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PainAttack)
{
PARAM_ACTION_PROLOGUE;
if (!self->target)
return 0;
PARAM_CLASS_OPT (spawntype, AActor) { spawntype = NULL; }
PARAM_ANGLE_OPT (angle) { angle = 0; }
PARAM_INT_OPT (flags) { flags = 0; }
PARAM_INT_OPT (limit) { limit = -1; }
if (spawntype == NULL) spawntype = PClass::FindActor("LostSoul");
if (!(flags & PAF_AIMFACING))
A_FaceTarget (self);
A_PainShootSkull (self, self->angle+angle, spawntype, flags, limit);
return 0;
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DualPainAttack)
{
PARAM_ACTION_PROLOGUE;
if (!self->target)
return 0;
PClassActor *spawntype = GetSpawnType(numparam > NAP ? &param[NAP] : NULL);
A_FaceTarget (self);
A_PainShootSkull (self, self->angle + ANG45, spawntype);
A_PainShootSkull (self, self->angle - ANG45, spawntype);
return 0;
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PainDie)
{
PARAM_ACTION_PROLOGUE;
if (self->target != NULL && self->IsFriend(self->target))
{ // And I thought you were my friend!
self->flags &= ~MF_FRIENDLY;
}
PClassActor *spawntype = GetSpawnType(numparam > NAP ? &param[NAP] : NULL);
A_Unblock(self, true);
A_PainShootSkull (self, self->angle + ANG90, spawntype);
A_PainShootSkull (self, self->angle + ANG180, spawntype);
A_PainShootSkull (self, self->angle + ANG270, spawntype);
return 0;
}