/* 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. */ 213 %{ #include "StdH.h" %} uses "EntitiesMP/ModelHolder2"; uses "EntitiesMP/Projectile"; class CEruptor : CModelHolder2 { name "Eruptor"; thumbnail "Thumbnails\\Eruptor.tbn"; features "HasName", "IsTargetable"; properties: // stretch 10 FLOAT m_fStretchAll "Er StretchAll" = 1.0f, 11 FLOAT m_fStretchX "Er StretchX" = 1.0f, 12 FLOAT m_fStretchY "Er StretchY" = 1.0f, 13 FLOAT m_fStretchZ "Er StretchZ" = 1.0f, // random stretch 15 BOOL m_bRandomStretch "Er Stretch Random" = FALSE, // random stretch 16 FLOAT m_fStretchHeight "Er Stretch Height (Y%)" = 0.2f, // stretch height 17 FLOAT m_fStretchWidth "Er Stretch Width (X%)" = 0.2f, // stretch width 18 FLOAT m_fStretchDepth "Er Stretch Depth (Z%)" = 0.2f, // strecth depth // spawn properties 20 FLOAT m_fAngle "Er Angle" 'Q' = 45.0f, // spawn spatial angle 21 FLOAT m_fMaxSpeed "Er Speed max" 'V' = 20.0f, // max speed 22 FLOAT m_fMinSpeed "Er Speed min" 'B' = 10.0f, // min speed 23 FLOAT m_fTime "Er Spawn time" 'F' = 1.0f, // spawn every x seconds 24 FLOAT m_fRandomWait "Er Random wait" 'G' = 0.0f, // wait between two spawns 25 enum ProjectileType m_ptType "Er Type" 'T' = PRT_LAVA_COMET, 26 BOOL m_bShootInArc "Er Shoot In Arc" 'S' = TRUE, 27 FLOAT m_fProjectileStretch "Er projectile stretch" = 1.0f, // strecth components: 1 class CLASS_PROJECTILE "Classes\\Projectile.ecl", functions: void Precache(void) { PrecacheClass(CLASS_PROJECTILE, m_ptType); } /* calculates launch velocity and heading correction for angular launch */ void CalculateAngularLaunchParams( CMovableEntity *penTarget, FLOAT3D vShooting, FLOAT3D vTarget, FLOAT3D vSpeedDest, ANGLE aPitch, ANGLE &aHeading, FLOAT &fLaunchSpeed) { FLOAT3D vNewTarget = vTarget; FLOAT3D &vGravity = penTarget->en_vGravityDir; FLOAT fa = TanFast(AngleDeg(aPitch)); FLOAT3D vd, vyd0; FLOAT fd, fyd0; FLOAT fTime = 0.0f; FLOAT fLastTime = 0.0f; INDEX iIterations = 0; do { iIterations++; FLOAT3D vDistance = vNewTarget-vShooting; GetParallelAndNormalComponents(vDistance, vGravity, vyd0, vd); fd = vd.Length(); fyd0 = vyd0.Length(); fLastTime=fTime; fTime = Sqrt(2.0f)*Sqrt((fa*fd-fyd0)/penTarget->en_fGravityA); vNewTarget = vTarget+vSpeedDest*fTime; } while( Abs(fTime-fLastTime) > _pTimer->TickQuantum && iIterations<10); // calculate launch speed fLaunchSpeed = 0.707108f*fd/ (Cos(AngleDeg(aPitch))*Sqrt((fa*fd-fyd0)/penTarget->en_fGravityA)); // calculate heading correction FLOAT3D vDir = (vNewTarget-vShooting).Normalize(); ANGLE3D aAngles; DirectionVectorToAngles(vDir, aAngles); aHeading = aAngles(1); } // spawn one projectile towards the target void SpawnShoot(CEntity *penTarget) { // if not movable entity if (penTarget==NULL || !(penTarget->GetPhysicsFlags()&EPF_MOVABLE)) { // do nothing return; } CPlacement3D plLava = GetPlacement(); FLOAT fSpeed = (m_fMaxSpeed-m_fMinSpeed)*FRnd() + m_fMinSpeed; // if shootind with free falling projectile if (m_bShootInArc) { // calculate speed for angular launch FLOAT fPitch = GetPlacement().pl_OrientationAngle(2); FLOAT fHeading; CalculateAngularLaunchParams((CMovableEntity*)penTarget, GetPlacement().pl_PositionVector, penTarget->GetPlacement().pl_PositionVector, ((CMovableEntity*)penTarget)->en_vCurrentTranslationAbsolute, fPitch, fHeading, fSpeed); // if the heading is out of range if (Abs(NormalizeAngle(GetPlacement().pl_OrientationAngle(1)-fHeading)) >m_fAngle) { // do nothing return; } plLava.pl_OrientationAngle(1) = fHeading; // if shootind with propelled projectile } else { // calculate direction FLOAT3D vTargetDir = (penTarget->GetPlacement().pl_PositionVector- GetPlacement().pl_PositionVector).Normalize(); FLOAT3D vShootDir; AnglesToDirectionVector(GetPlacement().pl_OrientationAngle, vShootDir); // if the heading is out of range if (Abs(vTargetDir%vShootDir) < Cos(m_fAngle)) { // do nothing return; } DirectionVectorToAngles(vTargetDir, plLava.pl_OrientationAngle); } // create lava SpawnProjectile(plLava, fSpeed); } // spawn one projectile in random direction void SpawnRandom(void) { // generate speed FLOAT fSpeed = (m_fMaxSpeed-m_fMinSpeed)*FRnd() + m_fMinSpeed; ANGLE3D aAngle((FRnd()*2-1)*m_fAngle, (FRnd()*2-1)*m_fAngle, 0); // create placement CPlacement3D plLava(FLOAT3D(0, 0, 0), aAngle); plLava.RelativeToAbsolute(GetPlacement()); SpawnProjectile(plLava, fSpeed); } // fire projectile in given direction with given speed void SpawnProjectile(const CPlacement3D &pl, FLOAT fSpeed) { CEntityPointer penLava = CreateEntity(pl, CLASS_PROJECTILE); // launch ELaunchProjectile eLaunch; eLaunch.penLauncher = this; eLaunch.prtType = m_ptType; eLaunch.fSpeed = fSpeed; eLaunch.fStretch=m_fProjectileStretch; penLava->Initialize(eLaunch); // stretch if (!(penLava->GetFlags()&ENF_DELETED)) { FLOAT3D fStretchRandom(1, 1, 1); if (m_bRandomStretch) { fStretchRandom(1) = (FRnd()*m_fStretchWidth *2 - m_fStretchWidth ) + 1; fStretchRandom(2) = (FRnd()*m_fStretchHeight*2 - m_fStretchHeight) + 1; fStretchRandom(3) = (FRnd()*m_fStretchDepth *2 - m_fStretchDepth ) + 1; } FLOAT3D vOldStretch = penLava->GetModelObject()->mo_Stretch; penLava->GetModelObject()->mo_Stretch = FLOAT3D( m_fStretchAll*m_fStretchX*fStretchRandom(1)*vOldStretch(1), m_fStretchAll*m_fStretchY*fStretchRandom(2)*vOldStretch(2), m_fStretchAll*m_fStretchZ*fStretchRandom(3)*vOldStretch(3)); penLava->ModelChangeNotify(); } } procedures: /************************************************************ * A C T I O N S * ************************************************************/ // active state Active(EVoid) { wait() { on (EBegin) : { call AutoSpawns(); } on (EEnvironmentStop) : { jump Inactive(); } } }; // inactive state Inactive(EVoid) { wait() { on (EBegin) : { resume; } on (EEnvironmentStart) : { jump Active(); } } }; // spawn projectiles automatically AutoSpawns(EVoid) { while (TRUE) { // wait before spawn next autowait(m_fTime); // spawn one projectile SpawnRandom(); // random wait if (m_fRandomWait > 0.0f) { autowait(m_fRandomWait); } } }; /************************************************************ * M A I N L O O P * ************************************************************/ // main loop MainLoop(EVoid) { wait() { on(EBegin) : { call Inactive(); }; on(ETrigger eTrigger) : { SpawnShoot(eTrigger.penCaused); resume; } otherwise() : {resume;}; } // cease to exist Destroy(); return; }; Main(EVoid) { // init as model CModelHolder2::InitModelHolder(); // limit values if (m_fTime <= 0.0f) { m_fTime = 0.05f; } if (m_fMaxSpeed < m_fMinSpeed) { m_fMaxSpeed = m_fMinSpeed; } if (m_fAngle < 0.0f) { m_fAngle = 0.0f; } // stretch factors must not have extreme values if (Abs(m_fStretchX) < 0.01f) { m_fStretchX = 0.01f; } if (Abs(m_fStretchY) < 0.01f) { m_fStretchY = 0.01f; } if (Abs(m_fStretchZ) < 0.01f) { m_fStretchZ = 0.01f; } if (m_fStretchAll< 0.01f) { m_fStretchAll = 0.01f; } if (Abs(m_fStretchX) >100.0f) { m_fStretchX = 100.0f*Sgn(m_fStretchX); } if (Abs(m_fStretchY) >100.0f) { m_fStretchY = 100.0f*Sgn(m_fStretchY); } if (Abs(m_fStretchZ) >100.0f) { m_fStretchZ = 100.0f*Sgn(m_fStretchZ); } if (m_fStretchAll>100.0f) { m_fStretchAll = 100.0f; } if (m_fStretchWidth <0.0f) { m_fStretchWidth = 0.0f; }; if (m_fStretchHeight<0.0f) { m_fStretchHeight = 0.0f; }; if (m_fStretchDepth <0.0f) { m_fStretchDepth = 0.0f; }; if (m_fStretchWidth >1.0f) { m_fStretchWidth = 1.0f; }; if (m_fStretchHeight>1.0f) { m_fStretchHeight = 1.0f; }; if (m_fStretchDepth >1.0f) { m_fStretchDepth = 1.0f; }; jump MainLoop(); } };