You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1434 lines
40 KiB
1434 lines
40 KiB
// Our Swarm Drone - the basic angry fighting alien |
|
|
|
#include "cbase.h" |
|
#include "asw_alien_shover.h" |
|
|
|
#include "ai_hint.h" |
|
#include "ai_memory.h" |
|
#include "ai_moveprobe.h" |
|
#include "npcevent.h" |
|
#include "IEffects.h" |
|
#include "ndebugoverlay.h" |
|
#include "soundent.h" |
|
#include "soundenvelope.h" |
|
#include "ai_squad.h" |
|
#include "ai_network.h" |
|
#include "ai_pathfinder.h" |
|
#include "ai_navigator.h" |
|
#include "ai_senses.h" |
|
#include "ai_blended_movement.h" |
|
#include "physics_prop_ragdoll.h" |
|
#include "iservervehicle.h" |
|
#include "player_pickup.h" |
|
#include "props.h" |
|
#include "antlion_dust.h" |
|
#include "decals.h" |
|
#include "prop_combine_ball.h" |
|
#include "eventqueue.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
#define ALIEN_SHOVER_MAX_OBJECTS 128 |
|
#define ALIEN_SHOVER_MIN_OBJECT_MASS 8 |
|
#define ALIEN_SHOVER_OBJECTFINDING_FOV 0.7 |
|
// stats for shove attack |
|
#define ALIEN_SHOVER_MELEE1_RANGE 156.0f |
|
#define ALIEN_SHOVER_MELEE1_CONE 0.7f |
|
#define ALIEN_SWAT_DELAY 5.0f |
|
#define SHOVER_PHYSICS_SEARCH_DEPTH 100 |
|
#define SHOVER_PLAYER_MAX_SWAT_DIST 1000 |
|
#define SHOVER_FARTHEST_PHYSICS_OBJECT 40.0*12.0 |
|
#define SHOVER_PHYSOBJ_SWATDIST 100 |
|
|
|
ConVar asw_debug_aliens("asw_debug_aliens", "0", FCVAR_CHEAT); |
|
ConVar asw_alien_shover_speed("asw_alien_shover_speed", "50", FCVAR_CHEAT, "Speed of a 75kg object thrown by an alien"); |
|
|
|
Activity ACT_ALIEN_SHOVER_SHOVE_PHYSOBJECT; |
|
Activity ACT_ALIEN_SHOVER_ROAR; |
|
|
|
// Anim events |
|
int AE_ALIEN_SHOVER_SHOVE_PHYSOBJECT; |
|
int AE_ALIEN_SHOVER_SHOVE; |
|
int AE_ALIEN_SHOVER_ROAR; |
|
|
|
|
|
CASW_Alien_Shover::CASW_Alien_Shover( void )// : CASW_Alien() |
|
{ |
|
|
|
} |
|
|
|
LINK_ENTITY_TO_CLASS( asw_alien_shover, CASW_Alien_Shover ); |
|
|
|
BEGIN_DATADESC( CASW_Alien_Shover ) |
|
DEFINE_FIELD( m_hShoveTarget, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_hOldTarget, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_hLastFailedPhysicsTarget, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_hPhysicsTarget, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_vecPhysicsTargetStartPos, FIELD_POSITION_VECTOR ), |
|
DEFINE_FIELD( m_vecPhysicsHitPosition, FIELD_POSITION_VECTOR ), |
|
DEFINE_FIELD( m_flPhysicsCheckTime, FIELD_TIME ), |
|
DEFINE_FIELD( m_flNextSwat, FIELD_TIME ), |
|
DEFINE_FIELD( m_hObstructor, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_bCanRoar, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_flNextRoarTime, FIELD_TIME ), |
|
|
|
DEFINE_INPUTFUNC( FIELD_STRING, "SetShoveTarget", InputSetShoveTarget ), |
|
END_DATADESC() |
|
|
|
|
|
//============================================================================================== |
|
// ALIEN SHOVER PHYSICS DAMAGE TABLE |
|
//============================================================================================== |
|
static impactentry_t alienShoverLinearTable[] = |
|
{ |
|
{ 100*100, 10 }, |
|
{ 250*250, 25 }, |
|
{ 350*350, 50 }, |
|
{ 500*500, 75 }, |
|
{ 1000*1000,100 }, |
|
}; |
|
|
|
static impactentry_t alienShoverAngularTable[] = |
|
{ |
|
{ 50* 50, 10 }, |
|
{ 100*100, 25 }, |
|
{ 150*150, 50 }, |
|
{ 200*200, 75 }, |
|
}; |
|
|
|
impactdamagetable_t gAlienShoverImpactDamageTable = |
|
{ |
|
alienShoverLinearTable, |
|
alienShoverAngularTable, |
|
|
|
ARRAYSIZE(alienShoverLinearTable), |
|
ARRAYSIZE(alienShoverAngularTable), |
|
|
|
200*200,// minimum linear speed squared |
|
180*180,// minimum angular speed squared (360 deg/s to cause spin/slice damage) |
|
15, // can't take damage from anything under 15kg |
|
|
|
10, // anything less than 10kg is "small" |
|
5, // never take more than 1 pt of damage from anything under 15kg |
|
128*128,// <15kg objects must go faster than 36 in/s to do damage |
|
|
|
45, // large mass in kg |
|
2, // large mass scale (anything over 500kg does 4X as much energy to read from damage table) |
|
1, // large mass falling scale |
|
0, // my min velocity |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : const impactdamagetable_t |
|
//----------------------------------------------------------------------------- |
|
const impactdamagetable_t &CASW_Alien_Shover::GetPhysicsImpactDamageTable( void ) |
|
{ |
|
return gAlienShoverImpactDamageTable; |
|
} |
|
|
|
void CASW_Alien_Shover::Spawn() |
|
{ |
|
m_flPhysicsCheckTime = 0; |
|
m_flNextSwat = gpGlobals->curtime; |
|
m_hShoveTarget = NULL; |
|
m_hPhysicsTarget = NULL; |
|
m_hLastFailedPhysicsTarget = NULL; |
|
m_bCanRoar = true; |
|
m_flNextRoarTime = 0; |
|
|
|
PrecacheScriptSound("ASW_Drone.Shove"); |
|
|
|
BaseClass::Spawn(); |
|
} |
|
|
|
void CASW_Alien_Shover::Activate( void ) |
|
{ |
|
BaseClass::Activate(); |
|
|
|
// Find all nearby physics objects and add them to the list of objects we will sense |
|
CBaseEntity *pObject = NULL; |
|
while ( ( pObject = gEntList.FindEntityInSphere( pObject, WorldSpaceCenter(), 2500 ) ) != NULL ) |
|
{ |
|
// Can't throw around debris |
|
if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ) |
|
continue; |
|
|
|
// We can only throw a few types of things |
|
if ( !FClassnameIs( pObject, "prop_physics" ) && !FClassnameIs( pObject, "func_physbox" ) ) |
|
continue; |
|
|
|
// Ensure it's mass is within range |
|
IPhysicsObject *pPhysObj = pObject->VPhysicsGetObject(); |
|
if( ( pPhysObj == NULL ) || ( pPhysObj->GetMass() > GetMaxShoverObjectMass() ) || ( pPhysObj->GetMass() < ALIEN_SHOVER_MIN_OBJECT_MASS ) ) |
|
continue; |
|
|
|
// Tell the AI sensing list that we want to consider this |
|
g_AI_SensedObjectsManager.AddEntity( pObject ); |
|
|
|
if ( asw_debug_aliens.GetInt() == 5 ) |
|
{ |
|
Msg("Alien Shover: Added prop with model '%s' to sense list.\n", STRING(pObject->GetModelName()) ); |
|
pObject->m_debugOverlays |= OVERLAY_BBOX_BIT; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Our enemy is unreachable. Select a schedule. |
|
//----------------------------------------------------------------------------- |
|
int CASW_Alien_Shover::SelectUnreachableSchedule( void ) |
|
{ |
|
// Look for a physics objects nearby |
|
m_hLastFailedPhysicsTarget = NULL; |
|
UpdatePhysicsTarget( true ); |
|
if ( HasCondition( COND_ALIEN_SHOVER_PHYSICS_TARGET ) ) |
|
{ |
|
//Msg("Alien shoving phys object from selectunreachableschedule\n"); |
|
return SCHED_ALIEN_SHOVER_PHYSICS_ATTACK; |
|
} |
|
|
|
// Otherwise, roar at the player |
|
if ( m_bCanRoar && HasCondition(COND_SEE_ENEMY) && m_flNextRoarTime < gpGlobals->curtime ) |
|
{ |
|
m_flNextRoarTime = gpGlobals->curtime + RandomFloat( 20,40 ); |
|
return SCHED_ALIEN_SHOVER_ROAR; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
int CASW_Alien_Shover::SelectCombatSchedule( void ) |
|
{ |
|
//Physics target |
|
if ( HasCondition( COND_ALIEN_SHOVER_PHYSICS_TARGET ) ) |
|
{ |
|
//Msg("Alien shoving physics object from selectcombatschedule\n"); |
|
return SCHED_ALIEN_SHOVER_PHYSICS_ATTACK; |
|
} |
|
|
|
// Attack if we can |
|
if ( HasCondition(COND_CAN_MELEE_ATTACK1) ) |
|
return SCHED_MELEE_ATTACK1; |
|
|
|
// See if we can bash what's in our way, or roar |
|
if ( HasCondition( COND_ENEMY_UNREACHABLE ) ) |
|
{ |
|
int iUnreach = SelectUnreachableSchedule(); |
|
if (iUnreach != -1) |
|
return iUnreach; |
|
} |
|
|
|
return BaseClass::SelectSchedule(); |
|
} |
|
|
|
int CASW_Alien_Shover::SelectSchedule( void ) |
|
{ |
|
// Debug physics object finding |
|
if ( 0 ) //asw_debug_aliens.GetInt() == 3 ) |
|
{ |
|
m_flPhysicsCheckTime = 0; |
|
UpdatePhysicsTarget( false ); |
|
return SCHED_IDLE_STAND; |
|
} |
|
|
|
// See if we need to clear a path to our enemy |
|
if ( HasCondition( COND_ENEMY_OCCLUDED ) || HasCondition( COND_ENEMY_UNREACHABLE ) ) |
|
{ |
|
CBaseEntity *pBlocker = GetEnemyOccluder(); |
|
|
|
if ( ( pBlocker != NULL ) && FClassnameIs( pBlocker, "prop_physics" ) ) |
|
{ |
|
m_hPhysicsTarget = pBlocker; |
|
//Msg("Alien shoving physics object from selectschedule as enemy is occluded\n"); |
|
return SCHED_ALIEN_SHOVER_PHYSICS_ATTACK; |
|
} |
|
} |
|
|
|
//Only do these in combat states |
|
if ( m_NPCState == NPC_STATE_COMBAT && GetEnemy() ) |
|
return SelectCombatSchedule(); |
|
|
|
return BaseClass::SelectSchedule(); |
|
} |
|
|
|
void CASW_Alien_Shover::Shove( void ) |
|
{ |
|
if ( GetNextAttack() > gpGlobals->curtime ) |
|
return; |
|
|
|
CBaseEntity *pHurt = NULL; |
|
CBaseEntity *pTarget; |
|
|
|
pTarget = ( m_hShoveTarget == NULL ) ? GetEnemy() : m_hShoveTarget.Get(); |
|
|
|
if ( pTarget == NULL ) |
|
{ |
|
m_hShoveTarget = NULL; |
|
return; |
|
} |
|
|
|
//Always damage bullseyes if we're scripted to hit them |
|
if ( pTarget->Classify() == CLASS_BULLSEYE ) |
|
{ |
|
pTarget->TakeDamage( CTakeDamageInfo( this, this, 1.0f, DMG_CRUSH ) ); |
|
m_hShoveTarget = NULL; |
|
return; |
|
} |
|
|
|
//float damage = ( pTarget->IsPlayer() ) ? sk_antlionguard_dmg_shove.GetFloat() : 250; |
|
float damage = 250.0f; |
|
|
|
// If the target's still inside the shove cone, ensure we hit him |
|
Vector vecForward, vecEnd; |
|
AngleVectors( GetAbsAngles(), &vecForward ); |
|
float flDistSqr = ( pTarget->WorldSpaceCenter() - WorldSpaceCenter() ).LengthSqr(); |
|
Vector2D v2LOS = ( pTarget->WorldSpaceCenter() - WorldSpaceCenter() ).AsVector2D(); |
|
Vector2DNormalize(v2LOS); |
|
float flDot = DotProduct2D (v2LOS, vecForward.AsVector2D() ); |
|
if ( flDistSqr < (ALIEN_SHOVER_MELEE1_RANGE*ALIEN_SHOVER_MELEE1_RANGE) && flDot >= ALIEN_SHOVER_MELEE1_CONE ) |
|
{ |
|
vecEnd = pTarget->WorldSpaceCenter(); |
|
} |
|
else |
|
{ |
|
vecEnd = WorldSpaceCenter() + ( BodyDirection3D() * ALIEN_SHOVER_MELEE1_RANGE ); |
|
} |
|
|
|
// Use the melee trace to ensure we hit everything there |
|
trace_t tr; |
|
CTakeDamageInfo dmgInfo( this, this, damage, DMG_SLASH ); |
|
CTraceFilterMelee traceFilter( this, COLLISION_GROUP_NONE, &dmgInfo, 1.0, true ); |
|
Ray_t ray; |
|
ray.Init( WorldSpaceCenter(), vecEnd, Vector(-16,-16,-16), Vector(16,16,16) ); |
|
enginetrace->TraceRay( ray, MASK_SHOT_HULL, &traceFilter, &tr ); |
|
pHurt = tr.m_pEnt; |
|
|
|
// Knock things around |
|
//ImpactShock( tr.endpos, 100.0f, 250.0f ); |
|
|
|
if ( pHurt ) |
|
{ |
|
Vector traceDir = ( tr.endpos - tr.startpos ); |
|
VectorNormalize( traceDir ); |
|
|
|
// Generate enough force to make a 75kg guy move away at 600 in/sec |
|
Vector vecForce = traceDir * ImpulseScale( 75, asw_alien_shover_speed.GetFloat() ); |
|
CTakeDamageInfo info( this, this, vecForce, tr.endpos, damage, DMG_CLUB ); |
|
pHurt->TakeDamage( info ); |
|
|
|
m_hShoveTarget = NULL; |
|
|
|
EmitSound( "ASW_Drone.Shove" ); |
|
|
|
// If the player, throw him around |
|
if ( pHurt->IsPlayer() ) |
|
{ |
|
//Punch the view |
|
pHurt->ViewPunch( QAngle(20,0,-20) ); |
|
|
|
//Shake the screen |
|
UTIL_ScreenShake( pHurt->GetAbsOrigin(), 100.0, 1.5, 1.0, 2, SHAKE_START ); |
|
|
|
//Red damage indicator |
|
color32 red = {128,0,0,128}; |
|
UTIL_ScreenFade( pHurt, red, 1.0f, 0.1f, FFADE_IN ); |
|
|
|
Vector forward, up; |
|
AngleVectors( GetLocalAngles(), &forward, NULL, &up ); |
|
pHurt->ApplyAbsVelocityImpulse( forward * 400 + up * 150 ); |
|
} |
|
else |
|
{ |
|
CBaseCombatCharacter *pVictim = ToBaseCombatCharacter( pHurt ); |
|
|
|
if ( pVictim ) |
|
{ |
|
pVictim->ApplyAbsVelocityImpulse( BodyDirection2D() * 400 + Vector( 0, 0, 250 ) ); |
|
} |
|
|
|
m_hShoveTarget = NULL; |
|
} |
|
} |
|
} |
|
|
|
void CASW_Alien_Shover::HandleAnimEvent( animevent_t *pEvent ) |
|
{ |
|
int nEvent = pEvent->Event(); |
|
|
|
if ( nEvent == AE_ALIEN_SHOVER_SHOVE_PHYSOBJECT ) |
|
{ |
|
if ( m_hPhysicsTarget == NULL ) |
|
{ |
|
// Disrupt other objects near us anyway |
|
ImpactShock( WorldSpaceCenter(), 150, 250.0f ); |
|
return; |
|
} |
|
|
|
//Setup the throw velocity |
|
IPhysicsObject *physObj = m_hPhysicsTarget->VPhysicsGetObject(); |
|
|
|
Vector targetDir = ( GetEnemy()->GetAbsOrigin() - m_hPhysicsTarget->WorldSpaceCenter() ); |
|
float targetDist = VectorNormalize( targetDir ); |
|
|
|
// Must still be close enough to our target |
|
if ( UTIL_DistApprox( WorldSpaceCenter(), m_hPhysicsTarget->WorldSpaceCenter() ) > 300 ) |
|
{ |
|
m_hPhysicsTarget = NULL; |
|
return; |
|
} |
|
|
|
if ( targetDist < 512 ) |
|
targetDist = 512; |
|
|
|
if ( targetDist > 1024 ) |
|
targetDist = 1024; |
|
|
|
targetDir *= targetDist * 3 * physObj->GetMass(); //FIXME: Scale by skill |
|
targetDir[2] += physObj->GetMass() * 350.0f; |
|
|
|
//Display dust |
|
Vector vecRandom = RandomVector( -1, 1); |
|
VectorNormalize( vecRandom ); |
|
g_pEffects->Dust( m_hPhysicsTarget->WorldSpaceCenter(), vecRandom, 64.0f, 32 ); |
|
|
|
// If it's being held by the player, break that bond |
|
Pickup_ForcePlayerToDropThisObject( m_hPhysicsTarget ); |
|
|
|
EmitSound( "ASW_Drone.Shove" ); |
|
|
|
//Send it flying |
|
AngularImpulse angVel( random->RandomFloat(-180, 180), 100, random->RandomFloat(-360, 360) ); |
|
physObj->ApplyForceCenter( targetDir ); |
|
physObj->AddVelocity( NULL, &angVel ); |
|
|
|
//Clear the state information, we're done |
|
ClearCondition( COND_ALIEN_SHOVER_PHYSICS_TARGET ); |
|
ClearCondition( COND_ALIEN_SHOVER_PHYSICS_TARGET_INVALID ); |
|
|
|
// Disrupt other objects near it |
|
ImpactShock( m_hPhysicsTarget->WorldSpaceCenter(), 150, 250.0f, m_hPhysicsTarget ); |
|
|
|
m_hPhysicsTarget = NULL; |
|
m_flPhysicsCheckTime = gpGlobals->curtime + ALIEN_SWAT_DELAY; |
|
return; |
|
} |
|
|
|
if ( nEvent == AE_ALIEN_SHOVER_SHOVE ) |
|
{ |
|
EmitSound("NPC_AntlionGuard.StepLight", pEvent->eventtime ); |
|
Shove(); |
|
return; |
|
} |
|
|
|
|
|
if ( nEvent == AE_ALIEN_SHOVER_ROAR ) |
|
{ |
|
EmitSound( "NPC_AntlionGuard.Roar" ); |
|
return; |
|
} |
|
|
|
|
|
BaseClass::HandleAnimEvent( pEvent ); |
|
} |
|
|
|
void CASW_Alien_Shover::StartTask( const Task_t *pTask ) |
|
{ |
|
switch ( pTask->iTask ) |
|
{ |
|
case TASK_ALIEN_SHOVER_SHOVE_PHYSOBJECT: |
|
{ |
|
if ( ( m_hPhysicsTarget == NULL ) || ( GetEnemy() == NULL ) ) |
|
{ |
|
TaskFail( "Tried to shove a NULL physics target!\n" ); |
|
break; |
|
} |
|
|
|
//Get the direction and distance to our thrown object |
|
Vector dirToTarget = ( m_hPhysicsTarget->WorldSpaceCenter() - WorldSpaceCenter() ); |
|
float distToTarget = VectorNormalize( dirToTarget ); |
|
dirToTarget.z = 0; |
|
|
|
//Validate it's still close enough to shove |
|
//FIXME: Real numbers |
|
if ( distToTarget > 256.0f ) |
|
{ |
|
m_hLastFailedPhysicsTarget = NULL; |
|
m_hPhysicsTarget = NULL; |
|
|
|
TaskFail( "Shove target moved\n" ); |
|
break; |
|
} |
|
|
|
//Validate its offset from our facing |
|
float targetYaw = UTIL_VecToYaw( dirToTarget ); |
|
float offset = UTIL_AngleDiff( targetYaw, UTIL_AngleMod( GetLocalAngles().y ) ); |
|
|
|
if ( fabs( offset ) > 55 ) |
|
{ |
|
m_hLastFailedPhysicsTarget = NULL; |
|
m_hPhysicsTarget = NULL; |
|
|
|
TaskFail( "Shove target off-center\n" ); |
|
break; |
|
} |
|
|
|
//Blend properly |
|
//SetPoseParameter( "throw", offset ); |
|
|
|
RemoveAllGestures(); |
|
|
|
//Start playing the animation |
|
SetActivity( ACT_ALIEN_SHOVER_SHOVE_PHYSOBJECT ); |
|
} |
|
break; |
|
|
|
case TASK_ALIEN_SHOVER_FIND_PHYSOBJECT: |
|
{ |
|
// Force the antlion guard to find a physobject |
|
m_flPhysicsCheckTime = 0; |
|
UpdatePhysicsTarget( true, 1024 ); |
|
if ( m_hPhysicsTarget ) |
|
{ |
|
TaskComplete(); |
|
} |
|
else |
|
{ |
|
TaskFail( "Failed to find a physobject.\n" ); |
|
} |
|
} |
|
break; |
|
|
|
case TASK_ALIEN_SHOVER_GET_PATH_TO_PHYSOBJECT: |
|
{ |
|
Vector vecGoalPos; |
|
Vector vecDir; |
|
|
|
if (!m_hPhysicsTarget.IsValid()) |
|
{ |
|
AI_NavGoal_t goal( GetAbsOrigin() ); |
|
GetNavigator()->SetGoal(goal); |
|
TaskComplete(); |
|
break; |
|
} |
|
|
|
vecDir = GetLocalOrigin() - m_hPhysicsTarget->GetLocalOrigin(); |
|
VectorNormalize(vecDir); |
|
vecDir.z = 0; |
|
|
|
AI_NavGoal_t goal( m_hPhysicsTarget->WorldSpaceCenter() ); |
|
goal.pTarget = m_hPhysicsTarget; |
|
GetNavigator()->SetGoal( goal ); |
|
|
|
if ( asw_debug_aliens.GetInt() == 1 ) |
|
{ |
|
NDebugOverlay::Cross3D( vecGoalPos, Vector(8,8,8), -Vector(8,8,8), 0, 255, 0, true, 2.0f ); |
|
NDebugOverlay::Line( vecGoalPos, m_hPhysicsTarget->WorldSpaceCenter(), 0, 255, 0, true, 2.0f ); |
|
NDebugOverlay::Line( m_hPhysicsTarget->WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), 0, 255, 0, true, 2.0f ); |
|
} |
|
|
|
TaskComplete(); |
|
|
|
/* |
|
if ( ( m_hPhysicsTarget == NULL ) || ( GetEnemy() == NULL ) ) |
|
{ |
|
TaskFail( "Tried to find a path to NULL physics target!\n" ); |
|
break; |
|
} |
|
|
|
Vector vecGoalPos; |
|
Vector vecDir; |
|
|
|
vecDir = GetLocalOrigin() - m_hPhysicsTarget->GetLocalOrigin(); |
|
VectorNormalize(vecDir); |
|
vecDir.z = 0; |
|
|
|
AI_NavGoal_t goal( m_hPhysicsTarget->WorldSpaceCenter() ); |
|
goal.pTarget = m_hPhysicsTarget; |
|
//GetNavigator()->SetGoal( goal ); |
|
|
|
//TaskComplete(); |
|
|
|
//Vector vecGoalPos = m_vecPhysicsHitPosition; |
|
//AI_NavGoal_t goal( GOALTYPE_LOCATION, vecGoalPos, ACT_RUN ); |
|
|
|
if ( GetNavigator()->SetGoal( goal ) ) |
|
{ |
|
if ( asw_debug_aliens.GetInt() == 1 ) |
|
{ |
|
NDebugOverlay::Cross3D( vecGoalPos, Vector(8,8,8), -Vector(8,8,8), 0, 255, 0, true, 2.0f ); |
|
NDebugOverlay::Line( vecGoalPos, m_hPhysicsTarget->WorldSpaceCenter(), 0, 255, 0, true, 2.0f ); |
|
NDebugOverlay::Line( m_hPhysicsTarget->WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), 0, 255, 0, true, 2.0f ); |
|
} |
|
|
|
// Face the enemy |
|
GetNavigator()->SetArrivalDirection( GetEnemy() ); |
|
TaskComplete(); |
|
} |
|
else |
|
{ |
|
if ( asw_debug_aliens.GetInt() == 1 ) |
|
{ |
|
NDebugOverlay::Cross3D( vecGoalPos, Vector(8,8,8), -Vector(8,8,8), 255, 0, 0, true, 2.0f ); |
|
NDebugOverlay::Line( vecGoalPos, m_hPhysicsTarget->WorldSpaceCenter(), 255, 0, 0, true, 2.0f ); |
|
NDebugOverlay::Line( m_hPhysicsTarget->WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), 255, 0, 0, true, 2.0f ); |
|
} |
|
|
|
m_hLastFailedPhysicsTarget = m_hPhysicsTarget; |
|
m_hPhysicsTarget = NULL; |
|
TaskFail( "Unable to navigate to physics attack target!\n" ); |
|
break; |
|
} |
|
*/ |
|
} |
|
break; |
|
|
|
case TASK_ALIEN_SHOVER_OPPORTUNITY_THROW: |
|
{ |
|
// If we've got some live antlions, look for a physics object to throw |
|
if ( RandomFloat(0,1) > 0.3 ) |
|
{ |
|
m_hLastFailedPhysicsTarget = NULL; |
|
UpdatePhysicsTarget( true ); |
|
if ( HasCondition( COND_ALIEN_SHOVER_PHYSICS_TARGET ) ) |
|
{ |
|
//Msg("Alien shoving from opportunity throw\n"); |
|
SetSchedule( SCHED_ALIEN_SHOVER_PHYSICS_ATTACK ); |
|
} |
|
} |
|
|
|
TaskComplete(); |
|
} |
|
break; |
|
|
|
default: |
|
BaseClass::StartTask(pTask); |
|
break; |
|
} |
|
} |
|
|
|
void CASW_Alien_Shover::RunTask( const Task_t *pTask ) |
|
{ |
|
switch (pTask->iTask) |
|
{ |
|
case TASK_ALIEN_SHOVER_SHOVE_PHYSOBJECT: |
|
|
|
if ( IsActivityFinished() ) |
|
{ |
|
TaskComplete(); |
|
} |
|
|
|
break; |
|
|
|
default: |
|
BaseClass::RunTask(pTask); |
|
break; |
|
} |
|
} |
|
|
|
void CASW_Alien_Shover::InputSetShoveTarget( inputdata_t &inputdata ) |
|
{ |
|
if ( IsAlive() == false ) |
|
return; |
|
|
|
CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, inputdata.value.String(), NULL, inputdata.pActivator, inputdata.pCaller ); |
|
|
|
if ( pTarget == NULL ) |
|
{ |
|
Warning( "**Alien Shover %s cannot find shove target %s\n", GetClassname(), inputdata.value.String() ); |
|
m_hShoveTarget = NULL; |
|
return; |
|
} |
|
|
|
m_hShoveTarget = pTarget; |
|
} |
|
|
|
|
|
void CASW_Alien_Shover::UpdatePhysicsTarget( bool bAllowFartherObjects, float flRadius ) |
|
{ |
|
if ( GetEnemy() == NULL ) |
|
return; |
|
|
|
/*if ( m_hPhysicsTarget != NULL ) |
|
{ |
|
//Check to see if it's moved too much since we first picked it up |
|
if ( UTIL_DistApprox( m_hPhysicsTarget->WorldSpaceCenter(), m_vecPhysicsTargetStartPos ) > 256.0f ) |
|
{ |
|
ClearCondition( COND_ALIEN_SHOVER_PHYSICS_TARGET ); |
|
SetCondition( COND_ALIEN_SHOVER_PHYSICS_TARGET_INVALID ); |
|
} |
|
else |
|
{ |
|
SetCondition( COND_ALIEN_SHOVER_PHYSICS_TARGET ); |
|
ClearCondition( COND_ALIEN_SHOVER_PHYSICS_TARGET_INVALID ); |
|
} |
|
|
|
if ( asw_debug_aliens.GetInt() == 3 ) |
|
{ |
|
NDebugOverlay::Cross3D( m_hPhysicsTarget->WorldSpaceCenter(), -Vector(32,32,32), Vector(32,32,32), 255, 255, 255, true, 0.1f ); |
|
} |
|
|
|
return; |
|
}*/ |
|
|
|
if ( m_flPhysicsCheckTime <= gpGlobals->curtime ) |
|
FindNearestPhysicsObject(GetMaxShoverObjectMass()); // max ma |
|
//m_hPhysicsTarget = FindPhysicsObjectTarget( GetEnemy(), flRadius, ALIEN_SHOVER_OBJECTFINDING_FOV, bAllowFartherObjects ); |
|
|
|
return; |
|
|
|
if ( m_hPhysicsTarget != NULL ) |
|
{ |
|
SetCondition( COND_ALIEN_SHOVER_PHYSICS_TARGET ); |
|
m_vecPhysicsTargetStartPos = m_hPhysicsTarget->WorldSpaceCenter(); |
|
m_hLastFailedPhysicsTarget = NULL; |
|
|
|
// We must steer around this obstacle until we've thrown it, this stops us from |
|
// shoving it out of the way while we travel there |
|
m_hPhysicsTarget->SetNavIgnore(); |
|
} |
|
|
|
m_flPhysicsCheckTime = gpGlobals->curtime + 2.0f; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Push and sweep away small mass items |
|
//----------------------------------------------------------------------------- |
|
void CASW_Alien_Shover::SweepPhysicsDebris( void ) |
|
{ |
|
CBaseEntity *pList[ALIEN_SHOVER_MAX_OBJECTS]; |
|
CBaseEntity *pObject; |
|
IPhysicsObject *pPhysObj; |
|
Vector vecDelta(128,128,8); |
|
int i; |
|
|
|
if ( asw_debug_aliens.GetInt() == 1 ) |
|
{ |
|
NDebugOverlay::Box( GetAbsOrigin(), vecDelta, -vecDelta, 255, 0, 0, true, 0.1f ); |
|
} |
|
|
|
int count = UTIL_EntitiesInBox( pList, ALIEN_SHOVER_MAX_OBJECTS, GetAbsOrigin() - vecDelta, GetAbsOrigin() + vecDelta, 0 ); |
|
|
|
for( i = 0, pObject = pList[0]; i < count; i++, pObject = pList[i] ) |
|
{ |
|
if ( pObject == NULL ) |
|
continue; |
|
|
|
// Don't ignore our shoving target |
|
if ( pObject == m_hPhysicsTarget ) |
|
continue; |
|
|
|
pPhysObj = pObject->VPhysicsGetObject(); |
|
|
|
if( pPhysObj == NULL || pPhysObj->GetMass() > GetMaxShoverObjectMass() ) |
|
continue; |
|
|
|
if ( FClassnameIs( pObject, "prop_physics" ) == false ) |
|
continue; |
|
|
|
pObject->SetNavIgnore(); |
|
} |
|
} |
|
|
|
void CASW_Alien_Shover::PrescheduleThink( void ) |
|
{ |
|
BaseClass::PrescheduleThink(); |
|
|
|
// Don't do anything after death |
|
if ( m_NPCState == NPC_STATE_DEAD ) |
|
return; |
|
|
|
|
|
// Check our current physics target |
|
if ( m_hPhysicsTarget != NULL ) |
|
{ |
|
if ( asw_debug_aliens.GetInt() == 1 ) |
|
{ |
|
NDebugOverlay::Cross3D( m_hPhysicsTarget->WorldSpaceCenter(), Vector(15,15,15), -Vector(15,15,15), 0, 255, 0, true, 0.1f ); |
|
} |
|
} |
|
|
|
// Automatically update our physics target when chasing enemies |
|
if ( IsCurSchedule( SCHED_CHASE_ENEMY ) ) |
|
{ |
|
UpdatePhysicsTarget( false ); |
|
} |
|
else if ( !IsCurSchedule( SCHED_ALIEN_SHOVER_PHYSICS_ATTACK ) ) |
|
{ |
|
ClearCondition( COND_ALIEN_SHOVER_PHYSICS_TARGET ); |
|
m_hPhysicsTarget = NULL; |
|
} |
|
|
|
//SweepPhysicsDebris(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Return the point at which the alien wants to stand on to knock the physics object at the enemy |
|
//----------------------------------------------------------------------------- |
|
Vector CASW_Alien_Shover::GetPhysicsHitPosition( CBaseEntity *pObject, Vector &vecTrajectory, float &flClearDistance ) |
|
{ |
|
Assert( GetEnemy() ); |
|
|
|
// Get the trajectory we want to knock the object along |
|
vecTrajectory = GetEnemy()->WorldSpaceCenter() - pObject->WorldSpaceCenter(); |
|
VectorNormalize( vecTrajectory ); |
|
vecTrajectory.z = 0; |
|
|
|
// Get the distance we want to be from the object when we hit it |
|
IPhysicsObject *pPhys = pObject->VPhysicsGetObject(); |
|
Vector extent = physcollision->CollideGetExtent( pPhys->GetCollide(), pObject->GetAbsOrigin(), pObject->GetAbsAngles(), -vecTrajectory ); |
|
flClearDistance = ( extent - pObject->WorldSpaceCenter() ).Length() + CollisionProp()->BoundingRadius(); // asw rem + 32.0f; |
|
return (pObject->WorldSpaceCenter() + ( vecTrajectory * -flClearDistance )); |
|
} |
|
|
|
|
|
// zombie style search for a phys object |
|
bool CASW_Alien_Shover::FindNearestPhysicsObject( int iMaxMass ) |
|
{ |
|
CBaseEntity *pList[ SHOVER_PHYSICS_SEARCH_DEPTH ]; |
|
CBaseEntity *pNearest = NULL; |
|
float flDist; |
|
IPhysicsObject *pPhysObj; |
|
int i; |
|
Vector vecDirToEnemy; |
|
Vector vecDirToObject; |
|
|
|
if ( !GetEnemy() ) |
|
{ |
|
// Can't swat, or no enemy, so no swat. |
|
m_hPhysicsTarget = NULL; |
|
return false; |
|
} |
|
|
|
vecDirToEnemy = GetEnemy()->GetAbsOrigin() - GetAbsOrigin(); |
|
float dist = VectorNormalize(vecDirToEnemy); |
|
vecDirToEnemy.z = 0; |
|
|
|
if( dist > SHOVER_PLAYER_MAX_SWAT_DIST ) |
|
{ |
|
// Player is too far away. Don't bother |
|
// trying to swat anything at them until |
|
// they are closer. |
|
return false; |
|
} |
|
|
|
float flNearestDist = MIN( dist, SHOVER_FARTHEST_PHYSICS_OBJECT * 0.5 ); |
|
Vector vecDelta( flNearestDist, flNearestDist, GetHullHeight() * 2.0 ); |
|
|
|
class CShoverSwatEntitiesEnum : public CFlaggedEntitiesEnum |
|
{ |
|
public: |
|
CShoverSwatEntitiesEnum( CBaseEntity **pList, int listMax, int iMaxMass ) |
|
: CFlaggedEntitiesEnum( pList, listMax, 0 ), |
|
m_iMaxMass( iMaxMass ) |
|
{ |
|
} |
|
|
|
virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ) |
|
{ |
|
CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() ); |
|
if ( pEntity && |
|
pEntity->VPhysicsGetObject() && |
|
pEntity->VPhysicsGetObject()->GetMass() <= m_iMaxMass && |
|
pEntity->VPhysicsGetObject()->IsAsleep() && |
|
pEntity->VPhysicsGetObject()->IsMoveable() ) |
|
{ |
|
return CFlaggedEntitiesEnum::EnumElement( pHandleEntity ); |
|
} |
|
return ITERATION_CONTINUE; |
|
} |
|
|
|
int m_iMaxMass; |
|
}; |
|
|
|
CShoverSwatEntitiesEnum swatEnum( pList, SHOVER_PHYSICS_SEARCH_DEPTH, iMaxMass ); |
|
|
|
int count = UTIL_EntitiesInBox( GetAbsOrigin() - vecDelta, GetAbsOrigin() + vecDelta, &swatEnum ); |
|
|
|
// magically know where they are |
|
Vector vecZombieKnees; |
|
CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.25f ), &vecZombieKnees ); |
|
|
|
for( i = 0 ; i < count ; i++ ) |
|
{ |
|
pPhysObj = pList[ i ]->VPhysicsGetObject(); |
|
|
|
Assert( !( !pPhysObj || pPhysObj->GetMass() > iMaxMass || !pPhysObj->IsAsleep() ) ); |
|
|
|
Vector center = pList[ i ]->WorldSpaceCenter(); |
|
flDist = UTIL_DistApprox2D( GetAbsOrigin(), center ); |
|
|
|
if( flDist >= flNearestDist ) |
|
continue; |
|
|
|
// This object is closer... but is it between the player and the zombie? |
|
vecDirToObject = pList[ i ]->WorldSpaceCenter() - GetAbsOrigin(); |
|
VectorNormalize(vecDirToObject); |
|
vecDirToObject.z = 0; |
|
|
|
if( DotProduct( vecDirToEnemy, vecDirToObject ) < 0.8 ) |
|
continue; |
|
|
|
if( flDist >= UTIL_DistApprox2D( center, GetEnemy()->GetAbsOrigin() ) ) |
|
continue; |
|
|
|
// don't swat things where the highest point is under my knees |
|
// NOTE: This is a rough test; a more exact test is going to occur below |
|
if ( (center.z + pList[i]->BoundingRadius()) < vecZombieKnees.z ) |
|
continue; |
|
|
|
// don't swat things that are over my head. |
|
if( center.z > EyePosition().z ) |
|
continue; |
|
|
|
vcollide_t *pCollide = modelinfo->GetVCollide( pList[i]->GetModelIndex() ); |
|
if (!pCollide) |
|
continue; |
|
Vector objMins, objMaxs; |
|
physcollision->CollideGetAABB( &objMins, &objMaxs, pCollide->solids[0], pList[i]->GetAbsOrigin(), pList[i]->GetAbsAngles() ); |
|
|
|
if ( objMaxs.z < vecZombieKnees.z ) |
|
continue; |
|
|
|
if ( !FVisible( pList[i] ) ) |
|
continue; |
|
|
|
// Make this the last check, since it makes a string. |
|
// Don't swat server ragdolls! |
|
if ( FClassnameIs( pList[ i ], "physics_prop_ragdoll" ) ) |
|
continue; |
|
|
|
if ( FClassnameIs( pList[ i ], "prop_ragdoll" ) ) |
|
continue; |
|
|
|
// The object must also be closer to the zombie than it is to the enemy |
|
pNearest = pList[ i ]; |
|
flNearestDist = flDist; |
|
} |
|
|
|
m_hPhysicsTarget = pNearest; |
|
|
|
if( m_hPhysicsTarget == NULL ) |
|
{ |
|
return false; |
|
} |
|
else |
|
{ |
|
return true; |
|
} |
|
} |
|
|
|
// antlion style search for a phys object |
|
CBaseEntity *CASW_Alien_Shover::FindPhysicsObjectTarget( CBaseEntity *pTarget, float radius, float targetCone, bool allowFartherObjects ) |
|
{ |
|
CBaseEntity *pNearest = NULL; |
|
|
|
// If we're allowed to look for farther objects, find the nearest object to the guard. |
|
// Otherwise, find the nearest object to the vector to the target. |
|
float flNearestDist = -1.0; |
|
if ( allowFartherObjects ) |
|
{ |
|
flNearestDist = radius; |
|
} |
|
|
|
Vector vecDirToTarget = pTarget->GetAbsOrigin() - GetAbsOrigin(); |
|
VectorNormalize( vecDirToTarget ); |
|
vecDirToTarget.z = 0; |
|
|
|
bool bDebug = asw_debug_aliens.GetInt() == 3; |
|
if ( bDebug ) |
|
{ |
|
if ( m_hLastFailedPhysicsTarget ) |
|
{ |
|
NDebugOverlay::Cross3D( m_hLastFailedPhysicsTarget->WorldSpaceCenter(), -Vector(32,32,32), Vector(32,32,32) , 255, 255, 0, true, 1.0f ); |
|
} |
|
} |
|
|
|
// Traipse through the sensed object list |
|
AISightIter_t iter; |
|
CBaseEntity *pObject; |
|
for ( pObject = GetSenses()->GetFirstSeenEntity( &iter, SEEN_MISC ); pObject; pObject = GetSenses()->GetNextSeenEntity( &iter ) ) |
|
{ |
|
// If we couldn't shove this object last time, don't try again |
|
if ( pObject == m_hLastFailedPhysicsTarget ) |
|
continue; |
|
|
|
IPhysicsObject *pPhysObj = pObject->VPhysicsGetObject(); |
|
if ( !pPhysObj ) |
|
continue; |
|
|
|
// Ignore motion disabled props |
|
if ( !pPhysObj->IsMoveable() ) |
|
continue; |
|
|
|
// Ignore physics objects that are too low to really be noticed by the player |
|
Vector vecAbsMins, vecAbsMaxs; |
|
pObject->CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs ); |
|
if ( fabs(vecAbsMaxs.z - vecAbsMins.z) < 28 ) |
|
continue; |
|
|
|
// Ignore objects moving too fast |
|
Vector velocity; |
|
pPhysObj->GetVelocity( &velocity, NULL ); |
|
if ( velocity.LengthSqr() > (16*16) ) |
|
continue; |
|
|
|
Vector center = pObject->WorldSpaceCenter(); |
|
Vector vecDirToObject = pObject->WorldSpaceCenter() - GetAbsOrigin(); |
|
VectorNormalize( vecDirToObject ); |
|
vecDirToObject.z = 0; |
|
float flDist = 0; |
|
|
|
if ( !allowFartherObjects ) |
|
{ |
|
// Validate our cone of sight |
|
if ( DotProduct( vecDirToTarget, vecDirToObject ) < targetCone ) |
|
{ |
|
if ( bDebug ) |
|
{ |
|
NDebugOverlay::Cross3D( center, Vector(15,15,15), -Vector(15,15,15), 255, 0, 255, true, 1.0f ); |
|
} |
|
|
|
continue; |
|
} |
|
|
|
// Object must be closer than our target |
|
if ( UTIL_DistApprox2D( GetAbsOrigin(), center ) > UTIL_DistApprox2D( GetAbsOrigin(), GetEnemy()->GetAbsOrigin() ) ) |
|
{ |
|
if ( bDebug ) |
|
{ |
|
NDebugOverlay::Cross3D( center, Vector(15,15,15), -Vector(15,15,15), 0, 255, 255, true, 1.0f ); |
|
} |
|
|
|
continue; |
|
} |
|
|
|
// Must be closer to the line towards the target than a previously valid object |
|
flDist = DotProduct( vecDirToTarget, vecDirToObject ); |
|
if ( flDist < flNearestDist ) |
|
{ |
|
if ( bDebug ) |
|
{ |
|
NDebugOverlay::Cross3D( center, Vector(15,15,15), -Vector(15,15,15), 255, 0, 0, true, 1.0f ); |
|
} |
|
|
|
continue; |
|
} |
|
} |
|
else |
|
{ |
|
// Must be closer than the nearest phys object |
|
flDist = UTIL_DistApprox2D( GetAbsOrigin(), center ); |
|
if ( flDist > flNearestDist ) |
|
{ |
|
if ( bDebug ) |
|
{ |
|
NDebugOverlay::Cross3D( center, Vector(15,15,15), -Vector(15,15,15), 255, 0, 0, true, 1.0f ); |
|
} |
|
|
|
continue; |
|
} |
|
} |
|
|
|
// Check for a clear shove path (roughly) |
|
trace_t tr; |
|
UTIL_TraceLine( pObject->WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); |
|
|
|
// See how close to our target we got |
|
if ( ( tr.endpos - GetEnemy()->WorldSpaceCenter() ).LengthSqr() > (256*256) ) |
|
continue; |
|
|
|
// Get the position we want to be at to swing at the object |
|
float flClearDistance; |
|
Vector vecTrajectory; |
|
Vector vecHitPosition = GetPhysicsHitPosition( pObject, vecTrajectory, flClearDistance ); |
|
Vector vecObjectPosition = pObject->WorldSpaceCenter(); |
|
|
|
if ( bDebug ) |
|
{ |
|
NDebugOverlay::Box( vecObjectPosition, NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 255, 0, 0, true, 1.0f ); |
|
NDebugOverlay::Box( vecHitPosition, NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 0, 255, 0, true, 1.0f ); |
|
} |
|
|
|
// Can we move to the spot behind the prop? |
|
UTIL_TraceHull( vecHitPosition, vecHitPosition, WorldAlignMins(), WorldAlignMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); |
|
if ( tr.startsolid || tr.allsolid || (tr.m_pEnt && tr.m_pEnt != pObject) ) |
|
{ |
|
if ( bDebug ) |
|
{ |
|
NDebugOverlay::Box( vecHitPosition, WorldAlignMins(), WorldAlignMaxs(), 255, 0, 0, 8, 1.0f ); |
|
NDebugOverlay::Line( vecObjectPosition, vecHitPosition, 255, 0, 0, true, 1.0f ); |
|
} |
|
|
|
// Can we get at it at from an angle on the side of it we're on? |
|
Vector vecUp(0,0,1); |
|
Vector vecRight; |
|
CrossProduct( vecUp, vecTrajectory, vecRight ); |
|
|
|
// Is the guard to the right? or the left? |
|
Vector vecToGuard = ( WorldSpaceCenter() - vecObjectPosition ); |
|
VectorNormalize( vecToGuard ); |
|
if ( DotProduct( vecRight, vecToGuard ) > 0 ) |
|
{ |
|
vecHitPosition = vecHitPosition + (vecRight * 64) + (vecTrajectory * 64); |
|
} |
|
else |
|
{ |
|
vecHitPosition = vecHitPosition - (vecRight * 64) + (vecTrajectory * 64); |
|
} |
|
|
|
if ( asw_debug_aliens.GetInt() == 4 ) |
|
{ |
|
NDebugOverlay::Box( vecHitPosition, NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 0, 255, 0, true, 1.0f ); |
|
NDebugOverlay::Line( vecObjectPosition, vecHitPosition, 255, 0, 0, true, 1.0f ); |
|
} |
|
|
|
// Now try and move from the side position |
|
UTIL_TraceHull( vecHitPosition, vecHitPosition, WorldAlignMins(), WorldAlignMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); |
|
if ( tr.startsolid || tr.allsolid || (tr.m_pEnt && tr.m_pEnt != pObject) ) |
|
{ |
|
if ( asw_debug_aliens.GetInt() == 4 ) |
|
{ |
|
NDebugOverlay::Box( vecHitPosition, WorldAlignMins(), WorldAlignMaxs(), 255, 0, 0, 8, 1.0f ); |
|
NDebugOverlay::Line( vecObjectPosition, vecHitPosition, 255, 0, 0, true, 1.0f ); |
|
} |
|
continue; |
|
} |
|
} |
|
|
|
// Take this as the best object so far |
|
pNearest = pObject; |
|
flNearestDist = flDist; |
|
m_vecPhysicsHitPosition = vecHitPosition; |
|
|
|
if ( bDebug ) |
|
{ |
|
NDebugOverlay::Cross3D( center, Vector(15,15,15), -Vector(15,15,15), 255, 255, 0, true, 0.5f ); |
|
} |
|
} |
|
|
|
if ( pNearest && bDebug ) |
|
{ |
|
NDebugOverlay::Cross3D( pNearest->WorldSpaceCenter(), Vector(30,30,30), -Vector(30,30,30), 255, 255, 255, true, 0.5f ); |
|
} |
|
|
|
return pNearest; |
|
} |
|
|
|
void CASW_Alien_Shover::ImpactShock( const Vector &origin, float radius, float magnitude, CBaseEntity *pIgnored ) |
|
{ |
|
// Also do a local physics explosion to push objects away |
|
float adjustedDamage, flDist; |
|
Vector vecSpot; |
|
float falloff = 1.0f / 2.5f; |
|
|
|
CBaseEntity *pEntity = NULL; |
|
|
|
// Find anything within our radius |
|
while ( ( pEntity = gEntList.FindEntityInSphere( pEntity, origin, radius ) ) != NULL ) |
|
{ |
|
// Don't affect the ignored target |
|
if ( pEntity == pIgnored ) |
|
continue; |
|
if ( pEntity == this ) |
|
continue; |
|
|
|
// UNDONE: Ask the object if it should get force if it's not MOVETYPE_VPHYSICS? |
|
if ( pEntity->GetMoveType() == MOVETYPE_VPHYSICS || ( pEntity->VPhysicsGetObject() && pEntity->IsPlayer() == false ) ) |
|
{ |
|
vecSpot = pEntity->BodyTarget( GetAbsOrigin() ); |
|
|
|
// decrease damage for an ent that's farther from the bomb. |
|
flDist = ( GetAbsOrigin() - vecSpot ).Length(); |
|
|
|
if ( radius == 0 || flDist <= radius ) |
|
{ |
|
adjustedDamage = flDist * falloff; |
|
adjustedDamage = magnitude - adjustedDamage; |
|
|
|
if ( adjustedDamage < 1 ) |
|
{ |
|
adjustedDamage = 1; |
|
} |
|
|
|
CTakeDamageInfo info( this, this, adjustedDamage, DMG_BLAST ); |
|
CalculateExplosiveDamageForce( &info, (vecSpot - GetAbsOrigin()), GetAbsOrigin() ); |
|
|
|
pEntity->VPhysicsTakeDamage( info ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
int CASW_Alien_Shover::TranslateSchedule( int scheduleType ) |
|
{ |
|
switch( scheduleType ) |
|
{ |
|
case SCHED_CHASE_ENEMY: |
|
{ |
|
return SCHED_SHOVER_CHASE_ENEMY; |
|
} |
|
break; |
|
case SCHED_CHASE_ENEMY_FAILED: |
|
{ |
|
int baseType = BaseClass::TranslateSchedule(scheduleType); |
|
if ( baseType != SCHED_CHASE_ENEMY_FAILED ) |
|
return baseType; |
|
|
|
int iUnreach = SelectUnreachableSchedule(); |
|
if (iUnreach != -1) |
|
return iUnreach; |
|
} |
|
break; |
|
case SCHED_ALIEN_SHOVER_PHYSICS_ATTACK: |
|
// If the object is far away, move and swat it. If it's close, just swat it. |
|
if (random->RandomFloat() < 0.5f) |
|
{ |
|
if( DistToPhysicsEnt() > SHOVER_PHYSOBJ_SWATDIST ) |
|
{ |
|
return SCHED_ALIEN_SHOVER_PHYSICS_ATTACK_MOVE; |
|
} |
|
else |
|
{ |
|
return SCHED_ALIEN_SHOVER_PHYSICS_ATTACK; |
|
} |
|
} |
|
else |
|
{ |
|
if( DistToPhysicsEnt() > SHOVER_PHYSOBJ_SWATDIST ) |
|
{ |
|
return SCHED_ALIEN_SHOVER_PHYSICS_ATTACKITEM_MOVE; |
|
} |
|
else |
|
{ |
|
return SCHED_ALIEN_ATTACKITEM; |
|
} |
|
|
|
} |
|
break; |
|
} |
|
|
|
return BaseClass::TranslateSchedule( scheduleType ); |
|
} |
|
|
|
void CASW_Alien_Shover::GatherConditions( void ) |
|
{ |
|
BaseClass::GatherConditions(); |
|
|
|
if( m_NPCState == NPC_STATE_COMBAT ) |
|
{ |
|
UpdatePhysicsTarget( false ); |
|
} |
|
|
|
if( (m_hPhysicsTarget != NULL) && gpGlobals->curtime >= m_flNextSwat && HasCondition( COND_SEE_ENEMY ) |
|
&& m_AlienOrders == AOT_None) // don't try and throw physics objects if we have specific alien orders to follow |
|
{ |
|
SetCondition( COND_ALIEN_SHOVER_PHYSICS_TARGET ); |
|
} |
|
else |
|
{ |
|
ClearCondition( COND_ALIEN_SHOVER_PHYSICS_TARGET ); |
|
} |
|
} |
|
|
|
int CASW_Alien_Shover::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ) |
|
{ |
|
// If we can swat physics objects, see if we can swat our obstructor |
|
if ( IsPathTaskFailure( taskFailCode ) && |
|
m_hObstructor != NULL && m_hObstructor->VPhysicsGetObject() && |
|
m_hObstructor->VPhysicsGetObject()->GetMass() < 100 ) |
|
{ |
|
m_hPhysicsTarget = m_hObstructor; |
|
m_hObstructor = NULL; |
|
//Msg("Alien shoving phys object from fail schedule\n"); |
|
return SCHED_ALIEN_ATTACKITEM; |
|
} |
|
|
|
m_hObstructor = NULL; |
|
|
|
return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode ); |
|
} |
|
|
|
bool CASW_Alien_Shover::OnInsufficientStopDist( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ) |
|
{ |
|
if ( pMoveGoal->directTrace.fStatus == AIMR_BLOCKED_ENTITY && gpGlobals->curtime >= m_flNextSwat ) |
|
{ |
|
//Msg("Alien setting obstructor\n"); |
|
m_hObstructor = pMoveGoal->directTrace.pObstruction; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
float CASW_Alien_Shover::DistToPhysicsEnt( void ) |
|
{ |
|
//return ( GetLocalOrigin() - m_hPhysicsEnt->GetLocalOrigin() ).Length(); |
|
if ( m_hPhysicsTarget != NULL ) |
|
return UTIL_DistApprox2D( GetAbsOrigin(), m_hPhysicsTarget->WorldSpaceCenter() ); |
|
return SHOVER_PHYSOBJ_SWATDIST + 1; |
|
} |
|
|
|
AI_BEGIN_CUSTOM_NPC( npc_alien_shover, CASW_Alien_Shover ) |
|
|
|
//Tasks |
|
DECLARE_TASK( TASK_ALIEN_SHOVER_GET_PATH_TO_PHYSOBJECT ) |
|
DECLARE_TASK( TASK_ALIEN_SHOVER_SHOVE_PHYSOBJECT ) |
|
DECLARE_TASK( TASK_ALIEN_SHOVER_OPPORTUNITY_THROW ) |
|
DECLARE_TASK( TASK_ALIEN_SHOVER_FIND_PHYSOBJECT ) |
|
|
|
//Activities |
|
DECLARE_ACTIVITY( ACT_ALIEN_SHOVER_SHOVE_PHYSOBJECT ) |
|
DECLARE_ACTIVITY( ACT_ALIEN_SHOVER_ROAR ) |
|
|
|
//Adrian: events go here |
|
DECLARE_ANIMEVENT( AE_ALIEN_SHOVER_SHOVE_PHYSOBJECT ) |
|
DECLARE_ANIMEVENT( AE_ALIEN_SHOVER_SHOVE ) |
|
DECLARE_ANIMEVENT( AE_ALIEN_SHOVER_ROAR ) |
|
|
|
DECLARE_CONDITION( COND_ALIEN_SHOVER_PHYSICS_TARGET ) |
|
DECLARE_CONDITION( COND_ALIEN_SHOVER_PHYSICS_TARGET_INVALID ) |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_ALIEN_ATTACKITEM, |
|
|
|
" Tasks" |
|
" TASK_FACE_ENEMY 0" |
|
" TASK_MELEE_ATTACK1 0" |
|
" " |
|
" Interrupts" |
|
" COND_ENEMY_DEAD" |
|
" COND_NEW_ENEMY" |
|
) |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_ALIEN_SHOVER_PHYSICS_ATTACKITEM_MOVE, |
|
|
|
" Tasks" |
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY" |
|
" TASK_ALIEN_SHOVER_GET_PATH_TO_PHYSOBJECT 0" |
|
" TASK_RUN_PATH 0" |
|
" TASK_WAIT_FOR_MOVEMENT 0" |
|
" TASK_FACE_ENEMY 0" |
|
" TASK_MELEE_ATTACK1 0" |
|
"" |
|
" Interrupts" |
|
" COND_TASK_FAILED" |
|
" COND_ENEMY_DEAD" |
|
" COND_LOST_ENEMY" |
|
" COND_ALIEN_SHOVER_PHYSICS_TARGET_INVALID" |
|
) |
|
|
|
//================================================== |
|
// SCHED_ALIEN_SHOVER_PHYSICS_ATTACK |
|
//================================================== |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_ALIEN_SHOVER_PHYSICS_ATTACK_MOVE, |
|
|
|
" Tasks" |
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY" |
|
" TASK_ALIEN_SHOVER_GET_PATH_TO_PHYSOBJECT 0" |
|
" TASK_RUN_PATH 0" |
|
" TASK_WAIT_FOR_MOVEMENT 0" |
|
" TASK_FACE_ENEMY 0" |
|
" TASK_ALIEN_SHOVER_SHOVE_PHYSOBJECT 0" |
|
"" |
|
" Interrupts" |
|
" COND_TASK_FAILED" |
|
" COND_ENEMY_DEAD" |
|
" COND_LOST_ENEMY" |
|
" COND_ALIEN_SHOVER_PHYSICS_TARGET_INVALID" |
|
) |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_ALIEN_SHOVER_PHYSICS_ATTACK, |
|
|
|
" Tasks" |
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY" |
|
" TASK_FACE_ENEMY 0" |
|
" TASK_ALIEN_SHOVER_SHOVE_PHYSOBJECT 0" |
|
"" |
|
" Interrupts" |
|
" COND_ENEMY_DEAD" |
|
" COND_LOST_ENEMY" |
|
) |
|
|
|
//================================================== |
|
// SCHED_FORCE_ALIEN_SHOVER_PHYSICS_ATTACK |
|
//================================================== |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_FORCE_ALIEN_SHOVER_PHYSICS_ATTACK, |
|
|
|
" Tasks" |
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ALIEN_SHOVER_CANT_ATTACK" |
|
" TASK_ALIEN_SHOVER_FIND_PHYSOBJECT 0" |
|
" TASK_SET_SCHEDULE SCHEDULE:SCHED_ALIEN_SHOVER_PHYSICS_ATTACK" |
|
"" |
|
" Interrupts" |
|
) |
|
|
|
//================================================== |
|
// SCHED_ANTLIONGUARD_ROAR |
|
//================================================== |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_ALIEN_SHOVER_ROAR, |
|
|
|
" Tasks" |
|
" TASK_STOP_MOVING 0" |
|
" TASK_FACE_ENEMY 0" |
|
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_ALIEN_SHOVER_ROAR" |
|
" " |
|
" Interrupts" |
|
) |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_ALIEN_SHOVER_CANT_ATTACK, |
|
|
|
" Tasks" |
|
" TASK_SET_ROUTE_SEARCH_TIME 5" // Spend 5 seconds trying to build a path if stuck |
|
" TASK_GET_PATH_TO_RANDOM_NODE 200" |
|
" TASK_WALK_PATH 0" |
|
" TASK_WAIT_FOR_MOVEMENT 0" |
|
" TASK_WAIT_PVS 0" |
|
"" |
|
" Interrupts" |
|
" COND_GIVE_WAY" |
|
" COND_NEW_ENEMY" |
|
) |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_SHOVER_CHASE_ENEMY, |
|
|
|
" Tasks" |
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED" |
|
//" TASK_SET_TOLERANCE_DISTANCE 24" |
|
" TASK_GET_CHASE_PATH_TO_ENEMY 300" |
|
" TASK_RUN_PATH 0" |
|
" TASK_WAIT_FOR_MOVEMENT 0" |
|
//" TASK_FACE_ENEMY 0" |
|
" " |
|
" Interrupts" |
|
" COND_NEW_ENEMY" |
|
" COND_ENEMY_DEAD" |
|
" COND_ENEMY_UNREACHABLE" |
|
" COND_CAN_RANGE_ATTACK1" |
|
" COND_CAN_MELEE_ATTACK1" |
|
" COND_CAN_RANGE_ATTACK2" |
|
" COND_CAN_MELEE_ATTACK2" |
|
" COND_TOO_CLOSE_TO_ATTACK" |
|
" COND_TASK_FAILED" |
|
" COND_ALIEN_SHOVER_PHYSICS_TARGET" |
|
) |
|
|
|
AI_END_CUSTOM_NPC() |