mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-08 05:51:26 +00:00
5dc42121b7
treated it as a mace and wrote into some undefined memory. - Fixed: A_BishopMissileWeave didn't initialize special2 for proper movement. - Added a speed parameter to A_SkullAttack. - Fixed: Black as first or only blood color didn't work. - Fixed: Sounds played in wi_stuff.cpp and f_finale.cpp need the CHAN_UI flag. - Fixed: Spawning a player could play the *gasp sound. - Fixed: SBARINFO's health display didn't scale to the proper maximum. - Added Skulltag's Teleport_NoStop action special. SVN r1074 (trunk)
2971 lines
84 KiB
C++
2971 lines
84 KiB
C++
#include "templates.h"
|
|
#include "actor.h"
|
|
#include "info.h"
|
|
#include "s_sound.h"
|
|
#include "m_random.h"
|
|
#include "a_pickups.h"
|
|
#include "a_hereticglobal.h"
|
|
#include "d_player.h"
|
|
#include "p_pspr.h"
|
|
#include "p_local.h"
|
|
#include "gstrings.h"
|
|
#include "p_effect.h"
|
|
#include "gstrings.h"
|
|
#include "p_enemy.h"
|
|
#include "gi.h"
|
|
#include "r_translate.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_spawnmace ("SpawnMace");
|
|
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)
|
|
|
|
|
|
#define USE_GWND_AMMO_1 1
|
|
#define USE_GWND_AMMO_2 1
|
|
#define USE_CBOW_AMMO_1 1
|
|
#define USE_CBOW_AMMO_2 1
|
|
#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);
|
|
|
|
// Base Heretic weapon class ------------------------------------------------
|
|
|
|
IMPLEMENT_STATELESS_ACTOR (AHereticWeapon, Heretic, -1, 0)
|
|
PROP_Weapon_Kickback (150)
|
|
END_DEFAULTS
|
|
|
|
// --- Staff ----------------------------------------------------------------
|
|
|
|
void A_StaffAttackPL1 (AActor *);
|
|
void A_StaffAttackPL2 (AActor *);
|
|
|
|
// Staff --------------------------------------------------------------------
|
|
|
|
class AStaff : public AHereticWeapon
|
|
{
|
|
DECLARE_ACTOR (AStaff, AHereticWeapon)
|
|
};
|
|
|
|
class AStaffPowered : public AStaff
|
|
{
|
|
DECLARE_STATELESS_ACTOR (AStaffPowered, AStaff)
|
|
};
|
|
|
|
FState AStaff::States[] =
|
|
{
|
|
#define S_STAFFREADY 0
|
|
S_NORMAL (STFF, 'A', 1, A_WeaponReady , &States[S_STAFFREADY]),
|
|
|
|
#define S_STAFFDOWN (S_STAFFREADY+1)
|
|
S_NORMAL (STFF, 'A', 1, A_Lower , &States[S_STAFFDOWN]),
|
|
|
|
#define S_STAFFUP (S_STAFFDOWN+1)
|
|
S_NORMAL (STFF, 'A', 1, A_Raise , &States[S_STAFFUP]),
|
|
|
|
#define S_STAFFREADY2 (S_STAFFUP+1)
|
|
S_NORMAL (STFF, 'D', 4, A_WeaponReady , &States[S_STAFFREADY2+1]),
|
|
S_NORMAL (STFF, 'E', 4, A_WeaponReady , &States[S_STAFFREADY2+2]),
|
|
S_NORMAL (STFF, 'F', 4, A_WeaponReady , &States[S_STAFFREADY2+0]),
|
|
|
|
#define S_STAFFDOWN2 (S_STAFFREADY2+3)
|
|
S_NORMAL (STFF, 'D', 1, A_Lower , &States[S_STAFFDOWN2]),
|
|
|
|
#define S_STAFFUP2 (S_STAFFDOWN2+1)
|
|
S_NORMAL (STFF, 'D', 1, A_Raise , &States[S_STAFFUP2]),
|
|
|
|
#define S_STAFFATK1 (S_STAFFUP2+1)
|
|
S_NORMAL (STFF, 'B', 6, NULL , &States[S_STAFFATK1+1]),
|
|
S_NORMAL (STFF, 'C', 8, A_StaffAttackPL1 , &States[S_STAFFATK1+2]),
|
|
S_NORMAL (STFF, 'B', 8, A_ReFire , &States[S_STAFFREADY]),
|
|
|
|
#define S_STAFFATK2 (S_STAFFATK1+3)
|
|
S_NORMAL (STFF, 'G', 6, NULL , &States[S_STAFFATK2+1]),
|
|
S_NORMAL (STFF, 'H', 8, A_StaffAttackPL2 , &States[S_STAFFATK2+2]),
|
|
S_NORMAL (STFF, 'G', 8, A_ReFire , &States[S_STAFFREADY2+0])
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AStaff, Heretic, -1, 0)
|
|
PROP_Weapon_SelectionOrder (3800)
|
|
PROP_Flags2Set(MF2_THRUGHOST)
|
|
PROP_Weapon_Flags (WIF_WIMPY_WEAPON|WIF_BOT_MELEE)
|
|
PROP_Weapon_UpState (S_STAFFUP)
|
|
PROP_Weapon_DownState (S_STAFFDOWN)
|
|
PROP_Weapon_ReadyState (S_STAFFREADY)
|
|
PROP_Weapon_AtkState (S_STAFFATK1)
|
|
PROP_Weapon_SisterType ("StaffPowered")
|
|
END_DEFAULTS
|
|
|
|
IMPLEMENT_STATELESS_ACTOR (AStaffPowered, Heretic, -1, 0)
|
|
PROP_Weapon_Flags (WIF_WIMPY_WEAPON|WIF_READYSNDHALF|WIF_POWERED_UP|WIF_BOT_MELEE|WIF_STAFF2_KICKBACK)
|
|
PROP_Weapon_UpState (S_STAFFUP2)
|
|
PROP_Weapon_DownState (S_STAFFDOWN2)
|
|
PROP_Weapon_ReadyState (S_STAFFREADY2)
|
|
PROP_Weapon_AtkState (S_STAFFATK2)
|
|
PROP_Weapon_ReadySound ("weapons/staffcrackle")
|
|
PROP_Weapon_SisterType ("Staff")
|
|
END_DEFAULTS
|
|
|
|
// Staff puff ---------------------------------------------------------------
|
|
|
|
FState AStaffPuff::States[] =
|
|
{
|
|
S_BRIGHT (PUF3, 'A', 4, NULL , &States[1]),
|
|
S_NORMAL (PUF3, 'B', 4, NULL , &States[2]),
|
|
S_NORMAL (PUF3, 'C', 4, NULL , &States[3]),
|
|
S_NORMAL (PUF3, 'D', 4, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AStaffPuff, Heretic, -1, 0)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY)
|
|
PROP_Flags3 (MF3_PUFFONACTORS)
|
|
PROP_RenderStyle (STYLE_Translucent)
|
|
PROP_Alpha (HR_SHADOW)
|
|
PROP_SpawnState (0)
|
|
PROP_AttackSound ("weapons/staffhit")
|
|
END_DEFAULTS
|
|
|
|
void AStaffPuff::BeginPlay ()
|
|
{
|
|
Super::BeginPlay ();
|
|
momz = FRACUNIT;
|
|
}
|
|
|
|
// Staff puff 2 -------------------------------------------------------------
|
|
|
|
class AStaffPuff2 : public AStaffPuff
|
|
{
|
|
DECLARE_ACTOR (AStaffPuff2, AStaffPuff)
|
|
public:
|
|
void BeginPlay ();
|
|
};
|
|
|
|
FState AStaffPuff2::States[] =
|
|
{
|
|
S_BRIGHT (PUF4, 'A', 4, NULL , &States[1]),
|
|
S_BRIGHT (PUF4, 'B', 4, NULL , &States[2]),
|
|
S_BRIGHT (PUF4, 'C', 4, NULL , &States[3]),
|
|
S_BRIGHT (PUF4, 'D', 4, NULL , &States[4]),
|
|
S_BRIGHT (PUF4, 'E', 4, NULL , &States[5]),
|
|
S_BRIGHT (PUF4, 'F', 4, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AStaffPuff2, Heretic, -1, 0)
|
|
PROP_SpawnState (0)
|
|
PROP_RenderStyle (STYLE_Add)
|
|
PROP_Alpha (OPAQUE)
|
|
PROP_AttackSound ("weapons/staffpowerhit")
|
|
END_DEFAULTS
|
|
|
|
void AStaffPuff2::BeginPlay ()
|
|
{
|
|
Super::BeginPlay ();
|
|
momz = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_StaffAttackPL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_StaffAttackPL1 (AActor *actor)
|
|
{
|
|
angle_t angle;
|
|
int damage;
|
|
int slope;
|
|
player_t *player;
|
|
AActor *linetarget;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
damage = 5+(pr_sap()&15);
|
|
angle = actor->angle;
|
|
angle += pr_sap.Random2() << 18;
|
|
slope = P_AimLineAttack (actor, angle, MELEERANGE, &linetarget);
|
|
P_LineAttack (actor, angle, MELEERANGE, slope, damage, NAME_Melee, RUNTIME_CLASS(AStaffPuff), true);
|
|
if (linetarget)
|
|
{
|
|
//S_StartSound(player->mo, sfx_stfhit);
|
|
// turn to face target
|
|
actor->angle = R_PointToAngle2 (actor->x,
|
|
actor->y, linetarget->x, linetarget->y);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_StaffAttackPL2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_StaffAttackPL2 (AActor *actor)
|
|
{
|
|
angle_t angle;
|
|
int damage;
|
|
int slope;
|
|
player_t *player;
|
|
AActor *linetarget;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
// P_inter.c:P_DamageMobj() handles target momentums
|
|
damage = 18+(pr_sap2()&63);
|
|
angle = actor->angle;
|
|
angle += pr_sap2.Random2() << 18;
|
|
slope = P_AimLineAttack (actor, angle, MELEERANGE, &linetarget);
|
|
P_LineAttack (actor, angle, MELEERANGE, slope, damage, NAME_Melee, RUNTIME_CLASS(AStaffPuff2), true);
|
|
if (linetarget)
|
|
{
|
|
//S_StartSound(player->mo, sfx_stfpow);
|
|
// turn to face target
|
|
actor->angle = R_PointToAngle2 (actor->x,
|
|
actor->y, linetarget->x, linetarget->y);
|
|
}
|
|
}
|
|
|
|
// --- Gold wand ------------------------------------------------------------
|
|
|
|
void A_FireGoldWandPL1 (AActor *);
|
|
void A_FireGoldWandPL2 (AActor *);
|
|
|
|
// Gold wand ----------------------------------------------------------------
|
|
|
|
class AGoldWand : public AHereticWeapon
|
|
{
|
|
DECLARE_ACTOR (AGoldWand, AHereticWeapon)
|
|
};
|
|
|
|
class AGoldWandPowered : public AGoldWand
|
|
{
|
|
DECLARE_STATELESS_ACTOR (AGoldWandPowered, AGoldWand)
|
|
};
|
|
|
|
FState AGoldWand::States[] =
|
|
{
|
|
#define S_GOLDWANDREADY 0
|
|
S_NORMAL (GWND, 'A', 1, A_WeaponReady , &States[S_GOLDWANDREADY]),
|
|
|
|
#define S_GOLDWANDDOWN (S_GOLDWANDREADY+1)
|
|
S_NORMAL (GWND, 'A', 1, A_Lower , &States[S_GOLDWANDDOWN]),
|
|
|
|
#define S_GOLDWANDUP (S_GOLDWANDDOWN+1)
|
|
S_NORMAL (GWND, 'A', 1, A_Raise , &States[S_GOLDWANDUP]),
|
|
|
|
#define S_GOLDWANDATK1 (S_GOLDWANDUP+1)
|
|
S_NORMAL (GWND, 'B', 3, NULL , &States[S_GOLDWANDATK1+1]),
|
|
S_NORMAL (GWND, 'C', 5, A_FireGoldWandPL1 , &States[S_GOLDWANDATK1+2]),
|
|
S_NORMAL (GWND, 'D', 3, NULL , &States[S_GOLDWANDATK1+3]),
|
|
S_NORMAL (GWND, 'D', 0, A_ReFire , &States[S_GOLDWANDREADY]),
|
|
|
|
#define S_GOLDWANDATK2 (S_GOLDWANDATK1+4)
|
|
S_NORMAL (GWND, 'B', 3, NULL , &States[S_GOLDWANDATK2+1]),
|
|
S_NORMAL (GWND, 'C', 4, A_FireGoldWandPL2 , &States[S_GOLDWANDATK2+2]),
|
|
S_NORMAL (GWND, 'D', 3, NULL , &States[S_GOLDWANDATK2+3]),
|
|
S_NORMAL (GWND, 'D', 0, A_ReFire , &States[S_GOLDWANDREADY])
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AGoldWand, Heretic, -1, 0)
|
|
PROP_Flags5 (MF5_BLOODSPLATTER)
|
|
PROP_Weapon_SelectionOrder (2000)
|
|
PROP_Weapon_AmmoUse1 (USE_GWND_AMMO_1)
|
|
PROP_Weapon_AmmoGive1 (25)
|
|
PROP_Weapon_UpState (S_GOLDWANDUP)
|
|
PROP_Weapon_DownState (S_GOLDWANDDOWN)
|
|
PROP_Weapon_ReadyState (S_GOLDWANDREADY)
|
|
PROP_Weapon_AtkState (S_GOLDWANDATK1)
|
|
PROP_Weapon_YAdjust (5)
|
|
PROP_Weapon_MoveCombatDist (25000000)
|
|
PROP_Weapon_AmmoType1 ("GoldWandAmmo")
|
|
PROP_Weapon_SisterType ("GoldWandPowered")
|
|
END_DEFAULTS
|
|
|
|
IMPLEMENT_STATELESS_ACTOR (AGoldWandPowered, Heretic, -1, 0)
|
|
PROP_Weapon_Flags (WIF_POWERED_UP)
|
|
PROP_Weapon_AmmoUse1 (USE_GWND_AMMO_2)
|
|
PROP_Weapon_AmmoGive1 (0)
|
|
PROP_Weapon_AtkState (S_GOLDWANDATK2)
|
|
PROP_Weapon_SisterType ("GoldWand")
|
|
END_DEFAULTS
|
|
|
|
// Gold wand FX1 ------------------------------------------------------------
|
|
|
|
class AGoldWandFX1 : public AActor
|
|
{
|
|
DECLARE_ACTOR (AGoldWandFX1, AActor)
|
|
};
|
|
|
|
FState AGoldWandFX1::States[] =
|
|
{
|
|
#define S_GWANDFX1 0
|
|
S_BRIGHT (FX01, 'A', 6, NULL , &States[S_GWANDFX1+1]),
|
|
S_BRIGHT (FX01, 'B', 6, NULL , &States[S_GWANDFX1+0]),
|
|
|
|
#define S_GWANDFXI1 (S_GWANDFX1+2)
|
|
S_BRIGHT (FX01, 'E', 3, NULL , &States[S_GWANDFXI1+1]),
|
|
S_BRIGHT (FX01, 'F', 3, NULL , &States[S_GWANDFXI1+2]),
|
|
S_BRIGHT (FX01, 'G', 3, NULL , &States[S_GWANDFXI1+3]),
|
|
S_BRIGHT (FX01, 'H', 3, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AGoldWandFX1, Heretic, -1, 151)
|
|
PROP_RadiusFixed (10)
|
|
PROP_HeightFixed (6)
|
|
PROP_SpeedFixed (22)
|
|
PROP_Damage (2)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY)
|
|
PROP_Flags2 (MF2_NOTELEPORT)
|
|
PROP_RenderStyle (STYLE_Add)
|
|
|
|
PROP_SpawnState (S_GWANDFX1)
|
|
PROP_DeathState (S_GWANDFXI1)
|
|
|
|
PROP_DeathSound ("weapons/wandhit")
|
|
END_DEFAULTS
|
|
|
|
// Gold wand FX2 ------------------------------------------------------------
|
|
|
|
class AGoldWandFX2 : public AGoldWandFX1
|
|
{
|
|
DECLARE_ACTOR (AGoldWandFX2, AGoldWandFX1)
|
|
};
|
|
|
|
FState AGoldWandFX2::States[] =
|
|
{
|
|
S_BRIGHT (FX01, 'C', 6, NULL , &States[1]),
|
|
S_BRIGHT (FX01, 'D', 6, NULL , &States[0])
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AGoldWandFX2, Heretic, -1, 152)
|
|
PROP_SpeedFixed (18)
|
|
PROP_Damage (1)
|
|
|
|
PROP_SpawnState (0)
|
|
|
|
PROP_DeathSound ("")
|
|
END_DEFAULTS
|
|
|
|
// Gold wand puff 1 ---------------------------------------------------------
|
|
|
|
class AGoldWandPuff1 : public AActor
|
|
{
|
|
DECLARE_ACTOR (AGoldWandPuff1, AActor)
|
|
};
|
|
|
|
FState AGoldWandPuff1::States[] =
|
|
{
|
|
S_BRIGHT (PUF2, 'A', 3, NULL , &States[1]),
|
|
S_BRIGHT (PUF2, 'B', 3, NULL , &States[2]),
|
|
S_BRIGHT (PUF2, 'C', 3, NULL , &States[3]),
|
|
S_BRIGHT (PUF2, 'D', 3, NULL , &States[4]),
|
|
S_BRIGHT (PUF2, 'E', 3, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AGoldWandPuff1, Heretic, -1, 0)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY)
|
|
PROP_Flags3 (MF3_PUFFONACTORS)
|
|
PROP_SpawnState (0)
|
|
PROP_RenderStyle (STYLE_Add)
|
|
END_DEFAULTS
|
|
|
|
// Gold wand puff 2 ---------------------------------------------------------
|
|
|
|
class AGoldWandPuff2 : public AGoldWandFX1
|
|
{
|
|
DECLARE_STATELESS_ACTOR (AGoldWandPuff2, AGoldWandFX1)
|
|
};
|
|
|
|
IMPLEMENT_STATELESS_ACTOR (AGoldWandPuff2, Heretic, -1, 0)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY)
|
|
PROP_SpawnState (S_GWANDFXI1)
|
|
END_DEFAULTS
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireGoldWandPL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FireGoldWandPL1 (AActor *actor)
|
|
{
|
|
angle_t angle;
|
|
int damage;
|
|
player_t *player;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
angle_t pitch = P_BulletSlope(actor);
|
|
damage = 7+(pr_fgw()&7);
|
|
angle = actor->angle;
|
|
if (player->refire)
|
|
{
|
|
angle += pr_fgw.Random2() << 18;
|
|
}
|
|
P_LineAttack (actor, angle, PLAYERMISSILERANGE, pitch, damage, NAME_None, RUNTIME_CLASS(AGoldWandPuff1));
|
|
S_Sound (actor, CHAN_WEAPON, "weapons/wandhit", 1, ATTN_NORM);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireGoldWandPL2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FireGoldWandPL2 (AActor *actor)
|
|
{
|
|
int i;
|
|
angle_t angle;
|
|
int damage;
|
|
fixed_t momz;
|
|
player_t *player;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
angle_t pitch = P_BulletSlope(actor);
|
|
momz = FixedMul (GetDefault<AGoldWandFX2>()->Speed,
|
|
finetangent[FINEANGLES/4-((signed)pitch>>ANGLETOFINESHIFT)]);
|
|
P_SpawnMissileAngle (actor, RUNTIME_CLASS(AGoldWandFX2), actor->angle-(ANG45/8), momz);
|
|
P_SpawnMissileAngle (actor, RUNTIME_CLASS(AGoldWandFX2), actor->angle+(ANG45/8), momz);
|
|
angle = actor->angle-(ANG45/8);
|
|
for(i = 0; i < 5; i++)
|
|
{
|
|
damage = 1+(pr_fgw2()&7);
|
|
P_LineAttack (actor, angle, PLAYERMISSILERANGE, pitch, damage, NAME_None, RUNTIME_CLASS(AGoldWandPuff2));
|
|
angle += ((ANG45/8)*2)/4;
|
|
}
|
|
S_Sound (actor, CHAN_WEAPON, "weapons/wandhit", 1, ATTN_NORM);
|
|
}
|
|
|
|
// --- Crossbow -------------------------------------------------------------
|
|
|
|
void A_FireCrossbowPL1 (AActor *);
|
|
void A_FireCrossbowPL2 (AActor *);
|
|
void A_BoltSpark (AActor *);
|
|
|
|
// Crossbow -----------------------------------------------------------------
|
|
|
|
class ACrossbow : public AHereticWeapon
|
|
{
|
|
DECLARE_ACTOR (ACrossbow, AHereticWeapon)
|
|
};
|
|
|
|
class ACrossbowPowered : public ACrossbow
|
|
{
|
|
DECLARE_STATELESS_ACTOR (ACrossbowPowered, ACrossbow)
|
|
};
|
|
|
|
FState ACrossbow::States[] =
|
|
{
|
|
#define S_WBOW 0
|
|
S_NORMAL (WBOW, 'A', -1, NULL , NULL),
|
|
|
|
#define S_CRBOW (S_WBOW+1)
|
|
S_NORMAL (CRBW, 'A', 1, A_WeaponReady , &States[S_CRBOW+1]),
|
|
S_NORMAL (CRBW, 'A', 1, A_WeaponReady , &States[S_CRBOW+2]),
|
|
S_NORMAL (CRBW, 'A', 1, A_WeaponReady , &States[S_CRBOW+3]),
|
|
S_NORMAL (CRBW, 'A', 1, A_WeaponReady , &States[S_CRBOW+4]),
|
|
S_NORMAL (CRBW, 'A', 1, A_WeaponReady , &States[S_CRBOW+5]),
|
|
S_NORMAL (CRBW, 'A', 1, A_WeaponReady , &States[S_CRBOW+6]),
|
|
S_NORMAL (CRBW, 'B', 1, A_WeaponReady , &States[S_CRBOW+7]),
|
|
S_NORMAL (CRBW, 'B', 1, A_WeaponReady , &States[S_CRBOW+8]),
|
|
S_NORMAL (CRBW, 'B', 1, A_WeaponReady , &States[S_CRBOW+9]),
|
|
S_NORMAL (CRBW, 'B', 1, A_WeaponReady , &States[S_CRBOW+10]),
|
|
S_NORMAL (CRBW, 'B', 1, A_WeaponReady , &States[S_CRBOW+11]),
|
|
S_NORMAL (CRBW, 'B', 1, A_WeaponReady , &States[S_CRBOW+12]),
|
|
S_NORMAL (CRBW, 'C', 1, A_WeaponReady , &States[S_CRBOW+13]),
|
|
S_NORMAL (CRBW, 'C', 1, A_WeaponReady , &States[S_CRBOW+14]),
|
|
S_NORMAL (CRBW, 'C', 1, A_WeaponReady , &States[S_CRBOW+15]),
|
|
S_NORMAL (CRBW, 'C', 1, A_WeaponReady , &States[S_CRBOW+16]),
|
|
S_NORMAL (CRBW, 'C', 1, A_WeaponReady , &States[S_CRBOW+17]),
|
|
S_NORMAL (CRBW, 'C', 1, A_WeaponReady , &States[S_CRBOW+0]),
|
|
|
|
#define S_CRBOWDOWN (S_CRBOW+18)
|
|
S_NORMAL (CRBW, 'A', 1, A_Lower , &States[S_CRBOWDOWN]),
|
|
|
|
#define S_CRBOWUP (S_CRBOWDOWN+1)
|
|
S_NORMAL (CRBW, 'A', 1, A_Raise , &States[S_CRBOWUP]),
|
|
|
|
#define S_CRBOWATK1 (S_CRBOWUP+1)
|
|
S_NORMAL (CRBW, 'D', 6, A_FireCrossbowPL1 , &States[S_CRBOWATK1+1]),
|
|
S_NORMAL (CRBW, 'E', 3, NULL , &States[S_CRBOWATK1+2]),
|
|
S_NORMAL (CRBW, 'F', 3, NULL , &States[S_CRBOWATK1+3]),
|
|
S_NORMAL (CRBW, 'G', 3, NULL , &States[S_CRBOWATK1+4]),
|
|
S_NORMAL (CRBW, 'H', 3, NULL , &States[S_CRBOWATK1+5]),
|
|
S_NORMAL (CRBW, 'A', 4, NULL , &States[S_CRBOWATK1+6]),
|
|
S_NORMAL (CRBW, 'B', 4, NULL , &States[S_CRBOWATK1+7]),
|
|
S_NORMAL (CRBW, 'C', 5, A_ReFire , &States[S_CRBOW+0]),
|
|
|
|
#define S_CRBOWATK2 (S_CRBOWATK1+8)
|
|
S_NORMAL (CRBW, 'D', 5, A_FireCrossbowPL2 , &States[S_CRBOWATK2+1]),
|
|
S_NORMAL (CRBW, 'E', 3, NULL , &States[S_CRBOWATK2+2]),
|
|
S_NORMAL (CRBW, 'F', 2, NULL , &States[S_CRBOWATK2+3]),
|
|
S_NORMAL (CRBW, 'G', 3, NULL , &States[S_CRBOWATK2+4]),
|
|
S_NORMAL (CRBW, 'H', 2, NULL , &States[S_CRBOWATK2+5]),
|
|
S_NORMAL (CRBW, 'A', 3, NULL , &States[S_CRBOWATK2+6]),
|
|
S_NORMAL (CRBW, 'B', 3, NULL , &States[S_CRBOWATK2+7]),
|
|
S_NORMAL (CRBW, 'C', 4, A_ReFire , &States[S_CRBOW+0])
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ACrossbow, Heretic, 2001, 27)
|
|
PROP_Flags (MF_SPECIAL)
|
|
PROP_SpawnState (S_WBOW)
|
|
|
|
PROP_Weapon_SelectionOrder (800)
|
|
PROP_Weapon_AmmoUse1 (USE_CBOW_AMMO_1)
|
|
PROP_Weapon_AmmoGive1 (10)
|
|
PROP_Weapon_UpState (S_CRBOWUP)
|
|
PROP_Weapon_DownState (S_CRBOWDOWN)
|
|
PROP_Weapon_ReadyState (S_CRBOW)
|
|
PROP_Weapon_AtkState (S_CRBOWATK1)
|
|
PROP_Weapon_YAdjust (15)
|
|
PROP_Weapon_MoveCombatDist (24000000)
|
|
PROP_Weapon_AmmoType1 ("CrossbowAmmo")
|
|
PROP_Weapon_SisterType ("CrossbowPowered")
|
|
PROP_Weapon_ProjectileType ("CrossbowFX1")
|
|
PROP_Inventory_PickupMessage("$TXT_WPNCROSSBOW")
|
|
END_DEFAULTS
|
|
|
|
IMPLEMENT_STATELESS_ACTOR (ACrossbowPowered, Heretic, -1, 0)
|
|
PROP_Weapon_Flags (WIF_POWERED_UP)
|
|
PROP_Weapon_AmmoUse1 (USE_CBOW_AMMO_2)
|
|
PROP_Weapon_AmmoGive1 (0)
|
|
PROP_Weapon_AtkState (S_CRBOWATK2)
|
|
PROP_Weapon_SisterType ("Crossbow")
|
|
PROP_Weapon_ProjectileType ("CrossbowFX2")
|
|
END_DEFAULTS
|
|
|
|
// Crossbow FX1 -------------------------------------------------------------
|
|
|
|
class ACrossbowFX1 : public AActor
|
|
{
|
|
DECLARE_ACTOR (ACrossbowFX1, AActor)
|
|
};
|
|
|
|
FState ACrossbowFX1::States[] =
|
|
{
|
|
#define S_CRBOWFX1 0
|
|
S_BRIGHT (FX03, 'B', 1, NULL , &States[S_CRBOWFX1]),
|
|
|
|
#define S_CRBOWFXI1 (S_CRBOWFX1+1)
|
|
S_BRIGHT (FX03, 'H', 8, NULL , &States[S_CRBOWFXI1+1]),
|
|
S_BRIGHT (FX03, 'I', 8, NULL , &States[S_CRBOWFXI1+2]),
|
|
S_BRIGHT (FX03, 'J', 8, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ACrossbowFX1, Heretic, -1, 147)
|
|
PROP_RadiusFixed (11)
|
|
PROP_HeightFixed (8)
|
|
PROP_SpeedFixed (30)
|
|
PROP_Damage (10)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY)
|
|
PROP_Flags2 (MF2_NOTELEPORT|MF2_PCROSS|MF2_IMPACT)
|
|
PROP_RenderStyle (STYLE_Add)
|
|
|
|
PROP_SpawnState (S_CRBOWFX1)
|
|
PROP_DeathState (S_CRBOWFXI1)
|
|
|
|
PROP_SeeSound ("weapons/bowshoot")
|
|
PROP_DeathSound ("weapons/bowhit")
|
|
END_DEFAULTS
|
|
|
|
// Crossbow FX2 -------------------------------------------------------------
|
|
|
|
class ACrossbowFX2 : public ACrossbowFX1
|
|
{
|
|
DECLARE_ACTOR (ACrossbowFX2, ACrossbowFX1)
|
|
};
|
|
|
|
FState ACrossbowFX2::States[] =
|
|
{
|
|
#define S_CRBOWFX2 0
|
|
S_BRIGHT (FX03, 'B', 1, A_BoltSpark , &States[S_CRBOWFX2])
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ACrossbowFX2, Heretic, -1, 148)
|
|
PROP_SpeedFixed (32)
|
|
PROP_Damage (6)
|
|
PROP_RenderStyle (STYLE_Add)
|
|
PROP_SpawnState (S_CRBOWFX2)
|
|
END_DEFAULTS
|
|
|
|
// Crossbow FX3 -------------------------------------------------------------
|
|
|
|
class ACrossbowFX3 : public ACrossbowFX1
|
|
{
|
|
DECLARE_ACTOR (ACrossbowFX3, ACrossbowFX1)
|
|
};
|
|
|
|
FState ACrossbowFX3::States[] =
|
|
{
|
|
#define S_CRBOWFX3 0
|
|
S_BRIGHT (FX03, 'A', 1, NULL , &States[S_CRBOWFX3]),
|
|
|
|
#define S_CRBOWFXI3 (S_CRBOWFX3+1)
|
|
S_BRIGHT (FX03, 'C', 8, NULL , &States[S_CRBOWFXI3+1]),
|
|
S_BRIGHT (FX03, 'D', 8, NULL , &States[S_CRBOWFXI3+2]),
|
|
S_BRIGHT (FX03, 'E', 8, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ACrossbowFX3, Heretic, -1, 149)
|
|
PROP_SpeedFixed (20)
|
|
PROP_Damage (2)
|
|
PROP_FlagsClear (MF_NOBLOCKMAP)
|
|
PROP_Flags2 (MF2_WINDTHRUST|MF2_THRUGHOST|MF2_NOTELEPORT|MF2_PCROSS|MF2_IMPACT)
|
|
PROP_RenderStyle (STYLE_Add)
|
|
|
|
PROP_SpawnState (S_CRBOWFX3)
|
|
PROP_DeathState (S_CRBOWFXI3)
|
|
|
|
PROP_SeeSound ("")
|
|
END_DEFAULTS
|
|
|
|
// Crossbow FX4 -------------------------------------------------------------
|
|
|
|
class ACrossbowFX4 : public AActor
|
|
{
|
|
DECLARE_ACTOR (ACrossbowFX4, AActor)
|
|
};
|
|
|
|
FState ACrossbowFX4::States[] =
|
|
{
|
|
#define S_CRBOWFX4 0
|
|
S_BRIGHT (FX03, 'F', 8, NULL , &States[S_CRBOWFX4+1]),
|
|
S_BRIGHT (FX03, 'G', 8, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ACrossbowFX4, Heretic, -1, 0)
|
|
PROP_Flags (MF_NOBLOCKMAP)
|
|
PROP_Gravity (FRACUNIT/8)
|
|
PROP_RenderStyle (STYLE_Add)
|
|
PROP_SpawnState (S_CRBOWFX4)
|
|
END_DEFAULTS
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireCrossbowPL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FireCrossbowPL1 (AActor *actor)
|
|
{
|
|
player_t *player;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ACrossbowFX1));
|
|
P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ACrossbowFX3), actor->angle-(ANG45/10));
|
|
P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ACrossbowFX3), actor->angle+(ANG45/10));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireCrossbowPL2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FireCrossbowPL2(AActor *actor)
|
|
{
|
|
player_t *player;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = actor->player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ACrossbowFX2));
|
|
P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ACrossbowFX2), actor->angle-(ANG45/10));
|
|
P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ACrossbowFX2), actor->angle+(ANG45/10));
|
|
P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ACrossbowFX3), actor->angle-(ANG45/5));
|
|
P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ACrossbowFX3), actor->angle+(ANG45/5));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_BoltSpark
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_BoltSpark (AActor *bolt)
|
|
{
|
|
AActor *spark;
|
|
|
|
if (pr_boltspark() > 50)
|
|
{
|
|
spark = Spawn<ACrossbowFX4> (bolt->x, bolt->y, bolt->z, ALLOW_REPLACE);
|
|
spark->x += pr_boltspark.Random2() << 10;
|
|
spark->y += pr_boltspark.Random2() << 10;
|
|
}
|
|
}
|
|
|
|
// --- Mace -----------------------------------------------------------------
|
|
|
|
#define MAGIC_JUNK 1234
|
|
|
|
void A_FireMacePL1B (AActor *);
|
|
void A_FireMacePL1 (AActor *);
|
|
void A_MacePL1Check (AActor *);
|
|
void A_MaceBallImpact (AActor *);
|
|
void A_MaceBallImpact2 (AActor *);
|
|
void A_FireMacePL2 (AActor *);
|
|
void A_DeathBallImpact (AActor *);
|
|
|
|
// The mace itself ----------------------------------------------------------
|
|
|
|
class AMace : public AHereticWeapon
|
|
{
|
|
DECLARE_ACTOR (AMace, AHereticWeapon)
|
|
HAS_OBJECT_POINTERS
|
|
public:
|
|
void Serialize (FArchive &arc);
|
|
protected:
|
|
bool DoRespawn ();
|
|
int NumMaceSpots;
|
|
TObjPtr<AActor> FirstSpot;
|
|
private:
|
|
|
|
friend void A_SpawnMace (AActor *self);
|
|
};
|
|
|
|
class AMacePowered : public AMace
|
|
{
|
|
DECLARE_STATELESS_ACTOR (AMacePowered, AMace)
|
|
};
|
|
|
|
IMPLEMENT_POINTY_CLASS (AMace)
|
|
DECLARE_POINTER (FirstSpot)
|
|
END_POINTERS
|
|
|
|
void AMace::Serialize (FArchive &arc)
|
|
{
|
|
Super::Serialize (arc);
|
|
arc << NumMaceSpots << FirstSpot;
|
|
}
|
|
|
|
FState AMace::States[] =
|
|
{
|
|
#define S_WMCE 0
|
|
S_NORMAL (WMCE, 'A', -1, NULL , NULL),
|
|
|
|
#define S_MACEREADY (S_WMCE+1)
|
|
S_NORMAL (MACE, 'A', 1, A_WeaponReady , &States[S_MACEREADY]),
|
|
|
|
#define S_MACEDOWN (S_MACEREADY+1)
|
|
S_NORMAL (MACE, 'A', 1, A_Lower , &States[S_MACEDOWN]),
|
|
|
|
#define S_MACEUP (S_MACEDOWN+1)
|
|
S_NORMAL (MACE, 'A', 1, A_Raise , &States[S_MACEUP]),
|
|
|
|
#define S_MACEATK1 (S_MACEUP+1)
|
|
S_NORMAL (MACE, 'B', 4, NULL , &States[S_MACEATK1+1]),
|
|
S_NORMAL (MACE, 'C', 3, A_FireMacePL1 , &States[S_MACEATK1+2]),
|
|
S_NORMAL (MACE, 'D', 3, A_FireMacePL1 , &States[S_MACEATK1+3]),
|
|
S_NORMAL (MACE, 'E', 3, A_FireMacePL1 , &States[S_MACEATK1+4]),
|
|
S_NORMAL (MACE, 'F', 3, A_FireMacePL1 , &States[S_MACEATK1+5]),
|
|
S_NORMAL (MACE, 'C', 4, A_ReFire , &States[S_MACEATK1+6]),
|
|
S_NORMAL (MACE, 'D', 4, NULL , &States[S_MACEATK1+7]),
|
|
S_NORMAL (MACE, 'E', 4, NULL , &States[S_MACEATK1+8]),
|
|
S_NORMAL (MACE, 'F', 4, NULL , &States[S_MACEATK1+9]),
|
|
S_NORMAL (MACE, 'B', 4, NULL , &States[S_MACEREADY]),
|
|
|
|
#define S_MACEATK2 (S_MACEATK1+10)
|
|
S_NORMAL (MACE, 'B', 4, NULL , &States[S_MACEATK2+1]),
|
|
S_NORMAL (MACE, 'D', 4, A_FireMacePL2 , &States[S_MACEATK2+2]),
|
|
S_NORMAL (MACE, 'B', 4, NULL , &States[S_MACEATK2+3]),
|
|
S_NORMAL (MACE, 'A', 8, A_ReFire , &States[S_MACEREADY])
|
|
};
|
|
|
|
BEGIN_DEFAULTS (AMace, Heretic, -1, 0)
|
|
PROP_Flags (MF_SPECIAL)
|
|
PROP_SpawnState (0)
|
|
|
|
PROP_Weapon_SelectionOrder (1400)
|
|
PROP_Weapon_Flags (WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE)
|
|
PROP_Weapon_AmmoUse1 (USE_MACE_AMMO_1)
|
|
PROP_Weapon_AmmoGive1 (50)
|
|
PROP_Weapon_UpState (S_MACEUP)
|
|
PROP_Weapon_DownState (S_MACEDOWN)
|
|
PROP_Weapon_ReadyState (S_MACEREADY)
|
|
PROP_Weapon_AtkState (S_MACEATK1)
|
|
PROP_Weapon_HoldAtkState (S_MACEATK1+1)
|
|
PROP_Weapon_YAdjust (15)
|
|
PROP_Weapon_MoveCombatDist (27000000)
|
|
PROP_Weapon_AmmoType1 ("MaceAmmo")
|
|
PROP_Weapon_SisterType ("MacePowered")
|
|
PROP_Weapon_ProjectileType ("MaceFX2")
|
|
PROP_Inventory_PickupMessage("$TXT_WPNMACE")
|
|
END_DEFAULTS
|
|
|
|
IMPLEMENT_STATELESS_ACTOR (AMacePowered, Heretic, -1, 31)
|
|
PROP_Weapon_Flags (WIF_POWERED_UP|WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE)
|
|
PROP_Weapon_AmmoUse1 (USE_MACE_AMMO_2)
|
|
PROP_Weapon_AmmoGive1 (0)
|
|
PROP_Weapon_AtkState (S_MACEATK2)
|
|
PROP_Weapon_HoldAtkState (S_MACEATK2)
|
|
PROP_Weapon_SisterType ("Mace")
|
|
PROP_Weapon_ProjectileType ("MaceFX4")
|
|
END_DEFAULTS
|
|
|
|
// Mace FX1 -----------------------------------------------------------------
|
|
|
|
class AMaceFX1 : public AActor
|
|
{
|
|
DECLARE_ACTOR (AMaceFX1, AActor)
|
|
};
|
|
|
|
FState AMaceFX1::States[] =
|
|
{
|
|
#define S_MACEFX1 0
|
|
S_NORMAL (FX02, 'A', 4, A_MacePL1Check , &States[S_MACEFX1+1]),
|
|
S_NORMAL (FX02, 'B', 4, A_MacePL1Check , &States[S_MACEFX1+0]),
|
|
|
|
#define S_MACEFXI1 (S_MACEFX1+2)
|
|
S_BRIGHT (FX02, 'F', 4, A_MaceBallImpact , &States[S_MACEFXI1+1]),
|
|
S_BRIGHT (FX02, 'G', 4, NULL , &States[S_MACEFXI1+2]),
|
|
S_BRIGHT (FX02, 'H', 4, NULL , &States[S_MACEFXI1+3]),
|
|
S_BRIGHT (FX02, 'I', 4, NULL , &States[S_MACEFXI1+4]),
|
|
S_BRIGHT (FX02, 'J', 4, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AMaceFX1, Heretic, -1, 154)
|
|
PROP_RadiusFixed (8)
|
|
PROP_HeightFixed (6)
|
|
PROP_SpeedFixed (20)
|
|
PROP_Damage (2)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY)
|
|
PROP_Flags2 (MF2_HERETICBOUNCE|MF2_THRUGHOST|MF2_NOTELEPORT|MF2_PCROSS|MF2_IMPACT)
|
|
PROP_Flags3 (MF3_WARNBOT)
|
|
|
|
PROP_SpawnState (S_MACEFX1)
|
|
PROP_DeathState (S_MACEFXI1)
|
|
|
|
PROP_SeeSound ("weapons/maceshoot")
|
|
END_DEFAULTS
|
|
|
|
// Mace FX2 -----------------------------------------------------------------
|
|
|
|
class AMaceFX2 : public AActor
|
|
{
|
|
DECLARE_ACTOR (AMaceFX2, AActor)
|
|
};
|
|
|
|
FState AMaceFX2::States[] =
|
|
{
|
|
#define S_MACEFX2 0
|
|
S_NORMAL (FX02, 'C', 4, NULL , &States[S_MACEFX2+1]),
|
|
S_NORMAL (FX02, 'D', 4, NULL , &States[S_MACEFX2+0]),
|
|
|
|
#define S_MACEFXI2 (S_MACEFX2+2)
|
|
S_BRIGHT (FX02, 'F', 4, A_MaceBallImpact2 , &AMaceFX1::States[S_MACEFXI1+1])
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AMaceFX2, Heretic, -1, 156)
|
|
PROP_RadiusFixed (8)
|
|
PROP_HeightFixed (6)
|
|
PROP_SpeedFixed (10)
|
|
PROP_Damage (6)
|
|
PROP_Gravity (FRACUNIT/8)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF)
|
|
PROP_Flags2 (MF2_HERETICBOUNCE|MF2_THRUGHOST|MF2_NOTELEPORT|MF2_PCROSS|MF2_IMPACT)
|
|
|
|
PROP_SpawnState (S_MACEFX2)
|
|
PROP_DeathState (S_MACEFXI2)
|
|
END_DEFAULTS
|
|
|
|
// Mace FX3 -----------------------------------------------------------------
|
|
|
|
class AMaceFX3 : public AMaceFX1
|
|
{
|
|
DECLARE_ACTOR (AMaceFX3, AMaceFX1)
|
|
};
|
|
|
|
FState AMaceFX3::States[] =
|
|
{
|
|
#define S_MACEFX3 0
|
|
S_NORMAL (FX02, 'A', 4, NULL , &States[S_MACEFX3+1]),
|
|
S_NORMAL (FX02, 'B', 4, NULL , &States[S_MACEFX3+0])
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AMaceFX3, Heretic, -1, 155)
|
|
PROP_SpeedFixed (7)
|
|
PROP_Damage (4)
|
|
PROP_Gravity (FRACUNIT/8)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF)
|
|
PROP_Flags2 (MF2_HERETICBOUNCE|MF2_THRUGHOST|MF2_NOTELEPORT|MF2_PCROSS|MF2_IMPACT)
|
|
|
|
PROP_SpawnState (S_MACEFX3)
|
|
END_DEFAULTS
|
|
|
|
// Mace FX4 -----------------------------------------------------------------
|
|
|
|
class AMaceFX4 : public AActor
|
|
{
|
|
DECLARE_ACTOR (AMaceFX4, AActor)
|
|
public:
|
|
int DoSpecialDamage (AActor *target, int damage);
|
|
};
|
|
|
|
FState AMaceFX4::States[] =
|
|
{
|
|
#define S_MACEFX4 0
|
|
S_NORMAL (FX02, 'E', 99, NULL , &States[S_MACEFX4+0]),
|
|
|
|
#define S_MACEFXI4 (S_MACEFX4+1)
|
|
S_BRIGHT (FX02, 'C', 4, A_DeathBallImpact , &AMaceFX1::States[S_MACEFXI1+1])
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AMaceFX4, Heretic, -1, 153)
|
|
PROP_RadiusFixed (8)
|
|
PROP_HeightFixed (6)
|
|
PROP_SpeedFixed (7)
|
|
PROP_Damage (18)
|
|
PROP_Gravity (FRACUNIT/8)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF)
|
|
PROP_Flags2 (MF2_HERETICBOUNCE|MF2_THRUGHOST|MF2_TELESTOMP|MF2_PCROSS|MF2_IMPACT)
|
|
|
|
PROP_SpawnState (S_MACEFX4)
|
|
PROP_DeathState (S_MACEFXI4)
|
|
END_DEFAULTS
|
|
|
|
int AMaceFX4::DoSpecialDamage (AActor *target, int damage)
|
|
{
|
|
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 1000000; // Something's gonna die
|
|
}
|
|
|
|
// Mace spawn spot ----------------------------------------------------------
|
|
|
|
void A_SpawnMace (AActor *);
|
|
|
|
class AMaceSpawner : public AActor
|
|
{
|
|
DECLARE_ACTOR (AMaceSpawner, AActor)
|
|
|
|
// Uses target to point to the next mace spawner in the list
|
|
};
|
|
|
|
FState AMaceSpawner::States[] =
|
|
{
|
|
S_NORMAL (TNT1, 'A', 1, NULL, &States[1]),
|
|
S_NORMAL (TNT1, 'A', -1, A_SpawnMace, NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AMaceSpawner, Heretic, 2002, 0)
|
|
PROP_Flags (MF_NOSECTOR|MF_NOBLOCKMAP)
|
|
PROP_SpawnState (0)
|
|
END_DEFAULTS
|
|
|
|
|
|
static bool RespawnMace (AActor *mace, AActor *FirstSpot, int NumMaceSpots)
|
|
{
|
|
if (NumMaceSpots > 0)
|
|
{
|
|
int spotnum = pr_macerespawn () % NumMaceSpots;
|
|
AActor *spot = FirstSpot;
|
|
|
|
while (spotnum > 0)
|
|
{
|
|
spot = spot->target;
|
|
spotnum--;
|
|
}
|
|
|
|
mace->SetOrigin (spot->x, spot->y, spot->z);
|
|
mace->z = mace->floorz;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Every mace spawn spot will execute this action. The first one
|
|
// will build a list of all mace spots in the level and spawn a
|
|
// mace. The rest of the spots will do nothing.
|
|
|
|
void A_SpawnMace (AActor *self)
|
|
{
|
|
if (self->target != NULL)
|
|
{ // Another spot already did it
|
|
return;
|
|
}
|
|
|
|
TThinkerIterator<AMaceSpawner> iterator;
|
|
AActor *spot;
|
|
AMaceSpawner *firstSpot;
|
|
AMace *mace;
|
|
int numspots = 0;
|
|
|
|
spot = firstSpot = iterator.Next ();
|
|
while (spot != NULL)
|
|
{
|
|
numspots++;
|
|
spot->target = iterator.Next ();
|
|
if (spot->target == NULL)
|
|
{
|
|
spot->target = firstSpot;
|
|
spot = NULL;
|
|
}
|
|
else
|
|
{
|
|
spot = spot->target;
|
|
}
|
|
}
|
|
if (numspots == 0)
|
|
{
|
|
return;
|
|
}
|
|
if (!deathmatch && pr_spawnmace() < 64)
|
|
{ // Sometimes doesn't show up if not in deathmatch
|
|
return;
|
|
}
|
|
mace = Spawn<AMace> (self->x, self->y, self->z, ALLOW_REPLACE);
|
|
|
|
if (mace)
|
|
{
|
|
if (mace->IsKindOf(RUNTIME_CLASS(AMace)))
|
|
{
|
|
// remember the values for later
|
|
// (works only for the original mace!)
|
|
mace->FirstSpot = firstSpot;
|
|
mace->NumMaceSpots = numspots;
|
|
}
|
|
RespawnMace(mace, firstSpot, numspots);
|
|
// We want this mace to respawn.
|
|
mace->flags &= ~MF_DROPPED;
|
|
}
|
|
}
|
|
|
|
// AMace::DoRespawn
|
|
// Moves the mace to a different spot when it respawns
|
|
|
|
bool AMace::DoRespawn ()
|
|
{
|
|
return RespawnMace(this, FirstSpot, NumMaceSpots);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireMacePL1B
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_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<AMaceFX2> (actor->x, actor->y, actor->z + 28*FRACUNIT
|
|
- actor->floorclip, ALLOW_REPLACE);
|
|
ball->momz = 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->momx = (actor->momx>>1)+FixedMul(ball->Speed, finecosine[angle]);
|
|
ball->momy = (actor->momy>>1)+FixedMul(ball->Speed, finesine[angle]);
|
|
S_Sound (ball, CHAN_BODY, "weapons/maceshoot", 1, ATTN_NORM);
|
|
P_CheckMissileSpawn (ball);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireMacePL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FireMacePL1 (AActor *actor)
|
|
{
|
|
AActor *ball;
|
|
player_t *player;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (pr_maceatk() < 28)
|
|
{
|
|
A_FireMacePL1B (actor);
|
|
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 (actor, RUNTIME_CLASS(AMaceFX1),
|
|
actor->angle+(((pr_maceatk()&7)-4)<<24));
|
|
if (ball)
|
|
{
|
|
ball->special1 = 16; // tics till dropoff
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_MacePL1Check
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_MacePL1Check (AActor *ball)
|
|
{
|
|
if (ball->special1 == 0)
|
|
{
|
|
return;
|
|
}
|
|
ball->special1 -= 4;
|
|
if (ball->special1 > 0)
|
|
{
|
|
return;
|
|
}
|
|
ball->special1 = 0;
|
|
ball->flags &= ~MF_NOGRAVITY;
|
|
ball->gravity = FRACUNIT/8;
|
|
// [RH] Avoid some precision loss by scaling the momentum directly
|
|
#if 0
|
|
angle_t angle = ball->angle>>ANGLETOFINESHIFT;
|
|
ball->momx = FixedMul(7*FRACUNIT, finecosine[angle]);
|
|
ball->momy = FixedMul(7*FRACUNIT, finesine[angle]);
|
|
#else
|
|
float momscale = sqrtf ((float)ball->momx * (float)ball->momx +
|
|
(float)ball->momy * (float)ball->momy);
|
|
momscale = 458752.f / momscale;
|
|
ball->momx = (int)(ball->momx * momscale);
|
|
ball->momy = (int)(ball->momy * momscale);
|
|
#endif
|
|
ball->momz -= ball->momz>>1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_MaceBallImpact
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_MaceBallImpact (AActor *ball)
|
|
{
|
|
if ((ball->health != MAGIC_JUNK) && (ball->flags & MF_INBOUNCE))
|
|
{ // Bounce
|
|
ball->health = MAGIC_JUNK;
|
|
ball->momz = (ball->momz * 192) >> 8;
|
|
ball->flags2 &= ~MF2_BOUNCETYPE;
|
|
ball->SetState (ball->SpawnState);
|
|
S_Sound (ball, CHAN_BODY, "weapons/macebounce", 1, ATTN_NORM);
|
|
}
|
|
else
|
|
{ // Explode
|
|
ball->momx = ball->momy = ball->momz = 0;
|
|
ball->flags |= MF_NOGRAVITY;
|
|
ball->gravity = FRACUNIT;
|
|
S_Sound (ball, CHAN_BODY, "weapons/macehit", 1, ATTN_NORM);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_MaceBallImpact2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_MaceBallImpact2 (AActor *ball)
|
|
{
|
|
AActor *tiny;
|
|
angle_t angle;
|
|
|
|
if (ball->flags & MF_INBOUNCE)
|
|
{
|
|
fixed_t floordist = ball->z - ball->floorz;
|
|
fixed_t ceildist = ball->ceilingz - ball->z;
|
|
fixed_t vel;
|
|
|
|
if (floordist <= ceildist)
|
|
{
|
|
vel = MulScale32 (ball->momz, ball->Sector->floorplane.c);
|
|
}
|
|
else
|
|
{
|
|
vel = MulScale32 (ball->momz, ball->Sector->ceilingplane.c);
|
|
}
|
|
if (vel < 2)
|
|
{
|
|
goto boom;
|
|
}
|
|
|
|
// Bounce
|
|
ball->momz = (ball->momz * 192) >> 8;
|
|
ball->SetState (ball->SpawnState);
|
|
|
|
tiny = Spawn<AMaceFX3> (ball->x, ball->y, ball->z, ALLOW_REPLACE);
|
|
angle = ball->angle+ANG90;
|
|
tiny->target = ball->target;
|
|
tiny->angle = angle;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
tiny->momx = (ball->momx>>1)+FixedMul(ball->momz-FRACUNIT,
|
|
finecosine[angle]);
|
|
tiny->momy = (ball->momy>>1)+FixedMul(ball->momz-FRACUNIT,
|
|
finesine[angle]);
|
|
tiny->momz = ball->momz;
|
|
P_CheckMissileSpawn (tiny);
|
|
|
|
tiny = Spawn<AMaceFX3> (ball->x, ball->y, ball->z, ALLOW_REPLACE);
|
|
angle = ball->angle-ANG90;
|
|
tiny->target = ball->target;
|
|
tiny->angle = angle;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
tiny->momx = (ball->momx>>1)+FixedMul(ball->momz-FRACUNIT,
|
|
finecosine[angle]);
|
|
tiny->momy = (ball->momy>>1)+FixedMul(ball->momz-FRACUNIT,
|
|
finesine[angle]);
|
|
tiny->momz = ball->momz;
|
|
P_CheckMissileSpawn (tiny);
|
|
}
|
|
else
|
|
{ // Explode
|
|
boom:
|
|
ball->momx = ball->momy = ball->momz = 0;
|
|
ball->flags |= MF_NOGRAVITY;
|
|
ball->flags2 &= ~MF2_BOUNCETYPE;
|
|
ball->gravity = FRACUNIT;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireMacePL2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FireMacePL2 (AActor *actor)
|
|
{
|
|
AActor *mo;
|
|
player_t *player;
|
|
AActor *linetarget;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
mo = P_SpawnPlayerMissile (actor, 0,0,0, RUNTIME_CLASS(AMaceFX4), actor->angle, &linetarget);
|
|
if (mo)
|
|
{
|
|
mo->momx += actor->momx;
|
|
mo->momy += actor->momy;
|
|
mo->momz = 2*FRACUNIT+
|
|
clamp<fixed_t>(finetangent[FINEANGLES/4-(actor->pitch>>ANGLETOFINESHIFT)], -5*FRACUNIT, 5*FRACUNIT);
|
|
if (linetarget)
|
|
{
|
|
mo->tracer = linetarget;
|
|
}
|
|
}
|
|
S_Sound (actor, CHAN_WEAPON, "weapons/maceshoot", 1, ATTN_NORM);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_DeathBallImpact
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_DeathBallImpact (AActor *ball)
|
|
{
|
|
int i;
|
|
AActor *target;
|
|
angle_t angle = 0;
|
|
bool newAngle;
|
|
AActor *linetarget;
|
|
|
|
if ((ball->z <= ball->floorz) && P_HitFloor (ball))
|
|
{ // Landed in some sort of liquid
|
|
ball->Destroy ();
|
|
return;
|
|
}
|
|
if (ball->flags & MF_INBOUNCE)
|
|
{
|
|
fixed_t floordist = ball->z - ball->floorz;
|
|
fixed_t ceildist = ball->ceilingz - ball->z;
|
|
fixed_t vel;
|
|
|
|
if (floordist <= ceildist)
|
|
{
|
|
vel = MulScale32 (ball->momz, ball->Sector->floorplane.c);
|
|
}
|
|
else
|
|
{
|
|
vel = MulScale32 (ball->momz, ball->Sector->ceilingplane.c);
|
|
}
|
|
if (vel < 2)
|
|
{
|
|
goto boom;
|
|
}
|
|
|
|
// Bounce
|
|
newAngle = false;
|
|
target = ball->tracer;
|
|
if (target)
|
|
{
|
|
if (!(target->flags&MF_SHOOTABLE))
|
|
{ // Target died
|
|
ball->tracer = NULL;
|
|
}
|
|
else
|
|
{ // Seek
|
|
angle = R_PointToAngle2(ball->x, ball->y,
|
|
target->x, target->y);
|
|
newAngle = true;
|
|
}
|
|
}
|
|
else
|
|
{ // Find new target
|
|
angle = 0;
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
P_AimLineAttack (ball, angle, 10*64*FRACUNIT, &linetarget);
|
|
if (linetarget && ball->target != linetarget)
|
|
{
|
|
ball->tracer = linetarget;
|
|
angle = R_PointToAngle2 (ball->x, ball->y,
|
|
linetarget->x, linetarget->y);
|
|
newAngle = true;
|
|
break;
|
|
}
|
|
angle += ANGLE_45/2;
|
|
}
|
|
}
|
|
if (newAngle)
|
|
{
|
|
ball->angle = angle;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
ball->momx = FixedMul (ball->Speed, finecosine[angle]);
|
|
ball->momy = FixedMul (ball->Speed, finesine[angle]);
|
|
}
|
|
ball->SetState (ball->SpawnState);
|
|
S_Sound (ball, CHAN_BODY, "weapons/macestop", 1, ATTN_NORM);
|
|
}
|
|
else
|
|
{ // Explode
|
|
boom:
|
|
ball->momx = ball->momy = ball->momz = 0;
|
|
ball->flags |= MF_NOGRAVITY;
|
|
ball->gravity = FRACUNIT;
|
|
S_Sound (ball, CHAN_BODY, "weapons/maceexplode", 1, ATTN_NORM);
|
|
}
|
|
}
|
|
|
|
// --- Gauntlets ------------------------------------------------------------
|
|
|
|
void A_GauntletAttack (AActor *);
|
|
void A_GauntletSound (AActor *);
|
|
|
|
// Gauntlets ----------------------------------------------------------------
|
|
|
|
class AGauntlets : public AHereticWeapon
|
|
{
|
|
DECLARE_ACTOR (AGauntlets, AHereticWeapon)
|
|
};
|
|
|
|
class AGauntletsPowered : public AGauntlets
|
|
{
|
|
DECLARE_STATELESS_ACTOR (AGauntletsPowered, AGauntlets)
|
|
};
|
|
|
|
FState AGauntlets::States[] =
|
|
{
|
|
#define S_WGNT 0
|
|
S_NORMAL (WGNT, 'A', -1, NULL , NULL),
|
|
|
|
#define S_GAUNTLETREADY (S_WGNT+1)
|
|
S_NORMAL (GAUN, 'A', 1, A_WeaponReady , &States[S_GAUNTLETREADY]),
|
|
|
|
#define S_GAUNTLETDOWN (S_GAUNTLETREADY+1)
|
|
S_NORMAL (GAUN, 'A', 1, A_Lower , &States[S_GAUNTLETDOWN]),
|
|
|
|
#define S_GAUNTLETUP (S_GAUNTLETDOWN+1)
|
|
S_NORMAL (GAUN, 'A', 1, A_Raise , &States[S_GAUNTLETUP]),
|
|
|
|
#define S_GAUNTLETREADY2 (S_GAUNTLETUP+1)
|
|
S_NORMAL (GAUN, 'G', 4, A_WeaponReady , &States[S_GAUNTLETREADY2+1]),
|
|
S_NORMAL (GAUN, 'H', 4, A_WeaponReady , &States[S_GAUNTLETREADY2+2]),
|
|
S_NORMAL (GAUN, 'I', 4, A_WeaponReady , &States[S_GAUNTLETREADY2+0]),
|
|
|
|
#define S_GAUNTLETDOWN2 (S_GAUNTLETREADY2+3)
|
|
S_NORMAL (GAUN, 'G', 1, A_Lower , &States[S_GAUNTLETDOWN2]),
|
|
|
|
#define S_GAUNTLETUP2 (S_GAUNTLETDOWN2+1)
|
|
S_NORMAL (GAUN, 'G', 1, A_Raise , &States[S_GAUNTLETUP2]),
|
|
|
|
#define S_GAUNTLETATK1 (S_GAUNTLETUP2+1)
|
|
S_NORMAL (GAUN, 'B', 4, A_GauntletSound , &States[S_GAUNTLETATK1+1]),
|
|
S_NORMAL (GAUN, 'C', 4, NULL , &States[S_GAUNTLETATK1+2]),
|
|
S_BRIGHT (GAUN, 'D', 4, A_GauntletAttack , &States[S_GAUNTLETATK1+3]),
|
|
S_BRIGHT (GAUN, 'E', 4, A_GauntletAttack , &States[S_GAUNTLETATK1+4]),
|
|
S_BRIGHT (GAUN, 'F', 4, A_GauntletAttack , &States[S_GAUNTLETATK1+5]),
|
|
S_NORMAL (GAUN, 'C', 4, A_ReFire , &States[S_GAUNTLETATK1+6]),
|
|
S_NORMAL (GAUN, 'B', 4, A_Light0 , &States[S_GAUNTLETREADY]),
|
|
|
|
#define S_GAUNTLETATK2 (S_GAUNTLETATK1+7)
|
|
S_NORMAL (GAUN, 'J', 4, A_GauntletSound , &States[S_GAUNTLETATK2+1]),
|
|
S_NORMAL (GAUN, 'K', 4, NULL , &States[S_GAUNTLETATK2+2]),
|
|
S_BRIGHT (GAUN, 'L', 4, A_GauntletAttack , &States[S_GAUNTLETATK2+3]),
|
|
S_BRIGHT (GAUN, 'M', 4, A_GauntletAttack , &States[S_GAUNTLETATK2+4]),
|
|
S_BRIGHT (GAUN, 'N', 4, A_GauntletAttack , &States[S_GAUNTLETATK2+5]),
|
|
S_NORMAL (GAUN, 'K', 4, A_ReFire , &States[S_GAUNTLETATK2+6]),
|
|
S_NORMAL (GAUN, 'J', 4, A_Light0 , &States[S_GAUNTLETREADY2+0])
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AGauntlets, Heretic, 2005, 32)
|
|
PROP_Flags (MF_SPECIAL)
|
|
PROP_Flags5 (MF5_BLOODSPLATTER)
|
|
PROP_SpawnState (S_WGNT)
|
|
|
|
PROP_Weapon_SelectionOrder (2300)
|
|
PROP_Weapon_Flags (WIF_WIMPY_WEAPON|WIF_BOT_MELEE)
|
|
PROP_Weapon_UpState (S_GAUNTLETUP)
|
|
PROP_Weapon_DownState (S_GAUNTLETDOWN)
|
|
PROP_Weapon_ReadyState (S_GAUNTLETREADY)
|
|
PROP_Weapon_AtkState (S_GAUNTLETATK1)
|
|
PROP_Weapon_HoldAtkState (S_GAUNTLETATK1+2)
|
|
PROP_Weapon_Kickback (0)
|
|
PROP_Weapon_YAdjust (15)
|
|
PROP_Weapon_UpSound ("weapons/gauntletsactivate")
|
|
PROP_Weapon_SisterType ("GauntletsPowered")
|
|
PROP_Inventory_PickupMessage("$TXT_WPNGAUNTLETS")
|
|
END_DEFAULTS
|
|
|
|
IMPLEMENT_STATELESS_ACTOR (AGauntletsPowered, Heretic, -1, 0)
|
|
PROP_Weapon_Flags (WIF_WIMPY_WEAPON|WIF_POWERED_UP|WIF_BOT_MELEE)
|
|
PROP_Weapon_UpState (S_GAUNTLETUP2)
|
|
PROP_Weapon_DownState (S_GAUNTLETDOWN2)
|
|
PROP_Weapon_ReadyState (S_GAUNTLETREADY2)
|
|
PROP_Weapon_AtkState (S_GAUNTLETATK2)
|
|
PROP_Weapon_HoldAtkState (S_GAUNTLETATK2+2)
|
|
PROP_Weapon_SisterType ("Gauntlets")
|
|
END_DEFAULTS
|
|
|
|
void A_GauntletSound (AActor *actor)
|
|
{
|
|
// Play the sound for the initial gauntlet attack
|
|
S_Sound (actor, CHAN_WEAPON, "weapons/gauntletsuse", 1, ATTN_NORM);
|
|
}
|
|
|
|
// Gauntlet puff 1 ----------------------------------------------------------
|
|
|
|
class AGauntletPuff1 : public AActor
|
|
{
|
|
DECLARE_ACTOR (AGauntletPuff1, AActor)
|
|
public:
|
|
void BeginPlay ();
|
|
};
|
|
|
|
FState AGauntletPuff1::States[] =
|
|
{
|
|
#define S_GAUNTLETPUFF1 0
|
|
S_BRIGHT (PUF1, 'A', 4, NULL , &States[S_GAUNTLETPUFF1+1]),
|
|
S_BRIGHT (PUF1, 'B', 4, NULL , &States[S_GAUNTLETPUFF1+2]),
|
|
S_BRIGHT (PUF1, 'C', 4, NULL , &States[S_GAUNTLETPUFF1+3]),
|
|
S_BRIGHT (PUF1, 'D', 4, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AGauntletPuff1, Heretic, -1, 0)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY)
|
|
PROP_Flags3 (MF3_PUFFONACTORS)
|
|
PROP_RenderStyle (STYLE_Translucent)
|
|
PROP_Alpha (HR_SHADOW)
|
|
PROP_SpawnState (S_GAUNTLETPUFF1)
|
|
END_DEFAULTS
|
|
|
|
void AGauntletPuff1::BeginPlay ()
|
|
{
|
|
Super::BeginPlay ();
|
|
momz = FRACUNIT * 8 / 10;
|
|
}
|
|
|
|
// Gauntlett puff 2 ---------------------------------------------------------
|
|
|
|
class AGauntletPuff2 : public AGauntletPuff1
|
|
{
|
|
DECLARE_ACTOR (AGauntletPuff2, AGauntletPuff1)
|
|
};
|
|
|
|
FState AGauntletPuff2::States[] =
|
|
{
|
|
#define S_GAUNTLETPUFF2 0
|
|
S_BRIGHT (PUF1, 'E', 4, NULL , &States[S_GAUNTLETPUFF2+1]),
|
|
S_BRIGHT (PUF1, 'F', 4, NULL , &States[S_GAUNTLETPUFF2+2]),
|
|
S_BRIGHT (PUF1, 'G', 4, NULL , &States[S_GAUNTLETPUFF2+3]),
|
|
S_BRIGHT (PUF1, 'H', 4, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AGauntletPuff2, Heretic, -1, 0)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY)
|
|
PROP_RenderStyle (STYLE_Translucent)
|
|
PROP_Alpha (HR_SHADOW)
|
|
|
|
PROP_SpawnState (S_GAUNTLETPUFF2)
|
|
END_DEFAULTS
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC A_GauntletAttack
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void A_GauntletAttack (AActor *actor)
|
|
{
|
|
angle_t angle;
|
|
int damage;
|
|
int slope;
|
|
int randVal;
|
|
fixed_t dist;
|
|
player_t *player;
|
|
const PClass *pufftype;
|
|
AInventory *power;
|
|
AActor *linetarget;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
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 = actor->angle;
|
|
power = actor->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2));
|
|
if (power)
|
|
{
|
|
damage = pr_gatk.HitDice (2);
|
|
dist = 4*MELEERANGE;
|
|
angle += pr_gatk.Random2() << 17;
|
|
pufftype = RUNTIME_CLASS(AGauntletPuff2);
|
|
}
|
|
else
|
|
{
|
|
damage = pr_gatk.HitDice (2);
|
|
dist = MELEERANGE+1;
|
|
angle += pr_gatk.Random2() << 18;
|
|
pufftype = RUNTIME_CLASS(AGauntletPuff1);
|
|
}
|
|
slope = P_AimLineAttack (actor, angle, dist, &linetarget);
|
|
P_LineAttack (actor, angle, dist, slope, damage, NAME_Melee, pufftype);
|
|
if (!linetarget)
|
|
{
|
|
if (pr_gatk() > 64)
|
|
{
|
|
player->extralight = !player->extralight;
|
|
}
|
|
S_Sound (actor, 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)
|
|
{
|
|
P_GiveBody (actor, damage>>1);
|
|
S_Sound (actor, CHAN_AUTO, "weapons/gauntletspowhit", 1, ATTN_NORM);
|
|
}
|
|
else
|
|
{
|
|
S_Sound (actor, CHAN_AUTO, "weapons/gauntletshit", 1, ATTN_NORM);
|
|
}
|
|
// turn to face target
|
|
angle = R_PointToAngle2 (actor->x, actor->y,
|
|
linetarget->x, linetarget->y);
|
|
if (angle-actor->angle > ANG180)
|
|
{
|
|
if ((int)(angle-actor->angle) < -ANG90/20)
|
|
actor->angle = angle+ANG90/21;
|
|
else
|
|
actor->angle -= ANG90/20;
|
|
}
|
|
else
|
|
{
|
|
if (angle-actor->angle > ANG90/20)
|
|
actor->angle = angle-ANG90/21;
|
|
else
|
|
actor->angle += ANG90/20;
|
|
}
|
|
actor->flags |= MF_JUSTATTACKED;
|
|
}
|
|
|
|
// --- Blaster (aka Claw) ---------------------------------------------------
|
|
|
|
void A_FireBlasterPL1 (AActor *);
|
|
void A_FireBlasterPL2 (AActor *);
|
|
void A_SpawnRippers (AActor *);
|
|
|
|
// Blaster ------------------------------------------------------------------
|
|
|
|
class ABlaster : public AHereticWeapon
|
|
{
|
|
DECLARE_ACTOR (ABlaster, AHereticWeapon)
|
|
};
|
|
|
|
class ABlasterPowered : public ABlaster
|
|
{
|
|
DECLARE_STATELESS_ACTOR (ABlasterPowered, ABlaster)
|
|
};
|
|
|
|
FState ABlaster::States[] =
|
|
{
|
|
#define S_BLSR 0
|
|
S_NORMAL (WBLS, 'A', -1, NULL , NULL),
|
|
|
|
#define S_BLASTERREADY (S_BLSR+1)
|
|
S_NORMAL (BLSR, 'A', 1, A_WeaponReady , &States[S_BLASTERREADY]),
|
|
|
|
#define S_BLASTERDOWN (S_BLASTERREADY+1)
|
|
S_NORMAL (BLSR, 'A', 1, A_Lower , &States[S_BLASTERDOWN]),
|
|
|
|
#define S_BLASTERUP (S_BLASTERDOWN+1)
|
|
S_NORMAL (BLSR, 'A', 1, A_Raise , &States[S_BLASTERUP]),
|
|
|
|
#define S_BLASTERATK1 (S_BLASTERUP+1)
|
|
S_NORMAL (BLSR, 'B', 3, NULL , &States[S_BLASTERATK1+1]),
|
|
S_NORMAL (BLSR, 'C', 3, NULL , &States[S_BLASTERATK1+2]),
|
|
S_NORMAL (BLSR, 'D', 2, A_FireBlasterPL1 , &States[S_BLASTERATK1+3]),
|
|
S_NORMAL (BLSR, 'C', 2, NULL , &States[S_BLASTERATK1+4]),
|
|
S_NORMAL (BLSR, 'B', 2, NULL , &States[S_BLASTERATK1+5]),
|
|
S_NORMAL (BLSR, 'A', 0, A_ReFire , &States[S_BLASTERREADY]),
|
|
|
|
#define S_BLASTERATK2 (S_BLASTERATK1+6)
|
|
S_NORMAL (BLSR, 'B', 0, NULL , &States[S_BLASTERATK2+1]),
|
|
S_NORMAL (BLSR, 'C', 0, NULL , &States[S_BLASTERATK2+2]),
|
|
S_NORMAL (BLSR, 'D', 3, A_FireBlasterPL2 , &States[S_BLASTERATK2+3]),
|
|
S_NORMAL (BLSR, 'C', 4, NULL , &States[S_BLASTERATK2+4]),
|
|
S_NORMAL (BLSR, 'B', 4, NULL , &States[S_BLASTERATK2+5]),
|
|
S_NORMAL (BLSR, 'A', 0, A_ReFire , &States[S_BLASTERREADY])
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ABlaster, Heretic, 53, 28)
|
|
PROP_Flags (MF_SPECIAL)
|
|
PROP_Flags5 (MF5_BLOODSPLATTER)
|
|
PROP_SpawnState (S_BLSR)
|
|
|
|
PROP_Weapon_SelectionOrder (500)
|
|
PROP_Weapon_AmmoUse1 (USE_BLSR_AMMO_1)
|
|
PROP_Weapon_AmmoGive1 (30)
|
|
PROP_Weapon_UpState (S_BLASTERUP)
|
|
PROP_Weapon_DownState (S_BLASTERDOWN)
|
|
PROP_Weapon_ReadyState (S_BLASTERREADY)
|
|
PROP_Weapon_AtkState (S_BLASTERATK1)
|
|
PROP_Weapon_HoldAtkState (S_BLASTERATK1+2)
|
|
PROP_Weapon_YAdjust (15)
|
|
PROP_Weapon_MoveCombatDist (27000000)
|
|
PROP_Weapon_AmmoType1 ("BlasterAmmo")
|
|
PROP_Weapon_SisterType ("BlasterPowered")
|
|
PROP_Inventory_PickupMessage("$TXT_WPNBLASTER")
|
|
END_DEFAULTS
|
|
|
|
IMPLEMENT_STATELESS_ACTOR (ABlasterPowered, Heretic, -1, 0)
|
|
PROP_Weapon_Flags (WIF_POWERED_UP)
|
|
PROP_Weapon_AmmoUse1 (USE_BLSR_AMMO_2)
|
|
PROP_Weapon_AmmoGive1 (0)
|
|
PROP_Weapon_AtkState (S_BLASTERATK2)
|
|
PROP_Weapon_HoldAtkState (S_BLASTERATK2+2)
|
|
PROP_Weapon_SisterType ("Blaster")
|
|
PROP_Weapon_ProjectileType ("BlasterFX1")
|
|
END_DEFAULTS
|
|
|
|
// Blaster FX 1 -------------------------------------------------------------
|
|
|
|
class ABlasterFX1 : public AActor
|
|
{
|
|
DECLARE_ACTOR (ABlasterFX1, AActor)
|
|
public:
|
|
void Tick ();
|
|
int DoSpecialDamage (AActor *target, int damage);
|
|
};
|
|
|
|
FState ABlasterFX1::States[] =
|
|
{
|
|
#define S_BLASTERFX1 0
|
|
S_NORMAL (ACLO, 'E', 200, NULL , &States[S_BLASTERFX1+0]),
|
|
|
|
#define S_BLASTERFXI1 (S_BLASTERFX1+1)
|
|
S_BRIGHT (FX18, 'A', 3, A_SpawnRippers , &States[S_BLASTERFXI1+1]),
|
|
S_BRIGHT (FX18, 'B', 3, NULL , &States[S_BLASTERFXI1+2]),
|
|
S_BRIGHT (FX18, 'C', 4, NULL , &States[S_BLASTERFXI1+3]),
|
|
S_BRIGHT (FX18, 'D', 4, NULL , &States[S_BLASTERFXI1+4]),
|
|
S_BRIGHT (FX18, 'E', 4, NULL , &States[S_BLASTERFXI1+5]),
|
|
S_BRIGHT (FX18, 'F', 4, NULL , &States[S_BLASTERFXI1+6]),
|
|
S_BRIGHT (FX18, 'G', 4, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ABlasterFX1, Heretic, -1, 0)
|
|
PROP_RadiusFixed (12)
|
|
PROP_HeightFixed (8)
|
|
PROP_SpeedFixed (184)
|
|
PROP_Damage (2)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY)
|
|
PROP_Flags2 (MF2_NOTELEPORT|MF2_PCROSS|MF2_IMPACT)
|
|
|
|
PROP_SpawnState (S_BLASTERFX1)
|
|
PROP_DeathState (S_BLASTERFXI1)
|
|
|
|
PROP_DeathSound ("weapons/blasterhit")
|
|
END_DEFAULTS
|
|
|
|
int ABlasterFX1::DoSpecialDamage (AActor *target, int damage)
|
|
{
|
|
if (target->IsKindOf (PClass::FindClass ("Ironlich")))
|
|
{ // Less damage to Ironlich bosses
|
|
damage = pr_bfx1() & 1;
|
|
if (!damage)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
return damage;
|
|
}
|
|
|
|
// Blaster smoke ------------------------------------------------------------
|
|
|
|
class ABlasterSmoke : public AActor
|
|
{
|
|
DECLARE_ACTOR (ABlasterSmoke, AActor)
|
|
};
|
|
|
|
FState ABlasterSmoke::States[] =
|
|
{
|
|
#define S_BLASTERSMOKE 0
|
|
S_NORMAL (FX18, 'H', 4, NULL , &States[S_BLASTERSMOKE+1]),
|
|
S_NORMAL (FX18, 'I', 4, NULL , &States[S_BLASTERSMOKE+2]),
|
|
S_NORMAL (FX18, 'J', 4, NULL , &States[S_BLASTERSMOKE+3]),
|
|
S_NORMAL (FX18, 'K', 4, NULL , &States[S_BLASTERSMOKE+4]),
|
|
S_NORMAL (FX18, 'L', 4, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ABlasterSmoke, Heretic, -1, 0)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY)
|
|
PROP_Flags2 (MF2_NOTELEPORT|MF2_CANNOTPUSH)
|
|
PROP_RenderStyle (STYLE_Translucent)
|
|
PROP_Alpha (HR_SHADOW)
|
|
|
|
PROP_SpawnState (S_BLASTERSMOKE)
|
|
END_DEFAULTS
|
|
|
|
// Ripper -------------------------------------------------------------------
|
|
|
|
class ARipper : public AActor
|
|
{
|
|
DECLARE_ACTOR (ARipper, AActor)
|
|
public:
|
|
int DoSpecialDamage (AActor *target, int damage);
|
|
};
|
|
|
|
FState ARipper::States[] =
|
|
{
|
|
#define S_RIPPER 0
|
|
S_NORMAL (FX18, 'M', 4, NULL , &States[S_RIPPER+1]),
|
|
S_NORMAL (FX18, 'N', 5, NULL , &States[S_RIPPER+0]),
|
|
|
|
#define S_RIPPERX (S_RIPPER+2)
|
|
S_BRIGHT (FX18, 'O', 4, NULL , &States[S_RIPPERX+1]),
|
|
S_BRIGHT (FX18, 'P', 4, NULL , &States[S_RIPPERX+2]),
|
|
S_BRIGHT (FX18, 'Q', 4, NULL , &States[S_RIPPERX+3]),
|
|
S_BRIGHT (FX18, 'R', 4, NULL , &States[S_RIPPERX+4]),
|
|
S_BRIGHT (FX18, 'S', 4, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ARipper, Heretic, -1, 157)
|
|
PROP_RadiusFixed (8)
|
|
PROP_HeightFixed (6)
|
|
PROP_SpeedFixed (14)
|
|
PROP_Damage (1)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY)
|
|
PROP_Flags2 (MF2_NOTELEPORT|MF2_RIP|MF2_PCROSS|MF2_IMPACT)
|
|
PROP_Flags3 (MF3_WARNBOT)
|
|
|
|
PROP_SpawnState (S_RIPPER)
|
|
PROP_DeathState (S_RIPPERX)
|
|
|
|
PROP_DeathSound ("weapons/blasterpowhit")
|
|
END_DEFAULTS
|
|
|
|
int ARipper::DoSpecialDamage (AActor *target, int damage)
|
|
{
|
|
if (target->IsKindOf (PClass::FindClass ("Ironlich")))
|
|
{ // Less damage to Ironlich bosses
|
|
damage = pr_ripd() & 1;
|
|
if (!damage)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
return damage;
|
|
}
|
|
|
|
// Blaster Puff -------------------------------------------------------------
|
|
|
|
class ABlasterPuff : public AActor
|
|
{
|
|
DECLARE_ACTOR (ABlasterPuff, AActor)
|
|
};
|
|
|
|
FState ABlasterPuff::States[] =
|
|
{
|
|
#define S_BLASTERPUFF1 0
|
|
S_BRIGHT (FX17, 'A', 4, NULL , &States[S_BLASTERPUFF1+1]),
|
|
S_BRIGHT (FX17, 'B', 4, NULL , &States[S_BLASTERPUFF1+2]),
|
|
S_BRIGHT (FX17, 'C', 4, NULL , &States[S_BLASTERPUFF1+3]),
|
|
S_BRIGHT (FX17, 'D', 4, NULL , &States[S_BLASTERPUFF1+4]),
|
|
S_BRIGHT (FX17, 'E', 4, NULL , NULL),
|
|
|
|
#define S_BLASTERPUFF2 (S_BLASTERPUFF1+5)
|
|
S_BRIGHT (FX17, 'F', 3, NULL , &States[S_BLASTERPUFF2+1]),
|
|
S_BRIGHT (FX17, 'G', 3, NULL , &States[S_BLASTERPUFF2+2]),
|
|
S_BRIGHT (FX17, 'H', 4, NULL , &States[S_BLASTERPUFF2+3]),
|
|
S_BRIGHT (FX17, 'I', 4, NULL , &States[S_BLASTERPUFF2+4]),
|
|
S_BRIGHT (FX17, 'J', 4, NULL , &States[S_BLASTERPUFF2+5]),
|
|
S_BRIGHT (FX17, 'K', 4, NULL , &States[S_BLASTERPUFF2+6]),
|
|
S_BRIGHT (FX17, 'L', 4, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ABlasterPuff, Heretic, -1, 0)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY)
|
|
PROP_Flags3 (MF3_PUFFONACTORS)
|
|
PROP_RenderStyle (STYLE_Add)
|
|
|
|
PROP_SpawnState (S_BLASTERPUFF2)
|
|
PROP_CrashState (S_BLASTERPUFF1)
|
|
END_DEFAULTS
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireBlasterPL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FireBlasterPL1 (AActor *actor)
|
|
{
|
|
angle_t angle;
|
|
int damage;
|
|
player_t *player;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = actor->player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
angle_t pitch = P_BulletSlope(actor);
|
|
damage = pr_fb1.HitDice (4);
|
|
angle = actor->angle;
|
|
if (player->refire)
|
|
{
|
|
angle += pr_fb1.Random2() << 18;
|
|
}
|
|
P_LineAttack (actor, angle, PLAYERMISSILERANGE, pitch, damage, NAME_None, RUNTIME_CLASS(ABlasterPuff));
|
|
S_Sound (actor, CHAN_WEAPON, "weapons/blastershoot", 1, ATTN_NORM);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireBlasterPL2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FireBlasterPL2 (AActor *actor)
|
|
{
|
|
player_t *player;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = actor->player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ABlasterFX1));
|
|
S_Sound (actor, CHAN_WEAPON, "weapons/blastershoot", 1, ATTN_NORM);
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_SpawnRippers
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_SpawnRippers (AActor *actor)
|
|
{
|
|
int i;
|
|
angle_t angle;
|
|
AActor *ripper;
|
|
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
ripper = Spawn<ARipper> (actor->x, actor->y, actor->z, ALLOW_REPLACE);
|
|
angle = i*ANG45;
|
|
ripper->target = actor->target;
|
|
ripper->angle = angle;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
ripper->momx = FixedMul (ripper->Speed, finecosine[angle]);
|
|
ripper->momy = FixedMul (ripper->Speed, finesine[angle]);
|
|
P_CheckMissileSpawn (ripper);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC P_BlasterMobjThinker
|
|
//
|
|
// Thinker for the ultra-fast blaster PL2 ripper-spawning missile.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void ABlasterFX1::Tick ()
|
|
{
|
|
int i;
|
|
fixed_t xfrac;
|
|
fixed_t yfrac;
|
|
fixed_t zfrac;
|
|
int changexy;
|
|
|
|
PrevX = x;
|
|
PrevY = y;
|
|
PrevZ = z;
|
|
|
|
// Handle movement
|
|
if (momx || momy || (z != floorz) || momz)
|
|
{
|
|
xfrac = momx>>3;
|
|
yfrac = momy>>3;
|
|
zfrac = momz>>3;
|
|
changexy = xfrac | yfrac;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
if (changexy)
|
|
{
|
|
if (!P_TryMove (this, x + xfrac, y + yfrac, true))
|
|
{ // Blocked move
|
|
P_ExplodeMissile (this, BlockingLine, BlockingMobj);
|
|
return;
|
|
}
|
|
}
|
|
z += zfrac;
|
|
if (z <= floorz)
|
|
{ // Hit the floor
|
|
z = floorz;
|
|
P_HitFloor (this);
|
|
P_ExplodeMissile (this, NULL, NULL);
|
|
return;
|
|
}
|
|
if (z + height > ceilingz)
|
|
{ // Hit the ceiling
|
|
z = ceilingz - height;
|
|
P_ExplodeMissile (this, NULL, NULL);
|
|
return;
|
|
}
|
|
if (changexy && (pr_bfx1t() < 64))
|
|
{
|
|
Spawn<ABlasterSmoke> (x, y, MAX<fixed_t> (z - 8 * FRACUNIT, floorz), ALLOW_REPLACE);
|
|
}
|
|
}
|
|
}
|
|
// Advance the state
|
|
if (tics != -1)
|
|
{
|
|
tics--;
|
|
while (!tics)
|
|
{
|
|
if (!SetState (state->GetNextState ()))
|
|
{ // mobj was removed
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Skull rod ------------------------------------------------------------
|
|
|
|
void A_FireSkullRodPL1 (AActor *);
|
|
void A_FireSkullRodPL2 (AActor *);
|
|
void A_SkullRodPL2Seek (AActor *);
|
|
void A_AddPlayerRain (AActor *);
|
|
void A_HideInCeiling (AActor *);
|
|
void A_SkullRodStorm (AActor *);
|
|
void A_RainImpact (AActor *);
|
|
|
|
// Skull (Horn) Rod ---------------------------------------------------------
|
|
|
|
class ASkullRod : public AHereticWeapon
|
|
{
|
|
DECLARE_ACTOR (ASkullRod, AHereticWeapon)
|
|
};
|
|
|
|
class ASkullRodPowered : public ASkullRod
|
|
{
|
|
DECLARE_STATELESS_ACTOR (ASkullRodPowered, ASkullRod)
|
|
};
|
|
|
|
FState ASkullRod::States[] =
|
|
{
|
|
#define S_WSKL 0
|
|
S_NORMAL (WSKL, 'A', -1, NULL , NULL),
|
|
|
|
#define S_HORNRODREADY (S_WSKL+1)
|
|
S_NORMAL (HROD, 'A', 1, A_WeaponReady , &States[S_HORNRODREADY]),
|
|
|
|
#define S_HORNRODDOWN (S_HORNRODREADY+1)
|
|
S_NORMAL (HROD, 'A', 1, A_Lower , &States[S_HORNRODDOWN]),
|
|
|
|
#define S_HORNRODUP (S_HORNRODDOWN+1)
|
|
S_NORMAL (HROD, 'A', 1, A_Raise , &States[S_HORNRODUP]),
|
|
|
|
#define S_HORNRODATK1 (S_HORNRODUP+1)
|
|
S_NORMAL (HROD, 'A', 4, A_FireSkullRodPL1 , &States[S_HORNRODATK1+1]),
|
|
S_NORMAL (HROD, 'B', 4, A_FireSkullRodPL1 , &States[S_HORNRODATK1+2]),
|
|
S_NORMAL (HROD, 'B', 0, A_ReFire , &States[S_HORNRODREADY]),
|
|
|
|
#define S_HORNRODATK2 (S_HORNRODATK1+3)
|
|
S_NORMAL (HROD, 'C', 2, NULL , &States[S_HORNRODATK2+1]),
|
|
S_NORMAL (HROD, 'D', 3, NULL , &States[S_HORNRODATK2+2]),
|
|
S_NORMAL (HROD, 'E', 2, NULL , &States[S_HORNRODATK2+3]),
|
|
S_NORMAL (HROD, 'F', 3, NULL , &States[S_HORNRODATK2+4]),
|
|
S_NORMAL (HROD, 'G', 4, A_FireSkullRodPL2 , &States[S_HORNRODATK2+5]),
|
|
S_NORMAL (HROD, 'F', 2, NULL , &States[S_HORNRODATK2+6]),
|
|
S_NORMAL (HROD, 'E', 3, NULL , &States[S_HORNRODATK2+7]),
|
|
S_NORMAL (HROD, 'D', 2, NULL , &States[S_HORNRODATK2+8]),
|
|
S_NORMAL (HROD, 'C', 2, A_ReFire , &States[S_HORNRODREADY])
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ASkullRod, Heretic, 2004, 30)
|
|
PROP_Flags (MF_SPECIAL)
|
|
PROP_SpawnState (S_WSKL)
|
|
|
|
PROP_Weapon_SelectionOrder (200)
|
|
PROP_Weapon_AmmoUse1 (USE_SKRD_AMMO_1)
|
|
PROP_Weapon_AmmoGive1 (50)
|
|
PROP_Weapon_UpState (S_HORNRODUP)
|
|
PROP_Weapon_DownState (S_HORNRODDOWN)
|
|
PROP_Weapon_ReadyState (S_HORNRODREADY)
|
|
PROP_Weapon_AtkState (S_HORNRODATK1)
|
|
PROP_Weapon_YAdjust (15)
|
|
PROP_Weapon_MoveCombatDist (27000000)
|
|
PROP_Weapon_AmmoType1 ("SkullRodAmmo")
|
|
PROP_Weapon_SisterType ("SkullRodPowered")
|
|
PROP_Weapon_ProjectileType ("HornRodFX1")
|
|
PROP_Inventory_PickupMessage("$TXT_WPNSKULLROD")
|
|
END_DEFAULTS
|
|
|
|
IMPLEMENT_STATELESS_ACTOR (ASkullRodPowered, Heretic, -1, 0)
|
|
PROP_Weapon_Flags (WIF_POWERED_UP)
|
|
PROP_Weapon_AmmoUse1 (USE_SKRD_AMMO_2)
|
|
PROP_Weapon_AmmoGive1 (0)
|
|
PROP_Weapon_AtkState (S_HORNRODATK2)
|
|
PROP_Weapon_SisterType ("SkullRod")
|
|
PROP_Weapon_ProjectileType ("HornRodFX2")
|
|
END_DEFAULTS
|
|
|
|
// Horn Rod FX 1 ------------------------------------------------------------
|
|
|
|
class AHornRodFX1 : public AActor
|
|
{
|
|
DECLARE_ACTOR (AHornRodFX1, AActor)
|
|
};
|
|
|
|
FState AHornRodFX1::States[] =
|
|
{
|
|
#define S_HRODFX1 0
|
|
S_BRIGHT (FX00, 'A', 6, NULL , &States[S_HRODFX1+1]),
|
|
S_BRIGHT (FX00, 'B', 6, NULL , &States[S_HRODFX1+0]),
|
|
|
|
#define S_HRODFXI1 (S_HRODFX1+2)
|
|
S_BRIGHT (FX00, 'H', 5, NULL , &States[S_HRODFXI1+1]),
|
|
S_BRIGHT (FX00, 'I', 5, NULL , &States[S_HRODFXI1+2]),
|
|
S_BRIGHT (FX00, 'J', 4, NULL , &States[S_HRODFXI1+3]),
|
|
S_BRIGHT (FX00, 'K', 4, NULL , &States[S_HRODFXI1+4]),
|
|
S_BRIGHT (FX00, 'L', 3, NULL , &States[S_HRODFXI1+5]),
|
|
S_BRIGHT (FX00, 'M', 3, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AHornRodFX1, Heretic, -1, 160)
|
|
PROP_RadiusFixed (12)
|
|
PROP_HeightFixed (8)
|
|
PROP_SpeedFixed (22)
|
|
PROP_Damage (3)
|
|
PROP_Flags (MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY)
|
|
PROP_Flags2 (MF2_WINDTHRUST|MF2_NOTELEPORT|MF2_PCROSS|MF2_IMPACT)
|
|
PROP_Flags3 (MF3_WARNBOT)
|
|
PROP_RenderStyle (STYLE_Add)
|
|
|
|
PROP_SpawnState (S_HRODFX1)
|
|
PROP_DeathState (S_HRODFXI1)
|
|
|
|
PROP_SeeSound ("weapons/hornrodshoot")
|
|
PROP_DeathSound ("weapons/hornrodhit")
|
|
END_DEFAULTS
|
|
|
|
// Horn Rod FX 2 ------------------------------------------------------------
|
|
|
|
class AHornRodFX2 : public AActor
|
|
{
|
|
DECLARE_ACTOR (AHornRodFX2, AActor)
|
|
public:
|
|
int DoSpecialDamage (AActor *target, int damage);
|
|
};
|
|
|
|
FState AHornRodFX2::States[] =
|
|
{
|
|
#define S_HRODFX2 0
|
|
S_BRIGHT (FX00, 'C', 3, NULL , &States[S_HRODFX2+1]),
|
|
S_BRIGHT (FX00, 'D', 3, A_SkullRodPL2Seek , &States[S_HRODFX2+2]),
|
|
S_BRIGHT (FX00, 'E', 3, NULL , &States[S_HRODFX2+3]),
|
|
S_BRIGHT (FX00, 'F', 3, A_SkullRodPL2Seek , &States[S_HRODFX2+0]),
|
|
|
|
#define S_HRODFXI2 (S_HRODFX2+4)
|
|
S_BRIGHT (FX00, 'H', 5, A_AddPlayerRain , &States[S_HRODFXI2+1]),
|
|
S_BRIGHT (FX00, 'I', 5, NULL , &States[S_HRODFXI2+2]),
|
|
S_BRIGHT (FX00, 'J', 4, NULL , &States[S_HRODFXI2+3]),
|
|
S_BRIGHT (FX00, 'K', 3, NULL , &States[S_HRODFXI2+4]),
|
|
S_BRIGHT (FX00, 'L', 3, NULL , &States[S_HRODFXI2+5]),
|
|
S_BRIGHT (FX00, 'M', 3, NULL , &States[S_HRODFXI2+6]),
|
|
S_NORMAL (FX00, 'G', 1, A_HideInCeiling , &States[S_HRODFXI2+7]),
|
|
S_NORMAL (FX00, 'G', 1, A_SkullRodStorm , &States[S_HRODFXI2+7])
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AHornRodFX2, Heretic, -1, 0)
|
|
PROP_RadiusFixed (12)
|
|
PROP_HeightFixed (8)
|
|
PROP_SpeedFixed (22)
|
|
PROP_Damage (10)
|
|
PROP_SpawnHealth (4*35)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY)
|
|
PROP_Flags2 (MF2_NOTELEPORT|MF2_PCROSS|MF2_IMPACT)
|
|
PROP_RenderStyle (STYLE_Add)
|
|
|
|
PROP_SpawnState (S_HRODFX2)
|
|
PROP_DeathState (S_HRODFXI2)
|
|
|
|
PROP_SeeSound ("weapons/hornrodshoot")
|
|
PROP_DeathSound ("weapons/hornrodpowhit")
|
|
END_DEFAULTS
|
|
|
|
int AHornRodFX2::DoSpecialDamage (AActor *target, int damage)
|
|
{
|
|
if (target->IsKindOf (RUNTIME_CLASS (ASorcerer2)) && pr_hrfx2() < 96)
|
|
{ // D'Sparil teleports away
|
|
P_DSparilTeleport (target);
|
|
return -1;
|
|
}
|
|
return damage;
|
|
}
|
|
|
|
// Rain pillar 1 ------------------------------------------------------------
|
|
|
|
class ARainPillar : public AActor
|
|
{
|
|
DECLARE_ACTOR (ARainPillar, AActor)
|
|
public:
|
|
int DoSpecialDamage (AActor *target, int damage);
|
|
};
|
|
|
|
FState ARainPillar::States[] =
|
|
{
|
|
#define S_RAINPLR 0
|
|
S_BRIGHT (FX22, 'A', -1, NULL , NULL),
|
|
|
|
#define S_RAINPLRX (S_RAINPLR+1)
|
|
S_BRIGHT (FX22, 'B', 4, A_RainImpact , &States[S_RAINPLRX+1]),
|
|
S_BRIGHT (FX22, 'C', 4, NULL , &States[S_RAINPLRX+2]),
|
|
S_BRIGHT (FX22, 'D', 4, NULL , &States[S_RAINPLRX+3]),
|
|
S_BRIGHT (FX22, 'E', 4, NULL , &States[S_RAINPLRX+4]),
|
|
S_BRIGHT (FX22, 'F', 4, NULL , NULL),
|
|
|
|
#define S_RAINAIRXPLR (S_RAINPLRX+5)
|
|
S_BRIGHT (FX22, 'G', 4, NULL , &States[S_RAINAIRXPLR+1]),
|
|
S_BRIGHT (FX22, 'H', 4, NULL , &States[S_RAINAIRXPLR+2]),
|
|
S_BRIGHT (FX22, 'I', 4, NULL , NULL),
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (ARainPillar, Heretic, -1, 0)
|
|
PROP_RadiusFixed (5)
|
|
PROP_HeightFixed (12)
|
|
PROP_SpeedFixed (12)
|
|
PROP_Damage (5)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY)
|
|
PROP_Flags2 (MF2_NOTELEPORT)
|
|
PROP_RenderStyle (STYLE_Add)
|
|
|
|
PROP_SpawnState (S_RAINPLR)
|
|
PROP_DeathState (S_RAINPLRX)
|
|
END_DEFAULTS
|
|
|
|
int ARainPillar::DoSpecialDamage (AActor *target, int damage)
|
|
{
|
|
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_STATELESS_ACTOR (ARainTracker, AInventory)
|
|
public:
|
|
void Serialize (FArchive &arc);
|
|
AActor *Rain1, *Rain2;
|
|
};
|
|
|
|
IMPLEMENT_STATELESS_ACTOR (ARainTracker, Any, -1, 0)
|
|
PROP_Inventory_FlagsSet (IF_UNDROPPABLE)
|
|
END_DEFAULTS
|
|
|
|
void ARainTracker::Serialize (FArchive &arc)
|
|
{
|
|
Super::Serialize (arc);
|
|
arc << Rain1 << Rain2;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireSkullRodPL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FireSkullRodPL1 (AActor *actor)
|
|
{
|
|
AActor *mo;
|
|
player_t *player;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
mo = P_SpawnPlayerMissile (actor, RUNTIME_CLASS(AHornRodFX1));
|
|
// 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.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FireSkullRodPL2 (AActor *actor)
|
|
{
|
|
player_t *player;
|
|
AActor *MissileActor;
|
|
AActor *linetarget;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
P_SpawnPlayerMissile (actor, 0,0,0, RUNTIME_CLASS(AHornRodFX2), actor->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_SkullRodPL2Seek
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_SkullRodPL2Seek (AActor *actor)
|
|
{
|
|
P_SeekerMissile (actor, ANGLE_1*10, ANGLE_1*30);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_AddPlayerRain
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_AddPlayerRain (AActor *actor)
|
|
{
|
|
ARainTracker *tracker;
|
|
|
|
if (actor->target == NULL || actor->target->health <= 0)
|
|
{ // Shooter is dead or nonexistant
|
|
return;
|
|
}
|
|
|
|
tracker = actor->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 *> (actor->target->GiveInventoryType (RUNTIME_CLASS(ARainTracker)));
|
|
}
|
|
// Add rain mobj to list
|
|
if (tracker->Rain1)
|
|
{
|
|
tracker->Rain2 = actor;
|
|
}
|
|
else
|
|
{
|
|
tracker->Rain1 = actor;
|
|
}
|
|
actor->special1 = S_FindSound ("misc/rain");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_SkullRodStorm
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_SkullRodStorm (AActor *actor)
|
|
{
|
|
fixed_t x;
|
|
fixed_t y;
|
|
AActor *mo;
|
|
ARainTracker *tracker;
|
|
|
|
if (actor->health-- == 0)
|
|
{
|
|
S_StopSound (actor, CHAN_BODY);
|
|
if (actor->target == NULL)
|
|
{ // Player left the game
|
|
actor->Destroy ();
|
|
return;
|
|
}
|
|
tracker = actor->target->FindInventory<ARainTracker> ();
|
|
if (tracker != NULL)
|
|
{
|
|
if (tracker->Rain1 == actor)
|
|
{
|
|
tracker->Rain1 = NULL;
|
|
}
|
|
else if (tracker->Rain2 == actor)
|
|
{
|
|
tracker->Rain2 = NULL;
|
|
}
|
|
}
|
|
actor->Destroy ();
|
|
return;
|
|
}
|
|
if (pr_storm() < 25)
|
|
{ // Fudge rain frequency
|
|
return;
|
|
}
|
|
x = actor->x + ((pr_storm()&127) - 64) * FRACUNIT;
|
|
y = actor->y + ((pr_storm()&127) - 64) * FRACUNIT;
|
|
mo = Spawn<ARainPillar> (x, y, ONCEILINGZ, ALLOW_REPLACE);
|
|
mo->Translation = multiplayer ?
|
|
TRANSLATION(TRANSLATION_PlayersExtra,actor->special2) : 0;
|
|
mo->target = actor->target;
|
|
mo->momx = 1; // Force collision detection
|
|
mo->momz = -mo->Speed;
|
|
mo->special2 = actor->special2; // Transfer player number
|
|
P_CheckMissileSpawn (mo);
|
|
if (actor->special1 != -1 && !S_IsActorPlayingSomething (actor, CHAN_BODY, -1))
|
|
{
|
|
S_Sound (actor, CHAN_BODY|CHAN_LOOP, actor->special1, 1, ATTN_NORM);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_RainImpact
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_RainImpact (AActor *actor)
|
|
{
|
|
if (actor->z > actor->floorz)
|
|
{
|
|
actor->SetState (&ARainPillar::States[S_RAINAIRXPLR]);
|
|
}
|
|
else if (pr_impact() < 40)
|
|
{
|
|
P_HitFloor (actor);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_HideInCeiling
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_HideInCeiling (AActor *actor)
|
|
{
|
|
actor->z = actor->ceilingz + 4*FRACUNIT;
|
|
}
|
|
|
|
// --- Phoenix Rod ----------------------------------------------------------
|
|
|
|
void A_FirePhoenixPL1 (AActor *);
|
|
void A_InitPhoenixPL2 (AActor *);
|
|
void A_FirePhoenixPL2 (AActor *);
|
|
void A_ShutdownPhoenixPL2 (AActor *);
|
|
void A_PhoenixPuff (AActor *);
|
|
void A_FlameEnd (AActor *);
|
|
void A_FloatPuff (AActor *);
|
|
|
|
// Phoenix Rod --------------------------------------------------------------
|
|
|
|
class APhoenixRod : public AHereticWeapon
|
|
{
|
|
DECLARE_ACTOR (APhoenixRod, AHereticWeapon)
|
|
public:
|
|
void Serialize (FArchive &arc)
|
|
{
|
|
Super::Serialize (arc);
|
|
arc << FlameCount;
|
|
}
|
|
int FlameCount; // for flamethrower duration
|
|
};
|
|
|
|
class APhoenixRodPowered : public APhoenixRod
|
|
{
|
|
DECLARE_STATELESS_ACTOR (APhoenixRodPowered, APhoenixRod)
|
|
public:
|
|
void EndPowerup ();
|
|
};
|
|
|
|
FState APhoenixRod::States[] =
|
|
{
|
|
#define S_WPHX 0
|
|
S_NORMAL (WPHX, 'A', -1, NULL , NULL),
|
|
|
|
#define S_PHOENIXREADY (S_WPHX+1)
|
|
S_NORMAL (PHNX, 'A', 1, A_WeaponReady , &States[S_PHOENIXREADY]),
|
|
|
|
#define S_PHOENIXDOWN (S_PHOENIXREADY+1)
|
|
S_NORMAL (PHNX, 'A', 1, A_Lower , &States[S_PHOENIXDOWN]),
|
|
|
|
#define S_PHOENIXUP (S_PHOENIXDOWN+1)
|
|
S_NORMAL (PHNX, 'A', 1, A_Raise , &States[S_PHOENIXUP]),
|
|
|
|
#define S_PHOENIXATK1 (S_PHOENIXUP+1)
|
|
S_NORMAL (PHNX, 'B', 5, NULL , &States[S_PHOENIXATK1+1]),
|
|
S_NORMAL (PHNX, 'C', 7, A_FirePhoenixPL1 , &States[S_PHOENIXATK1+2]),
|
|
S_NORMAL (PHNX, 'D', 4, NULL , &States[S_PHOENIXATK1+3]),
|
|
S_NORMAL (PHNX, 'B', 4, NULL , &States[S_PHOENIXATK1+4]),
|
|
S_NORMAL (PHNX, 'B', 0, A_ReFire , &States[S_PHOENIXREADY]),
|
|
|
|
#define S_PHOENIXATK2 (S_PHOENIXATK1+5)
|
|
S_NORMAL (PHNX, 'B', 3, A_InitPhoenixPL2 , &States[S_PHOENIXATK2+1]),
|
|
S_BRIGHT (PHNX, 'C', 1, A_FirePhoenixPL2 , &States[S_PHOENIXATK2+2]),
|
|
S_NORMAL (PHNX, 'B', 4, A_ReFire , &States[S_PHOENIXATK2+3]),
|
|
S_NORMAL (PHNX, 'B', 4, A_ShutdownPhoenixPL2 , &States[S_PHOENIXREADY])
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (APhoenixRod, Heretic, 2003, 29)
|
|
PROP_Flags (MF_SPECIAL)
|
|
PROP_SpawnState (S_WPHX)
|
|
|
|
PROP_Weapon_Flags (WIF_NOAUTOFIRE|WIF_BOT_REACTION_SKILL_THING)
|
|
PROP_Weapon_SelectionOrder (2600)
|
|
PROP_Weapon_AmmoUse1 (USE_PHRD_AMMO_1)
|
|
PROP_Weapon_AmmoGive1 (2)
|
|
PROP_Weapon_UpState (S_PHOENIXUP)
|
|
PROP_Weapon_DownState (S_PHOENIXDOWN)
|
|
PROP_Weapon_ReadyState (S_PHOENIXREADY)
|
|
PROP_Weapon_AtkState (S_PHOENIXATK1)
|
|
PROP_Weapon_YAdjust (15)
|
|
PROP_Weapon_MoveCombatDist (18350080)
|
|
PROP_Weapon_AmmoType1 ("PhoenixRodAmmo")
|
|
PROP_Weapon_SisterType ("PhoenixRodPowered")
|
|
PROP_Weapon_ProjectileType ("PhoenixFX1")
|
|
PROP_Inventory_PickupMessage("$TXT_WPNPHOENIXROD")
|
|
END_DEFAULTS
|
|
|
|
IMPLEMENT_STATELESS_ACTOR (APhoenixRodPowered, Heretic, -1, 0)
|
|
PROP_Weapon_Flags (WIF_NOAUTOFIRE|WIF_POWERED_UP|WIF_BOT_MELEE)
|
|
PROP_Weapon_AmmoUse1 (USE_PHRD_AMMO_2)
|
|
PROP_Weapon_AmmoGive1 (0)
|
|
PROP_Weapon_AtkState (S_PHOENIXATK2)
|
|
PROP_Weapon_HoldAtkState (S_PHOENIXATK2+1)
|
|
PROP_Weapon_MoveCombatDist (0)
|
|
PROP_Weapon_SisterType ("PhoenixRod")
|
|
PROP_Weapon_ProjectileType ("PhoenixFX2")
|
|
END_DEFAULTS
|
|
|
|
void APhoenixRodPowered::EndPowerup ()
|
|
{
|
|
P_SetPsprite (Owner->player, ps_weapon, &APhoenixRod::States[S_PHOENIXREADY]);
|
|
DepleteAmmo (bAltFire);
|
|
Owner->player->refire = 0;
|
|
S_StopSound (Owner, CHAN_WEAPON);
|
|
Owner->player->ReadyWeapon = SisterWeapon;
|
|
}
|
|
|
|
// Phoenix FX 1 -------------------------------------------------------------
|
|
|
|
FState APhoenixFX1::States[] =
|
|
{
|
|
#define S_PHOENIXFX1 0
|
|
S_BRIGHT (FX04, 'A', 4, A_PhoenixPuff , &States[S_PHOENIXFX1+0]),
|
|
|
|
#define S_PHOENIXFXI1 (S_PHOENIXFX1+1)
|
|
S_BRIGHT (FX08, 'A', 6, A_Explode , &States[S_PHOENIXFXI1+1]),
|
|
S_BRIGHT (FX08, 'B', 5, NULL , &States[S_PHOENIXFXI1+2]),
|
|
S_BRIGHT (FX08, 'C', 5, NULL , &States[S_PHOENIXFXI1+3]),
|
|
S_BRIGHT (FX08, 'D', 4, NULL , &States[S_PHOENIXFXI1+4]),
|
|
S_BRIGHT (FX08, 'E', 4, NULL , &States[S_PHOENIXFXI1+5]),
|
|
S_BRIGHT (FX08, 'F', 4, NULL , &States[S_PHOENIXFXI1+6]),
|
|
S_BRIGHT (FX08, 'G', 4, NULL , &States[S_PHOENIXFXI1+7]),
|
|
S_BRIGHT (FX08, 'H', 4, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (APhoenixFX1, Heretic, -1, 163)
|
|
PROP_RadiusFixed (11)
|
|
PROP_HeightFixed (8)
|
|
PROP_SpeedFixed (20)
|
|
PROP_Damage (20)
|
|
PROP_DamageType (NAME_Fire)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY)
|
|
PROP_Flags2 (MF2_THRUGHOST|MF2_NOTELEPORT|MF2_PCROSS|MF2_IMPACT)
|
|
PROP_RenderStyle (STYLE_Add)
|
|
|
|
PROP_SpawnState (S_PHOENIXFX1)
|
|
PROP_DeathState (S_PHOENIXFXI1)
|
|
|
|
PROP_SeeSound ("weapons/phoenixshoot")
|
|
PROP_DeathSound ("weapons/phoenixhit")
|
|
END_DEFAULTS
|
|
|
|
int APhoenixFX1::DoSpecialDamage (AActor *target, int damage)
|
|
{
|
|
if (target->IsKindOf (RUNTIME_CLASS (ASorcerer2)) && pr_pfx1() < 96)
|
|
{ // D'Sparil teleports away
|
|
P_DSparilTeleport (target);
|
|
return -1;
|
|
}
|
|
return damage;
|
|
}
|
|
|
|
// Phoenix puff -------------------------------------------------------------
|
|
|
|
FState APhoenixPuff::States[] =
|
|
{
|
|
S_NORMAL (FX04, 'B', 4, NULL , &States[1]),
|
|
S_NORMAL (FX04, 'C', 4, NULL , &States[2]),
|
|
S_NORMAL (FX04, 'D', 4, NULL , &States[3]),
|
|
S_NORMAL (FX04, 'E', 4, NULL , &States[4]),
|
|
S_NORMAL (FX04, 'F', 4, NULL , NULL),
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (APhoenixPuff, Heretic, -1, 0)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY)
|
|
PROP_Flags2 (MF2_NOTELEPORT|MF2_CANNOTPUSH)
|
|
PROP_RenderStyle (STYLE_Translucent)
|
|
PROP_Alpha (HR_SHADOW)
|
|
|
|
PROP_SpawnState (0)
|
|
END_DEFAULTS
|
|
|
|
// Phoenix FX 2 -------------------------------------------------------------
|
|
|
|
class APhoenixFX2 : public AActor
|
|
{
|
|
DECLARE_ACTOR (APhoenixFX2, AActor)
|
|
public:
|
|
int DoSpecialDamage (AActor *target, int damage);
|
|
};
|
|
|
|
FState APhoenixFX2::States[] =
|
|
{
|
|
#define S_PHOENIXFX2 0
|
|
S_BRIGHT (FX09, 'A', 2, NULL , &States[S_PHOENIXFX2+1]),
|
|
S_BRIGHT (FX09, 'B', 2, NULL , &States[S_PHOENIXFX2+2]),
|
|
S_BRIGHT (FX09, 'A', 2, NULL , &States[S_PHOENIXFX2+3]),
|
|
S_BRIGHT (FX09, 'B', 2, NULL , &States[S_PHOENIXFX2+4]),
|
|
S_BRIGHT (FX09, 'A', 2, NULL , &States[S_PHOENIXFX2+5]),
|
|
S_BRIGHT (FX09, 'B', 2, A_FlameEnd , &States[S_PHOENIXFX2+6]),
|
|
S_BRIGHT (FX09, 'C', 2, NULL , &States[S_PHOENIXFX2+7]),
|
|
S_BRIGHT (FX09, 'D', 2, NULL , &States[S_PHOENIXFX2+8]),
|
|
S_BRIGHT (FX09, 'E', 2, NULL , &States[S_PHOENIXFX2+9]),
|
|
S_BRIGHT (FX09, 'F', 2, NULL , NULL),
|
|
|
|
#define S_PHOENIXFXI2 (S_PHOENIXFX2+10)
|
|
S_BRIGHT (FX09, 'G', 3, NULL , &States[S_PHOENIXFXI2+1]),
|
|
S_BRIGHT (FX09, 'H', 3, A_FloatPuff , &States[S_PHOENIXFXI2+2]),
|
|
S_BRIGHT (FX09, 'I', 4, NULL , &States[S_PHOENIXFXI2+3]),
|
|
S_BRIGHT (FX09, 'J', 5, NULL , &States[S_PHOENIXFXI2+4]),
|
|
S_BRIGHT (FX09, 'K', 5, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (APhoenixFX2, Heretic, -1, 0)
|
|
PROP_RadiusFixed (6)
|
|
PROP_HeightFixed (8)
|
|
PROP_SpeedFixed (10)
|
|
PROP_Damage (2)
|
|
PROP_DamageType (NAME_Fire)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY)
|
|
PROP_Flags2 (MF2_NOTELEPORT|MF2_PCROSS|MF2_IMPACT)
|
|
PROP_RenderStyle (STYLE_Add)
|
|
|
|
PROP_SpawnState (S_PHOENIXFX2)
|
|
PROP_DeathState (S_PHOENIXFXI2)
|
|
END_DEFAULTS
|
|
|
|
int APhoenixFX2::DoSpecialDamage (AActor *target, int damage)
|
|
{
|
|
if (target->player && pr_pfx2 () < 128)
|
|
{ // Freeze player for a bit
|
|
target->reactiontime += 4;
|
|
}
|
|
return damage;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FirePhoenixPL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FirePhoenixPL1 (AActor *actor)
|
|
{
|
|
angle_t angle;
|
|
player_t *player;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AWeapon *weapon = actor->player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
P_SpawnPlayerMissile (actor, RUNTIME_CLASS(APhoenixFX1));
|
|
angle = actor->angle + ANG180;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
actor->momx += FixedMul (4*FRACUNIT, finecosine[angle]);
|
|
actor->momy += FixedMul (4*FRACUNIT, finesine[angle]);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_PhoenixPuff
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_PhoenixPuff (AActor *actor)
|
|
{
|
|
AActor *puff;
|
|
angle_t angle;
|
|
|
|
//[RH] Heretic never sets the target for seeking
|
|
//P_SeekerMissile (actor, ANGLE_1*5, ANGLE_1*10);
|
|
puff = Spawn<APhoenixPuff> (actor->x, actor->y, actor->z, ALLOW_REPLACE);
|
|
angle = actor->angle + ANG90;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
puff->momx = FixedMul (FRACUNIT*13/10, finecosine[angle]);
|
|
puff->momy = FixedMul (FRACUNIT*13/10, finesine[angle]);
|
|
puff->momz = 0;
|
|
puff = Spawn<APhoenixPuff> (actor->x, actor->y, actor->z, ALLOW_REPLACE);
|
|
angle = actor->angle - ANG90;
|
|
angle >>= ANGLETOFINESHIFT;
|
|
puff->momx = FixedMul (FRACUNIT*13/10, finecosine[angle]);
|
|
puff->momy = FixedMul (FRACUNIT*13/10, finesine[angle]);
|
|
puff->momz = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_InitPhoenixPL2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_InitPhoenixPL2 (AActor *actor)
|
|
{
|
|
if (actor->player != NULL)
|
|
{
|
|
APhoenixRod *flamethrower = static_cast<APhoenixRod *> (actor->player->ReadyWeapon);
|
|
if (flamethrower != NULL)
|
|
{
|
|
flamethrower->FlameCount = FLAME_THROWER_TICS;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FirePhoenixPL2
|
|
//
|
|
// Flame thrower effect.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FirePhoenixPL2 (AActor *actor)
|
|
{
|
|
AActor *mo;
|
|
angle_t angle;
|
|
fixed_t x, y, z;
|
|
fixed_t slope;
|
|
FSoundID soundid;
|
|
player_t *player;
|
|
APhoenixRod *flamethrower;
|
|
|
|
if (NULL == (player = actor->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, &APhoenixRod::States[S_PHOENIXATK2+3]);
|
|
player->refire = 0;
|
|
S_StopSound (actor, CHAN_WEAPON);
|
|
return;
|
|
}
|
|
angle = actor->angle;
|
|
x = actor->x + (pr_fp2.Random2() << 9);
|
|
y = actor->y + (pr_fp2.Random2() << 9);
|
|
z = actor->z + 26*FRACUNIT + finetangent[FINEANGLES/4-(actor->pitch>>ANGLETOFINESHIFT)];
|
|
z -= actor->floorclip;
|
|
slope = finetangent[FINEANGLES/4-(actor->pitch>>ANGLETOFINESHIFT)] + (FRACUNIT/10);
|
|
mo = Spawn<APhoenixFX2> (x, y, z, ALLOW_REPLACE);
|
|
mo->target = actor;
|
|
mo->angle = angle;
|
|
mo->momx = actor->momx + FixedMul (mo->Speed, finecosine[angle>>ANGLETOFINESHIFT]);
|
|
mo->momy = actor->momy + FixedMul (mo->Speed, finesine[angle>>ANGLETOFINESHIFT]);
|
|
mo->momz = FixedMul (mo->Speed, slope);
|
|
if (!player->refire || !S_IsActorPlayingSomething (actor, CHAN_WEAPON, -1))
|
|
{
|
|
S_Sound (actor, CHAN_WEAPON|CHAN_LOOP, soundid, 1, ATTN_NORM);
|
|
}
|
|
P_CheckMissileSpawn (mo);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_ShutdownPhoenixPL2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_ShutdownPhoenixPL2 (AActor *actor)
|
|
{
|
|
player_t *player;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
S_StopSound (actor, CHAN_WEAPON);
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
if (weapon != NULL)
|
|
{
|
|
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
|
return;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FlameEnd
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FlameEnd (AActor *actor)
|
|
{
|
|
actor->momz += FRACUNIT*3/2;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FloatPuff
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_FloatPuff (AActor *puff)
|
|
{
|
|
puff->momz += FRACUNIT*18/10;
|
|
}
|
|
|