Serious-Engine/Sources/EntitiesMP/Devil.es

2448 lines
81 KiB
JavaScript

/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
332
%{
#include "StdH.h"
#include "Models/Enemies/Devil/Devil.h"
#include "Models/Enemies/Devil/Weapons/Laser.h"
#include "Models/Weapons/RocketLauncher/RocketLauncherItem.h"
#include "EntitiesMP/Effector.h"
#include "EntitiesMP/WorldSettingsController.h"
#include "EntitiesMP/PyramidSpaceShip.h"
#include "EntitiesMP/BackgroundViewer.h"
#define DEVIL_LASER_SPEED 100.0f
#define DEVIL_ROCKET_SPEED 60.0f
%}
uses "EntitiesMP/DevilMarker";
uses "EntitiesMP/EnemyBase";
uses "EntitiesMP/Projectile";
uses "EntitiesMP/Bullet";
event EBrushDestroyedByDevil {
FLOAT3D vDamageDir,
};
event ERegenerationImpuls {
};
enum DevilCommandType {
0 DC_GRAB_LOWER_WEAPONS "Grab lower weapons",
1 DC_FORCE_ACTION "Force next action",
2 DC_STOP_MOVING "Stop moving",
3 DC_STOP_ATTACK "Stop attacking",
4 DC_JUMP_INTO_PYRAMID "Jump into pyramid",
5 DC_FORCE_ATTACK_RADIUS "Force attack radius",
6 DC_DECREASE_ATTACK_RADIUS "Decrease attack radius",
7 DC_TELEPORT_INTO_PYRAMID "Teleport into pyramid",
};
event EDevilCommand {
enum DevilCommandType dctType,
CEntityPointer penForcedAction,
FLOAT fAttackRadius,
FLOAT3D vCenterOfAttack,
};
enum DevilState {
0 DS_NOT_EXISTING "Not existing", // idle
1 DS_DESTROYING_CITY "Destroying city", // process of destroying city
2 DS_ENEMY "Enemy", // behave as normal enemy
3 DS_JUMPING_INTO_PYRAMID "Jumping into pyramid", // jumping into pyramid
4 DS_PYRAMID_FIGHT "Pyramid fight", // pyramid fight
5 DS_REGENERATION_IMPULSE "Regenerating with impulse", // drinking power
};
enum DevilAttackPower {
1 DAP_PLAYER_HUNT "Player hunt", // process of hunting player
2 DAP_LOW_POWER_ATTACK "Low power attack", // low power attack
3 DAP_MEDIUM_POWER_ATTACK "Medium power attack", // medium power attack
4 DAP_FULL_POWER_ATTACK "Full power attack", // full power attack
5 DAP_NOT_ATTACKING "Not attacking", // not attacking
};
%{
static FLOAT3D vLastStartPosition;
static FLOAT vLastAttackRadius;
extern INDEX cht_bKillFinalBoss;
extern INDEX cht_bDebugFinalBoss;
extern INDEX cht_bDumpFinalBossData;
extern INDEX cht_bDebugFinalBossAnimations;
#define LIGHT_ANIM_FIRE 3
#define LIGHT_ANIM_NONE 5
// parameters defining boss
#define SIZE 50.0f
#define DEVIL_HOOF_RADIUS 0.25f*SIZE
#define DEVIL_HIT_HOOF_OFFSET FLOAT3D(-0.149021f, 0.084463f, -0.294425f)*SIZE
#define DEVIL_WALK_HOOF_RIGHT_OFFSET FLOAT3D(0.374725f, 0.0776713f, 0.0754253f)*SIZE
#define DEVIL_WALK_HOOF_LEFT_OFFSET FLOAT3D(-0.402306f, 0.0864595f, 0.292397f)*SIZE
#define ATT_PROJECTILE_GUN (FLOAT3D(-0.703544f, 1.12582f, -0.329834f)*SIZE)
#define ATT_LASER (FLOAT3D(0.63626f, 1.13841f, -0.033062f)*SIZE)
#define ATT_ELECTRICITYGUN (FLOAT3D(-0.764868f, 1.27992f, -0.311084f)*SIZE)
#define ATT_ROCKETLAUNCHER (FLOAT3D(0.654788f, 1.30318f, -0.259358f)*SIZE)
#define MAGIC_PROJECTILE_EXIT (FLOAT3D(0.035866f, 1.400f, -0.792264f)*SIZE)
#define ELECTROGUN_PIPE (FLOAT3D(-0.00423616f, -0.0216781f, -0.506613f)*SIZE)
#define LASER_PIPE (FLOAT3D(0.0172566f, -0.123152f, -0.232228f)*SIZE)
#define PROJECTILEGUN_PIPE (FLOAT3D(0.0359023f, -0.000490744f, -0.394403f)*SIZE)
#define ROCKETLAUNCHER_PIPE (FLOAT3D(4.68194e-005f, 0.0483391f, -0.475134f)*SIZE)
#define HEALTH_MULTIPLIER 1.0f
#define BOSS_HEALTH (40000.0f*HEALTH_MULTIPLIER)
#define HEALTH_IMPULSE (10000.0f*HEALTH_MULTIPLIER)
#define HEALTH_CLASS_1 (5000*HEALTH_MULTIPLIER)
#define HEALTH_CLASS_2 (7500*HEALTH_MULTIPLIER)
#define HEALTH_CLASS_3 (10000*HEALTH_MULTIPLIER)
#define HEALTH_CLASS_4 (15000*HEALTH_MULTIPLIER)
#define CLASS_2_CANNON_FACTOR 0.75f
#define CLASS_3_ROCKETLAUNCHER_FACTOR 0.75f
#define CLASS_4_ROCKETLAUNCHER_FACTOR 0.25f
#define TM_HEALTH_IMPULSE 4.0f
// info structure
static EntityInfo eiDevil = {
EIBT_FLESH, 50000.0f,
0.0f, 2.0f*SIZE, 0.0f,
0.0f, 1.4f*SIZE, 0.0f,
};
%}
class CDevil : CEnemyBase {
name "Devil";
thumbnail "Thumbnails\\Devil.tbn";
properties:
1 INDEX m_iAttID = 0, // internal temp var
2 FLOAT m_fDeltaWeaponPitch = 0.0f, // for adjusting weapon pitch
3 FLOAT m_fDeltaWeaponHdg = 0.0f, // for adjusting weapon hdg
4 FLOAT m_fFireTime = 0.0f, // time to fire bullets
5 CAnimObject m_aoLightAnimation, // light animation object
6 CEntityPointer m_penAction "Action" 'O', // ptr to action marker entity
8 INDEX m_iFiredProjectiles = 0, // internal counter of fired projectiles
9 INDEX m_iToFireProjectiles = 0, // internal counter of projectiles that should be fired
10 FLOAT m_fPauseStretcher = 0, // stretch factor for pauses between fireing
11 FLOAT m_tmLastPause = 0.0f, // last pause between two fireings
12 enum DevilState m_dsDevilState = DS_NOT_EXISTING,// current devil state
13 FLOAT m_tmLastAngry = -1.0f, // last angry state
14 CPlacement3D m_plTeleport = CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0)),
16 FLOAT m_tmTemp = 0,
17 enum DevilState m_dsLastDevilState = DS_REGENERATION_IMPULSE,// last devil state
18 enum DevilAttackPower m_dapAttackPower = DAP_PLAYER_HUNT,// current devil state
19 enum DevilAttackPower m_dapLastAttackPower = DAP_NOT_ATTACKING,
20 BOOL m_bHasUpperWeapons = FALSE,
21 FLOAT3D m_vElectricitySource = FLOAT3D( 0,0,0), // position of electricity ray target
22 FLOAT3D m_vElectricityTarget = FLOAT3D( 0,0,0), // position of electricity ray target
23 BOOL m_bRenderElectricity = FALSE, // if electricity particles are rendered
24 FLOAT m_fAdjustWeaponTime = 0.0f, // time for weapon to lock enemy
25 BOOL m_bWasOnceInMainLoop = FALSE, // if MainLoop was called at least once
26 FLOAT m_tmHitBySpaceShipBeam = -1, // last time when was hit by space ship beam
27 CSoundObject m_soLeft, // left foot sound
28 CSoundObject m_soRight, // right foot sound
29 FLOAT m_fLastWalkTime = -1.0f, // last walk time
30 FLOAT m_tmFireBreathStart = UpperLimit(0.0f), // time when fire breath started
31 FLOAT m_tmFireBreathStop = 0.0f, // time when fire breath stopped
32 FLOAT3D m_vFireBreathSource = FLOAT3D( 0,0,0), // position of fire breath source
33 FLOAT3D m_vFireBreathTarget = FLOAT3D( 0,0,0), // position of fire breath target
34 FLOAT m_tmRegenerationStart = UpperLimit(0.0f), // time when regeneration started
35 FLOAT m_tmRegenerationStop = 0.0f, // time when regeneration stopped
36 FLOAT m_tmNextFXTime = 0.0f, // next effect time
37 INDEX m_iNextChannel = 0, // next channel to play sound on
// weapon sound channels
38 CSoundObject m_soWeapon0,
39 CSoundObject m_soWeapon1,
40 CSoundObject m_soWeapon2,
41 CSoundObject m_soWeapon3,
42 CSoundObject m_soWeapon4,
43 INDEX m_iAngryAnim=0, // random angry animation
44 INDEX m_iAngrySound=0, // random angry animation
45 FLOAT m_tmDeathTime = -1.0f, // time of death
50 INDEX m_iLastCurrentAnim = -1,
51 INDEX m_iLastScheduledAnim = -1,
52 enum DevilState m_dsPreRegenerationDevilState = DS_ENEMY,
60 CSoundObject m_soClimb, // sound of climbing
61 CSoundObject m_soGrabLowerWeapons,
62 CSoundObject m_soGrabUpperWeapons,
63 CSoundObject m_soJumpIntoPyramid,
70 BOOL m_bForMPIntro "MP Intro" = FALSE,
{
CEntity *penBullet; // bullet
CLightSource m_lsLightSource;
}
components:
0 class CLASS_BASE "Classes\\EnemyBase.ecl",
1 class CLASS_PROJECTILE "Classes\\Projectile.ecl",
2 class CLASS_EFFECTOR "Classes\\Effector.ecl",
// ************** DEVIL **************
10 model MODEL_DEVIL "Models\\Enemies\\Devil\\Devil.mdl",
11 texture TEXTURE_DEVIL "Models\\Enemies\\Devil\\Devil.tex",
// ************** LASER **************
20 model MODEL_LASER "Models\\Enemies\\Devil\\Weapons\\Laser.mdl",
21 texture TEXTURE_LASER "Models\\Enemies\\Devil\\Weapons\\Laser.tex",
// ************** ROCKET LAUNCHER **************
22 model MODEL_ROCKETLAUNCHER "Models\\Enemies\\Devil\\Weapons\\RocketLauncher.mdl",
23 texture TEXTURE_ROCKETLAUNCHER "Models\\Enemies\\Devil\\Weapons\\RocketLauncher.tex",
// ************** PROJECTILE GUN **************
24 model MODEL_PROJECTILEGUN "Models\\Enemies\\Devil\\Weapons\\ProjectileGun.mdl",
25 texture TEXTURE_PROJECTILEGUN "Models\\Enemies\\Devil\\Weapons\\ProjectileGun.tex",
// ************** ELECTRICITY GUN *************
26 model MODEL_ELECTRICITYGUN "Models\\Enemies\\Devil\\Weapons\\ElectricityGun.mdl",
27 texture TEXTURE_ELECTRICITYGUN "Models\\Enemies\\Devil\\Weapons\\ElectricityGun.tex",
// ************** SOUNDS **************
60 sound SOUND_ANGER01 "Models\\Enemies\\Devil\\Sounds\\Anger01.wav",
61 sound SOUND_ANGER02 "Models\\Enemies\\Devil\\Sounds\\Anger02.wav",
62 sound SOUND_ATTACKCLOSE "Models\\Enemies\\Devil\\Sounds\\AttackClose.wav",
63 sound SOUND_CELEBRATE01 "Models\\Enemies\\Devil\\Sounds\\Celebrate01.wav",
65 sound SOUND_DEATH "Models\\Enemies\\Devil\\Sounds\\Death.wav",
66 sound SOUND_DRAW_LOWER_WEAPONS "Models\\Enemies\\Devil\\Sounds\\GrabWeaponsLower.wav",
67 sound SOUND_DRAW_UPPER_WEAPONS "Models\\Enemies\\Devil\\Sounds\\GrabWeaponsUpper.wav",
68 sound SOUND_GETUP "Models\\Enemies\\Devil\\Sounds\\Getup.wav",
69 sound SOUND_IDLE "Models\\Enemies\\Devil\\Sounds\\Idle.wav",
70 sound SOUND_PUNCH "Models\\Enemies\\Devil\\Sounds\\Punch.wav",
71 sound SOUND_SMASH "Models\\Enemies\\Devil\\Sounds\\Smash.wav",
72 sound SOUND_WALK_LEFT "Models\\Enemies\\Devil\\Sounds\\WalkL.wav",
73 sound SOUND_WALK_RIGHT "Models\\Enemies\\Devil\\Sounds\\WalkR.wav",
74 sound SOUND_WOUND "Models\\Enemies\\Devil\\Sounds\\Wound.wav",
75 sound SOUND_ATTACK_BREATH_START "Models\\Enemies\\Devil\\Sounds\\AttackBreathStart.wav",
76 sound SOUND_ATTACK_BREATH_FIRE "Models\\Enemies\\Devil\\Sounds\\BreathProjectile.wav",
77 sound SOUND_ATTACK_BREATH_END "Models\\Enemies\\Devil\\Sounds\\AttackBreathEnd.wav",
78 sound SOUND_HEAL "Models\\Enemies\\Devil\\Sounds\\Heal.wav",
79 sound SOUND_ROCKETLAUNCHER "Models\\Enemies\\Devil\\Sounds\\RocketLauncher.wav",
80 sound SOUND_LASER "Models\\Enemies\\Devil\\Sounds\\Laser.wav",
81 sound SOUND_LAVABOMB "Models\\Enemies\\Devil\\Sounds\\LavaBomb.wav",
82 sound SOUND_GHOSTBUSTER "Models\\Enemies\\Devil\\Sounds\\Ghostbuster.wav",
83 sound SOUND_ATTACK_BREATH_LOOP "Models\\Enemies\\Devil\\Sounds\\AttackBreath.wav",
84 sound SOUND_CLIMB "Models\\Enemies\\Devil\\Sounds\\Enter.wav",
85 sound SOUND_DEATHPARTICLES "Models\\Enemies\\Devil\\Sounds\\DeathParticles.wav",
86 sound SOUND_DISAPPEAR "Models\\Enemies\\Devil\\Sounds\\Disappear.wav",
functions:
// describe how this enemy killed player
virtual CTString GetPlayerKillDescription(const CTString &strPlayerName, const EDeath &eDeath)
{
CTString str;
str.PrintF(TRANSV("Ugh Zan killed %s"), (const char *) strPlayerName);
return str;
}
void Precache(void) {
CEnemyBase::Precache();
// ************** DEVIL **************
PrecacheModel (MODEL_DEVIL );
PrecacheTexture (TEXTURE_DEVIL );
// ************** LASER **************
PrecacheModel (MODEL_LASER );
PrecacheTexture (TEXTURE_LASER );
// ************** ROCKET LAUNCHER **************
PrecacheModel (MODEL_ROCKETLAUNCHER );
PrecacheTexture (TEXTURE_ROCKETLAUNCHER );
// ************** ELECTRICITY GUN **************
PrecacheModel (MODEL_ELECTRICITYGUN);
PrecacheTexture (TEXTURE_ELECTRICITYGUN);
// ************** PROJECTILE GUN **************
PrecacheModel (MODEL_PROJECTILEGUN);
PrecacheTexture (TEXTURE_PROJECTILEGUN);
// ************** PREDICTED PROJECTILE **************
PrecacheClass(CLASS_PROJECTILE, PRT_LAVAMAN_BIG_BOMB);
PrecacheClass(CLASS_PROJECTILE, PRT_DEVIL_GUIDED_PROJECTILE);
PrecacheClass(CLASS_PROJECTILE, PRT_DEVIL_LASER);
PrecacheClass(CLASS_PROJECTILE, PRT_DEVIL_ROCKET);
// ************** SOUNDS **************
PrecacheSound (SOUND_ANGER01 );
PrecacheSound (SOUND_ANGER02 );
PrecacheSound (SOUND_ATTACKCLOSE );
PrecacheSound (SOUND_CELEBRATE01 );
PrecacheSound (SOUND_DEATH );
PrecacheSound (SOUND_DRAW_LOWER_WEAPONS );
PrecacheSound (SOUND_DRAW_UPPER_WEAPONS );
PrecacheSound (SOUND_GETUP );
PrecacheSound (SOUND_IDLE );
PrecacheSound (SOUND_PUNCH );
PrecacheSound (SOUND_SMASH );
PrecacheSound (SOUND_WALK_LEFT );
PrecacheSound (SOUND_WALK_RIGHT );
PrecacheSound (SOUND_WOUND );
PrecacheSound (SOUND_ATTACK_BREATH_START );
PrecacheSound (SOUND_ATTACK_BREATH_FIRE );
PrecacheSound (SOUND_ATTACK_BREATH_END );
PrecacheSound (SOUND_HEAL );
PrecacheSound (SOUND_ROCKETLAUNCHER );
PrecacheSound (SOUND_LASER );
PrecacheSound (SOUND_LAVABOMB );
PrecacheSound (SOUND_GHOSTBUSTER );
PrecacheSound (SOUND_ATTACK_BREATH_LOOP );
PrecacheSound (SOUND_CLIMB );
PrecacheSound (SOUND_DEATHPARTICLES );
PrecacheSound (SOUND_DISAPPEAR );
}
// Validate offered target for one property
BOOL IsTargetValid(SLONG slPropertyOffset, CEntity *penTarget)
{
if(penTarget==NULL)
{
return FALSE;
}
return (IsDerivedFromClass(penTarget, "Devil Marker"));
}
/* Read from stream. */
void Read_t( CTStream *istr) { // throw char *
CEnemyBase::Read_t(istr);
// setup light source
SetupLightSource();
}
/* Get static light source information. */
CLightSource *GetLightSource(void) {
if (!IsPredictor()) {
return &m_lsLightSource;
} else {
return NULL;
}
}
// Setup light source
void SetupLightSource(void) {
// setup light source
CLightSource lsNew;
lsNew.ls_ulFlags = LSF_NONPERSISTENT|LSF_DYNAMIC;
lsNew.ls_rHotSpot = 2.0f;
lsNew.ls_rFallOff = 8.0f;
lsNew.ls_colColor = RGBToColor(128, 128, 128);
lsNew.ls_plftLensFlare = NULL;
lsNew.ls_ubPolygonalMask = 0;
lsNew.ls_paoLightAnimation = &m_aoLightAnimation;
m_lsLightSource.ls_penEntity = this;
m_lsLightSource.SetLightSource(lsNew);
}
// play light animation
void PlayLightAnim(INDEX iAnim, ULONG ulFlags) {
if (m_aoLightAnimation.GetData()!=NULL) {
m_aoLightAnimation.PlayAnim(iAnim, ulFlags);
}
};
/* Entity info */
void *GetEntityInfo(void) {
return &eiDevil;
};
BOOL ForcesCannonballToExplode(void)
{
return TRUE;
}
void SetSpeedsToDesiredPosition(const FLOAT3D &vPosDelta, FLOAT fPosDist, BOOL bGoingToPlayer)
{
if(m_penEnemy!=NULL)
{
FLOAT fEnemyDistance = CalcDist(m_penEnemy);
FLOAT fRadius1 = 75.0f;
FLOAT fRadius2 = 200.0f;
FLOAT fSpeedRadius1 = 6.0f;
FLOAT fSpeedRadius2 = 14.0f;
FLOAT fDistanceRatio = CalculateRatio( fEnemyDistance, fRadius1, fRadius2, 1, 0);
if( fEnemyDistance>=fRadius2)
{
fDistanceRatio = 1.0f;
}
m_fAttackRunSpeed = fSpeedRadius1+fDistanceRatio*(fSpeedRadius2-fSpeedRadius1);
m_fCloseRunSpeed = m_fAttackRunSpeed;
if( cht_bDebugFinalBoss)
{
CPrintF( "Enm dist:%g, Speed=%g\n", fEnemyDistance, m_fAttackRunSpeed);
}
}
CEnemyBase::SetSpeedsToDesiredPosition(vPosDelta, fPosDist, bGoingToPlayer);
}
FLOAT GetCrushHealth(void)
{
return 1000.0f;
}
void SelectRandomAnger(void)
{
if( IRnd()%2) {
m_iAngryAnim = DEVIL_ANIM_ANGER01;
m_iAngrySound = SOUND_ANGER01;
} else {
m_iAngryAnim = DEVIL_ANIM_ANGER02;
m_iAngrySound = SOUND_ANGER02;
}
}
virtual FLOAT GetLockRotationSpeed(void)
{
return m_aAttackRotateSpeed*4;
};
void ShakeItBaby(FLOAT tmShaketime, FLOAT fPower)
{
CWorldSettingsController *pwsc = GetWSC(this);
if (pwsc!=NULL) {
pwsc->m_tmShakeStarted = tmShaketime;
pwsc->m_vShakePos = GetPlacement().pl_PositionVector;
pwsc->m_fShakeFalloff = 400.0f;
pwsc->m_fShakeFade = 3.0f;
pwsc->m_fShakeIntensityZ = 0.0f;
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 = FALSE;
}
}
void ShakeItFarBaby(FLOAT tmShaketime, FLOAT fPower)
{
CWorldSettingsController *pwsc = GetWSC(this);
if (pwsc!=NULL) {
pwsc->m_tmShakeStarted = tmShaketime;
pwsc->m_vShakePos = GetPlacement().pl_PositionVector;
pwsc->m_fShakeFalloff = 2048.0f;
pwsc->m_fShakeFade = 2.0f;
pwsc->m_fShakeIntensityZ = 0.0f;
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 = FALSE;
}
}
void InflictHoofDamage( FLOAT3D vOffset)
{
// apply range damage for right foot
FLOAT3D vFootRel = vOffset;
FLOAT3D vFootAbs = vFootRel*GetRotationMatrix()+GetPlacement().pl_PositionVector;
InflictRangeDamage(this, DMT_IMPACT, 1000.0f, vFootAbs, DEVIL_HOOF_RADIUS, DEVIL_HOOF_RADIUS);
}
void ApplyFootQuake(void)
{
CModelObject &mo = *GetModelObject();
TIME tmNow = _pTimer->CurrentTick();
FLOAT tmAnim = -1;
FLOAT tmWalkLen = mo.GetAnimLength(DEVIL_ANIM_WALK);
FLOAT tmLeftFootOffset = 0.4f;
FLOAT tmRightFootOffset = 2.05f;
// if we are now playing walk anim, but another anim is scheduled to happen after walk
if (mo.ao_iLastAnim==DEVIL_ANIM_WALK && mo.ao_tmAnimStart>tmNow)
{
// we started one anim time back from anim start time
tmAnim = mo.ao_tmAnimStart-tmWalkLen;
}
else if (mo.ao_iCurrentAnim==DEVIL_ANIM_WALK && mo.ao_tmAnimStart<=tmNow)
{
tmAnim = mo.ao_tmAnimStart;
}
// if we are now playing walk wo idle anim, but another anim is scheduled to happen after walk to idle
else if (mo.ao_iLastAnim==DEVIL_ANIM_FROMWALKTOIDLE && mo.ao_tmAnimStart>tmNow)
{
// we started one anim time back from anim start time
tmAnim = mo.ao_tmAnimStart-tmWalkLen;
tmLeftFootOffset = 0.6f;
tmRightFootOffset = 1.7f;
}
else if (mo.ao_iCurrentAnim==DEVIL_ANIM_FROMWALKTOIDLE && mo.ao_tmAnimStart<=tmNow)
{
tmAnim = mo.ao_tmAnimStart;
tmLeftFootOffset = 0.6f;
tmRightFootOffset = 1.7f;
}
// WARNING !!! foot variable names are switched
if( tmAnim!=-1)
{
FLOAT tmAnimLast = tmAnim+INDEX((tmNow-tmAnim)/tmWalkLen)*tmWalkLen;
FLOAT tmLeftFootDown = tmAnimLast+tmLeftFootOffset;
FLOAT tmRightFootDown = tmAnimLast+tmRightFootOffset;
CWorldSettingsController *pwsc = GetWSC(this);
if( pwsc!=NULL)
{
if( tmNow>=tmRightFootDown && pwsc->m_tmShakeStarted<tmRightFootDown-0.1f)
{
// apply range damage for right foot
InflictHoofDamage( DEVIL_WALK_HOOF_LEFT_OFFSET);
// shake
ShakeItBaby(tmRightFootDown, 1.0f);
PlaySound(m_soRight, SOUND_WALK_RIGHT, SOF_3D);
}
else if(tmNow>=tmLeftFootDown && pwsc->m_tmShakeStarted<tmLeftFootDown-0.1f)
{
// apply range damage for left foot
InflictHoofDamage( DEVIL_WALK_HOOF_RIGHT_OFFSET);
// shake
ShakeItBaby(tmLeftFootDown, 1.0f);
PlaySound(m_soLeft, SOUND_WALK_LEFT, SOF_3D);
}
}
}
}
void StopFireBreathParticles(void)
{
m_tmFireBreathStop = _pTimer->CurrentTick();
}
void StopRegenerationParticles(void)
{
m_tmRegenerationStop = _pTimer->CurrentTick();
}
void TurnOnPhysics(void)
{
SetPhysicsFlags(EPF_MODEL_WALKING);
SetCollisionFlags(ECF_MODEL);
}
void TurnOffPhysics(void)
{
SetPhysicsFlags(EPF_MODEL_WALKING&~EPF_TRANSLATEDBYGRAVITY);
SetCollisionFlags(((ECBI_MODEL)<<ECB_TEST) | ((ECBI_MODEL)<<ECB_PASS) | ((ECBI_ITEM)<<ECB_IS));
}
// render particles
void RenderParticles(void)
{
if( m_bRenderElectricity)
{
// calculate electricity ray source pos
Particles_Ghostbuster(m_vElectricitySource, m_vElectricityTarget, 24, 2.0f, 2.0f, 96.0f);
}
// fire breath particles
if( _pTimer->CurrentTick()>m_tmFireBreathStart)
{
// render fire breath particles
INDEX ctRendered = Particles_FireBreath(this, m_vFireBreathSource, m_vFireBreathTarget,
m_tmFireBreathStart, m_tmFireBreathStop);
// if should stop rendering fire breath particles
if( _pTimer->CurrentTick()>m_tmFireBreathStop && ctRendered==0)
{
m_tmFireBreathStart = UpperLimit(0.0f);
}
}
// regeneration particles
if( _pTimer->CurrentTick()>m_tmRegenerationStart)
{
// render fire breath particles
INDEX ctRendered = Particles_Regeneration(this, m_tmRegenerationStart, m_tmRegenerationStop, 1.0f, FALSE);
// if should stop rendering regeneration particles
if( _pTimer->CurrentTick()>m_tmRegenerationStop && ctRendered==0)
{
m_tmRegenerationStart = UpperLimit(0.0f);
}
}
// if is dead
if( m_tmDeathTime != -1.0f && _pTimer->CurrentTick()>m_tmDeathTime && _pTimer->CurrentTick()<m_tmDeathTime+4.0f)
{
INDEX ctRendered = Particles_Regeneration(this, m_tmDeathTime, m_tmDeathTime+2.0f, 0.25f, TRUE);
}
CEnemyBase::RenderParticles();
}
FLOAT3D GetWeaponPositionRelative(void)
{
CAttachmentModelObject &amo = *GetModelObject()->GetAttachmentModel(m_iAttID);
FLOAT3D vAttachment = FLOAT3D(0,0,0);
switch(m_iAttID)
{
case DEVIL_ATTACHMENT_LASER:
vAttachment = ATT_LASER;
break;
case DEVIL_ATTACHMENT_PROJECTILEGUN:
vAttachment = ATT_PROJECTILE_GUN;
break;
case DEVIL_ATTACHMENT_ELETRICITYGUN:
vAttachment = ATT_ELECTRICITYGUN;
break;
case DEVIL_ATTACHMENT_ROCKETLAUNCHER:
vAttachment = ATT_ROCKETLAUNCHER;
break;
default:
ASSERTALWAYS("Invalid attachment ID");
}
return(vAttachment);
}
FLOAT3D GetWeaponPositionAbsolute(void)
{
return GetPlacement().pl_PositionVector + GetWeaponPositionRelative()*GetRotationMatrix();
}
FLOAT3D GetFireingPositionRelative(void)
{
CAttachmentModelObject &amo = *GetModelObject()->GetAttachmentModel(m_iAttID);
FLOAT3D vWeaponPipe = FLOAT3D(0,0,0);
FLOAT3D vAttachment = FLOAT3D(0,0,0);
switch(m_iAttID)
{
case DEVIL_ATTACHMENT_LASER:
vWeaponPipe = LASER_PIPE;
vAttachment = ATT_LASER;
break;
case DEVIL_ATTACHMENT_PROJECTILEGUN:
vWeaponPipe = PROJECTILEGUN_PIPE;
vAttachment = ATT_PROJECTILE_GUN;
break;
case DEVIL_ATTACHMENT_ELETRICITYGUN:
vWeaponPipe = ELECTROGUN_PIPE;
vAttachment = ATT_ELECTRICITYGUN;
break;
case DEVIL_ATTACHMENT_ROCKETLAUNCHER:
vWeaponPipe = ROCKETLAUNCHER_PIPE;
vAttachment = ATT_ROCKETLAUNCHER;
break;
default:
ASSERTALWAYS("Invalid attachment ID");
}
// create matrix
FLOATmatrix3D mWpn;
MakeRotationMatrixFast(mWpn, amo.amo_plRelative.pl_OrientationAngle);
return (vAttachment+vWeaponPipe*mWpn);
}
FLOAT3D GetFireingPositionAbsolute(void)
{
return GetPlacement().pl_PositionVector + GetFireingPositionRelative()*GetRotationMatrix();
}
/*
Regeneration legend:
--------------------
- if less than 0 -> no generation
- class 1 -> invoke impulse regeneration +HEALTH_IMPULSE
- class 2 -> drops when constantly hitted with cannonballs
- class 3 -> drops when constantly hitted with rockets
- class 4 -> drops when constantly hitted with rockets
- if greater than class 5 -> no regeneration
*/
void ApplyTickRegeneration(void)
{
if( cht_bKillFinalBoss && GetSP()->sp_bSinglePlayer)
{
cht_bKillFinalBoss=FALSE;
SetHealth(-1);
return;
}
// if currently regenerating or died or out of healing range or recently hit by space ship beam
if(m_dsDevilState == DS_REGENERATION_IMPULSE ||
GetHealth()<=0 || GetHealth()>=HEALTH_CLASS_4 ||
_pTimer->CurrentTick()<m_tmHitBySpaceShipBeam+0.5f)
{
return;
}
FLOAT fDmgRocketsPerTick = 1800.0f/10.0f*_pTimer->TickQuantum;
FLOAT fDmgCannonsPerTick = 2959.0f/10.0f*_pTimer->TickQuantum;
FLOAT fRegeneration = 0.0f;
if(GetHealth()<HEALTH_CLASS_1)
{
SendEvent(ERegenerationImpuls());
}
else if(GetHealth()<HEALTH_CLASS_2)
{
fRegeneration = fDmgCannonsPerTick*CLASS_2_CANNON_FACTOR;
}
else if(GetHealth()<HEALTH_CLASS_3)
{
fRegeneration = fDmgRocketsPerTick*CLASS_3_ROCKETLAUNCHER_FACTOR;
}
else if(GetHealth()<HEALTH_CLASS_4)
{
fRegeneration = fDmgRocketsPerTick*CLASS_4_ROCKETLAUNCHER_FACTOR;
}
// apply regeneration
SetHealth(GetHealth()+fRegeneration);
};
/* Post moving */
void PostMoving(void)
{
ApplyFootQuake();
CEnemyBase::PostMoving();
// discard non-moving optimization
en_ulFlags &= ~ENF_INRENDERING;
ApplyTickRegeneration();
}
/* Receive damage */
void ReceiveDamage(CEntity *penInflictor, enum DamageType dmtType,
FLOAT fDamageAmmount, const FLOAT3D &vHitPoint, const FLOAT3D &vDirection)
{
// don't allow telefrag damage
if( dmtType == DMT_TELEPORT)
{
return;
}
if( !(m_dsDevilState==DS_ENEMY || m_dsDevilState==DS_PYRAMID_FIGHT) || penInflictor==this)
{
return;
}
if(m_dsDevilState!=DS_PYRAMID_FIGHT)
{
if( GetHealth()<1000.0f)
{
return;
}
fDamageAmmount=ClampUp(fDamageAmmount, GetHealth()/2.0f);
}
CEnemyBase::ReceiveDamage(penInflictor, dmtType, fDamageAmmount, vHitPoint, vDirection);
};
BOOL AdjustShadingParameters(FLOAT3D &vLightDirection, COLOR &colLight, COLOR &colAmbient)
{
if( cht_bDebugFinalBoss)
{
// print state change
if( m_dsDevilState!=m_dsLastDevilState)
{
m_dsLastDevilState = m_dsDevilState;
CTString strDevilState = DevilState_enum.NameForValue(INDEX(m_dsDevilState));
CPrintF( "New devil state: %s\n", (const char *) strDevilState);
}
// print fire power state change
if( m_dapAttackPower!=m_dapLastAttackPower)
{
m_dapLastAttackPower = m_dapAttackPower;
CTString strAttackPower = DevilAttackPower_enum.NameForValue(INDEX(m_dapAttackPower));
CPrintF( "New attack power: %s\n", (const char *) strAttackPower);
}
// print radius of attack change
if( (vLastStartPosition != m_vStartPosition) ||
(vLastAttackRadius != m_fAttackRadius) )
{
vLastStartPosition = m_vStartPosition;
vLastAttackRadius = m_fAttackRadius;
CPrintF( "Coordinate of attack (%g, %g, %g), Radius of attack: %g\n",
m_vStartPosition(1), m_vStartPosition(2), m_vStartPosition(3),
m_fAttackRadius);
}
}
if(cht_bDebugFinalBossAnimations)
{
TIME tmNow = _pTimer->CurrentTick();
CModelObject &mo = *GetModelObject();
// obtain current and scheduled animation
INDEX iCurrentAnim, iScheduledAnim;
if (mo.ao_tmAnimStart>tmNow)
{
iCurrentAnim = mo.ao_iLastAnim;
iScheduledAnim = mo.ao_iCurrentAnim;
}
else
{
iCurrentAnim = mo.ao_iCurrentAnim;
iScheduledAnim = -1;
}
if( iCurrentAnim!=m_iLastCurrentAnim || iScheduledAnim!=m_iLastScheduledAnim)
{
CAnimData *pad = mo.GetData();
CAnimInfo aiCurrent;
mo.GetAnimInfo(iCurrentAnim, aiCurrent);
CTString strCurrentAnimName = aiCurrent.ai_AnimName;
CTString strScheduledAnimName = ".....";
if(iScheduledAnim != -1)
{
CAnimInfo aiScheduled;
mo.GetAnimInfo(iScheduledAnim, aiScheduled);
strScheduledAnimName = aiScheduled.ai_AnimName;
}
CPrintF("Time: %-10g %20s, %s\n",
_pTimer->GetLerpedCurrentTick(),
(const char *) strCurrentAnimName,
(const char *) strScheduledAnimName);
}
m_iLastCurrentAnim = iCurrentAnim;
m_iLastScheduledAnim = iScheduledAnim;
}
if( cht_bDumpFinalBossData)
{
cht_bDumpFinalBossData=FALSE;
// dump devil data to console
CPrintF("\n\n\n\n\n\n\n");
CPrintF("Devil class data ...................\n");
CPrintF("\n\n");
CTString strAttackPower = DevilAttackPower_enum.NameForValue(INDEX(m_dapAttackPower));
CPrintF( "Attack power: %s\n", (const char *) strAttackPower);
CTString strDevilState = DevilState_enum.NameForValue(INDEX(m_dsDevilState));
CPrintF( "Devil state: %s\n", (const char *) strDevilState);
CPrintF("m_fFireTime = %g\n", m_fFireTime);
CPrintF("m_iFiredProjectiles = %d\n", m_iFiredProjectiles);
CPrintF("m_iToFireProjectiles = %d\n", m_iToFireProjectiles);
CPrintF("m_tmLastPause = %g\n", m_tmLastPause);
CPrintF("m_fPauseStretcher = %g\n", m_fPauseStretcher);
CPrintF("m_tmLastAngry = %g\n", m_tmLastAngry);
CPrintF("m_bHasUpperWeapons = %d\n", m_bHasUpperWeapons);
CPrintF("m_fAdjustWeaponTime = %g\n", m_fAdjustWeaponTime);
CPrintF("m_bWasOnceInMainLoop = %d\n", m_bWasOnceInMainLoop);
CPrintF("m_tmHitBySpaceShipBeam = %g\n", m_tmHitBySpaceShipBeam);
CPrintF("m_fLastWalkTime = %g\n", m_fLastWalkTime);
CPrintF("m_tmFireBreathStart = %g\n", m_tmFireBreathStart);
CPrintF("m_tmFireBreathStop = %g\n", m_tmFireBreathStop);
CPrintF("m_tmRegenerationStart = %g\n", m_tmRegenerationStart);
CPrintF("m_tmRegenerationStop = %g\n", m_tmRegenerationStop);
CPrintF("m_tmNextFXTime = %g\n", m_tmNextFXTime);
CPrintF("m_tmDeathTime = %g\n", m_tmDeathTime);
CPrintF("Health = %g\n", GetHealth());
CPrintF("\n\n\n\n\n\n\n");
CPrintF("Enemy base data ...................\n");
CPrintF("\n\n");
CPrintF( "m_ttTarget (type): %d\n", INDEX(m_ttTarget));
CPrintF( "m_penWatcher %x\n", (const char *) (m_penWatcher->GetName()));
CTString strEnemyName = "Null ptr, no name";
if( m_penEnemy != NULL)
{
strEnemyName = m_penEnemy->GetName();
}
CPrintF( "m_penEnemy %x, enemy name: %s\n", (const char *) (m_penEnemy->GetName()), (const char *) strEnemyName);
CPrintF( "m_vStartPosition (%g, %g, %g)\n", m_vStartPosition(1), m_vStartPosition(2), m_vStartPosition(3));
CPrintF( "m_vStartDirection (%g, %g, %g)\n", m_vStartDirection(1), m_vStartDirection(2), m_vStartDirection(3));
CPrintF( "m_bOnStartPosition = %d\n", m_bOnStartPosition);
CPrintF( "m_fFallHeight = %g\n", m_fFallHeight);
CPrintF( "m_fStepHeight = %g\n", m_fStepHeight);
CPrintF( "m_fSenseRange = %g\n", m_fSenseRange);
CPrintF( "m_fViewAngle = %g\n", m_fViewAngle);
CPrintF( "m_fWalkSpeed = %g\n", m_fWalkSpeed);
CPrintF( "m_aWalkRotateSpeed = %g\n", m_aWalkRotateSpeed);
CPrintF( "m_fAttackRunSpeed = %g\n", m_fAttackRunSpeed);
CPrintF( "m_aAttackRotateSpeed = %g\n", m_aAttackRotateSpeed);
CPrintF( "m_fCloseRunSpeed = %g\n", m_fCloseRunSpeed);
CPrintF( "m_aCloseRotateSpeed = %g\n", m_aCloseRotateSpeed);
CPrintF( "m_fAttackDistance = %g\n", m_fAttackDistance);
CPrintF( "m_fCloseDistance = %g\n", m_fCloseDistance);
CPrintF( "m_fAttackFireTime = %g\n", m_fAttackFireTime);
CPrintF( "m_fCloseFireTime = %g\n", m_fCloseFireTime);
CPrintF( "m_fStopDistance = %g\n", m_fStopDistance);
CPrintF( "m_fIgnoreRange = %g\n", m_fIgnoreRange);
CPrintF( "m_fLockOnEnemyTime = %g\n", m_fLockOnEnemyTime);
CPrintF( "m_fMoveTime = %g\n", m_fMoveTime);
CPrintF( "m_vDesiredPosition (%g, %g, %g)\n", m_vDesiredPosition(1), m_vDesiredPosition(2), m_vDesiredPosition(3));
CTString strDestinationType = DestinationType_enum.NameForValue(INDEX(m_dtDestination));
CPrintF( "m_dtDestination: %s\n", (const char *) strDestinationType);
CPrintF( "m_penPathMarker %x\n", (const char *) (m_penPathMarker->GetName()));
CPrintF( "m_vPlayerSpotted (%g, %g, %g)\n", m_vPlayerSpotted(1), m_vPlayerSpotted(2), m_vPlayerSpotted(3));
CPrintF( "m_fMoveFrequency = %g\n", m_fMoveFrequency);
CPrintF( "m_fMoveSpeed = %g\n", m_fMoveSpeed);
CPrintF( "m_aRotateSpeed = %g\n", m_aRotateSpeed);
CPrintF( "m_fLockStartTime = %g\n", m_fLockStartTime);
CPrintF( "m_fRangeLast = %g\n", m_fRangeLast);
CPrintF( "m_fShootTime = %g\n", m_fShootTime);
CPrintF( "m_fAttackRadius = %g\n", m_fAttackRadius);
CPrintF( "m_tmGiveUp = %g\n", m_tmGiveUp);
CPrintF( "m_fActivityRange = %g\n", m_fActivityRange);
CTString strMarkerName = "Null ptr, no name";
if( m_penMarker != NULL)
{
strMarkerName = m_penMarker->GetName();
}
CPrintF( "m_penMarker %x, marker name: %s\n", (const char *) (m_penMarker->GetName()), (const char *) strMarkerName);
CTString strMainMusicHolderName = "Null ptr, no name";
if( m_penMainMusicHolder != NULL)
{
strMainMusicHolderName = m_penMainMusicHolder->GetName();
}
CPrintF( "m_penMainMusicHolder %x, MainMusicHolder name: %s\n", (const char *) (m_penMainMusicHolder->GetName()), (const char *) strMainMusicHolderName);
CPrintF( "m_tmLastFussTime = %g\n", m_tmLastFussTime);
CPrintF( "m_iScore = %d\n", m_iScore);
CPrintF( "m_fMaxHealth = %g\n", m_fMaxHealth);
CPrintF( "m_bBoss = %d\n", m_bBoss);
CPrintF( "m_fSpiritStartTime = %g\n", m_fSpiritStartTime);
CPrintF( "m_tmSpraySpawned = %g\n", m_tmSpraySpawned);
CPrintF( "m_fSprayDamage = %g\n", m_fSprayDamage);
CPrintF( "m_fMaxDamageAmmount = %g\n", m_fMaxDamageAmmount );
}
vLightDirection = FLOAT3D(0.0f, 270.0f, 0.0f);
colAmbient = RGBToColor(32,32,32);
colLight = RGBToColor(255,235,145);
return CMovableModelEntity::AdjustShadingParameters(vLightDirection, colLight, colAmbient);
//return CEnemyBase::AdjustShadingParameters(vLightDirection, colLight, colAmbient);
};
// damage anim
INDEX AnimForDamage(FLOAT fDamage) {
StartModelAnim(DEVIL_ANIM_WOUNDLOOP, 0);
return DEVIL_ANIM_WOUNDLOOP;
};
// death
INDEX AnimForDeath(void) {
StartModelAnim(DEVIL_ANIM_DEATH, 0);
return DEVIL_ANIM_DEATH;
};
void DeathNotify(void) {
StopFireBreathParticles();
StopRegenerationParticles();
};
// virtual anim functions
void StandingAnim(void) {
StartModelAnim(DEVIL_ANIM_IDLE, AOF_SMOOTHCHANGE|AOF_LOOPING|AOF_NORESTART);
};
void WalkingAnim(void) {
if( !m_bForMPIntro)
{
CModelObject &mo = *GetModelObject();
INDEX iAnim = mo.GetAnim();
if (iAnim==DEVIL_ANIM_WALK)
{
// do nothing
} else if (iAnim==DEVIL_ANIM_FROMIDLETOWALK) {
StartModelAnim(DEVIL_ANIM_WALK, AOF_LOOPING|AOF_SMOOTHCHANGE);
} else {
StartModelAnim(DEVIL_ANIM_FROMIDLETOWALK, AOF_SMOOTHCHANGE);
}
}
};
void RunningAnim(void) {
WalkingAnim();
};
void RotatingAnim(void) {
WalkingAnim();
};
// virtual sound functions
void IdleSound(void) {
PlaySound(m_soSound, SOUND_IDLE, SOF_3D);
};
void SightSound(void) {
//PlaySound(m_soSound, SOUND_SIGHT, SOF_3D);
};
void WoundSound(void) {
PlaySound(m_soSound, SOUND_WOUND, SOF_3D);
};
void DeathSound(void) {
PlaySound(m_soSound, SOUND_DEATH, SOF_3D|SOF_VOLUMETRIC);
};
// start fire laser
void StartFireLaser(void) {
//PlaySound(m_soSound, SOUND_FIRE, SOF_3D|SOF_LOOP);
PlayLightAnim(LIGHT_ANIM_FIRE, AOF_LOOPING|SOF_3D);
};
// fire one laser
void FireOneLaser(FLOAT fRatio, FLOAT fDeltaPitch)
{
PlayWeaponSound( SOUND_LASER);
FLOAT3D vWpnPipeRel = GetFireingPositionRelative();
FLOAT3D vWpnPipeAbs = GetFireingPositionAbsolute();
// calculate predicted position
FLOAT3D vTarget = m_penEnemy->GetPlacement().pl_PositionVector;
FLOAT3D vSpeedDst = ((CMovableEntity&) *m_penEnemy).en_vCurrentTranslationAbsolute*fRatio;
FLOAT fSpeedSrc = DEVIL_LASER_SPEED;
m_vDesiredPosition = CalculatePredictedPosition(vWpnPipeAbs, vTarget, fSpeedSrc,
vSpeedDst, GetPlacement().pl_PositionVector(2) );
// shoot predicted propelled projectile
ShootPredictedProjectile(PRT_DEVIL_LASER, m_vDesiredPosition, vWpnPipeRel, ANGLE3D(0, fDeltaPitch, 0));
//PlaySound(m_soSound, SOUND_FIRE, SOF_3D|SOF_LOOP);
PlayLightAnim(LIGHT_ANIM_FIRE, AOF_LOOPING);
};
// stop fire laser
void StopFireLaser(void) {
m_soSound.Stop();
PlayLightAnim(LIGHT_ANIM_NONE, 0);
};
// start fire rocket
void StartFireRocket(void) {
//PlaySound(m_soSound, SOUND_FIRE, SOF_3D|SOF_LOOP);
PlayLightAnim(LIGHT_ANIM_FIRE, AOF_LOOPING);
};
void PlayWeaponSound( ULONG idSound)
{
CSoundObject &so = (&m_soWeapon0)[m_iNextChannel];
m_iNextChannel = (m_iNextChannel+1)%5;
PlaySound(so, idSound, SOF_3D);
}
// fire one rocket
void FireOneRocket(FLOAT fRatio)
{
PlayWeaponSound( SOUND_ROCKETLAUNCHER);
FLOAT3D vWpnPipeRel = GetFireingPositionRelative();
FLOAT3D vWpnPipeAbs = GetFireingPositionAbsolute();
// calculate predicted position
FLOAT3D vTarget = m_penEnemy->GetPlacement().pl_PositionVector;
FLOAT3D vSpeedDst = ((CMovableEntity&) *m_penEnemy).en_vCurrentTranslationAbsolute*fRatio;
FLOAT fSpeedSrc = DEVIL_ROCKET_SPEED;
m_vDesiredPosition = CalculatePredictedPosition(vWpnPipeAbs, vTarget, fSpeedSrc,
vSpeedDst, GetPlacement().pl_PositionVector(2) );
// shoot predicted propelled projectile
ShootPredictedProjectile(PRT_DEVIL_ROCKET, m_vDesiredPosition, vWpnPipeRel, ANGLE3D(0, 0, 0));
//PlaySound(m_soSound, SOUND_FIRE, SOF_3D|SOF_LOOP);
PlayLightAnim(LIGHT_ANIM_FIRE, AOF_LOOPING);
//PlaySound(m_soSound, SOUND_FIRE, SOF_3D|SOF_LOOP);
PlayLightAnim(LIGHT_ANIM_FIRE, AOF_LOOPING);
};
// stop fire rocket
void StopFireRocket(void) {
m_soSound.Stop();
PlayLightAnim(LIGHT_ANIM_NONE, 0);
};
void AddLowerWeapons(void)
{
// laser
AddAttachmentToModel(this, *GetModelObject(), DEVIL_ATTACHMENT_LASER, MODEL_LASER, TEXTURE_LASER, 0, 0, 0);
// projectile gun
AddAttachmentToModel(this, *GetModelObject(), DEVIL_ATTACHMENT_PROJECTILEGUN, MODEL_PROJECTILEGUN, TEXTURE_PROJECTILEGUN, 0, 0, 0);
GetModelObject()->StretchModel(FLOAT3D(SIZE, SIZE, SIZE));
};
void AddUpperWeapons(void)
{
// rocket launcher
AddAttachmentToModel(this, *GetModelObject(), DEVIL_ATTACHMENT_ROCKETLAUNCHER, MODEL_ROCKETLAUNCHER, TEXTURE_ROCKETLAUNCHER, 0, 0, 0);
// electro gun
AddAttachmentToModel(this, *GetModelObject(), DEVIL_ATTACHMENT_ELETRICITYGUN, MODEL_ELECTRICITYGUN, TEXTURE_ELECTRICITYGUN, 0, 0, 0);
GetModelObject()->StretchModel(FLOAT3D(SIZE, SIZE, SIZE));
};
void RemoveWeapons(void)
{
// remove all weapons
RemoveAttachmentFromModel(*GetModelObject(), DEVIL_ATTACHMENT_LASER);
RemoveAttachmentFromModel(*GetModelObject(), DEVIL_ATTACHMENT_PROJECTILEGUN);
RemoveAttachmentFromModel(*GetModelObject(), DEVIL_ATTACHMENT_ELETRICITYGUN);
RemoveAttachmentFromModel(*GetModelObject(), DEVIL_ATTACHMENT_ROCKETLAUNCHER);
}
class CDevilMarker *GetAction(void)
{
CDevilMarker *penAction = (CDevilMarker *) (CEntity*) m_penAction;
ASSERT( penAction != NULL);
return penAction;
};
/************************************************************
* PREDICTED PROJECTILE *
************************************************************/
void F_FirePredictedProjectile(void)
{
PlayWeaponSound( SOUND_LAVABOMB);
FLOAT3D vFireingRel = GetFireingPositionRelative();
FLOAT3D vFireingAbs = GetFireingPositionAbsolute();
FLOAT3D vTarget = m_penEnemy->GetPlacement().pl_PositionVector;
FLOAT3D vSpeedDest = ((CMovableEntity&) *m_penEnemy).en_vCurrentTranslationAbsolute;
FLOAT fLaunchSpeed;
FLOAT fRelativeHdg;
// obtain current gun orientation
CAttachmentModelObject &amo = *GetModelObject()->GetAttachmentModel(m_iAttID);
FLOAT fPitch = amo.amo_plRelative.pl_OrientationAngle(2);
// calculate parameters for predicted angular launch curve
EntityInfo *peiTarget = (EntityInfo*) (m_penEnemy->GetEntityInfo());
CalculateAngularLaunchParams( vFireingAbs, 0, vTarget, vSpeedDest, fPitch, fLaunchSpeed, fRelativeHdg);
// target enemy body
FLOAT3D vShootTarget;
GetEntityInfoPosition(m_penEnemy, peiTarget->vTargetCenter, vShootTarget);
// launch
CPlacement3D pl;
PrepareFreeFlyingProjectile(pl, vShootTarget, vFireingRel, ANGLE3D( fRelativeHdg, fPitch, 0));
CEntityPointer penProjectile = CreateEntity(pl, CLASS_PROJECTILE);
ELaunchProjectile eLaunch;
eLaunch.penLauncher = this;
eLaunch.prtType = PRT_LAVAMAN_BIG_BOMB;
eLaunch.fSpeed = fLaunchSpeed;
penProjectile->Initialize(eLaunch);
}
/* Handle an event, return false if the event is not handled. */
BOOL HandleEvent(const CEntityEvent &ee)
{
if (ee.ee_slEvent==EVENTCODE_EDevilCommand)
{
EDevilCommand eDevilCommand = ((EDevilCommand &) ee);
if( eDevilCommand.dctType == DC_FORCE_ATTACK_RADIUS)
{
m_fAttackRadius = eDevilCommand.fAttackRadius;
m_vStartPosition = eDevilCommand.vCenterOfAttack;
}
if( eDevilCommand.dctType == DC_DECREASE_ATTACK_RADIUS)
{
if( m_fAttackRadius>21.0f)
{
m_fAttackRadius -= 20.0f;
}
}
}
return CEnemyBase::HandleEvent(ee);
}
procedures:
/************************************************************
* TRY TO REACH DESTINATION *
************************************************************/
// move to given destination position
WalkTo(EVoid)
{
autocall WaitCurrentAnimEnd() EReturn;
WalkingAnim();
m_vDesiredPosition = GetAction()->GetPlacement().pl_PositionVector;
// setup parameters
m_fMoveFrequency = 0.25f;
m_fMoveSpeed = 15.0f;
m_aRotateSpeed = 60.0f;
m_fMoveTime = _pTimer->CurrentTick() + CalcDistanceInPlaneToDestination()/m_fMoveSpeed + 5.0f;
// while not there and time not expired
while (CalcDistanceInPlaneToDestination()>m_fMoveSpeed*m_fMoveFrequency*4.0f &&
m_fMoveTime>_pTimer->CurrentTick()) {
// every once in a while
wait (m_fMoveFrequency) {
on (EBegin) : {
// adjust direction and speed
ULONG ulFlags = SetDesiredMovement();
MovementAnimation(ulFlags);
resume;
}
on (ETimer) : { stop; }
}
}
StopMoving();
// return to the caller
return EReturn();
};
/************************************************************
* CITY DESTROYING *
************************************************************/
DestroyCity()
{
m_soSound.Set3DParameters(1000.0f, 500.0f, 3.0f, 1.0f);
while(TRUE)
{
if( GetAction()->m_datType == DAT_RISE)
{
autocall Rise() EReturn;
}
else if( GetAction()->m_datType == DAT_ROAR)
{
SelectRandomAnger();
autocall Angry() EReturn;
}
else if( GetAction()->m_datType == DAT_SMASH)
{
autocall Smash() EReturn;
}
else if( GetAction()->m_datType == DAT_PUNCH)
{
autocall Punch() EReturn;
}
else if( GetAction()->m_datType == DAT_HIT_GROUND)
{
autocall HitGround() EReturn;
}
else if( GetAction()->m_datType == DAT_JUMP)
{
// do nothing, obsolete
}
else if( GetAction()->m_datType == DAT_WAIT)
{
autocall WaitCurrentAnimEnd() EReturn;
StartModelAnim(DEVIL_ANIM_IDLE, 0);
autowait(GetModelObject()->GetAnimLength(DEVIL_ANIM_IDLE)*GetAction()->m_iWaitIdles);
}
else if( GetAction()->m_datType == DAT_WALK)
{
autocall WalkTo() EReturn;
}
else if( GetAction()->m_datType == DAT_STOP_DESTROYING)
{
return EReturn();
}
else if(TRUE)
{
StartModelAnim(DEVIL_ANIM_IDLE, 0);
autowait(GetModelObject()->GetAnimLength(DEVIL_ANIM_IDLE));
}
// switch to next action
m_penAction = GetAction()->m_penTarget;
if( GetAction()->m_penTrigger != NULL)
{
GetAction()->m_penTrigger->SendEvent(ETrigger());
}
}
};
WaitCurrentAnimEnd()
{
autowait(_pTimer->TickQuantum);
CModelObject &mo = *GetModelObject();
FLOAT tmWait = mo.GetAnimLength( mo.ao_iCurrentAnim )-mo.GetPassedTime();
if( tmWait > _pTimer->TickQuantum)
{
FLOAT fTimeToWait = tmWait-_pTimer->TickQuantum*2;
if( fTimeToWait>=_pTimer->TickQuantum)
{
autowait(fTimeToWait);
}
}
return EReturn();
}
WaitWalkToEnd()
{
if(GetModelObject()->GetAnim()==DEVIL_ANIM_WALK)
{
autocall WaitCurrentAnimEnd() EReturn;
StartModelAnim(DEVIL_ANIM_FROMWALKTOIDLE, AOF_SMOOTHCHANGE);
autowait( GetModelObject()->GetAnimLength(DEVIL_ANIM_FROMWALKTOIDLE)-0.1f);
}
autocall WaitCurrentAnimEnd() EReturn;
return EReturn();
}
WaitWalkOrIdleToEnd()
{
if(GetModelObject()->GetAnim()==DEVIL_ANIM_WALK)
{
autocall WaitCurrentAnimEnd() EReturn;
StartModelAnim(DEVIL_ANIM_FROMWALKTOIDLE, AOF_SMOOTHCHANGE);
autowait( GetModelObject()->GetAnimLength(DEVIL_ANIM_FROMWALKTOIDLE)-0.1f);
}
else if(GetModelObject()->GetAnim()==DEVIL_ANIM_FROMIDLETOWALK)
{
autocall WaitCurrentAnimEnd() EReturn;
StartModelAnim(DEVIL_ANIM_FROMWALKTOIDLE, AOF_SMOOTHCHANGE);
autowait( GetModelObject()->GetAnimLength(DEVIL_ANIM_FROMWALKTOIDLE)-0.1f);
}
else if(GetModelObject()->GetAnim()==DEVIL_ANIM_IDLE)
{
autocall WaitCurrentAnimEnd() EReturn;
}
return EReturn();
}
Rise()
{
autocall WaitCurrentAnimEnd() EReturn;
PlaySound(m_soSound, SOUND_GETUP, SOF_3D);
GetModelObject()->PlayAnim(DEVIL_ANIM_GETUP, 0);
return EReturn();
}
Celebrate()
{
autocall WaitWalkToEnd() EReturn;
PlaySound(m_soSound, SOUND_CELEBRATE01, SOF_3D);
GetModelObject()->PlayAnim(DEVIL_ANIM_CELEBRATE01, AOF_SMOOTHCHANGE);
autowait( GetModelObject()->GetAnimLength(DEVIL_ANIM_CELEBRATE01)-0.1f);
return EReturn();
}
Angry()
{
autocall WaitWalkToEnd() EReturn;
GetModelObject()->PlayAnim(m_iAngryAnim, AOF_SMOOTHCHANGE);
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
PlaySound(m_soSound, m_iAngrySound, SOF_3D);
autowait( GetModelObject()->GetAnimLength(m_iAngryAnim)-0.1f);
return EReturn();
}
SubBeamDamage1()
{
StopMoving();
PlaySound(m_soSound, SOUND_WOUND, SOF_3D);
GetModelObject()->PlayAnim(DEVIL_ANIM_WOUNDSTART, AOF_SMOOTHCHANGE);
autowait( GetModelObject()->GetAnimLength(DEVIL_ANIM_WOUNDSTART)-0.1f);
GetModelObject()->PlayAnim(DEVIL_ANIM_WOUNDLOOP, AOF_LOOPING|AOF_SMOOTHCHANGE);
jump SubBeamDamage2();
}
SubBeamDamage2()
{
while(TRUE) {
wait(0.1f)
{
on (EBegin) : { resume; }
on (EHitBySpaceShipBeam) : {
m_tmHitBySpaceShipBeam = _pTimer->CurrentTick();
stop;
}
on (ETimer) : { jump SubBeamDamage3(); }
}
}
}
SubBeamDamage3()
{
GetModelObject()->PlayAnim(DEVIL_ANIM_WOUNDEND, AOF_SMOOTHCHANGE);
autowait( GetModelObject()->GetAnimLength(DEVIL_ANIM_WOUNDEND)-0.1f);
GetModelObject()->PlayAnim(DEVIL_ANIM_IDLE, AOF_LOOPING|AOF_SMOOTHCHANGE);
return EReturn();
}
BeamDamage()
{
wait()
{
on (EBegin) : { call SubBeamDamage1(); }
on (EHitBySpaceShipBeam) : {
m_tmHitBySpaceShipBeam = _pTimer->CurrentTick();
resume;
}
on (EReturn) : { stop; }
}
return EReturn();
}
Smash()
{
//autocall WaitCurrentAnimEnd() EReturn;
//autocall WaitWalkToEnd() EReturn;
GetModelObject()->PlayAnim(DEVIL_ANIM_FROMWALKTOIDLE, AOF_SMOOTHCHANGE);
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
GetModelObject()->PlayAnim(DEVIL_ANIM_SMASH, AOF_SMOOTHCHANGE);
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
autowait(0.05f);
PlaySound(m_soSound, SOUND_SMASH, SOF_3D);
autowait(0.7f);
if( GetAction()->m_penToDestroy1 != NULL)
{
EBrushDestroyedByDevil ebdbd;
ebdbd.vDamageDir = FLOAT3D( -0.125f, 0.0f, -0.5f);
GetAction()->m_penToDestroy1->SendEvent(ebdbd);
}
autowait(2.8f-1.0f);
if( GetAction()->m_penToDestroy2 != NULL)
{
EBrushDestroyedByDevil ebdbd;
ebdbd.vDamageDir = FLOAT3D( -0.125f, 0.0f, -0.5f);
GetAction()->m_penToDestroy2->SendEvent(ebdbd);
}
return EReturn();
}
Punch()
{
//autocall WaitWalkToEnd() EReturn;
//autocall WaitCurrentAnimEnd() EReturn;
GetModelObject()->PlayAnim(DEVIL_ANIM_FROMWALKTOIDLE, AOF_SMOOTHCHANGE);
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
GetModelObject()->PlayAnim(DEVIL_ANIM_PUNCH, AOF_SMOOTHCHANGE);
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
autowait(0.05f);
PlaySound(m_soSound, SOUND_PUNCH, SOF_3D);
autowait(0.8f);
if( GetAction()->m_penToDestroy1 != NULL)
{
EBrushDestroyedByDevil ebdbd;
ebdbd.vDamageDir = FLOAT3D( -0.125f, 0.0f, -0.5f);
GetAction()->m_penToDestroy1->SendEvent(ebdbd);
}
autowait(2.8f-1.1f);
if( GetAction()->m_penToDestroy2 != NULL)
{
EBrushDestroyedByDevil ebdbd;
ebdbd.vDamageDir = FLOAT3D( 0.125f, 0.0f, -0.5f);
GetAction()->m_penToDestroy2->SendEvent(ebdbd);
}
return EReturn();
}
HitGround()
{
autocall WaitWalkToEnd() EReturn;
PlaySound(m_soSound, SOUND_ATTACKCLOSE, SOF_3D);
GetModelObject()->PlayAnim(DEVIL_ANIM_ATTACKCLOSE, AOF_SMOOTHCHANGE);
autowait(1.44f);
ShakeItBaby(_pTimer->CurrentTick(), 5.0f);
CPlacement3D plObelisk = GetPlacement();
// spawn spray spray
CEntity *penEffector = CreateEntity( plObelisk, CLASS_EFFECTOR);
// set spawn parameters
ESpawnEffector eSpawnEffector;
eSpawnEffector.tmLifeTime = 6.0f;
eSpawnEffector.fSize = 1.0f;
eSpawnEffector.eetType = ET_HIT_GROUND;
eSpawnEffector.vDamageDir = FLOAT3D( 0.0f, 2.0f, 0.0f);
// initialize spray
penEffector->Initialize( eSpawnEffector);
return EReturn();
}
GrabLowerWeapons()
{
autocall WaitWalkToEnd() EReturn;
GetModelObject()->PlayAnim(DEVIL_ANIM_GRABWEAPONS01, AOF_SMOOTHCHANGE);
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
PlaySound(m_soSound, SOUND_DRAW_LOWER_WEAPONS, SOF_3D);
autowait(0.84f);
AddLowerWeapons();
WalkingAnim();
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
return EReturn();
}
GrabUpperWeapons()
{
autocall WaitWalkToEnd() EReturn;
GetModelObject()->PlayAnim(DEVIL_ANIM_GRABWEAPONS02, AOF_SMOOTHCHANGE);
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
PlaySound(m_soSound, SOUND_DRAW_UPPER_WEAPONS, SOF_3D);
autowait(0.84f);
AddUpperWeapons();
m_bHasUpperWeapons = TRUE;
WalkingAnim();
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
return EReturn();
}
GrabBothWeapons()
{
autocall WaitWalkToEnd() EReturn;
GetModelObject()->PlayAnim(DEVIL_ANIM_GRABWEAPONS01, AOF_SMOOTHCHANGE);
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
PlaySound(m_soGrabLowerWeapons, SOUND_DRAW_LOWER_WEAPONS, SOF_3D);
autowait(0.84f);
AddLowerWeapons();
GetModelObject()->PlayAnim(DEVIL_ANIM_GRABWEAPONS02, AOF_SMOOTHCHANGE);
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
PlaySound(m_soGrabUpperWeapons, SOUND_DRAW_UPPER_WEAPONS, SOF_3D);
autowait(0.84f);
AddUpperWeapons();
m_bHasUpperWeapons = TRUE;
WalkingAnim();
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
return EReturn();
}
PreMainLoop(EVoid) : CEnemyBase::PreMainLoop
{
m_soSound.Set3DParameters(1000.0f, 500.0f, 2.0f, 1.0f);
m_soGrabLowerWeapons.Set3DParameters(1000.0f, 500.0f, 2.0f, 1.0f);
m_soGrabUpperWeapons.Set3DParameters(1000.0f, 500.0f, 2.0f, 1.0f);
m_soJumpIntoPyramid.Set3DParameters(1000.0f, 500.0f, 2.0f, 1.0f);
m_soLeft.Set3DParameters(1000.0f, 500.0f, 2.0f, 1.0f);
m_soRight.Set3DParameters(1000.0f, 500.0f, 2.0f, 1.0f);
m_soWeapon0.Set3DParameters(1000.0f, 500.0f, 1.0f, 1.0f);
m_soWeapon1.Set3DParameters(1000.0f, 500.0f, 1.0f, 1.0f);
m_soWeapon2.Set3DParameters(1000.0f, 500.0f, 1.0f, 1.0f);
m_soWeapon3.Set3DParameters(1000.0f, 500.0f, 1.0f, 1.0f);
m_soWeapon4.Set3DParameters(1000.0f, 500.0f, 1.0f, 1.0f);
TurnOnPhysics();
if (m_penEnemy==NULL)
{
// get some player for trigger source if any is existing
CEntity *penEnemy = FixupCausedToPlayer(this, m_penEnemy, /*bWarning=*/FALSE);
if (penEnemy!=m_penEnemy) {
SetTargetSoft(penEnemy);
}
}
return EReturn();
}
/************************************************************
* PROCEDURES WHEN HARMED *
************************************************************/
// Play wound animation
BeWounded(EDamage eDamage) : CEnemyBase::BeWounded
{
StopMoving();
// determine damage anim and play the wounding
autowait(GetModelObject()->GetAnimLength(AnimForDamage(eDamage.fAmount)));
return EReturn();
};
/************************************************************
* C L O S E A T T A C K *
************************************************************/
Hit(EVoid) : CEnemyBase::Hit {
autocall WaitCurrentAnimEnd() EReturn;
StartModelAnim(DEVIL_ANIM_ATTACKCLOSE, AOF_SMOOTHCHANGE);
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
PlaySound(m_soSound, SOUND_ATTACKCLOSE, SOF_3D);
autowait(1.4f);
ShakeItBaby(_pTimer->CurrentTick(), 5.0f);
if( CalcDist(m_penEnemy) < m_fCloseDistance)
{
InflictDirectDamage(m_penEnemy, this, DMT_IMPACT, 1000.0f,
m_penEnemy->GetPlacement().pl_PositionVector, FLOAT3D(0,1,0));
}
InflictHoofDamage( DEVIL_HIT_HOOF_OFFSET);
autowait(GetModelObject()->GetAnimLength(DEVIL_ANIM_ATTACKCLOSE-1.4f)-_pTimer->TickQuantum);
return EReturn();
};
/************************************************************
* A T T A C K E N E M Y *
************************************************************/
// initial preparation
InitializeAttack(EVoid) : CEnemyBase::InitializeAttack {
jump CEnemyBase::InitializeAttack();
};
Fire(EVoid) : CEnemyBase::Fire
{
m_iToFireProjectiles = 0;
m_fAttackFireTime = 10.0f;
m_fPauseStretcher = 1.0f;
if( m_dapAttackPower==DAP_MEDIUM_POWER_ATTACK &&
(_pTimer->CurrentTick()-m_fLastWalkTime) > 6.0f)
{
m_fAttackFireTime = 6.0f;
m_fLastWalkTime = _pTimer->CurrentTick()+6.0f;
return;
}
switch( m_dapAttackPower)
{
case DAP_PLAYER_HUNT:
if( _pTimer->CurrentTick()-m_tmLastAngry > 10.0f)
{
m_fAttackFireTime = 7.5+FRnd()*5;
m_tmLastAngry = _pTimer->CurrentTick();
SelectRandomAnger();
jump Angry();
}
return;
break;
case DAP_LOW_POWER_ATTACK:
m_iToFireProjectiles = 2;
m_fAttackFireTime = 5.0f;
m_fPauseStretcher = 1.0f;
break;
case DAP_MEDIUM_POWER_ATTACK:
m_iToFireProjectiles = 3;
m_fAttackFireTime = 0.1f;
m_fPauseStretcher = 0.5f;
break;
case DAP_FULL_POWER_ATTACK:
m_iToFireProjectiles = 4;
m_fAttackFireTime = 0.1f;
m_fPauseStretcher = 0.1f;
break;
}
INDEX iRnd = IRnd()%5;
if( !m_bHasUpperWeapons)
{
iRnd = IRnd()%3;
}
/*iRnd = Clamp(INDEX(tmp_af[0]), INDEX(0), INDEX(4));*/
switch(iRnd)
{
case 0:
jump FirePredictedProjectile();
break;
case 1:
jump FireLaser();
break;
case 2:
jump FireGuidedProjectile();
break;
case 3:
jump FireRocketLauncher();
break;
case 4:
jump FireElectricityGun();
break;
}
};
// call this to lock on player for some time - set m_fLockOnEnemyTime before calling
DevilLockOnEnemy(EVoid)
{
// stop moving
StopMoving();
// play animation for locking
ChargeAnim();
// wait charge time
m_fLockStartTime = _pTimer->CurrentTick();
while (m_fLockStartTime+GetProp(m_fLockOnEnemyTime) > _pTimer->CurrentTick()) {
// each tick
m_fMoveFrequency = 0.05f;
wait (m_fMoveFrequency) {
on (ETimer) : { stop; }
on (EBegin) : {
m_vDesiredPosition = PlayerDestinationPos();
// if not heading towards enemy
if (!IsInPlaneFrustum(m_penEnemy, CosFast(30.0f))) {
m_fLockStartTime = -10000.0f;
stop;
}
/* if (!IsInPlaneFrustum(m_penEnemy, CosFast(5.0f))) {
m_fMoveSpeed = 0.0f;
m_aRotateSpeed = GetLockRotationSpeed();
// if heading towards enemy
} else {
m_fMoveSpeed = 0.0f;
m_aRotateSpeed = 0.0f;
}*/
// adjust direction and speed
//ULONG ulFlags = SetDesiredMovement();
//MovementAnimation(ulFlags);
resume;
}
}
}
// stop rotating
StopRotating();
// return to caller
return EReturn();
};
AdjustWeaponForFire()
{
FLOAT3D vRelWeapon = GetWeaponPositionRelative();
FLOAT3D vAbsWeapon = GetPlacement().pl_PositionVector + vRelWeapon*GetRotationMatrix();
m_fFireTime = _pTimer->CurrentTick()+m_fAdjustWeaponTime;
FLOAT3D vEnemy = m_penEnemy->GetPlacement().pl_PositionVector;
FLOAT3D vDir = (vEnemy-vAbsWeapon).Normalize();
ANGLE3D aAngles;
DirectionVectorToAngles(vDir, aAngles);
CPlacement3D plRelPl = CPlacement3D(FLOAT3D(0,0,0),aAngles);
plRelPl.AbsoluteToRelative(GetPlacement());
FLOAT fWantedHdg = plRelPl.pl_OrientationAngle(1);
FLOAT fWantedPitch = plRelPl.pl_OrientationAngle(2);
// adjust angle of projectile gun
if( m_iAttID == DEVIL_ATTACHMENT_PROJECTILEGUN)
{
FLOAT3D vShooting = GetPlacement().pl_PositionVector;
FLOAT3D vTarget = m_penEnemy->GetPlacement().pl_PositionVector;
FLOAT fDistanceFactor = 1.0f-ClampUp( (vShooting-vTarget).Length()/250.0f, 1.0f);
fWantedPitch = 20-fDistanceFactor*50.0f;
}
CAttachmentModelObject &amo = *GetModelObject()->GetAttachmentModel(m_iAttID);
m_fDeltaWeaponHdg = (fWantedHdg -amo.amo_plRelative.pl_OrientationAngle(1))/(m_fAdjustWeaponTime/_pTimer->TickQuantum);
m_fDeltaWeaponPitch = (fWantedPitch-amo.amo_plRelative.pl_OrientationAngle(2))/(m_fAdjustWeaponTime/_pTimer->TickQuantum);
while (m_fFireTime > _pTimer->CurrentTick())
{
wait(_pTimer->TickQuantum)
{
on (EBegin) :
{
CAttachmentModelObject &amo = *GetModelObject()->GetAttachmentModel(m_iAttID);
amo.amo_plRelative.pl_OrientationAngle(1) += m_fDeltaWeaponHdg;
amo.amo_plRelative.pl_OrientationAngle(2) += m_fDeltaWeaponPitch;
resume;
}
on (ETimer) : { stop; }
}
}
return EReturn();
}
StraightenUpWeapon()
{
FLOAT fAdjustWeaponTime = 0.25f;
m_fFireTime = _pTimer->CurrentTick()+fAdjustWeaponTime;
CAttachmentModelObject &amo = *GetModelObject()->GetAttachmentModel(m_iAttID);
m_fDeltaWeaponHdg = amo.amo_plRelative.pl_OrientationAngle(1)/(fAdjustWeaponTime/_pTimer->TickQuantum);
m_fDeltaWeaponPitch = amo.amo_plRelative.pl_OrientationAngle(2)/(fAdjustWeaponTime/_pTimer->TickQuantum);
while (m_fFireTime > _pTimer->CurrentTick())
{
wait(_pTimer->TickQuantum)
{
on (EBegin) :
{
CAttachmentModelObject &amo = *GetModelObject()->GetAttachmentModel(m_iAttID);
amo.amo_plRelative.pl_OrientationAngle(1) -= m_fDeltaWeaponHdg;
amo.amo_plRelative.pl_OrientationAngle(2) -= m_fDeltaWeaponPitch;
resume;
}
on (ETimer) : { stop; }
}
}
return EReturn();
}
FireLaser(EVoid)
{
autocall WaitWalkOrIdleToEnd() EReturn;
// to fire
StartModelAnim(DEVIL_ANIM_FROMIDLETOATTACK01, AOF_SMOOTHCHANGE);
autowait( GetModelObject()->GetAnimLength(DEVIL_ANIM_FROMIDLETOATTACK01)-0.1f);
StartModelAnim(DEVIL_ANIM_ATTACK01, AOF_SMOOTHCHANGE|AOF_LOOPING);
m_iAttID = DEVIL_ATTACHMENT_LASER;
m_fAdjustWeaponTime = 0.25f;
autocall AdjustWeaponForFire() EReturn;
// fire lasers
StartFireLaser();
m_iFiredProjectiles = 0;
while (m_iFiredProjectiles<m_iToFireProjectiles*10)
{
autocall DevilLockOnEnemy() EReturn;
m_tmLastPause = 0.1f*m_fPauseStretcher;
autowait(m_tmLastPause);
// fire laser
FLOAT fPredictionRatio = (FRnd()-0.5f)*0.25f;
if(m_iFiredProjectiles&1)
{
fPredictionRatio = 1.0f;
}
FLOAT fDeltaPitch = (FRnd()-0.5f)*1.0f;
FireOneLaser(fPredictionRatio, fDeltaPitch);
m_iFiredProjectiles++;
if (!IsInFrustum(m_penEnemy, CosFast(30.0f))) {
m_iFiredProjectiles = 10000;
}
}
autocall StraightenUpWeapon() EReturn;
StopFireLaser();
// from fire
//StartModelAnim(DEVIL_ANIM_IDLE, 0);
//autowait(0.25f*m_fPauseStretcher);
MaybeSwitchToAnotherPlayer();
// shoot completed
return EReturn();
};
FireRocketLauncher(EVoid)
{
autocall WaitWalkOrIdleToEnd() EReturn;
// to fire
StartModelAnim(DEVIL_ANIM_FROMIDLETOATTACK02, AOF_SMOOTHCHANGE);
autowait( GetModelObject()->GetAnimLength(DEVIL_ANIM_FROMIDLETOATTACK02)-0.1f);
StartModelAnim(DEVIL_ANIM_ATTACK02, AOF_SMOOTHCHANGE|AOF_LOOPING);
m_iAttID = DEVIL_ATTACHMENT_ROCKETLAUNCHER;
m_fAdjustWeaponTime = 0.5f;
autocall AdjustWeaponForFire() EReturn;
// fire rockets
StartFireRocket();
m_iFiredProjectiles = 0;
while (m_iFiredProjectiles<m_iToFireProjectiles)
{
autocall DevilLockOnEnemy() EReturn;
m_tmLastPause = 0.5f+0.3f*m_fPauseStretcher;
autowait( m_tmLastPause);
FLOAT fPredictionRatio = 0.25f+m_iFiredProjectiles*(0.75f/m_iToFireProjectiles);
fPredictionRatio = 1.0f;
m_iFiredProjectiles++;
// fire rocket
FireOneRocket(fPredictionRatio);
if (!IsInFrustum(m_penEnemy, CosFast(30.0f))) {
m_iFiredProjectiles = 10000;
}
}
autocall StraightenUpWeapon() EReturn;
StopFireRocket();
// from fire
//StartModelAnim(DEVIL_ANIM_IDLE, 0);
//autowait(0.25f*m_fPauseStretcher);
MaybeSwitchToAnotherPlayer();
// shoot completed
return EReturn();
};
FirePredictedProjectile(EVoid)
{
autocall WaitWalkOrIdleToEnd() EReturn;
// to fire
StartModelAnim(DEVIL_ANIM_FROMIDLETOATTACK01, AOF_SMOOTHCHANGE);
autowait( GetModelObject()->GetAnimLength(DEVIL_ANIM_FROMIDLETOATTACK01)-0.1f);
StartModelAnim(DEVIL_ANIM_ATTACK01, AOF_SMOOTHCHANGE|AOF_LOOPING);
m_iAttID = DEVIL_ATTACHMENT_PROJECTILEGUN;
m_fAdjustWeaponTime = 0.5f;
autocall AdjustWeaponForFire() EReturn;
// start fireing predicted magic projectile
m_iFiredProjectiles = 0;
while (m_iFiredProjectiles<m_iToFireProjectiles)
{
m_fAdjustWeaponTime = 0.45f;
autocall AdjustWeaponForFire() EReturn;
F_FirePredictedProjectile();
autowait( 0.8f-m_fAdjustWeaponTime);
m_iFiredProjectiles++;
if (!IsInFrustum(m_penEnemy, CosFast(30.0f))) {
m_iFiredProjectiles = 10000;
}
}
autocall StraightenUpWeapon() EReturn;
// from fire
StartModelAnim(DEVIL_ANIM_FROMATTACK01TOIDLE, AOF_SMOOTHCHANGE);
//autowait( GetModelObject()->GetAnimLength(DEVIL_ANIM_FROMATTACK01TOIDLE)-0.1f);
//StartModelAnim(DEVIL_ANIM_IDLE, AOF_SMOOTHCHANGE|AOF_LOOPING);
//autowait(0.25f*m_fPauseStretcher);
MaybeSwitchToAnotherPlayer();
// shoot completed
return EReturn();
}
FireElectricityGun(EVoid)
{
autocall WaitWalkOrIdleToEnd() EReturn;
// to fire
StartModelAnim(DEVIL_ANIM_FROMIDLETOATTACK02, AOF_SMOOTHCHANGE);
autowait( GetModelObject()->GetAnimLength(DEVIL_ANIM_FROMIDLETOATTACK02)-0.1f);
StartModelAnim(DEVIL_ANIM_ATTACK02, AOF_SMOOTHCHANGE|AOF_LOOPING);
m_iAttID = DEVIL_ATTACHMENT_ELETRICITYGUN;
m_fAdjustWeaponTime = 0.5f;
//autocall AdjustWeaponForFire() EReturn;
// start fireing electricity
m_iFiredProjectiles = 0;
while (m_iFiredProjectiles<m_iToFireProjectiles)
{
m_fAdjustWeaponTime = 0.45f;
autocall AdjustWeaponForFire() EReturn;
// shoot from gun
const FLOATmatrix3D &m = GetRotationMatrix();
m_vElectricitySource = GetFireingPositionAbsolute();
// target enemy body
EntityInfo *peiTarget = (EntityInfo*) (m_penEnemy->GetEntityInfo());
GetEntityInfoPosition(m_penEnemy, peiTarget->vTargetCenter, m_vElectricityTarget);
// give some time so player can move away from electricity beam
autowait(0.4f);
// fire electricity beam
m_bRenderElectricity = TRUE;
m_tmTemp = _pTimer->CurrentTick();
m_tmNextFXTime = m_tmTemp-_pTimer->TickQuantum;
PlayWeaponSound( SOUND_GHOSTBUSTER);
while( _pTimer->CurrentTick() < m_tmTemp+0.75f)
{
wait(_pTimer->TickQuantum) {
on (EBegin): {
// correct electricity beam target
FLOAT3D vNewTarget;
EntityInfo *peiTarget = (EntityInfo*) (m_penEnemy->GetEntityInfo());
GetEntityInfoPosition(m_penEnemy, peiTarget->vTargetCenter, vNewTarget);
FLOAT3D vDiff = vNewTarget-m_vElectricityTarget;
// if we have valid length
if( vDiff.Length() > 1.0f)
{
// calculate adjustment
m_vElectricityTarget = m_vElectricityTarget+vDiff.Normalize()*10.0f*_pTimer->TickQuantum;
}
// cast ray
CCastRay crRay( this, m_vElectricitySource, m_vElectricityTarget);
crRay.cr_bHitTranslucentPortals = FALSE;
crRay.cr_bPhysical = FALSE;
crRay.cr_ttHitModels = CCastRay::TT_COLLISIONBOX;
GetWorld()->CastRay(crRay);
// if entity is hit
if( crRay.cr_penHit != NULL)
{
// apply damage
InflictDirectDamage( crRay.cr_penHit, this, DMT_BULLET, 50.0f*_pTimer->TickQuantum/0.5f,
FLOAT3D(0, 0, 0), (m_vElectricitySource-m_vElectricityTarget).Normalize());
}
if( _pTimer->CurrentTick()>m_tmNextFXTime)
{
m_tmNextFXTime = _pTimer->CurrentTick()+0.125f+FRnd()*0.125f;
CPlacement3D plElectricityTarget = CPlacement3D( m_vElectricityTarget, ANGLE3D(0,0,0));
CEntity *penEffector = CreateEntity( plElectricityTarget, CLASS_EFFECTOR);
// set spawn parameters
ESpawnEffector eSpawnEffector;
eSpawnEffector.tmLifeTime = 6.0f;
eSpawnEffector.fSize = 0.025f;
eSpawnEffector.eetType = ET_HIT_GROUND;
eSpawnEffector.vDamageDir = FLOAT3D( 0.0f, 2.0f, 0.0f);
// initialize spray
penEffector->Initialize( eSpawnEffector);
}
resume;
};
on (ETimer): { stop; };
}
}
m_soSound.Stop();
m_bRenderElectricity = FALSE;
autowait( 0.8f-m_fAdjustWeaponTime);
m_iFiredProjectiles++;
if (!IsInFrustum(m_penEnemy, CosFast(30.0f))) {
m_iFiredProjectiles = 10000;
}
}
autocall StraightenUpWeapon() EReturn;
// from idle
StartModelAnim(DEVIL_ANIM_FROMATTACK02TOIDLE, AOF_SMOOTHCHANGE);
//autowait( GetModelObject()->GetAnimLength(DEVIL_ANIM_FROMATTACK02TOIDLE)-0.1f);
//StartModelAnim(DEVIL_ANIM_IDLE, AOF_SMOOTHCHANGE|AOF_LOOPING);
MaybeSwitchToAnotherPlayer();
// shoot completed
return EReturn();
}
FireGuidedProjectile(EVoid)
{
autocall WaitWalkOrIdleToEnd() EReturn;
StartModelAnim(DEVIL_ANIM_ATTACKBREATHSTART, AOF_SMOOTHCHANGE);
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
PlaySound(m_soLeft, SOUND_ATTACK_BREATH_START, SOF_3D);
StartModelAnim(DEVIL_ANIM_ATTACKBREATH, AOF_SMOOTHCHANGE);
autocall CMovableModelEntity::WaitUntilScheduledAnimStarts() EReturn;
PlaySound(m_soRight, SOUND_ATTACK_BREATH_LOOP, SOF_LOOP|SOF_3D);
// start fireing predicted magic projectile
m_iFiredProjectiles = 0;
m_tmFireBreathStart = _pTimer->CurrentTick();
m_tmFireBreathStop = UpperLimit(0.0f);
// calculate breath source and target positions
const FLOATmatrix3D &m = GetRotationMatrix();
m_vFireBreathSource = GetPlacement().pl_PositionVector+MAGIC_PROJECTILE_EXIT*m;
m_vFireBreathTarget = m_penEnemy->GetPlacement().pl_PositionVector-FLOAT3D(0,20.0f,0);
while (m_iFiredProjectiles<m_iToFireProjectiles)
{
m_tmLastPause = 0.45f;
autowait( m_tmLastPause);
// fire one guided projectile
ShootProjectile(PRT_DEVIL_GUIDED_PROJECTILE, MAGIC_PROJECTILE_EXIT,
ANGLE3D( AngleDeg(10.0f*Cos(m_iFiredProjectiles*360.0/6.0f)), -AngleDeg(20.0f*Sin(m_iFiredProjectiles*180.0/6.0f)), 0));
PlayWeaponSound( SOUND_ATTACK_BREATH_FIRE);
autowait(0.8f-m_tmLastPause);
m_iFiredProjectiles++;
}
StopFireBreathParticles();
// from fire
autocall WaitCurrentAnimEnd() EReturn;
PlaySound(m_soSound, SOUND_ATTACK_BREATH_END, SOF_3D);
m_soRight.Stop();
StartModelAnim(DEVIL_ANIM_ATTACKBREATHEND, AOF_SMOOTHCHANGE);
MaybeSwitchToAnotherPlayer();
// shoot completed
return EReturn();
}
JumpIntoPyramid(EVoid)
{
// remove collision and gravity
TurnOffPhysics();
StopMoving();
// remove weapons
RemoveWeapons();
SetTargetNone();
SetPlacement(m_plTeleport);
GetModelObject()->PlayAnim(DEVIL_ANIM_CLIMB, 0);
PlaySound(m_soJumpIntoPyramid, SOUND_CLIMB, SOF_3D);
autowait(7.0f);
// turn it arround
m_tmTemp = _pTimer->CurrentTick();
while( _pTimer->CurrentTick() < m_tmTemp+0.7f)
{
wait(_pTimer->TickQuantum) {
on (EBegin): { resume; };
on (ETimer): { stop; };
otherwise(): { resume; };
}
CPlacement3D plCurrent = GetPlacement();
FLOAT aDelta = -35.0f/0.7f*_pTimer->TickQuantum;
plCurrent.pl_OrientationAngle+=FLOAT3D(aDelta,0,0);
SetPlacement(plCurrent);
}
ShakeItFarBaby(_pTimer->CurrentTick(), 1.5f);
autowait(GetModelObject()->GetAnimLength(DEVIL_ANIM_CLIMB)-7.335f-_pTimer->TickQuantum);
SelectRandomAnger();
GetModelObject()->PlayAnim(m_iAngryAnim, 0);
PlaySound(m_soSound, m_iAngrySound, SOF_3D);
autowait( GetModelObject()->GetAnimLength(m_iAngryAnim)-0.1f);
StopMoving();
StopRotating();
TurnOnPhysics();
autocall GrabBothWeapons() EReturn;
m_dsDevilState = DS_PYRAMID_FIGHT;
m_dapAttackPower = DAP_MEDIUM_POWER_ATTACK;
m_fAttackRadius = 1e6f;
m_fAttackRunSpeed = 8.0f;
return EReturn();
}
TeleportIntoPyramid(EVoid)
{
// remove weapons
RemoveWeapons();
SetTargetNone();
Teleport(m_plTeleport, FALSE);
StopMoving();
StopRotating();
SelectRandomAnger();
GetModelObject()->PlayAnim(m_iAngryAnim, 0);
PlaySound(m_soSound, m_iAngrySound, SOF_3D);
autowait( GetModelObject()->GetAnimLength(m_iAngryAnim)-0.1f);
TurnOnPhysics();
autocall GrabBothWeapons() EReturn;
m_dsDevilState = DS_PYRAMID_FIGHT;
m_dapAttackPower = DAP_MEDIUM_POWER_ATTACK;
m_fAttackRadius = 1e6f;
m_fAttackRunSpeed = 8.0f;
return EReturn();
}
RegenerationImpulse(EVoid)
{
m_dsPreRegenerationDevilState = m_dsDevilState;
m_dsDevilState = DS_REGENERATION_IMPULSE;
GetModelObject()->PlayAnim(DEVIL_ANIM_HEAL, 0);
PlaySound(m_soSound, SOUND_HEAL, SOF_3D);
StopFireBreathParticles();
m_tmRegenerationStart = _pTimer->CurrentTick();
m_tmRegenerationStop = m_tmRegenerationStart+TM_HEALTH_IMPULSE-1.5f/*Regeneration particle life time*/;
// apply health impulse
m_tmTemp = _pTimer->CurrentTick();
while( _pTimer->CurrentTick() < m_tmTemp+TM_HEALTH_IMPULSE)
{
wait(_pTimer->TickQuantum) {
on (EBegin): { resume; };
on (ETimer): { stop; };
otherwise(): { resume; };
}
SetHealth(GetHealth()+HEALTH_IMPULSE*_pTimer->TickQuantum/TM_HEALTH_IMPULSE);
}
m_dsDevilState = m_dsPreRegenerationDevilState;
return EReturn();
}
StopAttack(EVoid) : CEnemyBase::StopAttack {
if(m_penEnemy==NULL)
{
autocall Celebrate() EReturn;
}
jump CEnemyBase::StopAttack();
};
ContinueInMainLoop(EVoid)
{
SwitchToModel();
// continue behavior in base class
if( !m_bWasOnceInMainLoop)
{
m_bWasOnceInMainLoop = TRUE;
jump CEnemyBase::MainLoop();
}
else
{
// get some player for trigger source if any is existing
CEntity *penEnemy = FixupCausedToPlayer(this, m_penEnemy, /*bWarning=*/FALSE);
if (penEnemy!=m_penEnemy) {
SetTargetSoft(penEnemy);
}
jump CEnemyBase::StandardBehavior();
}
}
MPIntro(EVoid)
{
m_dsDevilState=DS_PYRAMID_FIGHT;
jump ContinueInMainLoop();
}
/************************************************************
* D E A T H *
************************************************************/
Death(EVoid) : CEnemyBase::Death {
SetFlags(GetFlags()&~ENF_ALIVE);
StopFireBreathParticles();
StopRegenerationParticles();
// stop moving
StopMoving();
DeathSound(); // death sound
// set physic flags
SetCollisionFlags(ECF_MODEL);
// start death anim
AnimForDeath();
autowait(4.66f);
ShakeItFarBaby(_pTimer->CurrentTick(), 5.0f);
autowait(GetModelObject()->GetAnimLength(DEVIL_ANIM_DEATH)-4.66f);
m_tmDeathTime = _pTimer->CurrentTick();
CWorldSettingsController *pwsc = GetWSC(this);
if (pwsc!=NULL)
{
pwsc->m_colGlade=C_WHITE;
pwsc->m_tmGlaringStarted = _pTimer->CurrentTick()+1.5f;
pwsc->m_tmGlaringEnded = pwsc->m_tmGlaringStarted+1.0f,
pwsc->m_fGlaringFadeInRatio = 0.2f;
pwsc->m_fGlaringFadeOutRatio = 0.7f;
}
autowait(1.5f);
PlaySound(m_soLeft, SOUND_DISAPPEAR, SOF_3D|SOF_VOLUMETRIC);
autowait(0.25f);
SwitchToEditorModel();
PlaySound(m_soRight, SOUND_DEATHPARTICLES, SOF_3D);
return EEnd();
};
/************************************************************
* M A I N *
************************************************************/
Main(EVoid) {
m_sptType = SPT_NONE;
// declare yourself as a model
InitAsEditorModel();
SetPhysicsFlags(EPF_MODEL_WALKING&~EPF_TRANSLATEDBYGRAVITY);
SetCollisionFlags(((ECBI_MODEL)<<ECB_TEST) | ((ECBI_MODEL)<<ECB_PASS) | ((ECBI_ITEM)<<ECB_IS));
SetFlags(GetFlags()|ENF_ALIVE);
// this one is boss!
m_bBoss = TRUE;
if( !m_bForMPIntro)
{
SetHealth(BOSS_HEALTH);
}
else
{
SetHealth(5000);
}
m_fMaxHealth = BOSS_HEALTH;
m_fBlowUpAmount = 1e6;
m_fBodyParts = 6;
m_fDamageWounded = 1e9;
en_fDensity = 2500.0f;
m_bHasUpperWeapons = FALSE;
m_bRenderElectricity = FALSE;
m_bOnStartPosition = FALSE;
m_tmGiveUp = UpperLimit(0.0f);
m_tmDeathTime = -1.0f;
// setup default speeds and radiuses
/*
if( tmp_af[0]==0)
{
tmp_af[0]=100.0f;
tmp_af[1]=200.0f;
tmp_af[2]=6.0f;
tmp_af[3]=12.0f;
tmp_af[4]=15.0f;
}
*/
// set your appearance
SetComponents(this, *GetModelObject(), MODEL_DEVIL, TEXTURE_DEVIL, 0, 0, 0);
// stretch devil
GetModelObject()->StretchModel(FLOAT3D(SIZE, SIZE, SIZE));
ModelChangeNotify();
StandingAnim();
// setup moving speed
m_fWalkSpeed = 10.0f;
m_aWalkRotateSpeed = AngleDeg(90.0f);
m_fAttackRunSpeed = 8.0f;
m_aAttackRotateSpeed = AngleDeg(90.0f);
m_fCloseRunSpeed = 8.0f;
m_aCloseRotateSpeed = AngleDeg(90.0f);
// setup attack distances
m_fAttackDistance = 1e24f;
m_fCloseDistance = 50.0f;
m_fStopDistance = 10.0f;
m_fAttackFireTime = 10.0f;
m_fCloseFireTime = 5.0f;
m_fIgnoreRange = UpperLimit(0.0f);
en_fAcceleration = en_fDeceleration = 50.0f;
m_fLockOnEnemyTime = 0.05f;
m_fPauseStretcher = 1.0f;
m_bWasOnceInMainLoop = FALSE;
// setup light source
SetupLightSource();
// set light animation if available
try {
m_aoLightAnimation.SetData_t(CTFILENAME("Animations\\BasicEffects.ani"));
} catch (char *strError) {
WarningMessage(TRANS("Cannot load Animations\\BasicEffects.ani: %s"), strError);
}
PlayLightAnim(LIGHT_ANIM_NONE, 0);
autowait(0.1f);
m_iScore = 0;
if( !m_bForMPIntro)
{
StartModelAnim(DEVIL_ANIM_POSEDOWN, 0);
wait() {
on (EBegin) : { resume; }
on (ETrigger) : { stop; }
}
}
else
{
StartModelAnim(DEVIL_ANIM_DEFAULT, 0);
}
SwitchToModel();
// set initial state
m_dsDevilState = DS_NOT_EXISTING;
wait()
{
on (EBegin) : {
if( cht_bDebugFinalBoss)
{
CPrintF("Main loop, event: Begin\n");
}
if( !m_bForMPIntro)
{
if(m_dsDevilState == DS_NOT_EXISTING)
{
m_dsDevilState = DS_DESTROYING_CITY;
call DestroyCity();
}
}
resume;
}
on (ETrigger) :
{
if( cht_bDebugFinalBoss)
{
CPrintF("Main loop, event: Trigger\n");
}
resume;
}
on (EDevilCommand eDevilCommand) :
{
if( cht_bDebugFinalBoss)
{
CTString strDevilCommand = DevilCommandType_enum.NameForValue(INDEX(eDevilCommand.dctType));
CPrintF("Main loop, event: Devil command: %s\n", (const char *) strDevilCommand);
}
if( eDevilCommand.dctType == DC_GRAB_LOWER_WEAPONS)
{
m_dapAttackPower = DAP_LOW_POWER_ATTACK;
m_dsDevilState = DS_ENEMY;
call GrabLowerWeapons();
}
// force given action marker
else if( eDevilCommand.dctType == DC_FORCE_ACTION)
{
m_penAction = eDevilCommand.penForcedAction;
call DestroyCity();
}
else if( eDevilCommand.dctType == DC_STOP_MOVING)
{
m_vStartPosition = GetPlacement().pl_PositionVector;
m_fAttackRadius = 0.0f;
}
else if( eDevilCommand.dctType == DC_STOP_ATTACK)
{
SetTargetNone();
}
else if( eDevilCommand.dctType == DC_JUMP_INTO_PYRAMID)
{
GetModelObject()->PlayAnim( DEVIL_ANIM_IDLE, 0);
m_plTeleport = eDevilCommand.penForcedAction->GetPlacement();
m_dsDevilState = DS_JUMPING_INTO_PYRAMID;
call JumpIntoPyramid();
}
else if( eDevilCommand.dctType == DC_TELEPORT_INTO_PYRAMID)
{
GetModelObject()->PlayAnim( DEVIL_ANIM_IDLE, 0);
m_plTeleport = eDevilCommand.penForcedAction->GetPlacement();
m_dsDevilState = DS_JUMPING_INTO_PYRAMID;
call TeleportIntoPyramid();
}
resume;
}
on (ERegenerationImpuls) :
{
if( cht_bDebugFinalBoss)
{
CPrintF("Main loop, event: Regeneration impulse\n");
}
m_bRenderElectricity = FALSE;
call RegenerationImpulse();
resume;
}
on (EHitBySpaceShipBeam) :
{
if( cht_bDebugFinalBoss)
{
CPrintF("Main loop, event: Hit by space ship beam\n");
}
m_bRenderElectricity = FALSE;
m_tmHitBySpaceShipBeam = _pTimer->CurrentTick();
call BeamDamage();
resume;
}
// if dead
on (EDeath eDeath) : {
if( !(GetFlags()&ENF_ALIVE))
{
resume;
}
if( cht_bDebugFinalBoss)
{
CPrintF("Main loop, event: Death\n");
}
// die
m_bRenderElectricity = FALSE;
jump CEnemyBase::Die(eDeath);
}
on( EEnvironmentStart):
{
call MPIntro();
resume;
}
on (EReturn) :
{
if( cht_bDebugFinalBoss)
{
CPrintF("Main loop, event: Return\n");
}
if( m_dsDevilState==DS_DESTROYING_CITY)
{
m_soSound.Set3DParameters(1000.0f, 500.0f, 2.0f, 1.0f);
m_dsDevilState = DS_ENEMY;
if( m_dapAttackPower == DAP_NOT_ATTACKING)
{
m_dapAttackPower = DAP_PLAYER_HUNT;
}
}
call ContinueInMainLoop();
resume;
}
}
};
};