⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 npc_hydra.cpp

📁 hl2 source code. Do not use it illegal.
💻 CPP
📖 第 1 页 / 共 4 页
字号:
#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 + -