📄 npc_hydra.cpp
字号:
if (m_body.Count() <= 3)
return false;
// don't contract overly long segments
if (m_body[2].flActualLength > m_idealSegmentLength * 2.0)
return false;
if (!IsValidConnection( 1, 3 ))
return false;
m_body.Remove( 2 );
CalcRelaxedLength( );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Find the first stuck node that's closest to the head
// Input :
// Output :
//-----------------------------------------------------------------------------
int CNPC_Hydra::VirtualRoot( )
{
// first first stuck segment closest to head;
int iStuckHead;
for (iStuckHead = m_body.Count() - 2; iStuckHead > 1; iStuckHead--)
{
if (m_body[iStuckHead].bStuck)
{
return iStuckHead;
}
}
return 1;
}
//-----------------------------------------------------------------------------
// Purpose: Insert a node before the given node.
// Input :
// Output :
//-----------------------------------------------------------------------------
bool CNPC_Hydra::AddNodeBefore( int iNode )
{
if (iNode < 1)
return false;
HydraBone bone;
bone.vecPos = (m_body[iNode].vecPos + m_body[iNode-1].vecPos) * 0.5;
bone.vecDelta = (m_body[iNode].vecDelta + m_body[iNode-1].vecDelta) * 0.5;
/*
// FIXME: can't do this, may be embedded in the world
int i0 = (iNode>2)?iNode-2:0;
int i1 = (iNode>1)?iNode-1:0;
int i2 = iNode;
int i3 = (iNode<m_body.Count()-1)?iNode+1:m_body.Count()-1;
Catmull_Rom_Spline( m_body[i0].vecPos, m_body[i1].vecPos, m_body[i2].vecPos, m_body[i3].vecPos, 0.5, bone.vecPos );
*/
bone.flActualLength = (m_body[iNode].vecPos - bone.vecPos).Length();
bone.flIdealLength = m_idealSegmentLength;
m_body[iNode-1].flActualLength = bone.flActualLength;
//Vector vecGoalPos;
//float flGoalInfluence;
m_body.InsertBefore( iNode, bone );
return true;
}
bool CNPC_Hydra::AddNodeAfter( int iNode )
{
AddNodeBefore( iNode + 1 );
return false;
}
bool CNPC_Hydra::GrowFromVirtualRoot( )
{
if (m_body[1].flActualLength < m_idealSegmentLength * 0.5)
return false;
return AddNodeAfter( 1 );
}
bool CNPC_Hydra::GrowFromMostStretched( )
{
int iNode = VirtualRoot( );
int iLongest = iNode;
float dist = m_idealSegmentLength * 0.5;
for (iNode; iNode < m_body.Count() - 1; iNode++)
{
if (m_body[iNode].flActualLength > dist)
{
iLongest = iNode;
dist = m_body[iNode].flActualLength;
}
}
if (m_body[iLongest].flActualLength <= dist)
{
return AddNodeAfter( iLongest );
}
return false;
}
void CNPC_Hydra::CalcRelaxedLength( )
{
m_flRelaxedLength = m_idealSegmentLength * (m_body.Count() -2) + HYDRA_OUTWARD_BIAS;
}
bool CNPC_Hydra::IsValidConnection( int iNode0, int iNode1 )
{
trace_t tr;
// check to make sure new connection is valid
AI_TraceHull(m_body[iNode0].vecPos, m_body[iNode1].vecPos,
Vector( -2, -2, -2 ), Vector( 2, 2, 2 ),
MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr);
if (tr.fraction == 1.0)
{
return true;
}
return false;
}
//-------------------------------------
float CNPC_Hydra::MaxYawSpeed()
{
return 0;
if( IsMoving() )
{
return 20;
}
switch( GetActivity() )
{
case ACT_180_LEFT:
return 30;
break;
case ACT_TURN_LEFT:
case ACT_TURN_RIGHT:
return 30;
break;
default:
return 15;
break;
}
}
//-------------------------------------
int CNPC_Hydra::TranslateSchedule( int scheduleType )
{
return BaseClass::TranslateSchedule( scheduleType );
}
//-------------------------------------
void CNPC_Hydra::HandleAnimEvent( animevent_t *pEvent )
{
BaseClass::HandleAnimEvent( pEvent );
}
//-------------------------------------
void CNPC_Hydra::PrescheduleThink()
{
BaseClass::PrescheduleThink();
if ( m_bStabbedEntity )
{
UpdateStabbedEntity();
}
}
//-------------------------------------
int CNPC_Hydra::SelectSchedule ()
{
switch ( m_NPCState )
{
case NPC_STATE_IDLE:
{
SetState( NPC_STATE_ALERT );
return SCHED_HYDRA_DEPLOY;
}
break;
case NPC_STATE_ALERT:
{
return SCHED_HYDRA_STAB;
}
break;
case NPC_STATE_COMBAT:
{
if (HasCondition( COND_HYDRA_SNAGGED ))
{
return SCHED_HYDRA_PULLBACK;
}
else if (HasCondition( COND_HYDRA_OVERSTRETCH ))
{
return SCHED_HYDRA_STAB;
}
return SCHED_HYDRA_STAB;
}
break;
}
return BaseClass::SelectSchedule();
}
//-------------------------------------
void CNPC_Hydra::StartTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_HYDRA_DEPLOY:
m_vecHeadGoal = GetAbsOrigin( ) + m_vecOutward * 100;
m_idealLength = 100;
m_vecHeadDir = m_vecOutward;
return;
case TASK_HYDRA_PREP_STAB:
{
m_flTaskEndTime = gpGlobals->curtime + pTask->flTaskData;
// Go outward
m_vecHeadGoal = GetAbsOrigin( ) + m_vecOutward * 100;
SetTarget( (CBaseEntity *)UTIL_PlayerByIndex( 1 ) );
if (GetEnemy())
{
SetTarget( GetEnemy() );
}
//CPASAttenuationFilter filter( this, "NPC_Hydra.Alert" );
//Vector vecHead = EyePosition();
//EmitSound( filter, entindex(), "NPC_Hydra.Alert", &vecHead );
}
return;
case TASK_HYDRA_STAB:
{
//CPASAttenuationFilter filter( this, "NPC_Hydra.Attack" );
//Vector vecHead = EyePosition();
//EmitSound( filter, entindex(), "NPC_Hydra.Attack", &vecHead );
m_flTaskEndTime = gpGlobals->curtime + 0.5;
}
return;
case TASK_HYDRA_PULLBACK:
m_vecHeadGoal = GetAbsOrigin( ) + m_vecOutward * pTask->flTaskData;
m_idealLength = pTask->flTaskData * 1.1;
return;
default:
BaseClass::StartTask( pTask );
break;
}
}
//-------------------------------------
void CNPC_Hydra::RunTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_HYDRA_DEPLOY:
{
m_flHeadGoalInfluence = 1.0;
float dist = (EyePosition() - m_vecHeadGoal).Length();
if (dist < m_idealSegmentLength)
{
TaskComplete();
}
AimHeadInTravelDirection( 0.2 );
}
break;
case TASK_HYDRA_PREP_STAB:
{
int i;
if (m_body.Count() < 2)
{
TaskFail( "hydra is too short to begin stab" );
return;
}
CBaseEntity *pTarget = GetTarget();
if (pTarget == NULL)
{
TaskFail( FAIL_NO_TARGET );
}
if (pTarget->IsPlayer())
{
m_vecTarget = pTarget->EyePosition( );
}
else
{
m_vecTarget = pTarget->BodyTarget( EyePosition( ) );
}
float distToTarget = (m_vecTarget - m_vecHeadGoal).Length();
float distToBase = (m_vecHeadGoal - GetAbsOrigin()).Length();
m_idealLength = distToTarget + distToBase * 0.5;
if (m_idealLength > HYDRA_MAX_LENGTH)
m_idealLength = HYDRA_MAX_LENGTH;
if (distToTarget < 100.0)
{
m_vecTargetDir = (m_vecTarget - m_vecHeadGoal);
VectorNormalize( m_vecTargetDir );
m_vecHeadGoal = m_vecHeadGoal - m_vecTargetDir * (100 - distToTarget) * 0.5;
}
else if (distToTarget > 200.0)
{
m_vecTargetDir = (m_vecTarget - m_vecHeadGoal);
VectorNormalize( m_vecTargetDir );
m_vecHeadGoal = m_vecHeadGoal - m_vecTargetDir * (200.0 - distToTarget) * 0.5;
}
// face enemy
m_vecTargetDir = (m_vecTarget - m_body[m_body.Count()-1].vecPos);
VectorNormalize( m_vecTargetDir );
m_vecHeadDir = m_vecHeadDir * 0.6 + m_vecTargetDir * 0.4;
VectorNormalize( m_vecHeadDir.GetForModify() );
// build tension towards strike time
float influence = 1.0 - (m_flTaskEndTime - gpGlobals->curtime) / pTask->flTaskData;
if (influence > 1)
influence = 1.0;
influence = influence * influence * influence;
m_flHeadGoalInfluence = influence;
// keep head segment straight
i = m_body.Count() - 2;
m_body[i].vecGoalPos = m_vecHeadGoal - m_vecHeadDir * m_body[i].flActualLength;
m_body[i].flGoalInfluence = influence;
// curve neck into spiral
float distBackFromHead = m_body[i].flActualLength;
Vector right, up;
VectorVectors( m_vecHeadDir, right, up );
for (i = i - 1; i > 1 && distBackFromHead < distToTarget; i--)
{
distBackFromHead += m_body[i].flActualLength;
float r = (distBackFromHead / 200) * 3.1415 * 2;
// spiral
Vector p0 = m_vecHeadGoal
- m_vecHeadDir * distBackFromHead * 0.5
+ cos( r ) * m_body[i].flActualLength * right
+ sin( r ) * m_body[i].flActualLength * up;
// base
r = (distBackFromHead / m_idealLength) * 3.1415 * 0.2;
r = sin( r );
p0 = p0 * (1 - r) + r * GetAbsOrigin();
m_body[i].vecGoalPos = p0;
m_body[i].flGoalInfluence = influence * (1.0 - (distBackFromHead / distToTarget));
/*
if ( (pEnemy->EyePosition( ) - m_body[i].vecPos).Length() < distBackFromHead)
{
if ( gpGlobals->curtime - m_flLastAttackTime > 4.0)
{
TaskComplete();
}
return;
}
*/
}
// look to see if any of the goal positions are stuck
for (i = i; i < m_body.Count() - 1; i++)
{
if (m_body[i].bStuck)
{
Vector delta = DotProduct( m_body[i].vecGoalPos - m_body[i].vecPos, m_vecHeadDir) * m_vecHeadDir;
m_vecHeadGoal -= delta * m_body[i].flGoalInfluence;
break;
}
}
if ( gpGlobals->curtime >= m_flTaskEndTime )
{
if (distToTarget < 500)
{
TaskComplete( );
return;
}
else
{
TaskFail( "target is too far away" );
return;
}
}
}
return;
case TASK_HYDRA_STAB:
{
int i;
if (m_body.Count() < 2)
{
TaskFail( "hydra is too short to begin stab" );
return;
}
if (m_flTaskEndTime <= gpGlobals->curtime)
{
TaskComplete( );
return;
}
m_flHeadGoalInfluence = 1.0;
// face enemy
//m_vecHeadDir = (pEnemy->EyePosition( ) - m_body[m_body.Count()-1].vecPos);
//VectorNormalize( m_vecHeadDir.GetForModify() );
// keep head segment straight
i = m_body.Count() - 2;
m_body[i].vecGoalPos = m_vecHeadGoal + m_vecHeadDir * m_body[i].flActualLength;
m_body[i].flGoalInfluence = 1.0;
Vector vecToTarget = (m_vecTarget - EyePosition( ));
// check to see if we went past target
if (DotProduct( vecToTarget, m_vecHeadDir ) < 0.0)
{
TaskComplete( );
return;
}
float distToTarget = vecToTarget.Length();
float distToBase = (EyePosition( ) - GetAbsOrigin()).Length();
m_idealLength = distToTarget + distToBase;
/*
if (distToTarget < 20)
{
m_vecHeadGoal = m_vecTarget;
SetLastAttackTime( gpGlobals->curtime );
TaskComplete();
return;
}
else
*/
{
// hit enemy
m_vecHeadGoal = m_vecTarget + m_vecHeadDir * 300;
}
if (m_idealLength > HYDRA_MAX_LENGTH)
m_idealLength = HYDRA_MAX_LENGTH;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -