2024-08-29 19:18:30 -04:00
# include "cbase.h"
# include "ai_default.h"
# include "ai_task.h"
# include "ai_schedule.h"
# include "ai_node.h"
# include "ai_hull.h"
# include "ai_hint.h"
# include "ai_squad.h"
# include "ai_senses.h"
# include "ai_navigator.h"
# include "ai_motor.h"
# include "ai_behavior.h"
# include "ai_baseactor.h"
# include "ai_behavior_lead.h"
# include "ai_behavior_follow.h"
# include "ai_behavior_standoff.h"
# include "ai_behavior_assault.h"
# include "ai_playerally.h"
# include "ai_pathfinder.h"
# include "ai_link.h"
# include "soundent.h"
# include "game.h"
# include "npcevent.h"
# include "entitylist.h"
# include "activitylist.h"
# include "vstdlib/random.h"
# include "engine/IEngineSound.h"
# include "sceneentity.h"
# include "asw_marine.h"
# include "asw_player.h"
# include "asw_marine_resource.h"
# include "asw_marine_profile.h"
# include "asw_weapon.h"
# include "asw_marine_speech.h"
//#include "asw_drone.h"
# include "asw_pickup.h"
# include "asw_pickup_weapon.h"
# include "asw_gamerules.h"
# include "asw_mission_manager.h"
# include "weapon_flaregun.h"
# include "ammodef.h"
# include "asw_shareddefs.h"
# include "asw_sentry_base.h"
# include "asw_button_area.h"
# include "asw_equipment_list.h"
# include "asw_weapon_parse.h"
# include "asw_weapon_ammo_bag_shared.h"
# include "asw_fx_shared.h"
# include "asw_parasite.h"
# include "shareddefs.h"
# include "datacache/imdlcache.h"
# include "asw_bloodhound.h"
# include "beam_shared.h"
# include "rope.h"
# include "shot_manipulator.h"
# include "asw_use_area.h"
# include "asw_computer_area.h"
# include "ai_network.h"
# include "ai_networkmanager.h"
# include "asw_util_shared.h"
# include "asw_ammo.h"
# include "asw_ammo_drop.h"
# include "asw_door_area.h"
# include "asw_door.h"
# include "smartptr.h"
# include "ai_waypoint.h"
# include "particle_parse.h"
# include "asw_alien_goo_shared.h"
# include "asw_prop_physics.h"
# include "asw_weapon_heal_gun_shared.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
// anim events
int AE_MARINE_KICK ;
int AE_MARINE_UNFREEZE ;
// activities
int ACT_MARINE_GETTING_UP ;
int ACT_MARINE_LAYING_ON_FLOOR ;
ConVar asw_marine_aim_error_max ( " asw_marine_aim_error_max " , " 20.0f " , FCVAR_CHEAT , " Maximum firing error angle for AI marines with base accuracy skill \n " ) ;
ConVar asw_marine_aim_error_min ( " asw_marine_aim_error_min " , " 5.0f " , FCVAR_CHEAT , " Minimum firing error angle for AI marines with base accuracy skill \n " ) ;
// todo: have this value vary based on marine skill/level/distance
ConVar asw_marine_aim_error_decay_multiplier ( " asw_marine_aim_error_decay_multiplier " , " 0.9f " , FCVAR_CHEAT , " Value multiplied per turn to reduce aim error over time \n " ) ;
ConVar asw_blind_follow ( " asw_blind_follow " , " 0 " , FCVAR_NONE , " Set to 1 to give marines short sight range while following (old school alien swarm style) " ) ;
ConVar asw_debug_marine_aim ( " asw_debug_marine_aim " , " 0 " , FCVAR_CHEAT , " Shows debug info on marine aiming " ) ;
ConVar asw_debug_throw ( " asw_debug_throw " , " 0 " , FCVAR_CHEAT , " Show node debug info on throw visibility checks " ) ;
ConVar asw_debug_order_weld ( " asw_debug_order_weld " , " 0 " , FCVAR_DEVELOPMENTONLY , " Debug lines for ordering marines to offhand weld a door " ) ;
extern ConVar ai_lead_time ;
# define ASW_MARINE_GOO_SCAN_TIME 0.5f
// offsets and yaws for following formation
const static Vector g_MarineFollowOffset [ ] =
{
Vector ( - 60 , - 70 , 0 ) ,
Vector ( - 120 , 0 , 0 ) ,
Vector ( - 60 , 70 , 0 ) ,
Vector ( - 40 , - 80 , 0 ) ,
Vector ( - 40 , - 80 , 0 ) ,
Vector ( - 40 , - 80 , 0 ) ,
} ;
const static float g_MarineFollowDirection [ ] =
{
- 70 ,
180 ,
70 ,
90 ,
90 ,
90
} ;
bool CASW_Marine : : CreateBehaviors ( )
{
//AddBehavior( &m_ActBusyBehavior );
//AddBehavior( &m_AssaultBehavior );
//AddBehavior( &m_StandoffBehavior );
return true ;
}
// make the view cone
bool CASW_Marine : : FInViewCone ( const Vector & vecSpot )
{
Vector los = ( vecSpot - EyePosition ( ) ) ;
float flDist = los . NormalizeInPlace ( ) ;
if ( flDist < = GetCloseCombatSightRange ( ) ) // see 360 up close
return true ;
if ( GetASWOrders ( ) = = ASW_ORDER_HOLD_POSITION )
{
// otherwise check if this enemy is within our holding cone
Vector holdingDir = vec3_origin ;
QAngle holdingAng ( 0 , m_fHoldingYaw , 0 ) ;
AngleVectors ( holdingAng , & holdingDir ) ;
float flDot = DotProduct ( los , holdingDir ) ;
return ( flDot > m_flFieldOfView ) ;
}
else if ( GetASWOrders ( ) = = ASW_ORDER_FOLLOW )
{
Vector vecHead = HeadDirection2D ( ) ;
float flDot = DotProduct ( los , vecHead ) ;
return ( flDot > m_flFieldOfView ) ;
}
// see 360 otherwise
return true ;
}
float CASW_Marine : : GetFollowSightRange ( )
{
return GetSenses ( ) - > GetDistLook ( ) ;
}
float CASW_Marine : : GetCloseCombatSightRange ( )
{
if ( ASWGameRules ( ) )
{
switch ( ASWGameRules ( ) - > GetSkillLevel ( ) )
{
case 1 : return ASW_CLOSE_COMBAT_SIGHT_RANGE_EASY ; break ;
case 2 : return ASW_CLOSE_COMBAT_SIGHT_RANGE_NORMAL ; break ;
case 3 : return ASW_CLOSE_COMBAT_SIGHT_RANGE_HARD ; break ;
case 4 :
2024-08-29 20:12:35 -04:00
case 5 :
2024-08-29 19:18:30 -04:00
default : return ASW_CLOSE_COMBAT_SIGHT_RANGE_INSANE ; break ;
}
}
return ASW_CLOSE_COMBAT_SIGHT_RANGE_INSANE ;
}
bool CASW_Marine : : WeaponLOSCondition ( const Vector & ownerPos , const Vector & targetPos , bool bSetConditions )
{
if ( ! BaseClass : : WeaponLOSCondition ( ownerPos , targetPos , bSetConditions ) )
return false ;
if ( ! GetSenses ( ) )
{
if ( bSetConditions )
SetCondition ( COND_NOT_FACING_ATTACK ) ;
return false ;
}
float flDist = targetPos . DistTo ( ownerPos ) ;
// too far away?
if ( flDist > GetSenses ( ) - > GetDistLook ( ) )
{
if ( bSetConditions )
SetCondition ( COND_TOO_FAR_TO_ATTACK ) ;
return false ;
}
if ( flDist < GetCloseCombatSightRange ( ) )
{
return true ;
}
Vector vBodyDir = BodyDirection2D ( ) ;
float flDot = DotProduct ( targetPos - ownerPos , vBodyDir ) ;
// in hold mode, we can shoot if it's in our cone, or if it's very close
if ( GetASWOrders ( ) = = ASW_ORDER_HOLD_POSITION )
{
if ( flDot > ASW_HOLD_POSITION_FOV_DOT )
{
//if (bSetConditions)
//SetCondition(COND_CAN_RANGE_ATTACK1);
return true ;
}
if ( bSetConditions )
SetCondition ( COND_NOT_FACING_ATTACK ) ;
return false ;
}
if ( GetASWOrders ( ) = = ASW_ORDER_FOLLOW )
{
if ( flDot < ASW_FOLLOW_MODE_FOV_DOT )
return false ;
}
if ( flDist < GetFollowSightRange ( ) ) //&& flDot > 0.5f )
{
//if (bSetConditions)
//SetCondition(COND_CAN_RANGE_ATTACK1);
return true ;
}
if ( bSetConditions )
SetCondition ( COND_TOO_FAR_TO_ATTACK ) ;
return false ;
}
/*
int CASW_Marine : : RangeAttack1Conditions ( float flDot , float flDist )
{
if ( ! GetSenses ( ) )
return COND_NOT_FACING_ATTACK ;
// too far away?
if ( flDist > GetSenses ( ) - > GetDistLook ( ) )
{
return COND_TOO_FAR_TO_ATTACK ;
}
// in hold mode, we can shoot if it's in our cone, or if it's very close
if ( GetASWOrders ( ) = = ASW_ORDER_HOLD_POSITION )
{
if ( flDist < GetCloseCombatSightRange ( ) )
return COND_CAN_RANGE_ATTACK1 ;
if ( flDot > 0.5f )
return COND_CAN_RANGE_ATTACK1 ;
else
return COND_NOT_FACING_ATTACK ;
}
if ( flDist < GetFollowSightRange ( ) )
{
Msg ( " %f in follow mode, alien is within follow range (r=%f fw=%f \n " , gpGlobals - > curtime , flDist , GetFollowSightRange ( ) ) ;
return COND_CAN_RANGE_ATTACK1 ;
}
Msg ( " %f in follow mode, alien IS NOT in follow range (r=%f fw=%f \n " , gpGlobals - > curtime , flDist , GetFollowSightRange ( ) ) ;
// must mean we're in follow mode and it's too far away
return COND_TOO_FAR_TO_ATTACK ;
}
*/
// ========== ASW Schedule Stuff =========
int CASW_Marine : : SelectSchedule ( )
{
if ( ( HasCondition ( COND_ENEMY_DEAD ) | | HasCondition ( COND_ENEMY_OCCLUDED ) | | HasCondition ( COND_WEAPON_SIGHT_OCCLUDED ) )
& & m_fOverkillShootTime > gpGlobals - > curtime )
{
return SCHED_ASW_OVERKILL_SHOOT ;
}
if ( m_bWaitingToRappel & & ! m_bOnGround )
{
return SCHED_ASW_RAPPEL ;
}
if ( m_bPreventMovement )
return SCHED_ASW_HOLD_POSITION ;
// if we acquire a new enemy, create our aim error offset
if ( HasCondition ( COND_NEW_ENEMY ) )
SetNewAimError ( GetEnemy ( ) ) ;
ClearCondition ( COND_ASW_NEW_ORDERS ) ;
if ( HasCondition ( COND_PATH_BLOCKED_BY_PHYSICS_PROP ) | | HasCondition ( COND_COMPLETELY_OUT_OF_AMMO ) )
{
int iMeleeSchedule = SelectMeleeSchedule ( ) ;
if ( iMeleeSchedule ! = - 1 )
return iMeleeSchedule ;
}
int iOffhandSchedule = SelectOffhandItemSchedule ( ) ;
if ( iOffhandSchedule ! = - 1 )
return iOffhandSchedule ;
int iHackingSchedule = SelectHackingSchedule ( ) ;
if ( iHackingSchedule ! = - 1 )
return iHackingSchedule ;
if ( m_hUsingEntity . Get ( ) )
return SCHED_ASW_USING_OVER_TIME ;
int iHealSchedule = SelectHealSchedule ( ) ;
if ( iHealSchedule ! = - 1 )
return iHealSchedule ;
if ( HasCondition ( COND_SQUADMATE_NEEDS_AMMO ) )
{
int iResupplySchedule = SelectGiveAmmoSchedule ( ) ;
if ( iResupplySchedule ! = - 1 )
return iResupplySchedule ;
}
int iTakeAmmoSchedule = SelectTakeAmmoSchedule ( ) ;
if ( iTakeAmmoSchedule ! = - 1 )
return iTakeAmmoSchedule ;
if ( HasCondition ( COND_SQUADMATE_WANTS_AMMO ) )
{
int iResupplySchedule = SelectGiveAmmoSchedule ( ) ;
if ( iResupplySchedule ! = - 1 )
return iResupplySchedule ;
}
// === following ===
if ( GetASWOrders ( ) = = ASW_ORDER_FOLLOW )
{
int iFollowSchedule = SelectFollowSchedule ( ) ;
if ( iFollowSchedule ! = - 1 )
return iFollowSchedule ;
}
if ( GetASWOrders ( ) = = ASW_ORDER_MOVE_TO )
{ /*
if ( HasCondition ( COND_CAN_RANGE_ATTACK1 ) )
{
//Msg("Marine's select schedule returning SCHED_RANGE_ATTACK1\n");
CBaseEntity * pEnemy = GetEnemy ( ) ;
if ( pEnemy ) // don't shoot unless we actually have an enemy
return SCHED_RANGE_ATTACK1 ;
} */
// if we're already there, hold position
float dist = GetAbsOrigin ( ) . DistTo ( m_vecMoveToOrderPos ) ;
if ( dist > 30 )
{
return SCHED_ASW_MOVE_TO_ORDER_POS ;
}
SetASWOrders ( ASW_ORDER_HOLD_POSITION , m_fHoldingYaw ) ;
}
// === holding position ===
if ( HasCondition ( COND_CAN_RANGE_ATTACK1 ) )
{
//Msg("Marine's select schedule returning SCHED_RANGE_ATTACK1\n");
CBaseEntity * pEnemy = GetEnemy ( ) ;
if ( pEnemy ) // don't shoot unless we actually have an enemy
return SCHED_RANGE_ATTACK1 ;
}
//Msg("Marine's select schedule returning SCHED_ASW_HOLD_POSITION\n");
return SCHED_ASW_HOLD_POSITION ;
}
// translation of schedules
int CASW_Marine : : TranslateSchedule ( int scheduleType )
{
Assert ( scheduleType ! = SCHED_STANDOFF ) ;
Assert ( scheduleType ! = SCHED_CHASE_ENEMY ) ;
int result = CAI_PlayerAlly : : TranslateSchedule ( scheduleType ) ;
if ( result = = SCHED_RANGE_ATTACK1 )
result = SCHED_ASW_RANGE_ATTACK1 ;
return result ;
}
//-----------------------------------------------------------------------------
void CASW_Marine : : TaskFail ( AI_TaskFailureCode_t code )
{
if ( IsCurSchedule ( SCHED_ASW_MOVE_TO_ORDER_POS , false ) )
{
// if we failed to reach some ammo to pickup, then don't try to pick it up again for a while
if ( m_hTakeAmmo . Get ( ) )
{
m_hIgnoreAmmo . AddToTail ( m_hTakeAmmo ) ;
m_hTakeAmmo = NULL ;
m_flResetAmmoIgnoreListTime = gpGlobals - > curtime + 15.0f ;
}
if ( m_hTakeAmmoDrop . Get ( ) )
{
m_hIgnoreAmmo . AddToTail ( m_hTakeAmmoDrop ) ;
m_hTakeAmmoDrop = NULL ;
m_flResetAmmoIgnoreListTime = gpGlobals - > curtime + 15.0f ;
}
// change orders back to hold/follow
m_hAreaToUse = NULL ;
if ( m_bWasFollowing )
{
SetASWOrders ( ASW_ORDER_FOLLOW ) ;
}
else
{
SetASWOrders ( ASW_ORDER_HOLD_POSITION , GetHoldingYaw ( ) ) ;
}
}
else if ( IsCurSchedule ( SCHED_MELEE_ATTACK_PROP1 , false ) )
{
m_hPhysicsPropTarget = NULL ;
}
BaseClass : : TaskFail ( code ) ;
}
ConVar asw_marine_auto_hack ( " asw_marine_auto_hack " , " 0 " , FCVAR_CHEAT , " If set to 1, marine will automatically hack nearby computers and button panels " ) ;
# define AUTO_HACK_DIST 768.0f
int CASW_Marine : : SelectHackingSchedule ( )
{
if ( ! GetMarineProfile ( ) | | GetMarineProfile ( ) - > GetMarineClass ( ) ! = MARINE_CLASS_TECH )
return - 1 ;
if ( asw_marine_auto_hack . GetBool ( ) )
{
CASW_Use_Area * pClosestArea = NULL ;
float flClosestDist = FLT_MAX ;
// check for a computer hack nearby
for ( int i = 0 ; i < IASW_Use_Area_List : : AutoList ( ) . Count ( ) ; i + + )
{
CASW_Use_Area * pArea = static_cast < CASW_Use_Area * > ( IASW_Use_Area_List : : AutoList ( ) [ i ] ) ;
if ( pArea - > Classify ( ) = = CLASS_ASW_BUTTON_PANEL )
{
CASW_Button_Area * pButton = assert_cast < CASW_Button_Area * > ( pArea ) ;
if ( ! pButton - > IsLocked ( ) | | ! pButton - > HasPower ( ) )
continue ;
float flDist = GetAbsOrigin ( ) . DistTo ( pArea - > WorldSpaceCenter ( ) ) ;
if ( flDist < flClosestDist & & flDist < AUTO_HACK_DIST )
{
flClosestDist = flDist ;
pClosestArea = pArea ;
}
}
else if ( pArea - > Classify ( ) = = CLASS_ASW_COMPUTER_AREA )
{
CASW_Computer_Area * pComputer = assert_cast < CASW_Computer_Area * > ( pArea ) ;
if ( pComputer - > IsLocked ( ) | | ( pComputer - > HasDownloadObjective ( ) & & pComputer - > GetHackProgress ( ) < 1.0f ) )
{
float flDist = GetAbsOrigin ( ) . DistTo ( pArea - > WorldSpaceCenter ( ) ) ;
if ( flDist < flClosestDist & & flDist < AUTO_HACK_DIST )
{
flClosestDist = flDist ;
pClosestArea = pArea ;
}
}
}
}
if ( pClosestArea )
{
m_hAreaToUse = pClosestArea ;
}
}
if ( m_hAreaToUse . Get ( ) )
{
if ( m_hAreaToUse - > IsUsable ( this ) )
{
// notify to kill the effect in a couple of seconds
CASW_Player * pPlayer = GetCommander ( ) ;
if ( pPlayer & & pPlayer - > GetMarine ( ) )
{
CSingleUserRecipientFilter user ( pPlayer ) ;
UserMessageBegin ( user , " ASWOrderStopItemFX " ) ;
WRITE_SHORT ( entindex ( ) ) ;
WRITE_BOOL ( false ) ;
WRITE_BOOL ( false ) ;
MessageEnd ( ) ;
}
if ( m_hUsingEntity . Get ( ) = = m_hAreaToUse . Get ( ) )
{
return SCHED_ASW_USING_OVER_TIME ;
}
else
{
return SCHED_ASW_USE_AREA ;
}
}
else
{
m_vecMoveToOrderPos = m_hAreaToUse - > WorldSpaceCenter ( ) ; // TODO: hacking spot marked up in the level?
return SCHED_ASW_MOVE_TO_ORDER_POS ;
}
}
return - 1 ;
}
void CASW_Marine : : OrderHackArea ( CASW_Use_Area * pArea )
{
if ( ! pArea )
return ;
bool bHackOrdered = false ;
if ( pArea - > Classify ( ) = = CLASS_ASW_BUTTON_PANEL )
{
CASW_Button_Area * pButton = assert_cast < CASW_Button_Area * > ( pArea ) ;
if ( ! pButton - > IsLocked ( ) | | ! pButton - > HasPower ( ) )
return ;
float flDist = GetAbsOrigin ( ) . DistTo ( pArea - > WorldSpaceCenter ( ) ) ;
if ( flDist < AUTO_HACK_DIST )
{
m_hAreaToUse = pArea ;
bHackOrdered = true ;
}
}
else if ( pArea - > Classify ( ) = = CLASS_ASW_COMPUTER_AREA )
{
CASW_Computer_Area * pComputer = assert_cast < CASW_Computer_Area * > ( pArea ) ;
if ( pComputer - > IsLocked ( ) | | ( pComputer - > HasDownloadObjective ( ) & & pComputer - > GetHackProgress ( ) < 1.0f ) )
{
float flDist = GetAbsOrigin ( ) . DistTo ( pArea - > WorldSpaceCenter ( ) ) ;
if ( flDist < AUTO_HACK_DIST )
{
m_hAreaToUse = pArea ;
bHackOrdered = true ;
}
}
}
if ( bHackOrdered )
{
CASW_Player * pPlayer = GetCommander ( ) ;
if ( pPlayer & & pPlayer - > GetMarine ( ) )
{
Vector vecOrigin = pArea - > WorldSpaceCenter ( ) ;
trace_t tr ;
// Find where that leg hits the ground
UTIL_TraceLine ( vecOrigin , vecOrigin + Vector ( 0 , 0 , - 60 ) ,
MASK_SOLID_BRUSHONLY , this , COLLISION_GROUP_NONE , & tr ) ;
CSingleUserRecipientFilter user ( pPlayer ) ;
UserMessageBegin ( user , " ASWOrderUseItemFX " ) ;
WRITE_SHORT ( entindex ( ) ) ;
WRITE_SHORT ( ASW_USE_ORDER_HACK ) ;
WRITE_SHORT ( - 1 ) ;
WRITE_FLOAT ( tr . endpos . x ) ;
WRITE_FLOAT ( tr . endpos . y ) ;
WRITE_FLOAT ( tr . endpos . z ) ;
MessageEnd ( ) ;
}
}
}
int CASW_Marine : : SelectTakeAmmoSchedule ( )
{
if ( ! m_hTakeAmmo . Get ( ) & & ! m_hTakeAmmoDrop . Get ( ) )
{
if ( gpGlobals - > curtime < m_flNextAmmoScanTime )
return - 1 ;
m_flNextAmmoScanTime = gpGlobals - > curtime + 1.0f ;
if ( m_flResetAmmoIgnoreListTime ! = 0.0f & & gpGlobals - > curtime > m_flResetAmmoIgnoreListTime )
{
m_hIgnoreAmmo . Purge ( ) ;
m_flResetAmmoIgnoreListTime = 0.0f ;
}
const float flAmmoScanRadiusSqr = 384.0f * 384.0f ;
int nAmmoPickupCount = IAmmoPickupAutoList : : AutoList ( ) . Count ( ) ;
for ( int i = 0 ; i < nAmmoPickupCount ; i + + )
{
CASW_Ammo * pAmmo = static_cast < CASW_Ammo * > ( IAmmoPickupAutoList : : AutoList ( ) [ i ] ) ;
EHANDLE hAmmo = pAmmo ;
if ( m_hIgnoreAmmo . Find ( hAmmo ) ! = m_hIgnoreAmmo . InvalidIndex ( ) )
continue ;
// check ammo is nearby
if ( GetAbsOrigin ( ) . DistToSqr ( pAmmo - > GetAbsOrigin ( ) ) > flAmmoScanRadiusSqr )
continue ;
// check we need this ammo
if ( ! pAmmo - > AllowedToPickup ( this ) )
continue ;
int nCurrentCount = GetAmmoCount ( pAmmo - > GetAmmoType ( ) ) ;
// check we don't have more ammo than a human player
bool bHumanNeedsAmmo = false ;
for ( int m = 0 ; m < ASWGameResource ( ) - > GetMaxMarineResources ( ) ; m + + )
{
CASW_Marine_Resource * pMR = ASWGameResource ( ) - > GetMarineResource ( m ) ;
if ( ! pMR | | pMR = = GetMarineResource ( ) | | ! pMR - > IsInhabited ( ) | | ! pMR - > GetMarineEntity ( ) )
continue ;
if ( ! pAmmo - > AllowedToPickup ( pMR - > GetMarineEntity ( ) ) )
continue ;
if ( pMR - > GetMarineEntity ( ) - > GetAmmoCount ( pAmmo - > GetAmmoType ( ) ) < nCurrentCount )
{
bHumanNeedsAmmo = true ;
break ;
}
}
if ( bHumanNeedsAmmo )
continue ;
m_hTakeAmmo = pAmmo ;
m_hTakeAmmoDrop = NULL ;
break ;
}
// search for ammo satchel drops
if ( ! m_hTakeAmmo )
{
int nAmmoDropCount = IAmmoDropAutoList : : AutoList ( ) . Count ( ) ;
for ( int i = 0 ; i < nAmmoDropCount ; i + + )
{
CASW_Ammo_Drop * pAmmoDrop = static_cast < CASW_Ammo_Drop * > ( IAmmoDropAutoList : : AutoList ( ) [ i ] ) ;
EHANDLE hAmmoDrop = pAmmoDrop ;
EHANDLE hAmmo = pAmmoDrop ;
if ( m_hIgnoreAmmo . Find ( hAmmoDrop ) ! = m_hIgnoreAmmo . InvalidIndex ( ) )
continue ;
if ( GetAbsOrigin ( ) . DistToSqr ( pAmmoDrop - > GetAbsOrigin ( ) ) > flAmmoScanRadiusSqr )
continue ;
if ( ! pAmmoDrop - > AllowedToPickup ( this ) )
continue ;
bool bHumanNeedsAmmo = false ;
for ( int j = 0 ; j < ASWGameResource ( ) - > GetMaxMarineResources ( ) ; j + + )
{
CASW_Marine_Resource * pMR = ASWGameResource ( ) - > GetMarineResource ( j ) ;
if ( ! pMR | | pMR = = GetMarineResource ( ) | | ! pMR - > IsInhabited ( ) | | ! pMR - > GetMarineEntity ( ) )
continue ;
if ( ! pAmmoDrop - > AllowedToPickup ( pMR - > GetMarineEntity ( ) ) )
continue ;
if ( ! pAmmoDrop - > NeedsAmmoMoreThan ( this , pMR - > GetMarineEntity ( ) ) )
{
bHumanNeedsAmmo = true ;
break ;
}
}
if ( bHumanNeedsAmmo )
continue ;
m_hTakeAmmoDrop = pAmmoDrop ;
break ;
}
}
}
EHANDLE hAmmo = ( m_hTakeAmmo . Get ( ) ? m_hTakeAmmo : ( m_hTakeAmmoDrop . Get ( ) ? m_hTakeAmmoDrop : NULL ) ) ;
if ( hAmmo ! = NULL )
{
SetPoseParameter ( " move_x " , 1.0f ) ;
SetPoseParameter ( " move_y " , 0.0f ) ;
// if we're in range of the deploy spot, then use the item
if ( hAmmo - > GetAbsOrigin ( ) . DistTo ( GetAbsOrigin ( ) ) < ASW_MARINE_USE_RADIUS )
{
if ( m_hTakeAmmo . Get ( ) )
{
m_hTakeAmmo - > ActivateUseIcon ( this , ASW_USE_RELEASE_QUICK ) ;
}
else
{
m_hTakeAmmoDrop - > ActivateUseIcon ( this , ASW_USE_RELEASE_QUICK ) ;
}
m_hTakeAmmo = NULL ;
m_hTakeAmmoDrop = NULL ;
DoAnimationEvent ( PLAYERANIMEVENT_PICKUP ) ;
m_flNextAmmoScanTime = gpGlobals - > curtime + 1.0f ;
return SCHED_ASW_PICKUP_WAIT ;
}
else
{
// move to the ammo
m_vecMoveToOrderPos = hAmmo - > GetAbsOrigin ( ) ;
return SCHED_ASW_MOVE_TO_ORDER_POS ;
}
}
return - 1 ;
}
bool CASW_Marine : : OnObstructionPreSteer ( AILocalMoveGoal_t * pMoveGoal , float distClear , AIMoveResult_t * pResult )
{
SetPhysicsPropTarget ( NULL ) ;
if ( pMoveGoal - > directTrace . pObstruction )
{
CASW_Prop_Physics * pPropPhysics = dynamic_cast < CASW_Prop_Physics * > ( pMoveGoal - > directTrace . pObstruction ) ;
if ( pPropPhysics & & pPropPhysics - > m_takedamage = = DAMAGE_YES & & pPropPhysics - > m_iHealth > 0 )
{
SetPhysicsPropTarget ( pPropPhysics ) ;
}
}
return false ;
}
void CASW_Marine : : GatherConditions ( )
{
BaseClass : : GatherConditions ( ) ;
ClearCondition ( COND_PATH_BLOCKED_BY_PHYSICS_PROP ) ;
ClearCondition ( COND_PROP_DESTROYED ) ;
ClearCondition ( COND_COMPLETELY_OUT_OF_AMMO ) ;
if ( ! GetCurSchedule ( ) )
return ;
bool bClearTargets = false ;
if ( GetCurSchedule ( ) - > GetId ( ) = = GetGlobalScheduleId ( SCHED_MELEE_ATTACK_PROP1 ) )
{
if ( ! GetPhysicsPropTarget ( ) | | GetPhysicsPropTarget ( ) - > GetHealth ( ) < = 0 )
{
SetCondition ( COND_PROP_DESTROYED ) ;
bClearTargets = true ;
}
}
else
{
if ( GetPhysicsPropTarget ( ) )
{
if ( GetPhysicsPropTarget ( ) - > GetHealth ( ) > 0 )
{
SetCondition ( COND_PATH_BLOCKED_BY_PHYSICS_PROP ) ;
}
else
{
bClearTargets = true ;
}
}
}
if ( bClearTargets )
{
if ( GetEnemy ( ) = = GetPhysicsPropTarget ( ) )
{
SetEnemy ( NULL ) ;
}
SetPhysicsPropTarget ( NULL ) ;
}
int iCurSchedule = GetCurSchedule ( ) - > GetId ( ) ;
bool bRangedAttackSchedule = false ;
switch ( iCurSchedule )
{
case SCHED_RANGE_ATTACK1 :
case SCHED_RANGE_ATTACK2 :
case SCHED_SPECIAL_ATTACK1 :
case SCHED_SPECIAL_ATTACK2 :
{
bRangedAttackSchedule = true ;
}
break ;
} ;
if ( IsOutOfAmmo ( ) & & bRangedAttackSchedule )
{
SetCondition ( COND_COMPLETELY_OUT_OF_AMMO ) ;
}
}
void CASW_Marine : : BuildScheduleTestBits ( )
{
// ammo bag functionality needs to interrupt base class behaviors
SetCustomInterruptCondition ( COND_SQUADMATE_WANTS_AMMO ) ;
SetCustomInterruptCondition ( COND_SQUADMATE_NEEDS_AMMO ) ;
SetCustomInterruptCondition ( COND_PATH_BLOCKED_BY_PHYSICS_PROP ) ;
SetCustomInterruptCondition ( COND_COMPLETELY_OUT_OF_AMMO ) ;
}
int CASW_Marine : : SelectGiveAmmoSchedule ( )
{
// iterate over all teammates, looking for most needy target for ammo
CASW_Game_Resource * pGameResource = ASWGameResource ( ) ;
for ( int i = 0 ; i < pGameResource - > GetMaxMarineResources ( ) ; i + + )
{
CASW_Marine_Resource * pMarineResource = pGameResource - > GetMarineResource ( i ) ;
if ( ! pMarineResource )
continue ;
CASW_Marine * pMarine = pMarineResource - > GetMarineEntity ( ) ;
if ( ! pMarine | | ( pMarine = = this ) )
continue ;
// see if the current marine can use ammo I have
if ( CanGiveAmmoTo ( pMarine ) )
{
// $TODO: find most appropriate give ammo target a la AmmoNeed_t
m_hGiveAmmoTarget = pMarine ;
return SCHED_ASW_GIVE_AMMO ;
}
}
m_hGiveAmmoTarget = NULL ;
return - 1 ;
}
bool CASW_Marine : : CanHeal ( ) const
{
for ( int iWeapon = 0 ; iWeapon < ASW_NUM_INVENTORY_SLOTS ; iWeapon + + )
{
CASW_Weapon * pWeapon = GetASWWeapon ( iWeapon ) ;
if ( pWeapon & & pWeapon - > Classify ( ) = = CLASS_ASW_HEAL_GUN & & pWeapon - > HasAmmo ( ) )
{
return true ;
}
}
return false ;
}
# define MARINE_START_HEAL_THRESHOLD 0.65f
# define MARINE_STOP_HEAL_THRESHOLD 0.85f
int CASW_Marine : : SelectHealSchedule ( )
{
// iterate over all teammates, looking for most needy target for health
CASW_Game_Resource * pGameResource = ASWGameResource ( ) ;
for ( int i = 0 ; i < pGameResource - > GetMaxMarineResources ( ) ; i + + )
{
CASW_Marine_Resource * pMarineResource = pGameResource - > GetMarineResource ( i ) ;
if ( ! pMarineResource )
continue ;
CASW_Marine * pMarine = pMarineResource - > GetMarineEntity ( ) ;
if ( ! pMarine )
continue ;
// see if the current marine can use ammo I have
if ( CanHeal ( ) & & pMarine - > GetHealth ( ) < pMarine - > GetMaxHealth ( ) * MARINE_START_HEAL_THRESHOLD )
{
m_hHealTarget = pMarine ;
return SCHED_ASW_HEAL_MARINE ;
}
}
m_hHealTarget = NULL ;
return - 1 ;
}
void CASW_Marine : : SetASWOrders ( ASW_Orders NewOrders , float fHoldingYaw , const Vector * pOrderPos )
{
Vector vecPos = vec3_origin ;
if ( pOrderPos )
vecPos = * pOrderPos ;
if ( m_ASWOrders ! = NewOrders | | m_vecMoveToOrderPos ! = vecPos
| | ( fHoldingYaw ! = - 1 & & fHoldingYaw ! = m_fHoldingYaw ) )
{
SetCondition ( COND_ASW_NEW_ORDERS ) ;
if ( NewOrders = = ASW_ORDER_HOLD_POSITION )
{
SetDistLook ( ASW_HOLD_POSITION_SIGHT_RANGE ) ; // make marines see further when holding position
m_flFieldOfView = ASW_HOLD_POSITION_FOV_DOT ;
}
else
{
m_flFieldOfView = ASW_FOLLOW_MODE_FOV_DOT ;
if ( ! asw_blind_follow . GetBool ( ) )
{
SetDistLook ( ASW_FOLLOW_MODE_SIGHT_RANGE ) ;
SetHeightLook ( ASW_FOLLOW_MODE_SIGHT_HEIGHT ) ;
}
else
{
SetDistLook ( ASW_DUMB_FOLLOW_MODE_SIGHT_RANGE ) ;
SetHeightLook ( ASW_DUMB_FOLLOW_MODE_SIGHT_HEIGHT ) ;
}
}
}
if ( NewOrders ! = ASW_ORDER_FOLLOW )
{
GetSquadFormation ( ) - > Remove ( this , true ) ;
m_hMarineFollowTarget = NULL ;
}
m_bWasFollowing = ( NewOrders = = ASW_ORDER_FOLLOW | | ( NewOrders ! = ASW_ORDER_HOLD_POSITION & & m_ASWOrders = = ASW_ORDER_FOLLOW ) ) ; // keeps track of follow vs hold, so we can return to the right orders after completing the new ones
m_ASWOrders = NewOrders ;
m_vecMoveToOrderPos = vecPos ;
if ( fHoldingYaw ! = - 1 )
m_fHoldingYaw = fHoldingYaw ;
//Msg("Marine receives asw orders: %d\n", NewOrders);
}
void CASW_Marine : : OrdersFromPlayer ( CASW_Player * pPlayer , ASW_Orders NewOrders , CBaseEntity * pMarine , bool bChatter , float fHoldingYaw , Vector * pVecOrderPos )
{
MDLCACHE_CRITICAL_SECTION ( ) ;
// asw temp - AI ignore follow orders in SP
//if (gpGlobals->maxClients <= 1 && NewOrders == ASW_ORDER_FOLLOW)
//return;
if ( fHoldingYaw = = - 1 )
fHoldingYaw = GetAbsAngles ( ) [ YAW ] ;
m_bDoneOrderChatter = false ;
SetASWOrders ( NewOrders , fHoldingYaw , pVecOrderPos ) ;
if ( NewOrders = = ASW_ORDER_FOLLOW )
{
if ( pMarine & & pMarine - > Classify ( ) = = CLASS_ASW_MARINE )
{
// Assert( assert_cast<CASW_Marine*>( pMarine )->GetSquadFormation()->Leader() == pMarine );
CASW_Marine * pReallyMarine = assert_cast < CASW_Marine * > ( pMarine ) ;
if ( pReallyMarine ! = GetSquadLeader ( ) )
GetSquadFormation ( ) - > ChangeLeader ( pReallyMarine ) ;
if ( ! GetSquadFormation ( ) - > IsValid ( GetSquadFormation ( ) - > Find ( this ) ) )
pReallyMarine - > GetSquadFormation ( ) - > Add ( this ) ;
}
else
{
GetSquadFormation ( ) - > Remove ( this , true ) ;
}
//DoEmote(2); // make them smile on receiving orders
// make sure his move_x/y pose parameters are at full moving forwards, so the AI follow movement will detect some sequence motion when calculating goal speed
SetPoseParameter ( " move_x " , 1.0f ) ;
SetPoseParameter ( " move_y " , 0.0f ) ;
if ( bChatter & & ! IsInfested ( ) )
GetMarineSpeech ( ) - > QueueChatter ( CHATTER_USE , gpGlobals - > curtime + 0.1f , gpGlobals - > curtime + 5.0f ) ;
}
else if ( NewOrders = = ASW_ORDER_HOLD_POSITION )
{
//m_fHoldingYaw = GetAbsAngles().y;
if ( bChatter & & ! IsInfested ( ) & & ( GetFlags ( ) & FL_ONGROUND ) & & ! m_hUsingEntity . Get ( ) )
GetMarineSpeech ( ) - > QueueChatter ( CHATTER_HOLDING_POSITION , gpGlobals - > curtime + 0.1f , gpGlobals - > curtime + 5.0f ) ;
}
else if ( NewOrders = = ASW_ORDER_MOVE_TO & & pVecOrderPos ! = NULL )
{
//DoEmote(2); // make them smile on receiving orders
// make sure his move_x/y pose parameters are at full moving fowards, so the AI follow movement will detect some sequence motion when calculating goal speed
SetPoseParameter ( " move_x " , 1.0f ) ;
SetPoseParameter ( " move_y " , 0.0f ) ;
//Msg("Recieived orders to move to %f %f %f\n",
//pVecOrderPos[0], pVecOrderPos[1], pVecOrderPos[2]);
//m_vecMoveToOrderPos = *pVecOrderPos;
//Msg("m_vecMoveToOrderPos= %f %f %f\n",
// m_vecMoveToOrderPos[0], m_vecMoveToOrderPos[1], m_vecMoveToOrderPos[2]);
}
m_fCachedIdealSpeed = MaxSpeed ( ) ;
}
static void DebugWaypoint ( AI_Waypoint_t * pWay , int r , int g , int b , float flDuration = 3 )
{
if ( pWay & & pWay - > GetNext ( ) )
{
NDebugOverlay : : Line ( pWay - > GetPos ( ) , pWay - > GetNext ( ) - > GetPos ( ) , r , g , b , true , 3 ) ;
return DebugWaypoint ( pWay - > GetNext ( ) , r , g , b , flDuration ) ;
}
}
/// because AI_Waypoint_t::flPathDistGoal is broken
static float GetWaypointDistToEnd ( const Vector & vStartPos , AI_Waypoint_t * way )
{
if ( ! way ) return FLT_MAX ;
float ret = way - > GetPos ( ) . DistTo ( vStartPos ) ;
while ( way - > GetNext ( ) )
{
ret + = way - > GetNext ( ) - > GetPos ( ) . DistTo ( way - > GetPos ( ) ) ;
way = way - > GetNext ( ) ;
}
return ret ;
}
/// When a marine uses an offhand item, sometimes you need to pick a different
/// location for its use than exactly where the cursor was. And sometimes
/// you need to kibosh the use altogether. Returns true if the offhand use
/// should continue, false means it should be aborted
static bool AdjustOffhandItemDestination ( CASW_Marine * pMarine , CASW_Weapon * pWeapon , const Vector & vecDestIn , Vector * RESTRICT vecDestOut )
{
// NDebugOverlay::Cross3D( vecDestIn, 16, 255, 16, 16, true, 3 );
if ( pWeapon - > Classify ( ) = = CLASS_ASW_WELDER )
{
// find a door near the given position
CASW_Door_Area * pClosestArea = NULL ;
CASW_Door * RESTRICT pDoor = NULL ;
float flClosestDist = FLT_MAX ;
for ( int i = 0 ; i < IASW_Use_Area_List : : AutoList ( ) . Count ( ) ; i + + )
{
CASW_Use_Area * pArea = static_cast < CASW_Use_Area * > ( IASW_Use_Area_List : : AutoList ( ) [ i ] ) ;
if ( pArea - > Classify ( ) = = CLASS_ASW_DOOR_AREA )
{
CASW_Door_Area * pDoorArea = assert_cast < CASW_Door_Area * > ( pArea ) ;
float flDist = ( vecDestIn - pArea - > WorldSpaceCenter ( ) ) . Length2D ( ) ;
if ( flDist < flClosestDist & & flDist < 240 )
{
flClosestDist = flDist ;
pClosestArea = pDoorArea ;
}
}
}
if ( ! pClosestArea )
{
return false ;
}
pDoor = pClosestArea - > GetASWDoor ( ) ;
if ( ! pDoor )
{
AssertMsg ( false , " Door area is missing its door! \n " ) ;
return false ;
}
if ( asw_debug_order_weld . GetBool ( ) ) NDebugOverlay : : EntityBounds ( pDoor , 0 , 0 , 255 , 64 , 3 ) ;
// pick a point to either side of the door and build routes to them
Vector vStandPoints [ 2 ] ;
float fkickout = pMarine - > BoundingRadius ( ) * 1.1f ;
float flTolerance = pMarine - > BoundingRadius ( ) * 1.1f ;
// get the door's transform, and punch its saved "closed position" into the translate row
if ( true )
{
matrix3x4_t mat = pDoor - > EntityToWorldTransform ( ) ;
mat . SetOrigin ( pDoor - > GetClosedPosition ( ) ) ;
VectorTransform ( Vector ( fkickout , 0 , 0 ) , mat , vStandPoints [ 0 ] ) ;
VectorTransform ( Vector ( - fkickout , 0 , 0 ) , mat , vStandPoints [ 1 ] ) ;
}
else
{
pDoor - > EntityToWorldSpace ( Vector ( fkickout , 0 , 0 ) , vStandPoints + 0 ) ; // forward
pDoor - > EntityToWorldSpace ( Vector ( - fkickout , 0 , 0 ) , vStandPoints + 1 ) ;
}
CAI_Pathfinder * pathfinder = pMarine - > GetPathfinder ( ) ;
AI_Waypoint_t * routeFwd =
pathfinder - > BuildRoute ( pMarine - > GetAbsOrigin ( ) , vStandPoints [ 0 ] ,
NULL , flTolerance , NAV_GROUND , bits_BUILD_GET_CLOSE ) ;
AI_Waypoint_t * routeBack =
pathfinder - > BuildRoute ( pMarine - > GetAbsOrigin ( ) , vStandPoints [ 1 ] ,
NULL , flTolerance , NAV_GROUND , bits_BUILD_GET_CLOSE ) ;
if ( asw_debug_order_weld . GetBool ( ) )
{
if ( routeFwd )
{
NDebugOverlay : : Circle ( vStandPoints [ 0 ] , QAngle ( 90 , 0 , 0 ) , 24 , 0 , 128 , 255 , 255 , true , 3 ) ;
}
else
{
NDebugOverlay : : Circle ( vStandPoints [ 0 ] , QAngle ( 90 , 0 , 0 ) , 24 , 255 , 0 , 0 , 255 , true , 3 ) ;
}
if ( routeBack )
{
NDebugOverlay : : Circle ( vStandPoints [ 1 ] , QAngle ( 90 , 0 , 0 ) , 24 , 0 , 255 , 128 , 255 , true , 3 ) ;
}
else
{
NDebugOverlay : : Circle ( vStandPoints [ 1 ] , QAngle ( 90 , 0 , 0 ) , 24 , 255 , 0 , 0 , 255 , true , 3 ) ;
}
}
if ( ! routeBack & & ! routeFwd )
{
delete routeFwd ;
delete routeBack ;
return false ;
}
CASW_Marine * pLeader = static_cast < CASW_Marine * > ( pMarine - > GetSquadLeader ( ) ) ;
float flDists [ 2 ] ;
flDists [ 0 ] = routeFwd ? GetWaypointDistToEnd ( pMarine - > GetAbsOrigin ( ) , routeFwd ) : FLT_MAX ;
flDists [ 1 ] = routeBack ? GetWaypointDistToEnd ( pMarine - > GetAbsOrigin ( ) , routeBack ) : FLT_MAX ;
bool bUseForwardRoute = ( routeFwd & & ( ! routeBack | | flDists [ 0 ] < flDists [ 1 ] ) ) ;
if ( pLeader )
{
// move to whichever one is closer to our follow leader
CAI_Pathfinder * pathfinder = pLeader - > GetPathfinder ( ) ;
CPlainAutoPtr < AI_Waypoint_t > leaderRouteFwd (
pathfinder - > BuildRoute ( pLeader - > GetAbsOrigin ( ) , vStandPoints [ 0 ] ,
NULL , flTolerance , NAV_GROUND , bits_BUILD_GET_CLOSE ) ) ;
CPlainAutoPtr < AI_Waypoint_t > leaderRouteBack (
pathfinder - > BuildRoute ( pLeader - > GetAbsOrigin ( ) , vStandPoints [ 1 ] ,
NULL , flTolerance , NAV_GROUND , bits_BUILD_GET_CLOSE ) ) ;
float flLeaderDists [ 2 ] ;
flLeaderDists [ 0 ] = leaderRouteFwd . IsValid ( ) ? GetWaypointDistToEnd ( pLeader - > GetAbsOrigin ( ) , leaderRouteFwd . Get ( ) ) : FLT_MAX ;
flLeaderDists [ 1 ] = leaderRouteBack . IsValid ( ) ? GetWaypointDistToEnd ( pLeader - > GetAbsOrigin ( ) , leaderRouteBack . Get ( ) ) : FLT_MAX ;
if ( leaderRouteFwd . IsValid ( ) | | leaderRouteBack . IsValid ( ) )
{
bool bLeaderUseForwardRoute = ( leaderRouteFwd . IsValid ( ) & & ( ! leaderRouteBack . IsValid ( ) | | flLeaderDists [ 0 ] < flLeaderDists [ 1 ] ) ) ;
if ( bLeaderUseForwardRoute ! = bUseForwardRoute )
{
// leader is on an opposite side to the marine, push the point forward a bit more so marine clears the doorway he's running through
matrix3x4_t mat = pDoor - > EntityToWorldTransform ( ) ;
mat . SetOrigin ( pDoor - > GetClosedPosition ( ) ) ;
float fkickout = pMarine - > BoundingRadius ( ) * 2.2f ;
VectorTransform ( Vector ( fkickout , 0 , 0 ) , mat , vStandPoints [ 0 ] ) ;
VectorTransform ( Vector ( - fkickout , 0 , 0 ) , mat , vStandPoints [ 1 ] ) ;
delete routeFwd ;
delete routeBack ;
routeFwd =
pathfinder - > BuildRoute ( pMarine - > GetAbsOrigin ( ) , vStandPoints [ 0 ] ,
NULL , flTolerance , NAV_GROUND , bits_BUILD_GET_CLOSE ) ;
routeBack =
pathfinder - > BuildRoute ( pMarine - > GetAbsOrigin ( ) , vStandPoints [ 1 ] ,
NULL , flTolerance , NAV_GROUND , bits_BUILD_GET_CLOSE ) ;
if ( ! routeBack & & ! routeFwd )
{
delete routeFwd ;
delete routeBack ;
return false ;
}
flDists [ 0 ] = routeFwd ? GetWaypointDistToEnd ( pMarine - > GetAbsOrigin ( ) , routeFwd ) : FLT_MAX ;
flDists [ 1 ] = routeBack ? GetWaypointDistToEnd ( pMarine - > GetAbsOrigin ( ) , routeBack ) : FLT_MAX ;
if ( ( bLeaderUseForwardRoute & & routeFwd ) | | ( ! bLeaderUseForwardRoute & & routeBack ) )
{
bUseForwardRoute = bLeaderUseForwardRoute ;
}
}
}
}
// pick the shorter (or only existing) route
if ( bUseForwardRoute )
{
* vecDestOut = routeFwd - > GetLast ( ) - > GetPos ( ) ; // vStandPoints[0];
if ( asw_debug_order_weld . GetBool ( ) )
{
DebugWaypoint ( routeFwd , 255 , 255 , 0 ) ;
if ( routeBack )
{
Msg ( " %.3f < %.3f \n " , flDists [ 0 ] , flDists [ 1 ] ) ;
DebugWaypoint ( routeBack , 0 , 255 , 0 ) ;
}
}
}
else
{
* vecDestOut = routeBack - > GetLast ( ) - > GetPos ( ) ; // vStandPoints[1];
if ( asw_debug_order_weld . GetBool ( ) )
{
DebugWaypoint ( routeBack , 255 , 255 , 0 ) ;
if ( routeFwd )
{
Msg ( " %.3f < %.3f \n " , flDists [ 1 ] , flDists [ 0 ] ) ;
DebugWaypoint ( routeFwd , 0 , 255 , 0 ) ;
}
}
}
delete routeFwd ;
delete routeBack ;
return true ;
}
else
{
// do nothing, just copy through
* vecDestOut = vecDestIn ;
return true ;
}
}
void CASW_Marine : : OrderUseOffhandItem ( int iInventorySlot , const Vector & vecDest )
{
// check we have an item in that slot
CASW_Weapon * pWeapon = GetASWWeapon ( iInventorySlot ) ;
if ( ! pWeapon | | ! pWeapon - > GetWeaponInfo ( ) | | ! pWeapon - > GetWeaponInfo ( ) - > m_bOffhandActivate )
return ;
// m_vecOffhandItemSpot = vecDest;
if ( ! AdjustOffhandItemDestination ( this , pWeapon , vecDest , & m_vecOffhandItemSpot ) )
return ;
else if ( asw_debug_order_weld . GetBool ( ) )
{
NDebugOverlay : : Cross ( m_vecOffhandItemSpot , 12.0f , 255 , 0 , 0 , true , 3 ) ;
}
if ( pWeapon - > GetWeaponInfo ( ) - > m_nOffhandOrderType = = ASW_OFFHAND_USE_IMMEDIATELY )
{
pWeapon - > OffhandActivate ( ) ;
return ;
}
CASW_Player * pPlayer = GetCommander ( ) ;
if ( pPlayer )
{
//DispatchParticleEffect( "marine_hit_blood_ff", vecDest, QAngle( 0, 0, 0 ) );
// Tell the player's client that he's been hurt.
CSingleUserRecipientFilter user ( pPlayer ) ;
UserMessageBegin ( user , " ASWOrderUseItemFX " ) ;
WRITE_SHORT ( entindex ( ) ) ;
WRITE_SHORT ( ASW_USE_ORDER_WITH_ITEM ) ;
WRITE_SHORT ( iInventorySlot ) ;
WRITE_FLOAT ( m_vecOffhandItemSpot . x ) ;
WRITE_FLOAT ( m_vecOffhandItemSpot . y ) ;
WRITE_FLOAT ( m_vecOffhandItemSpot . z ) ;
MessageEnd ( ) ;
}
m_ASWOrders = ASW_ORDER_USE_OFFHAND_ITEM ;
m_hOffhandItemToUse = pWeapon ;
SetCondition ( COND_ASW_NEW_ORDERS ) ;
}
# define ASW_DEPLOY_RANGE 75.0f
int CASW_Marine : : SelectOffhandItemSchedule ( )
{
if ( m_bWaitingForWeld )
{
if ( gpGlobals - > curtime > m_flBeginWeldTime + 5.0f )
{
FinishedUsingOffhandItem ( false ) ;
return SCHED_ASW_FOLLOW_WAIT ;
}
return SCHED_ASW_FOLLOW_WAIT ;
}
if ( GetASWOrders ( ) ! = ASW_ORDER_USE_OFFHAND_ITEM )
return - 1 ;
if ( ! m_hOffhandItemToUse . Get ( ) )
return - 1 ;
const CASW_WeaponInfo * pInfo = m_hOffhandItemToUse - > GetWeaponInfo ( ) ;
if ( ! pInfo )
return - 1 ;
SetPoseParameter ( " move_x " , 1.0f ) ;
SetPoseParameter ( " move_y " , 0.0f ) ;
if ( pInfo - > m_nOffhandOrderType = = ASW_OFFHAND_DEPLOY )
{
// if we're in range of the deploy spot, then use the item
if ( ( m_vecOffhandItemSpot - GetAbsOrigin ( ) ) . Length2D ( ) < ASW_DEPLOY_RANGE )
{
m_hOffhandItemToUse - > OffhandActivate ( ) ;
FinishedUsingOffhandItem ( false ) ;
if ( m_bWaitingForWeld )
{
return SCHED_ASW_FOLLOW_WAIT ;
}
}
else
{
// otherwise, move to just in front of the deploy spot
Vector dir = GetAbsOrigin ( ) - m_vecOffhandItemSpot ;
dir . z = 0 ;
dir . NormalizeInPlace ( ) ;
m_vecMoveToOrderPos = m_vecOffhandItemSpot + dir * 50.0f ;
return SCHED_ASW_MOVE_TO_ORDER_POS ;
}
}
else
{
// if we have line of sight to the target spot
if ( CanThrowOffhand ( m_hOffhandItemToUse , GetOffhandThrowSource ( ) , m_vecOffhandItemSpot , asw_debug_throw . GetInt ( ) = = 3 ) )
{
m_hOffhandItemToUse - > OffhandActivate ( ) ;
FinishedUsingOffhandItem ( true ) ;
}
else
{
const float MIN_THROW_RANGE = 50.0f ;
const float MAX_THROW_RANGE = 700.0f ;
Vector pos ;
int iNode = FindThrowNode ( m_vecOffhandItemSpot , MIN_THROW_RANGE , MAX_THROW_RANGE , 1.0f ) ;
if ( iNode ! = NO_NODE )
{
// move to the spot with line of sight
m_vecMoveToOrderPos = g_pBigAINet - > GetNode ( iNode ) - > GetPosition ( GetHullType ( ) ) ;
return SCHED_ASW_MOVE_TO_ORDER_POS ;
}
else
{
// abort
Msg ( " Failed to find throw node \n " ) ;
FinishedUsingOffhandItem ( true , true ) ;
}
}
}
return - 1 ;
}
void CASW_Marine : : FinishedUsingOffhandItem ( bool bItemThrown , bool bFailed )
{
// go back to following our commander's marine
CASW_Player * pPlayer = GetCommander ( ) ;
if ( pPlayer & & pPlayer - > GetMarine ( ) )
{
OrdersFromPlayer ( pPlayer , ASW_ORDER_FOLLOW , pPlayer - > GetMarine ( ) , true ) ;
CSingleUserRecipientFilter user ( pPlayer ) ;
UserMessageBegin ( user , " ASWOrderStopItemFX " ) ;
WRITE_SHORT ( entindex ( ) ) ;
WRITE_BOOL ( bItemThrown ) ;
WRITE_BOOL ( bFailed ) ;
MessageEnd ( ) ;
}
else
{
OrdersFromPlayer ( NULL , ASW_ORDER_HOLD_POSITION , this , true , GetLocalAngles ( ) . y ) ;
}
}
// checks if our thrown offhand item will reach the destination
bool CASW_Marine : : CanThrowOffhand ( CASW_Weapon * pWeapon , const Vector & vecSrc , const Vector & vecDest , bool bDrawArc )
{
if ( ! pWeapon )
return false ;
Vector vecVelocity = UTIL_LaunchVector ( vecSrc , vecDest , pWeapon - > GetThrowGravity ( ) ) * 28.0f ;
Vector vecResult = UTIL_Check_Throw ( vecSrc , vecVelocity , pWeapon - > GetThrowGravity ( ) ,
- Vector ( 12 , 12 , 12 ) , Vector ( 12 , 12 , 12 ) , MASK_NPCSOLID , COLLISION_GROUP_PROJECTILE , this , bDrawArc ) ;
float flDist = vecResult . DistTo ( vecDest ) ;
return ( flDist < 50.0f ) ;
}
int CASW_Marine : : FindThrowNode ( const Vector & vThreatPos , float flMinThreatDist , float flMaxThreatDist , float flBlockTime )
{
if ( ! CAI_NetworkManager : : NetworksLoaded ( ) )
return NO_NODE ;
AI_PROFILE_SCOPE ( CASW_Marine : : FindThrowNode ) ;
Remember ( bits_MEMORY_TASK_EXPENSIVE ) ;
int iMyNode = GetPathfinder ( ) - > NearestNodeToNPC ( ) ;
if ( iMyNode = = NO_NODE )
{
Vector pos = GetAbsOrigin ( ) ;
DevWarning ( 2 , " FindThrowNode() - %s has no nearest node! (Check near %f %f %f) \n " , GetClassname ( ) , pos . x , pos . y , pos . z ) ;
return NO_NODE ;
}
// ------------------------------------------------------------------------------------
// We're going to search for a shoot node by expanding to our current node's neighbors
// and then their neighbors, until a shooting position is found, or all nodes are beyond MaxDist
// ------------------------------------------------------------------------------------
AI_NearNode_t * pBuffer = ( AI_NearNode_t * ) stackalloc ( sizeof ( AI_NearNode_t ) * g_pBigAINet - > NumNodes ( ) ) ;
CNodeList list ( pBuffer , g_pBigAINet - > NumNodes ( ) ) ;
CVarBitVec wasVisited ( g_pBigAINet - > NumNodes ( ) ) ; // Nodes visited
// mark start as visited
wasVisited . Set ( iMyNode ) ;
list . Insert ( AI_NearNode_t ( iMyNode , 0 ) ) ;
static int nSearchRandomizer = 0 ; // tries to ensure the links are searched in a different order each time;
while ( list . Count ( ) )
{
int nodeIndex = list . ElementAtHead ( ) . nodeIndex ;
// remove this item from the list
list . RemoveAtHead ( ) ;
const Vector & nodeOrigin = g_pBigAINet - > GetNode ( nodeIndex ) - > GetPosition ( GetHullType ( ) ) ;
// HACKHACK: Can't we rework this loop and get rid of this?
// skip the starting node, or we probably wouldn't have called this function.
if ( nodeIndex ! = iMyNode )
{
bool skip = false ;
// Don't accept climb nodes, and assume my nearest node isn't valid because
// we decided to make this check in the first place. Keep moving
if ( ! skip & & ! g_pBigAINet - > GetNode ( nodeIndex ) - > IsLocked ( ) & &
g_pBigAINet - > GetNode ( nodeIndex ) - > GetType ( ) ! = NODE_CLIMB )
{
// Now check its distance and only accept if in range
float flThreatDist = ( nodeOrigin - vThreatPos ) . Length ( ) ;
if ( flThreatDist < flMaxThreatDist & &
flThreatDist > flMinThreatDist )
{
//CAI_Node *pNode = g_pBigAINet->GetNode(nodeIndex);
if ( CanThrowOffhand ( m_hOffhandItemToUse . Get ( ) , GetOffhandThrowSource ( & nodeOrigin ) , vThreatPos , asw_debug_throw . GetInt ( ) = = 2 ) )
{
// Note when this node was used, so we don't try
// to use it again right away.
g_pBigAINet - > GetNode ( nodeIndex ) - > Lock ( flBlockTime ) ;
if ( asw_debug_throw . GetBool ( ) )
{
NDebugOverlay : : Text ( nodeOrigin , CFmtStr ( " %d:los " , nodeIndex ) , false , 1 ) ;
// draw the arc
CanThrowOffhand ( m_hOffhandItemToUse . Get ( ) , GetOffhandThrowSource ( & nodeOrigin ) , vThreatPos , true ) ;
}
// The next NPC who searches should use a slight different pattern
nSearchRandomizer = nodeIndex ;
return nodeIndex ;
}
else
{
if ( asw_debug_throw . GetBool ( ) )
{
NDebugOverlay : : Text ( nodeOrigin , CFmtStr ( " %d:!throw " , nodeIndex ) , false , 1 ) ;
}
}
}
else
{
if ( asw_debug_throw . GetBool ( ) )
{
CFmtStr msg ( " %d:%s " , nodeIndex , ( flThreatDist < flMaxThreatDist ) ? " too close " : " too far " ) ;
NDebugOverlay : : Text ( nodeOrigin , msg , false , 1 ) ;
}
}
}
}
// Go through each link and add connected nodes to the list
for ( int link = 0 ; link < g_pBigAINet - > GetNode ( nodeIndex ) - > NumLinks ( ) ; link + + )
{
int index = ( link + nSearchRandomizer ) % g_pBigAINet - > GetNode ( nodeIndex ) - > NumLinks ( ) ;
CAI_Link * nodeLink = g_pBigAINet - > GetNode ( nodeIndex ) - > GetLinkByIndex ( index ) ;
if ( ! GetPathfinder ( ) - > IsLinkUsable ( nodeLink , iMyNode ) )
continue ;
int newID = nodeLink - > DestNodeID ( nodeIndex ) ;
// If not already visited, add to the list
if ( ! wasVisited . IsBitSet ( newID ) )
{
float dist = ( GetLocalOrigin ( ) - g_pBigAINet - > GetNode ( newID ) - > GetPosition ( GetHullType ( ) ) ) . LengthSqr ( ) ;
list . Insert ( AI_NearNode_t ( newID , dist ) ) ;
wasVisited . Set ( newID ) ;
}
}
}
// We failed. No range attack node node was found
return NO_NODE ;
}
// custom follow stuff
# define ASW_FOLLOW_DISTANCE 230
# define ASW_FOLLOW_DISTANCE_NO_LOS 100
# define ASW_FORMATION_FOLLOW_DISTANCE 40
bool CASW_Marine : : NeedToUpdateSquad ( )
{
// basically, if I am a leader and I've moved, an update is needed.
return ( GetSquadLeader ( ) = = this & & GetSquadFormation ( ) - > ShouldUpdateFollowPositions ( ) ) ;
}
bool CASW_Marine : : NeedToFollowMove ( )
{
CASW_Marine * RESTRICT pLeader = GetSquadLeader ( ) ;
if ( ! pLeader | | pLeader = = this )
return false ;
if ( IsOutOfAmmo ( ) & & GetEnemy ( ) )
return false ;
// only move if we're not near our saved follow point
float dist = ( GetAbsOrigin ( ) - GetFollowPos ( ) ) . Length2DSqr ( ) ;
return dist > ( ASW_FORMATION_FOLLOW_DISTANCE * ASW_FORMATION_FOLLOW_DISTANCE ) ;
}
static bool ValidMarineMeleeTarget ( CBaseEntity * pEnt )
{
if ( ! pEnt )
return false ;
// don't punch big scary aliens
Class_T entClass = pEnt - > Classify ( ) ;
if ( entClass = = CLASS_ASW_BOOMER | | entClass = = CLASS_ASW_SHIELDBUG | |
entClass = = CLASS_ASW_HARVESTER | | entClass = = CLASS_ASW_MORTAR_BUG )
return false ;
return true ;
}
int CASW_Marine : : SelectMeleeSchedule ( )
{
if ( GetPhysicsPropTarget ( ) )
{
SetEnemy ( GetPhysicsPropTarget ( ) ) ;
return SCHED_MELEE_ATTACK_PROP1 ;
}
else if ( GetEnemy ( ) )
{
bool bLastManStanding = true ;
CASW_Game_Resource * pGameResource = ASWGameResource ( ) ;
for ( int i = 0 ; i < pGameResource - > GetMaxMarineResources ( ) ; i + + )
{
CASW_Marine_Resource * pMR = pGameResource - > GetMarineResource ( i ) ;
CASW_Marine * pMarine = pMR ? pMR - > GetMarineEntity ( ) : NULL ;
if ( pMarine & & pMarine ! = this & & pMarine - > GetHealth ( ) > 0 )
{
bLastManStanding = false ;
break ;
}
}
if ( GetHealth ( ) > GetMaxHealth ( ) * 0.5f | | bLastManStanding | | GetAbsOrigin ( ) . DistToSqr ( GetEnemyLKP ( ) ) < Square ( 100.0f ) )
{
if ( ValidMarineMeleeTarget ( GetEnemy ( ) ) )
{
return SCHED_ENGAGE_AND_MELEE_ATTACK1 ;
}
}
}
return - 1 ;
}
ConVar asw_follow_slow_distance ( " asw_follow_slow_distance " , " 1200.0f " , FCVAR_CHEAT , " Marines will follow their leader slowly during combat if he's within this distance " ) ;
int CASW_Marine : : SelectFollowSchedule ( )
{
// if we don't have anyone to follow, revert to holding position orders
if ( ! GetSquadLeader ( ) & & ASWGameRules ( ) & & gpGlobals - > curtime > ASWGameRules ( ) - > m_fMissionStartedTime + 4.0f )
{
SetASWOrders ( ASW_ORDER_HOLD_POSITION , GetAbsAngles ( ) [ YAW ] , & GetAbsOrigin ( ) ) ;
return SCHED_ASW_HOLD_POSITION ;
}
if ( NeedToFollowMove ( ) )
{
return SCHED_ASW_FOLLOW_MOVE ;
}
// shoot if we need to
if ( HasCondition ( COND_CAN_RANGE_ATTACK1 ) )
{
//Msg("Marine's select schedule returning SCHED_RANGE_ATTACK1\n");
CBaseEntity * pEnemy = GetEnemy ( ) ;
if ( pEnemy ) // don't shoot unless we actually have an enemy
return SCHED_RANGE_ATTACK1 ;
}
if ( IsOutOfAmmo ( ) & & GetEnemy ( ) )
{
SetCondition ( COND_COMPLETELY_OUT_OF_AMMO ) ;
int iMeleeSchedule = SelectMeleeSchedule ( ) ;
if ( iMeleeSchedule ! = - 1 )
return iMeleeSchedule ;
}
// check if we're too near another marine
//CASW_Marine *pCloseMarine = TooCloseToAnotherMarine();
//if (pCloseMarine)
//{
//Msg("marine is too close to %d\n", pCloseMarine->entindex());
//return SCHED_ASW_FOLLOW_BACK_OFF;
//}
return SCHED_ASW_FOLLOW_WAIT ;
}
# define ASW_MARINE_TOO_CLOSE 40
CASW_Marine * CASW_Marine : : TooCloseToAnotherMarine ( )
{
if ( ! ASWGameResource ( ) )
return NULL ;
CASW_Game_Resource * pGameResource = ASWGameResource ( ) ;
for ( int i = 0 ; i < pGameResource - > GetMaxMarineResources ( ) ; i + + )
{
CASW_Marine_Resource * pMR = pGameResource - > GetMarineResource ( i ) ;
if ( ! pMR )
continue ;
CASW_Marine * pMarine = pMR - > GetMarineEntity ( ) ;
if ( ! pMarine | | pMarine = = this )
continue ;
float dist = pMarine - > GetAbsOrigin ( ) . DistTo ( GetAbsOrigin ( ) ) ;
if ( dist < ASW_MARINE_TOO_CLOSE )
{
Msg ( " marine %d is %f away \n " , i , dist ) ;
return pMarine ;
}
}
return NULL ;
}
void CASW_Marine : : StartTask ( const Task_t * pTask )
{
switch ( pTask - > iTask )
{
case TASK_ASW_ORDER_TO_DEPLOY_SPOT :
{
CASW_Bloodhound * pDropship = dynamic_cast < CASW_Bloodhound * > ( gEntList . FindEntityByClassname ( NULL , " asw_bloodhound " ) ) ;
if ( pDropship )
pDropship - > MarineLanded ( this ) ;
TaskComplete ( ) ;
break ;
}
case TASK_ASW_FACE_HOLDING_YAW :
case TASK_ASW_FACE_USING_ITEM :
{
SetWait ( pTask - > flTaskData ) ;
SetIdealActivity ( ACT_IDLE ) ;
break ;
}
case TASK_ASW_START_USING_AREA :
{
if ( m_hAreaToUse . Get ( ) & & m_hAreaToUse - > IsUsable ( this ) )
{
m_hAreaToUse - > ActivateUseIcon ( this , ASW_USE_RELEASE_QUICK ) ;
}
TaskComplete ( ) ;
break ;
}
case TASK_ASW_CHATTER_CONFIRM :
{
if ( ! m_bDoneOrderChatter )
{
m_bDoneOrderChatter = true ;
if ( ! IsInfested ( ) & & random - > RandomFloat ( ) < = pTask - > flTaskData )
GetMarineSpeech ( ) - > QueueChatter ( CHATTER_USE , gpGlobals - > curtime + 0.1f , gpGlobals - > curtime + 5.0f , GetCommander ( ) ) ;
}
TaskComplete ( ) ;
break ;
}
case TASK_ASW_FACE_FOLLOW_WAIT :
{
SetWait ( pTask - > flTaskData ) ;
break ;
}
case TASK_ASW_FACE_ENEMY_WITH_ERROR :
{
break ;
}
case TASK_ASW_OVERKILL_SHOOT :
{
m_fStartedFiringTime = gpGlobals - > curtime ;
break ;
}
case TASK_ASW_GET_PATH_TO_ORDER_POS :
{
//NDebugOverlay::Line( GetAbsOrigin(), m_vecMoveToOrderPos, 255, 0, 0, true, 4.0f );
float flTolerance = AIN_HULL_TOLERANCE ;
if ( m_vecMoveToOrderPos = = m_vecOffhandItemSpot )
{
flTolerance = 100.0f ;
}
AI_NavGoal_t goal ( m_vecMoveToOrderPos , ACT_RUN , flTolerance ) ;
GetNavigator ( ) - > SetGoal ( goal ) ;
TaskComplete ( ) ;
break ;
}
case TASK_ASW_GET_PATH_TO_FOLLOW_TARGET :
{
if ( ! GetSquadLeader ( ) )
TaskFail ( " No follow target " ) ;
else
{
AI_NavGoal_t goal ( GetFollowPos ( ) , ACT_RUN , 60 ) ; // AIN_HULL_TOLERANCE
GetNavigator ( ) - > SetGoal ( goal ) ;
TaskComplete ( ) ;
}
}
break ;
case TASK_ASW_GET_PATH_TO_PROP :
{
if ( ! GetPhysicsPropTarget ( ) )
TaskFail ( " No prop " ) ;
else
{
AI_NavGoal_t goal ( GetPhysicsPropTarget ( ) - > WorldSpaceCenter ( ) , ACT_RUN , 60 ) ; // AIN_HULL_TOLERANCE
GetNavigator ( ) - > SetGoal ( goal ) ;
TaskComplete ( ) ;
}
}
break ;
case TASK_ASW_WAIT_FOR_FOLLOW_MOVEMENT :
case TASK_ASW_WAIT_FOR_MOVEMENT :
{
if ( GetNavigator ( ) - > GetGoalType ( ) = = GOALTYPE_NONE )
{
TaskComplete ( ) ;
GetNavigator ( ) - > ClearGoal ( ) ; // Clear residual state
}
else if ( ! GetNavigator ( ) - > IsGoalActive ( ) )
{
SetIdealActivity ( GetStoppedActivity ( ) ) ;
}
else
{
// Check validity of goal type
ValidateNavGoal ( ) ;
}
break ;
}
case TASK_ASW_GET_BACK_OFF_PATH :
{
CASW_Marine * pCloseMarine = TooCloseToAnotherMarine ( ) ;
if ( ! pCloseMarine )
TaskFail ( " No marine too close " ) ;
else
{
Vector diff = GetAbsOrigin ( ) - pCloseMarine - > GetAbsOrigin ( ) ;
Vector diffnorm = diff ;
VectorNormalize ( diffnorm ) ;
Vector vecGoalPos = GetAbsOrigin ( ) + diffnorm * 60 ; // move to x units away from this marine
AI_NavGoal_t goal ( vecGoalPos , ACT_RUN , AIN_HULL_TOLERANCE ) ;
GetNavigator ( ) - > SetGoal ( goal ) ;
TaskComplete ( ) ;
}
}
break ;
case TASK_MOVE_AWAY_PATH :
GetMotor ( ) - > SetIdealYaw ( UTIL_AngleMod ( GetLocalAngles ( ) . y - 180.0f ) ) ;
BaseClass : : StartTask ( pTask ) ;
break ;
case TASK_ASW_RAPPEL :
{
CreateZipline ( ) ;
SetDescentSpeed ( ) ;
}
break ;
case TASK_ASW_HIT_GROUND :
m_bOnGround = true ;
TaskComplete ( ) ;
break ;
case TASK_ASW_GET_PATH_TO_GIVE_AMMO :
if ( ! m_hGiveAmmoTarget )
{
TaskComplete ( ) ;
}
break ;
case TASK_ASW_MOVE_TO_GIVE_AMMO :
if ( ! m_hGiveAmmoTarget )
{
TaskComplete ( ) ;
}
break ;
case TASK_ASW_SWAP_TO_AMMO_BAG :
{
CASW_Weapon * pWeapon = GetActiveASWWeapon ( ) ;
if ( ! pWeapon | | pWeapon - > Classify ( ) ! = CLASS_ASW_AMMO_BAG )
{
for ( int iWeapon = 0 ; iWeapon < ASW_NUM_INVENTORY_SLOTS ; iWeapon + + )
{
CASW_Weapon * pWeapon = GetASWWeapon ( iWeapon ) ;
if ( pWeapon & & pWeapon - > Classify ( ) = = CLASS_ASW_AMMO_BAG )
{
Weapon_Switch ( pWeapon ) ;
break ;
}
}
}
TaskComplete ( ) ;
}
break ;
case TASK_ASW_GIVE_AMMO_TO_MARINE :
if ( m_hGiveAmmoTarget )
{
CASW_Weapon * pWeapon = GetActiveASWWeapon ( ) ;
if ( pWeapon & & pWeapon - > Classify ( ) = = CLASS_ASW_AMMO_BAG )
{
CASW_Weapon_Ammo_Bag * pAmmoBag = dynamic_cast < CASW_Weapon_Ammo_Bag * > ( pWeapon ) ;
pAmmoBag - > ThrowAmmo ( ) ;
}
}
TaskComplete ( ) ;
break ;
case TASK_MELEE_ATTACK1 :
{
if ( ! GetEnemy ( ) )
{
TaskFail ( " No enemy! " ) ;
}
else if ( GetAbsOrigin ( ) . DistToSqr ( GetEnemy ( ) - > GetAbsOrigin ( ) ) > Square ( 100.0f ) )
{
if ( GetEnemy ( ) = = GetPhysicsPropTarget ( ) )
{
SetPhysicsPropTarget ( NULL ) ;
}
TaskFail ( " Melee target too far! " ) ;
}
else
{
DoAnimationEvent ( ( PlayerAnimEvent_t ) ( PLAYERANIMEVENT_MELEE + ( int ) pTask - > flTaskData ) ) ; // send anim event to clients
BaseClass : : StartTask ( pTask ) ;
}
}
break ;
case TASK_ASW_GET_PATH_TO_HEAL :
{
if ( ! m_hHealTarget )
{
TaskFail ( FAIL_NO_GOAL ) ;
}
}
break ;
case TASK_ASW_MOVE_TO_HEAL :
{
if ( ! m_hHealTarget )
{
TaskFail ( FAIL_NO_GOAL ) ;
}
}
break ;
case TASK_ASW_SWAP_TO_HEAL_GUN :
{
CASW_Weapon * pWeapon = GetActiveASWWeapon ( ) ;
if ( ! pWeapon | | pWeapon - > Classify ( ) ! = CLASS_ASW_HEAL_GUN )
{
for ( int iWeapon = 0 ; iWeapon < ASW_NUM_INVENTORY_SLOTS ; iWeapon + + )
{
CASW_Weapon * pWeapon = GetASWWeapon ( iWeapon ) ;
if ( pWeapon & & pWeapon - > Classify ( ) = = CLASS_ASW_HEAL_GUN & & pWeapon - > HasAmmo ( ) )
{
Weapon_Switch ( pWeapon ) ;
break ;
}
}
}
TaskComplete ( ) ;
}
break ;
case TASK_ASW_HEAL_MARINE :
{
CASW_Weapon * pWeapon = GetActiveASWWeapon ( ) ;
if ( ! pWeapon | | pWeapon - > Classify ( ) ! = CLASS_ASW_HEAL_GUN | | ! pWeapon - > HasAmmo ( ) )
{
TaskComplete ( ) ;
}
else
{
CASW_Weapon_Heal_Gun * pHealgun = static_cast < CASW_Weapon_Heal_Gun * > ( pWeapon ) ;
// if the gun to longer has a target...
if ( ! pHealgun | | ! m_hHealTarget | |
m_hHealTarget - > GetHealth ( ) < = 0 | |
m_hHealTarget - > GetHealth ( ) > = m_hHealTarget - > GetMaxHealth ( ) * MARINE_STOP_HEAL_THRESHOLD | |
m_hHealTarget - > GetAbsOrigin ( ) . DistToSqr ( GetAbsOrigin ( ) ) > = Square ( CASW_Weapon_Heal_Gun : : GetWeaponRange ( ) * 0.5 ) )
{
TaskComplete ( ) ;
}
else
{
bool bHealFailed = false ;
// facing another direction
Vector vecAttach = m_hHealTarget - > GetAbsOrigin ( ) - GetAbsOrigin ( ) ;
Vector vecForward ;
AngleVectors ( ASWEyeAngles ( ) , & vecForward ) ;
if ( DotProduct ( vecForward , vecAttach ) < 0.0f )
{
TaskComplete ( ) ;
bHealFailed = true ;
}
if ( ! bHealFailed )
pHealgun - > HealAttach ( m_hHealTarget ) ;
}
}
}
break ;
default :
{
return BaseClass : : StartTask ( pTask ) ;
break ;
}
}
}
CBaseEntity * CASW_Marine : : BestAlienGooTarget ( )
{
CASW_Alien_Goo * pAlienGooTarget = NULL ;
float flClosestDistSqr = GetFollowSightRange ( ) * GetFollowSightRange ( ) ;
for ( int iAlienGoo = 0 ; iAlienGoo < g_AlienGoo . Count ( ) ; iAlienGoo + + )
{
// Get the instance of alien goo - if it exists.
CASW_Alien_Goo * pAlienGoo = g_AlienGoo [ iAlienGoo ] ;
if ( ! pAlienGoo )
continue ;
float flDistSqr = GetAbsOrigin ( ) . DistToSqr ( pAlienGoo - > GetAbsOrigin ( ) ) ;
if ( flDistSqr < flClosestDistSqr & & FInViewCone ( pAlienGoo - > GetAbsOrigin ( ) ) & & ! pAlienGoo - > m_bHasGrubs ) // grubs = decorative, not a target
{
flClosestDistSqr = flDistSqr ;
pAlienGooTarget = pAlienGoo ;
}
}
return pAlienGooTarget ;
}
bool CASW_Marine : : EngageNewAlienGooTarget ( )
{
CASW_Weapon * pActiveWeapon = GetActiveASWWeapon ( ) ;
CASW_Weapon * pFlamer = NULL ;
if ( pActiveWeapon & & pActiveWeapon - > Classify ( ) = = CLASS_ASW_FLAMER & & pActiveWeapon - > HasAmmo ( ) )
{
pFlamer = pActiveWeapon ;
}
else
{
for ( int iWeapon = 0 ; iWeapon < ASW_NUM_INVENTORY_SLOTS ; iWeapon + + )
{
CASW_Weapon * pWeapon = GetASWWeapon ( iWeapon ) ;
if ( pWeapon & & pWeapon - > Classify ( ) = = CLASS_ASW_FLAMER & & pWeapon - > HasAmmo ( ) )
{
pFlamer = pWeapon ;
break ;
}
}
}
if ( pFlamer )
{
EHANDLE hAlienGooTarget = BestAlienGooTarget ( ) ;
if ( hAlienGooTarget . Get ( ) )
{
SetAlienGooTarget ( hAlienGooTarget ) ;
// only switch weapons if we have a valid target and the weapon is usable
if ( pFlamer ! = pActiveWeapon )
{
Weapon_Switch ( pFlamer ) ;
}
return true ;
}
}
return false ;
}
const Vector & CASW_Marine : : GetEnemyLKP ( ) const
{
// Only override default behavior if we're tracking alien goo and have no other target
if ( GetAlienGooTarget ( ) & & ( ! GetEnemy ( ) | | GetAlienGooTarget ( ) = = GetEnemy ( ) ) )
{
return GetAlienGooTarget ( ) - > GetAbsOrigin ( ) ;
}
else
{
CASW_Prop_Physics * pPropPhysics = dynamic_cast < CASW_Prop_Physics * > ( GetEnemy ( ) ) ;
if ( pPropPhysics & & pPropPhysics - > m_takedamage = = DAMAGE_YES & & pPropPhysics - > m_iHealth > 0 )
{
return pPropPhysics - > GetAbsOrigin ( ) ;
}
return BaseClass : : GetEnemyLKP ( ) ;
}
}
void CASW_Marine : : RunTask ( const Task_t * pTask )
{
bool bOld = m_bWantsToFire ;
m_bWantsToFire = false ;
CheckForAIWeaponSwitch ( ) ;
// check for firing on the move, if our enemy is in range, with LOS and we're facing him
if ( GetASWOrders ( ) = = ASW_ORDER_MOVE_TO | | GetASWOrders ( ) = = ASW_ORDER_FOLLOW )
{
bool bMelee = GetCurSchedule ( ) - > GetId ( ) = = GetGlobalScheduleId ( SCHED_MELEE_ATTACK_PROP1 ) ;
if ( ! GetEnemy ( ) )
{
if ( m_flLastGooScanTime + ASW_MARINE_GOO_SCAN_TIME < gpGlobals - > curtime )
{
EngageNewAlienGooTarget ( ) ;
m_flLastGooScanTime = gpGlobals - > curtime ;
}
// base AI doesn't see biomass - Need to override every time if biomass targeted
if ( GetAlienGooTarget ( ) )
{
SetEnemy ( GetAlienGooTarget ( ) ) ;
}
else if ( bMelee & & GetPhysicsPropTarget ( ) )
{
SetEnemy ( GetPhysicsPropTarget ( ) ) ;
}
}
else if ( ! bMelee & & GetEnemy ( ) = = GetPhysicsPropTarget ( ) )
{
SetEnemy ( NULL ) ;
SetPhysicsPropTarget ( NULL ) ;
}
CASW_Weapon * pWeapon = GetActiveASWWeapon ( ) ;
bool bOffensiveWeapon = ( pWeapon & & pWeapon - > IsOffensiveWeapon ( ) ) ;
2024-08-29 19:27:02 -04:00
if ( GetEnemy ( ) )
{
Vector vecEnemyLKP = GetEnemyLKP ( ) ;
if ( bOffensiveWeapon & & ! bMelee & & FInAimCone ( vecEnemyLKP ) & &
GetAbsOrigin ( ) . DistTo ( GetEnemy ( ) - > GetAbsOrigin ( ) ) < = GetFollowSightRange ( ) )
2024-08-29 19:18:30 -04:00
{
2024-08-29 19:27:02 -04:00
// check it's a valid offensive weapon
bool bWeaponHasLOS = WeaponLOSCondition ( GetAbsOrigin ( ) , GetEnemy ( ) - > EyePosition ( ) , true ) ;
if ( ! bWeaponHasLOS )
{
bWeaponHasLOS = WeaponLOSCondition ( GetAbsOrigin ( ) , GetEnemy ( ) - > BodyTarget ( GetAbsOrigin ( ) ) , true ) ;
}
if ( bWeaponHasLOS & & ! HasCondition ( COND_ENEMY_TOO_FAR ) )
{
m_bWantsToFire = true ;
m_fMarineAimError * = asw_marine_aim_error_decay_multiplier . GetFloat ( ) ;
if ( pTask - > iTask ! = TASK_ASW_OVERKILL_SHOOT )
m_vecOverkillPos = vecEnemyLKP ;
m_fRandomFacing = UTIL_VecToYaw ( vecEnemyLKP - GetAbsOrigin ( ) ) ;
float fTwitch = float ( GetHealth ( ) ) / float ( GetMaxHealth ( ) ) ;
if ( fTwitch < 0.2 )
fTwitch = 0.2f ;
m_fNewRandomFacingTime = gpGlobals - > curtime + random - > RandomFloat ( 5 * fTwitch , 9.0f * fTwitch ) ;
}
else if ( asw_debug_marine_aim . GetBool ( ) )
{
Msg ( " Following AI not shooting enemy as we don't have weapon LOS \n " ) ;
}
2024-08-29 19:18:30 -04:00
}
else if ( asw_debug_marine_aim . GetBool ( ) )
{
2024-08-29 19:27:02 -04:00
Msg ( " Following AI not shooting enemy: InAimcone=%d FacingIdeal=%d \n " ,
FInAimCone ( vecEnemyLKP ) , FacingIdeal ( ) ) ;
2024-08-29 19:18:30 -04:00
2024-08-29 19:27:02 -04:00
Vector los = ( vecEnemyLKP - GetAbsOrigin ( ) ) ;
2024-08-29 19:18:30 -04:00
2024-08-29 19:27:02 -04:00
// do this in 2D
los . z = 0 ;
VectorNormalize ( los ) ;
2024-08-29 19:18:30 -04:00
2024-08-29 19:27:02 -04:00
Vector facingDir = BodyDirection2D ( ) ;
2024-08-29 19:18:30 -04:00
2024-08-29 19:27:02 -04:00
float flDot = DotProduct ( los , facingDir ) ;
Msg ( " los = %f %f %f (%f) " , VectorExpand ( los ) , UTIL_VecToYaw ( los ) ) ;
Msg ( " fac = %f %f %f (%f) " , VectorExpand ( facingDir ) , UTIL_VecToYaw ( facingDir ) ) ;
Msg ( " flDot = %f eye = %f \n " , flDot , UTIL_VecToYaw ( EyeDirection2D ( ) ) ) ;
2024-08-29 19:18:30 -04:00
2024-08-29 19:27:02 -04:00
NDebugOverlay : : Line ( GetAbsOrigin ( ) , GetAbsOrigin ( ) + facingDir * 200 , 0 , 0 , 255 , true , 0.1f ) ;
NDebugOverlay : : Line ( GetAbsOrigin ( ) , GetAbsOrigin ( ) + los * 200 , 255 , 0 , 0 , true , 0.1f ) ;
}
2024-08-29 19:18:30 -04:00
}
}
switch ( pTask - > iTask )
{
case TASK_ASW_FACE_HOLDING_YAW :
{
GetMotor ( ) - > SetIdealYawAndUpdate ( m_fHoldingYaw ) ;
Scan ( ) ;
if ( IsWaitFinished ( ) )
{
TaskComplete ( ) ;
}
break ;
}
case TASK_ASW_FACE_USING_ITEM :
{
if ( ! m_hUsingEntity . Get ( ) )
{
GetMotor ( ) - > SetIdealYawAndUpdate ( m_fHoldingYaw ) ;
TaskComplete ( ) ;
}
else
{
CASW_Use_Area * pArea = dynamic_cast < CASW_Use_Area * > ( m_hUsingEntity - > GetBaseEntity ( ) ) ;
Vector vecUseTarget = m_hUsingEntity . Get ( ) - > GetAbsOrigin ( ) ;
if ( pArea & & pArea - > GetProp ( ) )
vecUseTarget = pArea - > GetProp ( ) - > GetAbsOrigin ( ) ;
Vector diff = vecUseTarget - GetAbsOrigin ( ) ;
float fUsingYaw = UTIL_VecToYaw ( diff ) ;
GetMotor ( ) - > SetIdealYawAndUpdate ( fUsingYaw ) ;
}
break ;
}
case TASK_ASW_FACE_FOLLOW_WAIT :
{
UpdateFacing ( ) ;
if ( IsWaitFinished ( ) )
{
TaskComplete ( ) ;
}
break ;
}
case TASK_ASW_WAIT_FOR_FOLLOW_MOVEMENT :
{
UpdateFacing ( ) ;
bool fTimeExpired = ( pTask - > flTaskData ! = 0 & & pTask - > flTaskData < gpGlobals - > curtime - GetTimeTaskStarted ( ) ) ;
if ( fTimeExpired | | GetNavigator ( ) - > GetGoalType ( ) = = GOALTYPE_NONE )
{
TaskComplete ( ) ;
GetNavigator ( ) - > StopMoving ( ) ; // Stop moving
}
else if ( ! GetNavigator ( ) - > IsGoalActive ( ) )
{
SetIdealActivity ( GetStoppedActivity ( ) ) ;
}
else
{
// Check validity of goal type
ValidateNavGoal ( ) ;
const Vector & vecFollowPos = GetFollowPos ( ) ;
if ( ( GetNavigator ( ) - > GetGoalPos ( ) - vecFollowPos ) . LengthSqr ( ) > Square ( 60 ) )
{
if ( GetNavigator ( ) - > GetNavType ( ) ! = NAV_JUMP )
{
if ( ! GetNavigator ( ) - > UpdateGoalPos ( vecFollowPos ) )
{
TaskFail ( FAIL_NO_ROUTE ) ;
}
}
}
else if ( ! NeedToFollowMove ( ) )
{
TaskComplete ( ) ;
}
else
{
// try to keep facing towards the last known position of the enemy
//if (GetEnemy())
//{
//Vector vecEnemyLKP = GetEnemyLKP();
//AddFacingTarget( GetEnemy(), vecEnemyLKP, 1.0, 0.8 );
//Msg("follow task adding facing target\n");
//}
}
}
break ;
}
case TASK_ASW_WAIT_FOR_MOVEMENT :
{
UpdateFacing ( ) ;
if ( IsMovementFrozen ( ) )
{
TaskFail ( FAIL_FROZEN ) ;
break ;
}
bool fTimeExpired = ( pTask - > flTaskData ! = 0 & & pTask - > flTaskData < gpGlobals - > curtime - GetTimeTaskStarted ( ) ) ;
if ( fTimeExpired | | GetNavigator ( ) - > GetGoalType ( ) = = GOALTYPE_NONE )
{
TaskComplete ( ) ;
GetNavigator ( ) - > StopMoving ( ) ; // Stop moving
}
else if ( ! GetNavigator ( ) - > IsGoalActive ( ) )
{
SetIdealActivity ( GetStoppedActivity ( ) ) ;
}
else
{
// Check validity of goal type
ValidateNavGoal ( ) ;
}
break ;
}
case TASK_ASW_FACE_ENEMY_WITH_ERROR :
{
// If the yaw is locked, this function will not act correctly
Assert ( GetMotor ( ) - > IsYawLocked ( ) = = false ) ;
UpdateFacing ( ) ;
if ( FacingIdeal ( ) )
{
TaskComplete ( ) ;
}
break ;
}
case TASK_ASW_OVERKILL_SHOOT :
{
RunTaskRangeAttack1 ( pTask ) ;
break ;
}
case TASK_ASW_RAPPEL :
{
// If we don't do this, the beam won't show up sometimes. Ideally, all beams would update their
// bboxes correctly, but we're close to shipping and we can't change that now.
if ( m_hLine )
{
m_hLine - > RelinkBeam ( ) ;
}
//if( GetEnemy() )
//{
// Face the enemy if there's one.
//Vector vecEnemyLKP = GetEnemyLKP();
//GetMotor()->SetIdealYawToTargetAndUpdate( vecEnemyLKP );
//}
SetDescentSpeed ( ) ;
if ( GetFlags ( ) & FL_ONGROUND )
{
//m_OnRappelTouchdown.FireOutput( GetOuter(), GetOuter(), 0 );
RemoveFlag ( FL_FLY ) ;
CutZipline ( ) ;
TaskComplete ( ) ;
}
}
break ;
case TASK_ASW_GET_PATH_TO_GIVE_AMMO :
{
if ( m_hGiveAmmoTarget )
{
AI_NavGoal_t goal ( m_hGiveAmmoTarget - > GetAbsOrigin ( ) , ACT_RUN , CASW_Weapon_Ammo_Bag : : GetAIMaxAmmoGiveDistance ( ) ) ;
GetNavigator ( ) - > SetGoal ( goal ) ;
}
TaskComplete ( ) ;
}
break ;
case TASK_ASW_MOVE_TO_GIVE_AMMO :
// if target is still valid, move to target
if ( m_hGiveAmmoTarget )
{
UpdateFacing ( ) ;
bool bTimeExpired = ( pTask - > flTaskData ! = 0 & & pTask - > flTaskData < gpGlobals - > curtime - GetTimeTaskStarted ( ) ) ;
bool bArrived = ( ( GetNavigator ( ) - > GetGoalPos ( ) - GetAbsOrigin ( ) ) . LengthSqr ( ) < Square ( CASW_Weapon_Ammo_Bag : : GetAIMaxAmmoGiveDistance ( ) ) ) ;
if ( bTimeExpired | | GetNavigator ( ) - > GetGoalType ( ) = = GOALTYPE_NONE | | bArrived )
{
TaskComplete ( ) ;
GetNavigator ( ) - > StopMoving ( ) ;
}
else if ( ! GetNavigator ( ) - > IsGoalActive ( ) )
{
SetIdealActivity ( GetStoppedActivity ( ) ) ;
}
else
{
// Check validity of goal type
ValidateNavGoal ( ) ;
// see if target marine has moved far enough that we need to adjust path
const Vector & vecCurrentPos = m_hGiveAmmoTarget - > GetAbsOrigin ( ) ;
if ( ( GetNavigator ( ) - > GetGoalPos ( ) - vecCurrentPos ) . LengthSqr ( ) > Square ( CASW_Weapon_Ammo_Bag : : GetAIMaxAmmoGiveDistance ( ) ) )
{
if ( GetNavigator ( ) - > GetNavType ( ) ! = NAV_JUMP )
{
if ( ! GetNavigator ( ) - > UpdateGoalPos ( vecCurrentPos ) )
{
TaskFail ( FAIL_NO_ROUTE ) ;
}
}
}
}
}
else
// otherwise, the move task is complete (either we've arrived or the target is invalid)
{
m_hGiveAmmoTarget = NULL ;
TaskComplete ( ) ;
}
break ;
case TASK_ASW_GET_PATH_TO_HEAL :
{
if ( m_hHealTarget )
{
AI_NavGoal_t goal ( m_hHealTarget - > GetAbsOrigin ( ) , ACT_RUN , CASW_Weapon_Heal_Gun : : GetWeaponRange ( ) * 0.5f ) ;
GetNavigator ( ) - > SetGoal ( goal ) ;
}
TaskComplete ( ) ;
}
break ;
case TASK_ASW_MOVE_TO_HEAL :
{
// if target is still valid, move to target
if ( m_hHealTarget )
{
UpdateFacing ( ) ;
bool bTimeExpired = ( pTask - > flTaskData ! = 0 & & pTask - > flTaskData < gpGlobals - > curtime - GetTimeTaskStarted ( ) ) ;
bool bArrived = ( ( GetNavigator ( ) - > GetGoalPos ( ) - GetAbsOrigin ( ) ) . LengthSqr ( ) < Square ( CASW_Weapon_Heal_Gun : : GetWeaponRange ( ) * 0.5f ) ) ;
if ( bTimeExpired | | GetNavigator ( ) - > GetGoalType ( ) = = GOALTYPE_NONE | | bArrived )
{
TaskComplete ( ) ;
GetNavigator ( ) - > StopMoving ( ) ;
}
else if ( ! GetNavigator ( ) - > IsGoalActive ( ) )
{
SetIdealActivity ( GetStoppedActivity ( ) ) ;
}
else
{
// Check validity of goal type
ValidateNavGoal ( ) ;
// see if target marine has moved far enough that we need to adjust path
const Vector & vecCurrentPos = m_hHealTarget - > GetAbsOrigin ( ) ;
if ( ( GetNavigator ( ) - > GetGoalPos ( ) - vecCurrentPos ) . LengthSqr ( ) > Square ( CASW_Weapon_Heal_Gun : : GetWeaponRange ( ) * 0.5f ) )
{
if ( GetNavigator ( ) - > GetNavType ( ) ! = NAV_JUMP )
{
if ( ! GetNavigator ( ) - > UpdateGoalPos ( vecCurrentPos ) )
{
TaskFail ( FAIL_NO_ROUTE ) ;
}
}
}
}
}
else
// otherwise, the move task is complete (either we've arrived or the target is invalid)
{
m_hHealTarget = NULL ;
TaskComplete ( ) ;
}
}
break ;
case TASK_ASW_SWAP_TO_HEAL_GUN :
{
// this is done in StartTask
}
break ;
case TASK_ASW_HEAL_MARINE :
{
CASW_Weapon * pWeapon = GetActiveASWWeapon ( ) ;
if ( ! m_hHealTarget | | ! pWeapon | | pWeapon - > Classify ( ) ! = CLASS_ASW_HEAL_GUN | | ! pWeapon - > HasAmmo ( ) )
{
TaskComplete ( ) ;
}
else
{
CASW_Weapon_Heal_Gun * pHealgun = static_cast < CASW_Weapon_Heal_Gun * > ( pWeapon ) ;
bool bHealSucceeded = true ;
// if the gun to longer has a target...
if ( m_hHealTarget - > GetHealth ( ) < = 0 | |
m_hHealTarget - > GetHealth ( ) > = m_hHealTarget - > GetMaxHealth ( ) * MARINE_STOP_HEAL_THRESHOLD )
{
TaskComplete ( ) ;
bHealSucceeded = false ;
}
else
{
if ( m_hHealTarget - > GetAbsOrigin ( ) . DistToSqr ( GetAbsOrigin ( ) ) > = Square ( CASW_Weapon_Heal_Gun : : GetWeaponRange ( ) * 0.75 ) )
{
TaskComplete ( ) ;
}
else
{
// facing another direction
Vector vecAttach = m_hHealTarget - > GetAbsOrigin ( ) - GetAbsOrigin ( ) ;
Vector vecForward ;
AngleVectors ( ASWEyeAngles ( ) , & vecForward ) ;
if ( DotProduct ( vecForward , vecAttach ) < 0.0f )
{
TaskComplete ( ) ;
}
}
}
if ( ! pHealgun | | ! pHealgun - > HasHealAttachTarget ( ) )
{
bHealSucceeded = false ;
TaskComplete ( ) ;
}
if ( bHealSucceeded )
pHealgun - > PrimaryAttack ( ) ;
}
}
break ;
default :
{
BaseClass : : RunTask ( pTask ) ;
}
}
if ( bOld & & ! m_bWantsToFire )
{
//Msg("Stopped firing\n");
}
}
void CASW_Marine : : CheckForAIWeaponSwitch ( )
{
if ( ! GetEnemy ( ) )
return ;
CASW_Weapon * pWeapon = GetActiveASWWeapon ( ) ;
if ( pWeapon & & pWeapon - > IsOffensiveWeapon ( ) & & pWeapon - > HasPrimaryAmmo ( ) )
return ;
// see if any of our other inventory items are valid weapons
for ( int i = 0 ; i < ASW_NUM_INVENTORY_SLOTS ; i + + )
{
CASW_Weapon * pOtherWeapon = GetASWWeapon ( i ) ;
if ( pOtherWeapon ! = pWeapon & & pOtherWeapon & & pOtherWeapon - > IsOffensiveWeapon ( ) & & pOtherWeapon - > HasPrimaryAmmo ( ) )
{
Weapon_Switch ( pOtherWeapon ) ;
m_iHurtWithoutOffensiveWeapon = 0 ;
return ;
}
}
// if we have no guns with primary ammo, search for one with secondary ammo
for ( int i = 0 ; i < ASW_NUM_INVENTORY_SLOTS ; i + + )
{
CASW_Weapon * pOtherWeapon = GetASWWeapon ( i ) ;
if ( pOtherWeapon ! = pWeapon & & pOtherWeapon & & pOtherWeapon - > IsOffensiveWeapon ( ) & & pOtherWeapon - > HasAmmo ( ) )
{
Weapon_Switch ( pOtherWeapon ) ;
m_iHurtWithoutOffensiveWeapon = 0 ;
return ;
}
}
}
# define ASW_OVERKILL_TIME random->RandomFloat(0.5f,1.0f)
void CASW_Marine : : RunTaskRangeAttack1 ( const Task_t * pTask )
{
m_bWantsToFire = true ;
AutoMovement ( ) ;
Vector vecEnemyLKP = GetEnemyLKP ( ) ;
float fAimYaw ;
if ( pTask - > iTask ! = TASK_ASW_OVERKILL_SHOOT )
fAimYaw = CalcIdealYaw ( vecEnemyLKP ) ;
else
fAimYaw = CalcIdealYaw ( m_vecOverkillPos ) ;
//add in our aim error
fAimYaw + = m_fMarineAimError ;
// reduce the aim error
m_fMarineAimError * = asw_marine_aim_error_decay_multiplier . GetFloat ( ) ;
// If our enemy was killed, but I'm not done animating, the last known position comes
// back as the origin and makes the me face the world origin if my attack schedule
// doesn't break when my enemy dies. (sjb)
if ( vecEnemyLKP ! = vec3_origin )
{
if ( ( pTask - > iTask = = TASK_RANGE_ATTACK1 | | pTask - > iTask = = TASK_RELOAD | | pTask - > iTask = = TASK_ASW_OVERKILL_SHOOT ) & &
( CapabilitiesGet ( ) & bits_CAP_AIM_GUN ) & &
FInAimCone ( vecEnemyLKP ) )
{
GetMotor ( ) - > SetIdealYawAndUpdate ( fAimYaw , AI_KEEP_YAW_SPEED ) ;
if ( pTask - > iTask ! = TASK_ASW_OVERKILL_SHOOT )
m_fOverkillShootTime = gpGlobals - > curtime + ASW_OVERKILL_TIME ;
}
else
{
GetMotor ( ) - > SetIdealYawAndUpdate ( fAimYaw , AI_KEEP_YAW_SPEED ) ;
}
if ( pTask - > iTask ! = TASK_ASW_OVERKILL_SHOOT )
{
m_vecOverkillPos = vecEnemyLKP ;
}
}
if ( pTask - > iTask ! = TASK_ASW_OVERKILL_SHOOT )
{
if ( gpGlobals - > curtime > m_fStartedFiringTime + 0.3f )
{
TaskComplete ( ) ;
return ;
}
if ( HasCondition ( COND_ENEMY_DEAD )
| | HasCondition ( COND_ENEMY_OCCLUDED )
| | HasCondition ( COND_WEAPON_BLOCKED_BY_FRIEND )
| | HasCondition ( COND_WEAPON_SIGHT_OCCLUDED )
| | HasCondition ( COND_ASW_NEW_ORDERS ) )
{
m_bWantsToFire = false ;
TaskComplete ( ) ;
}
}
else
{
if ( gpGlobals - > curtime > m_fOverkillShootTime )
{
TaskComplete ( ) ;
return ;
}
}
}
void CASW_Marine : : StartTaskRangeAttack1 ( const Task_t * pTask )
{
if ( GetEnemy ( ) = = NULL )
{
//Msg("Started ranged attack task with no enemy!");
}
m_fStartedFiringTime = gpGlobals - > curtime ;
BaseClass : : StartTaskRangeAttack1 ( pTask ) ;
}
bool CASW_Marine : : SetNewAimError ( CBaseEntity * pTarget )
{
if ( ! pTarget )
return false ;
// find our current yaw to the enemy
float currentYaw = UTIL_AngleMod ( GetLocalAngles ( ) . y ) ;
Vector enemyDir = GetEnemy ( ) - > WorldSpaceCenter ( ) - WorldSpaceCenter ( ) ;
VectorNormalize ( enemyDir ) ;
float angleDiff = VecToYaw ( enemyDir ) ;
angleDiff = UTIL_AngleDiff ( angleDiff , currentYaw ) ;
// create some random error amount, within our angle
m_fMarineAimError = random - > RandomFloat ( asw_marine_aim_error_min . GetFloat ( ) , asw_marine_aim_error_max . GetFloat ( ) ) ;
if ( m_fMarineAimError > fabs ( angleDiff ) )
{
m_fMarineAimError = fabs ( angleDiff ) ;
}
if ( angleDiff > 0 )
{
m_fMarineAimError = - m_fMarineAimError ;
}
if ( asw_debug_marine_aim . GetBool ( ) )
{
Msg ( " Set marine's aim error to %f \n " , m_fMarineAimError ) ;
}
return true ;
}
// adjusts a target point by our aim error angle
void CASW_Marine : : AddAimErrorToTarget ( Vector & vecTarget )
{
Vector diff = vecTarget ;
Vector diff_rotated ;
matrix3x4_t fRotateMatrix ;
AngleMatrix ( QAngle ( 0 , m_fMarineAimError , 0 ) , fRotateMatrix ) ;
VectorRotate ( diff , fRotateMatrix , diff_rotated ) ;
VectorRotate ( diff , QAngle ( 0 , m_fMarineAimError , 0 ) , diff_rotated ) ;
vecTarget = diff_rotated ;
return ;
}
//-----------------------------------------------------------------------------
// Purpose: Return the actual position the NPC wants to fire at when it's trying
// to hit it's current enemy.
//-----------------------------------------------------------------------------
Vector CASW_Marine : : GetActualShootPosition ( const Vector & shootOrigin )
{
// Project the target's location into the future.
Vector vecEnemyLKP = GetEnemyLKP ( ) ;
Vector vecEnemyOffset = GetEnemy ( ) - > BodyTarget ( shootOrigin ) - GetEnemy ( ) - > GetAbsOrigin ( ) ;
Vector vecTargetPosition = vecEnemyOffset + vecEnemyLKP ;
// lead for some fraction of a second.
return ( vecTargetPosition + ( GetEnemy ( ) - > GetSmoothedVelocity ( ) * ai_lead_time . GetFloat ( ) ) ) ;
}
// potentially miss
// asw - this is mostly copied from ai_basenpc, but with our added aim error on top
Vector CASW_Marine : : GetActualShootTrajectory ( const Vector & shootOrigin )
{
const Task_t * pCurTask = GetTask ( ) ;
if ( pCurTask & & pCurTask - > iTask = = TASK_ASW_OVERKILL_SHOOT )
{
// if we're overkill shooting, fire at the overkill pos
Vector shotDir = m_vecOverkillPos + Vector ( 0 , 0 , 45 ) - shootOrigin ;
VectorNormalize ( shotDir ) ;
CollectShotStats ( shootOrigin , shotDir ) ;
CShotManipulator manipulator ( shotDir ) ;
manipulator . ApplySpread ( GetAttackSpread ( GetActiveWeapon ( ) , GetEnemy ( ) ) , GetSpreadBias ( GetActiveWeapon ( ) , GetEnemy ( ) ) ) ;
shotDir = manipulator . GetResult ( ) ;
return shotDir ;
}
Vector vecShotDir ;
if ( ! GetEnemy ( ) )
{
vecShotDir = GetShootEnemyDir ( shootOrigin ) ;
}
else
{
Vector vecProjectedPosition = GetActualShootPosition ( shootOrigin ) ;
vecShotDir = vecProjectedPosition - shootOrigin ;
VectorNormalize ( vecShotDir ) ;
// NOW we have a shoot direction. Where a 100% accurate bullet should go.
// Modify it by weapon proficiency.
// construct a manipulator
CShotManipulator manipulator ( vecShotDir ) ;
manipulator . ApplySpread ( GetAttackSpread ( GetActiveWeapon ( ) , GetEnemy ( ) ) , GetSpreadBias ( GetActiveWeapon ( ) , GetEnemy ( ) ) ) ;
vecShotDir = manipulator . GetResult ( ) ;
}
AddAimErrorToTarget ( vecShotDir ) ; // asw
return vecShotDir ;
}
void CASW_Marine : : DrawDebugGeometryOverlays ( )
{
if ( GetASWOrders ( ) = = ASW_ORDER_MOVE_TO )
{
float flViewRange = acos ( 0.8 ) ;
Vector vEyeDir = EyeDirection2D ( ) ;
Vector vLeftDir , vRightDir ;
float fSin , fCos ;
SinCos ( flViewRange , & fSin , & fCos ) ;
vLeftDir . x = vEyeDir . x * fCos - vEyeDir . y * fSin ;
vLeftDir . y = vEyeDir . x * fSin + vEyeDir . y * fCos ;
vLeftDir . z = vEyeDir . z ;
fSin = sin ( - flViewRange ) ;
fCos = cos ( - flViewRange ) ;
vRightDir . x = vEyeDir . x * fCos - vEyeDir . y * fSin ;
vRightDir . y = vEyeDir . x * fSin + vEyeDir . y * fCos ;
vRightDir . z = vEyeDir . z ;
//NDebugOverlay::BoxDirection(m_vecMoveToOrderPos, Vector(0,0,-40), Vector(200,0,40), vLeftDir, 255, 0, 0, 50, 0 );
//NDebugOverlay::BoxDirection(m_vecMoveToOrderPos, Vector(0,0,-40), Vector(200,0,40), vRightDir, 255, 0, 0, 50, 0 );
//NDebugOverlay::BoxDirection(m_vecMoveToOrderPos, Vector(0,0,-40), Vector(200,0,40), vEyeDir, 0, 255, 0, 50, 0 );
NDebugOverlay : : Box ( m_vecMoveToOrderPos , - Vector ( 2 , 2 , 2 ) , Vector ( 2 , 2 , 2 ) , 0 , 255 , 0 , 128 , 0 ) ;
}
BaseClass : : DrawDebugGeometryOverlays ( ) ;
if ( GetSquadFormation ( ) )
{
GetSquadFormation ( ) - > DrawDebugGeometryOverlays ( ) ;
}
}
ConVar asw_marine_face_last_enemy_time ( " asw_marine_face_last_enemy_time " , " 5.0 " , FCVAR_CHEAT , " Amount of time that AI marines will face their last enemy after losing it while in follow mode " ) ;
void CASW_Marine : : UpdateFacing ( )
{
if ( gpGlobals - > curtime > m_flNextYawOffsetTime )
{
RecalculateAIYawOffset ( ) ;
}
if ( m_vecFacingPointFromServer . Get ( ) ! = vec3_origin )
{
float flAimYaw = UTIL_VecToYaw ( m_vecFacingPointFromServer . Get ( ) - GetAbsOrigin ( ) ) ;
GetMotor ( ) - > SetIdealYawAndUpdate ( flAimYaw ) ;
}
else if ( GetEnemy ( ) )
{
Vector vecEnemyLKP = GetEnemyLKP ( ) ;
float flAimYaw = CalcIdealYaw ( vecEnemyLKP ) + m_fMarineAimError ;
GetMotor ( ) - > SetIdealYawAndUpdate ( flAimYaw ) ;
m_flLastEnemyYaw = flAimYaw ;
m_flLastEnemyYawTime = gpGlobals - > curtime ;
m_flAIYawOffset = 0 ;
if ( asw_debug_marine_aim . GetBool ( ) )
{
Vector vecAim ;
QAngle angAim = QAngle ( 0 , flAimYaw , 0 ) ;
AngleVectors ( angAim , & vecAim ) ;
NDebugOverlay : : Line ( GetAbsOrigin ( ) , GetAbsOrigin ( ) + vecAim * 50 , 255 , 255 , 0 , false , 0.1f ) ;
NDebugOverlay : : Line ( GetAbsOrigin ( ) , vecEnemyLKP , 255 , 255 , 255 , false , 0.1f ) ;
Msg ( " aim error = %f fAimYaw = %f \n " , m_fMarineAimError , flAimYaw ) ;
}
}
else if ( IsCurSchedule ( SCHED_ASW_MOVE_TO_ORDER_POS ) ) // face our order destination
{
Vector vecEnemyLKP = GetEnemyLKP ( ) ;
float flAimYaw = CalcIdealYaw ( m_vecMoveToOrderPos ) ;
GetMotor ( ) - > SetIdealYawAndUpdate ( flAimYaw ) ;
if ( asw_debug_marine_aim . GetBool ( ) )
{
Vector vecAim ;
QAngle angAim = QAngle ( 0 , flAimYaw , 0 ) ;
AngleVectors ( angAim , & vecAim ) ;
NDebugOverlay : : Line ( GetAbsOrigin ( ) , GetAbsOrigin ( ) + vecAim * 50 , 255 , 255 , 0 , false , 0.1f ) ;
NDebugOverlay : : Line ( GetAbsOrigin ( ) , vecEnemyLKP , 255 , 255 , 255 , false , 0.1f ) ;
Msg ( " aim error = %f fAimYaw = %f \n " , m_fMarineAimError , flAimYaw ) ;
}
}
else if ( IsCurSchedule ( SCHED_ASW_HEAL_MARINE ) ) // face the marine that we want to heal
{
if ( m_hHealTarget . Get ( ) )
{
float flAimYaw = CalcIdealYaw ( m_hHealTarget . Get ( ) - > GetAbsOrigin ( ) ) ;
GetMotor ( ) - > SetIdealYawAndUpdate ( flAimYaw ) ;
if ( asw_debug_marine_aim . GetBool ( ) )
{
Vector vecAim ;
QAngle angAim = QAngle ( 0 , flAimYaw , 0 ) ;
AngleVectors ( angAim , & vecAim ) ;
NDebugOverlay : : Line ( GetAbsOrigin ( ) , GetAbsOrigin ( ) + vecAim * 50 , 0 , 255 , 0 , false , 0.2f ) ;
}
}
}
else if ( GetASWOrders ( ) = = ASW_ORDER_FOLLOW )
{
float flAimYaw = GetSquadFormation ( ) - > GetYaw ( GetSquadFormation ( ) - > Find ( this ) ) ;
if ( gpGlobals - > curtime < m_flLastEnemyYawTime + asw_marine_face_last_enemy_time . GetFloat ( ) )
{
flAimYaw = m_flLastEnemyYaw ;
}
flAimYaw + = m_flAIYawOffset ;
GetMotor ( ) - > SetIdealYawAndUpdate ( flAimYaw ) ;
if ( asw_debug_marine_aim . GetBool ( ) )
{
Vector vecAim ;
QAngle angAim = QAngle ( 0 , flAimYaw , 0 ) ;
AngleVectors ( angAim , & vecAim ) ;
NDebugOverlay : : Line ( GetAbsOrigin ( ) , GetAbsOrigin ( ) + vecAim * 50 , 255 , 255 , 0 , false , 0.1f ) ;
Msg ( " aim error = %f fAimYaw = %f m_flAIYawOffset = %f \n " , m_fMarineAimError , flAimYaw , m_flAIYawOffset ) ;
}
}
// else
// {
// // face our destination
// Vector vecDest = GetNavigator()->GetGoalPos();
//
// float fAimYaw = CalcIdealYaw( vecDest );// + m_fMarineAimError; // TODO: put aim error back in
// GetMotor()->SetIdealYawAndUpdate( fAimYaw );
// }
}
bool CASW_Marine : : OverrideMoveFacing ( const AILocalMoveGoal_t & move , float flInterval )
{
// make him looking towards his holding direction as he moves
/*
if ( GetASWOrders ( ) = = ASW_ORDER_MOVE_TO )
{
QAngle holding ( 0 , m_fHoldingYaw , 0 ) ;
Vector vecForward ;
AngleVectors ( holding , & vecForward ) ;
Vector vDest = GetNavigator ( ) - > GetGoalPos ( ) ;
Msg ( " Adding facing target.. \n " ) ;
AddFacingTarget ( vDest + vecForward * 100.0f , 10.0f , 1.0f ) ;
} */
// if we're moving to a specific spot, make sure we face our enemy so we can shoot while on the move
if ( GetEnemy ( ) & & ( GetASWOrders ( ) = = ASW_ORDER_MOVE_TO | | GetASWOrders ( ) = = ASW_ORDER_FOLLOW ) )
{
// ASWTODO - is this move_yaw set needed? (or needs to be move_x/y?)
//float flMoveYaw = UTIL_VecToYaw( move.dir );
//float idealYaw = UTIL_AngleMod( flMoveYaw );
//float flEYaw = UTIL_VecToYaw( GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter() );
// UpdateFacing();
// find movement direction to compensate for not being turned far enough
// ASWTODO - is this move_yaw set needed? (or needs to be move_x/y?)
/*
float fSequenceMoveYaw = GetSequenceMoveYaw ( GetSequence ( ) ) ;
float flDiff = UTIL_AngleDiff ( flMoveYaw , GetLocalAngles ( ) . y + fSequenceMoveYaw ) ;
SetPoseParameter ( " move_yaw " , GetPoseParameter ( " move_yaw " ) + flDiff ) ;
*/
return true ;
}
return true ;
return BaseClass : : OverrideMoveFacing ( move , flInterval ) ;
}
// check for AI changing weapon if he's getting hurt and has a non-offensive weapon equipped
bool CASW_Marine : : CheckAutoWeaponSwitch ( )
{
CASW_Weapon * pWeapon = GetActiveASWWeapon ( ) ;
if ( pWeapon & & pWeapon - > IsOffensiveWeapon ( ) )
return true ;
// marine doesn't auto switch weapons the first two times he's hurt
m_iHurtWithoutOffensiveWeapon + + ;
if ( m_iHurtWithoutOffensiveWeapon < 3 )
return false ;
// find best offensive weapon
CASW_Weapon * pBestWeapon = NULL ;
for ( int i = 0 ; i < ASW_MAX_MARINE_WEAPONS ; i + + )
{
pWeapon = GetASWWeapon ( i ) ;
if ( pWeapon & & pWeapon - > IsOffensiveWeapon ( ) )
{
pBestWeapon = pWeapon ;
break ;
}
}
if ( pBestWeapon )
{
Weapon_Switch ( pWeapon ) ;
m_iHurtWithoutOffensiveWeapon = 0 ;
return true ;
}
return false ;
}
inline const Vector & CASW_Marine : : GetFollowPos ( )
{
m_hMarineFollowTarget = GetSquadLeader ( ) ;
// if I'm in a squad and it has a leader, then
// use the computed position. Otherwise fall
// back to current pos.
unsigned slot = GetSquadFormation ( ) - > Find ( this ) ;
return ( GetSquadFormation ( ) - > IsValid ( slot ) ?
GetSquadFormation ( ) - > GetIdealPosition ( slot ) :
GetAbsOrigin ( ) ) ;
}
// ===== Rappeling ===================================
void CASW_Marine : : BeginRappel ( )
{
m_bWaitingToRappel = true ;
m_bOnGround = false ;
RemoveFlag ( FL_ONGROUND ) ;
// Send the message to begin rappeling!
SetCondition ( COND_ASW_BEGIN_RAPPEL ) ;
m_vecRopeAnchor = GetAbsOrigin ( ) ;
}
void CASW_Marine : : CutZipline ( )
{
if ( m_hLine . Get ( ) )
{
UTIL_Remove ( m_hLine ) ;
}
// create one just hanging down in its place
CBeam * pBeam ;
pBeam = CBeam : : BeamCreate ( " cable/cable.vmt " , 1 ) ;
pBeam - > SetColor ( 150 , 150 , 150 ) ;
pBeam - > SetWidth ( 1.0 ) ; // was 0.3
pBeam - > SetEndWidth ( 1.0 ) ;
//pBeam->PointEntInit( GetAbsOrigin() + Vector( 0, 0, 80 ), this );
//pBeam->SetEndAttachment( attachment );
//pBeam->SetAbsEndPos(GetAbsOrigin());
pBeam - > PointsInit ( m_vecRopeAnchor , GetAbsOrigin ( ) ) ;
if ( m_hLine . Get ( ) )
{
UTIL_Remove ( m_hLine ) ;
}
m_hLine . Set ( pBeam ) ;
//CBaseEntity *pAnchor = CreateEntityByName( "asw_rope_anchor" );
//pAnchor->SetOwnerEntity( this );
//pAnchor->SetAbsOrigin( m_vecRopeAnchor );
//pAnchor->Spawn();
}
void CASW_Marine : : CreateZipline ( )
{
if ( ! m_hLine )
{
int attachment = LookupAttachment ( " zipline " ) ;
if ( attachment ! = - 1 )
{
CBeam * pBeam ;
pBeam = CBeam : : BeamCreate ( " cable/cable.vmt " , 1 ) ;
pBeam - > SetColor ( 150 , 150 , 150 ) ;
pBeam - > SetWidth ( 1.0 ) ; // was 0.3
pBeam - > SetEndWidth ( 1.0 ) ;
pBeam - > PointEntInit ( GetAbsOrigin ( ) + Vector ( 0 , 0 , 80 ) , this ) ;
pBeam - > SetEndAttachment ( attachment ) ;
m_hLine . Set ( pBeam ) ;
}
}
}
# define RAPPEL_MAX_SPEED 600 // Go this fast if you're really high.
# define RAPPEL_MIN_SPEED 60 // Go no slower than this.
# define RAPPEL_DECEL_DIST (20.0f * 12.0f) // Start slowing down when you're this close to the ground.
void CASW_Marine : : SetDescentSpeed ( )
{
// Trace to the floor and see how close we're getting. Slow down if we're close.
// STOP if there's an NPC under us.
trace_t tr ;
AI_TraceLine ( GetAbsOrigin ( ) , GetAbsOrigin ( ) - Vector ( 0 , 0 , 8192 ) , MASK_SHOT , this , COLLISION_GROUP_NONE , & tr ) ;
float flDist = fabs ( GetAbsOrigin ( ) . z - tr . endpos . z ) ;
float speed = RAPPEL_MAX_SPEED ;
if ( flDist < = RAPPEL_DECEL_DIST )
{
float factor ;
factor = flDist / RAPPEL_DECEL_DIST ;
speed = MAX ( RAPPEL_MIN_SPEED , speed * factor ) ;
}
Vector vecNewVelocity = vec3_origin ;
vecNewVelocity . z = - speed ;
SetAbsVelocity ( vecNewVelocity ) ;
}
void CASW_Marine : : CleanupOnDeath ( CBaseEntity * pCulprit , bool bFireDeathOutput )
{
BaseClass : : CleanupOnDeath ( pCulprit , bFireDeathOutput ) ;
//This will remove the beam and create a rope if the NPC dies while rappeling down.
if ( m_hLine )
{
CutZipline ( ) ;
}
}
// use head direction to determine if spot is shootable
bool CASW_Marine : : FInAimCone ( const Vector & vecSpot )
{
return BaseClass : : FInAimCone ( vecSpot ) ;
Vector los = ( vecSpot - Weapon_ShootPosition ( ) ) ;
// do this in 2D
los . z = 0 ;
VectorNormalize ( los ) ;
Vector facingDir = HeadDirection2D ( ) ;
float flDot = DotProduct ( los , facingDir ) ;
if ( CapabilitiesGet ( ) & bits_CAP_AIM_GUN )
{
// FIXME: query current animation for ranges
return ( flDot > DOT_30DEGREE ) ;
}
if ( flDot > 0.994 ) //!!!BUGBUG - magic number same as FacingIdeal(), what is this?
return true ;
return false ;
}
void CASW_Marine : : OnWeldStarted ( )
{
m_bWaitingForWeld = true ;
m_flBeginWeldTime = gpGlobals - > curtime ;
SetCondition ( COND_ASW_NEW_ORDERS ) ;
}
void CASW_Marine : : OnWeldFinished ( )
{
m_bWaitingForWeld = false ;
SetCondition ( COND_ASW_NEW_ORDERS ) ;
}
ConVar asw_debug_combat_status ( " asw_debug_combat_status " , " 0 " , FCVAR_CHEAT , " Show marine's combat status on the screen " ) ;
void CASW_Marine : : UpdateCombatStatus ( )
{
const float flCombatEnemyDist = 512.0f ;
const float flNearbyMarineDist = 768.0f ;
CASW_Game_Resource * pGameResource = ASWGameResource ( ) ;
if ( ! IsInhabited ( ) & & GetEnemy ( ) & & GetEnemy ( ) - > GetAbsOrigin ( ) . DistTo ( GetAbsOrigin ( ) ) < flCombatEnemyDist )
{
m_flLastSquadEnemyTime = gpGlobals - > curtime ;
}
else
{
// check nearby squad mates
for ( int i = 0 ; i < pGameResource - > GetMaxMarineResources ( ) ; i + + )
{
CASW_Marine_Resource * pMR = pGameResource - > GetMarineResource ( i ) ;
CASW_Marine * pOtherMarine = pMR ? pMR - > GetMarineEntity ( ) : NULL ;
if ( pOtherMarine & & pOtherMarine ! = this & & ! pOtherMarine - > IsInhabited ( ) & & pOtherMarine - > GetEnemy ( )
& & pOtherMarine - > GetAbsOrigin ( ) . DistTo ( GetAbsOrigin ( ) ) < flNearbyMarineDist
& & pOtherMarine - > GetEnemy ( ) - > GetAbsOrigin ( ) . DistTo ( pOtherMarine - > GetAbsOrigin ( ) ) < flCombatEnemyDist )
{
m_flLastSquadEnemyTime = gpGlobals - > curtime ;
break ;
}
}
}
m_flLastSquadShotAlienTime = 0.0f ;
for ( int i = 0 ; i < pGameResource - > GetMaxMarineResources ( ) ; i + + )
{
CASW_Marine_Resource * pMR = pGameResource - > GetMarineResource ( i ) ;
CASW_Marine * pOtherMarine = pMR ? pMR - > GetMarineEntity ( ) : NULL ;
if ( pOtherMarine ! = this & & pOtherMarine & & pOtherMarine - > GetAbsOrigin ( ) . DistTo ( GetAbsOrigin ( ) ) < flNearbyMarineDist )
{
m_flLastSquadShotAlienTime = MAX ( m_flLastSquadShotAlienTime , pOtherMarine - > m_flLastHurtAlienTime ) ;
}
}
if ( asw_debug_combat_status . GetBool ( ) )
{
engine - > Con_NPrintf ( GetMarineResource ( ) - > m_MarineProfileIndex , " %s: %s " , GetMarineProfile ( ) - > GetShortName ( ) , IsInCombat ( ) ? " COMBAT " : " not in combat " ) ;
}
}
ConVar asw_marine_force_combat_status ( " asw_marine_force_combat_status " , " 0 " , FCVAR_CHEAT ) ;
bool CASW_Marine : : IsInCombat ( )
{
if ( asw_marine_force_combat_status . GetBool ( ) )
return true ;
const float flCombatTime = 10.0f ;
return ( ( m_flLastSquadEnemyTime ! = 0.0f & & m_flLastSquadEnemyTime > gpGlobals - > curtime - flCombatTime ) | |
( m_flLastSquadShotAlienTime ! = 0.0f & & m_flLastSquadShotAlienTime > gpGlobals - > curtime - flCombatTime ) ) ;
}
bool CASW_Marine : : FValidateHintType ( CAI_Hint * pHint )
{
if ( pHint - > HintType ( ) = = HINT_FOLLOW_WAIT_POINT )
return true ;
return BaseClass : : FValidateHintType ( pHint ) ;
}
ConVar asw_marine_random_yaw ( " asw_marine_random_yaw " , " 40 " , FCVAR_NONE , " Min/max angle the marine will change his yaw when idling in follow mode " ) ;
ConVar asw_marine_yaw_interval_min ( " asw_marine_yaw_interval_min " , " 3 " , FCVAR_NONE , " Min time between AI marine shifting his yaw " ) ;
ConVar asw_marine_yaw_interval_max ( " asw_marine_yaw_interval_max " , " 8 " , FCVAR_NONE , " Max time between AI marine shifting his yaw " ) ;
ConVar asw_marine_toggle_crouch_chance ( " asw_marine_toggle_crouch_chance " , " 0.4 " , FCVAR_NONE , " Chance of AI changing between crouched and non-crouched while idling in follow mode " ) ;
void CASW_Marine : : RecalculateAIYawOffset ( )
{
m_flAIYawOffset = RandomFloat ( - asw_marine_random_yaw . GetFloat ( ) , asw_marine_random_yaw . GetFloat ( ) ) ;
if ( asw_blind_follow . GetBool ( ) )
{
m_bAICrouch = ( GetASWOrders ( ) = = ASW_ORDER_HOLD_POSITION ) ;
}
else if ( RandomFloat ( ) < asw_marine_toggle_crouch_chance . GetFloat ( ) )
{
m_bAICrouch = ! m_bAICrouch ;
}
m_flNextYawOffsetTime = gpGlobals - > curtime + RandomFloat ( asw_marine_yaw_interval_min . GetFloat ( ) , asw_marine_yaw_interval_max . GetFloat ( ) ) ;
}
// disables collision between the marine and certain props that the marine cannot break reliably
void CASW_Marine : : CheckForDisablingAICollision ( CBaseEntity * pEntity )
{
if ( ! pEntity )
return ;
// don't disable collision between the marine and breakable crates
const char * szModelName = STRING ( pEntity - > GetModelName ( ) ) ;
if ( ! szModelName )
return ;
if ( ! CanMarineGetStuckOnProp ( szModelName ) )
return ;
PhysDisableEntityCollisions ( this , pEntity ) ;
SetPhysicsPropTarget ( NULL ) ;
}
//-----------------------------------------------------------------------------
// Schedules
//-----------------------------------------------------------------------------
AI_BEGIN_CUSTOM_NPC ( asw_marine , CASW_Marine )
DECLARE_CONDITION ( COND_ASW_NEW_ORDERS )
DECLARE_CONDITION ( COND_ASW_BEGIN_RAPPEL )
DECLARE_CONDITION ( COND_SQUADMATE_WANTS_AMMO )
DECLARE_CONDITION ( COND_SQUADMATE_NEEDS_AMMO )
DECLARE_CONDITION ( COND_PATH_BLOCKED_BY_PHYSICS_PROP )
DECLARE_CONDITION ( COND_PROP_DESTROYED )
DECLARE_CONDITION ( COND_COMPLETELY_OUT_OF_AMMO )
DECLARE_TASK ( TASK_ASW_FACE_HOLDING_YAW )
DECLARE_TASK ( TASK_ASW_FACE_USING_ITEM )
DECLARE_TASK ( TASK_ASW_START_USING_AREA )
DECLARE_TASK ( TASK_ASW_FACE_ENEMY_WITH_ERROR )
DECLARE_TASK ( TASK_ASW_OVERKILL_SHOOT )
DECLARE_TASK ( TASK_ASW_GET_PATH_TO_ORDER_POS )
DECLARE_TASK ( TASK_ASW_GET_PATH_TO_FOLLOW_TARGET )
DECLARE_TASK ( TASK_ASW_GET_PATH_TO_PROP )
DECLARE_TASK ( TASK_ASW_FACE_FOLLOW_WAIT )
DECLARE_TASK ( TASK_ASW_GET_BACK_OFF_PATH )
DECLARE_TASK ( TASK_ASW_WAIT_FOR_FOLLOW_MOVEMENT )
DECLARE_TASK ( TASK_ASW_WAIT_FOR_MOVEMENT )
DECLARE_TASK ( TASK_ASW_CHATTER_CONFIRM )
DECLARE_TASK ( TASK_ASW_RAPPEL )
DECLARE_TASK ( TASK_ASW_HIT_GROUND )
DECLARE_TASK ( TASK_ASW_ORDER_TO_DEPLOY_SPOT )
DECLARE_TASK ( TASK_ASW_GET_PATH_TO_GIVE_AMMO )
DECLARE_TASK ( TASK_ASW_MOVE_TO_GIVE_AMMO )
DECLARE_TASK ( TASK_ASW_SWAP_TO_AMMO_BAG )
DECLARE_TASK ( TASK_ASW_GIVE_AMMO_TO_MARINE )
DECLARE_TASK ( TASK_ASW_GET_PATH_TO_HEAL )
DECLARE_TASK ( TASK_ASW_MOVE_TO_HEAL )
DECLARE_TASK ( TASK_ASW_SWAP_TO_HEAL_GUN )
DECLARE_TASK ( TASK_ASW_HEAL_MARINE )
DECLARE_ANIMEVENT ( AE_MARINE_KICK )
DECLARE_ANIMEVENT ( AE_MARINE_UNFREEZE )
// Activities
DECLARE_ACTIVITY ( ACT_MARINE_GETTING_UP )
DECLARE_ACTIVITY ( ACT_MARINE_LAYING_ON_FLOOR )
DEFINE_SCHEDULE
(
SCHED_ASW_HOLD_POSITION ,
" Tasks "
" TASK_STOP_MOVING 0 "
//" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
" TASK_ASW_FACE_HOLDING_YAW 2 "
" "
" Interrupts "
" COND_NEW_ENEMY "
" COND_ENEMY_DEAD "
" COND_LIGHT_DAMAGE "
" COND_HEAVY_DAMAGE "
" COND_SEE_ENEMY "
" COND_CAN_RANGE_ATTACK1 "
" COND_CAN_RANGE_ATTACK2 "
" COND_CAN_MELEE_ATTACK1 "
" COND_CAN_MELEE_ATTACK2 "
" COND_IDLE_INTERRUPT "
" COND_ASW_NEW_ORDERS "
" COND_ASW_BEGIN_RAPPEL "
)
DEFINE_SCHEDULE
(
SCHED_ASW_USE_AREA ,
" Tasks "
" TASK_STOP_MOVING 0 "
" TASK_ASW_START_USING_AREA 0 "
" TASK_ASW_FACE_USING_ITEM 0 "
" "
" Interrupts "
" COND_NEW_ENEMY "
" COND_ENEMY_DEAD "
" COND_LIGHT_DAMAGE "
" COND_HEAVY_DAMAGE "
" COND_SEE_ENEMY "
" COND_CAN_RANGE_ATTACK1 "
" COND_CAN_RANGE_ATTACK2 "
" COND_CAN_MELEE_ATTACK1 "
" COND_CAN_MELEE_ATTACK2 "
" COND_IDLE_INTERRUPT "
" COND_ASW_NEW_ORDERS "
" COND_ASW_BEGIN_RAPPEL "
)
DEFINE_SCHEDULE
(
SCHED_ASW_USING_OVER_TIME ,
" Tasks "
" TASK_STOP_MOVING 0 "
" TASK_ASW_FACE_USING_ITEM 0 "
" "
" Interrupts "
" COND_NEW_ENEMY "
" COND_ENEMY_DEAD "
" COND_LIGHT_DAMAGE "
" COND_HEAVY_DAMAGE "
" COND_SEE_ENEMY "
" COND_CAN_RANGE_ATTACK1 "
" COND_CAN_RANGE_ATTACK2 "
" COND_CAN_MELEE_ATTACK1 "
" COND_CAN_MELEE_ATTACK2 "
" COND_IDLE_INTERRUPT "
" COND_ASW_NEW_ORDERS "
" COND_ASW_BEGIN_RAPPEL "
)
DEFINE_SCHEDULE
(
SCHED_ASW_OVERKILL_SHOOT ,
" Tasks "
" TASK_ASW_OVERKILL_SHOOT 0 "
" "
" Interrupts "
" COND_NO_PRIMARY_AMMO "
" COND_ASW_NEW_ORDERS "
)
DEFINE_SCHEDULE
(
SCHED_ASW_RANGE_ATTACK1 ,
" Tasks "
" TASK_STOP_MOVING 0 "
" TASK_ASW_FACE_ENEMY_WITH_ERROR 0 "
" TASK_ANNOUNCE_ATTACK 1 " // 1 = primary attack
" TASK_RANGE_ATTACK1 0 "
" "
" Interrupts "
" COND_NEW_ENEMY "
" COND_ENEMY_DEAD "
" COND_LIGHT_DAMAGE "
" COND_HEAVY_DAMAGE "
" COND_ENEMY_OCCLUDED "
" COND_NO_PRIMARY_AMMO "
" COND_HEAR_DANGER "
" COND_WEAPON_BLOCKED_BY_FRIEND "
" COND_WEAPON_SIGHT_OCCLUDED "
" COND_LOST_ENEMY "
" COND_ASW_NEW_ORDERS "
)
DEFINE_SCHEDULE
(
SCHED_ASW_MOVE_TO_ORDER_POS ,
" Tasks "
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASW_FOLLOW_WAIT "
" TASK_ASW_GET_PATH_TO_ORDER_POS 0 "
" TASK_ASW_CHATTER_CONFIRM 0.4 "
" TASK_RUN_PATH 0 "
" TASK_ASW_WAIT_FOR_MOVEMENT 0 "
" TASK_STOP_MOVING 1 "
" "
" Interrupts "
" COND_ASW_NEW_ORDERS "
" COND_NEW_ENEMY "
" COND_SEE_ENEMY "
" COND_CAN_RANGE_ATTACK1 "
" COND_CAN_RANGE_ATTACK2 "
)
DEFINE_SCHEDULE
(
SCHED_ASW_FOLLOW_MOVE ,
" Tasks "
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASW_FOLLOW_WAIT "
" TASK_ASW_GET_PATH_TO_FOLLOW_TARGET 0 "
" TASK_RUN_PATH 0 "
" TASK_ASW_WAIT_FOR_FOLLOW_MOVEMENT 0 "
" TASK_STOP_MOVING 1 "
" "
" Interrupts "
" COND_ASW_NEW_ORDERS "
" COND_NEW_ENEMY "
" COND_SEE_ENEMY "
" COND_CAN_RANGE_ATTACK1 "
" COND_CAN_RANGE_ATTACK2 "
)
DEFINE_SCHEDULE
(
SCHED_ASW_FOLLOW_WAIT ,
" Tasks "
" TASK_STOP_MOVING 0 "
" TASK_ASW_FACE_FOLLOW_WAIT 0.3 "
" "
" Interrupts "
" COND_NEW_ENEMY "
" COND_ENEMY_DEAD "
" COND_LIGHT_DAMAGE "
" COND_HEAVY_DAMAGE "
" COND_SEE_ENEMY "
" COND_CAN_RANGE_ATTACK1 "
" COND_CAN_RANGE_ATTACK2 "
" COND_CAN_MELEE_ATTACK1 "
" COND_CAN_MELEE_ATTACK2 "
" COND_IDLE_INTERRUPT "
" COND_ASW_NEW_ORDERS "
" COND_GIVE_WAY "
)
DEFINE_SCHEDULE
(
SCHED_ASW_PICKUP_WAIT ,
" Tasks "
" TASK_STOP_MOVING 0 "
" TASK_WAIT 1.5 "
" "
" Interrupts "
)
DEFINE_SCHEDULE
(
SCHED_ASW_FOLLOW_BACK_OFF ,
" Tasks "
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASW_FOLLOW_WAIT "
" TASK_ASW_GET_BACK_OFF_PATH 0 "
" TASK_RUN_PATH 0 "
" TASK_WAIT_FOR_MOVEMENT 0 "
" TASK_STOP_MOVING 1 "
" "
" Interrupts "
" COND_ASW_NEW_ORDERS "
" COND_NEW_ENEMY "
" COND_SEE_ENEMY "
" COND_CAN_RANGE_ATTACK1 "
" COND_CAN_RANGE_ATTACK2 "
)
//===============================================
//===============================================
DEFINE_SCHEDULE
(
SCHED_ASW_RAPPEL_WAIT ,
" Tasks "
" TASK_SET_ACTIVITY ACTIVITY:ACT_RAPPEL_LOOP "
" TASK_WAIT_INDEFINITE 0 "
" "
" Interrupts "
" COND_ASW_BEGIN_RAPPEL "
) ;
//===============================================
//===============================================
DEFINE_SCHEDULE
(
SCHED_ASW_RAPPEL ,
" Tasks "
" TASK_SET_ACTIVITY ACTIVITY:ACT_RAPPEL_LOOP "
" TASK_WAIT 0.2 "
" TASK_ASW_RAPPEL 0 "
" TASK_ASW_HIT_GROUND 0 "
" TASK_ASW_ORDER_TO_DEPLOY_SPOT 0 "
//" TASK_SET_SCHEDULE SCHEDULE:SCHED_ASW_CLEAR_RAPPEL_POINT"
" "
" Interrupts "
" "
" COND_NEW_ENEMY " // Only so the enemy selection code will pick an enemy!
) ;
//===============================================
//===============================================
DEFINE_SCHEDULE
(
SCHED_ASW_CLEAR_RAPPEL_POINT ,
" Tasks "
" TASK_ASW_HIT_GROUND 0 "
" TASK_MOVE_AWAY_PATH 128 " // Clear this spot for other rappellers
" TASK_RUN_PATH 0 "
" TASK_WAIT_FOR_MOVEMENT 0 "
" "
" Interrupts "
" "
) ;
//===============================================
//===============================================
DEFINE_SCHEDULE
(
SCHED_ASW_GIVE_AMMO ,
" Tasks "
" TASK_ASW_GET_PATH_TO_GIVE_AMMO 0 "
" TASK_ASW_MOVE_TO_GIVE_AMMO 0 "
" TASK_ASW_SWAP_TO_AMMO_BAG 0 "
" TASK_ASW_GIVE_AMMO_TO_MARINE 0 "
" "
" Interrupts "
" "
) ;
//===============================================
//===============================================
DEFINE_SCHEDULE
(
SCHED_ASW_HEAL_MARINE ,
" Tasks "
" TASK_ASW_GET_PATH_TO_HEAL 0 "
" TASK_ASW_MOVE_TO_HEAL 0 "
" TASK_ASW_SWAP_TO_HEAL_GUN 0 "
" TASK_ASW_HEAL_MARINE 0 "
" "
" Interrupts "
" "
) ;
//=========================================================
//=========================================================
DEFINE_SCHEDULE
(
SCHED_MELEE_ATTACK_PROP1 ,
" Tasks "
" TASK_STOP_MOVING 0 "
" TASK_ASW_GET_PATH_TO_PROP 40 " // 40 = attack distance
" TASK_RUN_PATH 0 "
" TASK_WAIT_FOR_MOVEMENT 0 "
" TASK_FACE_ENEMY 0 "
" TASK_ANNOUNCE_ATTACK 1 " // 1 = primary attack
" TASK_MELEE_ATTACK1 1 "
" TASK_FACE_ENEMY 0 "
" TASK_ANNOUNCE_ATTACK 1 " // 1 = primary attack
" TASK_MELEE_ATTACK1 2 "
" TASK_FACE_ENEMY 0 "
" TASK_ANNOUNCE_ATTACK 1 " // 1 = primary attack
" TASK_MELEE_ATTACK1 3 "
" "
" Interrupts "
//" COND_NEW_ENEMY"
" COND_PROP_DESTROYED "
" COND_LIGHT_DAMAGE "
" COND_HEAVY_DAMAGE "
" COND_ENEMY_OCCLUDED "
) ;
//=========================================================
//=========================================================
DEFINE_SCHEDULE
(
SCHED_ENGAGE_AND_MELEE_ATTACK1 ,
" Tasks "
" TASK_STOP_MOVING 0 "
" TASK_GET_CHASE_PATH_TO_ENEMY 40 " // 40 = attack distance
" TASK_RUN_PATH 0 "
" TASK_WAIT_FOR_MOVEMENT 0 "
" TASK_FACE_ENEMY 0 "
" TASK_ANNOUNCE_ATTACK 1 " // 1 = primary attack
" TASK_MELEE_ATTACK1 1 "
" TASK_FACE_ENEMY 0 "
" TASK_ANNOUNCE_ATTACK 1 " // 1 = primary attack
" TASK_MELEE_ATTACK1 2 "
" TASK_FACE_ENEMY 0 "
" TASK_ANNOUNCE_ATTACK 1 " // 1 = primary attack
" TASK_MELEE_ATTACK1 3 "
" "
" Interrupts "
" COND_NEW_ENEMY "
" COND_ENEMY_DEAD "
" COND_LIGHT_DAMAGE "
" COND_HEAVY_DAMAGE "
" COND_ENEMY_OCCLUDED "
) ;
AI_END_CUSTOM_NPC ( )
// DECLARE_TASK( TASK_ASW_TRACK_AND_FIRE )