Classes:ScriptedMarine

From ZDoom Wiki
Jump to navigation Jump to search
Note: Wait! Stop! You do not need to copy this actor's code into your project! Here's why:
  1. This actor is already defined in GZDoom, there's no reason to define it again.
  2. In fact, trying to define an actor with the same name will cause an error (because it already exists).
  3. If you want to make your own version of this actor, use inheritance.
  4. Definitions for existing actors are put on the wiki for reference purpose only.
Marine
Actor type Monster Game MiniDoomLogoIcon.png (Doom)
DoomEd Number 9100 Class Name ScriptedMarine
Spawn ID 151 Identifier T_SCRIPTEDMARINE


Classes: ScriptedMarine
 →MarineBFG
 →MarineBerserk
 →MarineChaingun
 →MarineChainsaw
 →MarineFist
 →MarinePistol
 →MarinePlasma
 →MarineRailgun
 →MarineRocket
 →MarineSSG
 →MarineShotgun

The Scripted Marine is a duplication of the Doom player which can be controlled via scripting during cutscenes or gameplay, to simulate the player or other marines wearing the same armor. Though the actor has several attacks coded, he does not actually ever use any by default. These attack states are used, however, by its children classes.

As with all marines, you can use the special ACS functions SetMarineSprite and SetMarineWeapon to change this actor's appearance and weapon.

ZScript definition

Note: The ZScript definition below is for reference and may be different in the current version of GZDoom.The most up-to-date version of this code can be found on GZDoom GitHub.
class ScriptedMarine : Actor
{
  const MARINE_PAIN_CHANCE = 160;

  enum EMarineWeapon
  {
    WEAPON_Dummy,
    WEAPON_Fist,
    WEAPON_BerserkFist,
    WEAPON_Chainsaw,
    WEAPON_Pistol,
    WEAPON_Shotgun,
    WEAPON_SuperShotgun,
    WEAPON_Chaingun,
    WEAPON_RocketLauncher,
    WEAPON_PlasmaRifle,
    WEAPON_Railgun,
    WEAPON_BFG
  };
  
  struct WeaponStates
  {
    state melee;
    state missile;
  }

  int CurrentWeapon;
  SpriteID SpriteOverride;
  
  Default
  {
    Health 100;
    Radius 16;
    Height 56;
    Mass 100;
    Speed 8;
    Painchance MARINE_PAIN_CHANCE;
    MONSTER;
    -COUNTKILL
    Translation 0;
    Damage 100;
    DeathSound "*death";
    PainSound "*pain50";
  }
  
  States
  {
  Spawn:
    PLAY A 4 A_MarineLook;
    PLAY A 4 A_MarineNoise;
    Loop;
  Idle:
    PLAY A 4 A_MarineLook;
    PLAY A 4 A_MarineNoise;
    PLAY A 4 A_MarineLook;
    PLAY B 4 A_MarineNoise;
    PLAY B 4 A_MarineLook;
    PLAY B 4 A_MarineNoise;
    Loop;
  See:
    PLAY ABCD 4 A_MarineChase;
    Loop;

  Melee.Fist:    
    PLAY E 4 A_FaceTarget;
    PLAY E 4 A_M_Punch(1);
    PLAY A 9;
    PLAY A 0 A_M_Refire(1, "FistEnd");
    Loop;
  FistEnd:
    PLAY A 5 A_FaceTarget;
    Goto See;
  Melee.Berserk:
    PLAY E 4 A_FaceTarget;
    PLAY E 4 A_M_Punch(10);
    PLAY A 9;
    PLAY A 0 A_M_Refire(1, "FistEnd");
    Loop;
  Melee.Chainsaw:
    PLAY E 4 A_MarineNoise;
    PLAY E 4 A_M_Saw;
    PLAY E 0 A_M_SawRefire;
    goto Melee.Chainsaw+1;

  Missile:
  Missile.None:
    PLAY E 12 A_FaceTarget;
    Goto Idle;
    PLAY F 6 BRIGHT;
    Loop;
  Missile.Pistol:
    PLAY E 4 A_FaceTarget;
    PLAY F 6 BRIGHT A_M_FirePistol(1);
    PLAY A 4 A_FaceTarget;
    PLAY A 0 A_M_Refire(0, "ShootEnd");
    Goto Fireloop.Pistol;
  ShootEnd:
    PLAY A 5;
    Goto See;
  Fireloop.Pistol:
    PLAY F 6 BRIGHT A_M_FirePistol(0);
    PLAY A 4 A_FaceTarget;
    PLAY A 0 A_M_Refire(0, "ShootEnd");
    Goto Fireloop.Pistol;
  Missile.Shotgun:
    PLAY E 3 A_M_CheckAttack;
    PLAY F 7 BRIGHT A_M_FireShotgun;
    Goto See;
  Missile.SSG:
    PLAY E 3 A_M_CheckAttack;
    PLAY F 7 BRIGHT A_M_FireShotgun2;
    Goto See;
  Missile.Chaingun:
    PLAY E 4 A_FaceTarget;
    PLAY FF 4 BRIGHT A_M_FireCGun(1);
    PLAY FF 4 BRIGHT A_M_FireCGun(0);
    PLAY A 0 A_M_Refire(0, "See");
    Goto Missile.Chaingun+3;
  Missile.Rocket:
    PLAY E 8;
    PLAY F 6 BRIGHT A_M_FireMissile;
    PLAY E 6;
    PLAY A 0 A_M_Refire(0, "See");
    Loop;
  Missile.Plasma:
    PLAY E 2 A_FaceTarget;
    PLAY E 0 A_FaceTarget;
    PLAY F 3 BRIGHT A_M_FirePlasma;
    PLAY A 0 A_M_Refire(0, "See");
    Goto Missile.Plasma+1;
  Missile.Railgun:
    PLAY E 4 A_M_CheckAttack;
    PLAY F 6 BRIGHT A_M_FireRailgun;
    Goto See;
  Missile.BFG:
    PLAY E 5 A_M_BFGSound;
    PLAY EEEEE 5 A_FaceTarget;
    PLAY F 6 BRIGHT A_M_FireBFG;
    PLAY A 4 A_FaceTarget;
    PLAY A 0 A_M_Refire(0, "See");
    Loop;

  SkipAttack:
    PLAY A 1;
    Goto See;
  Pain:
    PLAY G 4;
    PLAY G 4 A_Pain;
    Goto Idle;
  Death:
    PLAY H 10;
    PLAY I 10 A_Scream;
    PLAY J 10 A_NoBlocking;
    PLAY KLM 10;
    PLAY N -1;
    Stop;
  XDeath:
    PLAY O 5;
    PLAY P 5 A_XScream;
    PLAY Q 5 A_NoBlocking;
    PLAY RSTUV 5;
    PLAY W -1;
    Stop;
  Raise:
    PLAY MLKJIH 5;
    Goto See;
  }
  
  //============================================================================
  //
  // 
  //
  //============================================================================

  private bool GetWeaponStates(int weap, out WeaponStates wstates)
  {
    static const statelabel MeleeNames[] = 
    {
      "Melee.None", "Melee.Fist", "Melee.Berserk", "Melee.Chainsaw", "Melee.Pistol", "Melee.Shotgun", 
      "Melee.SSG", "Melee.Chaingun", "Melee.Rocket", "Melee.Plasma", "Melee.Railgun", "Melee.BFG"
    };

    static const statelabel MissileNames[] = 
    {
      "Missile.None", "Missile.Fist", "Missile.Berserk", "Missile.Chainsaw", "Missile.Pistol", "Missile.Shotgun", 
      "Missile.SSG", "Missile.Chaingun", "Missile.Rocket", "Missile.Plasma", "Missile.Railgun", "Missile.BFG"
    };
    
    if (weap < WEAPON_Dummy || weap > WEAPON_BFG) weap = WEAPON_Dummy;

    wstates.melee = FindState(MeleeNames[weap], true);
    wstates.missile = FindState(MissileNames[weap], true);

    return wstates.melee != null || wstates.missile != null;
  }

  //============================================================================
  //
  // 
  //
  //============================================================================

  override void BeginPlay ()
  {
    Super.BeginPlay ();

    // Set the current weapon
    for(int i = WEAPON_Dummy; i <= WEAPON_BFG; i++)
    {
      WeaponStates wstates;
      if (GetWeaponStates(i, wstates))
      {
        if (wstates.melee == MeleeState && wstates.missile == MissileState)
        {
          CurrentWeapon = i;
        }
      }
    }
  }

  //============================================================================
  //
  // 
  //
  //============================================================================
  
  override void Tick ()
  {
    Super.Tick ();

    // Override the standard sprite, if desired
    if (SpriteOverride != 0 && sprite == SpawnState.sprite)
    {
      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:
            A_StartSound ("weapons/sshoto", CHAN_WEAPON);
            break; 
          case 28:   
            A_StartSound ("weapons/sshotl", CHAN_WEAPON);
            break;
          case 41:  
            A_StartSound ("weapons/sshotc", CHAN_WEAPON);
            break;
          }
        }
        else
        {
          special1 = 0;
        }
      }
      else
      { // Wait for a long refire time
        if (level.maptime >= special1)
        {
          special1 = 0;
        }
        else
        {
          bJustAttacked = true;
        }
      }
    }
  }

  //============================================================================
  //
  // A_M_Refire
  //
  //============================================================================

  void A_M_Refire (bool ignoremissile = false, statelabel jumpto = null)
  {
    if (target == null || target.health <= 0)
    {
      if (MissileState && random[SMarineRefire]() < 160)
      { // Look for a new target most of the time
        if (LookForPlayers (true) && CheckMissileRange ())
        { // Found somebody new and in range, so don't stop shooting
          return;
        }
      }
      if (jumpto != null) SetStateLabel (jumpto);
      else SetState(CurState + 1);
      return;
    }
    if (((ignoremissile || MissileState == null) && !CheckMeleeRange ()) ||
      !CheckSight (target) ||  random[SMarineRefire]() < 4)  // Small chance of stopping even when target not dead
    {
      if (jumpto != null) SetStateLabel (jumpto);
      else SetState(CurState + 1);
    }
  }

  //============================================================================
  //
  // A_M_SawRefire
  //
  //============================================================================

  void A_M_SawRefire ()
  {
    if (target == null || target.health <= 0 || !CheckMeleeRange ())
    {
      SetStateLabel ("See");
    }
  }

  //============================================================================
  //
  // A_MarineNoise
  //
  //============================================================================

  void A_MarineNoise ()
  {
    if (CurrentWeapon == WEAPON_Chainsaw)
    {
      A_StartSound ("weapons/sawidle", CHAN_WEAPON);
    }
  }

  //============================================================================
  //
  // A_MarineChase
  //
  //============================================================================

  void A_MarineChase ()
  {
    A_MarineNoise();
    A_Chase ();
  }

  //============================================================================
  //
  // A_MarineLook
  //
  //============================================================================

  void A_MarineLook ()
  {
    A_MarineNoise();
    A_Look();
  }

  //============================================================================
  //
  // A_M_Punch (also used in the rocket attack.)
  //
  //============================================================================

  void A_M_Punch(int damagemul)
  {
    FTranslatedLineTarget t;

    if (target == null)
      return;

    int damage = (random[SMarinePunch](1, 10) << 1) * damagemul;

    A_FaceTarget ();
    double ang = angle + random2[SMarinePunch]() * (5.625 / 256);
    double pitch = AimLineAttack (ang, DEFMELEERANGE);
    LineAttack (ang, DEFMELEERANGE, pitch, damage, 'Melee', "BulletPuff", true, t);

    // turn to face target
    if (t.linetarget)
    {
      A_StartSound ("*fist", CHAN_WEAPON);
      angle = t.angleFromSource;
    }
  }

  //============================================================================
  //
  // P_GunShot2
  //
  //============================================================================

  private void GunShot2 (bool accurate, double pitch, class<Actor> pufftype)
  {
    int damage = 5 * random[SMarineGunshot](1,3);
    double ang = angle;

    if (!accurate)
    {
      ang += Random2[SMarineGunshot]() * (5.625 / 256);
    }

    LineAttack (ang, MISSILERANGE, pitch, damage, 'Hitscan', pufftype);
  }

  //============================================================================
  //
  // A_M_FirePistol
  //
  //============================================================================

  void A_M_FirePistol (bool accurate)
  {
    if (target == null)
      return;

    A_StartSound ("weapons/pistol", CHAN_WEAPON);
    A_FaceTarget ();
    GunShot2 (accurate, AimLineAttack (angle, MISSILERANGE), "BulletPuff");
  }

  //============================================================================
  //
  // A_M_FireShotgun
  //
  //============================================================================

  void A_M_FireShotgun ()
  {
    if (target == null)
      return;

    A_StartSound ("weapons/shotgf", CHAN_WEAPON);
    A_FaceTarget ();
    double pitch = AimLineAttack (angle, MISSILERANGE);
    for (int i = 0; i < 7; ++i)
    {
      GunShot2 (false, pitch, "BulletPuff");
    }
    special1 = level.maptime + 27;
  }

  //============================================================================
  //
  // A_M_CheckAttack
  //
  //============================================================================

  void A_M_CheckAttack ()
  {
    if (special1 != 0 || target == null)
    {
      SetStateLabel ("SkipAttack");
    }
    else
    {
      A_FaceTarget ();
    }
  }

  //============================================================================
  //
  // A_M_FireShotgun2
  //
  //============================================================================

  void A_M_FireShotgun2 ()
  {
    if (target == null)
      return;

    A_StartSound ("weapons/sshotf", CHAN_WEAPON);
    A_FaceTarget ();
    double pitch = AimLineAttack (angle, MISSILERANGE);
    for (int i = 0; i < 20; ++i)
    {
      int damage = 5*(random[SMarineFireSSG](1, 3));
      double ang = angle + Random2[SMarineFireSSG]() * (11.25 / 256);

      LineAttack (ang, MISSILERANGE, pitch + Random2[SMarineFireSSG]() * (7.097 / 256), damage, 'Hitscan', "BulletPuff");
    }
    special1 = level.maptime;
  }

  //============================================================================
  //
  // A_M_FireCGun
  //
  //============================================================================

  void A_M_FireCGun(bool accurate)
  {
    if (target == null)
      return;

    A_StartSound ("weapons/chngun", CHAN_WEAPON);
    A_FaceTarget ();
    GunShot2 (accurate, AimLineAttack (angle, MISSILERANGE), "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 ()
  {
    if (target == null)
      return;

    if (CheckMeleeRange ())
    { // If too close, punch it
      A_M_Punch(1);
    }
    else
    {
      A_FaceTarget ();
      SpawnMissile (target, "Rocket");
    }
  }

  //============================================================================
  //
  // A_M_FireRailgun
  //
  //============================================================================

  void A_M_FireRailgun ()
  {
    if (target == null)
      return;

    A_MonsterRail();
    special1 = level.maptime + 50;
  }

  //============================================================================
  //
  // A_M_FirePlasma
  //
  //============================================================================

  void A_M_FirePlasma ()
  {
    if (target == null)
      return;

    A_FaceTarget ();
    SpawnMissile (target, "PlasmaBall");
    special1 = level.maptime + 20;
  }

  //============================================================================
  //
  // A_M_BFGsound
  //
  //============================================================================

  void A_M_BFGsound ()
  {
    if (target == null)
      return;

    if (special1 != 0)
    {
      SetState (SeeState);
    }
    else
    {
      A_FaceTarget ();
      A_StartSound ("weapons/bfgf", CHAN_WEAPON);
      // Don't interrupt the firing sequence
      PainChance = 0;
    }
  }

  //============================================================================
  //
  // A_M_FireBFG
  //
  //============================================================================

  void A_M_FireBFG ()
  {
    if (target == null)
      return;

    A_FaceTarget ();
    SpawnMissile (target, "BFGBall");
    special1 = level.maptime + 30;
    PainChance = MARINE_PAIN_CHANCE;
  }
    
  //---------------------------------------------------------------------------

  final void SetWeapon (int type)
  {
    WeaponStates wstates;
    if (GetWeaponStates(type, wstates))
    {
      static const class<Actor> classes[] = {
        "ScriptedMarine",
        "MarineFist",
        "MarineBerserk",
        "MarineChainsaw",
        "MarinePistol",
        "MarineShotgun",
        "MarineSSG",
        "MarineChaingun",
        "MarineRocket",
        "MarinePlasma",
        "MarineRailgun",
        "MarineBFG"
      };
      
      MeleeState = wstates.melee;
      MissileState = wstates.missile;
      DecalGenerator = GetDefaultByType(classes[type]).DecalGenerator;
    }
  }

  final void SetSprite (class<Actor> source)
  {
    if (source == null)
    { // A valid actor class wasn't passed, so use the standard sprite
      SpriteOverride = sprite = SpawnState.sprite;
      // Copy the standard scaling
      Scale = Default.Scale;
    }
    else
    { // Use the same sprite and scaling the passed class spawns with
      readonly<Actor> def = GetDefaultByType (source);
      SpriteOverride = sprite = def.SpawnState.sprite;
      Scale = def.Scale;
    }
  }
}

DECORATE definition

Note: This is legacy code, kept for archival purposes only. DECORATE is deprecated in GZDoom and is completely superseded by ZScript. GZDoom internally uses the ZScript definition above.
ACTOR ScriptedMarine native
{
  Health 100
  Radius 16
  Height 56
  Mass 100
  Speed 8
  PainChance 160
  Monster
  -COUNTKILL
  Translation 0
  Damage 100
  DeathSound "*death"
  PainSound "*pain50"

  action native A_M_Refire(bool ignoremissile=false);
  action native A_M_CheckAttack();
  action native A_MarineChase();
  action native A_MarineLook();
  action native A_MarineNoise();
  action native A_M_Punch(int force);
  action native A_M_SawRefire();
  action native A_M_FirePistol(bool accurate);
  action native A_M_FireShotgun();
  action native A_M_FireShotgun2();
  action native A_M_FireCGun(bool accurate);
  action native A_M_FireMissile();
  action native A_M_FirePlasma();
  action native A_M_FireRailgun();
  action native A_M_BFGSound();
  action native A_M_FireBFG();

  States
  {
  Spawn:
    PLAY A 4 A_MarineLook
    PLAY A 4 A_MarineNoise
    Loop
  Idle:
    PLAY A 4 A_MarineLook
    PLAY A 4 A_MarineNoise
    PLAY A 4 A_MarineLook
    PLAY B 4 A_MarineNoise
    PLAY B 4 A_MarineLook
    PLAY B 4 A_MarineNoise
    Loop
  See:
    PLAY ABCD 4 A_MarineChase
    Loop

  Melee.Fist:
    PLAY E 4 A_FaceTarget
    PLAY E 4 A_M_Punch(1)
    PLAY A 9
    PLAY A 0 A_M_Refire(1)
    Loop
    PLAY A 5 A_FaceTarget
    Goto See
  Melee.Berserk:
    PLAY E 4 A_FaceTarget
    PLAY E 4 A_M_Punch(10)
    PLAY A 9
    PLAY A 0 A_M_Refire(1)
    Loop
    PLAY A 5 A_FaceTarget
    Goto See
  Melee.Chainsaw:
    PLAY E 4 A_MarineNoise
    PLAY E 4 A_M_Saw
    PLAY E 0 A_M_SawRefire
    Goto Melee.Chainsaw+1
    PLAY A 0
    Goto See

  Missile:
  Missile.None:
    PLAY E 12 A_FaceTarget
    Goto Idle
    PLAY F 6 Bright
    Loop
  Missile.Pistol:
    PLAY E 4 A_FaceTarget
    PLAY F 6 Bright A_M_FirePistol(1)
    PLAY A 4 A_FaceTarget
    PLAY A 0 A_M_Refire
    PLAY A 5
    Goto See
  Fireloop.Pistol:
    PLAY F 6 Bright A_M_FirePistol(0)
    PLAY A 4 A_FaceTarget
    PLAY A 0 A_M_Refire
    Goto Fireloop.Pistol
    PLAY A 5
    Goto See
  Missile.Shotgun:
    PLAY E 3 A_M_CheckAttack
    PLAY F 7 Bright A_M_FireShotgun
    Goto See
  Missile.SSG:
    PLAY E 3 A_M_CheckAttack
    PLAY F 7 Bright A_M_FireShotgun2
    Goto See
  Missile.Chaingun:
    PLAY E 4 A_FaceTarget
    PLAY FF 4 Bright A_M_FireCGun(1)
    PLAY FF 4 Bright A_M_FireCGun(0)
    PLAY A 0 A_M_Refire
    Goto Missile.Chaingun+3
    PLAY A 0
    Goto See
  Missile.Rocket:
    PLAY E 8
    PLAY F 6 Bright A_M_FireMissile
    PLAY E 6
    PLAY A 0 A_M_Refire
    Loop
    PLAY A 0
    Goto See
  Missile.Plasma:
    PLAY E 2 A_FaceTarget
    PLAY E 0 A_FaceTarget
    PLAY F 3 Bright A_M_FirePlasma
    PLAY A 0 A_M_Refire
    Goto Missile.Plasma+1
    PLAY A 0
    Goto See
  Missile.Railgun:
    PLAY E 4 A_M_CheckAttack
    PLAY F 6 Bright A_M_FireRailgun
    Goto See
  Missile.BFG:
    PLAY E 5 A_M_BFGSound
    PLAY EEEEE 5 A_FaceTarget
    PLAY F 6 Bright A_M_FireBFG
    PLAY A 4 A_FaceTarget
    PLAY A 0 A_M_Refire
    Loop
    PLAY A 0
    Goto See

  SkipAttack:
    PLAY A 1
    Goto See
  Pain:
    PLAY G 4
    PLAY G 4 A_Pain
    Goto Idle
  Death:
    PLAY H 10
    PLAY I 10 A_Scream
    PLAY J 10 A_NoBlocking
    PLAY KLM 10
    PLAY N -1
    Stop
  XDeath:
    PLAY O 5
    PLAY P 5 A_XScream
    PLAY Q 5 A_NoBlocking
    PLAY RSTUV 5
    PLAY W -1
    Stop
  Raise:
    PLAY MLKJIH 5
    Goto See
  }
}