// TO-DO: // - Minus points (pev.frags) in TakeDamage() // - Don't take damage from entities (scorpion and beartrap e.g.) with player as their owner namespace HLWanted_Horse { const int HORSE_HEALTH = 110; const int HORSE_KICK_DMG = 15; const float HORSE_NEXT_ATTACK_DELAY = 1.3; const array pIdleSounds = { "wanted/horse/horse1.wav", "wanted/horse/horse3.wav", "wanted/horse/horse6.wav", "wanted/horse/horse7.wav", "wanted/horse/horse8.wav", "wanted/horse/horse9.wav" }; const array pAttackSounds = { "wanted/horse/horse2.wav", "wanted/horse/horse4.wav", "wanted/horse/horse5.wav" }; class monster_horse : CBaseCustomMonster { private float m_flNextAttackTime; private float m_flNextPainTime; int ISoundMask( void ) { return bits_SOUND_WORLD; } int Classify( void ) { return self.GetClassification( CLASS_NONE ); } int IRelationship( CBaseEntity@ pTarget ) { if( pTarget.pev.classname == "monster_bear" || pTarget.pev.classname == "monster_snake" || pTarget.pev.classname == "monster_puma" ) { return R_FR; } return self.IRelationship( pTarget ); } void SetYawSpeed( void ) { int ys; ys = 120; pev.yaw_speed = ys; } bool CheckMeleeAttack1( float flDot, float flDist ) { if( flDist <= 80 && flDot >= 0.7 && /*self.m_hEnemy.GetEntity() !is null &&*/ self.pev.FlagBitSet( FL_ONGROUND ) ) { return true; } return false; } bool CheckMeleeAttack2( float flDot, float flDist ) { return false; } bool CheckRangeAttack1( float flDot, float flDist ) { return false; } bool CheckRangeAttack2( float flDot, float flDist ) { return false; } void Spawn() { Precache( ); if( !self.SetupModel() ) g_EntityFuncs.SetModel( self, "models/wanted/horse.mdl" ); g_EntityFuncs.SetSize( self.pev, Vector( -42, -16, 0 ), Vector( 16, 16, 60 ) ); self.pev.solid = SOLID_SLIDEBOX; self.pev.movetype = MOVETYPE_STEP; self.m_bloodColor = BLOOD_COLOR_RED; self.pev.effects = 0; self.pev.health = HORSE_HEALTH; self.m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) self.m_MonsterState = MONSTERSTATE_NONE; m_flNextAttackTime = g_Engine.time; m_flNextPainTime = g_Engine.time; g_EntityFuncs.DispatchKeyValue( self.edict(), "displayname", "Horse" ); self.MonsterInit(); self.SetActivity( ACT_IDLE ); self.pev.view_ofs = Vector ( 0, 0, 1 );// position of the eyes relative to monster's origin. self.pev.takedamage = DAMAGE_YES; self.pev.nextthink = g_Engine.time + 0.1; } void Precache() { g_Game.PrecacheModel("models/wanted/horse.mdl"); g_Game.PrecacheModel("models/wanted/horsegibs.mdl"); uint i; for( i = 0; i < pIdleSounds.length(); i++ ) { g_SoundSystem.PrecacheSound( pIdleSounds[i] ); g_Game.PrecacheGeneric( "sound/" + pIdleSounds[i] ); } for( i = 0; i < pAttackSounds.length(); i++ ) { g_SoundSystem.PrecacheSound( pAttackSounds[i] ); g_Game.PrecacheGeneric( "sound/" + pAttackSounds[i] ); } g_SoundSystem.PrecacheSound( "wanted/horse/pain.wav" ); g_Game.PrecacheGeneric( "sound/" + "wanted/horse/pain.wav" ); } void Think( void ) { float flInterval = self.StudioFrameAdvance(); pev.nextthink = g_Engine.time + 0.1; self.DispatchAnimEvents( flInterval ); switch( self.m_Activity ) { case ACT_MELEE_ATTACK1: if( self.m_fSequenceFinished ) { self.SetActivity( ACT_IDLE ); } break; case ACT_IDLE: if( self.m_fSequenceFinished ) { self.SetActivity( ACT_IDLE ); if( self.IsAlive() ) { if( Math.RandomLong(0,1) == 1 ) IdleSound(); } } break; } BaseClass.Think(); } int TakeDamage( entvars_t@ pevInflictor, entvars_t@ pevAttacker, float flDamage, int bitsDamageType ) { int g_npckill = int( g_EngineFuncs.CVarGetFloat( "mp_npckill" ) ); if( pevAttacker.ClassNameIs("player") && (g_npckill == 0 || g_npckill == 2) ) return 0; if( self.IsAlive() ) { PainSound(); Attack(); if( pevAttacker.classname == "player" ) { float points = Math.min(flDamage, pev.health)*0.05f; pevAttacker.frags -= points; } } return BaseClass.TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); } void Attack( void ) { if( m_flNextAttackTime > g_Engine.time ) return; if( self.m_Activity == ACT_IDLE ) { self.SetActivity( ACT_MELEE_ATTACK1 ); CBaseEntity@ pHurt = CheckTraceHullAttack( self, 80, HORSE_KICK_DMG, DMG_SLASH ); if( pHurt !is null ) { if( (pHurt.pev.flags & (FL_MONSTER|FL_CLIENT)) == 1 ) { pHurt.pev.punchangle.z = -18; pHurt.pev.punchangle.x = 5; pHurt.pev.velocity = pHurt.pev.velocity - g_Engine.v_right * 100; } } //if( Math.RandomLong(0,1) == 1 ) AttackSound(); } m_flNextAttackTime = g_Engine.time + HORSE_NEXT_ATTACK_DELAY; } void Killed( entvars_t@ pevAttacker, int iGib ) { GetSoundEntInstance().InsertSound( bits_SOUND_WORLD, pev.origin, 128, 1, self ); pev.deadflag = DEAD_DYING; self.FCheckAITrigger(); pev.health = 0; pev.solid = SOLID_NOT; pev.takedamage = DAMAGE_NO; pev.velocity = g_vecZero; self.SetActivity( ACT_DIESIMPLE ); self.SetBoneController( 0, 0 ); self.StudioFrameAdvance( 0.1 ); SetTouch( null ); SetThink( ThinkFunction( DyingThink ) ); pev.nextthink = g_Engine.time + 0.1; } void DyingThink() { pev.nextthink = g_Engine.time + 0.1; float flInterval = self.StudioFrameAdvance( 0.1 ); self.DispatchAnimEvents( flInterval ); if( self.m_fSequenceFinished ) { // death anim finished. self.StopAnimation(); pev.deadflag = DEAD_DEAD; if( self.pev.spawnflags & SF_MONSTER_FADECORPSE == 0 ) { SetThink( ThinkFunction( FadeOut ) ); pev.nextthink = g_Engine.time + 15.0f; // fade out corpse after 15 sec } else // has "Don't Fade Corpse" bitset { SetThink( null ); return; } } } void IdleSound() { // Play a random idle sound g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, pIdleSounds[Math.RandomLong(0,(pIdleSounds.length() - 1))], 1.0, ATTN_NORM, 0, 100 + Math.RandomLong(-5,5) ); } void AttackSound() { // Play a random attack sound g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, pAttackSounds[Math.RandomLong(0,(pAttackSounds.length() - 1))], 1.0, ATTN_NORM, 0, 100 + Math.RandomLong(-5,5) ); } void PainSound() { if( m_flNextPainTime > g_Engine.time ) return; int pitch = 95 + Math.RandomLong(0,9); if( Math.RandomLong(0,5) < 2 ) g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, "wanted/horse/pain.wav", 1.0, ATTN_NORM, 0, pitch ); m_flNextPainTime = g_Engine.time + 0.5; } CBaseEntity@ CheckTraceHullAttack( CBaseMonster@ pThis, float flDist, int iDamage, int iDmgType ) { TraceResult tr; if( pThis.IsPlayer() ) Math.MakeVectors( pThis.pev.angles ); else Math.MakeAimVectors( pThis.pev.angles ); Vector vecStart = self.pev.origin; //vecStart.z += self.pev.size.z * 0.5; vecStart.z += 64.0f; Vector vecEnd = vecStart + (g_Engine.v_forward * flDist ) - (g_Engine.v_up * flDist * 0.3); g_Utility.TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, pThis.edict(), tr ); if( tr.pHit !is null ) { CBaseEntity@ pEntity = g_EntityFuncs.Instance( tr.pHit ); if( iDamage > 0 ) { pEntity.TakeDamage( pThis.pev, pThis.pev, iDamage, iDmgType ); } return pEntity; } return null; } void DestroyThink( void ) { self.SUB_Remove(); } void FadeOut() { pev.nextthink = g_Engine.time + 0.1; if( pev.rendermode == kRenderNormal ) { pev.renderamt = 255; pev.rendermode = kRenderTransTexture; } if( pev.renderamt > 7 ) { pev.renderamt -= 7; } else { pev.renderamt = 0; pev.nextthink = g_Engine.time + 0.1f; SetThink( ThinkFunction( DestroyThink ) ); } } } void Register() { g_CustomEntityFuncs.RegisterCustomEntity( "HLWanted_Horse::monster_horse", "monster_horse" ); } } //namespace HLWanted_Horse END