/* 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. */ 346 %{ #include "StdH.h" #include "EntitiesMP/BackgroundViewer.h" #include "EntitiesMP/WorldSettingsController.h" #include "EntitiesMP/Common/PathFinding.h" #include "EntitiesMP/NavigationMarker.h" #include "ModelsMP/Enemies/ExotechLarva/ExotechLarva.h" #include "ModelsMP/Enemies/ExotechLarva/Body.h" #include "ModelsMP/Enemies/ExotechLarva/Arm.h" #include "ModelsMP/Enemies/ExotechLarva/BackArms.h" #include "ModelsMP/Enemies/ExotechLarva/Weapons/PlasmaGun.h" %} uses "EntitiesMP/ModelHolder2"; uses "EntitiesMP/Projectile"; uses "EntitiesMP/SoundHolder"; uses "EntitiesMP/BloodSpray"; uses "EntitiesMP/CannonBall"; uses "EntitiesMP/ExotechLarvaCharger"; uses "EntitiesMP/LarvaOffspring"; enum LarvaTarget { 0 LT_NONE "", // no target 1 LT_ENEMY "", // follow enemy 2 LT_RECHARGER "", // go to a recharger point }; event ELarvaArmDestroyed { INDEX iArm, }; event ELarvaRechargePose { BOOL bStart, // animate to pose, or return to default }; %{ // info structure static EntityInfo eiExotechLarva = { EIBT_FLESH, 9999999999.9f, 0.0f, -1.0f, 0.0f, // source (eyes) 0.0f, -1.5f, 0.0f, // target (body) }; #define MF_MOVEZ (1L<<0) #define LARVA_HANDLE_TRANSLATE 4.4f #define FIREPOS_PLASMA_RIGHT FLOAT3D(+3.08f, -1.20f+LARVA_HANDLE_TRANSLATE, -0.16f) #define FIREPOS_PLASMA_LEFT FLOAT3D(-3.08f, -1.20f+LARVA_HANDLE_TRANSLATE, -0.16f) #define FIREPOS_LASER_RIGHT FLOAT3D(+2.31f, 0.16f+LARVA_HANDLE_TRANSLATE, -3.57f) #define FIREPOS_LASER_LEFT FLOAT3D(-2.20f, 0.18f+LARVA_HANDLE_TRANSLATE, -3.57f) #define FIREPOS_TAIL FLOAT3D( 0.00f, -2.64f+LARVA_HANDLE_TRANSLATE, -0.22f) //#define FIREPOS_MOUTH FLOAT3D( 0.00f, -0.75f, -2.09f) // PERCENT_RIGHTBLOW has to be greater then PERCENT_LEFTBLOW or some things // won't work correctly #define PERCENT_RIGHTBLOW 0.6666f #define PERCENT_LEFTBLOW 0.3333f #define ARM_LEFT (1L<<0) #define ARM_RIGHT (1L<<1) %} class CExotechLarva: CEnemyBase { name "ExotechLarva"; thumbnail "Thumbnails\\ExotechLarva.tbn"; properties: 10 CEntityPointer m_penMarkerNew "Larva 1st Grid Marker", 11 CEntityPointer m_penMarkerOld, 15 FLOAT m_fStopRadius "Larva MinDist From Player" = 25.0f, 16 FLOAT m_fStretch = 2.5f, 17 FLOAT m_fLarvaHealth = 20000.0f, 19 FLOAT m_fRechargePerSecond "Larva Recharge health/sec" = 100.0f, 18 enum LarvaTarget m_ltTarget = LT_ENEMY, // type of target 30 CEntityPointer m_penFirstRechargeTarget "Larva First Recharge target", 31 BOOL m_bRechargedAtLeastOnce = FALSE, 20 FLOAT3D m_vFirePosLeftPlasmaRel = FLOAT3D(0.0f, 0.0f, 0.0f), 21 FLOAT3D m_vFirePosRightPlasmaRel = FLOAT3D(0.0f, 0.0f, 0.0f), //22 FLOAT3D m_vFirePosMouthRel = FLOAT3D(0.0f, 0.0f, 0.0f), 23 FLOAT3D m_vFirePosTailRel = FLOAT3D(0.0f, 0.0f, 0.0f), 24 FLOAT3D m_vFirePosLeftLaserAbs = FLOAT3D(0.0f, 0.0f, 0.0f), 25 FLOAT3D m_vFirePosRightLaserAbs = FLOAT3D(0.0f, 0.0f, 0.0f), 40 BOOL m_bLeftArmActive = TRUE, 41 BOOL m_bRightArmActive = TRUE, 42 INDEX m_iExplodingArm = 1, 45 FLOAT m_fMaxRechargedHealth = 1.0f, 46 BOOL m_bExploding = FALSE, 47 BOOL m_bActive = TRUE, 48 BOOL m_bRechargePose = FALSE, 49 BOOL m_bLaserActive = FALSE, 51 BOOL m_bInitialMove = TRUE, 54 CEntityPointer m_penRecharger "Larva Recharger" COLOR(C_GREEN|0xFF), 60 FLOAT m_tmLastTargateChange = 0.0f, // internal positions for explosions 70 CPlacement3D m_plExpArmPos = CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0)), 71 FLOAT3D m_aExpArmRot = FLOAT3D(0.0f, 0.0f, 0.0f), 72 CPlacement3D m_plExpGunPos = CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0)), 73 FLOAT3D m_aExpGunRot = FLOAT3D(0.0f, 0.0f, 0.0f), 74 FLOAT3D m_vExpDamage = FLOAT3D(0.0f, 0.0f, 0.0f), 75 INDEX m_iExplosions = 0, 80 INDEX m_iRnd = 0, // temporary holder for random variable 81 BOOL m_bRecharging = FALSE, // internal // temporary variables for reconstructing lost events 90 CEntityPointer m_penDeathInflictor, 100 FLOAT m_tmDontFireLaserBefore = 0.0f, 101 FLOAT m_fMinimumLaserWait "Larva min. laser interval" = 5.0f, 102 BOOL m_bRenderLeftLaser = FALSE, 103 BOOL m_bRenderRightLaser = FALSE, 104 FLOAT3D m_vLeftLaserTarget = FLOAT3D(0.0f, 0.0f, 0.0f), 105 FLOAT3D m_vRightLaserTarget = FLOAT3D(0.0f, 0.0f, 0.0f), 110 BOOL m_bInvulnerable = FALSE, 150 CEntityPointer m_penLeftArmDestroyTarget "Larva ArmBlow#1 Target", 151 CEntityPointer m_penRightArmDestroyTarget "Larva ArmBlow#2 Target", 152 CEntityPointer m_penDeathTarget "Larva Death Target", 200 CSoundObject m_soFire1, // sound channel for firing 201 CSoundObject m_soFire2, // sound channel for firing 202 CSoundObject m_soFire3, // sound channel for firing 203 CSoundObject m_soVoice, // sound channel for voice 204 CSoundObject m_soChirp, // sound channel for chirping 205 CSoundObject m_soLaser, // sound channel for laser components: 1 class CLASS_BASE "Classes\\EnemyBase.ecl", 2 class CLASS_BASIC_EFFECT "Classes\\BasicEffect.ecl", 3 class CLASS_PROJECTILE "Classes\\Projectile.ecl", 4 class CLASS_BLOOD_SPRAY "Classes\\BloodSpray.ecl", 5 class CLASS_LARVAOFFSPRING "Classes\\LarvaOffspring.ecl", 10 model MODEL_EXOTECHLARVA "ModelsMP\\Enemies\\ExotechLarva\\ExotechLarva.mdl", 11 texture TEXTURE_EXOTECHLARVA "ModelsMP\\Enemies\\ExotechLarva\\ExotechLarva.tex", 12 model MODEL_BODY "ModelsMP\\Enemies\\ExotechLarva\\Body.mdl", 13 texture TEXTURE_BODY "ModelsMP\\Enemies\\ExotechLarva\\Body.tex", 14 model MODEL_BEAM "ModelsMP\\Enemies\\ExotechLarva\\Beam.mdl", 15 texture TEXTURE_BEAM "ModelsMP\\Effects\\Laser\\Laser.tex", 16 model MODEL_ENERGYBEAMS "ModelsMP\\Enemies\\ExotechLarva\\EnergyBeams.mdl", 17 texture TEXTURE_ENERGYBEAMS "ModelsMP\\Enemies\\ExotechLarva\\EnergyBeams.tex", 18 model MODEL_FLARE "ModelsMP\\Enemies\\ExotechLarva\\EffectFlare.mdl", 19 texture TEXTURE_FLARE "ModelsMP\\Enemies\\ExotechLarva\\EffectFlare.tex", 30 model MODEL_WING "ModelsMP\\Enemies\\ExotechLarva\\Arm.mdl", 31 texture TEXTURE_WING "ModelsMP\\Enemies\\ExotechLarva\\Arm.tex", 32 model MODEL_PLASMAGUN "ModelsMP\\Enemies\\ExotechLarva\\Weapons\\PlasmaGun.mdl", 33 texture TEXTURE_PLASMAGUN "ModelsMP\\Enemies\\ExotechLarva\\Weapons\\PlasmaGun.tex", 34 model MODEL_BLADES "ModelsMP\\Enemies\\ExotechLarva\\BackArms.mdl", 36 model MODEL_DEBRIS_BODY "ModelsMP\\Enemies\\ExotechLarva\\Debris\\BodyDebris.mdl", 37 model MODEL_DEBRIS_TAIL01 "ModelsMP\\Enemies\\ExotechLarva\\Debris\\TailDebris01.mdl", 38 model MODEL_DEBRIS_TAIL02 "ModelsMP\\Enemies\\ExotechLarva\\Debris\\TailDebris02.mdl", 39 model MODEL_DEBRIS_FLESH "Models\\Effects\\Debris\\Flesh\\Flesh.mdl", 40 texture TEXTURE_DEBRIS_FLESH "Models\\Effects\\Debris\\Flesh\\FleshRed.tex", 41 model MODEL_PLASMA "ModelsMP\\Enemies\\ExotechLarva\\Projectile\\Projectile.mdl", 42 texture TEXTURE_PLASMA "ModelsMP\\Enemies\\ExotechLarva\\Projectile\\Projectile.tex", // ************** SOUNDS ************** 50 sound SOUND_FIRE_PLASMA "ModelsMP\\Enemies\\ExotechLarva\\Sounds\\FirePlasma.wav", 51 sound SOUND_FIRE_TAIL "ModelsMP\\Enemies\\ExotechLarva\\Sounds\\FireTail.wav", 52 sound SOUND_LASER_CHARGE "ModelsMP\\Enemies\\ExotechLarva\\Sounds\\LaserCharge.wav", 53 sound SOUND_DEATH "ModelsMP\\Enemies\\ExotechLarva\\Sounds\\Death.wav", 54 sound SOUND_ARMDESTROY "ModelsMP\\Enemies\\ExotechLarva\\Sounds\\ArmDestroy.wav", 55 sound SOUND_CHIRP "ModelsMP\\Enemies\\ExotechLarva\\Sounds\\Chirp.wav", 56 sound SOUND_DEPLOYLASER "ModelsMP\\Enemies\\ExotechLarva\\Sounds\\DeployLaser.wav", functions: BOOL IsTargetValid(SLONG slPropertyOffset, CEntity *penTarget) { if( slPropertyOffset == offsetof(CExotechLarva, m_penMarkerNew)) { if (IsOfClass(penTarget, "NavigationMarker")) { return TRUE; } else { return FALSE; } } if( slPropertyOffset == offsetof(CExotechLarva, m_penRecharger)) { if (IsOfClass(penTarget, "ExotechLarvaCharger")) { return TRUE; } else { return FALSE; } } return CEntity::IsTargetValid(slPropertyOffset, penTarget); } BOOL DoSafetyChecks(void) { if (m_penMarkerNew==NULL) { WarningMessage("First ExotechLarva marker not set! Destroying Larva...\n"); return FALSE; } if (m_penRecharger==NULL) { WarningMessage("ExotechLarva Recharger target not set! Destroying Larva...\n"); return FALSE; } return TRUE; } void FindNewTarget() { // if we have a valid enemy, return if (m_penEnemy!=NULL) { if (m_penEnemy->GetFlags()&ENF_ALIVE && !(m_penEnemy->GetFlags()&ENF_DELETED)) { return; } } // find actual number of players INDEX ctMaxPlayers = GetMaxPlayers(); CEntity *penPlayer; for(INDEX i=0; iGetFlags()&ENF_ALIVE) && !(penPlayer->GetFlags()&ENF_DELETED)) { m_penEnemy = penPlayer; } } } } BOOL AnyPlayerCloserThen(FLOAT fDistance) { BOOL bClose = FALSE; // find actual number of players INDEX ctMaxPlayers = GetMaxPlayers(); CEntity *penPlayer; for(INDEX i=0; iGetFlags()&ENF_ALIVE) && !(penPlayer->GetFlags()&ENF_DELETED) && DistanceTo(this, penPlayer)CurrentTick()) { MaybeSwitchToAnotherPlayer(); m_tmLastTargateChange = _pTimer->CurrentTick(); } } class CWorldSettingsController *GetWSC(void) { CWorldSettingsController *pwsc = NULL; // obtain bcg viewer CBackgroundViewer *penBcgViewer = (CBackgroundViewer *) GetWorld()->GetBackgroundViewer(); if( penBcgViewer != NULL) { // obtain world settings controller pwsc = (CWorldSettingsController *) &*penBcgViewer->m_penWorldSettingsController; } return pwsc; } /* Shake ground */ void ShakeItBaby(FLOAT tmShaketime, FLOAT fPower, BOOL bFadeIn) { CWorldSettingsController *pwsc = GetWSC(); if (pwsc!=NULL) { pwsc->m_tmShakeStarted = tmShaketime; pwsc->m_vShakePos = GetPlacement().pl_PositionVector; pwsc->m_fShakeFalloff = 450.0f; pwsc->m_fShakeFade = 3.0f; pwsc->m_fShakeIntensityZ = 0; pwsc->m_tmShakeFrequencyZ = 5.0f; pwsc->m_fShakeIntensityY = 0.1f*fPower; pwsc->m_tmShakeFrequencyY = 5.0f; pwsc->m_fShakeIntensityB = 2.5f*fPower; pwsc->m_tmShakeFrequencyB = 7.2f; pwsc->m_bShakeFadeIn = bFadeIn; } } void ShootTailProjectile(void) { //ShootProjectile(PRT_LARVA_TAIL_PROJECTILE, m_vFirePosTailRel, ANGLE3D(0, -10, 0)); if (m_penEnemy == NULL) { return; } // target enemy body EntityInfo *peiTarget = (EntityInfo*) (m_penEnemy->GetEntityInfo()); FLOAT3D vShootTarget; GetEntityInfoPosition(m_penEnemy, peiTarget->vTargetCenter, vShootTarget); // launch CPlacement3D pl; PreparePropelledProjectile(pl, vShootTarget, m_vFirePosTailRel, ANGLE3D(0, -10, 0)); CEntityPointer penProjectile = CreateEntity(pl, CLASS_LARVAOFFSPRING); ELaunchLarvaOffspring ello; ello.penLauncher = this; penProjectile->Initialize(ello); } BOOL IsOnMarker(CEntity *penMarker) { if (penMarker==NULL) { return FALSE; } if (DistanceTo(this, penMarker)<0.1f) { return TRUE; } // else return FALSE; } FLOAT DistanceXZ(CEntity *E1, CEntity *E2) { FLOAT3D vE1pos = E1->GetPlacement().pl_PositionVector; FLOAT3D vE2pos = E2->GetPlacement().pl_PositionVector; vE1pos(2)=0.0f; vE2pos(2)=0.0f; return (vE2pos - vE1pos).Length(); } void SpawnWingDebris() { FLOAT3D vTranslation = m_vExpDamage + en_vCurrentTranslationAbsolute; Debris_Begin(EIBT_FLESH, DPT_BLOODTRAIL, BET_BLOODSTAIN, 1.0f, m_vExpDamage, en_vCurrentTranslationAbsolute, 5.0f, 2.0f); Debris_Spawn_Independent(this, this, MODEL_WING, TEXTURE_WING, 0, 0, 0, 0, m_fStretch, m_plExpArmPos, vTranslation , m_aExpArmRot); vTranslation += FLOAT3D(FRnd()*4.0f-2.0f, FRnd()*4.0f-2.0f, FRnd()*4.0f-2.0f); Debris_Spawn_Independent(this, this, MODEL_PLASMAGUN, TEXTURE_PLASMAGUN, 0, 0, 0, 0, m_fStretch, m_plExpGunPos, vTranslation , m_aExpGunRot); } void ReceiveDamage(CEntity *penInflictor, enum DamageType dmtType, FLOAT fDamageAmmount, const FLOAT3D &vHitPoint, const FLOAT3D &vDirection) { if (m_bInvulnerable) { return; } // cannot hurt ourselves if (IsOfClass(penInflictor, "ExotechLarva")) { return; } // preliminary adjustment of damage // take less damage from heavy bullets (e.g. sniper) if(dmtType==DMT_BULLET && fDamageAmmount>100.0f) { fDamageAmmount *= 0.66f; } // cannonballs inflict less damage then the default if(dmtType==DMT_CANNONBALL) { fDamageAmmount *= 0.5f; } FLOAT fHealthNow = GetHealth(); FLOAT fHealthAfter = GetHealth() - fDamageAmmount; FLOAT fHealthBlow01 = m_fMaxHealth*PERCENT_RIGHTBLOW; FLOAT fHealthBlow02 = m_fMaxHealth*PERCENT_LEFTBLOW; // adjust damage fDamageAmmount *=DamageStrength( ((EntityInfo*)GetEntityInfo())->Eeibt, dmtType); // apply game extra damage per enemy and per player fDamageAmmount *=GetGameDamageMultiplier(); // enough damage to blow both arms if (fHealthNow>fHealthBlow01 && fHealthAfterfHealthBlow02 && fHealthAfterfHealthBlow02 && fHealthAfter<0) { fDamageAmmount = fHealthNow - 1; } // if no damage if (fDamageAmmount ==0) { // do nothing return; } // spawn blood spray CPlacement3D plSpray = CPlacement3D( vHitPoint, ANGLE3D(0, 0, 0)); m_penSpray = CreateEntity( plSpray, CLASS_BLOOD_SPRAY); ESpawnSpray eSpawnSpray; eSpawnSpray.colBurnColor=C_WHITE|CT_OPAQUE; if( m_fMaxDamageAmmount > 10.0f) { eSpawnSpray.fDamagePower = 3.0f; } else if(m_fSprayDamage+fDamageAmmount>50.0f) { eSpawnSpray.fDamagePower = 2.0f; } else { eSpawnSpray.fDamagePower = 1.0f; } switch(IRnd()%4) { case 0: case 1: case 2: // blood spray m_penSpray->SetParent(this); eSpawnSpray.sptType = SPT_BLOOD; break; case 3: // sparks spray eSpawnSpray.sptType = SPT_ELECTRICITY_SPARKS; break; } eSpawnSpray.fSizeMultiplier = 1.0f; // setup direction of spray FLOAT3D vHitPointRelative = vHitPoint - GetPlacement().pl_PositionVector; FLOAT3D vReflectingNormal; GetNormalComponent( vHitPointRelative, en_vGravityDir, vReflectingNormal); vReflectingNormal.Normalize(); vReflectingNormal(1)/=5.0f; FLOAT3D vProjectedComponent = vReflectingNormal*(vDirection%vReflectingNormal); FLOAT3D vSpilDirection = vDirection-vProjectedComponent*2.0f-en_vGravityDir*0.5f; eSpawnSpray.vDirection = vSpilDirection; eSpawnSpray.penOwner = this; // initialize spray m_penSpray->Initialize( eSpawnSpray); m_tmSpraySpawned = _pTimer->CurrentTick(); m_fSprayDamage = 0.0f; m_fMaxDamageAmmount = 0.0f; // instead of: // CMovableModelEntity::ReceiveDamage(penInflictor, dmtType, fDamageAmmount, // vHitPoint, vDirection); // do this (because we don't want an event posted each time we're damaged): // reduce your health en_fHealth-=fDamageAmmount; // if health reached zero if (en_fHealth<=0) { // throw an event that you have died EDeath eDeath; SendEvent(eDeath); } if (m_bRightArmActive) { if (GetHealth()GetAttachmentModel(BODY_ATTACHMENT_ARM_LEFT); amo = amo->amo_moModelObject.GetAttachmentModel(ARM_ATTACHMENT_PLASMAGUN); return &(amo->amo_moModelObject); }; CModelObject *PlasmaRightModel(void) { CAttachmentModelObject *amo = GetModelObject()->GetAttachmentModel(BODY_ATTACHMENT_ARM_RIGHT); amo = amo->amo_moModelObject.GetAttachmentModel(ARM_ATTACHMENT_PLASMAGUN); return &(amo->amo_moModelObject); }; BOOL RechargerActive() { if (((CExotechLarvaCharger *)&*m_penRecharger)->m_bActive) { return TRUE; } return FALSE; } /*void CheckRechargerTargets(void) { m_bRechargerExists = FALSE; CEntityPointer *penModel = &m_penRCModel01; CEntityPointer *penMarker = &m_penRCMarker01; for (INDEX i=0; i<4; i++) { // if model pointer is valid and model is not destroyed if (&*penModel[i]) { if (!((*penModel[i]).en_ulFlags&ENF_DELETED)) { // at least one exists m_bRechargerExists = TRUE; } else if (m_penRechargerTarget==&*penMarker[i]) { m_penRechargerTarget=NULL; penMarker[i] = NULL; } // otherwise make sure it is not the current recharging target } else if (m_penRechargerTarget==&*penMarker[i]) { m_penRechargerTarget=NULL; penMarker[i] = NULL; } } } BOOL CurrentRechargerExists(void) { if (m_penRechargerTarget) { if (!(m_penRechargerTarget->en_ulFlags&ENF_DELETED)) { return TRUE; }; } return FALSE; } BOOL FindClosestRecharger(void) { FLOAT fDistance = UpperLimit(1.0f); m_penRechargerTarget = NULL; if (m_penRCMarker01) { if (DistanceTo(this, m_penRCMarker01)GetAttachmentModel(BODY_ATTACHMENT_ARM_LEFT); return (amo.amo_plRelative.pl_OrientationAngle(2) + GetPlacement().pl_OrientationAngle(2)); } return 0.0f; } ULONG SetDesiredMovement(void) { ULONG ulFlags = 0; FLOAT3D vPos; CEntity *penMarker = m_penMarkerNew; CEntity *penTarget; if (m_ltTarget==LT_ENEMY && m_penEnemy) { penTarget = m_penEnemy; } else if (m_ltTarget==LT_RECHARGER) { penTarget = m_penRecharger; } else { return ulFlags; } // CPrintF("target = %s at %f\n", penTarget->GetName(), _pTimer->CurrentTick()); if (IsOnMarker(m_penMarkerNew)) { PATH_FindNextMarker(penTarget, GetPlacement().pl_PositionVector, penTarget->GetPlacement().pl_PositionVector, penMarker, vPos); if (penMarker!=NULL) { // remember the old marker m_penMarkerOld = m_penMarkerNew; // and set the new target m_penMarkerNew = penMarker; } MoveToMarker(m_penMarkerNew); ulFlags |= MF_MOVEZ; } else { MoveToMarker(m_penMarkerNew); ulFlags |= MF_MOVEZ; } if (m_ltTarget==LT_ENEMY && DistanceTo(this, penTarget)GetPlacement().pl_PositionVector - GetPlacement().pl_PositionVector; if (vDesiredDir.Length()>0.0f) { vDesiredDir.Normalize(); FLOAT3D vSpeed = vDesiredDir*m_fAttackRunSpeed; SetDesiredTranslation(vSpeed); } } // pre moving void PreMoving() { if (m_bActive && !m_bRenderLeftLaser && !m_bRenderRightLaser) { // rotate to enemy if (m_penEnemy!=NULL) { FLOAT3D vToEnemy; vToEnemy = (m_penEnemy->GetPlacement().pl_PositionVector - GetPlacement().pl_PositionVector).Normalize(); ANGLE3D aAngle; DirectionVectorToAngles(vToEnemy, aAngle); aAngle(1) = aAngle(1) - GetPlacement().pl_OrientationAngle(1); aAngle(1) = NormalizeAngle(aAngle(1)); SetDesiredRotation(FLOAT3D(aAngle(1)*2.0f, 0.0f, 0.0f)); } else { SetDesiredRotation(FLOAT3D(0.0f, 0.0f, 0.0f)); } // lower speed if needed, not to miss the marker if (en_vCurrentTranslationAbsolute.Length()*_pTimer->TickQuantum*2.0f > DistanceTo(this, m_penMarkerNew)) { FLOAT3D vToMarker = m_penMarkerNew->GetPlacement().pl_PositionVector - GetPlacement().pl_PositionVector; SetDesiredTranslation(vToMarker/_pTimer->TickQuantum) ; } // stop when on marker if (IsOnMarker(m_penMarkerNew)) { ForceStopTranslation(); } } else { ForceFullStop(); } CEnemyBase::PreMoving(); } void RenderParticles(void) { FLOATmatrix3D m; CPlacement3D plLarva; if (m_bRenderLeftLaser || m_bRenderRightLaser) { plLarva = GetLerpedPlacement(); MakeRotationMatrix(m, plLarva.pl_OrientationAngle); } if (m_bRenderLeftLaser) { FLOAT3D vSource = (FIREPOS_LASER_LEFT*m_fStretch)*m + plLarva.pl_PositionVector; Particles_ExotechLarvaLaser(this, vSource, m_vLeftLaserTarget); } if (m_bRenderRightLaser) { FLOAT3D vSource = (FIREPOS_LASER_RIGHT*m_fStretch)*m + plLarva.pl_PositionVector; Particles_ExotechLarvaLaser(this, vSource, m_vRightLaserTarget); } if (m_bRechargePose && ((CExotechLarvaCharger *)&*m_penRecharger)->m_bBeamActive) { Particles_LarvaEnergy(this, FLOAT3D(0.0f, LARVA_HANDLE_TRANSLATE, 0.0f)*m_fStretch); } } void SizeModel(void) { return; } void UpdateFiringPos() { m_vFirePosLeftLaserAbs = (FIREPOS_LASER_LEFT*m_fStretch)*GetRotationMatrix() + GetPlacement().pl_PositionVector; m_vFirePosRightLaserAbs = (FIREPOS_LASER_RIGHT*m_fStretch)*GetRotationMatrix() + GetPlacement().pl_PositionVector; } void BlowUp(void) { NOTHING; } // adjust sound and watcher parameters here if needed void EnemyPostInit(void) { m_soFire1.Set3DParameters(600.0f, 150.0f, 2.0f, 1.0f); m_soFire2.Set3DParameters(600.0f, 150.0f, 2.0f, 1.0f); m_soFire3.Set3DParameters(600.0f, 150.0f, 2.0f, 1.0f); m_soVoice.Set3DParameters(600.0f, 150.0f, 2.0f, 1.0f); m_soChirp.Set3DParameters(150.0f, 50.0f, 2.0f, 1.0f); m_soLaser.Set3DParameters(300.0f, 200.0f, 3.0f, 1.0f); } void FireLaser(void) { FLOAT3D vLaserTarget; if (!m_penEnemy) { return; } if (IsVisible(m_penEnemy)) { vLaserTarget = m_penEnemy->GetPlacement().pl_PositionVector; } else if (TRUE) { vLaserTarget = m_vPlayerSpotted; } // cast 1st ray CCastRay crRay1( this, m_vFirePosLeftLaserAbs, vLaserTarget); crRay1.cr_fTestR = 0.10f; crRay1.cr_bHitTranslucentPortals = FALSE; crRay1.cr_bPhysical = FALSE; crRay1.cr_ttHitModels = CCastRay::TT_COLLISIONBOX; GetWorld()->CastRay(crRay1); // if entity is hit if( crRay1.cr_penHit != NULL) { m_bRenderLeftLaser = TRUE; m_vLeftLaserTarget = crRay1.cr_vHit; // apply damage InflictDirectDamage( crRay1.cr_penHit, this, DMT_BURNING, 25.0f, FLOAT3D(0, 0, 0), (m_vFirePosLeftLaserAbs-m_vLeftLaserTarget).Normalize()); if (crRay1.cr_penHit->GetRenderType()!=RT_BRUSH) { crRay1.cr_ttHitModels = CCastRay::TT_NONE; GetWorld()->ContinueCast(crRay1); if (crRay1.cr_penHit != NULL) { m_vLeftLaserTarget = crRay1.cr_vHit; } } } else if (TRUE) { m_bRenderLeftLaser = FALSE; } // cast 2nd ray CCastRay crRay2( this, m_vFirePosRightLaserAbs, vLaserTarget); crRay2.cr_fTestR = 0.10f; crRay2.cr_bHitTranslucentPortals = FALSE; crRay2.cr_bPhysical = FALSE; crRay2.cr_ttHitModels = CCastRay::TT_COLLISIONBOX; GetWorld()->CastRay(crRay2); // if entity is hit if( crRay2.cr_penHit != NULL) { m_bRenderRightLaser = TRUE; m_vRightLaserTarget = crRay2.cr_vHit; // apply damage InflictDirectDamage( crRay2.cr_penHit, this, DMT_BURNING, 25.0f, FLOAT3D(0, 0, 0), (m_vFirePosRightLaserAbs-m_vRightLaserTarget).Normalize()); if (crRay2.cr_penHit->GetRenderType()!=RT_BRUSH) { crRay2.cr_ttHitModels = CCastRay::TT_NONE; GetWorld()->ContinueCast(crRay2); if (crRay2.cr_penHit != NULL) { m_vRightLaserTarget = crRay2.cr_vHit; } } } else if (TRUE) { m_bRenderRightLaser = FALSE; } } void ExplodeLaser(void) { if (m_bRenderLeftLaser) { ESpawnEffect eSpawnEffect; eSpawnEffect.colMuliplier = C_WHITE|CT_OPAQUE; eSpawnEffect.betType = BET_CANNON; eSpawnEffect.vStretch = FLOAT3D(m_fStretch*0.5, m_fStretch*0.5, m_fStretch*0.5); CEntityPointer penExplosion = CreateEntity(CPlacement3D(m_vLeftLaserTarget, ANGLE3D(0.0f, 0.0f, 0.0f)), CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); // explosion debris eSpawnEffect.betType = BET_EXPLOSION_DEBRIS; penExplosion = CreateEntity(CPlacement3D(m_vLeftLaserTarget, ANGLE3D(0.0f, 0.0f, 0.0f)), CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); // explosion smoke eSpawnEffect.betType = BET_EXPLOSION_SMOKE; penExplosion = CreateEntity(CPlacement3D(m_vLeftLaserTarget, ANGLE3D(0.0f, 0.0f, 0.0f)), CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); InflictRangeDamage( this, DMT_EXPLOSION, 25.0f, m_vLeftLaserTarget, 5.0f, 25.0f); } if (m_bRenderRightLaser) { ESpawnEffect eSpawnEffect; eSpawnEffect.colMuliplier = C_WHITE|CT_OPAQUE; eSpawnEffect.betType = BET_CANNON; eSpawnEffect.vStretch = FLOAT3D(m_fStretch*0.5, m_fStretch*0.5, m_fStretch*0.5); CEntityPointer penExplosion = CreateEntity(CPlacement3D(m_vLeftLaserTarget, ANGLE3D(0.0f, 0.0f, 0.0f)), CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); // explosion debris eSpawnEffect.betType = BET_EXPLOSION_DEBRIS; penExplosion = CreateEntity(CPlacement3D(m_vLeftLaserTarget, ANGLE3D(0.0f, 0.0f, 0.0f)), CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); // explosion smoke eSpawnEffect.betType = BET_EXPLOSION_SMOKE; penExplosion = CreateEntity(CPlacement3D(m_vLeftLaserTarget, ANGLE3D(0.0f, 0.0f, 0.0f)), CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); InflictRangeDamage( this, DMT_EXPLOSION, 25.0f, m_vLeftLaserTarget, 5.0f, 25.0f); } } /****************************************/ /* P R O C E D U R E S */ /****************************************/ procedures: // override wounding so that Larva doesn't stutter BeWounded(EDamage eDamage) : CEnemyBase::BeWounded { return EReturn(); }; ArmExplosion() { FLOATmatrix3D mRot; FLOAT3D vPos; m_bActive = FALSE; // right arm if (m_iExplodingArm==ARM_RIGHT) { MakeRotationMatrixFast(mRot, ANGLE3D(0.0f, 0.0f, 0.0f)); vPos = FLOAT3D(0.0f, 0.0f, 0.0f); GetModelForRendering()->GetAttachmentTransformations(BODY_ATTACHMENT_ARM_RIGHT, mRot, vPos, FALSE); m_plExpArmPos.pl_PositionVector = vPos*GetRotationMatrix() + GetPlacement().pl_PositionVector; m_plExpArmPos.pl_OrientationAngle = GetPlacement().pl_OrientationAngle; CAttachmentModelObject &amo0 = *GetModelObject()->GetAttachmentModel(BODY_ATTACHMENT_ARM_RIGHT); amo0.amo_moModelObject.GetAttachmentTransformations(ARM_ATTACHMENT_PLASMAGUN, mRot, vPos, FALSE); m_plExpGunPos.pl_PositionVector = vPos*GetRotationMatrix() + GetPlacement().pl_PositionVector; m_plExpGunPos.pl_OrientationAngle = GetPlacement().pl_OrientationAngle; m_vExpDamage = FLOAT3D( +12.0f, 15.0f, 0.0f); if (m_penLeftArmDestroyTarget) { SendToTarget(m_penLeftArmDestroyTarget, EET_TRIGGER, FixupCausedToPlayer(this, m_penEnemy)); } } // left arm if (m_iExplodingArm==ARM_LEFT) { MakeRotationMatrixFast(mRot, ANGLE3D(0.0f, 0.0f, 0.0f)); vPos = FLOAT3D(0.0f, 0.0f, 0.0f); GetModelForRendering()->GetAttachmentTransformations(BODY_ATTACHMENT_ARM_LEFT, mRot, vPos, FALSE); m_plExpArmPos.pl_PositionVector = vPos*GetRotationMatrix() + GetPlacement().pl_PositionVector; m_plExpArmPos.pl_OrientationAngle = GetPlacement().pl_OrientationAngle; m_plExpArmPos.pl_OrientationAngle(1)+=180.0f; CAttachmentModelObject &amo0 = *GetModelObject()->GetAttachmentModel(BODY_ATTACHMENT_ARM_LEFT); amo0.amo_moModelObject.GetAttachmentTransformations(ARM_ATTACHMENT_PLASMAGUN, mRot, vPos, FALSE); m_plExpGunPos.pl_PositionVector = vPos*GetRotationMatrix() + GetPlacement().pl_PositionVector; m_plExpGunPos.pl_OrientationAngle = GetPlacement().pl_OrientationAngle; m_vExpDamage = FLOAT3D( -12.0f, 15.0f, 0.0f); if (m_penRightArmDestroyTarget) { SendToTarget(m_penRightArmDestroyTarget, EET_TRIGGER, FixupCausedToPlayer(this, m_penEnemy)); } } m_aExpArmRot = ANGLE3D(FRnd()*360.0f-180.0f, FRnd()*360.0f-180.0f, FRnd()*360.0f-180.0f); m_aExpGunRot = ANGLE3D(FRnd()*360.0f-180.0f, FRnd()*360.0f-180.0f, FRnd()*360.0f-180.0f); m_vExpDamage = m_vExpDamage*GetRotationMatrix(); if (m_iExplodingArm==ARM_RIGHT) { m_bRightArmActive = FALSE; } if (m_iExplodingArm==ARM_LEFT) { m_bLeftArmActive = FALSE; } PlaySound(m_soVoice, SOUND_ARMDESTROY, SOF_3D); // spawn explosion #1 CPlacement3D pl = GetPlacement(); pl.pl_PositionVector += FLOAT3D(0.0f, LARVA_HANDLE_TRANSLATE, 0.0f); ShakeItBaby(_pTimer->CurrentTick(), 0.5f, FALSE); ESpawnEffect eSpawnEffect; eSpawnEffect.colMuliplier = C_WHITE|CT_OPAQUE; eSpawnEffect.betType = BET_CANNON; eSpawnEffect.vStretch = FLOAT3D(m_fStretch*0.5, m_fStretch*0.5, m_fStretch*0.5); CEntityPointer penExplosion = CreateEntity(pl, CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); autowait(FRnd()*0.25f+0.15f); // spawn explosion #2 ShakeItBaby(_pTimer->CurrentTick(), 0.5f, FALSE); ESpawnEffect eSpawnEffect; eSpawnEffect.colMuliplier = C_WHITE|CT_OPAQUE; eSpawnEffect.betType = BET_CANNON; eSpawnEffect.vStretch = FLOAT3D(m_fStretch, m_fStretch, m_fStretch); CPlacement3D plMiddle; plMiddle.pl_PositionVector = (m_plExpArmPos.pl_PositionVector + m_plExpGunPos.pl_PositionVector)/2.0f; plMiddle.pl_OrientationAngle = m_plExpArmPos.pl_OrientationAngle; CEntityPointer penExplosion = CreateEntity(plMiddle, CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); autowait(FRnd()*0.15f+0.15f); // spawn explosion #3 & #4 CPlacement3D pl = GetPlacement(); pl.pl_PositionVector += FLOAT3D(0.0f, LARVA_HANDLE_TRANSLATE, 0.0f); ShakeItBaby(_pTimer->CurrentTick(), 1.0f, FALSE); ESpawnEffect eSpawnEffect; eSpawnEffect.colMuliplier = C_WHITE|CT_OPAQUE; eSpawnEffect.betType = BET_CANNON; eSpawnEffect.vStretch = FLOAT3D(m_fStretch*1.5,m_fStretch*1.5,m_fStretch*1.5); CEntityPointer penExplosion = CreateEntity(pl, CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); eSpawnEffect.betType = BET_ROCKET; penExplosion = CreateEntity(m_plExpGunPos, CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); SpawnWingDebris(); RemoveWing(m_iExplodingArm); // wait a little bit 'to recover' autowait(1.5f); m_bExploding = FALSE; m_bActive = TRUE; // restart behaveour SendEvent(EBegin()); return EReturn(); } Die(EDeath eDeath) : CEnemyBase::Die { m_penDeathInflictor = eDeath.eLastDamage.penInflictor; m_bActive = FALSE; m_iExplosions = 8; PlaySound(m_soChirp, SOUND_DEATH, SOF_3D); m_soLaser.Stop(); // spawn explosions while ((m_iExplosions--)>0) { ShakeItBaby(_pTimer->CurrentTick(), 0.5f, FALSE); // randomize explosion position and size CPlacement3D plExplosion; plExplosion.pl_OrientationAngle = ANGLE3D(0.0f, 0.0f, 0.0f); plExplosion.pl_PositionVector = FLOAT3D(FRnd()*2.0-1.0f, FRnd()*3.0-1.5f+LARVA_HANDLE_TRANSLATE, FRnd()*2.0-1.0f)*m_fStretch + GetPlacement().pl_PositionVector; FLOAT vExpSize = (FRnd()*0.7f+0.7f)*m_fStretch; ESpawnEffect eSpawnEffect; eSpawnEffect.colMuliplier = C_WHITE|CT_OPAQUE; eSpawnEffect.betType = BET_CANNON; eSpawnEffect.vStretch = FLOAT3D(vExpSize, vExpSize, vExpSize); CEntityPointer penExplosion = CreateEntity(plExplosion, CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); autowait(FRnd()*0.05f+0.35f); } ShakeItBaby(_pTimer->CurrentTick(), 2.0f, FALSE); // final explosions CPlacement3D plExplosion; plExplosion.pl_OrientationAngle = ANGLE3D(0.0f, 0.0f, 0.0f); plExplosion.pl_PositionVector = FLOAT3D(0.0f,-1.5f+LARVA_HANDLE_TRANSLATE, 1.5f)*m_fStretch + GetPlacement().pl_PositionVector; ESpawnEffect eSpawnEffect; eSpawnEffect.colMuliplier = C_WHITE|CT_OPAQUE; eSpawnEffect.betType = BET_CANNON; eSpawnEffect.vStretch = FLOAT3D(m_fStretch, m_fStretch, m_fStretch)*2.0f; CEntityPointer penExplosion = CreateEntity(plExplosion, CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); plExplosion.pl_PositionVector = FLOAT3D(-1.0f,-0.2f+LARVA_HANDLE_TRANSLATE,-1.5f)*m_fStretch + GetPlacement().pl_PositionVector; penExplosion = CreateEntity(plExplosion, CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); plExplosion.pl_PositionVector = FLOAT3D(1.0f, 1.7f+LARVA_HANDLE_TRANSLATE, 0.1f)*m_fStretch + GetPlacement().pl_PositionVector; penExplosion = CreateEntity(plExplosion, CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); plExplosion.pl_PositionVector = GetPlacement().pl_PositionVector; eSpawnEffect.betType = BET_ROCKET; penExplosion = CreateEntity(plExplosion, CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); // end debris m_vExpDamage = FLOAT3D( 0.0f, 15.0f, 0.0f); FLOAT3D vTranslation = m_vExpDamage; CPlacement3D plDebris = GetPlacement(); plDebris.pl_PositionVector += FLOAT3D(0.0f, LARVA_HANDLE_TRANSLATE, 0.0f); Debris_Begin(EIBT_FLESH, DPT_BLOODTRAIL, BET_BLOODSTAIN, 1.0f, m_vExpDamage, en_vCurrentTranslationAbsolute, 5.0f, 2.0f); Debris_Spawn_Independent(this, this, MODEL_DEBRIS_BODY, TEXTURE_BODY, 0, 0, 0, 0, m_fStretch, plDebris, vTranslation, ANGLE3D(45.0f, 230.0f, 0.0f)); vTranslation += FLOAT3D(FRnd()*4.0f-2.0f, FRnd()*4.0f-2.0f, FRnd()*4.0f-2.0f); Debris_Spawn_Independent(this, this, MODEL_DEBRIS_TAIL01, TEXTURE_BODY, 0, 0, 0, 0, m_fStretch, plDebris, vTranslation, ANGLE3D(15.0f, 130.0f, 0.0f)); vTranslation += FLOAT3D(FRnd()*4.0f-2.0f, FRnd()*4.0f-2.0f, FRnd()*4.0f-2.0f); Debris_Spawn_Independent(this, this, MODEL_DEBRIS_TAIL02, TEXTURE_BODY, 0, 0, 0, 0, m_fStretch, plDebris, vTranslation, ANGLE3D(145.0f, 30.0f, 0.0f)); for (INDEX i=0; i<8; i++) { Debris_Spawn(this, this, MODEL_DEBRIS_FLESH, TEXTURE_DEBRIS_FLESH , 0, 0, 0, 0, m_fStretch, FLOAT3D(FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f+LARVA_HANDLE_TRANSLATE, FRnd()*0.6f+0.2f)); } // explosion eSpawnEffect.colMuliplier = C_WHITE|CT_OPAQUE; eSpawnEffect.betType = BET_EXPLOSION_DEBRIS; eSpawnEffect.vStretch = FLOAT3D(1,1,1); penExplosion = CreateEntity(plExplosion, CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); // explosion smoke eSpawnEffect.betType = BET_EXPLOSION_SMOKE; penExplosion = CreateEntity(plExplosion, CLASS_BASIC_EFFECT); penExplosion->Initialize(eSpawnEffect); EDeath eDeath; eDeath.eLastDamage.penInflictor = m_penDeathInflictor; EActivateBeam eab; eab.bTurnOn = FALSE; m_penRecharger->SendEvent(eab); if (m_penDeathTarget) { SendToTarget(m_penDeathTarget, EET_TRIGGER, FixupCausedToPlayer(this, m_penEnemy)); } jump CEnemyBase::Die(eDeath); } Fire(EVoid) : CEnemyBase::Fire { UpdateFiringPos(); // if larva has at least one wing if (m_bLeftArmActive || m_bRightArmActive) { m_iRnd = IRnd()%9; if (m_iRnd>6 && !m_bRechargePose && GetHealth()>0.1f*m_fMaxHealth) { PlaySound(m_soFire3, SOUND_FIRE_TAIL, SOF_3D); ShootTailProjectile(); } if (m_iRnd>6 && m_bRechargePose) { m_iRnd = 3; } if (m_iRnd>3) { return EReturn(); } while(m_iRnd>0) { if (m_bLeftArmActive) { PlaySound(m_soFire1, SOUND_FIRE_PLASMA, SOF_3D); ShootProjectile(PRT_LARVA_PLASMA, m_vFirePosLeftPlasmaRel, ANGLE3D(0, 0, 0)); RemoveAttachmentFromModel(*PlasmaLeftModel(), PLASMAGUN_ATTACHMENT_PROJECTILE); autowait(0.25f); PlasmaLeftModel()->PlayAnim(PLASMAGUN_ANIM_SPAWNING, 0); autowait(0.25f); AddAttachmentToModel(this, *PlasmaLeftModel(), PLASMAGUN_ATTACHMENT_PROJECTILE, MODEL_PLASMA, TEXTURE_PLASMA, 0, 0, 0); CAttachmentModelObject *amo = PlasmaLeftModel()->GetAttachmentModel(BODY_ATTACHMENT_ARM_LEFT); amo->amo_moModelObject.StretchModel(FLOAT3D(m_fStretch, m_fStretch, m_fStretch)); } if (m_bRightArmActive) { PlaySound(m_soFire2, SOUND_FIRE_PLASMA, SOF_3D); ShootProjectile(PRT_LARVA_PLASMA, m_vFirePosRightPlasmaRel, ANGLE3D(0, 0, 0)); RemoveAttachmentFromModel(*PlasmaRightModel(), PLASMAGUN_ATTACHMENT_PROJECTILE); autowait(0.25f); PlasmaRightModel()->PlayAnim(PLASMAGUN_ANIM_SPAWNING, 0); autowait(0.25f); AddAttachmentToModel(this, *PlasmaRightModel(), PLASMAGUN_ATTACHMENT_PROJECTILE, MODEL_PLASMA, TEXTURE_PLASMA, 0, 0, 0); CAttachmentModelObject *amo = PlasmaRightModel()->GetAttachmentModel(BODY_ATTACHMENT_ARM_LEFT); amo->amo_moModelObject.StretchModel(FLOAT3D(m_fStretch, m_fStretch, m_fStretch)); } m_iRnd--; } // if no wings left fire laser } else if (TRUE) { m_iRnd = IRnd()%10; if (m_iRnd>6 && !m_bRechargePose && GetHealth()>0.1f*m_fMaxHealth) { PlaySound(m_soFire3, SOUND_FIRE_TAIL, SOF_3D); ShootTailProjectile(); } if (m_iRnd<4 && _pTimer->CurrentTick()>m_tmDontFireLaserBefore) { PlaySound(m_soLaser, SOUND_LASER_CHARGE, SOF_3D); SpawnReminder(this, 3.0f, 129); m_tmDontFireLaserBefore = _pTimer->CurrentTick()+m_fMinimumLaserWait; } } PerhapsChangeTarget(); return EReturn(); } Hit(EVoid) : CEnemyBase::Hit { return EReturn(); } BeIdle(EVoid) : CEnemyBase::BeIdle { PerhapsChangeTarget(); autowait(0.5f); while (TRUE) { FindNewTarget(); SendEvent(EReconsiderBehavior()); autowait(0.5f); } }; LarvaLoop() { FindNewTarget(); SendEvent(EReconsiderBehavior()); StartModelAnim(BODY_ANIM_IDLE, AOF_SMOOTHCHANGE|AOF_LOOPING); SpawnReminder(this, 0.5f, 128); SpawnReminder(this, 0.5f, 145); // fire guided reminder wait () { on (EBegin) : { if (!m_bLeftArmActive && !m_bRightArmActive) { CModelObject &amo = GetModelObject()->GetAttachmentModel(BODY_ATTACHMENT_BACKARMS)->amo_moModelObject; amo.PlayAnim(BACKARMS_ANIM_ACTIVATING, AOF_SMOOTHCHANGE|AOF_NORESTART); PlaySound(m_soFire1, SOUND_DEPLOYLASER, SOF_3D); SpawnReminder(this, amo.GetAnimLength(BACKARMS_ANIM_ACTIVATING), 160); } call CEnemyBase::MainLoop(); } on (ELarvaArmDestroyed ead) : { m_iExplodingArm = ead.iArm; call ArmExplosion(); } on (ELarvaRechargePose elrp) : { if (elrp.bStart==TRUE && m_bRechargePose!=TRUE) { StartModelAnim(BODY_ANIM_TORECHARGING, AOF_SMOOTHCHANGE|AOF_NORESTART); SpawnReminder(this, GetModelObject()->GetAnimLength(BODY_ANIM_TORECHARGING), 156); } if (elrp.bStart==FALSE && m_bRechargePose!=FALSE) { StartModelAnim(BODY_ANIM_FROMRECHARGING, AOF_SMOOTHCHANGE|AOF_NORESTART); SpawnReminder(this, GetModelObject()->GetAnimLength(BODY_ANIM_FROMRECHARGING), 157); } resume; } on (EReminder er) : { // if this is not our reminder, pass (although there are no states below this?) if (er.iValue==128) { // while recharger is active keep respawning the reminder to return here if (RechargerActive()) { SpawnReminder(this, 1.0f, 128); } else { m_bRecharging = FALSE; m_ltTarget = LT_ENEMY; // return to idle pose ELarvaRechargePose elrp; elrp.bStart = FALSE; SendEvent(elrp); } // if larva is in recharge mode and close enough to recharger and beam is up if (m_bActive && m_bRecharging && DistanceXZ(this, m_penRecharger)<5.0f) { if (m_bRechargePose) { if (((CExotechLarvaCharger *)&*m_penRecharger)->m_bBeamActive) { if (!m_bRechargedAtLeastOnce) { if (m_penFirstRechargeTarget) { SendToTarget(m_penFirstRechargeTarget , EET_TRIGGER, FixupCausedToPlayer(this, m_penEnemy)); } m_bRechargedAtLeastOnce = TRUE; } SetHealth(ClampUp(GetHealth()+m_fRechargePerSecond, m_fMaxHealth*m_fMaxRechargedHealth)); if (GetHealth()>m_fMaxHealth*0.95) { m_ltTarget = LT_ENEMY; m_bRecharging = FALSE; // deactivate beam EActivateBeam eab; eab.bTurnOn = FALSE; m_penRecharger->SendEvent(eab); // return to idle pose ELarvaRechargePose elrp; elrp.bStart = FALSE; SendEvent(elrp); } } else if (TRUE) { EActivateBeam eab; eab.bTurnOn = TRUE; m_penRecharger->SendEvent(eab); } } else { ELarvaRechargePose elrp; elrp.bStart = TRUE; SendEvent(elrp); } } // if larva is in normal mode else if (TRUE) { if (GetHealth()<(m_fLarvaHealth*0.7f)) { if (!RechargerActive()) { m_ltTarget = LT_ENEMY; } else { m_bRecharging = TRUE; m_ltTarget = LT_RECHARGER; } } } resume; // check to see if guided missile firing is needed } else if (er.iValue==145) { FindNewTarget(); if (AnyPlayerCloserThen(9.0f) && GetHealth()>0.1f*m_fMaxHealth) { UpdateFiringPos(); PlaySound(m_soFire3, SOUND_FIRE_TAIL, SOF_3D); ShootTailProjectile(); } else if (m_penEnemy && GetHealth()>0.1f*m_fMaxHealth) { if (!IsVisible(m_penEnemy)) { INDEX iRnd = IRnd()%6; if (iRnd>4) { UpdateFiringPos(); PlaySound(m_soFire3, SOUND_FIRE_TAIL, SOF_3D); ShootTailProjectile(); } } } SpawnReminder(this, 0.5f, 145); resume; // begin rendering laser } else if (er.iValue==129) { if (m_bActive && m_bLaserActive) { FireLaser(); } SpawnReminder(this, 0.35f, 130); resume; // explode the laser without turning it off } else if (er.iValue==130) { if (m_bActive) { ExplodeLaser(); } SpawnReminder(this, 0.75f, 131); resume; // finally stop rendering the laser } else if (er.iValue==131) { m_bRenderLeftLaser = FALSE; m_bRenderRightLaser = FALSE; resume; // start charging anim } else if (er.iValue==156) { m_bRechargePose = TRUE; StartModelAnim(BODY_ANIM_RECHARGING, AOF_SMOOTHCHANGE|AOF_LOOPING); resume; // return to idle anim } else if (er.iValue==157) { m_bRechargePose = FALSE; StartModelAnim(BODY_ANIM_IDLE, AOF_SMOOTHCHANGE|AOF_LOOPING); // stop charging anim } else if (er.iValue==160) { CModelObject &amo = GetModelObject()->GetAttachmentModel(BODY_ATTACHMENT_BACKARMS)->amo_moModelObject; amo.PlayAnim(BACKARMS_ANIM_ACTIVE, AOF_SMOOTHCHANGE|AOF_LOOPING); m_bLaserActive = TRUE; } resume; } } } Main(EVoid) { // declare yourself as a model InitAsModel(); SetPhysicsFlags(EPF_MODEL_FLYING|EPF_HASLUNGS|EPF_ABSOLUTETRANSLATE); SetCollisionFlags(ECF_MODEL); SetFlags(GetFlags()|ENF_ALIVE); en_fDensity = 2000.0f; // set your appearance SetModel(MODEL_BODY); SetModelMainTexture(TEXTURE_BODY); // add left side attachments AddAttachmentToModel(this, *GetModelObject(), BODY_ATTACHMENT_ARM_LEFT, MODEL_WING, TEXTURE_WING, 0, 0, 0); CModelObject &amo0 = GetModelObject()->GetAttachmentModel(BODY_ATTACHMENT_ARM_LEFT)->amo_moModelObject; AddAttachmentToModel(this, amo0, ARM_ATTACHMENT_PLASMAGUN, MODEL_PLASMAGUN, TEXTURE_PLASMAGUN, 0, 0, 0); // add right side attachments AddAttachmentToModel(this, *GetModelObject(), BODY_ATTACHMENT_ARM_RIGHT, MODEL_WING, TEXTURE_WING, 0, 0, 0); CModelObject &amo1 = GetModelObject()->GetAttachmentModel(BODY_ATTACHMENT_ARM_RIGHT)->amo_moModelObject; amo1.StretchModel(FLOAT3D(-1.0f, 1.0f, 1.0f)); AddAttachmentToModel(this, amo1, ARM_ATTACHMENT_PLASMAGUN, MODEL_PLASMAGUN, TEXTURE_PLASMAGUN, 0, 0, 0); CModelObject &amo2 = amo1.GetAttachmentModel(ARM_ATTACHMENT_PLASMAGUN)->amo_moModelObject; amo2.StretchModel(FLOAT3D(-1.0f, 1.0f, 1.0f)); // add blades AddAttachmentToModel(this, *GetModelObject(), BODY_ATTACHMENT_BACKARMS, MODEL_BLADES, TEXTURE_BODY, 0, 0, 0); // add holder AddAttachmentToModel(this, *GetModelObject(), BODY_ATTACHMENT_EXOTECHLARVA, MODEL_EXOTECHLARVA, TEXTURE_EXOTECHLARVA, 0, 0, 0); CModelObject &amo3 = GetModelObject()->GetAttachmentModel(BODY_ATTACHMENT_EXOTECHLARVA)->amo_moModelObject; AddAttachmentToModel(this, amo3, EXOTECHLARVA_ATTACHMENT_BEAM, MODEL_BEAM, TEXTURE_BEAM, 0, 0, 0); AddAttachmentToModel(this, amo3, EXOTECHLARVA_ATTACHMENT_ENERGYBEAMS, MODEL_ENERGYBEAMS, TEXTURE_ENERGYBEAMS, 0, 0, 0); AddAttachmentToModel(this, amo3, EXOTECHLARVA_ATTACHMENT_FLARE, MODEL_FLARE, TEXTURE_FLARE, 0, 0, 0); AddAttachmentToModel(this, *PlasmaLeftModel(), PLASMAGUN_ATTACHMENT_PROJECTILE, MODEL_PLASMA, TEXTURE_PLASMA, 0, 0, 0); AddAttachmentToModel(this, *PlasmaRightModel(), PLASMAGUN_ATTACHMENT_PROJECTILE, MODEL_PLASMA, TEXTURE_PLASMA, 0, 0, 0); // set the size of this model GetModelObject()->StretchModelRelative(FLOAT3D(m_fStretch, m_fStretch, m_fStretch)); m_vFirePosLeftPlasmaRel = FIREPOS_PLASMA_LEFT*m_fStretch; m_vFirePosRightPlasmaRel = FIREPOS_PLASMA_RIGHT*m_fStretch; //m_vFirePosMouthRel = FIREPOS_MOUTH*m_fStretch; m_vFirePosTailRel = FIREPOS_TAIL*m_fStretch; // this is a boss m_bBoss = TRUE; // setup moving speed m_fWalkSpeed = 0.0f; m_aWalkRotateSpeed = 100.0f; m_fAttackRunSpeed = 7.5f; m_aAttackRotateSpeed = 100.0f; // setup attack distances m_fStopDistance = m_fStopRadius; m_fBlowUpAmount = 100.0f; m_fBodyParts = 0; m_fDamageWounded = 0.0f; m_iScore = 750000; m_sptType = SPT_BLOOD; m_fAttackDistance = 100.0f; m_fCloseDistance = 0.0f; m_fAttackFireTime = 0.5f; m_fCloseFireTime = 0.5f; // set acceleration/deceleration en_fAcceleration = UpperLimit(1.0f); en_fDeceleration = UpperLimit(1.0f); // set health SetHealth(m_fLarvaHealth); m_fMaxHealth = m_fLarvaHealth; m_bActive = TRUE; m_bExploding = FALSE; m_bLaserActive = FALSE; // set stretch factors for height and width //GetModelObject()->StretchModel(FLOAT3D(2.0f, 2.0f, 2.0f)); ModelChangeNotify(); StandingAnim(); autowait(0.05f); // make larva invulnerable 'till start marker m_bInvulnerable = TRUE; if (!DoSafetyChecks()) { Destroy(); return; } // wait to be triggered wait() { on (EBegin) : { resume; } on (ETrigger) : { stop; } otherwise (): { resume; } } PlaySound(m_soChirp, SOUND_CHIRP, SOF_3D|SOF_LOOP); // move to first marker while (DistanceTo(this, m_penMarkerNew)>5.0f) { wait(0.05f) { on (EBegin) : { resume; } on (ETimer) : { FLOAT3D vToMarker = m_penMarkerNew->GetPlacement().pl_PositionVector - GetPlacement().pl_PositionVector; vToMarker.Normalize(); SetDesiredTranslation(vToMarker*m_fAttackRunSpeed); stop; } } } m_bInvulnerable = FALSE; // one state under base class to intercept some events jump LarvaLoop(); //jump CEnemyBase::MainLoop(); }; };