mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-12-13 13:40:53 +00:00
72bbb19cc7
From what I can see, GCC would miscompile the involved loops, because the index variable is 'signed int' and the multiplication with an unsigned would cause signed overflow (undefined behavior). Change the index variable type to 'unsigned int' to expect unsigned overflow (conformant to standard).
208 lines
5.2 KiB
C++
208 lines
5.2 KiB
C++
/*
|
|
#include "actor.h"
|
|
#include "info.h"
|
|
#include "m_random.h"
|
|
#include "s_sound.h"
|
|
#include "p_local.h"
|
|
#include "p_enemy.h"
|
|
#include "a_action.h"
|
|
#include "gstrings.h"
|
|
#include "thingdef/thingdef.h"
|
|
#include "g_level.h"
|
|
*/
|
|
|
|
static FRandom pr_foo ("WhirlwindDamage");
|
|
static FRandom pr_atk ("LichAttack");
|
|
static FRandom pr_seek ("WhirlwindSeek");
|
|
|
|
class AWhirlwind : public AActor
|
|
{
|
|
DECLARE_CLASS (AWhirlwind, AActor)
|
|
public:
|
|
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
|
|
};
|
|
|
|
IMPLEMENT_CLASS(AWhirlwind)
|
|
|
|
int AWhirlwind::DoSpecialDamage (AActor *target, int damage, FName damagetype)
|
|
{
|
|
int randVal;
|
|
|
|
target->angle += pr_foo.Random2() << 20;
|
|
target->velx += pr_foo.Random2() << 10;
|
|
target->vely += pr_foo.Random2() << 10;
|
|
if ((level.time & 16) && !(target->flags2 & MF2_BOSS))
|
|
{
|
|
randVal = pr_foo();
|
|
if (randVal > 160)
|
|
{
|
|
randVal = 160;
|
|
}
|
|
target->velz += randVal << 11;
|
|
if (target->velz > 12*FRACUNIT)
|
|
{
|
|
target->velz = 12*FRACUNIT;
|
|
}
|
|
}
|
|
if (!(level.time & 7))
|
|
{
|
|
P_DamageMobj (target, NULL, this->target, 3, NAME_Melee);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_LichAttack
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_LichAttack)
|
|
{
|
|
int i;
|
|
AActor *fire;
|
|
AActor *baseFire;
|
|
AActor *mo;
|
|
AActor *target;
|
|
int randAttack;
|
|
static const int atkResolve1[] = { 50, 150 };
|
|
static const int atkResolve2[] = { 150, 200 };
|
|
int dist;
|
|
|
|
// Ice ball (close 20% : far 60%)
|
|
// Fire column (close 40% : far 20%)
|
|
// Whirlwind (close 40% : far 20%)
|
|
// Distance threshold = 8 cells
|
|
|
|
target = self->target;
|
|
if (target == NULL)
|
|
{
|
|
return;
|
|
}
|
|
A_FaceTarget (self);
|
|
if (self->CheckMeleeRange ())
|
|
{
|
|
int damage = pr_atk.HitDice (6);
|
|
int newdam = P_DamageMobj (target, self, self, damage, NAME_Melee);
|
|
P_TraceBleed (newdam > 0 ? newdam : damage, target, self);
|
|
return;
|
|
}
|
|
dist = P_AproxDistance (self->x-target->x, self->y-target->y)
|
|
> 8*64*FRACUNIT;
|
|
randAttack = pr_atk ();
|
|
if (randAttack < atkResolve1[dist])
|
|
{ // Ice ball
|
|
P_SpawnMissile (self, target, PClass::FindClass("HeadFX1"));
|
|
S_Sound (self, CHAN_BODY, "ironlich/attack2", 1, ATTN_NORM);
|
|
}
|
|
else if (randAttack < atkResolve2[dist])
|
|
{ // Fire column
|
|
baseFire = P_SpawnMissile (self, target, PClass::FindClass("HeadFX3"));
|
|
if (baseFire != NULL)
|
|
{
|
|
baseFire->SetState (baseFire->FindState("NoGrow"));
|
|
for (i = 0; i < 5; i++)
|
|
{
|
|
fire = Spawn("HeadFX3", baseFire->x, baseFire->y,
|
|
baseFire->z, ALLOW_REPLACE);
|
|
if (i == 0)
|
|
{
|
|
S_Sound (self, CHAN_BODY, "ironlich/attack1", 1, ATTN_NORM);
|
|
}
|
|
fire->target = baseFire->target;
|
|
fire->angle = baseFire->angle;
|
|
fire->velx = baseFire->velx;
|
|
fire->vely = baseFire->vely;
|
|
fire->velz = baseFire->velz;
|
|
fire->Damage = 0;
|
|
fire->health = (i+1) * 2;
|
|
P_CheckMissileSpawn (fire, self->radius);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // Whirlwind
|
|
mo = P_SpawnMissile (self, target, RUNTIME_CLASS(AWhirlwind));
|
|
if (mo != NULL)
|
|
{
|
|
mo->z -= 32*FRACUNIT;
|
|
mo->tracer = target;
|
|
mo->special1 = 60;
|
|
mo->special2 = 50; // Timer for active sound
|
|
mo->health = 20*TICRATE; // Duration
|
|
S_Sound (self, CHAN_BODY, "ironlich/attack3", 1, ATTN_NORM);
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_WhirlwindSeek
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_WhirlwindSeek)
|
|
{
|
|
self->health -= 3;
|
|
if (self->health < 0)
|
|
{
|
|
self->velx = self->vely = self->velz = 0;
|
|
self->SetState (self->FindState(NAME_Death));
|
|
self->flags &= ~MF_MISSILE;
|
|
return;
|
|
}
|
|
if ((self->special2 -= 3) < 0)
|
|
{
|
|
self->special2 = 58 + (pr_seek() & 31);
|
|
S_Sound (self, CHAN_BODY, "ironlich/attack3", 1, ATTN_NORM);
|
|
}
|
|
if (self->tracer && self->tracer->flags&MF_SHADOW)
|
|
{
|
|
return;
|
|
}
|
|
P_SeekerMissile (self, ANGLE_1*10, ANGLE_1*30);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_LichIceImpact
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_LichIceImpact)
|
|
{
|
|
unsigned int i;
|
|
angle_t angle;
|
|
AActor *shard;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
shard = Spawn("HeadFX2", self->x, self->y, self->z, ALLOW_REPLACE);
|
|
angle = i*ANG45;
|
|
shard->target = self->target;
|
|
shard->angle = angle;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
shard->velx = FixedMul (shard->Speed, finecosine[angle]);
|
|
shard->vely = FixedMul (shard->Speed, finesine[angle]);
|
|
shard->velz = -FRACUNIT*6/10;
|
|
P_CheckMissileSpawn (shard, self->radius);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_LichFireGrow
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_LichFireGrow)
|
|
{
|
|
self->health--;
|
|
self->z += 9*FRACUNIT;
|
|
if (self->health == 0)
|
|
{
|
|
self->Damage = self->GetDefault()->Damage;
|
|
self->SetState (self->FindState("NoGrow"));
|
|
}
|
|
}
|
|
|