335 %{ #include "Entities/StdH/StdH.h" #include "Models/Enemies/Gizmo/Gizmo.h" %} uses "Entities/EnemyBase"; uses "Entities/BasicEffects"; %{ // info structure static EntityInfo eiGizmo = { EIBT_FLESH, 100.0f, 0.0f, 1.3f, 0.0f, // source (eyes) 0.0f, 1.0f, 0.0f, // target (body) }; #define EXPLODE_GIZMO 2.5f %} class CGizmo: CEnemyBase { name "Gizmo"; thumbnail "Thumbnails\\Gizmo.tbn"; properties: // class internal 1 BOOL m_bExploded = FALSE, components: 1 class CLASS_BASE "Classes\\EnemyBase.ecl", 2 class CLASS_BLOOD_SPRAY "Classes\\BloodSpray.ecl", 3 class CLASS_BASIC_EFFECT "Classes\\BasicEffect.ecl", // ************** DATA ************** 10 model MODEL_GIZMO "Models\\Enemies\\Gizmo\\Gizmo.mdl", 20 texture TEXTURE_GIZMO "Models\\Enemies\\Gizmo\\Gizmo.tex", 50 sound SOUND_IDLE "Models\\Enemies\\Gizmo\\Sounds\\Idle.wav", 51 sound SOUND_JUMP "Models\\Enemies\\Gizmo\\Sounds\\Jump.wav", 52 sound SOUND_DEATH_JUMP "Models\\Enemies\\Gizmo\\Sounds\\JumpDeath.wav", 53 sound SOUND_SIGHT "Models\\Enemies\\Gizmo\\Sounds\\Sight.wav", functions: // describe how this enemy killed player virtual CTString GetPlayerKillDescription(const CTString &strPlayerName, const EDeath &eDeath) { CTString str; str.PrintF(TRANS("%s ate a marsh hopper"), (const char*)strPlayerName); return str; } virtual const CTFileName &GetComputerMessageName(void) const { static DECLARE_CTFILENAME(fnm, "Data\\Messages\\Enemies\\Gizmo.txt"); return fnm; }; /* Entity info */ void *GetEntityInfo(void) { return &eiGizmo; }; void Precache(void) { CEnemyBase::Precache(); PrecacheSound(SOUND_SIGHT); PrecacheSound(SOUND_IDLE); PrecacheSound(SOUND_JUMP); PrecacheSound(SOUND_DEATH_JUMP); PrecacheClass(CLASS_BASIC_EFFECT, BET_GIZMO_SPLASH_FX); PrecacheClass(CLASS_BLOOD_SPRAY); }; void SightSound(void) { PlaySound(m_soSound, SOUND_SIGHT, SOF_3D); }; void RunningAnim(void) { StartModelAnim(GIZMO_ANIM_RUN, 0); }; void MortalJumpAnim(void) { StartModelAnim(GIZMO_ANIM_RUN, 0); }; void StandAnim(void) { StartModelAnim(GIZMO_ANIM_IDLE, AOF_LOOPING|AOF_NORESTART); }; // virtual sound functions void IdleSound(void) { PlaySound(m_soSound, SOUND_IDLE, SOF_3D); }; /************************************************************ * BLOW UP FUNCTIONS * ************************************************************/ void BlowUpNotify(void) { Explode(); }; // explode only once void Explode(void) { if (!m_bExploded) { m_bExploded = TRUE; // spawn blood spray CPlacement3D plSpray = GetPlacement(); CEntity *penSpray = CreateEntity( plSpray, CLASS_BLOOD_SPRAY); penSpray->SetParent( this); ESpawnSpray eSpawnSpray; eSpawnSpray.fDamagePower = 2.0f; eSpawnSpray.fSizeMultiplier = 1.0f; eSpawnSpray.sptType = SPT_SLIME; eSpawnSpray.vDirection = en_vCurrentTranslationAbsolute/8.0f; eSpawnSpray.penOwner = this; penSpray->Initialize( eSpawnSpray); // spawn splash fx (sound) CPlacement3D plSplash = GetPlacement(); CEntityPointer penSplash = CreateEntity(plSplash, CLASS_BASIC_EFFECT); ESpawnEffect ese; ese.colMuliplier = C_WHITE|CT_OPAQUE; ese.betType = BET_GIZMO_SPLASH_FX; penSplash->Initialize(ese); } }; // gizmo should always blow up BOOL ShouldBlowUp(void) { return TRUE; } // leave stain virtual void LeaveStain(BOOL bGrow) { ESpawnEffect ese; FLOAT3D vPoint; FLOATplane3D vPlaneNormal; FLOAT fDistanceToEdge; // get your size FLOATaabbox3D box; GetBoundingBox(box); // on plane if( GetNearestPolygon(vPoint, vPlaneNormal, fDistanceToEdge)) { // if near to polygon and away from last stain point if( (vPoint-GetPlacement().pl_PositionVector).Length()<0.5f ) { FLOAT fStretch = box.Size().Length(); // stain ese.colMuliplier = C_WHITE|CT_OPAQUE; ese.betType = BET_GIZMOSTAIN; ese.vStretch = FLOAT3D( fStretch*0.75f, fStretch*0.75f, 1.0f); ese.vNormal = FLOAT3D( vPlaneNormal); ese.vDirection = FLOAT3D( 0, 0, 0); FLOAT3D vPos = vPoint+ese.vNormal/50.0f*(FRnd()+0.5f); CEntityPointer penEffect = CreateEntity( CPlacement3D(vPos, ANGLE3D(0,0,0)), CLASS_BASIC_EFFECT); penEffect->Initialize(ese); } } }; procedures: /************************************************************ * A T T A C K E N E M Y * ************************************************************/ // close range -> move toward enemy and try to jump onto it PerformAttack(EVoid) : CEnemyBase::PerformAttack { while (TRUE) { // ------------ Exit close attack if out of range or enemy is dead // if attacking is futile if (ShouldCeaseAttack()) { SetTargetNone(); return EReturn(); } // stop moving SetDesiredTranslation(FLOAT3D(0.0f, 0.0f, 0.0f)); SetDesiredRotation(ANGLE3D(0, 0, 0)); // ------------ Wait for some time on the ground FLOAT fWaitTime = 0.25f+FRnd()*0.4f; wait( fWaitTime) { on (EBegin) : { resume; }; on (ESound) : { resume; } // ignore all sounds on (EWatch) : { resume; } // ignore watch on (ETimer) : { stop; } // timer tick expire } autocall JumpOnce() EReturn; } } JumpOnce(EVoid) { // ------------ Jump either in slightly randomized direction or mortal, streight and fast toward enemy // we are always going for enemy m_vDesiredPosition = m_penEnemy->GetPlacement().pl_PositionVector; m_fMoveFrequency = 0.1f; // if we are close enough for mortal jump if( CalcPlaneDist(m_penEnemy) < 10.0f) { // set mortal jump parameters (no random) m_fMoveSpeed = m_fCloseRunSpeed*1.5f; m_aRotateSpeed = m_aCloseRotateSpeed*0.5f; FLOAT fSpeedX = 0.0f; FLOAT fSpeedY = 10.0f; FLOAT fSpeedZ = -m_fMoveSpeed; // if can't see enemy if( !IsInFrustum(m_penEnemy, CosFast(30.0f))) { // rotate a lot m_aRotateSpeed = m_aCloseRotateSpeed*1.5f; // but don't jump too much fSpeedY /= 2.0f; fSpeedZ /= 4.0f; PlaySound(m_soSound, SOUND_JUMP, SOF_3D); } else { PlaySound(m_soSound, SOUND_DEATH_JUMP, SOF_3D); } FLOAT3D vTranslation(fSpeedX, fSpeedY, fSpeedZ); SetDesiredTranslation(vTranslation); MortalJumpAnim(); } // start slightly randomized jump else { m_fMoveSpeed = m_fCloseRunSpeed; m_aRotateSpeed = m_aCloseRotateSpeed; // set random jump parameters FLOAT fSpeedX = (FRnd()-0.5f)*10.0f; FLOAT fSpeedY = FRnd()*5.0f+5.0f; FLOAT fSpeedZ = -m_fMoveSpeed-FRnd()*2.5f; FLOAT3D vTranslation(fSpeedX, fSpeedY, fSpeedZ); SetDesiredTranslation(vTranslation); RunningAnim(); PlaySound(m_soSound, SOUND_JUMP, SOF_3D); } // ------------ While in air, adjust directions, on touch start new jump or explode while (TRUE) { // adjust direction and speed m_fMoveSpeed = 0.0f; m_aRotateSpeed = m_aCloseRotateSpeed; FLOAT3D vTranslation = GetDesiredTranslation(); SetDesiredMovement(); SetDesiredTranslation(vTranslation); wait(m_fMoveFrequency) { on (EBegin) : { resume; }; on (ESound) : { resume; } // ignore all sounds on (EWatch) : { resume; } // ignore watch on (ETimer) : { stop; } // timer tick expire on (ETouch etouch) : { // if we touched ground if( etouch.penOther->GetRenderType() & RT_BRUSH) { return EReturn(); } // we touched player, explode else if ( IsDerivedFromClass( etouch.penOther, "Player")) { InflictDirectDamage(etouch.penOther, this, DMT_IMPACT, 10.0f, GetPlacement().pl_PositionVector, -en_vGravityDir); SetHealth(-10000.0f); m_vDamage = FLOAT3D(0,10000,0); SendEvent(EDeath()); } // we didn't touch ground nor player, ignore resume; } } } }; /************************************************************ * M A I N * ************************************************************/ Main(EVoid) { // declare yourself as a model InitAsModel(); SetPhysicsFlags(EPF_MODEL_WALKING|EPF_HASLUNGS); SetCollisionFlags(ECF_MODEL); SetFlags(GetFlags()|ENF_ALIVE); SetHealth(9.5f); m_fMaxHealth = 9.5f; en_tmMaxHoldBreath = 5.0f; en_fDensity = 2000.0f; m_fBlowUpSize = 2.0f; // set your appearance SetModel(MODEL_GIZMO); SetModelMainTexture(TEXTURE_GIZMO); // setup moving speed m_fWalkSpeed = FRnd() + 1.5f; m_aWalkRotateSpeed = AngleDeg(FRnd()*10.0f + 500.0f); m_fAttackRunSpeed = FRnd()*5.0f + 15.0f; m_aAttackRotateSpeed = AngleDeg(FRnd()*100 + 600.0f); m_fCloseRunSpeed = FRnd()*5.0f + 15.0f; m_aCloseRotateSpeed = AngleDeg(360.0f); // setup attack distances m_fAttackDistance = 400.0f; m_fCloseDistance = 250.0f; m_fStopDistance = 0.0f; m_fAttackFireTime = 2.0f; m_fCloseFireTime = 0.5f; m_fIgnoreRange = 500.0f; // damage/explode properties m_fBlowUpAmount = 0.0f; m_fBodyParts = 0; m_fDamageWounded = 0.0f; m_iScore = 500; m_sptType = SPT_SLIME; en_fDeceleration = 150.0f; // set stretch factors for height and width GetModelObject()->StretchModel(FLOAT3D(1.25f, 1.25f, 1.25f)); ModelChangeNotify(); StandingAnim(); // continue behavior in base class jump CEnemyBase::MainLoop(); }; };