📄 npc_hydra.cpp
字号:
#if 0
// move delta's back toward the root
for (i = iLast; i > iFirst; i--)
{
Vector tmp = m_body[i].vecDelta;
m_body[i].vecDelta = tmp * 0.8;
m_body[i-1].vecDelta += tmp * 0.2;
}
#endif
// prevent stretching
int maxChecks = m_body.Count() * 4;
i = iLast;
while (i > iFirst && maxChecks > 0)
{
bool didStretch = false;
Vector stretch = (m_body[i].vecPos + m_body[i].vecDelta) - (m_body[i-1].vecPos + m_body[i-1].vecDelta);
float t = VectorNormalize( stretch );
if (t > flGoalSegmentLength)
{
float f0 = DotProduct( m_body[i].vecDelta, stretch );
float f1 = DotProduct( m_body[i-1].vecDelta, stretch );
if (f0 > 0 && f0 > f1)
{
// Vector limit = stretch * (f0 - flGoalSegmentLength);
Vector limit = stretch * (t - flGoalSegmentLength);
// propagate pulling back down the chain
m_body[i].vecDelta -= limit * 0.5;
m_body[i-1].vecDelta += limit * 0.5;
didStretch = true;
}
}
if (didStretch)
{
if (i < iLast)
{
i++;
}
}
else
{
i--;
}
maxChecks--;
}
}
//-----------------------------------------------------------------------------
// Purpose: Move the body, check for collisions
// Input :
// Output :
//-----------------------------------------------------------------------------
void CNPC_Hydra::MoveBody( )
{
int i;
int iFirst = 2;
int iLast = m_body.Count() - 1;
// clear stuck flags
for (i = 0; i <= iLast; i++)
{
m_body[i].bStuck = false;
}
// try to move all the nodes
for (i = iFirst; i <= iLast; i++)
{
trace_t tr;
// check direct movement
AI_TraceHull(m_body[i].vecPos, m_body[i].vecPos + m_body[i].vecDelta,
Vector( -2, -2, -2 ), Vector( 2, 2, 2 ),
MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr);
Vector direct = tr.endpos;
Vector delta = Vector( 0, 0, 0 );
Vector slide = m_body[i].vecDelta;
if (tr.fraction != 1.0)
{
// slow down and remove all motion in the direction of the plane
direct += tr.plane.normal;
Vector impactSpeed = (slide * tr.plane.normal) * tr.plane.normal;
slide = (slide - impactSpeed) * 0.8;
if (tr.m_pEnt)
{
if (i == iLast)
{
Stab( tr.m_pEnt, impactSpeed, tr );
}
else
{
Nudge( tr.m_pEnt, direct, impactSpeed );
}
}
// slow down and remove all motion in the direction of the plane
slide = (slide - (slide * tr.plane.normal) * tr.plane.normal) * 0.8;
// try to move the remaining distance anyways
AI_TraceHull(direct, direct + slide * (1 - tr.fraction),
Vector( -2, -2, -2 ), Vector( 2, 2, 2 ),
MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr);
// NDebugOverlay::Line( m_body[i].vecPos, tr.endpos, 255, 255, 0, true, 1);
direct = tr.endpos;
m_body[i].bStuck = true;
}
// make sure the new segment doesn't intersect the world
AI_TraceHull(direct, m_body[i-1].vecPos,
Vector( -2, -2, -2 ), Vector( 2, 2, 2 ),
MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr);
if (tr.fraction == 1.0)
{
if (i+1 < iLast)
{
AI_TraceHull(direct, m_body[i+1].vecPos,
Vector( -2, -2, -2 ), Vector( 2, 2, 2 ),
MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr);
}
if (tr.fraction == 1.0)
{
m_body[i].vecPos = direct;
delta = slide;
}
else
{
// FIXME: compute nudge force
m_body[i].bStuck = true;
//m_body[i+1].bStuck = true;
}
}
else
{
// FIXME: compute nudge force
m_body[i].bStuck = true;
//m_body[i-1].bStuck = true;
}
// m_body[i-1].vecDelta += (m_body[i].vecDelta - delta) * 0.25;
// m_body[i+1].vecDelta += (m_body[i].vecDelta - delta) * 0.25;
m_body[i].vecDelta = delta;
}
}
//-----------------------------------------------------------------------------
// Purpose: Push physics objects around if they get hit
// Input : vecContact = point in space where contact supposidly happened
// vecSpeed = in/sec of contact
// Output :
//-----------------------------------------------------------------------------
void CNPC_Hydra::Nudge( CBaseEntity *pOther, const Vector &vecContact, const Vector &vecSpeed )
{
if ( pOther->GetMoveType() != MOVETYPE_VPHYSICS )
{
return;
}
IPhysicsObject *pOtherPhysics = pOther->VPhysicsGetObject();
// Put the force on the line between the "contact point" and hit object origin
//Vector posOther;
//pOtherPhysics->GetPosition( &posOther, NULL );
// force is a 30kg object going 100 in/s
pOtherPhysics->ApplyForceOffset( vecSpeed * 30, vecContact );
}
//-----------------------------------------------------------------------------
// Purpose: Push physics objects around if they get hit
// Input : vecContact = point in space where contact supposidly happened
// vecSpeed = in/sec of contact
// Output :
//-----------------------------------------------------------------------------
void CNPC_Hydra::Stab( CBaseEntity *pOther, const Vector &vecSpeed, trace_t &tr )
{
if (pOther->m_takedamage == DAMAGE_YES && !pOther->IsPlayer())
{
Vector dir = vecSpeed;
VectorNormalize( dir );
if ( !sv_hydraTestSpike.GetInt() )
{
ClearMultiDamage();
// FIXME: this is bogus
CTakeDamageInfo info( this, this, pOther->m_iHealth+25, DMG_SLASH );
CalculateMeleeDamageForce( &info, dir, tr.endpos );
pOther->DispatchTraceAttack( info, dir, &tr );
ApplyMultiDamage();
}
else
{
CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(pOther);
if ( pAnimating )
{
AttachStabbedEntity( pAnimating, vecSpeed * 30, tr );
}
}
}
else
{
Nudge( pOther, tr.endpos, vecSpeed );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : vecContact = point in space where contact supposidly happened
// vecSpeed = in/sec of contact
// Output :
//-----------------------------------------------------------------------------
void CNPC_Hydra::Kick( CBaseEntity *pHitEntity, const Vector &vecContact, const Vector &vecSpeed )
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : vecContact = point in space where contact supposidly happened
// vecSpeed = in/sec of contact
// Output :
//-----------------------------------------------------------------------------
void CNPC_Hydra::Splash( const Vector &vecSplashPos )
{
}
//-----------------------------------------------------------------------------
// Purpose: Calculate the actual hydra length
// Input :
// Output :
//-----------------------------------------------------------------------------
void CNPC_Hydra::CheckLength( )
{
int i;
ClearCondition( COND_HYDRA_SNAGGED );
ClearCondition( COND_HYDRA_NOSTUCK );
ClearCondition( COND_HYDRA_OVERSTRETCH );
m_bHasStuckSegments = m_body[m_body.Count() - 1].bStuck;
m_flCurrentLength = 0;
for (i = 1; i < m_body.Count() - 1; i++)
{
float length = (m_body[i+1].vecPos - m_body[i].vecPos).Length();
Assert( m_body[i+1].vecPos.IsValid( ) );
Assert( m_body[i].vecPos.IsValid( ) );
Assert( IsFinite( length ) );
m_body[i].flActualLength = length;
m_flCurrentLength += length;
// check for over streatched segements
if (length > m_idealSegmentLength * 3.0 && (m_body[i].bStuck || m_body[i+1].bStuck))
{
//NDebugOverlay::Line( m_body[i].vecPos, m_body[i+1].vecPos, 255, 0, 0, true, 1.0);
SetCondition( COND_HYDRA_SNAGGED );
}
if (m_body[i].bStuck)
{
m_bHasStuckSegments = true;
}
}
if (m_flCurrentLength > HYDRA_MAX_LENGTH) // FIXME
{
SetCondition( COND_HYDRA_OVERSTRETCH );
}
if (!m_bHasStuckSegments)
{
SetCondition( COND_HYDRA_NOSTUCK );
}
}
//-----------------------------------------------------------------------------
// Purpose: Grow or shrink the hydra, as needed
// Input :
// Output :
//-----------------------------------------------------------------------------
void CNPC_Hydra::AdjustLength( )
{
m_body[0].vecPos = m_body[1].vecPos - m_vecOutward * m_idealSegmentLength ;
// Msg( "actual %.0f ideal %.0f relaxed %.0f\n", actualLength, m_idealLength, m_idealSegmentLength * (m_body.Count() - 3) );
CalcRelaxedLength( );
// "NPC_Hydra.ExtendTentacle"
bool bAdjustFailed = false;
bool bShouldAdjust = false;
if (m_flCurrentLength < m_idealLength)
{
if (m_flRelaxedLength + m_idealSegmentLength * 0.5 < m_idealLength)
{
bShouldAdjust = true;
//if (!GrowFromMostStretched( ))
if (!GrowFromVirtualRoot())
{
bAdjustFailed = true;
}
}
}
else if (m_flCurrentLength > m_idealLength)
{
// if (relaxedLength > actualLength)
if (m_flRelaxedLength - m_idealSegmentLength * 0.5 > m_idealLength || HasCondition( COND_HYDRA_SNAGGED ))
{
bShouldAdjust = true;
if (!ContractFromRoot())
{
if (!ContractBetweenStuckSegments())
{
if (!ContractFromHead())
{
bAdjustFailed = true;
}
}
}
}
else if (gpGlobals->curtime - m_flLastAdjustmentTime > 1.0)
{
bShouldAdjust = true;
// start to panic
if (!GrowFromMostStretched( ))
{
bAdjustFailed = true;
}
// SplitLongestSegment( );
/*
if (!ContractBetweenStuckSegments())
{
if (!ContractFromHead())
{
}
}
*/
}
else
{
bAdjustFailed = true;
}
}
if (!bAdjustFailed)
{
m_flLastAdjustmentTime = gpGlobals->curtime;
if (bShouldAdjust && !m_bExtendSoundActive)
{
m_bExtendSoundActive = true;
//CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
//controller.SoundChangeVolume( m_pExtendTentacleSound, 1.0, 0.1 );
}
}
else if (bShouldAdjust)
{
m_bExtendSoundActive = false;
//CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
//controller.SoundChangeVolume( m_pExtendTentacleSound, 0.0, 0.3 );
}
CalcRelaxedLength( );
}
//-----------------------------------------------------------------------------
// Purpose: Remove nodes, starting at the end, regardless of length
// Input :
// Output :
//-----------------------------------------------------------------------------
bool CNPC_Hydra::ContractFromHead( )
{
if (m_body.Count() <= 2)
{
return false;
}
int iNode = m_body.Count() - 1;
if (m_body[iNode].bStuck && m_body[iNode-1].flActualLength > m_idealSegmentLength * 2.0)
{
AddNodeBefore( iNode );
iNode = m_body.Count() - 1;
}
if (m_body.Count() <= 3)
{
return false;
}
// always legal since no new link is being formed
m_body.Remove( iNode );
CalcRelaxedLength( );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Starting at the first stuck node back from the head, find a node to remove
// between it and the actual root who is part of a chain that isn't too long.
// Input :
// Output :
//-----------------------------------------------------------------------------
bool CNPC_Hydra::ContractBetweenStuckSegments( )
{
if (m_body.Count() <= 3)
return false;
// first first stuck segment closest to head;
int iStuckHead = VirtualRoot( );
if (iStuckHead < 3)
return false;
// find a non stuck node with the shortest distance between its neighbors
int iShortest = -1;
float dist = m_idealSegmentLength * 2;
int i;
for (i = iStuckHead - 1; i > 2; i--)
{
if (!m_body[i].bStuck)
{
float length = (m_body[i-1].vecPos - m_body[i+1].vecPos).Length();
// check segment length
if (length < dist )
{
if (IsValidConnection( i-1, i+1 ))
{
dist = length;
iShortest = i;
}
}
}
}
if (iShortest = -1)
return false;
// FIXME: check for tunneling
m_body.Remove( iShortest );
CalcRelaxedLength( );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Try to remove segment closest to root
// Input :
// Output :
//-----------------------------------------------------------------------------
bool CNPC_Hydra::ContractFromRoot( )
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -