313
%{
#include "Entities/StdH/StdH.h"
%}

uses "Entities/EnemyBase";
uses "Entities/Debris";
uses "Entities/EnemyMarker";


enum EnemyDiveType {
  0 EDT_GROUND_ONLY         "Ground only",                    // can't dive
  1 EDT_DIVE_ONLY           "Dive only",                      // always dive can't walk
  2 EDT_GROUND_DIVE         "Ground and dive",                // dive and walk
};


class export CEnemyDive : CEnemyBase {
name      "Enemy Dive";
thumbnail "";

properties:
  1 enum EnemyDiveType m_EedtType "Type" 'T' = EDT_DIVE_ONLY, // dive type
  2 BOOL m_bInLiquid              "In liquid" 'Q' = TRUE,     // entity is in liquid

 // moving/attack properties - CAN BE SET
 // these following must be ordered exactly like this for GetProp() to function
 10 FLOAT m_fDiveWalkSpeed = 1.0f,                    // dive walk speed
 11 ANGLE m_aDiveWalkRotateSpeed = AngleDeg(10.0f),   // dive walk rotate speed
 12 FLOAT m_fDiveAttackRunSpeed = 1.0f,               // dive attack run speed
 13 ANGLE m_aDiveAttackRotateSpeed = AngleDeg(10.0f), // dive attack rotate speed
 14 FLOAT m_fDiveCloseRunSpeed = 1.0f,                // dive close run speed
 15 ANGLE m_aDiveCloseRotateSpeed = AngleDeg(10.0f),  // dive close rotate speed
 20 FLOAT m_fDiveAttackDistance = 50.0f,     // dive attack distance mode
 21 FLOAT m_fDiveCloseDistance = 10.0f,      // dive close distance mode
 22 FLOAT m_fDiveAttackFireTime = 2.0f,      // dive attack distance fire time
 23 FLOAT m_fDiveCloseFireTime = 1.0f,       // dive close distance fire time
 24 FLOAT m_fDiveStopDistance = 0.0f,        // dive stop moving toward enemy if closer than stop distance
 25 FLOAT m_fDiveIgnoreRange = 200.0f,       // dive cease attack if enemy farther
 26 FLOAT m_fDiveLockOnEnemyTime = 0.0f,     // dive time needed to fire

components:
  1 class   CLASS_BASE            "Classes\\EnemyBase.ecl",

functions:
  // overridable function for access to different properties of derived classes (flying/diving)
  virtual FLOAT &GetProp(FLOAT &m_fBase)
  {
    if (m_bInLiquid) {
      return *((&m_fBase)+(&m_fDiveWalkSpeed-&m_fWalkSpeed));
    } else {
      return m_fBase;
    }
  }

  // diving enemies never use pathfinding
  void StartPathFinding(void)
  {
    m_dtDestination=DT_PLAYERSPOTTED;
    m_vPlayerSpotted = PlayerDestinationPos();
  }

  virtual void AdjustDifficulty(void)
  {
    FLOAT fMoveSpeed = GetSP()->sp_fEnemyMovementSpeed;
    FLOAT fAttackSpeed = GetSP()->sp_fEnemyMovementSpeed;
//    m_fDiveWalkSpeed *= fMoveSpeed;
//    m_aDiveWalkRotateSpeed *= fMoveSpeed;
    m_fDiveAttackRunSpeed *= fMoveSpeed;
    m_aDiveAttackRotateSpeed *= fMoveSpeed;
    m_fDiveCloseRunSpeed *= fMoveSpeed;
    m_aDiveCloseRotateSpeed *= fMoveSpeed;
    m_fDiveAttackFireTime *= 1/fAttackSpeed;
    m_fDiveCloseFireTime *= 1/fAttackSpeed;
    m_fDiveLockOnEnemyTime *= 1/fAttackSpeed;

    CEnemyBase::AdjustDifficulty();
  }
  // close attack if possible
  virtual BOOL CanHitEnemy(CEntity *penTarget, FLOAT fCosAngle) {
    if (IsInPlaneFrustum(penTarget, fCosAngle)) {
      return IsVisibleCheckAll(penTarget);
    }
    return FALSE;
  };
/************************************************************
 *                        POST MOVING                       *
 ************************************************************/
  void PostMoving(void) {
    CEnemyBase::PostMoving();
    // change to liquid
    if (m_EedtType!=EDT_GROUND_ONLY && !m_bInLiquid && en_fImmersionFactor>0.9f &&
        (GetWorld()->wo_actContentTypes[en_iDnContent].ct_ulFlags&CTF_SWIMABLE)) {
      m_bInLiquid = TRUE;
      ChangeCollisionToLiquid();
      SendEvent(ERestartAttack());
    }
    // change to ground
    if (m_EedtType!=EDT_DIVE_ONLY && m_bInLiquid && (en_fImmersionFactor<0.5f || en_fImmersionFactor==1.0f) &&
        en_penReference!=NULL && !(GetWorld()->wo_actContentTypes[en_iUpContent].ct_ulFlags&CTF_SWIMABLE)) {
      m_bInLiquid = FALSE;
      ChangeCollisionToGround();
      SendEvent(ERestartAttack());
    }
  };



/************************************************************
 *                     MOVING FUNCTIONS                     *
 ************************************************************/
  // set desired rotation and translation to go/orient towards desired position
  // and get the resulting movement type
  virtual ULONG SetDesiredMovement(void) 
  {
    // if not in air
    if (!m_bInLiquid) {
      // use base class
      return CEnemyBase::SetDesiredMovement();
    }

    // get base rotation from base class
    ULONG ulFlags = CEnemyBase::SetDesiredMovement();

    // if we may move
    if (m_fMoveSpeed>0.0f) {
      // fix translation for 3d movement
      FLOAT3D vTranslation = (m_vDesiredPosition - GetPlacement().pl_PositionVector) * !en_mRotation;
      vTranslation(1) = 0.0f;
      if (vTranslation(3)>0) { 
        vTranslation(3) = 0.0f;
      }
      vTranslation.Normalize();
      vTranslation *= m_fMoveSpeed;
      SetDesiredTranslation(vTranslation);
    }

    return ulFlags;
  }

  // check whether may move while attacking
  BOOL MayMoveToAttack(void) 
  {
    return WouldNotLeaveAttackRadius();
  }

/************************************************************
 *                CLASS SUPPORT FUNCTIONS                   *
 ************************************************************/
  // set entity position
  void SetEntityPosition() {
    switch (m_EedtType) {
      case EDT_GROUND_ONLY:         // can't dive
        m_bInLiquid = FALSE;
        break;
      case EDT_DIVE_ONLY:           // always dive can't walk
        m_bInLiquid = TRUE;
        break;
      case EDT_GROUND_DIVE:         // dive and walk
        break;
    }

    // in liquid
    if (m_bInLiquid) {
      ChangeCollisionToLiquid();
    } else {
      ChangeCollisionToGround();
    }

    StandingAnim();
  };


/************************************************************
 *          VIRTUAL FUNCTIONS THAT NEED OVERRIDE            *
 ************************************************************/
  virtual void ChangeCollisionToLiquid(void) {}
  virtual void ChangeCollisionToGround(void) {}



procedures:
/************************************************************
 *      PROCEDURES WHEN NO ANY SPECIAL ACTION               *
 ************************************************************/
/************************************************************
 *                 ATTACK ENEMY PROCEDURES                  *
 ************************************************************/
  // this is called to hit the player when near
  Hit(EVoid) : CEnemyBase::Hit
  { 
    if (m_bInLiquid) {
      jump DiveHit();
    } else {
      jump GroundHit();
    }
  }

  // this is called to shoot at player when far away or otherwise unreachable
  Fire(EVoid) : CEnemyBase::Fire
  { 
    if (m_bInLiquid) {
      jump DiveFire();
    } else {
      jump GroundFire();
    }
  }

/************************************************************
 *                M  A  I  N    L  O  O  P                  *
 ************************************************************/
  // main loop
  MainLoop(EVoid) : CEnemyBase::MainLoop {
    SetEntityPosition();
    jump CEnemyBase::MainLoop();
  };

  // dummy main
  Main(EVoid) {
    return;
  };



/************************************************************
 *          VIRTUAL PROCEDURES THAT NEED OVERRIDE           *
 ************************************************************/
  // this is called to hit the player when near and you are on ground
  GroundHit(EVoid) 
  { 
    return EReturn(); 
  }
  // this is called to shoot at player when far away or otherwise unreachable and you are on ground
  GroundFire(EVoid) 
  { 
    return EReturn(); 
  }

  // this is called to hit the player when near and you are in water
  DiveHit(EVoid) 
  { 
    return EReturn(); 
  }

  // this is called to shoot at player when far away or otherwise unreachable and you are in water
  DiveFire(EVoid) 
  { 
    return EReturn(); 
  }
};