/////////////////////////////////////////////////////////////////////// // 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; }
/////////////////////////////////////////////////////////////////////// // 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 } }
/////////////////////////////////////////////////////////////////////// // 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; }
/////////////////////////////////////////////////////////////////////// // Only called once per level, when saved will not be called again // // Downside of the routine is that items can not move about. If the level // has been saved before and reloaded, it could cause a problem if there // are items that spawn at random locations. // //#define DEBUG // uncomment to write out items to a file. /////////////////////////////////////////////////////////////////////// void ACEIT_BuildItemNodeTable (qboolean rebuild) { edict_t *items; int i,item_index; vec3_t v,v1,v2; int j; #ifdef DEBUG FILE *pOut; // for testing if((pOut = fopen("items.txt","wt"))==NULL) return; #endif num_items = 0; // Add game items //for (items = g_edicts; items < &g_edicts[globals.num_edicts]; items++) items = g_edicts+1; // skip the worldspawn for (j = 1; j < globals.num_edicts; j++, items++) { // filter out crap if (!items->inuse) // Knightmare added continue; if (!items->classname) continue; if (items->solid == SOLID_NOT) continue; ///////////////////////////////////////////////////////////////// // Items ///////////////////////////////////////////////////////////////// item_index = ACEIT_ClassnameToIndex(items->classname); //////////////////////////////////////////////////////////////// // SPECIAL NAV NODE DROPPING CODE //////////////////////////////////////////////////////////////// // Special node dropping for platforms if(strcmp(items->classname,"func_plat")==0) { if(!rebuild) ACEND_AddNode(items,NODE_PLATFORM); item_index = 99; // to allow to pass the item index test } // Special node dropping for teleporters if(strcmp(items->classname,"misc_teleporter_dest")==0 || strcmp(items->classname,"misc_teleporter")==0) { if(!rebuild) ACEND_AddNode(items,NODE_TELEPORTER); item_index = 99; } #ifdef DEBUG if(item_index == INVALID) fprintf(pOut,"Rejected item: %s node: %d pos: %f %f %f\n",items->classname,item_table[num_items].node,items->s.origin[0],items->s.origin[1],items->s.origin[2]); else fprintf(pOut,"item: %s node: %d pos: %f %f %f\n",items->classname,item_table[num_items].node,items->s.origin[0],items->s.origin[1],items->s.origin[2]); #endif if (item_index == INVALID) continue; // add a pointer to the item entity item_table[num_items].ent = items; item_table[num_items].item = item_index; // If new, add nodes for items if(!rebuild) { // Add a new node at the item's location. item_table[num_items].node = ACEND_AddNode(items,NODE_ITEM); num_items++; } else // Now if rebuilding, just relink ent structures { // Find stored location for(i=0;i<numnodes;i++) { if(nodes[i].type == NODE_ITEM || nodes[i].type == NODE_PLATFORM || nodes[i].type == NODE_TELEPORTER) // valid types { VectorCopy(items->s.origin,v); // Add 16 to item type nodes if(nodes[i].type == NODE_ITEM) v[2] += 16; // Add 32 to teleporter if(nodes[i].type == NODE_TELEPORTER) v[2] += 32; if(nodes[i].type == NODE_PLATFORM) { VectorCopy(items->maxs,v1); VectorCopy(items->mins,v2); // To get the center v[0] = (v1[0] - v2[0]) / 2 + v2[0]; v[1] = (v1[1] - v2[1]) / 2 + v2[1]; v[2] = items->mins[2]+64; } if(v[0] == nodes[i].origin[0] && v[1] == nodes[i].origin[1] && v[2] == nodes[i].origin[2]) { // found a match now link to facts item_table[num_items].node = i; #ifdef DEBUG fprintf(pOut,"Relink item: %s node: %d pos: %f %f %f\n",items->classname,item_table[num_items].node,items->s.origin[0],items->s.origin[1],items->s.origin[2]); #endif num_items++; } } } } } #ifdef DEBUG fclose(pOut); #endif }
// Only called once per level, when saved will not be called again // // Downside of the routine is that items can not move about. If the level // has been saved before and reloaded, it could cause a problem if there // are items that spawn at random locations. void ACEIT_BuildItemNodeTable(qboolean rebuild) { int i; gentity_t *ent; vec3_t v, v1, v2; int nodeType; for(i = 0, ent = &g_entities[0];i < level.num_entities;i++, ent++) { if (!ent->inuse) continue; /* // special node dropping for platforms if(!Q_stricmp(ent->item->classname, "func_plat")) { if(!rebuild) ACEND_AddNode(ent, NODE_PLATFORM); item_index = 99; // to allow to pass the item index test } // special node dropping for teleporters if(!Q_stricmp(ent->item->classname, "misc_teleporter_dest") || !Q_stricmp(ent->item->classname, "trigger_teleport")) { if(!rebuild) ACEND_AddNode(ent, NODE_TELEPORTER); item_index = 99; } */ /*if(ent->item) { // FIXME: ignore dropped items for now, because they would create too many nodes // it would be necessary to remove nodes if we want to support them if(ent->flags & FL_DROPPED_ITEM) continue; nodeType = NODE_ITEM; } else*/ if (!Q_stricmp(ent->classname, "trigger_teleport")) { nodeType = NODE_TRIGGER_TELEPORT; } /* else if(!Q_stricmp(ent->classname, "misc_teleporter_dest")) { nodeType = NODE_TARGET_TELEPORT; } */ else if (!Q_stricmp(ent->classname, "trigger_push")) { nodeType = NODE_JUMPPAD; } else { // entity does not matter continue; } // if new, add nodes for items if (!rebuild) { // add a new node at the item's location. G_Printf("Node Added from buildItemNodeTable"); ent->node = ACEND_AddNode(ent, nodeType); } else { // find stored location for(i = 0;i < numNodes;i++) { if (nodes[i].type == NODE_ITEM || nodes[i].type == NODE_PLATFORM || nodes[i].type == NODE_TRIGGER_TELEPORT || nodes[i].type == NODE_JUMPPAD) // valid types { if (nodes[i].type == NODE_ITEM) { VectorCopy(ent->s.origin, v); //v[2] += 16; } else if (nodes[i].type == NODE_TRIGGER_TELEPORT) { VectorAdd(ent->r.absmin, ent->r.absmax, v); VectorScale(v, 0.5, v); } /* else if(nodes[i].type == NODE_TARGET_TELEPORT) { v[2] += 32; } */ else if (nodes[i].type == NODE_JUMPPAD) { VectorAdd(ent->r.absmin, ent->r.absmax, v); VectorScale(v, 0.5, v); // add jumppad target offset VectorNormalize2(ent->s.origin2, v2); VectorMA(v, 32, v2, v); } else /*if(nodes[i].type == NODE_PLATFORM) */ { VectorCopy(ent->r.maxs, v1); VectorCopy(ent->r.mins, v2); // to get the center v[0] = (v1[0] - v2[0]) / 2 + v2[0]; v[1] = (v1[1] - v2[1]) / 2 + v2[1]; v[2] = ent->r.mins[2] + 64; } SnapVector(v); if ( /*ent->node != INVALID || */VectorCompare(v, nodes[i].origin)) { /* if(!VectorCompare(v, nodes[i].origin)) { // update node origin VectorCopy(v, nodes[i].origin); } else */ { // found a match now link to facts ent->node = i; } #if 0 //defined(_DEBUG) if(ent->item) { G_Printf("relink item: %s node: %d pos: %f %f %f\n", ent->item->classname, ent->node, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2]); } else { G_Printf("relink entity: %s node: %d pos: %f %f %f\n", ent->classname, ent->node, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2]); } #endif break; } } } if (i == numNodes) { // add a new node at the item's location. ent->node = ACEND_AddNode(ent, nodeType); } } #if 0 //defined(_DEBUG) //if(item_index == INVALID) // fprintf(pOut, "Rejected item: %s node: %d pos: %f %f %f\n", ent->item->classname, ent->node, // ent->s.origin[0], ent->s.origin[1], ent->s.origin[2]); //else if(ent->item) { G_Printf("accepted item: %s node: %d pos: %f %f %f\n", ent->item->classname, ent->node, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2]); } else { G_Printf("accepted entity: %s node: %d pos: %f %f %f\n", ent->classname, ent->node, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2]); } #endif } }
/////////////////////////////////////////////////////////////////////// // 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 }
// 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 }