BANNED
Join Date: Nov 2009
Location: 9`su 09`n0n7e`r0f76a
|
12-17-2009
, 19:12
Re: [ES][OFF] Todas las Boludeces Aca!!
|
#98
|
PHP Code:
void CAI_BaseActor::StudioFrameAdvance () { // clear out head and eye latched values m_fLatchedPositions &= ~(HUMANOID_LATCHED_ALL);
BaseClass::StudioFrameAdvance(); }
void CAI_BaseActor::Precache() { BaseClass::Precache();
if ( NULL_STRING != m_iszExpressionOverride ) { PrecacheInstancedScene( STRING( m_iszExpressionOverride ) ); }
if ( m_iszIdleExpression != NULL_STRING ) { PrecacheInstancedScene( STRING(m_iszIdleExpression ) ); }
if ( m_iszCombatExpression != NULL_STRING ) { PrecacheInstancedScene( STRING(m_iszCombatExpression ) ); }
if ( m_iszAlertExpression != NULL_STRING ) { PrecacheInstancedScene( STRING(m_iszAlertExpression) ); }
if ( m_iszDeathExpression != NULL_STRING ) { PrecacheInstancedScene( STRING(m_iszDeathExpression) ); } }
static char const *g_ServerSideFlexControllers[] = { "body_rightleft", //"body_updown", //"body_tilt", "chest_rightleft", //"chest_updown", //"chest_tilt", "head_forwardback", "head_rightleft", "head_updown", "head_tilt",
"gesture_updown", "gesture_rightleft" };
//----------------------------------------------------------------------------- // Purpose: Static method // Input : *szName - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CAI_BaseActor::IsServerSideFlexController( char const *szName ) { int c = ARRAYSIZE( g_ServerSideFlexControllers ); for ( int i = 0; i < c; ++i ) { if ( !Q_stricmp( szName, g_ServerSideFlexControllers[ i ] ) ) return true; } return false; }
void CAI_BaseActor::SetModel( const char *szModelName ) { BaseClass::SetModel( szModelName );
//Init( m_ParameterBodyTransY, "body_trans_Y" ); //Init( m_ParameterBodyTransX, "body_trans_X" ); //Init( m_ParameterBodyLift, "body_lift" ); Init( m_ParameterBodyYaw, "body_yaw" ); //Init( m_ParameterBodyPitch, "body_pitch" ); //Init( m_ParameterBodyRoll, "body_roll" ); Init( m_ParameterSpineYaw, "spine_yaw" ); //Init( m_ParameterSpinePitch, "spine_pitch" ); //Init( m_ParameterSpineRoll, "spine_roll" ); Init( m_ParameterNeckTrans, "neck_trans" ); Init( m_ParameterHeadYaw, "head_yaw" ); Init( m_ParameterHeadPitch, "head_pitch" ); Init( m_ParameterHeadRoll, "head_roll" );
//Init( m_FlexweightMoveRightLeft, "move_rightleft" ); //Init( m_FlexweightMoveForwardBack, "move_forwardback" ); //Init( m_FlexweightMoveUpDown, "move_updown" ); Init( m_FlexweightBodyRightLeft, "body_rightleft" ); //Init( m_FlexweightBodyUpDown, "body_updown" ); //Init( m_FlexweightBodyTilt, "body_tilt" ); Init( m_FlexweightChestRightLeft, "chest_rightleft" ); //Init( m_FlexweightChestUpDown, "chest_updown" ); //Init( m_FlexweightChestTilt, "chest_tilt" ); Init( m_FlexweightHeadForwardBack, "head_forwardback" ); Init( m_FlexweightHeadRightLeft, "head_rightleft" ); Init( m_FlexweightHeadUpDown, "head_updown" ); Init( m_FlexweightHeadTilt, "head_tilt" );
Init( m_ParameterGestureHeight, "gesture_height" ); Init( m_ParameterGestureWidth, "gesture_width" ); Init( m_FlexweightGestureUpDown, "gesture_updown" ); Init( m_FlexweightGestureRightLeft, "gesture_rightleft" ); }
//----------------------------------------------------------------------------- // Purpose: //-----------------------------------------------------------------------------
bool CAI_BaseActor::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget ) { Assert( info ); Assert( info->m_pScene ); Assert( info->m_pEvent );
// FIXME: this code looks duplicated switch ( info->m_pEvent->GetType() ) { case CChoreoEvent::FACE: { return BaseClass::StartSceneEvent( info, scene, event, actor, pTarget ); } break;
case CChoreoEvent::GENERIC: { if (stricmp( event->GetParameters(), "AI_BLINK") == 0) { info->m_nType = SCENE_AI_BLINK; // blink eyes Blink(); // don't blink for duration, or next random blink time float flDuration = (event->GetEndTime() - scene->GetTime()); m_flBlinktime = gpGlobals->curtime + max( flDuration, random->RandomFloat( 1.5, 4.5 ) ); } else if (stricmp( event->GetParameters(), "AI_HOLSTER") == 0) { // FIXME: temp code for test info->m_nType = SCENE_AI_HOLSTER; info->m_iLayer = HolsterWeapon(); return true; } else if (stricmp( event->GetParameters(), "AI_UNHOLSTER") == 0) { // FIXME: temp code for test info->m_nType = SCENE_AI_UNHOLSTER; info->m_iLayer = UnholsterWeapon(); return true; } else if (stricmp( event->GetParameters(), "AI_AIM") == 0) { info->m_nType = SCENE_AI_AIM; info->m_hTarget = pTarget; } else if (stricmp( event->GetParameters(), "AI_RANDOMLOOK") == 0) { info->m_nType = SCENE_AI_RANDOMLOOK; info->m_flNext = 0.0; } else if (stricmp( event->GetParameters(), "AI_RANDOMFACEFLEX") == 0) { info->m_nType = SCENE_AI_RANDOMFACEFLEX; info->m_flNext = 0.0; info->InitWeight( this ); } else if (stricmp( event->GetParameters(), "AI_RANDOMHEADFLEX") == 0) { info->m_nType = SCENE_AI_RANDOMHEADFLEX; info->m_flNext = 0.0; } else if (stricmp( event->GetParameters(), "AI_IGNORECOLLISION") == 0) { CBaseEntity *pTarget = FindNamedEntity( event->GetParameters2( ) );
if (pTarget) { info->m_nType = SCENE_AI_IGNORECOLLISION; info->m_hTarget = pTarget; float remaining = event->GetEndTime() - scene->GetTime(); NPCPhysics_CreateSolver( this, pTarget, true, remaining ); info->m_flNext = gpGlobals->curtime + remaining; return true; } else { Warning( "CSceneEntity %s unable to find actor named \"%s\"\n", scene->GetFilename(), event->GetParameters2() ); return false; } } else if (stricmp( event->GetParameters(), "AI_DISABLEAI") == 0) { info->m_nType = SCENE_AI_DISABLEAI; } else { return BaseClass::StartSceneEvent( info, scene, event, actor, pTarget ); } return true; } break;
default: return BaseClass::StartSceneEvent( info, scene, event, actor, pTarget ); } }
bool CAI_BaseActor::ProcessSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event ) { Assert( info ); Assert( info->m_pScene ); Assert( info->m_pEvent );
// FIXME: this code looks duplicated switch ( info->m_pEvent->GetType() ) { case CChoreoEvent::FACE: { // make sure target exists if (info->m_hTarget == NULL) return false;
bool bInScene = false; // lockbodyfacing is designed to run on top of both normal AI and on top of // scripted_sequences. By allowing torso turns during post-idles, pre-idles, // act-busy's, scripted_sequences, normal AI movements, etc., it increases // the functionality of those AI features without breaking their assuptions // that the entity won't be made to "turn" by something outside of those // AI's control. // lockbody facing is also usefull when npcs are moving and you want them to turn // towards something but still walk in the direction of travel. if (!event->IsLockBodyFacing()) bInScene = EnterSceneSequence( scene, event, true );
// make sure we're still able to play this command if (!info->m_bStarted) { info->m_flInitialYaw = GetLocalAngles().y; info->m_flTargetYaw = info->m_flInitialYaw; info->m_flFacingYaw = info->m_flInitialYaw; if (IsMoving()) { info->m_flWeight = 1.0; } else { info->m_flWeight = 0.0; } }
// lock in place if aiming at self if (info->m_hTarget == this) { return true; }
if (!bInScene || info->m_bIsMoving != IsMoving()) { info->m_flInitialYaw = GetLocalAngles().y; } info->m_bIsMoving = IsMoving();
// Msg("%f : %f - %f\n", scene->GetTime(), event->GetStartTime(), event->GetEndTime() ); float flTime = clamp( scene->GetTime(), event->GetStartTime(), event->GetEndTime() - 0.1 ); float intensity = event->GetIntensity( flTime );
// clamp in-ramp to 0.5 seconds float flDuration = scene->GetTime() - event->GetStartTime(); float flMaxIntensity = flDuration < 0.5f ? SimpleSpline( flDuration / 0.5f ) : 1.0f; intensity = clamp( intensity, 0.0f, flMaxIntensity );
if (bInScene && info->m_bIsMoving) { info->m_flInitialYaw = GetLocalAngles().y; }
if (!event->IsLockBodyFacing()) { if (!info->m_bIsMoving && bInScene) { AccumulateIdealYaw( info->m_flFacingYaw, intensity ); } }
float diff; float dir; float flSpineYaw; float flBodyYaw; // move upper body to account for missing body yaw diff = UTIL_AngleDiff( info->m_flTargetYaw, GetLocalAngles().y ); if (diff < 0) { diff = -diff; dir = -1; } else { dir = 1; } flSpineYaw = min( diff, 30 ); flBodyYaw = min( diff - flSpineYaw, 30 ); m_goalSpineYaw = m_goalSpineYaw * (1.0 - intensity) + intensity * flSpineYaw * dir; m_goalBodyYaw = m_goalBodyYaw * (1.0 - intensity) + intensity * flBodyYaw * dir;
/* NDebugOverlay::YawArrow( GetAbsOrigin(), GetLocalAngles().y, 64, 16, 255, 255, 255, 0, true, 0.1 ); NDebugOverlay::YawArrow( GetAbsOrigin() + Vector( 0, 0, 8 ), GetLocalAngles().y + m_goalBodyYaw, 64, 16, 255, 128, 128, 0, true, 0.1 ); NDebugOverlay::YawArrow( GetAbsOrigin() + Vector( 0, 0, 16 ), GetLocalAngles().y + m_goalSpineYaw, 64, 16, 128, 255, 128, 0, true, 0.1 ); NDebugOverlay::YawArrow( GetAbsOrigin() + Vector( 0, 0, 24 ), info->m_flTargetYaw, 64, 16, 128, 128, 255, 0, true, 0.1 ); */
CAI_BaseNPC *pGoalNpc = info->m_hTarget->MyNPCPointer();
float goalYaw = GetLocalAngles().y; if ( pGoalNpc ) { goalYaw = CalcIdealYaw( pGoalNpc->FacingPosition() ); } else { goalYaw = CalcIdealYaw( info->m_hTarget->EyePosition() ); }
if (developer.GetInt() > 0 && scene_showfaceto.GetBool()) { NDebugOverlay::YawArrow( GetAbsOrigin() + Vector( 0, 0, 1 ), goalYaw, 8 + 32 * intensity, 8, 255, 255, 255, 0, true, 0.12 ); }
diff = UTIL_AngleDiff( goalYaw, info->m_flInitialYaw ) * intensity; dir = 1.0;
// debounce delta a bit info->m_flTargetYaw = UTIL_AngleMod( info->m_flInitialYaw + diff );
if (diff < 0) { diff = -diff; dir = -1; }
// calc how much to use the spine for turning float spineintensity = (1.0 - max( 0.0, (intensity - 0.5) / 0.5 )); // force spine to full if not in scene or locked if (!bInScene || event->IsLockBodyFacing() ) { spineintensity = 1.0; }
flSpineYaw = min( diff * spineintensity, 30 ); flBodyYaw = min( diff * spineintensity - flSpineYaw, 30 ); info->m_flFacingYaw = info->m_flInitialYaw + (diff - flBodyYaw - flSpineYaw) * dir;
if (!event->IsLockBodyFacing()) { AddFacingTarget( info->m_hTarget, intensity, 0.2 ); // facing targets are lagged by one frame } return true; } case CChoreoEvent::GENERIC: { switch(info->m_nType) { case SCENE_AI_BLINK: { // keep eyes not blinking for duration float flDuration = (event->GetEndTime() - scene->GetTime()); m_flBlinktime = max( m_flBlinktime, gpGlobals->curtime + flDuration ); } return true; case SCENE_AI_HOLSTER: { } return true; case SCENE_AI_UNHOLSTER: { } return true; case SCENE_AI_AIM: { if ( info->m_hTarget ) { Vector vecAimTargetLoc = info->m_hTarget->EyePosition(); Vector vecAimDir = vecAimTargetLoc - EyePosition();
VectorNormalize( vecAimDir ); SetAim( vecAimDir); } } return true; case SCENE_AI_RANDOMLOOK: { if (info->m_flNext < gpGlobals->curtime) { info->m_flNext = gpGlobals->curtime + PickLookTarget( m_syntheticLookQueue ) - 0.4; if (m_syntheticLookQueue.Count() > 0) { float flDuration = (event->GetEndTime() - scene->GetTime()); int i = m_syntheticLookQueue.Count() - 1; m_syntheticLookQueue[i].m_flEndTime = min( m_syntheticLookQueue[i].m_flEndTime, gpGlobals->curtime + flDuration ); m_syntheticLookQueue[i].m_flInterest = 0.1; } } } return true; case SCENE_AI_RANDOMFACEFLEX: return RandomFaceFlex( info, scene, event ); case SCENE_AI_RANDOMHEADFLEX: return true; case SCENE_AI_IGNORECOLLISION: if (info->m_hTarget && info->m_flNext < gpGlobals->curtime) { float remaining = event->GetEndTime() - scene->GetTime(); NPCPhysics_CreateSolver( this, info->m_hTarget, true, remaining ); info->m_flNext = gpGlobals->curtime + remaining; }
// FIXME: needs to handle scene pause return true; case SCENE_AI_DISABLEAI: if (!(GetState() == NPC_STATE_SCRIPT || IsCurSchedule( SCHED_SCENE_GENERIC )) ) { EnterSceneSequence( scene, event ); } return true; default: return false; } } break; default: return BaseClass::ProcessSceneEvent( info, scene, event ); } }
bool CAI_BaseActor::RandomFaceFlex( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event ) { if (info->m_flNext < gpGlobals->curtime) { const flexsettinghdr_t *pSettinghdr = ( const flexsettinghdr_t * )FindSceneFile( event->GetParameters2() ); if (pSettinghdr == NULL) { pSettinghdr = ( const flexsettinghdr_t * )FindSceneFile( "random" ); } if ( pSettinghdr ) { info->m_flNext = gpGlobals->curtime + random->RandomFloat( 0.3, 0.5 ) * (30.0 / pSettinghdr->numflexsettings);
flexsetting_t const *pSetting = NULL; pSetting = pSettinghdr->pSetting( random->RandomInt( 0, pSettinghdr->numflexsettings - 1 ) );
flexweight_t *pWeights = NULL; int truecount = pSetting->psetting( (byte *)pSettinghdr, 0, &pWeights ); if ( !pWeights ) return false;
int i; for (i = 0; i < truecount; i++, pWeights++) { // Translate to local flex controller // this is translating from the settings's local index to the models local index int index = FlexControllerLocalToGlobal( pSettinghdr, pWeights->key );
// FIXME: this is supposed to blend based on pWeight->influence, but the order is wrong... // float value = GetFlexWeight( index ) * (1 - scale * pWeights->influence) + scale * pWeights->weight;
// Add scaled weighting in to total m_flextarget[ index ] = pWeights->weight; } } else { return false; } }
// adjust intensity if this is a background scene and there's other flex animations playing float intensity = info->UpdateWeight( this ) * event->GetIntensity( scene->GetTime() );
// slide it up. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++) { float weight = GetFlexWeight( i );
if (weight != m_flextarget[i]) { float delta = (m_flextarget[i] - weight) / random->RandomFloat( 2.0, 4.0 ); weight = weight + delta * intensity; } weight = clamp( weight, 0.0f, 1.0f ); SetFlexWeight( i, weight ); }
return true; }
bool CAI_BaseActor::ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled ) { Assert( info ); Assert( info->m_pScene ); Assert( info->m_pEvent );
// FIXME: this code looks duplicated switch ( info->m_pEvent->GetType() ) { case CChoreoEvent::FACE: { return BaseClass::ClearSceneEvent( info, fastKill, canceled ); } break; default: return BaseClass::ClearSceneEvent( info, fastKill, canceled ); } }
bool CAI_BaseActor::CheckSceneEventCompletion( CSceneEventInfo *info, float currenttime, CChoreoScene *scene, CChoreoEvent *event ) { Assert( info ); Assert( info->m_pScene ); Assert( info->m_pEvent );
switch ( event->GetType() ) { case CChoreoEvent::GENERIC: { switch( info->m_nType) { case SCENE_AI_HOLSTER: case SCENE_AI_UNHOLSTER: { if (info->m_iLayer == -1) { return true; } float preload = event->GetEndTime() - currenttime; if (preload < 0) { return true; } float t = (1.0 - GetLayerCycle( info->m_iLayer )) * SequenceDuration( GetLayerSequence( info->m_iLayer ) );
return (t <= preload); } } } }
return BaseClass::CheckSceneEventCompletion( info, currenttime, scene, event ); }
//----------------------------------------------------------------------------- // Purpose: clear out latched state //----------------------------------------------------------------------------- void CAI_BaseActor::SetViewtarget( const Vector &viewtarget ) { // clear out eye latch m_fLatchedPositions &= ~HUMANOID_LATCHED_EYE;
BaseClass::SetViewtarget( viewtarget ); }
//----------------------------------------------------------------------------- // Purpose: Returns true position of the eyeballs //----------------------------------------------------------------------------- void CAI_BaseActor::UpdateLatchedValues( ) { if (!(m_fLatchedPositions & HUMANOID_LATCHED_HEAD)) { // set head latch m_fLatchedPositions |= HUMANOID_LATCHED_HEAD;
if (!HasCondition( COND_IN_PVS ) || !GetAttachment( "eyes", m_latchedEyeOrigin, &m_latchedHeadDirection )) { m_latchedEyeOrigin = BaseClass::EyePosition( ); AngleVectors( GetLocalAngles(), &m_latchedHeadDirection ); } // clear out eye latch m_fLatchedPositions &= ~(HUMANOID_LATCHED_EYE); // DevMsg( "eyeball %4f %4f %4f : %3f %3f %3f\n", origin.x, origin.y, origin.z, angles.x, angles.y, angles.z ); }
if (!(m_fLatchedPositions & HUMANOID_LATCHED_EYE)) { m_fLatchedPositions |= HUMANOID_LATCHED_EYE;
if ( CapabilitiesGet() & bits_CAP_ANIMATEDFACE ) { m_latchedEyeDirection = GetViewtarget() - m_latchedEyeOrigin; VectorNormalize( m_latchedEyeDirection ); } else { m_latchedEyeDirection = m_latchedHeadDirection; } } }
//----------------------------------------------------------------------------- // Purpose: Returns true position of the eyeballs //----------------------------------------------------------------------------- Vector CAI_BaseActor::EyePosition( ) { UpdateLatchedValues();
return m_latchedEyeOrigin; }
#define MIN_LOOK_TARGET_DIST 1.0f #define MAX_FULL_LOOK_TARGET_DIST 10.0f
//----------------------------------------------------------------------------- // Purpose: Returns true if target is in legal range of eye movement for the current head position //----------------------------------------------------------------------------- bool CAI_BaseActor::ValidEyeTarget(const Vector &lookTargetPos) { Vector vHeadDir = HeadDirection3D( ); Vector lookTargetDir = lookTargetPos - EyePosition(); float flDist = VectorNormalize(lookTargetDir);
if (flDist < MIN_LOOK_TARGET_DIST) { return false; }
// Only look if it doesn't crank my eyeballs too far float dotPr = DotProduct(lookTargetDir, vHeadDir); // DevMsg( "ValidEyeTarget( %4f %4f %4f ) %3f\n", lookTargetPos.x, lookTargetPos.y, lookTargetPos.z, dotPr );
if (dotPr > 0.259) // +- 75 degrees // if (dotPr > 0.86) // +- 30 degrees { return true; } return false; }
//----------------------------------------------------------------------------- // Purpose: Returns true if target is in legal range of possible head movements //----------------------------------------------------------------------------- bool CAI_BaseActor::ValidHeadTarget(const Vector &lookTargetPos) { Vector vFacing = BodyDirection3D(); Vector lookTargetDir = lookTargetPos - EyePosition(); float flDist = VectorNormalize(lookTargetDir);
if (flDist < MIN_LOOK_TARGET_DIST) { return false; }
// Only look if it doesn't crank my head too far float dotPr = DotProduct(lookTargetDir, vFacing); if (dotPr > 0 && fabs( lookTargetDir.z ) < 0.7) // +- 90 degrees side to side, +- 45 up/down { return true; } return false; }
//----------------------------------------------------------------------------- // Purpose: Returns how much to try to look at the target //----------------------------------------------------------------------------- float CAI_BaseActor::HeadTargetValidity(const Vector &lookTargetPos) { Vector vFacing = BodyDirection3D();
int iForward = LookupAttachment( "forward" ); if (iForward) { Vector tmp1; GetAttachment( iForward, tmp1, &vFacing, NULL, NULL ); }
Vector lookTargetDir = lookTargetPos - EyePosition(); float flDist = lookTargetDir.Length2D(); VectorNormalize(lookTargetDir);
if (flDist <= MIN_LOOK_TARGET_DIST) { return 0; }
// Only look if it doesn't crank my head too far float dotPr = DotProduct(lookTargetDir, vFacing); // only look if target is within +-135 degrees // scale 1..-0.707 == 1..1, -.707..-1 == 1..0 // X * b + b = 1 == 1 / (X + 1) = b, 3.4142 float flInterest = clamp( 3.4142 + 3.4142 * dotPr, 0, 1 );
// stop looking when point too close if (flDist < MAX_FULL_LOOK_TARGET_DIST) { flInterest = flInterest * (flDist - MIN_LOOK_TARGET_DIST ) / (MAX_FULL_LOOK_TARGET_DIST - MIN_LOOK_TARGET_DIST); }
return flInterest; }
//----------------------------------------------------------------------------- // Purpose: Integrate head turn over time //----------------------------------------------------------------------------- void CAI_BaseActor::SetHeadDirection( const Vector &vTargetPos, float flInterval) { Assert(0); // Actors shouldn't be calling this, it doesn't do anything }
float CAI_BaseActor::ClampWithBias( PoseParameter_t index, float value, float base ) { return EdgeLimitPoseParameter( (int)index, value, base ); }
//----------------------------------------------------------------------------- // Purpose: Accumulate all the wanted yaw changes //-----------------------------------------------------------------------------
void CAI_BaseActor::AccumulateIdealYaw( float flYaw, float flIntensity ) { float diff = AngleDiff( flYaw, GetLocalAngles().y ); m_flAccumYawDelta += diff * flIntensity; m_flAccumYawScale += flIntensity; }
//----------------------------------------------------------------------------- // Purpose: do any pending yaw movements //-----------------------------------------------------------------------------
bool CAI_BaseActor::SetAccumulatedYawAndUpdate( void ) { if (m_flAccumYawScale > 0.0) { float diff = m_flAccumYawDelta / m_flAccumYawScale; float facing = GetLocalAngles().y + diff;
m_flAccumYawDelta = 0.0; m_flAccumYawScale = 0.0;
if (IsCurSchedule( SCHED_SCENE_GENERIC )) { if (!IsMoving()) { GetMotor()->SetIdealYawAndUpdate( facing ); return true; } } } return false; }
//----------------------------------------------------------------------------- // Purpose: match actors "forward" attachment to point in direction of vHeadTarget //-----------------------------------------------------------------------------
void CAI_BaseActor::UpdateBodyControl( ) { // FIXME: only during idle, or in response to accel/decel //Set( m_ParameterBodyTransY, Get( m_FlexweightMoveRightLeft ) ); //Set( m_ParameterBodyTransX, Get( m_FlexweightMoveForwardBack ) ); //Set( m_ParameterBodyLift, Get( m_FlexweightMoveUpDown ) ); Set( m_ParameterBodyYaw, Get( m_FlexweightBodyRightLeft ) + m_goalBodyYaw ); //Set( m_ParameterBodyPitch, Get( m_FlexweightBodyUpDown ) ); //Set( m_ParameterBodyRoll, Get( m_FlexweightBodyTilt ) ); Set( m_ParameterSpineYaw, Get( m_FlexweightChestRightLeft ) + m_goalSpineYaw ); //Set( m_ParameterSpinePitch, Get( m_FlexweightChestUpDown ) ); //Set( m_ParameterSpineRoll, Get( m_FlexweightChestTilt ) ); Set( m_ParameterNeckTrans, Get( m_FlexweightHeadForwardBack ) ); }
static ConVar scene_clamplookat( "scene_clamplookat", "1", FCVAR_NONE, "Clamp head turns to a max of 20 degrees per think." );
void CAI_BaseActor::UpdateHeadControl( const Vector &vHeadTarget, float flHeadInfluence ) { float flTarget; float flLimit;
if (!(CapabilitiesGet() & bits_CAP_TURN_HEAD)) { return; }
// calc current animation head bias, movement needs to clamp accumulated with this QAngle angBias; QAngle vTargetAngles;
int iEyes = LookupAttachment( "eyes" ); int iChest = LookupAttachment( "chest" ); int iForward = LookupAttachment( "forward" );
matrix3x4_t eyesToWorld; matrix3x4_t forwardToWorld, worldToForward;
if (iEyes <= 0 || iForward <= 0) { // Head control on model without "eyes" or "forward" attachment // Most likely this is a cheaple or a generic_actor set to a model that doesn't support head/eye turning. // DevWarning( "%s using model \"%s\" that doesn't support head turning\n", GetClassname(), STRING( GetModelName() ) ); CapabilitiesRemove( bits_CAP_TURN_HEAD ); return; }
GetAttachment( iEyes, eyesToWorld );
GetAttachment( iForward, forwardToWorld ); MatrixInvert( forwardToWorld, worldToForward );
// Lookup chest attachment to do compounded range limit checks if (iChest > 0) { matrix3x4_t chestToWorld, worldToChest; GetAttachment( iChest, chestToWorld ); MatrixInvert( chestToWorld, worldToChest ); matrix3x4_t tmpM; ConcatTransforms( worldToChest, eyesToWorld, tmpM ); MatrixAngles( tmpM, angBias );
angBias.y -= Get( m_ParameterHeadYaw ); angBias.x -= Get( m_ParameterHeadPitch ); angBias.z -= Get( m_ParameterHeadRoll );
/* if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) ) { // Msg("bias %f %f %f\n", angBias.x, angBias.y, angBias.z );
Vector tmp1, tmp2; VectorTransform( Vector( 0, 0, 0), chestToWorld, tmp1 ); VectorTransform( Vector( 100, 0, 0), chestToWorld, tmp2 ); NDebugOverlay::Line( tmp1, tmp2, 0,0,255, false, 0.12 );
VectorTransform( Vector( 0, 0, 0), eyesToWorld, tmp1 ); VectorTransform( Vector( 100, 0, 0), eyesToWorld, tmp2 ); NDebugOverlay::Line( tmp1, tmp2, 0,0,255, false, 0.12 );
// NDebugOverlay::Line( EyePosition(), pEntity->EyePosition(), 0,0,255, false, 0.5); } */ } else { angBias.Init( 0, 0, 0 ); }
matrix3x4_t targetXform; targetXform = forwardToWorld; Vector vTargetDir = vHeadTarget - EyePosition();
if (scene_clamplookat.GetBool()) { // scale down pitch when the target is behind the head Vector vTargetLocal; VectorNormalize( vTargetDir ); VectorIRotate( vTargetDir, forwardToWorld, vTargetLocal ); vTargetLocal.z *= clamp( vTargetLocal.x, 0.1, 1.0 ); VectorNormalize( vTargetLocal ); VectorRotate( vTargetLocal, forwardToWorld, vTargetDir );
// clamp local influence when target is behind the head flHeadInfluence = flHeadInfluence * clamp( vTargetLocal.x * 2.0 + 2.0, 0.0, 1.0 ); }
Studio_AlignIKMatrix( targetXform, vTargetDir );
matrix3x4_t headXform; ConcatTransforms( worldToForward, targetXform, headXform ); MatrixAngles( headXform, vTargetAngles );
// partially debounce head goal float s0 = 1.0 - flHeadInfluence + GetHeadDebounce() * flHeadInfluence; float s1 = (1.0 - s0); // limit velocity of head turns m_goalHeadCorrection.x = UTIL_Approach( m_goalHeadCorrection.x * s0 + vTargetAngles.x * s1, m_goalHeadCorrection.x, 10.0 ); m_goalHeadCorrection.y = UTIL_Approach( m_goalHeadCorrection.y * s0 + vTargetAngles.y * s1, m_goalHeadCorrection.y, 30.0 ); m_goalHeadCorrection.z = UTIL_Approach( m_goalHeadCorrection.z * s0 + vTargetAngles.z * s1, m_goalHeadCorrection.z, 10.0 );
/* if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) ) { // Msg( "yaw %.1f (%f) pitch %.1f (%.1f)\n", m_goalHeadCorrection.y, vTargetAngles.y, vTargetAngles.x, m_goalHeadCorrection.x ); // Msg( "yaw %.2f (goal %.2f) (influence %.2f) (flex %.2f)\n", flLimit, m_goalHeadCorrection.y, flHeadInfluence, Get( m_FlexweightHeadRightLeft ) ); } */
flTarget = m_goalHeadCorrection.y + Get( m_FlexweightHeadRightLeft ); flLimit = ClampWithBias( m_ParameterHeadYaw, flTarget, angBias.y ); /* if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) ) { Msg( "yaw %5.1f : (%5.1f : %5.1f) %5.1f (%5.1f)\n", flLimit, m_goalHeadCorrection.y, Get( m_FlexweightHeadRightLeft ), angBias.y, Get( m_ParameterHeadYaw ) ); } */ Set( m_ParameterHeadYaw, flLimit );
flTarget = m_goalHeadCorrection.x + Get( m_FlexweightHeadUpDown ); flLimit = ClampWithBias( m_ParameterHeadPitch, flTarget, angBias.x ); /* if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) ) { Msg( "pitch %5.1f : (%5.1f : %5.1f) %5.1f (%5.1f)\n", flLimit, m_goalHeadCorrection.x, Get( m_FlexweightHeadUpDown ), angBias.x, Get( m_ParameterHeadPitch ) ); } */ Set( m_ParameterHeadPitch, flLimit );
flTarget = m_goalHeadCorrection.z + Get( m_FlexweightHeadTilt ); flLimit = ClampWithBias( m_ParameterHeadRoll, flTarget, angBias.z ); /* if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) ) { Msg( "roll %5.1f : (%5.1f : %5.1f) %5.1f (%5.1f)\n", flLimit, m_goalHeadCorrection.z, Get( m_FlexweightHeadTilt ), angBias.z, Get( m_ParameterHeadRoll ) ); } */ Set( m_ParameterHeadRoll, flLimit ); }
//------------------------------------------------------------------------------ // Purpose : Calculate the NPC's eye direction in 2D world space // Input : // Output : //------------------------------------------------------------------------------ Vector CAI_BaseActor::EyeDirection2D( void ) { Vector vEyeDirection = EyeDirection3D( ); vEyeDirection.z = 0;
vEyeDirection.AsVector2D().NormalizeInPlace();
return vEyeDirection; }
//------------------------------------------------------------------------------ // Purpose : Calculate the NPC's eye direction in 2D world space // Input : // Output : //------------------------------------------------------------------------------ Vector CAI_BaseActor::EyeDirection3D( void ) { UpdateLatchedValues( );
return m_latchedEyeDirection; }
//------------------------------------------------------------------------------ // Purpose : Calculate the NPC's head direction in 2D world space // Input : // Output : //------------------------------------------------------------------------------ Vector CAI_BaseActor::HeadDirection2D( void ) { Vector vHeadDirection = HeadDirection3D(); vHeadDirection.z = 0; vHeadDirection.AsVector2D().NormalizeInPlace(); return vHeadDirection; }
//------------------------------------------------------------------------------ // Purpose : Calculate the NPC's head direction in 3D world space // Input : // Output : //------------------------------------------------------------------------------ Vector CAI_BaseActor::HeadDirection3D( ) { UpdateLatchedValues( );
return m_latchedHeadDirection; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CAI_BaseActor::HasActiveLookTargets( void ) { return m_lookQueue.Count() != 0; }
//----------------------------------------------------------------------------- // Purpose: Clear any active look targets for the specified entity //----------------------------------------------------------------------------- void CAI_BaseActor::ClearLookTarget( CBaseEntity *pTarget ) { int iIndex = m_lookQueue.Find( pTarget ); if ( iIndex != m_lookQueue.InvalidIndex() ) { m_lookQueue.Remove(iIndex); }
iIndex = m_randomLookQueue.Find( pTarget ); if ( iIndex != m_randomLookQueue.InvalidIndex() ) { m_randomLookQueue.Remove(iIndex);
// Figure out the new random look time m_flNextRandomLookTime = gpGlobals->curtime + 1.0; for (int i = 0; i < m_randomLookQueue.Count(); i++) { if ( m_randomLookQueue[i].m_flEndTime > m_flNextRandomLookTime ) { m_flNextRandomLookTime = m_randomLookQueue[i].m_flEndTime + 0.4; } } } }
//----------------------------------------------------------------------------- // Purpose: Look at other NPCs and clients from time to time //----------------------------------------------------------------------------- float CAI_BaseActor::PickLookTarget( bool bExcludePlayers, float minTime, float maxTime ) { return PickLookTarget( m_randomLookQueue, bExcludePlayers, minTime, maxTime ); }
float CAI_BaseActor::PickLookTarget( CAI_InterestTarget &queue, bool bExcludePlayers, float minTime, float maxTime ) { AILookTargetArgs_t args; args.vTarget = vec3_invalid; args.flDuration = random->RandomFloat( minTime, maxTime ); args.flInfluence = random->RandomFloat( 0.3, 0.5 ); args.flRamp = random->RandomFloat( 0.2, 0.4 ); args.bExcludePlayers = bExcludePlayers; args.pQueue = &queue; bool foundLookTarget = true; if ( !PickTacticalLookTarget( &args ) ) { if ( !PickRandomLookTarget( &args ) ) { foundLookTarget = false; } } if ( !foundLookTarget ) { // DevMsg("nothing to see\n" ); MakeRandomLookTarget( &args, minTime, maxTime ); } // See if derived NPCs want to do anything with this look target before I use it OnSelectedLookTarget( &args );
if ( args.hTarget != NULL ) { Assert( args.vTarget == vec3_invalid ); queue.Add( args.hTarget, args.flInfluence, args.flDuration, args.flRamp ); } else { Assert( args.vTarget != vec3_invalid ); queue.Add( args.vTarget, args.flInfluence, args.flDuration, args.flRamp ); }
return args.flDuration; }
bool CAI_BaseActor::PickTacticalLookTarget( AILookTargetArgs_t *pArgs ) { CBaseEntity *pEnemy = GetEnemy();
if (pEnemy != NULL) { if ( ( FVisible( pEnemy ) || random->RandomInt(0, 3) == 0 ) && ValidHeadTarget(pEnemy->EyePosition())) { // look at enemy closer pArgs->hTarget = pEnemy; pArgs->flInfluence = random->RandomFloat( 0.7, 1.0 ); pArgs->flRamp = 0; return true; } else { // look at something else for a shorter time pArgs->flDuration = random->RandomFloat( 0.5, 0.8 ); // move head faster pArgs->flRamp = 0.2; } } return false; }
bool CAI_BaseActor::PickRandomLookTarget( AILookTargetArgs_t *pArgs ) { bool bIsNavigating = ( GetNavigator()->IsGoalActive() && GetNavigator()->IsGoalSet() ); if ( bIsNavigating && random->RandomInt(1, 10) <= 3 ) { Vector navLookPoint; Vector delta; if ( GetNavigator()->GetPointAlongPath( &navLookPoint, 12 * 12 ) && (delta = navLookPoint - GetAbsOrigin()).Length() > 8.0 * 12.0 ) { if ( random->RandomInt(1, 10) <= 5 ) { pArgs->vTarget = navLookPoint; pArgs->flDuration = random->RandomFloat( 0.2, 0.4 ); } else { pArgs->hTarget = this; pArgs->flDuration = random->RandomFloat( 1.0, 2.0 ); } pArgs->flRamp = 0.2; return true; } }
if ( GetState() == NPC_STATE_COMBAT && random->RandomInt(1, 10) <= 8 ) { // if in combat, look forward 80% of the time? pArgs->hTarget = this; return true; }
CBaseEntity *pBestEntity = NULL; CBaseEntity *pEntity = NULL; int iHighestImportance = 0; int iConsidered = 0; for ( CEntitySphereQuery sphere( GetAbsOrigin(), 30 * 12, 0 ); (pEntity = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() ) { if (pEntity == this) { continue; }
if ( pArgs->bExcludePlayers && pEntity->GetFlags() & FL_CLIENT ) { // Don't look at any players. continue; }
if (!pEntity->IsViewable()) { // Don't look at things without a model, or aren't tagged as interesting continue; }
if ( pEntity->GetOwnerEntity() && !pEntity->GetOwnerEntity()->IsViewable() ) { // Don't look at things that are associated with non-viewable owners. // Specifically, this prevents NPC's looking at beams or sprites that // are part of a viewmodel. (sjb) continue; }
// Don't look at any object that is ultimately parented to the player. // These objects will almost always be at the player's origin (feet), and it // looks bad when an actor looks at the player's feet. (sjb) CBaseEntity *pParent = pEntity->GetParent(); bool bObjectParentedToPlayer = false; while( pParent ) { if( pParent->IsPlayer() ) { bObjectParentedToPlayer = true; break; }
pParent = pParent->GetParent(); }
if( bObjectParentedToPlayer ) continue; // skip entities we're already looking at if ( pArgs->pQueue->Find( pEntity ) != pArgs->pQueue->InvalidIndex() ) continue;
// keep track of number of interesting things iConsidered++;
if ((pEntity->GetFlags() & FL_CLIENT) && (pEntity->IsMoving() || random->RandomInt( 0, 2) == 0)) { if (FVisible( pEntity ) && ValidHeadTarget(pEntity->EyePosition())) { pArgs->flDuration = random->RandomFloat( 1.0, 4.0 ); pBestEntity = pEntity; break; } } Vector delta = (pEntity->EyePosition() - EyePosition()); VectorNormalize( delta );
int iImportance; #if 0 // consider things in front to be more important than things to the sides iImportance = (DotProduct( delta, HeadDirection3D() ); #else // No, for now, give all targets random priority (as long as they're in front) iImportance = random->RandomInt( 1, 100 ); #endif // make other npcs, and moving npc's far more important if (pEntity->MyNPCPointer()) { iImportance *= 10; if (pEntity->IsMoving()) { iImportance *= 10; } }
if ( iImportance > iHighestImportance ) { if (FVisible( pEntity ) && ValidHeadTarget(pEntity->EyePosition())) { iHighestImportance = iImportance; pBestEntity = pEntity; // NDebugOverlay::Line( EyePosition(), pEntity->EyePosition(), 0,0,255, false, 0.5); } } }
// if there were too few things to look at, don't trust the item if (iConsidered < random->RandomInt( 0, 5)) { pBestEntity = NULL; }
if (pBestEntity) { //Msg("looking at %s\n", pBestEntity->GetClassname() ); //NDebugOverlay::Line( EyePosition(), pBestEntity->WorldSpaceCenter(), 255, 0, 0, false, 5 ); pArgs->hTarget = pBestEntity; return true; } return false; }
//----------------------------------------------------------------------------- // All attempts to find a target have failed, so just make something up. //----------------------------------------------------------------------------- void CAI_BaseActor::MakeRandomLookTarget( AILookTargetArgs_t *pArgs, float minTime, float maxTime ) { Vector forward, right, up; GetVectors( &forward, &right, &up );
// DevMsg("random view\n");
// For now, just look farther afield while driving in the vehicle. Without this we look around wildly! #ifdef HL2_EPISODIC if ( MyCombatCharacterPointer() && MyCombatCharacterPointer()->IsInAVehicle() ) { pArgs->vTarget = EyePosition() + forward * 2048 + right * random->RandomFloat(-650,650) + up * random->RandomFloat(-32,32); } else #endif // HL2_EPISODIC { pArgs->vTarget = EyePosition() + forward * 128 + right * random->RandomFloat(-32,32) + up * random->RandomFloat(-16,16); }
pArgs->flDuration = random->RandomFloat( minTime, maxTime ); pArgs->flInfluence = 0.01; pArgs->flRamp = random->RandomFloat( 0.8, 2.8 ); }
//----------------------------------------------------------------------------- // Purpose: Make sure we're looking at what we're shooting at //-----------------------------------------------------------------------------
void CAI_BaseActor::StartTaskRangeAttack1( const Task_t *pTask ) { BaseClass::StartTaskRangeAttack1( pTask ); if (GetEnemy()) { AddLookTarget( GetEnemy(), 1.0, 0.5, 0.2 ); } }
//----------------------------------------------------------------------------- // Purpose: Set direction that the NPC is looking //----------------------------------------------------------------------------- void CAI_BaseActor::AddLookTarget( CBaseEntity *pTarget, float flImportance, float flDuration, float flRamp ) { m_lookQueue.Add( pTarget, flImportance, flDuration, flRamp ); }
void CAI_BaseActor::AddLookTarget( const Vector &vecPosition, float flImportance, float flDuration, float flRamp ) { m_lookQueue.Add( vecPosition, flImportance, flDuration, flRamp ); }
//----------------------------------------------------------------------------- // Purpose: Maintain eye, head, body postures, etc. //----------------------------------------------------------------------------- void CAI_BaseActor::MaintainLookTargets( float flInterval ) { int i;
if ( m_iszExpressionScene != NULL_STRING && m_hExpressionSceneEnt == NULL ) { InstancedScriptedScene( this, STRING(m_iszExpressionScene), &m_hExpressionSceneEnt, 0.0, true ); }
// decay body/spine yaw m_goalSpineYaw = m_goalSpineYaw * 0.8; m_goalBodyYaw = m_goalBodyYaw * 0.8; m_goalHeadCorrection = m_goalHeadCorrection * 0.8;
// ARRGGHHH, this needs to be moved!!!! SetAccumulatedYawAndUpdate( ); ProcessSceneEvents( ); MaintainTurnActivity( ); DoBodyLean( ); UpdateBodyControl( ); InvalidateBoneCache();
// cached versions of the current eye position Vector vEyePosition = EyePosition( );
// FIXME: make this client side and automatic // set gesture positions Set( m_ParameterGestureHeight, Get( m_FlexweightGestureUpDown ) ); Set( m_ParameterGestureWidth, Get( m_FlexweightGestureRightLeft ) );
// initialize goal head direction to be current direction - this frames animation layering/pose parameters - // but with the head controlls removed. Vector vHead = HeadDirection3D( ); float flHeadInfluence = 0.0;
// NDebugOverlay::Line( vEyePosition, vEyePosition + vHead * 16, 0,0,255, false, 0.1);
// clean up look targets m_lookQueue.Cleanup();
// clean up random look targets m_randomLookQueue.Cleanup();
// clean up synthetic look targets m_syntheticLookQueue.Cleanup();
// if there's real things to look at, turn off the random targets if (m_lookQueue.Count() != 0 || m_syntheticLookQueue.Count() != 0) { for (i = 0; i < m_randomLookQueue.Count(); i++) { if (gpGlobals->curtime < m_randomLookQueue[i].m_flEndTime - m_randomLookQueue[i].m_flRamp - 0.2) { m_randomLookQueue[i].m_flEndTime = gpGlobals->curtime + m_randomLookQueue[i].m_flRamp + 0.2; } } m_flNextRandomLookTime = gpGlobals->curtime + 1.0; } else if (gpGlobals->curtime >= m_flNextRandomLookTime && GetState() != NPC_STATE_SCRIPT) { // Look at whatever! m_flNextRandomLookTime = gpGlobals->curtime + PickLookTarget( m_randomLookQueue ) - 0.4; }
// don't bother with any of the rest if the player can't see you if (!HasCondition( COND_IN_PVS )) { return; }
// Time to finish the current random expression? Or time to pick a new one? if ( m_flNextRandomExpressionTime && gpGlobals->curtime > m_flNextRandomExpressionTime ) { // Random expressions need to be cleared, because they don't loop. So if we // pick the same one again, we want to restart it. ClearExpression();
PlayExpressionForState( GetState() ); }
CUtlVector<CAI_InterestTarget_t *> active; // clean up random look targets for (i = 0; i < m_randomLookQueue.Count(); i++) { active.AddToTail( &m_randomLookQueue[i] ); } for (i = 0; i < m_lookQueue.Count(); i++) { active.AddToTail( &m_lookQueue[i] ); } for (i = 0; i < m_syntheticLookQueue.Count(); i++) { active.AddToTail( &m_syntheticLookQueue[i] ); } // figure out ideal head yaw bool bValidHeadTarget = false; bool bExpectedHeadTarget = false; for (i = 0; i < active.Count();i++) { Vector dir; float flDist = 100.0f; bExpectedHeadTarget = true; float flInterest = active[i]->Interest( );
if (active[i]->IsThis( this )) { int iForward = LookupAttachment( "forward" ); if (iForward) { Vector tmp1; GetAttachment( iForward, tmp1, &dir, NULL, NULL ); } else { dir = HeadDirection3D(); } } else { dir = active[i]->GetPosition() - vEyePosition; flDist = VectorNormalize( dir ); flInterest = flInterest * HeadTargetValidity( active[i]->GetPosition() ); } /* if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) ) { DevMsg( "head (%d) %.2f : %s : %.1f %.1f %.1f\n", i, flInterest, active[i]->m_hTarget->GetClassname(), active[i]->GetPosition().x, active[i]->GetPosition().y, active[i]->GetPosition().z ); } */ if (flInterest > 0.0) { if (flHeadInfluence == 0.0) { vHead = dir; flHeadInfluence = flInterest; } else { flHeadInfluence = flHeadInfluence * (1 - flInterest) + flInterest; float w = flInterest / flHeadInfluence; vHead = vHead * (1 - w) + dir * w; }
bValidHeadTarget = true;
// NDebugOverlay::Line( vEyePosition, vEyePosition + dir * 64, 0,255,0, false, 0.1); } else { // NDebugOverlay::Line( vEyePosition, active[i]->GetPosition(), 255,0,0, false, 0.1); } }
Assert( flHeadInfluence <= 1.0 );
// turn head toward target if (bValidHeadTarget) { UpdateHeadControl( vEyePosition + vHead * 100, flHeadInfluence ); m_goalHeadDirection = vHead; m_goalHeadInfluence = flHeadInfluence; } else { // no target, decay all head control direction m_goalHeadDirection = m_goalHeadDirection * 0.8 + vHead * 0.2;
m_goalHeadInfluence = max( m_goalHeadInfluence - 0.2, 0 );
VectorNormalize( m_goalHeadDirection ); UpdateHeadControl( vEyePosition + m_goalHeadDirection * 100, m_goalHeadInfluence ); // NDebugOverlay::Line( vEyePosition, vEyePosition + m_goalHeadDirection * 100, 255,0,0, false, 0.1); }
// DevMsg( "%.1f %.1f ", GetPoseParameter( "head_pitch" ), GetPoseParameter( "head_roll" ) );
// figure out eye target // eyes need to look directly at a target, even if the head doesn't quite aim there yet. bool bFoundTarget = false; EHANDLE hTarget = NULL;
for (i = active.Count() - 1; i >= 0; i--) { if (active[i]->IsThis( this )) { // DevMsg( "eyes (%d) %s\n", i, STRING( active[i]->m_hTarget->GetEntityName().ToCStr() ) ); bFoundTarget = true; hTarget = this; SetViewtarget( vEyePosition + HeadDirection3D() * 100 ); // NDebugOverlay::Line( vEyePosition, vEyePosition + HeadDirection3D() * 100, 255,0,0, false, 0.1); break; } else { // E3 Hack if (ValidEyeTarget(active[i]->GetPosition())) { // DevMsg( "eyes (%d) %s\n", i, STRING( pTarget->GetEntityName().ToCStr() ) );
bFoundTarget = true; hTarget = active[i]->m_hTarget; SetViewtarget( active[i]->GetPosition() ); break; } } }
// FIXME: add blink when changing targets if (m_hLookTarget != hTarget) { m_flBlinktime -= 0.5; m_hLookTarget = hTarget;
if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) && ai_debug_looktargets.GetInt() == 2 && m_hLookTarget.Get() ) { if ( m_hLookTarget != this ) { Vector vecEyePos = m_hLookTarget->EyePosition(); NDebugOverlay::Box( vecEyePos, -Vector(5,5,5), Vector(5,5,5), 0, 255, 0, 255, 20 ); NDebugOverlay::Line( EyePosition(), vecEyePos, 0,255,0, true, 20 ); NDebugOverlay::Text( vecEyePos, UTIL_VarArgs( "%s (%s)", m_hLookTarget->GetClassname(), m_hLookTarget->GetDebugName() ), false, 20 ); } }
OnNewLookTarget(); }
// this should take into acount where it will try to be.... if (!bFoundTarget && !ValidEyeTarget( GetViewtarget() )) { Vector right, up; VectorVectors( HeadDirection3D(), right, up ); // DevMsg("random view\n"); SetViewtarget( EyePosition() + HeadDirection3D() * 128 + right * random->RandomFloat(-32,32) + up * random->RandomFloat(-16,16) ); }
if ( m_hLookTarget != NULL ) { Vector absVel = m_hLookTarget->GetAbsVelocity(); CBaseEntity *ground = m_hLookTarget->GetGroundEntity(); if ( ground && ground->GetMoveType() == MOVETYPE_PUSH) { absVel = absVel + ground->GetAbsVelocity(); }
#ifdef HL2_EPISODIC // Translate our position if riding in a vehicle if ( m_hLookTarget->MyCombatCharacterPointer() ) { CBaseCombatCharacter *pBCC = m_hLookTarget->MyCombatCharacterPointer(); CBaseEntity *pVehicle = pBCC->GetVehicleEntity(); if ( pVehicle ) { IPhysicsObject *pObj = pVehicle->VPhysicsGetObject(); if ( pObj ) { Vector vecVelocity; pObj->GetVelocity( &vecVelocity, NULL );
absVel += vecVelocity; } } } #endif //HL2_EPISODIC
if ( !VectorCompare( absVel, vec3_origin ) ) { Vector viewTarget = GetViewtarget();
// Forward one think cycle viewTarget += absVel * flInterval;
SetViewtarget( viewTarget ); } }
// NDebugOverlay::Triangle( vEyePosition, GetViewtarget(), GetAbsOrigin(), 255, 255, 255, 10, false, 0.1 );
// DevMsg("pitch %.1f yaw %.1f\n", GetFlexWeight( "eyes_updown" ), GetFlexWeight( "eyes_rightleft" ) );
// blink if (m_flBlinktime < gpGlobals->curtime) { Blink(); m_flBlinktime = gpGlobals->curtime + random->RandomFloat( 1.5, 4.5 ); }
if ( ai_debug_looktargets.GetInt() == 1 && (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) ) { NDebugOverlay::Box( GetViewtarget(), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, 0, 20 ); NDebugOverlay::Line( EyePosition(),GetViewtarget(), 0,255,0, false, .1 ); } }
//-----------------------------------------------------------------------------
void CAI_BaseActor::PlayExpressionForState( NPC_STATE state ) { // If we have an override expression, use it above everything else if ( m_iszExpressionOverride != NULL_STRING && state != NPC_STATE_DEAD ) { SetExpression( STRING(m_iszExpressionOverride) ); return; }
// If we have a random expression, use that const char *pszExpression = SelectRandomExpressionForState( state ); if ( pszExpression && *pszExpression ) { float flDuration = SetExpression( pszExpression ); m_flNextRandomExpressionTime = gpGlobals->curtime + flDuration; return; } else { // Stop looking for random expressions for this state m_flNextRandomExpressionTime = 0; }
// Lastly, use the base expression loops switch ( state ) { case NPC_STATE_IDLE: if ( m_iszIdleExpression != NULL_STRING ) { SetExpression( STRING(m_iszIdleExpression) ); } break;
case NPC_STATE_COMBAT: if ( m_iszCombatExpression != NULL_STRING ) { SetExpression( STRING(m_iszCombatExpression) ); } break;
case NPC_STATE_ALERT: if ( m_iszAlertExpression != NULL_STRING ) { SetExpression( STRING(m_iszAlertExpression) ); } break;
case NPC_STATE_PLAYDEAD: case NPC_STATE_DEAD: if ( m_iszDeathExpression != NULL_STRING ) { SetExpression( STRING(m_iszDeathExpression) ); } break; } }
//----------------------------------------------------------------------------- // Purpose: Return a random expression for the specified state to play over // the state's expression loop. //----------------------------------------------------------------------------- const char *CAI_BaseActor::SelectRandomExpressionForState( NPC_STATE state ) { return NULL; }
//-----------------------------------------------------------------------------
void CAI_BaseActor::OnStateChange( NPC_STATE OldState, NPC_STATE NewState ) { PlayExpressionForState( NewState );
#ifdef HL2_EPISODIC // If we've just switched states, ensure we stop any scenes that asked to be stopped if ( OldState == NPC_STATE_IDLE ) { RemoveActorFromScriptedScenes( this, true, true ); } #endif
BaseClass::OnStateChange( OldState, NewState ); }
//-----------------------------------------------------------------------------
float CAI_BaseActor::SetExpression( const char *pszExpressionScene ) { if ( !pszExpressionScene || !*pszExpressionScene ) { ClearExpression(); return 0; }
if ( m_iszExpressionScene != NULL_STRING && stricmp( STRING(m_iszExpressionScene), pszExpressionScene ) == 0 ) { return 0; }
if ( m_hExpressionSceneEnt != NULL ) { StopScriptedScene( this, m_hExpressionSceneEnt ); }
if ( ai_debug_expressions.GetInt() ) { Msg("%s (%s) set expression to: %s\n", GetClassname(), GetDebugName(), pszExpressionScene ); }
m_iszExpressionScene = NULL_STRING; if ( pszExpressionScene ) { float flDuration = InstancedScriptedScene( this, pszExpressionScene, &m_hExpressionSceneEnt, 0.0, true );
if ( m_hExpressionSceneEnt != NULL ) { m_iszExpressionScene = AllocPooledString( pszExpressionScene ); }
return flDuration; }
return 0; }
//-----------------------------------------------------------------------------
void CAI_BaseActor::ClearExpression() { if ( m_hExpressionSceneEnt != NULL ) { StopScriptedScene( this, m_hExpressionSceneEnt ); } m_iszExpressionScene = NULL_STRING; }
//-----------------------------------------------------------------------------
const char *CAI_BaseActor::GetExpression() { return STRING(m_iszExpressionScene); }
//-----------------------------------------------------------------------------
void CAI_BaseActor::InputSetExpressionOverride( inputdata_t &inputdata ) { bool fHadOverride = ( m_iszExpressionOverride != NULL_STRING ); m_iszExpressionOverride = inputdata.value.StringID(); if ( m_iszExpressionOverride != NULL_STRING ) { SetExpression( STRING(m_iszExpressionOverride) ); } else if ( fHadOverride ) { PlayExpressionForState( GetState() ); } }
//-----------------------------------------------------------------------------
bool CAI_BaseActor::UseSemaphore( void ) { if ( m_bDontUseSemaphore ) return false;
return true; }
//-----------------------------------------------------------------------------
CAI_Expresser *CAI_BaseActor::CreateExpresser() { m_pExpresser = new CAI_Expresser(this); return m_pExpresser; }
//-----------------------------------------------------------------------------
CAI_Expresser *CAI_BaseActor::GetExpresser() { return m_pExpresser; } //-----------------------------------------------------------------------------
bool CAI_BaseActor::CreateComponents() { if ( !BaseClass::CreateComponents() ) return false;
m_pExpresser = CreateExpresser(); if ( !m_pExpresser) return false;
m_pExpresser->Connect(this);
return true; }
//-----------------------------------------------------------------------------
PHP Code:
#ifndef AI_BEHAVIOR_H #define AI_BEHAVIOR_H
#include "ai_component.h" #include "ai_basenpc.h" #include "ai_default.h" #include "AI_Criteria.h" #include "networkvar.h"
#ifdef DEBUG #pragma warning(push) #include <typeinfo> #pragma warning(pop) #pragma warning(disable:4290) #endif
#if defined( _WIN32 ) #pragma once #endif
//----------------------------------------------------------------------------- // CAI_Behavior... // // Purpose: The core component that defines a behavior in an NPC by selecting // schedules and running tasks // // Intended to be used as an organizational tool as well as a way // for various NPCs to share behaviors without sharing an inheritance // relationship, and without cramming those behaviors into the base // NPC class. //-----------------------------------------------------------------------------
//----------------------------------------------------------------------------- // Purpose: Base class defines interface to behaviors and provides bridging // methods //-----------------------------------------------------------------------------
class IBehaviorBackBridge;
//-------------------------------------
abstract_class CAI_BehaviorBase : public CAI_Component { DECLARE_CLASS( CAI_BehaviorBase, CAI_Component ) public: CAI_BehaviorBase(CAI_BaseNPC *pOuter = NULL) : CAI_Component(pOuter), m_pBackBridge(NULL) { }
virtual const char *GetName() = 0;
virtual bool KeyValue( const char *szKeyName, const char *szValue ) { return false; } bool IsRunning() { Assert( GetOuter() ); return ( GetOuter()->GetRunningBehavior() == this ); } virtual bool CanSelectSchedule() { return true; } virtual void BeginScheduleSelection() {} virtual void EndScheduleSelection() {} void SetBackBridge( IBehaviorBackBridge *pBackBridge ) { Assert( m_pBackBridge == NULL || pBackBridge == NULL ); m_pBackBridge = pBackBridge; }
void BridgePrecache() { Precache(); } void BridgeSpawn() { Spawn(); } void BridgeUpdateOnRemove() { UpdateOnRemove(); } void BridgeEvent_Killed( const CTakeDamageInfo &info ) { Event_Killed( info ); } void BridgeCleanupOnDeath( CBaseEntity *pCulprit, bool bFireDeathOutput ) { CleanupOnDeath( pCulprit, bFireDeathOutput ); }
void BridgeOnChangeHintGroup( string_t oldGroup, string_t newGroup ) { OnChangeHintGroup( oldGroup, newGroup ); }
void BridgeGatherConditions() { GatherConditions(); } void BridgePrescheduleThink() { PrescheduleThink(); } void BridgeOnScheduleChange() { OnScheduleChange(); } void BridgeOnStartSchedule( int scheduleType );
int BridgeSelectSchedule(); bool BridgeSelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode, int *pResult ); bool BridgeStartTask( const Task_t *pTask ); bool BridgeRunTask( const Task_t *pTask); bool BridgeAimGun( void ); int BridgeTranslateSchedule( int scheduleType ); bool BridgeGetSchedule( int localScheduleID, CAI_Schedule **ppResult ); bool BridgeTaskName(int taskID, const char **); Activity BridgeNPC_TranslateActivity( Activity activity ); void BridgeBuildScheduleTestBits() { BuildScheduleTestBits(); } bool BridgeIsCurTaskContinuousMove( bool *pResult ); void BridgeOnMovementFailed() { OnMovementFailed(); } void BridgeOnMovementComplete() { OnMovementComplete(); } float BridgeGetDefaultNavGoalTolerance(); bool BridgeFValidateHintType( CAI_Hint *pHint, bool *pResult ); bool BridgeIsValidEnemy( CBaseEntity *pEnemy ); CBaseEntity *BridgeBestEnemy(); bool BridgeIsValidCover( const Vector &vLocation, CAI_Hint const *pHint ); bool BridgeIsValidShootPosition( const Vector &vLocation, CAI_Node *pNode, CAI_Hint const *pHint ); float BridgeGetMaxTacticalLateralMovement( void ); bool BridgeShouldIgnoreSound( CSound *pSound ); void BridgeOnSeeEntity( CBaseEntity *pEntity ); void BridgeOnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ); bool BridgeIsInterruptable( void ); bool BridgeIsNavigationUrgent( void ); bool BridgeShouldPlayerAvoid( void ); int BridgeOnTakeDamage_Alive( const CTakeDamageInfo &info ); float BridgeGetReasonableFacingDist( void ); bool BridgeShouldAlwaysThink( bool *pResult ); void BridgeOnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon ); void BridgeOnRestore(); virtual bool BridgeSpeakMapmakerInterruptConcept( string_t iszConcept ); bool BridgeCanFlinch( void ); bool BridgeIsCrouching( void ); bool BridgeIsCrouchedActivity( Activity activity ); bool BridgeQueryHearSound( CSound *pSound ); bool BridgeCanRunAScriptedNPCInteraction( bool bForced ); Activity BridgeGetFlinchActivity( bool bHeavyDamage, bool bGesture ); bool BridgeOnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ); void BridgeModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet ); void BridgeTeleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity ); void BridgeHandleAnimEvent( animevent_t *pEvent );
virtual void GatherConditions(); virtual void GatherConditionsNotActive() { return; } // Override this and your behavior will call this in place of GatherConditions() when your behavior is NOT the active one. virtual void OnUpdateShotRegulator() {}
virtual CAI_ClassScheduleIdSpace *GetClassScheduleIdSpace();
virtual int DrawDebugTextOverlays( int text_offset );
virtual int Save( ISave &save ); virtual int Restore( IRestore &restore );
static void SaveBehaviors(ISave &save, CAI_BehaviorBase *pCurrentBehavior, CAI_BehaviorBase **ppBehavior, int nBehaviors ); static int RestoreBehaviors(IRestore &restore, CAI_BehaviorBase **ppBehavior, int nBehaviors ); // returns index of "current" behavior, or -1
protected:
int GetNpcState() { return GetOuter()->m_NPCState; }
virtual void Precache() {} virtual void Spawn() {} virtual void UpdateOnRemove() {} virtual void Event_Killed( const CTakeDamageInfo &info ) {} virtual void CleanupOnDeath( CBaseEntity *pCulprit, bool bFireDeathOutput ) {} virtual void PrescheduleThink(); virtual void OnScheduleChange(); virtual void OnStartSchedule( int scheduleType );
virtual int SelectSchedule(); virtual int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ); virtual void StartTask( const Task_t *pTask ); virtual void RunTask( const Task_t *pTask ); virtual void AimGun( void ); virtual int TranslateSchedule( int scheduleType ); virtual CAI_Schedule *GetSchedule(int schedule); virtual const char *GetSchedulingErrorName(); virtual void BuildScheduleTestBits() {} bool IsCurSchedule( int schedId, bool fIdeal = true );
CAI_Hint * GetHintNode() { return GetOuter()->GetHintNode(); } const CAI_Hint *GetHintNode() const { return GetOuter()->GetHintNode(); } void SetHintNode( CAI_Hint *pHintNode ) { GetOuter()->SetHintNode( pHintNode ); } void ClearHintNode( float reuseDelay = 0.0 ) { GetOuter()->ClearHintNode( reuseDelay ); }
protected: // Used by derived classes to chain a task to a task that might not be the // one they are currently handling: void ChainStartTask( int task, float taskData = 0 ); void ChainRunTask( int task, float taskData = 0 );
protected:
virtual Activity NPC_TranslateActivity( Activity activity );
virtual bool IsCurTaskContinuousMove(); virtual void OnMovementFailed() {}; virtual void OnMovementComplete() {}; virtual float GetDefaultNavGoalTolerance(); virtual bool FValidateHintType( CAI_Hint *pHint );
virtual bool IsValidEnemy( CBaseEntity *pEnemy ); virtual CBaseEntity *BestEnemy(); virtual bool IsValidCover( const Vector &vLocation, CAI_Hint const *pHint ); virtual bool IsValidShootPosition( const Vector &vLocation, CAI_Node *pNode, CAI_Hint const *pHint ); virtual float GetMaxTacticalLateralMovement( void ); virtual bool ShouldIgnoreSound( CSound *pSound ); virtual void OnSeeEntity( CBaseEntity *pEntity ); virtual void OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ); virtual bool IsInterruptable( void ); virtual bool IsNavigationUrgent( void ); virtual int OnTakeDamage_Alive( const CTakeDamageInfo &info ); virtual float GetReasonableFacingDist( void ); virtual bool ShouldPlayerAvoid( void ); virtual bool CanFlinch( void ); virtual bool IsCrouching( void ); virtual bool IsCrouchedActivity( Activity activity ); virtual bool QueryHearSound( CSound *pSound ); virtual bool CanRunAScriptedNPCInteraction( bool bForced ); virtual Activity GetFlinchActivity( bool bHeavyDamage, bool bGesture ); virtual bool OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ); virtual void ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet ); virtual void Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity ); virtual void HandleAnimEvent( animevent_t *pEvent );
virtual bool ShouldAlwaysThink();
virtual void OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon ) {}; virtual bool SpeakMapmakerInterruptConcept( string_t iszConcept ) { return false; }; virtual void OnRestore() {}; bool NotifyChangeBehaviorStatus( bool fCanFinishSchedule = false );
bool HaveSequenceForActivity( Activity activity ) { return GetOuter()->HaveSequenceForActivity( activity ); } //---------------------------------
string_t GetHintGroup() { return GetOuter()->GetHintGroup(); } void ClearHintGroup() { GetOuter()->ClearHintGroup(); } void SetHintGroup( string_t name ) { GetOuter()->SetHintGroup( name ); }
virtual void OnChangeHintGroup( string_t oldGroup, string_t newGroup ) {}
// // These allow derived classes to implement custom schedules // static CAI_GlobalScheduleNamespace *GetSchedulingSymbols() { return CAI_BaseNPC::GetSchedulingSymbols(); } static bool LoadSchedules() { return true; } virtual bool IsBehaviorSchedule( int scheduleType ) { return false; }
CAI_Navigator * GetNavigator() { return GetOuter()->GetNavigator(); } CAI_Motor * GetMotor() { return GetOuter()->GetMotor(); } CAI_TacticalServices * GetTacticalServices() { return GetOuter()->GetTacticalServices(); }
bool m_fOverrode; IBehaviorBackBridge *m_pBackBridge;
DECLARE_DATADESC(); };
//----------------------------------------------------------------------------- // Purpose: Template provides provides back bridge to owning class and // establishes namespace settings //-----------------------------------------------------------------------------
template <class NPC_CLASS = CAI_BaseNPC, const int ID_SPACE_OFFSET = 100000> class CAI_Behavior : public CAI_ComponentWithOuter<NPC_CLASS, CAI_BehaviorBase> { public: DECLARE_CLASS_NOFRIEND( CAI_Behavior, NPC_CLASS ); enum { NEXT_TASK = ID_SPACE_OFFSET, NEXT_SCHEDULE = ID_SPACE_OFFSET, NEXT_CONDITION = ID_SPACE_OFFSET };
void SetCondition( int condition ) { if ( condition >= ID_SPACE_OFFSET && condition < ID_SPACE_OFFSET + 10000 ) // it's local to us condition = GetClassScheduleIdSpace()->ConditionLocalToGlobal( condition ); GetOuter()->SetCondition( condition ); }
bool HasCondition( int condition ) { if ( condition >= ID_SPACE_OFFSET && condition < ID_SPACE_OFFSET + 10000 ) // it's local to us condition = GetClassScheduleIdSpace()->ConditionLocalToGlobal( condition ); return GetOuter()->HasCondition( condition ); }
bool HasInterruptCondition( int condition ) { if ( condition >= ID_SPACE_OFFSET && condition < ID_SPACE_OFFSET + 10000 ) // it's local to us condition = GetClassScheduleIdSpace()->ConditionLocalToGlobal( condition ); return GetOuter()->HasInterruptCondition( condition ); }
void ClearCondition( int condition ) { if ( condition >= ID_SPACE_OFFSET && condition < ID_SPACE_OFFSET + 10000 ) // it's local to us condition = GetClassScheduleIdSpace()->ConditionLocalToGlobal( condition ); GetOuter()->ClearCondition( condition ); }
protected: CAI_Behavior(NPC_CLASS *pOuter = NULL) : CAI_ComponentWithOuter<NPC_CLASS, CAI_BehaviorBase>(pOuter) { }
static CAI_GlobalScheduleNamespace *GetSchedulingSymbols() { return NPC_CLASS::GetSchedulingSymbols(); } virtual CAI_ClassScheduleIdSpace *GetClassScheduleIdSpace() { return GetOuter()->GetClassScheduleIdSpace(); }
static CAI_ClassScheduleIdSpace &AccessClassScheduleIdSpaceDirect() { return NPC_CLASS::AccessClassScheduleIdSpaceDirect(); }
private: virtual bool IsBehaviorSchedule( int scheduleType ) { return ( scheduleType >= ID_SPACE_OFFSET && scheduleType < ID_SPACE_OFFSET + 10000 ); } };
//----------------------------------------------------------------------------- // Purpose: Some bridges a little more complicated to allow behavior to see // what base class would do or control order in which it's donw //-----------------------------------------------------------------------------
abstract_class IBehaviorBackBridge { public: virtual void BackBridge_GatherConditions() = 0; virtual int BackBridge_SelectSchedule() = 0; virtual int BackBridge_TranslateSchedule( int scheduleType ) = 0; virtual Activity BackBridge_NPC_TranslateActivity( Activity activity ) = 0; virtual bool BackBridge_IsValidEnemy(CBaseEntity *pEnemy) = 0; virtual CBaseEntity* BackBridge_BestEnemy(void) = 0; virtual bool BackBridge_IsValidCover( const Vector &vLocation, CAI_Hint const *pHint ) = 0; virtual bool BackBridge_IsValidShootPosition( const Vector &vLocation, CAI_Node *pNode, CAI_Hint const *pHint ) = 0; virtual float BackBridge_GetMaxTacticalLateralMovement( void ) = 0; virtual bool BackBridge_ShouldIgnoreSound( CSound *pSound ) = 0; virtual void BackBridge_OnSeeEntity( CBaseEntity *pEntity ) = 0; virtual void BackBridge_OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ) = 0; virtual bool BackBridge_IsInterruptable( void ) = 0; virtual bool BackBridge_IsNavigationUrgent( void ) = 0; virtual bool BackBridge_ShouldPlayerAvoid( void ) = 0; virtual int BackBridge_OnTakeDamage_Alive( const CTakeDamageInfo &info ) = 0; virtual float BackBridge_GetDefaultNavGoalTolerance() = 0; virtual float BackBridge_GetReasonableFacingDist( void ) = 0; virtual bool BackBridge_CanFlinch( void ) = 0; virtual bool BackBridge_IsCrouching( void ) = 0; virtual bool BackBridge_IsCrouchedActivity( Activity activity ) = 0; virtual bool BackBridge_QueryHearSound( CSound *pSound ) = 0; virtual bool BackBridge_CanRunAScriptedNPCInteraction( bool bForced ) = 0; virtual Activity BackBridge_GetFlinchActivity( bool bHeavyDamage, bool bGesture ) = 0; virtual bool BackBridge_OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ) = 0; virtual void BackBridge_ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet ) = 0; virtual void BackBridge_Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity ) = 0;
virtual void BackBridge_HandleAnimEvent( animevent_t *pEvent ) = 0;
//-------------------------------------
};
//----------------------------------------------------------------------------- // Purpose: The common instantiation of the above template //-----------------------------------------------------------------------------
typedef CAI_Behavior<> CAI_SimpleBehavior;
//----------------------------------------------------------------------------- // Purpose: Base class for AIs that want to act as a host for CAI_Behaviors // NPCs aren't required to use this, but probably want to. //-----------------------------------------------------------------------------
template <class BASE_NPC> class CAI_BehaviorHost : public BASE_NPC, private IBehaviorBackBridge { public: DECLARE_CLASS_NOFRIEND( CAI_BehaviorHost, BASE_NPC );
CAI_BehaviorHost() : m_pCurBehavior(NULL) { #ifdef DEBUG m_fDebugInCreateBehaviors = false; #endif }
void CleanupOnDeath( CBaseEntity *pCulprit = NULL, bool bFireDeathOutput = true );
virtual int Save( ISave &save ); virtual int Restore( IRestore &restore ); virtual bool CreateComponents();
// Automatically called during entity construction, derived class calls AddBehavior() virtual bool CreateBehaviors() { return true; } // forces movement and sets a new schedule virtual bool ScheduledMoveToGoalEntity( int scheduleType, CBaseEntity *pGoalEntity, Activity movementActivity ); virtual bool ScheduledFollowPath( int scheduleType, CBaseEntity *pPathStart, Activity movementActivity ); virtual void ForceSelectedGo(CBaseEntity *pPlayer, const Vector &targetPos, const Vector &traceDir, bool bRun); virtual void ForceSelectedGoRandom(void);
// Bridges void Precache(); void NPCInit(); void UpdateOnRemove(); void Event_Killed( const CTakeDamageInfo &info ); void GatherConditions(); void PrescheduleThink(); int SelectSchedule(); void KeepRunningBehavior(); int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ); void OnScheduleChange(); void OnStartSchedule( int scheduleType ); int TranslateSchedule( int scheduleType ); void StartTask( const Task_t *pTask ); void RunTask( const Task_t *pTask ); void AimGun( void ); CAI_Schedule * GetSchedule(int localScheduleID); const char * TaskName(int taskID); void BuildScheduleTestBits();
void OnChangeHintGroup( string_t oldGroup, string_t newGroup ); Activity NPC_TranslateActivity( Activity activity );
bool IsCurTaskContinuousMove(); void OnMovementFailed(); void OnMovementComplete(); bool FValidateHintType( CAI_Hint *pHint ); float GetDefaultNavGoalTolerance();
bool IsValidEnemy(CBaseEntity *pEnemy); CBaseEntity* BestEnemy(void); bool IsValidCover( const Vector &vLocation, CAI_Hint const *pHint ); bool IsValidShootPosition( const Vector &vLocation, CAI_Node *pNode, CAI_Hint const *pHint ); float GetMaxTacticalLateralMovement( void ); bool ShouldIgnoreSound( CSound *pSound ); void OnSeeEntity( CBaseEntity *pEntity ); void OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ); bool IsInterruptable( void ); bool IsNavigationUrgent( void ); bool ShouldPlayerAvoid( void ); int OnTakeDamage_Alive( const CTakeDamageInfo &info ); float GetReasonableFacingDist( void ); bool CanFlinch( void ); bool IsCrouching( void ); bool IsCrouchedActivity( Activity activity ); bool QueryHearSound( CSound *pSound ); bool CanRunAScriptedNPCInteraction( bool bForced ); Activity GetFlinchActivity( bool bHeavyDamage, bool bGesture ); bool OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ); void HandleAnimEvent( animevent_t *pEvent ); bool ShouldAlwaysThink();
void OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon ); virtual bool SpeakMapmakerInterruptConcept( string_t iszConcept );
void OnRestore();
void ModifyOrAppendCriteria( AI_CriteriaSet& set ); void Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity );
//---------------------------------
virtual bool OnBehaviorChangeStatus( CAI_BehaviorBase *pBehavior, bool fCanFinishSchedule ); virtual void OnChangeRunningBehavior( CAI_BehaviorBase *pOldBehavior, CAI_BehaviorBase *pNewBehavior );
protected: void AddBehavior( CAI_BehaviorBase *pBehavior ); bool BehaviorSelectSchedule(); virtual bool ShouldBehaviorSelectSchedule( CAI_BehaviorBase *pBehavior ) { return true; }
bool IsRunningBehavior() const; CAI_BehaviorBase *GetRunningBehavior(); CAI_BehaviorBase *DeferSchedulingToBehavior( CAI_BehaviorBase *pNewBehavior ); void ChangeBehaviorTo( CAI_BehaviorBase *pNewBehavior );
CAI_Schedule * GetNewSchedule(); CAI_Schedule * GetFailSchedule(); private: void BackBridge_GatherConditions(); int BackBridge_SelectSchedule(); int BackBridge_TranslateSchedule( int scheduleType ); Activity BackBridge_NPC_TranslateActivity( Activity activity ); bool BackBridge_IsValidEnemy(CBaseEntity *pEnemy); CBaseEntity* BackBridge_BestEnemy(void); bool BackBridge_IsValidCover( const Vector &vLocation, CAI_Hint const *pHint ); bool BackBridge_IsValidShootPosition( const Vector &vLocation, CAI_Node *pNode, CAI_Hint const *pHint ); float BackBridge_GetMaxTacticalLateralMovement( void ); bool BackBridge_ShouldIgnoreSound( CSound *pSound ); void BackBridge_OnSeeEntity( CBaseEntity *pEntity ); void BackBridge_OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ); bool BackBridge_IsInterruptable( void ); bool BackBridge_IsNavigationUrgent( void ); bool BackBridge_ShouldPlayerAvoid( void ); int BackBridge_OnTakeDamage_Alive( const CTakeDamageInfo &info ); float BackBridge_GetDefaultNavGoalTolerance(); float BackBridge_GetReasonableFacingDist( void ); bool BackBridge_CanFlinch( void ); bool BackBridge_IsCrouching( void ); bool BackBridge_IsCrouchedActivity( Activity activity ); bool BackBridge_QueryHearSound( CSound *pSound ); bool BackBridge_CanRunAScriptedNPCInteraction( bool bForced ); Activity BackBridge_GetFlinchActivity( bool bHeavyDamage, bool bGesture ); bool BackBridge_OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ); void BackBridge_ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet ); void BackBridge_Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity );
void BackBridge_HandleAnimEvent( animevent_t *pEvent );
CAI_BehaviorBase **AccessBehaviors(); int NumBehaviors();
CAI_BehaviorBase * m_pCurBehavior; CUtlVector<CAI_BehaviorBase *> m_Behaviors;
bool m_bCalledBehaviorSelectSchedule; #ifdef DEBUG bool m_fDebugInCreateBehaviors; #endif };
//-----------------------------------------------------------------------------
// The first frame a behavior begins schedule selection, it won't have had it's GatherConditions() // called. To fix this, BeginScheduleSelection() manually calls the new behavior's GatherConditions(), // but sets this global so that the baseclass GatherConditions() isn't called as well. extern bool g_bBehaviorHost_PreventBaseClassGatherConditions;
//-----------------------------------------------------------------------------
inline void CAI_BehaviorBase::BridgeOnStartSchedule( int scheduleType ) { int localId = AI_IdIsGlobal( scheduleType ) ? GetClassScheduleIdSpace()->ScheduleGlobalToLocal( scheduleType ) : scheduleType; OnStartSchedule( localId ); }
//-------------------------------------
inline int CAI_BehaviorBase::BridgeSelectSchedule() { int result = SelectSchedule();
if ( IsBehaviorSchedule( result ) ) return GetClassScheduleIdSpace()->ScheduleLocalToGlobal( result );
return result; }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeSelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode, int *pResult ) { m_fOverrode = true; int result = SelectFailSchedule( failedSchedule, failedTask, taskFailCode ); if ( m_fOverrode ) { if ( result != SCHED_NONE ) { if ( IsBehaviorSchedule( result ) ) *pResult = GetClassScheduleIdSpace()->ScheduleLocalToGlobal( result ); else *pResult = result; return true; } Warning( "An AI behavior is in control but has no recommended schedule\n" ); } return false; }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeStartTask( const Task_t *pTask ) { m_fOverrode = true; StartTask( pTask ); return m_fOverrode; }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeRunTask( const Task_t *pTask) { m_fOverrode = true; RunTask( pTask ); return m_fOverrode; }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeAimGun( void ) { m_fOverrode = true; AimGun(); return m_fOverrode; }
//-------------------------------------
inline void CAI_BehaviorBase::ChainStartTask( int task, float taskData ) { Task_t tempTask = { task, taskData };
bool fPrevOverride = m_fOverrode; GetOuter()->StartTask( (const Task_t *)&tempTask ); m_fOverrode = fPrevOverride;; }
//-------------------------------------
inline void CAI_BehaviorBase::ChainRunTask( int task, float taskData ) { Task_t tempTask = { task, taskData }; bool fPrevOverride = m_fOverrode; GetOuter()->RunTask( (const Task_t *) &tempTask ); m_fOverrode = fPrevOverride;; }
//-------------------------------------
inline int CAI_BehaviorBase::BridgeTranslateSchedule( int scheduleType ) { int localId = AI_IdIsGlobal( scheduleType ) ? GetClassScheduleIdSpace()->ScheduleGlobalToLocal( scheduleType ) : scheduleType; int result = TranslateSchedule( localId ); return result; }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeGetSchedule( int localScheduleID, CAI_Schedule **ppResult ) { *ppResult = GetSchedule( localScheduleID ); return (*ppResult != NULL ); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeTaskName( int taskID, const char **ppResult ) { if ( AI_IdIsLocal( taskID ) ) { *ppResult = GetSchedulingSymbols()->TaskIdToSymbol( GetClassScheduleIdSpace()->TaskLocalToGlobal( taskID ) ); return (*ppResult != NULL ); } return false; }
//-------------------------------------
inline Activity CAI_BehaviorBase::BridgeNPC_TranslateActivity( Activity activity ) { return NPC_TranslateActivity( activity ); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeIsCurTaskContinuousMove( bool *pResult ) { bool fPrevOverride = m_fOverrode; m_fOverrode = true; *pResult = IsCurTaskContinuousMove(); bool result = m_fOverrode; m_fOverrode = fPrevOverride; return result; }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeFValidateHintType( CAI_Hint *pHint, bool *pResult ) { bool fPrevOverride = m_fOverrode; m_fOverrode = true; *pResult = FValidateHintType( pHint ); bool result = m_fOverrode; m_fOverrode = fPrevOverride; return result; }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeIsValidEnemy( CBaseEntity *pEnemy ) { return IsValidEnemy( pEnemy ); }
//-------------------------------------
inline CBaseEntity *CAI_BehaviorBase::BridgeBestEnemy() { return BestEnemy(); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeIsValidCover( const Vector &vLocation, CAI_Hint const *pHint ) { return IsValidCover( vLocation, pHint ); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeIsValidShootPosition( const Vector &vLocation, CAI_Node *pNode, CAI_Hint const *pHint ) { return IsValidShootPosition( vLocation, pNode, pHint ); }
//-------------------------------------
inline float CAI_BehaviorBase::BridgeGetMaxTacticalLateralMovement( void ) { return GetMaxTacticalLateralMovement(); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeShouldIgnoreSound( CSound *pSound ) { return ShouldIgnoreSound( pSound ); }
//-------------------------------------
inline void CAI_BehaviorBase::BridgeOnSeeEntity( CBaseEntity *pEntity ) { OnSeeEntity( pEntity ); }
//-------------------------------------
inline void CAI_BehaviorBase::BridgeOnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ) { OnFriendDamaged( pSquadmate, pAttacker ); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeIsInterruptable( void ) { return IsInterruptable(); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeIsNavigationUrgent( void ) { return IsNavigationUrgent(); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeCanFlinch( void ) { return CanFlinch(); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeIsCrouching( void ) { return IsCrouching(); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeIsCrouchedActivity( Activity activity ) { return IsCrouchedActivity( activity ); }
inline bool CAI_BehaviorBase::BridgeQueryHearSound( CSound *pSound ) { return QueryHearSound( pSound ); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeCanRunAScriptedNPCInteraction( bool bForced ) { return CanRunAScriptedNPCInteraction( bForced ); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeShouldPlayerAvoid( void ) { return ShouldPlayerAvoid(); }
//-------------------------------------
inline int CAI_BehaviorBase::BridgeOnTakeDamage_Alive( const CTakeDamageInfo &info ) { return OnTakeDamage_Alive( info ); }
//-------------------------------------
inline float CAI_BehaviorBase::BridgeGetReasonableFacingDist( void ) { return GetReasonableFacingDist(); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeShouldAlwaysThink( bool *pResult ) { bool fPrevOverride = m_fOverrode; m_fOverrode = true; *pResult = ShouldAlwaysThink(); bool result = m_fOverrode; m_fOverrode = fPrevOverride; return result; }
//-------------------------------------
inline void CAI_BehaviorBase::BridgeOnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon ) { OnChangeActiveWeapon( pOldWeapon, pNewWeapon ); }
//-------------------------------------
inline bool CAI_BehaviorBase::BridgeSpeakMapmakerInterruptConcept( string_t iszConcept ) { return SpeakMapmakerInterruptConcept( iszConcept ); }
//-------------------------------------
inline void CAI_BehaviorBase::BridgeOnRestore() { OnRestore(); }
//-------------------------------------
inline float CAI_BehaviorBase::BridgeGetDefaultNavGoalTolerance() { return GetDefaultNavGoalTolerance(); }
//-----------------------------------------------------------------------------
inline Activity CAI_BehaviorBase::BridgeGetFlinchActivity( bool bHeavyDamage, bool bGesture ) { return GetFlinchActivity( bHeavyDamage, bGesture ); }
//-----------------------------------------------------------------------------
inline bool CAI_BehaviorBase::BridgeOnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ) { return OnCalcBaseMove( pMoveGoal, distClear, pResult ); }
//-----------------------------------------------------------------------------
inline void CAI_BehaviorBase::BridgeModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet ) { ModifyOrAppendCriteria( criteriaSet ); }
//-----------------------------------------------------------------------------
inline void CAI_BehaviorBase::BridgeTeleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity ) { Teleport( newPosition, newAngles, newVelocity ); }
//-----------------------------------------------------------------------------
inline void CAI_BehaviorBase::BridgeHandleAnimEvent( animevent_t *pEvent ) { HandleAnimEvent( pEvent ); }
//-----------------------------------------------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::CleanupOnDeath( CBaseEntity *pCulprit, bool bFireDeathOutput ) { DeferSchedulingToBehavior( NULL ); for( int i = 0; i < m_Behaviors.Count(); i++ ) { m_Behaviors[i]->BridgeCleanupOnDeath( pCulprit, bFireDeathOutput ); } BaseClass::CleanupOnDeath( pCulprit, bFireDeathOutput ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::GatherConditions() { // Iterate over behaviors and call GatherConditionsNotActive() on each behavior // not currently active. for( int i = 0; i < m_Behaviors.Count(); i++ ) { if( m_Behaviors[i] != m_pCurBehavior ) { m_Behaviors[i]->GatherConditionsNotActive(); } }
if ( m_pCurBehavior ) m_pCurBehavior->BridgeGatherConditions(); else BaseClass::GatherConditions(); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::BackBridge_GatherConditions() { if ( g_bBehaviorHost_PreventBaseClassGatherConditions ) return;
BaseClass::GatherConditions(); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::OnScheduleChange() { if ( m_pCurBehavior ) m_pCurBehavior->BridgeOnScheduleChange(); BaseClass::OnScheduleChange(); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::OnStartSchedule( int scheduleType ) { if ( m_pCurBehavior ) m_pCurBehavior->BridgeOnStartSchedule( scheduleType ); BaseClass::OnStartSchedule( scheduleType ); }
//-------------------------------------
template <class BASE_NPC> inline int CAI_BehaviorHost<BASE_NPC>::BackBridge_SelectSchedule() { return BaseClass::SelectSchedule(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BehaviorSelectSchedule() { for ( int i = 0; i < m_Behaviors.Count(); i++ ) { if ( m_Behaviors[i]->CanSelectSchedule() && ShouldBehaviorSelectSchedule( m_Behaviors[i] ) ) { DeferSchedulingToBehavior( m_Behaviors[i] ); return true; } } DeferSchedulingToBehavior( NULL ); return false; }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::IsRunningBehavior() const { return ( m_pCurBehavior != NULL ); }
//-------------------------------------
template <class BASE_NPC> inline CAI_BehaviorBase *CAI_BehaviorHost<BASE_NPC>::GetRunningBehavior() { return m_pCurBehavior; }
//-------------------------------------
template <class BASE_NPC> inline CAI_Schedule *CAI_BehaviorHost<BASE_NPC>::GetNewSchedule() { m_bCalledBehaviorSelectSchedule = false; CAI_Schedule *pResult = BaseClass::GetNewSchedule(); if ( !m_bCalledBehaviorSelectSchedule && m_pCurBehavior ) DeferSchedulingToBehavior( NULL ); return pResult; }
//-------------------------------------
template <class BASE_NPC> inline CAI_Schedule *CAI_BehaviorHost<BASE_NPC>::GetFailSchedule() { m_bCalledBehaviorSelectSchedule = false; CAI_Schedule *pResult = BaseClass::GetFailSchedule(); if ( !m_bCalledBehaviorSelectSchedule && m_pCurBehavior ) DeferSchedulingToBehavior( NULL ); return pResult; }
//------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::ChangeBehaviorTo( CAI_BehaviorBase *pNewBehavior ) { bool change = ( m_pCurBehavior != pNewBehavior ); CAI_BehaviorBase *pOldBehavior = m_pCurBehavior; m_pCurBehavior = pNewBehavior; if ( change ) { if ( m_pCurBehavior ) { m_pCurBehavior->BeginScheduleSelection();
g_bBehaviorHost_PreventBaseClassGatherConditions = true; m_pCurBehavior->GatherConditions(); g_bBehaviorHost_PreventBaseClassGatherConditions = false; }
if ( pOldBehavior ) { pOldBehavior->EndScheduleSelection(); VacateStrategySlot(); }
OnChangeRunningBehavior( pOldBehavior, pNewBehavior ); } }
//-------------------------------------
template <class BASE_NPC> inline CAI_BehaviorBase *CAI_BehaviorHost<BASE_NPC>::DeferSchedulingToBehavior( CAI_BehaviorBase *pNewBehavior ) { CAI_BehaviorBase *pOldBehavior = m_pCurBehavior; ChangeBehaviorTo( pNewBehavior ); return pOldBehavior; }
//-------------------------------------
template <class BASE_NPC> inline int CAI_BehaviorHost<BASE_NPC>::BackBridge_TranslateSchedule( int scheduleType ) { return BaseClass::TranslateSchedule( scheduleType ); }
//-------------------------------------
template <class BASE_NPC> inline int CAI_BehaviorHost<BASE_NPC>::TranslateSchedule( int scheduleType ) { if ( m_pCurBehavior ) { return m_pCurBehavior->BridgeTranslateSchedule( scheduleType ); } return BaseClass::TranslateSchedule( scheduleType ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::PrescheduleThink() { BaseClass::PrescheduleThink(); if ( m_pCurBehavior ) m_pCurBehavior->BridgePrescheduleThink(); }
//-------------------------------------
template <class BASE_NPC> inline int CAI_BehaviorHost<BASE_NPC>::SelectSchedule() { m_bCalledBehaviorSelectSchedule = true; if ( m_pCurBehavior ) { return m_pCurBehavior->BridgeSelectSchedule(); }
return BaseClass::SelectSchedule(); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::KeepRunningBehavior() { if ( m_pCurBehavior ) m_bCalledBehaviorSelectSchedule = true; }
//-------------------------------------
template <class BASE_NPC> inline int CAI_BehaviorHost<BASE_NPC>::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ) { m_bCalledBehaviorSelectSchedule = true; int result = 0; if ( m_pCurBehavior && m_pCurBehavior->BridgeSelectFailSchedule( failedSchedule, failedTask, taskFailCode, &result ) ) return result; return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::StartTask( const Task_t *pTask ) { if ( m_pCurBehavior && m_pCurBehavior->BridgeStartTask( pTask ) ) return; BaseClass::StartTask( pTask ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::RunTask( const Task_t *pTask ) { if ( m_pCurBehavior && m_pCurBehavior->BridgeRunTask( pTask ) ) return; BaseClass::RunTask( pTask ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::AimGun( void ) { if ( m_pCurBehavior && m_pCurBehavior->BridgeAimGun() ) return; BaseClass::AimGun(); }
//-------------------------------------
template <class BASE_NPC> inline CAI_Schedule *CAI_BehaviorHost<BASE_NPC>::GetSchedule(int localScheduleID) { CAI_Schedule *pResult; if ( m_pCurBehavior && m_pCurBehavior->BridgeGetSchedule( localScheduleID, &pResult ) ) return pResult; return BaseClass::GetSchedule( localScheduleID ); }
//-------------------------------------
template <class BASE_NPC> inline const char *CAI_BehaviorHost<BASE_NPC>::TaskName(int taskID) { const char *pszResult = NULL; if ( m_pCurBehavior && m_pCurBehavior->BridgeTaskName( taskID, &pszResult ) ) return pszResult; return BaseClass::TaskName( taskID ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::BuildScheduleTestBits() { if ( m_pCurBehavior ) m_pCurBehavior->BridgeBuildScheduleTestBits(); BaseClass::BuildScheduleTestBits(); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::OnChangeHintGroup( string_t oldGroup, string_t newGroup ) { for( int i = 0; i < m_Behaviors.Count(); i++ ) { m_Behaviors[i]->BridgeOnChangeHintGroup( oldGroup, newGroup ); } BaseClass::OnChangeHintGroup( oldGroup, newGroup ); }
//-------------------------------------
template <class BASE_NPC> inline Activity CAI_BehaviorHost<BASE_NPC>::BackBridge_NPC_TranslateActivity( Activity activity ) { return BaseClass::NPC_TranslateActivity( activity ); }
//-------------------------------------
template <class BASE_NPC> inline Activity CAI_BehaviorHost<BASE_NPC>::NPC_TranslateActivity( Activity activity ) { if ( m_pCurBehavior ) { return m_pCurBehavior->BridgeNPC_TranslateActivity( activity ); } return BaseClass::NPC_TranslateActivity( activity ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::IsCurTaskContinuousMove() { bool result = false; if ( m_pCurBehavior && m_pCurBehavior->BridgeIsCurTaskContinuousMove( &result ) ) return result; return BaseClass::IsCurTaskContinuousMove(); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::OnMovementFailed() { if ( m_pCurBehavior ) m_pCurBehavior->BridgeOnMovementFailed(); BaseClass::OnMovementFailed(); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::OnMovementComplete() { if ( m_pCurBehavior ) m_pCurBehavior->BridgeOnMovementComplete(); BaseClass::OnMovementComplete(); }
//-------------------------------------
template <class BASE_NPC> inline float CAI_BehaviorHost<BASE_NPC>::GetDefaultNavGoalTolerance() { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeGetDefaultNavGoalTolerance(); return BaseClass::GetDefaultNavGoalTolerance(); }
//-------------------------------------
template <class BASE_NPC> inline float CAI_BehaviorHost<BASE_NPC>::BackBridge_GetDefaultNavGoalTolerance() { return BaseClass::GetDefaultNavGoalTolerance(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::FValidateHintType( CAI_Hint *pHint ) { bool result = false; if ( m_pCurBehavior && m_pCurBehavior->BridgeFValidateHintType( pHint, &result ) ) return result; return BaseClass::FValidateHintType( pHint ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_IsValidEnemy(CBaseEntity *pEnemy) { return BaseClass::IsValidEnemy( pEnemy ); }
//-------------------------------------
template <class BASE_NPC> inline CBaseEntity *CAI_BehaviorHost<BASE_NPC>::BackBridge_BestEnemy(void) { return BaseClass::BestEnemy(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_IsValidCover( const Vector &vLocation, CAI_Hint const *pHint ) { return BaseClass::IsValidCover( vLocation, pHint ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_IsValidShootPosition( const Vector &vLocation, CAI_Node *pNode, CAI_Hint const *pHint ) { return BaseClass::IsValidShootPosition( vLocation, pNode, pHint ); }
//-------------------------------------
template <class BASE_NPC> inline float CAI_BehaviorHost<BASE_NPC>::BackBridge_GetMaxTacticalLateralMovement( void ) { return BaseClass::GetMaxTacticalLateralMovement(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_ShouldIgnoreSound( CSound *pSound ) { return BaseClass::ShouldIgnoreSound( pSound ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::BackBridge_OnSeeEntity( CBaseEntity *pEntity ) { BaseClass::OnSeeEntity( pEntity ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::BackBridge_OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ) { BaseClass::OnFriendDamaged( pSquadmate, pAttacker ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_IsInterruptable( void ) { return BaseClass::IsInterruptable(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_IsNavigationUrgent( void ) { return BaseClass::IsNavigationUrgent(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_CanFlinch( void ) { return BaseClass::CanFlinch(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_IsCrouching( void ) { return BaseClass::IsCrouching(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_IsCrouchedActivity( Activity activity ) { return BaseClass::IsCrouchedActivity( activity ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_QueryHearSound( CSound *pSound ) { return BaseClass::QueryHearSound( pSound ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_CanRunAScriptedNPCInteraction( bool bForced ) { return BaseClass::CanRunAScriptedNPCInteraction( bForced ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_ShouldPlayerAvoid( void ) { return BaseClass::ShouldPlayerAvoid(); }
//-------------------------------------
template <class BASE_NPC> inline int CAI_BehaviorHost<BASE_NPC>::BackBridge_OnTakeDamage_Alive( const CTakeDamageInfo &info ) { return BaseClass::OnTakeDamage_Alive( info ); }
//-------------------------------------
template <class BASE_NPC> inline float CAI_BehaviorHost<BASE_NPC>::BackBridge_GetReasonableFacingDist( void ) { return BaseClass::GetReasonableFacingDist(); }
//-------------------------------------
template <class BASE_NPC> inline Activity CAI_BehaviorHost<BASE_NPC>::BackBridge_GetFlinchActivity( bool bHeavyDamage, bool bGesture ) { return BaseClass::GetFlinchActivity( bHeavyDamage, bGesture ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::BackBridge_OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ) { return BaseClass::OnCalcBaseMove( pMoveGoal, distClear, pResult ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::BackBridge_ModifyOrAppendCriteria( AI_CriteriaSet &criteriaSet ) { BaseClass::ModifyOrAppendCriteria( criteriaSet ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::BackBridge_Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity ) { BaseClass::Teleport( newPosition, newAngles, newVelocity ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::BackBridge_HandleAnimEvent( animevent_t *pEvent ) { BaseClass::HandleAnimEvent( pEvent ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::IsValidEnemy( CBaseEntity *pEnemy ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeIsValidEnemy( pEnemy ); return BaseClass::IsValidEnemy( pEnemy ); }
//-------------------------------------
template <class BASE_NPC> inline CBaseEntity *CAI_BehaviorHost<BASE_NPC>::BestEnemy() { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeBestEnemy(); return BaseClass::BestEnemy(); } //-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::ShouldAlwaysThink() { bool result = false; if ( m_pCurBehavior && m_pCurBehavior->BridgeShouldAlwaysThink( &result ) ) return result; return BaseClass::ShouldAlwaysThink(); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon ) { for( int i = 0; i < m_Behaviors.Count(); i++ ) { m_Behaviors[i]->BridgeOnChangeActiveWeapon( pOldWeapon, pNewWeapon ); } BaseClass::OnChangeActiveWeapon( pOldWeapon, pNewWeapon ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::SpeakMapmakerInterruptConcept( string_t iszConcept ) { for( int i = 0; i < m_Behaviors.Count(); i++ ) { if ( m_Behaviors[i]->BridgeSpeakMapmakerInterruptConcept( iszConcept ) ) return true; }
return false; }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::OnRestore() { for( int i = 0; i < m_Behaviors.Count(); i++ ) { m_Behaviors[i]->BridgeOnRestore(); } BaseClass::OnRestore(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::IsValidCover( const Vector &vLocation, CAI_Hint const *pHint ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeIsValidCover( vLocation, pHint ); return BaseClass::IsValidCover( vLocation, pHint ); } //-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::IsValidShootPosition( const Vector &vLocation, CAI_Node *pNode, CAI_Hint const *pHint ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeIsValidShootPosition( vLocation, pNode, pHint ); return BaseClass::IsValidShootPosition( vLocation, pNode, pHint ); }
//-------------------------------------
template <class BASE_NPC> inline float CAI_BehaviorHost<BASE_NPC>::GetMaxTacticalLateralMovement( void ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeGetMaxTacticalLateralMovement();
return BaseClass::GetMaxTacticalLateralMovement(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::ShouldIgnoreSound( CSound *pSound ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeShouldIgnoreSound( pSound ); return BaseClass::ShouldIgnoreSound( pSound ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::OnSeeEntity( CBaseEntity *pEntity ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeOnSeeEntity( pEntity );
BaseClass::OnSeeEntity( pEntity ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeOnFriendDamaged( pSquadmate, pAttacker );
BaseClass::OnFriendDamaged( pSquadmate, pAttacker ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::IsInterruptable( void ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeIsInterruptable(); return BaseClass::IsInterruptable(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::IsNavigationUrgent( void ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeIsNavigationUrgent();
return BaseClass::IsNavigationUrgent(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::CanFlinch( void ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeCanFlinch();
return BaseClass::CanFlinch(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::IsCrouching( void ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeIsCrouching();
return BaseClass::IsCrouching(); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::IsCrouchedActivity( Activity activity ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeIsCrouchedActivity( activity );
return BaseClass::IsCrouchedActivity( activity ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::QueryHearSound( CSound *pSound ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeQueryHearSound( pSound );
return BaseClass::QueryHearSound( pSound ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::CanRunAScriptedNPCInteraction( bool bForced ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeCanRunAScriptedNPCInteraction( bForced );
return BaseClass::CanRunAScriptedNPCInteraction( bForced ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::ShouldPlayerAvoid( void ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeShouldPlayerAvoid(); return BaseClass::ShouldPlayerAvoid(); }
//-------------------------------------
template <class BASE_NPC> inline int CAI_BehaviorHost<BASE_NPC>::OnTakeDamage_Alive( const CTakeDamageInfo &info ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeOnTakeDamage_Alive( info ); return BaseClass::OnTakeDamage_Alive( info ); }
//-------------------------------------
template <class BASE_NPC> inline float CAI_BehaviorHost<BASE_NPC>::GetReasonableFacingDist( void ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeGetReasonableFacingDist(); return BaseClass::GetReasonableFacingDist(); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::Precache() { BaseClass::Precache(); for( int i = 0; i < m_Behaviors.Count(); i++ ) { m_Behaviors[i]->BridgePrecache(); } }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::ScheduledMoveToGoalEntity( int scheduleType, CBaseEntity *pGoalEntity, Activity movementActivity ) { // If a behavior is active, we need to stop running it ChangeBehaviorTo( NULL );
return BaseClass::ScheduledMoveToGoalEntity( scheduleType, pGoalEntity, movementActivity ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::ScheduledFollowPath( int scheduleType, CBaseEntity *pPathStart, Activity movementActivity ) { // If a behavior is active, we need to stop running it ChangeBehaviorTo( NULL );
return BaseClass::ScheduledFollowPath( scheduleType, pPathStart, movementActivity ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::ForceSelectedGo(CBaseEntity *pPlayer, const Vector &targetPos, const Vector &traceDir, bool bRun) { // If a behavior is active, we need to stop running it ChangeBehaviorTo( NULL );
BaseClass::ForceSelectedGo(pPlayer, targetPos, traceDir, bRun); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::ForceSelectedGoRandom(void) { // If a behavior is active, we need to stop running it ChangeBehaviorTo( NULL );
BaseClass::ForceSelectedGoRandom(); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::NPCInit() { BaseClass::NPCInit(); for( int i = 0; i < m_Behaviors.Count(); i++ ) { m_Behaviors[i]->BridgeSpawn(); } }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::UpdateOnRemove() { for( int i = 0; i < m_Behaviors.Count(); i++ ) { m_Behaviors[i]->BridgeUpdateOnRemove(); } BaseClass::UpdateOnRemove(); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::Event_Killed( const CTakeDamageInfo &info ) { for( int i = 0; i < m_Behaviors.Count(); i++ ) { m_Behaviors[i]->BridgeEvent_Killed( info ); } BaseClass::Event_Killed( info ); }
//-------------------------------------
template <class BASE_NPC> inline Activity CAI_BehaviorHost<BASE_NPC>::GetFlinchActivity( bool bHeavyDamage, bool bGesture ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeGetFlinchActivity( bHeavyDamage, bGesture );
return BaseClass::GetFlinchActivity( bHeavyDamage, bGesture ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeOnCalcBaseMove( pMoveGoal, distClear, pResult );
return BaseClass::OnCalcBaseMove( pMoveGoal, distClear, pResult ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::ModifyOrAppendCriteria( AI_CriteriaSet &criteriaSet ) { BaseClass::ModifyOrAppendCriteria( criteriaSet );
if ( m_pCurBehavior ) { // Append active behavior name criteriaSet.AppendCriteria( "active_behavior", GetRunningBehavior()->GetName() ); m_pCurBehavior->BridgeModifyOrAppendCriteria( criteriaSet ); return; } }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity ) { if ( m_pCurBehavior ) { m_pCurBehavior->BridgeTeleport( newPosition, newAngles, newVelocity ); return; }
BaseClass::Teleport( newPosition, newAngles, newVelocity ); }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::HandleAnimEvent( animevent_t *pEvent ) { if ( m_pCurBehavior ) return m_pCurBehavior->BridgeHandleAnimEvent( pEvent );
return BaseClass::HandleAnimEvent( pEvent ); }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::OnBehaviorChangeStatus( CAI_BehaviorBase *pBehavior, bool fCanFinishSchedule ) { if ( pBehavior == GetRunningBehavior() && !pBehavior->CanSelectSchedule() && !fCanFinishSchedule ) { DeferSchedulingToBehavior( NULL ); return true; } return false; }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::OnChangeRunningBehavior( CAI_BehaviorBase *pOldBehavior, CAI_BehaviorBase *pNewBehavior ) { }
//-------------------------------------
template <class BASE_NPC> inline void CAI_BehaviorHost<BASE_NPC>::AddBehavior( CAI_BehaviorBase *pBehavior ) { #ifdef DEBUG Assert( m_Behaviors.Find( pBehavior ) == m_Behaviors.InvalidIndex() ); Assert( m_fDebugInCreateBehaviors ); for ( int i = 0; i < m_Behaviors.Count(); i++) { Assert( typeid(*m_Behaviors[i]) != typeid(*pBehavior) ); } #endif m_Behaviors.AddToTail( pBehavior ); pBehavior->SetOuter( this ); pBehavior->SetBackBridge( this ); }
//-------------------------------------
template <class BASE_NPC> inline CAI_BehaviorBase **CAI_BehaviorHost<BASE_NPC>::AccessBehaviors() { if (m_Behaviors.Count()) return m_Behaviors.Base(); return NULL; }
//-------------------------------------
template <class BASE_NPC> inline int CAI_BehaviorHost<BASE_NPC>::NumBehaviors() { return m_Behaviors.Count(); }
//-------------------------------------
template <class BASE_NPC> inline int CAI_BehaviorHost<BASE_NPC>::Save( ISave &save ) { int result = BaseClass::Save( save ); if ( result ) CAI_BehaviorBase::SaveBehaviors( save, m_pCurBehavior, AccessBehaviors(), NumBehaviors() ); return result; }
//-------------------------------------
template <class BASE_NPC> inline int CAI_BehaviorHost<BASE_NPC>::Restore( IRestore &restore ) { int result = BaseClass::Restore( restore ); if ( result ) { int iCurrent = CAI_BehaviorBase::RestoreBehaviors( restore, AccessBehaviors(), NumBehaviors() ); if ( iCurrent != -1 ) m_pCurBehavior = AccessBehaviors()[iCurrent]; else m_pCurBehavior = NULL; } return result; }
//-------------------------------------
template <class BASE_NPC> inline bool CAI_BehaviorHost<BASE_NPC>::CreateComponents() { if ( BaseClass::CreateComponents() ) { #ifdef DEBUG m_fDebugInCreateBehaviors = true; #endif bool result = CreateBehaviors(); #ifdef DEBUG m_fDebugInCreateBehaviors = false; #endif return result; } return false; }
//-----------------------------------------------------------------------------
#endif // AI_BEHAVIOR_H
Last edited by 01101101; 12-17-2009 at 19:16.
|
|