503
%{
#include "StdH.h"
%}

uses "Entities/BasicEffects";
uses "Entities/Light";
uses "Entities/AmmoItem";

// input parameter for launching the projectile
event EDropPipebomb {
  CEntityPointer penLauncher,     // who launched it
  FLOAT fSpeed,                   // launch speed
};

%{
#define ECF_PIPEBOMB ( \
  ((ECBI_MODEL|ECBI_BRUSH|ECBI_PROJECTILE_SOLID|ECBI_MODEL_HOLDER)<<ECB_TEST) |\
  ((ECBI_PROJECTILE_SOLID)<<ECB_IS) )

void CPipebomb_OnPrecache(CDLLEntityClass *pdec, INDEX iUser) 
{
  pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_GRENADE);
  pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_EXPLOSIONSTAIN);
  pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_SHOCKWAVE);
  pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_GRENADE_PLANE);
  pdec->PrecacheModel(MODEL_PIPEBOMB);
  pdec->PrecacheTexture(TEXTURE_PIPEBOMB);
  pdec->PrecacheSound(SOUND_LAUNCH);
}
%}

class CPipebomb : CMovableModelEntity {
name      "Pipebomb";
thumbnail "";
features "ImplementsOnPrecache", "CanBePredictable";

properties:
  1 CEntityPointer m_penLauncher,     // who lanuched it
  2 FLOAT m_fIgnoreTime = 0.0f,       // time when laucher will be ignored
  3 FLOAT m_fSpeed = 0.0f,            // launch speed
  4 BOOL m_bCollected = FALSE,        // collect -> do not explode

{
  CLightSource m_lsLightSource;
}

components:
  1 class   CLASS_BASIC_EFFECT  "Classes\\BasicEffect.ecl",
  2 class   CLASS_LIGHT         "Classes\\Light.ecl",

// ********* PIPEBOMB (GRENADE) *********
 10 model   MODEL_PIPEBOMB      "Models\\Weapons\\Pipebomb\\Grenade\\Grenade.mdl",
 11 texture TEXTURE_PIPEBOMB    "Models\\Weapons\\Pipebomb\\Grenade\\Grenade.tex",
 12 sound   SOUND_LAUNCH        "Sounds\\Weapons\\RocketFired.wav",

functions:
  /* 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 = C_vdRED;
    lsNew.ls_rFallOff = 1.0f;
    lsNew.ls_rHotSpot = 0.1f;
    lsNew.ls_plftLensFlare = &_lftYellowStarRedRingFar;
    lsNew.ls_ubPolygonalMask = 0;
    lsNew.ls_paoLightAnimation = NULL;

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

  // render particles
  void RenderParticles(void) {
    Particles_GrenadeTrail(this);
  }

/************************************************************
 *                       PIPEBOMB                           *
 ************************************************************/
void Pipebomb(void) {
  // set appearance
  InitAsModel();
  SetPhysicsFlags(EPF_MODEL_BOUNCING);
  SetCollisionFlags(ECF_PIPEBOMB);
  //GetModelObject()->StretchModel(FLOAT3D(2.5f, 2.5f, 2.5f));
  //ModelChangeNotify();
  SetModel(MODEL_PIPEBOMB);
  SetModelMainTexture(TEXTURE_PIPEBOMB);
  // start moving
  LaunchAsFreeProjectile(FLOAT3D(0.0f, 0.0f, -m_fSpeed), (CMovableEntity*)&*m_penLauncher);
  SetDesiredRotation(ANGLE3D(0, FRnd()*120.0f+120.0f, FRnd()*250.0f-125.0f));
  en_fBounceDampNormal   = 0.7f;
  en_fBounceDampParallel = 0.7f;
  en_fJumpControlMultiplier = 0.0f;
  en_fCollisionSpeedLimit = 45.0f;
  en_fCollisionDamageFactor = 10.0f;
  SetHealth(20.0f);
};

void PipebombExplosion(void) {
  ESpawnEffect ese;
  FLOAT3D vPoint;
  FLOATplane3D vPlaneNormal;
  FLOAT fDistanceToEdge;

  // explosion
  ese.colMuliplier = C_WHITE|CT_OPAQUE;
  ese.betType = BET_GRENADE;
  ese.vStretch = FLOAT3D(1,1,1);
  SpawnEffect(GetPlacement(), ese);
  // spawn sound event in range
  if( IsDerivedFromClass( m_penLauncher, "Player")) {
    SpawnRangeSound( m_penLauncher, this, SNDT_PLAYER, 50.0f);
  }

  // on plane
  if (GetNearestPolygon(vPoint, vPlaneNormal, fDistanceToEdge)) {
    if ((vPoint-GetPlacement().pl_PositionVector).Length() < 3.5f) {
      // wall stain
      ese.betType = BET_EXPLOSIONSTAIN;
      ese.vNormal = FLOAT3D(vPlaneNormal);
      SpawnEffect(CPlacement3D(vPoint, ANGLE3D(0, 0, 0)), ese);
      // shock wave
      ese.betType = BET_SHOCKWAVE;
      ese.vNormal = FLOAT3D(vPlaneNormal);
      SpawnEffect(CPlacement3D(vPoint, ANGLE3D(0, 0, 0)), ese);
      // second explosion on plane
      ese.betType = BET_GRENADE_PLANE;
      ese.vNormal = FLOAT3D(vPlaneNormal);
      SpawnEffect(CPlacement3D(vPoint+ese.vNormal/50.0f, ANGLE3D(0, 0, 0)), ese);
    }
  }
};




/************************************************************
 *             C O M M O N   F U N C T I O N S              *
 ************************************************************/
// projectile hitted (or time expired or can't move any more)
void ProjectileHitted(void) {
  // explode ...
  InflictRangeDamage(m_penLauncher, DMT_EXPLOSION, 100.0f,
      GetPlacement().pl_PositionVector, 4.0f, 8.0f);
  // sound event
  ESound eSound;
  eSound.EsndtSound = SNDT_EXPLOSION;
  eSound.penTarget = m_penLauncher;
  SendEventInRange(eSound, FLOATaabbox3D(GetPlacement().pl_PositionVector, 50.0f));
};


// spawn effect
void SpawnEffect(const CPlacement3D &plEffect, const ESpawnEffect &eSpawnEffect) {
  CEntityPointer penEffect = CreateEntity(plEffect, CLASS_BASIC_EFFECT);
  penEffect->Initialize(eSpawnEffect);
};




/************************************************************
 *                   P R O C E D U R E S                    *
 ************************************************************/
procedures:
  // --->>> PROJECTILE SLIDE ON BRUSH
  ProjectileSlide(EVoid) {
    m_bCollected = FALSE;
    // fly loop
    wait() {
      on (EBegin) : { resume; }
      // collected
      on (ETouch etouch) : {
        // clear time limit for launcher
        if (etouch.penOther->GetRenderType() & RT_BRUSH) {
          m_fIgnoreTime = 0.0f;
          resume;
        }

        BOOL bCollect;
        // ignore launcher within 0.5 second
        bCollect = etouch.penOther!=m_penLauncher || _pTimer->CurrentTick()>m_fIgnoreTime;
        // speed must be almost zero
        bCollect &= (en_vCurrentTranslationAbsolute.Length() < 0.25f);
        // only if can be collected
        EAmmoItem eai;
        eai.EaitType = AIT_GRENADES;
        eai.iQuantity = 1;
        if (bCollect && etouch.penOther->ReceiveItem(eai)) {
          m_bCollected = TRUE;
          stop;
        }
        resume;
      }
      // killed
      on (EDeath) : {
        ProjectileHitted();
        stop;
      }
      // activated
      on (EStart) : {
        ProjectileHitted();
        stop;
      }
    }
    return EEnd();
  };

  // --->>> MAIN
  Main(EDropPipebomb edrop) {
    // remember the initial parameters
    ASSERT(edrop.penLauncher!=NULL);
    m_penLauncher = edrop.penLauncher;
    m_fSpeed = edrop.fSpeed;

    // projectile initialization
    Pipebomb();

    // setup light source
    SetupLightSource();

    // remember lauching time
    m_fIgnoreTime = _pTimer->CurrentTick() + 0.5f;

    // slide
    autocall ProjectileSlide() EEnd;

    // pipebomb explosion if not collected
    if (!m_bCollected) {
      PipebombExplosion();
    }

    Destroy();

    return;
  }
};
