mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-12-15 23:12:14 +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).
1365 lines
35 KiB
C++
1365 lines
35 KiB
C++
/*
|
|
#include "templates.h"
|
|
#include "actor.h"
|
|
#include "info.h"
|
|
#include "s_sound.h"
|
|
#include "m_random.h"
|
|
#include "a_pickups.h"
|
|
#include "d_player.h"
|
|
#include "p_pspr.h"
|
|
#include "p_local.h"
|
|
#include "gstrings.h"
|
|
#include "gi.h"
|
|
#include "r_data/r_translate.h"
|
|
#include "thingdef/thingdef.h"
|
|
#include "doomstat.h"
|
|
*/
|
|
|
|
static FRandom pr_sap ("StaffAtkPL1");
|
|
static FRandom pr_sap2 ("StaffAtkPL2");
|
|
static FRandom pr_fgw ("FireWandPL1");
|
|
static FRandom pr_fgw2 ("FireWandPL2");
|
|
static FRandom pr_boltspark ("BoltSpark");
|
|
static FRandom pr_macerespawn ("MaceRespawn");
|
|
static FRandom pr_maceatk ("FireMacePL1");
|
|
static FRandom pr_gatk ("GauntletAttack");
|
|
static FRandom pr_bfx1 ("BlasterFX1");
|
|
static FRandom pr_ripd ("RipperD");
|
|
static FRandom pr_fb1 ("FireBlasterPL1");
|
|
static FRandom pr_bfx1t ("BlasterFX1Tick");
|
|
static FRandom pr_hrfx2 ("HornRodFX2");
|
|
static FRandom pr_rp ("RainPillar");
|
|
static FRandom pr_fsr1 ("FireSkullRodPL1");
|
|
static FRandom pr_storm ("SkullRodStorm");
|
|
static FRandom pr_impact ("RainImpact");
|
|
static FRandom pr_pfx1 ("PhoenixFX1");
|
|
static FRandom pr_pfx2 ("PhoenixFX2");
|
|
static FRandom pr_fp2 ("FirePhoenixPL2");
|
|
|
|
#define FLAME_THROWER_TICS (10*TICRATE)
|
|
|
|
void P_DSparilTeleport (AActor *actor);
|
|
|
|
#define USE_BLSR_AMMO_1 1
|
|
#define USE_BLSR_AMMO_2 5
|
|
#define USE_SKRD_AMMO_1 1
|
|
#define USE_SKRD_AMMO_2 5
|
|
#define USE_PHRD_AMMO_1 1
|
|
#define USE_PHRD_AMMO_2 1
|
|
#define USE_MACE_AMMO_1 1
|
|
#define USE_MACE_AMMO_2 5
|
|
|
|
extern bool P_AutoUseChaosDevice (player_t *player);
|
|
|
|
// --- Staff ----------------------------------------------------------------
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_StaffAttackPL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StaffAttack)
|
|
{
|
|
angle_t angle;
|
|
int slope;
|
|
player_t *player;
|
|
AActor *linetarget;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ACTION_PARAM_START(2);
|
|
ACTION_PARAM_INT(damage, 0);
|
|
ACTION_PARAM_CLASS(puff, 1);
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
if (puff == NULL) puff = PClass::FindClass(NAME_BulletPuff); // just to be sure
|
|
angle = self->angle;
|
|
angle += pr_sap.Random2() << 18;
|
|
slope = P_AimLineAttack (self, angle, MELEERANGE, &linetarget);
|
|
P_LineAttack (self, angle, MELEERANGE, slope, damage, NAME_Melee, puff, true, &linetarget);
|
|
if (linetarget)
|
|
{
|
|
//S_StartSound(player->mo, sfx_stfhit);
|
|
// turn to face target
|
|
self->angle = R_PointToAngle2 (self->x,
|
|
self->y, linetarget->x, linetarget->y);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireGoldWandPL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_FireGoldWandPL1)
|
|
{
|
|
angle_t angle;
|
|
int damage;
|
|
player_t *player;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
angle_t pitch = P_BulletSlope(self);
|
|
damage = 7+(pr_fgw()&7);
|
|
angle = self->angle;
|
|
if (player->refire)
|
|
{
|
|
angle += pr_fgw.Random2() << 18;
|
|
}
|
|
P_LineAttack (self, angle, PLAYERMISSILERANGE, pitch, damage, NAME_Hitscan, "GoldWandPuff1");
|
|
S_Sound (self, CHAN_WEAPON, "weapons/wandhit", 1, ATTN_NORM);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireGoldWandPL2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_FireGoldWandPL2)
|
|
{
|
|
int i;
|
|
angle_t angle;
|
|
int damage;
|
|
fixed_t velz;
|
|
player_t *player;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
angle_t pitch = P_BulletSlope(self);
|
|
velz = FixedMul (GetDefaultByName("GoldWandFX2")->Speed,
|
|
finetangent[FINEANGLES/4-((signed)pitch>>ANGLETOFINESHIFT)]);
|
|
P_SpawnMissileAngle (self, PClass::FindClass("GoldWandFX2"), self->angle-(ANG45/8), velz);
|
|
P_SpawnMissileAngle (self, PClass::FindClass("GoldWandFX2"), self->angle+(ANG45/8), velz);
|
|
angle = self->angle-(ANG45/8);
|
|
for(i = 0; i < 5; i++)
|
|
{
|
|
damage = 1+(pr_fgw2()&7);
|
|
P_LineAttack (self, angle, PLAYERMISSILERANGE, pitch, damage, NAME_Hitscan, "GoldWandPuff2");
|
|
angle += ((ANG45/8)*2)/4;
|
|
}
|
|
S_Sound (self, CHAN_WEAPON, "weapons/wandhit", 1, ATTN_NORM);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireCrossbowPL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_FireCrossbowPL1)
|
|
{
|
|
player_t *player;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
P_SpawnPlayerMissile (self, PClass::FindClass("CrossbowFX1"));
|
|
P_SpawnPlayerMissile (self, PClass::FindClass("CrossbowFX3"), self->angle-(ANG45/10));
|
|
P_SpawnPlayerMissile (self, PClass::FindClass("CrossbowFX3"), self->angle+(ANG45/10));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireCrossbowPL2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_FireCrossbowPL2)
|
|
{
|
|
player_t *player;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = self->player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
P_SpawnPlayerMissile (self, PClass::FindClass("CrossbowFX2"));
|
|
P_SpawnPlayerMissile (self, PClass::FindClass("CrossbowFX2"), self->angle-(ANG45/10));
|
|
P_SpawnPlayerMissile (self, PClass::FindClass("CrossbowFX2"), self->angle+(ANG45/10));
|
|
P_SpawnPlayerMissile (self, PClass::FindClass("CrossbowFX3"), self->angle-(ANG45/5));
|
|
P_SpawnPlayerMissile (self, PClass::FindClass("CrossbowFX3"), self->angle+(ANG45/5));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC A_GauntletAttack
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GauntletAttack)
|
|
{
|
|
angle_t angle;
|
|
int damage;
|
|
int slope;
|
|
int randVal;
|
|
fixed_t dist;
|
|
player_t *player;
|
|
const PClass *pufftype;
|
|
AActor *linetarget;
|
|
int actualdamage = 0;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ACTION_PARAM_START(1);
|
|
ACTION_PARAM_INT(power, 0);
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
player->psprites[ps_weapon].sx = ((pr_gatk()&3)-2) * FRACUNIT;
|
|
player->psprites[ps_weapon].sy = WEAPONTOP + (pr_gatk()&3) * FRACUNIT;
|
|
angle = self->angle;
|
|
if (power)
|
|
{
|
|
damage = pr_gatk.HitDice (2);
|
|
dist = 4*MELEERANGE;
|
|
angle += pr_gatk.Random2() << 17;
|
|
pufftype = PClass::FindClass("GauntletPuff2");
|
|
}
|
|
else
|
|
{
|
|
damage = pr_gatk.HitDice (2);
|
|
dist = MELEERANGE+1;
|
|
angle += pr_gatk.Random2() << 18;
|
|
pufftype = PClass::FindClass("GauntletPuff1");
|
|
}
|
|
slope = P_AimLineAttack (self, angle, dist, &linetarget);
|
|
P_LineAttack (self, angle, dist, slope, damage, NAME_Melee, pufftype, false, &linetarget, &actualdamage);
|
|
if (!linetarget)
|
|
{
|
|
if (pr_gatk() > 64)
|
|
{
|
|
player->extralight = !player->extralight;
|
|
}
|
|
S_Sound (self, CHAN_AUTO, "weapons/gauntletson", 1, ATTN_NORM);
|
|
return;
|
|
}
|
|
randVal = pr_gatk();
|
|
if (randVal < 64)
|
|
{
|
|
player->extralight = 0;
|
|
}
|
|
else if (randVal < 160)
|
|
{
|
|
player->extralight = 1;
|
|
}
|
|
else
|
|
{
|
|
player->extralight = 2;
|
|
}
|
|
if (power)
|
|
{
|
|
if (!(linetarget->flags5 & MF5_DONTDRAIN)) P_GiveBody (self, actualdamage>>1);
|
|
S_Sound (self, CHAN_AUTO, "weapons/gauntletspowhit", 1, ATTN_NORM);
|
|
}
|
|
else
|
|
{
|
|
S_Sound (self, CHAN_AUTO, "weapons/gauntletshit", 1, ATTN_NORM);
|
|
}
|
|
// turn to face target
|
|
angle = R_PointToAngle2 (self->x, self->y,
|
|
linetarget->x, linetarget->y);
|
|
if (angle-self->angle > ANG180)
|
|
{
|
|
if ((int)(angle-self->angle) < -ANG90/20)
|
|
self->angle = angle+ANG90/21;
|
|
else
|
|
self->angle -= ANG90/20;
|
|
}
|
|
else
|
|
{
|
|
if (angle-self->angle > ANG90/20)
|
|
self->angle = angle-ANG90/21;
|
|
else
|
|
self->angle += ANG90/20;
|
|
}
|
|
self->flags |= MF_JUSTATTACKED;
|
|
}
|
|
|
|
// --- Mace -----------------------------------------------------------------
|
|
|
|
#define MAGIC_JUNK 1234
|
|
|
|
// Mace FX4 -----------------------------------------------------------------
|
|
|
|
class AMaceFX4 : public AActor
|
|
{
|
|
DECLARE_CLASS (AMaceFX4, AActor)
|
|
public:
|
|
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
|
|
};
|
|
|
|
IMPLEMENT_CLASS (AMaceFX4)
|
|
|
|
int AMaceFX4::DoSpecialDamage (AActor *target, int damage, FName damagetype)
|
|
{
|
|
if ((target->flags2 & MF2_BOSS) || (target->flags3 & MF3_DONTSQUASH) || target->IsTeammate (this->target))
|
|
{ // Don't allow cheap boss kills and don't instagib teammates
|
|
return damage;
|
|
}
|
|
else if (target->player)
|
|
{ // Player specific checks
|
|
if (target->player->mo->flags2 & MF2_INVULNERABLE)
|
|
{ // Can't hurt invulnerable players
|
|
return -1;
|
|
}
|
|
if (P_AutoUseChaosDevice (target->player))
|
|
{ // Player was saved using chaos device
|
|
return -1;
|
|
}
|
|
}
|
|
return TELEFRAG_DAMAGE; // Something's gonna die
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireMacePL1B
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void FireMacePL1B (AActor *actor)
|
|
{
|
|
AActor *ball;
|
|
angle_t angle;
|
|
player_t *player;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
ball = Spawn("MaceFX2", actor->x, actor->y, actor->z + 28*FRACUNIT
|
|
- actor->floorclip, ALLOW_REPLACE);
|
|
ball->velz = 2*FRACUNIT+/*((player->lookdir)<<(FRACBITS-5))*/
|
|
finetangent[FINEANGLES/4-(actor->pitch>>ANGLETOFINESHIFT)];
|
|
angle = actor->angle;
|
|
ball->target = actor;
|
|
ball->angle = angle;
|
|
ball->z += 2*finetangent[FINEANGLES/4-(actor->pitch>>ANGLETOFINESHIFT)];
|
|
angle >>= ANGLETOFINESHIFT;
|
|
ball->velx = (actor->velx>>1) + FixedMul(ball->Speed, finecosine[angle]);
|
|
ball->vely = (actor->vely>>1) + FixedMul(ball->Speed, finesine[angle]);
|
|
S_Sound (ball, CHAN_BODY, "weapons/maceshoot", 1, ATTN_NORM);
|
|
P_CheckMissileSpawn (ball, actor->radius);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireMacePL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_FireMacePL1)
|
|
{
|
|
AActor *ball;
|
|
player_t *player;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (pr_maceatk() < 28)
|
|
{
|
|
FireMacePL1B (self);
|
|
return;
|
|
}
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
player->psprites[ps_weapon].sx = ((pr_maceatk()&3)-2)*FRACUNIT;
|
|
player->psprites[ps_weapon].sy = WEAPONTOP+(pr_maceatk()&3)*FRACUNIT;
|
|
ball = P_SpawnPlayerMissile (self, PClass::FindClass("MaceFX1"),
|
|
self->angle+(((pr_maceatk()&7)-4)<<24));
|
|
if (ball)
|
|
{
|
|
ball->special1 = 16; // tics till dropoff
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_MacePL1Check
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_MacePL1Check)
|
|
{
|
|
if (self->special1 == 0)
|
|
{
|
|
return;
|
|
}
|
|
self->special1 -= 4;
|
|
if (self->special1 > 0)
|
|
{
|
|
return;
|
|
}
|
|
self->special1 = 0;
|
|
self->flags &= ~MF_NOGRAVITY;
|
|
self->gravity = FRACUNIT/8;
|
|
// [RH] Avoid some precision loss by scaling the velocity directly
|
|
#if 0
|
|
// This is the original code, for reference.
|
|
angle_t angle = self->angle>>ANGLETOFINESHIFT;
|
|
self->velx = FixedMul(7*FRACUNIT, finecosine[angle]);
|
|
self->vely = FixedMul(7*FRACUNIT, finesine[angle]);
|
|
#else
|
|
double velscale = sqrt ((double)self->velx * (double)self->velx +
|
|
(double)self->vely * (double)self->vely);
|
|
velscale = 458752 / velscale;
|
|
self->velx = (int)(self->velx * velscale);
|
|
self->vely = (int)(self->vely * velscale);
|
|
#endif
|
|
self->velz -= self->velz >> 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_MaceBallImpact
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_MaceBallImpact)
|
|
{
|
|
if ((self->health != MAGIC_JUNK) && (self->flags & MF_INBOUNCE))
|
|
{ // Bounce
|
|
self->health = MAGIC_JUNK;
|
|
self->velz = (self->velz * 192) >> 8;
|
|
self->BounceFlags = BOUNCE_None;
|
|
self->SetState (self->SpawnState);
|
|
S_Sound (self, CHAN_BODY, "weapons/macebounce", 1, ATTN_NORM);
|
|
}
|
|
else
|
|
{ // Explode
|
|
self->velx = self->vely = self->velz = 0;
|
|
self->flags |= MF_NOGRAVITY;
|
|
self->gravity = FRACUNIT;
|
|
S_Sound (self, CHAN_BODY, "weapons/macehit", 1, ATTN_NORM);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_MaceBallImpact2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_MaceBallImpact2)
|
|
{
|
|
AActor *tiny;
|
|
angle_t angle;
|
|
|
|
if (self->flags & MF_INBOUNCE)
|
|
{
|
|
fixed_t floordist = self->z - self->floorz;
|
|
fixed_t ceildist = self->ceilingz - self->z;
|
|
fixed_t vel;
|
|
|
|
if (floordist <= ceildist)
|
|
{
|
|
vel = MulScale32 (self->velz, self->Sector->floorplane.c);
|
|
}
|
|
else
|
|
{
|
|
vel = MulScale32 (self->velz, self->Sector->ceilingplane.c);
|
|
}
|
|
if (vel < 2)
|
|
{
|
|
goto boom;
|
|
}
|
|
|
|
// Bounce
|
|
self->velz = (self->velz * 192) >> 8;
|
|
self->SetState (self->SpawnState);
|
|
|
|
tiny = Spawn("MaceFX3", self->x, self->y, self->z, ALLOW_REPLACE);
|
|
angle = self->angle+ANG90;
|
|
tiny->target = self->target;
|
|
tiny->angle = angle;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
tiny->velx = (self->velx>>1) + FixedMul(self->velz-FRACUNIT, finecosine[angle]);
|
|
tiny->vely = (self->vely>>1) + FixedMul(self->velz-FRACUNIT, finesine[angle]);
|
|
tiny->velz = self->velz;
|
|
P_CheckMissileSpawn (tiny, self->radius);
|
|
|
|
tiny = Spawn("MaceFX3", self->x, self->y, self->z, ALLOW_REPLACE);
|
|
angle = self->angle-ANG90;
|
|
tiny->target = self->target;
|
|
tiny->angle = angle;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
tiny->velx = (self->velx>>1) + FixedMul(self->velz-FRACUNIT, finecosine[angle]);
|
|
tiny->vely = (self->vely>>1) + FixedMul(self->velz-FRACUNIT, finesine[angle]);
|
|
tiny->velz = self->velz;
|
|
P_CheckMissileSpawn (tiny, self->radius);
|
|
}
|
|
else
|
|
{ // Explode
|
|
boom:
|
|
self->velx = self->vely = self->velz = 0;
|
|
self->flags |= MF_NOGRAVITY;
|
|
self->BounceFlags = BOUNCE_None;
|
|
self->gravity = FRACUNIT;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireMacePL2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_FireMacePL2)
|
|
{
|
|
AActor *mo;
|
|
player_t *player;
|
|
AActor *linetarget;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
mo = P_SpawnPlayerMissile (self, 0,0,0, RUNTIME_CLASS(AMaceFX4), self->angle, &linetarget);
|
|
if (mo)
|
|
{
|
|
mo->velx += self->velx;
|
|
mo->vely += self->vely;
|
|
mo->velz = 2*FRACUNIT+
|
|
clamp<fixed_t>(finetangent[FINEANGLES/4-(self->pitch>>ANGLETOFINESHIFT)], -5*FRACUNIT, 5*FRACUNIT);
|
|
if (linetarget)
|
|
{
|
|
mo->tracer = linetarget;
|
|
}
|
|
}
|
|
S_Sound (self, CHAN_WEAPON, "weapons/maceshoot", 1, ATTN_NORM);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_DeathBallImpact
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_DeathBallImpact)
|
|
{
|
|
int i;
|
|
AActor *target;
|
|
angle_t angle = 0;
|
|
bool newAngle;
|
|
AActor *linetarget;
|
|
|
|
if ((self->z <= self->floorz) && P_HitFloor (self))
|
|
{ // Landed in some sort of liquid
|
|
self->Destroy ();
|
|
return;
|
|
}
|
|
if (self->flags & MF_INBOUNCE)
|
|
{
|
|
fixed_t floordist = self->z - self->floorz;
|
|
fixed_t ceildist = self->ceilingz - self->z;
|
|
fixed_t vel;
|
|
|
|
if (floordist <= ceildist)
|
|
{
|
|
vel = MulScale32 (self->velz, self->Sector->floorplane.c);
|
|
}
|
|
else
|
|
{
|
|
vel = MulScale32 (self->velz, self->Sector->ceilingplane.c);
|
|
}
|
|
if (vel < 2)
|
|
{
|
|
goto boom;
|
|
}
|
|
|
|
// Bounce
|
|
newAngle = false;
|
|
target = self->tracer;
|
|
if (target)
|
|
{
|
|
if (!(target->flags&MF_SHOOTABLE))
|
|
{ // Target died
|
|
self->tracer = NULL;
|
|
}
|
|
else
|
|
{ // Seek
|
|
angle = R_PointToAngle2(self->x, self->y,
|
|
target->x, target->y);
|
|
newAngle = true;
|
|
}
|
|
}
|
|
else
|
|
{ // Find new target
|
|
angle = 0;
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
P_AimLineAttack (self, angle, 10*64*FRACUNIT, &linetarget, 0, ALF_NOFRIENDS, NULL, self->target);
|
|
if (linetarget && self->target != linetarget)
|
|
{
|
|
self->tracer = linetarget;
|
|
angle = R_PointToAngle2 (self->x, self->y,
|
|
linetarget->x, linetarget->y);
|
|
newAngle = true;
|
|
break;
|
|
}
|
|
angle += ANGLE_45/2;
|
|
}
|
|
}
|
|
if (newAngle)
|
|
{
|
|
self->angle = angle;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
self->velx = FixedMul (self->Speed, finecosine[angle]);
|
|
self->vely = FixedMul (self->Speed, finesine[angle]);
|
|
}
|
|
self->SetState (self->SpawnState);
|
|
S_Sound (self, CHAN_BODY, "weapons/macestop", 1, ATTN_NORM);
|
|
}
|
|
else
|
|
{ // Explode
|
|
boom:
|
|
self->velx = self->vely = self->velz = 0;
|
|
self->flags |= MF_NOGRAVITY;
|
|
self->gravity = FRACUNIT;
|
|
S_Sound (self, CHAN_BODY, "weapons/maceexplode", 1, ATTN_NORM);
|
|
}
|
|
}
|
|
|
|
|
|
// Blaster FX 1 -------------------------------------------------------------
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Thinker for the ultra-fast blaster PL2 ripper-spawning missile.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
class ABlasterFX1 : public AFastProjectile
|
|
{
|
|
DECLARE_CLASS(ABlasterFX1, AFastProjectile)
|
|
public:
|
|
void Effect ();
|
|
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
|
|
};
|
|
|
|
int ABlasterFX1::DoSpecialDamage (AActor *target, int damage, FName damagetype)
|
|
{
|
|
if (target->IsKindOf (PClass::FindClass ("Ironlich")))
|
|
{ // Less damage to Ironlich bosses
|
|
damage = pr_bfx1() & 1;
|
|
if (!damage)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
return damage;
|
|
}
|
|
|
|
void ABlasterFX1::Effect ()
|
|
{
|
|
if (pr_bfx1t() < 64)
|
|
{
|
|
Spawn("BlasterSmoke", x, y, MAX<fixed_t> (z - 8 * FRACUNIT, floorz), ALLOW_REPLACE);
|
|
}
|
|
}
|
|
|
|
IMPLEMENT_CLASS(ABlasterFX1)
|
|
|
|
// Ripper -------------------------------------------------------------------
|
|
|
|
|
|
class ARipper : public AActor
|
|
{
|
|
DECLARE_CLASS (ARipper, AActor)
|
|
public:
|
|
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
|
|
};
|
|
|
|
IMPLEMENT_CLASS(ARipper)
|
|
|
|
int ARipper::DoSpecialDamage (AActor *target, int damage, FName damagetype)
|
|
{
|
|
if (target->IsKindOf (PClass::FindClass ("Ironlich")))
|
|
{ // Less damage to Ironlich bosses
|
|
damage = pr_ripd() & 1;
|
|
if (!damage)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
return damage;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireBlasterPL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_FireBlasterPL1)
|
|
{
|
|
angle_t angle;
|
|
int damage;
|
|
player_t *player;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = self->player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
angle_t pitch = P_BulletSlope(self);
|
|
damage = pr_fb1.HitDice (4);
|
|
angle = self->angle;
|
|
if (player->refire)
|
|
{
|
|
angle += pr_fb1.Random2() << 18;
|
|
}
|
|
P_LineAttack (self, angle, PLAYERMISSILERANGE, pitch, damage, NAME_Hitscan, "BlasterPuff");
|
|
S_Sound (self, CHAN_WEAPON, "weapons/blastershoot", 1, ATTN_NORM);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_SpawnRippers
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_SpawnRippers)
|
|
{
|
|
unsigned int i;
|
|
angle_t angle;
|
|
AActor *ripper;
|
|
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
ripper = Spawn<ARipper> (self->x, self->y, self->z, ALLOW_REPLACE);
|
|
angle = i*ANG45;
|
|
ripper->target = self->target;
|
|
ripper->angle = angle;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
ripper->velx = FixedMul (ripper->Speed, finecosine[angle]);
|
|
ripper->vely = FixedMul (ripper->Speed, finesine[angle]);
|
|
P_CheckMissileSpawn (ripper, self->radius);
|
|
}
|
|
}
|
|
|
|
// --- Skull rod ------------------------------------------------------------
|
|
|
|
|
|
// Horn Rod FX 2 ------------------------------------------------------------
|
|
|
|
class AHornRodFX2 : public AActor
|
|
{
|
|
DECLARE_CLASS (AHornRodFX2, AActor)
|
|
public:
|
|
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
|
|
};
|
|
|
|
IMPLEMENT_CLASS (AHornRodFX2)
|
|
|
|
int AHornRodFX2::DoSpecialDamage (AActor *target, int damage, FName damagetype)
|
|
{
|
|
if (target->IsKindOf (PClass::FindClass("Sorcerer2")) && pr_hrfx2() < 96)
|
|
{ // D'Sparil teleports away
|
|
P_DSparilTeleport (target);
|
|
return -1;
|
|
}
|
|
return damage;
|
|
}
|
|
|
|
// Rain pillar 1 ------------------------------------------------------------
|
|
|
|
class ARainPillar : public AActor
|
|
{
|
|
DECLARE_CLASS (ARainPillar, AActor)
|
|
public:
|
|
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
|
|
};
|
|
|
|
IMPLEMENT_CLASS (ARainPillar)
|
|
|
|
int ARainPillar::DoSpecialDamage (AActor *target, int damage, FName damagetype)
|
|
{
|
|
if (target->flags2 & MF2_BOSS)
|
|
{ // Decrease damage for bosses
|
|
damage = (pr_rp() & 7) + 1;
|
|
}
|
|
return damage;
|
|
}
|
|
|
|
// Rain tracker "inventory" item --------------------------------------------
|
|
|
|
class ARainTracker : public AInventory
|
|
{
|
|
DECLARE_CLASS (ARainTracker, AInventory)
|
|
public:
|
|
void Serialize (FArchive &arc);
|
|
AActor *Rain1, *Rain2;
|
|
};
|
|
|
|
IMPLEMENT_CLASS (ARainTracker)
|
|
|
|
void ARainTracker::Serialize (FArchive &arc)
|
|
{
|
|
Super::Serialize (arc);
|
|
arc << Rain1 << Rain2;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireSkullRodPL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_FireSkullRodPL1)
|
|
{
|
|
AActor *mo;
|
|
player_t *player;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
mo = P_SpawnPlayerMissile (self, PClass::FindClass("HornRodFX1"));
|
|
// Randomize the first frame
|
|
if (mo && pr_fsr1() > 128)
|
|
{
|
|
mo->SetState (mo->state->GetNextState());
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireSkullRodPL2
|
|
//
|
|
// The special2 field holds the player number that shot the rain missile.
|
|
// The special1 field holds the id of the rain sound.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_FireSkullRodPL2)
|
|
{
|
|
player_t *player;
|
|
AActor *MissileActor;
|
|
AActor *linetarget;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
P_SpawnPlayerMissile (self, 0,0,0, RUNTIME_CLASS(AHornRodFX2), self->angle, &linetarget, &MissileActor);
|
|
// Use MissileActor instead of the return value from
|
|
// P_SpawnPlayerMissile because we need to give info to the mobj
|
|
// even if it exploded immediately.
|
|
if (MissileActor != NULL)
|
|
{
|
|
MissileActor->special2 = (int)(player - players);
|
|
if (linetarget)
|
|
{
|
|
MissileActor->tracer = linetarget;
|
|
}
|
|
S_Sound (MissileActor, CHAN_WEAPON, "weapons/hornrodpowshoot", 1, ATTN_NORM);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_AddPlayerRain
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_AddPlayerRain)
|
|
{
|
|
ARainTracker *tracker;
|
|
|
|
if (self->target == NULL || self->target->health <= 0)
|
|
{ // Shooter is dead or nonexistant
|
|
return;
|
|
}
|
|
|
|
tracker = self->target->FindInventory<ARainTracker> ();
|
|
|
|
// They player is only allowed two rainstorms at a time. Shooting more
|
|
// than that will cause the oldest one to terminate.
|
|
if (tracker != NULL)
|
|
{
|
|
if (tracker->Rain1 && tracker->Rain2)
|
|
{ // Terminate an active rain
|
|
if (tracker->Rain1->health < tracker->Rain2->health)
|
|
{
|
|
if (tracker->Rain1->health > 16)
|
|
{
|
|
tracker->Rain1->health = 16;
|
|
}
|
|
tracker->Rain1 = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (tracker->Rain2->health > 16)
|
|
{
|
|
tracker->Rain2->health = 16;
|
|
}
|
|
tracker->Rain2 = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tracker = static_cast<ARainTracker *> (self->target->GiveInventoryType (RUNTIME_CLASS(ARainTracker)));
|
|
}
|
|
// Add rain mobj to list
|
|
if (tracker->Rain1)
|
|
{
|
|
tracker->Rain2 = self;
|
|
}
|
|
else
|
|
{
|
|
tracker->Rain1 = self;
|
|
}
|
|
self->special1 = S_FindSound ("misc/rain");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_SkullRodStorm
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_SkullRodStorm)
|
|
{
|
|
fixed_t x;
|
|
fixed_t y;
|
|
AActor *mo;
|
|
ARainTracker *tracker;
|
|
|
|
if (self->health-- == 0)
|
|
{
|
|
S_StopSound (self, CHAN_BODY);
|
|
if (self->target == NULL)
|
|
{ // Player left the game
|
|
self->Destroy ();
|
|
return;
|
|
}
|
|
tracker = self->target->FindInventory<ARainTracker> ();
|
|
if (tracker != NULL)
|
|
{
|
|
if (tracker->Rain1 == self)
|
|
{
|
|
tracker->Rain1 = NULL;
|
|
}
|
|
else if (tracker->Rain2 == self)
|
|
{
|
|
tracker->Rain2 = NULL;
|
|
}
|
|
}
|
|
self->Destroy ();
|
|
return;
|
|
}
|
|
if (pr_storm() < 25)
|
|
{ // Fudge rain frequency
|
|
return;
|
|
}
|
|
x = self->x + ((pr_storm()&127) - 64) * FRACUNIT;
|
|
y = self->y + ((pr_storm()&127) - 64) * FRACUNIT;
|
|
mo = Spawn<ARainPillar> (x, y, ONCEILINGZ, ALLOW_REPLACE);
|
|
#ifdef _3DFLOORS
|
|
// We used bouncecount to store the 3D floor index in A_HideInCeiling
|
|
if (!mo) return;
|
|
fixed_t newz;
|
|
if (self->bouncecount >= 0
|
|
&& (unsigned)self->bouncecount < self->Sector->e->XFloor.ffloors.Size())
|
|
newz = self->Sector->e->XFloor.ffloors[self->bouncecount]->bottom.plane->ZatPoint(x, y);// - 40 * FRACUNIT;
|
|
else
|
|
newz = self->Sector->ceilingplane.ZatPoint(x, y);
|
|
int moceiling = P_Find3DFloor(NULL, x, y, newz, false, false, newz);
|
|
if (moceiling >= 0)
|
|
mo->z = newz - mo->height;
|
|
#endif
|
|
mo->Translation = multiplayer ?
|
|
TRANSLATION(TRANSLATION_PlayersExtra,self->special2) : 0;
|
|
mo->target = self->target;
|
|
mo->velx = 1; // Force collision detection
|
|
mo->velz = -mo->Speed;
|
|
mo->special2 = self->special2; // Transfer player number
|
|
P_CheckMissileSpawn (mo, self->radius);
|
|
if (self->special1 != -1 && !S_IsActorPlayingSomething (self, CHAN_BODY, -1))
|
|
{
|
|
S_Sound (self, CHAN_BODY|CHAN_LOOP, self->special1, 1, ATTN_NORM);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_RainImpact
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_RainImpact)
|
|
{
|
|
if (self->z > self->floorz)
|
|
{
|
|
self->SetState (self->FindState("NotFloor"));
|
|
}
|
|
else if (pr_impact() < 40)
|
|
{
|
|
P_HitFloor (self);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_HideInCeiling
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_HideInCeiling)
|
|
{
|
|
#ifdef _3DFLOORS
|
|
// We use bouncecount to store the 3D floor index
|
|
fixed_t foo;
|
|
for (unsigned int i=0; i< self->Sector->e->XFloor.ffloors.Size(); i++)
|
|
{
|
|
F3DFloor * rover = self->Sector->e->XFloor.ffloors[i];
|
|
if(!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue;
|
|
|
|
if ((foo = rover->bottom.plane->ZatPoint(self->x, self->y)) >= (self->z + self->height))
|
|
{
|
|
self->z = foo + 4*FRACUNIT;
|
|
self->bouncecount = i;
|
|
return;
|
|
}
|
|
}
|
|
self->bouncecount = -1;
|
|
#endif
|
|
self->z = self->ceilingz + 4*FRACUNIT;
|
|
}
|
|
|
|
// --- Phoenix Rod ----------------------------------------------------------
|
|
|
|
class APhoenixRod : public AWeapon
|
|
{
|
|
DECLARE_CLASS (APhoenixRod, AWeapon)
|
|
public:
|
|
void Serialize (FArchive &arc)
|
|
{
|
|
Super::Serialize (arc);
|
|
arc << FlameCount;
|
|
}
|
|
int FlameCount; // for flamethrower duration
|
|
};
|
|
|
|
class APhoenixRodPowered : public APhoenixRod
|
|
{
|
|
DECLARE_CLASS (APhoenixRodPowered, APhoenixRod)
|
|
public:
|
|
void EndPowerup ();
|
|
};
|
|
|
|
IMPLEMENT_CLASS (APhoenixRod)
|
|
IMPLEMENT_CLASS (APhoenixRodPowered)
|
|
|
|
void APhoenixRodPowered::EndPowerup ()
|
|
{
|
|
P_SetPsprite (Owner->player, ps_weapon, SisterWeapon->GetReadyState());
|
|
DepleteAmmo (bAltFire);
|
|
Owner->player->refire = 0;
|
|
S_StopSound (Owner, CHAN_WEAPON);
|
|
Owner->player->ReadyWeapon = SisterWeapon;
|
|
}
|
|
|
|
class APhoenixFX1 : public AActor
|
|
{
|
|
DECLARE_CLASS (APhoenixFX1, AActor)
|
|
public:
|
|
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
|
|
};
|
|
|
|
|
|
IMPLEMENT_CLASS (APhoenixFX1)
|
|
|
|
int APhoenixFX1::DoSpecialDamage (AActor *target, int damage, FName damagetype)
|
|
{
|
|
if (target->IsKindOf (PClass::FindClass("Sorcerer2")) && pr_hrfx2() < 96)
|
|
{ // D'Sparil teleports away
|
|
P_DSparilTeleport (target);
|
|
return -1;
|
|
}
|
|
return damage;
|
|
}
|
|
|
|
// Phoenix FX 2 -------------------------------------------------------------
|
|
|
|
class APhoenixFX2 : public AActor
|
|
{
|
|
DECLARE_CLASS (APhoenixFX2, AActor)
|
|
public:
|
|
int DoSpecialDamage (AActor *target, int damage, FName damagetype);
|
|
};
|
|
|
|
IMPLEMENT_CLASS (APhoenixFX2)
|
|
|
|
int APhoenixFX2::DoSpecialDamage (AActor *target, int damage, FName damagetype)
|
|
{
|
|
if (target->player && pr_pfx2 () < 128)
|
|
{ // Freeze player for a bit
|
|
target->reactiontime += 4;
|
|
}
|
|
return damage;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FirePhoenixPL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_FirePhoenixPL1)
|
|
{
|
|
angle_t angle;
|
|
player_t *player;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = self->player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
P_SpawnPlayerMissile (self, RUNTIME_CLASS(APhoenixFX1));
|
|
angle = self->angle + ANG180;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
self->velx += FixedMul (4*FRACUNIT, finecosine[angle]);
|
|
self->vely += FixedMul (4*FRACUNIT, finesine[angle]);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_PhoenixPuff
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_PhoenixPuff)
|
|
{
|
|
AActor *puff;
|
|
angle_t angle;
|
|
|
|
//[RH] Heretic never sets the target for seeking
|
|
//P_SeekerMissile (self, ANGLE_1*5, ANGLE_1*10);
|
|
puff = Spawn("PhoenixPuff", self->x, self->y, self->z, ALLOW_REPLACE);
|
|
angle = self->angle + ANG90;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
puff->velx = FixedMul (FRACUNIT*13/10, finecosine[angle]);
|
|
puff->vely = FixedMul (FRACUNIT*13/10, finesine[angle]);
|
|
puff->velz = 0;
|
|
puff = Spawn("PhoenixPuff", self->x, self->y, self->z, ALLOW_REPLACE);
|
|
angle = self->angle - ANG90;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
puff->velx = FixedMul (FRACUNIT*13/10, finecosine[angle]);
|
|
puff->vely = FixedMul (FRACUNIT*13/10, finesine[angle]);
|
|
puff->velz = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_InitPhoenixPL2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_InitPhoenixPL2)
|
|
{
|
|
if (self->player != NULL)
|
|
{
|
|
APhoenixRod *flamethrower = static_cast<APhoenixRod *> (self->player->ReadyWeapon);
|
|
if (flamethrower != NULL)
|
|
{
|
|
flamethrower->FlameCount = FLAME_THROWER_TICS;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FirePhoenixPL2
|
|
//
|
|
// Flame thrower effect.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_FirePhoenixPL2)
|
|
{
|
|
AActor *mo;
|
|
angle_t angle;
|
|
fixed_t x, y, z;
|
|
|
|
fixed_t slope;
|
|
FSoundID soundid;
|
|
player_t *player;
|
|
APhoenixRod *flamethrower;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
soundid = "weapons/phoenixpowshoot";
|
|
|
|
flamethrower = static_cast<APhoenixRod *> (player->ReadyWeapon);
|
|
if (flamethrower == NULL || --flamethrower->FlameCount == 0)
|
|
{ // Out of flame
|
|
P_SetPsprite (player, ps_weapon, flamethrower->FindState("Powerdown"));
|
|
player->refire = 0;
|
|
S_StopSound (self, CHAN_WEAPON);
|
|
return;
|
|
}
|
|
angle = self->angle;
|
|
x = self->x + (pr_fp2.Random2() << 9);
|
|
y = self->y + (pr_fp2.Random2() << 9);
|
|
z = self->z + 26*FRACUNIT + finetangent[FINEANGLES/4-(self->pitch>>ANGLETOFINESHIFT)];
|
|
z -= self->floorclip;
|
|
slope = finetangent[FINEANGLES/4-(self->pitch>>ANGLETOFINESHIFT)] + (FRACUNIT/10);
|
|
mo = Spawn("PhoenixFX2", x, y, z, ALLOW_REPLACE);
|
|
mo->target = self;
|
|
mo->angle = angle;
|
|
mo->velx = self->velx + FixedMul (mo->Speed, finecosine[angle>>ANGLETOFINESHIFT]);
|
|
mo->vely = self->vely + FixedMul (mo->Speed, finesine[angle>>ANGLETOFINESHIFT]);
|
|
mo->velz = FixedMul (mo->Speed, slope);
|
|
if (!player->refire || !S_IsActorPlayingSomething (self, CHAN_WEAPON, -1))
|
|
{
|
|
S_Sound (self, CHAN_WEAPON|CHAN_LOOP, soundid, 1, ATTN_NORM);
|
|
}
|
|
P_CheckMissileSpawn (mo, self->radius);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_ShutdownPhoenixPL2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_ShutdownPhoenixPL2)
|
|
{
|
|
player_t *player;
|
|
|
|
if (NULL == (player = self->player))
|
|
{
|
|
return;
|
|
}
|
|
S_StopSound (self, CHAN_WEAPON);
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FlameEnd
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_FlameEnd)
|
|
{
|
|
self->velz += FRACUNIT*3/2;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FloatPuff
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_FloatPuff)
|
|
{
|
|
self->velz += FRACUNIT*18/10;
|
|
}
|
|
|