mirror of
https://github.com/mapbase-source/source-sdk-2013.git
synced 2025-02-24 12:41:09 +03:00
Merge branch 'mapbase-source:develop' into develop
This commit is contained in:
commit
f86a967af2
4
.github/workflows/mapbase_build-base.yml
vendored
4
.github/workflows/mapbase_build-base.yml
vendored
@ -194,7 +194,9 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install GCC/G++ multilib
|
- name: Install GCC/G++ multilib
|
||||||
run: sudo apt-get install gcc-multilib g++-multilib
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install gcc-multilib g++-multilib
|
||||||
|
|
||||||
- name: Pick game
|
- name: Pick game
|
||||||
if: inputs.project-group == 'game' || inputs.project-group == 'shaders'
|
if: inputs.project-group == 'game' || inputs.project-group == 'shaders'
|
||||||
|
2
README
2
README
@ -144,6 +144,8 @@ Direct contributions:
|
|||||||
=-- https://github.com/mapbase-source/source-sdk-2013/pull/206 (Fix CScriptNetMsgHelper::WriteEntity())
|
=-- https://github.com/mapbase-source/source-sdk-2013/pull/206 (Fix CScriptNetMsgHelper::WriteEntity())
|
||||||
=-- https://github.com/mapbase-source/source-sdk-2013/pull/213 (VScript HUD visibility control, optimizations for 3D skybox angles/fake worldportals)
|
=-- https://github.com/mapbase-source/source-sdk-2013/pull/213 (VScript HUD visibility control, optimizations for 3D skybox angles/fake worldportals)
|
||||||
=-- https://github.com/mapbase-source/source-sdk-2013/pull/229 (VScript VGUI HUD viewport parenting, game_text and vgui_text_display VScript font fallback)
|
=-- https://github.com/mapbase-source/source-sdk-2013/pull/229 (VScript VGUI HUD viewport parenting, game_text and vgui_text_display VScript font fallback)
|
||||||
|
=-- https://github.com/mapbase-source/source-sdk-2013/pull/261 (Misc VScript additions)
|
||||||
|
=-- https://github.com/mapbase-source/source-sdk-2013/pull/279 (weapon_custom_scripted fixes)
|
||||||
|
|
||||||
== Contributions from z33ky:
|
== Contributions from z33ky:
|
||||||
=-- https://github.com/mapbase-source/source-sdk-2013/pull/21 (Various GCC/Linux compilation fixes)
|
=-- https://github.com/mapbase-source/source-sdk-2013/pull/21 (Various GCC/Linux compilation fixes)
|
||||||
|
@ -4042,6 +4042,92 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#ifdef MAPBASE // From Alien Swarm SDK
|
||||||
|
case AE_CL_STOP_PARTICLE_EFFECT:
|
||||||
|
{
|
||||||
|
char token[256];
|
||||||
|
char szParticleEffect[256];
|
||||||
|
|
||||||
|
// Get the particle effect name
|
||||||
|
const char *p = options;
|
||||||
|
p = nexttoken(token, p, ' ', sizeof(token));
|
||||||
|
if ( token )
|
||||||
|
{
|
||||||
|
Q_strncpy( szParticleEffect, token, sizeof(szParticleEffect) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the attachment point index
|
||||||
|
p = nexttoken(token, p, ' ', sizeof(token));
|
||||||
|
bool bStopInstantly = ( token && !Q_stricmp( token, "instantly" ) );
|
||||||
|
|
||||||
|
ParticleProp()->StopParticlesNamed( szParticleEffect, bStopInstantly );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AE_CL_ADD_PARTICLE_EFFECT_CP:
|
||||||
|
{
|
||||||
|
int iControlPoint = 1;
|
||||||
|
int iAttachment = -1;
|
||||||
|
int iAttachType = PATTACH_ABSORIGIN_FOLLOW;
|
||||||
|
int iEffectIndex = -1;
|
||||||
|
char token[256];
|
||||||
|
char szParticleEffect[256];
|
||||||
|
|
||||||
|
// Get the particle effect name
|
||||||
|
const char *p = options;
|
||||||
|
p = nexttoken(token, p, ' ', sizeof(token));
|
||||||
|
if ( token )
|
||||||
|
{
|
||||||
|
Q_strncpy( szParticleEffect, token, sizeof(szParticleEffect) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the control point number
|
||||||
|
p = nexttoken(token, p, ' ', sizeof(token));
|
||||||
|
if ( token )
|
||||||
|
{
|
||||||
|
iControlPoint = atoi( token );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the attachment type
|
||||||
|
p = nexttoken(token, p, ' ', sizeof(token));
|
||||||
|
if ( token )
|
||||||
|
{
|
||||||
|
iAttachType = GetAttachTypeFromString( token );
|
||||||
|
if ( iAttachType == -1 )
|
||||||
|
{
|
||||||
|
Warning("Invalid attach type specified for particle effect anim event. Trying to spawn effect '%s' with attach type of '%s'\n", szParticleEffect, token );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the attachment point index
|
||||||
|
p = nexttoken(token, p, ' ', sizeof(token));
|
||||||
|
if ( token )
|
||||||
|
{
|
||||||
|
iAttachment = atoi(token);
|
||||||
|
|
||||||
|
// See if we can find any attachment points matching the name
|
||||||
|
if ( token[0] != '0' && iAttachment == 0 )
|
||||||
|
{
|
||||||
|
iAttachment = LookupAttachment( token );
|
||||||
|
if ( iAttachment == -1 )
|
||||||
|
{
|
||||||
|
Warning("Failed to find attachment point specified for particle effect anim event. Trying to spawn effect '%s' on attachment named '%s'\n", szParticleEffect, token );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iEffectIndex = ParticleProp()->FindEffect( szParticleEffect );
|
||||||
|
if ( iEffectIndex == -1 )
|
||||||
|
{
|
||||||
|
Warning("Failed to find specified particle effect. Trying to add CP to '%s' on attachment named '%s'\n", szParticleEffect, token );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ParticleProp()->AddControlPoint( iEffectIndex, iControlPoint, this, (ParticleAttachment_t)iAttachType, iAttachment );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case AE_CL_PLAYSOUND:
|
case AE_CL_PLAYSOUND:
|
||||||
{
|
{
|
||||||
CLocalPlayerFilter filter;
|
CLocalPlayerFilter filter;
|
||||||
@ -4291,6 +4377,22 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
case AE_VSCRIPT_RUN:
|
||||||
|
{
|
||||||
|
if (!RunScript( options ))
|
||||||
|
Warning( "%s failed to run AE_VSCRIPT_RUN on client with \"%s\"\n", GetDebugName(), options );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AE_VSCRIPT_RUN_FILE:
|
||||||
|
{
|
||||||
|
if (!RunScriptFile( options ))
|
||||||
|
Warning( "%s failed to run AE_VSCRIPT_RUN_FILE on client with \"%s\"\n", GetDebugName(), options );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1477,6 +1477,11 @@ public:
|
|||||||
{
|
{
|
||||||
ivgui()->AddTickSignal( this->GetVPanel(), i );
|
ivgui()->AddTickSignal( this->GetVPanel(), i );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RemoveTickSignal()
|
||||||
|
{
|
||||||
|
ivgui()->RemoveTickSignal( this->GetVPanel() );
|
||||||
|
}
|
||||||
#if SCRIPT_VGUI_SIGNAL_INTERFACE
|
#if SCRIPT_VGUI_SIGNAL_INTERFACE
|
||||||
void AddActionSignalTarget( HSCRIPT messageTarget )
|
void AddActionSignalTarget( HSCRIPT messageTarget )
|
||||||
{
|
{
|
||||||
@ -1867,6 +1872,7 @@ public:
|
|||||||
DEFINE_SCRIPTFUNC( MakeReadyForUse, "" )\
|
DEFINE_SCRIPTFUNC( MakeReadyForUse, "" )\
|
||||||
DEFINE_SCRIPTFUNC( GetName, "" )\
|
DEFINE_SCRIPTFUNC( GetName, "" )\
|
||||||
DEFINE_SCRIPTFUNC( AddTickSignal, "" )\
|
DEFINE_SCRIPTFUNC( AddTickSignal, "" )\
|
||||||
|
DEFINE_SCRIPTFUNC( RemoveTickSignal, "" )\
|
||||||
\
|
\
|
||||||
DEFINE_SCRIPTFUNC( GetParent, "" )\
|
DEFINE_SCRIPTFUNC( GetParent, "" )\
|
||||||
DEFINE_SCRIPTFUNC( SetParent, "" )\
|
DEFINE_SCRIPTFUNC( SetParent, "" )\
|
||||||
|
@ -76,6 +76,23 @@ int CAI_BaseNPC::GetActivityID(const char* actName)
|
|||||||
return m_pActivitySR->GetStringID(actName);
|
return m_pActivitySR->GetStringID(actName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Purpose: Gets an activity ID or registers a new private one if it doesn't exist
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
int CAI_BaseNPC::GetOrRegisterActivity( const char *actName )
|
||||||
|
{
|
||||||
|
int actID = GetActivityID( actName );
|
||||||
|
if (actID == ACT_INVALID)
|
||||||
|
{
|
||||||
|
actID = ActivityList_RegisterPrivateActivity( actName );
|
||||||
|
AddActivityToSR( actName, actID );
|
||||||
|
}
|
||||||
|
|
||||||
|
return actID;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#define ADD_ACTIVITY_TO_SR(activityname) AddActivityToSR(#activityname,activityname)
|
#define ADD_ACTIVITY_TO_SR(activityname) AddActivityToSR(#activityname,activityname)
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -671,13 +671,27 @@ void CAI_BaseNPC::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bo
|
|||||||
{
|
{
|
||||||
BaseClass::Ignite( flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner );
|
BaseClass::Ignite( flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner );
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Alyx's enemy ignited code from below can now be run on any NPC as long as
|
||||||
|
// it's our current enemy.
|
||||||
|
if ( GetEnemy() && GetEnemy()->IsNPC() )
|
||||||
|
{
|
||||||
|
GetEnemy()->MyNPCPointer()->EnemyIgnited( this );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HL2_EPISODIC
|
#ifdef HL2_EPISODIC
|
||||||
CBasePlayer *pPlayer = AI_GetSinglePlayer();
|
CBasePlayer *pPlayer = AI_GetSinglePlayer();
|
||||||
if ( pPlayer && pPlayer->IRelationType( this ) != D_LI )
|
if ( pPlayer && pPlayer->IRelationType( this ) != D_LI )
|
||||||
{
|
{
|
||||||
CNPC_Alyx *alyx = CNPC_Alyx::GetAlyx();
|
CNPC_Alyx *alyx = CNPC_Alyx::GetAlyx();
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Alyx's code continues to run if Alyx was not this NPC's enemy.
|
||||||
|
if ( alyx && alyx != GetEnemy() )
|
||||||
|
#else
|
||||||
if ( alyx )
|
if ( alyx )
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
alyx->EnemyIgnited( this );
|
alyx->EnemyIgnited( this );
|
||||||
}
|
}
|
||||||
@ -3026,6 +3040,10 @@ void CAI_BaseNPC::PopulatePoseParameters( void )
|
|||||||
m_poseAim_Yaw = LookupPoseParameter( "aim_yaw" );
|
m_poseAim_Yaw = LookupPoseParameter( "aim_yaw" );
|
||||||
m_poseMove_Yaw = LookupPoseParameter( "move_yaw" );
|
m_poseMove_Yaw = LookupPoseParameter( "move_yaw" );
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
m_poseInteractionRelativeYaw = LookupPoseParameter( "interaction_relative_yaw" );
|
||||||
|
#endif
|
||||||
|
|
||||||
BaseClass::PopulatePoseParameters();
|
BaseClass::PopulatePoseParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9526,6 +9544,12 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent )
|
|||||||
{
|
{
|
||||||
m_hCine->FireScriptEvent( atoi( pEvent->options ) );
|
m_hCine->FireScriptEvent( atoi( pEvent->options ) );
|
||||||
}
|
}
|
||||||
|
#ifdef MAPBASE
|
||||||
|
else if ( GetHintNode() )
|
||||||
|
{
|
||||||
|
GetHintNode()->FireScriptEvent( atoi( pEvent->options ) );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// FIXME: look so see if it's playing a vcd and fire those instead
|
// FIXME: look so see if it's playing a vcd and fire those instead
|
||||||
@ -12333,6 +12357,7 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC
|
|||||||
|
|
||||||
DEFINE_SCRIPTFUNC( IsCommandable, "Check if the NPC is commandable." )
|
DEFINE_SCRIPTFUNC( IsCommandable, "Check if the NPC is commandable." )
|
||||||
DEFINE_SCRIPTFUNC( IsInPlayerSquad, "Check if the NPC is in the player's squad." )
|
DEFINE_SCRIPTFUNC( IsInPlayerSquad, "Check if the NPC is in the player's squad." )
|
||||||
|
DEFINE_SCRIPTFUNC( IsMedic, "Returns true if this NPC is a medic." )
|
||||||
|
|
||||||
DEFINE_SCRIPTFUNC_NAMED( VScriptGetCine, "GetCine", "Get the NPC's currently running scripted sequence if it has one." )
|
DEFINE_SCRIPTFUNC_NAMED( VScriptGetCine, "GetCine", "Get the NPC's currently running scripted sequence if it has one." )
|
||||||
DEFINE_SCRIPTFUNC( GetScriptState, "Get the NPC's current scripted sequence state." )
|
DEFINE_SCRIPTFUNC( GetScriptState, "Get the NPC's current scripted sequence state." )
|
||||||
@ -12443,7 +12468,11 @@ BEGIN_SIMPLE_DATADESC( ScriptedNPCInteraction_t )
|
|||||||
DEFINE_FIELD( bValidOnCurrentEnemy, FIELD_BOOLEAN ),
|
DEFINE_FIELD( bValidOnCurrentEnemy, FIELD_BOOLEAN ),
|
||||||
DEFINE_FIELD( flNextAttemptTime, FIELD_TIME ),
|
DEFINE_FIELD( flNextAttemptTime, FIELD_TIME ),
|
||||||
#ifdef MAPBASE
|
#ifdef MAPBASE
|
||||||
DEFINE_FIELD( MiscCriteria, FIELD_STRING ),//DEFINE_UTLVECTOR( MiscCriteria, FIELD_EMBEDDED ),
|
DEFINE_EMBEDDED_ARRAY( sTheirPhases, SNPCINT_NUM_PHASES ),
|
||||||
|
DEFINE_FIELD( bHasSeparateSequenceNames, FIELD_BOOLEAN ),
|
||||||
|
DEFINE_FIELD( flMaxAngleDiff, FIELD_FLOAT ),
|
||||||
|
DEFINE_FIELD( iszRelatedInteractions, FIELD_STRING ),
|
||||||
|
DEFINE_FIELD( MiscCriteria, FIELD_STRING ),
|
||||||
#endif
|
#endif
|
||||||
END_DATADESC()
|
END_DATADESC()
|
||||||
|
|
||||||
@ -13507,6 +13536,10 @@ bool CAI_BaseNPC::CineCleanup()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear interaction partner, because we're not running a scripted sequence anymore
|
// Clear interaction partner, because we're not running a scripted sequence anymore
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// We need the interaction partner for server ragdoll death cleanup, so don't clear if we're not alive
|
||||||
|
if (IsAlive())
|
||||||
|
#endif
|
||||||
m_hInteractionPartner = NULL;
|
m_hInteractionPartner = NULL;
|
||||||
CleanupForcedInteraction();
|
CleanupForcedInteraction();
|
||||||
}
|
}
|
||||||
@ -14847,32 +14880,66 @@ void CAI_BaseNPC::ParseScriptedNPCInteractions(void)
|
|||||||
else if (!Q_strncmp(szName, "entry_sequence", 14))
|
else if (!Q_strncmp(szName, "entry_sequence", 14))
|
||||||
sInteraction.sPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString(szValue);
|
sInteraction.sPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString(szValue);
|
||||||
else if (!Q_strncmp(szName, "entry_activity", 14))
|
else if (!Q_strncmp(szName, "entry_activity", 14))
|
||||||
sInteraction.sPhases[SNPCINT_ENTRY].iActivity = GetActivityID(szValue);
|
sInteraction.sPhases[SNPCINT_ENTRY].iActivity = GetOrRegisterActivity(szValue);
|
||||||
|
|
||||||
else if (!Q_strncmp(szName, "sequence", 8))
|
else if (!Q_strncmp(szName, "sequence", 8))
|
||||||
sInteraction.sPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString(szValue);
|
sInteraction.sPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString(szValue);
|
||||||
else if (!Q_strncmp(szName, "activity", 8))
|
else if (!Q_strncmp(szName, "activity", 8))
|
||||||
sInteraction.sPhases[SNPCINT_SEQUENCE].iActivity = GetActivityID(szValue);
|
sInteraction.sPhases[SNPCINT_SEQUENCE].iActivity = GetOrRegisterActivity(szValue);
|
||||||
|
|
||||||
else if (!Q_strncmp(szName, "exit_sequence", 13))
|
else if (!Q_strncmp(szName, "exit_sequence", 13))
|
||||||
sInteraction.sPhases[SNPCINT_EXIT].iszSequence = AllocPooledString(szValue);
|
sInteraction.sPhases[SNPCINT_EXIT].iszSequence = AllocPooledString(szValue);
|
||||||
else if (!Q_strncmp(szName, "exit_activity", 13))
|
else if (!Q_strncmp(szName, "exit_activity", 13))
|
||||||
sInteraction.sPhases[SNPCINT_EXIT].iActivity = GetActivityID(szValue);
|
sInteraction.sPhases[SNPCINT_EXIT].iActivity = GetOrRegisterActivity(szValue);
|
||||||
|
|
||||||
|
else if (!Q_strncmp(szName, "their_", 6))
|
||||||
|
{
|
||||||
|
const char *szTheirName = szName + 6;
|
||||||
|
sInteraction.bHasSeparateSequenceNames = true;
|
||||||
|
|
||||||
|
if (!Q_strncmp(szTheirName, "entry_sequence", 14))
|
||||||
|
sInteraction.sTheirPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString(szValue);
|
||||||
|
else if (!Q_strncmp(szTheirName, "entry_activity", 14))
|
||||||
|
sInteraction.sTheirPhases[SNPCINT_ENTRY].iActivity = GetOrRegisterActivity(szValue);
|
||||||
|
|
||||||
|
else if (!Q_strncmp(szTheirName, "sequence", 8))
|
||||||
|
sInteraction.sTheirPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString(szValue);
|
||||||
|
else if (!Q_strncmp(szTheirName, "activity", 8))
|
||||||
|
sInteraction.sTheirPhases[SNPCINT_SEQUENCE].iActivity = GetOrRegisterActivity(szValue);
|
||||||
|
|
||||||
|
else if (!Q_strncmp(szTheirName, "exit_sequence", 13))
|
||||||
|
sInteraction.sTheirPhases[SNPCINT_EXIT].iszSequence = AllocPooledString(szValue);
|
||||||
|
else if (!Q_strncmp(szTheirName, "exit_activity", 13))
|
||||||
|
sInteraction.sTheirPhases[SNPCINT_EXIT].iActivity = GetOrRegisterActivity(szValue);
|
||||||
|
|
||||||
|
// Add anything else to our miscellaneous criteria
|
||||||
|
else
|
||||||
|
{
|
||||||
|
szCriteria = UTIL_VarArgs("%s,%s:%s", szCriteria, szName, szValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else if (!Q_strncmp(szName, "delay", 5))
|
else if (!Q_strncmp(szName, "delay", 5))
|
||||||
sInteraction.flDelay = atof(szValue);
|
sInteraction.flDelay = atof(szValue);
|
||||||
else if (!Q_strncmp(szName, "origin_max_delta", 16))
|
else if (!Q_strncmp(szName, "origin_max_delta", 16))
|
||||||
sInteraction.flDistSqr = atof(szValue);
|
sInteraction.flDistSqr = atof(szValue);
|
||||||
|
else if (!Q_strncmp(szName, "angles_max_diff", 15))
|
||||||
|
sInteraction.flMaxAngleDiff = atof(szValue);
|
||||||
|
|
||||||
else if (!Q_strncmp(szName, "loop_in_action", 14) && !FStrEq(szValue, "0"))
|
else if (!Q_strncmp(szName, "loop_in_action", 14) && !FStrEq(szValue, "0"))
|
||||||
sInteraction.iFlags |= SCNPC_FLAG_LOOP_IN_ACTION;
|
sInteraction.iFlags |= SCNPC_FLAG_LOOP_IN_ACTION;
|
||||||
|
|
||||||
else if (!Q_strncmp(szName, "dont_teleport_at_end", 20))
|
else if (!Q_strncmp(szName, "dont_teleport_at_end", 20))
|
||||||
{
|
{
|
||||||
if (!Q_stricmp(szValue, "me") || !Q_stricmp(szValue, "both"))
|
if (!Q_stricmp(szValue, "me"))
|
||||||
sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_ME;
|
sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_ME;
|
||||||
else if (!Q_stricmp(szValue, "them") || !Q_stricmp(szValue, "both"))
|
else if (!Q_stricmp(szValue, "them"))
|
||||||
sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_THEM;
|
sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_THEM;
|
||||||
|
else if (!Q_stricmp( szValue, "both" ))
|
||||||
|
{
|
||||||
|
sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_ME;
|
||||||
|
sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_THEM;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (!Q_strncmp(szName, "needs_weapon", 12))
|
else if (!Q_strncmp(szName, "needs_weapon", 12))
|
||||||
@ -14899,6 +14966,11 @@ void CAI_BaseNPC::ParseScriptedNPCInteractions(void)
|
|||||||
sInteraction.iszTheirWeapon = AllocPooledString(szValue);
|
sInteraction.iszTheirWeapon = AllocPooledString(szValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (!Q_strncmp(szName, "related_interactions", 20))
|
||||||
|
{
|
||||||
|
sInteraction.iszRelatedInteractions = AllocPooledString(szValue);
|
||||||
|
}
|
||||||
|
|
||||||
// Add anything else to our miscellaneous criteria
|
// Add anything else to our miscellaneous criteria
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -15130,8 +15202,23 @@ void CAI_BaseNPC::AddScriptedNPCInteraction( ScriptedNPCInteraction_t *pInteract
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Purpose:
|
// Purpose:
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
const char *CAI_BaseNPC::GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase )
|
const char *CAI_BaseNPC::GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase, bool bOtherNPC )
|
||||||
{
|
{
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if (bOtherNPC && pInteraction->bHasSeparateSequenceNames)
|
||||||
|
{
|
||||||
|
// Check unique phases
|
||||||
|
if ( pInteraction->sTheirPhases[iPhase].iActivity != ACT_INVALID )
|
||||||
|
{
|
||||||
|
int iSequence = SelectWeightedSequence( (Activity)pInteraction->sTheirPhases[iPhase].iActivity );
|
||||||
|
return GetSequenceName( iSequence );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( pInteraction->sTheirPhases[iPhase].iszSequence != NULL_STRING )
|
||||||
|
return STRING(pInteraction->sTheirPhases[iPhase].iszSequence);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if ( pInteraction->sPhases[iPhase].iActivity != ACT_INVALID )
|
if ( pInteraction->sPhases[iPhase].iActivity != ACT_INVALID )
|
||||||
{
|
{
|
||||||
int iSequence = SelectWeightedSequence( (Activity)pInteraction->sPhases[iPhase].iActivity );
|
int iSequence = SelectWeightedSequence( (Activity)pInteraction->sPhases[iPhase].iActivity );
|
||||||
@ -15225,6 +15312,37 @@ void CAI_BaseNPC::StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedN
|
|||||||
|
|
||||||
// Setup next attempt
|
// Setup next attempt
|
||||||
pInteraction->flNextAttemptTime = gpGlobals->curtime + pInteraction->flDelay + RandomFloat(-2,2);
|
pInteraction->flNextAttemptTime = gpGlobals->curtime + pInteraction->flDelay + RandomFloat(-2,2);
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if (pInteraction->iszRelatedInteractions != NULL_STRING)
|
||||||
|
{
|
||||||
|
// Delay related interactions as well
|
||||||
|
char szRelatedInteractions[256];
|
||||||
|
Q_strncpy( szRelatedInteractions, STRING( pInteraction->iszRelatedInteractions ), sizeof( szRelatedInteractions ) );
|
||||||
|
|
||||||
|
char *pszInteraction = strtok( szRelatedInteractions, "," );
|
||||||
|
while (pszInteraction)
|
||||||
|
{
|
||||||
|
bool bWildCard = Matcher_ContainsWildcard( pszInteraction );
|
||||||
|
|
||||||
|
for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ )
|
||||||
|
{
|
||||||
|
ScriptedNPCInteraction_t *pOtherInteraction = &m_ScriptedInteractions[i];
|
||||||
|
|
||||||
|
if ( Matcher_NamesMatch( pszInteraction, STRING( pOtherInteraction->iszInteractionName ) ) && pOtherInteraction != pInteraction )
|
||||||
|
{
|
||||||
|
if (pOtherInteraction->flNextAttemptTime < pInteraction->flNextAttemptTime)
|
||||||
|
pOtherInteraction->flNextAttemptTime = pInteraction->flNextAttemptTime;
|
||||||
|
|
||||||
|
// Not looking for multiple
|
||||||
|
if (!bWildCard)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pszInteraction = strtok( NULL, "," );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Spawn a scripted sequence for this NPC to play the interaction anim
|
// Spawn a scripted sequence for this NPC to play the interaction anim
|
||||||
CAI_ScriptedSequence *pMySequence = (CAI_ScriptedSequence*)CreateEntityByName( "scripted_sequence" );
|
CAI_ScriptedSequence *pMySequence = (CAI_ScriptedSequence*)CreateEntityByName( "scripted_sequence" );
|
||||||
@ -15256,6 +15374,15 @@ void CAI_BaseNPC::StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedN
|
|||||||
CAI_ScriptedSequence *pTheirSequence = NULL;
|
CAI_ScriptedSequence *pTheirSequence = NULL;
|
||||||
if ( pOtherNPC )
|
if ( pOtherNPC )
|
||||||
{
|
{
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if (pInteraction->bHasSeparateSequenceNames)
|
||||||
|
{
|
||||||
|
pszEntrySequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_ENTRY, true );
|
||||||
|
pszSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_SEQUENCE, true );
|
||||||
|
pszExitSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_EXIT, true );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
pTheirSequence = (CAI_ScriptedSequence*)CreateEntityByName( "scripted_sequence" );
|
pTheirSequence = (CAI_ScriptedSequence*)CreateEntityByName( "scripted_sequence" );
|
||||||
pTheirSequence->KeyValue( "m_iszEntry", pszEntrySequence );
|
pTheirSequence->KeyValue( "m_iszEntry", pszEntrySequence );
|
||||||
pTheirSequence->KeyValue( "m_iszPlay", pszSequence );
|
pTheirSequence->KeyValue( "m_iszPlay", pszSequence );
|
||||||
@ -15279,6 +15406,26 @@ void CAI_BaseNPC::StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedN
|
|||||||
|
|
||||||
// Tell their sequence to keep their position relative to me
|
// Tell their sequence to keep their position relative to me
|
||||||
pTheirSequence->SetupInteractionPosition( this, pInteraction->matDesiredLocalToWorld );
|
pTheirSequence->SetupInteractionPosition( this, pInteraction->matDesiredLocalToWorld );
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if ( !(pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_ANGLES) )
|
||||||
|
{
|
||||||
|
// Set up interaction yaw pose if it exists
|
||||||
|
float flYaw = AngleDistance( angDesired.y, angOtherAngles.y );
|
||||||
|
|
||||||
|
int nInteractionPose = LookupPoseInteractionRelativeYaw();
|
||||||
|
if (nInteractionPose > -1)
|
||||||
|
{
|
||||||
|
SetPoseParameter( nInteractionPose, flYaw );
|
||||||
|
}
|
||||||
|
|
||||||
|
nInteractionPose = pOtherNPC->LookupPoseInteractionRelativeYaw();
|
||||||
|
if (nInteractionPose > -1)
|
||||||
|
{
|
||||||
|
pOtherNPC->SetPoseParameter( nInteractionPose, flYaw );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spawn both sequences at once
|
// Spawn both sequences at once
|
||||||
@ -15367,7 +15514,7 @@ bool CAI_BaseNPC::CanRunAScriptedNPCInteraction( bool bForced )
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Default AI prevents interactions while melee attacking, but not ranged attacking
|
// Default AI prevents interactions while melee attacking, but not ranged attacking
|
||||||
if ( IsCurSchedule( SCHED_MELEE_ATTACK1 ) || IsCurSchedule( SCHED_MELEE_ATTACK2 ) )
|
if ( ( IsCurSchedule( SCHED_MELEE_ATTACK1 ) || IsCurSchedule( SCHED_MELEE_ATTACK2 ) ) && !CanStartDynamicInteractionDuringMelee() )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15556,8 +15703,14 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void )
|
|||||||
|
|
||||||
if (bSame)
|
if (bSame)
|
||||||
continue;
|
continue;
|
||||||
#endif
|
|
||||||
|
|
||||||
|
// Resolve the activity or sequence, and make sure our enemy has it
|
||||||
|
const char *pszSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_SEQUENCE, true );
|
||||||
|
if ( !pszSequence )
|
||||||
|
continue;
|
||||||
|
if ( pNPC->LookupSequence( pszSequence ) == -1 )
|
||||||
|
continue;
|
||||||
|
#else
|
||||||
// Use sequence? or activity?
|
// Use sequence? or activity?
|
||||||
if ( pInteraction->sPhases[SNPCINT_SEQUENCE].iActivity != ACT_INVALID )
|
if ( pInteraction->sPhases[SNPCINT_SEQUENCE].iActivity != ACT_INVALID )
|
||||||
{
|
{
|
||||||
@ -15573,53 +15726,6 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void )
|
|||||||
if ( pNPC->LookupSequence( STRING(pInteraction->sPhases[SNPCINT_SEQUENCE].iszSequence) ) == -1 )
|
if ( pNPC->LookupSequence( STRING(pInteraction->sPhases[SNPCINT_SEQUENCE].iszSequence) ) == -1 )
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MAPBASE
|
|
||||||
if (pInteraction->MiscCriteria != NULL_STRING)
|
|
||||||
{
|
|
||||||
// Test against response system criteria
|
|
||||||
AI_CriteriaSet set;
|
|
||||||
ModifyOrAppendCriteria( set );
|
|
||||||
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
|
|
||||||
if( pPlayer )
|
|
||||||
pPlayer->ModifyOrAppendPlayerCriteria( set );
|
|
||||||
ReAppendContextCriteria( set );
|
|
||||||
|
|
||||||
DevMsg("Testing %s misc criteria\n", STRING(pInteraction->MiscCriteria));
|
|
||||||
|
|
||||||
int index;
|
|
||||||
const char *criteriavalue;
|
|
||||||
char key[128];
|
|
||||||
char value[128];
|
|
||||||
const char *p = STRING(pInteraction->MiscCriteria);
|
|
||||||
while ( p )
|
|
||||||
{
|
|
||||||
#ifdef NEW_RESPONSE_SYSTEM
|
|
||||||
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL, STRING(pInteraction->MiscCriteria) );
|
|
||||||
#else
|
|
||||||
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
index = set.FindCriterionIndex(key);
|
|
||||||
if (index != -1)
|
|
||||||
{
|
|
||||||
criteriavalue = set.GetValue(index);
|
|
||||||
if (!Matcher_Match(value, criteriavalue))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Test with empty string in case our criteria is != or something
|
|
||||||
criteriavalue = "";
|
|
||||||
if (!Matcher_Match(value, criteriavalue))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pInteraction->bValidOnCurrentEnemy = true;
|
pInteraction->bValidOnCurrentEnemy = true;
|
||||||
@ -15789,7 +15895,95 @@ bool CAI_BaseNPC::InteractionIsAllowed( CAI_BaseNPC *pOtherNPC, ScriptedNPCInter
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
// m_iDynamicInteractionsAllowed == TRS_FALSE case is already handled in CanRunAScriptedNPCInteraction().
|
// m_iDynamicInteractionsAllowed == TRS_FALSE case is already handled in CanRunAScriptedNPCInteraction().
|
||||||
return !(pInteraction->iFlags & SCNPC_FLAG_MAPBASE_ADDITION && m_iDynamicInteractionsAllowed == TRS_NONE);
|
if (pInteraction->iFlags & SCNPC_FLAG_MAPBASE_ADDITION && m_iDynamicInteractionsAllowed == TRS_NONE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Test misc. criteria here since some of it may not have been valid on initial calculation, but could be now
|
||||||
|
if (pInteraction->MiscCriteria != NULL_STRING)
|
||||||
|
{
|
||||||
|
// Test against response system criteria
|
||||||
|
AI_CriteriaSet set;
|
||||||
|
ModifyOrAppendCriteria( set );
|
||||||
|
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
|
||||||
|
if (pPlayer)
|
||||||
|
pPlayer->ModifyOrAppendPlayerCriteria( set );
|
||||||
|
|
||||||
|
// Get criteria from target if we want it
|
||||||
|
if ( V_strstr( STRING( pInteraction->MiscCriteria ), "their_" ) )
|
||||||
|
{
|
||||||
|
// Currently, in order to get everything which might be desired, we call the other NPC's ModifyOrAppendCriteria.
|
||||||
|
// We put it in a separate criteria set, then assign a prefix and append it to the main set, similar to how contexts are appended.
|
||||||
|
// This includes a few global criterions which we might not need, so we throw them out before they're merged.
|
||||||
|
// This isn't a very efficient solution, but there are no better options available without rewriting parts of the response criteria system.
|
||||||
|
AI_CriteriaSet theirSet;
|
||||||
|
pOtherNPC->ModifyOrAppendCriteria( theirSet );
|
||||||
|
|
||||||
|
set.EnsureCapacity( (theirSet.GetCount()-2) + set.GetCount() ); // We know we'll be throwing out 2 global criterions
|
||||||
|
|
||||||
|
char sz[ 128 ];
|
||||||
|
for ( int i = 0; i < theirSet.GetCount(); i++ )
|
||||||
|
{
|
||||||
|
const char *name = theirSet.GetName( i );
|
||||||
|
const char *value = theirSet.GetValue( i );
|
||||||
|
|
||||||
|
if (FStrEq( name, "map" ) || FStrEq( name, "episodic" ) || FStrEq( name, "is_console" )
|
||||||
|
|| FStrEq( name, "month" ) || FStrEq( name, "day" )
|
||||||
|
|| FStrEq( name, "is_console" ) || FStrEq( name, "is_pc" )
|
||||||
|
|| V_strnicmp( name, "world", 5 ) == 0)
|
||||||
|
{
|
||||||
|
// Global criterion, ignore
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_snprintf( sz, sizeof( sz ), "their_%s", name );
|
||||||
|
|
||||||
|
if (ai_debug_dyninteractions.GetInt() == 3)
|
||||||
|
Msg( "%i: %s -> %s:%s\n", i, name, sz, value );
|
||||||
|
|
||||||
|
set.AppendCriteria( sz, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append this afterwards because it has its own prefix system
|
||||||
|
pOtherNPC->AppendContextToCriteria( set, "their_" );
|
||||||
|
}
|
||||||
|
|
||||||
|
ReAppendContextCriteria( set );
|
||||||
|
|
||||||
|
int index;
|
||||||
|
const char *criteriavalue;
|
||||||
|
char key[128];
|
||||||
|
char value[128];
|
||||||
|
const char *p = STRING( pInteraction->MiscCriteria );
|
||||||
|
while ( p )
|
||||||
|
{
|
||||||
|
#ifdef NEW_RESPONSE_SYSTEM
|
||||||
|
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL, STRING( pInteraction->MiscCriteria ) );
|
||||||
|
#else
|
||||||
|
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
index = set.FindCriterionIndex( key );
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
criteriavalue = set.GetValue( index );
|
||||||
|
if (!Matcher_Match( value, criteriavalue ))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Test with empty string in case our criteria is != or something
|
||||||
|
criteriavalue = "";
|
||||||
|
if (!Matcher_Match( value, criteriavalue ))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -15827,6 +16021,11 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte
|
|||||||
{
|
{
|
||||||
Msg(" %s distsqr: %0.2f (%0.2f %0.2f %0.2f), desired: <%0.2f (%0.2f %0.2f %0.2f)\n", GetDebugName(), flDistSqr,
|
Msg(" %s distsqr: %0.2f (%0.2f %0.2f %0.2f), desired: <%0.2f (%0.2f %0.2f %0.2f)\n", GetDebugName(), flDistSqr,
|
||||||
pOtherNPC->GetAbsOrigin().x, pOtherNPC->GetAbsOrigin().y, pOtherNPC->GetAbsOrigin().z, pInteraction->flDistSqr, vecOrigin.x, vecOrigin.y, vecOrigin.z );
|
pOtherNPC->GetAbsOrigin().x, pOtherNPC->GetAbsOrigin().y, pOtherNPC->GetAbsOrigin().z, pInteraction->flDistSqr, vecOrigin.x, vecOrigin.y, vecOrigin.z );
|
||||||
|
#ifdef MAPBASE
|
||||||
|
Vector vecForward, vecRight;
|
||||||
|
GetVectors( &vecForward, &vecRight, NULL );
|
||||||
|
NDebugOverlay::Circle( vecOrigin + Vector(0,0,2), vecForward, vecRight, FastSqrt(pInteraction->flDistSqr), 255, 0, 0, 255, true, 0.1f );
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -15839,6 +16038,11 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte
|
|||||||
Msg(" %s is at: %0.2f %0.2f %0.2f\n", GetDebugName(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z );
|
Msg(" %s is at: %0.2f %0.2f %0.2f\n", GetDebugName(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z );
|
||||||
Msg(" %s distsqr: %0.2f (%0.2f %0.2f %0.2f), desired: (%0.2f %0.2f %0.2f)\n", GetDebugName(), flDistSqr,
|
Msg(" %s distsqr: %0.2f (%0.2f %0.2f %0.2f), desired: (%0.2f %0.2f %0.2f)\n", GetDebugName(), flDistSqr,
|
||||||
pOtherNPC->GetAbsOrigin().x, pOtherNPC->GetAbsOrigin().y, pOtherNPC->GetAbsOrigin().z, vecOrigin.x, vecOrigin.y, vecOrigin.z );
|
pOtherNPC->GetAbsOrigin().x, pOtherNPC->GetAbsOrigin().y, pOtherNPC->GetAbsOrigin().z, vecOrigin.x, vecOrigin.y, vecOrigin.z );
|
||||||
|
#ifdef MAPBASE
|
||||||
|
Vector vecForward, vecRight;
|
||||||
|
GetVectors( &vecForward, &vecRight, NULL );
|
||||||
|
NDebugOverlay::Circle( vecOrigin + Vector( 0, 0, 2 ), vecForward, vecRight, FastSqrt( pInteraction->flDistSqr ), 255, 0, 0, 255, true, 0.1f );
|
||||||
|
#endif
|
||||||
|
|
||||||
if ( pOtherNPC )
|
if ( pOtherNPC )
|
||||||
{
|
{
|
||||||
@ -15855,14 +16059,28 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte
|
|||||||
for ( int ang = 0; ang < 3; ang++ )
|
for ( int ang = 0; ang < 3; ang++ )
|
||||||
{
|
{
|
||||||
float flAngDiff = AngleDiff( angEnemyAngles[ang], angAngles[ang] );
|
float flAngDiff = AngleDiff( angEnemyAngles[ang], angAngles[ang] );
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if ( fabs(flAngDiff) > pInteraction->flMaxAngleDiff )
|
||||||
|
#else
|
||||||
if ( fabs(flAngDiff) > DSS_MAX_ANGLE_DIFF )
|
if ( fabs(flAngDiff) > DSS_MAX_ANGLE_DIFF )
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
bMatches = false;
|
bMatches = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( !bMatches )
|
if ( !bMatches )
|
||||||
|
{
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if ( bDebug )
|
||||||
|
{
|
||||||
|
Msg(" %s angle not matched: (%0.2f %0.2f %0.2f), desired (%0.2f, %0.2f, %0.2f)\n", GetDebugName(),
|
||||||
|
anglemod(angEnemyAngles.x), anglemod(angEnemyAngles.y), anglemod(angEnemyAngles.z), anglemod(angAngles.x), anglemod(angAngles.y), anglemod(angAngles.z) );
|
||||||
|
Msg(" diff: (%0.2f, %0.2f, %0.2f)\n", AngleDiff( angEnemyAngles.x, angAngles.x ), AngleDiff( angEnemyAngles.y, angAngles.y ), AngleDiff( angEnemyAngles.z, angAngles.z ) );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ( bDebug )
|
if ( bDebug )
|
||||||
{
|
{
|
||||||
@ -15870,6 +16088,13 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte
|
|||||||
anglemod(angEnemyAngles.x), anglemod(angEnemyAngles.y), anglemod(angEnemyAngles.z), anglemod(angAngles.x), anglemod(angAngles.y), anglemod(angAngles.z) );
|
anglemod(angEnemyAngles.x), anglemod(angEnemyAngles.y), anglemod(angEnemyAngles.z), anglemod(angAngles.x), anglemod(angAngles.y), anglemod(angAngles.z) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef MAPBASE
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If we're not using angles, then use the NPC's current angles
|
||||||
|
angAngles = pOtherNPC->GetAbsAngles();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// TODO: Velocity check, if we're supposed to
|
// TODO: Velocity check, if we're supposed to
|
||||||
if ( pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_VELOCITY )
|
if ( pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_VELOCITY )
|
||||||
@ -15949,6 +16174,7 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte
|
|||||||
if ( bDebug )
|
if ( bDebug )
|
||||||
{
|
{
|
||||||
NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 255,0,0, 100, 1.0 );
|
NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 255,0,0, 100, 1.0 );
|
||||||
|
NDebugOverlay::HorzArrow( GetAbsOrigin(), vecPos, 16.0f, 255, 0, 0, 255, true, 1.0f );
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -15956,7 +16182,39 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte
|
|||||||
{
|
{
|
||||||
//NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 0,255,0, 100, 1.0 );
|
//NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 0,255,0, 100, 1.0 );
|
||||||
|
|
||||||
NDebugOverlay::Axis( vecPos, angAngles, 20, true, 10.0 );
|
NDebugOverlay::Axis( vecPos, angAngles, 20, true, 1.0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Instead, make sure we fit into where the sequence movement ends at
|
||||||
|
const char *pszSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_SEQUENCE );
|
||||||
|
int nSeq = LookupSequence( pszSequence );
|
||||||
|
if ( pszSequence && nSeq != -1 )
|
||||||
|
{
|
||||||
|
Vector vecDeltaPos;
|
||||||
|
QAngle angDeltaAngles;
|
||||||
|
GetSequenceMovement( nSeq, 0.0f, 1.0f, vecDeltaPos, angDeltaAngles );
|
||||||
|
if (!vecDeltaPos.IsZero())
|
||||||
|
{
|
||||||
|
QAngle angInteraction = GetAbsAngles();
|
||||||
|
angInteraction[YAW] = m_flInteractionYaw;
|
||||||
|
|
||||||
|
Vector vecPos;
|
||||||
|
VectorRotate( vecDeltaPos, angInteraction, vecPos );
|
||||||
|
vecPos += GetAbsOrigin();
|
||||||
|
|
||||||
|
AI_TraceHull( vecPos, vecPos, GetHullMins(), GetHullMaxs(), MASK_SOLID, &traceFilter, &tr);
|
||||||
|
if ( tr.fraction != 1.0 )
|
||||||
|
{
|
||||||
|
if ( bDebug )
|
||||||
|
{
|
||||||
|
NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 255,0,0, 100, 1.0 );
|
||||||
|
NDebugOverlay::HorzArrow( GetAbsOrigin(), vecPos, 16.0f, 255, 0, 0, 255, true, 1.0f );
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -15980,6 +16238,25 @@ bool CAI_BaseNPC::HasInteractionCantDie( void )
|
|||||||
return ( m_bCannotDieDuringInteraction && IsRunningDynamicInteraction() );
|
return ( m_bCannotDieDuringInteraction && IsRunningDynamicInteraction() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Purpose: Return true if this NPC has valid interactions on the current enemy.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
bool CAI_BaseNPC::HasValidInteractionsOnCurrentEnemy( void )
|
||||||
|
{
|
||||||
|
if ( !GetEnemy() || !GetEnemy()->IsNPC() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ )
|
||||||
|
{
|
||||||
|
ScriptedNPCInteraction_t *pInteraction = &m_ScriptedInteractions[i];
|
||||||
|
|
||||||
|
if ( pInteraction->bValidOnCurrentEnemy )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Purpose:
|
// Purpose:
|
||||||
// Input : &inputdata -
|
// Input : &inputdata -
|
||||||
@ -16213,6 +16490,21 @@ void CAI_BaseNPC::ModifyOrAppendEnemyCriteria( AI_CriteriaSet& set, CBaseEntity
|
|||||||
set.AppendCriteria( "enemyclass", g_pGameRules->AIClassText( pEnemy->Classify() ) ); // UTIL_VarArgs("%i", pEnemy->Classify())
|
set.AppendCriteria( "enemyclass", g_pGameRules->AIClassText( pEnemy->Classify() ) ); // UTIL_VarArgs("%i", pEnemy->Classify())
|
||||||
set.AppendCriteria( "distancetoenemy", UTIL_VarArgs( "%f", EnemyDistance(pEnemy) ) );
|
set.AppendCriteria( "distancetoenemy", UTIL_VarArgs( "%f", EnemyDistance(pEnemy) ) );
|
||||||
set.AppendCriteria( "timesincecombat", "-1" );
|
set.AppendCriteria( "timesincecombat", "-1" );
|
||||||
|
|
||||||
|
CAI_BaseNPC *pNPC = pEnemy->MyNPCPointer();
|
||||||
|
if (pNPC)
|
||||||
|
{
|
||||||
|
set.AppendCriteria("enemy_is_npc", "1");
|
||||||
|
|
||||||
|
set.AppendCriteria( "enemy_activity", CAI_BaseNPC::GetActivityName( pNPC->GetActivity() ) );
|
||||||
|
set.AppendCriteria( "enemy_weapon", pNPC->GetActiveWeapon() ? pNPC->GetActiveWeapon()->GetClassname() : "0" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
set.AppendCriteria("enemy_is_npc", "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
pEnemy->AppendContextToCriteria( set, "enemy_" );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -425,6 +425,9 @@ struct ScriptedNPCInteraction_t
|
|||||||
iszTheirWeapon = NULL_STRING;
|
iszTheirWeapon = NULL_STRING;
|
||||||
#ifdef MAPBASE
|
#ifdef MAPBASE
|
||||||
vecRelativeEndPos = vec3_origin;
|
vecRelativeEndPos = vec3_origin;
|
||||||
|
bHasSeparateSequenceNames = false;
|
||||||
|
flMaxAngleDiff = DSS_MAX_ANGLE_DIFF;
|
||||||
|
iszRelatedInteractions = NULL_STRING;
|
||||||
MiscCriteria = NULL_STRING;
|
MiscCriteria = NULL_STRING;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -432,6 +435,10 @@ struct ScriptedNPCInteraction_t
|
|||||||
{
|
{
|
||||||
sPhases[i].iszSequence = NULL_STRING;
|
sPhases[i].iszSequence = NULL_STRING;
|
||||||
sPhases[i].iActivity = ACT_INVALID;
|
sPhases[i].iActivity = ACT_INVALID;
|
||||||
|
#ifdef MAPBASE
|
||||||
|
sTheirPhases[i].iszSequence = NULL_STRING;
|
||||||
|
sTheirPhases[i].iActivity = ACT_INVALID;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,10 +466,14 @@ struct ScriptedNPCInteraction_t
|
|||||||
float flNextAttemptTime;
|
float flNextAttemptTime;
|
||||||
|
|
||||||
#ifdef MAPBASE
|
#ifdef MAPBASE
|
||||||
// Unrecognized keyvalues are tested against response criteria later.
|
ScriptedNPCInteraction_Phases_t sTheirPhases[SNPCINT_NUM_PHASES]; // The animations played by the target NPC, if they are different
|
||||||
// This was originally a CUtlVector that carries response contexts, but I couldn't get it working due to some CUtlVector-struct shenanigans.
|
bool bHasSeparateSequenceNames;
|
||||||
// It works when we use a single string_t that's split and read each time the code runs, but feel free to improve on this.
|
|
||||||
string_t MiscCriteria; // CUtlVector<ResponseContext_t>
|
float flMaxAngleDiff;
|
||||||
|
string_t iszRelatedInteractions; // These interactions will be delayed as well when this interaction is used.
|
||||||
|
|
||||||
|
// Unrecognized keyvalues which are tested against response criteria later.
|
||||||
|
string_t MiscCriteria;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DECLARE_SIMPLE_DATADESC();
|
DECLARE_SIMPLE_DATADESC();
|
||||||
@ -838,6 +849,9 @@ protected: // pose parameters
|
|||||||
int m_poseAim_Pitch;
|
int m_poseAim_Pitch;
|
||||||
int m_poseAim_Yaw;
|
int m_poseAim_Yaw;
|
||||||
int m_poseMove_Yaw;
|
int m_poseMove_Yaw;
|
||||||
|
#ifdef MAPBASE
|
||||||
|
int m_poseInteractionRelativeYaw;
|
||||||
|
#endif
|
||||||
virtual void PopulatePoseParameters( void );
|
virtual void PopulatePoseParameters( void );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -846,6 +860,10 @@ public:
|
|||||||
// Return the stored pose parameter for "move_yaw"
|
// Return the stored pose parameter for "move_yaw"
|
||||||
inline int LookupPoseMoveYaw() { return m_poseMove_Yaw; }
|
inline int LookupPoseMoveYaw() { return m_poseMove_Yaw; }
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
inline int LookupPoseInteractionRelativeYaw() { return m_poseInteractionRelativeYaw; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
//
|
//
|
||||||
@ -1304,10 +1322,14 @@ private:
|
|||||||
public:
|
public:
|
||||||
float GetInteractionYaw( void ) const { return m_flInteractionYaw; }
|
float GetInteractionYaw( void ) const { return m_flInteractionYaw; }
|
||||||
|
|
||||||
|
bool IsRunningDynamicInteraction( void ) { return (m_iInteractionState != NPCINT_NOT_RUNNING && (m_hCine != NULL)); }
|
||||||
|
bool IsActiveDynamicInteraction( void ) { return (m_iInteractionState == NPCINT_RUNNING_ACTIVE && (m_hCine != NULL)); }
|
||||||
|
CAI_BaseNPC *GetInteractionPartner( void );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void ParseScriptedNPCInteractions( void );
|
void ParseScriptedNPCInteractions( void );
|
||||||
void AddScriptedNPCInteraction( ScriptedNPCInteraction_t *pInteraction );
|
void AddScriptedNPCInteraction( ScriptedNPCInteraction_t *pInteraction );
|
||||||
const char *GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase );
|
const char *GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase, bool bOtherNPC = false );
|
||||||
void StartRunningInteraction( CAI_BaseNPC *pOtherNPC, bool bActive );
|
void StartRunningInteraction( CAI_BaseNPC *pOtherNPC, bool bActive );
|
||||||
void StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector vecOtherOrigin, QAngle angOtherAngles );
|
void StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector vecOtherOrigin, QAngle angOtherAngles );
|
||||||
void CheckForScriptedNPCInteractions( void );
|
void CheckForScriptedNPCInteractions( void );
|
||||||
@ -1320,17 +1342,16 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
bool InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector &vecOrigin, QAngle &angAngles );
|
bool InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector &vecOrigin, QAngle &angAngles );
|
||||||
virtual bool CanRunAScriptedNPCInteraction( bool bForced = false );
|
virtual bool CanRunAScriptedNPCInteraction( bool bForced = false );
|
||||||
bool IsRunningDynamicInteraction( void ) { return (m_iInteractionState != NPCINT_NOT_RUNNING && (m_hCine != NULL)); }
|
|
||||||
bool IsActiveDynamicInteraction( void ) { return (m_iInteractionState == NPCINT_RUNNING_ACTIVE && (m_hCine != NULL)); }
|
|
||||||
ScriptedNPCInteraction_t *GetRunningDynamicInteraction( void ) { return &(m_ScriptedInteractions[m_iInteractionPlaying]); }
|
ScriptedNPCInteraction_t *GetRunningDynamicInteraction( void ) { return &(m_ScriptedInteractions[m_iInteractionPlaying]); }
|
||||||
void SetInteractionCantDie( bool bCantDie ) { m_bCannotDieDuringInteraction = bCantDie; }
|
void SetInteractionCantDie( bool bCantDie ) { m_bCannotDieDuringInteraction = bCantDie; }
|
||||||
bool HasInteractionCantDie( void );
|
bool HasInteractionCantDie( void );
|
||||||
|
bool HasValidInteractionsOnCurrentEnemy( void );
|
||||||
|
virtual bool CanStartDynamicInteractionDuringMelee() { return false; }
|
||||||
|
|
||||||
void InputForceInteractionWithNPC( inputdata_t &inputdata );
|
void InputForceInteractionWithNPC( inputdata_t &inputdata );
|
||||||
void StartForcedInteraction( CAI_BaseNPC *pNPC, int iInteraction );
|
void StartForcedInteraction( CAI_BaseNPC *pNPC, int iInteraction );
|
||||||
void CleanupForcedInteraction( void );
|
void CleanupForcedInteraction( void );
|
||||||
void CalculateForcedInteractionPosition( void );
|
void CalculateForcedInteractionPosition( void );
|
||||||
CAI_BaseNPC *GetInteractionPartner( void );
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Forced interactions
|
// Forced interactions
|
||||||
@ -1974,6 +1995,9 @@ public:
|
|||||||
//---------------------------------
|
//---------------------------------
|
||||||
|
|
||||||
virtual void Ignite( float flFlameLifetime, bool bNPCOnly = true, float flSize = 0.0f, bool bCalledByLevelDesigner = false );
|
virtual void Ignite( float flFlameLifetime, bool bNPCOnly = true, float flSize = 0.0f, bool bCalledByLevelDesigner = false );
|
||||||
|
#ifdef MAPBASE
|
||||||
|
virtual void EnemyIgnited( CAI_BaseNPC *pVictim ) {}
|
||||||
|
#endif
|
||||||
virtual bool PassesDamageFilter( const CTakeDamageInfo &info );
|
virtual bool PassesDamageFilter( const CTakeDamageInfo &info );
|
||||||
|
|
||||||
//---------------------------------
|
//---------------------------------
|
||||||
@ -2228,6 +2252,9 @@ public:
|
|||||||
static const char* GetActivityName (int actID);
|
static const char* GetActivityName (int actID);
|
||||||
|
|
||||||
static void AddActivityToSR(const char *actName, int conID);
|
static void AddActivityToSR(const char *actName, int conID);
|
||||||
|
#ifdef MAPBASE
|
||||||
|
static int GetOrRegisterActivity( const char *actName );
|
||||||
|
#endif
|
||||||
|
|
||||||
static void AddEventToSR(const char *eventName, int conID);
|
static void AddEventToSR(const char *eventName, int conID);
|
||||||
static const char* GetEventName (int actID);
|
static const char* GetEventName (int actID);
|
||||||
|
@ -1610,6 +1610,12 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask )
|
|||||||
// as this should only run with the NPC "receiving" the interaction
|
// as this should only run with the NPC "receiving" the interaction
|
||||||
ScriptedNPCInteraction_t *pInteraction = m_hForcedInteractionPartner->GetRunningDynamicInteraction();
|
ScriptedNPCInteraction_t *pInteraction = m_hForcedInteractionPartner->GetRunningDynamicInteraction();
|
||||||
|
|
||||||
|
if ( !(pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_ANGLES) )
|
||||||
|
{
|
||||||
|
TaskComplete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Get our target's origin
|
// Get our target's origin
|
||||||
Vector vecTarget = m_hForcedInteractionPartner->GetAbsOrigin();
|
Vector vecTarget = m_hForcedInteractionPartner->GetAbsOrigin();
|
||||||
|
|
||||||
@ -1617,7 +1623,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask )
|
|||||||
float angInteractionAngle = pInteraction->angRelativeAngles.y;
|
float angInteractionAngle = pInteraction->angRelativeAngles.y;
|
||||||
angInteractionAngle += 180.0f;
|
angInteractionAngle += 180.0f;
|
||||||
|
|
||||||
GetMotor()->SetIdealYaw( CalcIdealYaw( vecTarget ) + angInteractionAngle );
|
GetMotor()->SetIdealYaw( AngleNormalize( CalcIdealYaw( vecTarget ) + angInteractionAngle ) );
|
||||||
|
|
||||||
if (FacingIdeal())
|
if (FacingIdeal())
|
||||||
TaskComplete();
|
TaskComplete();
|
||||||
@ -4113,6 +4119,15 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask )
|
|||||||
m_hCine->SynchronizeSequence( this );
|
m_hCine->SynchronizeSequence( this );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if ( IsRunningDynamicInteraction() && m_poseInteractionRelativeYaw > -1 )
|
||||||
|
{
|
||||||
|
// Animations in progress require pose parameters to be set every frame, so keep setting the interaction relative yaw pose.
|
||||||
|
// The random value is added to help it pass server transmit checks.
|
||||||
|
SetPoseParameter( m_poseInteractionRelativeYaw, GetPoseParameter( m_poseInteractionRelativeYaw ) + RandomFloat( -0.1f, 0.1f ) );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +88,15 @@ static void DispatchComeback( CAI_ExpresserWithFollowup *pExpress, CBaseEntity *
|
|||||||
// add in any provided contexts from the parameters onto the ones stored in the followup
|
// add in any provided contexts from the parameters onto the ones stored in the followup
|
||||||
criteria.Merge( followup.followup_contexts );
|
criteria.Merge( followup.followup_contexts );
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if (CAI_ExpresserSink *pSink = dynamic_cast<CAI_ExpresserSink *>(pRespondent))
|
||||||
|
{
|
||||||
|
criteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", (pRespondent->GetAbsOrigin() - pSpeaker->GetAbsOrigin()).Length() ) );
|
||||||
|
g_ResponseQueueManager.GetQueue()->AppendFollowupCriteria( followup.followup_concept, criteria, pSink->GetSinkExpresser(), pSink, pRespondent, pSpeaker, kDRT_SPECIFIC );
|
||||||
|
|
||||||
|
pSink->Speak( followup.followup_concept, &criteria );
|
||||||
|
}
|
||||||
|
#else
|
||||||
// This is kludgy and needs to be fixed in class hierarchy, but for now, try to guess at the most likely
|
// This is kludgy and needs to be fixed in class hierarchy, but for now, try to guess at the most likely
|
||||||
// kinds of targets and dispatch to them.
|
// kinds of targets and dispatch to them.
|
||||||
if (CBaseMultiplayerPlayer *pPlayer = dynamic_cast<CBaseMultiplayerPlayer *>(pRespondent))
|
if (CBaseMultiplayerPlayer *pPlayer = dynamic_cast<CBaseMultiplayerPlayer *>(pRespondent))
|
||||||
@ -99,6 +108,7 @@ static void DispatchComeback( CAI_ExpresserWithFollowup *pExpress, CBaseEntity *
|
|||||||
{
|
{
|
||||||
pActor->Speak( followup.followup_concept, &criteria );
|
pActor->Speak( followup.followup_concept, &criteria );
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -902,6 +902,17 @@ BEGIN_DATADESC( CAI_Hint )
|
|||||||
DEFINE_OUTPUT( m_OnNPCStartedUsing, "OnNPCStartedUsing" ),
|
DEFINE_OUTPUT( m_OnNPCStartedUsing, "OnNPCStartedUsing" ),
|
||||||
DEFINE_OUTPUT( m_OnNPCStoppedUsing, "OnNPCStoppedUsing" ),
|
DEFINE_OUTPUT( m_OnNPCStoppedUsing, "OnNPCStoppedUsing" ),
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
DEFINE_OUTPUT( m_OnScriptEvent[0], "OnScriptEvent01" ),
|
||||||
|
DEFINE_OUTPUT( m_OnScriptEvent[1], "OnScriptEvent02" ),
|
||||||
|
DEFINE_OUTPUT( m_OnScriptEvent[2], "OnScriptEvent03" ),
|
||||||
|
DEFINE_OUTPUT( m_OnScriptEvent[3], "OnScriptEvent04" ),
|
||||||
|
DEFINE_OUTPUT( m_OnScriptEvent[4], "OnScriptEvent05" ),
|
||||||
|
DEFINE_OUTPUT( m_OnScriptEvent[5], "OnScriptEvent06" ),
|
||||||
|
DEFINE_OUTPUT( m_OnScriptEvent[6], "OnScriptEvent07" ),
|
||||||
|
DEFINE_OUTPUT( m_OnScriptEvent[7], "OnScriptEvent08" ),
|
||||||
|
#endif
|
||||||
|
|
||||||
END_DATADESC( );
|
END_DATADESC( );
|
||||||
|
|
||||||
#ifdef MAPBASE_VSCRIPT
|
#ifdef MAPBASE_VSCRIPT
|
||||||
@ -1705,6 +1716,19 @@ void CAI_Hint::NPCStoppedUsing( CAI_BaseNPC *pNPC )
|
|||||||
m_OnNPCStoppedUsing.Set( pNPC, pNPC, this );
|
m_OnNPCStoppedUsing.Set( pNPC, pNPC, this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Purpose:
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void CAI_Hint::FireScriptEvent( int nEvent )
|
||||||
|
{
|
||||||
|
if ( ( nEvent >= 1 ) && ( nEvent <= 8 ) )
|
||||||
|
{
|
||||||
|
m_OnScriptEvent[nEvent - 1].FireOutput( m_hHintOwner, this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
CON_COMMAND(ai_dump_hints, "")
|
CON_COMMAND(ai_dump_hints, "")
|
||||||
{
|
{
|
||||||
|
@ -323,6 +323,9 @@ public:
|
|||||||
void FixupTargetNode();
|
void FixupTargetNode();
|
||||||
void NPCStartedUsing( CAI_BaseNPC *pNPC );
|
void NPCStartedUsing( CAI_BaseNPC *pNPC );
|
||||||
void NPCStoppedUsing( CAI_BaseNPC *pNPC );
|
void NPCStoppedUsing( CAI_BaseNPC *pNPC );
|
||||||
|
#ifdef MAPBASE
|
||||||
|
void FireScriptEvent( int nEvent );
|
||||||
|
#endif
|
||||||
|
|
||||||
HintIgnoreFacing_t GetIgnoreFacing() const { return m_NodeData.fIgnoreFacing; }
|
HintIgnoreFacing_t GetIgnoreFacing() const { return m_NodeData.fIgnoreFacing; }
|
||||||
|
|
||||||
@ -385,6 +388,10 @@ private:
|
|||||||
float m_nodeFOV;
|
float m_nodeFOV;
|
||||||
Vector m_vecForward;
|
Vector m_vecForward;
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
COutputEvent m_OnScriptEvent[8];
|
||||||
|
#endif
|
||||||
|
|
||||||
// The next hint in list of all hints
|
// The next hint in list of all hints
|
||||||
friend class CAI_HintManager;
|
friend class CAI_HintManager;
|
||||||
|
|
||||||
|
@ -128,6 +128,12 @@ ConceptInfo_t g_ConceptInfos[] =
|
|||||||
|
|
||||||
// Passenger behaviour
|
// Passenger behaviour
|
||||||
{ TLK_PASSENGER_NEW_RADAR_CONTACT, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
|
{ TLK_PASSENGER_NEW_RADAR_CONTACT, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
{ TLK_TAKING_FIRE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
|
||||||
|
{ TLK_NEW_ENEMY, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
|
||||||
|
{ TLK_COMBAT_IDLE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -1259,6 +1265,38 @@ void CAI_PlayerAlly::OnKilledNPC( CBaseCombatCharacter *pKilled )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void CAI_PlayerAlly::OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector &vecDir, const Vector &vecEnd )
|
||||||
|
{
|
||||||
|
BaseClass::OnEnemyRangeAttackedMe( pEnemy, vecDir, vecEnd );
|
||||||
|
|
||||||
|
if ( IRelationType( pEnemy ) <= D_FR )
|
||||||
|
{
|
||||||
|
AI_CriteriaSet modifiers;
|
||||||
|
ModifyOrAppendEnemyCriteria( modifiers, pEnemy );
|
||||||
|
|
||||||
|
Vector vecEntDir = (pEnemy->EyePosition() - EyePosition());
|
||||||
|
float flDot = DotProduct( vecEntDir.Normalized(), vecDir );
|
||||||
|
modifiers.AppendCriteria( "shot_dot", CNumStr( flDot ) );
|
||||||
|
|
||||||
|
if (GetLastDamageTime() == gpGlobals->curtime)
|
||||||
|
modifiers.AppendCriteria( "missed", "0" );
|
||||||
|
else
|
||||||
|
modifiers.AppendCriteria( "missed", "1" );
|
||||||
|
|
||||||
|
// Check if they're out of ammo
|
||||||
|
if ( pEnemy->IsCombatCharacter() && pEnemy->MyCombatCharacterPointer()->GetActiveWeapon() && pEnemy->MyCombatCharacterPointer()->GetActiveWeapon()->Clip1() <= 0 )
|
||||||
|
modifiers.AppendCriteria( "last_attack", "1" );
|
||||||
|
else
|
||||||
|
modifiers.AppendCriteria( "last_attack", "0" );
|
||||||
|
|
||||||
|
SpeakIfAllowed( TLK_TAKING_FIRE, modifiers );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void CAI_PlayerAlly::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
|
void CAI_PlayerAlly::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
|
||||||
{
|
{
|
||||||
@ -1762,6 +1800,54 @@ bool CAI_PlayerAlly::IsAllowedToSpeak( AIConcept_t concept, bool bRespondingToPl
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Purpose: Specifically for player allies handling followup responses.
|
||||||
|
// Better-accounts for unknown concepts so that users are free in what they use.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
bool CAI_PlayerAlly::IsAllowedToSpeakFollowup( AIConcept_t concept, CBaseEntity *pIssuer, bool bSpecific )
|
||||||
|
{
|
||||||
|
CAI_AllySpeechManager * pSpeechManager = GetAllySpeechManager();
|
||||||
|
ConceptInfo_t * pInfo = pSpeechManager->GetConceptInfo( concept );
|
||||||
|
ConceptCategory_t category = SPEECH_PRIORITY; // Must be SPEECH_PRIORITY to get around semaphore
|
||||||
|
|
||||||
|
if ( !IsOkToSpeak( category, true ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If this followup is specifically targeted towards us, speak if we're not already speaking
|
||||||
|
// If it's meant to be spoken by anyone, respect speech delay and semaphore
|
||||||
|
if ( bSpecific )
|
||||||
|
{
|
||||||
|
if ( !GetExpresser()->CanSpeakAfterMyself() )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( !GetExpresser()->CanSpeak() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
CAI_TimedSemaphore *pSemaphore = GetExpresser()->GetMySpeechSemaphore( this );
|
||||||
|
if ( pSemaphore && !pSemaphore->IsAvailable( this ) )
|
||||||
|
{
|
||||||
|
// Only if the semaphore holder isn't the one dispatching the followup
|
||||||
|
if ( pSemaphore->GetOwner() != pIssuer )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !pSpeechManager->ConceptDelayExpired( concept ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( ( pInfo && pInfo->flags & AICF_SPEAK_ONCE ) && GetExpresser()->SpokeConcept( concept ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( !GetExpresser()->CanSpeakConcept( concept ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
bool CAI_PlayerAlly::SpeakIfAllowed( AIConcept_t concept, const char *modifiers, bool bRespondingToPlayer, char *pszOutResponseChosen, size_t bufsize )
|
bool CAI_PlayerAlly::SpeakIfAllowed( AIConcept_t concept, const char *modifiers, bool bRespondingToPlayer, char *pszOutResponseChosen, size_t bufsize )
|
||||||
|
@ -132,6 +132,13 @@
|
|||||||
#define TLK_TGCATCHUP "TLK_TGCATCHUP"
|
#define TLK_TGCATCHUP "TLK_TGCATCHUP"
|
||||||
#define TLK_TGENDTOUR "TLK_TGENDTOUR"
|
#define TLK_TGENDTOUR "TLK_TGENDTOUR"
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Additional concepts for companions in mods
|
||||||
|
#define TLK_TAKING_FIRE "TLK_TAKING_FIRE" // Someone fired at me (regardless of whether I was hit)
|
||||||
|
#define TLK_NEW_ENEMY "TLK_NEW_ENEMY" // A new enemy appeared while combat was already in progress
|
||||||
|
#define TLK_COMBAT_IDLE "TLK_COMBAT_IDLE" // Similar to TLK_ATTACKING, but specifically for when *not* currently attacking (e.g. when in cover or reloading)
|
||||||
|
#endif
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
#define TALKRANGE_MIN 500.0 // don't talk to anyone farther away than this
|
#define TALKRANGE_MIN 500.0 // don't talk to anyone farther away than this
|
||||||
@ -315,6 +322,10 @@ public:
|
|||||||
//---------------------------------
|
//---------------------------------
|
||||||
void OnKilledNPC( CBaseCombatCharacter *pKilled );
|
void OnKilledNPC( CBaseCombatCharacter *pKilled );
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
void OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector &vecDir, const Vector &vecEnd );
|
||||||
|
#endif
|
||||||
|
|
||||||
//---------------------------------
|
//---------------------------------
|
||||||
// Damage handling
|
// Damage handling
|
||||||
//---------------------------------
|
//---------------------------------
|
||||||
@ -392,6 +403,9 @@ public:
|
|||||||
|
|
||||||
bool ShouldSpeakRandom( AIConcept_t concept, int iChance );
|
bool ShouldSpeakRandom( AIConcept_t concept, int iChance );
|
||||||
bool IsAllowedToSpeak( AIConcept_t concept, bool bRespondingToPlayer = false );
|
bool IsAllowedToSpeak( AIConcept_t concept, bool bRespondingToPlayer = false );
|
||||||
|
#ifdef MAPBASE
|
||||||
|
bool IsAllowedToSpeakFollowup( AIConcept_t concept, CBaseEntity *pIssuer, bool bSpecific );
|
||||||
|
#endif
|
||||||
virtual bool SpeakIfAllowed( AIConcept_t concept, const char *modifiers = NULL, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 );
|
virtual bool SpeakIfAllowed( AIConcept_t concept, const char *modifiers = NULL, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 );
|
||||||
#ifdef MAPBASE
|
#ifdef MAPBASE
|
||||||
virtual bool SpeakIfAllowed( AIConcept_t concept, AI_CriteriaSet& modifiers, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 );
|
virtual bool SpeakIfAllowed( AIConcept_t concept, AI_CriteriaSet& modifiers, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 );
|
||||||
|
@ -126,6 +126,13 @@ public:
|
|||||||
virtual void OnSpokeConcept( AIConcept_t concept, AI_Response *response ) {};
|
virtual void OnSpokeConcept( AIConcept_t concept, AI_Response *response ) {};
|
||||||
virtual void OnStartSpeaking() {}
|
virtual void OnStartSpeaking() {}
|
||||||
virtual bool UseSemaphore() { return true; }
|
virtual bool UseSemaphore() { return true; }
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Works around issues with CAI_ExpresserHost<> class hierarchy
|
||||||
|
virtual CAI_Expresser *GetSinkExpresser() { return NULL; }
|
||||||
|
virtual bool IsAllowedToSpeakFollowup( AIConcept_t concept, CBaseEntity *pIssuer, bool bSpecific ) { return true; }
|
||||||
|
virtual bool Speak( AIConcept_t concept, AI_CriteriaSet *pCriteria, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ) { return false; }
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ConceptHistory_t
|
struct ConceptHistory_t
|
||||||
@ -244,9 +251,15 @@ public:
|
|||||||
static bool RunScriptResponse( CBaseEntity *pTarget, const char *response, AI_CriteriaSet *criteria, bool file );
|
static bool RunScriptResponse( CBaseEntity *pTarget, const char *response, AI_CriteriaSet *criteria, bool file );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
public:
|
||||||
|
#else
|
||||||
protected:
|
protected:
|
||||||
|
#endif
|
||||||
CAI_TimedSemaphore *GetMySpeechSemaphore( CBaseEntity *pNpc );
|
CAI_TimedSemaphore *GetMySpeechSemaphore( CBaseEntity *pNpc );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
bool SpeakRawScene( const char *pszScene, float delay, AI_Response *response, IRecipientFilter *filter = NULL );
|
bool SpeakRawScene( const char *pszScene, float delay, AI_Response *response, IRecipientFilter *filter = NULL );
|
||||||
// This will create a fake .vcd/CChoreoScene to wrap the sound to be played
|
// This will create a fake .vcd/CChoreoScene to wrap the sound to be played
|
||||||
#ifdef MAPBASE
|
#ifdef MAPBASE
|
||||||
@ -311,11 +324,15 @@ private:
|
|||||||
//
|
//
|
||||||
|
|
||||||
template <class BASE_NPC>
|
template <class BASE_NPC>
|
||||||
class CAI_ExpresserHost : public BASE_NPC, protected CAI_ExpresserSink
|
class CAI_ExpresserHost : public BASE_NPC, public CAI_ExpresserSink
|
||||||
{
|
{
|
||||||
DECLARE_CLASS_NOFRIEND( CAI_ExpresserHost, BASE_NPC );
|
DECLARE_CLASS_NOFRIEND( CAI_ExpresserHost, BASE_NPC );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
#ifdef MAPBASE
|
||||||
|
CAI_Expresser *GetSinkExpresser() { return this->GetExpresser(); }
|
||||||
|
#endif
|
||||||
|
|
||||||
virtual void NoteSpeaking( float duration, float delay );
|
virtual void NoteSpeaking( float duration, float delay );
|
||||||
|
|
||||||
virtual bool Speak( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL );
|
virtual bool Speak( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL );
|
||||||
|
@ -11,6 +11,9 @@
|
|||||||
#include "ai_baseactor.h"
|
#include "ai_baseactor.h"
|
||||||
#include "ai_speech.h"
|
#include "ai_speech.h"
|
||||||
//#include "flex_expresser.h"
|
//#include "flex_expresser.h"
|
||||||
|
#ifdef MAPBASE
|
||||||
|
#include "sceneentity.h"
|
||||||
|
#endif
|
||||||
// memdbgon must be the last include file in a .cpp file!!!
|
// memdbgon must be the last include file in a .cpp file!!!
|
||||||
#include <tier0/memdbgon.h>
|
#include <tier0/memdbgon.h>
|
||||||
|
|
||||||
@ -170,15 +173,25 @@ void CResponseQueue::RemoveExpresserHost(CBaseEntity *host)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
/// Get the expresser for a base entity.
|
||||||
|
static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt, CAI_ExpresserSink **ppSink = NULL)
|
||||||
|
{
|
||||||
|
if ( CAI_ExpresserSink *pSink = dynamic_cast<CAI_ExpresserSink *>(pEnt) )
|
||||||
|
{
|
||||||
|
if (ppSink)
|
||||||
|
*ppSink = pSink;
|
||||||
|
return pSink->GetSinkExpresser();
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
/// Get the expresser for a base entity.
|
/// Get the expresser for a base entity.
|
||||||
/// TODO: Kind of an ugly hack until I get the class hierarchy straightened out.
|
/// TODO: Kind of an ugly hack until I get the class hierarchy straightened out.
|
||||||
static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt)
|
static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt)
|
||||||
{
|
{
|
||||||
#ifdef MAPBASE
|
|
||||||
if ( CBasePlayer *pPlayer = ToBasePlayer(pEnt) )
|
|
||||||
#else
|
|
||||||
if ( CBaseMultiplayerPlayer *pPlayer = dynamic_cast<CBaseMultiplayerPlayer *>(pEnt) )
|
if ( CBaseMultiplayerPlayer *pPlayer = dynamic_cast<CBaseMultiplayerPlayer *>(pEnt) )
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
return pPlayer->GetExpresser();
|
return pPlayer->GetExpresser();
|
||||||
}
|
}
|
||||||
@ -197,6 +210,7 @@ static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void CResponseQueue::CDeferredResponse::Quash()
|
void CResponseQueue::CDeferredResponse::Quash()
|
||||||
@ -205,6 +219,23 @@ void CResponseQueue::CDeferredResponse::Quash()
|
|||||||
m_fDispatchTime = 0;
|
m_fDispatchTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
void CResponseQueue::AppendFollowupCriteria( AIConcept_t concept, AI_CriteriaSet &set, CAI_Expresser *pEx,
|
||||||
|
CAI_ExpresserSink *pSink, CBaseEntity *pTarget, CBaseEntity *pIssuer, DeferredResponseTarget_t nTargetType )
|
||||||
|
{
|
||||||
|
// Allows control over which followups interrupt speech routines
|
||||||
|
set.AppendCriteria( "followup_allowed_to_speak", (pSink->IsAllowedToSpeakFollowup( concept, pIssuer, nTargetType == kDRT_SPECIFIC )) ? "1" : "0" );
|
||||||
|
|
||||||
|
set.AppendCriteria( "followup_target_type", UTIL_VarArgs( "%i", (int)nTargetType ) );
|
||||||
|
|
||||||
|
// NOTE: This assumes any expresser entity derived from CBaseFlex is also derived from CBaseCombatCharacter
|
||||||
|
if (pTarget->IsCombatCharacter())
|
||||||
|
set.AppendCriteria( "is_speaking", (pEx->IsSpeaking() || IsRunningScriptedSceneWithSpeechAndNotPaused( assert_cast<CBaseFlex*>(pTarget) )) ? "1" : "0" );
|
||||||
|
else
|
||||||
|
set.AppendCriteria( "is_speaking", "0" );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
|
bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
|
||||||
{
|
{
|
||||||
// find the target.
|
// find the target.
|
||||||
@ -272,9 +303,15 @@ bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
|
|||||||
continue; // too far
|
continue; // too far
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
CAI_ExpresserSink *pSink = NULL;
|
||||||
|
pEx = InferExpresserFromBaseEntity( pTarget, &pSink );
|
||||||
|
#else
|
||||||
pEx = InferExpresserFromBaseEntity(pTarget);
|
pEx = InferExpresserFromBaseEntity(pTarget);
|
||||||
|
#endif
|
||||||
if ( !pEx || pTarget == pIssuer )
|
if ( !pEx || pTarget == pIssuer )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
AI_CriteriaSet characterCriteria;
|
AI_CriteriaSet characterCriteria;
|
||||||
pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
|
pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
|
||||||
characterCriteria.Merge(&deferredCriteria);
|
characterCriteria.Merge(&deferredCriteria);
|
||||||
@ -282,6 +319,11 @@ bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
|
|||||||
{
|
{
|
||||||
characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) );
|
characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
AppendFollowupCriteria( response.m_concept, characterCriteria, pEx, pSink, pTarget, pIssuer, kDRT_ALL );
|
||||||
|
#endif
|
||||||
|
|
||||||
AI_Response prospectiveResponse;
|
AI_Response prospectiveResponse;
|
||||||
if ( pEx->FindResponse( prospectiveResponse, response.m_concept, &characterCriteria ) )
|
if ( pEx->FindResponse( prospectiveResponse, response.m_concept, &characterCriteria ) )
|
||||||
{
|
{
|
||||||
@ -304,14 +346,26 @@ bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
|
|||||||
return false; // we're done right here.
|
return false; // we're done right here.
|
||||||
|
|
||||||
// Get the expresser for the target.
|
// Get the expresser for the target.
|
||||||
|
#ifdef MAPBASE
|
||||||
|
CAI_ExpresserSink *pSink = NULL;
|
||||||
|
pEx = InferExpresserFromBaseEntity( pTarget, &pSink );
|
||||||
|
#else
|
||||||
pEx = InferExpresserFromBaseEntity(pTarget);
|
pEx = InferExpresserFromBaseEntity(pTarget);
|
||||||
|
#endif
|
||||||
if (!pEx)
|
if (!pEx)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
AI_CriteriaSet characterCriteria;
|
AI_CriteriaSet characterCriteria;
|
||||||
pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
|
pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
|
||||||
characterCriteria.Merge(&deferredCriteria);
|
characterCriteria.Merge(&deferredCriteria);
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if ( pIssuer )
|
||||||
|
{
|
||||||
|
characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", (pTarget->GetAbsOrigin() - pIssuer->GetAbsOrigin()).Length() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendFollowupCriteria( response.m_concept, characterCriteria, pEx, pSink, pTarget, pIssuer, kDRT_SPECIFIC );
|
||||||
|
#endif
|
||||||
pEx->Speak( response.m_concept, &characterCriteria );
|
pEx->Speak( response.m_concept, &characterCriteria );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -364,7 +418,12 @@ bool CResponseQueue::DispatchOneResponse_ThenANY( CDeferredResponse &response, A
|
|||||||
continue; // too far
|
continue; // too far
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
CAI_ExpresserSink *pSink = NULL;
|
||||||
|
pEx = InferExpresserFromBaseEntity( pTarget, &pSink );
|
||||||
|
#else
|
||||||
pEx = InferExpresserFromBaseEntity(pTarget);
|
pEx = InferExpresserFromBaseEntity(pTarget);
|
||||||
|
#endif
|
||||||
if ( !pEx )
|
if ( !pEx )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -376,6 +435,11 @@ bool CResponseQueue::DispatchOneResponse_ThenANY( CDeferredResponse &response, A
|
|||||||
{
|
{
|
||||||
characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) );
|
characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
AppendFollowupCriteria( response.m_concept, characterCriteria, pEx, pSink, pTarget, pIssuer, kDRT_ANY );
|
||||||
|
#endif
|
||||||
|
|
||||||
AI_Response prospectiveResponse;
|
AI_Response prospectiveResponse;
|
||||||
|
|
||||||
#ifdef MAPBASE
|
#ifdef MAPBASE
|
||||||
|
@ -116,6 +116,11 @@ public:
|
|||||||
inline int GetNumExpresserTargets() const;
|
inline int GetNumExpresserTargets() const;
|
||||||
inline CBaseEntity *GetExpresserHost(int which) const;
|
inline CBaseEntity *GetExpresserHost(int which) const;
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
void AppendFollowupCriteria( AIConcept_t concept, AI_CriteriaSet &set, CAI_Expresser *pEx,
|
||||||
|
CAI_ExpresserSink *pSink, CBaseEntity *pTarget, CBaseEntity *pIssuer, DeferredResponseTarget_t nTargetType );
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Actually send off one response to a consumer
|
/// Actually send off one response to a consumer
|
||||||
/// Return true if dispatch succeeded
|
/// Return true if dispatch succeeded
|
||||||
|
@ -1333,7 +1333,7 @@ void CBaseAnimating::HandleAnimEvent( animevent_t *pEvent )
|
|||||||
#ifdef MAPBASE
|
#ifdef MAPBASE
|
||||||
else if ( pEvent->event == AE_NPC_RESPONSE )
|
else if ( pEvent->event == AE_NPC_RESPONSE )
|
||||||
{
|
{
|
||||||
if (!MyNPCPointer()->GetExpresser()->IsSpeaking())
|
if (MyNPCPointer() && MyNPCPointer()->GetExpresser() && !MyNPCPointer()->GetExpresser()->IsSpeaking())
|
||||||
{
|
{
|
||||||
DispatchResponse( pEvent->options );
|
DispatchResponse( pEvent->options );
|
||||||
}
|
}
|
||||||
@ -1344,6 +1344,18 @@ void CBaseAnimating::HandleAnimEvent( animevent_t *pEvent )
|
|||||||
DispatchResponse( pEvent->options );
|
DispatchResponse( pEvent->options );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if ( pEvent->event == AE_VSCRIPT_RUN )
|
||||||
|
{
|
||||||
|
if (!RunScript( pEvent->options ))
|
||||||
|
Warning( "%s failed to run AE_VSCRIPT_RUN on server with \"%s\"\n", GetDebugName(), pEvent->options );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( pEvent->event == AE_VSCRIPT_RUN_FILE )
|
||||||
|
{
|
||||||
|
if (!RunScriptFile( pEvent->options ))
|
||||||
|
Warning( "%s failed to run AE_VSCRIPT_RUN_FILE on server with \"%s\"\n", GetDebugName(), pEvent->options );
|
||||||
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
else if ( pEvent->event == AE_RAGDOLL )
|
else if ( pEvent->event == AE_RAGDOLL )
|
||||||
{
|
{
|
||||||
|
@ -263,6 +263,10 @@ public:
|
|||||||
|
|
||||||
virtual bool CanBecomeServerRagdoll( void ) { return true; }
|
virtual bool CanBecomeServerRagdoll( void ) { return true; }
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
virtual void OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector &vecDir, const Vector &vecEnd ) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
// -----------------------
|
// -----------------------
|
||||||
// Damage
|
// Damage
|
||||||
// -----------------------
|
// -----------------------
|
||||||
|
@ -10300,8 +10300,9 @@ bool CBaseEntity::ScriptAddOutput( const char *pszOutputName, const char *pszTar
|
|||||||
const char *CBaseEntity::ScriptGetKeyValue( const char *pszKeyName )
|
const char *CBaseEntity::ScriptGetKeyValue( const char *pszKeyName )
|
||||||
{
|
{
|
||||||
static char szValue[128];
|
static char szValue[128];
|
||||||
GetKeyValue( pszKeyName, szValue, sizeof(szValue) );
|
if ( GetKeyValue( pszKeyName, szValue, sizeof(szValue) ) )
|
||||||
return szValue;
|
return szValue;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -190,6 +190,24 @@ int CAI_FuncTankBehavior::SelectSchedule()
|
|||||||
return SCHED_IDLE_STAND;
|
return SCHED_IDLE_STAND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Purpose:
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void CAI_FuncTankBehavior::ModifyOrAppendCriteria( AI_CriteriaSet &set )
|
||||||
|
{
|
||||||
|
BaseClass::ModifyOrAppendCriteria( set );
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
set.AppendCriteria( "ft_mounted", m_bMounted ? "1" : "0" );
|
||||||
|
|
||||||
|
if (m_hFuncTank)
|
||||||
|
{
|
||||||
|
set.AppendCriteria( "ft_classname", m_hFuncTank->GetClassname() );
|
||||||
|
m_hFuncTank->AppendContextToCriteria( set, "ft_" );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Purpose:
|
// Purpose:
|
||||||
// Input : activity -
|
// Input : activity -
|
||||||
|
@ -55,6 +55,8 @@ public:
|
|||||||
bool CanManTank( CFuncTank *pTank, bool bForced );
|
bool CanManTank( CFuncTank *pTank, bool bForced );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void ModifyOrAppendCriteria( AI_CriteriaSet &set );
|
||||||
|
|
||||||
Activity NPC_TranslateActivity( Activity activity );
|
Activity NPC_TranslateActivity( Activity activity );
|
||||||
|
|
||||||
// Conditions:
|
// Conditions:
|
||||||
|
@ -159,6 +159,10 @@ ConVar zombie_decaymax( "zombie_decaymax", "0.4" );
|
|||||||
|
|
||||||
ConVar zombie_ambushdist( "zombie_ambushdist", "16000" );
|
ConVar zombie_ambushdist( "zombie_ambushdist", "16000" );
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
ConVar zombie_no_flinch_during_unique_anim( "zombie_no_flinch_during_unique_anim", "1", FCVAR_NONE, "Prevents zombies from flinching during actbusies and scripted sequences." );
|
||||||
|
#endif
|
||||||
|
|
||||||
//=========================================================
|
//=========================================================
|
||||||
// For a couple of reasons, we keep a running count of how
|
// For a couple of reasons, we keep a running count of how
|
||||||
// many zombies in the world are angry at any given time.
|
// many zombies in the world are angry at any given time.
|
||||||
@ -1745,7 +1749,11 @@ void CNPC_BaseZombie::HandleAnimEvent( animevent_t *pEvent )
|
|||||||
|
|
||||||
dmgInfo.SetDamagePosition( vecHeadCrabPosition );
|
dmgInfo.SetDamagePosition( vecHeadCrabPosition );
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
ReleaseHeadcrab( vecHeadCrabPosition, vVelocity *iSpeed, true, false, true );
|
||||||
|
#else
|
||||||
ReleaseHeadcrab( EyePosition(), vVelocity * iSpeed, true, false, true );
|
ReleaseHeadcrab( EyePosition(), vVelocity * iSpeed, true, false, true );
|
||||||
|
#endif
|
||||||
|
|
||||||
GuessDamageForce( &dmgInfo, vVelocity, vecHeadCrabPosition, 0.5f );
|
GuessDamageForce( &dmgInfo, vVelocity, vecHeadCrabPosition, 0.5f );
|
||||||
TakeDamage( dmgInfo );
|
TakeDamage( dmgInfo );
|
||||||
@ -1927,6 +1935,31 @@ void CNPC_BaseZombie::OnScheduleChange( void )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------
|
||||||
|
//---------------------------------------------------------
|
||||||
|
|
||||||
|
bool CNPC_BaseZombie::CanFlinch( void )
|
||||||
|
{
|
||||||
|
if (!BaseClass::CanFlinch())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if (zombie_no_flinch_during_unique_anim.GetBool())
|
||||||
|
{
|
||||||
|
// Don't flinch if currently playing actbusy animation (navigating to or from one is fine)
|
||||||
|
if (m_ActBusyBehavior.IsInsideActBusy())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Don't flinch if currently playing scripted sequence (navigating to or from one is fine)
|
||||||
|
if (m_NPCState == NPC_STATE_SCRIPT && (IsCurSchedule( SCHED_SCRIPTED_WAIT, false ) || IsCurSchedule( SCHED_SCRIPTED_FACE, false )))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
int CNPC_BaseZombie::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
|
int CNPC_BaseZombie::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
|
||||||
@ -2435,6 +2468,20 @@ void CNPC_BaseZombie::RemoveHead( void )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------
|
||||||
|
//---------------------------------------------------------
|
||||||
|
void CNPC_BaseZombie::SetModel( const char *szModelName )
|
||||||
|
{
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Zombies setting the same model again is a problem when they should maintain their current sequence (e.g. during dynamic interactions)
|
||||||
|
if ( IsRunningDynamicInteraction() && GetModelIndex() != 0 && FStrEq( szModelName, STRING(GetModelName()) ) )
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BaseClass::SetModel( szModelName );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CNPC_BaseZombie::ShouldPlayFootstepMoan( void )
|
bool CNPC_BaseZombie::ShouldPlayFootstepMoan( void )
|
||||||
{
|
{
|
||||||
if( random->RandomInt( 1, zombie_stepfreq.GetInt() * s_iAngryZombies ) == 1 )
|
if( random->RandomInt( 1, zombie_stepfreq.GetInt() * s_iAngryZombies ) == 1 )
|
||||||
@ -2450,9 +2497,15 @@ bool CNPC_BaseZombie::ShouldPlayFootstepMoan( void )
|
|||||||
#define CRAB_HULL_EXPAND 1.1f
|
#define CRAB_HULL_EXPAND 1.1f
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
bool CNPC_BaseZombie::HeadcrabFits( CBaseAnimating *pCrab )
|
bool CNPC_BaseZombie::HeadcrabFits( CBaseAnimating *pCrab, const Vector *vecOrigin )
|
||||||
{
|
{
|
||||||
Vector vecSpawnLoc = pCrab->GetAbsOrigin();
|
Vector vecSpawnLoc;
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if (vecOrigin)
|
||||||
|
vecSpawnLoc = *vecOrigin;
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
vecSpawnLoc = pCrab->GetAbsOrigin();
|
||||||
|
|
||||||
CTraceFilterSimpleList traceFilter( COLLISION_GROUP_NONE );
|
CTraceFilterSimpleList traceFilter( COLLISION_GROUP_NONE );
|
||||||
traceFilter.AddEntityToIgnore( pCrab );
|
traceFilter.AddEntityToIgnore( pCrab );
|
||||||
@ -2535,7 +2588,12 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve
|
|||||||
SetHeadcrabSpawnLocation( iCrabAttachment, pAnimatingGib );
|
SetHeadcrabSpawnLocation( iCrabAttachment, pAnimatingGib );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Server ragdolls don't have a valid origin on spawn, so we have to use the origin originally passed
|
||||||
|
if( !HeadcrabFits( pAnimatingGib, m_bForceServerRagdoll ? &vecOrigin : NULL ) )
|
||||||
|
#else
|
||||||
if( !HeadcrabFits(pAnimatingGib) )
|
if( !HeadcrabFits(pAnimatingGib) )
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
UTIL_Remove(pGib);
|
UTIL_Remove(pGib);
|
||||||
return;
|
return;
|
||||||
@ -2552,11 +2610,20 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve
|
|||||||
|
|
||||||
if( UTIL_ShouldShowBlood(BLOOD_COLOR_YELLOW) )
|
if( UTIL_ShouldShowBlood(BLOOD_COLOR_YELLOW) )
|
||||||
{
|
{
|
||||||
UTIL_BloodImpact( pGib->WorldSpaceCenter(), Vector(0,0,1), BLOOD_COLOR_YELLOW, 1 );
|
Vector vecGibCenter;
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Server ragdolls don't have a valid origin on spawn, so we have to use the origin originally passed
|
||||||
|
if (m_bForceServerRagdoll)
|
||||||
|
vecGibCenter = vecOrigin;
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
vecGibCenter = pGib->WorldSpaceCenter();
|
||||||
|
|
||||||
|
UTIL_BloodImpact( vecGibCenter, Vector(0,0,1), BLOOD_COLOR_YELLOW, 1 );
|
||||||
|
|
||||||
for ( int i = 0 ; i < 3 ; i++ )
|
for ( int i = 0 ; i < 3 ; i++ )
|
||||||
{
|
{
|
||||||
Vector vecSpot = pGib->WorldSpaceCenter();
|
Vector vecSpot = vecGibCenter;
|
||||||
|
|
||||||
vecSpot.x += random->RandomFloat( -8, 8 );
|
vecSpot.x += random->RandomFloat( -8, 8 );
|
||||||
vecSpot.y += random->RandomFloat( -8, 8 );
|
vecSpot.y += random->RandomFloat( -8, 8 );
|
||||||
@ -2590,6 +2657,9 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve
|
|||||||
// Inherit some misc. properties
|
// Inherit some misc. properties
|
||||||
pCrab->m_bForceServerRagdoll = m_bForceServerRagdoll;
|
pCrab->m_bForceServerRagdoll = m_bForceServerRagdoll;
|
||||||
pCrab->m_iViewHideFlags = m_iViewHideFlags;
|
pCrab->m_iViewHideFlags = m_iViewHideFlags;
|
||||||
|
|
||||||
|
// Add response context for companion response (more reliable than checking for post-death zombie entity)
|
||||||
|
pCrab->AddContext( "from_zombie", "1", 2.0f );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// make me the crab's owner to avoid collision issues
|
// make me the crab's owner to avoid collision issues
|
||||||
|
@ -151,6 +151,8 @@ public:
|
|||||||
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
||||||
virtual float GetReactionDelay( CBaseEntity *pEnemy ) { return 0.0; }
|
virtual float GetReactionDelay( CBaseEntity *pEnemy ) { return 0.0; }
|
||||||
|
|
||||||
|
bool CanFlinch( void );
|
||||||
|
|
||||||
virtual int SelectSchedule ( void );
|
virtual int SelectSchedule ( void );
|
||||||
virtual int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode );
|
virtual int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode );
|
||||||
virtual void BuildScheduleTestBits( void );
|
virtual void BuildScheduleTestBits( void );
|
||||||
@ -186,9 +188,10 @@ public:
|
|||||||
// Headcrab releasing/breaking apart
|
// Headcrab releasing/breaking apart
|
||||||
void RemoveHead( void );
|
void RemoveHead( void );
|
||||||
virtual void SetZombieModel( void ) { };
|
virtual void SetZombieModel( void ) { };
|
||||||
|
virtual void SetModel( const char *szModelName );
|
||||||
virtual void BecomeTorso( const Vector &vecTorsoForce, const Vector &vecLegsForce );
|
virtual void BecomeTorso( const Vector &vecTorsoForce, const Vector &vecLegsForce );
|
||||||
virtual bool CanBecomeLiveTorso() { return false; }
|
virtual bool CanBecomeLiveTorso() { return false; }
|
||||||
virtual bool HeadcrabFits( CBaseAnimating *pCrab );
|
virtual bool HeadcrabFits( CBaseAnimating *pCrab, const Vector *vecOrigin = NULL );
|
||||||
void ReleaseHeadcrab( const Vector &vecOrigin, const Vector &vecVelocity, bool fRemoveHead, bool fRagdollBody, bool fRagdollCrab = false );
|
void ReleaseHeadcrab( const Vector &vecOrigin, const Vector &vecVelocity, bool fRemoveHead, bool fRagdollBody, bool fRagdollCrab = false );
|
||||||
void SetHeadcrabSpawnLocation( int iCrabAttachment, CBaseAnimating *pCrab );
|
void SetHeadcrabSpawnLocation( int iCrabAttachment, CBaseAnimating *pCrab );
|
||||||
|
|
||||||
|
@ -1092,10 +1092,14 @@ void CNPC_Alyx::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void CNPC_Alyx::EnemyIgnited( CAI_BaseNPC *pVictim )
|
void CNPC_Alyx::EnemyIgnited( CAI_BaseNPC *pVictim )
|
||||||
{
|
{
|
||||||
|
#ifdef MAPBASE
|
||||||
|
BaseClass::EnemyIgnited( pVictim );
|
||||||
|
#else
|
||||||
if ( FVisible( pVictim ) )
|
if ( FVisible( pVictim ) )
|
||||||
{
|
{
|
||||||
SpeakIfAllowed( TLK_ENEMY_BURNING );
|
SpeakIfAllowed( TLK_ENEMY_BURNING );
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -1252,6 +1256,7 @@ void CNPC_Alyx::DoCustomSpeechAI( void )
|
|||||||
|
|
||||||
CBasePlayer *pPlayer = AI_GetSinglePlayer();
|
CBasePlayer *pPlayer = AI_GetSinglePlayer();
|
||||||
|
|
||||||
|
#ifndef MAPBASE // Ported to CNPC_PlayerCompanion
|
||||||
if ( HasCondition(COND_NEW_ENEMY) && GetEnemy() )
|
if ( HasCondition(COND_NEW_ENEMY) && GetEnemy() )
|
||||||
{
|
{
|
||||||
if ( GetEnemy()->Classify() == CLASS_HEADCRAB )
|
if ( GetEnemy()->Classify() == CLASS_HEADCRAB )
|
||||||
@ -1278,6 +1283,7 @@ void CNPC_Alyx::DoCustomSpeechAI( void )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Darkness mode speech
|
// Darkness mode speech
|
||||||
ClearCondition( COND_ALYX_IN_DARK );
|
ClearCondition( COND_ALYX_IN_DARK );
|
||||||
@ -1917,6 +1923,7 @@ int CNPC_Alyx::SelectSchedule( void )
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
int CNPC_Alyx::SelectScheduleDanger( void )
|
int CNPC_Alyx::SelectScheduleDanger( void )
|
||||||
{
|
{
|
||||||
|
#ifndef MAPBASE
|
||||||
if( HasCondition( COND_HEAR_DANGER ) )
|
if( HasCondition( COND_HEAR_DANGER ) )
|
||||||
{
|
{
|
||||||
CSound *pSound;
|
CSound *pSound;
|
||||||
@ -1929,6 +1936,7 @@ int CNPC_Alyx::SelectScheduleDanger( void )
|
|||||||
SpeakIfAllowed( TLK_DANGER_ZOMBINE_GRENADE );
|
SpeakIfAllowed( TLK_DANGER_ZOMBINE_GRENADE );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return BaseClass::SelectScheduleDanger();
|
return BaseClass::SelectScheduleDanger();
|
||||||
}
|
}
|
||||||
|
@ -412,7 +412,6 @@ ScriptHook_t CNPC_Citizen::g_Hook_SelectModel;
|
|||||||
|
|
||||||
BEGIN_ENT_SCRIPTDESC( CNPC_Citizen, CAI_BaseActor, "npc_citizen from Half-Life 2" )
|
BEGIN_ENT_SCRIPTDESC( CNPC_Citizen, CAI_BaseActor, "npc_citizen from Half-Life 2" )
|
||||||
|
|
||||||
DEFINE_SCRIPTFUNC( IsMedic, "Returns true if this citizen is a medic." )
|
|
||||||
DEFINE_SCRIPTFUNC( IsAmmoResupplier, "Returns true if this citizen is an ammo resupplier." )
|
DEFINE_SCRIPTFUNC( IsAmmoResupplier, "Returns true if this citizen is an ammo resupplier." )
|
||||||
DEFINE_SCRIPTFUNC( CanHeal, "Returns true if this citizen is a medic or ammo resupplier currently able to heal/give ammo." )
|
DEFINE_SCRIPTFUNC( CanHeal, "Returns true if this citizen is a medic or ammo resupplier currently able to heal/give ammo." )
|
||||||
|
|
||||||
|
@ -47,6 +47,8 @@ ConVar npc_combine_protected_run( "npc_combine_protected_run", "0", FCVAR_NONE,
|
|||||||
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_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." );
|
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." );
|
||||||
|
|
||||||
|
ConVar npc_combine_fixed_shootpos( "npc_combine_fixed_shootpos", "0", FCVAR_NONE, "Mapbase: Toggles fixed Combine soldier shoot position." );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define COMBINE_SKIN_DEFAULT 0
|
#define COMBINE_SKIN_DEFAULT 0
|
||||||
@ -2959,6 +2961,28 @@ Vector CNPC_Combine::Weapon_ShootPosition( )
|
|||||||
// FIXME: rename this "estimated" since it's not based on animation
|
// FIXME: rename this "estimated" since it's not based on animation
|
||||||
// FIXME: the orientation won't be correct when testing from arbitary positions for arbitary angles
|
// FIXME: the orientation won't be correct when testing from arbitary positions for arbitary angles
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// HACKHACK: This weapon shoot position code does not work properly when in close range, causing the aim
|
||||||
|
// to drift to the left as the enemy gets closer to it.
|
||||||
|
// This problem is usually bearable for regular combat, but it causes dynamic interaction yaw to be offset
|
||||||
|
// as well, preventing most from ever being triggered.
|
||||||
|
// Ideally, this should be fixed from the root cause, but due to the sensitivity of such a change, this is
|
||||||
|
// currently being tied to a cvar which is off by default.
|
||||||
|
//
|
||||||
|
// If the cvar is disabled but the soldier has valid interactions on its current enemy, then a separate hack
|
||||||
|
// will still attempt to correct the drift as the enemy gets closer.
|
||||||
|
if ( npc_combine_fixed_shootpos.GetBool() )
|
||||||
|
{
|
||||||
|
right *= 0.0f;
|
||||||
|
}
|
||||||
|
else if ( HasValidInteractionsOnCurrentEnemy() )
|
||||||
|
{
|
||||||
|
float flDistSqr = GetEnemy()->WorldSpaceCenter().DistToSqr( WorldSpaceCenter() );
|
||||||
|
if (flDistSqr < Square( 128.0f ))
|
||||||
|
right *= (flDistSqr / Square( 128.0f ));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if ( bStanding )
|
if ( bStanding )
|
||||||
{
|
{
|
||||||
if( HasShotgun() )
|
if( HasShotgun() )
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
#include "mapbase/GlobalStrings.h"
|
#include "mapbase/GlobalStrings.h"
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
#include "vehicle_base.h"
|
#include "vehicle_base.h"
|
||||||
|
#include "npc_headcrab.h"
|
||||||
|
#include "npc_BaseZombie.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ConVar ai_debug_readiness("ai_debug_readiness", "0" );
|
ConVar ai_debug_readiness("ai_debug_readiness", "0" );
|
||||||
@ -640,6 +642,55 @@ void CNPC_PlayerCompanion::DoCustomSpeechAI( void )
|
|||||||
{
|
{
|
||||||
SpeakIfAllowed( TLK_PLDEAD );
|
SpeakIfAllowed( TLK_PLDEAD );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Unique new enemy concepts ported from Alyx
|
||||||
|
// The casts have been changed to dynamic_cast due to the risk of non-CBaseHeadcrab/CNPC_BaseZombie enemies using those classes
|
||||||
|
if ( HasCondition(COND_NEW_ENEMY) && GetEnemy() )
|
||||||
|
{
|
||||||
|
int nClass = GetEnemy()->Classify();
|
||||||
|
if ( nClass == CLASS_HEADCRAB )
|
||||||
|
{
|
||||||
|
CBaseHeadcrab *pHC = dynamic_cast<CBaseHeadcrab*>(GetEnemy());
|
||||||
|
if ( pHC )
|
||||||
|
{
|
||||||
|
// If we see a headcrab for the first time as he's jumping at me, freak out!
|
||||||
|
if ( ( GetEnemy()->GetEnemy() == this ) && pHC->IsJumping() && gpGlobals->curtime - GetEnemies()->FirstTimeSeen(GetEnemy()) < 0.5 )
|
||||||
|
{
|
||||||
|
SpeakIfAllowed( "TLK_SPOTTED_INCOMING_HEADCRAB" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If we see a headcrab leaving a zombie that just died, mention it
|
||||||
|
// (Note that this is now a response context since some death types remove the zombie instantly)
|
||||||
|
int nContext = pHC->FindContextByName( "from_zombie" );
|
||||||
|
if ( nContext > -1 && !ContextExpired( nContext ) ) // pHC->GetOwnerEntity() && ( pHC->GetOwnerEntity()->Classify() == CLASS_ZOMBIE ) && !pHC->GetOwnerEntity()->IsAlive()
|
||||||
|
{
|
||||||
|
SpeakIfAllowed( "TLK_SPOTTED_HEADCRAB_LEAVING_ZOMBIE" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( nClass == CLASS_ZOMBIE )
|
||||||
|
{
|
||||||
|
CNPC_BaseZombie *pZombie = dynamic_cast<CNPC_BaseZombie*>(GetEnemy());
|
||||||
|
// If we see a zombie getting up, mention it
|
||||||
|
if ( pZombie && pZombie->IsGettingUp() )
|
||||||
|
{
|
||||||
|
SpeakIfAllowed( "TLK_SPOTTED_ZOMBIE_WAKEUP" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( gpGlobals->curtime - GetEnemies()->TimeAtFirstHand( GetEnemy() ) <= 1.0f && nClass != CLASS_BULLSEYE )
|
||||||
|
{
|
||||||
|
// New concept which did not originate from Alyx, but is in the same category as the above concepts.
|
||||||
|
// This is meant to be used when a new enemy enters the arena while combat is already in progress.
|
||||||
|
// (Note that this can still trigger when combat begins, but unlike TLK_STARTCOMBAT, it has no delay
|
||||||
|
// between combat engagements.)
|
||||||
|
SpeakIfAllowed( TLK_NEW_ENEMY );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -910,8 +961,21 @@ int CNPC_PlayerCompanion::SelectScheduleDanger()
|
|||||||
|
|
||||||
if ( pSound && (pSound->m_iType & SOUND_DANGER) )
|
if ( pSound && (pSound->m_iType & SOUND_DANGER) )
|
||||||
{
|
{
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if ( pSound->SoundChannel() == SOUNDENT_CHANNEL_ZOMBINE_GRENADE )
|
||||||
|
{
|
||||||
|
SetSpeechTarget( pSound->m_hOwner );
|
||||||
|
SpeakIfAllowed( TLK_DANGER_ZOMBINE_GRENADE );
|
||||||
|
}
|
||||||
|
else if (!(pSound->SoundContext() & (SOUND_CONTEXT_MORTAR | SOUND_CONTEXT_FROM_SNIPER)) || IsOkToCombatSpeak())
|
||||||
|
{
|
||||||
|
SetSpeechTarget( pSound->m_hOwner );
|
||||||
|
SpeakIfAllowed( TLK_DANGER );
|
||||||
|
}
|
||||||
|
#else
|
||||||
if ( !(pSound->SoundContext() & (SOUND_CONTEXT_MORTAR|SOUND_CONTEXT_FROM_SNIPER)) || IsOkToCombatSpeak() )
|
if ( !(pSound->SoundContext() & (SOUND_CONTEXT_MORTAR|SOUND_CONTEXT_FROM_SNIPER)) || IsOkToCombatSpeak() )
|
||||||
SpeakIfAllowed( TLK_DANGER );
|
SpeakIfAllowed( TLK_DANGER );
|
||||||
|
#endif
|
||||||
|
|
||||||
if ( HasCondition( COND_PC_SAFE_FROM_MORTAR ) )
|
if ( HasCondition( COND_PC_SAFE_FROM_MORTAR ) )
|
||||||
{
|
{
|
||||||
@ -4309,6 +4373,20 @@ void CNPC_PlayerCompanion::Event_KilledOther( CBaseEntity *pVictim, const CTakeD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Purpose: Called by enemy NPC's when they are ignited
|
||||||
|
// Input : pVictim - entity that was ignited
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void CNPC_PlayerCompanion::EnemyIgnited( CAI_BaseNPC *pVictim )
|
||||||
|
{
|
||||||
|
BaseClass::EnemyIgnited( pVictim );
|
||||||
|
|
||||||
|
if ( FVisible( pVictim ) )
|
||||||
|
{
|
||||||
|
SpeakIfAllowed( TLK_ENEMY_BURNING );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Purpose: Handles custom combat speech stuff ported from Alyx.
|
// Purpose: Handles custom combat speech stuff ported from Alyx.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -4376,6 +4454,21 @@ void CNPC_PlayerCompanion::DoCustomCombatAI( void )
|
|||||||
{
|
{
|
||||||
SpeakIfAllowed( TLK_MANY_ENEMIES );
|
SpeakIfAllowed( TLK_MANY_ENEMIES );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're not currently attacking or vulnerable, try speaking
|
||||||
|
else if ( gpGlobals->curtime - GetLastAttackTime() > 1.0f && (!HasCondition( COND_SEE_ENEMY ) || IsCurSchedule( SCHED_RELOAD ) || IsCurSchedule( SCHED_HIDE_AND_RELOAD )) )
|
||||||
|
{
|
||||||
|
int chance = ( IsMoving() ) ? 20 : 3;
|
||||||
|
if ( ShouldSpeakRandom( TLK_COMBAT_IDLE, chance ) )
|
||||||
|
{
|
||||||
|
AI_CriteriaSet modifiers;
|
||||||
|
|
||||||
|
modifiers.AppendCriteria( "in_cover", HasMemory( bits_MEMORY_INCOVER ) ? "1" : "0" );
|
||||||
|
modifiers.AppendCriteria( "lastseenenemy", UTIL_VarArgs( "%f", gpGlobals->curtime - GetEnemyLastTimeSeen() ) );
|
||||||
|
|
||||||
|
SpeakIfAllowed( TLK_COMBAT_IDLE, modifiers );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -242,6 +242,7 @@ public:
|
|||||||
|
|
||||||
virtual void Event_Killed( const CTakeDamageInfo &info );
|
virtual void Event_Killed( const CTakeDamageInfo &info );
|
||||||
virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info );
|
virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info );
|
||||||
|
virtual void EnemyIgnited( CAI_BaseNPC *pVictim );
|
||||||
virtual void DoCustomCombatAI( void );
|
virtual void DoCustomCombatAI( void );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -526,6 +526,10 @@ bool CWeaponPistol::Reload( void )
|
|||||||
return fRet;
|
return fRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
ConVar weapon_pistol_upwards_viewkick( "weapon_pistol_upwards_viewkick", "0" );
|
||||||
|
#endif
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Purpose:
|
// Purpose:
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -538,7 +542,11 @@ void CWeaponPistol::AddViewKick( void )
|
|||||||
|
|
||||||
QAngle viewPunch;
|
QAngle viewPunch;
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
viewPunch.x = weapon_pistol_upwards_viewkick.GetBool() ? random->RandomFloat( -0.5f, -0.25f ) : random->RandomFloat( 0.25f, 0.5f );
|
||||||
|
#else
|
||||||
viewPunch.x = random->RandomFloat( 0.25f, 0.5f );
|
viewPunch.x = random->RandomFloat( 0.25f, 0.5f );
|
||||||
|
#endif
|
||||||
viewPunch.y = random->RandomFloat( -.6f, .6f );
|
viewPunch.y = random->RandomFloat( -.6f, .6f );
|
||||||
viewPunch.z = 0.0f;
|
viewPunch.z = 0.0f;
|
||||||
|
|
||||||
|
@ -1557,6 +1557,16 @@ CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, con
|
|||||||
pRagdoll->CollisionProp()->SetCollisionBounds( mins, maxs );
|
pRagdoll->CollisionProp()->SetCollisionBounds( mins, maxs );
|
||||||
|
|
||||||
#ifdef MAPBASE
|
#ifdef MAPBASE
|
||||||
|
// If this was a NPC running a dynamic interaction, disable collisions with the interaction partner
|
||||||
|
if (pAnimating->IsNPC() /*&& pAnimating->MyNPCPointer()->IsRunningDynamicInteraction()*/)
|
||||||
|
{
|
||||||
|
CAI_BaseNPC *pNPC = pAnimating->MyNPCPointer();
|
||||||
|
if (pNPC->GetInteractionPartner() && pNPC->GetInteractionPartner()->VPhysicsGetObject())
|
||||||
|
{
|
||||||
|
PhysDisableEntityCollisions( pRagdoll, pNPC->GetInteractionPartner() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
variant_t variant;
|
variant_t variant;
|
||||||
variant.SetEntity(pRagdoll);
|
variant.SetEntity(pRagdoll);
|
||||||
pAnimating->FireNamedOutput("OnServerRagdoll", variant, pRagdoll, pAnimating);
|
pAnimating->FireNamedOutput("OnServerRagdoll", variant, pRagdoll, pAnimating);
|
||||||
|
@ -7734,6 +7734,93 @@ void CBasePlayer::ResetAutoaim( void )
|
|||||||
m_fOnTarget = false;
|
m_fOnTarget = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
ConVar player_debug_probable_aim_target( "player_debug_probable_aim_target", "0", FCVAR_CHEAT, "" );
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Purpose:
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
CBaseEntity *CBasePlayer::GetProbableAimTarget( const Vector &vecSrc, const Vector &vecDir )
|
||||||
|
{
|
||||||
|
trace_t tr;
|
||||||
|
CBaseEntity *pIgnore = NULL;
|
||||||
|
if (IsInAVehicle())
|
||||||
|
pIgnore = GetVehicleEntity();
|
||||||
|
|
||||||
|
CTraceFilterSkipTwoEntities traceFilter( this, pIgnore, COLLISION_GROUP_NONE );
|
||||||
|
|
||||||
|
// Based on dot product and distance
|
||||||
|
// If we aim directly at something, only return it if there's not a larger entity slightly off-center
|
||||||
|
// Should be weighted based on whether an entity is a NPC, etc.
|
||||||
|
CBaseEntity *pBestEnt = NULL;
|
||||||
|
float flBestWeight = 0.0f;
|
||||||
|
for (CBaseEntity *pEntity = UTIL_EntitiesInPVS( this, NULL ); pEntity; pEntity = UTIL_EntitiesInPVS( this, pEntity ))
|
||||||
|
{
|
||||||
|
// Combat characters can be unviewable if they just died
|
||||||
|
if (!pEntity->IsViewable() && !pEntity->IsCombatCharacter())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (pEntity == this || pEntity->GetMoveParent() == this || pEntity == GetVehicleEntity())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Vector vecEntDir = (pEntity->EyePosition() - vecSrc);
|
||||||
|
float flDot = DotProduct( vecEntDir.Normalized(), vecDir);
|
||||||
|
|
||||||
|
if (flDot < m_flFieldOfView)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Make sure we can see it
|
||||||
|
UTIL_TraceLine( vecSrc, pEntity->EyePosition(), MASK_SHOT, &traceFilter, &tr );
|
||||||
|
if (tr.m_pEnt != pEntity)
|
||||||
|
{
|
||||||
|
if (pEntity->IsCombatCharacter())
|
||||||
|
{
|
||||||
|
// Trace between centers as well just in case our eyes are blocked
|
||||||
|
UTIL_TraceLine( WorldSpaceCenter(), pEntity->WorldSpaceCenter(), MASK_SHOT, &traceFilter, &tr );
|
||||||
|
if (tr.m_pEnt != pEntity)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float flWeight = flDot - (vecEntDir.LengthSqr() / Square( 2048.0f ));
|
||||||
|
|
||||||
|
if (pEntity->IsCombatCharacter())
|
||||||
|
{
|
||||||
|
// Hostile NPCs are more likely targets
|
||||||
|
if (IRelationType( pEntity ) <= D_FR)
|
||||||
|
flWeight += 0.5f;
|
||||||
|
}
|
||||||
|
else if (pEntity->GetFlags() & FL_AIMTARGET)
|
||||||
|
{
|
||||||
|
// FL_AIMTARGET is often used for props like explosive barrels
|
||||||
|
flWeight += 0.25f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player_debug_probable_aim_target.GetBool())
|
||||||
|
{
|
||||||
|
float flWeightClamped = 1.0f - RemapValClamped( flWeight, -2.0f, 2.0f, 0.0f, 1.0f );
|
||||||
|
pEntity->EntityText( 0, UTIL_VarArgs( "%f", flWeight ), 2.0f, flWeightClamped * 255.0f, 255.0f, flWeightClamped * 255.0f, 255 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flWeight > flBestWeight)
|
||||||
|
{
|
||||||
|
pBestEnt = pEntity;
|
||||||
|
flBestWeight = flWeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player_debug_probable_aim_target.GetBool())
|
||||||
|
{
|
||||||
|
Msg( "Best probable aim target is %s\n", pBestEnt->GetDebugName() );
|
||||||
|
NDebugOverlay::EntityBounds( pBestEnt, 255, 100, 0, 0, 2.0f );
|
||||||
|
}
|
||||||
|
|
||||||
|
return pBestEnt;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// > Weapon stuff
|
// > Weapon stuff
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
@ -608,6 +608,11 @@ public:
|
|||||||
virtual bool ShouldAutoaim( void );
|
virtual bool ShouldAutoaim( void );
|
||||||
void SetTargetInfo( Vector &vecSrc, float flDist );
|
void SetTargetInfo( Vector &vecSrc, float flDist );
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Tries to figure out what the player is trying to aim at
|
||||||
|
CBaseEntity *GetProbableAimTarget( const Vector &vecSrc, const Vector &vecDir );
|
||||||
|
#endif
|
||||||
|
|
||||||
void SetViewEntity( CBaseEntity *pEntity );
|
void SetViewEntity( CBaseEntity *pEntity );
|
||||||
CBaseEntity *GetViewEntity( void ) { return m_hViewEntity; }
|
CBaseEntity *GetViewEntity( void ) { return m_hViewEntity; }
|
||||||
|
|
||||||
|
@ -1396,11 +1396,31 @@ void CAI_ScriptedSequence::ModifyScriptedAutoMovement( Vector *vecNewPos )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VMatrix matInteractionPosition = m_matInteractionPosition;
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Account for our own sequence movement
|
||||||
|
pAnimating = m_hTargetEnt->GetBaseAnimating();
|
||||||
|
if (pAnimating)
|
||||||
|
{
|
||||||
|
Vector vecDeltaPos;
|
||||||
|
QAngle angDeltaAngles;
|
||||||
|
|
||||||
|
pAnimating->GetSequenceMovement( pAnimating->GetSequence(), 0.0f, pAnimating->GetCycle(), vecDeltaPos, angDeltaAngles );
|
||||||
|
if (!vecDeltaPos.IsZero())
|
||||||
|
{
|
||||||
|
VMatrix matLocalMovement;
|
||||||
|
matLocalMovement.SetupMatrixOrgAngles( vecDeltaPos, angDeltaAngles );
|
||||||
|
MatrixMultiply( m_matInteractionPosition, matLocalMovement, matInteractionPosition );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// We've been asked to maintain a specific position relative to the other NPC
|
// We've been asked to maintain a specific position relative to the other NPC
|
||||||
// we're interacting with. Lerp towards the relative position.
|
// we're interacting with. Lerp towards the relative position.
|
||||||
VMatrix matMeToWorld, matLocalToWorld;
|
VMatrix matMeToWorld, matLocalToWorld;
|
||||||
matMeToWorld.SetupMatrixOrgAngles( vecRelativeOrigin, angRelativeAngles );
|
matMeToWorld.SetupMatrixOrgAngles( vecRelativeOrigin, angRelativeAngles );
|
||||||
MatrixMultiply( matMeToWorld, m_matInteractionPosition, matLocalToWorld );
|
MatrixMultiply( matMeToWorld, matInteractionPosition, matLocalToWorld );
|
||||||
|
|
||||||
// Get the desired NPC position in worldspace
|
// Get the desired NPC position in worldspace
|
||||||
Vector vecOrigin;
|
Vector vecOrigin;
|
||||||
|
@ -83,6 +83,10 @@ ConVar ai_shot_bias_min( "ai_shot_bias_min", "-1.0", FCVAR_REPLICATED );
|
|||||||
ConVar ai_shot_bias_max( "ai_shot_bias_max", "1.0", FCVAR_REPLICATED );
|
ConVar ai_shot_bias_max( "ai_shot_bias_max", "1.0", FCVAR_REPLICATED );
|
||||||
ConVar ai_debug_shoot_positions( "ai_debug_shoot_positions", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
|
ConVar ai_debug_shoot_positions( "ai_debug_shoot_positions", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
|
||||||
|
|
||||||
|
#if defined(MAPBASE) && defined(GAME_DLL)
|
||||||
|
ConVar ai_shot_notify_targets( "ai_shot_notify_targets", "0", FCVAR_NONE, "Allows fired bullets to notify the NPCs and players they are targeting, regardless of whether they hit them or not. Can be used for custom AI and speech." );
|
||||||
|
#endif
|
||||||
|
|
||||||
// Utility func to throttle rate at which the "reasonable position" spew goes out
|
// Utility func to throttle rate at which the "reasonable position" spew goes out
|
||||||
static double s_LastEntityReasonableEmitTime;
|
static double s_LastEntityReasonableEmitTime;
|
||||||
bool CheckEmitReasonablePhysicsSpew()
|
bool CheckEmitReasonablePhysicsSpew()
|
||||||
@ -2081,6 +2085,25 @@ void CBaseEntity::FireBullets( const FireBulletsInfo_t &info )
|
|||||||
CTakeDamageInfo dmgInfo( this, pAttacker, flCumulativeDamage, nDamageType );
|
CTakeDamageInfo dmgInfo( this, pAttacker, flCumulativeDamage, nDamageType );
|
||||||
gamestats->Event_WeaponHit( pPlayer, info.m_bPrimaryAttack, pPlayer->GetActiveWeapon()->GetClassname(), dmgInfo );
|
gamestats->Event_WeaponHit( pPlayer, info.m_bPrimaryAttack, pPlayer->GetActiveWeapon()->GetClassname(), dmgInfo );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if ( ai_shot_notify_targets.GetBool() )
|
||||||
|
{
|
||||||
|
if ( IsPlayer() )
|
||||||
|
{
|
||||||
|
// Look for probable target to notify of attack
|
||||||
|
CBaseEntity *pAimTarget = static_cast<CBasePlayer*>(this)->GetProbableAimTarget( info.m_vecSrc, info.m_vecDirShooting );
|
||||||
|
if ( pAimTarget && pAimTarget->IsCombatCharacter() )
|
||||||
|
{
|
||||||
|
pAimTarget->MyCombatCharacterPointer()->OnEnemyRangeAttackedMe( this, vecDir, vecEnd );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( GetEnemy() && GetEnemy()->IsCombatCharacter() )
|
||||||
|
{
|
||||||
|
GetEnemy()->MyCombatCharacterPointer()->OnEnemyRangeAttackedMe( this, vecDir, vecEnd );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,6 +233,11 @@ void EventList_RegisterSharedEvents( void )
|
|||||||
REGISTER_SHARED_ANIMEVENT( AE_SV_DUSTTRAIL, AE_TYPE_SERVER );
|
REGISTER_SHARED_ANIMEVENT( AE_SV_DUSTTRAIL, AE_TYPE_SERVER );
|
||||||
|
|
||||||
REGISTER_SHARED_ANIMEVENT( AE_CL_CREATE_PARTICLE_EFFECT, AE_TYPE_CLIENT );
|
REGISTER_SHARED_ANIMEVENT( AE_CL_CREATE_PARTICLE_EFFECT, AE_TYPE_CLIENT );
|
||||||
|
#ifdef MAPBASE // From Alien Swarm SDK
|
||||||
|
REGISTER_SHARED_ANIMEVENT( AE_CL_STOP_PARTICLE_EFFECT, AE_TYPE_CLIENT );
|
||||||
|
REGISTER_SHARED_ANIMEVENT( AE_CL_ADD_PARTICLE_EFFECT_CP, AE_TYPE_CLIENT );
|
||||||
|
//REGISTER_SHARED_ANIMEVENT( AE_CL_CREATE_PARTICLE_BRASS, AE_TYPE_CLIENT );
|
||||||
|
#endif
|
||||||
|
|
||||||
REGISTER_SHARED_ANIMEVENT( AE_RAGDOLL, AE_TYPE_SERVER );
|
REGISTER_SHARED_ANIMEVENT( AE_RAGDOLL, AE_TYPE_SERVER );
|
||||||
|
|
||||||
@ -252,5 +257,8 @@ void EventList_RegisterSharedEvents( void )
|
|||||||
#ifdef MAPBASE
|
#ifdef MAPBASE
|
||||||
REGISTER_SHARED_ANIMEVENT( AE_NPC_RESPONSE, AE_TYPE_SERVER );
|
REGISTER_SHARED_ANIMEVENT( AE_NPC_RESPONSE, AE_TYPE_SERVER );
|
||||||
REGISTER_SHARED_ANIMEVENT( AE_NPC_RESPONSE_FORCED, AE_TYPE_SERVER );
|
REGISTER_SHARED_ANIMEVENT( AE_NPC_RESPONSE_FORCED, AE_TYPE_SERVER );
|
||||||
|
|
||||||
|
REGISTER_SHARED_ANIMEVENT( AE_VSCRIPT_RUN, AE_TYPE_CLIENT | AE_TYPE_SERVER );
|
||||||
|
REGISTER_SHARED_ANIMEVENT( AE_VSCRIPT_RUN_FILE, AE_TYPE_CLIENT | AE_TYPE_SERVER );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
@ -69,6 +69,11 @@ typedef enum
|
|||||||
AE_SV_DUSTTRAIL,
|
AE_SV_DUSTTRAIL,
|
||||||
|
|
||||||
AE_CL_CREATE_PARTICLE_EFFECT,
|
AE_CL_CREATE_PARTICLE_EFFECT,
|
||||||
|
#ifdef MAPBASE // From Alien Swarm SDK
|
||||||
|
AE_CL_STOP_PARTICLE_EFFECT,
|
||||||
|
AE_CL_ADD_PARTICLE_EFFECT_CP,
|
||||||
|
//AE_CL_CREATE_PARTICLE_BRASS,
|
||||||
|
#endif
|
||||||
|
|
||||||
AE_RAGDOLL,
|
AE_RAGDOLL,
|
||||||
|
|
||||||
@ -88,6 +93,9 @@ typedef enum
|
|||||||
#ifdef MAPBASE
|
#ifdef MAPBASE
|
||||||
AE_NPC_RESPONSE, // Play a response system concept if we're not speaking
|
AE_NPC_RESPONSE, // Play a response system concept if we're not speaking
|
||||||
AE_NPC_RESPONSE_FORCED, // Always play a response system concept
|
AE_NPC_RESPONSE_FORCED, // Always play a response system concept
|
||||||
|
|
||||||
|
AE_VSCRIPT_RUN, // Run vscript code (server + client)
|
||||||
|
AE_VSCRIPT_RUN_FILE, // Run vscript file (server + client)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LAST_SHARED_ANIMEVENT,
|
LAST_SHARED_ANIMEVENT,
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
#include "igameevents.h"
|
#include "igameevents.h"
|
||||||
#include "engine/ivdebugoverlay.h"
|
#include "engine/ivdebugoverlay.h"
|
||||||
|
#include "icommandline.h"
|
||||||
|
|
||||||
#ifdef CLIENT_DLL
|
#ifdef CLIENT_DLL
|
||||||
#include "IEffects.h"
|
#include "IEffects.h"
|
||||||
@ -1094,7 +1095,7 @@ const char *CScriptReadWriteFile::FileRead( const char *szFile )
|
|||||||
char pszFullName[MAX_PATH];
|
char pszFullName[MAX_PATH];
|
||||||
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
|
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
|
||||||
|
|
||||||
if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
|
if ( !CommandLine()->FindParm( "-script_dotslash_read" ) && !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
|
||||||
{
|
{
|
||||||
DevWarning( 2, "Invalid file location : %s\n", szFile );
|
DevWarning( 2, "Invalid file location : %s\n", szFile );
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1143,7 +1144,7 @@ bool CScriptReadWriteFile::FileExists( const char *szFile )
|
|||||||
char pszFullName[MAX_PATH];
|
char pszFullName[MAX_PATH];
|
||||||
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
|
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
|
||||||
|
|
||||||
if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
|
if ( !CommandLine()->FindParm( "-script_dotslash_read" ) && !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
|
||||||
{
|
{
|
||||||
DevWarning( 2, "Invalid file location : %s\n", szFile );
|
DevWarning( 2, "Invalid file location : %s\n", szFile );
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1224,7 +1225,7 @@ HSCRIPT CScriptReadWriteFile::KeyValuesRead( const char *szFile )
|
|||||||
char pszFullName[MAX_PATH];
|
char pszFullName[MAX_PATH];
|
||||||
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
|
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
|
||||||
|
|
||||||
if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
|
if ( !CommandLine()->FindParm( "-script_dotslash_read" ) && !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
|
||||||
{
|
{
|
||||||
DevWarning( 2, "Invalid file location : %s\n", szFile );
|
DevWarning( 2, "Invalid file location : %s\n", szFile );
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -180,7 +180,7 @@ bool CWeaponCustomScripted::RunWeaponHook( ScriptHook_t &hook, HSCRIPT &cached,
|
|||||||
{
|
{
|
||||||
if ( !cached )
|
if ( !cached )
|
||||||
{
|
{
|
||||||
if ( hook.CanRunInScope( m_ScriptScope ) )
|
if ( m_ScriptScope.IsInitialized() && hook.CanRunInScope( m_ScriptScope ) )
|
||||||
{
|
{
|
||||||
cached = hook.m_hFunc;
|
cached = hook.m_hFunc;
|
||||||
}
|
}
|
||||||
@ -188,6 +188,7 @@ bool CWeaponCustomScripted::RunWeaponHook( ScriptHook_t &hook, HSCRIPT &cached,
|
|||||||
|
|
||||||
if (cached)
|
if (cached)
|
||||||
{
|
{
|
||||||
|
hook.m_hFunc = cached;
|
||||||
return hook.Call( m_ScriptScope, retVal, pArgs, false );
|
return hook.Call( m_ScriptScope, retVal, pArgs, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,7 +329,7 @@ void CWeaponCustomScripted::ItemPreFrame( void )
|
|||||||
{
|
{
|
||||||
SIMPLE_VOID_OVERRIDE( ItemPreFrame, NULL );
|
SIMPLE_VOID_OVERRIDE( ItemPreFrame, NULL );
|
||||||
|
|
||||||
BaseClass::ItemPostFrame();
|
BaseClass::ItemPreFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWeaponCustomScripted::ItemPostFrame( void )
|
void CWeaponCustomScripted::ItemPostFrame( void )
|
||||||
|
@ -118,15 +118,14 @@ class CSimpleCallChainer
|
|||||||
|
|
||||||
function PostScriptExecute()
|
function PostScriptExecute()
|
||||||
{
|
{
|
||||||
local func;
|
if ( prefix in scope )
|
||||||
try {
|
{
|
||||||
func = scope[prefix];
|
local func = scope[prefix];
|
||||||
} catch(e) {
|
if ( typeof func == "function" )
|
||||||
return;
|
{
|
||||||
|
chain.push(func);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (typeof(func) != "function")
|
|
||||||
return;
|
|
||||||
chain.push(func);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function Call()
|
function Call()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user