gzdoom/src/g_heretic/a_hereticweaps.cpp
Christoph Oelckers b54b9bad7a - Fixed: A_VileAttack positioned the fire on the wrong side of the target.
- Reorganized the HackHack code so that the image creation was moved into
  MakeTexture. This was necessary because Unload deleted the pixel data
  and broke the whole thing.
- Fixed: FPatchTexture::HackHack and FDoomStatusbarTexture::DrawToBar used the
  obsolete and uninitialized variable Near255. 
- Removed the span creation code specific to FPatchTexture. It only has an
  advantage when the lump has already been loaded in memory but since that
  is no longer the case now the generic version in FTexture is actually better.
- Changed: FTexture::CopyToBlock no longer uses the spans but the pixel buffer
  directly. Since most patches in multipatch textures are non transparent
  the added overhead from creating the spans far outweighs any savings they
  might provide. It is also simpler to handle for mirrored or rotated patches now.
- Changed: Textures only create the spans when really needed. Flats and native
  textures, for example, do not and it only created needless overhead that they
  were always created along with the pixel buffer.
- Made use of player and actor variables consistent in a_hereticweaps.cpp.
- Fixed: A few calls to P_SpawnPlayerMissile passed 0 as angle



SVN r911 (trunk)
2008-04-14 12:10:45 +00:00

2959 lines
83 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, 0)
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
// 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)
{
mace->FirstSpot = firstSpot;
mace->NumMaceSpots = numspots;
mace->DoRespawn ();
// 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 ()
{
if (NumMaceSpots > 0)
{
int spotnum = pr_macerespawn () % NumMaceSpots;
AActor *spot = FirstSpot;
while (spotnum > 0)
{
spot = spot->target;
spotnum--;
}
SetOrigin (spot->x, spot->y, spot->z);
z = floorz;
}
return true;
}
//----------------------------------------------------------------------------
//
// 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)
{
AActor *mo;
player_t *player;
if (NULL == (player = actor->player))
{
return;
}
AWeapon *weapon = actor->player->ReadyWeapon;
if (weapon != NULL)
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return;
}
mo = P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ABlasterFX1));
S_Sound (mo, 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_SoundID (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;
int soundid;
player_t *player;
APhoenixRod *flamethrower;
if (NULL == (player = actor->player))
{
return;
}
soundid = S_FindSound ("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_SoundID (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;
}