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
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()
|
|
|