/* 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. */

345
%{
#include "EntitiesMP/StdH/StdH.h"
%}

uses "EntitiesMP/ModelHolder2";
uses "EntitiesMP/Projectile";
uses "EntitiesMP/SoundHolder";
uses "EntitiesMP/BloodSpray";
uses "EntitiesMP/CannonBall";

enum FireType {
  0 SFT_WOODEN_DART "Wooden dart",
  1 SFT_FIRE        "Fire",
  2 SFT_GAS         "-none-",
  3 SFT_IRONBALL    "Ironball",
  4 SFT_FIREBALL    "Fireball",
};

class CShooter: CModelHolder2 {
name      "Shooter";
thumbnail "Thumbnails\\Shooter.tbn";
features  "HasName", "IsTargetable";

properties:
  
  2  FLOAT m_fShootingPeriod                "Shooting Period" = 1.0f,
  5  enum FireType m_sftType                "Type" 'Y' = SFT_WOODEN_DART,
  7  FLOAT m_fHealth                        "Health" = 0.0f,
  8  FLOAT m_fCannonBallSize                "Cannon/fire ball size" = 1.0f,
  9  FLOAT m_fCannonBallPower               "Cannon/fire ball power" = 10.0f, 
 10 ANIMATION m_iModelPreFireAnimation     "Model pre-fire animation" = 0,
 11 ANIMATION m_iTexturePreFireAnimation   "Texture pre-fire animation" = 0,
 12 ANIMATION m_iModelPostFireAnimation    "Model post-fire animation" = 0,
 13 ANIMATION m_iTexturePostFireAnimation  "Texture post-fire animation" = 0,
 14 FLOAT m_fFlameBurstDuration            "Flame burst duration" = 1.0f,
 15 FLOAT m_fRndBeginWait                  "Random begin wait time" = 0.0f,

 20 CEntityPointer m_penSoundLaunch        "Sound launch",   // sound when firing
 21 CSoundObject m_soLaunch,  
  
 30 CEntityPointer m_penFlame,
 //internal:

 50 BOOL m_bFiring = FALSE,
 51 BOOL m_bIndestructable = FALSE,

 60 FLOAT m_tmFlameStart = 0.0f,

components:
  1 class CLASS_PROJECTILE    "Classes\\Projectile.ecl",
  2 class CLASS_BLOOD_SPRAY   "Classes\\BloodSpray.ecl",
  3 class CLASS_CANNONBALL    "Classes\\CannonBall.ecl",

functions:                                        
  
  void Precache(void) {
    CModelHolder2::Precache();
    PrecacheClass(CLASS_PROJECTILE, PRT_SHOOTER_WOODEN_DART);
    PrecacheClass(CLASS_PROJECTILE, PRT_SHOOTER_FIREBALL);
    PrecacheClass(CLASS_CANNONBALL);
  };

  void ReceiveDamage(CEntity *penInflictor, enum DamageType dmtType,
    FLOAT fDamageAmmount, const FLOAT3D &vHitPoint, const FLOAT3D &vDirection) 
  {
    // receive damage if not indestructable, and shooter can't hurt another shooter
    if (!m_bIndestructable && !IsOfClass(penInflictor, "Shooter")) {
      if (m_tmSpraySpawned<=_pTimer->CurrentTick()-_pTimer->TickQuantum*8 
          && m_penDestruction!=NULL) {
        
        CModelDestruction *penDestruction = GetDestruction();
        
        // spawn blood spray
        CPlacement3D plSpray = CPlacement3D( vHitPoint, ANGLE3D(0, 0, 0));
        m_penSpray = CreateEntity( plSpray, CLASS_BLOOD_SPRAY);
        m_penSpray->SetParent(this);
        ESpawnSpray eSpawnSpray;
        eSpawnSpray.colBurnColor=C_WHITE|CT_OPAQUE;
        
        // adjust spray power
        if( fDamageAmmount > 50.0f) {
          eSpawnSpray.fDamagePower = 3.0f;
        } else if(fDamageAmmount > 25.0f ) {
          eSpawnSpray.fDamagePower = 2.0f;
        } else {
          eSpawnSpray.fDamagePower = 1.0f;
        }
        
        // remember spray type
        eSpawnSpray.sptType = penDestruction->m_sptType;
        eSpawnSpray.fSizeMultiplier = 1.0f;
        
        // get your down vector (simulates gravity)
        FLOAT3D vDn(-en_mRotation(1,2), -en_mRotation(2,2), -en_mRotation(3,2));
        
        // setup direction of spray
        FLOAT3D vHitPointRelative = vHitPoint - GetPlacement().pl_PositionVector;
        FLOAT3D vReflectingNormal;
        GetNormalComponent( vHitPointRelative, vDn, vReflectingNormal);
        vReflectingNormal.Normalize();
        
        vReflectingNormal(1)/=5.0f;
        
        FLOAT3D vProjectedComponent = vReflectingNormal*(vDirection%vReflectingNormal);
        FLOAT3D vSpilDirection = vDirection-vProjectedComponent*2.0f-vDn*0.5f;
        
        eSpawnSpray.vDirection = vSpilDirection;
        eSpawnSpray.penOwner = this;
        
        // initialize spray
        m_penSpray->Initialize( eSpawnSpray);
        m_tmSpraySpawned = _pTimer->CurrentTick();
      }
  
      CRationalEntity::ReceiveDamage(penInflictor, dmtType, fDamageAmmount, vHitPoint, vDirection);
    }
  }

  // render particles
  void RenderParticles(void)
  {
    // fire particles
    if (m_sftType==SFT_FIRE) {
    }
    CModelHolder2::RenderParticles();
  }

  /* Get anim data for given animation property - return NULL for none. */
  CAnimData *GetAnimData(SLONG slPropertyOffset) 
  {
    if (slPropertyOffset==_offsetof(CShooter, m_iModelPreFireAnimation) ||
        slPropertyOffset==_offsetof(CShooter, m_iModelPostFireAnimation)) {
      return GetModelObject()->GetData();
    } else if (slPropertyOffset==_offsetof(CShooter, m_iTexturePreFireAnimation) ||
               slPropertyOffset==_offsetof(CShooter, m_iTexturePostFireAnimation)) {
      return GetModelObject()->mo_toTexture.GetData();
    } else {
      return CModelHolder2::GetAnimData(slPropertyOffset);
    }
  }

  // shoot projectile on enemy
  CEntity *ShootProjectile(enum ProjectileType pt, const FLOAT3D &vOffset, const ANGLE3D &aOffset) {
    // launch
    CPlacement3D pl;
    pl = GetPlacement();
    CEntityPointer penProjectile = CreateEntity(pl, CLASS_PROJECTILE);
    ELaunchProjectile eLaunch;
    eLaunch.penLauncher = this;
    eLaunch.prtType = pt;
    penProjectile->Initialize(eLaunch);

    return penProjectile;
  };

  // fire flame
  void FireFlame(void) {
    // flame start position
    CPlacement3D plFlame;
    plFlame = GetPlacement();
    
    FLOAT3D vNormDir;
    AnglesToDirectionVector(plFlame.pl_OrientationAngle, vNormDir);
    plFlame.pl_PositionVector += vNormDir*0.1f;
    
    // create flame
    CEntityPointer penFlame = CreateEntity(plFlame, CLASS_PROJECTILE);
    // init and launch flame
    ELaunchProjectile eLaunch;
    eLaunch.penLauncher = this;
    eLaunch.prtType = PRT_SHOOTER_FLAME;
    penFlame->Initialize(eLaunch);
    // link last flame with this one (if not NULL or deleted)
    if (m_penFlame!=NULL && !(m_penFlame->GetFlags()&ENF_DELETED)) {
      ((CProjectile&)*m_penFlame).m_penParticles = penFlame;
    }
    // link to this
    ((CProjectile&)*penFlame).m_penParticles = this;
    // store last flame
    m_penFlame = penFlame;
  };

  void StopFlame(void) {
    ((CProjectile&)*m_penFlame).m_penParticles = NULL;
    //m_penFlame = NULL;
  }

  void PlayFireSound(void) {
    // if sound entity exists
    if (m_penSoundLaunch!=NULL) {
      CSoundHolder &sh = (CSoundHolder&)*m_penSoundLaunch;
      m_soLaunch.Set3DParameters(FLOAT(sh.m_rFallOffRange), FLOAT(sh.m_rHotSpotRange), sh.m_fVolume, 1.0f);
      PlaySound(m_soLaunch, sh.m_fnSound, sh.m_iPlayType);
    }
  };

  void ShootCannonball()
  {
    // cannon ball start position
    CPlacement3D plBall = GetPlacement();
    // create cannon ball
    CEntityPointer penBall = CreateEntity(plBall, CLASS_CANNONBALL);
    // init and launch cannon ball
    ELaunchCannonBall eLaunch;
    eLaunch.penLauncher = this;
    eLaunch.fLaunchPower = 10.0f+m_fCannonBallPower; // ranges from 50-150 (since iPower can be max 100)
    eLaunch.cbtType = CBT_IRON;
    eLaunch.fSize = m_fCannonBallSize;
    penBall->Initialize(eLaunch);
  };

  void ShootFireball()
  {
    // cannon ball start position
    CPlacement3D plBall = GetPlacement();
    // create cannon ball
    CEntityPointer penBall = CreateEntity(plBall, CLASS_CANNONBALL);
    // init and launch cannon ball
    ELaunchCannonBall eLaunch;
    eLaunch.penLauncher = this;
    eLaunch.fLaunchPower = 10.0f+m_fCannonBallPower; // ranges from 50-150 (since iPower can be max 100)
    eLaunch.cbtType = CBT_IRON;
    eLaunch.fSize = m_fCannonBallSize;
    penBall->Initialize(eLaunch);
  };

procedures:
  
  FireOnce()
  {
    if (m_sftType==SFT_FIRE) { jump FlameBurst(); }
    
    PlayFireSound();

    GetModelObject()->PlayAnim(m_iModelPreFireAnimation, 0);
    GetModelObject()->mo_toTexture.PlayAnim(m_iTexturePreFireAnimation, 0);
    autowait(GetModelObject()->GetAnimLength(m_iModelPreFireAnimation));
    
    switch (m_sftType) {
      case SFT_WOODEN_DART:
        ShootProjectile(PRT_SHOOTER_WOODEN_DART, FLOAT3D (0.0f, 0.0f, 0.0f), ANGLE3D (0.0f, 0.0f, 0.0f));
        break;
      case SFT_GAS:
        break;
      case SFT_IRONBALL:
        ShootCannonball();
        break;
      case SFT_FIREBALL:
        ShootProjectile(PRT_SHOOTER_FIREBALL, FLOAT3D (0.0f, 0.0f, 0.0f), ANGLE3D (0.0f, 0.0f, 0.0f));
        break;
    }
    
    GetModelObject()->PlayAnim(m_iModelPostFireAnimation, 0);
    GetModelObject()->mo_toTexture.PlayAnim(m_iTexturePostFireAnimation, 0);
    autowait(GetModelObject()->GetAnimLength(m_iModelPostFireAnimation));
    
    return EEnd();
  }
  
  FireContinuous() {
    
    // possible random wait
    if (m_fRndBeginWait>0.0f)
    {
      FLOAT fRndWait = FRnd()*m_fRndBeginWait+0.05f;
      autowait(fRndWait);
    }

    while(m_bFiring) {
      autocall FireOnce() EEnd;
      autowait(m_fShootingPeriod);
    }
    return EReturn();
  };

  FlameBurst() {
    PlayFireSound();
    m_penFlame = NULL;
    m_tmFlameStart = _pTimer->CurrentTick();
    while(_pTimer->CurrentTick( ) < m_tmFlameStart + m_fFlameBurstDuration)
    {
      // wait a bit and fire 
      autowait(0.05f);
      FireFlame();
    }
    StopFlame();
    return EEnd();
  };

  MainLoop() {
    //main loop
    wait() {
      on (EBegin) : {
        resume;
      }
      on (ETrigger) : {
        if (!m_bFiring) {
          call FireOnce();
        } else {
          resume;
        }
      }
      on (EActivate) : {
        m_bFiring = TRUE;
        call FireContinuous();
      }
      on (EDeactivate) : {
        m_bFiring = FALSE;
        resume;
      }
      on (EDeath) : {
        if (m_penDestruction!=NULL) {
          jump CModelHolder2::Die();
        } else {
          Destroy();
          return;
        }
      }
      on (EReturn) : {
        resume;
      }
    }
  };

  Main() {
    // init as model
    CModelHolder2::InitModelHolder();

    if (m_fHealth>0.0f) {
      SetHealth(m_fHealth);
      m_bIndestructable = FALSE;
    } else {
      SetHealth(10000.0f);
      m_bIndestructable = TRUE;
    }

    ClampUp(m_fCannonBallSize, 10.0f);
    ClampDn(m_fCannonBallSize, 0.1f);
    ClampUp(m_fCannonBallPower, 100.0f);
    ClampDn(m_fCannonBallPower, 0.0f);
    
    if (m_penSoundLaunch!=NULL && !IsOfClass(m_penSoundLaunch, "SoundHolder")) {
      WarningMessage( "Entity '%s' is not of class SoundHolder!", (const char *) m_penSoundLaunch->GetName());
      m_penSoundLaunch=NULL;
    }
    if (m_penDestruction!=NULL && !IsOfClass(m_penDestruction, "ModelDestruction")) {
      WarningMessage( "Entity '%s' is not of class ModelDestruction!", (const char *) m_penDestruction->GetName());
      m_penDestruction=NULL;
    }

    autowait(_pTimer->TickQuantum);

    jump MainLoop();
    
    return;
  };
};