#include "cbase.h" #include "asw_simple_grub.h" #include "asw_trace_filter_melee.h" #include "te_effect_dispatch.h" #include "asw_util_shared.h" #include "asw_marine.h" #include "asw_marine_resource.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define SWARM_GRUB_MODEL "models/swarm/Grubs/Grub.mdl" LINK_ENTITY_TO_CLASS( asw_grub, CASW_Simple_Grub ); PRECACHE_REGISTER( asw_grub ); BEGIN_DATADESC( CASW_Simple_Grub ) DEFINE_ENTITYFUNC(GrubTouch), END_DATADESC() extern ConVar asw_debug_simple_alien; //IMPLEMENT_SERVERCLASS_ST(CASW_Simple_Grub, DT_ASW_Simple_Drone) //END_SEND_TABLE() // ========================================= // Creation // ========================================= CASW_Simple_Grub::CASW_Simple_Grub() { m_bSkipGravity = false; } CASW_Simple_Grub::~CASW_Simple_Grub() { } void CASW_Simple_Grub::Spawn(void) { BaseClass::Spawn(); SetCollisionGroup( ASW_COLLISION_GROUP_GRUBS ); SetModel( SWARM_GRUB_MODEL ); SetHullType( HULL_TINY ); AddSolidFlags(FSOLID_TRIGGER); UTIL_SetSize(this, Vector(-12,-12, 0), Vector(12, 12, 12)); SetTouch( &CASW_Simple_Grub::GrubTouch ); m_iHealth = 1; SetBlocksLOS(false); } void CASW_Simple_Grub::GrubTouch( CBaseEntity *pOther ) { if (!pOther) return; if (pOther->Classify() == CLASS_ASW_MARINE && pOther->GetAbsVelocity().Length() > 100) { Vector xydiff = GetAbsOrigin() - pOther->GetAbsOrigin(); xydiff.z = 0; if (xydiff.Length() < 20) { CTakeDamageInfo dmg(pOther, pOther, Vector(0, 0, -1), GetAbsOrigin() + Vector(0, 0, 1), 20, DMG_CRUSH); TakeDamage(dmg); } } } void CASW_Simple_Grub::Precache() { BaseClass::Precache(); PrecacheModel( SWARM_GRUB_MODEL ); PrecacheModel( "Models/Swarm/Grubs/GrubGib4.mdl" ); PrecacheScriptSound("ASW_Grub.Pain"); PrecacheScriptSound("ASW_Parasite.Attack"); PrecacheScriptSound("ASW_Parasite.Idle"); PrecacheParticleSystem( "grub_death" ); PrecacheParticleSystem( "grub_death_fire" ); } // ========================================= // Anims // ========================================= void CASW_Simple_Grub::PlayRunningAnimation() { ResetSequence(LookupSequence("CrawlA")); m_flPlaybackRate = 1.25f; } void CASW_Simple_Grub::PlayFallingAnimation() { ResetSequence(LookupSequence("Jump")); m_flPlaybackRate = 1.25f; } void CASW_Simple_Grub::PlayAttackingAnimation() { ResetSequence(LookupSequence("CrawlA")); m_flPlaybackRate = 1.25f; } // ========================================= // Sounds // ========================================= void CASW_Simple_Grub::PainSound( const CTakeDamageInfo &info ) { if (gpGlobals->curtime > m_fNextPainSound) { EmitSound("ASW_Grub.Pain"); m_fNextPainSound = gpGlobals->curtime + 0.5f; } } void CASW_Simple_Grub::AlertSound() { } void CASW_Simple_Grub::DeathSound( const CTakeDamageInfo &info ) { } void CASW_Simple_Grub::AttackSound() { } // ========================================= // Gibbing // ========================================= bool CASW_Simple_Grub::ShouldGib( const CTakeDamageInfo &info ) { return true; } bool CASW_Simple_Grub::CorpseGib( const CTakeDamageInfo &info ) { CEffectData data; data.m_vOrigin = WorldSpaceCenter(); data.m_vNormal = data.m_vOrigin - info.GetDamagePosition(); VectorNormalize( data.m_vNormal ); data.m_flScale = RemapVal( m_iHealth, 0, -500, 1, 3 ); data.m_flScale = clamp( data.m_flScale, 1, 3 ); data.m_fFlags = (IsOnFire() || (info.GetDamageType() & DMG_BURN)) ? ASW_GIBFLAG_ON_FIRE : 0; CPASFilter filter( data.m_vOrigin ); filter.SetIgnorePredictionCull(true); DispatchEffect( filter, 0.0, "GrubGib", data ); //DispatchEffect( "GrubGib", data ); return true; } // ========================================= // Movement // ========================================= float CASW_Simple_Grub::GetIdealSpeed() const { return 100.0f; } void CASW_Simple_Grub::AlienThink() { float delta = gpGlobals->curtime - m_fLastThinkTime; BaseClass::AlienThink(); UpdatePitch(delta); /* if (GetEnemy() && GetAbsOrigin().DistTo(GetEnemy()->GetAbsOrigin()) < 800) { // check if a marine is standing on us trace_t tr; UTIL_TraceLine( GetAbsOrigin() - Vector(5, 5, -1), GetAbsOrigin() + Vector(5, 5, 5), MASK_SOLID, this, COLLISION_GROUP_PLAYER, &tr ); if (tr.fraction < 1 && tr.m_pEnt) { if (tr.m_pEnt->Classify() == CLASS_ASW_MARINE) { CTakeDamageInfo dmg(tr.m_pEnt, tr.m_pEnt, Vector(0, 0, -1), GetAbsOrigin() + Vector(0, 0, 1), 20, DMG_CRUSH); TakeDamage(dmg); } } // think more frequently so we don't miss a player stomping on us SetNextThink( gpGlobals->curtime + 0.025f ); }*/ } void CASW_Simple_Grub::UpdatePitch(float delta) { float current = UTIL_AngleMod( GetLocalAngles()[PITCH] ); float ideal = UTIL_AngleMod( GetIdealPitch() ); float dt = MIN( 0.2, delta ); float newPitch = ASW_ClampYaw( GetPitchSpeed(), current, ideal, dt ); if (newPitch != current) { QAngle angles = GetLocalAngles(); angles[PITCH] = newPitch; SetLocalAngles( angles ); } } float CASW_Simple_Grub::GetPitchSpeed() const { return 2000.0f; } float CASW_Simple_Grub::GetIdealPitch() { if (m_bSkipGravity) // we're climbing return -90; return 0; } float CASW_Simple_Grub::GetFaceEnemyDistance() const { return 50.0f; } float CASW_Simple_Grub::GetZigZagChaseDistance() const { return 50.0f; } bool CASW_Simple_Grub::ApplyGravity(Vector &vecTarget, float deltatime) { if (m_bSkipGravity) { if (asw_debug_simple_alien.GetBool()) Msg("Skipping grav\n"); return false; } return BaseClass::ApplyGravity(vecTarget, deltatime); } // run towards the target like a normal simple alien, but if he hit some geometry, try to climb up over it // todo: perform move could check if gravity was skipped and switch us to a climbing anim instead of a running bool CASW_Simple_Grub::TryMove(const Vector &vecSrc, Vector &vecTarget, float deltatime, bool bStepMove) { m_bSkipGravity = false; // do a trace to the dest Ray_t ray; trace_t trace; CTraceFilterSimple traceFilter(this, GetCollisionGroup() ); ray.Init( vecSrc, vecTarget, GetHullMins(), GetHullMaxs() ); enginetrace->TraceRay( ray, MASK_NPCSOLID, &traceFilter, &trace ); if (trace.startsolid) { // doh, we're stuck in something! // todo: move us to a safe spot? wait for push out phys props? if (asw_debug_simple_alien.GetBool()) Msg("CASW_Simple_Grub stuck!\n"); m_MoveFailure.trace = trace; m_MoveFailure.vecStartPos = vecSrc; m_MoveFailure.vecTargetPos = vecTarget; return false; } if (trace.fraction < 0.1f) // barely/didn't move { // try and do a 'stepped up' move to the target if (!bStepMove) { Vector vecStepSrc = vecSrc; vecStepSrc.z += 24; Vector vecStepTarget = vecTarget; vecTarget.z += 24; if (TryMove(vecStepSrc, vecStepTarget, deltatime, true)) { vecTarget = vecStepTarget; return true; } } // if we collide with geometry, try to climb up over it if (trace.m_pEnt && trace.m_pEnt->IsWorld() && GetEnemy() && !bStepMove) { // check our enemy is on the other side of this obstruction Vector vecEnemyDiff = GetEnemy()->GetAbsOrigin() - GetAbsOrigin(); vecEnemyDiff.NormalizeInPlace(); if (vecEnemyDiff.Dot(trace.plane.normal) < 0) { Vector vecClimbSrc = vecSrc; Vector vecClimbTarget = vecSrc; vecClimbTarget.z += GetIdealSpeed() * deltatime * 0.7f; // 70% speed when climbing... if (TryMove(vecClimbSrc, vecClimbTarget, deltatime, true)) { m_bSkipGravity = true; if (asw_debug_simple_alien.GetBool()) Msg("Setting skip grav, moved %f z=%f\n", GetIdealSpeed() * deltatime * 0.7f, vecClimbTarget.z); // 70% speed when climbing... vecTarget = vecClimbTarget; return true; } } } m_MoveFailure.trace = trace; m_MoveFailure.vecStartPos = vecSrc; m_MoveFailure.vecTargetPos = vecTarget; return false; } else if (trace.fraction < 1) // we hit something early, but we did move { // we hit something early m_MoveFailure.trace = trace; m_MoveFailure.vecStartPos = vecSrc; m_MoveFailure.vecTargetPos = vecTarget; vecTarget = trace.endpos; } return true; } void CASW_Simple_Grub::Event_Killed( const CTakeDamageInfo &info ) { CASW_Marine *pMarine = dynamic_cast(info.GetAttacker()); if (pMarine && pMarine->GetMarineResource()) { pMarine->GetMarineResource()->m_iGrubKills++; } BaseClass::Event_Killed(info); }