Example #1
0
///////////////////////////////////////////////////////////////////////
// Check for adding ladder nodes
///////////////////////////////////////////////////////////////////////
qboolean ACEND_CheckForLadder(edict_t *self)
{
	int closest_node;

	// If there is a ladder and we are moving up, see if we should add a ladder node
	if (gi.pointcontents(self->s.origin) & CONTENTS_LADDER && self->velocity[2] > 0)
	{
		//debug_printf("contents: %x\n",tr.contents);

		closest_node = ACEND_FindClosestReachableNode(self,NODE_DENSITY,NODE_LADDER); 
		if(closest_node == -1)
		{
			closest_node = ACEND_AddNode(self,NODE_LADDER);
	
			// Now add link
		    ACEND_UpdateNodeEdge(self->last_node,closest_node);	   
			
			// Set current to last
			self->last_node = closest_node;
		}
		else
		{
			ACEND_UpdateNodeEdge(self->last_node,closest_node);	   
			self->last_node = closest_node; // set visited to last
		}
		return true;
	}
	return false;
}
Example #2
0
///////////////////////////////////////////////////////////////////////
// Capture when the grappling hook has been fired for mapping purposes.
///////////////////////////////////////////////////////////////////////
void ACEND_GrapFired(edict_t *self)
{
	int closest_node;
	
	if(!self->owner)
		return; // should not be here
	
	// Check to see if the grapple is in pull mode
	if(self->owner->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL)
	{
		// Look for the closest node of type grapple
		closest_node = ACEND_FindClosestReachableNode(self,NODE_DENSITY,NODE_GRAPPLE);
		if(closest_node == -1 ) // we need to drop a node
		{	
			closest_node = ACEND_AddNode(self,NODE_GRAPPLE);
			 
			// Add an edge
			ACEND_UpdateNodeEdge(self->owner->last_node,closest_node);
		
			self->owner->last_node = closest_node;
		}
		else
			self->owner->last_node = closest_node; // zero out so other nodes will not be linked
	}
}
void ACESP_SetupBotState(gentity_t * self)
{
	int             clientNum;
	char            userinfo[MAX_INFO_STRING];
	//char           *team;

	//G_Printf("ACESP_SetupBotState()\n");

	clientNum = self->client - level.clients;
	trap_GetUserinfo(clientNum, userinfo, sizeof(userinfo));

	//self->classname = "acebot";
	self->enemy = NULL;

	//self->bs.turnSpeed = 35;	// 100 is deadly fast
	self->bs.moveTarget = NULL;
	self->bs.state = STATE_MOVE;

	// set the current node
	ACEND_setCurrentNode(self, ACEND_FindClosestReachableNode(self, NODE_DENSITY, NODE_MOVE));
	self->bs.goalNode = self->bs.currentNode;
	ACEND_setNextNode(self, self->bs.currentNode);

	//self->bs.nextNode = self->bs.currentNode;
	self->bs.lastNode = INVALID;
	self->bs.next_move_time = level.time;
	self->bs.suicide_timeout = level.time + 15000;

	/*
	   // is the bot part of a team when gameplay has changed?
	   team = Info_ValueForKey(userinfo, "team");
	   if(!team || !*team)
	   {
	   if(g_gametype.integer >= GT_TEAM)
	   {
	   if(PickTeam(clientNum) == TEAM_RED)
	   {
	   team = "red";
	   }
	   else
	   {
	   team = "blue";
	   }
	   }
	   else
	   {
	   team = "red";
	   }
	   //Info_SetValueForKey(userinfo, "team", team);

	   // need to send this or bots will be spectators
	   trap_BotClientCommand(self - g_entities, va("team %s", team));
	   }
	 */

	//if(g_gametype.integer >= GT_TEAM)
	//  trap_BotClientCommand(self - g_entities, va("team %s", Info_ValueForKey(userinfo, "team")));
}
Example #4
0
// Turns on showing of the path, set goal to -1 to 
// shut off. (utility function)
void ACEND_ShowPath(gentity_t * self, int goalNode)
{
	int             currentNode;

	currentNode = ACEND_FindClosestReachableNode(self, NODE_DENSITY, NODE_ALL);
	if(currentNode == INVALID)
	{
		trap_SendServerCommand(self - g_entities, "print \"no closest reachable node!\n\"");
		return;
	}

	ACEND_DrawPath(currentNode, goalNode);
}
Example #5
0
///////////////////////////////////////////////////////////////////////
// Special command processor
///////////////////////////////////////////////////////////////////////
qboolean ACECM_Commands(edict_t *ent)
{
    char *cmd;
    int  node;

    cmd = gi.argv(0);

    if ((Q_strcasecmp(cmd, "addnode") == 0) && debug_mode)
    {
        ent->last_node = ACEND_AddNode(ent, atoi(gi.argv(1)));
    }

    else if ((Q_strcasecmp(cmd, "removelink") == 0) && debug_mode)
    {
        ACEND_RemoveNodeEdge(ent, atoi(gi.argv(1)), atoi(gi.argv(2)));
    }

    else if ((Q_strcasecmp(cmd, "addlink") == 0) && debug_mode)
    {
        ACEND_UpdateNodeEdge(atoi(gi.argv(1)), atoi(gi.argv(2)));
    }

    else if ((Q_strcasecmp(cmd, "showpath") == 0) && debug_mode)
    {
        ACEND_ShowPath(ent, atoi(gi.argv(1)));
    }

    else if ((Q_strcasecmp(cmd, "findnode") == 0) && debug_mode)
    {
        node = ACEND_FindClosestReachableNode(ent, NODE_DENSITY, NODE_ALL);
        safe_bprintf(PRINT_MEDIUM, "node: %d type: %d x: %f y: %f z %f\n", node, nodes[node].type, nodes[node].origin[0], nodes[node].origin[1], nodes[node].origin[2]);
    }

    else if ((Q_strcasecmp(cmd, "movenode") == 0) && debug_mode)
    {
        node = atoi(gi.argv(1));
        nodes[node].origin[0] = atof(gi.argv(2));
        nodes[node].origin[1] = atof(gi.argv(3));
        nodes[node].origin[2] = atof(gi.argv(4));
        safe_bprintf(PRINT_MEDIUM, "node: %d moved to x: %f y: %f z %f\n", node, nodes[node].origin[0], nodes[node].origin[1], nodes[node].origin[2]);
    }

    else
    {
        return false;
    }

    return true;
}
Example #6
0
///////////////////////////////////////////////////////////////////////
// Set up the goal
///////////////////////////////////////////////////////////////////////
void ACEND_SetGoal(edict_t *self, int goal_node)
{
	int node;

	self->goal_node = goal_node;
	node = ACEND_FindClosestReachableNode(self, NODE_DENSITY*3, NODE_ALL);
	
	if(node == -1)
		return;
	
	if(debug_mode)
		debug_printf("%s new start node selected %d\n",self->client->pers.netname,node);
	
	
	self->current_node = node;
	self->next_node = self->current_node; // make sure we get to the nearest node first
	self->node_timeout = 0;

}
Example #7
0
void ACEND_SetGoal(gentity_t * self, int goalNode)
{
	int             node;

	self->bs.goalNode = goalNode;

	node = ACEND_FindClosestReachableNode(self, NODE_DENSITY * 3, NODE_ALL);

	if(node == INVALID)
		return;

	if(ace_debug.integer)
		trap_SendServerCommand(-1, va("print \"%s: new start node selected %d\n\"", self->client->pers.netname, node));

	self->bs.currentNode = node;
	self->bs.nextNode = self->bs.currentNode;	// make sure we get to the nearest node first
	self->bs.node_timeout = 0;

	if(ace_showPath.integer)
	{
		// draw path to LR goal
		ACEND_DrawPath(self->bs.currentNode, self->bs.goalNode);
	}
}
Example #8
0
///////////////////////////////////////////////////////////////////////
// Modified version of id's code
///////////////////////////////////////////////////////////////////////
void ACESP_PutClientInServer(edict_t *bot, qboolean respawn, int team)
{
	vec3_t	mins = {-16, -16, -24};
	vec3_t	maxs = {16, 16, 32};
	int		index;
	vec3_t	spawn_origin, spawn_angles;
	gclient_t	*client;
	client_persistant_t	saved;
	client_respawn_t	resp;
	char *s;
	int			spawn_style;
	int			spawn_health;

	// find a spawn point
	// do it before setting health back up, so farthest
	// ranging doesn't count this client
	SelectSpawnPoint (bot, spawn_origin, spawn_angles, &spawn_style, &spawn_health);
	
	index = bot-g_edicts-1;
	client = bot->client;

	// deathmatch wipes most client data every spawn
	if (deathmatch->value)
	{
		char userinfo[MAX_INFO_STRING];

		resp = bot->client->resp;
		memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
		InitClientPersistant (client, spawn_style);
		ClientUserinfoChanged (bot, userinfo);
	}
	else
		memset(&resp, 0, sizeof(resp));
	
	// clear everything but the persistant data
	saved = client->pers;
	memset(client, 0, sizeof(*client));
	client->pers = saved;
	client->resp = resp;
	
	// copy some data from the client to the entity
	FetchClientEntData (bot);
	
	// clear entity values
	bot->groundentity = NULL;
	bot->client = &game.clients[index];
	bot->takedamage = DAMAGE_AIM;
	bot->movetype = MOVETYPE_WALK;
	bot->viewheight = 24;
	bot->classname = "bot";
	bot->mass = 200;
	bot->solid = SOLID_BBOX;
	bot->deadflag = DEAD_NO;
	bot->air_finished = level.time + 12;
	bot->clipmask = MASK_PLAYERSOLID;
	bot->model = "players/male/tris.md2";
	bot->pain = player_pain;
	bot->die = player_die;
	bot->waterlevel = 0;
	bot->watertype = 0;
	bot->flags &= ~FL_NO_KNOCKBACK;
	bot->svflags &= ~SVF_DEADMONSTER;
	bot->is_jumping = false;
	
	if (ctf->value)
	{
		client->resp.ctf_team = team;
		client->resp.ctf_state = CTF_STATE_START;
		s = Info_ValueForKey(client->pers.userinfo, "skin");
		CTFAssignSkin(bot, s);
	}

	VectorCopy(mins, bot->mins);
	VectorCopy(maxs, bot->maxs);
	VectorClear(bot->velocity);

	// clear playerstate values
	memset(&bot->client->ps, 0, sizeof(client->ps));
	
	client->ps.pmove.origin[0] = spawn_origin[0]*8;
	client->ps.pmove.origin[1] = spawn_origin[1]*8;
	client->ps.pmove.origin[2] = spawn_origin[2]*8;

//ZOID
	client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
//ZOID

	if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
	{
		client->ps.fov = 90;
	}
	else
	{
		client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov"));
		if (client->ps.fov < 1)
			client->ps.fov = 90;
		else if (client->ps.fov > 160)
			client->ps.fov = 160;
	}

	// Knightmare- fix for null model?
	if (client->pers.weapon && client->pers.weapon->view_model)
		client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model);

	// clear entity state values
	bot->s.effects = 0;
	bot->s.skinnum = bot - g_edicts - 1;
	bot->s.modelindex = MAX_MODELS-1;		// will use the skin specified model
	bot->s.modelindex2 = MAX_MODELS-1;		// custom gun model
	bot->s.frame = 0;
	VectorCopy(spawn_origin, bot->s.origin);
	bot->s.origin[2] += 1;	// make sure off ground

	// set the delta angle
	for (int i = 0; i < 3; i++)
		client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]);

	bot->s.angles[PITCH] = 0;
	bot->s.angles[YAW] = spawn_angles[YAW];
	bot->s.angles[ROLL] = 0;
	VectorCopy(bot->s.angles, client->ps.viewangles);
	VectorCopy(bot->s.angles, client->v_angle);
	
	// force the current weapon up
	client->newweapon = client->pers.weapon;
	ChangeWeapon (bot);

	bot->enemy = NULL;
	bot->movetarget = NULL; 
	bot->state = STATE_MOVE;

	// Set the current node
	bot->current_node = ACEND_FindClosestReachableNode(bot,NODE_DENSITY, NODE_ALL);
	bot->goal_node = bot->current_node;
	bot->next_node = bot->current_node;
	bot->next_move_time = level.time;		
	bot->suicide_timeout = level.time + 15.0;

	// If we are not respawning hold off for up to three seconds before releasing into game
    if (!respawn)
	{
		bot->think = ACESP_HoldSpawn;
		//bot->nextthink = level.time + 0.1; //mxd
		bot->nextthink = level.time + random()*3.0f; // up to three seconds
	}
	else
	{
		if (!KillBox(bot))
		{	// could't spawn in?
		}

		gi.linkentity(bot);

		bot->think = ACEAI_Think;
		bot->nextthink = level.time + FRAMETIME;

		// send effect
		gi.WriteByte(svc_muzzleflash);
		gi.WriteShort(bot-g_edicts);
		gi.WriteByte(MZ_LOGIN);
		gi.multicast(bot->s.origin, MULTICAST_PVS);
	}
}
Example #9
0
///////////////////////////////////////////////////////////////////////
// Turns on showing of the path, set goal to -1 to 
// shut off. (utility function)
///////////////////////////////////////////////////////////////////////
void ACEND_ShowPath(edict_t *self, int goal_node)
{
	show_path_from = ACEND_FindClosestReachableNode(self, NODE_DENSITY, NODE_ALL);
	show_path_to = goal_node;
}
Example #10
0
///////////////////////////////////////////////////////////////////////
// This routine is called to hook in the pathing code and sets
// the current node if valid.
///////////////////////////////////////////////////////////////////////
void ACEND_PathMap(edict_t *self)
{
	int closest_node;
	static float last_update=0; // start off low
	vec3_t v;

	if(level.time < last_update)
		return;

	last_update = level.time + 0.15; // slow down updates a bit

	// Special node drawing code for debugging
    if(show_path_to != -1)
		ACEND_DrawPath();
	
	////////////////////////////////////////////////////////
	// Special check for ladder nodes
	///////////////////////////////////////////////////////
	if(ACEND_CheckForLadder(self)) // check for ladder nodes
		return;

	// Not on ground, and not in the water, so bail
    if(!self->groundentity && !self->waterlevel)
		return;

	////////////////////////////////////////////////////////
	// Lava/Slime
	////////////////////////////////////////////////////////
	VectorCopy(self->s.origin,v);
	v[2] -= 18;
	if(gi.pointcontents(v) & (CONTENTS_LAVA|CONTENTS_SLIME))
		return; // no nodes in slime
	
    ////////////////////////////////////////////////////////
	// Jumping
	///////////////////////////////////////////////////////
	if(self->is_jumping)
	{
	   // See if there is a closeby jump landing node (prevent adding too many)
		closest_node = ACEND_FindClosestReachableNode(self, 64, NODE_JUMP);

		if(closest_node == INVALID)
			closest_node = ACEND_AddNode(self,NODE_JUMP);
		
		// Now add link
		if(self->last_node != -1)
			ACEND_UpdateNodeEdge(self->last_node, closest_node);	   

		self->is_jumping = false;
		return;
	}

	////////////////////////////////////////////////////////////
	// Grapple
	// Do not add nodes during grapple, added elsewhere manually
	////////////////////////////////////////////////////////////
	if(ctf->value && self->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL)
		return;
	 
	// Iterate through all nodes to make sure far enough apart
	closest_node = ACEND_FindClosestReachableNode(self, NODE_DENSITY, NODE_ALL);

	////////////////////////////////////////////////////////
	// Special Check for Platforms
	////////////////////////////////////////////////////////
	if(self->groundentity && self->groundentity->use == Use_Plat)
	{
		if(closest_node == INVALID)
			return; // Do not want to do anything here.

		// Here we want to add links
		if(closest_node != self->last_node && self->last_node != INVALID)
			ACEND_UpdateNodeEdge(self->last_node,closest_node);	   

		self->last_node = closest_node; // set visited to last
		return;
	}
	 
	 ////////////////////////////////////////////////////////
	 // Add Nodes as needed
	 ////////////////////////////////////////////////////////
	 if(closest_node == INVALID)
	 {
		// Add nodes in the water as needed
		if(self->waterlevel)
			closest_node = ACEND_AddNode(self,NODE_WATER);
		else
		    closest_node = ACEND_AddNode(self,NODE_MOVE);
		
		// Now add link
		if(self->last_node != -1)
			ACEND_UpdateNodeEdge(self->last_node, closest_node);	   
			
	 }
	 else if(closest_node != self->last_node && self->last_node != INVALID)
	 	ACEND_UpdateNodeEdge(self->last_node,closest_node);	   
	
	 self->last_node = closest_node; // set visited to last
	
}
Example #11
0
///////////////////////////////////////////////////////////////////////
// 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.
///////////////////////////////////////////////////////////////////////
void ACEAI_PickLongRangeGoal(edict_t *self)
{

	int i;
	int node;
	float weight,best_weight=0.0;
	int current_node,goal_node;
	edict_t *goal_ent;
	float cost;
	
	// look for a target 
	current_node = ACEND_FindClosestReachableNode(self,NODE_DENSITY,NODE_ALL);

	self->current_node = current_node;
	
	if(current_node == -1)
	{
		self->state = STATE_WANDER;
		self->wander_timeout = level.time + 1.0;
		self->goal_node = -1;
		return;
	}

	///////////////////////////////////////////////////////
	// Items
	///////////////////////////////////////////////////////
	for (i=0; i<num_items; i++)
	{
		// ignore items that are not there.
		if (!item_table[i].ent || !item_table[i].ent->inuse || item_table[i].ent->solid == SOLID_NOT)
			continue;
		
		cost = ACEND_FindCost(current_node,item_table[i].node);
		
		if (cost == INVALID || cost < 2) // ignore invalid and very short hops
			continue;
	
		weight = ACEIT_ItemNeed(self, item_table[i].item);

		// If I am on team one and I have the flag for the other team....return it
		// Knightmare- rewrote for 3Team CTF
		//if (ctf->value && (item_table[i].item == ITEMLIST_FLAG2 || item_table[i].item == ITEMLIST_FLAG1) &&
		//	(self->client->resp.ctf_team == CTF_TEAM1 && self->client->pers.inventory[ITEMLIST_FLAG2] ||
		//	self->client->resp.ctf_team == CTF_TEAM2 && self->client->pers.inventory[ITEMLIST_FLAG1]))
		if (ctf->value && (
				(item_table[i].item == ITEMLIST_FLAG1 && self->client->resp.ctf_team == CTF_TEAM1 &&
					(self->client->pers.inventory[ITEMLIST_FLAG2] || self->client->pers.inventory[ITEMLIST_FLAG3]) )
				|| (item_table[i].item == ITEMLIST_FLAG2 && self->client->resp.ctf_team == CTF_TEAM2 &&
					(self->client->pers.inventory[ITEMLIST_FLAG1] || self->client->pers.inventory[ITEMLIST_FLAG3]) )
				|| (item_table[i].item == ITEMLIST_FLAG3 && self->client->resp.ctf_team == CTF_TEAM3 &&
					(self->client->pers.inventory[ITEMLIST_FLAG1] || self->client->pers.inventory[ITEMLIST_FLAG2]) )
			))
			weight = 10.0;

		// Knightmare- in 3Team CTF mode, make double captures a lower priority than
		// getting back to base with the flag we already have.
		if (ttctf->value && (
			(self->client->resp.ctf_team == CTF_TEAM1
				&& (self->client->pers.inventory[ITEMLIST_FLAG2] || self->client->pers.inventory[ITEMLIST_FLAG3])
				&& (item_table[i].item == ITEMLIST_FLAG2 || item_table[i].item == ITEMLIST_FLAG3) )
			|| (self->client->resp.ctf_team == CTF_TEAM2
				&& (self->client->pers.inventory[ITEMLIST_FLAG1] || self->client->pers.inventory[ITEMLIST_FLAG3])
				&& (item_table[i].item == ITEMLIST_FLAG1 || item_table[i].item == ITEMLIST_FLAG3) )
			|| (self->client->resp.ctf_team == CTF_TEAM3
				&& (self->client->pers.inventory[ITEMLIST_FLAG1] || self->client->pers.inventory[ITEMLIST_FLAG2])
				&& (item_table[i].item == ITEMLIST_FLAG1 || item_table[i].item == ITEMLIST_FLAG2) )
			))
			weight = 6.5;

		weight *= random(); // Allow random variations
		weight /= cost; // Check against cost of getting there
				
		if (weight > best_weight)
		{
			best_weight = weight;
			goal_node = item_table[i].node;
			goal_ent = item_table[i].ent;
		}
	}

	///////////////////////////////////////////////////////
	// Players
	///////////////////////////////////////////////////////
	// This should be its own function and is for now just
	// finds a player to set as the goal.
	for(i=0;i<num_players;i++)
	{
		if(players[i] == self)
			continue;

		node = ACEND_FindClosestReachableNode(players[i],NODE_DENSITY,NODE_ALL);
		cost = ACEND_FindCost(current_node, node);

		if(cost == INVALID || cost < 3) // ignore invalid and very short hops
			continue;

		// Player carrying the flag?
		if(ctf->value && (players[i]->client->pers.inventory[ITEMLIST_FLAG2] || players[i]->client->pers.inventory[ITEMLIST_FLAG1]))
		  weight = 2.0;
		else
		  weight = 0.3; 
		
		weight *= random(); // Allow random variations
		weight /= cost; // Check against cost of getting there
		
		if(weight > best_weight)
		{		
			best_weight = weight;
			goal_node = node;
			goal_ent = players[i];
		}	
	}

	// If do not find a goal, go wandering....
	if(best_weight == 0.0 || goal_node == INVALID)
	{
		self->goal_node = INVALID;
		self->state = STATE_WANDER;
		self->wander_timeout = level.time + 1.0;
		if(debug_mode)
			debug_printf("%s did not find a LR goal, wandering.\n",self->client->pers.netname);
		return; // no path? 
	}
	
	// OK, everything valid, let's start moving to our goal.
	self->state = STATE_MOVE;
	self->tries = 0; // Reset the count of how many times we tried this goal
	 
	if(goal_ent != NULL && debug_mode)
		debug_printf("%s selected a %s at node %d for LR goal.\n",self->client->pers.netname, goal_ent->classname, goal_node);

	ACEND_SetGoal(self,goal_node);

}
Example #12
0
// This routine is called to hook in the pathing code and sets
// the current node if valid.
void ACEND_PathMap(gentity_t * self)
{
	int             closestNode;
	static float    lastUpdate = 0;	// start off low
	vec3_t          v;
	//qboolean        isJumping;
	//int             i;

#if 0
	if(level.time < lastUpdate)
		return;
#endif

	lastUpdate = level.time + 150;	// slow down updates a bit

#if 0
	if(self->r.svFlags & SVF_BOT)
		return;
#endif

	// don't add links when you went into a trap
	if(self->health <= 0)
		return;

#if 1
	if(self->s.groundEntityNum == ENTITYNUM_NONE && !(self->r.svFlags & SVF_BOT))
	{
#if 0
		isJumping = qfalse;
		for(i = 0; i < self->client->ps.eventSequence; i++)
		{
			if(self->client->ps.events[i] == EV_JUMP)
				isJumping = qtrue;
		}

		if(isJumping)
#else
		if((self->client->ps.pm_flags & PMF_JUMP_HELD))
#endif
		{
			if(ace_debug.integer)
				trap_SendServerCommand(-1, va("print \"%s: jumping\n\"", self->client->pers.netname));

			// see if there is a closeby jump landing node (prevent adding too many)
			closestNode = ACEND_FindClosestReachableNode(self, 64, NODE_JUMP);

			if(closestNode == INVALID)
				closestNode = ACEND_AddNode(self, NODE_JUMP);

			// now add link
			if(self->bs.lastNode != INVALID)
				ACEND_UpdateNodeEdge(self->bs.lastNode, closestNode);

			self->bs.isJumping = qfalse;
			return;
		}
	}
#endif

	// not on ground, and not in the water, so bail
	if(self->s.groundEntityNum == ENTITYNUM_NONE)
	{
		/*
		   if(self->bs.lastNode != INVALID)
		   {
		   // we might have been pushed by a jump pad
		   if(nodes[self->bs.lastNode].type != NODE_JUMPPAD)
		   return;
		   }
		   else if(!self->waterlevel)
		   {
		   return;
		   }
		 */
	}


	// lava / slime
	VectorCopy(self->client->ps.origin, v);
	v[2] -= 18;

	if(trap_PointContents(self->client->ps.origin, -1) & (CONTENTS_LAVA | CONTENTS_SLIME))
		return;					// no nodes in slime

	// Grapple
	// Do not add nodes during grapple, added elsewhere manually

	/*
	   if(ctf->value && self->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL)
	   return;
	 */

	// iterate through all nodes to make sure far enough apart
	closestNode = ACEND_FindClosestReachableNode(self, NODE_DENSITY, NODE_ALL);


	// Special Check for Platforms
	/* FIXME
	   if(self->groundentity && self->groundentity->use == Use_Plat)
	   {
	   if(closestNode == INVALID)
	   return;              // Do not want to do anything here.

	   // Here we want to add links
	   if(closestNode != self->lastNode && self->lastNode != INVALID)
	   ACEND_UpdateNodeEdge(self->lastNode, closestNode);

	   self->lastNode = closestNode;    // set visited to last
	   return;
	   }
	 */


	if(closestNode != INVALID)
	{
		// add automatically some links between nodes

		if(closestNode != self->bs.lastNode && self->bs.lastNode != INVALID)
		{
			ACEND_UpdateNodeEdge(self->bs.lastNode, closestNode);
			if(ace_showLinks.integer)
				ACEND_DrawPath(self->bs.lastNode, closestNode);
		}

		self->bs.lastNode = closestNode;	// set visited to last
	}
#if 1
	else if(closestNode == INVALID && self->s.groundEntityNum != ENTITYNUM_NONE)
	{
		// add nodes in the water as needed
		if(self->waterlevel)
			closestNode = ACEND_AddNode(self, NODE_WATER);
		else
			closestNode = ACEND_AddNode(self, NODE_MOVE);

		// now add link
		if(self->bs.lastNode != INVALID)
		{
			ACEND_UpdateNodeEdge(self->bs.lastNode, closestNode);

			if(ace_showLinks.integer)
				ACEND_DrawPath(self->bs.lastNode, closestNode);
		}

		self->bs.lastNode = closestNode;	// set visited to last
	}
#endif
}