324
%{
#include "StdH.h"
#include "Models/Enemies/Walker/Walker.h"
%}

uses "Entities/EnemyBase";
uses "Entities/Projectile";

enum WalkerChar {
  0 WLC_SOLDIER   "Soldier",    // soldier
  1 WLC_SERGEANT  "Sergeant",   // sergeant
};

%{
// info structure
static EntityInfo eiWalker = {
  EIBT_FLESH, 1000.0f,
  0.0f, 5.4f, 0.0f,
  0.0f, 4.5f, 0.0f,
};

#define SIZE_SOLDIER   (0.5f)
#define SIZE_SERGEANT  (1.0f)
#define FIRE_LEFT_ARM   FLOAT3D(-2.5f, 5.0f, 0.0f)
#define FIRE_RIGHT_ARM  FLOAT3D(+2.5f, 5.0f, 0.0f)
#define FIRE_DEATH_LEFT   FLOAT3D( 0.0f, 7.0f, -2.0f)
#define FIRE_DEATH_RIGHT  FLOAT3D(3.75f, 4.2f, -2.5f)

#define WALKERSOUND(soundname) ((m_EwcChar==WLC_SOLDIER)? (SOUND_SOLDIER_##soundname) : (SOUND_SERGEANT_##soundname))
%}


class CWalker : CEnemyBase {
name      "Walker";
thumbnail "Thumbnails\\Walker.tbn";

properties:
  1 enum WalkerChar m_EwcChar   "Character" 'C' = WLC_SOLDIER,
  2 INDEX m_iLoopCounter = 0,
  3 FLOAT m_fSize = 1.0f,
  4 BOOL m_bWalkSoundPlaying = FALSE,
  5 FLOAT m_fThreatDistance = 5.0f,

  10 CSoundObject m_soFeet,
  11 CSoundObject m_soFire1,
  12 CSoundObject m_soFire2,
  13 CSoundObject m_soFire3,
  14 CSoundObject m_soFire4,
  
components:
  0 class   CLASS_BASE          "Classes\\EnemyBase.ecl",
  1 class   CLASS_PROJECTILE    "Classes\\Projectile.ecl",

 10 model   MODEL_WALKER              "Models\\Enemies\\Walker\\Walker.mdl",
 11 texture TEXTURE_WALKER_SOLDIER    "Models\\Enemies\\Walker\\Walker02.tex",
 12 texture TEXTURE_WALKER_SERGEANT   "Models\\Enemies\\Walker\\Walker01.tex",
 14 model   MODEL_LASER               "Models\\Enemies\\Walker\\Laser.mdl",
 15 texture TEXTURE_LASER             "Models\\Enemies\\Walker\\Laser.tex",
 16 model   MODEL_ROCKETLAUNCHER      "Models\\Enemies\\Walker\\RocketLauncher.mdl",
 17 texture TEXTURE_ROCKETLAUNCHER    "Models\\Enemies\\Walker\\RocketLauncher.tex",

// ************** SOUNDS **************
 50 sound   SOUND_SOLDIER_IDLE        "Models\\Enemies\\Walker\\Sounds\\Soldier\\Idle.wav",
 51 sound   SOUND_SOLDIER_SIGHT       "Models\\Enemies\\Walker\\Sounds\\Soldier\\Sight.wav",
 53 sound   SOUND_SOLDIER_FIRE_LASER  "Models\\Enemies\\Walker\\Sounds\\Soldier\\Fire.wav",
 54 sound   SOUND_SOLDIER_DEATH       "Models\\Enemies\\Walker\\Sounds\\Soldier\\Death.wav",
 55 sound   SOUND_SOLDIER_WALK        "Models\\Enemies\\Walker\\Sounds\\Soldier\\Walk.wav",

 60 sound   SOUND_SERGEANT_IDLE        "Models\\Enemies\\Walker\\Sounds\\Sergeant\\Idle.wav",
 61 sound   SOUND_SERGEANT_SIGHT       "Models\\Enemies\\Walker\\Sounds\\Sergeant\\Sight.wav",
 63 sound   SOUND_SERGEANT_FIRE_ROCKET "Models\\Enemies\\Walker\\Sounds\\Sergeant\\Fire.wav",
 64 sound   SOUND_SERGEANT_DEATH       "Models\\Enemies\\Walker\\Sounds\\Sergeant\\Death.wav",
 65 sound   SOUND_SERGEANT_WALK        "Models\\Enemies\\Walker\\Sounds\\Sergeant\\Walk.wav",

 /*
 70 model   MODEL_WALKER_HEAD1   "Models\\Enemies\\Walker\\Debris\\Head.mdl",
 71 model   MODEL_WALKER_HEAD2   "Models\\Enemies\\Walker\\Debris\\Head2.mdl",
 72 model   MODEL_WALKER_LEG     "Models\\Enemies\\Walker\\Debris\\Leg.mdl",
 */

functions:
  // describe how this enemy killed player
  virtual CTString GetPlayerKillDescription(const CTString &strPlayerName, const EDeath &eDeath)
  {
    CTString str;
    str.PrintF(TRANS("A Biomech blew %s away"), strPlayerName);
    return str;
  }

  virtual const CTFileName &GetComputerMessageName(void) const {
    static DECLARE_CTFILENAME(fnmSoldier,  "Data\\Messages\\Enemies\\WalkerSmall.txt");
    static DECLARE_CTFILENAME(fnmSergeant, "Data\\Messages\\Enemies\\WalkerBig.txt");
    switch(m_EwcChar) {
    default: ASSERT(FALSE);
    case WLC_SOLDIER:   return fnmSoldier;
    case WLC_SERGEANT: return fnmSergeant;
    }
  }
  // overridable function to get range for switching to another player
  FLOAT GetThreatDistance(void)
  {
    return m_fThreatDistance;
  }

  BOOL ForcesCannonballToExplode(void)
  {
    if (m_EwcChar==WLC_SERGEANT) {
      return TRUE;
    }
    return CEnemyBase::ForcesCannonballToExplode();
  }

  void Precache(void) {
    CEnemyBase::Precache();

    PrecacheModel(MODEL_WALKER);

    if (m_EwcChar==WLC_SOLDIER)
    {
      // sounds
      PrecacheSound(SOUND_SOLDIER_IDLE );
      PrecacheSound(SOUND_SOLDIER_SIGHT);
      PrecacheSound(SOUND_SOLDIER_DEATH);
      PrecacheSound(SOUND_SOLDIER_FIRE_LASER);
      PrecacheSound(SOUND_SOLDIER_WALK);
      // model's texture
      PrecacheTexture(TEXTURE_WALKER_SOLDIER);
      // weapon
      PrecacheModel(MODEL_LASER);
      PrecacheTexture(TEXTURE_LASER);
      // projectile
      PrecacheClass(CLASS_PROJECTILE, PRT_CYBORG_LASER);
    }
    else
    {
      // sounds
      PrecacheSound(SOUND_SERGEANT_IDLE);
      PrecacheSound(SOUND_SERGEANT_SIGHT);
      PrecacheSound(SOUND_SERGEANT_DEATH);
      PrecacheSound(SOUND_SERGEANT_FIRE_ROCKET);
      PrecacheSound(SOUND_SERGEANT_WALK);
      // model's texture
      PrecacheTexture(TEXTURE_WALKER_SERGEANT);
      // weapon
      PrecacheModel(MODEL_ROCKETLAUNCHER);
      PrecacheTexture(TEXTURE_ROCKETLAUNCHER);
      // projectile
      PrecacheClass(CLASS_PROJECTILE, PRT_WALKER_ROCKET);
    }
  };
  /* Entity info */
  void *GetEntityInfo(void) {
    return &eiWalker;
  };

  FLOAT GetCrushHealth(void)
  {
    if (m_EwcChar==WLC_SERGEANT) {
      return 100.0f;
    }
    return 0.0f;
  }

  /* Receive damage */
  void ReceiveDamage(CEntity *penInflictor, enum DamageType dmtType,
    FLOAT fDamageAmmount, const FLOAT3D &vHitPoint, const FLOAT3D &vDirection) 
  {
    // walker can't harm walker
    if (!IsOfClass(penInflictor, "Walker") ||
      ((CWalker*)penInflictor)->m_EwcChar!=m_EwcChar) {
      CEnemyBase::ReceiveDamage(penInflictor, dmtType, fDamageAmmount, vHitPoint, vDirection);
    }
  };


  // virtual anim functions
  void StandingAnim(void) {
    DeactivateWalkingSound();
    StartModelAnim(WALKER_ANIM_STAND01, AOF_LOOPING|AOF_NORESTART);
  };
  void StandingAnimFight(void)
  {
    DeactivateWalkingSound();
    StartModelAnim(WALKER_ANIM_IDLEFIGHT, AOF_LOOPING|AOF_NORESTART);
  }
  void WalkingAnim(void) {
    ActivateWalkingSound();
    if (m_EwcChar==WLC_SERGEANT) {
      StartModelAnim(WALKER_ANIM_WALKBIG, AOF_LOOPING|AOF_NORESTART);
    } else {
      StartModelAnim(WALKER_ANIM_WALK, AOF_LOOPING|AOF_NORESTART);
    }
  };
  void RunningAnim(void) {
    WalkingAnim();
  };
  void RotatingAnim(void) {
    WalkingAnim();
  };

  // virtual sound functions
  void IdleSound(void) {
    PlaySound(m_soSound, WALKERSOUND(IDLE), SOF_3D);
  };
  void SightSound(void) {
    PlaySound(m_soSound, WALKERSOUND(SIGHT), SOF_3D);
  };
  void DeathSound(void) {
    PlaySound(m_soSound, WALKERSOUND(DEATH), SOF_3D);
  };

  // walking sounds
  void ActivateWalkingSound(void)
  {
    if (!m_bWalkSoundPlaying) {
      PlaySound(m_soFeet, WALKERSOUND(WALK), SOF_3D|SOF_LOOP);
      m_bWalkSoundPlaying = TRUE;
    }
  }
  void DeactivateWalkingSound(void)
  {
    m_soFeet.Stop();
    m_bWalkSoundPlaying = FALSE;
  }

  // fire death rocket
  void FireDeathRocket(FLOAT3D &vPos) {
    CPlacement3D plRocket;
    plRocket.pl_PositionVector = vPos;
    plRocket.pl_OrientationAngle = ANGLE3D(0, -5.0f-FRnd()*10.0f, 0);
    plRocket.RelativeToAbsolute(GetPlacement());
    CEntityPointer penProjectile = CreateEntity(plRocket, CLASS_PROJECTILE);
    ELaunchProjectile eLaunch;
    eLaunch.penLauncher = this;
    eLaunch.prtType = PRT_WALKER_ROCKET;
    penProjectile->Initialize(eLaunch);
  };
  // fire death laser
  void FireDeathLaser(FLOAT3D &vPos) {
    CPlacement3D plLaser;
    plLaser.pl_PositionVector = vPos;
    plLaser.pl_OrientationAngle = ANGLE3D(0, -5.0f-FRnd()*10.0f, 0);
    plLaser.RelativeToAbsolute(GetPlacement());
    CEntityPointer penProjectile = CreateEntity(plLaser, CLASS_PROJECTILE);
    ELaunchProjectile eLaunch;
    eLaunch.penLauncher = this;
    eLaunch.prtType = PRT_CYBORG_LASER;
    penProjectile->Initialize(eLaunch);
  };



  // adjust sound and watcher parameters here if needed
  void EnemyPostInit(void) 
  {
    // set sound default parameters
    m_soSound.Set3DParameters(160.0f, 50.0f, 1.0f, 1.0f);
    m_soFeet.Set3DParameters(160.0f, 50.0f, 1.0f, 1.0f);
    m_soFire1.Set3DParameters(160.0f, 50.0f, 1.0f, 1.0f);
    m_soFire2.Set3DParameters(160.0f, 50.0f, 1.0f, 1.0f);
    m_soFire3.Set3DParameters(160.0f, 50.0f, 1.0f, 1.0f);
    m_soFire4.Set3DParameters(160.0f, 50.0f, 1.0f, 1.0f);
  };

/************************************************************
 *                 BLOW UP FUNCTIONS                        *
 ************************************************************/
  // spawn body parts
/*  void BlowUp(void)
  {
    // get your size
    FLOATaabbox3D box;
    GetBoundingBox(box);
    FLOAT fEntitySize = box.Size().MaxNorm();

    FLOAT3D vNormalizedDamage = m_vDamage-m_vDamage*(m_fBlowUpAmount/m_vDamage.Length());
    vNormalizedDamage /= Sqrt(vNormalizedDamage.Length());

    vNormalizedDamage *= 0.75f;
    FLOAT3D vBodySpeed = en_vCurrentTranslationAbsolute-en_vGravityDir*(en_vGravityDir%en_vCurrentTranslationAbsolute);

    // spawn debris
    Debris_Begin(EIBT_FLESH, DPT_NONE, BET_NONE, fEntitySize, vNormalizedDamage, vBodySpeed, 5.0f, 2.0f);
    Debris_Spawn(this, this, MODEL_WALKER_HEAD1, TEXTURE_WALKER_SOLDIER, 0, 0, 0, 0, 0.1250f,
      FLOAT3D(FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f));
    Debris_Spawn(this, this, MODEL_WALKER_HEAD2, TEXTURE_WALKER_SOLDIER, 0, 0, 0, 0, 0.125f,
      FLOAT3D(FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f));
    Debris_Spawn(this, this, MODEL_WALKER_LEG, TEXTURE_WALKER_SOLDIER, 0, 0, 0, 0, 0.125f,
      FLOAT3D(FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f));
    Debris_Spawn(this, this, MODEL_WALKER_LEG, TEXTURE_WALKER_SOLDIER, 0, 0, 0, 0, 0.125f,
      FLOAT3D(FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f));

    // hide yourself (must do this after spawning debris)
    SwitchToEditorModel();
    SetPhysicsFlags(EPF_MODEL_IMMATERIAL);
    SetCollisionFlags(ECF_IMMATERIAL);
  };*/

procedures:
/************************************************************
 *                A T T A C K   E N E M Y                   *
 ************************************************************/
  Fire(EVoid) : CEnemyBase::Fire {
    DeactivateWalkingSound();
    // to fire
    StartModelAnim(WALKER_ANIM_TOFIRE, 0);
    m_fLockOnEnemyTime = GetModelObject()->GetAnimLength(WALKER_ANIM_TOFIRE);
    autocall CEnemyBase::LockOnEnemy() EReturn;

    // sergeant 4 rockets
    if (m_EwcChar==WLC_SERGEANT) {
      StartModelAnim(WALKER_ANIM_FIRERIGHT, AOF_LOOPING);
      ShootProjectile(PRT_WALKER_ROCKET, FIRE_RIGHT_ARM*m_fSize, ANGLE3D(0, 0, 0));
      PlaySound(m_soFire1, SOUND_SERGEANT_FIRE_ROCKET, SOF_3D);
      if (GetSP()->sp_gdGameDifficulty<=CSessionProperties::GD_EASY) {
        m_fLockOnEnemyTime = 1.0f;
      } else {
        m_fLockOnEnemyTime = 0.5f;
      }
      autocall CEnemyBase::LockOnEnemy() EReturn;
      StartModelAnim(WALKER_ANIM_FIRELEFT, AOF_LOOPING);
      ShootProjectile(PRT_WALKER_ROCKET, FIRE_LEFT_ARM*m_fSize, ANGLE3D(0, 0, 0));
      PlaySound(m_soFire2, SOUND_SERGEANT_FIRE_ROCKET, SOF_3D);

//      m_fLockOnEnemyTime = 0.25f;
//      autocall CEnemyBase::LockOnEnemy() EReturn;
    } 
    if (m_EwcChar==WLC_SOLDIER) {
      if (GetSP()->sp_gdGameDifficulty<=CSessionProperties::GD_EASY) {
        m_iLoopCounter = 4;
      } else {
        m_iLoopCounter = 8;
      }
      while(m_iLoopCounter>0) {
        if (m_iLoopCounter%2) {
          StartModelAnim(WALKER_ANIM_FIRELEFT, AOF_LOOPING);
          ShootProjectile(PRT_CYBORG_LASER, FIRE_LEFT_ARM*m_fSize, ANGLE3D(0, 0, 0));
        } else {
          StartModelAnim(WALKER_ANIM_FIRERIGHT, AOF_LOOPING);
          ShootProjectile(PRT_CYBORG_LASER, FIRE_RIGHT_ARM*m_fSize, ANGLE3D(0, 0, 0));
        }
        INDEX iChannel = m_iLoopCounter%4;
        if (iChannel==0) {
          PlaySound(m_soFire1, SOUND_SOLDIER_FIRE_LASER, SOF_3D);
        } else if (iChannel==1) {
          PlaySound(m_soFire2, SOUND_SOLDIER_FIRE_LASER, SOF_3D);
        } else if (iChannel==2) {
          PlaySound(m_soFire3, SOUND_SOLDIER_FIRE_LASER, SOF_3D);
        } else if (iChannel==3) {
          PlaySound(m_soFire4, SOUND_SOLDIER_FIRE_LASER, SOF_3D);
        }
        if (m_iLoopCounter>1) {
          if (GetSP()->sp_gdGameDifficulty<=CSessionProperties::GD_EASY) {
            m_fLockOnEnemyTime = 0.4f;
          } else {
            m_fLockOnEnemyTime = 0.1f;
          }
          autocall CEnemyBase::LockOnEnemy() EReturn;
        }
        m_iLoopCounter--;
      }
    }
    StopMoving();

    MaybeSwitchToAnotherPlayer();

    // from fire
    StartModelAnim(WALKER_ANIM_FROMFIRE, 0);
    autowait(GetModelObject()->GetAnimLength(WALKER_ANIM_FROMFIRE));

    // wait for a while
    StandingAnimFight();
    autowait(FRnd()*0.1f+0.1f);

    return EReturn();
  };



/************************************************************
 *                    D  E  A  T  H                         *
 ************************************************************/
  Death(EVoid) : CEnemyBase::Death {
    // stop moving
    StopMoving();
    DeathSound();     // death sound
    DeactivateWalkingSound();

    // set physic flags
    SetCollisionFlags(ECF_MODEL);
    SetFlags(GetFlags() | ENF_SEETHROUGH);

    // death notify (change collision box)
    ChangeCollisionBoxIndexWhenPossible(WALKER_COLLISION_BOX_DEATH);

    // start death anim
    StartModelAnim(WALKER_ANIM_DEATH, 0);
    autowait(0.9f);

    // one rocket/laser from left or right arm
    if (m_EwcChar==WLC_SERGEANT) {
      if (IRnd()&1) {
        FireDeathRocket(FIRE_DEATH_RIGHT*m_fSize);
      } else {
        FireDeathRocket(FIRE_DEATH_LEFT*m_fSize);
      }
      PlaySound(m_soSound, SOUND_SERGEANT_FIRE_ROCKET, SOF_3D);
    }
    if (m_EwcChar==WLC_SOLDIER) {
      if (IRnd()&1) {
        FireDeathLaser(FIRE_DEATH_RIGHT*m_fSize);
      } else {
        FireDeathLaser(FIRE_DEATH_LEFT*m_fSize);
      }
      PlaySound(m_soFire2, SOUND_SOLDIER_FIRE_LASER, SOF_3D);
    }
    autowait(0.6f);

    return EEnd();
  };

/************************************************************
 *                       M  A  I  N                         *
 ************************************************************/
  Main(EVoid) {
    // declare yourself as a model
    InitAsModel();
    SetPhysicsFlags(EPF_MODEL_WALKING);
    SetCollisionFlags(ECF_MODEL);
    SetFlags(GetFlags()|ENF_ALIVE);
    if (m_EwcChar==WLC_SERGEANT) {
      SetHealth(750.0f);
      m_fMaxHealth = 750.0f;
    } else {
      SetHealth(150.0f);
      m_fMaxHealth = 150.0f;
    }
    en_fDensity = 3000.0f;

    m_sptType = SPT_ELECTRICITY_SPARKS;

    // set your appearance
    SetModel(MODEL_WALKER);
    if (m_EwcChar==WLC_SERGEANT) {
      m_fSize = 1.0f;
      SetModelMainTexture(TEXTURE_WALKER_SERGEANT);
      AddAttachment(WALKER_ATTACHMENT_ROCKETLAUNCHER_LT, MODEL_ROCKETLAUNCHER, TEXTURE_ROCKETLAUNCHER);
      AddAttachment(WALKER_ATTACHMENT_ROCKETLAUNCHER_RT, MODEL_ROCKETLAUNCHER, TEXTURE_ROCKETLAUNCHER);
      GetModelObject()->StretchModel(FLOAT3D(1,1,1));
      ModelChangeNotify();
      CModelObject *pmoRight = &GetModelObject()->GetAttachmentModel(WALKER_ATTACHMENT_ROCKETLAUNCHER_RT)->amo_moModelObject;
      pmoRight->StretchModel(FLOAT3D(-1,1,1));
      m_fBlowUpAmount = 1E10f;
      m_iScore = 7500;
      m_fThreatDistance = 15;
    } else {
      m_fSize = 0.5f;
      SetModelMainTexture(TEXTURE_WALKER_SOLDIER);
      AddAttachment(WALKER_ATTACHMENT_LASER_LT, MODEL_LASER, TEXTURE_LASER);
      AddAttachment(WALKER_ATTACHMENT_LASER_RT, MODEL_LASER, TEXTURE_LASER);
      GetModelObject()->StretchModel(FLOAT3D(0.5f,0.5f,0.5f));
      ModelChangeNotify();
      CModelObject *pmoRight = &GetModelObject()->GetAttachmentModel(WALKER_ATTACHMENT_LASER_RT)->amo_moModelObject;
      pmoRight->StretchModel(FLOAT3D(-0.5f,0.5f,0.5f));
      m_fBlowUpAmount = 1E10f;
      //m_fBlowUpAmount = 100.0f;
      //m_bRobotBlowup = TRUE;
      m_iScore = 2000;
      m_fThreatDistance = 5;
    }
    if (m_fStepHeight==-1) {
      m_fStepHeight = 4.0f;
    }

    StandingAnim();
    // setup moving speed
    m_fWalkSpeed = FRnd()*1.5f + 9.0f;
    m_aWalkRotateSpeed = AngleDeg(FRnd()*50.0f + 500.0f);
    m_fAttackRunSpeed = m_fWalkSpeed;
    m_aAttackRotateSpeed = m_aWalkRotateSpeed/2;
    m_fCloseRunSpeed = m_fWalkSpeed;
    m_aCloseRotateSpeed = m_aWalkRotateSpeed/2;
    m_fWalkSpeed/=2.0f;
    // setup attack distances
    m_fAttackDistance = 150.0f;
    m_fCloseDistance = 0.0f;
    m_fStopDistance = 15.0f;
    m_fAttackFireTime = 3.0f;
    m_fCloseFireTime = 1.0f;
    m_fIgnoreRange = 300.0f;
    // damage/explode properties
    m_fBodyParts = 8;
    m_fDamageWounded = 100000.0f;

    // continue behavior in base class
    jump CEnemyBase::MainLoop();
  };
};
