// TO-DO: // - On GibMonster() use snakegibs.mdl namespace HLWanted_Snake { const int SNAKE_FLINCH_DELAY = 2; const int SNAKE_HEALTH = 15; const int SNAKE_BITE_DMG = 25; const int SNAKE_AE_ATTACK_RIGHT = 0x01; const int TASKSTATUS_RUNNING = 1; const array pAttackSounds = { "wanted/snake/snake_bite1.wav", "wanted/snake/snake_bite2.wav" }; const array pIdleSounds = { "wanted/snake/snake_idle1.wav", "wanted/snake/snake_idle2.wav" }; const array pPainSounds = { "wanted/snake/snake_pain1.wav", "wanted/snake/snake_pain2.wav" }; class monster_snake : CBaseCustomMonster { float m_flNextFlinch; void Spawn() { Precache(); if( !self.SetupModel() ) g_EntityFuncs.SetModel( self, "models/wanted/snake.mdl" ); //g_EntityFuncs.SetSize( self.pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX ); g_EntityFuncs.SetSize( self.pev, Vector(-12, -12, 0), Vector(12, 12, 24) ); self.pev.solid = SOLID_SLIDEBOX; self.pev.movetype = MOVETYPE_STEP; self.m_bloodColor = BLOOD_COLOR_RED; self.pev.health = SNAKE_HEALTH; self.pev.max_health = SNAKE_HEALTH; self.m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) self.m_MonsterState = MONSTERSTATE_NONE; self.m_afCapability = bits_CAP_DOORS_GROUP; g_EntityFuncs.DispatchKeyValue( self.edict(), "displayname", "Snake" ); self.MonsterInit(); self.pev.view_ofs = Vector( 0, 0, 20 ); // position of the eyes relative to monster's origin. } void Precache() { uint i; if( string( self.pev.model ).IsEmpty() ) { g_Game.PrecacheModel( "models/wanted/snake.mdl" ); g_Game.PrecacheModel( "models/wanted/snakegibs.mdl" ); } for( i = 0; i < pAttackSounds.length(); i++ ) { g_SoundSystem.PrecacheSound( pAttackSounds[i] ); g_Game.PrecacheGeneric( "sound/" + pAttackSounds[i] ); } for( i = 0; i < pIdleSounds.length(); i++ ) { g_SoundSystem.PrecacheSound( pIdleSounds[i] ); g_Game.PrecacheGeneric( "sound/" + pIdleSounds[i] ); } for( i = 0; i < pPainSounds.length(); i++ ) { g_SoundSystem.PrecacheSound( pPainSounds[i] ); g_Game.PrecacheGeneric( "sound/" + pPainSounds[i] ); } g_SoundSystem.PrecacheSound( "common/bodysplat.wav" ); } int IRelationship( CBaseEntity@ pTarget ) { if( pTarget.pev.classname == "monster_chicken" ) { return R_DL; } return self.IRelationship( pTarget ); } //========================================================= // SetYawSpeed - allows each sequence to have a different // turn rate associated with it. //========================================================= void SetYawSpeed() { int ys = 120; self.pev.yaw_speed = ys; } int Classify() { return CLASS_ALIEN_MONSTER; } bool CheckMeleeAttack1( float flDot, float flDist ) { if( flDist <= 70 && 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; } //========================================================= // HandleAnimEvent - catches the monster-specific messages // that occur when tagged animation frames are played. //========================================================= void HandleAnimEvent( MonsterEvent@ pEvent ) { switch( pEvent.event ) { case SNAKE_AE_ATTACK_RIGHT: { // do stuff for this event. CBaseEntity@ pHurt = CheckTraceHullAttack( self, 70, SNAKE_BITE_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; } } // Play a random attack sound g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_WEAPON, pAttackSounds[Math.RandomLong(0,(pAttackSounds.length() - 1))], 1.0, ATTN_NORM, 0, 100 + Math.RandomLong(-5,5) ); } break; default: BaseClass.HandleAnimEvent( pEvent ); break; } } int TakeDamage( entvars_t@ pevInflictor, entvars_t@ pevAttacker, float flDamage, int bitsDamageType ) { if( pevAttacker is null ) return 0; CBaseEntity@ pAttacker = g_EntityFuncs.Instance( pevAttacker ); if( self.CheckAttacker( pAttacker ) ) return 0; if( self.IsAlive() ) { PainSound(); if( pevAttacker.classname == "player" ) { float points = Math.min(flDamage, pev.health)*0.05f; pevAttacker.frags += points; } } return BaseClass.TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); } /*void Killed( entvars_t@ pevAttacker, int iGib ) { BaseClass.Killed( pevAttacker, GIB_ALWAYS ); }*/ /*void GibMonster() { g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_WEAPON, "common/bodysplat.wav", 1.0f, ATTN_NORM ); //CGib@ pGib = null; int cGibs = 4; int GIB_COUNT = 3; for( int cSplat = 0; cSplat < cGibs; cSplat++ ) { //@pGib = g_EntityFuncs.CreateGib( pev.origin, g_vecZero ); CBaseEntity@ pGib = g_EntityFuncs.CreateEntity( "gib", null, false ); CGib@ iGib = cast(pGib); g_EntityFuncs.DispatchSpawn( pGib.edict() ); g_EntityFuncs.SetModel( pGib, "models/wanted/snakegibs.mdl" ); pGib.pev.body = Math.RandomLong(0, GIB_COUNT-1); // spawn the gib somewhere in the monster's bounding volume pGib.pev.origin.x = pev.absmin.x + pev.size.x * (Math.RandomFloat( 0, 1 ) ); pGib.pev.origin.y = pev.absmin.y + pev.size.y * (Math.RandomFloat( 0, 1 ) ); pGib.pev.origin.z = pev.absmin.z + pev.size.z * (Math.RandomFloat( 0, 1 ) ) + 1; // mix in some noise pGib.pev.velocity.x += Math.RandomFloat( -0.25, 0.25 ); pGib.pev.velocity.y += Math.RandomFloat( -0.25, 0.25 ); pGib.pev.velocity.z += Math.RandomFloat( -0.25, 0.25 ); pGib.pev.velocity = pGib.pev.velocity * Math.RandomFloat( 300, 400 ); pGib.pev.avelocity.x = Math.RandomFloat( 100, 200 ); pGib.pev.avelocity.y = Math.RandomFloat( 100, 300 ); // copy owner's blood color iGib.m_bloodColor = self.BloodColor(); if( pev.health > -50) { pGib.pev.velocity = pGib.pev.velocity * 0.7; } else if( pev.health > -200) { pGib.pev.velocity = pGib.pev.velocity * 2; } else { pGib.pev.velocity = pGib.pev.velocity * 4; } pGib.pev.solid = SOLID_BBOX; g_EntityFuncs.SetSize( pGib.pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); iGib.LimitVelocity(); iGib.SetThink( ThinkFunction( iGib.Think ) ); pGib.pev.nextthink = g_Engine.time + 15.0f; } }*/ void PainSound() { int pitch = 95 + Math.RandomLong(0,9); if( Math.RandomLong(0,5) < 2 ) g_SoundSystem.EmitSoundDyn( self.edict(), CHAN_VOICE, pPainSounds[Math.RandomLong(0,(pPainSounds.length() - 1))], 1.0, ATTN_NORM, 0, pitch ); } 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 RunAI( void ) { if ( ( self.m_MonsterState == MONSTERSTATE_IDLE || self.m_MonsterState == MONSTERSTATE_ALERT ) && Math.RandomLong(0,99) == 0 && (pev.spawnflags & SF_MONSTER_GAG) == 0 ) IdleSound(); BaseClass.RunAI(); } //========================================================= // AI Schedules Specific to this monster //========================================================= /*int IgnoreConditions() { int iIgnore = BaseIgnoreConditions(); if( (self.m_Activity == ACT_MELEE_ATTACK1) || (self.m_Activity == ACT_MELEE_ATTACK1) ) { if( m_flNextFlinch >= g_Engine.time ) iIgnore |= (bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE); } if( (self.m_Activity == ACT_SMALL_FLINCH) || (self.m_Activity == ACT_BIG_FLINCH) ) { if( m_flNextFlinch < g_Engine.time ) m_flNextFlinch = g_Engine.time + SNAKE_FLINCH_DELAY; } return iIgnore; } int BaseIgnoreConditions() { int iIgnoreConditions = 0; if( !self.FShouldEat() ) { // not hungry? Ignore food smell. iIgnoreConditions |= bits_COND_SMELL_FOOD; } if( self.m_MonsterState == MONSTERSTATE_SCRIPT && self.m_pCine !is null ) iIgnoreConditions |= self.m_pCine.IgnoreConditions(); return iIgnoreConditions; }*/ 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; } } string GetSnakeName() { return "monster_snake"; } void Register() { g_CustomEntityFuncs.RegisterCustomEntity( "HLWanted_Snake::monster_snake", GetSnakeName() ); } } //namespace HLWanted_Snake END