/* 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. */ 353 %{ #include "StdH.h" #include "EntitiesMP/EnemyBase.h" #include "ModelsMP/Enemies/ExotechLarva/Projectile/TailProjectile.h" %} uses "EntitiesMP/BasicEffects"; uses "EntitiesMP/Light"; uses "EntitiesMP/Flame"; // input parameter for launching the LarvaOffspring event ELaunchLarvaOffspring { CEntityPointer penLauncher, // who launched it }; %{ #define ECF_OFFSPRING ( \ ((ECBI_MODEL|ECBI_BRUSH|ECBI_PROJECTILE_MAGIC|ECBI_PROJECTILE_SOLID|ECBI_ITEM|ECBI_MODEL_HOLDER|ECBI_CORPSE_SOLID)< range damage 24 FLOAT m_aRotateSpeed = 0.0f, // speed of rotation for guided LarvaOffsprings*/ 25 FLOAT m_tmExpandBox = 0.0f, // expand collision after a few seconds 30 CSoundObject m_soEffect, // sound channel 50 BOOL bLockedOn = TRUE, components: 1 class CLASS_BASIC_EFFECT "Classes\\BasicEffect.ecl", 10 model MODEL_LARVA_TAIL "ModelsMP\\Enemies\\ExotechLarva\\Projectile\\TailProjectile.mdl", 11 texture TEXTURE_LARVA_TAIL "ModelsMP\\Enemies\\ExotechLarva\\Projectile\\TailProjectile.tex", 12 sound SOUND_LARVETTE "ModelsMP\\Enemies\\ExotechLarva\\Sounds\\Squeak.wav", functions: // premoving void PreMoving(void) { if (m_tmExpandBox>0) { if (_pTimer->CurrentTick()>m_fStartTime+m_tmExpandBox) { ChangeCollisionBoxIndexWhenPossible(1); m_tmExpandBox = 0; } } CMovableModelEntity::PreMoving(); } void Precache() { PrecacheSound(SOUND_LARVETTE); PrecacheModel(MODEL_LARVA_TAIL); PrecacheTexture(TEXTURE_LARVA_TAIL); PrecacheClass(CLASS_BASIC_EFFECT, BET_ROCKET); PrecacheClass(CLASS_BASIC_EFFECT, BET_SHOCKWAVE); } void InitializeProjectile(void) { // we need target for guided misile if (IsDerivedFromClass(m_penLauncher, "Enemy Base")) { m_penTarget = ((CEnemyBase *) &*m_penLauncher)->m_penEnemy; } // set appearance InitAsModel(); SetPhysicsFlags(EPF_MODEL_SLIDING); SetCollisionFlags(ECF_OFFSPRING); SetModel(MODEL_LARVA_TAIL); SetModelMainTexture(TEXTURE_LARVA_TAIL); GetModelObject()->StretchModel(FLOAT3D(4.0f, 4.0f, 4.0f)); ModelChangeNotify(); // play the flying sound m_soEffect.Set3DParameters(50.0f, 10.0f, 1.0f, 1.0f); PlaySound(m_soEffect, SOUND_LARVETTE, SOF_3D|SOF_LOOP); // start moving LaunchAsPropelledProjectile(FLOAT3D(0.0f, 0.0f, -30.0f), (CMovableEntity*)(CEntity*)m_penLauncher); SetDesiredRotation(ANGLE3D(0, 0, 0)); m_fFlyTime = 12.0f; m_fDamageAmount = 10.0f; m_aRotateSpeed = 275.0f; SetHealth(10.0f); } void LarvaTailExplosion(void) { ESpawnEffect ese; FLOAT3D vPoint; FLOATplane3D vPlaneNormal; FLOAT fDistanceToEdge; // explosion ese.colMuliplier = C_WHITE|CT_OPAQUE; ese.betType = BET_ROCKET; 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, m_fSoundRange); } // explosion debris ese.betType = BET_EXPLOSION_DEBRIS; SpawnEffect(GetPlacement(), ese); // explosion smoke ese.betType = BET_EXPLOSION_SMOKE; SpawnEffect(GetPlacement(), ese); // on plane if (GetNearestPolygon(vPoint, vPlaneNormal, fDistanceToEdge)) { if ((vPoint-GetPlacement().pl_PositionVector).Length() < 3.5f) { // 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_ROCKET_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 * ************************************************************/ void ProjectileTouch(CEntityPointer penHit) { // explode if needed ProjectileHit(); // direct damage FLOAT3D vDirection; FLOAT fTransLen = en_vIntendedTranslation.Length(); if( fTransLen>0.5f) { vDirection = en_vIntendedTranslation/fTransLen; } else { vDirection = -en_vGravityDir; } const FLOAT fDamageMul = GetSeriousDamageMultiplier(m_penLauncher); InflictDirectDamage(penHit, m_penLauncher, DMT_PROJECTILE, m_fDamageAmount*fDamageMul, GetPlacement().pl_PositionVector, vDirection); }; void ProjectileHit(void) { // explode ... if (m_bExplode) { const FLOAT fDamageMul = GetSeriousDamageMultiplier(m_penLauncher); InflictRangeDamage(m_penLauncher, DMT_EXPLOSION, m_fRangeDamageAmount*fDamageMul, GetPlacement().pl_PositionVector, m_fDamageHotSpotRange, m_fDamageFallOffRange); } // sound event if (m_fSoundRange > 0.0f) { ESound eSound; eSound.EsndtSound = SNDT_EXPLOSION; eSound.penTarget = m_penLauncher; SendEventInRange(eSound, FLOATaabbox3D(GetPlacement().pl_PositionVector, m_fSoundRange)); } }; // spawn effect void SpawnEffect(const CPlacement3D &plEffect, const ESpawnEffect &eSpawnEffect) { CEntityPointer penEffect = CreateEntity(plEffect, CLASS_BASIC_EFFECT); penEffect->Initialize(eSpawnEffect); }; // Calculate current rotation speed to rich given orientation in future ANGLE GetRotationSpeed(ANGLE aWantedAngle, ANGLE aRotateSpeed, FLOAT fWaitFrequency) { ANGLE aResult; // if desired position is smaller if ( aWantedAngle<-aRotateSpeed*fWaitFrequency) { // start decreasing aResult = -aRotateSpeed; } // if desired position is bigger else if (aWantedAngle>aRotateSpeed*fWaitFrequency) { // start increasing aResult = +aRotateSpeed; } // if desired position is more-less ahead else { aResult = aWantedAngle/fWaitFrequency; } return aResult; } /************************************************************ * P R O C E D U R E S * ************************************************************/ procedures: LarvaOffspringGuidedSlide(EVoid) { // if already inside some entity CEntity *penObstacle; if (CheckForCollisionNow(0, &penObstacle)) { // explode now ProjectileTouch(penObstacle); return EEnd(); } // fly loop while( _pTimer->CurrentTick()<(m_fStartTime+m_fFlyTime)) { FLOAT fWaitFrequency = 0.1f; if (m_penTarget!=NULL) { // calculate desired position and angle EntityInfo *pei= (EntityInfo*) (m_penTarget->GetEntityInfo()); FLOAT3D vDesiredPosition; GetEntityInfoPosition( m_penTarget, pei->vSourceCenter, vDesiredPosition); FLOAT3D vDesiredDirection = (vDesiredPosition-GetPlacement().pl_PositionVector).Normalize(); // for heading ANGLE aWantedHeading = GetRelativeHeading( vDesiredDirection); ANGLE aHeading = GetRotationSpeed( aWantedHeading, m_aRotateSpeed, fWaitFrequency); // factor used to decrease speed of LarvaOffsprings oriented opposite of its target FLOAT fSpeedDecreasingFactor = ((180-Abs(aWantedHeading))/180.0f); // factor used to increase speed when far away from target FLOAT fSpeedIncreasingFactor = (vDesiredPosition-GetPlacement().pl_PositionVector).Length()/100; fSpeedIncreasingFactor = ClampDn(fSpeedIncreasingFactor, 1.0f); // decrease speed acodring to target's direction FLOAT fMaxSpeed = 30.0f*fSpeedIncreasingFactor; FLOAT fMinSpeedRatio = 0.5f; FLOAT fWantedSpeed = fMaxSpeed*( fMinSpeedRatio+(1-fMinSpeedRatio)*fSpeedDecreasingFactor); // adjust translation velocity SetDesiredTranslation( FLOAT3D(0, 0, -fWantedSpeed)); // adjust rotation speed m_aRotateSpeed = 75.0f*(1+0.5f*fSpeedDecreasingFactor); // calculate distance factor FLOAT fDistanceFactor = (vDesiredPosition-GetPlacement().pl_PositionVector).Length()/50.0; fDistanceFactor = ClampUp(fDistanceFactor, 4.0f); FLOAT fRNDHeading = (FRnd()-0.5f)*180*fDistanceFactor; // if we are looking near direction of target if( Abs( aWantedHeading) < 30.0f) { // adjust heading and pich SetDesiredRotation(ANGLE3D(aHeading+fRNDHeading,0,0)); } // just adjust heading else { SetDesiredRotation(ANGLE3D(aHeading,0,0)); } } wait( fWaitFrequency) { on (EBegin) : { resume; } on (EPass epass) : { BOOL bHit; // ignore launcher within 1 second bHit = epass.penOther!=m_penLauncher || _pTimer->CurrentTick()>m_fIgnoreTime; // ignore another LarvaOffspring bHit &= !IsOfClass(epass.penOther, "LarvaOffspring"); // ignore twister bHit &= !IsOfClass(epass.penOther, "Twister"); if (bHit) { ProjectileTouch(epass.penOther); return EEnd(); } resume; } on (EDeath) : { ProjectileHit(); return EEnd(); } on (ETimer) : { stop; } } } return EEnd(); }; Main(ELaunchLarvaOffspring eLaunch) { // remember the initial parameters ASSERT(eLaunch.penLauncher!=NULL); m_penLauncher = eLaunch.penLauncher; SetPredictable(TRUE); // remember lauching time m_fIgnoreTime = _pTimer->CurrentTick() + 1.0f; InitializeProjectile(); // fly m_fStartTime = _pTimer->CurrentTick(); autocall LarvaOffspringGuidedSlide() EEnd; LarvaTailExplosion(); Destroy(); return; } };