void NAV_CheckCalcPaths( void ) { if ( navCalcPathTime && navCalcPathTime < level.time ) {//first time we've ever loaded this map... //clear all the failed edges navigator.ClearAllFailedEdges(); //Calculate all paths NAV_CalculatePaths( level.mapname, giMapChecksum ); navigator.CalculatePaths(); #ifndef FINAL_BUILD if ( fatalErrors ) { gi.Printf( S_COLOR_RED"Not saving .nav file due to fatal nav errors\n" ); } else #endif if ( navigator.Save( level.mapname, giMapChecksum ) == qfalse ) { gi.Printf("Unable to save navigations data for map \"%s\" (checksum:%d)\n", level.mapname, giMapChecksum ); } navCalcPathTime = 0; } }
int AI_ClosestGroupEntityNumToPoint( AIGroupInfo_t &group, vec3_t point ) { int markerWP = WAYPOINT_NONE; int cost, bestCost = Q3_INFINITE; int closest = ENTITYNUM_NONE; if ( &group == NULL || group.numGroup <= 0 ) { return ENTITYNUM_NONE; } markerWP = NAV_FindClosestWaypointForPoint( &g_entities[group.member[0].number], point ); if ( markerWP == WAYPOINT_NONE ) { return ENTITYNUM_NONE; } for ( int i = 0; i < group.numGroup; i++ ) { cost = navigator.GetPathCost( group.member[i].waypoint, markerWP ); if ( cost < bestCost ) { bestCost = cost; closest = group.member[i].number; } } return closest; }
void NPC_BSSearchStart( int homeWp, bState_t bState ) { //FIXME: Reimplement NPCInfo->homeWp = homeWp; NPCInfo->tempBehavior = bState; NPCInfo->aiFlags |= NPCAI_ENROUTE_TO_HOMEWP; NPCInfo->investigateDebounceTime = 0; navigator.GetNodePosition( homeWp, NPCInfo->tempGoal->currentOrigin ); NPCInfo->tempGoal->waypoint = homeWp; //gi.Printf("\nHeading for wp %d...\n", NPCInfo->homeWp); }
qboolean NAVNEW_TestNodeConnectionBlocked( int wp1, int wp2, gentity_t *ignoreEnt, int goalEntNum, qboolean checkWorld, qboolean checkEnts ) {//see if the direct path between 2 nodes is blocked by architecture or an ent vec3_t pos1, pos2, mins, maxs; trace_t trace; int clipmask = MASK_NPCSOLID|CONTENTS_BOTCLIP; int ignoreEntNum; if ( !checkWorld && !checkEnts ) {//duh, nothing to trace against return qfalse; } navigator.GetNodePosition( wp1, pos1 ); navigator.GetNodePosition( wp2, pos2 ); if ( !checkWorld ) { clipmask &= ~(CONTENTS_SOLID|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP); } if ( !checkEnts ) { clipmask &= ~CONTENTS_BODY; } if ( ignoreEnt ) { VectorCopy( ignoreEnt->mins, mins ); VectorCopy( ignoreEnt->maxs, maxs ); ignoreEntNum = ignoreEnt->s.number; } else { VectorCopy( playerMins, mins ); VectorCopy( playerMaxs, maxs ); ignoreEntNum = ENTITYNUM_NONE; } mins[2] += STEPSIZE; //don't let box get inverted if ( mins[2] > maxs[2] ) { mins[2] = maxs[2]; } gi.trace( &trace, pos1, mins, maxs, pos2, ignoreEntNum, clipmask, G2_NOCOLLIDE, 0 ); if ( trace.fraction >= 1.0f || trace.entityNum == goalEntNum ) {//clear or hit goal return qfalse; } //hit something we weren't supposed to return qtrue; }
void NPC_BSSearch (void) { NPC_CheckEnemy(qtrue, qfalse); //Look for enemies, if find one: if ( NPC->enemy ) { if( NPCInfo->tempBehavior == BS_SEARCH ) {//if tempbehavior, set tempbehavior to default NPCInfo->tempBehavior = BS_DEFAULT; } else {//if bState, change to run and shoot NPCInfo->behaviorState = BS_HUNT_AND_KILL; NPC_BSRunAndShoot(); } return; } //FIXME: what if our goalEntity is not NULL and NOT our tempGoal - they must //want us to do something else? If tempBehavior, just default, else set //to run and shoot...? //FIXME: Reimplement if ( !NPCInfo->investigateDebounceTime ) {//On our way to a tempGoal float minGoalReachedDistSquared = 32*32; vec3_t vec; //Keep moving toward our tempGoal NPCInfo->goalEntity = NPCInfo->tempGoal; VectorSubtract ( NPCInfo->tempGoal->currentOrigin, NPC->currentOrigin, vec); if ( vec[2] < 24 ) { vec[2] = 0; } if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE ) { /* //FIXME: can't get the radius... float wpRadSq = waypoints[NPCInfo->tempGoal->waypoint].radius * waypoints[NPCInfo->tempGoal->waypoint].radius; if ( minGoalReachedDistSquared > wpRadSq ) { minGoalReachedDistSquared = wpRadSq; } */ minGoalReachedDistSquared = 32*32;//12*12; } if ( VectorLengthSquared( vec ) < minGoalReachedDistSquared ) { //Close enough, just got there NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE ); if ( ( NPCInfo->homeWp == WAYPOINT_NONE ) || ( NPC->waypoint == WAYPOINT_NONE ) ) { //Heading for or at an invalid waypoint, get out of this bState if( NPCInfo->tempBehavior == BS_SEARCH ) {//if tempbehavior, set tempbehavior to default NPCInfo->tempBehavior = BS_DEFAULT; } else {//if bState, change to stand guard NPCInfo->behaviorState = BS_STAND_GUARD; NPC_BSRunAndShoot(); } return; } if ( NPC->waypoint == NPCInfo->homeWp ) { //Just Reached our homeWp, if this is the first time, run your lostenemyscript if ( NPCInfo->aiFlags & NPCAI_ENROUTE_TO_HOMEWP ) { NPCInfo->aiFlags &= ~NPCAI_ENROUTE_TO_HOMEWP; G_ActivateBehavior( NPC, BSET_LOSTENEMY ); } } //gi.Printf("Got there.\n"); //gi.Printf("Looking..."); if( !Q_irand(0, 1) ) { NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_LOOKAROUND1, SETANIM_FLAG_NORMAL); } else { NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_IDLE1, SETANIM_FLAG_NORMAL); } NPCInfo->investigateDebounceTime = level.time + Q_irand(3000, 10000); } else { NPC_MoveToGoal( qtrue ); } } else { //We're there if ( NPCInfo->investigateDebounceTime > level.time ) { //Still waiting around for a bit //Turn angles every now and then to look around if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE ) { if ( !Q_irand( 0, 30 ) ) { int numEdges = navigator.GetNodeNumEdges( NPCInfo->tempGoal->waypoint ); if ( numEdges != WAYPOINT_NONE ) { int branchNum = Q_irand( 0, numEdges - 1 ); vec3_t branchPos, lookDir; int nextWp = navigator.GetNodeEdge( NPCInfo->tempGoal->waypoint, branchNum ); navigator.GetNodePosition( nextWp, branchPos ); VectorSubtract( branchPos, NPCInfo->tempGoal->currentOrigin, lookDir ); NPCInfo->desiredYaw = AngleNormalize360( vectoyaw( lookDir ) + Q_flrand( -45, 45 ) ); } //pick an angle +-45 degrees off of the dir of a random branch //from NPCInfo->tempGoal->waypoint //int branch = Q_irand( 0, (waypoints[NPCInfo->tempGoal->waypoint].numNeighbors - 1) ); //int nextWp = waypoints[NPCInfo->tempGoal->waypoint].nextWaypoint[branch][NPCInfo->stats.moveType]; //vec3_t lookDir; //VectorSubtract( waypoints[nextWp].origin, NPCInfo->tempGoal->currentOrigin, lookDir ); //Look in that direction +- 45 degrees //NPCInfo->desiredYaw = AngleNormalize360( vectoyaw( lookDir ) + Q_flrand( -45, 45 ) ); } } //gi.Printf("."); } else {//Just finished waiting NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE ); if ( NPC->waypoint == NPCInfo->homeWp ) { int numEdges = navigator.GetNodeNumEdges( NPCInfo->tempGoal->waypoint ); if ( numEdges != WAYPOINT_NONE ) { int branchNum = Q_irand( 0, numEdges - 1 ); int nextWp = navigator.GetNodeEdge( NPCInfo->homeWp, branchNum ); navigator.GetNodePosition( nextWp, NPCInfo->tempGoal->currentOrigin ); NPCInfo->tempGoal->waypoint = nextWp; } /* //Pick a random branch int branch = Q_irand( 0, (waypoints[NPCInfo->homeWp].numNeighbors - 1) ); int nextWp = waypoints[NPCInfo->homeWp].nextWaypoint[branch][NPCInfo->stats.moveType]; VectorCopy( waypoints[nextWp].origin, NPCInfo->tempGoal->currentOrigin ); NPCInfo->tempGoal->waypoint = nextWp; //gi.Printf("\nHeading for wp %d...\n", waypoints[NPCInfo->homeWp].nextWaypoint[branch][NPCInfo->stats.moveType]); */ } else {//At a branch, so return home navigator.GetNodePosition( NPCInfo->homeWp, NPCInfo->tempGoal->currentOrigin ); NPCInfo->tempGoal->waypoint = NPCInfo->homeWp; /* VectorCopy( waypoints[NPCInfo->homeWp].origin, NPCInfo->tempGoal->currentOrigin ); NPCInfo->tempGoal->waypoint = NPCInfo->homeWp; //gi.Printf("\nHeading for wp %d...\n", NPCInfo->homeWp); */ } NPCInfo->investigateDebounceTime = 0; //Start moving toward our tempGoal NPCInfo->goalEntity = NPCInfo->tempGoal; NPC_MoveToGoal( qtrue ); } } NPC_UpdateAngles( qtrue, qtrue ); }
void NPC_BSFlee( void ) {//FIXME: keep checking for danger if ( TIMER_Done( NPC, "flee" ) && NPCInfo->tempBehavior == BS_FLEE ) { NPCInfo->tempBehavior = BS_DEFAULT; NPCInfo->squadState = SQUAD_IDLE; //FIXME: should we set some timer to make him stay in this spot for a bit, //so he doesn't just suddenly turn around and come back at the enemy? //OR, just stop running toward goal for last second or so of flee? } if ( NPC_CheckSurrender() ) { return; } gentity_t *goal = NPCInfo->goalEntity; if ( !goal ) { goal = NPCInfo->lastGoalEntity; if ( !goal ) {//???!!! goal = NPCInfo->tempGoal; } } if ( goal ) { qboolean reverseCourse = qtrue; //FIXME: if no weapon, find one and run to pick it up? //Let's try to find a waypoint that gets me away from this thing if ( NPC->waypoint == WAYPOINT_NONE ) { NPC->waypoint = NAV_GetNearestNode( NPC, NPC->lastWaypoint ); } if ( NPC->waypoint != WAYPOINT_NONE ) { int numEdges = navigator.GetNodeNumEdges( NPC->waypoint ); if ( numEdges != WAYPOINT_NONE ) { vec3_t dangerDir; int nextWp; VectorSubtract( NPCInfo->investigateGoal, NPC->currentOrigin, dangerDir ); VectorNormalize( dangerDir ); for ( int branchNum = 0; branchNum < numEdges; branchNum++ ) { vec3_t branchPos, runDir; nextWp = navigator.GetNodeEdge( NPC->waypoint, branchNum ); navigator.GetNodePosition( nextWp, branchPos ); VectorSubtract( branchPos, NPC->currentOrigin, runDir ); VectorNormalize( runDir ); if ( DotProduct( runDir, dangerDir ) > Q_flrand( 0, 0.5 ) ) {//don't run toward danger continue; } //FIXME: don't want to ping-pong back and forth NPC_SetMoveGoal( NPC, branchPos, 0, qtrue ); reverseCourse = qfalse; break; } } } qboolean moved = NPC_MoveToGoal( qfalse );//qtrue? (do try to move straight to (away from) goal) if ( NPC->s.weapon == WP_NONE && (moved == qfalse || reverseCourse) ) {//No weapon and no escape route... Just cower? Need anim. NPC_Surrender(); NPC_UpdateAngles( qtrue, qtrue ); return; } //If our move failed, then just run straight away from our goal //FIXME: We really shouldn't do this. if ( moved == qfalse ) { vec3_t dir; float dist; if ( reverseCourse ) { VectorSubtract( NPC->currentOrigin, goal->currentOrigin, dir ); } else { VectorSubtract( goal->currentOrigin, NPC->currentOrigin, dir ); } NPCInfo->distToGoal = dist = VectorNormalize( dir ); NPCInfo->desiredYaw = vectoyaw( dir ); NPCInfo->desiredPitch = 0; ucmd.forwardmove = 127; } else if ( reverseCourse ) { //ucmd.forwardmove *= -1; //ucmd.rightmove *= -1; //VectorScale( NPC->client->ps.moveDir, -1, NPC->client->ps.moveDir ); NPCInfo->desiredYaw *= -1; } //FIXME: can stop after a safe distance? ucmd.upmove = 0; ucmd.buttons &= ~BUTTON_WALKING; //FIXME: what do we do once we've gotten to our goal? } NPC_UpdateAngles( qtrue, qtrue ); NPC_CheckGetNewWeapon(); }
void NPC_BSWander (void) {//FIXME: don't actually go all the way to the next waypoint, just move in fits and jerks...? if ( !NPCInfo->investigateDebounceTime ) {//Starting out float minGoalReachedDistSquared = 64;//32*32; vec3_t vec; //Keep moving toward our tempGoal NPCInfo->goalEntity = NPCInfo->tempGoal; VectorSubtract ( NPCInfo->tempGoal->currentOrigin, NPC->currentOrigin, vec); if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE ) { minGoalReachedDistSquared = 64; } if ( VectorLengthSquared( vec ) < minGoalReachedDistSquared ) { //Close enough, just got there NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE ); if( !Q_irand(0, 1) ) { NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_LOOKAROUND1, SETANIM_FLAG_NORMAL); } else { NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_IDLE1, SETANIM_FLAG_NORMAL); } //Just got here, so Look around for a while NPCInfo->investigateDebounceTime = level.time + Q_irand(3000, 10000); } else { //Keep moving toward goal NPC_MoveToGoal( qtrue ); } } else { //We're there if ( NPCInfo->investigateDebounceTime > level.time ) { //Still waiting around for a bit //Turn angles every now and then to look around if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE ) { if ( !Q_irand( 0, 30 ) ) { int numEdges = navigator.GetNodeNumEdges( NPCInfo->tempGoal->waypoint ); if ( numEdges != WAYPOINT_NONE ) { int branchNum = Q_irand( 0, numEdges - 1 ); vec3_t branchPos, lookDir; int nextWp = navigator.GetNodeEdge( NPCInfo->tempGoal->waypoint, branchNum ); navigator.GetNodePosition( nextWp, branchPos ); VectorSubtract( branchPos, NPCInfo->tempGoal->currentOrigin, lookDir ); NPCInfo->desiredYaw = AngleNormalize360( vectoyaw( lookDir ) + Q_flrand( -45, 45 ) ); } } } } else {//Just finished waiting NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE ); if ( NPC->waypoint != WAYPOINT_NONE ) { int numEdges = navigator.GetNodeNumEdges( NPC->waypoint ); if ( numEdges != WAYPOINT_NONE ) { int branchNum = Q_irand( 0, numEdges - 1 ); int nextWp = navigator.GetNodeEdge( NPC->waypoint, branchNum ); navigator.GetNodePosition( nextWp, NPCInfo->tempGoal->currentOrigin ); NPCInfo->tempGoal->waypoint = nextWp; } NPCInfo->investigateDebounceTime = 0; //Start moving toward our tempGoal NPCInfo->goalEntity = NPCInfo->tempGoal; NPC_MoveToGoal( qtrue ); } } } NPC_UpdateAngles( qtrue, qtrue ); }
void InitGame( const char *mapname, const char *spawntarget, int checkSum, const char *entities, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition ) { int i; giMapChecksum = checkSum; g_eSavedGameJustLoaded = eSavedGameJustLoaded; g_qbLoadTransition = qbLoadTransition; gi.Printf ("------- Game Initialization -------\n"); gi.Printf ("gamename: %s\n", GAMEVERSION); gi.Printf ("gamedate: %s\n", __DATE__); srand( randomSeed ); G_InitCvars(); G_InitMemory(); // set some level globals memset( &level, 0, sizeof( level ) ); level.time = levelTime; level.globalTime = globalTime; Q_strncpyz( level.mapname, mapname, sizeof(level.mapname) ); if ( spawntarget != NULL && spawntarget[0] ) { Q_strncpyz( level.spawntarget, spawntarget, sizeof(level.spawntarget) ); } else { level.spawntarget[0] = 0; } G_InitWorldSession(); // initialize all entities for this game memset( g_entities, 0, MAX_GENTITIES * sizeof(g_entities[0]) ); globals.gentities = g_entities; ClearAllInUse(); // initialize all clients for this game level.maxclients = 1; level.clients = (struct gclient_s *) G_Alloc( level.maxclients * sizeof(level.clients[0]) ); // set client fields on player g_entities[0].client = level.clients; // always leave room for the max number of clients, // even if they aren't all used, so numbers inside that // range are NEVER anything but clients globals.num_entities = MAX_CLIENTS; //Set up NPC init data NPC_InitGame(); TIMER_Clear(); // //ICARUS INIT START gi.Printf("------ ICARUS Initialization ------\n"); gi.Printf("ICARUS version : %1.2f\n", ICARUS_VERSION); Interface_Init( &interface_export ); ICARUS_Init(); gi.Printf ("-----------------------------------\n"); //ICARUS INIT END // IT_LoadItemParms (); ClearRegisteredItems(); //FIXME: if this is from a loadgame, it needs to be sure to write this out whenever you do a savegame since the edges and routes are dynamic... navCalculatePaths = ( navigator.Load( mapname, checkSum ) == qfalse ); // parse the key/value pairs and spawn gentities G_SpawnEntitiesFromString( entities ); // general initialization G_FindTeams(); // SaveRegisteredItems(); gi.Printf ("-----------------------------------\n"); //randomize the rand functions byte num_calls = (byte)timeGetTime(); for(i = 0; i < (int)num_calls; i++) { rand(); } if ( navCalculatePaths ) {//not loaded - need to calc paths navCalcPathTime = level.time + START_TIME_NAV_CALC;//make sure all ents are in and linked } else {//loaded //FIXME: if this is from a loadgame, it needs to be sure to write this //out whenever you do a savegame since the edges and routes are dynamic... //OR: always do a navigator.CheckBlockedEdges() on map startup after nav-load/calc-paths navigator.pathsCalculated = qtrue;//just to be safe? Does this get saved out? No... assumed //need to do this, because combatpoint waypoints aren't saved out...? CP_FindCombatPointWaypoints(); navCalcPathTime = 0; if ( g_eSavedGameJustLoaded == eNO ) {//clear all the failed edges unless we just loaded the game (which would include failed edges) navigator.ClearAllFailedEdges(); } } player = &g_entities[0]; //Init dynamic music level.dmState = DM_EXPLORE; level.dmDebounceTime = 0; level.dmBeatTime = 0; level.curAlertID = 1;//0 is default for lastAlertEvent, so... eventClearTime = 0; }
void G_RunFrame( int levelTime ) { int i; gentity_t *ent; int msec; int ents_inuse=0; // someone's gonna be pissed I put this here... #if AI_TIMERS AITime = 0; navTime = 0; #endif// AI_TIMERS level.framenum++; level.previousTime = level.time; level.time = levelTime; msec = level.time - level.previousTime; NAV_CheckCalcPaths(); //ResetTeamCounters(); AI_UpdateGroups(); if ( d_altRoutes->integer ) { navigator.CheckAllFailedEdges(); } navigator.ClearCheckedNodes(); //remember last waypoint, clear current one // for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++) for ( i = 0; i < globals.num_entities ; i++) { // if ( !ent->inuse ) // continue; if(!PInUse(i)) continue; ent = &g_entities[i]; if ( ent->waypoint != WAYPOINT_NONE && ent->noWaypointTime < level.time ) { ent->lastWaypoint = ent->waypoint; ent->waypoint = WAYPOINT_NONE; } if ( d_altRoutes->integer ) { navigator.CheckFailedNodes( ent ); } } //Look to clear out old events ClearPlayerAlertEvents(); //Run the frame for all entities // for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++) for ( i = 0; i < globals.num_entities ; i++) { // if ( !ent->inuse ) // continue; if(!PInUse(i)) continue; ents_inuse++; ent = &g_entities[i]; // clear events that are too old if ( level.time - ent->eventTime > EVENT_VALID_MSEC ) { if ( ent->s.event ) { ent->s.event = 0; // &= EV_EVENT_BITS; if ( ent->client ) { ent->client->ps.externalEvent = 0; } } if ( ent->freeAfterEvent ) { // tempEntities or dropped items completely go away after their event G_FreeEntity( ent ); continue; } else if ( ent->unlinkAfterEvent ) { // items that will respawn will hide themselves after their pickup event ent->unlinkAfterEvent = qfalse; gi.unlinkentity( ent ); } } // temporary entities don't think if ( ent->freeAfterEvent ) continue; G_CheckTasksCompleted(ent); G_Roff( ent ); if( !ent->client ) { if ( !(ent->svFlags & SVF_SELF_ANIMATING) ) {//FIXME: make sure this is done only for models with frames? //Or just flag as animating? if ( ent->s.eFlags & EF_ANIM_ONCE ) { ent->s.frame++; } else if ( !(ent->s.eFlags & EF_ANIM_ALLFAST) ) { G_Animate( ent ); } } } G_CheckSpecialPersistentEvents( ent ); if ( ent->s.eType == ET_MISSILE ) { G_RunMissile( ent ); continue; } if ( ent->s.eType == ET_ITEM ) { G_RunItem( ent ); continue; } if ( ent->s.eType == ET_MOVER ) { if ( ent->model && Q_stricmp( "models/test/mikeg/tie_fighter.md3", ent->model ) == 0 ) { TieFighterThink( ent ); } G_RunMover( ent ); continue; } //The player if ( i == 0 ) { // decay batteries if the goggles are active if ( cg.zoomMode == 1 && ent->client->ps.batteryCharge > 0 ) { ent->client->ps.batteryCharge--; } else if ( cg.zoomMode == 3 && ent->client->ps.batteryCharge > 0 ) { ent->client->ps.batteryCharge -= 2; if ( ent->client->ps.batteryCharge < 0 ) { ent->client->ps.batteryCharge = 0; } } G_CheckEndLevelTimers( ent ); //Recalculate the nearest waypoint for the coming NPC updates NAV_FindPlayerWaypoint(); if( ent->taskManager && !stop_icarus ) { ent->taskManager->Update(); } //dead if ( ent->health <= 0 ) { if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE ) {//on the ground pitch_roll_for_slope( ent, NULL ); } } continue; // players are ucmd driven } G_RunThink( ent ); // be aware that ent may be free after returning from here, at least one func frees them ClearNPCGlobals(); // but these 2 funcs are ok //UpdateTeamCounters( ent ); // to call anyway on a freed ent. } // perform final fixups on the player ent = &g_entities[0]; if ( ent->inuse ) { ClientEndFrame( ent ); } if( g_numEntities->integer ) { gi.Printf( S_COLOR_WHITE"Number of Entities in use : %d\n", ents_inuse ); } //DEBUG STUFF NAV_ShowDebugInfo(); NPC_ShowDebugInfo(); G_DynamicMusicUpdate(); #if AI_TIMERS AITime -= navTime; if ( AITime > 20 ) { gi.Printf( S_COLOR_RED"ERROR: total AI time: %d\n", AITime ); } else if ( AITime > 10 ) { gi.Printf( S_COLOR_YELLOW"WARNING: total AI time: %d\n", AITime ); } else if ( AITime > 2 ) { gi.Printf( S_COLOR_GREEN"total AI time: %d\n", AITime ); } if ( navTime > 20 ) { gi.Printf( S_COLOR_RED"ERROR: total nav time: %d\n", navTime ); } else if ( navTime > 10 ) { gi.Printf( S_COLOR_YELLOW"WARNING: total nav time: %d\n", navTime ); } else if ( navTime > 2 ) { gi.Printf( S_COLOR_GREEN"total nav time: %d\n", navTime ); } #endif// AI_TIMERS #ifndef FINAL_BUILD if ( delayedShutDown != 0 && delayedShutDown < level.time ) { G_Error( "Game Errors. Scroll up the console to read them.\n" ); } #endif #ifdef _DEBUG if(!(level.framenum&0xff)) { ValidateInUseBits(); } #endif }
/* ------------------------- NAVNEW_MoveToGoal ------------------------- */ int NAVNEW_MoveToGoal( gentity_t *self, navInfo_t &info ) { int bestNode = WAYPOINT_NONE; qboolean foundClearPath = qfalse; vec3_t origin; navInfo_t tempInfo; qboolean setBlockedInfo = qtrue; qboolean inGoalWP; int numTries = 0; memcpy( &tempInfo, &info, sizeof( tempInfo ) ); //Must have a goal entity to move there if( self->NPC->goalEntity == NULL ) return WAYPOINT_NONE; if ( self->waypoint == WAYPOINT_NONE && self->noWaypointTime > level.time ) {//didn't have a valid one in about the past second, don't look again just yet return WAYPOINT_NONE; } if ( self->NPC->goalEntity->waypoint == WAYPOINT_NONE && self->NPC->goalEntity->noWaypointTime > level.time ) {//didn't have a valid one in about the past second, don't look again just yet return WAYPOINT_NONE; } if ( self->noWaypointTime > level.time && self->NPC->goalEntity->noWaypointTime > level.time ) {//just use current waypoints bestNode = navigator.GetBestNodeAltRoute( self->waypoint, self->NPC->goalEntity->waypoint, bestNode ); } //FIXME!!!!: this is making them wiggle back and forth between waypoints else if ( (bestNode = navigator.GetBestPathBetweenEnts( self, self->NPC->goalEntity, NF_CLEAR_PATH )) == NODE_NONE )//!NAVNEW_GetWaypoints( self, qtrue ) ) {//one of us didn't have a valid waypoint! if ( self->waypoint == NODE_NONE ) {//don't even try to find one again for a bit self->noWaypointTime = level.time + Q_irand( 500, 1500 ); } if ( self->NPC->goalEntity->waypoint == NODE_NONE ) {//don't even try to find one again for a bit self->NPC->goalEntity->noWaypointTime = level.time + Q_irand( 500, 1500 ); } return WAYPOINT_NONE; } else { if ( self->NPC->goalEntity->noWaypointTime < level.time ) { self->NPC->goalEntity->noWaypointTime = level.time + Q_irand( 500, 1500 ); } } while( !foundClearPath ) { /*inBestWP = */inGoalWP = qfalse; /* bestNode = navigator.GetBestNodeAltRoute( self->waypoint, self->NPC->goalEntity->waypoint, bestNode ); */ if ( bestNode == WAYPOINT_NONE ) { goto failed; } //see if we can get directly to the next node off bestNode en route to goal's node... //NOTE: shouldn't be necc. now /* int oldBestNode = bestNode; bestNode = NAV_TestBestNode( self, self->waypoint, bestNode, qtrue );//, self->NPC->goalEntity->waypoint );// //NOTE: Guaranteed to return something if ( bestNode != oldBestNode ) {//we were blocked somehow if ( setBlockedInfo ) { self->NPC->aiFlags |= NPCAI_BLOCKED; navigator.GetNodePosition( oldBestNode, NPCInfo->blockedDest ); } } */ navigator.GetNodePosition( bestNode, origin ); /* if ( !goalWPFailed ) {//we haven't already tried to go straight to goal or goal's wp if ( bestNode == self->NPC->goalEntity->waypoint ) {//our bestNode is the goal's wp if ( NAV_HitNavGoal( self->currentOrigin, self->mins, self->maxs, origin, navigator.GetNodeRadius( bestNode ), FlyingCreature( self ) ) ) {//we're in the goal's wp inGoalWP = qtrue; //we're in the goalEntity's waypoint already //so head for the goalEntity since we know it's clear of architecture //FIXME: this is pretty stupid because the NPCs try to go straight // towards their goal before then even try macro_nav... VectorCopy( self->NPC->goalEntity->currentOrigin, origin ); } } } */ if ( !inGoalWP ) {//not heading straight for goal if ( bestNode == self->waypoint ) {//we know it's clear or architecture //navigator.GetNodePosition( self->waypoint, origin ); /* if ( NAV_HitNavGoal( self->currentOrigin, self->mins, self->maxs, origin, navigator.GetNodeRadius( bestNode ), FlyingCreature( self ) ) ) {//we're in the wp we're heading for already inBestWP = qtrue; } */ } else {//heading to an edge off our confirmed clear waypoint... make sure it's clear //it it's not, bestNode will fall back to our waypoint int oldBestNode = bestNode; bestNode = NAV_TestBestNode( self, self->waypoint, bestNode, qtrue ); if ( bestNode == self->waypoint ) {//we fell back to our waypoint, reset the origin self->NPC->aiFlags |= NPCAI_BLOCKED; navigator.GetNodePosition( oldBestNode, NPCInfo->blockedDest ); navigator.GetNodePosition( bestNode, origin ); } } } //Com_Printf( "goalwp = %d, mywp = %d, node = %d, origin = %s\n", self->NPC->goalEntity->waypoint, self->waypoint, bestNode, vtos(origin) ); memcpy( &tempInfo, &info, sizeof( tempInfo ) ); VectorSubtract( origin, self->currentOrigin, tempInfo.direction ); VectorNormalize( tempInfo.direction ); //NOTE: One very important thing NAVNEW_AvoidCollision does is // it actually CHANGES the value of "direction" - it changes it to // whatever dir you need to go in to avoid the obstacle... foundClearPath = NAVNEW_AvoidCollision( self, self->NPC->goalEntity, tempInfo, setBlockedInfo, 5 ); if ( !foundClearPath ) {//blocked by an ent if ( inGoalWP ) {//we were heading straight for the goal, head for the goal's wp instead navigator.GetNodePosition( bestNode, origin ); foundClearPath = NAVNEW_AvoidCollision( self, self->NPC->goalEntity, tempInfo, setBlockedInfo, 5 ); } } if ( foundClearPath ) {//clear! //If we got set to blocked, clear it NPC_ClearBlocked( self ); //Take the dir memcpy( &info, &tempInfo, sizeof( info ) ); if ( self->s.weapon == WP_SABER ) {//jedi if ( info.direction[2] * info.distance > 64 ) { self->NPC->aiFlags |= NPCAI_BLOCKED; VectorCopy( origin, NPCInfo->blockedDest ); goto failed; } } } else {//blocked by ent! if ( setBlockedInfo ) { self->NPC->aiFlags |= NPCAI_BLOCKED; navigator.GetNodePosition( bestNode, NPCInfo->blockedDest ); } //Only set blocked info first time setBlockedInfo = qfalse; if ( inGoalWP ) {//we headed for our goal and failed and our goal's WP and failed if ( self->waypoint == self->NPC->goalEntity->waypoint ) {//our waypoint is our goal's waypoint, nothing we can do //remember that this node is blocked navigator.AddFailedNode( self, self->waypoint ); goto failed; } else {//try going for our waypoint this time //goalWPFailed = qtrue; inGoalWP = qfalse; } } else if ( bestNode != self->waypoint ) {//we headed toward our next waypoint (instead of our waypoint) and failed if ( d_altRoutes->integer ) {//mark this edge failed and try our waypoint //NOTE: don't assume there is something blocking the direct path // between my waypoint and the bestNode... I could be off // that path because of collision avoidance... if ( d_patched->integer &&//use patch-style navigation ( !navigator.NodesAreNeighbors( self->waypoint, bestNode ) || NAVNEW_TestNodeConnectionBlocked( self->waypoint, bestNode, self, self->NPC->goalEntity->s.number, qfalse, qtrue ) ) ) {//the direct path between these 2 nodes is blocked by an ent navigator.AddFailedEdge( self->s.number, self->waypoint, bestNode ); } bestNode = self->waypoint; } else { //we should stop goto failed; } } else {//we headed for *our* waypoint and couldn't get to it if ( d_altRoutes->integer ) { //remember that this node is blocked navigator.AddFailedNode( self, self->waypoint ); //Now we should get our waypoints again //FIXME: cache the trace-data for subsequent calls as only the route info would have changed //if ( (bestNode = navigator.GetBestPathBetweenEnts( self, self->NPC->goalEntity, NF_CLEAR_PATH )) == NODE_NONE )//!NAVNEW_GetWaypoints( self, qfalse ) ) {//one of our waypoints is WAYPOINT_NONE now goto failed; } } else { //we should stop goto failed; } } if ( ++numTries >= 10 ) { goto failed; } } } //finish: //Draw any debug info, if requested if ( NAVDEBUG_showEnemyPath ) { vec3_t dest, start; //Get the positions navigator.GetNodePosition( self->NPC->goalEntity->waypoint, dest ); navigator.GetNodePosition( bestNode, start ); //Draw the route CG_DrawNode( start, NODE_START ); if ( bestNode != self->waypoint ) { vec3_t wpPos; navigator.GetNodePosition( self->waypoint, wpPos ); CG_DrawNode( wpPos, NODE_NAVGOAL ); } CG_DrawNode( dest, NODE_GOAL ); CG_DrawEdge( dest, self->NPC->goalEntity->currentOrigin, EDGE_PATH ); CG_DrawNode( self->NPC->goalEntity->currentOrigin, NODE_GOAL ); navigator.ShowPath( bestNode, self->NPC->goalEntity->waypoint ); } self->NPC->shoveCount = 0; //let me keep this waypoint for a while if ( self->noWaypointTime < level.time ) { self->noWaypointTime = level.time + Q_irand( 500, 1500 ); } return bestNode; failed: //FIXME: What we should really do here is have a list of the goal's and our // closest clearpath waypoints, ranked. If the first set fails, try the rest // until there are no alternatives. navigator.GetNodePosition( self->waypoint, origin ); //do this to avoid ping-ponging? return WAYPOINT_NONE; /* //this was causing ping-ponging if ( DistanceSquared( origin, self->currentOrigin ) < 16 )//woo, magic number {//We're right up on our waypoint, so that won't help, return none //Or maybe find the nextbest here? return WAYPOINT_NONE; } else {//Try going to our waypoint bestNode = self->waypoint; VectorSubtract( origin, self->currentOrigin, info.direction ); VectorNormalize( info.direction ); } goto finish; */ }
void AI_SortGroupByPathCostToEnemy( AIGroupInfo_t *group ) { AIGroupMember_t bestMembers[MAX_GROUP_MEMBERS]; int i, j, k; qboolean sort = qfalse; if ( group->enemy != NULL ) {//FIXME: just use enemy->waypoint? group->enemyWP = NAV_FindClosestWaypointForEnt( group->enemy, WAYPOINT_NONE ); } else { group->enemyWP = WAYPOINT_NONE; } for ( i = 0; i < group->numGroup; i++ ) { if ( group->enemyWP == WAYPOINT_NONE ) {//FIXME: just use member->waypoint? group->member[i].waypoint = WAYPOINT_NONE; group->member[i].pathCostToEnemy = Q3_INFINITE; } else {//FIXME: just use member->waypoint? group->member[i].waypoint = NAV_FindClosestWaypointForEnt( group->enemy, WAYPOINT_NONE ); if ( group->member[i].waypoint != WAYPOINT_NONE ) { group->member[i].pathCostToEnemy = navigator.GetPathCost( group->member[i].waypoint, group->enemyWP ); //at least one of us has a path, so do sorting sort = qtrue; } else { group->member[i].pathCostToEnemy = Q3_INFINITE; } } } //Now sort if ( sort ) { //initialize bestMembers data for ( j = 0; j < group->numGroup; j++ ) { bestMembers[j].number = ENTITYNUM_NONE; } for ( i = 0; i < group->numGroup; i++ ) { for ( j = 0; j < group->numGroup; j++ ) { if ( bestMembers[j].number != ENTITYNUM_NONE ) {//slot occupied if ( group->member[i].pathCostToEnemy < bestMembers[j].pathCostToEnemy ) {//this guy has a shorter path than the one currenly in this spot, bump him and put myself in here for ( k = group->numGroup; k > j; k++ ) { memcpy( &bestMembers[k], &bestMembers[k-1], sizeof( bestMembers[k] ) ); } memcpy( &bestMembers[j], &group->member[i], sizeof( bestMembers[j] ) ); break; } } else {//slot unoccupied, reached end of list, throw self in here memcpy( &bestMembers[j], &group->member[i], sizeof( bestMembers[j] ) ); break; } } } //Okay, now bestMembers is a sorted list, just copy it into group->members for ( i = 0; i < group->numGroup; i++ ) { memcpy( &group->member[i], &bestMembers[i], sizeof( group->member[i] ) ); } } }
BOOL CRVTrackerZoom::OnUpdate(const CUIEvent &cEvent) { // Only move during idle if (m_bIdleOnly && (cEvent.GetType() != UIEVENT_NONE)) return TRUE; int32 zDelta = GetWheelDelta(); if( zDelta == 0 ) return FALSE; BOOL bZoomToCursor = GetApp()->GetOptions().GetControlsOptions()->IsZoomToCursor(); //clear out the wheel delta SetWheelDelta( 0 ); CNavigator *pNav = &m_pView->Nav(); // Set the initial speed value float fSpeed = (m_bFast) ? 3.0f : 0.5f; //see if we are in a parallel viewport if( m_pView->IsParallelViewType() ) { //see if we want to zoom to the cursor if(bZoomToCursor) { // Convert cursor position to world coordinates: CVector vCursor; CRect rect; m_pView->GetWindowRect( &rect ); CPoint cursorPt = m_cCurPt; m_pView->GetVertexFromPoint( cursorPt, vCursor, FALSE ); // Zoom: m_pView->ViewDef()->m_Magnify += ((CReal)zDelta * m_pView->ViewDef()->m_Magnify) / 300.0f; if( m_pView->ViewDef()->m_Magnify < 0.0001f ) m_pView->ViewDef()->m_Magnify = 0.0001f; CVector vCursor2; m_pView->GetVertexFromPoint( cursorPt, vCursor2, FALSE ); // This effectively makes the cursor position the focal point for the zoom. pNav->Pos() += vCursor - vCursor2; } else { //we just want to zoom in m_pView->ViewDef()->m_Magnify += ((CReal)zDelta * m_pView->ViewDef()->m_Magnify) / 300.0f; if( m_pView->ViewDef()->m_Magnify < 0.0001f ) m_pView->ViewDef()->m_Magnify = 0.0001f; } } //Walk-through, meaning that we take a ray through the screen else { //we are in a perspective viewport. if(!bZoomToCursor) { pNav->Pos() += pNav->Forward() * (float)zDelta * fSpeed; } else { CEditRay ray = m_pView->ViewDef()->MakeRayFromScreenPoint( m_cCurPt ); CVector forward = ray.m_Dir; forward.Norm(); pNav->Pos() += forward * ((CReal)zDelta * fSpeed); } } m_pView->Invalidate(); return TRUE; }