bool CAI_Pathfinder::IsLinkStillStale(int moveType, CAI_Link *nodeLink)
{
	if ( m_bIgnoreStaleLinks )
		return false;

	if ( !(nodeLink->m_LinkInfo & bits_LINK_STALE_SUGGESTED ) )
		return false;

	if ( gpGlobals->curtime < nodeLink->m_timeStaleExpires )
		return true;

	// NPC should only check one stale link per think
	if (gpGlobals->curtime == m_flLastStaleLinkCheckTime)
	{
		return true;
	}
	else
	{
		m_flLastStaleLinkCheckTime = gpGlobals->curtime;
	}
	
	// Test movement, if suceeds, clear the stale bit
	if (CheckStaleRoute(GetNetwork()->GetNode(nodeLink->m_iSrcID)->GetPosition(GetHullType()),
		GetNetwork()->GetNode(nodeLink->m_iDestID)->GetPosition(GetHullType()), moveType))
	{
		nodeLink->m_LinkInfo &= ~bits_LINK_STALE_SUGGESTED;
		return false;
	}

	nodeLink->m_timeStaleExpires = gpGlobals->curtime + 1.0;

	return true;
}
Esempio n. 2
0
//---------------------------------------------------------
//---------------------------------------------------------
void CZombie::SetZombieModel( void )
{
	Hull_t lastHull = GetHullType();

	if ( m_fIsTorso )
	{
		SetModel( "models/zombie/classic_torso.mdl" );
		SetHullType( HULL_TINY );
	}
	else
	{
		SetModel( "models/zombie/classic.mdl" );
		SetHullType( HULL_HUMAN );
	}
	SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless );

	SetHullSizeNormal( true );
	SetDefaultEyeOffset();
	SetActivity( ACT_IDLE );

	// hull changed size, notify vphysics
	// UNDONE: Solve this generally, systematically so other
	// NPCs can change size
	if ( lastHull != GetHullType() )
	{
		if ( VPhysicsGetObject() )
		{
			SetupVPhysicsHull();
		}
	}
}
//-----------------------------------------------------------------------------
// Computes the link type
//-----------------------------------------------------------------------------
Navigation_t CAI_Pathfinder::ComputeWaypointType( CAI_Node **ppNodes, int parentID, int destID )
{
	Navigation_t navType = NAV_NONE;

	CAI_Node *pNode = ppNodes[parentID];
	for (int link=0; link < pNode->NumLinks();link++) 
	{
		if (pNode->GetLinkByIndex(link)->DestNodeID(parentID) == destID)
		{
			// BRJ 10/1/02
			// FIXME: pNPC->CapabilitiesGet() is actually the mechanism by which fliers
			// filter out the bitfields in the waypoint type (most importantly, bits_MOVE_CAP_GROUND)
			// that would cause the waypoint distance to be computed in a 2D, as opposed to 3D fashion
			// This is a super-scary weak link if you ask me.
			int linkMoveTypeBits = pNode->GetLinkByIndex(link)->m_iAcceptedMoveTypes[GetHullType()];
			int moveTypeBits = ( linkMoveTypeBits & CapabilitiesGet());
			if ( !moveTypeBits && linkMoveTypeBits == bits_CAP_MOVE_JUMP )
			{
				Assert( pNode->GetHint() && pNode->GetHint()->HintType() == HINT_JUMP_OVERRIDE );
				ppNodes[destID]->Lock(0.3);
				moveTypeBits = linkMoveTypeBits;
			}
			Navigation_t linkType = MoveBitsToNavType( moveTypeBits );

			// This will only trigger if the links disagree about their nav type
			Assert( (navType == NAV_NONE) || (navType == linkType) );
			navType = linkType; 
			break;
		}
	}

	// @TODO (toml 10-15-02): one would not expect to come out of the above logic
	// with NAV_NONE. However, if a graph is newly built, it can contain malformed
	// links that are referred to by the destination node, not the source node.
	// This has to be fixed
	if ( navType == NAV_NONE )
	{
		pNode = ppNodes[destID];
		for (int link=0; link < pNode->NumLinks();link++) 
		{
			if (pNode->GetLinkByIndex(link)->DestNodeID(parentID) == destID)
			{
				int npcMoveBits = CapabilitiesGet();
				int nodeMoveBits = pNode->GetLinkByIndex(link)->m_iAcceptedMoveTypes[GetHullType()];
				int moveTypeBits = ( npcMoveBits & nodeMoveBits );
				Navigation_t linkType = MoveBitsToNavType( moveTypeBits );

				Assert( (navType == NAV_NONE) || (navType == linkType) );
				navType = linkType; 

				DevMsg( "Note: Strange link found between nodes in AI node graph\n" );
				break;
			}
		}
	}

	AssertMsg( navType != NAV_NONE, "Pathfinder appears to have output a path with consecutive nodes thate are not actually connected\n" );

	return navType;
}
Esempio n. 4
0
//-----------------------------------------------------------------------------
// Purpose: 
//
// NOTE: This function is still heavy with common code (found at the bottom).
//		 we should consider moving some into the base class! (sjb)
//-----------------------------------------------------------------------------
void CNPC_Monster::SetZombieModel( void )
{
	Hull_t lastHull = GetHullType();

	SetModel( cModel.ToCStr() );

	if (m_fIsTorso)
		SetHullType(HULL_TINY);
	else
		SetHullType(HULL_HUMAN);

	SetHullSizeNormal( true );
	SetDefaultEyeOffset();
	SetActivity( ACT_IDLE );

	m_nSkin = m_iSkin;

	if ( lastHull != GetHullType() )
	{
		if ( VPhysicsGetObject() )
		{
			SetupVPhysicsHull();
		}
	}

	CollisionProp()->SetSurroundingBoundsType( USE_OBB_COLLISION_BOUNDS );
}
Esempio n. 5
0
void CZombie::SetZombieModel( void )
{
	Hull_t lastHull = GetHullType();

	if(m_iZombieType == ZOMBIE_BISOU)
		SetModel( "models/zombie/Bisounours.mdl" );
	else
		SetModel( "models/zombie/classic.mdl" );

	SetHullType( HULL_HUMAN );

	//Commenté en attendant de trouver à quoi ça sert
	//SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless );

	SetHullSizeNormal( true );
	SetDefaultEyeOffset();
	SetActivity( ACT_IDLE );

	// hull changed size, notify vphysics
	// UNDONE: Solve this generally, systematically so other
	// NPCs can change size
	if ( lastHull != GetHullType() )
		if ( VPhysicsGetObject() )
			SetupVPhysicsHull();
}
int CAI_TacticalServices::FindBackAwayNode(const Vector &vecThreat )
{
	if ( !CAI_NetworkManager::NetworksLoaded() )
	{
		DevWarning( 2, "Graph not ready for FindBackAwayNode!\n" );
		return NO_NODE;
	}

	int iMyNode			= GetPathfinder()->NearestNodeToNPC();
	int iThreatNode		= GetPathfinder()->NearestNodeToPoint( vecThreat );

	if ( iMyNode == NO_NODE )
	{
		DevWarning( 2, "FindBackAwayNode() - %s has no nearest node!\n", GetEntClassname());
		return NO_NODE;
	}
	if ( iThreatNode == NO_NODE )
	{
		// DevWarning( 2, "FindBackAwayNode() - Threat has no nearest node!\n" );
		iThreatNode = iMyNode;
		// return false;
	}

	// A vector pointing to the threat.
	Vector vecToThreat;
	vecToThreat = vecThreat - GetLocalOrigin();

	// Get my current distance from the threat
	float flCurDist = VectorNormalize( vecToThreat );

	// Check my neighbors to find a node that's further away
	for (int link = 0; link < GetNetwork()->GetNode(iMyNode)->NumLinks(); link++) 
	{
		CAI_Link *nodeLink = GetNetwork()->GetNode(iMyNode)->GetLinkByIndex(link);

		if ( !m_pPathfinder->IsLinkUsable( nodeLink, iMyNode ) )
			continue;

		int destID = nodeLink->DestNodeID(iMyNode);

		float flTestDist = ( vecThreat - GetNetwork()->GetNode(destID)->GetPosition(GetHullType()) ).Length();

		if ( flTestDist > flCurDist )
		{
			// Make sure this node doesn't take me past the enemy's position.
			Vector vecToNode;
			vecToNode = GetNetwork()->GetNode(destID)->GetPosition(GetHullType()) - GetLocalOrigin();
			VectorNormalize( vecToNode );
		
			if( DotProduct( vecToNode, vecToThreat ) < 0.0 )
			{
				return destID;
			}
		}
	}
	return NO_NODE;
}
Esempio n. 7
0
// Purpose: Monitor the antlion's jump to play the proper landing sequence
bool CASW_Alien_Jumper::CheckLanding( void )
{
	trace_t	tr;
	Vector	testPos;

	//Amount of time to predict forward
	const float	timeStep = 0.1f;

	//Roughly looks one second into the future
	testPos = GetAbsOrigin() + ( GetAbsVelocity() * timeStep );
	testPos[2] -= ( 0.5 * sv_gravity.GetFloat() * GetGravity() * timeStep * timeStep);

	if ( asw_debug_aliens.GetInt() == 2 )
	{
		NDebugOverlay::Line( GetAbsOrigin(), testPos, 255, 0, 0, 0, 0.5f );
		NDebugOverlay::Cross3D( m_vecSavedJump, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, true, 0.5f );
	} 
	
	// Look below
	AI_TraceHull( GetAbsOrigin(), testPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );

	//See if we're about to contact, or have already contacted the ground
	if ( ( tr.fraction != 1.0f ) || ( GetFlags() & FL_ONGROUND ) )
	{
		int	sequence = SelectWeightedSequence( (Activity)ACT_ASW_ALIEN_LAND );

		if ( GetSequence() != sequence )
		{
			VacateStrategySlot();
			SetIdealActivity( (Activity) ACT_ASW_ALIEN_LAND );

			CreateDust( false );
			EmitSound( "ASW_Drone.Land" );

			// asw todo: make the alien attack here?
			//if ( GetEnemy() && GetEnemy()->IsPlayer()  )
			//{
				//CBasePlayer *pPlayer = ToBasePlayer( GetEnemy() );

				//if ( pPlayer && pPlayer->IsInAVehicle() == false )
					 //MeleeAttack( ANTLION_MELEE1_RANGE, sk_antlion_swipe_damage.GetFloat(), QAngle( 4.0f, 0.0f, 0.0f ), Vector( -250.0f, 1.0f, 1.0f ) );
			//}

			SetAbsVelocity( GetAbsVelocity() * 0.33f );
			return false;
		}

		return IsActivityFinished();
	}

	return false;
}
//-----------------------------------------------------------------------------
// Purpose: Monitor the alien's jump to play the proper landing sequence
//-----------------------------------------------------------------------------
bool CAI_ASW_JumpBehavior::CheckLanding( void )
{
    trace_t	tr;
    Vector	testPos;

    //Amount of time to predict forward
    const float	timeStep = 0.1f;

    //Roughly looks one second into the future
    testPos = GetAbsOrigin() + ( GetOuter()->GetAbsVelocity() * timeStep );
    testPos[2] -= ( 0.5 * sv_gravity.GetFloat() * GetGravity() * timeStep * timeStep);

    if ( asw_debug_jump_behavior.GetInt() == 2 )
    {
        NDebugOverlay::Line( GetAbsOrigin(), testPos, 255, 0, 0, 0, 0.5f );
        NDebugOverlay::Cross3D( m_vecSavedJump, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, true, 0.5f );
    }

    // Look below
    AI_TraceHull( GetAbsOrigin(), testPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), GetOuter()->GetAITraceMask(), GetOuter(), COLLISION_GROUP_NONE, &tr );

    //See if we're about to contact, or have already contacted the ground
    if ( ( tr.fraction != 1.0f ) || ( GetOuter()->GetFlags() & FL_ONGROUND ) )
    {
        int	sequence = GetOuter()->SelectWeightedSequence( (Activity)ACT_ALIEN_LAND );
        if ( GetSequence() != sequence )
        {
            CASW_Alien *pNPC = static_cast< CASW_Alien * >( GetOuter() );

            pNPC->VacateStrategySlot();
            pNPC->SetIdealActivity( (Activity) ACT_ALIEN_LAND );
            //pNPC->Land();

            if ( GetEnemy() && GetEnemy()->IsPlayer()  )
            {
                CBasePlayer *pPlayer = ToBasePlayer( GetEnemy() );

                if ( pPlayer && pPlayer->IsInAVehicle() == false )
                {
                    //pNPC->MeleeAttack( m_flDamageDistance, m_flJumpDamage, QAngle( 4.0f, 0.0f, 0.0f ), Vector( -250.0f, 1.0f, 1.0f ) );
                }
            }

            GetOuter()->SetAbsVelocity( GetOuter()->GetAbsVelocity() * 0.33f );
            return false;
        }

        return GetOuter()->IsActivityFinished();
    }

    return false;
}
Esempio n. 9
0
double ShipDesign::ProductionCost(int empire_id, int location_id) const {
    if (CHEAP_AND_FAST_SHIP_PRODUCTION) {
        return 1.0;
    } else {
        double cost_accumulator = 0.0;
        if (const HullType* hull = GetHullType(m_hull))
            cost_accumulator += hull->ProductionCost(empire_id, location_id);
        for (std::vector<std::string>::const_iterator it = m_parts.begin(); it != m_parts.end(); ++it)
            if (const PartType* part = GetPartType(*it))
                cost_accumulator += part->ProductionCost(empire_id, location_id);
        return std::max(0.0, cost_accumulator);
    }
}
Esempio n. 10
0
int ShipDesign::ProductionTime(int empire_id, int location_id) const {
    if (CHEAP_AND_FAST_SHIP_PRODUCTION) {
        return 1;
    } else {
        int time_accumulator = 1;
        if (const HullType* hull = GetHullType(m_hull))
            time_accumulator = std::max(time_accumulator, hull->ProductionTime(empire_id, location_id));
        for (std::vector<std::string>::const_iterator it = m_parts.begin(); it != m_parts.end(); ++it)
            if (const PartType* part = GetPartType(*it))
                time_accumulator = std::max(time_accumulator, part->ProductionTime(empire_id, location_id));
        return std::max(1, time_accumulator);
    }
}
//-----------------------------------------------------------------------------
// Purpose: Given an array of parentID's and endID, contruct a linked 
//			list of waypoints through those parents
//-----------------------------------------------------------------------------
AI_Waypoint_t* CAI_Pathfinder::MakeRouteFromParents( int *parentArray, int endID ) 
{
	AI_Waypoint_t *pOldWaypoint = NULL;
	AI_Waypoint_t *pNewWaypoint = NULL;
	int	currentID = endID;

	CAI_Node **pAInode = GetNetwork()->AccessNodes();

	while (currentID != NO_NODE) 
	{
		// Try to link it to the previous waypoint
		int prevID = parentArray[currentID];

		int destID; 
		if (prevID != NO_NODE)
		{
			destID = prevID;
		}
		else
		{		   
			// If we have no previous node, then use the next node
			if ( !pOldWaypoint )
				return NULL;
			destID = pOldWaypoint->iNodeID;
		}

		Navigation_t waypointType = ComputeWaypointType( pAInode, currentID, destID );

		// BRJ 10/1/02
		// FIXME: It appears potentially possible for us to compute waypoints 
		// here which the NPC is not capable of traversing (because 
		// pNPC->CapabilitiesGet() in ComputeWaypointType() above filters it out). 
		// It's also possible if none of the lines have an appropriate DestNodeID.
		// Um, shouldn't such a waypoint not be allowed?!?!?
		Assert( waypointType != NAV_NONE );

		pNewWaypoint = new AI_Waypoint_t( pAInode[currentID]->GetPosition(GetHullType()),
			pAInode[currentID]->GetYaw(), waypointType, bits_WP_TO_NODE, currentID );

		// Link it up...
		pNewWaypoint->SetNext( pOldWaypoint );
		pOldWaypoint = pNewWaypoint;

		currentID = prevID;
	}

	return pOldWaypoint;
}
Esempio n. 12
0
ShipDesign::ShipDesign(const std::string& name, const std::string& description,
                       int designed_on_turn, const std::string& hull, const std::vector<std::string>& parts,
                       const std::string& icon, const std::string& model,
                       bool name_desc_in_stringtable, bool monster) :
    m_id(INVALID_OBJECT_ID),
    m_name(name),
    m_description(description),
    m_designed_on_turn(designed_on_turn),
    m_hull(hull),
    m_parts(parts),
    m_is_monster(monster),
    m_icon(icon),
    m_3D_model(model),
    m_name_desc_in_stringtable(name_desc_in_stringtable),
    m_is_armed(false),
    m_detection(0.0),
    m_colony_capacity(0.0),
    m_troop_capacity(0.0),
    m_stealth(0.0),
    m_fuel(0.0),
    m_shields(0.0),
    m_structure(0.0),
    m_battle_speed(0.0),
    m_starlane_speed(0.0),
    m_min_SR_range(FLT_MAX),
    m_max_SR_range(0.0),
    m_min_LR_range(FLT_MAX),
    m_max_LR_range(0.0),
    m_min_PD_range(FLT_MAX),
    m_max_PD_range(0.0),
    m_min_weapon_range(FLT_MAX),
    m_max_weapon_range(0.0),
    m_min_non_PD_weapon_range(FLT_MAX),
    m_max_non_PD_weapon_range(0.0)
{
    // expand parts list to have empty values if fewer parts are given than hull has slots
    if (const HullType* hull_type = GetHullType(m_hull)) {
        if (m_parts.size() < hull_type->NumSlots())
            m_parts.resize(hull_type->NumSlots(), "");
    }

    if (!ValidDesign(m_hull, m_parts)) {
        Logger().errorStream() << "constructing an invalid ShipDesign!";
        Logger().errorStream() << Dump();
    }
    BuildStatCaches();
}
Esempio n. 13
0
bool ShipDesign::ProductionCostTimeLocationInvariant() const {
    if (CHEAP_AND_FAST_SHIP_PRODUCTION)
        return true;
    // as seen in ShipDesign::ProductionCost, the location is passed as the
    // local candidate in the ScriptingContext

    // check hull and all parts
    if (const HullType* hull = GetHullType(m_hull))
        if (!hull->ProductionCostTimeLocationInvariant())
            return false;
    for (std::vector<std::string>::const_iterator it = m_parts.begin(); it != m_parts.end(); ++it)
        if (const PartType* part = GetPartType(*it))
            if (!part->ProductionCostTimeLocationInvariant())
                return false;

    // if hull and all parts are invariant, so is whole design
    return true;
}
ShipDesign::ShipDesign(const std::string& name, const std::string& description,
                       int designed_on_turn, int designed_by_empire, const std::string& hull,
                       const std::vector<std::string>& parts,
                       const std::string& icon, const std::string& model,
                       bool name_desc_in_stringtable, bool monster) :
    m_id(INVALID_OBJECT_ID),
    m_name(name),
    m_description(description),
    m_designed_on_turn(designed_on_turn),
    m_designed_by_empire(designed_by_empire),
    m_hull(hull),
    m_parts(parts),
    m_is_monster(monster),
    m_icon(icon),
    m_3D_model(model),
    m_name_desc_in_stringtable(name_desc_in_stringtable),
    m_is_armed(false),
    m_can_bombard(false),
    m_detection(0.0),
    m_colony_capacity(0.0),
    m_troop_capacity(0.0),
    m_stealth(0.0),
    m_fuel(0.0),
    m_shields(0.0),
    m_structure(0.0),
    m_battle_speed(0.0),
    m_starlane_speed(0.0),
    m_research_generation(0.0),
    m_industry_generation(0.0),
    m_trade_generation(0.0),
    m_is_production_location(false)
{
    // expand parts list to have empty values if fewer parts are given than hull has slots
    if (const HullType* hull_type = GetHullType(m_hull)) {
        if (m_parts.size() < hull_type->NumSlots())
            m_parts.resize(hull_type->NumSlots(), "");
    }

    if (!ValidDesign(m_hull, m_parts)) {
        ErrorLogger() << "constructing an invalid ShipDesign!";
        ErrorLogger() << Dump();
    }
    BuildStatCaches();
}
bool CAI_Pathfinder::IsLinkUsable(CAI_Link *pLink, int startID)
{
	// --------------------------------------------------------------------------
	// Skip if link turned off
	// --------------------------------------------------------------------------
	if (pLink->m_LinkInfo & bits_LINK_OFF)
	{
		CDynamicLink *pDynamicLink = dynamic_cast<CDynamicLink *>(CEntity::Instance(pLink->m_pDynamicLink));

		if ( !pDynamicLink || *(pDynamicLink->m_strAllowUse.ptr) == NULL_STRING )
			return false;

		const char *pszAllowUse = STRING( *(pDynamicLink->m_strAllowUse) );
		if ( *(pDynamicLink->m_bInvertAllow) )
		{
			// Exlude only the specified entity name or classname
			if ( GetOuter()->NameMatches(pszAllowUse) || GetOuter()->ClassMatches( pszAllowUse ) )
				return false;
		}
		else
		{
			// Exclude everything but the allowed entity name or classname
			if ( !GetOuter()->NameMatches( pszAllowUse) && !GetOuter()->ClassMatches( pszAllowUse ) )
				return false;
		}
	}

	// --------------------------------------------------------------------------			
	//  Get the destination nodeID
	// --------------------------------------------------------------------------			
	int endID = pLink->DestNodeID(startID);

	// --------------------------------------------------------------------------
	// Make sure I have the ability to do the type of movement specified by the link
	// --------------------------------------------------------------------------
	int linkMoveTypes = pLink->m_iAcceptedMoveTypes[GetHullType()];
	int moveType = ( linkMoveTypes & CapabilitiesGet() );

	CAI_Node *pStartNode,*pEndNode;

	pStartNode = GetNetwork()->GetNode(startID);
	pEndNode = GetNetwork()->GetNode(endID);

	if ( (linkMoveTypes & bits_CAP_MOVE_JUMP) && !moveType )
	{
		CE_AI_Hint *pStartHint = dynamic_cast<CE_AI_Hint *>(pStartNode->GetHint());
		CE_AI_Hint *pEndHint = dynamic_cast<CE_AI_Hint *>(pEndNode->GetHint());
		if ( pStartHint && pEndHint )
		{
			if ( pStartHint->HintType() == HINT_JUMP_OVERRIDE && 
				 pEndHint->HintType() == HINT_JUMP_OVERRIDE &&
				 ( ( ( pStartHint->GetSpawnFlags() | pEndHint->GetSpawnFlags() ) & SF_ALLOW_JUMP_UP ) || pStartHint->GetAbsOrigin().z > pEndHint->GetAbsOrigin().z ) )
			{
				if ( !pStartNode->IsLocked() )
				{
					if ( pStartHint->GetTargetNode() == -1 || pStartHint->GetTargetNode() == endID )
						moveType = bits_CAP_MOVE_JUMP;
				}
			}
		}
	}

	if (!moveType)
	{
		return false;
	}

	// --------------------------------------------------------------------------
	// Check if NPC has a reason not to use the desintion node
	// --------------------------------------------------------------------------
	CEntity *cent = pEndNode->GetHint();

	if (GetOuter()->IsUnusableNode(endID, ((cent) ? cent->BaseEntity() : NULL)))
	{
		return false;
	}	

	// --------------------------------------------------------------------------
	// If a jump make sure the jump is within NPC's legal parameters for jumping
	// --------------------------------------------------------------------------
	if (moveType == bits_CAP_MOVE_JUMP)
	{	
		if (!GetOuter()->IsJumpLegal(pStartNode->GetPosition(GetHullType()), 
									 pEndNode->GetPosition(GetHullType()),
									 pEndNode->GetPosition(GetHullType())))
		{
			return false;
		}
	}

	// --------------------------------------------------------------------------
	// If an NPC suggested that this link is stale and I haven't checked it yet
	// I should make sure the link is still valid before proceeding
	// --------------------------------------------------------------------------
	if (pLink->m_LinkInfo & bits_LINK_STALE_SUGGESTED)
	{
		if (IsLinkStillStale(moveType, pLink))
		{
			return false;
		}
	}
	return true;
}
Esempio n. 16
0
//-----------------------------------------------------------------------------
// Purpose: Allow pre-frame adjustments on the player
//-----------------------------------------------------------------------------
void CHL1_Player::PreThink(void)
{
	CheckExplosionEffects();
	if ( player_showpredictedposition.GetBool() )
	{
		Vector	predPos;

		UTIL_PredictedPosition( this, player_showpredictedposition_timestep.GetFloat(), &predPos );

		NDebugOverlay::Box( predPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), 0, 255, 0, 0, 0.01f );
		NDebugOverlay::Line( GetAbsOrigin(), predPos, 0, 255, 0, 0, 0.01f );
	}

	int buttonsChanged;
	buttonsChanged = m_afButtonPressed | m_afButtonReleased;

	g_pGameRules->PlayerThink( this );

	if ( g_fGameOver || IsPlayerLockedInPlace() )
		return;         // intermission or finale

	ItemPreFrame( );
	WaterMove();

	if ( g_pGameRules && g_pGameRules->FAllowFlashlight() )
		m_Local.m_iHideHUD &= ~HIDEHUD_FLASHLIGHT;
	else
		m_Local.m_iHideHUD |= HIDEHUD_FLASHLIGHT;


	// checks if new client data (for HUD and view control) needs to be sent to the client
	UpdateClientData();
	
	CheckTimeBasedDamage();

	CheckSuitUpdate();

	if (m_lifeState >= LIFE_DYING)
	{
		PlayerDeathThink();
		return;
	}

	// So the correct flags get sent to client asap.
	//
	if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE )
		AddFlag( FL_ONTRAIN );
	else 
		RemoveFlag( FL_ONTRAIN );

	// Train speed control
	if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE )
	{
		CBaseEntity *pTrain = GetGroundEntity();
		float vel;

		if ( pTrain )
		{
			if ( !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) )
				pTrain = NULL;
		}

		if ( !pTrain )
		{
			if ( GetActiveWeapon() && (GetActiveWeapon()->ObjectCaps() & FCAP_DIRECTIONAL_USE) )
			{
				m_iTrain = TRAIN_ACTIVE | TRAIN_NEW;

				if ( m_nButtons & IN_FORWARD )
				{
					m_iTrain |= TRAIN_FAST;
				}
				else if ( m_nButtons & IN_BACK )
				{
					m_iTrain |= TRAIN_BACK;
				}
				else
				{
					m_iTrain |= TRAIN_NEUTRAL;
				}
				return;
			}
			else
			{
				trace_t trainTrace;
				// Maybe this is on the other side of a level transition
				UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,-38), 
					MASK_PLAYERSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &trainTrace );

				if ( trainTrace.fraction != 1.0 && trainTrace.m_pEnt )
					pTrain = trainTrace.m_pEnt;


				if ( !pTrain || !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) || !pTrain->OnControls(GetContainingEntity(pev)) )
				{
//					Warning( "In train mode with no train!\n" );
					m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE;
					m_iTrain = TRAIN_NEW|TRAIN_OFF;
					return;
				}
			}
		}
		else if ( !( GetFlags() & FL_ONGROUND ) || pTrain->HasSpawnFlags( SF_TRACKTRAIN_NOCONTROL ) || (m_nButtons & (IN_MOVELEFT|IN_MOVERIGHT) ) )
		{
			// Turn off the train if you jump, strafe, or the train controls go dead
			m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE;
			m_iTrain = TRAIN_NEW|TRAIN_OFF;
			return;
		}

		SetAbsVelocity( vec3_origin );
		vel = 0;
		if ( m_afButtonPressed & IN_FORWARD )
		{
			vel = 1;
			pTrain->Use( this, this, USE_SET, (float)vel );
		}
		else if ( m_afButtonPressed & IN_BACK )
		{
			vel = -1;
			pTrain->Use( this, this, USE_SET, (float)vel );
		}

		if (vel)
		{
			m_iTrain = TrainSpeed(pTrain->m_flSpeed, ((CFuncTrackTrain*)pTrain)->GetMaxSpeed());
			m_iTrain |= TRAIN_ACTIVE|TRAIN_NEW;
		}
	} 
	else if (m_iTrain & TRAIN_ACTIVE)
	{
		m_iTrain = TRAIN_NEW; // turn off train
	}

	// THIS CODE DOESN'T SEEM TO DO ANYTHING!!!
	// WHY IS IT STILL HERE? (sjb)
	if (m_nButtons & IN_JUMP)
	{
		// If on a ladder, jump off the ladder
		// else Jump
		Jump();
	}

	// If trying to duck, already ducked, or in the process of ducking
	if ((m_nButtons & IN_DUCK) || (GetFlags() & FL_DUCKING) || (m_afPhysicsFlags & PFLAG_DUCKING) )
		Duck();

	//
	// If we're not on the ground, we're falling. Update our falling velocity.
	//
	if ( !( GetFlags() & FL_ONGROUND ) )
	{
		m_Local.m_flFallVelocity = -GetAbsVelocity().z;
	}

	if ( m_afPhysicsFlags & PFLAG_ONBARNACLE )
	{
		SetAbsVelocity( vec3_origin );
	}
	// StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating?

	//Find targets for NPC to shoot if they decide to miss us
	FindMissTargets();
}
Esempio n. 17
0
void ShipDesign::BuildStatCaches() {
    const HullType* hull = GetHullType(m_hull);
    if (!hull) {
        ErrorLogger() << "ShipDesign::BuildStatCaches couldn't get hull with name " << m_hull;
        return;
    }

    m_producible =      hull->Producible();
    m_detection =       hull->Detection();
    m_colony_capacity = hull->ColonyCapacity();
    m_troop_capacity =  hull->TroopCapacity();
    m_stealth =         hull->Stealth();
    m_fuel =            hull->Fuel();
    m_shields =         hull->Shields();
    m_structure =       hull->Structure();
    m_speed =           hull->Speed();

    for (std::vector<std::string>::const_iterator it = m_parts.begin(); it != m_parts.end(); ++it) {
        if (it->empty())
            continue;

        const PartType* part = GetPartType(*it);
        if (!part) {
            ErrorLogger() << "ShipDesign::BuildStatCaches couldn't get part with name " << *it;
            continue;
        }

        if (!part->Producible())
            m_producible = false;

        switch (part->Class()) {
        case PC_SHORT_RANGE:
        case PC_MISSILES:
        case PC_FIGHTERS:
        case PC_POINT_DEFENSE: {
            m_is_armed = true;
            break;
        }
        case PC_COLONY:
            m_colony_capacity += part->Capacity();
            break;
        case PC_TROOPS:
            m_troop_capacity += part->Capacity();
            break;
        case PC_STEALTH:
            m_stealth += part->Capacity();
            break;
        case PC_SPEED:
            m_speed += part->Capacity();
            break;
        case PC_SHIELD:
            m_shields += part->Capacity();
            break;
        case PC_FUEL:
            m_fuel += part->Capacity();
            break;
        case PC_ARMOUR:
            m_structure += part->Capacity();
            break;
        case PC_DETECTION:
            m_detection += part->Capacity();
            break;
        case PC_BOMBARD:
            m_can_bombard = true;
            break;
        case PC_RESEARCH:
            m_research_generation += part->Capacity();
            break;
        case PC_INDUSTRY:
            m_industry_generation += part->Capacity();
            break;
        case PC_TRADE:
            m_trade_generation += part->Capacity();
            break;
        case PC_PRODICTION_LOCATION:
            m_is_production_location = true;
            break;
        default:
            break;
        }
    }
}
bool CAI_Pathfinder::Triangulate( Navigation_t navType, const Vector &vecStart, const Vector &vecEndIn, 
	float flDistToBlocker, const CEntity *pTargetEnt, Vector *pApex )
{
	if ( GetOuter()->IsFlaggedEfficient() )
		return false;

	Assert( pApex );

	Vector vecForward, vecUp, vecPerpendicular;
	VectorSubtract( vecEndIn, vecStart, vecForward );
	float flTotalDist = VectorNormalize( vecForward );

	Vector vecEnd;

	// If we're walking, then don't try to triangulate over large distances
	if ( navType != NAV_FLY && flTotalDist > MAX_TRIAGULATION_DIST)
	{
		vecEnd = vecForward * MAX_TRIAGULATION_DIST;
		flTotalDist = MAX_TRIAGULATION_DIST;
		if ( !GetOuter()->GetMoveProbe()->MoveLimit(navType, vecEnd, vecEndIn, MASK_NPCSOLID, pTargetEnt) )
		{
			return false;
		}

	}
	else
		vecEnd = vecEndIn;

	// Compute a direction vector perpendicular to the desired motion direction
	if ( 1.0f - fabs(vecForward.z) > 1e-3 )
	{
		vecUp.Init( 0, 0, 1 );
		CrossProduct( vecForward, vecUp, vecPerpendicular );	// Orthogonal to facing
	}
	else
	{
		vecUp.Init( 0, 1, 0 );
		vecPerpendicular.Init( 1, 0, 0 ); 
	}

	// Grab the size of the navigation bounding box
	float sizeX = 0.5f * NAI_Hull::Length(GetHullType());
	float sizeZ = 0.5f * NAI_Hull::Height(GetHullType());

	// start checking right about where the object is, picking two equidistant
	// starting points, one on the left, one on the right. As we progress 
	// through the loop, we'll push these away from the obstacle, hoping to 
	// find a way around on either side. m_vecSize.x is added to the ApexDist 
	// in order to help select an apex point that insures that the NPC is 
	// sufficiently past the obstacle before trying to turn back onto its original course.


	float flApexDist = flDistToBlocker + sizeX;
	if (flApexDist > flTotalDist) 
	{
		flApexDist = flTotalDist;
	}

	// Compute triangulation apex points (NAV_FLY attempts vertical triangulation too)
	Vector vecDelta[2];
	Vector vecApex[4];
	float pApexDist[4];

	Vector vecCenter;
	int nNumToTest = 2;
	VectorMultiply( vecPerpendicular, sizeX, vecDelta[0] );

	VectorMA( vecStart, flApexDist, vecForward, vecCenter );
	VectorSubtract( vecCenter, vecDelta[0], vecApex[0] );
	VectorAdd( vecCenter, vecDelta[0], vecApex[1] );
 	vecDelta[0] *= 2.0f;
	pApexDist[0] = pApexDist[1] = flApexDist;

	if (navType == NAV_FLY)
	{
		VectorMultiply( vecUp, 3.0f * sizeZ, vecDelta[1] );
		VectorSubtract( vecCenter, vecDelta[1], vecApex[2] );
		VectorAdd( vecCenter, vecDelta[1], vecApex[3] );
		pApexDist[2] = pApexDist[3] = flApexDist;
		nNumToTest = 4;
	}

	AIMoveTrace_t moveTrace;
	for (int i = 0; i < 2; ++i )
	{
		// NOTE: Do reverse order so fliers try to move over the top first 
		for (int j = nNumToTest; --j >= 0; )
		{
			if (TestTriangulationRoute(navType, vecStart, vecApex[j], vecEnd, pTargetEnt, &moveTrace))
			{
				*pApex  = vecApex[j];
				return true;
			}

			// Here, the starting half of the triangle was blocked. Lets
			// pull back the apex toward the start...
			if (IsMoveBlocked(moveTrace))
			{
				Vector vecStartToObstruction;
				VectorSubtract( moveTrace.vEndPosition, vecStart, vecStartToObstruction );
				float flDistToObstruction = DotProduct( vecStartToObstruction, vecForward );

				float flNewApexDist = pApexDist[j];
				if (pApexDist[j] > flDistToObstruction)
					flNewApexDist = flDistToObstruction;

				VectorMA( vecApex[j], flNewApexDist - pApexDist[j], vecForward, vecApex[j] );
				pApexDist[j] = flNewApexDist;
			}

			// NOTE: This has to occur *after* the code above because
			// the above code uses vecApex for some distance computations
			if (j & 0x1)
				vecApex[j] += vecDelta[j >> 1];
			else
				vecApex[j] -= vecDelta[j >> 1];
		}
	}
int CAI_TacticalServices::FindCoverNode(const Vector &vNearPos, const Vector &vThreatPos, const Vector &vThreatEyePos, float flMinDist, float flMaxDist )
{
	if ( !CAI_NetworkManager::NetworksLoaded() )
		return NO_NODE;

	AI_PROFILE_SCOPE( CAI_TacticalServices_FindCoverNode );

	MARK_TASK_EXPENSIVE();

	DebugFindCover( NO_NODE, GetOuter()->EyePosition(), vThreatEyePos, 0, 255, 255 );

	int iMyNode = GetPathfinder()->NearestNodeToPoint( vNearPos );

	if ( iMyNode == NO_NODE )
	{
		Vector pos = GetOuter()->GetAbsOrigin();
		DevWarning( 2, "FindCover() - %s has no nearest node! (Check near %f %f %f)\n", GetEntClassname(), pos.x, pos.y, pos.z);
		return NO_NODE;
	}

	if ( !flMaxDist )
	{
		// user didn't supply a MaxDist, so work up a crazy one.
		flMaxDist = 784;
	}

	if ( flMinDist > 0.5 * flMaxDist)
	{
		flMinDist = 0.5 * flMaxDist;
	}

	// ------------------------------------------------------------------------------------
	// We're going to search for a cover node by expanding to our current node's neighbors
	// and then their neighbors, until cover is found, or all nodes are beyond MaxDist
	// ------------------------------------------------------------------------------------
	AI_NearNode_t *pBuffer = (AI_NearNode_t *)stackalloc( sizeof(AI_NearNode_t) * GetNetwork()->NumNodes() );
	CNodeList list( pBuffer, GetNetwork()->NumNodes() );
	CVarBitVec wasVisited(GetNetwork()->NumNodes());	// Nodes visited

	// mark start as visited
	list.Insert( AI_NearNode_t(iMyNode, 0) ); 
	wasVisited.Set( iMyNode );
	float flMinDistSqr = flMinDist*flMinDist;
	float flMaxDistSqr = flMaxDist*flMaxDist;

	static int nSearchRandomizer = 0;		// tries to ensure the links are searched in a different order each time;

	// Search until the list is empty
	while( list.Count() )
	{
		// Get the node that is closest in the number of steps and remove from the list
		int nodeIndex = list.ElementAtHead().nodeIndex;
		list.RemoveAtHead();

		CAI_Node *pNode = GetNetwork()->GetNode(nodeIndex);
		Vector nodeOrigin = pNode->GetPosition(GetHullType());

		float dist = (vNearPos - nodeOrigin).LengthSqr();
		if (dist >= flMinDistSqr && dist < flMaxDistSqr)
		{
			Activity nCoverActivity = GetOuter()->GetCoverActivity( pNode->GetHint() );
			Vector vEyePos = nodeOrigin + GetOuter()->EyeOffset(nCoverActivity);

			if ( GetOuter()->IsValidCover( nodeOrigin, pNode->GetHint() ) )
			{
				// Check if this location will block the threat's line of sight to me
				if (GetOuter()->IsCoverPosition(vThreatEyePos, vEyePos))
				{
					// --------------------------------------------------------
					// Don't let anyone else use this node for a while
					// --------------------------------------------------------
					pNode->Lock( 1.0 );

					if ( pNode->GetHint() && ( pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_MED || pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_LOW ) )
					{
						if ( GetOuter()->GetHintNode() )
						{
							GetOuter()->GetHintNode()->Unlock(GetOuter()->GetHintDelay(GetOuter()->GetHintNode()->HintType()));
							GetOuter()->SetHintNode( NULL );
						}

						GetOuter()->SetHintNode( pNode->GetHint() );
					}

					// The next NPC who searches should use a slight different pattern
					nSearchRandomizer = nodeIndex;
					DebugFindCover( pNode->GetId(), vEyePos, vThreatEyePos, 0, 255, 0 );
					return nodeIndex;
				}
				else
				{
					DebugFindCover( pNode->GetId(), vEyePos, vThreatEyePos, 255, 0, 0 );
				}
			}
			else
			{
				DebugFindCover( pNode->GetId(), vEyePos, vThreatEyePos, 0, 0, 255 );
			}
		}

		// Add its children to the search list
		// Go through each link
		// UNDONE: Pass in a cost function to measure each link?
		for ( int link = 0; link < GetNetwork()->GetNode(nodeIndex)->NumLinks(); link++ ) 
		{
			int index = (link + nSearchRandomizer) % GetNetwork()->GetNode(nodeIndex)->NumLinks();
			CAI_Link *nodeLink = GetNetwork()->GetNode(nodeIndex)->GetLinkByIndex(index);

			if ( !m_pPathfinder->IsLinkUsable( nodeLink, iMyNode ) )
				continue;

			int newID = nodeLink->DestNodeID(nodeIndex);

			// If not already on the closed list, add to it and set its distance
			if (!wasVisited.IsBitSet(newID))
			{
				// Don't accept climb nodes or nodes that aren't ready to use yet
				if ( GetNetwork()->GetNode(newID)->GetType() != NODE_CLIMB && !GetNetwork()->GetNode(newID)->IsLocked() )
				{
					// UNDONE: Shouldn't we really accumulate the distance by path rather than
					// absolute distance.  After all, we are performing essentially an A* here.
					nodeOrigin = GetNetwork()->GetNode(newID)->GetPosition(GetHullType());
					dist = (vNearPos - nodeOrigin).LengthSqr();

					// use distance to threat as a heuristic to keep AIs from running toward
					// the threat in order to take cover from it.
					float threatDist = (vThreatPos - nodeOrigin).LengthSqr();

					// Now check this node is not too close towards the threat
					if ( dist < threatDist * 1.5 )
					{
						list.Insert( AI_NearNode_t(newID, dist) );
					}
				}
				// mark visited
				wasVisited.Set(newID);
			}
		}
	}

	// We failed.  Not cover node was found
	// Clear hint node used to set ducking
	GetOuter()->ClearHintNode();
	return NO_NODE;
}
int CAI_TacticalServices::FindLosNode(const Vector &vThreatPos, const Vector &vThreatEyePos, float flMinThreatDist, float flMaxThreatDist, float flBlockTime, FlankType_t eFlankType, const Vector &vecFlankRefPos, float flFlankParam )
{
	if ( !CAI_NetworkManager::NetworksLoaded() )
		return NO_NODE;

	AI_PROFILE_SCOPE( CAI_TacticalServices_FindLosNode );

	MARK_TASK_EXPENSIVE();

	int iMyNode	= GetPathfinder()->NearestNodeToNPC();
	if ( iMyNode == NO_NODE )
	{
		Vector pos = GetOuter()->GetAbsOrigin();
		DevWarning( 2, "FindCover() - %s has no nearest node! (Check near %f %f %f)\n", GetEntClassname(), pos.x, pos.y, pos.z);
		return NO_NODE;
	}

	// ------------------------------------------------------------------------------------
	// We're going to search for a shoot node by expanding to our current node's neighbors
	// and then their neighbors, until a shooting position is found, or all nodes are beyond MaxDist
	// ------------------------------------------------------------------------------------
	AI_NearNode_t *pBuffer = (AI_NearNode_t *)stackalloc( sizeof(AI_NearNode_t) * GetNetwork()->NumNodes() );
	CNodeList list( pBuffer, GetNetwork()->NumNodes() );
	CVarBitVec wasVisited(GetNetwork()->NumNodes());	// Nodes visited

	// mark start as visited
	wasVisited.Set( iMyNode );
	list.Insert( AI_NearNode_t(iMyNode, 0) );

	static int nSearchRandomizer = 0;		// tries to ensure the links are searched in a different order each time;

	while ( list.Count() )
	{
		int nodeIndex = list.ElementAtHead().nodeIndex;
		// remove this item from the list
		list.RemoveAtHead();

		const Vector &nodeOrigin = GetNetwork()->GetNode(nodeIndex)->GetPosition(GetHullType());

		// HACKHACK: Can't we rework this loop and get rid of this?
		// skip the starting node, or we probably wouldn't have called this function.
		if ( nodeIndex != iMyNode )
		{
			bool skip = false;

			// See if the node satisfies the flanking criteria.
			switch ( eFlankType )
			{
				case FLANKTYPE_NONE:
					break;
					
				case FLANKTYPE_RADIUS:
				{
					Vector vecDist = nodeOrigin - vecFlankRefPos;
					if ( vecDist.Length() < flFlankParam )
					{
						skip = true;
					}
					
					break;
				}
				
				case FLANKTYPE_ARC:
				{
					Vector vecEnemyToRef = vecFlankRefPos - vThreatPos;
					VectorNormalize( vecEnemyToRef );

					Vector vecEnemyToNode = nodeOrigin - vThreatPos;
					VectorNormalize( vecEnemyToNode );
					
					float flDot = DotProduct( vecEnemyToRef, vecEnemyToNode );
					
					if ( RAD2DEG( acos( flDot ) ) < flFlankParam )
					{
						skip = true;
					}
					
					break;
				}
			}

			// Don't accept climb nodes, and assume my nearest node isn't valid because
			// we decided to make this check in the first place.  Keep moving
			if ( !skip && !GetNetwork()->GetNode(nodeIndex)->IsLocked() &&
				GetNetwork()->GetNode(nodeIndex)->GetType() != NODE_CLIMB )
			{
				// Now check its distance and only accept if in range
				float flThreatDist = ( nodeOrigin - vThreatPos ).Length();

				if ( flThreatDist < flMaxThreatDist &&
					 flThreatDist > flMinThreatDist )
				{
					CAI_Node *pNode = GetNetwork()->GetNode(nodeIndex);
					if ( GetOuter()->IsValidShootPosition( nodeOrigin, pNode, pNode->GetHint() ) )
					{
						if (GetOuter()->TestShootPosition(nodeOrigin,vThreatEyePos))
						{
							// Note when this node was used, so we don't try 
							// to use it again right away.
							GetNetwork()->GetNode(nodeIndex)->Lock( flBlockTime );

#if 0
							if ( GetOuter()->GetHintNode() )
							{
								GetOuter()->GetHintNode()->Unlock(GetOuter()->GetHintDelay(GetOuter()->GetHintNode()->HintType()));
								GetOuter()->SetHintNode( NULL );
							}

							// This used to not be set, why? (kenb)
							// @Note (toml 05-19-04): I think because stomping  the hint can lead to
							// unintended side effects. The hint node is primarily a high level
							// tool, and certain NPCs break if it gets slammed here. If we need
							// this, we should propagate it out and let the schedule selector
							// or task decide to set the hint node
							GetOuter()->SetHintNode( GetNetwork()->GetNode(nodeIndex)->GetHint() );
#endif
							if ( ShouldDebugLos( nodeIndex ) )
							{
								NDebugOverlay::Text( nodeOrigin, CFmtStr( "%d:los!", nodeIndex), false, 1 );
							}

							// The next NPC who searches should use a slight different pattern
							nSearchRandomizer = nodeIndex;
							return nodeIndex;
						}
						else
						{
							if ( ShouldDebugLos( nodeIndex ) )
							{
								NDebugOverlay::Text( nodeOrigin, CFmtStr( "%d:!shoot", nodeIndex), false, 1 );
							}
						}
					}
					else
					{
						if ( ShouldDebugLos( nodeIndex ) )
						{
							NDebugOverlay::Text( nodeOrigin, CFmtStr( "%d:!valid", nodeIndex), false, 1 );
						}
					}
				}
				else
				{
					if ( ShouldDebugLos( nodeIndex ) )
					{
						CFmtStr msg( "%d:%s", nodeIndex, ( flThreatDist < flMaxThreatDist ) ? "too close" : "too far" );
						NDebugOverlay::Text( nodeOrigin, msg, false, 1 );
					}
				}
			}
		}

		// Go through each link and add connected nodes to the list
		for (int link=0; link < GetNetwork()->GetNode(nodeIndex)->NumLinks();link++) 
		{
			int index = (link + nSearchRandomizer) % GetNetwork()->GetNode(nodeIndex)->NumLinks();
			CAI_Link *nodeLink = GetNetwork()->GetNode(nodeIndex)->GetLinkByIndex(index);

			if ( !m_pPathfinder->IsLinkUsable( nodeLink, iMyNode ) )
				continue;

			int newID = nodeLink->DestNodeID(nodeIndex);

			// If not already visited, add to the list
			if (!wasVisited.IsBitSet(newID))
			{
				float dist = (GetLocalOrigin() - GetNetwork()->GetNode(newID)->GetPosition(GetHullType())).LengthSqr();
				list.Insert( AI_NearNode_t(newID, dist) );
				wasVisited.Set( newID );
			}
		}
	}
	// We failed.  No range attack node node was found
	return NO_NODE;
}
Vector CAI_TacticalServices::GetNodePos( int node )
{
	return GetNetwork()->GetNode((int)node)->GetPosition(GetHullType());
}
Esempio n. 22
0
void ShipDesign::BuildStatCaches() {
    const HullType* hull = GetHullType(m_hull);
    if (!hull) {
        Logger().errorStream() << "ShipDesign::BuildStatCaches couldn't get hull with name " << m_hull;
        return;
    }

    m_producible =      hull->Producible();
    m_detection =       hull->Detection();
    m_colony_capacity = hull->ColonyCapacity();
    m_troop_capacity =  hull->TroopCapacity();
    m_stealth =         hull->Stealth();
    m_fuel =            hull->Fuel();
    m_shields =         hull->Shields();
    m_structure =       hull->Structure();
    m_battle_speed =    hull->BattleSpeed();
    m_starlane_speed =  hull->StarlaneSpeed();

    for (std::vector<std::string>::const_iterator it = m_parts.begin(); it != m_parts.end(); ++it) {
        if (it->empty())
            continue;

        const PartType* part = GetPartType(*it);
        if (!part) {
            Logger().errorStream() << "ShipDesign::BuildStatCaches couldn't get part with name " << *it;
            continue;
        }

        if (!part->Producible())
            m_producible = false;

        switch (part->Class()) {
        case PC_SHORT_RANGE: {
            const DirectFireStats& stats = boost::get<DirectFireStats>(part->Stats());
            m_SR_weapons.insert(std::make_pair(stats.m_range, part));
            m_is_armed = true;
            m_min_SR_range = std::min(m_min_SR_range, stats.m_range);
            m_max_SR_range = std::max(m_max_SR_range, stats.m_range);
            m_min_weapon_range = std::min(m_min_weapon_range, stats.m_range);
            m_max_weapon_range = std::max(m_max_weapon_range, stats.m_range);
            m_min_non_PD_weapon_range = std::min(m_min_non_PD_weapon_range, stats.m_range);
            m_max_non_PD_weapon_range = std::max(m_max_non_PD_weapon_range, stats.m_range);
            break;
        }
        case PC_MISSILES: {
            const LRStats& stats = boost::get<LRStats>(part->Stats());
            m_LR_weapons.insert(std::make_pair(stats.m_range, part));
            m_is_armed = true;
            m_min_LR_range = std::min(m_min_LR_range, stats.m_range);
            m_max_LR_range = std::max(m_max_LR_range, stats.m_range);
            m_min_weapon_range = std::min(m_min_weapon_range, stats.m_range);
            m_max_weapon_range = std::max(m_max_weapon_range, stats.m_range);
            m_min_non_PD_weapon_range = std::min(m_min_non_PD_weapon_range, stats.m_range);
            m_max_non_PD_weapon_range = std::max(m_max_non_PD_weapon_range, stats.m_range);
            break;
        }
        case PC_FIGHTERS:
            m_F_weapons.push_back(part);
            m_is_armed = true;
            break;
        case PC_POINT_DEFENSE: {
            const DirectFireStats& stats = boost::get<DirectFireStats>(part->Stats());
            m_PD_weapons.insert(std::make_pair(stats.m_range, part));
            m_is_armed = true;
            m_min_PD_range = std::min(m_min_PD_range, stats.m_range);
            m_max_PD_range = std::max(m_max_PD_range, stats.m_range);
            m_min_weapon_range = std::min(m_min_weapon_range, stats.m_range);
            m_max_weapon_range = std::max(m_max_weapon_range, stats.m_range);
            break;
        }
        case PC_COLONY:
            m_colony_capacity += boost::get<float>(part->Stats());
            break;
        case PC_TROOPS:
            m_troop_capacity += boost::get<float>(part->Stats());
            break;
        case PC_STEALTH:
            m_stealth += boost::get<float>(part->Stats());
            break;
        case PC_BATTLE_SPEED:
            m_battle_speed += boost::get<float>(part->Stats());
            break;
        case PC_STARLANE_SPEED:
            m_starlane_speed += boost::get<float>(part->Stats());
            break;
        case PC_SHIELD:
            m_shields += boost::get<float>(part->Stats());
            break;
        case PC_FUEL:
            m_fuel += boost::get<float>(part->Stats());
            break;
        case PC_ARMOUR:
            m_structure += boost::get<float>(part->Stats());
            break;
        case PC_DETECTION:
            m_detection += boost::get<float>(part->Stats());
            break;
        default:
            break;
        }
    }

    if (m_SR_weapons.empty())
        m_min_SR_range = 0.0;
    if (m_LR_weapons.empty())
        m_min_LR_range = 0.0;
    if (m_PD_weapons.empty())
        m_min_PD_range = 0.0;
    if (!m_min_SR_range && !m_min_LR_range && !m_min_PD_range)
        m_min_weapon_range = 0.0;
    if (!m_min_LR_range && !m_min_PD_range)
        m_min_non_PD_weapon_range = 0.0;
}
AI_Waypoint_t *CAI_Pathfinder::FindBestPath(int startID, int endID) 
{
	if ( !GetNetwork()->NumNodes() )
		return NULL;

	int nNodes = GetNetwork()->NumNodes();
	CAI_Node **pAInode = GetNetwork()->AccessNodes();

	CVarBitVec	openBS(nNodes);
	CVarBitVec	closeBS(nNodes);

	// ------------- INITIALIZE ------------------------
	float* nodeG = (float *)stackalloc( nNodes * sizeof(float) );
	float* nodeH = (float *)stackalloc( nNodes * sizeof(float) );
	float* nodeF = (float *)stackalloc( nNodes * sizeof(float) );
	int*   nodeP = (int *)stackalloc( nNodes * sizeof(int) );		// Node parent 

	for (int node=0;node<nNodes;node++)
	{
		nodeG[node] = FLT_MAX;
		nodeP[node] = -1;
	}

	nodeG[startID] = 0;

	nodeH[startID] = 0.1*(pAInode[startID]->GetPosition(GetHullType())-pAInode[endID]->GetPosition(GetHullType())).Length(); // Don't want to over estimate
	nodeF[startID] = nodeG[startID] + nodeH[startID];

	openBS.Set(startID);
	closeBS.Set( startID );

	// --------------- FIND BEST PATH ------------------
	while (!openBS.IsAllClear()) 
	{
		int smallestID = CAI_Network::FindBSSmallest(&openBS,nodeF,nNodes);
	
		openBS.Clear(smallestID);

		CAI_Node *pSmallestNode = pAInode[smallestID];
		
		if (GetOuter()->IsUnusableNode(smallestID, pSmallestNode->m_pHint))
			continue;

		if (smallestID == endID) 
		{
			AI_Waypoint_t* route = MakeRouteFromParents(&nodeP[0], endID);
			return route;
		}

		// Check this if the node is immediately in the path after the startNode 
		// that it isn't blocked
		for (int link=0; link < pSmallestNode->NumLinks();link++) 
		{
			CAI_Link *nodeLink = pSmallestNode->GetLinkByIndex(link);
			
			if (!IsLinkUsable(nodeLink,smallestID))
				continue;

			// FIXME: the cost function should take into account Node costs (danger, flanking, etc).
			int moveType = nodeLink->m_iAcceptedMoveTypes[GetHullType()] & CapabilitiesGet();
			int testID	 = nodeLink->DestNodeID(smallestID);

			Vector r1 = pSmallestNode->GetPosition(GetHullType());
			Vector r2 = pAInode[testID]->GetPosition(GetHullType());
			float dist   = GetOuter()->GetNavigator()->MovementCost( moveType, r1, r2 ); // MovementCost takes ref parameters!!

			if ( dist == FLT_MAX )
				continue;

			float new_g  = nodeG[smallestID] + dist;

			if ( !closeBS.IsBitSet(testID) || (new_g < nodeG[testID]) ) 
			{
				nodeP[testID] = smallestID;
				nodeG[testID] = new_g;
				nodeH[testID] = (pAInode[testID]->GetPosition(GetHullType())-pAInode[endID]->GetPosition(GetHullType())).Length();
				nodeF[testID] = nodeG[testID] + nodeH[testID];

				closeBS.Set( testID );
				openBS.Set( testID );
			}
		}
	}

	return NULL;   
}