2016-03-12 01:20:51 +01:00
|
|
|
/* 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. */
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
346
|
|
|
|
%{
|
2016-04-01 20:04:24 +02:00
|
|
|
#include "EntitiesMP/StdH/StdH.h"
|
2016-03-11 14:57:17 +01:00
|
|
|
#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)
|
|
|
|
{
|
2016-03-29 03:03:54 +02:00
|
|
|
if( slPropertyOffset == _offsetof(CExotechLarva, m_penMarkerNew))
|
2016-03-11 14:57:17 +01:00
|
|
|
{
|
|
|
|
if (IsOfClass(penTarget, "NavigationMarker")) { return TRUE; }
|
|
|
|
else { return FALSE; }
|
|
|
|
}
|
2016-03-29 03:03:54 +02:00
|
|
|
if( slPropertyOffset == _offsetof(CExotechLarva, m_penRecharger))
|
2016-03-11 14:57:17 +01:00
|
|
|
{
|
|
|
|
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; i<ctMaxPlayers; i++) {
|
|
|
|
penPlayer=GetPlayerEntity(i);
|
|
|
|
if (penPlayer!=NULL && DistanceTo(this, penPlayer)<200.0f) {
|
|
|
|
// if there is no valid enemy
|
|
|
|
if (penPlayer!=NULL && (penPlayer->GetFlags()&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; i<ctMaxPlayers; i++) {
|
|
|
|
penPlayer=GetPlayerEntity(i);
|
|
|
|
if (penPlayer!=NULL) {
|
|
|
|
if ((penPlayer->GetFlags()&ENF_ALIVE) &&
|
|
|
|
!(penPlayer->GetFlags()&ENF_DELETED) &&
|
|
|
|
DistanceTo(this, penPlayer)<fDistance)
|
|
|
|
{
|
|
|
|
bClose = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bClose;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PerhapsChangeTarget() {
|
|
|
|
// if no current enemy, do nothing
|
|
|
|
if (!m_penEnemy) { return; }
|
|
|
|
// if enough time passed, try...
|
|
|
|
if (m_tmLastTargateChange+5.0f<_pTimer->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 && fHealthAfter<fHealthBlow02) {
|
|
|
|
fDamageAmmount = fHealthNow - fHealthBlow01 - 1;
|
|
|
|
} else if (m_bExploding) {
|
|
|
|
// if damage would cause the second explosion
|
|
|
|
if (fHealthNow>fHealthBlow02 && fHealthAfter<fHealthBlow02) {
|
|
|
|
fDamageAmmount = fHealthNow - fHealthBlow02 - 1;
|
|
|
|
// make sure we don't die while exploding
|
|
|
|
} else if (fHealthAfter<0.0f) {
|
|
|
|
fDamageAmmount = fHealthNow - 1;
|
|
|
|
}
|
|
|
|
} else if (fHealthNow>fHealthBlow02 && 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()<m_fMaxHealth*PERCENT_RIGHTBLOW) {
|
|
|
|
ELarvaArmDestroyed ead;
|
|
|
|
ead.iArm = ARM_RIGHT;
|
|
|
|
SendEvent(ead);
|
|
|
|
m_bExploding = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_bLeftArmActive) {
|
|
|
|
if (GetHealth()<m_fMaxHealth*PERCENT_LEFTBLOW) {
|
|
|
|
ELarvaArmDestroyed ead;
|
|
|
|
ead.iArm = ARM_LEFT;
|
|
|
|
SendEvent(ead);
|
|
|
|
m_bExploding = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// bosses don't darken when burning
|
|
|
|
m_colBurning=COLOR(C_WHITE|CT_OPAQUE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual CTString GetPlayerKillDescription(const CTString &strPlayerName, const EDeath &eDeath) {
|
|
|
|
CTString str;
|
2016-03-29 05:21:44 +02:00
|
|
|
str.PrintF(TRANSV("Exotech larva reduced %s to pulp."), (const char *) strPlayerName);
|
2016-03-11 14:57:17 +01:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Entity info */
|
|
|
|
void *GetEntityInfo(void) {
|
|
|
|
return &eiExotechLarva;
|
|
|
|
};
|
|
|
|
|
|
|
|
virtual const CTFileName &GetComputerMessageName(void) const {
|
|
|
|
static DECLARE_CTFILENAME(fnmLarva, "DataMP\\Messages\\Enemies\\ExotechLarva.txt");
|
|
|
|
return fnmLarva;
|
|
|
|
};
|
|
|
|
|
|
|
|
void Precache(void) {
|
|
|
|
CEnemyBase::Precache();
|
|
|
|
|
|
|
|
PrecacheClass(CLASS_BASIC_EFFECT, BET_ROCKET );
|
|
|
|
PrecacheClass(CLASS_BASIC_EFFECT, BET_CANNON );
|
|
|
|
PrecacheClass(CLASS_BLOOD_SPRAY );
|
|
|
|
PrecacheClass(CLASS_PROJECTILE, PRT_LARVA_TAIL_PROJECTILE);
|
|
|
|
PrecacheClass(CLASS_PROJECTILE, PRT_LARVA_PLASMA);
|
|
|
|
|
|
|
|
PrecacheModel (MODEL_EXOTECHLARVA );
|
|
|
|
PrecacheTexture(TEXTURE_EXOTECHLARVA );
|
|
|
|
PrecacheModel (MODEL_BODY );
|
|
|
|
PrecacheTexture(TEXTURE_BODY );
|
|
|
|
PrecacheModel (MODEL_BEAM );
|
|
|
|
PrecacheTexture(TEXTURE_BEAM );
|
|
|
|
PrecacheModel (MODEL_ENERGYBEAMS );
|
|
|
|
PrecacheTexture(TEXTURE_ENERGYBEAMS );
|
|
|
|
PrecacheModel (MODEL_FLARE );
|
|
|
|
PrecacheTexture(TEXTURE_FLARE );
|
|
|
|
PrecacheModel (MODEL_WING );
|
|
|
|
PrecacheTexture(TEXTURE_WING );
|
|
|
|
PrecacheModel (MODEL_PLASMAGUN );
|
|
|
|
PrecacheTexture(TEXTURE_PLASMAGUN );
|
|
|
|
|
|
|
|
PrecacheModel (MODEL_BLADES );
|
|
|
|
PrecacheModel (MODEL_DEBRIS_BODY );
|
|
|
|
PrecacheModel (MODEL_DEBRIS_TAIL01 );
|
|
|
|
PrecacheModel (MODEL_DEBRIS_TAIL02 );
|
|
|
|
|
|
|
|
PrecacheModel (MODEL_DEBRIS_FLESH );
|
|
|
|
PrecacheTexture(TEXTURE_DEBRIS_FLESH );
|
|
|
|
PrecacheModel (MODEL_PLASMA );
|
|
|
|
PrecacheTexture(TEXTURE_PLASMA );
|
|
|
|
PrecacheModel (MODEL_BODY );
|
|
|
|
PrecacheTexture(TEXTURE_BODY );
|
|
|
|
|
|
|
|
PrecacheSound(SOUND_FIRE_PLASMA );
|
|
|
|
PrecacheSound(SOUND_FIRE_TAIL );
|
|
|
|
PrecacheSound(SOUND_LASER_CHARGE );
|
|
|
|
PrecacheSound(SOUND_DEATH );
|
|
|
|
PrecacheSound(SOUND_ARMDESTROY );
|
|
|
|
PrecacheSound(SOUND_CHIRP );
|
|
|
|
PrecacheSound(SOUND_DEPLOYLASER );
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
// get the plasma ball attachments
|
|
|
|
CModelObject *PlasmaLeftModel(void) {
|
|
|
|
CAttachmentModelObject *amo = GetModelObject()->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)<fDistance) {
|
|
|
|
m_penRechargerTarget = m_penRCMarker01;
|
|
|
|
fDistance = DistanceTo(this, m_penRCMarker01);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_penRCMarker02) {
|
|
|
|
if (DistanceTo(this, m_penRCMarker02)<fDistance) {
|
|
|
|
m_penRechargerTarget = m_penRCMarker02;
|
|
|
|
fDistance = DistanceTo(this, m_penRCMarker02);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_penRCMarker03) {
|
|
|
|
if (DistanceTo(this, m_penRCMarker03)<fDistance) {
|
|
|
|
m_penRechargerTarget = m_penRCMarker03;
|
|
|
|
fDistance = DistanceTo(this, m_penRCMarker03);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_penRCMarker04) {
|
|
|
|
if (DistanceTo(this, m_penRCMarker04)<fDistance) {
|
|
|
|
m_penRechargerTarget = m_penRCMarker04;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (m_penRechargerTarget==NULL ? FALSE : TRUE);
|
|
|
|
}*/
|
|
|
|
|
|
|
|
void RemoveWing(INDEX iArm) {
|
|
|
|
// right arm
|
|
|
|
if (iArm==ARM_RIGHT) {
|
|
|
|
RemoveAttachmentFromModel(*GetModelObject(), BODY_ATTACHMENT_ARM_RIGHT);
|
|
|
|
}
|
|
|
|
// left arm
|
|
|
|
if (iArm==ARM_LEFT) {
|
|
|
|
RemoveAttachmentFromModel(*GetModelObject(), BODY_ATTACHMENT_ARM_LEFT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ANGLE GetArmsPitch(void) {
|
|
|
|
if (m_bLeftArmActive) {
|
|
|
|
CAttachmentModelObject &amo = *GetModelObject()->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)<m_fStopRadius) {
|
|
|
|
ForceFullStop();
|
|
|
|
}
|
|
|
|
|
|
|
|
return ulFlags;
|
|
|
|
};
|
|
|
|
|
|
|
|
void MoveToMarker(CEntity *penMarker) {
|
|
|
|
if(penMarker==NULL) { return; }
|
|
|
|
FLOAT3D vDesiredDir = penMarker->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();
|
|
|
|
};
|
|
|
|
};
|