qzdoom/src/g_doom/a_scriptedmarine.cpp
Christoph Oelckers 063c85b157 - Fixed: The global WeaponSection string was never freed. It has been replaced
with an FString now.
- Fixed: The music strings in the default level info were never freed and
  caused memory leaks when used repeatedly.
- Fixed: The intermusic string in the level info was never freed.
- Fixed: The default fire obituary should only be printed if the damage
  came from the environment. If it comes from a monster the monster specific
  obituary should be used instead.
- Added custom damage types from the floating point test release.
- Changed Pain Elemental's massacre check. Now A_PainDie checks for the damage 
  type and doesn't spawn anything if it is NAME_Massacre. A_PainDie can also 
  be used by other actors so a more generalized approach is needed than hard
  coding it into the Pain Elemental.
- Converted a few of Doom's monsters to DECORATE because I couldn't test the
  first version of the custom state code with the corpses inheriting from them.
- Added custom states from last year's floating point test release and fixed
  some bugs I found in that code. Unfortunately it wasn't all salvageable
  and it was easier to recreate some parts from scratch.



SVN r368 (trunk)
2006-10-31 14:53:21 +00:00

981 lines
29 KiB
C++

#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"
#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");
void A_MarineLook (AActor *);
void A_MarineNoise (AActor *);
void A_MarineChase (AActor *);
void A_M_Refire (AActor *);
void A_M_SawRefire (AActor *);
void A_M_Saw (AActor *);
void A_M_Punch (AActor *);
void A_M_BerserkPunch (AActor *);
void A_M_FirePistol (AActor *);
void A_M_FirePistolInaccurate (AActor *);
void A_M_FireShotgun (AActor *);
void A_M_CheckAttack (AActor *);
void A_M_FireShotgun2 (AActor *);
void A_M_FireCGunAccurate (AActor *);
void A_M_FireCGun (AActor *);
void A_M_FireMissile (AActor *);
void A_M_FireRailgun (AActor *);
void A_M_FirePlasma (AActor *);
void A_M_BFGsound (AActor *);
void A_M_FireBFG (AActor *);
// Scriptable marine -------------------------------------------------------
FState AScriptedMarine::States[] =
{
#define S_MPLAYSTILL 0
S_NORMAL (PLAY, 'A', 4, A_MarineLook , &States[S_MPLAYSTILL+1]),
S_NORMAL (PLAY, 'A', 4, A_MarineNoise , &States[S_MPLAYSTILL]),
#define S_MPLAY (S_MPLAYSTILL+2)
S_NORMAL (PLAY, 'A', 4, A_MarineLook , &States[S_MPLAY+1]),
S_NORMAL (PLAY, 'A', 4, A_MarineNoise , &States[S_MPLAY+2]),
S_NORMAL (PLAY, 'A', 4, A_MarineLook , &States[S_MPLAY+3]),
S_NORMAL (PLAY, 'B', 4, A_MarineNoise , &States[S_MPLAY+4]),
S_NORMAL (PLAY, 'B', 4, A_MarineLook , &States[S_MPLAY+5]),
S_NORMAL (PLAY, 'B', 4, A_MarineNoise , &States[S_MPLAY]),
#define S_MPLAY_RUN (S_MPLAY+6)
S_NORMAL (PLAY, 'A', 4, A_MarineChase , &States[S_MPLAY_RUN+1]),
S_NORMAL (PLAY, 'B', 4, A_MarineChase , &States[S_MPLAY_RUN+2]),
S_NORMAL (PLAY, 'C', 4, A_MarineChase , &States[S_MPLAY_RUN+3]),
S_NORMAL (PLAY, 'D', 4, A_MarineChase , &States[S_MPLAY_RUN+0]),
#define S_MPLAY_ATK (S_MPLAY_RUN+4)
S_NORMAL (PLAY, 'E', 12, A_FaceTarget , &States[S_MPLAY]),
S_BRIGHT (PLAY, 'F', 6, NULL , &States[S_MPLAY_ATK+0]),
#define S_MPLAY_PAIN (S_MPLAY_ATK+2)
S_NORMAL (PLAY, 'G', 4, NULL , &States[S_MPLAY_PAIN+1]),
S_NORMAL (PLAY, 'G', 4, A_Pain , &States[S_MPLAY]),
#define S_MPLAY_DIE (S_MPLAY_PAIN+2)
S_NORMAL (PLAY, 'H', 10, NULL , &States[S_MPLAY_DIE+1]),
S_NORMAL (PLAY, 'I', 10, A_Scream , &States[S_MPLAY_DIE+2]),
S_NORMAL (PLAY, 'J', 10, A_NoBlocking , &States[S_MPLAY_DIE+3]),
S_NORMAL (PLAY, 'K', 10, NULL , &States[S_MPLAY_DIE+4]),
S_NORMAL (PLAY, 'L', 10, NULL , &States[S_MPLAY_DIE+5]),
S_NORMAL (PLAY, 'M', 10, NULL , &States[S_MPLAY_DIE+6]),
S_NORMAL (PLAY, 'N', -1, NULL , NULL),
#define S_MPLAY_XDIE (S_MPLAY_DIE+7)
S_NORMAL (PLAY, 'O', 5, NULL , &States[S_MPLAY_XDIE+1]),
S_NORMAL (PLAY, 'P', 5, A_XScream , &States[S_MPLAY_XDIE+2]),
S_NORMAL (PLAY, 'Q', 5, A_NoBlocking , &States[S_MPLAY_XDIE+3]),
S_NORMAL (PLAY, 'R', 5, NULL , &States[S_MPLAY_XDIE+4]),
S_NORMAL (PLAY, 'S', 5, NULL , &States[S_MPLAY_XDIE+5]),
S_NORMAL (PLAY, 'T', 5, NULL , &States[S_MPLAY_XDIE+6]),
S_NORMAL (PLAY, 'U', 5, NULL , &States[S_MPLAY_XDIE+7]),
S_NORMAL (PLAY, 'V', 5, NULL , &States[S_MPLAY_XDIE+8]),
S_NORMAL (PLAY, 'W', -1, NULL , NULL),
#define S_MPLAY_RAISE (S_MPLAY_XDIE+9)
S_NORMAL (PLAY, 'M', 5, NULL , &States[S_MPLAY_RAISE+1]),
S_NORMAL (PLAY, 'L', 5, NULL , &States[S_MPLAY_RAISE+2]),
S_NORMAL (PLAY, 'K', 5, NULL , &States[S_MPLAY_RAISE+3]),
S_NORMAL (PLAY, 'J', 5, NULL , &States[S_MPLAY_RAISE+4]),
S_NORMAL (PLAY, 'I', 5, NULL , &States[S_MPLAY_RAISE+5]),
S_NORMAL (PLAY, 'H', 5, NULL , &States[S_MPLAY_RUN]),
#define S_MPLAY_ATK_CHAINSAW (S_MPLAY_RAISE+6)
S_NORMAL (PLAY, 'E', 4, A_MarineNoise , &States[S_MPLAY_ATK_CHAINSAW+1]),
S_BRIGHT (PLAY, 'F', 4, A_M_Saw , &States[S_MPLAY_ATK_CHAINSAW+2]),
S_BRIGHT (PLAY, 'F', 0, A_M_SawRefire , &States[S_MPLAY_ATK_CHAINSAW+1]),
S_NORMAL (PLAY, 'A', 0, NULL , &States[S_MPLAY_RUN]),
#define S_MPLAY_ATK_FIST (S_MPLAY_ATK_CHAINSAW+4)
S_NORMAL (PLAY, 'E', 4, A_FaceTarget , &States[S_MPLAY_ATK_FIST+1]),
S_NORMAL (PLAY, 'F', 4, A_M_Punch , &States[S_MPLAY_ATK_FIST+2]), // Purposefully not BRIGHT
S_NORMAL (PLAY, 'A', 9, NULL , &States[S_MPLAY_ATK_FIST+3]),
S_NORMAL (PLAY, 'A', 0, A_M_Refire , &States[S_MPLAY_ATK_FIST]),
S_NORMAL (PLAY, 'A', 5, A_FaceTarget , &States[S_MPLAY_RUN]),
#define S_MPLAY_ATK_BERSERK (S_MPLAY_ATK_FIST+5)
S_NORMAL (PLAY, 'E', 4, A_FaceTarget , &States[S_MPLAY_ATK_BERSERK+1]),
S_NORMAL (PLAY, 'F', 4, A_M_BerserkPunch , &States[S_MPLAY_ATK_BERSERK+2]), // Purposefully not BRIGHT
S_NORMAL (PLAY, 'A', 9, NULL , &States[S_MPLAY_ATK_BERSERK+3]),
S_NORMAL (PLAY, 'A', 0, A_M_Refire , &States[S_MPLAY_ATK_BERSERK]),
S_NORMAL (PLAY, 'A', 5, A_FaceTarget , &States[S_MPLAY_RUN]),
#define S_MPLAY_ATK_PISTOL (S_MPLAY_ATK_BERSERK+5)
S_NORMAL (PLAY, 'E', 4, A_FaceTarget , &States[S_MPLAY_ATK_PISTOL+1]),
S_BRIGHT (PLAY, 'F', 6, A_M_FirePistol , &States[S_MPLAY_ATK_PISTOL+2]),
S_NORMAL (PLAY, 'A', 4, A_FaceTarget , &States[S_MPLAY_ATK_PISTOL+3]),
S_NORMAL (PLAY, 'A', 0, A_M_Refire , &States[S_MPLAY_ATK_PISTOL+5]),
S_NORMAL (PLAY, 'A', 5, NULL , &States[S_MPLAY_RUN]),
S_BRIGHT (PLAY, 'F', 6, A_M_FirePistolInaccurate , &States[S_MPLAY_ATK_PISTOL+6]),
S_NORMAL (PLAY, 'A', 4, A_FaceTarget , &States[S_MPLAY_ATK_PISTOL+7]),
S_NORMAL (PLAY, 'A', 0, A_M_Refire , &States[S_MPLAY_ATK_PISTOL+5]),
S_NORMAL (PLAY, 'A', 5, A_FaceTarget , &States[S_MPLAY_RUN]),
#define S_MPLAY_ATK_SHOTGUN (S_MPLAY_ATK_PISTOL+9)
S_NORMAL (PLAY, 'E', 3, A_M_CheckAttack , &States[S_MPLAY_ATK_SHOTGUN+1]),
S_BRIGHT (PLAY, 'F', 7, A_M_FireShotgun , &States[S_MPLAY_RUN]),
#define S_MPLAY_ATK_DSHOTGUN (S_MPLAY_ATK_SHOTGUN+2)
S_NORMAL (PLAY, 'E', 3, A_M_CheckAttack , &States[S_MPLAY_ATK_DSHOTGUN+1]),
S_BRIGHT (PLAY, 'F', 7, A_M_FireShotgun2 , &States[S_MPLAY_RUN]),
#define S_MPLAY_SKIP_ATTACK (S_MPLAY_ATK_DSHOTGUN+2)
S_NORMAL (PLAY, 'A', 1, NULL , &States[S_MPLAY_RUN]),
#define S_MPLAY_ATK_CHAINGUN (S_MPLAY_SKIP_ATTACK+1)
S_NORMAL (PLAY, 'E', 4, A_FaceTarget , &States[S_MPLAY_ATK_CHAINGUN+1]),
S_BRIGHT (PLAY, 'F', 4, A_M_FireCGunAccurate , &States[S_MPLAY_ATK_CHAINGUN+2]),
S_BRIGHT (PLAY, 'F', 4, A_M_FireCGunAccurate , &States[S_MPLAY_ATK_CHAINGUN+3]),
S_BRIGHT (PLAY, 'F', 4, A_M_FireCGun , &States[S_MPLAY_ATK_CHAINGUN+4]),
S_BRIGHT (PLAY, 'F', 4, A_M_FireCGun , &States[S_MPLAY_ATK_CHAINGUN+5]),
S_NORMAL (PLAY, 'A', 0, A_M_Refire , &States[S_MPLAY_ATK_CHAINGUN+3]),
S_NORMAL (PLAY, 'A', 0, NULL , &States[S_MPLAY_RUN]),
#define S_MPLAY_ATK_ROCKET (S_MPLAY_ATK_CHAINGUN+7)
S_NORMAL (PLAY, 'E', 8, A_FaceTarget , &States[S_MPLAY_ATK_ROCKET+1]),
S_BRIGHT (PLAY, 'F', 6, A_M_FireMissile , &States[S_MPLAY_ATK_ROCKET+2]),
S_NORMAL (PLAY, 'A', 0, A_M_Refire , &States[S_MPLAY_ATK_ROCKET]),
S_NORMAL (PLAY, 'A', 0, NULL , &States[S_MPLAY_RUN]),
#define S_MPLAY_ATK_RAILGUN (S_MPLAY_ATK_ROCKET+4)
S_NORMAL (PLAY, 'E', 4, A_M_CheckAttack , &States[S_MPLAY_ATK_RAILGUN+1]),
S_BRIGHT (PLAY, 'F', 6, A_M_FireRailgun , &States[S_MPLAY_RUN]),
#define S_MPLAY_ATK_PLASMA (S_MPLAY_ATK_RAILGUN+2)
S_NORMAL (PLAY, 'E', 2, A_FaceTarget , &States[S_MPLAY_ATK_PLASMA+1]),
S_BRIGHT (PLAY, 'F', 3, A_M_FirePlasma , &States[S_MPLAY_ATK_PLASMA+2]),
S_NORMAL (PLAY, 'A', 0, A_M_Refire , &States[S_MPLAY_ATK_PLASMA+1]),
S_NORMAL (PLAY, 'A', 0, NULL , &States[S_MPLAY_RUN]),
#define S_MPLAY_ATK_BFG (S_MPLAY_ATK_PLASMA+4)
S_NORMAL (PLAY, 'E', 5, A_M_BFGsound , &States[S_MPLAY_ATK_BFG+1]),
S_NORMAL (PLAY, 'E', 5, A_FaceTarget , &States[S_MPLAY_ATK_BFG+2]),
S_NORMAL (PLAY, 'E', 5, A_FaceTarget , &States[S_MPLAY_ATK_BFG+3]),
S_NORMAL (PLAY, 'E', 5, A_FaceTarget , &States[S_MPLAY_ATK_BFG+4]),
S_NORMAL (PLAY, 'E', 5, A_FaceTarget , &States[S_MPLAY_ATK_BFG+5]),
S_NORMAL (PLAY, 'E', 5, A_FaceTarget , &States[S_MPLAY_ATK_BFG+6]),
S_BRIGHT (PLAY, 'F', 6, A_M_FireBFG , &States[S_MPLAY_ATK_BFG+7]),
S_NORMAL (PLAY, 'A', 4, A_FaceTarget , &States[S_MPLAY_ATK_BFG+8]),
S_NORMAL (PLAY, 'A', 0, A_M_Refire , &States[S_MPLAY_ATK_BFG]),
S_NORMAL (PLAY, 'A', 0, NULL , &States[S_MPLAY_RUN]),
};
IMPLEMENT_ACTOR (AScriptedMarine, Doom, 9100, 151)
PROP_SpawnHealth (100)
PROP_RadiusFixed (16)
PROP_HeightFixed (56)
PROP_Mass (100)
PROP_SpeedFixed (8)
PROP_PainChance (MARINE_PAIN_CHANCE)
PROP_Flags (MF_SOLID|MF_SHOOTABLE)
PROP_Flags2 (MF2_MCROSS|MF2_PASSMOBJ|MF2_PUSHWALL|MF2_FLOORCLIP)
PROP_Flags3 (MF3_ISMONSTER)
PROP_Translation (TRANSLATION_Standard,0) // Scripted marines wear black
PROP_Damage (100)
PROP_SpawnState (S_MPLAYSTILL)
PROP_SeeState (S_MPLAY_RUN)
PROP_PainState (S_MPLAY_PAIN)
PROP_MissileState (S_MPLAY_ATK)
PROP_DeathState (S_MPLAY_DIE)
PROP_XDeathState (S_MPLAY_XDIE)
PROP_RaiseState (S_MPLAY_RAISE)
PROP_DeathSound ("*death")
PROP_PainSound ("*pain50")
END_DEFAULTS
void AScriptedMarine::Serialize (FArchive &arc)
{
Super::Serialize (arc);
if (arc.IsStoring ())
{
arc.WriteSprite (SpriteOverride);
}
else
{
SpriteOverride = arc.ReadSprite ();
}
}
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;
}
}
void AScriptedMarine::BeginPlay ()
{
Super::BeginPlay ();
// Copy the standard player's scaling
xscale = GetDefaultByType(RUNTIME_CLASS(ADoomPlayer))->xscale;
yscale = GetDefaultByType(RUNTIME_CLASS(ADoomPlayer))->yscale;
}
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 (MissileState == &States[S_MPLAY_ATK_DSHOTGUN])
{ // 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_MarineChase
//
//============================================================================
void A_MarineChase (AActor *self)
{
if (self->MeleeState == &AScriptedMarine::States[S_MPLAY_ATK_CHAINSAW])
{
S_Sound (self, CHAN_WEAPON, "weapons/sawidle", 1, ATTN_NORM);
}
A_Chase (self);
}
//============================================================================
//
// A_MarineLook
//
//============================================================================
void A_MarineLook (AActor *self)
{
A_MarineNoise (self);
A_Look (self);
}
//============================================================================
//
// A_MarineNoise
//
//============================================================================
void A_MarineNoise (AActor *self)
{
if (self->MeleeState == &AScriptedMarine::States[S_MPLAY_ATK_CHAINSAW])
{
S_Sound (self, CHAN_WEAPON, "weapons/sawidle", 1, ATTN_NORM);
}
}
//============================================================================
//
// 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;
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), damage,
NAME_Melee, RUNTIME_CLASS(ABulletPuff));
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;
if (self->target == NULL)
return;
damage = (pr_m_punch()%10+1) << 1;
A_FaceTarget (self);
angle = self->angle + (pr_m_punch.Random2() << 18);
pitch = P_AimLineAttack (self, angle, MELEERANGE);
P_LineAttack (self, angle, MELEERANGE, pitch, damage, NAME_Melee, RUNTIME_CLASS(ABulletPuff));
// 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);
}
}
//============================================================================
//
// A_M_BerserkPunch
//
//============================================================================
void A_M_BerserkPunch (AActor *self)
{
angle_t angle;
int damage;
int pitch;
if (self->target == NULL)
return;
damage = ((pr_m_punch()%10+1) << 1) * 10;
A_FaceTarget (self);
angle = self->angle + (pr_m_punch.Random2() << 18);
pitch = P_AimLineAttack (self, angle, MELEERANGE);
P_LineAttack (self, angle, MELEERANGE, pitch, damage, NAME_Melee, RUNTIME_CLASS(ABulletPuff));
// 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;
S_Sound (self, CHAN_WEAPON, "weapons/pistol", 1, ATTN_NORM);
A_FaceTarget (self);
P_GunShot2 (self, true, P_AimLineAttack (self, self->angle, MISSILERANGE),
RUNTIME_CLASS(ABulletPuff));
}
//============================================================================
//
// A_M_FirePistolInaccurate
//
//============================================================================
void A_M_FirePistolInaccurate (AActor *self)
{
if (self->target == NULL)
return;
S_Sound (self, CHAN_WEAPON, "weapons/pistol", 1, ATTN_NORM);
A_FaceTarget (self);
P_GunShot2 (self, false, P_AimLineAttack (self, self->angle, MISSILERANGE),
RUNTIME_CLASS(ABulletPuff));
}
//============================================================================
//
// 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, RUNTIME_CLASS(ABulletPuff));
}
self->special1 = level.maptime + 27;
}
//============================================================================
//
// A_M_CheckAttack
//
//============================================================================
void A_M_CheckAttack (AActor *self)
{
if (self->special1 != 0 || self->target == NULL)
{
self->SetState (&AScriptedMarine::States[S_MPLAY_SKIP_ATTACK]);
}
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, RUNTIME_CLASS(ABulletPuff));
}
self->special1 = level.maptime;
}
//============================================================================
//
// A_M_FireCGunAccurate
//
//============================================================================
void A_M_FireCGunAccurate (AActor *self)
{
if (self->target == NULL)
return;
S_Sound (self, CHAN_WEAPON, "weapons/chngun", 1, ATTN_NORM);
A_FaceTarget (self);
P_GunShot2 (self, true, P_AimLineAttack (self, self->angle, MISSILERANGE),
RUNTIME_CLASS(ABulletPuff));
}
//============================================================================
//
// A_M_FireCGun
//
//============================================================================
void A_M_FireCGun (AActor *self)
{
if (self->target == NULL)
return;
S_Sound (self, CHAN_WEAPON, "weapons/chngun", 1, ATTN_NORM);
A_FaceTarget (self);
P_GunShot2 (self, false, P_AimLineAttack (self, self->angle, MISSILERANGE),
RUNTIME_CLASS(ABulletPuff));
}
//============================================================================
//
// 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, RUNTIME_CLASS(ARocket));
}
}
//============================================================================
//
// 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, RUNTIME_CLASS(APlasmaBall));
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, RUNTIME_CLASS(ABFGBall));
self->special1 = level.maptime + 30;
self->PainChance = MARINE_PAIN_CHANCE;
}
//---------------------------------------------------------------------------
class AMarineFist : public AScriptedMarine
{
DECLARE_STATELESS_ACTOR (AMarineFist, AScriptedMarine)
};
IMPLEMENT_STATELESS_ACTOR (AMarineFist, Doom, 9101, 0)
PROP_SpawnState (S_MPLAYSTILL)
PROP_MeleeState (S_MPLAY_ATK_FIST)
PROP_MissileState (255)
END_DEFAULTS
//---------------------------------------------------------------------------
class AMarineBerserk : public AScriptedMarine
{
DECLARE_STATELESS_ACTOR (AMarineBerserk, AScriptedMarine)
};
IMPLEMENT_STATELESS_ACTOR (AMarineBerserk, Doom, 9102, 0)
PROP_SpawnState (S_MPLAYSTILL)
PROP_MeleeState (S_MPLAY_ATK_BERSERK)
PROP_MissileState (255)
END_DEFAULTS
//---------------------------------------------------------------------------
class AMarineChainsaw : public AScriptedMarine
{
DECLARE_STATELESS_ACTOR (AMarineChainsaw, AScriptedMarine)
};
IMPLEMENT_STATELESS_ACTOR (AMarineChainsaw, Doom, 9103, 0)
PROP_SpawnState (S_MPLAYSTILL)
PROP_MeleeState (S_MPLAY_ATK_CHAINSAW)
PROP_MissileState (255)
END_DEFAULTS
//---------------------------------------------------------------------------
class AMarinePistol : public AScriptedMarine
{
DECLARE_STATELESS_ACTOR (AMarinePistol, AScriptedMarine)
};
IMPLEMENT_STATELESS_ACTOR (AMarinePistol, Doom, 9104, 0)
PROP_SpawnState (S_MPLAYSTILL)
PROP_MeleeState (255)
PROP_MissileState (S_MPLAY_ATK_PISTOL)
END_DEFAULTS
//---------------------------------------------------------------------------
class AMarineShotgun : public AScriptedMarine
{
DECLARE_STATELESS_ACTOR (AMarineShotgun, AScriptedMarine)
};
IMPLEMENT_STATELESS_ACTOR (AMarineShotgun, Doom, 9105, 0)
PROP_SpawnState (S_MPLAYSTILL)
PROP_MeleeState (255)
PROP_MissileState (S_MPLAY_ATK_SHOTGUN)
END_DEFAULTS
//---------------------------------------------------------------------------
class AMarineSSG : public AScriptedMarine
{
DECLARE_STATELESS_ACTOR (AMarineSSG, AScriptedMarine)
};
IMPLEMENT_STATELESS_ACTOR (AMarineSSG, Doom, 9106, 0)
PROP_SpawnState (S_MPLAYSTILL)
PROP_MeleeState (255)
PROP_MissileState (S_MPLAY_ATK_DSHOTGUN)
END_DEFAULTS
//---------------------------------------------------------------------------
class AMarineChaingun : public AScriptedMarine
{
DECLARE_STATELESS_ACTOR (AMarineChaingun, AScriptedMarine)
};
IMPLEMENT_STATELESS_ACTOR (AMarineChaingun, Doom, 9107, 0)
PROP_SpawnState (S_MPLAYSTILL)
PROP_MeleeState (255)
PROP_MissileState (S_MPLAY_ATK_CHAINGUN)
END_DEFAULTS
//---------------------------------------------------------------------------
class AMarineRocket : public AScriptedMarine
{
DECLARE_STATELESS_ACTOR (AMarineRocket, AScriptedMarine)
};
IMPLEMENT_STATELESS_ACTOR (AMarineRocket, Doom, 9108, 0)
PROP_SpawnState (S_MPLAYSTILL)
PROP_MeleeState (255)
PROP_MissileState (S_MPLAY_ATK_ROCKET)
END_DEFAULTS
//---------------------------------------------------------------------------
class AMarinePlasma : public AScriptedMarine
{
DECLARE_STATELESS_ACTOR (AMarinePlasma, AScriptedMarine)
};
IMPLEMENT_STATELESS_ACTOR (AMarinePlasma, Doom, 9109, 0)
PROP_SpawnState (S_MPLAYSTILL)
PROP_MeleeState (255)
PROP_MissileState (S_MPLAY_ATK_PLASMA)
END_DEFAULTS
//---------------------------------------------------------------------------
class AMarineRailgun : public AScriptedMarine
{
DECLARE_STATELESS_ACTOR (AMarineRailgun, AScriptedMarine)
};
IMPLEMENT_STATELESS_ACTOR (AMarineRailgun, Doom, 9110, 0)
PROP_SpawnState (S_MPLAYSTILL)
PROP_MeleeState (255)
PROP_MissileState (S_MPLAY_ATK_RAILGUN)
END_DEFAULTS
//---------------------------------------------------------------------------
class AMarineBFG : public AScriptedMarine
{
DECLARE_STATELESS_ACTOR (AMarineBFG, AScriptedMarine)
};
IMPLEMENT_STATELESS_ACTOR (AMarineBFG, Doom, 9111, 0)
PROP_SpawnState (S_MPLAYSTILL)
PROP_MeleeState (255)
PROP_MissileState (S_MPLAY_ATK_BFG)
END_DEFAULTS
//---------------------------------------------------------------------------
void AScriptedMarine::SetWeapon (EMarineWeapon type)
{
MissileState = NULL;
MeleeState = NULL;
DecalGenerator = NULL;
switch (type)
{
default:
case WEAPON_Dummy:
MissileState = &States[S_MPLAY_ATK];
DecalGenerator = GetDefaultByType (RUNTIME_CLASS(AScriptedMarine))->DecalGenerator;
break;
case WEAPON_Fist:
MeleeState = &States[S_MPLAY_ATK_FIST];
DecalGenerator = GetDefaultByType (RUNTIME_CLASS(AMarineFist))->DecalGenerator;
break;
case WEAPON_BerserkFist:
MeleeState = &States[S_MPLAY_ATK_BERSERK];
DecalGenerator = GetDefaultByType (RUNTIME_CLASS(AMarineBerserk))->DecalGenerator;
break;
case WEAPON_Chainsaw:
MeleeState = &States[S_MPLAY_ATK_CHAINSAW];
DecalGenerator = GetDefaultByType (RUNTIME_CLASS(AMarineChainsaw))->DecalGenerator;
break;
case WEAPON_Pistol:
MissileState = &States[S_MPLAY_ATK_PISTOL];
DecalGenerator = GetDefaultByType (RUNTIME_CLASS(AMarinePistol))->DecalGenerator;
break;
case WEAPON_Shotgun:
MissileState = &States[S_MPLAY_ATK_SHOTGUN];
DecalGenerator = GetDefaultByType (RUNTIME_CLASS(AMarineShotgun))->DecalGenerator;
break;
case WEAPON_SuperShotgun:
MissileState = &States[S_MPLAY_ATK_DSHOTGUN];
DecalGenerator = GetDefaultByType (RUNTIME_CLASS(AMarineSSG))->DecalGenerator;
break;
case WEAPON_Chaingun:
MissileState = &States[S_MPLAY_ATK_CHAINGUN];
DecalGenerator = GetDefaultByType (RUNTIME_CLASS(AMarineChaingun))->DecalGenerator;
break;
case WEAPON_RocketLauncher:
MissileState = &States[S_MPLAY_ATK_ROCKET];
DecalGenerator = GetDefaultByType (RUNTIME_CLASS(AMarineRocket))->DecalGenerator;
break;
case WEAPON_PlasmaRifle:
MissileState = &States[S_MPLAY_ATK_PLASMA];
DecalGenerator = GetDefaultByType (RUNTIME_CLASS(AMarinePlasma))->DecalGenerator;
break;
case WEAPON_Railgun:
MissileState = &States[S_MPLAY_ATK_RAILGUN];
DecalGenerator = GetDefaultByType (RUNTIME_CLASS(AMarineRailgun))->DecalGenerator;
break;
case WEAPON_BFG:
MissileState = &States[S_MPLAY_ATK_BFG];
DecalGenerator = GetDefaultByType (RUNTIME_CLASS(AMarineBFG))->DecalGenerator;
break;
}
}
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;
xscale = GetDefaultByType(RUNTIME_CLASS(ADoomPlayer))->xscale;
yscale = GetDefaultByType(RUNTIME_CLASS(ADoomPlayer))->yscale;
}
else
{ // Use the same sprite the passed class spawns with
SpriteOverride = sprite = GetDefaultByType (source)->SpawnState->sprite.index;
xscale = GetDefaultByType(source)->xscale;
yscale = GetDefaultByType(source)->yscale;
}
}