//========================================== // AI_AddNode_Door // Drop a node at each side of the door // and force them to link. Only typical // doors are covered. //========================================== int AI_AddNode_Door( edict_t *ent ) { edict_t *other; vec3_t mins, maxs; vec3_t forward, right, up; vec3_t door_origin; if (ent->flags & FL_TEAMSLAVE) return INVALID; //only team master will drop the nodes //make box formed by all team members boxes VectorCopy (ent->absmin, mins); VectorCopy (ent->absmax, maxs); for (other = ent->teamchain ; other ; other=other->teamchain) { AddPointToBounds (other->absmin, mins, maxs); AddPointToBounds (other->absmax, mins, maxs); } door_origin[0] = (maxs[0] - mins[0]) / 2 + mins[0]; door_origin[1] = (maxs[1] - mins[1]) / 2 + mins[1]; door_origin[2] = (maxs[2] - mins[2]) / 2 + mins[2]; //now find the crossing angle AngleVectors( ent->s.angles, forward, right, up ); VectorNormalize( right ); //add node nodes[nav.num_nodes].flags = 0; VectorMA( door_origin, 32, right, nodes[nav.num_nodes].origin); AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL ); nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL ); #ifdef SHOW_JUMPAD_GUESS AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, "models/powerups/health/mega_sphere.md3" ); #endif nav.num_nodes++; //add node 2 nodes[nav.num_nodes].flags = 0; VectorMA( door_origin, -32, right, nodes[nav.num_nodes].origin); AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL ); nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL ); #ifdef SHOW_JUMPAD_GUESS AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, "models/powerups/health/mega_sphere.md3" ); #endif //add links in both directions AI_AddLink( nav.num_nodes, nav.num_nodes-1, LINK_MOVE ); AI_AddLink( nav.num_nodes-1, nav.num_nodes, LINK_MOVE ); nav.num_nodes++; return nav.num_nodes-1; }
//========================================== // AI_LinkServerNodes // link the new nodes to&from those loaded from disk //========================================== int AI_LinkServerNodes( int start ) { int n1, n2; int count = 0; float pLinkRadius = NODE_DENSITY*1.2; qboolean ignoreHeight = true; if( start >= nav.num_nodes ) return 0; for( n1=start; n1<nav.num_nodes; n1++ ) { n2 = 0; n2 = AI_findNodeInRadius ( 0, nodes[n1].origin, pLinkRadius, ignoreHeight); while (n2 != -1) { if( nodes[n1].flags & NODEFLAGS_SERVERLINK || nodes[n2].flags & NODEFLAGS_SERVERLINK ) { if( AI_AddLink( n1, n2, AI_FindServerLinkType(n1, n2) ) ) count++; if( AI_AddLink( n2, n1, AI_FindServerLinkType(n2, n1) ) ) count++; } else { if( AI_AddLink( n1, n2, AI_FindLinkType(n1, n2) ) ) count++; if( AI_AddLink( n2, n1, AI_FindLinkType(n2, n1) ) ) count++; } n2 = AI_findNodeInRadius ( n2, nodes[n1].origin, pLinkRadius, ignoreHeight); } } return count; }
/* * AI_AddNode_JumpPad * Drop two nodes, one at jump pad and other * at predicted destity */ static int AI_AddNode_JumpPad( edict_t *ent ) { vec3_t v1, v2; vec3_t out; if( nav.num_nodes + 1 > MAX_NODES ) return NODE_INVALID; if( !AI_PredictJumpadDestity( ent, out ) ) return NODE_INVALID; // jumpad node nodes[nav.num_nodes].flags = ( NODEFLAGS_JUMPPAD|NODEFLAGS_SERVERLINK|NODEFLAGS_REACHATTOUCH ); // find the origin VectorCopy( ent->r.maxs, v1 ); VectorCopy( ent->r.mins, v2 ); nodes[nav.num_nodes].origin[0] = ( v1[0] - v2[0] ) / 2 + v2[0]; nodes[nav.num_nodes].origin[1] = ( v1[1] - v2[1] ) / 2 + v2[1]; nodes[nav.num_nodes].origin[2] = ent->r.maxs[2] + 16; // raise it up a bit nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL ); //put into ents table nav.navigableEnts[nav.num_navigableEnts].ent = ent; nav.navigableEnts[nav.num_navigableEnts].node = nav.num_nodes; nav.num_navigableEnts++; nav.num_nodes++; // Destiny node nodes[nav.num_nodes].flags = ( NODEFLAGS_JUMPPAD_LAND|NODEFLAGS_SERVERLINK ); nodes[nav.num_nodes].origin[0] = out[0]; nodes[nav.num_nodes].origin[1] = out[1]; nodes[nav.num_nodes].origin[2] = out[2]; AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL ); nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL ); //put into ents table nav.navigableEnts[nav.num_navigableEnts].ent = ent; nav.navigableEnts[nav.num_navigableEnts].node = nav.num_nodes; nav.num_navigableEnts++; // link jumpad to dest AI_AddLink( nav.num_nodes-1, nav.num_nodes, LINK_JUMPPAD ); nav.num_nodes++; return nav.num_nodes -1; }
//========================================== // AI_AddNode_Platform // drop two nodes one at top, one at bottom //========================================== int AI_AddNode_Platform( edict_t *ent ) { vec3_t v1,v2; if (nav.num_nodes + 1 > MAX_NODES) return INVALID; // Upper node nodes[nav.num_nodes].flags = (NODEFLAGS_PLATFORM|NODEFLAGS_SERVERLINK|NODEFLAGS_FLOAT); VectorCopy( ent->maxs, v1 ); VectorCopy( ent->mins, v2 ); nodes[nav.num_nodes].origin[0] = (v1[0] - v2[0]) / 2 + v2[0]; nodes[nav.num_nodes].origin[1] = (v1[1] - v2[1]) / 2 + v2[1]; nodes[nav.num_nodes].origin[2] = ent->maxs[2] + 8; nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL ); //put into ents table nav.ents[nav.num_ents].ent = ent; nav.ents[nav.num_ents].node = nav.num_nodes; nav.num_ents++; nav.num_nodes++; // Lower node nodes[nav.num_nodes].flags = (NODEFLAGS_PLATFORM|NODEFLAGS_SERVERLINK|NODEFLAGS_FLOAT); nodes[nav.num_nodes].origin[0] = nodes[nav.num_nodes-1].origin[0]; nodes[nav.num_nodes].origin[1] = nodes[nav.num_nodes-1].origin[1]; nodes[nav.num_nodes].origin[2] = ent->mins[2] + (AI_JUMPABLE_HEIGHT - 1); nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL ); //put into ents table nav.ents[nav.num_ents].ent = ent; nav.ents[nav.num_ents].node = nav.num_nodes; nav.num_ents++; // link lower to upper AI_AddLink( nav.num_nodes, nav.num_nodes-1, LINK_PLATFORM ); //next nav.num_nodes++; return nav.num_nodes-1; }
//========================================== // AI_AddNode_Teleporter // Drop two nodes, one at trigger and other // at target entity //========================================== int AI_AddNode_Teleporter( edict_t *ent ) { vec3_t v1,v2; edict_t *dest; if (nav.num_nodes + 1 > MAX_NODES) return INVALID; dest = G_Find ( NULL, FOFS(targetname), ent->target ); if (!dest) return INVALID; //NODE_TELEPORTER_IN nodes[nav.num_nodes].flags = (NODEFLAGS_TELEPORTER_IN|NODEFLAGS_SERVERLINK); VectorCopy( ent->maxs, v1 ); VectorCopy( ent->mins, v2 ); nodes[nav.num_nodes].origin[0] = (v1[0] - v2[0]) / 2 + v2[0]; nodes[nav.num_nodes].origin[1] = (v1[1] - v2[1]) / 2 + v2[1]; nodes[nav.num_nodes].origin[2] = ent->mins[2]+32; nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, ent ); nav.num_nodes++; //NODE_TELEPORTER_OUT nodes[nav.num_nodes].flags = (NODEFLAGS_TELEPORTER_OUT|NODEFLAGS_SERVERLINK); VectorCopy( dest->s.origin, nodes[nav.num_nodes].origin ); if ( ent->spawnflags & 1 ) // droptofloor nodes[nav.num_nodes].flags |= NODEFLAGS_FLOAT; else AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL ); nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, ent ); // link from teleport_in AI_AddLink( nav.num_nodes-1, nav.num_nodes, LINK_TELEPORT ); nav.num_nodes++; return nav.num_nodes -1; }
/* * AI_AddNode_Teleporter * Drop two nodes, one at trigger and other * at target entity */ static int AI_AddNode_Teleporter( edict_t *ent ) { vec3_t v1, v2; edict_t *dest; if( nav.num_nodes + 1 > MAX_NODES ) return NODE_INVALID; dest = G_Find( NULL, FOFS( targetname ), ent->target ); if( !dest ) return NODE_INVALID; // NODE_TELEPORTER_IN nodes[nav.num_nodes].flags = ( NODEFLAGS_TELEPORTER_IN|NODEFLAGS_SERVERLINK|NODEFLAGS_REACHATTOUCH ); if( !strcmp( ent->classname, "misc_teleporter" ) ) //jabot092(2) { nodes[nav.num_nodes].origin[0] = ent->s.origin[0]; nodes[nav.num_nodes].origin[1] = ent->s.origin[1]; nodes[nav.num_nodes].origin[2] = ent->s.origin[2]+16; } else { VectorCopy( ent->r.maxs, v1 ); VectorCopy( ent->r.mins, v2 ); nodes[nav.num_nodes].origin[0] = ( v1[0] - v2[0] ) / 2 + v2[0]; nodes[nav.num_nodes].origin[1] = ( v1[1] - v2[1] ) / 2 + v2[1]; nodes[nav.num_nodes].origin[2] = ent->r.mins[2]+32; } nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, ent ); #ifdef SHOW_JUMPAD_GUESS AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, PATH_AMMO_BOX_MODEL ); #endif //put into ents table nav.navigableEnts[nav.num_navigableEnts].ent = ent; nav.navigableEnts[nav.num_navigableEnts].node = nav.num_nodes; nav.num_navigableEnts++; nav.num_nodes++; //NODE_TELEPORTER_OUT nodes[nav.num_nodes].flags = ( NODEFLAGS_TELEPORTER_OUT|NODEFLAGS_SERVERLINK ); VectorCopy( dest->s.origin, nodes[nav.num_nodes].origin ); if( ent->spawnflags & 1 ) // droptofloor nodes[nav.num_nodes].flags |= NODEFLAGS_FLOAT; else AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL ); nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, ent ); #ifdef SHOW_JUMPAD_GUESS AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, PATH_AMMO_BOX_MODEL ); #endif //put into ents table nav.navigableEnts[nav.num_navigableEnts].ent = ent; nav.navigableEnts[nav.num_navigableEnts].node = nav.num_nodes; nav.num_navigableEnts++; // link from teleport_in AI_AddLink( nav.num_nodes-1, nav.num_nodes, LINK_TELEPORT ); nav.num_nodes++; return nav.num_nodes -1; }
/* * AI_AddNode_Platform * drop two nodes one at top, one at bottom */ static int AI_AddNode_Platform( edict_t *ent ) { float plat_dist; float platlip; int candidate; vec3_t lorg; if( nav.num_nodes + 2 > MAX_NODES ) return NODE_INVALID; if( ent->flags & FL_TEAMSLAVE ) return NODE_INVALID; // only team master will drop the nodes plat_dist = ent->moveinfo.start_origin[2] - ent->moveinfo.end_origin[2]; platlip = ( ent->r.maxs[2] - ent->r.mins[2] ) - plat_dist; //make a guess on lower plat position candidate = AI_AddNode_Platform_FindLowerLinkableCandidate( ent ); if( candidate != NODE_INVALID ) { //base lower on cadidate's height lorg[0] = ( ent->r.maxs[0] - ent->r.mins[0] ) / 2 + ent->r.mins[0]; lorg[1] = ( ent->r.maxs[1] - ent->r.mins[1] ) / 2 + ent->r.mins[1]; lorg[2] = nodes[candidate].origin[2]; } else { lorg[0] = ( ent->r.maxs[0] - ent->r.mins[0] ) / 2 + ent->r.mins[0]; lorg[1] = ( ent->r.maxs[1] - ent->r.mins[1] ) / 2 + ent->r.mins[1]; lorg[2] = ent->r.mins[2] + platlip + 16; } // Upper node nodes[nav.num_nodes].flags = ( NODEFLAGS_PLATFORM|NODEFLAGS_SERVERLINK|NODEFLAGS_FLOAT ); nodes[nav.num_nodes].origin[0] = lorg[0]; nodes[nav.num_nodes].origin[1] = lorg[1]; nodes[nav.num_nodes].origin[2] = lorg[2] + plat_dist; #ifdef SHOW_JUMPAD_GUESS AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, PATH_AMMO_BOX_MODEL ); #endif nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL ); //put into ents table nav.navigableEnts[nav.num_navigableEnts].ent = ent; nav.navigableEnts[nav.num_navigableEnts].node = nav.num_nodes; nav.num_navigableEnts++; nav.num_nodes++; // Lower node nodes[nav.num_nodes].flags = ( NODEFLAGS_PLATFORM|NODEFLAGS_SERVERLINK|NODEFLAGS_FLOAT ); nodes[nav.num_nodes].origin[0] = lorg[0]; nodes[nav.num_nodes].origin[1] = lorg[1]; nodes[nav.num_nodes].origin[2] = lorg[2] + 16; #ifdef SHOW_JUMPAD_GUESS AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, PATH_AMMO_BOX_MODEL ); #endif nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL ); //put into ents table nav.navigableEnts[nav.num_navigableEnts].ent = ent; nav.navigableEnts[nav.num_navigableEnts].node = nav.num_nodes; nav.num_navigableEnts++; // link lower to upper AI_AddLink( nav.num_nodes, nav.num_nodes-1, LINK_PLATFORM ); //next nav.num_nodes++; return nav.num_nodes-1; }
/* * AI_AddNode_Door * Drop a node at each side of the door * and force them to link. Only typical * doors are covered. */ static int AI_AddNode_Door( edict_t *ent ) { edict_t *other; vec3_t mins, maxs; vec3_t door_origin, movedir, moveangles; vec3_t moveaxis[3]; vec3_t MOVEDIR_UP = { 0, 0, 1 }; float nodeOffset = NODE_DENSITY * 0.75f; int i, j; int dropped[4]; if( ent->flags & FL_TEAMSLAVE ) return NODE_INVALID; // only team master will drop the nodes for( i = 0; i < 4; i++ ) dropped[i] = NODE_INVALID; //make box formed by all team members boxes VectorCopy( ent->r.absmin, mins ); VectorCopy( ent->r.absmax, maxs ); for( other = ent->teamchain; other; other = other->teamchain ) { AddPointToBounds( other->r.absmin, mins, maxs ); AddPointToBounds( other->r.absmax, mins, maxs ); } for( i = 0; i < 3; i++ ) door_origin[i] = ( maxs[i] + mins[i] ) * 0.5; VectorSubtract( ent->moveinfo.end_origin, ent->moveinfo.start_origin, movedir ); VectorNormalizeFast( movedir ); VecToAngles( movedir, moveangles ); AnglesToAxis( moveangles, moveaxis ); //add nodes in "side" direction nodes[nav.num_nodes].flags = 0; VectorMA( door_origin, nodeOffset, moveaxis[1], nodes[nav.num_nodes].origin ); #ifdef SHOW_JUMPAD_GUESS AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, PATH_AMMO_BOX_MODEL ); #endif if( AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL ) ) { nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL ); dropped[0] = nav.num_nodes; nav.num_nodes++; } nodes[nav.num_nodes].flags = 0; VectorMA( door_origin, -nodeOffset, moveaxis[1], nodes[nav.num_nodes].origin ); #ifdef SHOW_JUMPAD_GUESS AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, PATH_AMMO_BOX_MODEL ); #endif if( AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL ) ) { nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL ); dropped[1] = nav.num_nodes; nav.num_nodes++; } // if moving in the Y axis drop also in the other crossing direction and hope the // bad ones are inhibited by a solid if( DotProduct( MOVEDIR_UP, moveaxis[0] ) > 0.8 || DotProduct( MOVEDIR_UP, moveaxis[0] ) < -0.8 ) { nodes[nav.num_nodes].flags = 0; VectorMA( door_origin, nodeOffset, moveaxis[2], nodes[nav.num_nodes].origin ); #ifdef SHOW_JUMPAD_GUESS AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, PATH_AMMO_BOX_MODEL ); #endif if( AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL ) ) { nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL ); dropped[2] = nav.num_nodes; nav.num_nodes++; } nodes[nav.num_nodes].flags = 0; VectorMA( door_origin, -nodeOffset, moveaxis[2], nodes[nav.num_nodes].origin ); #ifdef SHOW_JUMPAD_GUESS AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, PATH_AMMO_BOX_MODEL ); #endif if( AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL ) ) { nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL ); dropped[3] = nav.num_nodes; nav.num_nodes++; } } // link those we dropped for( i = 0; i < 4; i++ ) { if( dropped[i] == NODE_INVALID ) continue; //put into ents table nav.navigableEnts[nav.num_navigableEnts].ent = ent; nav.navigableEnts[nav.num_navigableEnts].node = dropped[i]; nav.num_navigableEnts++; for( j = 0; j < 4; j++ ) { if( dropped[j] == NODE_INVALID ) continue; AI_AddLink( dropped[i], dropped[j], LINK_MOVE|LINK_DOOR ); } } return nav.num_nodes-1; }