📄 npc_hydra.cpp
字号:
// curve neck into spiral
float distBackFromHead = m_body[i].flActualLength;
Vector right, up;
VectorVectors( m_vecHeadDir, right, up );
#if 1
for (i = i - 1; i > 1 && distBackFromHead < distToTarget; i--)
{
Vector p0 = m_vecHeadGoal - m_vecHeadDir * distBackFromHead * 1.0;
m_body[i].vecGoalPos = p0;
if ((m_vecTarget - m_body[i].vecPos).Length() > distToTarget + distBackFromHead)
{
m_body[i].flGoalInfluence = 1.0 - (distBackFromHead / distToTarget);
}
else
{
m_body[i].vecGoalPos = EyePosition( ) - m_vecHeadDir * distBackFromHead;
m_body[i].flGoalInfluence = 1.0 - (distBackFromHead / distToTarget);
}
distBackFromHead += m_body[i].flActualLength;
}
#endif
}
return;
case TASK_HYDRA_PULLBACK:
{
if (m_body.Count() < 2)
{
TaskFail( "hydra is too short to begin stab" );
return;
}
CBaseEntity *pEnemy = (CBaseEntity *)UTIL_PlayerByIndex( 1 );
if (GetEnemy() != NULL)
{
pEnemy = GetEnemy();
}
AimHeadInTravelDirection( 0.2 );
// float dist = (EyePosition() - m_vecHeadGoal).Length();
if (m_flCurrentLength < m_idealLength + m_idealSegmentLength)
{
TaskComplete();
}
}
break;
default:
BaseClass::RunTask( pTask );
break;
}
}
//-------------------------------------
Vector CNPC_Hydra::EyePosition( )
{
int i = m_body.Count() - 1;
if (i >= 0)
{
return m_body[i].vecPos;
}
return GetAbsOrigin();
}
const QAngle &CNPC_Hydra::EyeAngles()
{
return GetAbsAngles();
}
Vector CNPC_Hydra::BodyTarget( const Vector &posSrc, bool bNoisy)
{
int i;
if (m_body.Count() < 2)
{
return GetAbsOrigin();
}
int iShortest = 1;
float flShortestDist = (posSrc - m_body[iShortest].vecPos).LengthSqr();
for (i = 2; i < m_body.Count(); i++)
{
float flDist = (posSrc - m_body[i].vecPos).LengthSqr();
if (flDist < flShortestDist)
{
iShortest = i;
flShortestDist = flDist;
}
}
// NDebugOverlay::Box(m_body[iShortest].vecPos, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0, 0, 255, 20, .1);
return m_body[iShortest].vecPos;
}
void CNPC_Hydra::AimHeadInTravelDirection( float flInfluence )
{
// aim in the direction of movement enemy
Vector delta = m_body[m_body.Count()-1].vecDelta;
VectorNormalize( delta );
if (DotProduct( delta, m_vecHeadDir ) < 0)
{
delta = -delta;
}
m_vecHeadDir = m_vecHeadDir * (1 - flInfluence) + delta * flInfluence;
VectorNormalize( m_vecHeadDir.GetForModify() );
}
//-------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Hydra impaling is done by creating an entity, forming a constraint
// between that entity and the target ragdoll, and then updating then
// entity to follow the hydra.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: This is the entity we create to follow the hydra
//-----------------------------------------------------------------------------
class CHydraImpale : public CBaseAnimating
{
DECLARE_CLASS( CHydraImpale, CBaseAnimating );
public:
DECLARE_DATADESC();
void Spawn( void );
void Precache( void );
void ImpaleThink( void );
IPhysicsConstraint *CreateConstraint( CNPC_Hydra *pHydra, IPhysicsObject *pTargetPhys, IPhysicsConstraintGroup *pGroup );
static CHydraImpale *Create( CNPC_Hydra *pHydra, CBaseEntity *pObject2 );
public:
IPhysicsConstraint *m_pConstraint;
CHandle<CNPC_Hydra> m_hHydra;
};
BEGIN_DATADESC( CHydraImpale )
DEFINE_PHYSPTR( CHydraImpale, m_pConstraint ),
DEFINE_FIELD( CHydraImpale, m_hHydra, FIELD_EHANDLE ),
DEFINE_THINKFUNC( CHydraImpale, ImpaleThink ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( hydra_impale, CHydraImpale );
//-----------------------------------------------------------------------------
// Purpose: To by usable by the constraint system, this needs to have a phys model.
//-----------------------------------------------------------------------------
void CHydraImpale::Spawn( void )
{
Precache();
SetModel( "models/props_junk/cardboard_box001a.mdl" );
m_fEffects |= EF_NODRAW;
// We don't want this to be solid, because we don't want it to collide with the hydra.
SetSolid( SOLID_VPHYSICS );
AddSolidFlags( FSOLID_NOT_SOLID );
VPhysicsInitShadow( false, false );
// Disable movement on this sucker, we're going to move him manually
SetMoveType( MOVETYPE_FLY );
BaseClass::Spawn();
m_pConstraint = NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHydraImpale::Precache( void )
{
PrecacheModel( "models/props_junk/cardboard_box001a.mdl" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose: Update the impale entity's position to the hydra's desired
//-----------------------------------------------------------------------------
void CHydraImpale::ImpaleThink( void )
{
if ( !m_hHydra )
{
// Remove ourselves.
m_pConstraint->Deactivate();
UTIL_Remove( this );
return;
}
// Ask the Hydra where he'd like the ragdoll, and move ourselves there
Vector vecOrigin;
QAngle vecAngles;
m_hHydra->GetDesiredImpaledPosition( &vecOrigin, &vecAngles );
SetAbsOrigin( vecOrigin );
SetAbsAngles( vecAngles );
//NDebugOverlay::Cross3D( vecOrigin, Vector( -5, -5, -5 ), Vector( 5, 5, 5 ), 255, 0, 0, 20, .1);
SetNextThink( gpGlobals->curtime + 0.1f );
}
//-----------------------------------------------------------------------------
// Purpose: Activate/create the constraint
//-----------------------------------------------------------------------------
IPhysicsConstraint *CHydraImpale::CreateConstraint( CNPC_Hydra *pHydra, IPhysicsObject *pTargetPhys, IPhysicsConstraintGroup *pGroup )
{
m_hHydra = pHydra;
IPhysicsObject *pImpalePhysObject = VPhysicsGetObject();
Assert( pImpalePhysObject );
constraint_fixedparams_t fixed;
fixed.Defaults();
fixed.InitWithCurrentObjectState( pImpalePhysObject, pTargetPhys );
fixed.constraint.Defaults();
m_pConstraint = physenv->CreateFixedConstraint( pImpalePhysObject, pTargetPhys, pGroup, fixed );
if ( m_pConstraint )
{
m_pConstraint->SetGameData( (void *)this );
}
SetThink( ImpaleThink );
SetNextThink( gpGlobals->curtime );
return m_pConstraint;
}
//-----------------------------------------------------------------------------
// Purpose: Create a Hydra Impale between the hydra and the entity passed in
//-----------------------------------------------------------------------------
CHydraImpale *CHydraImpale::Create( CNPC_Hydra *pHydra, CBaseEntity *pTarget )
{
Vector vecOrigin;
QAngle vecAngles;
pHydra->GetDesiredImpaledPosition( &vecOrigin, &vecAngles );
pTarget->Teleport( &vecOrigin, &vecAngles, &vec3_origin );
CHydraImpale *pImpale = (CHydraImpale *)CBaseEntity::Create( "hydra_impale", vecOrigin, vecAngles );
if ( !pImpale )
return NULL;
IPhysicsObject *pTargetPhysObject = pTarget->VPhysicsGetObject();
if ( !pTargetPhysObject )
{
Msg(" Error: Tried to hydra_impale an entity without a physics model.\n" );
return NULL;
}
IPhysicsConstraintGroup *pGroup = NULL;
// Ragdoll? If so, use it's constraint group
CRagdollProp *pRagdoll = dynamic_cast<CRagdollProp*>(pTarget);
if ( pRagdoll )
{
pGroup = pRagdoll->GetRagdoll()->pGroup;
}
if ( !pImpale->CreateConstraint( pHydra, pTargetPhysObject, pGroup ) )
return NULL;
return pImpale;
}
void CNPC_Hydra::AttachStabbedEntity( CBaseAnimating *pAnimating, Vector vecForce, trace_t &tr )
{
CTakeDamageInfo info( this, this, pAnimating->m_iHealth+25, DMG_SLASH );
info.SetDamageForce( vecForce );
info.SetDamagePosition( tr.endpos );
CBaseEntity *pRagdoll = CreateServerRagdoll( pAnimating, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS );
// Create our impale entity
CHydraImpale::Create( this, pRagdoll );
m_bStabbedEntity = true;
UTIL_Remove( pAnimating );
}
void CNPC_Hydra::UpdateStabbedEntity( void )
{
/*
CBaseEntity *pEntity = m_grabController.GetAttached();
if ( !pEntity )
{
DetachStabbedEntity( false );
return;
}
QAngle vecAngles(0,0,1);
Vector vecOrigin = m_body[m_body.Count()-2].vecPos;
//NDebugOverlay::Cross3D( vecOrigin, Vector( -5, -5, -5 ), Vector( 5, 5, 5 ), 255, 0, 0, 20, .1);
m_grabController.SetTargetPosition( vecOrigin, vecAngles );
*/
}
void CNPC_Hydra::DetachStabbedEntity( bool playSound )
{
/*
CBaseEntity *pObject = m_grabController.GetAttached();
if ( pObject != NULL )
{
IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
// Enable collision with this object again
if ( pPhysics != NULL )
{
physenv->EnableCollisions( pPhysics, VPhysicsGetObject() );
pPhysics->RecheckCollisionFilter();
}
}
m_grabController.DetachEntity();
*/
if ( playSound )
{
//Play the detach sound
}
m_bStabbedEntity = false;
}
void CNPC_Hydra::GetDesiredImpaledPosition( Vector *vecOrigin, QAngle *vecAngles )
{
*vecOrigin = m_body[m_body.Count()-2].vecPos;
*vecAngles = QAngle(0,0,0);
}
//-----------------------------------------------------------------------------
//
// CNPC_Hydra Schedules
//
//-------------------------------------
AI_BEGIN_CUSTOM_NPC( npc_hydra, CNPC_Hydra )
//Register our interactions
//Conditions
DECLARE_CONDITION( COND_HYDRA_SNAGGED )
DECLARE_CONDITION( COND_HYDRA_STUCK )
DECLARE_CONDITION( COND_HYDRA_OVERSHOOT )
DECLARE_CONDITION( COND_HYDRA_OVERSTRETCH )
DECLARE_CONDITION( COND_HYDRA_STRIKE )
DECLARE_CONDITION( COND_HYDRA_NOSTUCK )
//Squad slots
//Tasks
DECLARE_TASK( TASK_HYDRA_RETRACT )
DECLARE_TASK( TASK_HYDRA_DEPLOY )
DECLARE_TASK( TASK_HYDRA_GET_OBJECT )
DECLARE_TASK( TASK_HYDRA_THROW_OBJECT )
DECLARE_TASK( TASK_HYDRA_PREP_STAB )
DECLARE_TASK( TASK_HYDRA_STAB )
DECLARE_TASK( TASK_HYDRA_PULLBACK )
//Activities
DECLARE_ACTIVITY( ACT_HYDRA_COWER )
DECLARE_ACTIVITY( ACT_HYDRA_STAB )
//=========================================================
// > SCHED_HYDRA_STAND_LOOK
//=========================================================
DEFINE_SCHEDULE
(
SCHED_HYDRA_DEPLOY,
" Tasks"
" TASK_HYDRA_DEPLOY 0"
" TASK_WAIT 0.5"
""
" Interrupts"
" COND_NEW_ENEMY"
)
//=========================================================
// > SCHED_HYDRA_COWER
//=========================================================
DEFINE_SCHEDULE
(
SCHED_HYDRA_RETRACT,
" Tasks"
" TASK_STOP_MOVING 0"
" TASK_SET_ACTIVITY ACTIVITY:ACT_HYDRA_COWER"
" TASK_WAIT 0.5"
""
" Interrupts"
)
DEFINE_SCHEDULE
(
SCHED_HYDRA_IDLE,
" Tasks"
" TASK_STOP_MOVING 0"
" TASK_WAIT_INDEFINITE 0"
""
" Interrupts "
" COND_NEW_ENEMY"
)
DEFINE_SCHEDULE
(
SCHED_HYDRA_STAB,
" Tasks"
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HYDRA_DEPLOY"
" TASK_HYDRA_PREP_STAB 4.0"
" TASK_HYDRA_STAB 0"
" TASK_WAIT 0.5"
// " TASK_HYDRA_PULLBACK 100"
""
" Interrupts "
" COND_NEW_ENEMY"
" COND_HYDRA_OVERSTRETCH"
)
DEFINE_SCHEDULE
(
SCHED_HYDRA_PULLBACK,
" Tasks"
" TASK_STOP_MOVING 0"
" TASK_WAIT 0.4"
" TASK_HYDRA_PULLBACK 100"
""
" Interrupts "
" COND_NEW_ENEMY"
)
DEFINE_SCHEDULE
(
SCHED_HYDRA_THROW,
" Tasks"
" TASK_STOP_MOVING 0"
" TASK_HYDRA_GET_OBJECT 0"
" TASK_WAIT_FOR_MOVEMENT 0"
" TASK_HYDRA_THROW_OBJECT 0"
" TASK_WAIT 1"
""
" Interrupts"
)
DEFINE_SCHEDULE
(
SCHED_HYDRA_RANGE_ATTACK,
" Tasks"
" TASK_STOP_MOVING 0"
" TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
" TASK_FACE_ENEMY 0"
" TASK_RANGE_ATTACK1 0"
""
" Interrupts"
" COND_NEW_ENEMY"
" COND_ENEMY_DEAD"
" COND_LIGHT_DAMAGE"
" COND_HEAVY_DAMAGE"
" COND_ENEMY_OCCLUDED"
" COND_NO_PRIMARY_AMMO"
" COND_HEAR_DANGER"
)
AI_END_CUSTOM_NPC()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -