//========================================== // AI_MoveToGoalEntity // Set bot to move to it's movetarget. Short range goals //========================================== qboolean AI_MoveToGoalEntity(edict_t *self, usercmd_t *ucmd) { if (!self->movetarget || !self->client) return false; // If a rocket or grenade is around deal with it // Simple, but effective (could be rewritten to be more accurate) if(!Q_stricmp(self->movetarget->classname,"rocket") || !Q_stricmp(self->movetarget->classname,"grenade") || !Q_stricmp(self->movetarget->classname,"hgrenade")) { VectorSubtract (self->movetarget->s.origin, self->s.origin, self->ai.move_vector); AI_ChangeAngle(self); // if(AIDevel.debugChased && bot_showcombat->value) // G_PrintMsg (AIDevel.chaseguy, PRINT_HIGH, "%s: Oh crap a rocket!\n",self->ai.pers.netname); // strafe left/right if(rand()%1 && AI_CanMove(self, BOT_MOVE_LEFT)) ucmd->sidemove = -400; else if(AI_CanMove(self, BOT_MOVE_RIGHT)) ucmd->sidemove = 400; return true; } // Set bot's movement direction VectorSubtract (self->movetarget->s.origin, self->s.origin, self->ai.move_vector); AI_ChangeAngle(self); if(!AI_CanMove(self, BOT_MOVE_FORWARD) ) { self->movetarget = NULL; ucmd->forwardmove = -400; return false; } //Move ucmd->forwardmove = 400; return true; }
//========================================== // BOT_DMclass_CombatMovement // // NOTE: Very simple for now, just a basic move about avoidance. // Change this routine for more advanced attack movement. //========================================== void BOT_DMclass_CombatMovement( edict_t *self, usercmd_t *ucmd ) { float c; float dist; bool rocket = false; vec3_t away_from_rocket = { 0, 0, 0 }; if( !self->enemy || self->ai->rush_item ) { BOT_DMclass_Move( self, ucmd ); return; } if( self->ai->pers.skillLevel >= 0.25f ) rocket = BOT_DMclass_FindRocket( self, away_from_rocket ); dist = DistanceFast( self->s.origin, self->enemy->s.origin ); c = random(); if( level.time > self->ai->combatmovepush_timeout ) { bool canMOVELEFT, canMOVERIGHT, canMOVEFRONT, canMOVEBACK; canMOVELEFT = AI_CanMove( self, BOT_MOVE_LEFT ); canMOVERIGHT = AI_CanMove( self, BOT_MOVE_RIGHT ); canMOVEFRONT = AI_CanMove( self, BOT_MOVE_FORWARD ); canMOVEBACK = AI_CanMove( self, BOT_MOVE_BACK ); self->ai->combatmovepush_timeout = level.time + AI_COMBATMOVE_TIMEOUT; VectorClear( self->ai->combatmovepushes ); if( rocket ) { //VectorScale(away_from_rocket,1,self->ai->combatmovepushes); if( away_from_rocket[0] ) { if( ( away_from_rocket[0] < 0 ) && canMOVEBACK ) self->ai->combatmovepushes[0] = -1; else if( ( away_from_rocket[0] > 0 ) && canMOVEFRONT ) self->ai->combatmovepushes[0] = 1; } if( away_from_rocket[1] ) { if( ( away_from_rocket[1] < 0 ) && canMOVELEFT ) self->ai->combatmovepushes[1] = -1; else if( ( away_from_rocket[1] > 0 ) && canMOVERIGHT ) self->ai->combatmovepushes[1] = 1; } ucmd->buttons |= BUTTON_SPECIAL; } else if( dist < 150 ) // range = AIWEAP_MELEE_RANGE; { if( self->s.weapon == WEAP_GUNBLADE ) // go into him! { ucmd->buttons &= ~BUTTON_ATTACK; // remove pressing fire if( canMOVEFRONT ) // move to your enemy self->ai->combatmovepushes[0] = 1; else if( c <= 0.5 && canMOVELEFT ) self->ai->combatmovepushes[1] = -1; else if( canMOVERIGHT ) self->ai->combatmovepushes[1] = 1; } else { //priorize sides if( canMOVELEFT || canMOVERIGHT ) { if( canMOVELEFT && canMOVERIGHT ) { self->ai->combatmovepushes[1] = c < 0.5 ? -1 : 1; } else if( canMOVELEFT ) { self->ai->combatmovepushes[1] = -1; } else { self->ai->combatmovepushes[1] = 1; } } if( c < 0.3 && canMOVEBACK ) self->ai->combatmovepushes[0] = -1; } } else if( dist < 500 ) //AIWEAP_SHORT_RANGE limit is Grenade Laucher range { if( canMOVELEFT || canMOVERIGHT ) { if( canMOVELEFT && canMOVERIGHT ) { self->ai->combatmovepushes[1] = c < 0.5 ? -1 : 1; } else if( canMOVELEFT ) { self->ai->combatmovepushes[1] = -1; } else { self->ai->combatmovepushes[1] = 1; } } if( c < 0.3 && canMOVEFRONT ) { self->ai->combatmovepushes[0] = 1; } } else if( dist < 900 ) { if( canMOVELEFT || canMOVERIGHT ) { if( canMOVELEFT && canMOVERIGHT ) { self->ai->combatmovepushes[1] = c < 0.5 ? -1 : 1; } else if( canMOVELEFT ) { self->ai->combatmovepushes[1] = -1; } else { self->ai->combatmovepushes[1] = 1; } } } else //range = AIWEAP_LONG_RANGE; { if( c < 0.75 && ( canMOVELEFT || canMOVERIGHT ) ) { if( canMOVELEFT && canMOVERIGHT ) { self->ai->combatmovepushes[1] = c < 0.5 ? -1 : 1; } else if( canMOVELEFT ) { self->ai->combatmovepushes[1] = -1; } else { self->ai->combatmovepushes[1] = 1; } } } } if( !rocket && ( self->health < 25 || ( dist >= 500 && c < 0.2 ) || ( dist >= 1000 && c < 0.5 ) ) ) { BOT_DMclass_Move( self, ucmd ); } if( !self->ai->camp_item ) { ucmd->forwardmove = self->ai->combatmovepushes[0]; } ucmd->sidemove = self->ai->combatmovepushes[1]; ucmd->upmove = self->ai->combatmovepushes[2]; }
void BOT_DMclass_MoveWander( edict_t *self, usercmd_t *ucmd ) { vec3_t temp; if( self->deadflag ) return; if( !self->r.client->ps.pmove.stats[PM_STAT_MAXSPEED] ) { return; } // Special check for elevators, stand still until the ride comes to a complete stop. if( self->groundentity && self->groundentity->use == Use_Plat ) { if( self->groundentity->moveinfo.state != STATE_UP && self->groundentity->moveinfo.state != STATE_DOWN ) { self->velocity[0] = 0; self->velocity[1] = 0; self->velocity[2] = 0; return; } } // Move To Goal (Short Range Goal, not following paths) if( !AI_MoveToShortRangeGoalEntity( self, ucmd ) ) { // Swimming? VectorCopy( self->s.origin, temp ); temp[2] += 24; if( G_PointContents( temp ) & MASK_WATER ) { // If drowning and no node, move up if( self->r.client && self->r.client->resp.next_drown_time > 0 ) { ucmd->upmove = 1; self->s.angles[PITCH] = -45; } else ucmd->upmove = 1; ucmd->forwardmove = 1; } // else self->r.client->next_drown_time = 0; // probably shound not be messing with this, but // Lava? temp[2] -= 48; if( G_PointContents( temp ) & ( CONTENTS_LAVA|CONTENTS_SLIME ) ) { self->s.angles[YAW] += random() * 360 - 180; ucmd->forwardmove = 1; if( self->groundentity ) ucmd->upmove = 1; else ucmd->upmove = 0; return; } // Check for special movement if( VectorLengthFast( self->velocity ) < 37 ) { if( random() > 0.1 && AI_SpecialMove( self, ucmd ) ) //jumps, crouches, turns... return; self->s.angles[YAW] += random() * 180 - 90; if( !self->is_step ) // if there is ground continue otherwise wait for next move ucmd->forwardmove = 0; //0 else if( AI_CanMove( self, BOT_MOVE_FORWARD ) ) { ucmd->forwardmove = 1; ucmd->buttons |= BUTTON_WALK; } return; } // Otherwise move slowly, walking wondering what's going on ucmd->buttons |= BUTTON_WALK; } if( AI_CanMove( self, BOT_MOVE_FORWARD ) ) ucmd->forwardmove = 1; else ucmd->forwardmove = -1; }
bool AI_FindPath( monsterCharacter_t* monster ) { node_t *startNode; node_t *bestNode; node_t newNode; node_t *actualNode; int i; monster->ai.searchNumber = 0; if( monster->ai.curPos[0] == monster->ai.goalPos[0] && monster->ai.curPos[1] == monster->ai.goalPos[1] ) return( 1 ); if( monster->ai.goalPos[0] == monster->ai.path[0][0] && monster->ai.goalPos[1] == monster->ai.path[0][1] ) return( 1 ); if( monster->ai.isGoal ) { if( monster->ai.totalSearch == 0 ) { startNode = AI_GetNode( monster, monster->ai.curPos ); startNode->onClosed = false; startNode->onOpen = true; startNode->parent = NULL; startNode->cost = 0; startNode->total = AI_CostFromNodeToNode( monster->ai.curPos, monster->ai.goalPos ) * monster->ai.heruristic; monster->ai.pathQueue.goal[0] = monster->ai.goalPos[0]; monster->ai.pathQueue.goal[1] = monster->ai.goalPos[1]; AI_PushPriorityQueue( monster->ai.pathQueue.open, startNode ); monster->ai.totalSearch = 0; } } while( !AI_IsPriorityQueueEmpty( monster->ai.pathQueue.open ) ) { if( monster->ai.searchNumber > NUMBER_OF_PATH_SEARCH ) { monster->ai.totalSearch += monster->ai.searchNumber; if( monster->ai.totalSearch > MAX_NUMBER_OF_PATH_SEARCH ) return( false ); monster->ai.isGoal = false; return( true ); } bestNode = AI_PopPriorityQueue( monster->ai.pathQueue.open ); if( bestNode->pos[0] == monster->ai.pathQueue.goal[0] && bestNode->pos[1] == monster->ai.pathQueue.goal[1] ) { AI_PushPriorityQueue( monster->ai.pathQueue.open, bestNode ); AI_MakePath( monster ); monster->ai.isGoal = true; return( true ); } for( i = 0; i < 8; i ++ ) { switch( i ) { case 0: newNode.pos[0] = bestNode->pos[0] - 1; newNode.pos[1] = bestNode->pos[1]; break; case 1: newNode.pos[0] = bestNode->pos[0] - 1; newNode.pos[1] = bestNode->pos[1] + 1; break; case 2: newNode.pos[0] = bestNode->pos[0]; newNode.pos[1] = bestNode->pos[1] + 1; break; case 3: newNode.pos[0] = bestNode->pos[0] + 1; newNode.pos[1] = bestNode->pos[1] + 1; break; case 4: newNode.pos[0] = bestNode->pos[0] + 1; newNode.pos[1] = bestNode->pos[1]; break; case 5: newNode.pos[0] = bestNode->pos[0] + 1; newNode.pos[1] = bestNode->pos[1] - 1; break; case 6: newNode.pos[0] = bestNode->pos[0]; newNode.pos[1] = bestNode->pos[1] - 1; break; case 7: newNode.pos[0] = bestNode->pos[0] - 1; newNode.pos[1] = bestNode->pos[1] - 1; break; } if( !AI_CanMove( monster, newNode.pos, i ) ) continue; if( bestNode->parent == NULL || \ bestNode->parent->pos[0] != newNode.pos[0] || \ bestNode->parent->pos[1] != newNode.pos[1] ) { newNode.parent = bestNode; newNode.cost = bestNode->cost + AI_CostFromNodeToNode( newNode.pos, bestNode->pos ); newNode.total = newNode.cost + AI_CostFromNodeToNode( newNode.pos, monster->ai.pathQueue.goal ) * monster->ai.heruristic; actualNode = AI_GetNode( monster, newNode.pos ); if( actualNode == NULL ) return( false ); if( !( actualNode->onOpen && newNode.total > actualNode->total ) && \ !( actualNode->onClosed && newNode.total > actualNode->total ) ) { actualNode->onClosed = false; actualNode->parent = newNode.parent; actualNode->cost = newNode.cost; actualNode->total = newNode.total; if( actualNode->onOpen ) { AI_UpdateNodeOnPriorityQueue( monster->ai.pathQueue.open, actualNode ); } else { AI_PushPriorityQueue( monster->ai.pathQueue.open, actualNode ); actualNode->onOpen = true; } monster->ai.searchNumber ++; } } } bestNode->onClosed = true; } return( false ); }