/* 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. */ 342 %{ #include "EntitiesMP/StdH/StdH.h" #include "ModelsMP/Enemies/ChainSawFreak/Freak.h" #include "ModelsMP/Enemies/ChainSawFreak/Saw.h" %} uses "EntitiesMP/EnemyBase"; uses "EntitiesMP/EnemyRunInto"; %{ #define FREAK_SIZE 1.05f // info structure static EntityInfo eiChainsawFreak = { EIBT_FLESH, 350.0f, 0.0f, 2.5f*FREAK_SIZE, 0.0f, // source (eyes) 0.0f, 1.5f*FREAK_SIZE, 0.0f, // target (body) }; #define HIT_DISTANCE 4.0f %} class CChainsawFreak : CEnemyRunInto { name "ChainsawFreak"; thumbnail "Thumbnails\\ChainsawFreak.tbn"; properties: 1 BOOL m_bRunAttack = FALSE, // run attack (attack local) 2 BOOL m_bSawHit = FALSE, // close attack local 3 CEntityPointer m_penLastTouched, // last touched 4 CSoundObject m_soFeet, // for running sound 5 BOOL m_bRunSoundPlaying = FALSE, 6 INDEX m_iRunType = 0, // which running animation? 10 BOOL m_bAttacking = FALSE, 11 FLOAT m_fSightSoundBegin = 0.0f, // when sight sound was played components: 0 class CLASS_BASE "Classes\\EnemyRunInto.ecl", 1 model MODEL_FREAK "ModelsMP\\Enemies\\ChainsawFreak\\Freak.mdl", 2 model MODEL_CHAINSAW "ModelsMP\\Enemies\\ChainsawFreak\\Saw.mdl", 3 texture TEXTURE_FREAK "ModelsMP\\Enemies\\ChainsawFreak\\Freak.tex", 4 texture TEXTURE_CHAINSAW "ModelsMP\\Enemies\\ChainsawFreak\\Saw.tex", // ************** SOUNDS ************** 50 sound SOUND_IDLE "ModelsMP\\Enemies\\ChainsawFreak\\Sounds\\Idle.wav", 51 sound SOUND_RUN "ModelsMP\\Enemies\\ChainsawFreak\\Sounds\\Run.wav", // 53 sound SOUND_RUNATTACK "ModelsMP\\Enemies\\ChainsawFreak\\Sounds\\RunAttack.wav", 54 sound SOUND_ATTACK "ModelsMP\\Enemies\\ChainsawFreak\\Sounds\\Attack.wav", 55 sound SOUND_WOUND "ModelsMP\\Enemies\\ChainsawFreak\\Sounds\\Wound.wav", 56 sound SOUND_DEATH "ModelsMP\\Enemies\\ChainsawFreak\\Sounds\\Death.wav", 57 sound SOUND_SIGHT "ModelsMP\\Enemies\\ChainsawFreak\\Sounds\\Sight.wav", functions: // describe how this enemy killed player virtual CTString GetPlayerKillDescription(const CTString &strPlayerName, const EDeath &eDeath) { CTString str; str.PrintF(TRANSV("Chainsaw freak dismembered %s"), (const char *) strPlayerName); return str; } void Precache(void) { CEnemyBase::Precache(); PrecacheSound(SOUND_IDLE ); PrecacheSound(SOUND_RUN ); // PrecacheSound(SOUND_RUNATTACK); PrecacheSound(SOUND_ATTACK ); PrecacheSound(SOUND_WOUND ); PrecacheSound(SOUND_DEATH ); PrecacheSound(SOUND_SIGHT ); }; /* Entity info */ void *GetEntityInfo(void) { return &eiChainsawFreak; }; FLOAT GetCrushHealth(void) { return 60.0f; } virtual const CTFileName &GetComputerMessageName(void) const { static DECLARE_CTFILENAME(fnm, "DataMP\\Messages\\Enemies\\ChainsawFreak.txt"); return fnm; }; // render particles /*void RenderParticles(void) { Particles_RunningDust(this); }*/ /* Receive damage */ void ReceiveDamage(CEntity *penInflictor, enum DamageType dmtType, FLOAT fDamageAmmount, const FLOAT3D &vHitPoint, const FLOAT3D &vDirection) { // note: chainsawfreaks can't hurt each others if (!IsOfClass(penInflictor, "ChainsawFreak")) { CEnemyBase::ReceiveDamage(penInflictor, dmtType, fDamageAmmount, vHitPoint, vDirection); } }; void AdjustDifficulty(void) { // chainsaw freak must not change his speed at different difficulties } // death INDEX AnimForDeath(void) { INDEX iAnim; if (en_vCurrentTranslationAbsolute.Length()>5.0f) { iAnim = FREAK_ANIM_DEATHRUN; } else { iAnim = FREAK_ANIM_DEATHSTAND; } ChangeCollisionBoxIndexWhenPossible(FREAK_COLLISION_BOX_DEATHRUN); StartModelAnim(iAnim, 0); m_bAttacking = FALSE; DeactivateRunningSound(); return iAnim; }; FLOAT WaitForDust(FLOAT3D &vStretch) { if(GetModelObject()->GetAnim()==FREAK_ANIM_DEATHRUN) { vStretch=FLOAT3D(1,1,2)*1.0f; return 0.65f; } else if(GetModelObject()->GetAnim()==FREAK_ANIM_DEATHSTAND) { vStretch=FLOAT3D(1,1,2)*1.5f; return 0.72f; } return -1.0f; }; void DeathNotify() { ChangeCollisionBoxIndexWhenPossible(FREAK_COLLISION_BOX_DEATH); SetCollisionFlags(ECF_MODEL); }; // virtual anim functions void StandingAnim(void) { StartModelAnim(FREAK_ANIM_IDLE, AOF_LOOPING|AOF_NORESTART); //DeactivateRunningSound(); }; void WalkingAnim(void) { StartModelAnim(FREAK_ANIM_WALK, AOF_LOOPING|AOF_NORESTART); //DeactivateRunningSound(); }; void RunningAnim(void) { switch(m_iRunType) { case 0: StartModelAnim(FREAK_ANIM_ATTACKRUN, AOF_LOOPING|AOF_NORESTART); break; case 1: StartModelAnim(FREAK_ANIM_ATTACKRUNFAR, AOF_LOOPING|AOF_NORESTART); break; case 2: StartModelAnim(FREAK_ANIM_ATTACKCHARGE, AOF_LOOPING|AOF_NORESTART); break; default: ASSERTALWAYS("Unknown Chainsaw freak run type!"); } //ActivateRunningSound(); }; void ChargeAnim(void) { StartModelAnim(FREAK_ANIM_RUNSLASHING, AOF_LOOPING|AOF_NORESTART); }; void RotatingAnim(void) { m_iRunType = IRnd()%3; StartModelAnim(FREAK_ANIM_ATTACKSTART, AOF_LOOPING|AOF_NORESTART); }; // virtual sound functions void IdleSound(void) { PlaySound(m_soSound, SOUND_IDLE, SOF_3D); }; /*void SightSound(void) { PlaySound(m_soFeet, SOUND_SIGHT, SOF_3D|SOF_SMOOTHCHANGE); };*/ void WoundSound(void) { PlaySound(m_soSound, SOUND_WOUND, SOF_3D); }; void DeathSound(void) { PlaySound(m_soSound, SOUND_DEATH, SOF_3D); }; // running sounds void ActivateRunningSound(void) { if (!m_bRunSoundPlaying) { PlaySound(m_soFeet, SOUND_RUN, SOF_3D|SOF_LOOP); m_bRunSoundPlaying = TRUE; } } void DeactivateRunningSound(void) { m_soFeet.Stop(); m_bRunSoundPlaying = FALSE; } /************************************************************ * ATTACK FUNCTIONS * ************************************************************/ // touched another live entity void LiveEntityTouched(ETouch etouch) { if (m_penLastTouched!=etouch.penOther || _pTimer->CurrentTick()>=m_fLastTouchedTime+0.25f) { // hit angle FLOAT3D vDirection = en_vCurrentTranslationAbsolute; vDirection.Normalize(); ANGLE aHitAngle = FLOAT3D(etouch.plCollision)%vDirection; // only hit target in front of you if (aHitAngle < 0.0f) { // increase mass - only if not another bull if (!IsOfSameClass(this, etouch.penOther)) { IncreaseKickedMass(etouch.penOther); } PlaySound(m_soSound, SOUND_ATTACK, SOF_3D); // store last touched m_penLastTouched = etouch.penOther; m_fLastTouchedTime = _pTimer->CurrentTick(); // damage FLOAT3D vDirection = m_penEnemy->GetPlacement().pl_PositionVector-GetPlacement().pl_PositionVector; vDirection.Normalize(); InflictDirectDamage(etouch.penOther, this, DMT_CHAINSAW, -aHitAngle*40.0f, FLOAT3D(0, 0, 0), vDirection); // kick touched entity FLOAT3D vSpeed = -FLOAT3D(etouch.plCollision); vSpeed = vSpeed*10.0f; const FLOATmatrix3D &m = GetRotationMatrix(); FLOAT3D vSpeedRel = vSpeed*!m; if (vSpeedRel(1)<-0.1f) { vSpeedRel(1)-=5.0f; } else { vSpeedRel(1)+=5.0f; } vSpeedRel(2)=5.0f; vSpeed = vSpeedRel*m; KickEntity(etouch.penOther, vSpeed); } } }; // touched entity with higher mass BOOL HigherMass(void) { return (m_fMassKicked > 500.0f); }; // adjust sound and watcher parameters here if needed void EnemyPostInit(void) { // set sound default parameters m_soFeet.Set3DParameters(500.0f, 50.0f, 1.0f, 1.0f); m_bRunSoundPlaying = FALSE; m_soSound.Set3DParameters(160.0f, 50.0f, 1.0f, 1.0f); }; void PreMoving() { if (!m_bRunSoundPlaying && _pTimer->CurrentTick()>m_fSightSoundBegin+2.0f && m_bAttacking) { ActivateRunningSound(); } CEnemyBase::PreMoving(); }; procedures: /************************************************************ * A T T A C K E N E M Y * ************************************************************/ // hit enemy Hit(EVoid) : CEnemyBase::Hit { if (CalcDist(m_penEnemy) < HIT_DISTANCE) { // attack with saw StartModelAnim(FREAK_ANIM_ATTACKSLASH, 0); // jump FLOAT3D vDir = (m_penEnemy->GetPlacement().pl_PositionVector - GetPlacement().pl_PositionVector).Normalize(); vDir *= !GetRotationMatrix(); vDir *= m_fCloseRunSpeed*1.5f; vDir(2) = 1.0f; GiveImpulseTranslationRelative(vDir); //DeactivateRunningSound(); m_bSawHit = FALSE; autowait(0.4f); PlaySound(m_soSound, SOUND_ATTACK, SOF_3D); if (CalcDist(m_penEnemy) < HIT_DISTANCE) { m_bSawHit = TRUE; } autowait(0.1f); if (CalcDist(m_penEnemy) < HIT_DISTANCE) { m_bSawHit = TRUE; } autowait(0.1f); if (CalcDist(m_penEnemy) < HIT_DISTANCE) { m_bSawHit = TRUE; } if (m_bSawHit) { FLOAT3D vDirection = m_penEnemy->GetPlacement().pl_PositionVector-GetPlacement().pl_PositionVector; vDirection.Normalize(); InflictDirectDamage(m_penEnemy, this, DMT_CHAINSAW, 20.0f, FLOAT3D(0, 0, 0), vDirection); vDirection = vDirection * 10.0f; //GetPitchDirection(AngleDeg(90.0f), vSpeed); FLOATmatrix3D mDirection; MakeRotationMatrixFast(mDirection, ANGLE3D(0.0f, 30.0f, 0.0f)); vDirection = vDirection * mDirection; KickEntity(m_penEnemy, vDirection); } autowait(0.6f); } // run to enemy m_fShootTime = _pTimer->CurrentTick() + 0.5f; return EReturn(); }; AttackEnemy(EVoid) : CEnemyBase::AttackEnemy { m_bAttacking = TRUE; PlaySound(m_soSound, SOUND_SIGHT, SOF_3D|SOF_SMOOTHCHANGE); m_fSightSoundBegin = _pTimer->CurrentTick(); jump CEnemyBase::AttackEnemy(); } BeIdle(EVoid) : CEnemyBase::BeIdle { m_bAttacking = FALSE; DeactivateRunningSound(); jump CEnemyBase::BeIdle(); } PostRunAwayFromEnemy(EVoid) : CEnemyRunInto::PostRunAwayFromEnemy { StartModelAnim(FREAK_ANIM_ATTACKRUNFAR, 0); autowait(0.25f); SetDesiredTranslation(FLOAT3D(0,0,0)); StartModelAnim(FREAK_ANIM_IDLE, 0); autowait(0.25f); return EReturn(); }; /************************************************************ * M A I N * ************************************************************/ Main(EVoid) { // declare yourself as a model InitAsModel(); SetPhysicsFlags(EPF_MODEL_WALKING); SetCollisionFlags(ECF_MODEL); SetFlags(GetFlags()|ENF_ALIVE); SetHealth(175.0f); m_fMaxHealth = 175.0f; en_fDensity = 2000.0f; // set your appearance SetModel(MODEL_FREAK); SetModelMainTexture(TEXTURE_FREAK); AddAttachment(FREAK_ATTACHMENT_CHAINSAW, MODEL_CHAINSAW, TEXTURE_CHAINSAW); StandingAnim(); // setup moving speed m_fWalkSpeed = FRnd() + 2.5f; m_aWalkRotateSpeed = AngleDeg(FRnd()*25.0f + 45.0f); m_fAttackRunSpeed = FRnd()*2.0f + 13.0f; m_fAttackRotateRunInto = AngleDeg(FRnd()*30 + 50.0f); m_aAttackRotateSpeed = m_fAttackRotateRunInto; m_fCloseRunSpeed = FRnd() + 10.5f; m_aCloseRotateSpeed = AngleDeg(FRnd()*50 + 250.0f); // setup attack distances m_fAttackDistance = 50.0f; m_fCloseDistance = 7.0f; m_fStopDistance = 0.0f; m_fAttackFireTime = 0.05f; m_fCloseFireTime = 1.0f; m_fIgnoreRange = 150.0f; // damage/explode properties6 m_fBlowUpAmount = 1E10f; m_fBodyParts = 6; m_fDamageWounded = 100000.0f; m_iScore = 1500; if (m_fStepHeight==-1) { m_fStepHeight = 4.0f; } m_fStopApproachDistance = 0.0f; ASSERT(m_fStopApproachDistanceStretchModel(FLOAT3D(FREAK_SIZE, FREAK_SIZE, FREAK_SIZE)); ModelChangeNotify(); m_bAttacking = FALSE; // continue behavior in base class jump CEnemyRunInto::MainLoop(); }; };