Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

579 lines
15 KiB

// ai_addon.cpp
#include "cbase.h"
#include "ai_addon.h"
#include "ai_basenpc.h"
#include "ai_behavior_addonhost.h"
//---------------------------------------------------------
// Answers YES if the attachment is on the model and not
// currently plugged by another add-on.
//
// Answers NO if the host's model lacks the attachment or
// the add-on is currently plugged by another add-on.
//
// This is terribly inefficient right now, but it works
// and lets us move on.
//---------------------------------------------------------
bool IsAddOnAttachmentAvailable( CAI_BaseNPC *pHost, char *pszAttachmentName )
{
char szOrigialAttachmentName[ 256 ];
Q_strcpy( szOrigialAttachmentName, pszAttachmentName );
int iCount = 0;
// If this loops 20 times, the translate function probably isn't returning the end of list value "" -Jeep
while ( iCount < 20 )
{
// Try another
Q_strcpy( pszAttachmentName, szOrigialAttachmentName );
pHost->TranslateAddOnAttachment( pszAttachmentName, iCount );
if ( pszAttachmentName[ 0 ] == '\0' )
{
return false;
}
if ( pHost->LookupAttachment(pszAttachmentName) == 0 )
{
// Translated to an attachment that doesn't exist
Msg("***AddOn Error! Host NPC %s does not have attachment %s\n", pHost->GetDebugName(), pszAttachmentName );
return false;
}
int iWishedAttachmentID = pHost->LookupAttachment( pszAttachmentName );
CAI_BehaviorBase **ppBehaviors = pHost->AccessBehaviors();
int nBehaviors = pHost->NumBehaviors();
bool bAttachmentFilled = false;
for ( int i = 0; i < nBehaviors && !bAttachmentFilled; i++ )
{
CAI_AddOnBehaviorBase *pAddOnBehavior = dynamic_cast<CAI_AddOnBehaviorBase *>(ppBehaviors[i]);
if ( pAddOnBehavior )
{
CAI_AddOn **ppAddOns = pAddOnBehavior->GetAddOnsBase();
int nAddOns = pAddOnBehavior->NumAddOns();
for ( int j = 0; j < nAddOns && !bAttachmentFilled; j++ )
{
bAttachmentFilled = ( ppAddOns[j]->GetAttachmentID() == iWishedAttachmentID );
}
}
}
if ( !bAttachmentFilled )
{
return true;
}
++iCount;
}
// We should never get here
DevWarning( "Translating the attachment was tried more than 50 times!\n" );
return false;
}
//---------------------------------------------------------
//---------------------------------------------------------
int CountAddOns( CAI_BaseNPC *pHost )
{
int nAddOns = 0;
CAI_BehaviorBase **ppBehaviors = pHost->AccessBehaviors();
int nBehaviors = pHost->NumBehaviors();
for ( int i = 0; i < nBehaviors; i++ )
{
CAI_AddOnBehaviorBase *pAddOnBehavior = dynamic_cast<CAI_AddOnBehaviorBase *>(ppBehaviors[i]);
if ( pAddOnBehavior )
{
nAddOns += pAddOnBehavior->NumAddOns();
}
}
return nAddOns;
}
//---------------------------------------------------------
//---------------------------------------------------------
BEGIN_DATADESC( CAI_AddOn )
DEFINE_FIELD( m_hNPCHost, FIELD_EHANDLE ),
DEFINE_THINKFUNC( DispatchAddOnThink ),
DEFINE_FIELD( m_hPhysReplacement, FIELD_EHANDLE ),
DEFINE_FIELD( m_iPhysReplacementSolidFlags, FIELD_INTEGER ),
DEFINE_FIELD( m_iPhysReplacementMoveType, FIELD_INTEGER ),
DEFINE_FIELD( m_angPhysReplacementLocalOrientation, FIELD_VECTOR ),
DEFINE_FIELD( m_vecPhysReplacementDetatchForce, FIELD_VECTOR ),
DEFINE_FIELD( m_bWasAttached, FIELD_BOOLEAN ),
DEFINE_FIELD( m_flWaitFinished, FIELD_TIME ),
DEFINE_FIELD( m_flNextAttachTime, FIELD_FLOAT ),
DEFINE_INPUTFUNC( FIELD_STRING, "Install", InputInstall ),
DEFINE_INPUTFUNC( FIELD_VOID, "Remove", InputRemove ),
END_DATADESC()
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_AddOn::Precache()
{
BaseClass::Precache();
PrecacheModel( GetAddOnModelName() );
PrecacheScriptSound( "AddOn.Install" );
}
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_AddOn::Spawn()
{
BaseClass::Spawn();
Precache();
CBaseEntity *pPhysReplacement = m_hPhysReplacement.Get();
if ( pPhysReplacement )
{
// Use the same model as the replacement
SetModel( pPhysReplacement->GetModelName().ToCStr() );
}
else
{
SetModel( GetAddOnModelName() );
}
SetCollisionGroup( COLLISION_GROUP_WEAPON );
VPhysicsInitNormal( SOLID_VPHYSICS, GetSolidFlags() | FSOLID_TRIGGER, false );
SetMoveType( MOVETYPE_VPHYSICS );
SetThink( &CAI_AddOn::DispatchAddOnThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_AddOn::UpdateOnRemove()
{
Remove();
BaseClass::UpdateOnRemove();
}
int CAI_AddOn::DrawDebugTextOverlays( void )
{
int text_offset = BaseClass::DrawDebugTextOverlays();
// Draw debug text for agent
m_AgentDebugOverlays = m_debugOverlays;
Vector vecLocalCenter;
ICollideable *pCollidable = GetCollideable();
if ( pCollidable )
{
VectorAdd( pCollidable->OBBMins(), pCollidable->OBBMaxs(), vecLocalCenter );
vecLocalCenter *= 0.5f;
if ( ( pCollidable->GetCollisionAngles() == vec3_angle ) || ( vecLocalCenter == vec3_origin ) )
{
VectorAdd( vecLocalCenter, pCollidable->GetCollisionOrigin(), m_vecAgentDebugOverlaysPos );
}
else
{
VectorTransform( vecLocalCenter, pCollidable->CollisionToWorldTransform(), m_vecAgentDebugOverlaysPos );
}
}
else
{
m_vecAgentDebugOverlaysPos = GetAbsOrigin();
}
text_offset = static_cast<CAI_Agent*>( this )->DrawDebugTextOverlays( text_offset );
return text_offset;
}
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_AddOn::GatherConditions()
{
CAI_Agent::GatherConditions();
ClearCondition( COND_ADDON_LOST_HOST );
if( GetNPCHost() )
{
m_bWasAttached = true;
}
else
{
if( m_bWasAttached == true )
{
SetCondition( COND_ADDON_LOST_HOST );
if ( m_flNextAttachTime != 0.0f && m_flNextAttachTime < gpGlobals->curtime )
{
m_flNextAttachTime = 0.0f;
m_bWasAttached = false;
}
}
}
}
//---------------------------------------------------------
//---------------------------------------------------------
int CAI_AddOn::SelectSchedule( void )
{
return SCHED_ADDON_NO_OWNER;
}
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_AddOn::StartTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_ADDON_WAIT:
m_flWaitFinished = gpGlobals->curtime + pTask->flTaskData;
break;
case TASK_ADDON_WAIT_RANDOM:
m_flWaitFinished = gpGlobals->curtime + random->RandomFloat( 0.1f, pTask->flTaskData );
break;
default:
CAI_Agent::StartTask( pTask );
break;
}
}
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_AddOn::RunTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_ADDON_WAIT:
case TASK_ADDON_WAIT_RANDOM:
if( gpGlobals->curtime > m_flWaitFinished )
TaskComplete();
break;
default:
CAI_Agent::RunTask( pTask );
break;
}
}
void CAI_AddOn::SetPhysReplacement( CBaseEntity *pEntity )
{
m_hPhysReplacement = pEntity;
}
//---------------------------------------------------------
//---------------------------------------------------------
bool CAI_AddOn::Attach( CAI_BaseNPC *pHost )
{
// Make sure we're not already attached to someone else!
Assert( GetAttachmentID() == INVALID_ADDON_ATTACHMENT_ID );
char szAttachmentName[ 256 ];
szAttachmentName[ 0 ] = '\0';
PickAttachment( pHost, szAttachmentName );
if ( szAttachmentName[ 0 ] == '\0' )
{
return false;
}
int iAttachmentIndex = pHost->LookupAttachment( szAttachmentName );
if ( !iAttachmentIndex )
{
return false;
}
Vector vecOrigin;
Vector vecForward, vecRight, vecUp;
QAngle angles;
pHost->GetAttachment( iAttachmentIndex, vecOrigin, angles );
AngleVectors( angles, &vecForward, &vecRight, &vecUp );
SetAbsOrigin( vecOrigin + GetAttachOffset( angles ) );
SetAbsAngles( GetAttachOrientation( angles ) );
m_iAttachmentID = iAttachmentIndex;
SetParent( pHost, iAttachmentIndex );
QAngle angLocalAngles = GetLocalOrientation();
SetLocalAngles( angLocalAngles );
// Stop acting physical
IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
if ( pPhysicsObject )
{
pPhysicsObject->EnableMotion( false );
pPhysicsObject->EnableCollisions( false );
}
SetMoveType( MOVETYPE_NONE );
// Handle the phys replacement
CBaseEntity *pPhysReplacement = m_hPhysReplacement.Get();
if ( pPhysReplacement )
{
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
pPlayer->ForceDropOfCarriedPhysObjects( pPhysReplacement );
pPhysReplacement->AddEffects( EF_NODRAW );
m_iPhysReplacementSolidFlags = pPhysReplacement->GetSolidFlags();
pPhysReplacement->SetSolidFlags( FSOLID_NOT_SOLID );
m_iPhysReplacementMoveType = pPhysReplacement->GetMoveType();
pPhysReplacement->SetMoveType( MOVETYPE_NONE );
pPhysReplacement->SetAbsOrigin( vecOrigin + GetAttachOffset( angles ) );
pPhysReplacement->SetAbsAngles( GetAttachOrientation( angles ) );
pPhysReplacement->SetParent( pHost, iAttachmentIndex );
pPhysReplacement->SetOwnerEntity( pHost );
m_angPhysReplacementLocalOrientation = pPhysReplacement->GetLocalAngles();
pPhysReplacement->SetLocalAngles( angLocalAngles );
IPhysicsObject *pReplacementPhysObject = pPhysReplacement->VPhysicsGetObject();
if ( pReplacementPhysObject )
{
pReplacementPhysObject->EnableMotion( false );
pReplacementPhysObject->EnableCollisions( false );
SetMoveType( MOVETYPE_NONE );
}
}
return true;
}
void CAI_AddOn::Dettach( void )
{
if ( !m_bWasAttached )
return;
m_flNextAttachTime = gpGlobals->curtime + 2.0f;
m_hNPCHost.Set( NULL );
SetParent( NULL );
IPhysicsObject *pPhysObject = NULL;
CBaseEntity *pPhysReplacement = m_hPhysReplacement.Get();
if ( pPhysReplacement )
{
// Make the replacement visible
pPhysReplacement->RemoveEffects( EF_NODRAW );
pPhysReplacement->SetSolidFlags( m_iPhysReplacementSolidFlags );
pPhysReplacement->SetMoveType( MoveType_t( m_iPhysReplacementMoveType ) );
pPhysObject = pPhysReplacement->VPhysicsGetObject();
if ( pPhysObject )
{
pPhysReplacement->SetMoveType( MOVETYPE_VPHYSICS );
}
pPhysReplacement->SetParent( NULL );
pPhysReplacement->SetOwnerEntity( NULL );
pPhysReplacement->SetLocalAngles( m_angPhysReplacementLocalOrientation );
// Kill ourselves off because we're being replaced
UTIL_Remove( this );
}
else
{
pPhysObject = VPhysicsGetObject();
if ( pPhysObject )
{
SetMoveType( MOVETYPE_VPHYSICS );
}
}
if ( pPhysObject )
{
// Start acting physical
pPhysObject->EnableCollisions( true );
pPhysObject->EnableMotion( true );
pPhysObject->EnableGravity( true );
pPhysObject->SetPosition( GetAbsOrigin(), GetAbsAngles(), true );
pPhysObject->Wake();
pPhysObject->AddVelocity( &m_vecPhysReplacementDetatchForce, NULL );
}
}
//---------------------------------------------------------
// Return true if I successfully attach to the NPC host.
//
// Return false if I am already attached to an NPC, or
// could not be attached to this host.
//---------------------------------------------------------
bool CAI_AddOn::Install( CAI_BaseNPC *pHost, bool bRemoveOnFail )
{
if( m_bWasAttached )
return false;
// Associate the addon with this host
Assert( m_hNPCHost == NULL ); // For now, prevent slamming from one host to the next.
m_hNPCHost.Set( pHost );
// Parent and
if ( Attach( pHost ) )
{
Bind();
return true;
}
// Failed to attach
m_hNPCHost = NULL;
if ( bRemoveOnFail || m_hPhysReplacement.Get() )
{
UTIL_Remove( this );
}
return false;
}
//---------------------------------------------------------
//---------------------------------------------------------
CAI_BaseNPC *CAI_AddOn::GetNPCHost()
{
return m_hNPCHost.Get();
}
//---------------------------------------------------------
//---------------------------------------------------------
CBaseEntity *CAI_AddOn::GetHostEnemy()
{
if( !GetNPCHost() )
return NULL;
return GetNPCHost()->GetEnemy();
}
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_AddOn::DispatchAddOnThink()
{
if( GetNPCHost() != NULL && !GetNPCHost()->IsAlive() )
{
EjectFromHost();
return;
}
CAI_Agent::Think(); SetNextThink( gpGlobals->curtime + GetThinkInterval() );
}
QAngle CAI_AddOn::GetLocalOrientation( void )
{
CBaseEntity *pPhysReplacement = m_hPhysReplacement.Get();
if ( pPhysReplacement )
{
CBaseAnimating *pBaseAnimatingReplacement = dynamic_cast<CBaseAnimating *>( pPhysReplacement );
if ( pBaseAnimatingReplacement )
{
int iMuzzle = pBaseAnimatingReplacement->LookupAttachment( "muzzle" );
if ( iMuzzle )
{
// Use the muzzle angles!
Vector vecMuzzleOrigin;
QAngle angMuzzleAngles;
pBaseAnimatingReplacement->GetAttachmentLocal( iMuzzle, vecMuzzleOrigin, angMuzzleAngles );
return angMuzzleAngles;
}
}
// Use the local angles
return pPhysReplacement->GetLocalAngles();
}
// No special angles to use
return QAngle( 0.0f, 0.0f, 0.0f );
}
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_AddOn::EjectFromHost()
{
Unbind();
m_hNPCHost.Set( NULL );
SetThink( NULL );
SetParent( NULL );
SetSize( Vector( 0,0,0), Vector(0,0,0) );
SetMoveType( MOVETYPE_FLYGRAVITY );
SetMoveCollide( MOVECOLLIDE_FLY_BOUNCE );
SetSolid( SOLID_BBOX );
Vector vecDir;
GetVectors( NULL, NULL, &vecDir );
SetAbsVelocity( GetAbsVelocity() + vecDir * RandomFloat(50, 200) );
QAngle avelocity( RandomFloat( 10, 60), RandomFloat( 10, 60), 0 );
SetLocalAngularVelocity( avelocity );
SetThink( &CBaseEntity::SUB_FadeOut );
SetNextThink( gpGlobals->curtime + 1.0f );
}
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_AddOn::InputInstall( inputdata_t &data )
{
CAI_BaseNPC *pHost = dynamic_cast<CAI_BaseNPC *>( gEntList.FindEntityByName( NULL, data.value.String() ) );
if( !pHost )
{
DevMsg(" AddOn: %s couldn't find Host %s\n", GetDebugName(), data.value.String() );
}
else
{
Install( pHost );
}
}
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_AddOn::InputRemove( inputdata_t &data )
{
Remove();
m_hNPCHost.Set( NULL );
SetThink( NULL );
SetParent( NULL );
UTIL_Remove( this );
}
AI_BEGIN_AGENT_(CAI_AddOn,CAI_Agent)
DECLARE_TASK( TASK_ADDON_WAIT )
DECLARE_TASK( TASK_ADDON_WAIT_RANDOM )
DECLARE_CONDITION( COND_ADDON_LOST_HOST )
DEFINE_SCHEDULE
(
SCHED_ADDON_NO_OWNER,
" Tasks"
" TASK_ADDON_WAIT 1"
" "
" Interrupts"
" "
)
AI_END_AGENT()