diff options
Diffstat (limited to 'src/jake2/game/Monster.java')
-rw-r--r-- | src/jake2/game/Monster.java | 648 |
1 files changed, 347 insertions, 301 deletions
diff --git a/src/jake2/game/Monster.java b/src/jake2/game/Monster.java index fb0ac21..8ced270 100644 --- a/src/jake2/game/Monster.java +++ b/src/jake2/game/Monster.java @@ -1,26 +1,25 @@ /* -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. - -*/ + * 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 17.12.2003 by RST. -// $Id: Monster.java,v 1.3 2004-09-12 18:25:49 salomo Exp $ - +// $Id: Monster.java,v 1.4 2004-09-22 19:22:06 salomo Exp $ package jake2.game; import jake2.Defines; @@ -31,284 +30,331 @@ import jake2.util.*; import java.util.*; -public class Monster extends GameAI { - - // FIXME monsters should call these with a totally accurate direction - // and we can mess it up based on skill. Spread should be for normal - // and we can tighten or loosen based on skill. We could muck with - // the damages too, but I'm not sure that's such a good idea. - public static void monster_fire_bullet( - edict_t self, - float[] start, - float[] dir, - int damage, - int kick, - int hspread, - int vspread, - int flashtype) { - Fire.fire_bullet(self, start, dir, damage, kick, hspread, vspread, Defines.MOD_UNKNOWN); - - GameBase.gi.WriteByte(Defines.svc_muzzleflash2); - GameBase.gi.WriteShort(self.index); - GameBase.gi.WriteByte(flashtype); - GameBase.gi.multicast(start, Defines.MULTICAST_PVS); - } - /** The Moster fires the shotgun. */ - public static void monster_fire_shotgun( - edict_t self, - float[] start, - float[] aimdir, - int damage, - int kick, - int hspread, - int vspread, - int count, - int flashtype) { - Fire.fire_shotgun(self, start, aimdir, damage, kick, hspread, vspread, count, Defines.MOD_UNKNOWN); - - GameBase.gi.WriteByte(Defines.svc_muzzleflash2); - GameBase.gi.WriteShort(self.index); - GameBase.gi.WriteByte(flashtype); - GameBase.gi.multicast(start, Defines.MULTICAST_PVS); - } - /** The Moster fires the blaster. */ - public static void monster_fire_blaster( - edict_t self, - float[] start, - float[] dir, - int damage, - int speed, - int flashtype, - int effect) { - Fire.fire_blaster(self, start, dir, damage, speed, effect, false); - - GameBase.gi.WriteByte(Defines.svc_muzzleflash2); - GameBase.gi.WriteShort(self.index); - GameBase.gi.WriteByte(flashtype); - GameBase.gi.multicast(start, Defines.MULTICAST_PVS); - } - /** The Moster fires the grenade. */ - public static void monster_fire_grenade(edict_t self, float[] start, float[] aimdir, int damage, int speed, int flashtype) { - Fire.fire_grenade(self, start, aimdir, damage, speed, 2.5f, damage + 40); - - GameBase.gi.WriteByte(Defines.svc_muzzleflash2); - GameBase.gi.WriteShort(self.index); - GameBase.gi.WriteByte(flashtype); - GameBase.gi.multicast(start, Defines.MULTICAST_PVS); - } - /** The Moster fires the rocket. */ - public static void monster_fire_rocket(edict_t self, float[] start, float[] dir, int damage, int speed, int flashtype) { - Fire.fire_rocket(self, start, dir, damage, speed, damage + 20, damage); - - GameBase.gi.WriteByte(Defines.svc_muzzleflash2); - GameBase.gi.WriteShort(self.index); - GameBase.gi.WriteByte(flashtype); - GameBase.gi.multicast(start, Defines.MULTICAST_PVS); - } - /** The Moster fires the railgun. */ - public static void monster_fire_railgun(edict_t self, float[] start, float[] aimdir, int damage, int kick, int flashtype) { - Fire.fire_rail(self, start, aimdir, damage, kick); - - GameBase.gi.WriteByte(Defines.svc_muzzleflash2); - GameBase.gi.WriteShort(self.index); - GameBase.gi.WriteByte(flashtype); - GameBase.gi.multicast(start, Defines.MULTICAST_PVS); - } - /** The Moster fires the bfg. */ - public static void monster_fire_bfg( - edict_t self, - float[] start, - float[] aimdir, - int damage, - int speed, - int kick, - float damage_radius, - int flashtype) { - Fire.fire_bfg(self, start, aimdir, damage, speed, damage_radius); - - GameBase.gi.WriteByte(Defines.svc_muzzleflash2); - GameBase.gi.WriteShort(self.index); - GameBase.gi.WriteByte(flashtype); - GameBase.gi.multicast(start, Defines.MULTICAST_PVS); - } - /* - ================ - monster_death_use - - When a monster dies, it fires all of its targets with the current - enemy as activator. - ================ - */ - public static void monster_death_use(edict_t self) { - self.flags &= ~(Defines.FL_FLY | Defines.FL_SWIM); - self.monsterinfo.aiflags &= Defines.AI_GOOD_GUY; - - if (self.item != null) { - GameUtil.Drop_Item(self, self.item); - self.item= null; - } - - if (self.deathtarget != null) - self.target= self.deathtarget; - - if (self.target == null) - return; - - GameUtil.G_UseTargets(self, self.enemy); - } - // ============================================================================ - public static boolean monster_start(edict_t self) { - if (GameBase.deathmatch.value != 0) { - GameUtil.G_FreeEdict(self); - return false; - } - - if ((self.spawnflags & 4) != 0 && 0 == (self.monsterinfo.aiflags & Defines.AI_GOOD_GUY)) { - self.spawnflags &= ~4; - self.spawnflags |= 1; - // gi.dprintf("fixed spawnflags on %s at %s\n", self.classname, vtos(self.s.origin)); - } - - if (0 == (self.monsterinfo.aiflags & Defines.AI_GOOD_GUY)) - GameBase.level.total_monsters++; - - self.nextthink= GameBase.level.time + Defines.FRAMETIME; - self.svflags |= Defines.SVF_MONSTER; - self.s.renderfx |= Defines.RF_FRAMELERP; - self.takedamage= Defines.DAMAGE_AIM; - self.air_finished= GameBase.level.time + 12; - self.use= GameUtilAdapters.monster_use; - self.max_health= self.health; - self.clipmask= Defines.MASK_MONSTERSOLID; - - self.s.skinnum= 0; - self.deadflag= Defines.DEAD_NO; - self.svflags &= ~Defines.SVF_DEADMONSTER; - - if (null == self.monsterinfo.checkattack) - self.monsterinfo.checkattack= GameUtilAdapters.M_CheckAttack; - Math3D.VectorCopy(self.s.origin, self.s.old_origin); - - if (GameBase.st.item != null && GameBase.st.item.length() > 0) { - self.item= GameUtil.FindItemByClassname(GameBase.st.item); - if (self.item == null) - GameBase.gi.dprintf( - "monster_start:" - + self.classname - + " at " - + Lib.vtos(self.s.origin) - + " has bad item: " - + GameBase.st.item - + "\n"); - } - - // randomize what frame they start on - if (self.monsterinfo.currentmove != null) - self.s.frame= - self.monsterinfo.currentmove.firstframe - + (Lib.rand() % (self.monsterinfo.currentmove.lastframe - self.monsterinfo.currentmove.firstframe + 1)); - - return true; - } - - public static void monster_start_go(edict_t self) { - - float[] v= { 0, 0, 0 }; - - if (self.health <= 0) - return; - - // check for target to combat_point and change to combattarget - if (self.target != null) { - boolean notcombat; - boolean fixup; - edict_t target= null; - notcombat= false; - fixup= false; - /* - if (true) { - Com.Printf("all entities:\n"); - - for (int n = 0; n < Game.globals.num_edicts; n++) { - edict_t ent = GameBase.g_edicts[n]; - Com.Printf( - "|%4i | %25s |%8.2f|%8.2f|%8.2f||%8.2f|%8.2f|%8.2f||%8.2f|%8.2f|%8.2f|\n", - new Vargs().add(n).add(ent.classname). - add(ent.s.origin[0]).add(ent.s.origin[1]).add(ent.s.origin[2]) - .add(ent.mins[0]).add(ent.mins[1]).add(ent.mins[2]) - .add(ent.maxs[0]).add(ent.maxs[1]).add(ent.maxs[2])); - } - sleep(10); - } - */ - - EdictIterator edit= null; - - while ((edit= GameBase.G_Find(edit, GameBase.findByTarget, self.target)) != null) { - target= edit.o; - if (Lib.strcmp(target.classname, "point_combat") == 0) { - self.combattarget= self.target; - fixup= true; - } - else { - notcombat= true; - } - } - if (notcombat && self.combattarget != null) - GameBase.gi.dprintf(self.classname + " at " + Lib.vtos(self.s.origin) + " has target with mixed types\n"); - if (fixup) - self.target= null; - } - - // validate combattarget - if (self.combattarget != null) { - edict_t target= null; - - EdictIterator edit= null; - while ((edit= GameBase.G_Find(edit, GameBase.findByTarget, self.combattarget)) != null) { - target= edit.o; - - if (Lib.strcmp(target.classname, "point_combat") != 0) { - GameBase.gi.dprintf( - self.classname - + " at " - + Lib.vtos(self.s.origin) - + " has bad combattarget " - + self.combattarget - + " : " - + target.classname - + " at " - + Lib.vtos(target.s.origin)); - } - } - } - - if (self.target != null) { - self.goalentity= self.movetarget= GameBase.G_PickTarget(self.target); - if (null == self.movetarget) { - GameBase.gi.dprintf(self.classname + " can't find target " + self.target + " at " + Lib.vtos(self.s.origin) + "\n"); - self.target= null; - self.monsterinfo.pausetime= 100000000; - self.monsterinfo.stand.think(self); - } - else if (Lib.strcmp(self.movetarget.classname, "path_corner") == 0) { - Math3D.VectorSubtract(self.goalentity.s.origin, self.s.origin, v); - self.ideal_yaw= self.s.angles[Defines.YAW]= Math3D.vectoyaw(v); - self.monsterinfo.walk.think(self); - self.target= null; - } - else { - self.goalentity= self.movetarget= null; - self.monsterinfo.pausetime= 100000000; - self.monsterinfo.stand.think(self); - } - } - else { - self.monsterinfo.pausetime= 100000000; - self.monsterinfo.stand.think(self); - } - - self.think= MonsterAdapters.monster_think; - self.nextthink= GameBase.level.time + Defines.FRAMETIME; - } -} +public class Monster { + + // FIXME monsters should call these with a totally accurate direction + // and we can mess it up based on skill. Spread should be for normal + // and we can tighten or loosen based on skill. We could muck with + // the damages too, but I'm not sure that's such a good idea. + public static void monster_fire_bullet(edict_t self, float[] start, + float[] dir, int damage, int kick, int hspread, int vspread, + int flashtype) { + Fire.fire_bullet(self, start, dir, damage, kick, hspread, vspread, + Defines.MOD_UNKNOWN); + + GameBase.gi.WriteByte(Defines.svc_muzzleflash2); + GameBase.gi.WriteShort(self.index); + GameBase.gi.WriteByte(flashtype); + GameBase.gi.multicast(start, Defines.MULTICAST_PVS); + } + + /** The Moster fires the shotgun. */ + public static void monster_fire_shotgun(edict_t self, float[] start, + float[] aimdir, int damage, int kick, int hspread, int vspread, + int count, int flashtype) { + Fire.fire_shotgun(self, start, aimdir, damage, kick, hspread, vspread, + count, Defines.MOD_UNKNOWN); + + GameBase.gi.WriteByte(Defines.svc_muzzleflash2); + GameBase.gi.WriteShort(self.index); + GameBase.gi.WriteByte(flashtype); + GameBase.gi.multicast(start, Defines.MULTICAST_PVS); + } + + /** The Moster fires the blaster. */ + public static void monster_fire_blaster(edict_t self, float[] start, + float[] dir, int damage, int speed, int flashtype, int effect) { + Fire.fire_blaster(self, start, dir, damage, speed, effect, false); + + GameBase.gi.WriteByte(Defines.svc_muzzleflash2); + GameBase.gi.WriteShort(self.index); + GameBase.gi.WriteByte(flashtype); + GameBase.gi.multicast(start, Defines.MULTICAST_PVS); + } + + /** The Moster fires the grenade. */ + public static void monster_fire_grenade(edict_t self, float[] start, + float[] aimdir, int damage, int speed, int flashtype) { + Fire + .fire_grenade(self, start, aimdir, damage, speed, 2.5f, + damage + 40); + + GameBase.gi.WriteByte(Defines.svc_muzzleflash2); + GameBase.gi.WriteShort(self.index); + GameBase.gi.WriteByte(flashtype); + GameBase.gi.multicast(start, Defines.MULTICAST_PVS); + } + + /** The Moster fires the rocket. */ + public static void monster_fire_rocket(edict_t self, float[] start, + float[] dir, int damage, int speed, int flashtype) { + Fire.fire_rocket(self, start, dir, damage, speed, damage + 20, damage); + + GameBase.gi.WriteByte(Defines.svc_muzzleflash2); + GameBase.gi.WriteShort(self.index); + GameBase.gi.WriteByte(flashtype); + GameBase.gi.multicast(start, Defines.MULTICAST_PVS); + } + + /** The Moster fires the railgun. */ + public static void monster_fire_railgun(edict_t self, float[] start, + float[] aimdir, int damage, int kick, int flashtype) { + Fire.fire_rail(self, start, aimdir, damage, kick); + + GameBase.gi.WriteByte(Defines.svc_muzzleflash2); + GameBase.gi.WriteShort(self.index); + GameBase.gi.WriteByte(flashtype); + GameBase.gi.multicast(start, Defines.MULTICAST_PVS); + } + + /** The Moster fires the bfg. */ + public static void monster_fire_bfg(edict_t self, float[] start, + float[] aimdir, int damage, int speed, int kick, + float damage_radius, int flashtype) { + Fire.fire_bfg(self, start, aimdir, damage, speed, damage_radius); + + GameBase.gi.WriteByte(Defines.svc_muzzleflash2); + GameBase.gi.WriteShort(self.index); + GameBase.gi.WriteByte(flashtype); + GameBase.gi.multicast(start, Defines.MULTICAST_PVS); + } + + /* + * ================ monster_death_use + * + * When a monster dies, it fires all of its targets with the current enemy + * as activator. ================ + */ + public static void monster_death_use(edict_t self) { + self.flags &= ~(Defines.FL_FLY | Defines.FL_SWIM); + self.monsterinfo.aiflags &= Defines.AI_GOOD_GUY; + + if (self.item != null) { + GameUtil.Drop_Item(self, self.item); + self.item = null; + } + + if (self.deathtarget != null) + self.target = self.deathtarget; + + if (self.target == null) + return; + + GameUtil.G_UseTargets(self, self.enemy); + } + + // ============================================================================ + public static boolean monster_start(edict_t self) { + if (GameBase.deathmatch.value != 0) { + GameUtil.G_FreeEdict(self); + return false; + } + + if ((self.spawnflags & 4) != 0 + && 0 == (self.monsterinfo.aiflags & Defines.AI_GOOD_GUY)) { + self.spawnflags &= ~4; + self.spawnflags |= 1; + // gi.dprintf("fixed spawnflags on %s at %s\n", self.classname, + // vtos(self.s.origin)); + } + + if (0 == (self.monsterinfo.aiflags & Defines.AI_GOOD_GUY)) + GameBase.level.total_monsters++; + + self.nextthink = GameBase.level.time + Defines.FRAMETIME; + self.svflags |= Defines.SVF_MONSTER; + self.s.renderfx |= Defines.RF_FRAMELERP; + self.takedamage = Defines.DAMAGE_AIM; + self.air_finished = GameBase.level.time + 12; + self.use = GameUtil.monster_use; + self.max_health = self.health; + self.clipmask = Defines.MASK_MONSTERSOLID; + + self.s.skinnum = 0; + self.deadflag = Defines.DEAD_NO; + self.svflags &= ~Defines.SVF_DEADMONSTER; + + if (null == self.monsterinfo.checkattack) + self.monsterinfo.checkattack = GameUtil.M_CheckAttack; + Math3D.VectorCopy(self.s.origin, self.s.old_origin); + + if (GameBase.st.item != null && GameBase.st.item.length() > 0) { + self.item = GameUtil.FindItemByClassname(GameBase.st.item); + if (self.item == null) + GameBase.gi.dprintf("monster_start:" + self.classname + " at " + + Lib.vtos(self.s.origin) + " has bad item: " + + GameBase.st.item + "\n"); + } + + // randomize what frame they start on + if (self.monsterinfo.currentmove != null) + self.s.frame = self.monsterinfo.currentmove.firstframe + + (Lib.rand() % (self.monsterinfo.currentmove.lastframe + - self.monsterinfo.currentmove.firstframe + 1)); + + return true; + } + + public static void monster_start_go(edict_t self) { + + float[] v = { 0, 0, 0 }; + + if (self.health <= 0) + return; + + // check for target to combat_point and change to combattarget + if (self.target != null) { + boolean notcombat; + boolean fixup; + edict_t target = null; + notcombat = false; + fixup = false; + /* + * if (true) { Com.Printf("all entities:\n"); + * + * for (int n = 0; n < Game.globals.num_edicts; n++) { edict_t ent = + * GameBase.g_edicts[n]; Com.Printf( "|%4i | %25s + * |%8.2f|%8.2f|%8.2f||%8.2f|%8.2f|%8.2f||%8.2f|%8.2f|%8.2f|\n", new + * Vargs().add(n).add(ent.classname). + * add(ent.s.origin[0]).add(ent.s.origin[1]).add(ent.s.origin[2]) + * .add(ent.mins[0]).add(ent.mins[1]).add(ent.mins[2]) + * .add(ent.maxs[0]).add(ent.maxs[1]).add(ent.maxs[2])); } + * sleep(10); } + */ + + EdictIterator edit = null; + + while ((edit = GameBase.G_Find(edit, GameBase.findByTarget, + self.target)) != null) { + target = edit.o; + if (Lib.strcmp(target.classname, "point_combat") == 0) { + self.combattarget = self.target; + fixup = true; + } else { + notcombat = true; + } + } + if (notcombat && self.combattarget != null) + GameBase.gi.dprintf(self.classname + " at " + + Lib.vtos(self.s.origin) + + " has target with mixed types\n"); + if (fixup) + self.target = null; + } + + // validate combattarget + if (self.combattarget != null) { + edict_t target = null; + + EdictIterator edit = null; + while ((edit = GameBase.G_Find(edit, GameBase.findByTarget, + self.combattarget)) != null) { + target = edit.o; + + if (Lib.strcmp(target.classname, "point_combat") != 0) { + GameBase.gi.dprintf(self.classname + " at " + + Lib.vtos(self.s.origin) + + " has bad combattarget " + self.combattarget + + " : " + target.classname + " at " + + Lib.vtos(target.s.origin)); + } + } + } + + if (self.target != null) { + self.goalentity = self.movetarget = GameBase + .G_PickTarget(self.target); + if (null == self.movetarget) { + GameBase.gi + .dprintf(self.classname + " can't find target " + + self.target + " at " + + Lib.vtos(self.s.origin) + "\n"); + self.target = null; + self.monsterinfo.pausetime = 100000000; + self.monsterinfo.stand.think(self); + } else if (Lib.strcmp(self.movetarget.classname, "path_corner") == 0) { + Math3D.VectorSubtract(self.goalentity.s.origin, self.s.origin, + v); + self.ideal_yaw = self.s.angles[Defines.YAW] = Math3D + .vectoyaw(v); + self.monsterinfo.walk.think(self); + self.target = null; + } else { + self.goalentity = self.movetarget = null; + self.monsterinfo.pausetime = 100000000; + self.monsterinfo.stand.think(self); + } + } else { + self.monsterinfo.pausetime = 100000000; + self.monsterinfo.stand.think(self); + } + + self.think = Monster.monster_think; + self.nextthink = GameBase.level.time + Defines.FRAMETIME; + } + + public static EntThinkAdapter monster_think = new EntThinkAdapter() { + public boolean think(edict_t self) { + + M.M_MoveFrame(self); + if (self.linkcount != self.monsterinfo.linkcount) { + self.monsterinfo.linkcount = self.linkcount; + M.M_CheckGround(self); + } + M.M_CatagorizePosition(self); + M.M_WorldEffects(self); + M.M_SetEffects(self); + return true; + } + }; + + public static EntThinkAdapter monster_triggered_spawn = new EntThinkAdapter() { + public boolean think(edict_t self) { + + self.s.origin[2] += 1; + GameUtil.KillBox(self); + + self.solid = Defines.SOLID_BBOX; + self.movetype = Defines.MOVETYPE_STEP; + self.svflags &= ~Defines.SVF_NOCLIENT; + self.air_finished = GameBase.level.time + 12; + GameBase.gi.linkentity(self); + + Monster.monster_start_go(self); + + if (self.enemy != null && 0 == (self.spawnflags & 1) + && 0 == (self.enemy.flags & Defines.FL_NOTARGET)) { + GameUtil.FoundTarget(self); + } else { + self.enemy = null; + } + return true; + } + }; + + // we have a one frame delay here so we don't telefrag the guy who activated + // us + public static EntUseAdapter monster_triggered_spawn_use = new EntUseAdapter() { + + public void use(edict_t self, edict_t other, edict_t activator) { + self.think = monster_triggered_spawn; + self.nextthink = GameBase.level.time + Defines.FRAMETIME; + if (activator.client != null) + self.enemy = activator; + self.use = GameUtil.monster_use; + } + }; + + public static EntThinkAdapter monster_triggered_start = new EntThinkAdapter() { + public boolean think(edict_t self) { + if (self.index == 312) + Com.p("monster_triggered_start"); + self.solid = Defines.SOLID_NOT; + self.movetype = Defines.MOVETYPE_NONE; + self.svflags |= Defines.SVF_NOCLIENT; + self.nextthink = 0; + self.use = monster_triggered_spawn_use; + return true; + } + }; +}
\ No newline at end of file |