Merge pull request #154 from mapbase-source/feature/npc-animation-uprising

The "NPC Animation Uprising" (Code)
This commit is contained in:
Blixibon 2021-11-05 23:37:33 -05:00 committed by GitHub
commit b7749dd92e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 2509 additions and 199 deletions

View File

@ -1990,6 +1990,10 @@ CollideType_t C_BaseAnimating::GetCollideType( void )
return BaseClass::GetCollideType();
}
#ifdef MAPBASE
ConVar ai_death_pose_enabled( "ai_death_pose_enabled", "1", FCVAR_NONE, "Toggles the death pose fix code, which cancels sequence transitions while a NPC is ragdolling." );
#endif
//-----------------------------------------------------------------------------
// Purpose: if the active sequence changes, keep track of the previous ones and decay them based on their decay rate
//-----------------------------------------------------------------------------
@ -2006,6 +2010,14 @@ void C_BaseAnimating::MaintainSequenceTransitions( IBoneSetup &boneSetup, float
return;
}
#ifdef MAPBASE
if ( IsAboutToRagdoll() && ai_death_pose_enabled.GetBool() )
{
m_nPrevNewSequenceParity = m_nNewSequenceParity;
return;
}
#endif
m_SequenceTransitioner.CheckForSequenceChange(
boneSetup.GetStudioHdr(),
GetSequence(),
@ -2785,14 +2797,29 @@ void C_BaseAnimating::CalculateIKLocks( float currentTime )
// debugoverlay->AddBoxOverlay( origin, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), QAngle( 0, 0, 0 ), 255, 0, 0, 0, 0 );
float d = (pTarget->est.pos - origin).Length();
Vector vecDelta = (origin - pTarget->est.pos);
float d = vecDelta.Length();
if ( d >= flDist)
continue;
flDist = d;
pTarget->SetPos( origin );
pTarget->SetAngles( angles );
#ifdef MAPBASE
// For blending purposes, IK attachments should obey weight
if ( pTarget->est.flWeight < 1.0f )
{
Quaternion qTarget;
AngleQuaternion( angles, qTarget );
QuaternionSlerp( pTarget->est.q, qTarget, pTarget->est.flWeight, pTarget->est.q );
pTarget->SetPos( pTarget->est.pos + (vecDelta * pTarget->est.flWeight) );
}
else
#endif
{
pTarget->SetPos( origin );
pTarget->SetAngles( angles );
}
// debugoverlay->AddBoxOverlay( pTarget->est.pos, Vector( -pTarget->est.radius, -pTarget->est.radius, -pTarget->est.radius ), Vector( pTarget->est.radius, pTarget->est.radius, pTarget->est.radius), QAngle( 0, 0, 0 ), 0, 255, 0, 0, 0 );
}

View File

@ -1095,9 +1095,9 @@ void CBaseAnimatingOverlay::SetLayerNoRestore( int iLayer, bool bNoRestore )
}
#ifdef MAPBASE // From Alien Swarm SDK
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
// From Alien Swarm SDK
//-----------------------------------------------------------------------------
void CBaseAnimatingOverlay::SetLayerNoEvents( int iLayer, bool bNoEvents )
{
@ -1113,6 +1113,17 @@ void CBaseAnimatingOverlay::SetLayerNoEvents( int iLayer, bool bNoEvents )
m_AnimOverlay[iLayer].m_fFlags &= ~ANIM_LAYER_NOEVENTS;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseAnimatingOverlay::IsLayerFinished( int iLayer )
{
if (!IsValidLayer( iLayer ))
return true;
return m_AnimOverlay[iLayer].m_bSequenceFinished;
}
#endif

View File

@ -181,8 +181,10 @@ public:
void SetLayerAutokill( int iLayer, bool bAutokill );
void SetLayerLooping( int iLayer, bool bLooping );
void SetLayerNoRestore( int iLayer, bool bNoRestore );
#ifdef MAPBASE // From Alien Swarm SDK
void SetLayerNoEvents( int iLayer, bool bNoEvents );
#ifdef MAPBASE
void SetLayerNoEvents( int iLayer, bool bNoEvents ); // From Alien Swarm SDK
bool IsLayerFinished( int iLayer );
#endif
Activity GetLayerActivity( int iLayer );

View File

@ -103,6 +103,12 @@ protected:
inline CBaseEntity *GetActivator();
#ifdef MAPBASE
inline float GetNPCOpenDistance() { return m_flNPCOpenDistance; }
inline Activity GetNPCOpenFrontActivity() { return m_eNPCOpenFrontActivity; }
inline Activity GetNPCOpenBackActivity() { return m_eNPCOpenBackActivity; }
#endif
private:
// Implement these in your leaf class.
@ -196,6 +202,12 @@ private:
string_t m_SoundOpen;
string_t m_SoundClose;
#ifdef MAPBASE
float m_flNPCOpenDistance;
Activity m_eNPCOpenFrontActivity;
Activity m_eNPCOpenBackActivity;
#endif
// dvs: FIXME: can we remove m_flSpeed from CBaseEntity?
//float m_flSpeed; // Rotation speed when opening or closing in degrees per second.

View File

@ -2180,15 +2180,22 @@ void CAI_BaseNPC::InitDefaultActivitySR(void)
ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_AR2 );
ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2 );
//ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2_LOW );
ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2_LOW );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_AR2 );
ADD_ACTIVITY_TO_SR( ACT_COVER_AR2_LOW );
#endif
#ifdef SHARED_COMBINE_ACTIVITIES
ADD_ACTIVITY_TO_SR( ACT_COMBINE_THROW_GRENADE );
ADD_ACTIVITY_TO_SR( ACT_COMBINE_AR2_ALTFIRE );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_COMBINE_THROW_GRENADE );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_COMBINE_AR2_ALTFIRE );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_SPECIAL_ATTACK1 );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_SPECIAL_ATTACK2 );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_ADVANCE );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_FORWARD );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_GROUP );
@ -2198,8 +2205,249 @@ void CAI_BaseNPC::InitDefaultActivitySR(void)
ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_TAKECOVER );
#endif
#ifdef COMPANION_HOLSTER_WORKAROUND
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
ADD_ACTIVITY_TO_SR( ACT_IDLE_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_WALK_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_RUN_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_RELOAD_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_REVOLVER_LOW );
ADD_ACTIVITY_TO_SR( ACT_RELOAD_REVOLVER_LOW );
ADD_ACTIVITY_TO_SR( ACT_COVER_REVOLVER_LOW );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_REVOLVER_LOW );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_REVOLVER );
ADD_ACTIVITY_TO_SR( ACT_IDLE_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_WALK_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_RUN_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_RELOAD_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_CROSSBOW_LOW );
ADD_ACTIVITY_TO_SR( ACT_RELOAD_CROSSBOW_LOW );
ADD_ACTIVITY_TO_SR( ACT_COVER_CROSSBOW_LOW );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_CROSSBOW_LOW );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_CROSSBOW );
ADD_ACTIVITY_TO_SR( ACT_IDLE_PISTOL_RELAXED );
ADD_ACTIVITY_TO_SR( ACT_IDLE_PISTOL_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_WALK_PISTOL_RELAXED );
ADD_ACTIVITY_TO_SR( ACT_WALK_PISTOL_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_RUN_PISTOL_RELAXED );
ADD_ACTIVITY_TO_SR( ACT_RUN_PISTOL_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_PISTOL_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_PISTOL_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_PISTOL_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_WALK_CROUCH_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_WALK_CROUCH_AIM_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_RUN_CROUCH_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_RUN_CROUCH_AIM_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_IDLE_SHOTGUN );
ADD_ACTIVITY_TO_SR( ACT_WALK_SHOTGUN );
ADD_ACTIVITY_TO_SR( ACT_RUN_SHOTGUN );
ADD_ACTIVITY_TO_SR( ACT_COVER_SHOTGUN_LOW );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SHOTGUN_LOW );
ADD_ACTIVITY_TO_SR( ACT_WALK_SHOTGUN_RELAXED );
ADD_ACTIVITY_TO_SR( ACT_WALK_SHOTGUN_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_RUN_SHOTGUN_RELAXED );
ADD_ACTIVITY_TO_SR( ACT_RUN_SHOTGUN_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_SHOTGUN_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_SHOTGUN_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_SHOTGUN_STIMULATED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_RPG_LOW );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_RPG_LOW );
ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_RPG );
ADD_ACTIVITY_TO_SR( ACT_WALK_MELEE );
ADD_ACTIVITY_TO_SR( ACT_RUN_MELEE );
ADD_ACTIVITY_TO_SR( ACT_RUN_PACKAGE );
ADD_ACTIVITY_TO_SR( ACT_RUN_SUITCASE );
ADD_ACTIVITY_TO_SR( ACT_ARM_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_ARM_SHOTGUN );
ADD_ACTIVITY_TO_SR( ACT_ARM_RPG );
ADD_ACTIVITY_TO_SR( ACT_ARM_MELEE );
ADD_ACTIVITY_TO_SR( ACT_DISARM_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_DISARM_SHOTGUN );
ADD_ACTIVITY_TO_SR( ACT_DISARM_RPG );
ADD_ACTIVITY_TO_SR( ACT_DISARM_MELEE );
#endif
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
ADD_ACTIVITY_TO_SR( ACT_CLIMB_ALL );
ADD_ACTIVITY_TO_SR( ACT_CLIMB_IDLE );
ADD_ACTIVITY_TO_SR( ACT_CLIMB_MOUNT_TOP );
ADD_ACTIVITY_TO_SR( ACT_CLIMB_MOUNT_BOTTOM );
ADD_ACTIVITY_TO_SR( ACT_CLIMB_DISMOUNT_BOTTOM );
#endif
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK1_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK2_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_AR2_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SMG1_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SHOTGUN_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_PISTOL_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_RPG_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_REVOLVER_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_CROSSBOW_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_AR2_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SMG1_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SHOTGUN_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_PISTOL_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_RPG_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_REVOLVER_MED );
ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_CROSSBOW_MED );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_R );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_L );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_R );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_L );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_R_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_L_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_R_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_L_RIFLE );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_R_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_L_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_R_PISTOL );
ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_L_PISTOL );
#endif
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: This is a multi-purpose table which links NPC activities to their gesture variants.
//-----------------------------------------------------------------------------
CAI_BaseNPC::actlink_t CAI_BaseNPC::gm_ActivityGestureLinks[] =
{
{ ACT_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK1 },
{ ACT_RANGE_ATTACK2, ACT_GESTURE_RANGE_ATTACK2 },
{ ACT_MELEE_ATTACK1, ACT_GESTURE_MELEE_ATTACK1 },
{ ACT_MELEE_ATTACK2, ACT_GESTURE_MELEE_ATTACK2 },
{ ACT_RELOAD, ACT_GESTURE_RELOAD },
{ ACT_RANGE_ATTACK1_LOW, ACT_GESTURE_RANGE_ATTACK1 }, // NOTE: ACT_GESTURE_RANGE_ATTACK1_LOW exists, but isn't used
{ ACT_RANGE_ATTACK2_LOW, ACT_GESTURE_RANGE_ATTACK2 }, // NOTE: ACT_GESTURE_RANGE_ATTACK2_LOW exists, but isn't used
{ ACT_RELOAD_LOW, ACT_GESTURE_RELOAD },
{ ACT_MELEE_ATTACK_SWING, ACT_GESTURE_MELEE_ATTACK_SWING },
// -----------------------------------------------------------
{ ACT_RANGE_ATTACK_AR2, ACT_GESTURE_RANGE_ATTACK_AR2 },
{ ACT_RANGE_ATTACK_AR2_LOW, ACT_GESTURE_RANGE_ATTACK_AR2 },
{ ACT_RANGE_ATTACK_SMG1, ACT_GESTURE_RANGE_ATTACK_SMG1 },
{ ACT_RANGE_ATTACK_SMG1_LOW, ACT_GESTURE_RANGE_ATTACK_SMG1 },
{ ACT_RANGE_ATTACK_SHOTGUN, ACT_GESTURE_RANGE_ATTACK_SHOTGUN },
{ ACT_RANGE_ATTACK_SHOTGUN_LOW, ACT_GESTURE_RANGE_ATTACK_SHOTGUN },
{ ACT_RANGE_ATTACK_PISTOL, ACT_GESTURE_RANGE_ATTACK_PISTOL },
{ ACT_RANGE_ATTACK_PISTOL_LOW, ACT_GESTURE_RANGE_ATTACK_PISTOL },
// -----------------------------------------------------------
{ ACT_SMALL_FLINCH, ACT_GESTURE_SMALL_FLINCH },
{ ACT_BIG_FLINCH, ACT_GESTURE_BIG_FLINCH },
{ ACT_FLINCH_HEAD, ACT_GESTURE_FLINCH_HEAD },
{ ACT_FLINCH_CHEST, ACT_GESTURE_FLINCH_CHEST },
{ ACT_FLINCH_STOMACH, ACT_GESTURE_FLINCH_STOMACH },
{ ACT_FLINCH_LEFTARM, ACT_GESTURE_FLINCH_LEFTARM },
{ ACT_FLINCH_RIGHTARM, ACT_GESTURE_FLINCH_RIGHTARM },
{ ACT_FLINCH_LEFTLEG, ACT_GESTURE_FLINCH_LEFTLEG },
{ ACT_FLINCH_RIGHTLEG, ACT_GESTURE_FLINCH_RIGHTLEG },
// -----------------------------------------------------------
#if AR2_ACTIVITY_FIX == 1
{ ACT_RELOAD_AR2, ACT_GESTURE_RELOAD_AR2 },
{ ACT_RELOAD_AR2_LOW, ACT_GESTURE_RELOAD_AR2 },
#endif
{ ACT_RELOAD_SMG1, ACT_GESTURE_RELOAD_SMG1 },
{ ACT_RELOAD_SMG1_LOW, ACT_GESTURE_RELOAD_SMG1 },
{ ACT_RELOAD_SHOTGUN, ACT_GESTURE_RELOAD_SHOTGUN },
{ ACT_RELOAD_SHOTGUN_LOW, ACT_GESTURE_RELOAD_SHOTGUN },
{ ACT_RELOAD_PISTOL, ACT_GESTURE_RELOAD_PISTOL },
{ ACT_RELOAD_PISTOL_LOW, ACT_GESTURE_RELOAD_PISTOL },
#ifdef SHARED_COMBINE_ACTIVITIES
{ ACT_SPECIAL_ATTACK1, ACT_GESTURE_SPECIAL_ATTACK1 },
{ ACT_SPECIAL_ATTACK2, ACT_GESTURE_SPECIAL_ATTACK2 },
{ ACT_COMBINE_THROW_GRENADE, ACT_GESTURE_COMBINE_THROW_GRENADE },
{ ACT_COMBINE_AR2_ALTFIRE, ACT_GESTURE_COMBINE_AR2_ALTFIRE },
{ ACT_SIGNAL_ADVANCE, ACT_GESTURE_SIGNAL_ADVANCE },
{ ACT_SIGNAL_FORWARD, ACT_GESTURE_SIGNAL_FORWARD },
{ ACT_SIGNAL_GROUP, ACT_GESTURE_SIGNAL_GROUP },
{ ACT_SIGNAL_HALT, ACT_GESTURE_SIGNAL_HALT },
{ ACT_SIGNAL_LEFT, ACT_GESTURE_SIGNAL_LEFT },
{ ACT_SIGNAL_RIGHT, ACT_GESTURE_SIGNAL_RIGHT },
{ ACT_SIGNAL_TAKECOVER, ACT_GESTURE_SIGNAL_TAKECOVER },
#endif
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_RANGE_ATTACK_REVOLVER, ACT_GESTURE_RANGE_ATTACK_REVOLVER },
{ ACT_RANGE_ATTACK_REVOLVER_LOW, ACT_GESTURE_RANGE_ATTACK_REVOLVER },
{ ACT_RANGE_ATTACK_CROSSBOW, ACT_GESTURE_RANGE_ATTACK_CROSSBOW },
{ ACT_RANGE_ATTACK_CROSSBOW_LOW, ACT_GESTURE_RANGE_ATTACK_CROSSBOW },
{ ACT_RANGE_ATTACK_RPG, ACT_GESTURE_RANGE_ATTACK_RPG },
{ ACT_RANGE_ATTACK_RPG_LOW, ACT_GESTURE_RANGE_ATTACK_RPG },
{ ACT_RELOAD_REVOLVER, ACT_GESTURE_RELOAD_REVOLVER },
{ ACT_RELOAD_REVOLVER_LOW, ACT_GESTURE_RELOAD_REVOLVER },
{ ACT_RELOAD_CROSSBOW, ACT_GESTURE_RELOAD_CROSSBOW },
{ ACT_RELOAD_CROSSBOW_LOW, ACT_GESTURE_RELOAD_CROSSBOW },
#endif
};
Activity CAI_BaseNPC::GetGestureVersionOfActivity( Activity inActivity )
{
actlink_t *pTable = gm_ActivityGestureLinks;
int actCount = ARRAYSIZE( gm_ActivityGestureLinks );
for ( int i = 0; i < actCount; i++, pTable++ )
{
if ( inActivity == pTable->sequence )
{
return pTable->gesture;
}
}
return ACT_INVALID;
}
Activity CAI_BaseNPC::GetSequenceVersionOfGesture( Activity inActivity )
{
actlink_t *pTable = gm_ActivityGestureLinks;
int actCount = ARRAYSIZE( gm_ActivityGestureLinks );
for (int i = 0; i < actCount; i++, pTable++)
{
if (inActivity == pTable->gesture)
{
return pTable->sequence;
}
}
return ACT_INVALID;
}
#endif

View File

@ -169,6 +169,8 @@ extern ConVar ai_vehicle_avoidance;
extern ISoundEmitterSystemBase *soundemitterbase;
ConVar ai_dynint_always_enabled( "ai_dynint_always_enabled", "0", FCVAR_NONE, "Makes the \"Don't Care\" setting equivalent to \"Yes\"." );
ConVar ai_debug_fake_sequence_gestures_always_play( "ai_debug_fake_sequence_gestures_always_play", "0", FCVAR_NONE, "Always plays fake sequence gestures." );
#endif
#ifndef _RETAIL
@ -313,6 +315,8 @@ ScriptHook_t CAI_BaseNPC::g_Hook_QuerySeeEntity;
ScriptHook_t CAI_BaseNPC::g_Hook_TranslateActivity;
ScriptHook_t CAI_BaseNPC::g_Hook_TranslateSchedule;
ScriptHook_t CAI_BaseNPC::g_Hook_GetActualShootPosition;
ScriptHook_t CAI_BaseNPC::g_Hook_OverrideMove;
ScriptHook_t CAI_BaseNPC::g_Hook_ShouldPlayFakeSequenceGesture;
#endif
//
@ -2594,7 +2598,11 @@ bool CAI_BaseNPC::FValidateHintType ( CAI_Hint *pHint )
Activity CAI_BaseNPC::GetHintActivity( short sHintType, Activity HintsActivity )
{
if ( HintsActivity != ACT_INVALID )
#ifdef MAPBASE
return TranslateActivity( HintsActivity ); // Always translate the activity
#else
return HintsActivity;
#endif
return ACT_IDLE;
}
@ -6509,7 +6517,7 @@ CAI_BaseNPC *CAI_BaseNPC::CreateCustomTarget( const Vector &vecOrigin, float dur
//-----------------------------------------------------------------------------
Activity CAI_BaseNPC::TranslateCrouchActivity( Activity eNewActivity )
{
if (CapabilitiesGet() & bits_CAP_DUCK)
if (CapabilitiesGet() & bits_CAP_DUCK && CanTranslateCrouchActivity())
{
// ========================================================================
// The main issue with cover hint nodes is that crouch activities are not translated at the right time
@ -6541,10 +6549,18 @@ Activity CAI_BaseNPC::TranslateCrouchActivity( Activity eNewActivity )
CAI_Hint *pHint = GetHintNode();
if (pHint)
{
if (pHint->HintType() == HINT_TACTICAL_COVER_LOW || pHint->HintType() == HINT_TACTICAL_COVER_MED)
if (pHint->HintType() == HINT_TACTICAL_COVER_LOW)
{
nCoverActivity = ACT_RANGE_ATTACK1_LOW;
}
else if (pHint->HintType() == HINT_TACTICAL_COVER_MED)
{
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
nCoverActivity = ACT_RANGE_ATTACK1_MED;
#else
nCoverActivity = ACT_RANGE_ATTACK1_LOW;
#endif
}
}
}
}
@ -6586,15 +6602,30 @@ Activity CAI_BaseNPC::NPC_BackupActivity( Activity eNewActivity )
//if (eNewActivity == ACT_DROP_WEAPON)
// return TranslateActivity(ACT_IDLE);
// ---------------------------------------------
// Accounts for certain act busy activities that aren't on all NPCs.
if (eNewActivity == ACT_BUSY_QUEUE || eNewActivity == ACT_BUSY_STAND)
return TranslateActivity(ACT_IDLE);
// ---------------------------------------------
if (eNewActivity == ACT_WALK_ANGRY)
return TranslateActivity(ACT_WALK);
// GetCoverActivity() should have this covered.
// ---------------------------------------------
// If one climbing animation isn't available, use the other
if (eNewActivity == ACT_CLIMB_DOWN)
return ACT_CLIMB_UP;
else if (eNewActivity == ACT_CLIMB_UP)
return ACT_CLIMB_DOWN;
// ---------------------------------------------
if (eNewActivity == ACT_COVER_MED)
eNewActivity = ACT_COVER_LOW;
//if (eNewActivity == ACT_COVER)
// return TranslateActivity(ACT_IDLE);
@ -6609,6 +6640,14 @@ Activity CAI_BaseNPC::NPC_BackupActivity( Activity eNewActivity )
//-----------------------------------------------------------------------------
Activity CAI_BaseNPC::NPC_TranslateActivity( Activity eNewActivity )
{
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
if ( GetNavType() == NAV_CLIMB && eNewActivity == ACT_IDLE )
{
// Schedules which break into idle activities should try to maintain the climbing animation.
return ACT_CLIMB_IDLE;
}
#endif
#ifdef MAPBASE
Assert( eNewActivity != ACT_INVALID );
@ -6831,7 +6870,12 @@ void CAI_BaseNPC::ResolveActivityToSequence(Activity NewActivity, int &iSequence
translatedActivity = TranslateActivity( NewActivity, &weaponActivity );
#ifdef MAPBASE
// Cover cases where TranslateActivity() returns a sequence by using ACT_SCRIPT_CUSTOM_MOVE
if ( NewActivity == ACT_SCRIPT_CUSTOM_MOVE || translatedActivity == ACT_SCRIPT_CUSTOM_MOVE )
#else
if ( NewActivity == ACT_SCRIPT_CUSTOM_MOVE )
#endif
{
iSequence = GetScriptCustomMoveSequence();
}
@ -7017,6 +7061,23 @@ void CAI_BaseNPC::SetIdealActivity( Activity NewActivity )
// Perform translation in case we need to change sequences within a single activity,
// such as between a standing idle and a crouching idle.
ResolveActivityToSequence(m_IdealActivity, m_nIdealSequence, m_IdealTranslatedActivity, m_IdealWeaponActivity);
#ifdef MAPBASE
// Check if we need a gesture to imitate this sequence
if ( ShouldPlayFakeSequenceGesture( m_IdealActivity, m_IdealTranslatedActivity ) )
{
Activity nGesture = SelectFakeSequenceGesture( m_IdealActivity, m_IdealTranslatedActivity );
if (nGesture != -1)
{
PlayFakeSequenceGesture( nGesture, m_IdealActivity, m_IdealTranslatedActivity );
}
}
else if (GetFakeSequenceGesture() != -1)
{
// Reset the current gesture sequence if there is one
ResetFakeSequenceGesture();
}
#endif
}
@ -7065,6 +7126,14 @@ void CAI_BaseNPC::AdvanceToIdealActivity(void)
//DevMsg("%s: Unable to get from sequence %s to %s!\n", GetClassname(), GetSequenceName(GetSequence()), GetSequenceName(m_nIdealSequence));
SetActivity(m_IdealActivity);
}
#ifdef MAPBASE
// If there was a gesture imitating a sequence, get rid of it
if ( GetFakeSequenceGesture() != -1 )
{
ResetFakeSequenceGesture();
}
#endif
}
@ -7122,6 +7191,12 @@ void CAI_BaseNPC::MaintainActivity(void)
}
// Else a transition sequence is in progress, do nothing.
}
#ifdef MAPBASE
else if (GetFakeSequenceGesture() != -1)
{
// Don't advance even if the sequence gesture is finished, as advancing would just play the original activity afterwards
}
#endif
// Else get a specific sequence for the activity and try to transition to that.
else
{
@ -7140,11 +7215,103 @@ void CAI_BaseNPC::MaintainActivity(void)
}
#ifdef MAPBASE
bool CAI_BaseNPC::ShouldPlayFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity )
{
// Don't do anything if we're resetting our activity
if (GetActivity() == ACT_RESET)
return false;
// No need to do this while we're moving
if (IsCurTaskContinuousMove())
return false;
if (ai_debug_fake_sequence_gestures_always_play.GetBool())
return true;
#ifdef MAPBASE_VSCRIPT
if (m_ScriptScope.IsInitialized() && g_Hook_ShouldPlayFakeSequenceGesture.CanRunInScope(m_ScriptScope))
{
// activity, translatedActivity
ScriptVariant_t functionReturn;
ScriptVariant_t args[] = { GetActivityName( nActivity ), GetActivityName( nTranslatedActivity ) };
if (g_Hook_ShouldPlayFakeSequenceGesture.Call( m_ScriptScope, &functionReturn, args ))
{
if (functionReturn.m_type == FIELD_BOOLEAN)
return functionReturn.m_bool;
}
}
#endif
if (GetHintNode() && GetHintNode()->HintActivityName() != NULL_STRING)
{
switch (GetHintNode()->HintType())
{
// Cover nodes with custom activities should allow NPCs to do things like reload while in cover.
case HINT_TACTICAL_COVER_LOW:
case HINT_TACTICAL_COVER_MED:
case HINT_TACTICAL_COVER_CUSTOM:
if (HasMemory( bits_MEMORY_INCOVER ))
{
// Don't attack while using a custom animation in cover
if (nActivity != ACT_RANGE_ATTACK1 && nActivity != ACT_RANGE_ATTACK1_LOW)
return true;
}
break;
}
}
return false;
}
Activity CAI_BaseNPC::SelectFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity )
{
return GetGestureVersionOfActivity( nTranslatedActivity );
}
inline void CAI_BaseNPC::PlayFakeSequenceGesture( Activity nActivity, Activity nSequence, Activity nTranslatedSequence )
{
RestartGesture( nActivity, true, true );
m_FakeSequenceGestureLayer = FindGestureLayer( nActivity );
switch ( nSequence )
{
case ACT_RANGE_ATTACK1:
//case ACT_RANGE_ATTACK2:
{
OnRangeAttack1();
// FIXME: this seems a bit wacked
Weapon_SetActivity( Weapon_TranslateActivity( nSequence ), 0 );
} break;
}
}
inline int CAI_BaseNPC::GetFakeSequenceGesture()
{
return m_FakeSequenceGestureLayer;
}
void CAI_BaseNPC::ResetFakeSequenceGesture()
{
SetLayerCycle( m_FakeSequenceGestureLayer, 1.0f );
m_FakeSequenceGestureLayer = -1;
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Returns true if our ideal activity has finished playing.
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::IsActivityFinished( void )
{
#ifdef MAPBASE
if (GetFakeSequenceGesture() != -1)
{
return IsLayerFinished( GetFakeSequenceGesture() );
}
#endif
return (IsSequenceFinished() && (GetSequence() == m_nIdealSequence));
}
@ -7180,6 +7347,15 @@ void CAI_BaseNPC::OnChangeActivity( Activity eNewActivity )
eNewActivity == ACT_WALK )
{
Stand();
#ifdef MAPBASE
// Unlock custom cover nodes
if (GetHintNode() && GetHintNode()->HintType() == HINT_TACTICAL_COVER_CUSTOM && HasMemory(bits_MEMORY_INCOVER))
{
GetHintNode()->Unlock( GetHintDelay( GetHintNode()->HintType() ) );
SetHintNode( NULL );
}
#endif
}
}
@ -7768,7 +7944,7 @@ int CAI_BaseNPC::HolsterWeapon( void )
if ( IsWeaponHolstered() )
return -1;
#ifdef COMPANION_HOLSTER_WORKAROUND
#ifdef MAPBASE
Activity activity = TranslateActivity( ACT_DISARM );
int iHolsterGesture = FindGestureLayer( activity );
if ( iHolsterGesture != -1 )
@ -7824,7 +8000,7 @@ int CAI_BaseNPC::UnholsterWeapon( void )
if ( !IsWeaponHolstered() )
return -1;
#ifdef COMPANION_HOLSTER_WORKAROUND
#ifdef MAPBASE
Activity activity = TranslateActivity( ACT_ARM );
int iHolsterGesture = FindGestureLayer( activity );
#else
@ -7853,13 +8029,12 @@ int CAI_BaseNPC::UnholsterWeapon( void )
{
SetActiveWeapon( GetWeapon(i) );
#ifdef COMPANION_HOLSTER_WORKAROUND
int iLayer = AddGesture( activity, true );
//iLayer = AddGesture( ACT_GESTURE_ARM, true );
#ifdef MAPBASE
int iLayer = AddGesture( TranslateActivity( ACT_ARM ), true );
#else
int iLayer = AddGesture( ACT_ARM, true );
//iLayer = AddGesture( ACT_GESTURE_ARM, true );
#endif
//iLayer = AddGesture( ACT_GESTURE_ARM, true );
if (iLayer != -1)
{
@ -8038,6 +8213,26 @@ bool CAI_BaseNPC::DoUnholster( void )
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if the NPC should be unholstering their weapon
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::ShouldUnholsterWeapon( void )
{
return GetState() == NPC_STATE_COMBAT;
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if the NPC can unholster their weapon
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::CanUnholsterWeapon( void )
{
// Don't unholster during special navigation
if ( GetNavType() == NAV_JUMP || GetNavType() == NAV_CLIMB )
return false;
return IsWeaponHolstered();
}
//------------------------------------------------------------------------------
// Purpose: Give the NPC in question the weapon specified
//------------------------------------------------------------------------------
@ -9104,27 +9299,45 @@ Activity CAI_BaseNPC::GetCoverActivity( CAI_Hint *pHint )
switch (pHint->HintType())
{
case HINT_TACTICAL_COVER_MED:
#ifndef MAPBASE // I know what you're thinking, but ACT_COVER_MED is pretty much deprecated at this point anyway.
{
nCoverActivity = ACT_COVER_MED;
#ifdef MAPBASE
// Some NPCs lack ACT_COVER_MED, but could easily use ACT_COVER_LOW.
if (SelectWeightedSequence(nCoverActivity) == ACTIVITY_NOT_AVAILABLE)
nCoverActivity = ACT_COVER_LOW;
#endif
// NPCs which lack ACT_COVER_MED should fall through to HINT_TACTICAL_COVER_LOW
if (SelectWeightedSequence( ACT_COVER_MED ) != ACTIVITY_NOT_AVAILABLE)
{
nCoverActivity = ACT_COVER_MED;
}
#else
nCoverActivity = ACT_COVER_MED;
break;
}
#endif
}
case HINT_TACTICAL_COVER_LOW:
{
#ifdef MAPBASE
if (pHint->HintActivityName() != NULL_STRING)
nCoverActivity = (Activity)CAI_BaseNPC::GetActivityID( STRING(pHint->HintActivityName()) );
else
#endif
// Make sure nCoverActivity wasn't already assigned above, then fall through to HINT_TACTICAL_COVER_CUSTOM
if (nCoverActivity == ACT_INVALID)
nCoverActivity = ACT_COVER_LOW;
#else
nCoverActivity = ACT_COVER_LOW;
break;
#endif
}
#ifdef MAPBASE
case HINT_TACTICAL_COVER_CUSTOM:
{
if (pHint->HintActivityName() != NULL_STRING)
{
nCoverActivity = (Activity)CAI_BaseNPC::GetActivityID( STRING(pHint->HintActivityName()) );
if (nCoverActivity == ACT_INVALID)
{
m_iszSceneCustomMoveSeq = pHint->HintActivityName();
nCoverActivity = ACT_SCRIPT_CUSTOM_MOVE;
}
}
break;
}
#endif
}
}
@ -9162,6 +9375,13 @@ float CAI_BaseNPC::CalcIdealYaw( const Vector &vecTarget )
return UTIL_VecToYaw( vecProjection - GetLocalOrigin() );
}
#ifdef MAPBASE
// Allow hint nodes to override the yaw without needing to control AI
else if (GetHintNode() && GetHintNode()->OverridesNPCYaw( this ))
{
return GetHintNode()->Yaw();
}
#endif
else
{
return UTIL_VecToYaw ( vecTarget - GetLocalOrigin() );
@ -9502,7 +9722,7 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent )
if ( GetActiveWeapon() )
{
#ifdef MAPBASE
GetActiveWeapon()->Reload_NPC();
GetActiveWeapon()->Reload_NPC( true );
#else
GetActiveWeapon()->WeaponSound( RELOAD_NPC );
GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1();
@ -9526,8 +9746,12 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent )
case EVENT_WEAPON_RELOAD_FILL_CLIP:
{
if ( GetActiveWeapon() )
{
{
#ifdef MAPBASE
GetActiveWeapon()->Reload_NPC( false );
#else
GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1();
#endif
ClearCondition(COND_LOW_PRIMARY_AMMO);
ClearCondition(COND_NO_PRIMARY_AMMO);
ClearCondition(COND_NO_SECONDARY_AMMO);
@ -9542,7 +9766,11 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent )
case NPC_EVENT_OPEN_DOOR:
{
#ifdef MAPBASE
CBasePropDoor *pDoor = m_hOpeningDoor;
#else
CBasePropDoor *pDoor = (CBasePropDoor *)(CBaseEntity *)GetNavigator()->GetPath()->GetCurWaypoint()->GetEHandleData();
#endif
if (pDoor != NULL)
{
OpenPropDoorNow( pDoor );
@ -11904,6 +12132,7 @@ BEGIN_DATADESC( CAI_BaseNPC )
DEFINE_KEYFIELD( m_FriendlyFireOverride, FIELD_INTEGER, "FriendlyFireOverride" ),
DEFINE_KEYFIELD( m_flSpeedModifier, FIELD_FLOAT, "BaseSpeedModifier" ),
DEFINE_FIELD( m_FakeSequenceGestureLayer, FIELD_INTEGER ),
#endif
// Satisfy classcheck
@ -12061,6 +12290,11 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC
DEFINE_SCRIPTFUNC_NAMED( ScriptSetActivityID, "SetActivityID", "Set the NPC's current activity ID." )
DEFINE_SCRIPTFUNC( ResetActivity, "Reset the NPC's current activity." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetGestureVersionOfActivity, "GetGestureVersionOfActivity", "Get the gesture activity counterpart of the specified sequence activity, if one exists." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetGestureVersionOfActivityID, "GetGestureVersionOfActivityID", "Get the gesture activity ID counterpart of the specified sequence activity ID, if one exists." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetSequenceVersionOfGesture, "GetSequenceVersionOfGesture", "Get the sequence activity counterpart of the specified gesture activity, if one exists." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetSequenceVersionOfGestureID, "GetSequenceVersionOfGestureID", "Get the sequence activity ID counterpart of the specified gesture activity ID, if one exists." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetSchedule, "GetSchedule", "Get the NPC's current schedule." )
DEFINE_SCRIPTFUNC_NAMED( VScriptGetScheduleID, "GetScheduleID", "Get the NPC's current schedule ID." )
DEFINE_SCRIPTFUNC_NAMED( VScriptSetSchedule, "SetSchedule", "Set the NPC's current schedule." )
@ -12107,10 +12341,17 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC
DEFINE_SCRIPTHOOK_PARAM( "schedule", FIELD_CSTRING )
DEFINE_SCRIPTHOOK_PARAM( "schedule_id", FIELD_INTEGER )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_GetActualShootPosition, "GetActualShootPosition", FIELD_VOID, "Called when the NPC is getting their actual shoot position, using the default shoot position as the parameter. (NOTE: NPCs which override this themselves might not always use this hook!)" )
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_GetActualShootPosition, "GetActualShootPosition", FIELD_VECTOR, "Called when the NPC is getting their actual shoot position, using the default shoot position as the parameter. (NOTE: NPCs which override this themselves might not always use this hook!)" )
DEFINE_SCRIPTHOOK_PARAM( "shootOrigin", FIELD_VECTOR )
DEFINE_SCRIPTHOOK_PARAM( "target", FIELD_HSCRIPT )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_OverrideMove, "OverrideMove", FIELD_VOID, "Called when the NPC runs movement code, allowing the NPC's movement to be overridden by some other method. (NOTE: NPCs which override this themselves might not always use this hook!)" )
DEFINE_SCRIPTHOOK_PARAM( "interval", FIELD_FLOAT )
END_SCRIPTHOOK()
BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_ShouldPlayFakeSequenceGesture, "ShouldPlayFakeSequenceGesture", FIELD_BOOLEAN, "Called when an activity is set on a NPC. Returning true will make the NPC convert the activity into a gesture (if a gesture is available) and continue their current activity instead." )
DEFINE_SCRIPTHOOK_PARAM( "activity", FIELD_CSTRING )
DEFINE_SCRIPTHOOK_PARAM( "translatedActivity", FIELD_CSTRING )
END_SCRIPTHOOK()
END_SCRIPTDESC();
#endif
@ -12761,6 +13002,8 @@ CAI_BaseNPC::CAI_BaseNPC(void)
#ifdef MAPBASE
m_iDynamicInteractionsAllowed = TRS_NONE;
m_flSpeedModifier = 1.0f;
m_FakeSequenceGestureLayer = -1;
#endif
}
@ -13715,6 +13958,10 @@ bool CAI_BaseNPC::OnUpcomingPropDoor( AILocalMoveGoal_t *pMoveGoal,
GetNavigator()->GetPath()->PrependWaypoints( pOpenDoorRoute );
#ifdef MAPBASE
GetNavigator()->SetArrivalDirection( opendata.vecFaceDir );
#endif
m_hOpeningDoor = pDoor;
pMoveGoal->maxDist = distClear;
*pResult = AIMR_CHANGE_TYPE;
@ -13735,6 +13982,21 @@ bool CAI_BaseNPC::OnUpcomingPropDoor( AILocalMoveGoal_t *pMoveGoal,
//-----------------------------------------------------------------------------
void CAI_BaseNPC::OpenPropDoorBegin( CBasePropDoor *pDoor )
{
#ifdef MAPBASE
opendata_t opendata;
pDoor->GetNPCOpenData(this, opendata);
if (HaveSequenceForActivity( opendata.eActivity ))
{
int iLayer = AddGesture( opendata.eActivity );
float flDuration = GetLayerDuration( iLayer );
// Face the door and wait for the activity to finish before trying to move through the doorway.
m_flMoveWaitFinished = gpGlobals->curtime + flDuration + pDoor->GetOpenInterval();
AddFacingTarget( opendata.vecFaceDir, 1.0, flDuration );
}
else
#else
// dvs: not quite working, disabled for now.
//opendata_t opendata;
//pDoor->GetNPCOpenData(this, opendata);
@ -13744,6 +14006,7 @@ void CAI_BaseNPC::OpenPropDoorBegin( CBasePropDoor *pDoor )
// SetIdealActivity(opendata.eActivity);
//}
//else
#endif
{
// We don't have an appropriate sequence, just open the door magically.
OpenPropDoorNow( pDoor );
@ -13763,6 +14026,15 @@ void CAI_BaseNPC::OpenPropDoorNow( CBasePropDoor *pDoor )
// Wait for the door to finish opening before trying to move through the doorway.
m_flMoveWaitFinished = gpGlobals->curtime + pDoor->GetOpenInterval();
#ifdef MAPBASE
// Remove the door from our waypoint
if (GetNavigator()->GetPath() && GetNavigator()->GetCurWaypointFlags() & bits_WP_TO_DOOR)
{
GetNavigator()->GetPath()->GetCurWaypoint()->ModifyFlags( bits_WP_TO_DOOR, false );
GetNavigator()->GetPath()->GetCurWaypoint()->m_hData = NULL;
}
#endif
}
@ -15952,6 +16224,26 @@ bool CAI_BaseNPC::CouldShootIfCrouching( CBaseEntity *pTarget )
return bResult;
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: Check if this position will block our line of sight if aiming low.
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::CouldShootIfCrouchingAt( const Vector &vecPosition, const Vector &vecForward, const Vector &vecRight, float flDist )
{
Vector vGunPos = vecPosition;
vGunPos += (GetCrouchGunOffset() + vecRight * 8);
trace_t tr;
AI_TraceLOS( vGunPos, vGunPos + (vecForward * flDist), this, &tr );
if (tr.fraction != 1.0)
{
return false;
}
return true;
}
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
@ -15967,9 +16259,16 @@ bool CAI_BaseNPC::IsCrouchedActivity( Activity activity )
case ACT_COVER_SMG1_LOW:
case ACT_RELOAD_SMG1_LOW:
#ifdef MAPBASE
//case ACT_RELOAD_AR2_LOW:
#if AR2_ACTIVITY_FIX == 1
case ACT_COVER_AR2_LOW:
case ACT_RELOAD_AR2_LOW:
#endif
case ACT_RELOAD_PISTOL_LOW:
case ACT_RELOAD_SHOTGUN_LOW:
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
case ACT_RELOAD_REVOLVER_LOW:
case ACT_RELOAD_CROSSBOW_LOW:
#endif
#endif
return true;
}

View File

@ -998,6 +998,7 @@ public:
Activity NPC_TranslateActivity( Activity eNewActivity );
#ifdef MAPBASE
Activity TranslateCrouchActivity( Activity baseAct );
virtual bool CanTranslateCrouchActivity( void ) { return true; }
virtual Activity NPC_BackupActivity( Activity eNewActivity );
#endif
Activity GetActivity( void ) { return m_Activity; }
@ -1020,6 +1021,25 @@ public:
void SetActivityAndSequence(Activity NewActivity, int iSequence, Activity translatedActivity, Activity weaponActivity);
#ifdef MAPBASE
//-----------------------------------------------------
// Returns the gesture variant of an activity (i.e. "ACT_GESTURE_RANGE_ATTACK1")
static Activity GetGestureVersionOfActivity( Activity inActivity );
// Returns the sequence variant of a gesture activity
static Activity GetSequenceVersionOfGesture( Activity inActivity );
//-----------------------------------------------------
virtual bool ShouldPlayFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity );
virtual Activity SelectFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity );
void PlayFakeSequenceGesture( Activity nActivity, Activity nSequence, Activity nTranslatedSequence );
int GetFakeSequenceGesture();
void ResetFakeSequenceGesture();
#endif
private:
void AdvanceToIdealActivity(void);
@ -1033,6 +1053,10 @@ private:
Activity m_IdealTranslatedActivity; // Desired actual translated animation state
Activity m_IdealWeaponActivity; // Desired weapon animation state
#ifdef MAPBASE
int m_FakeSequenceGestureLayer; // The gesture layer impersonating a sequence (-1 if invalid)
#endif
CNetworkVar(int, m_iDeathPose );
CNetworkVar(int, m_iDeathFrame );
@ -1217,6 +1241,8 @@ public:
#endif
#ifdef MAPBASE_VSCRIPT
private:
// VScript stuff uses "VScript" instead of just "Script" to avoid
// confusion with NPC_STATE_SCRIPT or StartScripting
HSCRIPT VScriptGetEnemy();
@ -1243,6 +1269,11 @@ public:
int ScriptTranslateActivity( const char *szActivity ) { return TranslateActivity( (Activity)GetActivityID( szActivity ) ); }
int ScriptTranslateActivityID( int iActivity ) { return TranslateActivity( (Activity)iActivity ); }
const char* VScriptGetGestureVersionOfActivity( const char *pszActivity ) { return GetActivityName( GetGestureVersionOfActivity( (Activity)GetActivityID( pszActivity ) ) ); }
int VScriptGetGestureVersionOfActivityID( int iActivity ) { return GetGestureVersionOfActivity( (Activity)iActivity ); }
const char* VScriptGetSequenceVersionOfGesture( const char *pszActivity ) { return GetActivityName( GetSequenceVersionOfGesture( (Activity)GetActivityID( pszActivity ) ) ); }
int VScriptGetSequenceVersionOfGestureID( int iActivity ) { return GetSequenceVersionOfGesture( (Activity)iActivity ); }
const char* VScriptGetSchedule();
int VScriptGetScheduleID();
void VScriptSetSchedule( const char *szSchedule );
@ -1731,8 +1762,8 @@ public:
virtual bool DoHolster(void);
virtual bool DoUnholster(void);
virtual bool ShouldUnholsterWeapon() { return GetState() == NPC_STATE_COMBAT; }
virtual bool CanUnholsterWeapon() { return IsWeaponHolstered(); }
virtual bool ShouldUnholsterWeapon();
virtual bool CanUnholsterWeapon();
void InputGiveWeaponHolstered( inputdata_t &inputdata );
void InputChangeWeapon( inputdata_t &inputdata );
@ -2207,6 +2238,10 @@ public:
inline void ForceCrouch( void );
inline void ClearForceCrouch( void );
#ifdef MAPBASE
bool CouldShootIfCrouchingAt( const Vector &vecPosition, const Vector &vecForward, const Vector &vecRight, float flDist = 48.0f );
#endif
protected:
virtual bool Crouch( void );
virtual bool Stand( void );
@ -2266,6 +2301,16 @@ private:
static CAI_GlobalScheduleNamespace gm_SchedulingSymbols;
static CAI_ClassScheduleIdSpace gm_ClassScheduleIdSpace;
#ifdef MAPBASE
typedef struct
{
Activity sequence;
Activity gesture;
} actlink_t;
static actlink_t gm_ActivityGestureLinks[];
#endif
public:
//----------------------------------------------------
// Debugging tools
@ -2317,6 +2362,7 @@ public:
static ScriptHook_t g_Hook_TranslateSchedule;
static ScriptHook_t g_Hook_GetActualShootPosition;
static ScriptHook_t g_Hook_OverrideMove;
static ScriptHook_t g_Hook_ShouldPlayFakeSequenceGesture;
#endif
private:

View File

@ -980,11 +980,12 @@ bool CAI_BaseNPC::FindCoverFromEnemy( bool bNodesOnly, float flMinDistance, floa
// FIXME: add to goal
if (GetHintNode())
{
GetNavigator()->SetArrivalActivity( GetCoverActivity( GetHintNode() ) );
#ifdef MAPBASE
if (GetHintNode()->GetIgnoreFacing() != HIF_NO)
#endif
GetHintNode()->NPCHandleStartNav( this, true );
#else
GetNavigator()->SetArrivalActivity( GetCoverActivity( GetHintNode() ) );
GetNavigator()->SetArrivalDirection( GetHintNode()->GetDirection() );
#endif
}
return true;
@ -1363,6 +1364,14 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask )
break;
case TASK_STOP_MOVING:
#ifdef MAPBASE
if ( GetNavType() == NAV_CLIMB )
{
// Don't clear the goal so that the climb can finish
DbgNavMsg( this, "Start TASK_STOP_MOVING with climb workaround\n" );
}
else
#endif
if ( ( GetNavigator()->IsGoalSet() && GetNavigator()->IsGoalActive() ) || GetNavType() == NAV_JUMP )
{
DbgNavMsg( this, "Start TASK_STOP_MOVING\n" );
@ -3348,8 +3357,40 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask )
// a navigation while in the middle of a climb
if (GetNavType() == NAV_CLIMB)
{
#ifdef MAPBASE
if (GetActivity() != ACT_CLIMB_DISMOUNT)
{
// Try to just pause the climb, but dismount if we're in SCHED_FAIL
if (IsCurSchedule( SCHED_FAIL, false ))
{
GetMotor()->MoveClimbStop();
}
else
{
GetMotor()->MoveClimbPause();
}
TaskComplete();
}
else if (IsActivityFinished())
{
// Dismount complete.
GetMotor()->MoveClimbStop();
// Fix up our position if we have to
Vector vecTeleportOrigin;
if (GetMotor()->MoveClimbShouldTeleportToSequenceEnd( vecTeleportOrigin ))
{
SetLocalOrigin( vecTeleportOrigin );
}
TaskComplete();
}
break;
#else
// wait until you reach the end
break;
#endif
}
DbgNavMsg( this, "TASK_STOP_MOVING Complete\n" );
@ -3394,6 +3435,17 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask )
// If the yaw is locked, this function will not act correctly
Assert( GetMotor()->IsYawLocked() == false );
#ifdef MAPBASE
if ( GetHintNode() && GetHintNode()->OverridesNPCYaw( this ) )
{
// If the yaw is supposed to use that of a hint node, chain to TASK_FACE_HINTNODE
GetMotor()->SetIdealYaw( GetHintNode()->Yaw() );
GetMotor()->SetIdealYaw( CalcReasonableFacing( true ) ); // CalcReasonableFacing() is based on previously set ideal yaw
ChainRunTask( TASK_FACE_HINTNODE, pTask->flTaskData );
break;
}
#endif
Vector vecEnemyLKP = GetEnemyLKP();
if (!FInAimCone( vecEnemyLKP ))
{
@ -4280,6 +4332,15 @@ void CAI_BaseNPC::SetTurnActivity ( void )
float flYD;
flYD = GetMotor()->DeltaIdealYaw();
#ifdef MAPBASE
// Allow AddTurnGesture() to decide this
if (GetMotor()->AddTurnGesture( flYD ))
{
SetIdealActivity( ACT_IDLE );
Remember( bits_MEMORY_TURNING );
return;
}
#else
// FIXME: unknown case, update yaw should catch these
/*
if (GetMotor()->AddTurnGesture( flYD ))
@ -4289,6 +4350,7 @@ void CAI_BaseNPC::SetTurnActivity ( void )
return;
}
*/
#endif
if( flYD <= -80 && flYD >= -100 && SelectWeightedSequence( ACT_90_RIGHT ) != ACTIVITY_NOT_AVAILABLE )
{

View File

@ -420,6 +420,24 @@ bool CAI_BehaviorBase::CanUnholsterWeapon( void )
return m_pBackBridge->BackBridge_CanUnholsterWeapon();
}
//-------------------------------------
bool CAI_BehaviorBase::ShouldPickADeathPose( void )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_ShouldPickADeathPose();
}
//-------------------------------------
bool CAI_BehaviorBase::CanTranslateCrouchActivity( void )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_CanTranslateCrouchActivity();
}
#endif
//-------------------------------------

View File

@ -132,6 +132,8 @@ public:
void BridgeHandleAnimEvent( animevent_t *pEvent );
#ifdef MAPBASE
bool BridgeCanUnholsterWeapon( void );
bool BridgeShouldPickADeathPose( void );
bool BridgeCanTranslateCrouchActivity( void );
#endif
virtual void GatherConditions();
@ -220,6 +222,8 @@ protected:
virtual void HandleAnimEvent( animevent_t *pEvent );
#ifdef MAPBASE
virtual bool CanUnholsterWeapon( void );
virtual bool ShouldPickADeathPose( void );
virtual bool CanTranslateCrouchActivity( void );
#endif
virtual bool ShouldAlwaysThink();
@ -370,6 +374,9 @@ public:
#ifdef MAPBASE
// For func_tank behavior
virtual bool BackBridge_CanUnholsterWeapon( void ) = 0;
virtual bool BackBridge_ShouldPickADeathPose( void ) = 0;
virtual bool BackBridge_CanTranslateCrouchActivity( void ) = 0;
#endif
//-------------------------------------
@ -470,6 +477,8 @@ public:
void HandleAnimEvent( animevent_t *pEvent );
#ifdef MAPBASE
bool CanUnholsterWeapon( void );
bool ShouldPickADeathPose( void );
bool CanTranslateCrouchActivity( void );
#endif
bool ShouldAlwaysThink();
@ -534,6 +543,9 @@ private:
#ifdef MAPBASE
// For func_tank behavior
bool BackBridge_CanUnholsterWeapon( void );
bool BackBridge_ShouldPickADeathPose( void );
bool BackBridge_CanTranslateCrouchActivity( void );
#endif
CAI_BehaviorBase **AccessBehaviors();
@ -913,6 +925,20 @@ inline bool CAI_BehaviorBase::BridgeCanUnholsterWeapon( void )
{
return CanUnholsterWeapon();
}
//-----------------------------------------------------------------------------
inline bool CAI_BehaviorBase::BridgeShouldPickADeathPose( void )
{
return ShouldPickADeathPose();
}
//-----------------------------------------------------------------------------
inline bool CAI_BehaviorBase::BridgeCanTranslateCrouchActivity( void )
{
return CanTranslateCrouchActivity();
}
#endif
//-----------------------------------------------------------------------------
@ -1498,6 +1524,22 @@ inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_CanUnholsterWeapon( void )
{
return BaseClass::CanUnholsterWeapon();
}
//-------------------------------------
template <class BASE_NPC>
inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_ShouldPickADeathPose( void )
{
return BaseClass::ShouldPickADeathPose();
}
//-------------------------------------
template <class BASE_NPC>
inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_CanTranslateCrouchActivity( void )
{
return BaseClass::CanTranslateCrouchActivity();
}
#endif
//-------------------------------------
@ -1914,6 +1956,28 @@ inline bool CAI_BehaviorHost<BASE_NPC>::CanUnholsterWeapon( void )
return BaseClass::CanUnholsterWeapon();
}
//-------------------------------------
template <class BASE_NPC>
inline bool CAI_BehaviorHost<BASE_NPC>::ShouldPickADeathPose( void )
{
if (m_pCurBehavior)
return m_pCurBehavior->BridgeShouldPickADeathPose();
return BaseClass::ShouldPickADeathPose();
}
//-------------------------------------
template <class BASE_NPC>
inline bool CAI_BehaviorHost<BASE_NPC>::CanTranslateCrouchActivity( void )
{
if (m_pCurBehavior)
return m_pCurBehavior->BridgeCanTranslateCrouchActivity();
return BaseClass::CanTranslateCrouchActivity();
}
#endif
//-------------------------------------

View File

@ -535,10 +535,11 @@ int CAI_StandoffBehavior::SelectScheduleCheckCover( void )
{
StandoffMsg( "Regulated to not shoot\n" );
#ifdef MAPBASE
if ( GetHintType() == HINT_TACTICAL_COVER_LOW || GetHintType() == HINT_TACTICAL_COVER_MED )
#else
if ( GetHintType() == HINT_TACTICAL_COVER_LOW )
if ( GetHintType() == HINT_TACTICAL_COVER_MED || GetCoverActivity() == ACT_COVER_MED )
SetPosture( AIP_CROUCHING_MED );
else
#endif
if ( GetHintType() == HINT_TACTICAL_COVER_LOW )
SetPosture( AIP_CROUCHING );
else
SetPosture( AIP_STANDING );
@ -557,7 +558,11 @@ int CAI_StandoffBehavior::SelectScheduleEstablishAim( void )
{
if ( HasCondition( COND_ENEMY_OCCLUDED ) )
{
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
if ( GetPosture() == AIP_CROUCHING || GetPosture() == AIP_CROUCHING_MED )
#else
if ( GetPosture() == AIP_CROUCHING )
#endif
{
// force a stand up, just in case
GetOuter()->SpeakSentence( STANDOFF_SENTENCE_STAND_CHECK_TARGET );
@ -668,6 +673,15 @@ Activity CAI_MappedActivityBehavior_Temporary::GetMappedActivity( AI_Posture_t p
{
if ( posture != AIP_STANDING )
{
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
// See UpdateTranslateActivityMap() for more information on what this is for
if ( posture == AIP_CROUCHING_MED )
{
if (activity != ACT_RANGE_ATTACK1)
posture = AIP_CROUCHING;
}
#endif
unsigned short iActivityTranslation = m_ActivityMap.Find( MAKE_ACTMAP_KEY( posture, activity ) );
if ( iActivityTranslation != m_ActivityMap.InvalidIndex() )
{
@ -685,10 +699,28 @@ Activity CAI_StandoffBehavior::NPC_TranslateActivity( Activity activity )
Activity coverActivity = GetCoverActivity();
if ( coverActivity != ACT_INVALID )
{
#ifdef MAPBASE
if ( GetPosture() == AIP_STANDING )
{
if ( coverActivity == ACT_COVER_LOW )
SetPosture( AIP_CROUCHING );
else if ( coverActivity == ACT_COVER_MED )
{
SetPosture( AIP_CROUCHING_MED );
coverActivity = ACT_COVER_LOW;
}
}
else if (coverActivity == ACT_COVER_MED)
coverActivity = ACT_COVER_LOW;
if ( activity == ACT_IDLE )
activity = coverActivity;
#else
if (activity == ACT_IDLE)
activity = coverActivity;
if ( GetPosture() == AIP_STANDING && coverActivity == ACT_COVER_LOW )
SetPosture( AIP_CROUCHING );
#endif
}
Activity result = GetMappedActivity( GetPosture(), activity );
@ -1089,12 +1121,25 @@ void CAI_StandoffBehavior::UnlockHintNode()
Activity CAI_StandoffBehavior::GetCoverActivity()
{
#ifdef MAPBASE
// This does two things:
// A. Allows medium cover nodes to be used, kind of.
// B. GetCoverActivity() already checks everything we checked here.
Activity coveract = GetOuter()->GetCoverActivity( GetHintNode() );
return coveract == ACT_IDLE ? ACT_INVALID : coveract;
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
// GetCoverActivity() already checks everything we checked here.
Activity coverActivity = GetOuter()->GetCoverActivity( GetHintNode() );
if (coverActivity == ACT_COVER_LOW)
{
// Check if this node will block our line of sight if aiming low.
Vector vHintPos, vHintForward, vHintRight;
GetHintNode()->GetPosition( GetHullType(), &vHintPos );
vHintForward = GetHintNode()->GetDirection();
GetHintNode()->GetVectors( NULL, &vHintRight, NULL );
if (GetOuter()->CouldShootIfCrouchingAt( vHintPos, vHintForward, vHintRight ))
{
coverActivity = ACT_COVER_MED;
}
}
return coverActivity == ACT_IDLE ? ACT_INVALID : coverActivity;
#else
CAI_Hint *pHintNode = GetHintNode();
if ( pHintNode && pHintNode->HintType() == HINT_TACTICAL_COVER_LOW )
@ -1112,6 +1157,14 @@ struct AI_ActivityMapping_t
Activity activity;
const char * pszWeapon;
Activity translation;
#ifdef MAPBASE
Activity backup;
AI_ActivityMapping_t( AI_Posture_t _p, Activity _a, const char *_w, Activity _t, Activity _b = ACT_INVALID )
{
posture = _p; activity = _a; pszWeapon = _w; translation = _t; backup = _b;
}
#endif
};
void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap()
@ -1125,15 +1178,60 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap()
{ AIP_CROUCHING, ACT_WALK_AIM, NULL, ACT_WALK_CROUCH_AIM, },
{ AIP_CROUCHING, ACT_RUN_AIM, NULL, ACT_RUN_CROUCH_AIM, },
{ AIP_CROUCHING, ACT_RELOAD, NULL, ACT_RELOAD_LOW, },
#ifdef MAPBASE
{ AIP_CROUCHING, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_LOW, },
{ AIP_CROUCHING, ACT_COVER_MED, NULL, ACT_COVER_LOW, },
#else
{ AIP_CROUCHING, ACT_RANGE_ATTACK_SMG1, NULL, ACT_RANGE_ATTACK_SMG1_LOW, },
{ AIP_CROUCHING, ACT_RANGE_ATTACK_AR2, NULL, ACT_RANGE_ATTACK_AR2_LOW, },
#endif
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
//
// ============ Really long explanation that should be in a wiki/documentation article somewhere ~ Blixibon, 10/27/2021 ============
//
// Standoff behavior assumes low attack animations allow NPCs to see over barricades, with ACT_COVER_LOW being their "safely in cover" animation.
// This is why AIP_CROUCHING translates ACT_RANGE_ATTACK1 to its low animation, but translates ACT_IDLE_ANGRY to ACT_COVER_LOW instead of ACT_RANGE_AIM_LOW,
// as this would ideally allow NPCs to pop in and out of cover to shoot.
// This is also why AIP_PEEKING translates ACT_COVER_LOW to ACT_RANGE_AIM_LOW, as it's supposed to force the NPC to peek over their cover.
//
// However, this assumption mainly just applies to metrocops. Citizens' low attacking animations crouch low to the ground (which isn't effective for
// shooting over most barricades) and, while they do have a distinct ACT_COVER_LOW animation with transitions, they are close enough together that popping
// in and out of cover is redundant in most cases. Meanwhile, Combine soldiers have identical ACT_COVER_LOW and ACT_RANGE_AIM_LOW animations, which means
// they do not pop in and out of cover and AIP_PEEKING does nothing. This may be the reason why Combine soldiers occasionally get stuck in cover after a fight.
//
// -------------------------------------------------------------
//
// As part of Mapbase v7.0's NPC activity overhaul, a new "medium cover" activity set has been added. Metrocops' previous "low cover" animation set (which, as
// mentioned before, is different from that of other NPCs) has been retroactively changed to use "medium cover". This was done for a few reasons unrelated to
// standoff behavior, but the important point is that these activities indicate a new cover height. This means we can use them to give standoff behavior more leeway
// for judging which animations to use in various levels of cover.
//
// Standoff behavior can use "medium cover" animations in cover which is too high for the "low" animations, and when the medium cover animations are not available,
// it simply falls back to the "standing" animations, thus resolving the issue with other NPCs not peeking in and out of cover without requiring new medium cover
// animations.
//
// In Mapbase, this is done by changing AIP_PEEKING to use the medium cover animations and adding a new alternate crouching posture posture called "AIP_CROUCHING_MED",
// which only uses the medium cover attack activity and otherwise automatically falls back to AIP_CROUCHING. AIP_CROUCHING_MED is automatically set if the NPC cannot
// get LOS from a regular crouching position.
//
{ AIP_CROUCHING_MED, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_MED, },
//----
{ AIP_PEEKING, ACT_IDLE, NULL, ACT_RANGE_AIM_MED, },
{ AIP_PEEKING, ACT_IDLE_ANGRY, NULL, ACT_RANGE_AIM_MED, },
{ AIP_PEEKING, ACT_COVER_LOW, NULL, ACT_RANGE_AIM_MED, ACT_IDLE_ANGRY },
{ AIP_PEEKING, ACT_COVER_MED, NULL, ACT_RANGE_AIM_MED, ACT_IDLE_ANGRY },
{ AIP_PEEKING, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_MED, },
{ AIP_PEEKING, ACT_RELOAD, NULL, ACT_RELOAD_LOW, },
#else
//----
{ AIP_PEEKING, ACT_IDLE, NULL, ACT_RANGE_AIM_LOW, },
{ AIP_PEEKING, ACT_IDLE_ANGRY, NULL, ACT_RANGE_AIM_LOW, },
{ AIP_PEEKING, ACT_COVER_LOW, NULL, ACT_RANGE_AIM_LOW, },
{ AIP_PEEKING, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_LOW, },
{ AIP_PEEKING, ACT_RELOAD, NULL, ACT_RELOAD_LOW, },
#endif
};
m_ActivityMap.RemoveAll();
@ -1145,7 +1243,7 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap()
if ( !mappings[i].pszWeapon || stricmp( mappings[i].pszWeapon, pszWeaponClass ) == 0 )
{
#ifdef MAPBASE
// Check backup activity
// Check NPC backup activity
if ( HaveSequenceForActivity( mappings[i].translation ) || HaveSequenceForActivity( GetOuter()->Weapon_TranslateActivity( mappings[i].translation ) ) || HaveSequenceForActivity( GetOuter()->Weapon_BackupActivity( mappings[i].translation ) ) )
#else
if ( HaveSequenceForActivity( mappings[i].translation ) || HaveSequenceForActivity( GetOuter()->Weapon_TranslateActivity( mappings[i].translation ) ) )
@ -1154,6 +1252,14 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap()
Assert( m_ActivityMap.Find( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ) ) == m_ActivityMap.InvalidIndex() );
m_ActivityMap.Insert( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ), mappings[i].translation );
}
#ifdef MAPBASE
// Check activity map backup activity
else if ( mappings[i].backup != ACT_INVALID && HaveSequenceForActivity( mappings[i].backup ) )
{
Assert( m_ActivityMap.Find( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ) ) == m_ActivityMap.InvalidIndex() );
m_ActivityMap.Insert( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ), mappings[i].backup );
}
#endif
}
}
}

View File

@ -51,6 +51,9 @@ enum AI_Posture_t
AIP_INDIFFERENT,
AIP_STANDING,
AIP_CROUCHING,
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
AIP_CROUCHING_MED, // See UpdateTranslateActivityMap() for more information on what this is for
#endif
AIP_PEEKING,
};
@ -149,6 +152,14 @@ protected:
// Standoff overrides base AI crouch handling
bool IsCrouching( void ) { return false; }
#ifdef MAPBASE
// Standoff overrides base cover activity translation
bool CanTranslateCrouchActivity( void ) { return false; }
// Don't do death poses while crouching
bool ShouldPickADeathPose( void ) { return (GetPosture() != AIP_CROUCHING && GetPosture() != AIP_PEEKING) && BaseClass::ShouldPickADeathPose(); }
#endif
private:

View File

@ -1640,10 +1640,17 @@ void CAI_BlendedMotor::MaintainTurnActivity( void )
ConVar scene_flatturn( "scene_flatturn", "1" );
#ifdef MAPBASE
ConVar ai_turning_enabled( "ai_turning_enabled", "1", FCVAR_NONE, "Enables NPC turning, which was previously disabled by Valve at some point after 2004 due to a now-unknown major issue." );
#endif
bool CAI_BlendedMotor::AddTurnGesture( float flYD )
{
// some funky bug with human turn gestures, disable for now
#ifdef MAPBASE
if (!ai_turning_enabled.GetBool())
#endif
return false;
// try using a turn gesture

View File

@ -35,6 +35,10 @@ CHintCriteria::CHintCriteria( void )
m_strGroup = NULL_STRING;
m_iFlags = 0;
m_HintTypes.Purge();
#ifdef MAPBASE // From Alien Swarm SDK
m_pfnFilter = NULL;
m_pFilterContext = NULL;
#endif
}
//-----------------------------------------------------------------------------
@ -1117,10 +1121,10 @@ void CAI_Hint::NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing )
HintIgnoreFacing_t facing = GetIgnoreFacing();
if (facing == HIF_DEFAULT)
facing = bDefaultFacing ? HIF_YES : HIF_NO;
facing = bDefaultFacing ? HIF_NO : HIF_YES;
if (facing == HIF_YES)
pNPC->GetNavigator()->SetArrivalDirection(GetDirection());
if (facing == HIF_NO)
pNPC->GetNavigator()->SetArrivalDirection( GetDirection() );
if (HintActivityName() != NULL_STRING)
{
@ -1139,6 +1143,51 @@ void CAI_Hint::NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing )
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if this hint should override a NPC's yaw even during regular AI.
//-----------------------------------------------------------------------------
bool CAI_Hint::OverridesNPCYaw( CAI_BaseNPC *pNPC )
{
switch (HintType())
{
case HINT_TACTICAL_COVER_CUSTOM:
case HINT_TACTICAL_COVER_MED:
case HINT_TACTICAL_COVER_LOW:
{
if (pNPC->HasMemory( bits_MEMORY_INCOVER ))
{
// By default, don't override yaw on cover nodes unless they use custom activities.
HintIgnoreFacing_t facing = GetIgnoreFacing();
if (facing == HIF_DEFAULT)
return ( HintActivityName() != NULL_STRING );
return facing == HIF_NO;
}
break;
}
case HINT_PLAYER_ALLY_MOVE_AWAY_DEST:
{
Vector vHintPos;
GetPosition( pNPC, &vHintPos );
if (VectorsAreEqual( vHintPos, pNPC->GetAbsOrigin(), 0.1f ))
{
// By default, don't override yaw on move away destinations unless they use custom activities.
HintIgnoreFacing_t facing = GetIgnoreFacing();
if (facing == HIF_DEFAULT)
return ( HintActivityName() != NULL_STRING );
return facing == HIF_NO;
}
break;
}
}
return false;
}
#endif
//-----------------------------------------------------------------------------
@ -1258,6 +1307,30 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint
return false;
}
#ifdef MAPBASE
// Test against generic filter
// (From Alien Swarm SDK)
if ( !hintCriteria.PassesFilter( this ) )
{
REPORTFAILURE( "Failed filter test" );
return false;
}
// (From Alien Swarm SDK)
int nRadius = GetRadius();
if ( nRadius != 0 )
{
// Calculate our distance
float distance = (GetAbsOrigin() - position).LengthSqr();
if ( distance > nRadius * nRadius )
{
REPORTFAILURE( "NPC is not within the node's radius." );
return false;
}
}
#endif
if ( hintCriteria.HasFlag(bits_HINT_NPC_IN_NODE_FOV) )
{
if ( pNPC == NULL )
@ -1377,10 +1450,18 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint
{
trace_t tr;
// Can my bounding box fit there?
#ifdef MAPBASE // From Alien Swarm SDK
Vector vStep( 0, 0, pNPC->StepHeight() );
AI_TraceHull ( GetAbsOrigin() + vStep, GetAbsOrigin(), pNPC->WorldAlignMins(), pNPC->WorldAlignMaxs() - vStep,
MASK_SOLID, pNPC, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction < 0.95 )
#else
AI_TraceHull ( GetAbsOrigin(), GetAbsOrigin(), pNPC->WorldAlignMins(), pNPC->WorldAlignMaxs(),
MASK_SOLID, pNPC, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction != 1.0 )
#endif
{
REPORTFAILURE( "Node isn't clear." );
return false;
@ -1396,6 +1477,15 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint
// Calculate our distance
float distance = (GetAbsOrigin() - position).Length();
#ifdef MAPBASE
// Divide by hint weight
float flWeight = GetHintWeight();
if ( flWeight != 1.0f )
{
distance *= GetHintWeightInverse();
}
#endif
// Must be closer than the current best
if ( distance > *flNearestDistance )
{
@ -1576,6 +1666,14 @@ void CAI_Hint::OnRestore()
m_NodeData.nNodeID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( m_NodeData.nWCNodeID );
FixupTargetNode();
#ifdef MAPBASE
if (m_NodeData.flWeight != 0.0f && m_NodeData.flWeight != 1.0f)
{
// Re-invert the weight
m_NodeData.flWeightInverse = 1.0f / m_NodeData.flWeight;
}
#endif
CAI_Node *pNode = GetNode();
if ( !pNode )
@ -1751,6 +1849,11 @@ void CC_ai_drop_hint( const CCommand &args )
nodeData.fIgnoreFacing = HIF_DEFAULT;
nodeData.minState = NPC_STATE_IDLE;
nodeData.maxState = NPC_STATE_COMBAT;
#ifdef MAPBASE
nodeData.nRadius = 0; // From Alien Swarm SDK
nodeData.flWeight = 1.0f;
nodeData.flWeightInverse = 1.0f;
#endif
CAI_Hint *pHint = CAI_HintManager::CreateHint( &nodeData, NULL );
if ( pHint )
{

View File

@ -112,6 +112,13 @@ enum Hint_e
// CS port hints
HINT_CSTRIKE_HOSTAGE_ESCAPE = 1100,
#ifdef MAPBASE
// Mapbase hints
// (these start at a high number to avoid potential conflicts with mod hints)
HINT_TACTICAL_COVER_CUSTOM = 10000, // Cover node with a custom hint activity (NPCs can take cover and reload here while playing said activity)
#endif
};
const char *GetHintTypeDescription( Hint_e iHintType );
const char *GetHintTypeDescription( CAI_Hint *pHint );
@ -120,6 +127,10 @@ const char *GetHintTypeDescription( CAI_Hint *pHint );
// CHintCriteria
//-----------------------------------------------------------------------------
#ifdef MAPBASE // From Alien Swarm SDK
typedef bool (*HintSearchFilterFunc_t)( void *pContext, CAI_Hint *pCandidate );
#endif
class CHintCriteria
{
public:
@ -134,6 +145,11 @@ public:
void SetGroup( string_t group );
string_t GetGroup( void ) const { return m_strGroup; }
#ifdef MAPBASE // From Alien Swarm SDK
void SetFilterFunc( HintSearchFilterFunc_t pfnFilter, void *pContext = NULL ) { m_pfnFilter = pfnFilter; m_pFilterContext = pContext; }
bool PassesFilter( CAI_Hint *pCandidate ) const { return (m_pfnFilter) ? (*m_pfnFilter)(m_pFilterContext, pCandidate) : true; }
#endif
int GetFirstHintType( void ) const { return m_iFirstHintType; }
int GetLastHintType( void ) const { return m_iLastHintType; }
bool MatchesHintType( int hintType ) const;
@ -176,6 +192,11 @@ private:
zoneList_t m_zoneInclude;
zoneList_t m_zoneExclude;
#ifdef MAPBASE
HintSearchFilterFunc_t m_pfnFilter;
void * m_pFilterContext;
#endif
};
class CAI_Node;
@ -310,11 +331,21 @@ public:
int GetNodeId() { return m_NodeData.nNodeID; }
int GetWCId() { return m_NodeData.nWCNodeID; }
#ifdef MAPBASE
int GetRadius() const { return m_NodeData.nRadius; } // From Alien Swarm SDK
float GetHintWeight() const { return m_NodeData.flWeight; }
float GetHintWeightInverse() const { return m_NodeData.flWeightInverse; } // Used to multiply distances
#endif
bool HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hintCriteria, const Vector &position, float *flNearestDistance, bool bIgnoreLock = false, bool bIgnoreHintType = false );
bool IsInNodeFOV( CBaseEntity *pOther );
#ifdef MAPBASE
void NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing );
// Returns true if this hint should override a NPC's yaw even during regular AI.
bool OverridesNPCYaw( CAI_BaseNPC *pNPC );
#endif
#ifdef MAPBASE_VSCRIPT

View File

@ -173,6 +173,10 @@ BEGIN_SIMPLE_DATADESC( HintNodeData )
DEFINE_KEYFIELD( fIgnoreFacing, FIELD_INTEGER, "IgnoreFacing" ),
DEFINE_KEYFIELD( minState, FIELD_INTEGER, "MinimumState" ),
DEFINE_KEYFIELD( maxState, FIELD_INTEGER, "MaximumState" ),
#ifdef MAPBASE
DEFINE_KEYFIELD( nRadius, FIELD_INTEGER, "radius" ), // From Alien Swarm SDK
DEFINE_KEYFIELD( flWeight, FIELD_FLOAT, "hintweight" ),
#endif
END_DATADESC()
@ -205,6 +209,17 @@ int CNodeEnt::Spawn( const char *pMapData )
m_NodeData.minState = NPC_STATE_IDLE;
if ( m_NodeData.maxState == NPC_STATE_NONE )
m_NodeData.maxState = NPC_STATE_COMBAT;
#ifdef MAPBASE
if (m_NodeData.flWeight == 0.0f)
{
m_NodeData.flWeight = 1.0f;
}
else if (m_NodeData.flWeight != 1.0f)
{
// Invert the weight so that it could be used as a direct multiplier for distances, etc.
m_NodeData.flWeightInverse = 1.0f / m_NodeData.flWeight;
}
#endif
// ---------------------------------------------------------------------------------
// If just a hint node (not used for navigation) just create a hint and bail
// ---------------------------------------------------------------------------------

View File

@ -42,6 +42,11 @@ struct HintNodeData
HintIgnoreFacing_t fIgnoreFacing;
NPC_STATE minState;
NPC_STATE maxState;
#ifdef MAPBASE
int nRadius; // From Alien Swarm SDK
float flWeight;
float flWeightInverse; // Not saved
#endif
int nWCNodeID; // Node ID assigned by worldcraft (not same as engine!)

View File

@ -14,6 +14,9 @@
#include "ai_basenpc.h"
#include "ai_localnavigator.h"
#include "ai_moveprobe.h"
#ifdef MAPBASE
#include "ai_hint.h"
#endif
#include "saverestore_utlvector.h"
// memdbgon must be the last include file in a .cpp file!!!
@ -235,18 +238,47 @@ void CAI_Motor::MoveClimbStart( const Vector &climbDest, const Vector &climbDir
// > code are not reciprocal for all state, and furthermore, stomp
// > other state?
bool bGoingUp = (climbDir.z > 0.01);
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
if ( bGoingUp && GetOuter()->HaveSequenceForActivity( ACT_CLIMB_MOUNT_BOTTOM ) )
{
SetActivity( ACT_CLIMB_MOUNT_BOTTOM );
// Steal m_vecDismount for this
GetOuter()->GetSequenceLinearMotion( GetSequence(), &m_vecDismount );
GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) );
}
else if ( !bGoingUp && GetOuter()->HaveSequenceForActivity( ACT_CLIMB_MOUNT_TOP ) )
{
SetActivity( ACT_CLIMB_MOUNT_TOP );
// Steal m_vecDismount for this
GetOuter()->GetSequenceLinearMotion( GetSequence(), &m_vecDismount );
GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) );
}
else
#endif
if ( fabsf( climbDir.z ) < .1 )
{
SetActivity( GetNavigator()->GetMovementActivity() );
}
else
{
SetActivity( (climbDir.z > -0.01 ) ? ACT_CLIMB_UP : ACT_CLIMB_DOWN );
SetActivity( bGoingUp ? ACT_CLIMB_UP : ACT_CLIMB_DOWN );
}
m_nDismountSequence = SelectWeightedSequence( ACT_CLIMB_DISMOUNT );
if (m_nDismountSequence != ACT_INVALID)
{
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
if ( !bGoingUp )
{
int nBottomDismount = SelectWeightedSequence( ACT_CLIMB_DISMOUNT_BOTTOM );
if (nBottomDismount != ACTIVITY_NOT_AVAILABLE)
m_nDismountSequence = nBottomDismount;
}
#endif
GetOuter()->GetSequenceLinearMotion( m_nDismountSequence, &m_vecDismount );
}
else
@ -262,6 +294,76 @@ void CAI_Motor::MoveClimbStart( const Vector &climbDest, const Vector &climbDir
AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw, int climbNodesLeft )
{
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
if ( (GetActivity() == ACT_CLIMB_MOUNT_TOP || GetActivity() == ACT_CLIMB_MOUNT_BOTTOM) )
{
if (!GetOuter()->IsActivityFinished())
{
// Wait for the mounting to finish
SetGroundEntity( NULL );
}
else
{
// Fix up our position if we have to
Vector vecTeleportOrigin;
if (MoveClimbShouldTeleportToSequenceEnd( vecTeleportOrigin ))
{
SetLocalOrigin( vecTeleportOrigin );
}
// Reset activity and start from the beginning
GetOuter()->ResetActivity();
return MoveClimbExecute( climbDest, climbDir, climbDist, yaw, climbNodesLeft );
}
}
else if ( fabsf( climbDir.z ) > .1 && (GetActivity() != ACT_CLIMB_DISMOUNT && GetActivity() != ACT_CLIMB_DISMOUNT_BOTTOM) )
{
bool bGoingUp = (climbDir.z > -0.01);
if ( GetOuter()->HaveSequenceForActivity( ACT_CLIMB_ALL ) )
{
SetActivity( ACT_CLIMB_ALL );
// TODO: Use UTIL_VecToPitch() instead if move_yaw becomes a true climb yaw and not just an up-down scalar
SetPoseParameter( GetOuter()->LookupPoseMoveYaw(), climbDir.z < 0 ? 180.0 : -180.0 );
}
else
{
Activity desiredActivity = bGoingUp ? ACT_CLIMB_UP : ACT_CLIMB_DOWN;
if ( GetActivity() != desiredActivity )
{
SetActivity( desiredActivity );
}
}
if (m_nDismountSequence != ACT_INVALID)
{
if (climbNodesLeft <= 2 && climbDist < fabs( m_vecDismount.z ))
{
if (bGoingUp)
{
// fixme: No other way to force m_nIdealSequence?
GetOuter()->SetActivity( ACT_CLIMB_DISMOUNT );
GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) );
}
else
{
if (GetSequence() != m_nDismountSequence && GetOuter()->GetSequenceActivity( m_nDismountSequence ) == ACT_CLIMB_DISMOUNT_BOTTOM)
{
SetActivity( ACT_CLIMB_DISMOUNT_BOTTOM );
}
}
}
}
}
else if ( climbDir.Length() == 0 && GetOuter()->GetInstantaneousVelocity() <= 0.01 )
{
// The NPC is somehow stuck climbing with no direction or movement.
// This can be caused by NPCs getting stuck in each other and/or being moved away from the ladder.
// In these cases, the NPC has to be made unstuck, or else they may remain in an immobile climbing state forever.
Warning( "%s had to abort climbing due to no direction or movement\n", GetOuter()->GetDebugName() );
return AIMR_ILLEGAL;
}
#else
if ( fabsf( climbDir.z ) > .1 )
{
if ( GetActivity() != ACT_CLIMB_DISMOUNT )
@ -292,13 +394,34 @@ AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vecto
}
}
}
#endif
float climbSpeed = GetOuter()->GetInstantaneousVelocity();
if (m_nDismountSequence != ACT_INVALID)
{
// catch situations where the climb mount/dismount finished before reaching goal
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
if ((GetActivity() == ACT_CLIMB_DISMOUNT || GetActivity() == ACT_CLIMB_DISMOUNT_BOTTOM))
{
SetGroundEntity( NULL );
if (GetOuter()->IsActivityFinished())
{
// Fix up our position if we have to
Vector vecTeleportOrigin;
if (MoveClimbShouldTeleportToSequenceEnd( vecTeleportOrigin ))
{
// Just force it to complete
climbDist = 0.0f;
}
climbSpeed = 200.0f;
}
}
#else
climbSpeed = MAX( climbSpeed, 30.0 );
#endif
}
else
{
@ -314,7 +437,7 @@ AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vecto
climbDist = 0;
const float climbTime = climbDist / climbSpeed;
SetMoveInterval( GetMoveInterval() - climbTime );
SetLocalOrigin( climbDest );
@ -330,6 +453,20 @@ AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vecto
// --------------------------------------------
SetIdealYawAndUpdate( yaw );
#ifdef MAPBASE
// Lock the yaw if we're in position
if ( UTIL_AngleMod( yaw ) == UTIL_AngleMod( GetLocalAngles().y ) )
{
SetYawLocked( true );
}
else if ( IsYawLocked() )
{
// We're in a different position now. Unlock the yaw and update it
SetYawLocked( false );
UpdateYaw( -1 );
}
#endif
return AIMR_OK;
}
@ -340,11 +477,62 @@ void CAI_Motor::MoveClimbStop()
else
SetActivity( ACT_IDLE );
#ifdef MAPBASE
// Unlock desired weapon state so NPCs can unholster their weapons again.
GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_IGNORE );
// Unlock yaw
SetYawLocked( false );
#endif
GetOuter()->RemoveFlag( FL_FLY );
SetSmoothedVelocity( vec3_origin );
SetGravity( 1.0 );
}
#ifdef MAPBASE
void CAI_Motor::MoveClimbPause()
{
if (GetActivity() != ACT_CLIMB_DISMOUNT
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
&& GetActivity() != ACT_CLIMB_MOUNT_TOP && GetActivity() != ACT_CLIMB_MOUNT_BOTTOM
#endif
)
{
if ( GetActivity() == ACT_CLIMB_ALL )
{
SetPoseParameter( GetOuter()->LookupPoseMoveYaw(), 0.0f );
}
SetSmoothedVelocity( vec3_origin );
}
else
{
// If already dismounting, do nothing
}
}
//-----------------------------------------------------------------------------
// Purpose: This is part of a hack needed in cases where ladder mount/dismount animations collide with the world and don't move properly.
// It's based off of the same code as scripted_sequence's teleportation fixup, although this function only resolves the bone origin and
// returns whether or not teleportation is necessary, as the teleportation is achieved in different ways for different uses of this code.
//-----------------------------------------------------------------------------
bool CAI_Motor::MoveClimbShouldTeleportToSequenceEnd( Vector &teleportOrigin )
{
QAngle new_angle;
GetOuter()->GetBonePosition( 0, teleportOrigin, new_angle );
// Ensure that there is actually a distance needed to teleport there
if ((GetLocalOrigin() - teleportOrigin).Length2DSqr() > Square( 8.0 ))
{
teleportOrigin.z = GetLocalOrigin().z;
return true;
}
return false;
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Motion for jumping
// Input :
@ -651,9 +839,20 @@ void CAI_Motor::MoveFacing( const AILocalMoveGoal_t &move )
{
// FIXME: move this up to navigator so that path goals can ignore these overrides.
Vector dir;
float flInfluence = GetFacingDirection( dir );
dir = move.facing * (1 - flInfluence) + dir * flInfluence;
VectorNormalize( dir );
#ifdef MAPBASE
if (IsDeceleratingToGoal() && (GetOuter()->GetHintNode() /*|| GetOuter()->m_hOpeningDoor*/))
{
// Don't let the facing queue interfere with arrival direction in important cases
dir = move.facing;
}
else
#endif
{
float flInfluence = GetFacingDirection( dir );
dir = move.facing * (1 - flInfluence) + dir * flInfluence;
VectorNormalize( dir );
}
// ideal facing direction
float idealYaw = UTIL_AngleMod( UTIL_VecToYaw( dir ) );

View File

@ -62,6 +62,10 @@ public:
virtual void MoveClimbStart( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw );
virtual AIMoveResult_t MoveClimbExecute( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw, int climbNodesLeft );
virtual void MoveClimbStop();
#ifdef MAPBASE
virtual void MoveClimbPause();
virtual bool MoveClimbShouldTeleportToSequenceEnd( Vector &teleportOrigin );
#endif
//---------------------------------
@ -83,6 +87,9 @@ public:
const Vector & GetCurVel() const { return m_vecVelocity; }
virtual float OverrideMaxYawSpeed( Activity activity ) { return -1; }
#ifdef MAPBASE
virtual
#endif
bool IsDeceleratingToGoal() const { return false; }
//---------------------------------

View File

@ -999,12 +999,23 @@ int CAI_Navigator::GetArrivalSequence( int curSequence )
activity = ACT_IDLE;
}
sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( activity ), curSequence );
Activity translatedActivity = GetOuter()->TranslateActivity( activity );
sequence = GetOuter()->SelectWeightedSequence( translatedActivity, curSequence );
if ( sequence == ACT_INVALID )
{
DevMsg( GetOuter(), "No appropriate sequence for arrival activity %s (%d)\n", GetOuter()->GetActivityName( GetPath()->GetArrivalActivity() ), GetPath()->GetArrivalActivity() );
sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( ACT_IDLE ), curSequence );
#ifdef MAPBASE
if ( translatedActivity == ACT_SCRIPT_CUSTOM_MOVE )
{
// ACT_SCRIPT_CUSTOM_MOVE allows activity translation to resolve into specific sequences
sequence = GetOuter()->GetScriptCustomMoveSequence();
}
else
#endif
{
DevMsg( GetOuter(), "No appropriate sequence for arrival activity %s (%d)\n", GetOuter()->GetActivityName( GetPath()->GetArrivalActivity() ), GetPath()->GetArrivalActivity() );
sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( ACT_IDLE ), curSequence );
}
}
Assert( sequence != ACT_INVALID );
GetPath()->SetArrivalSequence( sequence );
@ -1642,6 +1653,15 @@ void CAI_Navigator::MoveCalcBaseGoal( AILocalMoveGoal_t *pMoveGoal )
AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
if ( pCurWaypoint->GetNext() && pCurWaypoint->GetNext()->NavType() != pCurWaypoint->NavType() )
pMoveGoal->flags |= AILMG_TARGET_IS_TRANSITION;
#ifdef MAPBASE
// TODO: Better place for this code?
if (pMoveGoal->flags & AILMG_TARGET_IS_TRANSITION && pCurWaypoint->GetNext()->NavType() == NAV_CLIMB)
{
// NPCs need to holster their weapons before climbing.
GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_HOLSTERED );
}
#endif
}
const Task_t *pCurTask = GetOuter()->GetTask();
@ -2164,11 +2184,26 @@ bool CAI_Navigator::OnMoveBlocked( AIMoveResult_t *pResult )
if (pDoor != NULL)
{
GetOuter()->OpenPropDoorBegin( pDoor );
#ifdef MAPBASE
// Tell the navigation to stop running until we're done.
OnNewGoal();
#endif
*pResult = AIMR_OK;
return true;
}
}
#ifdef MAPBASE
if ( GetOuter()->m_hOpeningDoor )
{
// In the process of opening a door
// Because navigation is now supposed to terminate when a NPC begins opening a door, this code should not be reached.
DbgNavMsg( GetOuter(), "CAI_Navigator::OnMoveBlocked had to check for m_hOpeningDoor\n" );
*pResult = AIMR_OK;
return true;
}
#endif
// Allow the NPC to override this behavior
if ( GetOuter()->OnMoveBlocked( pResult ))
@ -2591,8 +2626,12 @@ bool CAI_Navigator::Move( float flInterval )
if ( GetNavType() == NAV_CLIMB )
{
#ifdef MAPBASE
GetMotor()->MoveClimbPause();
#else
GetMotor()->MoveClimbStop();
SetNavType( NAV_GROUND );
#endif
}
GetMotor()->MoveStop();
AssertMsg( TaskIsRunning() || TaskIsComplete(), ("Schedule stalled!!\n") );
@ -2695,6 +2734,11 @@ void CAI_Navigator::AdvancePath()
if (pDoor != NULL)
{
GetOuter()->OpenPropDoorBegin(pDoor);
#ifdef MAPBASE
// Tell the navigation to stop running until we're done.
OnNewGoal();
#endif
}
else
{
@ -3880,7 +3924,12 @@ bool CAI_Navigator::GetStoppingPath( CAI_WaypointList * pClippedWaypoints )
AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
if ( pCurWaypoint )
{
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
// Since regular climb nav can interrupt itself now, only do this when dismounting
bool bMustCompleteCurrent = ( (pCurWaypoint->NavType() == NAV_CLIMB && (GetActivity() == ACT_CLIMB_DISMOUNT || GetActivity() == ACT_CLIMB_MOUNT_TOP)) || pCurWaypoint->NavType() == NAV_JUMP );
#else
bool bMustCompleteCurrent = ( pCurWaypoint->NavType() == NAV_CLIMB || pCurWaypoint->NavType() == NAV_JUMP );
#endif
float distRemaining = GetMotor()->MinStoppingDist( 0 );
if ( bMustCompleteCurrent )

View File

@ -144,6 +144,31 @@ public:
int m_capabilities; // cache this
};
#ifdef MAPBASE
//-------------------------------------
// Purpose: A version of CNodeFilter which allows hints to influence the result.
//-------------------------------------
class CNodeHintFilter : public CNodeFilter
{
public:
CNodeHintFilter( CAI_BaseNPC *pNPC, const Vector &pos ) : CNodeFilter( pNPC, pos ) {}
CNodeHintFilter( const Vector &pos ) : CNodeFilter( pos ) {}
virtual float NodeDistanceSqr( CAI_Node &node )
{
// Heavier hints are considered closer
if ( node.GetHint() && node.GetHint()->GetHintWeight() != 1.0f && (node.GetHint()->GetGroup() == NULL_STRING || node.GetHint()->GetGroup() == m_pNPC->GetHintGroup()) )
{
return CNodeFilter::NodeDistanceSqr( node ) * node.GetHint()->GetHintWeightInverse();
}
else
{
return CNodeFilter::NodeDistanceSqr( node );
}
}
};
#endif
//-----------------------------------------------------------------------------
// CAI_Network
//-----------------------------------------------------------------------------
@ -351,7 +376,12 @@ int CAI_Network::NearestNodeToPoint( CAI_BaseNPC *pNPC, const Vector &vecOrigin,
// First get nodes distances and eliminate those that are beyond
// the maximum allowed distance for local movements
// ---------------------------------------------------------------
#ifdef MAPBASE
// Allow hint weight to influence supposed distance
CNodeHintFilter filter( pNPC, vecOrigin );
#else
CNodeFilter filter( pNPC, vecOrigin );
#endif
#ifdef AI_PERF_MON
m_nPerfStatNN++;
@ -605,8 +635,13 @@ CAI_Node *CAI_Network::AddNode( const Vector &origin, float yaw )
CAI_Link *CAI_Network::CreateLink( int srcID, int destID, CAI_DynamicLink *pDynamicLink )
{
#ifdef MAPBASE // From Alien Swarm SDK
CAI_Node *pSrcNode = GetNode( srcID );
CAI_Node *pDestNode = GetNode( destID );
#else
CAI_Node *pSrcNode = g_pBigAINet->GetNode( srcID );
CAI_Node *pDestNode = g_pBigAINet->GetNode( destID );
#endif
Assert( pSrcNode && pDestNode && pSrcNode != pDestNode );

View File

@ -411,7 +411,12 @@ int CAI_TacticalServices::FindCoverNode(const Vector &vNearPos, const Vector &vT
// --------------------------------------------------------
pNode->Lock( 1.0 );
#ifdef MAPBASE
if ( pNode->GetHint() && ( pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_MED || pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_LOW
|| pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_CUSTOM ) )
#else
if ( pNode->GetHint() && ( pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_MED || pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_LOW ) )
#endif
{
if ( GetOuter()->GetHintNode() )
{

View File

@ -61,11 +61,6 @@
extern int g_interactionBarnacleVictimReleased;
#endif //HL2_DLL
#ifdef MAPBASE
extern acttable_t *GetSMG1Acttable();
extern int GetSMG1ActtableCount();
#endif
extern ConVar weapon_showproficiency;
ConVar ai_show_hull_attacks( "ai_show_hull_attacks", "0" );
@ -2751,19 +2746,22 @@ Activity CBaseCombatCharacter::Weapon_BackupActivity( Activity activity, bool we
return activity;
}
acttable_t *pTable = GetSMG1Acttable();
int actCount = GetSMG1ActtableCount();
for ( int i = 0; i < actCount; i++, pTable++ )
acttable_t *pTable = pWeapon->GetBackupActivityList();
if (pTable)
{
if ( activity == pTable->baseAct )
int actCount = pWeapon->GetBackupActivityListCount();
for ( int i = 0; i < actCount; i++, pTable++ )
{
// Don't pick SMG animations we don't actually have an animation for.
if (GetModelPtr() ? !GetModelPtr()->HaveSequenceForActivity(pTable->weaponAct) : false)
if ( activity == pTable->baseAct )
{
return activity;
}
// Don't pick SMG animations we don't actually have an animation for.
if (GetModelPtr() ? !GetModelPtr()->HaveSequenceForActivity(pTable->weaponAct) : false)
{
return activity;
}
return (Activity)pTable->weaponAct;
return (Activity)pTable->weaponAct;
}
}
}

View File

@ -110,6 +110,28 @@ bool CAI_FuncTankBehavior::IsInterruptable( void )
return BaseClass::IsInterruptable();
}
ConVar ai_tank_allow_expanded_npcs( "ai_tank_allow_expanded_npcs", "1", FCVAR_NONE, "Allows Father Grigori, Barney, and vortigaunts to automatically man func_tanks." );
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAI_FuncTankBehavior::CanManTank( CFuncTank *pTank, bool bForced )
{
if (!bForced)
{
// In order to prevent potential problems in existing maps, Father Grigori, Barney, and vortigaunts can be set to not automatically man func_tanks by default.
if (ai_tank_allow_expanded_npcs.GetBool() == false)
{
const char *pszClass = GetOuter()->GetClassname();
if ( FStrEq( pszClass, "npc_monk" ) || FStrEq( pszClass, "npc_barney" ) || FStrEq( pszClass, "npc_vortigaunt" ) )
return false;
}
}
return true;
}
#endif
//-----------------------------------------------------------------------------

View File

@ -51,6 +51,8 @@ public:
void PrescheduleThink();
#ifdef MAPBASE
bool IsInterruptable( void );
bool CanManTank( CFuncTank *pTank, bool bForced );
#endif
Activity NPC_TranslateActivity( Activity activity );

View File

@ -402,7 +402,11 @@ void CFuncTank::InputFindNPCToManTank( inputdata_t &inputdata )
{
// Verify the npc has the func_tank controller behavior.
CAI_FuncTankBehavior *pBehavior;
#ifdef MAPBASE
if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, true ) )
#else
if ( pNPC->GetBehavior( &pBehavior ) )
#endif
{
m_hController = pNPC;
pBehavior->SetFuncTank( this );
@ -439,7 +443,7 @@ void CFuncTank::InputTeleportNPCToManTank( inputdata_t &inputdata )
{
// Verify the npc has the func_tank controller behavior.
CAI_FuncTankBehavior *pBehavior;
if ( pNPC->GetBehavior( &pBehavior ) )
if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, true ) )
{
Vector vecVec;
QAngle angAng;
@ -512,7 +516,7 @@ void CFuncTank::InputForceNPCToManTank( inputdata_t &inputdata )
{
// Verify the npc has the func_tank controller behavior.
CAI_FuncTankBehavior *pBehavior;
if ( pNPC->GetBehavior( &pBehavior ) )
if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, true ) )
{
// Set the forced condition
pBehavior->SetCondition( CAI_FuncTankBehavior::COND_FUNCTANK_FORCED );
@ -627,7 +631,11 @@ void CFuncTank::NPC_FindController( void )
continue;
CAI_FuncTankBehavior *pBehavior;
#ifdef MAPBASE
if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, false ) )
#else
if ( pNPC->GetBehavior( &pBehavior ) )
#endif
{
// Don't mount the func_tank if your "enemy" is within X feet or it or the npc.
CBaseEntity *pEnemy = pNPC->GetEnemy();

View File

@ -156,6 +156,8 @@ ConVar npc_alyx_crouch( "npc_alyx_crouch", "1" );
#ifdef MAPBASE
ConVar npc_alyx_interact_manhacks( "npc_alyx_interact_manhacks", "1" );
ConVar npc_alyx_interact_turrets( "npc_alyx_interact_turrets", "0" );
ConVar npc_alyx_allow_fly( "npc_alyx_allow_fly", "0", FCVAR_NONE, "Allows Alyx to use FL_FLY outside of scripted sequences, actbusy, or navigation." );
#endif
// global pointer to Alyx for fast lookups
@ -319,7 +321,9 @@ CNPC_Alyx *CNPC_Alyx::GetAlyx( void )
//=========================================================
bool CNPC_Alyx::CreateBehaviors()
{
#ifndef MAPBASE // Moved to CNPC_PlayerCompanion
AddBehavior( &m_FuncTankBehavior );
#endif
bool result = BaseClass::CreateBehaviors();
return result;
@ -885,7 +889,11 @@ void CNPC_Alyx::GatherConditions()
// ROBIN: This was here to solve a problem in a playtest. We've since found what we think was the cause.
// It's a useful piece of debug to have lying there, so I've left it in.
if ( (GetFlags() & FL_FLY) && m_NPCState != NPC_STATE_SCRIPT && !m_ActBusyBehavior.IsActive() && !m_PassengerBehavior.IsEnabled() )
if ( (GetFlags() & FL_FLY) && m_NPCState != NPC_STATE_SCRIPT && !m_ActBusyBehavior.IsActive() && !m_PassengerBehavior.IsEnabled()
#ifdef MAPBASE
&& GetNavType() != NAV_CLIMB && !npc_alyx_allow_fly.GetBool()
#endif
)
{
Warning( "Removed FL_FLY from Alyx, who wasn't running a script or actbusy. Time %.2f, map %s.\n", gpGlobals->curtime, STRING(gpGlobals->mapname) );
RemoveFlag( FL_FLY );
@ -1687,9 +1695,88 @@ Activity CNPC_Alyx::NPC_TranslateActivity( Activity activity )
case ACT_DROP_WEAPON: if ( HasShotgun() ) return (Activity)ACT_DROP_WEAPON_SHOTGUN;
}
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
// Alyx has her own pistol readiness animations which use the default activities
switch (activity)
{
case ACT_IDLE_PISTOL_RELAXED:
return ACT_IDLE_RELAXED;
case ACT_IDLE_PISTOL_STIMULATED:
return ACT_IDLE_STIMULATED;
case ACT_WALK_PISTOL_RELAXED:
return ACT_WALK;
case ACT_WALK_PISTOL_STIMULATED:
return ACT_WALK_PISTOL;
case ACT_RUN_PISTOL_RELAXED:
return ACT_RUN;
case ACT_RUN_PISTOL_STIMULATED:
return ACT_RUN_PISTOL;
}
#endif
return activity;
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Activity CNPC_Alyx::Weapon_TranslateActivity( Activity activity, bool *pRequired )
{
activity = BaseClass::Weapon_TranslateActivity( activity, pRequired );
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
// Alyx has her own pistol readiness animations which use the default activities
switch (activity)
{
case ACT_IDLE_PISTOL_RELAXED:
return ACT_IDLE_RELAXED;
case ACT_IDLE_PISTOL_STIMULATED:
return ACT_IDLE_STIMULATED;
case ACT_WALK_PISTOL_RELAXED:
return ACT_WALK;
case ACT_WALK_PISTOL_STIMULATED:
return ACT_WALK_PISTOL;
case ACT_RUN_PISTOL_RELAXED:
return ACT_RUN;
case ACT_RUN_PISTOL_STIMULATED:
return ACT_RUN_PISTOL;
}
#endif
return activity;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Activity CNPC_Alyx::Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired, CBaseCombatWeapon *pSpecificWeapon )
{
activity = BaseClass::Weapon_BackupActivity( activity, weaponTranslationWasRequired, pSpecificWeapon );
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
// Alyx has her own pistol readiness animations which use the default activities
switch (activity)
{
case ACT_IDLE_PISTOL_RELAXED:
return ACT_IDLE_RELAXED;
case ACT_IDLE_PISTOL_STIMULATED:
return ACT_IDLE_STIMULATED;
case ACT_WALK_PISTOL_RELAXED:
return ACT_WALK;
case ACT_WALK_PISTOL_STIMULATED:
return ACT_WALK_PISTOL;
case ACT_RUN_PISTOL_RELAXED:
return ACT_RUN;
case ACT_RUN_PISTOL_STIMULATED:
return ACT_RUN_PISTOL;
}
#endif
return activity;
}
#endif
bool CNPC_Alyx::ShouldDeferToFollowBehavior()
{
return BaseClass::ShouldDeferToFollowBehavior();

View File

@ -91,6 +91,10 @@ public:
bool CanSeeEntityInDarkness( CBaseEntity *pEntity );
bool IsCoverPosition( const Vector &vecThreat, const Vector &vecPosition );
Activity NPC_TranslateActivity ( Activity activity );
#ifdef MAPBASE
Activity Weapon_TranslateActivity( Activity baseAct, bool *pRequired = NULL );
Activity Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired = false, CBaseCombatWeapon *pSpecificWeapon = NULL );
#endif
bool ShouldDeferToFollowBehavior();
void BuildScheduleTestBits();
bool ShouldBehaviorSelectSchedule( CAI_BehaviorBase *pBehavior );
@ -231,7 +235,9 @@ private:
bool m_bShouldHaveEMP;
#ifndef MAPBASE // Moved to CNPC_PlayerCompanion
CAI_FuncTankBehavior m_FuncTankBehavior;
#endif
COutputEvent m_OnFinishInteractWithObject;
COutputEvent m_OnPlayerUse;

View File

@ -439,10 +439,11 @@ CSimpleSimTimer CNPC_Citizen::gm_PlayerSquadEvaluateTimer;
bool CNPC_Citizen::CreateBehaviors()
{
BaseClass::CreateBehaviors();
AddBehavior( &m_FuncTankBehavior );
#ifdef MAPBASE
AddBehavior( &m_RappelBehavior );
AddBehavior( &m_PolicingBehavior );
#else // Moved to CNPC_PlayerCompanion
AddBehavior( &m_FuncTankBehavior );
#endif
return true;
@ -2085,6 +2086,13 @@ Activity CNPC_Citizen::NPC_TranslateActivity( Activity activity )
return ACT_RUN_AIM_AR2_STIMULATED;
if (activity == ACT_WALK_AIM_AR2)
return ACT_WALK_AIM_AR2_STIMULATED;
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
if (activity == ACT_RUN_AIM_PISTOL)
return ACT_RUN_AIM_PISTOL_STIMULATED;
if (activity == ACT_WALK_AIM_PISTOL)
return ACT_WALK_AIM_PISTOL_STIMULATED;
#endif
}
#endif

View File

@ -370,7 +370,6 @@ private:
#endif
//-----------------------------------------------------
CAI_FuncTankBehavior m_FuncTankBehavior;
#ifdef MAPBASE
CAI_RappelBehavior m_RappelBehavior;
CAI_PolicingBehavior m_PolicingBehavior;
@ -378,6 +377,8 @@ private:
// Rappel
virtual bool IsWaitingToRappel( void ) { return m_RappelBehavior.IsWaitingToRappel(); }
void BeginRappel() { m_RappelBehavior.BeginRappel(); }
#else // Moved to CNPC_PlayerCompanion
CAI_FuncTankBehavior m_FuncTankBehavior;
#endif
CHandle<CAI_FollowGoal> m_hSavedFollowGoalEnt;

View File

@ -43,6 +43,7 @@ int g_fCombineQuestion; // true if an idle grunt asked a question. Cleared wh
#ifdef MAPBASE
ConVar npc_combine_idle_walk_easy( "npc_combine_idle_walk_easy", "1", FCVAR_NONE, "Mapbase: Allows Combine soldiers to use ACT_WALK_EASY as a walking animation when idle." );
ConVar npc_combine_unarmed_anims( "npc_combine_unarmed_anims", "1", FCVAR_NONE, "Mapbase: Allows Combine soldiers to use unarmed idle/walk animations when they have no weapon." );
ConVar npc_combine_protected_run( "npc_combine_protected_run", "0", FCVAR_NONE, "Mapbase: Allows Combine soldiers to use \"protected run\" animations." );
ConVar npc_combine_altfire_not_allies_only( "npc_combine_altfire_not_allies_only", "1", FCVAR_NONE, "Mapbase: Elites are normally only allowed to fire their alt-fire attack at the player and the player's allies; This allows elites to alt-fire at other enemies too." );
ConVar npc_combine_new_cover_behavior( "npc_combine_new_cover_behavior", "1", FCVAR_NONE, "Mapbase: Toggles small patches for parts of npc_combine AI related to soldiers failing to take cover. These patches are minimal and only change cases where npc_combine would otherwise look at an enemy without shooting or run up to the player to melee attack when they don't have to. Consult the Mapbase wiki for more information." );
@ -127,8 +128,9 @@ Activity ACT_COMBINE_AR2_ALTFIRE;
Activity ACT_WALK_EASY;
Activity ACT_WALK_MARCH;
#ifdef MAPBASE
Activity ACT_IDLE_UNARMED;
Activity ACT_WALK_UNARMED;
Activity ACT_TURRET_CARRY_IDLE;
Activity ACT_TURRET_CARRY_WALK;
Activity ACT_TURRET_CARRY_RUN;
#endif
// -----------------------------------------------
@ -544,7 +546,12 @@ void CNPC_Combine::GatherConditions()
if( GetState() == NPC_STATE_COMBAT )
{
#ifdef MAPBASE
// Don't override the standoff
if( IsCurSchedule( SCHED_COMBINE_WAIT_IN_COVER, false ) && !m_StandoffBehavior.IsActive() )
#else
if( IsCurSchedule( SCHED_COMBINE_WAIT_IN_COVER, false ) )
#endif
{
// Soldiers that are standing around doing nothing poll for attack slots so
// that they can respond quickly when one comes available. If they can
@ -1540,11 +1547,6 @@ void CNPC_Combine::BuildScheduleTestBits( void )
//-----------------------------------------------------------------------------
Activity CNPC_Combine::Weapon_TranslateActivity( Activity eNewActivity, bool *pRequired )
{
// We have differing low animations and ACT_CROUCHIDLE is not friendly to weapon translation.
// ACT_CROUCHIDLE is pretty much deprecated at this point anyway.
if (eNewActivity == ACT_CROUCHIDLE)
eNewActivity = ACT_RANGE_AIM_LOW;
return BaseClass::Weapon_TranslateActivity(eNewActivity, pRequired);
}
@ -1553,12 +1555,6 @@ Activity CNPC_Combine::Weapon_TranslateActivity( Activity eNewActivity, bool *pR
//-----------------------------------------------------------------------------
Activity CNPC_Combine::NPC_BackupActivity( Activity eNewActivity )
{
// Otherwise we move around, T-posing.
if (eNewActivity == ACT_WALK)
return ACT_WALK_UNARMED;
else if (eNewActivity == ACT_RUN)
return ACT_RUN_RIFLE;
// Some models might not contain ACT_COMBINE_BUGBAIT, which the soldier model uses instead of ACT_IDLE_ON_FIRE.
// Contrariwise, soldiers may be called to use ACT_IDLE_ON_FIRE in other parts of the AI and need to translate to ACT_COMBINE_BUGBAIT.
if (eNewActivity == ACT_COMBINE_BUGBAIT)
@ -1630,16 +1626,32 @@ Activity CNPC_Combine::NPC_TranslateActivity( Activity eNewActivity )
}
}
#ifdef MAPBASE
else if (!GetActiveWeapon() && npc_combine_unarmed_anims.GetBool() && HaveSequenceForActivity(ACT_IDLE_UNARMED))
else if (!GetActiveWeapon() && !npc_combine_unarmed_anims.GetBool())
{
if (eNewActivity == ACT_IDLE || eNewActivity == ACT_IDLE_ANGRY)
eNewActivity = ACT_IDLE_UNARMED;
eNewActivity = ACT_IDLE_SMG1;
else if (eNewActivity == ACT_WALK)
eNewActivity = ACT_WALK_UNARMED;
eNewActivity = ACT_WALK_RIFLE;
else if (eNewActivity == ACT_RUN)
eNewActivity = ACT_RUN_RIFLE;
}
else if (eNewActivity == ACT_WALK && m_NPCState == NPC_STATE_IDLE && npc_combine_idle_walk_easy.GetBool() && HaveSequenceForActivity(ACT_WALK_EASY))
else if (m_NPCState == NPC_STATE_IDLE && eNewActivity == ACT_WALK)
{
eNewActivity = ACT_WALK_EASY;
if (npc_combine_idle_walk_easy.GetBool())
{
// ACT_WALK_EASY has been replaced with ACT_WALK_RELAXED for weapon translation purposes
eNewActivity = ACT_WALK_RELAXED;
}
else if (GetActiveWeapon())
{
eNewActivity = ACT_WALK_RIFLE;
}
}
if ( eNewActivity == ACT_RUN && ( IsCurSchedule( SCHED_TAKE_COVER_FROM_BEST_SOUND ) || IsCurSchedule( SCHED_FLEE_FROM_BEST_SOUND ) ) )
{
if ( random->RandomInt( 0, 1 ) && npc_combine_protected_run.GetBool() && HaveSequenceForActivity( ACT_RUN_PROTECTED ) )
eNewActivity = ACT_RUN_PROTECTED;
}
#endif
@ -2606,7 +2618,6 @@ int CNPC_Combine::TranslateSchedule( int scheduleType )
#ifdef MAPBASE
// SCHED_COMBINE_WAIT_IN_COVER uses INCOVER, but only gets out of it when the soldier moves.
// That seems to mess up shooting, so this Forget() attempts to fix that.
// I don't know if there's a better workaround.
Forget( bits_MEMORY_INCOVER );
#endif
@ -3791,6 +3802,13 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea
{
return WEAPON_PROFICIENCY_GOOD;
}
#ifdef MAPBASE
else if ( pWeapon->ClassMatches( gm_isz_class_Pistol ) )
{
// Mods which need a lower soldier pistol accuracy can either change this value or use proficiency override in Hammer.
return WEAPON_PROFICIENCY_VERY_GOOD;
}
#endif
return BaseClass::CalcWeaponProficiency( pWeapon );
}
@ -3906,7 +3924,12 @@ bool CNPC_Combine::IsRunningApproachEnemySchedule()
bool CNPC_Combine::ShouldPickADeathPose( void )
{
#ifdef MAPBASE
// Check base class as well
return !IsCrouching() && BaseClass::ShouldPickADeathPose();
#else
return !IsCrouching();
#endif
}
//-----------------------------------------------------------------------------
@ -3940,8 +3963,9 @@ DECLARE_ACTIVITY( ACT_COMBINE_AR2_ALTFIRE )
DECLARE_ACTIVITY( ACT_WALK_EASY )
DECLARE_ACTIVITY( ACT_WALK_MARCH )
#ifdef MAPBASE
DECLARE_ACTIVITY( ACT_IDLE_UNARMED )
DECLARE_ACTIVITY( ACT_WALK_UNARMED )
DECLARE_ACTIVITY( ACT_TURRET_CARRY_IDLE )
DECLARE_ACTIVITY( ACT_TURRET_CARRY_WALK )
DECLARE_ACTIVITY( ACT_TURRET_CARRY_RUN )
#endif
DECLARE_ANIMEVENT( COMBINE_AE_BEGIN_ALTFIRE )

View File

@ -1296,7 +1296,11 @@ void CFastZombie::StartTask( const Task_t *pTask )
CBaseEntity *pEnemy = GetEnemy();
Vector vecJumpDir;
if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN )
if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
|| GetActivity() == ACT_CLIMB_ALL
#endif
)
{
// Jump off the pipe backwards!
Vector forward;
@ -1449,7 +1453,11 @@ int CFastZombie::TranslateSchedule( int scheduleType )
break;
case SCHED_FASTZOMBIE_UNSTICK_JUMP:
if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN || GetActivity() == ACT_CLIMB_DISMOUNT )
if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN || GetActivity() == ACT_CLIMB_DISMOUNT
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
|| (GetActivity() >= ACT_CLIMB_ALL && GetActivity() <= ACT_CLIMB_DISMOUNT_BOTTOM)
#endif
)
{
return SCHED_FASTZOMBIE_CLIMBING_UNSTICK_JUMP;
}
@ -1477,8 +1485,10 @@ int CFastZombie::TranslateSchedule( int scheduleType )
//---------------------------------------------------------
Activity CFastZombie::NPC_TranslateActivity( Activity baseAct )
{
#ifndef MAPBASE // Now covered by CAI_BaseNPC::NPC_BackupActivity
if ( baseAct == ACT_CLIMB_DOWN )
return ACT_CLIMB_UP;
#endif
return BaseClass::NPC_TranslateActivity( baseAct );
}

View File

@ -1537,7 +1537,12 @@ void CNPC_MetroPolice::OnUpdateShotRegulator( )
BaseClass::OnUpdateShotRegulator();
// FIXME: This code (except the burst interval) could be used for all weapon types
#ifdef MAPBASE
// Only if we actually have the pistol out
if ( EntIsClass( GetActiveWeapon(), gm_isz_class_Pistol ) )
#else
if( Weapon_OwnsThisType( "weapon_pistol" ) )
#endif
{
if ( m_nBurstMode == BURST_NOT_ACTIVE )
{
@ -3899,6 +3904,15 @@ int CNPC_MetroPolice::SelectScheduleNoDirectEnemy()
return SCHED_METROPOLICE_SMASH_PROP;
}
#ifdef MAPBASE
// If you see your enemy and you're still arming yourself, wait and don't just charge in
// (if your weapon is holstered, you're probably about to arm yourself)
if ( HasCondition( COND_SEE_ENEMY ) && GetWeapon(0) && (IsWeaponHolstered() || FindGestureLayer( TranslateActivity( ACT_ARM ) ) != -1) )
{
return SCHED_COMBAT_FACE;
}
#endif
return SCHED_METROPOLICE_CHASE_ENEMY;
}
@ -4522,34 +4536,7 @@ int CNPC_MetroPolice::SelectBehaviorOverrideSchedule()
//-----------------------------------------------------------------------------
bool CNPC_MetroPolice::IsCrouchedActivity( Activity activity )
{
Activity realActivity = TranslateActivity(activity);
switch ( realActivity )
{
case ACT_RELOAD_LOW:
case ACT_COVER_LOW:
case ACT_COVER_PISTOL_LOW:
case ACT_COVER_SMG1_LOW:
case ACT_RELOAD_SMG1_LOW:
//case ACT_RELOAD_AR2_LOW:
case ACT_RELOAD_PISTOL_LOW:
case ACT_RELOAD_SHOTGUN_LOW:
// These animations aren't actually "low" on metrocops
//case ACT_RANGE_AIM_LOW:
//case ACT_RANGE_AIM_AR2_LOW:
//case ACT_RANGE_AIM_SMG1_LOW:
//case ACT_RANGE_AIM_PISTOL_LOW:
//case ACT_RANGE_ATTACK1_LOW:
//case ACT_RANGE_ATTACK_AR2_LOW:
//case ACT_RANGE_ATTACK_SMG1_LOW:
//case ACT_RANGE_ATTACK_PISTOL_LOW:
//case ACT_RANGE_ATTACK2_LOW:
return true;
}
return false;
return BaseClass::IsCrouchedActivity( activity );
}
//-----------------------------------------------------------------------------

View File

@ -61,6 +61,10 @@ int AE_COMPANION_RELEASE_FLARE;
#define COMPANION_MELEE_DIST 64.0
#endif
#ifdef MAPBASE
ConVar ai_allow_new_weapons( "ai_allow_new_weapons", "1", FCVAR_NONE, "Allows companion NPCs to automatically pick up and use weapons they were unable pick up before, i.e. 357s or crossbows." );
#endif
#define MAX_TIME_BETWEEN_BARRELS_EXPLODING 5.0f
#define MAX_TIME_BETWEEN_CONSECUTIVE_PLAYER_KILLS 3.0f
@ -211,6 +215,10 @@ bool CNPC_PlayerCompanion::CreateBehaviors()
AddBehavior( &m_FollowBehavior );
AddBehavior( &m_LeadBehavior );
#endif//HL2_EPISODIC
#ifdef MAPBASE
AddBehavior( &m_FuncTankBehavior );
#endif
return BaseClass::CreateBehaviors();
}
@ -1642,6 +1650,19 @@ Activity CNPC_PlayerCompanion::TranslateActivityReadiness( Activity activity )
continue;
}
#ifdef MAPBASE
// If we don't have the readiness activity we selected and there's no backup activity available, break the loop and return the base act.
bool bRequired;
if ( !HaveSequenceForActivity( actremap.mappedActivity ) && !HaveSequenceForActivity( Weapon_TranslateActivity( actremap.mappedActivity, &bRequired ) ) )
{
Activity backupAct = Weapon_BackupActivity( actremap.mappedActivity, bRequired );
if ( backupAct != actremap.mappedActivity )
return backupAct;
else
break;
}
#endif
// We've successfully passed all criteria for remapping this
return actremap.mappedActivity;
}
@ -1665,21 +1686,6 @@ Activity CNPC_PlayerCompanion::NPC_TranslateActivity( Activity activity )
activity = ACT_RUN_PROTECTED;
}
#ifdef COMPANION_HOLSTER_WORKAROUND
if (activity == ACT_DISARM || activity == ACT_ARM)
{
CBaseCombatWeapon *pWeapon = GetActiveWeapon() ? GetActiveWeapon() : m_hWeapons[m_iLastHolsteredWeapon];
if (pWeapon && pWeapon->WeaponClassify() != WEPCLASS_HANDGUN)
{
switch (activity)
{
case ACT_DISARM: return ACT_DISARM_RIFLE;
case ACT_ARM: return ACT_ARM_RIFLE;
}
}
}
#endif
activity = BaseClass::NPC_TranslateActivity( activity );
if ( activity == ACT_IDLE )
@ -2748,6 +2754,13 @@ bool CNPC_PlayerCompanion::Weapon_CanUse( CBaseCombatWeapon *pWeapon )
{
return (NumWeaponsInSquad("weapon_shotgun") < 1 );
}
#ifdef MAPBASE
else if (EntIsClass( pWeapon, gm_isz_class_Pistol ) || EntIsClass( pWeapon, gm_isz_class_357 ) || EntIsClass( pWeapon, gm_isz_class_Crossbow ))
{
// The AI automatically detects these weapons as usable now that there's animations for them, so ensure this behavior can be toggled in situations where that's not desirable
return ai_allow_new_weapons.GetBool();
}
#endif
else
{
return true;

View File

@ -23,6 +23,7 @@
#endif
#ifdef MAPBASE
#include "ai_behavior_functank.h"
#include "mapbase/ai_grenade.h"
#endif
@ -432,6 +433,9 @@ protected:
CAI_OperatorBehavior m_OperatorBehavior;
CAI_PassengerBehaviorCompanion m_PassengerBehavior;
CAI_FearBehavior m_FearBehavior;
#endif
#ifdef MAPBASE
CAI_FuncTankBehavior m_FuncTankBehavior;
#endif
//-----------------------------------------------------

View File

@ -2718,6 +2718,15 @@ void CNPC_Vortigaunt::OnSquishedGrub( const CBaseEntity *pGrub )
//-----------------------------------------------------------------------------
void CNPC_Vortigaunt::AimGun( void )
{
#ifdef MAPBASE
// Use base for func_tank
if (m_FuncTankBehavior.IsRunning())
{
BaseClass::AimGun();
return;
}
#endif
// If our aim lock is on, don't bother
if ( m_flAimDelay >= gpGlobals->curtime )
return;

View File

@ -250,6 +250,10 @@ public:
virtual int SelectSchedule( void );
virtual int TranslateSchedule( int scheduleType );
#ifdef MAPBASE
Activity NPC_TranslateActivity( Activity eNewActivity );
#endif
bool KeyValue( const char *szKeyName, const char *szValue );
void PrescheduleThink( void );
@ -2036,6 +2040,23 @@ int CProtoSniper::TranslateSchedule( int scheduleType )
return BaseClass::TranslateSchedule( scheduleType );
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Activity CProtoSniper::NPC_TranslateActivity( Activity eNewActivity )
{
// ACT_IDLE is now just the soldier's unarmed idle animation.
// Use a gun-holding animation like what unhidden snipers were using before.
if (!HasSpawnFlags( SF_SNIPER_HIDDEN ) && eNewActivity == ACT_IDLE)
{
eNewActivity = ACT_IDLE_SMG1;
}
return BaseClass::NPC_TranslateActivity( eNewActivity );
}
#endif
//---------------------------------------------------------
//---------------------------------------------------------
void CProtoSniper::ScopeGlint()

View File

@ -27,6 +27,11 @@
// CWeapon357
//-----------------------------------------------------------------------------
#ifdef MAPBASE
extern acttable_t *GetPistolActtable();
extern int GetPistolActtableCount();
#endif
class CWeapon357 : public CBaseHLCombatWeapon
{
DECLARE_CLASS( CWeapon357, CBaseHLCombatWeapon );
@ -69,6 +74,9 @@ public:
void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir );
void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary );
virtual acttable_t *GetBackupActivityList() { return GetPistolActtable(); }
virtual int GetBackupActivityListCount() { return GetPistolActtableCount(); }
#endif
DECLARE_SERVERCLASS();
@ -91,6 +99,22 @@ END_DATADESC()
#ifdef MAPBASE
acttable_t CWeapon357::m_acttable[] =
{
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_IDLE, ACT_IDLE_REVOLVER, true },
{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_REVOLVER, true },
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_REVOLVER, true },
{ ACT_RELOAD, ACT_RELOAD_REVOLVER, true },
{ ACT_WALK_AIM, ACT_WALK_AIM_REVOLVER, true },
{ ACT_RUN_AIM, ACT_RUN_AIM_REVOLVER, true },
{ ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_REVOLVER, true },
{ ACT_RELOAD_LOW, ACT_RELOAD_REVOLVER_LOW, false },
{ ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_REVOLVER_LOW, false },
{ ACT_COVER_LOW, ACT_COVER_REVOLVER_LOW, false },
{ ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_REVOLVER_LOW, false },
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_REVOLVER, false },
{ ACT_WALK, ACT_WALK_REVOLVER, true },
{ ACT_RUN, ACT_RUN_REVOLVER, true },
#else
{ ACT_IDLE, ACT_IDLE_PISTOL, true },
{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_PISTOL, true },
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, true },
@ -105,24 +129,40 @@ acttable_t CWeapon357::m_acttable[] =
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_PISTOL, false },
{ ACT_WALK, ACT_WALK_PISTOL, false },
{ ACT_RUN, ACT_RUN_PISTOL, false },
#endif
//
// Activities ported from weapon_alyxgun below
//
// Readiness activities (not aiming)
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_IDLE_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims
{ ACT_IDLE_STIMULATED, ACT_IDLE_PISTOL_STIMULATED, false },
#else
{ ACT_IDLE_RELAXED, ACT_IDLE_PISTOL, false },//never aims
{ ACT_IDLE_STIMULATED, ACT_IDLE_STIMULATED, false },
#endif
{ ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims
{ ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false },
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_WALK_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims
{ ACT_WALK_STIMULATED, ACT_WALK_PISTOL_STIMULATED, false },
#else
{ ACT_WALK_RELAXED, ACT_WALK, false },//never aims
{ ACT_WALK_STIMULATED, ACT_WALK_STIMULATED, false },
#endif
{ ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims
{ ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false },
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_RUN_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims
{ ACT_RUN_STIMULATED, ACT_RUN_PISTOL_STIMULATED, false },
#else
{ ACT_RUN_RELAXED, ACT_RUN, false },//never aims
{ ACT_RUN_STIMULATED, ACT_RUN_STIMULATED, false },
#endif
{ ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims
{ ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false },
@ -153,6 +193,11 @@ acttable_t CWeapon357::m_acttable[] =
{ ACT_READINESS_RELAXED_TO_STIMULATED_WALK, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED_WALK, false },
{ ACT_READINESS_AGITATED_TO_STIMULATED, ACT_READINESS_PISTOL_AGITATED_TO_STIMULATED, false },
{ ACT_READINESS_STIMULATED_TO_RELAXED, ACT_READINESS_PISTOL_STIMULATED_TO_RELAXED, false },
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
{ ACT_RANGE_AIM_MED, ACT_RANGE_AIM_REVOLVER_MED, false },
{ ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_REVOLVER_MED, false },
#endif
};

View File

@ -44,18 +44,33 @@ acttable_t CWeaponAlyxGun::m_acttable[] =
#endif
// Readiness activities (not aiming)
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_IDLE_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims
{ ACT_IDLE_STIMULATED, ACT_IDLE_PISTOL_STIMULATED, false },
#else
{ ACT_IDLE_RELAXED, ACT_IDLE_PISTOL, false },//never aims
{ ACT_IDLE_STIMULATED, ACT_IDLE_STIMULATED, false },
#endif
{ ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims
{ ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false },
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_WALK_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims
{ ACT_WALK_STIMULATED, ACT_WALK_PISTOL_STIMULATED, false },
#else
{ ACT_WALK_RELAXED, ACT_WALK, false },//never aims
{ ACT_WALK_STIMULATED, ACT_WALK_STIMULATED, false },
#endif
{ ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims
{ ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false },
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_RUN_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims
{ ACT_RUN_STIMULATED, ACT_RUN_PISTOL_STIMULATED, false },
#else
{ ACT_RUN_RELAXED, ACT_RUN, false },//never aims
{ ACT_RUN_STIMULATED, ACT_RUN_STIMULATED, false },
#endif
{ ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims
{ ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false },

View File

@ -13,6 +13,11 @@
#pragma once
#endif
#ifdef MAPBASE
extern acttable_t *GetPistolActtable();
extern int GetPistolActtableCount();
#endif
class CWeaponAlyxGun : public CHLSelectFireMachineGun
{
DECLARE_DATADESC();
@ -51,6 +56,11 @@ public:
SetTouch(NULL);
}
#ifdef MAPBASE
virtual acttable_t *GetBackupActivityList() { return GetPistolActtable(); }
virtual int GetBackupActivityListCount() { return GetPistolActtableCount(); }
#endif
float m_flTooCloseTimer;
DECLARE_ACTTABLE();

View File

@ -38,10 +38,6 @@ ConVar sk_weapon_ar2_alt_fire_radius( "sk_weapon_ar2_alt_fire_radius", "10" );
ConVar sk_weapon_ar2_alt_fire_duration( "sk_weapon_ar2_alt_fire_duration", "2" );
ConVar sk_weapon_ar2_alt_fire_mass( "sk_weapon_ar2_alt_fire_mass", "150" );
#ifdef MAPBASE
extern acttable_t *GetSMG1Acttable();
#endif
//=========================================================
//=========================================================
@ -100,14 +96,14 @@ acttable_t CWeaponAR2::m_acttable[] =
{ ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true },
{ ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true },
{ ACT_RUN, ACT_RUN_AR2, true },
{ ACT_RUN_AIM, ACT_RUN_AIM_RIFLE, true },
{ ACT_RUN_AIM, ACT_RUN_AIM_AR2, true },
{ ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true },
{ ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true },
{ ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_AR2, false },
{ ACT_COVER_LOW, ACT_COVER_SMG1_LOW, false },
{ ACT_COVER_LOW, ACT_COVER_AR2_LOW, true },
{ ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_AR2_LOW, false },
{ ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_AR2_LOW, false },
{ ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false },
{ ACT_RELOAD_LOW, ACT_RELOAD_AR2_LOW, false },
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_AR2, true },
// { ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true },
#else
@ -160,6 +156,21 @@ acttable_t CWeaponAR2::m_acttable[] =
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true },
// { ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true },
#endif
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_ARM, ACT_ARM_RIFLE, false },
{ ACT_DISARM, ACT_DISARM_RIFLE, false },
#endif
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
{ ACT_RANGE_AIM_MED, ACT_RANGE_AIM_AR2_MED, false },
{ ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_AR2_MED, false },
{ ACT_COVER_WALL_R, ACT_COVER_WALL_R_RIFLE, false },
{ ACT_COVER_WALL_L, ACT_COVER_WALL_L_RIFLE, false },
{ ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_RIFLE, false },
{ ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_RIFLE, false },
#endif
};
IMPLEMENT_ACTTABLE(CWeaponAR2);

View File

@ -23,6 +23,13 @@ acttable_t CWeaponCitizenPackage::m_acttable[] =
{
{ ACT_IDLE, ACT_IDLE_PACKAGE, false },
{ ACT_WALK, ACT_WALK_PACKAGE, false },
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_RUN, ACT_RUN_PACKAGE, false },
{ ACT_IDLE_ANGRY, ACT_IDLE_PACKAGE, false },
{ ACT_WALK_AIM, ACT_WALK_PACKAGE, false },
{ ACT_RUN_AIM, ACT_RUN_PACKAGE, false },
#endif
};
IMPLEMENT_ACTTABLE(CWeaponCitizenPackage);
@ -70,5 +77,12 @@ acttable_t CWeaponCitizenSuitcase::m_acttable[] =
{
{ ACT_IDLE, ACT_IDLE_SUITCASE, false },
{ ACT_WALK, ACT_WALK_SUITCASE, false },
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_RUN, ACT_RUN_SUITCASE, false },
{ ACT_IDLE_ANGRY, ACT_IDLE_SUITCASE, false },
{ ACT_WALK_AIM, ACT_WALK_SUITCASE, false },
{ ACT_RUN_AIM, ACT_RUN_SUITCASE, false },
#endif
};
IMPLEMENT_ACTTABLE(CWeaponCitizenSuitcase);

View File

@ -542,7 +542,7 @@ public:
virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
virtual bool Reload( void );
#ifdef MAPBASE
virtual void Reload_NPC( void );
virtual void Reload_NPC( bool bPlaySound = true );
#endif
virtual void ItemPostFrame( void );
virtual void ItemBusyFrame( void );
@ -638,6 +638,15 @@ END_DATADESC()
#ifdef MAPBASE
acttable_t CWeaponCrossbow::m_acttable[] =
{
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_CROSSBOW, true },
{ ACT_RELOAD, ACT_RELOAD_CROSSBOW, true },
{ ACT_IDLE, ACT_IDLE_CROSSBOW, true },
{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_CROSSBOW, true },
{ ACT_WALK, ACT_WALK_CROSSBOW, true },
{ ACT_WALK_AIM, ACT_WALK_AIM_CROSSBOW, true },
#else
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG1, true },
{ ACT_RELOAD, ACT_RELOAD_SMG1, true },
{ ACT_IDLE, ACT_IDLE_SMG1, true },
@ -645,6 +654,7 @@ acttable_t CWeaponCrossbow::m_acttable[] =
{ ACT_WALK, ACT_WALK_RIFLE, true },
{ ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true },
#endif
// Readiness activities (not aiming)
{ ACT_IDLE_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims
@ -673,6 +683,24 @@ acttable_t CWeaponCrossbow::m_acttable[] =
{ ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims
//End readiness activities
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_WALK_AIM, ACT_WALK_AIM_CROSSBOW, true },
{ ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true },
{ ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true },
{ ACT_RUN, ACT_RUN_CROSSBOW, true },
{ ACT_RUN_AIM, ACT_RUN_AIM_CROSSBOW, true },
{ ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true },
{ ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true },
{ ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_CROSSBOW, true },
{ ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_CROSSBOW_LOW, true },
{ ACT_COVER_LOW, ACT_COVER_CROSSBOW_LOW, false },
{ ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_CROSSBOW_LOW, false },
{ ACT_RELOAD_LOW, ACT_RELOAD_CROSSBOW_LOW, false },
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_CROSSBOW, true },
{ ACT_ARM, ACT_ARM_RIFLE, false },
{ ACT_DISARM, ACT_DISARM_RIFLE, false },
#else
{ ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true },
{ ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true },
{ ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true },
@ -686,6 +714,12 @@ acttable_t CWeaponCrossbow::m_acttable[] =
{ ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SMG1_LOW, false },
{ ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false },
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true },
#endif
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
{ ACT_RANGE_AIM_MED, ACT_RANGE_AIM_CROSSBOW_MED, false },
{ ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_CROSSBOW_MED, false },
#endif
};
IMPLEMENT_ACTTABLE(CWeaponCrossbow);
@ -785,11 +819,15 @@ bool CWeaponCrossbow::Reload( void )
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponCrossbow::Reload_NPC( void )
void CWeaponCrossbow::Reload_NPC( bool bPlaySound )
{
BaseClass::Reload_NPC();
BaseClass::Reload_NPC( bPlaySound );
m_nSkin = 0;
int iBody = FindBodygroupByName( "bolt" );
if (iBody != -1)
SetBodygroup( iBody, 0 );
else
m_nSkin = 0;
}
#endif
@ -939,7 +977,11 @@ void CWeaponCrossbow::FireNPCBolt( CAI_BaseNPC *pOwner, Vector &vecShootOrigin,
m_iClip1--;
m_nSkin = 1;
int iBody = FindBodygroupByName( "bolt" );
if (iBody != -1)
SetBodygroup( iBody, 1 );
else
m_nSkin = 1;
WeaponSound( SINGLE_NPC );
WeaponSound( SPECIAL2 );

View File

@ -42,6 +42,13 @@ acttable_t CWeaponCrowbar::m_acttable[] =
{ ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true },
{ ACT_IDLE, ACT_IDLE_ANGRY_MELEE, false },
{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, false },
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_RUN, ACT_RUN_MELEE, false },
{ ACT_WALK, ACT_WALK_MELEE, false },
{ ACT_ARM, ACT_ARM_MELEE, false },
{ ACT_DISARM, ACT_DISARM_MELEE, false },
#endif
};
IMPLEMENT_ACTTABLE(CWeaponCrowbar);

View File

@ -46,6 +46,12 @@ public:
// Animation event
virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
#ifdef MAPBASE
// Don't use backup activities
acttable_t *GetBackupActivityList() { return NULL; }
int GetBackupActivityListCount() { return 0; }
#endif
private:
// Animation event handlers
void HandleAnimEventMeleeHit( animevent_t *pEvent, CBaseCombatCharacter *pOperator );

View File

@ -108,6 +108,12 @@ public:
return 0.5f;
}
#ifdef MAPBASE
// Pistols are their own backup activities
virtual acttable_t *GetBackupActivityList() { return NULL; }
virtual int GetBackupActivityListCount() { return 0; }
#endif
DECLARE_ACTTABLE();
private:
@ -155,6 +161,40 @@ acttable_t CWeaponPistol::m_acttable[] =
// Activities ported from weapon_alyxgun below
//
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
// Readiness activities (not aiming)
{ ACT_IDLE_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims
{ ACT_IDLE_STIMULATED, ACT_IDLE_PISTOL_STIMULATED, false },
{ ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims
{ ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false },
{ ACT_WALK_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims
{ ACT_WALK_STIMULATED, ACT_WALK_PISTOL_STIMULATED, false },
{ ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims
{ ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false },
{ ACT_RUN_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims
{ ACT_RUN_STIMULATED, ACT_RUN_PISTOL_STIMULATED, false },
{ ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims
{ ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false },
// Readiness activities (aiming)
{ ACT_IDLE_AIM_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims
{ ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_PISTOL_STIMULATED, false },
{ ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims
{ ACT_IDLE_AIM_STEALTH, ACT_IDLE_STEALTH_PISTOL, false },
{ ACT_WALK_AIM_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims
{ ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_PISTOL, false },
{ ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims
{ ACT_WALK_AIM_STEALTH, ACT_WALK_AIM_STEALTH_PISTOL, false },//always aims
{ ACT_RUN_AIM_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims
{ ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_PISTOL, false },
{ ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims
{ ACT_RUN_AIM_STEALTH, ACT_RUN_AIM_STEALTH_PISTOL, false },//always aims
//End readiness activities
#else
// Readiness activities (not aiming)
{ ACT_IDLE_RELAXED, ACT_IDLE_PISTOL, false },//never aims
{ ACT_IDLE_STIMULATED, ACT_IDLE_STIMULATED, false },
@ -187,6 +227,7 @@ acttable_t CWeaponPistol::m_acttable[] =
{ ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims
{ ACT_RUN_AIM_STEALTH, ACT_RUN_AIM_STEALTH_PISTOL, false },//always aims
//End readiness activities
#endif
// Crouch activities
{ ACT_CROUCHIDLE_STIMULATED, ACT_CROUCHIDLE_STIMULATED, false },
@ -199,11 +240,41 @@ acttable_t CWeaponPistol::m_acttable[] =
{ ACT_READINESS_AGITATED_TO_STIMULATED, ACT_READINESS_PISTOL_AGITATED_TO_STIMULATED, false },
{ ACT_READINESS_STIMULATED_TO_RELAXED, ACT_READINESS_PISTOL_STIMULATED_TO_RELAXED, false },
#endif
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_WALK_CROUCH, ACT_WALK_CROUCH_PISTOL, true },
{ ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_PISTOL, true },
{ ACT_RUN_CROUCH, ACT_RUN_CROUCH_PISTOL, true },
{ ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_PISTOL, true },
#endif
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
{ ACT_RANGE_AIM_MED, ACT_RANGE_AIM_PISTOL_MED, false },
{ ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_PISTOL_MED, false },
{ ACT_COVER_WALL_R, ACT_COVER_WALL_R_PISTOL, false },
{ ACT_COVER_WALL_L, ACT_COVER_WALL_L_PISTOL, false },
{ ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_PISTOL, false },
{ ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_PISTOL, false },
#endif
};
IMPLEMENT_ACTTABLE( CWeaponPistol );
#ifdef MAPBASE
// Allows Weapon_BackupActivity() to access the pistol's activity table.
acttable_t *GetPistolActtable()
{
return CWeaponPistol::m_acttable;
}
int GetPistolActtableCount()
{
return ARRAYSIZE(CWeaponPistol::m_acttable);
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------

View File

@ -1399,6 +1399,11 @@ PRECACHE_WEAPON_REGISTER(weapon_rpg);
acttable_t CWeaponRPG::m_acttable[] =
{
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_RPG, true },
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_RPG_LOW, false },
{ ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_RPG_LOW, false },
{ ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_RPG, false },
#endif
{ ACT_IDLE_RELAXED, ACT_IDLE_RPG_RELAXED, true },
{ ACT_IDLE_STIMULATED, ACT_IDLE_ANGRY_RPG, true },
@ -1411,6 +1416,16 @@ acttable_t CWeaponRPG::m_acttable[] =
{ ACT_RUN, ACT_RUN_RPG, true },
{ ACT_RUN_CROUCH, ACT_RUN_CROUCH_RPG, true },
{ ACT_COVER_LOW, ACT_COVER_LOW_RPG, true },
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_ARM, ACT_ARM_RPG, false },
{ ACT_DISARM, ACT_DISARM_RPG, false },
#endif
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
{ ACT_RANGE_AIM_MED, ACT_RANGE_AIM_RPG_MED, false },
{ ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_RPG_MED, false },
#endif
};
IMPLEMENT_ACTTABLE(CWeaponRPG);

View File

@ -108,6 +108,57 @@ END_DATADESC()
acttable_t CWeaponShotgun::m_acttable[] =
{
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
// Note that ACT_IDLE_SHOTGUN_AGITATED seems to be a stand-in for ACT_IDLE_SHOTGUN on citizens,
// but that isn't acceptable for NPCs which don't use readiness activities.
{ ACT_IDLE, ACT_IDLE_SHOTGUN, true },
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, true },
{ ACT_RELOAD, ACT_RELOAD_SHOTGUN, false },
{ ACT_WALK, ACT_WALK_SHOTGUN, true },
{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SHOTGUN, true },
// Readiness activities (not aiming)
{ ACT_IDLE_RELAXED, ACT_IDLE_SHOTGUN_RELAXED, false },//never aims
{ ACT_IDLE_STIMULATED, ACT_IDLE_SHOTGUN_STIMULATED, false },
{ ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_SHOTGUN, false },//always aims
{ ACT_WALK_RELAXED, ACT_WALK_SHOTGUN_RELAXED, false },//never aims
{ ACT_WALK_STIMULATED, ACT_WALK_SHOTGUN_STIMULATED, false },
{ ACT_WALK_AGITATED, ACT_WALK_AIM_SHOTGUN, false },//always aims
{ ACT_RUN_RELAXED, ACT_RUN_SHOTGUN_RELAXED, false },//never aims
{ ACT_RUN_STIMULATED, ACT_RUN_SHOTGUN_STIMULATED, false },
{ ACT_RUN_AGITATED, ACT_RUN_AIM_SHOTGUN, false },//always aims
// Readiness activities (aiming)
{ ACT_IDLE_AIM_RELAXED, ACT_IDLE_SHOTGUN_RELAXED, false },//never aims
{ ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_SHOTGUN_STIMULATED, false },
{ ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SHOTGUN, false },//always aims
{ ACT_WALK_AIM_RELAXED, ACT_WALK_SHOTGUN_RELAXED, false },//never aims
{ ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_SHOTGUN_STIMULATED, false },
{ ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_SHOTGUN, false },//always aims
{ ACT_RUN_AIM_RELAXED, ACT_RUN_SHOTGUN_RELAXED, false },//never aims
{ ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_SHOTGUN_STIMULATED, false },
{ ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_SHOTGUN, false },//always aims
//End readiness activities
{ ACT_WALK_AIM, ACT_WALK_AIM_SHOTGUN, true },
{ ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true },
{ ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true },
{ ACT_RUN, ACT_RUN_SHOTGUN, true },
{ ACT_RUN_AIM, ACT_RUN_AIM_SHOTGUN, true },
{ ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true },
{ ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true },
{ ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_SHOTGUN, true },
{ ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SHOTGUN_LOW, true },
{ ACT_RELOAD_LOW, ACT_RELOAD_SHOTGUN_LOW, false },
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SHOTGUN, false },
{ ACT_COVER_LOW, ACT_COVER_SHOTGUN_LOW, false },
{ ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SHOTGUN_LOW, false },
#else
{ ACT_IDLE, ACT_IDLE_SMG1, true }, // FIXME: hook to shotgun unique
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, true },
@ -153,6 +204,17 @@ acttable_t CWeaponShotgun::m_acttable[] =
{ ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SHOTGUN_LOW, true },
{ ACT_RELOAD_LOW, ACT_RELOAD_SHOTGUN_LOW, false },
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SHOTGUN, false },
#endif
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_ARM, ACT_ARM_SHOTGUN, true },
{ ACT_DISARM, ACT_DISARM_SHOTGUN, true },
#endif
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
{ ACT_RANGE_AIM_MED, ACT_RANGE_AIM_SHOTGUN_MED, false },
{ ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_SHOTGUN_MED, false },
#endif
};
IMPLEMENT_ACTTABLE(CWeaponShotgun);

View File

@ -134,6 +134,21 @@ acttable_t CWeaponSMG1::m_acttable[] =
{ ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SMG1_LOW, false },
{ ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false },
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true },
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_ARM, ACT_ARM_RIFLE, false },
{ ACT_DISARM, ACT_DISARM_RIFLE, false },
#endif
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
{ ACT_RANGE_AIM_MED, ACT_RANGE_AIM_SMG1_MED, false },
{ ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_SMG1_MED, false },
{ ACT_COVER_WALL_R, ACT_COVER_WALL_R_RIFLE, false },
{ ACT_COVER_WALL_L, ACT_COVER_WALL_L_RIFLE, false },
{ ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_RIFLE, false },
{ ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_RIFLE, false },
#endif
};
IMPLEMENT_ACTTABLE(CWeaponSMG1);

View File

@ -41,6 +41,7 @@
DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ), \
DEFINE_FIELD( m_iLastAnimEventHandled, FIELD_INTEGER ), \
DEFINE_INPUTFUNC( FIELD_STRING, "ThrowGrenadeAtTarget", InputThrowGrenadeAtTarget ), \
DEFINE_INPUTFUNC( FIELD_STRING, "ThrowGrenadeGestureAtTarget", InputThrowGrenadeGestureAtTarget ), \
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetGrenades", InputSetGrenades ), \
DEFINE_INPUTFUNC( FIELD_INTEGER, "AddGrenades", InputAddGrenades ), \
DEFINE_OUTPUT(m_OnThrowGrenade, "OnThrowGrenade"), \
@ -124,6 +125,7 @@ public:
void InputSetGrenades( inputdata_t &inputdata ) { AddGrenades( inputdata.value.Int() - m_iNumGrenades ); }
void InputAddGrenades( inputdata_t &inputdata ) { AddGrenades( inputdata.value.Int() ); }
void InputThrowGrenadeAtTarget( inputdata_t &inputdata );
void InputThrowGrenadeGestureAtTarget( inputdata_t &inputdata );
virtual void DelayGrenadeCheck( float delay ) { m_flNextGrenadeCheck = gpGlobals->curtime + delay; }
@ -294,6 +296,63 @@ void CAI_GrenadeUser<BASE_NPC>::InputThrowGrenadeAtTarget( inputdata_t &inputdat
this->ClearSchedule( "Told to throw grenade via input" );
}
//-----------------------------------------------------------------------------
// Purpose: Force the combine soldier to throw a grenade at the target using the gesture animation.
// If I'm a combine elite, fire my combine ball at the target instead.
// Input : &inputdata -
//-----------------------------------------------------------------------------
template <class BASE_NPC>
void CAI_GrenadeUser<BASE_NPC>::InputThrowGrenadeGestureAtTarget( inputdata_t &inputdata )
{
// Ignore if we're inside a scripted sequence
//if ( this->GetState() == NPC_STATE_SCRIPT && this->m_hCine )
// return;
CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller );
if ( !pEntity )
{
DevMsg("%s (%s) received ThrowGrenadeGestureAtTarget input, but couldn't find target entity '%s'\n", this->GetClassname(), this->GetDebugName(), inputdata.value.String() );
return;
}
m_hForcedGrenadeTarget = pEntity;
m_flNextGrenadeCheck = 0;
Vector vecTarget = m_hForcedGrenadeTarget->WorldSpaceCenter();
#ifdef SHARED_COMBINE_ACTIVITIES
if (IsAltFireCapable())
{
if (FVisible( m_hForcedGrenadeTarget ))
{
m_vecAltFireTarget = vecTarget;
m_hForcedGrenadeTarget = NULL;
int iLayer = AddGesture( ACT_GESTURE_COMBINE_AR2_ALTFIRE );
if (iLayer != -1)
{
this->GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + this->GetLayerDuration( iLayer ) );
}
}
}
else
{
// If we can, throw a grenade at the target.
// Ignore grenade count / distance / etc
if (CheckCanThrowGrenade( vecTarget ))
{
int iLayer = AddGesture( ACT_GESTURE_COMBINE_THROW_GRENADE );
if (iLayer != -1)
{
this->GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + this->GetLayerDuration( iLayer ) );
}
}
}
#else
Warning("Gesture grenades/alt-fire not supported\n");
#endif
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class BASE_NPC>

View File

@ -4099,6 +4099,11 @@ BEGIN_DATADESC(CBasePropDoor)
DEFINE_KEYFIELD(m_SoundClose, FIELD_SOUNDNAME, "soundcloseoverride"),
DEFINE_KEYFIELD(m_ls.sLockedSound, FIELD_SOUNDNAME, "soundlockedoverride"),
DEFINE_KEYFIELD(m_ls.sUnlockedSound, FIELD_SOUNDNAME, "soundunlockedoverride"),
#ifdef MAPBASE
DEFINE_KEYFIELD(m_flNPCOpenDistance, FIELD_FLOAT, "opendistoverride"),
DEFINE_KEYFIELD(m_eNPCOpenFrontActivity, FIELD_INTEGER, "openfrontactivityoverride"),
DEFINE_KEYFIELD(m_eNPCOpenBackActivity, FIELD_INTEGER, "openbackactivityoverride"),
#endif
DEFINE_KEYFIELD(m_SlaveName, FIELD_STRING, "slavename" ),
DEFINE_FIELD(m_bLocked, FIELD_BOOLEAN),
//DEFINE_KEYFIELD(m_flBlockDamage, FIELD_FLOAT, "dmg"),
@ -4143,6 +4148,11 @@ END_SEND_TABLE()
CBasePropDoor::CBasePropDoor( void )
{
m_hMaster = NULL;
#ifdef MAPBASE
m_flNPCOpenDistance = -1;
m_eNPCOpenFrontActivity = ACT_INVALID;
m_eNPCOpenBackActivity = ACT_INVALID;
#endif
}
//-----------------------------------------------------------------------------
@ -4340,6 +4350,32 @@ void CBasePropDoor::CalcDoorSounds()
{
strSoundLocked = AllocPooledString( pkvHardwareData->GetString( "locked" ) );
strSoundUnlocked = AllocPooledString( pkvHardwareData->GetString( "unlocked" ) );
#ifdef MAPBASE
if (m_eNPCOpenFrontActivity == ACT_INVALID)
{
const char *pszActivity = pkvHardwareData->GetString( "activity_front" );
if (pszActivity[0] != '\0')
{
m_eNPCOpenFrontActivity = (Activity)CAI_BaseNPC::GetActivityID( pszActivity );
if (m_eNPCOpenFrontActivity == ACT_INVALID)
m_eNPCOpenFrontActivity = ActivityList_RegisterPrivateActivity( pszActivity );
}
}
if (m_eNPCOpenBackActivity == ACT_INVALID)
{
const char *pszActivity = pkvHardwareData->GetString( "activity_back" );
if (pszActivity[0] != '\0')
{
m_eNPCOpenBackActivity = (Activity)CAI_BaseNPC::GetActivityID( pszActivity );
if (m_eNPCOpenBackActivity == ACT_INVALID)
m_eNPCOpenBackActivity = ActivityList_RegisterPrivateActivity( pszActivity );
}
}
if (m_flNPCOpenDistance == -1)
m_flNPCOpenDistance = pkvHardwareData->GetFloat( "npc_distance", 32.0 );
#endif
}
// If any sounds were missing, try the "defaults" block.
@ -5940,6 +5976,11 @@ void CPropDoorRotating::DoorResume( void )
AngularMove( m_angGoal, m_flSpeed );
}
#ifdef MAPBASE
ConVar ai_door_enable_acts( "ai_door_enable_acts", "1", FCVAR_NONE, "Enables the new door-opening activities." );
ConVar ai_door_open_dist_override( "ai_door_open_dist_override", "-1", FCVAR_NONE, "Overrides the distance from a door a NPC has to navigate to in order to open a door." );
#endif
//-----------------------------------------------------------------------------
// Purpose:
// Input : vecMoveDir -
@ -5961,20 +6002,37 @@ void CPropDoorRotating::GetNPCOpenData(CAI_BaseNPC *pNPC, opendata_t &opendata)
Vector vecNPCOrigin = pNPC->GetAbsOrigin();
#ifdef MAPBASE
float flPosOffset = ai_door_open_dist_override.GetFloat() >= 0.0f ? ai_door_open_dist_override.GetFloat() : GetNPCOpenDistance();
#else
float flPosOffset = 64;
#endif
if (pNPC->GetAbsOrigin().Dot(vecForward) > GetAbsOrigin().Dot(vecForward))
{
// In front of the door relative to the door's forward vector.
opendata.vecStandPos += vecForward * 64;
opendata.vecStandPos += vecForward * flPosOffset;
opendata.vecFaceDir = -vecForward;
#ifdef MAPBASE
opendata.eActivity = !ai_door_enable_acts.GetBool() ? ACT_INVALID : GetNPCOpenFrontActivity();
#endif
}
else
{
// Behind the door relative to the door's forward vector.
opendata.vecStandPos -= vecForward * 64;
opendata.vecStandPos -= vecForward * flPosOffset;
opendata.vecFaceDir = vecForward;
#ifdef MAPBASE
opendata.eActivity = !ai_door_enable_acts.GetBool() ? ACT_INVALID : GetNPCOpenBackActivity();
#endif
}
#ifdef MAPBASE
if (opendata.eActivity == ACT_INVALID)
opendata.eActivity = ACT_OPEN_DOOR;
#else
opendata.eActivity = ACT_OPEN_DOOR;
#endif
}

View File

@ -2296,15 +2296,22 @@ void ActivityList_RegisterSharedActivities( void )
REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR2 );
REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2 );
//REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2_LOW );
REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2_LOW );
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_AR2 );
REGISTER_SHARED_ACTIVITY( ACT_COVER_AR2_LOW );
#endif
#ifdef SHARED_COMBINE_ACTIVITIES
REGISTER_SHARED_ACTIVITY( ACT_COMBINE_THROW_GRENADE );
REGISTER_SHARED_ACTIVITY( ACT_COMBINE_AR2_ALTFIRE );
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_COMBINE_THROW_GRENADE );
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_COMBINE_AR2_ALTFIRE );
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SPECIAL_ATTACK1 );
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SPECIAL_ATTACK2 );
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_ADVANCE );
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_FORWARD );
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_GROUP );
@ -2314,9 +2321,133 @@ void ActivityList_RegisterSharedActivities( void )
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_TAKECOVER );
#endif
#ifdef COMPANION_HOLSTER_WORKAROUND
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
REGISTER_SHARED_ACTIVITY( ACT_IDLE_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_WALK_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_RUN_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_RELOAD_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER_LOW );
REGISTER_SHARED_ACTIVITY( ACT_RELOAD_REVOLVER_LOW );
REGISTER_SHARED_ACTIVITY( ACT_COVER_REVOLVER_LOW );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_REVOLVER_LOW );
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_REVOLVER );
REGISTER_SHARED_ACTIVITY( ACT_IDLE_CROSSBOW );
REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_CROSSBOW );
REGISTER_SHARED_ACTIVITY( ACT_WALK_CROSSBOW );
REGISTER_SHARED_ACTIVITY( ACT_RUN_CROSSBOW );
REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_CROSSBOW );
REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_CROSSBOW );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW );
REGISTER_SHARED_ACTIVITY( ACT_RELOAD_CROSSBOW );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW_LOW );
REGISTER_SHARED_ACTIVITY( ACT_RELOAD_CROSSBOW_LOW );
REGISTER_SHARED_ACTIVITY( ACT_COVER_CROSSBOW_LOW );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_CROSSBOW_LOW );
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_CROSSBOW );
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_CROSSBOW );
REGISTER_SHARED_ACTIVITY( ACT_IDLE_PISTOL_RELAXED );
REGISTER_SHARED_ACTIVITY( ACT_IDLE_PISTOL_STIMULATED );
REGISTER_SHARED_ACTIVITY( ACT_WALK_PISTOL_RELAXED );
REGISTER_SHARED_ACTIVITY( ACT_WALK_PISTOL_STIMULATED );
REGISTER_SHARED_ACTIVITY( ACT_RUN_PISTOL_RELAXED );
REGISTER_SHARED_ACTIVITY( ACT_RUN_PISTOL_STIMULATED );
REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_PISTOL_STIMULATED );
REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_PISTOL_STIMULATED );
REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_PISTOL_STIMULATED );
REGISTER_SHARED_ACTIVITY( ACT_WALK_CROUCH_PISTOL );
REGISTER_SHARED_ACTIVITY( ACT_WALK_CROUCH_AIM_PISTOL );
REGISTER_SHARED_ACTIVITY( ACT_RUN_CROUCH_PISTOL );
REGISTER_SHARED_ACTIVITY( ACT_RUN_CROUCH_AIM_PISTOL );
REGISTER_SHARED_ACTIVITY( ACT_IDLE_SHOTGUN );
REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN );
REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN );
REGISTER_SHARED_ACTIVITY( ACT_COVER_SHOTGUN_LOW );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SHOTGUN_LOW );
REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN_RELAXED );
REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN_STIMULATED );
REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN_RELAXED );
REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN_STIMULATED );
REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_SHOTGUN_STIMULATED );
REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SHOTGUN_STIMULATED );
REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SHOTGUN_STIMULATED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_RPG_LOW );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_RPG_LOW );
REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_RPG );
REGISTER_SHARED_ACTIVITY( ACT_WALK_MELEE );
REGISTER_SHARED_ACTIVITY( ACT_RUN_MELEE );
REGISTER_SHARED_ACTIVITY( ACT_RUN_PACKAGE );
REGISTER_SHARED_ACTIVITY( ACT_RUN_SUITCASE );
REGISTER_SHARED_ACTIVITY( ACT_ARM_RIFLE );
REGISTER_SHARED_ACTIVITY( ACT_ARM_SHOTGUN );
REGISTER_SHARED_ACTIVITY( ACT_ARM_RPG );
REGISTER_SHARED_ACTIVITY( ACT_ARM_MELEE );
REGISTER_SHARED_ACTIVITY( ACT_DISARM_RIFLE );
REGISTER_SHARED_ACTIVITY( ACT_DISARM_SHOTGUN );
REGISTER_SHARED_ACTIVITY( ACT_DISARM_RPG );
REGISTER_SHARED_ACTIVITY( ACT_DISARM_MELEE );
#endif
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
REGISTER_SHARED_ACTIVITY( ACT_CLIMB_ALL );
REGISTER_SHARED_ACTIVITY( ACT_CLIMB_IDLE );
REGISTER_SHARED_ACTIVITY( ACT_CLIMB_MOUNT_TOP );
REGISTER_SHARED_ACTIVITY( ACT_CLIMB_MOUNT_BOTTOM );
REGISTER_SHARED_ACTIVITY( ACT_CLIMB_DISMOUNT_BOTTOM );
#endif
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK1_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK2_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR2_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG1_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SHOTGUN_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_PISTOL_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_RPG_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_AR2_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SMG1_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SHOTGUN_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_PISTOL_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_RPG_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_REVOLVER_MED );
REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_CROSSBOW_MED );
REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R );
REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L );
REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R );
REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L );
REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R_RIFLE );
REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L_RIFLE );
REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R_RIFLE );
REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L_RIFLE );
REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R_PISTOL );
REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L_PISTOL );
REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R_PISTOL );
REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L_PISTOL );
#endif
AssertMsg( g_HighestActivity == LAST_SHARED_ACTIVITY - 1, "Not all activities from ai_activity.h registered in activitylist.cpp" );

View File

@ -13,42 +13,42 @@
#ifdef MAPBASE
// Mapbase adds a few shared activities.
//
// These used to be placed in between existing activities, as outside of the code activities are based off of strings.
// This seemed like a bad idea, but no problems arose at the time.
// I later discovered that apparently some things in MP use the direct integers instead of the enum names.
// I retroactively put all custom activities at the bottom of the enum instead.
// Their placements in activitylist.cpp and ai_activity.cpp have not been changed.
// Mapbase adds many new shared activities, primarily for NPCs.
//
// These are at the bottom of the enum to prevent disruptions in the order of existing activities.
//
// AR2 ACTIVITY FIX
// You know all of those AR2 activities that citizens and combine soldiers have?
// Yeah, those are unused. It appears Valve forgot to implement them.
// Citizens and Combine soldiers have several activities for the SMG1 and AR2 which differ from each other.
// Across both NPCs, there are around 20-40 different AR2 animations. Most of these animations are similar to the
// SMG1 animations, except their hand positions are adjusted to use the AR2 instead.
//
// What could be 20-40 different animations on two different characters are not even defined in code.
// I didn't even realize they were unused until I saw ACT_RELOAD_AR2, so I don't blame them for never realizing this.
// They work surprisingly well for probably never being tested in-game.
// Unfortunately, the vast majority of the AR2 animations in those models are not declared as real activities in
// code. The AR2 instead falls back to its SMG1 animation counterparts.
// This is thought to be an oversight which dates back to late in Half-Life 2's development.
//
// 1 = Add activities directly
// 2 = Add activities as custom activities (todo)
//
// 2 should only be preferable if adding them like this breaks something.
// This preprocessor declares those activities and implements them on the AR2. In-game, they work surprisingly well
// despite presumably never being tested in-game during HL2's development.
#define AR2_ACTIVITY_FIX 1
// COMPANION HOLSTER WORKAROUND
// I introduced a separate holster/unholster animation to male_shared
// and female_shared and I realized it might conflict with Alyx's animation.
//
// I came up with a solution--ACT_ARM_RIFLE and its disarm counterpart--to solve it.
// I didn't think about the fact I could've named them the same as Alyx's so her animations would overwrite it...
// ...so this has been deactivated.
//#define COMPANION_HOLSTER_WORKAROUND 1
// SHARED COMBINE ACTIVITIES
// This turns ACT_COMBINE_AR2_ALTFIRE and ACT_COMBINE_THROW_GRENADE into shared activities.
// This is necessary so other NPCs to use them without having to rely on a bunch of custom activities.
// This turns ACT_COMBINE_AR2_ALTFIRE, ACT_COMBINE_THROW_GRENADE, and their new gesture counterparts into shared activities.
// This is necessary for other NPCs to use them without having to rely on private custom activities declared through the AI definition system.
#define SHARED_COMBINE_ACTIVITIES 1
// EXPANDED HL2 WEAPON ACTIVITIES
// This enables a bunch of new activities for Half-Life 2 weapons, including new 357 animations and readiness activities for pistols.
#define EXPANDED_HL2_WEAPON_ACTIVITIES 1
// EXPANDED NAVIGATION ACTIVITIES
// This enables some new navigation-related activities.
#define EXPANDED_NAVIGATION_ACTIVITIES 1
// EXPANDED HL2 COVER ACTIVITIES
// This enables some new cover-related activities.
#define EXPANDED_HL2_COVER_ACTIVITIES 1
#endif
#define ACTIVITY_NOT_AVAILABLE -1
@ -2169,16 +2169,23 @@ typedef enum
ACT_RUN_AIM_AR2,
ACT_RELOAD_AR2,
//ACT_RELOAD_AR2_LOW,
ACT_RELOAD_AR2_LOW,
ACT_GESTURE_RELOAD_AR2,
ACT_COVER_AR2_LOW,
#endif
#ifdef SHARED_COMBINE_ACTIVITIES
ACT_COMBINE_THROW_GRENADE,
ACT_COMBINE_AR2_ALTFIRE,
// New gesture-based signals as activities for people who want to use them
// Gesture versions for existing Combine signal and grenade activities
ACT_GESTURE_COMBINE_THROW_GRENADE,
ACT_GESTURE_COMBINE_AR2_ALTFIRE,
ACT_GESTURE_SPECIAL_ATTACK1,
ACT_GESTURE_SPECIAL_ATTACK2,
ACT_GESTURE_SIGNAL_ADVANCE,
ACT_GESTURE_SIGNAL_FORWARD,
ACT_GESTURE_SIGNAL_GROUP,
@ -2188,9 +2195,143 @@ typedef enum
ACT_GESTURE_SIGNAL_TAKECOVER,
#endif
#ifdef COMPANION_HOLSTER_WORKAROUND
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
// Revolver (357)
ACT_IDLE_REVOLVER,
ACT_IDLE_ANGRY_REVOLVER,
ACT_WALK_REVOLVER,
ACT_RUN_REVOLVER,
ACT_WALK_AIM_REVOLVER,
ACT_RUN_AIM_REVOLVER,
ACT_RANGE_ATTACK_REVOLVER,
ACT_RELOAD_REVOLVER,
ACT_RANGE_ATTACK_REVOLVER_LOW,
ACT_RELOAD_REVOLVER_LOW,
ACT_COVER_REVOLVER_LOW,
ACT_RANGE_AIM_REVOLVER_LOW,
ACT_GESTURE_RANGE_ATTACK_REVOLVER,
ACT_GESTURE_RELOAD_REVOLVER,
// Crossbow
ACT_IDLE_CROSSBOW,
ACT_IDLE_ANGRY_CROSSBOW,
ACT_WALK_CROSSBOW,
ACT_RUN_CROSSBOW,
ACT_WALK_AIM_CROSSBOW,
ACT_RUN_AIM_CROSSBOW,
ACT_RANGE_ATTACK_CROSSBOW,
ACT_RELOAD_CROSSBOW,
ACT_RANGE_ATTACK_CROSSBOW_LOW,
ACT_RELOAD_CROSSBOW_LOW,
ACT_COVER_CROSSBOW_LOW,
ACT_RANGE_AIM_CROSSBOW_LOW,
ACT_GESTURE_RANGE_ATTACK_CROSSBOW,
ACT_GESTURE_RELOAD_CROSSBOW,
// Pistol
ACT_IDLE_PISTOL_RELAXED,
ACT_IDLE_PISTOL_STIMULATED,
ACT_WALK_PISTOL_RELAXED,
ACT_WALK_PISTOL_STIMULATED,
ACT_RUN_PISTOL_RELAXED,
ACT_RUN_PISTOL_STIMULATED,
ACT_IDLE_AIM_PISTOL_STIMULATED,
ACT_WALK_AIM_PISTOL_STIMULATED,
ACT_RUN_AIM_PISTOL_STIMULATED,
ACT_WALK_CROUCH_PISTOL,
ACT_WALK_CROUCH_AIM_PISTOL,
ACT_RUN_CROUCH_PISTOL,
ACT_RUN_CROUCH_AIM_PISTOL,
// Shotgun
ACT_IDLE_SHOTGUN,
ACT_WALK_SHOTGUN,
ACT_RUN_SHOTGUN,
ACT_COVER_SHOTGUN_LOW,
ACT_RANGE_AIM_SHOTGUN_LOW,
ACT_WALK_SHOTGUN_RELAXED,
ACT_WALK_SHOTGUN_STIMULATED,
ACT_RUN_SHOTGUN_RELAXED,
ACT_RUN_SHOTGUN_STIMULATED,
ACT_IDLE_AIM_SHOTGUN_STIMULATED,
ACT_WALK_AIM_SHOTGUN_STIMULATED,
ACT_RUN_AIM_SHOTGUN_STIMULATED,
// RPG
ACT_RANGE_AIM_RPG_LOW,
ACT_RANGE_ATTACK_RPG_LOW,
ACT_GESTURE_RANGE_ATTACK_RPG,
// Melee
ACT_WALK_MELEE,
ACT_RUN_MELEE,
// Citizen accessories
ACT_RUN_PACKAGE,
ACT_RUN_SUITCASE,
// Holster/Unholster
ACT_ARM_RIFLE,
ACT_ARM_SHOTGUN,
ACT_ARM_RPG,
ACT_ARM_MELEE,
ACT_DISARM_RIFLE,
ACT_DISARM_SHOTGUN,
ACT_DISARM_RPG,
ACT_DISARM_MELEE,
#endif
#ifdef EXPANDED_NAVIGATION_ACTIVITIES
ACT_CLIMB_ALL, // An actual blend animation which uses pose parameters for direction
ACT_CLIMB_IDLE,
ACT_CLIMB_MOUNT_TOP,
ACT_CLIMB_MOUNT_BOTTOM,
ACT_CLIMB_DISMOUNT_BOTTOM,
#endif
#ifdef EXPANDED_HL2_COVER_ACTIVITIES
// Crouch Cover Medium
ACT_RANGE_ATTACK1_MED,
ACT_RANGE_ATTACK2_MED,
ACT_RANGE_AIM_MED,
ACT_RANGE_ATTACK_AR2_MED,
ACT_RANGE_ATTACK_SMG1_MED,
ACT_RANGE_ATTACK_SHOTGUN_MED,
ACT_RANGE_ATTACK_PISTOL_MED,
ACT_RANGE_ATTACK_RPG_MED,
ACT_RANGE_ATTACK_REVOLVER_MED,
ACT_RANGE_ATTACK_CROSSBOW_MED,
ACT_RANGE_AIM_AR2_MED,
ACT_RANGE_AIM_SMG1_MED,
ACT_RANGE_AIM_SHOTGUN_MED,
ACT_RANGE_AIM_PISTOL_MED,
ACT_RANGE_AIM_RPG_MED,
ACT_RANGE_AIM_REVOLVER_MED,
ACT_RANGE_AIM_CROSSBOW_MED,
// Wall Cover (for use in custom cover hints)
ACT_COVER_WALL_R,
ACT_COVER_WALL_L,
ACT_COVER_WALL_LOW_R,
ACT_COVER_WALL_LOW_L,
ACT_COVER_WALL_R_RIFLE,
ACT_COVER_WALL_L_RIFLE,
ACT_COVER_WALL_LOW_R_RIFLE,
ACT_COVER_WALL_LOW_L_RIFLE,
ACT_COVER_WALL_R_PISTOL,
ACT_COVER_WALL_L_PISTOL,
ACT_COVER_WALL_LOW_R_PISTOL,
ACT_COVER_WALL_LOW_L_PISTOL,
#endif
// this is the end of the global activities, private per-monster activities start here.

View File

@ -1046,7 +1046,13 @@ WeaponClass_t CBaseCombatWeapon::WeaponClassify()
Activity idleact = ActivityOverride(ACT_IDLE_ANGRY, NULL);
switch (idleact)
{
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
case ACT_IDLE_ANGRY_REVOLVER:
#endif
case ACT_IDLE_ANGRY_PISTOL: return WEPCLASS_HANDGUN;
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
case ACT_IDLE_ANGRY_CROSSBOW: // For now, crossbows are rifles
#endif
case ACT_IDLE_ANGRY_SMG1:
case ACT_IDLE_ANGRY_AR2: return WEPCLASS_RIFLE;
case ACT_IDLE_ANGRY_SHOTGUN: return WEPCLASS_SHOTGUN;
@ -1077,6 +1083,11 @@ WeaponClass_t CBaseCombatWeapon::WeaponClassFromString(const char *str)
return WEPCLASS_INVALID;
}
#ifdef HL2_DLL
extern acttable_t *GetSMG1Acttable();
extern int GetSMG1ActtableCount();
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
@ -1084,12 +1095,32 @@ bool CBaseCombatWeapon::SupportsBackupActivity(Activity activity)
{
// Derived classes should override this.
// Pistol and melee users should not use SMG animations for missing pistol activities.
if (WeaponClassify() == WEPCLASS_HANDGUN || IsMeleeWeapon())
#ifdef HL2_DLL
// Melee users should not use SMG animations for missing activities.
if (IsMeleeWeapon() && GetBackupActivityList() == GetSMG1Acttable())
return false;
#endif
return true;
}
acttable_t *CBaseCombatWeapon::GetBackupActivityList()
{
#ifdef HL2_DLL
return GetSMG1Acttable();
#else
return NULL;
#endif
}
int CBaseCombatWeapon::GetBackupActivityListCount()
{
#ifdef HL2_DLL
return GetSMG1ActtableCount();
#else
return 0;
#endif
}
#endif
@ -2343,9 +2374,10 @@ bool CBaseCombatWeapon::Reload( void )
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseCombatWeapon::Reload_NPC( void )
void CBaseCombatWeapon::Reload_NPC( bool bPlaySound )
{
WeaponSound( RELOAD_NPC );
if (bPlaySound)
WeaponSound( RELOAD_NPC );
if (UsesClipsForAmmo1())
{

View File

@ -230,6 +230,8 @@ public:
static WeaponClass_t WeaponClassFromString(const char *str);
virtual bool SupportsBackupActivity(Activity activity);
virtual acttable_t *GetBackupActivityList();
virtual int GetBackupActivityListCount();
#endif
virtual void Equip( CBaseCombatCharacter *pOwner );
@ -319,7 +321,7 @@ public:
bool ReloadsSingly( void ) const;
#ifdef MAPBASE
// Originally created for the crossbow, can be used to add special NPC reloading behavior
virtual void Reload_NPC( void );
virtual void Reload_NPC( bool bPlaySound = true );
#endif
virtual bool AutoFiresFullClip( void ) { return false; }

View File

@ -81,6 +81,11 @@ acttable_t CWeaponStunStick::m_acttable[] =
#endif
{ ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true },
{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, true },
#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES
{ ACT_IDLE, ACT_IDLE_MELEE, false },
{ ACT_RUN, ACT_RUN_MELEE, false },
{ ACT_WALK, ACT_WALK_MELEE, false },
#endif
};
IMPLEMENT_ACTTABLE(CWeaponStunStick);

View File

@ -83,6 +83,12 @@ public:
float GetDamageForActivity( Activity hitActivity );
#ifdef MAPBASE
// Don't use backup activities
acttable_t *GetBackupActivityList() { return NULL; }
int GetBackupActivityListCount() { return 0; }
#endif
CWeaponStunStick( const CWeaponStunStick & );
private:

View File

@ -398,9 +398,10 @@ bool CWeaponCustomScripted::Reload( void )
return BaseClass::Reload();
}
void CWeaponCustomScripted::Reload_NPC( void )
void CWeaponCustomScripted::Reload_NPC( bool bPlaySound )
{
SIMPLE_VOID_OVERRIDE( Reload_NPC, NULL );
ScriptVariant_t pArgs[] = { bPlaySound };
SIMPLE_VOID_OVERRIDE( Reload_NPC, pArgs );
BaseClass::Reload_NPC();
}

View File

@ -77,7 +77,7 @@ public:
void FinishReload( void );
void AbortReload( void );
bool Reload( void );
void Reload_NPC( void );
void Reload_NPC( bool bPlaySound = true );
// Weapon firing
void PrimaryAttack( void ); // do "+ATTACK"