📄 gameai.java
字号:
/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307, USA. * */// Created on 02.11.2003 by RST.// $Id: GameAI.java,v 1.10 2005/12/27 21:02:30 salomo Exp $package jake2.game;import jake2.Defines;import jake2.Globals;import jake2.client.M;import jake2.util.Lib;import jake2.util.Math3D;public class GameAI { public static void AttackFinished(edict_t self, float time) { self.monsterinfo.attack_finished = GameBase.level.time + time; } /** Don't move, but turn towards ideal_yaw Distance is for slight position * adjustments needed by the animations. */ public static void ai_turn(edict_t self, float dist) { if (dist != 0) M.M_walkmove(self, self.s.angles[Defines.YAW], dist); if (GameUtil.FindTarget(self)) return; M.M_ChangeYaw(self); } /** * Checks, if the monster should turn left/right. */ public static boolean FacingIdeal(edict_t self) { float delta; delta = Math3D.anglemod(self.s.angles[Defines.YAW] - self.ideal_yaw); if (delta > 45 && delta < 315) return false; return true; } /** * Turn and close until within an angle to launch a melee attack. */ public static void ai_run_melee(edict_t self) { self.ideal_yaw = enemy_yaw; M.M_ChangeYaw(self); if (FacingIdeal(self)) { self.monsterinfo.melee.think(self); self.monsterinfo.attack_state = Defines.AS_STRAIGHT; } } /** * Turn in place until within an angle to launch a missile attack. */ public static void ai_run_missile(edict_t self) { self.ideal_yaw = enemy_yaw; M.M_ChangeYaw(self); if (FacingIdeal(self)) { self.monsterinfo.attack.think(self); self.monsterinfo.attack_state = Defines.AS_STRAIGHT; } }; /** * Strafe sideways, but stay at aproximately the same range. */ public static void ai_run_slide(edict_t self, float distance) { float ofs; self.ideal_yaw = enemy_yaw; M.M_ChangeYaw(self); if (self.monsterinfo.lefty != 0) ofs = 90; else ofs = -90; if (M.M_walkmove(self, self.ideal_yaw + ofs, distance)) return; self.monsterinfo.lefty = 1 - self.monsterinfo.lefty; M.M_walkmove(self, self.ideal_yaw - ofs, distance); } /** * 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 boolean ai_checkattack(edict_t self, float dist) { float temp[] = { 0, 0, 0 }; boolean 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; } } } 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; 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 enemy_vis = GameUtil.visible(self, self.enemy); if (enemy_vis) { self.monsterinfo.search_time = GameBase.level.time + 5; Math3D.VectorCopy(self.enemy.s.origin, self.monsterinfo.last_sighting); } enemy_infront = GameUtil.infront(self, self.enemy); enemy_range = GameUtil.range(self, self.enemy); Math3D.VectorSubtract(self.enemy.s.origin, self.s.origin, temp); enemy_yaw = Math3D.vectoyaw(temp); // JDC self.ideal_yaw = enemy_yaw; if (self.monsterinfo.attack_state == Defines.AS_MISSILE) { ai_run_missile(self); return true; } if (self.monsterinfo.attack_state == Defines.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.think(self); } /** * The monster is walking it's beat. */ static void ai_walk(edict_t self, float dist) { M.M_MoveToGoal(self, dist); // check for noticing a player if (GameUtil.FindTarget(self)) return; if ((self.monsterinfo.search != null) && (GameBase.level.time > self.monsterinfo.idle_time)) { if (self.monsterinfo.idle_time != 0) { self.monsterinfo.search.think(self); self.monsterinfo.idle_time = GameBase.level.time + 15 + Lib.random() * 15; } else { self.monsterinfo.idle_time = GameBase.level.time + Lib.random() * 15; } } } /** * Called once each frame to set level.sight_client to the player to be * checked for in findtarget. * * If all clients are either dead or in notarget, sight_client will be null. * * In coop games, sight_client will cycle between the clients. */ static void AI_SetSightClient() { edict_t ent; int start, check; if (GameBase.level.sight_client == null) start = 1; else start = GameBase.level.sight_client.index; check = start; while (true) { check++; if (check > GameBase.game.maxclients) check = 1; ent = GameBase.g_edicts[check]; if (ent.inuse && ent.health > 0 && (ent.flags & Defines.FL_NOTARGET) == 0) { GameBase.level.sight_client = ent; return; // got one } if (check == start) { GameBase.level.sight_client = null; return; // nobody to see } } } /** * Move the specified distance at current facing. This replaces the QC * functions: ai_forward, ai_back, ai_pain, and ai_painforward */ static void ai_move(edict_t self, float dist) { M.M_walkmove(self, self.s.angles[Defines.YAW], dist); } /** * Decides running or standing according to flag AI_STAND_GROUND. */ static void HuntTarget(edict_t self) { float[] vec = { 0, 0, 0 }; self.goalentity = self.enemy; if ((self.monsterinfo.aiflags & Defines.AI_STAND_GROUND) != 0) self.monsterinfo.stand.think(self); else self.monsterinfo.run.think(self); Math3D.VectorSubtract(self.enemy.s.origin, self.s.origin, vec); self.ideal_yaw = Math3D.vectoyaw(vec); // wait a while before first attack if (0 == (self.monsterinfo.aiflags & Defines.AI_STAND_GROUND)) GameUtil.AttackFinished(self, 1); } public static EntThinkAdapter walkmonster_start_go = new EntThinkAdapter() { public String getID() { return "walkmonster_start_go"; } public boolean think(edict_t self) { if (0 == (self.spawnflags & 2) && GameBase.level.time < 1) { M.M_droptofloor.think(self); if (self.groundentity != null) if (!M.M_walkmove(self, 0, 0)) GameBase.gi.dprintf(self.classname + " in solid at " + Lib.vtos(self.s.origin) + "\n"); } if (0 == self.yaw_speed) self.yaw_speed = 40; self.viewheight = 25; Monster.monster_start_go(self); if ((self.spawnflags & 2) != 0) Monster.monster_triggered_start.think(self); return true; } }; public static EntThinkAdapter walkmonster_start = new EntThinkAdapter() { public String getID() { return "walkmonster_start";} public boolean think(edict_t self) { self.think = walkmonster_start_go; Monster.monster_start(self); return true; } }; public static EntThinkAdapter flymonster_start_go = new EntThinkAdapter() { public String getID() { return "flymonster_start_go";} public boolean think(edict_t self) { if (!M.M_walkmove(self, 0, 0)) GameBase.gi.dprintf(self.classname + " in solid at " + Lib.vtos(self.s.origin) + "\n"); if (0 == self.yaw_speed) self.yaw_speed = 20; self.viewheight = 25; Monster.monster_start_go(self); if ((self.spawnflags & 2) != 0) Monster.monster_triggered_start.think(self); return true; } }; public static EntThinkAdapter flymonster_start = new EntThinkAdapter() {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -