/* Copyright (c) 2002-2012 Croteam Ltd. 
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation


This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */

323
%{
#include "EntitiesMP/StdH/StdH.h"
#include "Models/Enemies/Eyeman/Eyeman.h"
%}

uses "EntitiesMP/EnemyFly";

enum EyemanChar {
  0 EYC_SOLDIER   "Soldier",    // soldier
  1 EYC_SERGEANT  "Sergeant",   // sergeant
};

enum EyemanEnv {
  0 EYE_NORMAL    "Normal",
  1 EYE_LAVA      "Lava",
};

%{
// info structure
static EntityInfo eiEyemanBig = {
  EIBT_FLESH, 140.0f,
  0.0f, 1.4f, 0.0f,
  0.0f, 1.0f, 0.0f,
};
static EntityInfo eiEyemanSmall = {
  EIBT_FLESH, 120.0f,
  0.0f, 1.4f, 0.0f,
  0.0f, 1.0f, 0.0f,
};

#define BITE_AIR    3.0f
#define HIT_GROUND  2.0f
#define FIRE_GROUND   FLOAT3D(0.75f, 1.5f, -1.25f)
%}


class CEyeman : CEnemyFly {
name      "Eyeman";
thumbnail "Thumbnails\\Eyeman.tbn";

properties:
  1 enum EyemanChar m_EecChar "Character" 'C' = EYC_SOLDIER,      // character
  2 BOOL m_bInvisible "Invisible" 'I'=FALSE,
  3 enum EyemanEnv m_eeEnv "Environment" 'E' = EYE_NORMAL,
  4 BOOL m_bMumbleSoundPlaying = FALSE,
  5 CSoundObject m_soMumble,

components:
  0 class   CLASS_BASE        "Classes\\EnemyFly.ecl",
  1 model   MODEL_EYEMAN      "Models\\Enemies\\Eyeman\\Eyeman.mdl",
  2 texture TEXTURE_EYEMAN_SOLDIER    "Models\\Enemies\\Eyeman\\Eyeman4.tex",
  3 texture TEXTURE_EYEMAN_SERGEANT   "Models\\Enemies\\Eyeman\\Eyeman5.tex",
  5 texture TEXTURE_EYEMAN_LAVA   "Models\\Enemies\\Eyeman\\Eyeman6.tex",
  4 class   CLASS_BASIC_EFFECT    "Classes\\BasicEffect.ecl",

// ************** SOUNDS **************
 50 sound   SOUND_IDLE      "Models\\Enemies\\Eyeman\\Sounds\\Idle.wav",
 51 sound   SOUND_SIGHT     "Models\\Enemies\\Eyeman\\Sounds\\Sight.wav",
 52 sound   SOUND_WOUND     "Models\\Enemies\\Eyeman\\Sounds\\Wound.wav",
 53 sound   SOUND_BITE      "Models\\Enemies\\Eyeman\\Sounds\\Bite.wav",
 54 sound   SOUND_PUNCH     "Models\\Enemies\\Eyeman\\Sounds\\Punch.wav",
 55 sound   SOUND_DEATH     "Models\\Enemies\\Eyeman\\Sounds\\Death.wav",
 56 sound   SOUND_MUMBLE    "Models\\Enemies\\Eyeman\\Sounds\\Mumble.wav",

 /*
 60 model   MODEL_EYEMAN_BODY   "Models\\Enemies\\Eyeman\\Debris\\Torso.mdl",
 61 model   MODEL_EYEMAN_HAND   "Models\\Enemies\\Eyeman\\Debris\\Arm.mdl",
 62 model   MODEL_EYEMAN_LEGS   "Models\\Enemies\\Eyeman\\Debris\\Leg.mdl",
 */

functions:
  // describe how this enemy killed player
  virtual CTString GetPlayerKillDescription(const CTString &strPlayerName, const EDeath &eDeath)
  {
    CTString str;
    if (m_bInAir) {
      str.PrintF(TRANSV("A Gnaar bit %s to death"), (const char *) strPlayerName);
    } else {
      str.PrintF(TRANSV("%s was beaten up by a Gnaar"), (const char *) strPlayerName);
    }
    return str;
  }
  void Precache(void) {
    CEnemyBase::Precache();
    PrecacheSound(SOUND_IDLE );
    PrecacheSound(SOUND_SIGHT);
    PrecacheSound(SOUND_WOUND);
    PrecacheSound(SOUND_BITE );
    PrecacheSound(SOUND_PUNCH);
    PrecacheSound(SOUND_DEATH);
    PrecacheSound(SOUND_MUMBLE);
  };

  /* Entity info */
  void *GetEntityInfo(void) {
    if (m_EecChar==EYC_SERGEANT) {
      return &eiEyemanBig;
    } else {
      return &eiEyemanSmall;
    }
  };

  /* Receive damage */
  void ReceiveDamage(CEntity *penInflictor, enum DamageType dmtType,
    FLOAT fDamageAmmount, const FLOAT3D &vHitPoint, const FLOAT3D &vDirection) 
  {
    // eyeman can't harm eyeman
    if (!IsOfClass(penInflictor, "Eyeman")) {
      CEnemyFly::ReceiveDamage(penInflictor, dmtType, fDamageAmmount, vHitPoint, vDirection);
      // if died of chainsaw
      if (dmtType==DMT_CHAINSAW && GetHealth()<=0) {
        // must always blowup
        m_fBlowUpAmount = 0;
      }
    }
  };

  /* Fill in entity statistics - for AI purposes only */
  BOOL FillEntityStatistics(EntityStats *pes)
  {
    CEnemyBase::FillEntityStatistics(pes);
    switch(m_EecChar) {
    case EYC_SERGEANT: { pes->es_strName+=" Sergeant"; } break;
    case EYC_SOLDIER : { pes->es_strName+=" Soldier"; } break;
    }
    if (m_bInvisible) {
      pes->es_strName+=" Invisible";
    }
    return TRUE;
  }

  virtual const CTFileName &GetComputerMessageName(void) const {
    static DECLARE_CTFILENAME(fnmSergeant, "Data\\Messages\\Enemies\\EyemanGreen.txt");
    static DECLARE_CTFILENAME(fnmSoldier , "Data\\Messages\\Enemies\\EyemanPurple.txt");
    switch(m_EecChar) {
    default: ASSERT(FALSE);
    case EYC_SERGEANT: return fnmSergeant;
    case EYC_SOLDIER : return fnmSoldier;
    }
  };
  /* Adjust model shading parameters if needed. */
  BOOL AdjustShadingParameters(FLOAT3D &vLightDirection, COLOR &colLight, COLOR &colAmbient)
  {
    // no shadows for invisibles
    if (m_bInvisible) {
      colAmbient = C_WHITE;
      return FALSE;
    } else {
      return CEnemyBase::AdjustShadingParameters(vLightDirection, colLight, colAmbient);
    }
  }

  // damage anim
  INDEX AnimForDamage(FLOAT fDamage) {
    DeactivateMumblingSound();
    INDEX iAnim;
    if (m_bInAir) {
      switch (IRnd()%2) {
        case 0: iAnim = EYEMAN_ANIM_MORPHWOUND01; break;
        case 1: iAnim = EYEMAN_ANIM_MORPHWOUND02; break;
        default: ASSERTALWAYS("Eyeman unknown fly damage");
      }
    } else {
      FLOAT3D vFront;
      GetHeadingDirection(0, vFront);
      FLOAT fDamageDir = m_vDamage%vFront;
      if (Abs(fDamageDir)<=10) {
        switch (IRnd()%3) {
          case 0: iAnim = EYEMAN_ANIM_WOUND03; break;
          case 1: iAnim = EYEMAN_ANIM_WOUND06; break;
          case 2: iAnim = EYEMAN_ANIM_WOUND07; break;
        }
      } else {
        if (fDamageDir<0) {
          iAnim = EYEMAN_ANIM_FALL01;
        } else {
          iAnim = EYEMAN_ANIM_FALL02;
        }
      }
    }
    StartModelAnim(iAnim, 0);
    return iAnim;
  };

  // death
  INDEX AnimForDeath(void) {
    DeactivateMumblingSound();
    INDEX iAnim;
    if (m_bInAir) {
      iAnim = EYEMAN_ANIM_MORPHDEATH;
    } else {
      FLOAT3D vFront;
      GetHeadingDirection(0, vFront);
      FLOAT fDamageDir = m_vDamage%vFront;
      if (fDamageDir<0) {
        iAnim = EYEMAN_ANIM_DEATH02;
      } else {
        iAnim = EYEMAN_ANIM_DEATH01;
      }
    }
    StartModelAnim(iAnim, 0);
    return iAnim;
  };

  FLOAT WaitForDust(FLOAT3D &vStretch) {
    if(GetModelObject()->GetAnim()==EYEMAN_ANIM_DEATH01)
    {
      vStretch=FLOAT3D(1,1,1)*0.75f;
      return 0.5f;
    }
    else if(GetModelObject()->GetAnim()==EYEMAN_ANIM_DEATH02)
    {
      vStretch=FLOAT3D(1,1,1)*0.75f;
      return 0.5f;
    }
    else if(GetModelObject()->GetAnim()==EYEMAN_ANIM_MORPHDEATH)
    {
      vStretch=FLOAT3D(1,1,1)*1.0f;
      return 0.5f;
    }
    return -1.0f;
  };

  void DeathNotify(void) {
    ChangeCollisionBoxIndexWhenPossible(EYEMAN_COLLISION_BOX_DEATH);
    en_fDensity = 500.0f;
  };

  // mumbling sounds
  void ActivateMumblingSound(void)
  {
    if (!m_bMumbleSoundPlaying) {
      PlaySound(m_soMumble, SOUND_MUMBLE, SOF_3D|SOF_LOOP);
      m_bMumbleSoundPlaying = TRUE;
    }
  }
  void DeactivateMumblingSound(void)
  {
    m_soMumble.Stop();
    m_bMumbleSoundPlaying = FALSE;
  }

  // virtual anim functions
  void StandingAnim(void) {
    DeactivateMumblingSound();
    if (m_bInAir) {
      StartModelAnim(EYEMAN_ANIM_MORPHATTACKFLY, AOF_LOOPING|AOF_NORESTART);
    } else {
      StartModelAnim(EYEMAN_ANIM_STAND, AOF_LOOPING|AOF_NORESTART);
    }
  };
  void WalkingAnim(void) {
    ActivateMumblingSound();
    if (m_bInAir) {
      StartModelAnim(EYEMAN_ANIM_MORPHATTACKFLY, AOF_LOOPING|AOF_NORESTART);
    } else {
      StartModelAnim(EYEMAN_ANIM_WALK, AOF_LOOPING|AOF_NORESTART);
    }
  };
  void RunningAnim(void) {
    ActivateMumblingSound();
    if (m_bInAir) {
      StartModelAnim(EYEMAN_ANIM_MORPHATTACKFLY, AOF_LOOPING|AOF_NORESTART);
    } else {
      StartModelAnim(EYEMAN_ANIM_RUN, AOF_LOOPING|AOF_NORESTART);
    }
  };
  void RotatingAnim(void) {
    if (m_bInAir) {
      StartModelAnim(EYEMAN_ANIM_MORPHATTACKFLY, AOF_LOOPING|AOF_NORESTART);
    } else {
      StartModelAnim(EYEMAN_ANIM_RUN, AOF_LOOPING|AOF_NORESTART);
    }
  };
  FLOAT AirToGroundAnim(void) {
    StartModelAnim(EYEMAN_ANIM_MORPHUP, 0);
    return(GetModelObject()->GetAnimLength(EYEMAN_ANIM_MORPHUP));
  };
  FLOAT GroundToAirAnim(void) {
    StartModelAnim(EYEMAN_ANIM_MORPHDOWN, 0);
    return(GetModelObject()->GetAnimLength(EYEMAN_ANIM_MORPHDOWN));
  };
  void ChangeCollisionToAir() {
    ChangeCollisionBoxIndexWhenPossible(EYEMAN_COLLISION_BOX_AIR);
  };
  void ChangeCollisionToGround() {
    ChangeCollisionBoxIndexWhenPossible(EYEMAN_COLLISION_BOX_GROUND);
  };

  // virtual sound functions
  void IdleSound(void) {
    PlaySound(m_soSound, SOUND_IDLE, SOF_3D);
  };
  void SightSound(void) {
    PlaySound(m_soSound, SOUND_SIGHT, SOF_3D);
  };
  void WoundSound(void) {
    PlaySound(m_soSound, SOUND_WOUND, SOF_3D);
  };
  void DeathSound(void) {
    PlaySound(m_soSound, SOUND_DEATH, SOF_3D);
  };

/************************************************************
 *                 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_BLOODTRAIL, BET_BLOODSTAIN, fEntitySize, vNormalizedDamage, vBodySpeed, 1.0f, 0.0f);

    INDEX iTextureID = TEXTURE_EYEMAN_SOLDIER;
    if (m_EecChar==EYC_SERGEANT)
    {
      iTextureID = TEXTURE_EYEMAN_SERGEANT;
    }

    Debris_Spawn(this, this, MODEL_EYEMAN_BODY, iTextureID, 0, 0, 0, 0, 0.0f,
      FLOAT3D(FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f));
    Debris_Spawn(this, this, MODEL_EYEMAN_HAND, iTextureID, 0, 0, 0, 0, 0.0f,
      FLOAT3D(FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f));
    Debris_Spawn(this, this, MODEL_EYEMAN_HAND, iTextureID, 0, 0, 0, 0, 0.0f,
      FLOAT3D(FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f));
    Debris_Spawn(this, this, MODEL_EYEMAN_LEGS, iTextureID, 0, 0, 0, 0, 0.0f,
      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);
  };*/

/************************************************************
 *                     MOVING FUNCTIONS                     *
 ************************************************************/
  // check whether may move while attacking
  BOOL MayMoveToAttack(void) 
  {
    if (m_bInAir) {
      return WouldNotLeaveAttackRadius();
    } else {
      return CEnemyBase::MayMoveToAttack();
    }
  }

  // must be more relaxed about hitting then usual enemies
  BOOL CanHitEnemy(CEntity *penTarget, FLOAT fCosAngle) {
    if (IsInPlaneFrustum(penTarget, fCosAngle)) {
      return IsVisibleCheckAll(penTarget);
    }
    return FALSE;
  };
procedures:
/************************************************************
 *                A T T A C K   E N E M Y                   *
 ************************************************************/

  FlyHit(EVoid) : CEnemyFly::FlyHit {
    if (CalcDist(m_penEnemy) > BITE_AIR) {
      m_fShootTime = _pTimer->CurrentTick() + 0.25f;
      return EReturn();
    }
    StartModelAnim(EYEMAN_ANIM_MORPHATTACK, 0);
    StopMoving();
    PlaySound(m_soSound, SOUND_BITE, SOF_3D);
    // damage enemy
    autowait(0.4f);
    // damage enemy
    if (CalcDist(m_penEnemy) < BITE_AIR) {
      FLOAT3D vDirection = m_penEnemy->GetPlacement().pl_PositionVector-GetPlacement().pl_PositionVector;
      vDirection.SafeNormalize();
      InflictDirectDamage(m_penEnemy, this, DMT_CLOSERANGE, 3.5f, FLOAT3D(0, 0, 0), vDirection);
      // spawn blood cloud
      ESpawnEffect eSpawnEffect;
      eSpawnEffect.colMuliplier = C_WHITE|CT_OPAQUE;
      eSpawnEffect.betType = BET_BLOODEXPLODE;
      eSpawnEffect.vStretch = FLOAT3D(1,1,1);
      CPlacement3D plOne = GetPlacement();
      GetEntityPointRatio(
        FLOAT3D(Lerp(-0.2f, +0.2f, FRnd()), Lerp(-0.2f, +0.2f, FRnd()), -1.0f),
        plOne.pl_PositionVector);
      CEntityPointer penBloodCloud = CreateEntity( plOne, CLASS_BASIC_EFFECT);
      penBloodCloud->Initialize( eSpawnEffect);
    }
    autowait(0.24f);

    StandingAnim();
    return EReturn();
  };

  GroundHit(EVoid) : CEnemyFly::GroundHit {
    if (CalcDist(m_penEnemy) > HIT_GROUND) {
      m_fShootTime = _pTimer->CurrentTick() + 0.25f;
      return EReturn();
    }
    StartModelAnim(EYEMAN_ANIM_ATTACK02, 0);
    StopMoving();
    // damage enemy
    autowait(0.2f);
    // damage enemy
    if (CalcDist(m_penEnemy) < HIT_GROUND) {
      FLOAT3D vDirection = m_penEnemy->GetPlacement().pl_PositionVector-GetPlacement().pl_PositionVector;
      vDirection.SafeNormalize();
      InflictDirectDamage(m_penEnemy, this, DMT_CLOSERANGE, 3.5f, FLOAT3D(0, 0, 0), vDirection);
      PlaySound(m_soSound, SOUND_PUNCH, SOF_3D);
    }
    autowait(0.3f);
    // damage enemy
    if (CalcDist(m_penEnemy) < HIT_GROUND) {
      FLOAT3D vDirection = m_penEnemy->GetPlacement().pl_PositionVector-GetPlacement().pl_PositionVector;
      vDirection.SafeNormalize();
      InflictDirectDamage(m_penEnemy, this, DMT_CLOSERANGE, 3.5f, FLOAT3D(0, 0, 0), vDirection);
      PlaySound(m_soSound, SOUND_PUNCH, SOF_3D);
    }
    autowait(0.4f);

    StandingAnim();
    return EReturn();
  };

/************************************************************
 *                       M  A  I  N                         *
 ************************************************************/
  Main(EVoid) {
    // declare yourself as a model
    InitAsModel();
    SetPhysicsFlags(EPF_MODEL_WALKING|EPF_HASLUNGS);
    SetCollisionFlags(ECF_MODEL);
    SetFlags(GetFlags()|ENF_ALIVE);
    if (m_EecChar==EYC_SERGEANT) {
      SetHealth(90.0f);
      m_fMaxHealth = 90.0f;
      // damage/explode properties
      m_fBlowUpAmount = 130.0f;
      m_fBodyParts = 5;
      m_fBlowUpSize = 2.5f;
      m_fDamageWounded = 40.0f;
    } else {
      SetHealth(60.0f);
      m_fMaxHealth = 60.0f;
      // damage/explode properties
      m_fBlowUpAmount = 100.0f;
      m_fBodyParts = 5;
      m_fBlowUpSize = 2.0f;
      m_fDamageWounded = 25.0f;
    }
    en_fDensity = 2000.0f;
    if (m_EeftType == EFT_GROUND_ONLY) {
      en_tmMaxHoldBreath = 5.0f;
    } else {
      en_tmMaxHoldBreath = 30.0f;
    }

    // set your appearance
    SetModel(MODEL_EYEMAN);
    if (m_EecChar==EYC_SERGEANT) {
      SetModelMainTexture(TEXTURE_EYEMAN_SERGEANT);
      GetModelObject()->StretchModel(FLOAT3D(1.3f, 1.3f, 1.3f));
      ModelChangeNotify();
      m_iScore = 1000;
    } else {
      m_iScore = 500;
      if (m_eeEnv == EYE_LAVA) {
        SetModelMainTexture(TEXTURE_EYEMAN_LAVA);
      } else {
        SetModelMainTexture(TEXTURE_EYEMAN_SOLDIER);
      }
      GetModelObject()->StretchModel(FLOAT3D(1.0f, 1.0f, 1.0f));
      ModelChangeNotify();
    }
    if (m_bInvisible) {
      GetModelObject()->mo_colBlendColor = C_WHITE|0x25;
      m_iScore*=2;
    }
    // setup moving speed
    m_fWalkSpeed = FRnd() + 1.5f;
    m_aWalkRotateSpeed = FRnd()*10.0f + 500.0f;
    if (m_EecChar==EYC_SERGEANT) {
      m_fAttackRunSpeed = FRnd()*2.0f + 10.0f;
      m_aAttackRotateSpeed = AngleDeg(FRnd()*100 + 600.0f);
      m_fCloseRunSpeed = FRnd()*2.0f + 10.0f;
      m_aCloseRotateSpeed = AngleDeg(FRnd()*100 + 600.0f);
    } else {
      m_fAttackRunSpeed = FRnd()*2.0f + 9.0f;
      m_aAttackRotateSpeed = AngleDeg(FRnd()*100 + 600.0f);
      m_fCloseRunSpeed = FRnd()*2.0f + 9.0f;
      m_aCloseRotateSpeed = AngleDeg(FRnd()*100 + 600.0f);
    }
    // setup attack distances
    m_fAttackDistance = 100.0f;
    m_fCloseDistance = 3.5f;
    m_fStopDistance = 1.5f;
    m_fAttackFireTime = 2.0f;
    m_fCloseFireTime = 0.5f;
    m_fIgnoreRange = 200.0f;
    // fly moving properties
    m_fFlyWalkSpeed = FRnd()*2.0f + 3.0f;
    m_aFlyWalkRotateSpeed = FRnd()*20.0f + 600.0f;
    if (m_EecChar==EYC_SERGEANT) {
      m_fFlyAttackRunSpeed = FRnd()*2.0f + 9.5f;
      m_aFlyAttackRotateSpeed = FRnd()*25 + 350.0f;
      m_fFlyCloseRunSpeed = FRnd()*2.0f + 9.5f;
      m_aFlyCloseRotateSpeed = FRnd()*50 + 400.0f;
    } else {
      m_fFlyAttackRunSpeed = FRnd()*2.0f + 9.5f;
      m_aFlyAttackRotateSpeed = FRnd()*25 + 300.0f;
      m_fFlyCloseRunSpeed = FRnd()*2.0f + 9.5f;
      m_aFlyCloseRotateSpeed = FRnd()*50 + 300.0f;
    }
    m_fGroundToAirSpeed = 2.5f;
    m_fAirToGroundSpeed = 2.5f;
    m_fAirToGroundMin = 0.1f;
    m_fAirToGroundMax = 0.1f;
    m_fFlyHeight = 1.0f;
    // attack properties - CAN BE SET
    m_fFlyAttackDistance = 100.0f;
    m_fFlyCloseDistance = 10.0f;
    m_fFlyStopDistance = 1.5f;
    m_fFlyAttackFireTime = 2.0f;
    m_fFlyCloseFireTime = 0.5f;
    m_fFlyIgnoreRange = 200.0f;
    m_soMumble.Set3DParameters(25.0f, 0.0f, 1.0f, 1.0f);

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