1372 lines
36 KiB
C++
1372 lines
36 KiB
C++
#include "cbase.h"
|
|
#include "npcevent.h"
|
|
#include "asw_simple_alien.h"
|
|
#include "asw_shareddefs.h"
|
|
#include "asw_util_shared.h"
|
|
#include "asw_fx_shared.h"
|
|
#include "ai_debug_shared.h"
|
|
#include "asw_spawner.h"
|
|
#include "asw_marine.h"
|
|
#include "asw_trace_filter_melee.h"
|
|
#include "te_effect_dispatch.h"
|
|
#include "basecombatcharacter.h"
|
|
#include "asw_gamerules.h"
|
|
#include "EntityFlame.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
// TODO:
|
|
/*
|
|
[ ] add in acceleration?
|
|
[ ] make the alien walk up steps
|
|
[ ] and have gravity
|
|
[ ] orders - normal movement, pathing, etc?
|
|
*/
|
|
|
|
//LINK_ENTITY_TO_CLASS( asw_simple_alien, CASW_Simple_Alien );
|
|
|
|
BEGIN_DATADESC( CASW_Simple_Alien )
|
|
DEFINE_THINKFUNC( AlienThink ),
|
|
DEFINE_THINKFUNC( SleepThink ),
|
|
|
|
DEFINE_FIELD(m_fLastThinkTime, FIELD_TIME),
|
|
DEFINE_FIELD(m_iState, FIELD_INTEGER),
|
|
DEFINE_FIELD(m_AlienOrders, FIELD_INTEGER),
|
|
DEFINE_FIELD(m_vecAlienOrderSpot, FIELD_VECTOR),
|
|
DEFINE_FIELD(m_AlienOrderObject, FIELD_EHANDLE),
|
|
DEFINE_FIELD(m_hEnemy, FIELD_EHANDLE),
|
|
DEFINE_FIELD(m_bSleeping, FIELD_BOOLEAN),
|
|
DEFINE_FIELD(m_bIgnoreMarines, FIELD_BOOLEAN),
|
|
DEFINE_FIELD(m_bFailedMoveTo, FIELD_BOOLEAN),
|
|
DEFINE_FIELD(m_bMoving, FIELD_BOOLEAN),
|
|
DEFINE_FIELD(m_vecMoveTarget, FIELD_VECTOR),
|
|
DEFINE_FIELD(m_hMoveTarget, FIELD_EHANDLE),
|
|
DEFINE_FIELD(m_fArrivedTolerance, FIELD_FLOAT),
|
|
DEFINE_FIELD(m_bAttacking, FIELD_BOOLEAN),
|
|
DEFINE_FIELD( m_bHoldoutAlien, FIELD_BOOLEAN ),
|
|
END_DATADESC()
|
|
|
|
IMPLEMENT_SERVERCLASS_ST(CASW_Simple_Alien, DT_ASW_Simple_Alien)
|
|
|
|
END_SEND_TABLE()
|
|
|
|
ConVar asw_debug_alien_damage("asw_debug_alien_damage", "0", 0, "Print damage aliens receive");
|
|
extern ConVar asw_drone_health;
|
|
extern ConVar sk_asw_drone_damage;
|
|
extern ConVar asw_drone_gib_chance;
|
|
extern ConVar ai_show_hull_attacks;
|
|
extern ConVar sv_gravity;
|
|
ConVar asw_debug_simple_alien("asw_debug_simple_alien", "0", FCVAR_CHEAT, "Print debug text for simple aliens");
|
|
extern int AE_DRONE_WALK_FOOTSTEP;
|
|
extern int AE_DRONE_FOOTSTEP_SOFT;
|
|
extern int AE_DRONE_FOOTSTEP_HEAVY;
|
|
extern int AE_DRONE_MELEE_HIT1;
|
|
extern int AE_DRONE_MELEE_HIT2;
|
|
extern int AE_DRONE_MELEE1_SOUND;
|
|
extern int AE_DRONE_MELEE2_SOUND;
|
|
extern int AE_DRONE_MOUTH_BLEED;
|
|
extern int AE_DRONE_ALERT_SOUND;
|
|
extern int AE_DRONE_SHADOW_ON;
|
|
|
|
#define ASW_DRONE_MELEE1_RANGE 100.0f
|
|
|
|
// =========================================
|
|
// Creation
|
|
// =========================================
|
|
|
|
CASW_Simple_Alien::CASW_Simple_Alien()
|
|
{
|
|
m_fNextPainSound = 0;
|
|
m_bOnGround = false;
|
|
m_bHoldoutAlien = false;
|
|
}
|
|
|
|
CASW_Simple_Alien::~CASW_Simple_Alien()
|
|
{
|
|
}
|
|
|
|
void CASW_Simple_Alien::Spawn(void)
|
|
{
|
|
BaseClass::Spawn();
|
|
Precache();
|
|
|
|
// appearance
|
|
SetModel( SWARM_NEW_DRONE_MODEL );
|
|
m_flAnimTime = gpGlobals->curtime;
|
|
SetCycle( 0 );
|
|
PlayRunningAnimation();
|
|
|
|
// collision
|
|
SetHullType( HULL_MEDIUMBIG );
|
|
UTIL_SetSize(this, Vector(-19,-19,0), Vector(19,19,69));
|
|
SetSolid( SOLID_BBOX );
|
|
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
|
SetCollisionGroup( ASW_COLLISION_GROUP_ALIEN );
|
|
|
|
// movement
|
|
SetMoveType( MOVETYPE_STEP );
|
|
m_fArrivedTolerance = 30.0f;
|
|
|
|
// health
|
|
m_iHealth = ASWGameRules()->ModifyAlienHealthBySkillLevel(asw_drone_health.GetInt());
|
|
|
|
SetMaxHealth(m_iHealth);
|
|
m_takedamage = DAMAGE_YES;
|
|
|
|
// state
|
|
SetState(ASW_SIMPLE_ALIEN_IDLING);
|
|
SetSleeping(true);
|
|
m_fLastThinkTime = gpGlobals->curtime;
|
|
SetThink( &CASW_Simple_Alien::SleepThink );
|
|
SetNextThink( gpGlobals->curtime + random->RandomFloat(0.01f, 0.2f) );
|
|
|
|
UpdateSleeping();
|
|
}
|
|
|
|
void CASW_Simple_Alien::Precache()
|
|
{
|
|
BaseClass::Precache();
|
|
|
|
PrecacheModel( SWARM_NEW_DRONE_MODEL );
|
|
}
|
|
|
|
// =========================================
|
|
// Enemy
|
|
// =========================================
|
|
|
|
void CASW_Simple_Alien::SetEnemy(CBaseEntity *pEntity)
|
|
{
|
|
if (!pEntity)
|
|
{
|
|
m_hEnemy = NULL;
|
|
return;
|
|
}
|
|
|
|
m_hEnemy = pEntity;
|
|
SetState(ASW_SIMPLE_ALIEN_ATTACKING); // attack the taffer
|
|
}
|
|
|
|
float CASW_Simple_Alien::GetZigZagChaseDistance() const
|
|
{
|
|
return 192.0f;
|
|
}
|
|
|
|
Vector& CASW_Simple_Alien::GetChaseDestination(CBaseEntity *pEnt)
|
|
{
|
|
static Vector vecDest = vec3_origin;
|
|
vecDest = pEnt->GetAbsOrigin();
|
|
|
|
if (!pEnt)
|
|
return vecDest;
|
|
|
|
Vector vecDiff = vecDest - GetAbsOrigin();
|
|
vecDiff.z = 0;
|
|
float dist = vecDiff.Length2D();
|
|
if (dist > GetZigZagChaseDistance()) // do we need to zig zag?
|
|
{
|
|
QAngle angSideways(0, UTIL_VecToYaw(vecDiff), 0);
|
|
Vector vecForward, vecRight, vecUp;
|
|
AngleVectors(angSideways, &vecForward, &vecRight, &vecUp);
|
|
|
|
vecDest = GetAbsOrigin() + vecForward * 92.0f + vecRight * (random->RandomFloat() * 144 - 72);
|
|
}
|
|
return vecDest;
|
|
}
|
|
|
|
void CASW_Simple_Alien::FindNewEnemy()
|
|
{
|
|
float dist;
|
|
CBaseEntity *pNearest = UTIL_ASW_NearestMarine(GetAbsOrigin(), dist);
|
|
if (CanSee(pNearest))
|
|
{
|
|
SetEnemy(pNearest);
|
|
}
|
|
}
|
|
|
|
bool CASW_Simple_Alien::CanSee(CBaseEntity *pEntity)
|
|
{
|
|
if (!pEntity)
|
|
return false;
|
|
|
|
Vector diff = pEntity->GetAbsOrigin() - GetAbsOrigin();
|
|
if (diff.Length2D() < 768.0f)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// =========================================
|
|
// Thinking & Decision making
|
|
// =========================================
|
|
|
|
void CASW_Simple_Alien::AlienThink()
|
|
{
|
|
float delta = gpGlobals->curtime - m_fLastThinkTime;
|
|
|
|
StudioFrameAdvance();
|
|
DispatchAnimEvents(this);
|
|
|
|
UpdateYaw(delta);
|
|
|
|
if (m_bMoving)
|
|
{
|
|
PerformMovement(delta);
|
|
|
|
SetNextThink( gpGlobals->curtime + 0.1f );
|
|
}
|
|
else
|
|
{
|
|
WhatToDoNext(delta); // this will set the next think interval based on what the alien decides to do next
|
|
}
|
|
|
|
m_fLastThinkTime = gpGlobals->curtime;
|
|
}
|
|
|
|
void CASW_Simple_Alien::WhatToDoNext(float delta)
|
|
{
|
|
if (m_iState == ASW_SIMPLE_ALIEN_ATTACKING)
|
|
{
|
|
if (!GetEnemy() || !CanSee(GetEnemy()) || GetEnemy()->GetHealth() <= 0)
|
|
{
|
|
LostEnemy(); // clears our enemy and sets us back to idling
|
|
}
|
|
else
|
|
{
|
|
// head towards enemy
|
|
SetMoveTarget(GetChaseDestination(GetEnemy()));
|
|
|
|
PerformMovement(delta);
|
|
}
|
|
|
|
SetNextThink( gpGlobals->curtime + 0.1f );
|
|
}
|
|
else if (m_iState == ASW_SIMPLE_ALIEN_IDLING)
|
|
{
|
|
// look for a new enemy?
|
|
FindNewEnemy();
|
|
|
|
if (GetEnemy())
|
|
SetNextThink( gpGlobals->curtime + 0.1f );
|
|
else
|
|
SetNextThink( gpGlobals->curtime + 0.5f );
|
|
}
|
|
else if (m_iState == ASW_SIMPLE_ALIEN_DEAD)
|
|
{
|
|
// don't need to think again
|
|
}
|
|
|
|
// todo: if in moving to dest state, then move towards that dest, etc.
|
|
}
|
|
|
|
void CASW_Simple_Alien::LostEnemy()
|
|
{
|
|
SetEnemy(NULL);
|
|
SetState(ASW_SIMPLE_ALIEN_IDLING);
|
|
}
|
|
|
|
void CASW_Simple_Alien::SetState(int iNewState)
|
|
{
|
|
if (iNewState == m_iState)
|
|
return;
|
|
|
|
m_iState = iNewState;
|
|
}
|
|
|
|
// =========================================
|
|
// Movement
|
|
// =========================================
|
|
|
|
void CASW_Simple_Alien::SetMoveTarget(Vector &vecTarget)
|
|
{
|
|
m_vecMoveTarget = vecTarget;
|
|
m_hMoveTarget = NULL;
|
|
m_bMoving = true;
|
|
}
|
|
|
|
float CASW_Simple_Alien::GetIdealSpeed() const
|
|
{
|
|
return 300.0f;
|
|
}
|
|
|
|
float CASW_Simple_Alien::GetYawSpeed() const
|
|
{
|
|
return 360.0f;
|
|
}
|
|
|
|
void CASW_Simple_Alien::UpdateYaw(float delta)
|
|
{
|
|
// don't allow turning in the air
|
|
if (!m_bOnGround)
|
|
return;
|
|
float current = UTIL_AngleMod( GetLocalAngles().y );
|
|
float ideal = UTIL_AngleMod( GetIdealYaw() );
|
|
|
|
float dt = MIN( 0.2, delta );
|
|
float newYaw = ASW_ClampYaw( GetYawSpeed(), current, ideal, dt );
|
|
|
|
if (newYaw != current)
|
|
{
|
|
QAngle angles = GetLocalAngles();
|
|
angles.y = newYaw;
|
|
SetLocalAngles( angles );
|
|
}
|
|
}
|
|
|
|
float CASW_Simple_Alien::GetFaceEnemyDistance() const
|
|
{
|
|
return 140.0f;
|
|
}
|
|
|
|
float CASW_Simple_Alien::GetIdealYaw()
|
|
{
|
|
if (m_bMoving)
|
|
{
|
|
if (m_iState == ASW_SIMPLE_ALIEN_ATTACKING && GetEnemy()
|
|
&& GetEnemy()->GetAbsOrigin().DistTo(GetAbsOrigin()) < GetFaceEnemyDistance())
|
|
{
|
|
return UTIL_VecToYaw(GetEnemy()->GetAbsOrigin() - GetAbsOrigin());
|
|
}
|
|
else
|
|
{
|
|
if (m_hMoveTarget.Get())
|
|
{
|
|
m_vecMoveTarget = m_hMoveTarget->GetAbsOrigin();
|
|
}
|
|
return UTIL_VecToYaw(m_vecMoveTarget - GetAbsOrigin());
|
|
}
|
|
}
|
|
|
|
return GetAbsAngles()[YAW];
|
|
}
|
|
|
|
bool CASW_Simple_Alien::PerformMovement(float deltatime)
|
|
{
|
|
if (!m_bMoving)
|
|
return false;
|
|
|
|
if (m_hMoveTarget.Get()) // if we're moving to a specific object, update our target vector with its current location
|
|
{
|
|
m_vecMoveTarget = m_hMoveTarget->GetAbsOrigin();
|
|
}
|
|
Vector vecStartDiff = m_vecMoveTarget - GetAbsOrigin();
|
|
vecStartDiff.z = 0;
|
|
|
|
// check we're not there already
|
|
if (vecStartDiff.Length2D() <= m_fArrivedTolerance)
|
|
{
|
|
FinishedMovement();
|
|
return true;
|
|
}
|
|
|
|
// work out the new position we want to be in
|
|
Vector vecDir = vecStartDiff;
|
|
vecDir.NormalizeInPlace();
|
|
//Msg("moving with delta %f\n", delta);
|
|
Vector vecNewPos = GetAbsOrigin() + vecDir * GetIdealSpeed() * deltatime;
|
|
if (TryMove(GetAbsOrigin(), vecNewPos, deltatime))
|
|
{
|
|
// apply gravity to our new position
|
|
ApplyGravity(vecNewPos, deltatime);
|
|
|
|
// we moved (at least some portion, maybe all)
|
|
UTIL_SetOrigin(this, vecNewPos);
|
|
|
|
// check if we're close enough, or have gone past, the move target
|
|
Vector vecEndDiff = m_vecMoveTarget - GetAbsOrigin();
|
|
vecEndDiff.z = 0;
|
|
if (vecStartDiff.Dot(vecEndDiff) < 0 || vecEndDiff.Length2D() <= m_fArrivedTolerance)
|
|
{
|
|
// we've arrived
|
|
FinishedMovement();
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
vecNewPos = GetAbsOrigin();
|
|
if (ApplyGravity(vecNewPos, deltatime)) // if we failed to move forward, make sure gravity is still applied
|
|
{
|
|
// we moved (at least some portion, maybe all)
|
|
UTIL_SetOrigin(this, vecNewPos);
|
|
}
|
|
}
|
|
|
|
FailedMove();
|
|
return false;
|
|
}
|
|
|
|
bool CASW_Simple_Alien::TryMove(const Vector &vecSrc, Vector &vecTarget, float deltatime, bool bStepMove)
|
|
{
|
|
// 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_Alien 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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// drops us down from the specified position, to the floor
|
|
bool CASW_Simple_Alien::ApplyGravity(Vector &vecSrc, float deltatime)
|
|
{
|
|
// decide if we're on the ground or not
|
|
Ray_t ray;
|
|
trace_t trace;
|
|
CTraceFilterSimple traceFilter(this, GetCollisionGroup() );
|
|
ray.Init( vecSrc, vecSrc - Vector(0,0,2), GetHullMins(), GetHullMaxs() );
|
|
enginetrace->TraceRay( ray, MASK_NPCSOLID, &traceFilter, &trace );
|
|
|
|
m_bOnGround = (trace.fraction < 1.0f);
|
|
|
|
// if we're on the ground, just drop us down as much as we can
|
|
if (m_bOnGround)
|
|
{
|
|
Vector vecGravityTarget = vecSrc;
|
|
vecGravityTarget.z -= sv_gravity.GetFloat() * deltatime;
|
|
// do a trace to the floor
|
|
Ray_t ray;
|
|
trace_t trace;
|
|
CTraceFilterSimple traceFilter(this, GetCollisionGroup() );
|
|
ray.Init( vecSrc, vecGravityTarget, GetHullMins(), GetHullMaxs() );
|
|
enginetrace->TraceRay( ray, MASK_NPCSOLID, &traceFilter, &trace );
|
|
|
|
if (trace.fraction > 0 && fabs(trace.endpos.z - vecSrc.z) > 1) // if we moved up/down
|
|
{
|
|
vecSrc = trace.endpos;
|
|
|
|
return true;
|
|
}
|
|
m_fFallSpeed = 0; // clear fall speed if we can't fall any further
|
|
|
|
return false;
|
|
}
|
|
|
|
// we're falling, so apply the fall speed and increase the fall speed over time
|
|
m_fFallSpeed -= sv_gravity.GetFloat() * 1.5f * deltatime;
|
|
|
|
Vector vecGravityTarget = vecSrc;
|
|
vecGravityTarget.z += m_fFallSpeed * deltatime;
|
|
|
|
// do a trace to the floor
|
|
Ray_t ray2;
|
|
trace_t trace2;
|
|
CTraceFilterSimple traceFilter2(this, GetCollisionGroup() );
|
|
ray2.Init( vecSrc, vecGravityTarget, GetHullMins(), GetHullMaxs() );
|
|
enginetrace->TraceRay( ray2, MASK_NPCSOLID, &traceFilter2, &trace2 );
|
|
|
|
if (trace2.fraction > 0 && fabs(trace2.endpos.z - vecSrc.z) > 1) // if we moved up/down
|
|
{
|
|
vecSrc = trace2.endpos;
|
|
return true;
|
|
}
|
|
m_fFallSpeed = 0; // clear fall speed if we can't fall any further
|
|
return false;
|
|
}
|
|
|
|
// we failed to move this interval
|
|
// returns true if we lay in a new move target
|
|
bool CASW_Simple_Alien::FailedMove()
|
|
{
|
|
m_bMoving = false;
|
|
m_vecMoveTarget = GetAbsOrigin();
|
|
|
|
// we've hit something
|
|
if (m_MoveFailure.trace.m_pEnt)
|
|
{
|
|
Vector vecNormal = m_MoveFailure.trace.plane.normal;
|
|
// todo: if we've hit a marine, hurt him?
|
|
CASW_Marine *pMarine = dynamic_cast<CASW_Marine*>(m_MoveFailure.trace.m_pEnt);
|
|
if (pMarine)
|
|
{
|
|
// it's okay to be stuck on a marine, since that means we're probably clawing him to bits!
|
|
return false;
|
|
}
|
|
|
|
// if we've hit another alien, set our normal as though it were a cylinder collision, to get a more accurate avoidance
|
|
// note: won't work unless the actual movement of the alien is done with a cylinder too
|
|
//CASW_Simple_Alien *pAlien = dynamic_cast<CASW_Simple_Alien*>(m_MoveFailure.trace.m_pEnt);
|
|
//if (pAlien)
|
|
//{
|
|
//vecNormal = pAlien->GetAbsOrigin() - GetAbsOrigin();
|
|
//vecNormal.NormalizeInPlace();
|
|
//}
|
|
|
|
// otherwise, assume we've hit some prop or geometry
|
|
|
|
// if we have no enemy, just randomly move away from the wall
|
|
if (!GetEnemy())
|
|
{
|
|
SetMoveTarget(PickRandomDestination(32.0f, 16.0f * vecNormal));
|
|
return true;
|
|
}
|
|
|
|
// pick a random distance to move away from the wall
|
|
float fDist = 56.25f;
|
|
float fDistPick = random->RandomFloat();
|
|
if (fDistPick > 0.8f)
|
|
fDist = 187.5f;
|
|
else if (fDistPick > 0.4f)
|
|
fDist = 112.5f;
|
|
|
|
// find vectors for the two possible directions
|
|
float wall_yaw = UTIL_VecToYaw(vecNormal);
|
|
float wall_yaw_left = wall_yaw - 90.0f;
|
|
float wall_yaw_right = wall_yaw + 90.0f;
|
|
|
|
// which one takes us closer to the enemy?
|
|
float enemy_yaw = UTIL_VecToYaw(GetEnemy()->GetAbsOrigin() - GetAbsOrigin());
|
|
if (fabs(UTIL_AngleDiff(enemy_yaw, wall_yaw_left)) < fabs(UTIL_AngleDiff(enemy_yaw, wall_yaw_right)))
|
|
{
|
|
// go left
|
|
Vector vecLeft;
|
|
AngleVectors(QAngle(0, wall_yaw_left, 0), &vecLeft);
|
|
SetMoveTarget(GetAbsOrigin() + vecLeft * fDist);
|
|
}
|
|
else
|
|
{
|
|
// go right
|
|
Vector vecRight;
|
|
AngleVectors(QAngle(0, wall_yaw_right, 0), &vecRight);
|
|
SetMoveTarget(GetAbsOrigin() + vecRight * fDist);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CASW_Simple_Alien::FinishedMovement()
|
|
{
|
|
m_bMoving = false;
|
|
m_vecMoveTarget = GetAbsOrigin();
|
|
}
|
|
|
|
Vector CASW_Simple_Alien::PickRandomDestination(float dist, Vector bias)
|
|
{
|
|
Vector vecRandom = RandomVector(-1, 1);
|
|
vecRandom.z = 0;
|
|
return GetAbsOrigin() + vecRandom * dist + bias;
|
|
}
|
|
|
|
// =========================================
|
|
// Sleeping
|
|
// =========================================
|
|
|
|
void CASW_Simple_Alien::SleepThink()
|
|
{
|
|
UpdateSleeping(); // see if we should wake up
|
|
|
|
// todo: increase this interval if the nearest marine is a long way away?
|
|
if (m_bSleeping)
|
|
{
|
|
float interval = random->RandomFloat() + 1.0f;
|
|
SetNextThink( gpGlobals->curtime + interval );
|
|
}
|
|
|
|
m_fLastThinkTime = gpGlobals->curtime;
|
|
}
|
|
|
|
void CASW_Simple_Alien::UpdateSleeping()
|
|
{
|
|
if (m_bSleeping) // asleep: wake up if we can be seen
|
|
{
|
|
bool bCorpseCanSee = false;
|
|
bool bCanBeSeen = (UTIL_ASW_AnyMarineCanSee(GetAbsOrigin(), 384.0f, bCorpseCanSee) != NULL) || bCorpseCanSee;
|
|
SetSleeping(!bCanBeSeen);
|
|
}
|
|
else // awake: if have no orders, no enemy and can't be seen, sleep
|
|
{
|
|
if (GetEnemy() == NULL && GetAlienOrders() == AOT_None)
|
|
{
|
|
bool bCorpseCanSee = false;
|
|
bool bCanBeSeen = (UTIL_ASW_AnyMarineCanSee(GetAbsOrigin(), 384.0f, bCorpseCanSee) != NULL) || bCorpseCanSee;
|
|
SetSleeping(!bCanBeSeen);
|
|
}
|
|
}
|
|
}
|
|
|
|
// todo: turn off collision when asleep?
|
|
void CASW_Simple_Alien::SetSleeping(bool bAsleep)
|
|
{
|
|
if (bAsleep == m_bSleeping)
|
|
return;
|
|
|
|
m_bSleeping = bAsleep;
|
|
if (bAsleep)
|
|
{
|
|
// go asleep
|
|
AddEffects( EF_NODRAW );
|
|
SetThink( &CASW_Simple_Alien::SleepThink );
|
|
SetNextThink( gpGlobals->curtime + 0.1f );
|
|
}
|
|
else
|
|
{
|
|
// wake up
|
|
RemoveEffects( EF_NODRAW );
|
|
SetThink( &CASW_Simple_Alien::AlienThink );
|
|
SetNextThink( gpGlobals->curtime + 0.1f );
|
|
|
|
// set our enemy to the nearest marine
|
|
FindNewEnemy();
|
|
|
|
// if we couldn't see any marines, wake up, but just idle
|
|
if (!GetEnemy())
|
|
{
|
|
SetState(ASW_SIMPLE_ALIEN_IDLING);
|
|
}
|
|
else
|
|
{
|
|
AlertSound();
|
|
}
|
|
}
|
|
}
|
|
|
|
// =========================================
|
|
// Attacking
|
|
// =========================================
|
|
|
|
void CASW_Simple_Alien::ReachedEndOfSequence()
|
|
{
|
|
bool bShouldAttack = ShouldAttack();
|
|
if (bShouldAttack != m_bAttacking) // make sure we're playing the right sequence for running/attacking
|
|
{
|
|
m_bAttacking = bShouldAttack;
|
|
if (!m_bOnGround)
|
|
PlayFallingAnimation();
|
|
else if (m_bAttacking)
|
|
PlayAttackingAnimation();
|
|
else
|
|
PlayRunningAnimation();
|
|
|
|
m_flPlaybackRate = 1.25f;
|
|
}
|
|
else
|
|
{
|
|
if (!m_bOnGround)
|
|
PlayFallingAnimation();
|
|
else
|
|
PlayRunningAnimation();
|
|
}
|
|
|
|
}
|
|
|
|
bool CASW_Simple_Alien::ShouldAttack()
|
|
{
|
|
if (!GetEnemy() || GetEnemy()->GetHealth() <= 0)
|
|
return false;
|
|
|
|
float dist = GetEnemy()->GetAbsOrigin().DistTo(GetAbsOrigin());
|
|
return (dist < 100.0f);
|
|
}
|
|
|
|
void CASW_Simple_Alien::MeleeAttack( float distance, float damage, QAngle &viewPunch, Vector &shove )
|
|
{
|
|
Vector vecForceDir;
|
|
|
|
// Always hurt bullseyes for now
|
|
if ( ( GetEnemy() != NULL ) && ( GetEnemy()->Classify() == CLASS_BULLSEYE ) )
|
|
{
|
|
vecForceDir = (GetEnemy()->GetAbsOrigin() - GetAbsOrigin());
|
|
CTakeDamageInfo info( this, this, damage, DMG_SLASH );
|
|
CalculateMeleeDamageForce( &info, vecForceDir, GetEnemy()->GetAbsOrigin() );
|
|
GetEnemy()->TakeDamage( info );
|
|
return;
|
|
}
|
|
|
|
CBaseEntity *pHurt = CheckTraceHullAttack( distance, -Vector(16,16,32), Vector(16,16,32), damage, DMG_SLASH, 5.0f );
|
|
|
|
if ( pHurt )
|
|
{
|
|
vecForceDir = ( pHurt->WorldSpaceCenter() - WorldSpaceCenter() );
|
|
|
|
CBasePlayer *pPlayer = ToBasePlayer( pHurt );
|
|
|
|
if ( pPlayer != NULL )
|
|
{
|
|
//Kick the player angles
|
|
pPlayer->ViewPunch( viewPunch );
|
|
|
|
Vector dir = pHurt->GetAbsOrigin() - GetAbsOrigin();
|
|
VectorNormalize(dir);
|
|
|
|
QAngle angles;
|
|
VectorAngles( dir, angles );
|
|
Vector forward, right;
|
|
AngleVectors( angles, &forward, &right, NULL );
|
|
|
|
//Push the target back
|
|
pHurt->ApplyAbsVelocityImpulse( - right * shove[1] - forward * shove[0] );
|
|
}
|
|
|
|
// Play a random attack hit sound
|
|
AttackSound();
|
|
|
|
// bleed em
|
|
if ( UTIL_ShouldShowBlood(pHurt->BloodColor()) )
|
|
{
|
|
// Hit an NPC. Bleed them!
|
|
Vector vecBloodPos;
|
|
|
|
Vector forward, right, up;
|
|
AngleVectors( GetAbsAngles(), &forward, &right, &up );
|
|
|
|
//if( GetAttachment( "leftclaw", vecBloodPos ) )
|
|
{
|
|
//Vector diff = vecBloodPos - GetAbsOrigin();
|
|
//if (diff.z < 0)
|
|
//vecBloodPos.z = GetAbsOrigin().z - (diff.z * 2);
|
|
vecBloodPos = GetAbsOrigin() + forward * 60 - right * 14 + up * 50;
|
|
SpawnBlood( vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN( damage, 30 ) );
|
|
}
|
|
|
|
//if( GetAttachment( "rightclaw", vecBloodPos ) )
|
|
{
|
|
vecBloodPos = GetAbsOrigin() + forward * 60 + right * 14 + up * 50;
|
|
SpawnBlood( vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN( damage, 30 ) );
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
CBaseEntity *CASW_Simple_Alien::CheckTraceHullAttack( float flDist, const Vector &mins, const Vector &maxs, int iDamage, int iDmgType, float forceScale, bool bDamageAnyNPC )
|
|
{
|
|
// If only a length is given assume we want to trace in our facing direction
|
|
Vector forward;
|
|
AngleVectors( GetAbsAngles(), &forward );
|
|
Vector vStart = GetAbsOrigin();
|
|
|
|
// The ideal place to start the trace is in the center of the attacker's bounding box.
|
|
// however, we need to make sure there's enough clearance. Some of the smaller monsters aren't
|
|
// as big as the hull we try to trace with. (SJB)
|
|
float flVerticalOffset = WorldAlignSize().z * 0.5;
|
|
|
|
if( flVerticalOffset < maxs.z )
|
|
{
|
|
// There isn't enough room to trace this hull, it's going to drag the ground.
|
|
// so make the vertical offset just enough to clear the ground.
|
|
flVerticalOffset = maxs.z + 1.0;
|
|
}
|
|
|
|
vStart.z += flVerticalOffset;
|
|
Vector vEnd = vStart + (forward * flDist );
|
|
return CheckTraceHullAttack( vStart, vEnd, mins, maxs, iDamage, iDmgType, forceScale, bDamageAnyNPC );
|
|
}
|
|
|
|
CBaseEntity *CASW_Simple_Alien::CheckTraceHullAttack( const Vector &vStart, const Vector &vEnd, const Vector &mins, const Vector &maxs, int iDamage, int iDmgType, float flForceScale, bool bDamageAnyNPC )
|
|
{
|
|
// Handy debuging tool to visualize HullAttack trace
|
|
if ( ai_show_hull_attacks.GetBool() )
|
|
{
|
|
float length = (vEnd - vStart ).Length();
|
|
Vector direction = (vEnd - vStart );
|
|
VectorNormalize( direction );
|
|
Vector hullMaxs = maxs;
|
|
hullMaxs.x = length + hullMaxs.x;
|
|
NDebugOverlay::BoxDirection(vStart, mins, hullMaxs, direction, 100,255,255,20,1.0);
|
|
NDebugOverlay::BoxDirection(vStart, mins, maxs, direction, 255,0,0,20,1.0);
|
|
}
|
|
|
|
CASW_Trace_Filter_Melee traceFilter( this, COLLISION_GROUP_NONE, this, bDamageAnyNPC );
|
|
|
|
Ray_t ray;
|
|
ray.Init( vStart, vEnd, mins, maxs );
|
|
|
|
trace_t tr;
|
|
enginetrace->TraceRay( ray, MASK_SHOT_HULL, &traceFilter, &tr );
|
|
|
|
CBaseEntity *pEntity = traceFilter.m_pHit;
|
|
|
|
if ( pEntity == NULL )
|
|
{
|
|
// See if perhaps I'm trying to claw/bash someone who is standing on my head.
|
|
Vector vecTopCenter;
|
|
Vector vecEnd;
|
|
Vector vecMins, vecMaxs;
|
|
|
|
// Do a tracehull from the top center of my bounding box.
|
|
vecTopCenter = GetAbsOrigin();
|
|
CollisionProp()->WorldSpaceAABB( &vecMins, &vecMaxs );
|
|
vecTopCenter.z = vecMaxs.z + 1.0f;
|
|
vecEnd = vecTopCenter;
|
|
vecEnd.z += 2.0f;
|
|
|
|
ray.Init( vecTopCenter, vEnd, mins, maxs );
|
|
enginetrace->TraceRay( ray, MASK_SHOT_HULL, &traceFilter, &tr );
|
|
|
|
pEntity = traceFilter.m_pHit;
|
|
}
|
|
|
|
return pEntity;
|
|
}
|
|
|
|
// =========================================
|
|
// Alien Orders
|
|
// =========================================
|
|
|
|
void CASW_Simple_Alien::SetAlienOrders(AlienOrder_t Orders, Vector vecOrderSpot, CBaseEntity* pOrderObject)
|
|
{
|
|
m_AlienOrders = Orders;
|
|
m_vecAlienOrderSpot = vecOrderSpot; // unused currently
|
|
m_AlienOrderObject = pOrderObject;
|
|
|
|
if (Orders == AOT_None)
|
|
{
|
|
ClearAlienOrders();
|
|
return;
|
|
}
|
|
|
|
// todo: set our state etc based on these?
|
|
}
|
|
|
|
AlienOrder_t CASW_Simple_Alien::GetAlienOrders()
|
|
{
|
|
return m_AlienOrders;
|
|
}
|
|
|
|
void CASW_Simple_Alien::ClearAlienOrders()
|
|
{
|
|
m_AlienOrders = AOT_None;
|
|
m_vecAlienOrderSpot = vec3_origin;
|
|
m_AlienOrderObject = NULL;
|
|
m_bIgnoreMarines = false;
|
|
m_bFailedMoveTo = false;
|
|
}
|
|
|
|
void CASW_Simple_Alien::ASW_Ignite( float flFlameLifetime, float flSize, CBaseEntity *pAttacker, CBaseEntity *pDamagingWeapon )
|
|
{
|
|
if (AllowedToIgnite())
|
|
{
|
|
if( IsOnFire() )
|
|
return;
|
|
|
|
CEntityFlame *pFlame = CEntityFlame::Create( this, flFlameLifetime, flSize );
|
|
if (pFlame)
|
|
{
|
|
if (pAttacker)
|
|
pFlame->SetOwnerEntity(pAttacker);
|
|
pFlame->SetLifetime( flFlameLifetime );
|
|
AddFlag( FL_ONFIRE );
|
|
|
|
SetEffectEntity( pFlame );
|
|
}
|
|
|
|
m_OnIgnite.FireOutput( this, this );
|
|
}
|
|
}
|
|
|
|
// =========================================
|
|
// Health
|
|
// =========================================
|
|
|
|
int CASW_Simple_Alien::OnTakeDamage( const CTakeDamageInfo &info )
|
|
{
|
|
// don't get hurt if our attacker is our friend
|
|
if (info.GetAttacker() && info.GetAttacker()->Classify() != CLASS_NONE)
|
|
{
|
|
// Proper way to check, but BCC default relationship array is private :/
|
|
//Disposition_t disp = CBaseCombatCharacter::m_DefaultRelationship[Classify()][info.GetAttacker()->Classify()].disposition;
|
|
//if (disp == D_LI)
|
|
//return 0;
|
|
|
|
// Hacky way to stop simple aliens getting hurt by other Infested aliens
|
|
Class_T c = info.GetAttacker()->Classify();
|
|
if ( IsAlienClass( c ) )
|
|
return 0;
|
|
}
|
|
|
|
if (asw_debug_alien_damage.GetBool())
|
|
{
|
|
Msg("%d %s hurt by %f dmg\n", entindex(), GetClassname(), info.GetDamage());
|
|
}
|
|
|
|
int iDamage = BaseClass::OnTakeDamage(info);
|
|
|
|
if (iDamage > 0 && GetHealth() > 0)
|
|
{
|
|
PainSound(info);
|
|
}
|
|
|
|
return iDamage;
|
|
}
|
|
|
|
void CASW_Simple_Alien::ASWTraceBleed( float flDamage, const Vector &vecDir, trace_t *ptr, int bitsDamageType )
|
|
{
|
|
if ((BloodColor() == DONT_BLEED) || (BloodColor() == BLOOD_COLOR_MECH))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (flDamage == 0)
|
|
return;
|
|
|
|
if (! (bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_AIRBOAT)))
|
|
return;
|
|
|
|
// make blood decal on the wall!
|
|
trace_t Bloodtr;
|
|
Vector vecTraceDir;
|
|
float flNoise;
|
|
int cCount;
|
|
int i;
|
|
|
|
#ifdef GAME_DLL
|
|
if ( !IsAlive() )
|
|
{
|
|
// dealing with a dead npc.
|
|
if ( GetMaxHealth() <= 0 )
|
|
{
|
|
// no blood decal for a npc that has already decalled its limit.
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
m_iMaxHealth -= 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (flDamage < 10)
|
|
{
|
|
flNoise = 0.1;
|
|
cCount = 1;
|
|
}
|
|
else if (flDamage < 25)
|
|
{
|
|
flNoise = 0.2;
|
|
cCount = 2;
|
|
}
|
|
else
|
|
{
|
|
flNoise = 0.3;
|
|
cCount = 4;
|
|
}
|
|
|
|
float flTraceDist = (bitsDamageType & DMG_AIRBOAT) ? 384 : 172;
|
|
for ( i = 0 ; i < cCount ; i++ )
|
|
{
|
|
vecTraceDir = vecDir * -1;// trace in the opposite direction the shot came from (the direction the shot is going)
|
|
|
|
vecTraceDir.x += random->RandomFloat( -flNoise, flNoise );
|
|
vecTraceDir.y += random->RandomFloat( -flNoise, flNoise );
|
|
vecTraceDir.z += random->RandomFloat( -flNoise, flNoise );
|
|
|
|
// Don't bleed on grates.
|
|
Vector vecEndPos = ptr->endpos; // + m_LagCompensation.GetLagCompensationOffset();
|
|
AI_TraceLine( vecEndPos, vecEndPos + vecTraceDir * -flTraceDist, MASK_SOLID_BRUSHONLY & ~CONTENTS_GRATE, this, COLLISION_GROUP_NONE, &Bloodtr);
|
|
|
|
if ( Bloodtr.fraction != 1.0 )
|
|
{
|
|
UTIL_BloodDecalTrace( &Bloodtr, BloodColor() );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CASW_Simple_Alien::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
|
|
{
|
|
if ( m_takedamage == DAMAGE_NO )
|
|
return;
|
|
|
|
CTakeDamageInfo subInfo = info;
|
|
m_nForceBone = ptr->physicsbone; // save this bone for physics forces
|
|
Assert( m_nForceBone > -255 && m_nForceBone < 256 );
|
|
|
|
if ( subInfo.GetDamage() >= 1.0 && !(subInfo.GetDamageType() & DMG_SHOCK )
|
|
&& !(subInfo.GetDamageType() & DMG_BURN ))
|
|
{
|
|
// NPC's always bleed. Players only bleed in multiplayer.
|
|
//SpawnBlood( ptr->endpos, vecDir, BloodColor(), subInfo.GetDamage() );// a little surface blood.
|
|
UTIL_ASW_DroneBleed( ptr->endpos, vecDir, 4 ); // + m_LagCompensation.GetLagCompensationOffset()
|
|
ASWTraceBleed( subInfo.GetDamage(), vecDir, ptr, subInfo.GetDamageType() );
|
|
}
|
|
|
|
if( info.GetInflictor() )
|
|
{
|
|
subInfo.SetInflictor( info.GetInflictor() );
|
|
}
|
|
else
|
|
{
|
|
subInfo.SetInflictor( info.GetAttacker() );
|
|
}
|
|
|
|
AddMultiDamage( subInfo, this );
|
|
}
|
|
|
|
bool CASW_Simple_Alien::ShouldGib( const CTakeDamageInfo &info )
|
|
{
|
|
// don't gib if we burnt to death
|
|
if (info.GetDamageType() & DMG_BURN)
|
|
return false;
|
|
|
|
if (info.GetDamageType() & DMG_ALWAYSGIB)
|
|
return true;
|
|
|
|
return RandomFloat() < asw_drone_gib_chance.GetFloat();
|
|
}
|
|
|
|
bool CASW_Simple_Alien::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_nColor = m_nSkin;
|
|
|
|
//DispatchEffect( "DroneGib", data );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CASW_Simple_Alien::Event_Gibbed( const CTakeDamageInfo &info )
|
|
{
|
|
bool fade = false;
|
|
ConVar const *agibs = cvar->FindVar( "violence_agibs" );
|
|
if ( agibs && agibs->GetInt() == 0 )
|
|
{
|
|
fade = true;
|
|
}
|
|
|
|
m_takedamage = DAMAGE_NO;
|
|
AddSolidFlags( FSOLID_NOT_SOLID );
|
|
m_lifeState = LIFE_DEAD;
|
|
|
|
if ( fade )
|
|
{
|
|
CorpseFade();
|
|
return false;
|
|
}
|
|
|
|
AddEffects( EF_NODRAW ); // make the model invisible.
|
|
CorpseGib( info );
|
|
|
|
UTIL_Remove( this );
|
|
SetThink( NULL );
|
|
return true;
|
|
}
|
|
|
|
void CASW_Simple_Alien::CorpseFade()
|
|
{
|
|
StopAnimation();
|
|
SetAbsVelocity( vec3_origin );
|
|
SetMoveType( MOVETYPE_NONE );
|
|
SetLocalAngularVelocity( vec3_angle );
|
|
m_flAnimTime = gpGlobals->curtime;
|
|
AddEffects( EF_NOINTERP );
|
|
SUB_StartFadeOut();
|
|
}
|
|
|
|
Vector CASW_Simple_Alien::CalcDeathForceVector( const CTakeDamageInfo &info )
|
|
{
|
|
// Already have a damage force in the data, use that.
|
|
if ( info.GetDamageForce() != vec3_origin || (g_pGameRules->Damage_NoPhysicsForce(info.GetDamageType())))
|
|
{
|
|
if( info.GetDamageType() & DMG_BLAST )
|
|
{
|
|
float scale = random->RandomFloat( 0.85, 1.15 );
|
|
Vector force = info.GetDamageForce();
|
|
force.x *= scale;
|
|
force.y *= scale;
|
|
// Try to always exaggerate the upward force because we've got pretty harsh gravity
|
|
force.z *= (force.z > 0) ? 1.15 : scale;
|
|
return force;
|
|
}
|
|
|
|
return info.GetDamageForce();
|
|
}
|
|
|
|
CBaseEntity *pForce = info.GetInflictor();
|
|
if ( !pForce )
|
|
{
|
|
pForce = info.GetAttacker();
|
|
}
|
|
|
|
if ( pForce )
|
|
{
|
|
// Calculate an impulse large enough to push a 75kg man 4 in/sec per point of damage
|
|
float forceScale = info.GetDamage() * 75 * 4;
|
|
|
|
Vector forceVector;
|
|
// If the damage is a blast, point the force vector higher than usual, this gives
|
|
// the ragdolls a bodacious "really got blowed up" look.
|
|
if( info.GetDamageType() & DMG_BLAST )
|
|
{
|
|
// exaggerate the force from explosions a little (37.5%)
|
|
forceVector = (GetLocalOrigin() + Vector(0, 0, WorldAlignSize().z) ) - pForce->GetLocalOrigin();
|
|
VectorNormalize(forceVector);
|
|
forceVector *= 1.375f;
|
|
}
|
|
else
|
|
{
|
|
// taking damage from self? Take a little random force, but still try to collapse on the spot.
|
|
if ( this == pForce )
|
|
{
|
|
forceVector.x = random->RandomFloat( -1.0f, 1.0f );
|
|
forceVector.y = random->RandomFloat( -1.0f, 1.0f );
|
|
forceVector.z = 0.0;
|
|
forceScale = random->RandomFloat( 1000.0f, 2000.0f );
|
|
}
|
|
else
|
|
{
|
|
// UNDONE: Collision forces are baked in to CTakeDamageInfo now
|
|
// UNDONE: Is this MOVETYPE_VPHYSICS code still necessary?
|
|
if ( pForce->GetMoveType() == MOVETYPE_VPHYSICS )
|
|
{
|
|
// killed by a physics object
|
|
IPhysicsObject *pPhysics = VPhysicsGetObject();
|
|
if ( !pPhysics )
|
|
{
|
|
pPhysics = pForce->VPhysicsGetObject();
|
|
}
|
|
pPhysics->GetVelocity( &forceVector, NULL );
|
|
forceScale = pPhysics->GetMass();
|
|
}
|
|
else
|
|
{
|
|
forceVector = GetLocalOrigin() - pForce->GetLocalOrigin();
|
|
VectorNormalize(forceVector);
|
|
}
|
|
}
|
|
}
|
|
return forceVector * forceScale;
|
|
}
|
|
return vec3_origin;
|
|
}
|
|
|
|
void CASW_Simple_Alien::Event_Killed( const CTakeDamageInfo &info )
|
|
{
|
|
if (ASWGameRules())
|
|
{
|
|
ASWGameRules()->AlienKilled(this, info);
|
|
}
|
|
|
|
if (m_hSpawner.Get())
|
|
m_hSpawner->AlienKilled(this);
|
|
|
|
// Calculate death force
|
|
Vector forceVector = CalcDeathForceVector( info );
|
|
|
|
StopLoopingSounds();
|
|
DeathSound(info);
|
|
// todo: remove touch function?
|
|
|
|
SetState(ASW_SIMPLE_ALIEN_DEAD);
|
|
|
|
// todo: Select a death pose to extrapolate the ragdoll's velocity?
|
|
//SelectDeathPose( info );
|
|
|
|
if (!ShouldGib(info))
|
|
{
|
|
SetCollisionGroup(ASW_COLLISION_GROUP_PASSABLE); // don't block marines by dead bodies
|
|
|
|
if ( BecomeRagdollOnClient( forceVector ) )
|
|
{
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Event_Gibbed( info );
|
|
}
|
|
}
|
|
|
|
// =========================================
|
|
// Debugging
|
|
// =========================================
|
|
|
|
int CASW_Simple_Alien::DrawDebugTextOverlays()
|
|
{
|
|
int text_offset = 0;
|
|
|
|
text_offset = BaseClass::DrawDebugTextOverlays();
|
|
|
|
if (m_debugOverlays & OVERLAY_TEXT_BIT)
|
|
{
|
|
char tempstr[512];
|
|
// health
|
|
Q_snprintf(tempstr,sizeof(tempstr),"Health: %i", m_iHealth );
|
|
EntityText(text_offset,tempstr,0);
|
|
text_offset++;
|
|
// state
|
|
static const char *pStateNames[] = { "Idling", "Attacking" };
|
|
if ( m_iState < ARRAYSIZE(pStateNames) )
|
|
{
|
|
Q_snprintf(tempstr,sizeof(tempstr),"State: %s, ", pStateNames[m_iState] );
|
|
EntityText(text_offset,tempstr,0);
|
|
text_offset++;
|
|
}
|
|
// enemy
|
|
if (GetEnemy())
|
|
{
|
|
Q_snprintf(tempstr,sizeof(tempstr),"Enemy: %d %s", GetEnemy()->entindex(), GetEnemy()->GetClassname() );
|
|
EntityText(text_offset,tempstr,0);
|
|
text_offset++;
|
|
}
|
|
else
|
|
{
|
|
Q_snprintf(tempstr,sizeof(tempstr),"Enemy: NONE" );
|
|
EntityText(text_offset,tempstr,0);
|
|
text_offset++;
|
|
}
|
|
// alien orders
|
|
static const char *pOrderNames[] = { "SpreadThenHibernate", "MoveTo", "MoveToIgnoring", "MoveToNearestM", "None" };
|
|
if ( (int)m_AlienOrders < ARRAYSIZE(pOrderNames) )
|
|
{
|
|
Q_snprintf(tempstr,sizeof(tempstr),"Orders: %s, ", pOrderNames[m_iState] );
|
|
EntityText(text_offset,tempstr,0);
|
|
text_offset++;
|
|
}
|
|
// moving
|
|
Q_snprintf(tempstr,sizeof(tempstr),"Moving: %s, ", m_bMoving ? "Yes" : "No" );
|
|
EntityText(text_offset,tempstr,0);
|
|
text_offset++;
|
|
|
|
// sleeping
|
|
Q_snprintf(tempstr,sizeof(tempstr),"Sleeping: %s, ", m_bSleeping ? "Yes" : "No" );
|
|
EntityText(text_offset,tempstr,0);
|
|
text_offset++;
|
|
|
|
// collision
|
|
Q_snprintf(tempstr,sizeof(tempstr),"Col Group: %d, ", GetCollisionGroup());
|
|
EntityText(text_offset,tempstr,0);
|
|
text_offset++;
|
|
}
|
|
|
|
return text_offset;
|
|
}
|
|
|
|
void CASW_Simple_Alien::DrawDebugGeometryOverlays()
|
|
{
|
|
BaseClass::DrawDebugGeometryOverlays();
|
|
// draw route
|
|
if ((m_debugOverlays & OVERLAY_NPC_ROUTE_BIT))
|
|
{
|
|
if ( m_bMoving )
|
|
{
|
|
Vector vecDiff = m_vecMoveTarget - GetAbsOrigin();
|
|
NDebugOverlay::Line(WorldSpaceCenter(), WorldSpaceCenter() + vecDiff,0,0,255,true,0.0);
|
|
}
|
|
if (GetEnemy())
|
|
{
|
|
NDebugOverlay::Line(WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(),255,0,0,true,0.0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CASW_Simple_Alien::HandleAnimEvent( animevent_t *pEvent )
|
|
{
|
|
int nEvent = pEvent->Event();
|
|
|
|
if ( nEvent == AE_DRONE_WALK_FOOTSTEP )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( nEvent == AE_DRONE_FOOTSTEP_SOFT )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( nEvent == AE_DRONE_FOOTSTEP_HEAVY )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( nEvent == AE_DRONE_MELEE_HIT1 )
|
|
{
|
|
MeleeAttack( ASW_DRONE_MELEE1_RANGE, ASWGameRules()->ModifyAlienDamageBySkillLevel(sk_asw_drone_damage.GetFloat()), QAngle( 20.0f, 0.0f, -12.0f ), Vector( -250.0f, 1.0f, 1.0f ) );
|
|
return;
|
|
}
|
|
|
|
if ( nEvent == AE_DRONE_MELEE_HIT2 )
|
|
{
|
|
MeleeAttack( ASW_DRONE_MELEE1_RANGE, ASWGameRules()->ModifyAlienDamageBySkillLevel(sk_asw_drone_damage.GetFloat()), QAngle( 20.0f, 0.0f, 0.0f ), Vector( -350.0f, 1.0f, 1.0f ) );
|
|
return;
|
|
}
|
|
|
|
if ( nEvent == AE_DRONE_MELEE1_SOUND )
|
|
{
|
|
AttackSound();
|
|
return;
|
|
}
|
|
|
|
if ( nEvent == AE_DRONE_MELEE2_SOUND )
|
|
{
|
|
AttackSound();
|
|
return;
|
|
}
|
|
|
|
if ( nEvent == AE_DRONE_MOUTH_BLEED )
|
|
{
|
|
Vector vecOrigin, vecDir;
|
|
if (GetAttachment( LookupAttachment("mouth") , vecOrigin, &vecDir ))
|
|
UTIL_ASW_BloodDrips( vecOrigin+vecDir*3, vecDir, BLOOD_COLOR_RED, 6 );
|
|
return;
|
|
}
|
|
|
|
if ( nEvent == AE_DRONE_ALERT_SOUND )
|
|
{
|
|
EmitSound( "ASW_Drone.Alert" );
|
|
return;
|
|
}
|
|
|
|
if ( nEvent == AE_DRONE_SHADOW_ON)
|
|
{
|
|
RemoveEffects( EF_NOSHADOW );
|
|
return;
|
|
}
|
|
|
|
BaseClass::HandleAnimEvent( pEvent );
|
|
}
|
|
|
|
void CASW_Simple_Alien::SetHealthByDifficultyLevel()
|
|
{
|
|
SetHealth(ASWGameRules()->ModifyAlienHealthBySkillLevel(asw_drone_health.GetInt()));
|
|
} |