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

📄 npc_hydra.cpp

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