void FoundTarget (edict_t *self) { // let other monsters see this monster for a while if (self->enemy->r.client) { level.sight_entity = self; level.sight_entity_framenum = level.framenum; } self->show_hostile = level.time + 1000; // wake up other monsters VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); self->monsterinfo.trail_time = level.time; if (!self->combattarget) { HuntTarget (self); return; } self->goalentity = self->movetarget = G_PickTarget(self->combattarget); if (!self->movetarget) { self->goalentity = self->movetarget = self->enemy; HuntTarget (self); G_Printf ("%s at %s, combattarget %s not found\n", self->classname, vtos(self->s.origin), self->combattarget); return; } // clear out our combattarget, these are a one shot deal self->combattarget = NULL; self->monsterinfo.aiflags |= AI_COMBAT_POINT; // clear the targetname, that point is ours! self->movetarget->targetname = NULL; self->monsterinfo.pausetime = 0; // run for it self->monsterinfo.run (self); }
void FoundTarget() { if ( PROG_TO_EDICT( self->s.v.enemy )->ct == ctPlayer ) { // let other monsters see this monster for a while sight_entity = self; sight_entity_time = g_globalvars.time; } self->show_hostile = g_globalvars.time + 1; // wake up other monsters SightSound (); HuntTarget (); }
/* ===================== hintpath_stop Makes a monster stop following hint_paths. ===================== */ void hintpath_stop (edict_t *monster) { monster->movetarget = monster->goalentity = NULL; monster->monsterinfo.last_hint_time = level.time; monster->monsterinfo.goal_hint = NULL; monster->monsterinfo.aiflags &= ~AI_HINT_PATH; // If we don't have an enemy to get mad at, just stand around like an unactivated monster. if (!has_valid_enemy(monster)) { monster->enemy = NULL; monster->monsterinfo.pausetime = level.time + 100000000; monster->monsterinfo.stand (monster); } else if (visible(monster, monster->enemy)) // attack if we can see our foe FoundTarget (monster); else // keep pursuing HuntTarget (monster); }
void medic_StopPatrolling (edict_t *self) { self->goalentity = NULL; self->movetarget = NULL; self->monsterinfo.aiflags &= ~AI_MEDIC_PATROL; if (!(self->monsterinfo.aiflags & AI_MEDIC)) { if(medic_FindDeadMonster(self)) return; } if (has_valid_enemy(self)) { if (visible(self, self->enemy)) { FoundTarget (self); return; } HuntTarget (self); return; } if(self->monsterinfo.aiflags & AI_MEDIC) abortHeal(self,false); }
void thing_think (edict_t *self) { vec3_t vec; vec_t dist; edict_t *monster; monster = self->target_ent; if(level.time <= self->touch_debounce_time) { if(monster && monster->inuse) { if(monster->movetarget == self) { if(monster->health > 0) { VectorSubtract(monster->s.origin,self->s.origin,vec); vec[2] = 0; dist = VectorLength(vec); if(dist >= monster->size[0]) { self->nextthink = level.time + FRAMETIME; return; } } } } } if(!monster || !monster->inuse || (monster->health <= 0)) { G_FreeEdict(self); return; } if(monster->goalentity == self) monster->goalentity = NULL; if(monster->movetarget == self) monster->movetarget = NULL; if(monster->monsterinfo.aiflags & AI_FOLLOW_LEADER) { monster->monsterinfo.leader = NULL; if(monster->monsterinfo.old_leader && monster->monsterinfo.old_leader->inuse) { monster->monsterinfo.pausetime = level.time + 2; monster->monsterinfo.stand(monster); VectorCopy(monster->monsterinfo.old_leader->s.origin,self->move_origin); self->nextthink = level.time + 2; self->think = thing_restore_leader; gi.linkentity(monster); return; } else { monster->monsterinfo.aiflags &= ~AI_FOLLOW_LEADER; } } if(monster->monsterinfo.aiflags & AI_CHICKEN) { monster->monsterinfo.stand(monster); monster->monsterinfo.aiflags |= AI_STAND_GROUND; monster->monsterinfo.pausetime = level.time + 100000; } monster->vehicle = NULL; monster->monsterinfo.aiflags &= ~(AI_CHASE_THING | AI_SEEK_COVER | AI_EVADE_GRENADE); G_FreeEdict(self); if (has_valid_enemy(monster)) { monster->monsterinfo.pausetime = 0; monster->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND); monster->goalentity = monster->enemy; if (visible(monster, monster->enemy)) { FoundTarget (monster); return; } HuntTarget (monster); return; } monster->enemy = NULL; monster->monsterinfo.pausetime = level.time + 100000000; monster->monsterinfo.stand (monster); }
void thing_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { if(self->target_ent != other) return; if(other->health <= 0) { G_FreeEdict(self); return; } self->touch = NULL; if( self->target_ent->monsterinfo.aiflags & AI_SEEK_COVER ) { edict_t *monster; // For monster/actor seeking cover after firing, // pause a random bit before resuming attack self->touch_debounce_time = level.time + random()*6.; monster = self->target_ent; monster->monsterinfo.stand(monster); monster->monsterinfo.pausetime = self->touch_debounce_time; self->think = thing_think_pause; self->think(self); return; } if( self->target_ent->monsterinfo.aiflags & AI_EVADE_GRENADE ) { edict_t *grenade = other->next_grenade; if(other->goalentity == self) other->goalentity = NULL; if(other->movetarget == self) other->movetarget = NULL; other->vehicle = NULL; if(grenade) { // make sure this is still a grenade if(grenade->inuse) { if(Q_strcasecmp(grenade->classname,"grenade") && Q_strcasecmp(grenade->classname,"hgrenade")) other->next_grenade = grenade = NULL; } else other->next_grenade = grenade = NULL; } if(grenade) { if(self->touch_debounce_time > level.time) { other->monsterinfo.pausetime = self->touch_debounce_time + 0.1; other->monsterinfo.aiflags |= AI_STAND_GROUND; other->monsterinfo.stand(other); } else other->monsterinfo.pausetime = 0; other->enemy = grenade->owner; if (has_valid_enemy(other)) { other->goalentity = other->enemy; if (visible(other, other->enemy)) FoundTarget (other); else HuntTarget (other); } else { other->enemy = NULL; other->monsterinfo.stand (other); } if(other->monsterinfo.pausetime > 0) { self->think = thing_grenade_boom; self->nextthink = other->monsterinfo.pausetime; return; } other->monsterinfo.aiflags &= ~(AI_CHASE_THING | AI_EVADE_GRENADE); } else if (has_valid_enemy(other)) { other->monsterinfo.aiflags &= ~(AI_CHASE_THING | AI_EVADE_GRENADE); other->goalentity = other->enemy; if (visible(other, other->enemy)) FoundTarget (other); else HuntTarget (other); } else { other->enemy = NULL; other->monsterinfo.pausetime = level.time + 100000000; other->monsterinfo.aiflags &= ~(AI_CHASE_THING | AI_EVADE_GRENADE); other->monsterinfo.stand (other); } G_FreeEdict(self); return; } self->touch_debounce_time = 0; thing_think(self); }
qboolean medic_checkattack (edict_t *self) { if (!(self->monsterinfo.aiflags & AI_MEDIC)) { if( medic_FindDeadMonster(self) ) return false; } if (self->monsterinfo.aiflags & AI_MEDIC) { float r; vec3_t forward, right, offset, start; trace_t tr; // if we have 5 seconds or less before a timeout, // look for a hint_path to the target if ( (self->timestamp < level.time + 5) && (self->monsterinfo.last_hint_time + 5 < level.time) ) { // check for hint_paths. self->monsterinfo.last_hint_time = level.time; if (hintcheck_monsterlost(self)) { if(developer->value) gi.dprintf("medic at %s using hint_paths to find %s\n", vtos(self->s.origin), self->enemy->classname); self->timestamp = level.time + MEDIC_TRY_TIME; return false; } } // if we ran out of time, give up if (self->timestamp < level.time) { //if(developer->value) // gi.dprintf("medic at %s timed out, abort heal\n",vtos(self->s.origin)); abortHeal (self, true); self->timestamp = 0; return false; } // if our target went away if ((!self->enemy) || (!self->enemy->inuse)) { abortHeal (self,false); return false; } // if target is embedded in a solid if (embedded(self->enemy)) { abortHeal (self,false); return false; } r = realrange(self,self->enemy); if (r > MEDIC_MAX_HEAL_DISTANCE+10) { self->monsterinfo.attack_state = AS_STRAIGHT; //abortHeal(self,false); return false; } else if(r < MEDIC_MIN_DISTANCE) { abortHeal(self,false); return false; } // Lazarus 1.6.2.3: if point-to-point vector from cable to // target is blocked by a solid AngleVectors (self->s.angles, forward, right, NULL); // Offset [8] has the largest displacement to the left... not a sure // thing but this one should be the most severe test. VectorCopy (medic_cable_offsets[8], offset); G_ProjectSource (self->s.origin, offset, forward, right, start); tr = gi.trace(start,NULL,NULL,self->enemy->s.origin,self,MASK_SHOT|MASK_WATER); if (tr.fraction < 1.0 && tr.ent != self->enemy) return false; medic_attack(self); return true; } // Lazarus: NEVER attack other monsters if ((self->enemy) && (self->enemy->svflags & SVF_MONSTER)) { self->enemy = self->oldenemy; self->oldenemy = NULL; if(self->enemy && self->enemy->inuse) { if(visible(self,self->enemy)) FoundTarget(self); else HuntTarget(self); } return false; } return M_CheckAttack (self); }
/* ============= ai_run The monster has an enemy it is trying to kill ============= */ void ai_run( float dist ) { vec3_t tmpv; if ( k_bloodfest ) { if ( (int)self->s.v.flags & FL_SWIM ) { dist *= 5; // let fish swim faster in bloodfest mode. } else if ( self->bloodfest_boss ) { dist *= 2; // let boss move faster } } movedist = dist; // see if the enemy is dead if ( ISDEAD( PROG_TO_EDICT( self->s.v.enemy ) ) || ( (int)PROG_TO_EDICT( self->s.v.enemy )->s.v.flags & FL_NOTARGET ) ) { self->s.v.enemy = EDICT_TO_PROG( world ); // FIXME: look all around for other targets if ( self->oldenemy && ISLIVE( self->oldenemy ) && !( (int)self->oldenemy->s.v.flags & FL_NOTARGET ) ) { self->s.v.enemy = EDICT_TO_PROG( self->oldenemy ); HuntTarget (); } else { if ( !self->movetarget || self->movetarget == world ) { if ( self->th_stand ) self->th_stand(); } else { if ( self->th_walk ) self->th_walk(); } return; } } self->show_hostile = g_globalvars.time + 1; // wake up other monsters // check knowledge of enemy enemy_vis = visible( PROG_TO_EDICT( self->s.v.enemy ) ); if ( enemy_vis ) self->search_time = g_globalvars.time + 5; // does not search for enemy next 5 seconds // look for other coop players if ( coop && self->search_time < g_globalvars.time ) { if ( FindTarget() ) { // this is fix for too frequent enemy sighting, required for bloodfest mode. if ( !visible( PROG_TO_EDICT( self->s.v.enemy ) ) ) { self->search_time = g_globalvars.time + 5; // does not search for enemy next 5 seconds } return; } } enemy_infront = infront( PROG_TO_EDICT( self->s.v.enemy ) ); enemy_range = range( PROG_TO_EDICT( self->s.v.enemy ) ); VectorSubtract( PROG_TO_EDICT( self->s.v.enemy )->s.v.origin, self->s.v.origin, tmpv); enemy_yaw = vectoyaw( tmpv ); if ( self->attack_state == AS_MISSILE ) { //dprint ("ai_run_missile\n"); ai_run_missile(); return; } if ( self->attack_state == AS_MELEE ) { //dprint ("ai_run_melee\n"); ai_run_melee(); return; } if ( CheckAnyAttack() ) return; // beginning an attack if ( self->attack_state == AS_SLIDING ) { ai_run_slide(); return; } // head straight in movetogoal( dist ); // done in C code... }
/* ============= ai_checkattack Decides if we're going to attack or do something else used by ai_run and ai_stand ============= */ bool ai_checkattack (edict_t *self, float dist) { vec3_t temp; bool hesDeadJim; // this causes monsters to run blindly to the combat point w/o firing if (self->goalentity) { if (self->monsterinfo.aiflags & AI_COMBAT_POINT) return false; if (self->monsterinfo.aiflags & AI_SOUND_TARGET) { if (level.time - self->enemy->teleport_time > 5000) { if (self->goalentity == self->enemy) { if (self->movetarget) self->goalentity = self->movetarget; else self->goalentity = NULL; } self->monsterinfo.aiflags &= ~AI_SOUND_TARGET; if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND); } else { self->show_hostile = level.time + 1000; return false; } } } enemy_vis = false; // see if the enemy is dead hesDeadJim = false; if ((!self->enemy) || (!self->enemy->r.inuse)) { hesDeadJim = true; } else if (self->monsterinfo.aiflags & AI_MEDIC) { if (self->enemy->health > 0) { hesDeadJim = true; self->monsterinfo.aiflags &= ~AI_MEDIC; } } else { if (self->monsterinfo.aiflags & AI_BRUTAL) { if (self->enemy->health <= -80) hesDeadJim = true; } else { if (self->enemy->health <= 0) hesDeadJim = true; } } if (hesDeadJim) { self->enemy = NULL; // FIXME: look all around for other targets if (self->oldenemy && self->oldenemy->health > 0) { self->enemy = self->oldenemy; self->oldenemy = NULL; HuntTarget (self); } else { if (self->movetarget) { self->goalentity = self->movetarget; self->monsterinfo.walk (self); } else { // we need the pausetime otherwise the stand code // will just revert to walking with no target and // the monsters will wonder around aimlessly trying // to hunt the world entity self->monsterinfo.pausetime = level.time + 100000000; self->monsterinfo.stand (self); } return true; } } self->show_hostile = level.time + 1000; // wake up other monsters // check knowledge of enemy enemy_vis = G_Visible(self, self->enemy); if (enemy_vis) { self->monsterinfo.search_time = level.time + 5000; VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting); } // look for other coop players here // if (coop && self->monsterinfo.search_time < level.time) // { // if (FindTarget (self)) // return true; // } enemy_infront = G_InFront(self, self->enemy); enemy_range = range(self, self->enemy); VectorSubtract (self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw(temp); // JDC self->ideal_yaw = enemy_yaw; if (self->monsterinfo.attack_state == AS_MISSILE) { ai_run_missile (self); return true; } if (self->monsterinfo.attack_state == AS_MELEE) { ai_run_melee (self); return true; } // if enemy is not currently visible, we will never attack if (!enemy_vis) return false; return self->monsterinfo.checkattack (self); }