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

uses "Entities/Light";
uses "Entities/Bullet";
uses "Entities/PlayerWeapons";
uses "Engine/Classes/MovableEntity";

// input parameter for ghost buster ray
event EGhostBusterRay {
  CEntityPointer penOwner,        // entity which owns it
};

%{
#define HIT_DISTANCE 50.0f      // ray hit distance
#define HIT_DAMAGE 15.0f         // hit damage for every lerping bullet

void CGhostBusterRay_OnPrecache(CDLLEntityClass *pdec, INDEX iUser) 
{
  pdec->PrecacheClass(CLASS_BULLET);
  pdec->PrecacheModel(MODEL_RAY);
  pdec->PrecacheTexture(TEXTURE_RAY);
}
%}


class CGhostBusterRay : CMovableModelEntity {
name      "GhostBusterRay";
thumbnail "";
features "ImplementsOnPrecache", "CanBePredictable";

properties:
  1 CEntityPointer m_penOwner,    // entity which owns it
  2 BOOL m_bRender = FALSE,       // do not render on startup
  3 FLOAT3D m_vSrcOld = FLOAT3D(0.0f, 0.0f, 0.0f),
  4 FLOAT3D m_vDstOld = FLOAT3D(0.0f, 0.0f, 0.0f),
  5 FLOAT3D m_vSrc = FLOAT3D(0.0f, 0.0f, 0.0f),
  6 FLOAT3D m_vDst = FLOAT3D(0.0f, 0.0f, 0.0f),
 10 FLOAT3D m_iLastBulletPosition = FLOAT3D(32000.0f, 32000.0f, 32000.0f),   // for lerping
 11 CAnimObject m_aoLightAnim,
 12 INDEX m_ctPasses = 0,       // for lerping initialization

{
  CLightSource m_lsLightSource;
  CEntity *penBullet;
  const CPlacement3D *pplSource;
}

components:
  1 class   CLASS_LIGHT         "Classes\\Light.ecl",
  2 class   CLASS_BULLET        "Classes\\Bullet.ecl",

// ********* RAY *********
 10 model   MODEL_RAY           "Models\\Weapons\\GhostBuster\\Projectile\\Ray.mdl",
 11 texture TEXTURE_RAY         "Models\\Weapons\\GhostBuster\\Projectile\\Ray.tex",

functions:
  // add to prediction any entities that this entity depends on
  void AddDependentsToPrediction(void)
  {
    m_penOwner->AddToPrediction();
  }
  // render particles
  void RenderParticles(void)
  {
    if (m_ctPasses<2) {
      return;
    }
    FLOAT3D vLerpedSrc = Lerp(m_vSrcOld, m_vSrc, _pTimer->GetLerpFactor());
    FLOAT3D vLerpedDst = Lerp(m_vDstOld, m_vDst, _pTimer->GetLerpFactor());
    Particles_Ghostbuster(vLerpedSrc, vLerpedDst, 32, 1.0f);
  };

  /* Read from stream. */
  void Read_t( CTStream *istr) // throw char *
  {
    CMovableModelEntity::Read_t(istr);
    SetupLightSource();
  };

  /* Get static light source information. */
  CLightSource *GetLightSource(void)
  {
    if (!IsPredictor()) {
      return &m_lsLightSource;
    } else {
      return NULL;
    }
  };

  // Setup light source
  void SetupLightSource(void)
  {
    // setup light source
    CLightSource lsNew;
    lsNew.ls_ulFlags = LSF_NONPERSISTENT|LSF_DYNAMIC;
    lsNew.ls_colColor = RGBToColor(134,238,255);
    lsNew.ls_rFallOff = 10.0f;
    lsNew.ls_rHotSpot = 1.0f;
    lsNew.ls_plftLensFlare = NULL;
    lsNew.ls_ubPolygonalMask = 0;
    lsNew.ls_paoLightAnimation = &m_aoLightAnim;

    m_lsLightSource.ls_penEntity = this;
    m_lsLightSource.SetLightSource(lsNew);
  };



/************************************************************
 *                        DO MOVING                         *
 ************************************************************/
  void DoMoving(void) {
    en_plLastPlacement = GetPlacement();  // remember old placement for lerping
  };

  void PostMoving(void) {
    if (!IsOfClass(m_penOwner, "Player Weapons")) { return; }

    // from current owner position move away
    CPlacement3D plSource;
    ((CPlayerWeapons&)*m_penOwner).GetGhostBusterSourcePlacement(plSource);
    FLOAT3D vDirection, vDesired;
    AnglesToDirectionVector(plSource.pl_OrientationAngle, vDirection);
    vDesired = vDirection*HIT_DISTANCE;
    vDesired = plSource.pl_PositionVector + vDesired;

    // cast a ray to find if any brush is hit
    CCastRay crRay( ((CPlayerWeapons&)*m_penOwner).m_penPlayer, plSource.pl_PositionVector, vDesired);
    m_vSrcOld = m_vSrc;
    m_vSrc = plSource.pl_PositionVector;
    crRay.cr_bHitTranslucentPortals = FALSE;
    crRay.cr_ttHitModels = CCastRay::TT_COLLISIONBOX;
    GetWorld()->CastRay(crRay);

    // if hit anything set new position
    if (crRay.cr_penHit!=NULL) {
      vDesired = crRay.cr_vHit;
    }
    vDesired -= vDirection/10.0f;

    m_vDstOld = m_vDst;
    m_vDst = vDesired;

    // stretch model
    FLOAT fStretch = (plSource.pl_PositionVector - vDesired).Length();
    //GetModelObject()->mo_Stretch(3) = fStretch;
    GetModelObject()->mo_Stretch(3) = 0.001f;
    // set your new placement
    CPlacement3D plSet;
    plSet.pl_PositionVector = vDesired;
    plSet.pl_OrientationAngle = plSource.pl_OrientationAngle;
    SetPlacement(plSet);
    m_ctPasses++;
  };



/************************************************************
 *                      FIRE FUNCTIONS                      *
 ************************************************************/
  // prepare Bullet
  void PrepareBullet(const CPlacement3D &plBullet) {
    // create bullet
    penBullet = CreateEntity(plBullet, CLASS_BULLET);
    // init bullet
    EBulletInit eInit;
    eInit.penOwner = ((CPlayerWeapons&)*m_penOwner).m_penPlayer;
    eInit.fDamage = HIT_DAMAGE;
    penBullet->Initialize(eInit);
    ((CBullet&)*penBullet).m_EdtDamage = DMT_BULLET;
  };

  // fire
  void Fire(const CPlacement3D &plSource) {
    if (!IsOfClass(m_penOwner, "Player Weapons")) { return; }

    // fire lerped bullets
    PrepareBullet(plSource);
    ((CBullet&)*penBullet).CalcTarget(HIT_DISTANCE);
    ((CBullet&)*penBullet).m_fBulletSize = 0.5f;
    ((CBullet&)*penBullet).CalcJitterTarget(0.02f*HIT_DISTANCE);
    ((CBullet&)*penBullet).LaunchBullet(TRUE, FALSE, TRUE);
    ((CBullet&)*penBullet).DestroyBullet();
  };

  // destroy yourself
  void DestroyGhostBusterRay(void) {
    Destroy();
  };



/************************************************************
 *                   P R O C E D U R E S                    *
 ************************************************************/
procedures:
  // --->>> MAIN
  Main(EGhostBusterRay egbr) {
    // store owner
    ASSERT(egbr.penOwner!=NULL);
    m_penOwner = egbr.penOwner;

    // initialization
    InitAsModel();
    SetPhysicsFlags(EPF_MODEL_IMMATERIAL);
    SetCollisionFlags(ECF_IMMATERIAL);
    SetModel(MODEL_RAY);
    SetModelMainTexture(TEXTURE_RAY);

    try {
      m_aoLightAnim.SetData_t(CTFILENAME("Animations\\GhostbusterLightning.ani"));
      m_aoLightAnim.PlayAnim(0,AOF_LOOPING);
    } catch (char *strError) {
      CPrintF("%s", strError);
    }

    // setup light source
    SetupLightSource();

    // add to movers list
    AddToMovers();
    m_ctPasses = 0;

    return;
  }
};
