HLDS_DLLEXPORT void SV_MoveToOrigin_I(edict_t *ent, const vec3_t pflGoal, float dist, int iMoveType) { vec3_t var_c_temp; var_c_temp[0] = pflGoal[0]; var_c_temp[1] = pflGoal[1]; var_c_temp[2] = pflGoal[2]; if(ent->v.flags & (FL_FLY | FL_SWIM | FL_ONGROUND)) { if(iMoveType == MOVE_NORMAL) { if(SV_StepDirection(ent, ent->v.ideal_yaw, dist) == 0) { SV_NewChaseDir2(ent, var_c_temp, dist); } } else { var_c_temp[0] -= ent->v.origin[0]; var_c_temp[1] -= ent->v.origin[1]; if(ent->v.flags & (FL_FLY | FL_SWIM)) { var_c_temp[2] -= ent->v.origin[2]; } else { //If on ground, I guess we just don't worry about the third axis. Sorta makes sense. var_c_temp[2] = 0; } VectorNormalize(var_c_temp); VectorScale(var_c_temp, dist, var_c_temp); SV_FlyDirection(ent, var_c_temp); } } }
void SV_MoveToOrigin( edict_t *ent, const vec3_t pflGoal, float dist, int iMoveType ) { vec3_t vecDist; VectorCopy( pflGoal, vecDist ); if( ent->v.flags & ( FL_FLY|FL_SWIM|FL_ONGROUND )) { if( iMoveType == MOVE_NORMAL ) { if( SV_StepDirection( ent, ent->v.ideal_yaw, dist ) == 0 ) { SV_NewChaseDir( ent, vecDist, dist ); } } else { vecDist[0] -= ent->v.origin[0]; vecDist[1] -= ent->v.origin[1]; if( ent->v.flags & ( FL_FLY|FL_SWIM )) vecDist[2] -= ent->v.origin[2]; else vecDist[2] = 0.0f; VectorNormalize( vecDist ); VectorScale( vecDist, dist, vecDist ); SV_FlyDirection( ent, vecDist ); } } }
void SV_NewChaseDir1 (edict_t *self, edict_t *goal, float dist) { int i, bestyaw, minyaw, maxyaw; vec3_t v; if (!goal) return; VectorSubtract(goal->s.origin, self->s.origin, v); bestyaw = vectoyaw(v); minyaw = bestyaw - 90; maxyaw = bestyaw + 90; if (minyaw < 0) minyaw += 360; if (maxyaw > 360) maxyaw -= 360; for (i=minyaw; i<maxyaw; i+=30) { if (SV_StepDirection(self, i, dist, false)) return; } //gi.dprintf("couldnt find a better direction!\n"); SV_NewChaseDir(self, goal, dist); }
/* ====================== SV_MoveToGoal ====================== */ void SV_MoveToGoal (void) { edict_t *ent, *goal; float dist; #ifdef QUAKE2 edict_t *enemy; #endif ent = PROG_TO_EDICT(pr_global_struct->self); goal = PROG_TO_EDICT(ent->v.goalentity); dist = G_FLOAT(OFS_PARM0); if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) { G_FLOAT(OFS_RETURN) = 0; return; } // if the next step hits the enemy, return immediately #ifdef QUAKE2 enemy = PROG_TO_EDICT(ent->v.enemy); if (enemy != sv.edicts && SV_CloseEnough (ent, enemy, dist) ) #else if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnough (ent, goal, dist) ) #endif return; // bump around... if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->v.ideal_yaw, dist)) { SV_NewChaseDir (ent, goal, dist); } }
// NOTE: pos should be the final goal, because monster will stop moving after reaching it! void M_MoveToPosition (edict_t *ent, vec3_t pos, float dist) { vec3_t v; // stay in-place for medic healing if (ent->holdtime > level.time) return; // need to be touching the ground if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)) && !ent->waterlevel) { //gi.dprintf("not touching ground\n"); return; } // we are close enough if (distance(ent->s.origin, pos) < 32) { //gi.dprintf("close enough %.0f\n", distance(ent->s.origin, pos)); // look at the final position VectorSubtract(pos, ent->s.origin, v); VectorNormalize(v); ent->ideal_yaw = vectoyaw(v); M_ChangeYaw(ent); return; } // dont move so fast in the water if (!(ent->flags & (FL_FLY|FL_SWIM)) && (ent->waterlevel > 1)) dist *= 0.5; // if we can't take a step, try moving in another direction if (!SV_StepDirection (ent, ent->ideal_yaw, dist, true)) { //gi.dprintf("couldn't step\n"); // if the monster hasn't moved much, then increment // the number of frames it has been stuck if (distance(ent->s.origin, ent->monsterinfo.stuck_org) < 64) ent->monsterinfo.stuck_frames++; else ent->monsterinfo.stuck_frames = 0; // record current position for comparison VectorCopy(ent->s.origin, ent->monsterinfo.stuck_org); // attempt a course-correction if (ent->inuse && (level.time > ent->monsterinfo.bump_delay)) { //gi.dprintf("tried course correction\n"); //SV_NewChaseDir (ent, goal, dist); SV_NewChaseDir2(ent, pos, dist); ent->monsterinfo.bump_delay = level.time + FRAMETIME*GetRandom(3, 9); return; } } //else //gi.dprintf("step OK\n"); }
qboolean CheckYawStep (edict_t *self, float minyaw, float maxyaw, float dist) { int i, max; float yaw; AngleCheck(&minyaw); AngleCheck(&maxyaw); // calculate the maximum yaw variance max = 360 - fabs(minyaw - maxyaw); // we will start at the minimum yaw angle and move towards maxyaw yaw = minyaw; for (i = 0; i < max; i += 30) { // if we changed course a while ago, then try a partial step 50% of the time if (level.time - self->monsterinfo.bump_delay > 1.0 || random() < 0.5) { yaw += i; AngleCheck(&yaw); if (SV_StepDirection(self, yaw, dist, true)) { //gi.dprintf("attempt small step\n"); return true; } } // otherwise, we might be stuck, so try a full step else { if (SV_StepDirection(self, i, dist, false)) { //gi.dprintf("attempt full step\n"); return true; } } } return false; }
/* ==================== M_MoveAwayFromFlare ==================== */ qboolean M_MoveAwayFromFlare(edict_t *self, float dist) { edict_t *e = NULL; edict_t *goal = NULL; vec3_t delta; vec3_t forward; // find the closest flare while(1) { e = findradius(e, self->s.origin, 256); if (e == NULL) break; if (Q_stricmp(e->classname, "flare") == 0) break; } goal = G_Spawn(); self->goalentity = goal; if (e == NULL) { // just move forward AngleVectors(self->s.angles, forward, NULL, NULL); VectorMA(self->s.origin, 128, forward, goal->s.origin); } else { VectorSubtract(self->s.origin, e->s.origin, delta); VectorNormalize(delta); VectorMA(self->s.origin, 128, delta, goal->s.origin); } if (rand() & (7 == 1)) { // set the ideal_yaw VectorSubtract(goal->s.origin, self->s.origin, delta); self->ideal_yaw = vectoyaw(delta); } if ( (rand()&3)==1 || !SV_StepDirection (self, self->ideal_yaw, dist)) { SV_NewChaseDir (self, goal, dist); } self->goalentity = NULL; G_FreeEdict(goal); return true; }
/* ====================== M_MoveToGoal ====================== */ void M_MoveToGoal(edict_t *ent, float dist){ edict_t *goal; goal = ent->goalentity; if(!ent->groundentity && !(ent->flags &(FL_FLY | FL_SWIM))) return; // if the next step hits the enemy, return immediately if(ent->enemy && SV_CloseEnough(ent, ent->enemy, dist)) return; // bump around... if((rand()&3) == 1 || !SV_StepDirection(ent, ent->ideal_yaw, dist)){ if(ent->inuse) SV_NewChaseDir(ent, goal, dist); } }
/* ====================== SV_MoveToGoal ====================== */ void SV_MoveToGoal (void) { edict_t *ent, *goal; float dist; ent = PROG_TO_EDICT(PR_GLOBAL_STRUCT(self)); // Entity moving goal = PROG_TO_EDICT(ent->v.goalentity); // its goalentity dist = G_FLOAT(OFS_PARM0); // how far to move // Reset trace_plane_normal VectorCopy(vec3_origin, PR_GLOBAL_STRUCT(trace_plane_normal)); // If not onground, flying, or swimming, return 0 if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) { G_FLOAT(OFS_RETURN) = 0; return; } // if the next step hits the enemy, return immediately if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnough (ent, goal, dist) ) { G_FLOAT(OFS_RETURN) = 0; return; } // bump around... if (!SV_StepDirection (ent, ent->v.ideal_yaw, dist))//If can't go in a direction (including step check) or 30% chance... { SV_NewChaseDir (ent, goal, dist);//Find a new direction to go in instead G_FLOAT(OFS_RETURN) = 0; } else { if ((rand()&3)==1) { SV_NewChaseDir (ent, goal, dist);//Find a new direction to go in instead } G_FLOAT(OFS_RETURN) = 1; } return; }
/* ====================== M_MoveToGoal ====================== */ void M_MoveToGoal (edict_t *ent, float dist) { edict_t *goal; goal = ent->goalentity; if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM))) return; // if the next step hits the enemy, return immediately if (ent->enemy && SV_CloseEnough (ent, ent->enemy, dist) ) return; // bump around... // if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->ideal_yaw, dist)) // PMM - charging monsters (AI_CHARGING) don't deflect unless they have to if ( (((rand()&3)==1) && !(ent->monsterinfo.aiflags & AI_CHARGING)) || !SV_StepDirection (ent, ent->ideal_yaw, dist)) { if (ent->inuse) SV_NewChaseDir (ent, goal, dist); } }
/* ====================== M_MoveToGoal ====================== */ void M_MoveToGoal (edict_t *ent, float dist) { edict_t *goal; goal = ent->goalentity; if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM))) return; // if the next step hits the enemy, return immediately if (ent->enemy && SV_CloseEnough (ent, ent->enemy, dist) ) return; if ( (((rand()&3)==1) && !(ent->monsterinfo.aiflags & AI_CHARGING)) || !SV_StepDirection (ent, ent->ideal_yaw, dist)) { if (ent->monsterinfo.aiflags & AI_BLOCKED) { ent->monsterinfo.aiflags &= ~AI_BLOCKED; return; } if (ent->inuse) SV_NewChaseDir (ent, goal, dist); } }
/* ============= ai_schoolWalk The monster is walking it's beat ============= */ void ai_schoolWalk (edict_t *self, float dist) { float speed; if(!(self->monsterinfo.aiflags & AI_SCHOOLING)) { ai_walk(self, dist); return; } // init school var's for this frame self->zRaduisList = NULL; if(self->enemy || FindTarget(self)) { ai_walk(self, dist); return; } else { // run schooling routines switch(zSchoolMonsters(self, dist, 1, &speed)) { case 0: self->monsterinfo.stand (self); break; case 2: self->monsterinfo.run (self); break; } } // do the normal walk stuff SV_StepDirection (self, self->ideal_yaw, dist); }
void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) { float deltax,deltay; float d[3]; float tdir, olddir, turnaround; olddir = anglemod( (int)(actor->v.ideal_yaw/45)*45 ); turnaround = anglemod(olddir - 180); deltax = enemy->v.origin[0] - actor->v.origin[0]; deltay = enemy->v.origin[1] - actor->v.origin[1]; if (deltax>10) d[1]= 0; else if (deltax<-10) d[1]= 180; else d[1]= DI_NODIR; if (deltay<-10) d[2]= 270; else if (deltay>10) d[2]= 90; else d[2]= DI_NODIR; // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { if (d[1] == 0) tdir = d[2] == 90 ? 45 : 315; else tdir = d[2] == 90 ? 135 : 215; if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) return; } // try other directions if ( ((rand()&3) & 1) || abs(deltay)>abs(deltax)) { tdir=d[1]; d[1]=d[2]; d[2]=tdir; } if (d[1]!=DI_NODIR && d[1]!=turnaround && SV_StepDirection(actor, d[1], dist)) return; if (d[2]!=DI_NODIR && d[2]!=turnaround && SV_StepDirection(actor, d[2], dist)) return; /* there is no direct path to the player, so pick another direction */ if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist)) return; if (rand()&1) /*randomly determine direction of search*/ { for (tdir=0 ; tdir<=315 ; tdir += 45) if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) return; } else { for (tdir=315 ; tdir >=0 ; tdir -= 45) if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) return; } if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) ) return; actor->v.ideal_yaw = olddir; // can't move // if a bridge was pulled out from underneath a monster, it may not have // a valid standing position at all if (!SV_CheckBottom (actor)) SV_FixCheckBottom (actor); }
void SV_NewChaseDir( edict_t *actor, vec3_t destination, float dist ) { float deltax, deltay; float tempdir, olddir, turnaround; vec3_t d; olddir = anglemod(((int)( actor->v.ideal_yaw / 45.0f )) * 45.0f ); turnaround = anglemod( olddir - 180 ); deltax = destination[0] - actor->v.origin[0]; deltay = destination[1] - actor->v.origin[1]; if( deltax > 10 ) d[1] = 0.0f; else if( deltax < -10 ) d[1] = 180.0f; else d[1] = -1; if( deltay < -10 ) d[2] = 270.0f; else if( deltay > 10 ) d[2] = 90.0f; else d[2] = -1; // try direct route if( d[1] != -1 && d[2] != -1 ) { if( d[1] == 0.0f ) tempdir = ( d[2] == 90.0f ) ? 45.0f : 315.0f; else tempdir = ( d[2] == 90.0f ) ? 135.0f : 215.0f; if( tempdir != turnaround && SV_StepDirection( actor, tempdir, dist )) return; } // try other directions if( Com_RandomLong( 0, 1 ) != 0 || fabs( deltay ) > fabs( deltax )) { tempdir = d[1]; d[1] = d[2]; d[2] = tempdir; } if( d[1] != -1 && d[1] != turnaround && SV_StepDirection( actor, d[1], dist )) return; if( d[2] != -1 && d[2] != turnaround && SV_StepDirection( actor, d[2], dist )) return; // there is no direct path to the player, so pick another direction if( olddir != -1 && SV_StepDirection( actor, olddir, dist )) return; // fine, just run somewhere. if( Com_RandomLong( 0, 1 ) != 1 ) { for( tempdir = 0; tempdir <= 315; tempdir += 45 ) { if( tempdir != turnaround && SV_StepDirection( actor, tempdir, dist )) return; } } else { for( tempdir = 315; tempdir >= 0; tempdir -= 45 ) { if( tempdir != turnaround && SV_StepDirection( actor, tempdir, dist )) return; } } // we tried. run backwards. that ought to work... if( turnaround != -1 && SV_StepDirection( actor, turnaround, dist )) return; // well, we're stuck somehow. actor->v.ideal_yaw = olddir; // if a bridge was pulled out from underneath a monster, it may not have // a valid standing position at all. if( !SV_CheckBottom( actor, MOVE_NORMAL )) { actor->v.flags |= FL_PARTIALGROUND; } }
void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) { float deltax,deltay; float d[3]; float tdir, olddir, turnaround; //FIXME: how did we get here with no enemy if (!enemy) return; if(actor->flags & FL_ROBOT) olddir = anglemod( (int)(actor->ideal_yaw+0.5) ); else olddir = anglemod( (int)(actor->ideal_yaw/45)*45 ); turnaround = anglemod(olddir - 180); deltax = enemy->s.origin[0] - actor->s.origin[0]; deltay = enemy->s.origin[1] - actor->s.origin[1]; if(actor->flags & FL_ROBOT) { d[1] = d[2] = olddir; } else { if (deltax>10) d[1]= 0; else if (deltax<-10) d[1]= 180; else d[1]= DI_NODIR; if (deltay<-10) d[2]= 270; else if (deltay>10) d[2]= 90; else d[2]= DI_NODIR; } // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { if(actor->flags & FL_ROBOT) tdir = d[1]; else { if (d[1] == 0) tdir = d[2] == 90 ? 45 : 315; else tdir = d[2] == 90 ? 135 : 215; } if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) return; } // Robots give up if direct path doesn't work if(actor->flags & FL_ROBOT) { actor->ideal_yaw = olddir; // can't move if (!M_CheckBottom (actor)) SV_FixCheckBottom (actor); } // try other directions if ( ((rand()&3) & 1) || abs(deltay)>abs(deltax)) { tdir=d[1]; d[1]=d[2]; d[2]=tdir; } if (d[1]!=DI_NODIR && d[1]!=turnaround && SV_StepDirection(actor, d[1], dist)) return; if (d[2]!=DI_NODIR && d[2]!=turnaround && SV_StepDirection(actor, d[2], dist)) return; if (actor->inuse && (actor->health > 0) && actor->monsterinfo.blocked) { if (actor->monsterinfo.blocked(actor, dist)) return; } /* there is no direct path to the player, so pick another direction */ if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist)) return; if (rand()&1) /*randomly determine direction of search*/ { for (tdir=0 ; tdir<=315 ; tdir += 45) if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) return; } else { for (tdir=315 ; tdir >=0 ; tdir -= 45) if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) return; } if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) ) return; actor->ideal_yaw = olddir; // can't move // if a bridge was pulled out from underneath a monster, it may not have // a valid standing position at all if (!M_CheckBottom (actor)) SV_FixCheckBottom (actor); }
void SV_NewChaseDir2(edict_t *actor, vec3_t destination, float dist) { float deltax, deltay; float tempdir, olddir, turnaround; vec3_t d; olddir = anglemod(((int)(actor->v.ideal_yaw / 45.0)) * 45.0); //So, we're shaving down some of the precision. Ohkay. turnaround = anglemod(olddir - 180); deltax = destination[0] - actor->v.origin[0]; deltay = destination[1] - actor->v.origin[1]; if(deltax > 10) { d[1]= 0; } else if(deltax < -10) { d[1]= 180; } else { d[1] = -1; } //DI_NODIR, so in this function, -1 is a reserved 'null' like value. if(deltay < -10) { d[2] = 270; } else if(deltay > 10) { d[2] = 90; } else { d[2] = -1; } // try direct route if(d[1] != -1 && d[2] != -1) { if(d[1] == 0) { if(d[2] == 90) { tempdir = 45; } else { tempdir = 315; } } else { if(d[2] == 90) { tempdir = 135; } else { tempdir = 215; } } if(tempdir != turnaround && SV_StepDirection(actor, tempdir, dist) != 0) { return; } } // try other directions if(RandomLong(0, 1) != 0 || fabs(deltay) > fabs(deltax)) { //These were originally cast as int before compared. I cannot think of any compelling reason to do so. tempdir = d[1]; d[1] = d[2]; d[2] = tempdir; } if(d[1] != -1 && d[1] != turnaround && SV_StepDirection(actor, d[1], dist) != 0) { return; } if(d[2] != -1 && d[2] != turnaround && SV_StepDirection(actor, d[2], dist) != 0) { return; } /* there is no direct path to the player, so pick another direction */ if(olddir != -1 && SV_StepDirection(actor, olddir, dist) != 0) { return; } //Fine, just run somewhere. if(RandomLong(0, 1) != 0) { for(tempdir = 0; tempdir <= 315; tempdir += 45) { if(tempdir != turnaround && SV_StepDirection(actor, tempdir, dist) != 0) { return; } } } else { for(tempdir = 315; tempdir >= 0; tempdir -= 45) { if(tempdir != turnaround && SV_StepDirection(actor, tempdir, dist) != 0) { return; } } } //We tried. Run backwards. THAT ought to work... if(turnaround != -1 && SV_StepDirection(actor, turnaround, dist) != 0) { return; } //Well, we're stuck somehow. actor->v.ideal_yaw = olddir; // if a bridge was pulled out from underneath a monster, it may not have // a valid standing position at all. if(SV_CheckBottom(actor) == 0) { SV_FixCheckBottom(actor); } }
/* ====================== M_MoveToGoal ====================== */ void M_MoveToGoal (edict_t *ent, float dist) { edict_t *goal; goal = ent->goalentity; if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM))) return; // Lazarus range checks if (!(ent->monsterinfo.aiflags & (AI_CHASE_THING | AI_CHICKEN))) { if (ent->enemy && (ent->monsterinfo.min_range > 0) && ((goal==ent->enemy) || !goal) ) { float dist; dist = realrange(ent,ent->enemy); if(dist < ent->monsterinfo.min_range) { ent->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_RANGE_PAUSE); ent->monsterinfo.rangetime = level.time + 0.5; ent->monsterinfo.stand(ent); return; } } if ((ent->enemy) && (level.time > ent->monsterinfo.rangetime + 0.5) && ((goal==ent->enemy) || !goal) ) { float dist; dist = realrange(ent,ent->enemy); if((dist < ent->monsterinfo.ideal_range[0]) && (rand() & 3)) { ent->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_RANGE_PAUSE); ent->monsterinfo.rangetime = level.time + 1.0; ent->monsterinfo.stand(ent); return; } if((dist < ent->monsterinfo.ideal_range[1]) && (dist > ent->monsterinfo.ideal_range[0]) && (rand() & 1)) { ent->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_RANGE_PAUSE); ent->monsterinfo.rangetime = level.time + 0.2; ent->monsterinfo.stand(ent); return; } } } if( (ent->monsterinfo.aiflags & AI_FOLLOW_LEADER) && (ent->movetarget) && (ent->movetarget->inuse) && (ent->movetarget->health > 0) ) { if(ent->enemy) ent->monsterinfo.currentmove = &actor_move_run; else { float R; R = realrange(ent,ent->movetarget); if(R > ACTOR_FOLLOW_RUN_RANGE) ent->monsterinfo.currentmove = &actor_move_run; else if(R < ACTOR_FOLLOW_STAND_RANGE && ent->movetarget->client) { ent->monsterinfo.pausetime = level.time + 0.5; ent->monsterinfo.currentmove = &actor_move_stand; return; } else ent->monsterinfo.currentmove = &actor_move_walk; } } // If the next step hits the enemy, return immediately. Don't do this for // AI_CHASE_THING, since we want monster to actually touch or pass through // "thing" if (ent->enemy && !(ent->monsterinfo.aiflags & AI_CHASE_THING) && SV_CloseEnough (ent, ent->enemy, dist) ) return; // bump around... if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->ideal_yaw, dist)) { if (ent->inuse) SV_NewChaseDir (ent, goal, dist); } }
/* ====================== M_MoveToGoal ====================== */ void M_MoveToGoal (edict_t *ent, float dist) { edict_t *goal; goal = ent->goalentity; if (ent->holdtime > level.time) // stay in-place for medic healing return; if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)) && !ent->waterlevel) { //gi.dprintf("not touching ground\n"); return; } // if the next step hits the enemy, return immediately if (ent->enemy && (ent->enemy->solid == SOLID_BBOX) && SV_CloseEnough (ent, ent->enemy, dist) ) //GHz START { vec3_t v; // we need to keep turning to avoid getting stuck doing nothing if (ent->goalentity && ent->goalentity->inuse) // 3.89 make sure the monster still has a goal! { VectorSubtract(ent->goalentity->s.origin, ent->s.origin, v); VectorNormalize(v); ent->ideal_yaw = vectoyaw(v); M_ChangeYaw(ent); } return; } //GHz END // dont move so fast in the water if (!(ent->flags & (FL_FLY|FL_SWIM)) && (ent->waterlevel > 1)) dist *= 0.5; // bump around... // if we can't take a step, try moving in another direction if (!SV_StepDirection (ent, ent->ideal_yaw, dist, true)) { //gi.dprintf("couldnt step\n"); // if the monster hasn't moved much, then increment // the number of frames it has been stuck if (distance(ent->s.origin, ent->monsterinfo.stuck_org) < 64) ent->monsterinfo.stuck_frames++; else ent->monsterinfo.stuck_frames = 0; // record current position for comparison VectorCopy(ent->s.origin, ent->monsterinfo.stuck_org); // attempt a course-correction if (ent->inuse && (level.time > ent->monsterinfo.bump_delay)) { //gi.dprintf("tried course correction %s\n", ent->goalentity?"true":"false"); SV_NewChaseDir (ent, goal, dist); ent->monsterinfo.bump_delay = level.time + FRAMETIME*GetRandom(2, 5); return; } } }