mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-12-02 00:43:43 +00:00
334 lines
10 KiB
C++
334 lines
10 KiB
C++
|
#include "actor.h"
|
||
|
#include "gi.h"
|
||
|
#include "m_random.h"
|
||
|
#include "s_sound.h"
|
||
|
#include "d_player.h"
|
||
|
#include "a_action.h"
|
||
|
#include "p_local.h"
|
||
|
#include "p_enemy.h"
|
||
|
#include "a_action.h"
|
||
|
#include "p_pspr.h"
|
||
|
#include "gstrings.h"
|
||
|
#include "a_hexenglobal.h"
|
||
|
|
||
|
static FRandom pr_staffcheck ("CStaffCheck");
|
||
|
static FRandom pr_blink ("CStaffBlink");
|
||
|
|
||
|
void A_CStaffInitBlink (AActor *actor);
|
||
|
void A_CStaffCheckBlink (AActor *actor);
|
||
|
void A_CStaffCheck (AActor *actor);
|
||
|
void A_CStaffAttack (AActor *actor);
|
||
|
void A_CStaffMissileSlither (AActor *);
|
||
|
|
||
|
// The Cleric's Serpent Staff -----------------------------------------------
|
||
|
|
||
|
class ACWeapStaff : public AClericWeapon
|
||
|
{
|
||
|
DECLARE_ACTOR (ACWeapStaff, AClericWeapon)
|
||
|
public:
|
||
|
const char *PickupMessage ()
|
||
|
{
|
||
|
return GStrings("TXT_WEAPON_C2");
|
||
|
}
|
||
|
};
|
||
|
|
||
|
FState ACWeapStaff::States[] =
|
||
|
{
|
||
|
#define S_CSTAFF 0
|
||
|
S_NORMAL (WCSS, 'A', -1, NULL , NULL),
|
||
|
|
||
|
#define S_CSTAFFREADY (S_CSTAFF+1)
|
||
|
S_NORMAL (CSSF, 'C', 4, NULL , &States[S_CSTAFFREADY+1]),
|
||
|
S_NORMAL (CSSF, 'B', 3, A_CStaffInitBlink , &States[S_CSTAFFREADY+2]),
|
||
|
S_NORMAL (CSSF, 'A', 1, A_WeaponReady , &States[S_CSTAFFREADY+3]),
|
||
|
S_NORMAL (CSSF, 'A', 1, A_WeaponReady , &States[S_CSTAFFREADY+4]),
|
||
|
S_NORMAL (CSSF, 'A', 1, A_WeaponReady , &States[S_CSTAFFREADY+5]),
|
||
|
S_NORMAL (CSSF, 'A', 1, A_WeaponReady , &States[S_CSTAFFREADY+6]),
|
||
|
S_NORMAL (CSSF, 'A', 1, A_WeaponReady , &States[S_CSTAFFREADY+7]),
|
||
|
S_NORMAL (CSSF, 'A', 1, A_WeaponReady , &States[S_CSTAFFREADY+8]),
|
||
|
S_NORMAL (CSSF, 'A', 1, A_WeaponReady , &States[S_CSTAFFREADY+9]),
|
||
|
S_NORMAL (CSSF, 'A', 1, A_CStaffCheckBlink , &States[S_CSTAFFREADY+2]),
|
||
|
|
||
|
#define S_CSTAFFBLINK (S_CSTAFFREADY+10)
|
||
|
S_NORMAL (CSSF, 'B', 1, A_WeaponReady , &States[S_CSTAFFBLINK+1]),
|
||
|
S_NORMAL (CSSF, 'B', 1, A_WeaponReady , &States[S_CSTAFFBLINK+2]),
|
||
|
S_NORMAL (CSSF, 'B', 1, A_WeaponReady , &States[S_CSTAFFBLINK+3]),
|
||
|
S_NORMAL (CSSF, 'C', 1, A_WeaponReady , &States[S_CSTAFFBLINK+4]),
|
||
|
S_NORMAL (CSSF, 'C', 1, A_WeaponReady , &States[S_CSTAFFBLINK+5]),
|
||
|
S_NORMAL (CSSF, 'C', 1, A_WeaponReady , &States[S_CSTAFFBLINK+6]),
|
||
|
S_NORMAL (CSSF, 'C', 1, A_WeaponReady , &States[S_CSTAFFBLINK+7]),
|
||
|
S_NORMAL (CSSF, 'C', 1, A_WeaponReady , &States[S_CSTAFFBLINK+8]),
|
||
|
S_NORMAL (CSSF, 'B', 1, A_WeaponReady , &States[S_CSTAFFBLINK+9]),
|
||
|
S_NORMAL (CSSF, 'B', 1, A_WeaponReady , &States[S_CSTAFFBLINK+10]),
|
||
|
S_NORMAL (CSSF, 'B', 1, A_WeaponReady , &States[S_CSTAFFREADY+2]),
|
||
|
|
||
|
#define S_CSTAFFDOWN (S_CSTAFFBLINK+11)
|
||
|
S_NORMAL (CSSF, 'B', 3, NULL , &States[S_CSTAFFDOWN+1]),
|
||
|
S_NORMAL (CSSF, 'C', 4, NULL , &States[S_CSTAFFDOWN+2]),
|
||
|
S_NORMAL (CSSF, 'C', 1, A_Lower , &States[S_CSTAFFDOWN+2]),
|
||
|
|
||
|
#define S_CSTAFFUP (S_CSTAFFDOWN+3)
|
||
|
S_NORMAL (CSSF, 'C', 1, A_Raise , &States[S_CSTAFFUP]),
|
||
|
|
||
|
#define S_CSTAFFATK (S_CSTAFFUP+1)
|
||
|
S_NORMAL2 (CSSF, 'A', 1, A_CStaffCheck , &States[S_CSTAFFATK+1], 0, 45),
|
||
|
S_NORMAL2 (CSSF, 'J', 1, A_CStaffAttack , &States[S_CSTAFFATK+2], 0, 50),
|
||
|
S_NORMAL2 (CSSF, 'J', 2, NULL , &States[S_CSTAFFATK+3], 0, 50),
|
||
|
S_NORMAL2 (CSSF, 'J', 2, NULL , &States[S_CSTAFFATK+4], 0, 45),
|
||
|
S_NORMAL2 (CSSF, 'A', 2, NULL , &States[S_CSTAFFATK+5], 0, 40),
|
||
|
S_NORMAL2 (CSSF, 'A', 2, NULL , &States[S_CSTAFFREADY+2], 0, 36),
|
||
|
|
||
|
#define S_CSTAFFATK2 (S_CSTAFFATK+6)
|
||
|
S_NORMAL2 (CSSF, 'K', 10, NULL , &States[S_CSTAFFREADY+2], 0, 36),
|
||
|
};
|
||
|
|
||
|
IMPLEMENT_ACTOR (ACWeapStaff, Hexen, 10, 32)
|
||
|
PROP_Flags (MF_SPECIAL)
|
||
|
PROP_SpawnState (S_CSTAFF)
|
||
|
|
||
|
PROP_Weapon_SelectionOrder (1600)
|
||
|
PROP_Weapon_AmmoUse1 (1)
|
||
|
PROP_Weapon_AmmoGive1 (25)
|
||
|
PROP_Weapon_UpState (S_CSTAFFUP)
|
||
|
PROP_Weapon_DownState (S_CSTAFFDOWN)
|
||
|
PROP_Weapon_ReadyState (S_CSTAFFREADY)
|
||
|
PROP_Weapon_AtkState (S_CSTAFFATK)
|
||
|
PROP_Weapon_HoldAtkState (S_CSTAFFATK)
|
||
|
PROP_Weapon_Kickback (150)
|
||
|
PROP_Weapon_YAdjust (10)
|
||
|
PROP_Weapon_MoveCombatDist (25000000)
|
||
|
PROP_Weapon_AmmoType1 ("Mana1")
|
||
|
PROP_Weapon_ProjectileType ("CStaffMissile")
|
||
|
END_DEFAULTS
|
||
|
|
||
|
// Serpent Staff Missile ----------------------------------------------------
|
||
|
|
||
|
class ACStaffMissile : public AActor
|
||
|
{
|
||
|
DECLARE_ACTOR (ACStaffMissile, AActor)
|
||
|
public:
|
||
|
int DoSpecialDamage (AActor *target, int damage);
|
||
|
};
|
||
|
|
||
|
FState ACStaffMissile::States[] =
|
||
|
{
|
||
|
#define S_CSTAFF_MISSILE1 0
|
||
|
S_BRIGHT (CSSF, 'D', 1, A_CStaffMissileSlither , &States[S_CSTAFF_MISSILE1+1]),
|
||
|
S_BRIGHT (CSSF, 'D', 1, A_CStaffMissileSlither , &States[S_CSTAFF_MISSILE1+2]),
|
||
|
S_BRIGHT (CSSF, 'E', 1, A_CStaffMissileSlither , &States[S_CSTAFF_MISSILE1+3]),
|
||
|
S_BRIGHT (CSSF, 'E', 1, A_CStaffMissileSlither , &States[S_CSTAFF_MISSILE1]),
|
||
|
|
||
|
#define S_CSTAFF_MISSILE_X1 (S_CSTAFF_MISSILE1+4)
|
||
|
S_BRIGHT (CSSF, 'F', 4, NULL , &States[S_CSTAFF_MISSILE_X1+1]),
|
||
|
S_BRIGHT (CSSF, 'G', 4, NULL , &States[S_CSTAFF_MISSILE_X1+2]),
|
||
|
S_BRIGHT (CSSF, 'H', 3, NULL , &States[S_CSTAFF_MISSILE_X1+3]),
|
||
|
S_BRIGHT (CSSF, 'I', 3, NULL , NULL),
|
||
|
};
|
||
|
|
||
|
IMPLEMENT_ACTOR (ACStaffMissile, Hexen, -1, 0)
|
||
|
PROP_SpeedFixed (22)
|
||
|
PROP_RadiusFixed (12)
|
||
|
PROP_HeightFixed (10)
|
||
|
PROP_Damage (5)
|
||
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE)
|
||
|
PROP_Flags2 (MF2_NOTELEPORT|MF2_IMPACT|MF2_PCROSS)
|
||
|
PROP_RenderStyle (STYLE_Add)
|
||
|
|
||
|
PROP_SpawnState (S_CSTAFF_MISSILE1)
|
||
|
PROP_DeathState (S_CSTAFF_MISSILE_X1)
|
||
|
|
||
|
PROP_DeathSound ("ClericCStaffExplode")
|
||
|
END_DEFAULTS
|
||
|
|
||
|
int ACStaffMissile::DoSpecialDamage (AActor *target, int damage)
|
||
|
{
|
||
|
// Cleric Serpent Staff does poison damage
|
||
|
if (target->player)
|
||
|
{
|
||
|
P_PoisonPlayer (target->player, this, this->target, 20);
|
||
|
damage >>= 1;
|
||
|
}
|
||
|
return damage;
|
||
|
}
|
||
|
|
||
|
// Serpent Staff Puff -------------------------------------------------------
|
||
|
|
||
|
class ACStaffPuff : public AActor
|
||
|
{
|
||
|
DECLARE_ACTOR (ACStaffPuff, AActor)
|
||
|
};
|
||
|
|
||
|
FState ACStaffPuff::States[] =
|
||
|
{
|
||
|
S_NORMAL (FHFX, 'S', 4, NULL , &States[1]),
|
||
|
S_NORMAL (FHFX, 'T', 4, NULL , &States[2]),
|
||
|
S_NORMAL (FHFX, 'U', 4, NULL , &States[3]),
|
||
|
S_NORMAL (FHFX, 'V', 4, NULL , &States[4]),
|
||
|
S_NORMAL (FHFX, 'W', 4, NULL , NULL),
|
||
|
};
|
||
|
|
||
|
IMPLEMENT_ACTOR (ACStaffPuff, Hexen, -1, 0)
|
||
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY)
|
||
|
PROP_Flags3 (MF3_PUFFONACTORS)
|
||
|
PROP_RenderStyle (STYLE_Translucent)
|
||
|
PROP_Alpha (HX_SHADOW)
|
||
|
|
||
|
PROP_SpawnState (0)
|
||
|
|
||
|
PROP_SeeSound ("ClericCStaffHitThing")
|
||
|
END_DEFAULTS
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// A_CStaffCheck
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void A_CStaffCheck (AActor *actor)
|
||
|
{
|
||
|
AActor *pmo;
|
||
|
int damage;
|
||
|
int newLife;
|
||
|
angle_t angle;
|
||
|
int slope;
|
||
|
int i;
|
||
|
player_t *player;
|
||
|
|
||
|
if (NULL == (player = actor->player))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
AWeapon *weapon = actor->player->ReadyWeapon;
|
||
|
|
||
|
pmo = player->mo;
|
||
|
damage = 20+(pr_staffcheck()&15);
|
||
|
for (i = 0; i < 3; i++)
|
||
|
{
|
||
|
angle = pmo->angle+i*(ANG45/16);
|
||
|
slope = P_AimLineAttack (pmo, angle, fixed_t(1.5*MELEERANGE));
|
||
|
if (linetarget)
|
||
|
{
|
||
|
P_LineAttack (pmo, angle, fixed_t(1.5*MELEERANGE), slope, damage, MOD_HIT, RUNTIME_CLASS(ACStaffPuff));
|
||
|
pmo->angle = R_PointToAngle2 (pmo->x, pmo->y,
|
||
|
linetarget->x, linetarget->y);
|
||
|
if ((linetarget->player || linetarget->flags3&MF3_ISMONSTER)
|
||
|
&& (!(linetarget->flags2&(MF2_DORMANT+MF2_INVULNERABLE))))
|
||
|
{
|
||
|
newLife = player->health+(damage>>3);
|
||
|
newLife = newLife > 100 ? 100 : newLife;
|
||
|
if (newLife > player->health)
|
||
|
{
|
||
|
pmo->health = player->health = newLife;
|
||
|
}
|
||
|
P_SetPsprite (player, ps_weapon, &ACWeapStaff::States[S_CSTAFFATK2]);
|
||
|
}
|
||
|
if (weapon != NULL)
|
||
|
{
|
||
|
weapon->DepleteAmmo (weapon->bAltFire, false);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
angle = pmo->angle-i*(ANG45/16);
|
||
|
slope = P_AimLineAttack (player->mo, angle, fixed_t(1.5*MELEERANGE));
|
||
|
if (linetarget)
|
||
|
{
|
||
|
P_LineAttack (pmo, angle, fixed_t(1.5*MELEERANGE), slope, damage, MOD_HIT, RUNTIME_CLASS(ACStaffPuff));
|
||
|
pmo->angle = R_PointToAngle2 (pmo->x, pmo->y,
|
||
|
linetarget->x, linetarget->y);
|
||
|
if (linetarget->player || linetarget->flags3&MF3_ISMONSTER)
|
||
|
{
|
||
|
newLife = player->health+(damage>>4);
|
||
|
newLife = newLife > 100 ? 100 : newLife;
|
||
|
pmo->health = player->health = newLife;
|
||
|
P_SetPsprite (player, ps_weapon, &ACWeapStaff::States[S_CSTAFFATK2]);
|
||
|
}
|
||
|
weapon->DepleteAmmo (weapon->bAltFire, false);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// A_CStaffAttack
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void A_CStaffAttack (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(ACStaffMissile), actor->angle-(ANG45/15));
|
||
|
if (mo)
|
||
|
{
|
||
|
mo->special2 = 32;
|
||
|
}
|
||
|
mo = P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ACStaffMissile), actor->angle+(ANG45/15));
|
||
|
if (mo)
|
||
|
{
|
||
|
mo->special2 = 0;
|
||
|
}
|
||
|
S_Sound (actor, CHAN_WEAPON, "ClericCStaffFire", 1, ATTN_NORM);
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// A_CStaffMissileSlither
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void A_CStaffMissileSlither (AActor *actor)
|
||
|
{
|
||
|
fixed_t newX, newY;
|
||
|
int weaveXY;
|
||
|
int angle;
|
||
|
|
||
|
weaveXY = actor->special2;
|
||
|
angle = (actor->angle+ANG90)>>ANGLETOFINESHIFT;
|
||
|
newX = actor->x-FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]);
|
||
|
newY = actor->y-FixedMul(finesine[angle], FloatBobOffsets[weaveXY]);
|
||
|
weaveXY = (weaveXY+3)&63;
|
||
|
newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]);
|
||
|
newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY]);
|
||
|
P_TryMove (actor, newX, newY, true);
|
||
|
actor->special2 = weaveXY;
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// A_CStaffInitBlink
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void A_CStaffInitBlink (AActor *actor)
|
||
|
{
|
||
|
actor->special1 = (pr_blink()>>1)+20;
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// A_CStaffCheckBlink
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void A_CStaffCheckBlink (AActor *actor)
|
||
|
{
|
||
|
if (!--actor->special1)
|
||
|
{
|
||
|
P_SetPsprite (actor->player, ps_weapon, &ACWeapStaff::States[S_CSTAFFBLINK]);
|
||
|
actor->special1 = (pr_blink()+50)>>2;
|
||
|
}
|
||
|
}
|