예제 #1
0
/*
* Touch_Item
*/
void Touch_Item( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags )
{
	bool taken;
	const gsitem_t *item = ent->item;

	if( !other->r.client || G_ISGHOSTING( other ) )
		return;

	if( !( other->r.client->ps.pmove.stats[PM_STAT_FEATURES] & PMFEAT_ITEMPICK ) )
		return;

	if( !item || !( item->flags & ITFLAG_PICKABLE ) )
		return; // not a grabbable item

	if( !G_Gametype_CanPickUpItem( item ) )
		return;

	taken = G_PickupItem( other, item, ent->spawnflags, ent->count, ent->invpak );

	if( !( ent->spawnflags & ITEM_TARGETS_USED ) )
	{
		G_UseTargets( ent, other );
		ent->spawnflags |= ITEM_TARGETS_USED;
	}

	if( !taken )
		return;

	if( ent->spawnflags & ITEM_TIMED )
		ent->r.owner = other;

	// flash the screen
	G_AddPlayerStateEvent( other->r.client, PSEV_PICKUP, ( item->flags & IT_WEAPON ? item->tag : 0 ) );

	G_AwardPlayerPickup( other, ent );

	// for messages
	other->r.client->teamstate.last_pickup = ent;

	// show icon and name on status bar
	other->r.client->ps.stats[STAT_PICKUP_ITEM] = item->tag;
	other->r.client->resp.pickup_msg_time = level.time + 3000;

	if( ent->attenuation )
		Touch_ItemSound( other, item );

	if( !( ent->spawnflags & DROPPED_ITEM ) && G_Gametype_CanRespawnItem( item ) )
	{
		if( (item->type & IT_WEAPON ) && GS_RaceGametype() )
			return; // weapons stay in race
		SetRespawn( ent, G_Gametype_RespawnTimeForItem( item ) );
		return;
	}
	G_FreeEdict( ent );
}
예제 #2
0
파일: g_items.c 프로젝트: hettoo/racesow
/*
* Touch_Item
*/
void Touch_Item( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags )
{
	qboolean taken;

	if( !other->r.client || G_ISGHOSTING( other ) )
		return;

	if( !( other->r.client->ps.pmove.stats[PM_STAT_FEATURES] & PMFEAT_ITEMPICK ) )
		return;

	if( !ent->item || !( ent->item->flags & ITFLAG_PICKABLE ) )
		return; // not a grabbable item

	if( !G_Gametype_CanPickUpItem( ent->item ) )
		return;

	taken = G_PickupItem( ent, other );

	if( !( ent->spawnflags & ITEM_TARGETS_USED ) )
	{
		G_UseTargets( ent, other );
		ent->spawnflags |= ITEM_TARGETS_USED;
	}

	if( !taken )
		return;

	// flash the screen
	G_AddPlayerStateEvent( other->r.client, PSEV_PICKUP, ( ent->item->flags & IT_WEAPON ? ent->item->tag : 0 ) );

	G_AwardPlayerPickup( other, ent );

	// for messages
	other->r.client->teamstate.last_pickup = ent;

	// show icon and name on status bar
	other->r.client->ps.stats[STAT_PICKUP_ITEM] = ent->item->tag;
	other->r.client->resp.pickup_msg_time = level.time + 3000;

	if( ent->attenuation )
		Touch_ItemSound( other, ent->item );

	if( !( ent->spawnflags & ( DROPPED_ITEM | DROPPED_PLAYER_ITEM ) ) && G_Gametype_CanRespawnItem( ent->item ) )
		SetRespawn( ent, G_Gametype_RespawnTimeForItem( ent->item ) );
	else
		G_FreeEdict( ent );
}
예제 #3
0
파일: ai_main.cpp 프로젝트: MGXRace/racesow
//==========================================
// AI_PickShortRangeGoal
// Pick best goal based on importance and range. This function
// overrides the long range goal selection for items that
// are very close to the bot and are reachable.
//==========================================
static void AI_PickShortRangeGoal( edict_t *self )
{
	edict_t *bestGoal = NULL;
	float bestWeight = 0;
	nav_ents_t *goalEnt;
	const gsitem_t *item;
	bool canPickupItems;
	int i;

	if( !self->r.client || G_ISGHOSTING( self ) )
		return;

	if( self->ai->state_combat_timeout > level.time )
	{
		self->ai->shortRangeGoalTimeout = self->ai->state_combat_timeout;
		return;
	}

	if( self->ai->shortRangeGoalTimeout > level.time )
		return;

	canPickupItems = (self->r.client->ps.pmove.stats[PM_STAT_FEATURES] & PMFEAT_ITEMPICK) != 0 ? true : false;

	self->ai->shortRangeGoalTimeout = level.time + AI_SHORT_RANGE_GOAL_DELAY;

	self->movetarget = NULL;

	FOREACH_GOALENT( goalEnt )
	{
		float dist;

		i = goalEnt->id;
		if( !goalEnt->ent->r.inuse || goalEnt->ent->r.solid == SOLID_NOT )
			continue;

		if( goalEnt->ent->r.client )
			continue;

		if( self->ai->status.entityWeights[i] <= 0.0f )
			continue;

		item = goalEnt->ent->item;
		if( canPickupItems && item ) {
			if( !G_Gametype_CanPickUpItem( item ) || !( item->flags & ITFLAG_PICKABLE ) ) {
				continue;
			}
		}

		dist = DistanceFast( self->s.origin, goalEnt->ent->s.origin );
		if( goalEnt == self->ai->goalEnt ) {
			if( dist > AI_GOAL_SR_LR_RADIUS )
				continue;			
		}
		else {
			if( dist > AI_GOAL_SR_RADIUS )
				continue;
		}		

		clamp_low( dist, 0.01f );

		if( AI_ShortRangeReachable( self, goalEnt->ent->s.origin ) )
		{
			float weight;
			bool in_front = G_InFront( self, goalEnt->ent );

			// Long range goal gets top priority
			if( in_front && goalEnt == self->ai->goalEnt ) 
			{
				bestGoal = goalEnt->ent;
				break;
			}

			// get the one with the best weight
			weight = self->ai->status.entityWeights[i] / dist * (in_front ? 1.0f : 0.5f);
			if( weight > bestWeight )
			{
				bestWeight = weight;
				bestGoal = goalEnt->ent;
			}
		}
	}

	if( bestGoal )
	{
		self->movetarget = bestGoal;
		if( nav.debugMode && bot_showsrgoal->integer )
			G_PrintChasersf( self, "%i %s: selected a %s for SR goal.\n", level.framenum, self->ai->pers.netname, self->movetarget->classname );
	}
	else
	{
		// got nothing else to do so keep scanning
		self->ai->shortRangeGoalTimeout = level.time + AI_SHORT_RANGE_GOAL_DELAY_IDLE;
	}
}
예제 #4
0
파일: ai_main.cpp 프로젝트: MGXRace/racesow
//==========================================
// AI_PickLongRangeGoal
//
// Evaluate the best long range goal and send the bot on
// its way. This is a good time waster, so use it sparingly.
// Do not call it for every think cycle.
//
// jal: I don't think there is any problem by calling it,
// now that we have stored the costs at the nav.costs table (I don't do it anyway)
//==========================================
void AI_PickLongRangeGoal( edict_t *self )
{
#define WEIGHT_MAXDISTANCE_FACTOR 20000.0f
#define COST_INFLUENCE	0.5f
	int i;
	float weight, bestWeight = 0.0;
	int current_node;
	float cost;
	float dist;
	nav_ents_t *goalEnt, *bestGoalEnt = NULL;

	AI_ClearGoal( self );

	if( G_ISGHOSTING( self ) )
		return;

	if( self->ai->longRangeGoalTimeout > level.time )
		return;

	if( !self->r.client->ps.pmove.stats[PM_STAT_MAXSPEED] ) {
		return;
	}

	self->ai->longRangeGoalTimeout = level.time + AI_LONG_RANGE_GOAL_DELAY + brandom( 0, 1000 );

	// look for a target
	current_node = AI_FindClosestReachableNode( self->s.origin, self, ( ( 1 + self->ai->nearest_node_tries ) * NODE_DENSITY ), NODE_ALL );
	self->ai->current_node = current_node;

	if( current_node == NODE_INVALID )
	{
		if( nav.debugMode && bot_showlrgoal->integer )
			G_PrintChasersf( self, "%s: LRGOAL: Closest node not found. Tries:%i\n", self->ai->pers.netname, self->ai->nearest_node_tries );

		self->ai->nearest_node_tries++; // extend search radius with each try
		return;
	}

	self->ai->nearest_node_tries = 0;

	// Run the list of potential goal entities
	FOREACH_GOALENT( goalEnt )
	{
		i = goalEnt->id;
		if( !goalEnt->ent )
			continue;

		if( !goalEnt->ent->r.inuse )
		{
			goalEnt->node = NODE_INVALID;
			continue;
		}

		if( goalEnt->ent->r.client )
		{
			if( G_ISGHOSTING( goalEnt->ent ) || goalEnt->ent->flags & (FL_NOTARGET|FL_BUSY) )
				goalEnt->node = NODE_INVALID;
			else
				goalEnt->node = AI_FindClosestReachableNode( goalEnt->ent->s.origin, goalEnt->ent, NODE_DENSITY, NODE_ALL );
		}

		if( goalEnt->ent->item )
		{
			if( !G_Gametype_CanPickUpItem( goalEnt->ent->item ) )
				continue;
		}

		if( goalEnt->node == NODE_INVALID )
			continue;

		weight = self->ai->status.entityWeights[i];

		if( weight <= 0.0f )
			continue;

		// don't try to find cost for too far away objects
		dist = DistanceFast( self->s.origin, goalEnt->ent->s.origin );
		if( dist > WEIGHT_MAXDISTANCE_FACTOR * weight/* || dist < AI_GOAL_SR_RADIUS*/ )
			continue;

		cost = AI_FindCost( current_node, goalEnt->node, self->ai->status.moveTypesMask );
		if( cost == NODE_INVALID )
			continue;

		cost -= brandom( 0, 2000 ); // allow random variations
		clamp_low( cost, 1 );
		weight = ( 1000 * weight ) / ( cost * COST_INFLUENCE ); // Check against cost of getting there

		if( weight > bestWeight )
		{
			bestWeight = weight;
			bestGoalEnt = goalEnt;
		}
	}

	if( bestGoalEnt )
	{
		self->ai->goalEnt = bestGoalEnt;
		AI_SetGoal( self, bestGoalEnt->node );

		if( self->ai->goalEnt != NULL && nav.debugMode && bot_showlrgoal->integer )
			G_PrintChasersf( self, "%s: selected a %s at node %d for LR goal. (weight %f)\n", self->ai->pers.netname, self->ai->goalEnt->ent->classname, self->ai->goalEnt->node, bestWeight );

		return;
	}

	if( nav.debugMode && bot_showlrgoal->integer )
		G_PrintChasersf( self, "%s: did not find a LR goal.\n", self->ai->pers.netname );

#undef WEIGHT_MAXDISTANCE_FACTOR
#undef COST_INFLUENCE
}
예제 #5
0
SelectedNavEntity BotItemsSelector::SuggestGoalNavEntity( const SelectedNavEntity &currSelectedNavEntity ) {
	UpdateInternalItemAndGoalWeights();

	StaticVector<NavEntityAndWeight, MAX_NAVENTS> rawWeightCandidates;
	const auto levelTime = level.time;
	auto *navEntitiesRegistry = NavEntitiesRegistry::Instance();
	for( auto it = navEntitiesRegistry->begin(), end = navEntitiesRegistry->end(); it != end; ++it ) {
		const NavEntity *navEnt = *it;
		if( navEnt->IsDisabled() ) {
			continue;
		}

		// We cannot just set a zero internal weight for a temporarily disabled nav entity
		// (it might be overridden by an external weight, and we should not modify external weights
		// as script users expect them remaining the same unless explicitly changed via script API)
		if( disabledForSelectionUntil[navEnt->Id()] >= levelTime ) {
			continue;
		}

		// Since movable goals have been introduced (and clients qualify as movable goals), prevent picking itself as a goal.
		if( navEnt->Id() == ENTNUM( self ) ) {
			continue;
		}

		if( navEnt->Item() && !G_Gametype_CanPickUpItem( navEnt->Item() ) ) {
			continue;
		}

		// Reject an entity quickly if it looks like blocked by an enemy that is close to the entity.
		// Note than passing this test does not guarantee that entire path to the entity is not blocked by enemies.
		if( self->ai->botRef->routeCache->AreaDisabled( navEnt->AasAreaNum() ) ) {
			continue;
		}

		// This is a coarse and cheap test, helps to reject recently picked armors and powerups
		unsigned spawnTime = navEnt->SpawnTime();
		// A feasible spawn time (non-zero) always >= level.time.
		if( !spawnTime || level.time - spawnTime > 15000 ) {
			continue;
		}

		float weight = GetEntityWeight( navEnt->Id() );
		if( weight > 0 ) {
			rawWeightCandidates.push_back( NavEntityAndWeight( navEnt, weight ) );
		}
	}

	// Sort all pre-selected candidates by their raw weights
	std::sort( rawWeightCandidates.begin(), rawWeightCandidates.end() );

	// Try checking whether the bot is in some floor cluster to give a greater weight for items in the same cluster
	int currFloorClusterNum = 0;
	const auto &entityPhysicsState = self->ai->botRef->EntityPhysicsState();
	const auto *aasFloorClusterNums = AiAasWorld::Instance()->AreaFloorClusterNums();
	if( aasFloorClusterNums[entityPhysicsState->CurrAasAreaNum()] ) {
		currFloorClusterNum = aasFloorClusterNums[entityPhysicsState->CurrAasAreaNum()];
	} else if( aasFloorClusterNums[entityPhysicsState->DroppedToFloorAasAreaNum()] ) {
		currFloorClusterNum = aasFloorClusterNums[entityPhysicsState->DroppedToFloorAasAreaNum()];
	}

	const NavEntity *currGoalNavEntity = currSelectedNavEntity.navEntity;
	float currGoalEntWeight = 0.0f;
	float currGoalEntCost = 0.0f;
	const NavEntity *bestNavEnt = nullptr;
	float bestWeight = 0.000001f;
	float bestNavEntCost = 0.0f;
	// Test not more than 16 best pre-selected by raw weight candidates.
	// (We try to avoid too many expensive FindTravelTimeToGoalArea() calls,
	// thats why we start from the best item to avoid wasting these calls for low-priority items)
	for( unsigned i = 0, end = std::min( rawWeightCandidates.size(), 16U ); i < end; ++i ) {
		const NavEntity *navEnt = rawWeightCandidates[i].goal;
		float weight = rawWeightCandidates[i].weight;

		unsigned moveDuration = 1;
		unsigned waitDuration = 1;

		if( self->ai->botRef->CurrAreaNum() != navEnt->AasAreaNum() ) {
			// We ignore cost of traveling in goal area, since:
			// 1) to estimate it we have to retrieve reachability to goal area from last area before the goal area
			// 2) it is relative low compared to overall travel cost, and movement in areas is cheap anyway
			moveDuration = self->ai->botRef->botBrain.FindTravelTimeToGoalArea( navEnt->AasAreaNum() ) * 10U;
			// AAS functions return 0 as a "none" value, 1 as a lowest feasible value
			if( !moveDuration ) {
				continue;
			}

			if( navEnt->IsDroppedEntity() ) {
				// Do not pick an entity that is likely to dispose before it may be reached
				if( navEnt->Timeout() <= level.time + moveDuration ) {
					continue;
				}
			}
		}

		unsigned spawnTime = navEnt->SpawnTime();
		// The entity is not spawned and respawn time is unknown
		if( !spawnTime ) {
			continue;
		}

		// Entity origin may be reached at this time
		unsigned reachTime = level.time + moveDuration;
		if( reachTime < spawnTime ) {
			waitDuration = spawnTime - reachTime;
		}

		if( waitDuration > navEnt->MaxWaitDuration() ) {
			continue;
		}

		float moveCost = MOVE_TIME_WEIGHT * moveDuration * navEnt->CostInfluence();
		float cost = 0.0001f + moveCost + WAIT_TIME_WEIGHT * waitDuration * navEnt->CostInfluence();

		weight = ( 1000 * weight ) / cost;

		// If the bot is inside a floor cluster
		if( currFloorClusterNum ) {
			// Greatly increase weight for items in the same floor cluster
			if( currFloorClusterNum == aasFloorClusterNums[navEnt->AasAreaNum()] ) {
				weight *= 4.0f;
			}
		}

		// Store current weight of the current goal entity
		if( currGoalNavEntity == navEnt ) {
			currGoalEntWeight = weight;
			// Waiting time is handled by the planner for wait actions separately.
			currGoalEntCost = moveCost;
		}

		if( weight > bestWeight ) {
			bestNavEnt = navEnt;
			bestWeight = weight;
			// Waiting time is handled by the planner for wait actions separately.
			bestNavEntCost = moveCost;
		}
	}

	if( !bestNavEnt ) {
		Debug( "Can't find a feasible long-term goal nav. entity\n" );
		return SelectedNavEntity( nullptr, std::numeric_limits<float>::max(), 0.0f, level.time + 200 );
	}

	// If it is time to pick a new goal (not just re-evaluate current one), do not be too sticky to the current goal
	const float currToBestWeightThreshold = currGoalNavEntity != nullptr ? 0.6f : 0.8f;

	if( currGoalNavEntity && currGoalNavEntity == bestNavEnt ) {
		constexpr const char *format = "current goal entity %s is kept as still having best weight %.3f\n";
		Debug( format, currGoalNavEntity->Name(), bestWeight );
		return SelectedNavEntity( bestNavEnt, bestNavEntCost, GetGoalWeight( bestNavEnt->Id() ), level.time + 4000 );
	} else if( currGoalEntWeight > 0 && currGoalEntWeight / bestWeight > currToBestWeightThreshold ) {
		constexpr const char *format =
			"current goal entity %s is kept as having weight %.3f good enough to not consider picking another one\n";
		// If currGoalEntWeight > 0, currLongTermGoalEnt is guaranteed to be non-null
		Debug( format, currGoalNavEntity->Name(), currGoalEntWeight );
		return SelectedNavEntity( currGoalNavEntity, currGoalEntCost, GetGoalWeight( bestNavEnt->Id() ), level.time + 2500 );
	} else {
		if( currGoalNavEntity ) {
			const char *format = "suggested %s weighted %.3f as a long-term goal instead of %s weighted now as %.3f\n";
			Debug( format, bestNavEnt->Name(), bestWeight, currGoalNavEntity->Name(), currGoalEntWeight );
		} else {
			Debug( "suggested %s weighted %.3f as a new long-term goal\n", bestNavEnt->Name(), bestWeight );
		}
		return SelectedNavEntity( bestNavEnt, bestNavEntCost, GetGoalWeight( bestNavEnt->Id() ), level.time + 2500 );
	}
}