/** * Turn in place until within an angle to launch a missile attack. */ public static void ai_run_missile(edict_t self) { self.ideal_yaw = GameAI.enemy_yaw; M.M_ChangeYaw(self); if (GameAI.FacingIdeal(self)) { self.monsterinfo.attack.think(self); self.monsterinfo.attack_state = Defines.AS_STRAIGHT; } }
/** * Decides if we're going to attack or do something else used by ai_run and * ai_stand. * * .enemy Will be world if not currently angry at anyone. * * .movetarget The next path spot to walk toward. If .enemy, ignore * .movetarget. When an enemy is killed, the monster will try to return to * it's path. * * .hunt_time Set to time + something when the player is in sight, but * movement straight for him is blocked. This causes the monster to use wall * following code for movement direction instead of sighting on the player. * * .ideal_yaw A yaw angle of the intended direction, which will be turned * towards at up to 45 deg / state. If the enemy is in view and hunt_time is * not active, this will be the exact line towards the enemy. * * .pausetime A monster will leave it's stand state and head towards it's * .movetarget when time > .pausetime. * * walkmove(angle, speed) primitive is all or nothing */ public static bool ai_checkattack(edict_t self, float dist) { float[] temp = { 0, 0, 0 }; bool hesDeadJim; // this causes monsters to run blindly to the combat point w/o firing if (self.goalentity != null) { if ((self.monsterinfo.aiflags & Defines.AI_COMBAT_POINT) != 0) { return(false); } if ((self.monsterinfo.aiflags & Defines.AI_SOUND_TARGET) != 0) { if (GameBase.level.time - self.enemy.teleport_time > 5.0) { if (self.goalentity == self.enemy) { if (self.movetarget != null) { self.goalentity = self.movetarget; } else { self.goalentity = null; } } self.monsterinfo.aiflags &= ~Defines.AI_SOUND_TARGET; if ((self.monsterinfo.aiflags & Defines.AI_TEMP_STAND_GROUND) != 0) { self.monsterinfo.aiflags &= ~(Defines.AI_STAND_GROUND | Defines.AI_TEMP_STAND_GROUND); } } else { self.show_hostile = (int)GameBase.level.time + 1; return(false); } } } GameAI.enemy_vis = false; // see if the enemy is dead hesDeadJim = false; if (null == self.enemy || !self.enemy.inuse) { hesDeadJim = true; } else if ((self.monsterinfo.aiflags & Defines.AI_MEDIC) != 0) { if (self.enemy.health > 0) { hesDeadJim = true; self.monsterinfo.aiflags &= ~Defines.AI_MEDIC; } } else { if ((self.monsterinfo.aiflags & Defines.AI_BRUTAL) != 0) { 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 != null && self.oldenemy.health > 0) { self.enemy = self.oldenemy; self.oldenemy = null; GameAI.HuntTarget(self); } else { if (self.movetarget != null) { self.goalentity = self.movetarget; self.monsterinfo.walk.think(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 = GameBase.level.time + 100000000; self.monsterinfo.stand.think(self); } return(true); } } self.show_hostile = (int)GameBase.level.time + 1; // wake up other // monsters check knowledge of enemy GameAI.enemy_vis = GameUtil.visible(self, self.enemy); if (GameAI.enemy_vis) { self.monsterinfo.search_time = GameBase.level.time + 5; Math3D.VectorCopy(self.enemy.s.origin, self.monsterinfo.last_sighting); } GameAI.enemy_infront = GameUtil.infront(self, self.enemy); GameAI.enemy_range = GameUtil.range(self, self.enemy); Math3D.VectorSubtract(self.enemy.s.origin, self.s.origin, temp); GameAI.enemy_yaw = Math3D.vectoyaw(temp); // JDC self.ideal_yaw = enemy_yaw; if (self.monsterinfo.attack_state == Defines.AS_MISSILE) { GameAI.ai_run_missile(self); return(true); } if (self.monsterinfo.attack_state == Defines.AS_MELEE) { GameAI.ai_run_melee(self); return(true); } // if enemy is not currently visible, we will never attack if (!GameAI.enemy_vis) { return(false); } return(self.monsterinfo.checkattack.think(self)); }