/* * AI_TouchedEntity * Some AI has touched some entity. Some entities are declared to never be reached until touched. * See if it's one of them and declare it reached */ void AI_TouchedEntity( edict_t *self, edict_t *ent ) { int i; nav_ents_t *goalEnt; // right now we only support this on a few trigger entities (jumpads, teleporters) if( ent->r.solid != SOLID_TRIGGER && ent->item == NULL ) return; // clear short range goal, pick a new goal ASAP if( ent == self->movetarget ) { self->movetarget = NULL; self->ai->shortRangeGoalTimeout = level.time; } if( self->ai->goalEnt && ent == self->ai->goalEnt->ent ) { if( nav.debugMode && bot_showlrgoal->integer > 1 ) G_PrintChasersf( self, "REACHED entity %s\n", ent->classname ? ent->classname : "no classname" ); AI_ClearGoal( self ); return; } if( self->ai->next_node != NODE_INVALID && ( nodes[self->ai->next_node].flags & (NODEFLAGS_REACHATTOUCH|NODEFLAGS_ENTITYREACH) ) ) { for( i = 0; i < nav.num_navigableEnts; i++ ) { if( nav.navigableEnts[i].node == self->ai->next_node && nav.navigableEnts[i].ent == ent ) { if( nav.debugMode && bot_showlrgoal->integer > 1 ) G_PrintChasersf( self, "REACHED touch node %i with entity %s\n", self->ai->next_node, ent->classname ? ent->classname : "no classname" ); AI_NodeReached( self ); return; } } FOREACH_GOALENT( goalEnt ) { i = goalEnt->id; if( goalEnt->node == self->ai->next_node && goalEnt->ent == ent ) { if( nav.debugMode && bot_showlrgoal->integer > 1 ) G_PrintChasersf( self, "REACHED touch node %i with entity %s\n", self->ai->next_node, ent->classname ? ent->classname : "no classname" ); AI_NodeReached( self ); return; } } } }
bool AI_NodeReached_Generic( edict_t *self ) { bool reached = false; float RADIUS = NODE_REACH_RADIUS; if( !( AI_GetNodeFlags( self->ai->next_node ) & (NODEFLAGS_REACHATTOUCH|NODEFLAGS_ENTITYREACH) ) ) { if( self->ai->path.numNodes >= MIN_BUNNY_NODES ) { int n1 = self->ai->path.nodes[self->ai->path.numNodes]; int n2 = self->ai->path.nodes[self->ai->path.numNodes-1]; vec3_t n1origin, n2origin, origin; // if falling from a jump pad use a taller cylinder if( !self->groundentity && !self->is_step && !self->is_swim && ( AI_CurrentLinkType( self ) & LINK_JUMPPAD ) ) RADIUS = NODE_WIDE_REACH_RADIUS; // we use a wider radius in 2D, and a height range enough so they can't be jumped over AI_GetNodeOrigin( n1, n1origin ); AI_GetNodeOrigin( n2, n2origin ); VectorCopy( self->s.origin, origin ); n1origin[2] = n2origin[2] = origin[2] = 0; // see if reached the second if( n2 != NODE_INVALID && ( ( nodes[n2].origin[2] - 16 ) < self->s.origin[2] ) && ( nodes[n2].origin[2] + RADIUS > self->s.origin[2] ) && ( DistanceFast( n2origin, origin ) < RADIUS ) ) { AI_NodeReached( self ); // advance the first reached = true; // return the second as reached } // see if reached the first else if( ( ( nodes[n1].origin[2] - 16 ) < self->s.origin[2] ) && ( nodes[n1].origin[2] + RADIUS > self->s.origin[2] ) && ( DistanceFast( n1origin, origin ) < RADIUS ) ) { reached = true; // return the first as reached } } else { reached = ( DistanceFast( self->s.origin, nodes[self->ai->next_node].origin ) < RADIUS ) ? true : false; } } return reached; }
bool AI_NodeReached_Special( edict_t *self ) { bool reached = false; if( self->ai->next_node != NODE_INVALID && !( AI_GetNodeFlags( self->ai->next_node ) & (NODEFLAGS_REACHATTOUCH|NODEFLAGS_ENTITYREACH) ) ) { if( self->ai->path.numNodes >= MIN_BUNNY_NODES ) { int n1 = self->ai->path.nodes[self->ai->path.numNodes]; int n2 = self->ai->path.nodes[self->ai->path.numNodes-1]; vec3_t n1origin, n2origin, origin; // we use a wider radius in 2D, and a height range enough so they can't be jumped over AI_GetNodeOrigin( n1, n1origin ); AI_GetNodeOrigin( n2, n2origin ); VectorCopy( self->s.origin, origin ); n1origin[2] = n2origin[2] = origin[2] = 0; // see if reached the second if( ( ( nodes[n2].origin[2] - 16 ) < self->s.origin[2] ) && ( nodes[n2].origin[2] + NODE_WIDE_REACH_RADIUS > self->s.origin[2] ) && ( DistanceFast( n2origin, origin ) < NODE_WIDE_REACH_RADIUS ) && AI_ReachabilityVisible( self, nodes[n2].origin ) ) { AI_NodeReached( self ); // advance the first reached = true; // return the second as reached } // see if reached the first else if( ( ( nodes[n1].origin[2] - 16 ) < self->s.origin[2] ) && ( nodes[n1].origin[2] + NODE_WIDE_REACH_RADIUS > self->s.origin[2] ) && ( DistanceFast( n1origin, origin ) < NODE_WIDE_REACH_RADIUS ) && AI_ReachabilityVisible( self, nodes[n1].origin ) ) { reached = true; // return the first as reached } } else return AI_NodeReached_Generic( self ); } return reached; }
void BOT_DMclass_Move( edict_t *self, usercmd_t *ucmd ) { #define BOT_FORWARD_EPSILON 0.5f int i; unsigned int linkType; bool printLink = false; bool nodeReached = false; bool specialMovement = false; vec3_t v1, v2; vec3_t lookdir, pathdir; float lookDot; if( self->ai->next_node == NODE_INVALID || self->ai->goal_node == NODE_INVALID ) { BOT_DMclass_MoveWander( self, ucmd ); return; } linkType = AI_CurrentLinkType( self ); specialMovement = ( self->ai->path.numNodes >= MIN_BUNNY_NODES ) ? true : false; if( AI_GetNodeFlags( self->ai->next_node ) & (NODEFLAGS_REACHATTOUCH|NODEFLAGS_ENTITYREACH) ) specialMovement = false; if( linkType & (LINK_JUMP|LINK_JUMPPAD|LINK_CROUCH|LINK_FALL|LINK_WATER|LINK_LADDER|LINK_ROCKETJUMP) ) specialMovement = false; if( self->ai->pers.skillLevel < 0.33f ) specialMovement = false; if( specialMovement == false || self->groundentity ) self->ai->is_bunnyhop = false; VectorSubtract( nodes[self->ai->next_node].origin, self->s.origin, self->ai->move_vector ); // 2D, normalized versions of look and path directions pathdir[0] = self->ai->move_vector[0]; pathdir[1] = self->ai->move_vector[1]; pathdir[2] = 0.0f; VectorNormalize( pathdir ); AngleVectors( self->s.angles, lookdir, NULL, NULL ); lookdir[2] = 0.0f; VectorNormalize( lookdir ); lookDot = DotProduct( lookdir, pathdir ); // Ladder movement if( self->is_ladder ) { ucmd->forwardmove = 0; ucmd->upmove = 1; ucmd->sidemove = 0; if( nav.debugMode && printLink ) G_PrintChasersf( self, "LINK_LADDER\n" ); nodeReached = AI_NodeReached_Generic( self ); } else if( linkType & LINK_JUMPPAD ) { VectorCopy( self->s.origin, v1 ); VectorCopy( nodes[self->ai->next_node].origin, v2 ); v1[2] = v2[2] = 0; if( DistanceFast( v1, v2 ) > 32 && lookDot > BOT_FORWARD_EPSILON ) { ucmd->forwardmove = 1; // push towards destination ucmd->buttons |= BUTTON_WALK; } nodeReached = self->groundentity != NULL && AI_NodeReached_Generic( self ); } // Platform riding - No move, riding elevator else if( linkType & LINK_PLATFORM ) { VectorCopy( self->s.origin, v1 ); VectorCopy( nodes[self->ai->next_node].origin, v2 ); v1[2] = v2[2] = 0; if( DistanceFast( v1, v2 ) > 32 && lookDot > BOT_FORWARD_EPSILON ) ucmd->forwardmove = 1; // walk to center ucmd->buttons |= BUTTON_WALK; ucmd->upmove = 0; ucmd->sidemove = 0; if( nav.debugMode && printLink ) G_PrintChasersf( self, "LINK_PLATFORM (riding)\n" ); self->ai->move_vector[2] = 0; // put view horizontal nodeReached = AI_NodeReached_PlatformEnd( self ); } // entering platform else if( AI_GetNodeFlags( self->ai->next_node ) & NODEFLAGS_PLATFORM ) { ucmd->forwardmove = 1; ucmd->upmove = 0; ucmd->sidemove = 0; if( lookDot <= BOT_FORWARD_EPSILON ) ucmd->buttons |= BUTTON_WALK; if( nav.debugMode && printLink ) G_PrintChasersf( self, "NODEFLAGS_PLATFORM (moving to plat)\n" ); // is lift down? for( i = 0; i < nav.num_navigableEnts; i++ ) { if( nav.navigableEnts[i].node == self->ai->next_node ) { //testing line //vec3_t tPoint; //int j; //for(j=0; j<3; j++)//center of the ent // tPoint[j] = nav.ents[i].ent->s.origin[j] + 0.5*(nav.ents[i].ent->r.mins[j] + nav.ents[i].ent->r.maxs[j]); //tPoint[2] = nav.ents[i].ent->s.origin[2] + nav.ents[i].ent->r.maxs[2]; //tPoint[2] += 8; //AITools_DrawLine( self->s.origin, tPoint ); //if not reachable, wait for it (only height matters) if( ( nav.navigableEnts[i].ent->s.origin[2] + nav.navigableEnts[i].ent->r.maxs[2] ) > ( self->s.origin[2] + self->r.mins[2] + AI_JUMPABLE_HEIGHT ) && nav.navigableEnts[i].ent->moveinfo.state != STATE_BOTTOM ) { self->ai->blocked_timeout = level.time + 10000; ucmd->forwardmove = 0; } } } nodeReached = AI_NodeReached_PlatformStart( self ); } // Falling off ledge or jumping else if( !self->groundentity && !self->is_step && !self->is_swim && !self->ai->is_bunnyhop ) { ucmd->upmove = 0; ucmd->sidemove = 0; ucmd->forwardmove = 0; if( lookDot > BOT_FORWARD_EPSILON ) { ucmd->forwardmove = 1; // add fake strafe accel if( !(linkType & LINK_FALL) || linkType & (LINK_JUMP|LINK_ROCKETJUMP) ) { if( linkType & LINK_JUMP ) { if( AI_AttemptWalljump( self ) ) { ucmd->buttons |= BUTTON_SPECIAL; } if( VectorLengthFast( tv( self->velocity[0], self->velocity[1], 0 ) ) < 600 ) VectorMA( self->velocity, 6.0f, lookdir, self->velocity ); } else { if( VectorLengthFast( tv( self->velocity[0], self->velocity[1], 0 ) ) < 450 ) VectorMA( self->velocity, 1.0f, lookdir, self->velocity ); } } } else if( lookDot < -BOT_FORWARD_EPSILON ) ucmd->forwardmove = -1; if( nav.debugMode && printLink ) G_PrintChasersf( self, "FLY MOVE\n" ); nodeReached = AI_NodeReached_Generic( self ); } else // standard movement { ucmd->forwardmove = 1; ucmd->upmove = 0; ucmd->sidemove = 0; // starting a jump if( ( linkType & LINK_JUMP ) ) { if( self->groundentity ) { trace_t trace; vec3_t v1, v2; if( nav.debugMode && printLink ) G_PrintChasersf( self, "LINK_JUMP\n" ); //check floor in front, if there's none... Jump! VectorCopy( self->s.origin, v1 ); VectorNormalize2( self->ai->move_vector, v2 ); VectorMA( v1, 18, v2, v1 ); v1[2] += self->r.mins[2]; VectorCopy( v1, v2 ); v2[2] -= AI_JUMPABLE_HEIGHT; G_Trace( &trace, v1, vec3_origin, vec3_origin, v2, self, MASK_AISOLID ); if( !trace.startsolid && trace.fraction == 1.0 ) { //jump! // prevent double jumping on crates VectorCopy( self->s.origin, v1 ); v1[2] += self->r.mins[2]; G_Trace( &trace, v1, tv( -12, -12, -8 ), tv( 12, 12, 0 ), v1, self, MASK_AISOLID ); if( trace.startsolid ) ucmd->upmove = 1; } } nodeReached = AI_NodeReached_Generic( self ); } // starting a rocket jump else if( ( linkType & LINK_ROCKETJUMP ) ) { if( nav.debugMode && printLink ) G_PrintChasersf( self, "LINK_ROCKETJUMP\n" ); if( !self->ai->rj_triggered && self->groundentity && ( self->s.weapon == WEAP_ROCKETLAUNCHER ) ) { self->s.angles[PITCH] = 170; ucmd->upmove = 1; ucmd->buttons |= BUTTON_ATTACK; self->ai->rj_triggered = true; } nodeReached = AI_NodeReached_Generic( self ); } else { // Move To Short Range goal (not following paths) // plats, grapple, etc have higher priority than SR Goals, cause the bot will // drop from them and have to repeat the process from the beginning if( AI_MoveToShortRangeGoalEntity( self, ucmd ) ) { nodeReached = AI_NodeReached_Generic( self ); } else if( specialMovement && !self->is_swim ) // bunny-hopping movement here { BOT_DMclass_SpecialMove( self, lookdir, pathdir, ucmd ); nodeReached = AI_NodeReached_Special( self ); } else { nodeReached = AI_NodeReached_Generic( self ); } } // if static assume blocked and try to get free if( VectorLengthFast( self->velocity ) < 37 && ( ucmd->forwardmove || ucmd->sidemove || ucmd->upmove ) ) { if( random() > 0.1 && AI_SpecialMove( self, ucmd ) ) // jumps, crouches, turns... return; self->s.angles[YAW] += brandom( -90, 90 ); } } // swimming if( self->is_swim ) { if( !( G_PointContents( nodes[self->ai->next_node].origin ) & MASK_WATER ) ) // Exit water ucmd->upmove = 1; } AI_ChangeAngle( self ); if( nodeReached ) AI_NodeReached( self ); #undef BOT_FORWARD_EPSILON }