gzdoom/src/g_doom/a_scriptedmarine.cpp

610 lines
14 KiB
C++
Raw Normal View History

#include "actor.h"
#include "p_enemy.h"
#include "a_action.h"
#include "r_draw.h"
#include "m_random.h"
#include "p_local.h"
#include "a_doomglobal.h"
#include "s_sound.h"
#include "r_translate.h"
#include "thingdef/thingdef.h"
#define MARINE_PAIN_CHANCE 160
static FRandom pr_m_refire ("SMarineRefire");
static FRandom pr_m_punch ("SMarinePunch");
static FRandom pr_m_gunshot ("SMarineGunshot");
static FRandom pr_m_saw ("SMarineSaw");
static FRandom pr_m_fireshotgun2 ("SMarineFireSSG");
IMPLEMENT_CLASS (AScriptedMarine)
void AScriptedMarine::Serialize (FArchive &arc)
{
Super::Serialize (arc);
if (arc.IsStoring ())
{
arc.WriteSprite (SpriteOverride);
}
else
{
SpriteOverride = arc.ReadSprite ();
}
arc << CurrentWeapon;
}
void AScriptedMarine::Activate (AActor *activator)
{
if (flags2 & MF2_DORMANT)
{
flags2 &= ~MF2_DORMANT;
tics = 1;
}
}
void AScriptedMarine::Deactivate (AActor *activator)
{
if (!(flags2 & MF2_DORMANT))
{
flags2 |= MF2_DORMANT;
tics = -1;
}
}
bool AScriptedMarine::GetWeaponStates(int weap, FState *&melee, FState *&missile)
{
static ENamedName WeaponNames[] =
{
NAME_None,
NAME_Fist,
NAME_Berserk,
NAME_Chainsaw,
NAME_Pistol,
NAME_Shotgun,
NAME_SSG,
NAME_Chaingun,
NAME_Rocket,
NAME_Plasma,
NAME_Railgun,
NAME_BFG
};
if (weap < WEAPON_Dummy || weap > WEAPON_BFG) weap = WEAPON_Dummy;
melee = FindState(NAME_Melee, WeaponNames[weap], true);
missile = FindState(NAME_Missile, WeaponNames[weap], true);
return melee != NULL || missile != NULL;
}
void AScriptedMarine::BeginPlay ()
{
Super::BeginPlay ();
// Set the current weapon
for(int i=WEAPON_Dummy; i<=WEAPON_BFG; i++)
{
FState *melee, *missile;
if (GetWeaponStates(i, melee, missile))
{
if (melee == MeleeState && missile == MissileState)
{
CurrentWeapon = i;
}
}
}
// Copy the standard player's scaling
AActor * playerdef = GetDefaultByName("DoomPlayer");
if (playerdef != NULL)
{
scaleX = playerdef->scaleX;
scaleY = playerdef->scaleY;
}
}
void AScriptedMarine::Tick ()
{
Super::Tick ();
// Override the standard sprite, if desired
if (SpriteOverride != 0 && sprite == States[0].sprite.index)
{
sprite = SpriteOverride;
}
if (special1 != 0)
{
if (CurrentWeapon == WEAPON_SuperShotgun)
{ // Play SSG reload sounds
int ticks = level.maptime - special1;
if (ticks < 47)
{
switch (ticks)
{
case 14:
S_Sound (this, CHAN_WEAPON, "weapons/sshoto", 1, ATTN_NORM);
break;
case 28:
S_Sound (this, CHAN_WEAPON, "weapons/sshotl", 1, ATTN_NORM);
break;
case 41:
S_Sound (this, CHAN_WEAPON, "weapons/sshotc", 1, ATTN_NORM);
break;
}
}
else
{
special1 = 0;
}
}
else
{ // Wait for a long refire time
if (level.maptime >= special1)
{
special1 = 0;
}
else
{
flags |= MF_JUSTATTACKED;
}
}
}
}
//============================================================================
//
// A_M_Refire
//
//============================================================================
void A_M_Refire (AActor *self)
{
if (self->target == NULL || self->target->health <= 0)
{
if (self->MissileState && pr_m_refire() < 160)
{ // Look for a new target most of the time
if (P_LookForPlayers (self, true) && P_CheckMissileRange (self))
{ // Found somebody new and in range, so don't stop shooting
return;
}
}
self->SetState (self->state + 1);
return;
}
if ((self->MissileState == NULL && !self->CheckMeleeRange ()) ||
!P_CheckSight (self, self->target) ||
pr_m_refire() < 4) // Small chance of stopping even when target not dead
{
self->SetState (self->state + 1);
}
}
//============================================================================
//
// A_M_SawRefire
//
//============================================================================
void A_M_SawRefire (AActor *self)
{
if (self->target == NULL || self->target->health <= 0)
{
self->SetState (self->state + 1);
return;
}
if (!self->CheckMeleeRange ())
{
self->SetState (self->state + 1);
}
}
//============================================================================
//
// A_MarineNoise
//
//============================================================================
void A_MarineNoise (AActor *self)
{
if (static_cast<AScriptedMarine *>(self)->CurrentWeapon == AScriptedMarine::WEAPON_Chainsaw)
{
S_Sound (self, CHAN_WEAPON, "weapons/sawidle", 1, ATTN_NORM);
}
}
//============================================================================
//
// A_MarineChase
//
//============================================================================
void A_MarineChase (AActor *self)
{
A_MarineNoise (self);
A_Chase (self);
}
//============================================================================
//
// A_MarineLook
//
//============================================================================
void A_MarineLook (AActor *self)
{
A_MarineNoise (self);
A_Look (self);
}
//============================================================================
//
// A_M_Saw
//
//============================================================================
void A_M_Saw (AActor *self)
{
if (self->target == NULL)
return;
A_FaceTarget (self);
if (self->CheckMeleeRange ())
{
angle_t angle;
int damage;
AActor *linetarget;
damage = 2 * (pr_m_saw()%10+1);
angle = self->angle + (pr_m_saw.Random2() << 18);
P_LineAttack (self, angle, MELEERANGE+1,
P_AimLineAttack (self, angle, MELEERANGE+1, &linetarget), damage,
NAME_Melee, NAME_BulletPuff);
if (!linetarget)
{
S_Sound (self, CHAN_WEAPON, "weapons/sawfull", 1, ATTN_NORM);
return;
}
S_Sound (self, CHAN_WEAPON, "weapons/sawhit", 1, ATTN_NORM);
// turn to face target
angle = R_PointToAngle2 (self->x, self->y, linetarget->x, linetarget->y);
if (angle - self->angle > ANG180)
{
if (angle - self->angle < (angle_t)(-ANG90/20))
self->angle = angle + ANG90/21;
else
self->angle -= ANG90/20;
}
else
{
if (angle - self->angle > ANG90/20)
self->angle = angle - ANG90/21;
else
self->angle += ANG90/20;
}
}
else
{
S_Sound (self, CHAN_WEAPON, "weapons/sawfull", 1, ATTN_NORM);
}
//A_Chase (self);
}
//============================================================================
//
// A_M_Punch
//
//============================================================================
void A_M_Punch (AActor *self)
{
angle_t angle;
int damage;
int pitch;
AActor *linetarget;
if (self->target == NULL)
return;
int index=CheckIndex(1);
if (index<0) return;
damage = (pr_m_punch()%10+1) << 1;
damage *= EvalExpressionI (StateParameters[index], self);
A_FaceTarget (self);
angle = self->angle + (pr_m_punch.Random2() << 18);
pitch = P_AimLineAttack (self, angle, MELEERANGE, &linetarget);
P_LineAttack (self, angle, MELEERANGE, pitch, damage, NAME_Melee, NAME_BulletPuff, true);
// turn to face target
if (linetarget)
{
S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM);
self->angle = R_PointToAngle2 (self->x, self->y, linetarget->x, linetarget->y);
}
}
//============================================================================
//
// P_GunShot2
//
//============================================================================
void P_GunShot2 (AActor *mo, bool accurate, int pitch, const PClass *pufftype)
{
angle_t angle;
int damage;
damage = 5*(pr_m_gunshot()%3+1);
angle = mo->angle;
if (!accurate)
{
angle += pr_m_gunshot.Random2 () << 18;
}
P_LineAttack (mo, angle, MISSILERANGE, pitch, damage, NAME_None, pufftype);
}
//============================================================================
//
// A_M_FirePistol
//
//============================================================================
void A_M_FirePistol (AActor *self)
{
if (self->target == NULL)
return;
int index=CheckIndex(1);
if (index<0) return;
bool accurate = !!EvalExpressionI (StateParameters[index], self);
S_Sound (self, CHAN_WEAPON, "weapons/pistol", 1, ATTN_NORM);
A_FaceTarget (self);
P_GunShot2 (self, accurate, P_AimLineAttack (self, self->angle, MISSILERANGE),
PClass::FindClass(NAME_BulletPuff));
}
//============================================================================
//
// A_M_FireShotgun
//
//============================================================================
void A_M_FireShotgun (AActor *self)
{
int pitch;
if (self->target == NULL)
return;
S_Sound (self, CHAN_WEAPON, "weapons/shotgf", 1, ATTN_NORM);
A_FaceTarget (self);
pitch = P_AimLineAttack (self, self->angle, MISSILERANGE);
for (int i = 0; i < 7; ++i)
{
P_GunShot2 (self, false, pitch, PClass::FindClass(NAME_BulletPuff));
}
self->special1 = level.maptime + 27;
}
//============================================================================
//
// A_M_CheckAttack
//
//============================================================================
void A_M_CheckAttack (AActor *self)
{
if (self->special1 != 0 || self->target == NULL)
{
self->SetState (self->FindState("SkipAttack"));
}
else
{
A_FaceTarget (self);
}
}
//============================================================================
//
// A_M_FireShotgun2
//
//============================================================================
void A_M_FireShotgun2 (AActor *self)
{
int pitch;
if (self->target == NULL)
return;
S_Sound (self, CHAN_WEAPON, "weapons/sshotf", 1, ATTN_NORM);
A_FaceTarget (self);
pitch = P_AimLineAttack (self, self->angle, MISSILERANGE);
for (int i = 0; i < 20; ++i)
{
int damage = 5*(pr_m_fireshotgun2()%3+1);
angle_t angle = self->angle + (pr_m_fireshotgun2.Random2() << 19);
P_LineAttack (self, angle, MISSILERANGE,
pitch + (pr_m_fireshotgun2.Random2() * 332063), damage,
NAME_None, PClass::FindClass(NAME_BulletPuff));
}
self->special1 = level.maptime;
}
//============================================================================
//
// A_M_FireCGun
//
//============================================================================
void A_M_FireCGun (AActor *self)
{
if (self->target == NULL)
return;
int index=CheckIndex(1);
if (index<0) return;
bool accurate = !!EvalExpressionI (StateParameters[index], self);
S_Sound (self, CHAN_WEAPON, "weapons/chngun", 1, ATTN_NORM);
A_FaceTarget (self);
P_GunShot2 (self, accurate, P_AimLineAttack (self, self->angle, MISSILERANGE),
PClass::FindClass(NAME_BulletPuff));
}
//============================================================================
//
// A_M_FireMissile
//
// Giving a marine a rocket launcher is probably a bad idea unless you pump
// up his health, because he's just as likely to kill himself as he is to
// kill anything else with it.
//
//============================================================================
void A_M_FireMissile (AActor *self)
{
if (self->target == NULL)
return;
if (self->CheckMeleeRange ())
{ // If too close, punch it
A_M_Punch (self);
}
else
{
A_FaceTarget (self);
P_SpawnMissile (self, self->target, PClass::FindClass("Rocket"));
}
}
//============================================================================
//
// A_M_FireRailgun
//
//============================================================================
void A_M_FireRailgun (AActor *self)
{
if (self->target == NULL)
return;
A_MonsterRail (self);
self->special1 = level.maptime + 50;
}
//============================================================================
//
// A_M_FirePlasma
//
//============================================================================
void A_M_FirePlasma (AActor *self)
{
if (self->target == NULL)
return;
A_FaceTarget (self);
P_SpawnMissile (self, self->target, PClass::FindClass("PlasmaBall"));
self->special1 = level.maptime + 20;
}
//============================================================================
//
// A_M_BFGsound
//
//============================================================================
void A_M_BFGsound (AActor *self)
{
if (self->target == NULL)
return;
if (self->special1 != 0)
{
self->SetState (self->SeeState);
}
else
{
A_FaceTarget (self);
S_Sound (self, CHAN_WEAPON, "weapons/bfgf", 1, ATTN_NORM);
// Don't interrupt the firing sequence
self->PainChance = 0;
}
}
//============================================================================
//
// A_M_FireBFG
//
//============================================================================
void A_M_FireBFG (AActor *self)
{
if (self->target == NULL)
return;
A_FaceTarget (self);
P_SpawnMissile (self, self->target, PClass::FindClass("BFGBall"));
self->special1 = level.maptime + 30;
self->PainChance = MARINE_PAIN_CHANCE;
}
//---------------------------------------------------------------------------
void AScriptedMarine::SetWeapon (EMarineWeapon type)
{
if (GetWeaponStates(type, MeleeState, MissileState))
{
static const char *classes[] = {
"ScriptedMarine",
"MarineFist",
"MarineBerserk",
"MarineChainsaw",
"MarinePistol",
"MarineShotgun",
"MarineSSG",
"MarineChaingun",
"MarineRocket",
"MarinePlasma",
"MarineRailgun",
"MarineBFG"
};
const PClass *cls = PClass::FindClass(classes[type]);
if (cls != NULL)
DecalGenerator = GetDefaultByType(cls)->DecalGenerator;
else
DecalGenerator = NULL;
}
}
void AScriptedMarine::SetSprite (const PClass *source)
{
if (source == NULL || source->ActorInfo == NULL)
{ // A valid actor class wasn't passed, so use the standard sprite
SpriteOverride = sprite = States[0].sprite.index;
// Copy the standard player's scaling
AActor * playerdef = GetDefaultByName("DoomPlayer");
if (playerdef == NULL) playerdef = GetDefaultByType(RUNTIME_CLASS(AScriptedMarine));
scaleX = playerdef->scaleX;
scaleY = playerdef->scaleY;
}
else
{ // Use the same sprite the passed class spawns with
SpriteOverride = sprite = GetDefaultByType (source)->SpawnState->sprite.index;
scaleX = GetDefaultByType(source)->scaleX;
scaleY = GetDefaultByType(source)->scaleY;
}
}