aboutsummaryrefslogtreecommitdiffstats
path: root/src/jake2/game/GameTurret.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jake2/game/GameTurret.java')
-rw-r--r--src/jake2/game/GameTurret.java604
1 files changed, 440 insertions, 164 deletions
diff --git a/src/jake2/game/GameTurret.java b/src/jake2/game/GameTurret.java
index d0bfdbb..68e3098 100644
--- a/src/jake2/game/GameTurret.java
+++ b/src/jake2/game/GameTurret.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 28.12.2003 by RST.
-// $Id: GameTurret.java,v 1.2 2004-07-08 15:58:44 hzi Exp $
-
+// $Id: GameTurret.java,v 1.3 2004-09-22 19:22:05 salomo Exp $
package jake2.game;
import jake2.*;
@@ -31,147 +30,424 @@ import jake2.server.*;
import jake2.util.Lib;
import jake2.util.Math3D;
-public class GameTurret extends GameMisc {
-
- public static void AnglesNormalize(float[] vec) {
- while (vec[0] > 360)
- vec[0] -= 360;
- while (vec[0] < 0)
- vec[0] += 360;
- while (vec[1] > 360)
- vec[1] -= 360;
- while (vec[1] < 0)
- vec[1] += 360;
- }
-
- public static float SnapToEights(float x) {
- x *= 8.0;
- if (x > 0.0)
- x += 0.5;
- else
- x -= 0.5;
- return 0.125f * (int) x;
- }
-
- /*QUAKED turret_breach (0 0 0) ?
- This portion of the turret can change both pitch and yaw.
- The model should be made with a flat pitch.
- It (and the associated base) need to be oriented towards 0.
- Use "angle" to set the starting angle.
-
- "speed" default 50
- "dmg" default 10
- "angle" point this forward
- "target" point this at an info_notnull at the muzzle tip
- "minpitch" min acceptable pitch angle : default -30
- "maxpitch" max acceptable pitch angle : default 30
- "minyaw" min acceptable yaw angle : default 0
- "maxyaw" max acceptable yaw angle : default 360
- */
-
- public static void turret_breach_fire(edict_t self) {
- float[] f = { 0, 0, 0 }, r = { 0, 0, 0 }, u = { 0, 0, 0 };
- float[] start = { 0, 0, 0 };
- int damage;
- int speed;
-
- AngleVectors(self.s.angles, f, r, u);
- VectorMA(self.s.origin, self.move_origin[0], f, start);
- VectorMA(start, self.move_origin[1], r, start);
- VectorMA(start, self.move_origin[2], u, start);
-
- damage = (int) (100 + random() * 50);
- speed = (int) (550 + 50 * skill.value);
- Fire.fire_rocket(self.teammaster.owner, start, f, damage, speed, 150, damage);
- gi.positioned_sound(start, self, CHAN_WEAPON, gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0);
- }
-
- public static void SP_turret_breach(edict_t self) {
- self.solid = SOLID_BSP;
- self.movetype = MOVETYPE_PUSH;
- gi.setmodel(self, self.model);
-
- if (self.speed == 0)
- self.speed = 50;
- if (self.dmg == 0)
- self.dmg = 10;
-
- if (st.minpitch == 0)
- st.minpitch = -30;
- if (st.maxpitch == 0)
- st.maxpitch = 30;
- if (st.maxyaw == 0)
- st.maxyaw = 360;
-
- self.pos1[PITCH] = -1 * st.minpitch;
- self.pos1[YAW] = st.minyaw;
- self.pos2[PITCH] = -1 * st.maxpitch;
- self.pos2[YAW] = st.maxyaw;
-
- self.ideal_yaw = self.s.angles[YAW];
- self.move_angles[YAW] = self.ideal_yaw;
-
- self.blocked = GameTurretAdapters.turret_blocked;
-
- self.think = GameTurretAdapters.turret_breach_finish_init;
- self.nextthink = level.time + FRAMETIME;
- gi.linkentity(self);
- }
-
- /*QUAKED turret_base (0 0 0) ?
- This portion of the turret changes yaw only.
- MUST be teamed with a turret_breach.
- */
-
- public static void SP_turret_base(edict_t self) {
- self.solid = SOLID_BSP;
- self.movetype = MOVETYPE_PUSH;
- gi.setmodel(self, self.model);
- self.blocked = GameTurretAdapters.turret_blocked;
- gi.linkentity(self);
- }
-
- public static void SP_turret_driver(edict_t self) {
- if (deathmatch.value != 0) {
- G_FreeEdict(self);
- return;
- }
-
- self.movetype = MOVETYPE_PUSH;
- self.solid = SOLID_BBOX;
- self.s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2");
- VectorSet(self.mins, -16, -16, -24);
- VectorSet(self.maxs, 16, 16, 32);
-
- self.health = 100;
- self.gib_health = 0;
- self.mass = 200;
- self.viewheight = 24;
-
- self.die = GameTurretAdapters.turret_driver_die;
- self.monsterinfo.stand = M_Infantry.infantry_stand;
-
- self.flags |= FL_NO_KNOCKBACK;
-
- level.total_monsters++;
-
- self.svflags |= SVF_MONSTER;
- self.s.renderfx |= RF_FRAMELERP;
- self.takedamage = DAMAGE_AIM;
- self.use = GameUtilAdapters.monster_use;
- self.clipmask = MASK_MONSTERSOLID;
- VectorCopy(self.s.origin, self.s.old_origin);
- self.monsterinfo.aiflags |= AI_STAND_GROUND | AI_DUCKED;
-
- if (st.item != null) {
- self.item = FindItemByClassname(st.item);
- if (self.item == null)
- gi.dprintf(self.classname + " at " + vtos(self.s.origin) + " has bad item: " + st.item + "\n");
- }
-
- self.think = GameTurretAdapters.turret_driver_link;
- self.nextthink = level.time + FRAMETIME;
-
- gi.linkentity(self);
- }
-}
+public class GameTurret {
+
+ public static void AnglesNormalize(float[] vec) {
+ while (vec[0] > 360)
+ vec[0] -= 360;
+ while (vec[0] < 0)
+ vec[0] += 360;
+ while (vec[1] > 360)
+ vec[1] -= 360;
+ while (vec[1] < 0)
+ vec[1] += 360;
+ }
+
+ public static float SnapToEights(float x) {
+ x *= 8.0;
+ if (x > 0.0)
+ x += 0.5;
+ else
+ x -= 0.5;
+ return 0.125f * (int) x;
+ }
+
+ /*
+ * QUAKED turret_breach (0 0 0) ? This portion of the turret can change both
+ * pitch and yaw. The model should be made with a flat pitch. It (and the
+ * associated base) need to be oriented towards 0. Use "angle" to set the
+ * starting angle.
+ *
+ * "speed" default 50 "dmg" default 10 "angle" point this forward "target"
+ * point this at an info_notnull at the muzzle tip "minpitch" min acceptable
+ * pitch angle : default -30 "maxpitch" max acceptable pitch angle : default
+ * 30 "minyaw" min acceptable yaw angle : default 0 "maxyaw" max acceptable
+ * yaw angle : default 360
+ */
+
+ public static void turret_breach_fire(edict_t self) {
+ float[] f = { 0, 0, 0 }, r = { 0, 0, 0 }, u = { 0, 0, 0 };
+ float[] start = { 0, 0, 0 };
+ int damage;
+ int speed;
+
+ Math3D.AngleVectors(self.s.angles, f, r, u);
+ Math3D.VectorMA(self.s.origin, self.move_origin[0], f, start);
+ Math3D.VectorMA(start, self.move_origin[1], r, start);
+ Math3D.VectorMA(start, self.move_origin[2], u, start);
+
+ damage = (int) (100 + Lib.random() * 50);
+ speed = (int) (550 + 50 * GameBase.skill.value);
+ Fire.fire_rocket(self.teammaster.owner, start, f, damage, speed, 150,
+ damage);
+ GameBase.gi.positioned_sound(start, self, Defines.CHAN_WEAPON,
+ GameBase.gi.soundindex("weapons/rocklf1a.wav"), 1,
+ Defines.ATTN_NORM, 0);
+ }
+
+ public static void SP_turret_breach(edict_t self) {
+ self.solid = Defines.SOLID_BSP;
+ self.movetype = Defines.MOVETYPE_PUSH;
+ GameBase.gi.setmodel(self, self.model);
+
+ if (self.speed == 0)
+ self.speed = 50;
+ if (self.dmg == 0)
+ self.dmg = 10;
+
+ if (GameBase.st.minpitch == 0)
+ GameBase.st.minpitch = -30;
+ if (GameBase.st.maxpitch == 0)
+ GameBase.st.maxpitch = 30;
+ if (GameBase.st.maxyaw == 0)
+ GameBase.st.maxyaw = 360;
+
+ self.pos1[Defines.PITCH] = -1 * GameBase.st.minpitch;
+ self.pos1[Defines.YAW] = GameBase.st.minyaw;
+ self.pos2[Defines.PITCH] = -1 * GameBase.st.maxpitch;
+ self.pos2[Defines.YAW] = GameBase.st.maxyaw;
+
+ self.ideal_yaw = self.s.angles[Defines.YAW];
+ self.move_angles[Defines.YAW] = self.ideal_yaw;
+
+ self.blocked = GameTurret.turret_blocked;
+
+ self.think = GameTurret.turret_breach_finish_init;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ GameBase.gi.linkentity(self);
+ }
+
+ /*
+ * QUAKED turret_base (0 0 0) ? This portion of the turret changes yaw only.
+ * MUST be teamed with a turret_breach.
+ */
+
+ public static void SP_turret_base(edict_t self) {
+ self.solid = Defines.SOLID_BSP;
+ self.movetype = Defines.MOVETYPE_PUSH;
+ GameBase.gi.setmodel(self, self.model);
+ self.blocked = GameTurret.turret_blocked;
+ GameBase.gi.linkentity(self);
+ }
+
+ public static void SP_turret_driver(edict_t self) {
+ if (GameBase.deathmatch.value != 0) {
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+
+ self.movetype = Defines.MOVETYPE_PUSH;
+ self.solid = Defines.SOLID_BBOX;
+ self.s.modelindex = GameBase.gi
+ .modelindex("models/monsters/infantry/tris.md2");
+ Math3D.VectorSet(self.mins, -16, -16, -24);
+ Math3D.VectorSet(self.maxs, 16, 16, 32);
+
+ self.health = 100;
+ self.gib_health = 0;
+ self.mass = 200;
+ self.viewheight = 24;
+
+ self.die = GameTurret.turret_driver_die;
+ self.monsterinfo.stand = M_Infantry.infantry_stand;
+
+ self.flags |= Defines.FL_NO_KNOCKBACK;
+
+ GameBase.level.total_monsters++;
+
+ self.svflags |= Defines.SVF_MONSTER;
+ self.s.renderfx |= Defines.RF_FRAMELERP;
+ self.takedamage = Defines.DAMAGE_AIM;
+ self.use = GameUtil.monster_use;
+ self.clipmask = Defines.MASK_MONSTERSOLID;
+ Math3D.VectorCopy(self.s.origin, self.s.old_origin);
+ self.monsterinfo.aiflags |= Defines.AI_STAND_GROUND | Defines.AI_DUCKED;
+
+ if (GameBase.st.item != null) {
+ self.item = GameUtil.FindItemByClassname(GameBase.st.item);
+ if (self.item == null)
+ GameBase.gi.dprintf(self.classname + " at "
+ + Lib.vtos(self.s.origin) + " has bad item: "
+ + GameBase.st.item + "\n");
+ }
+
+ self.think = GameTurret.turret_driver_link;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+
+ GameBase.gi.linkentity(self);
+ }
+
+ static EntBlockedAdapter turret_blocked = new EntBlockedAdapter() {
+
+ public void blocked(edict_t self, edict_t other) {
+ edict_t attacker;
+
+ if (other.takedamage != 0) {
+ if (self.teammaster.owner != null)
+ attacker = self.teammaster.owner;
+ else
+ attacker = self.teammaster;
+ GameUtil.T_Damage(other, self, attacker, Globals.vec3_origin,
+ other.s.origin, Globals.vec3_origin,
+ self.teammaster.dmg, 10, 0, Defines.MOD_CRUSH);
+ }
+ }
+ };
+
+ static EntThinkAdapter turret_breach_think = new EntThinkAdapter() {
+ public boolean think(edict_t self) {
+
+ edict_t ent;
+ float[] current_angles = { 0, 0, 0 };
+ float[] delta = { 0, 0, 0 };
+
+ Math3D.VectorCopy(self.s.angles, current_angles);
+ GameTurret.AnglesNormalize(current_angles);
+
+ GameTurret.AnglesNormalize(self.move_angles);
+ if (self.move_angles[Defines.PITCH] > 180)
+ self.move_angles[Defines.PITCH] -= 360;
+
+ // clamp angles to mins & maxs
+ if (self.move_angles[Defines.PITCH] > self.pos1[Defines.PITCH])
+ self.move_angles[Defines.PITCH] = self.pos1[Defines.PITCH];
+ else if (self.move_angles[Defines.PITCH] < self.pos2[Defines.PITCH])
+ self.move_angles[Defines.PITCH] = self.pos2[Defines.PITCH];
+
+ if ((self.move_angles[Defines.YAW] < self.pos1[Defines.YAW])
+ || (self.move_angles[Defines.YAW] > self.pos2[Defines.YAW])) {
+ float dmin, dmax;
+
+ dmin = Math.abs(self.pos1[Defines.YAW]
+ - self.move_angles[Defines.YAW]);
+ if (dmin < -180)
+ dmin += 360;
+ else if (dmin > 180)
+ dmin -= 360;
+ dmax = Math.abs(self.pos2[Defines.YAW]
+ - self.move_angles[Defines.YAW]);
+ if (dmax < -180)
+ dmax += 360;
+ else if (dmax > 180)
+ dmax -= 360;
+ if (Math.abs(dmin) < Math.abs(dmax))
+ self.move_angles[Defines.YAW] = self.pos1[Defines.YAW];
+ else
+ self.move_angles[Defines.YAW] = self.pos2[Defines.YAW];
+ }
+
+ Math3D.VectorSubtract(self.move_angles, current_angles, delta);
+ if (delta[0] < -180)
+ delta[0] += 360;
+ else if (delta[0] > 180)
+ delta[0] -= 360;
+ if (delta[1] < -180)
+ delta[1] += 360;
+ else if (delta[1] > 180)
+ delta[1] -= 360;
+ delta[2] = 0;
+
+ if (delta[0] > self.speed * Defines.FRAMETIME)
+ delta[0] = self.speed * Defines.FRAMETIME;
+ if (delta[0] < -1 * self.speed * Defines.FRAMETIME)
+ delta[0] = -1 * self.speed * Defines.FRAMETIME;
+ if (delta[1] > self.speed * Defines.FRAMETIME)
+ delta[1] = self.speed * Defines.FRAMETIME;
+ if (delta[1] < -1 * self.speed * Defines.FRAMETIME)
+ delta[1] = -1 * self.speed * Defines.FRAMETIME;
+
+ Math3D.VectorScale(delta, 1.0f / Defines.FRAMETIME, self.avelocity);
+
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+
+ for (ent = self.teammaster; ent != null; ent = ent.teamchain)
+ ent.avelocity[1] = self.avelocity[1];
+
+ // if we have adriver, adjust his velocities
+ if (self.owner != null) {
+ float angle;
+ float target_z;
+ float diff;
+ float[] target = { 0, 0, 0 };
+ float[] dir = { 0, 0, 0 };
+
+ // angular is easy, just copy ours
+ self.owner.avelocity[0] = self.avelocity[0];
+ self.owner.avelocity[1] = self.avelocity[1];
+
+ // x & y
+ angle = self.s.angles[1] + self.owner.move_origin[1];
+ angle *= (Math.PI * 2 / 360);
+ target[0] = GameTurret
+ .SnapToEights((float) (self.s.origin[0] + Math
+ .cos(angle)
+ * self.owner.move_origin[0]));
+ target[1] = GameTurret
+ .SnapToEights((float) (self.s.origin[1] + Math
+ .sin(angle)
+ * self.owner.move_origin[0]));
+ target[2] = self.owner.s.origin[2];
+
+ Math3D.VectorSubtract(target, self.owner.s.origin, dir);
+ self.owner.velocity[0] = dir[0] * 1.0f / Defines.FRAMETIME;
+ self.owner.velocity[1] = dir[1] * 1.0f / Defines.FRAMETIME;
+
+ // z
+ angle = self.s.angles[Defines.PITCH]
+ * (float) (Math.PI * 2f / 360f);
+ target_z = GameTurret
+ .SnapToEights((float) (self.s.origin[2]
+ + self.owner.move_origin[0] * Math.tan(angle) + self.owner.move_origin[2]));
+
+ diff = target_z - self.owner.s.origin[2];
+ self.owner.velocity[2] = diff * 1.0f / Defines.FRAMETIME;
+
+ if ((self.spawnflags & 65536) != 0) {
+ GameTurret.turret_breach_fire(self);
+ self.spawnflags &= ~65536;
+ }
+ }
+ return true;
+ }
+ };
+
+ static EntThinkAdapter turret_breach_finish_init = new EntThinkAdapter() {
+ public boolean think(edict_t self) {
+
+ // get and save info for muzzle location
+ if (self.target == null) {
+ GameBase.gi.dprintf(self.classname + " at "
+ + Lib.vtos(self.s.origin) + " needs a target\n");
+ } else {
+ self.target_ent = GameBase.G_PickTarget(self.target);
+ Math3D.VectorSubtract(self.target_ent.s.origin, self.s.origin,
+ self.move_origin);
+ GameUtil.G_FreeEdict(self.target_ent);
+ }
+
+ self.teammaster.dmg = self.dmg;
+ self.think = turret_breach_think;
+ self.think.think(self);
+ return true;
+ }
+ };
+
+ /*
+ * QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32) Must NOT be on the
+ * team with the rest of the turret parts. Instead it must target the
+ * turret_breach.
+ */
+ static EntDieAdapter turret_driver_die = new EntDieAdapter() {
+ public void die(edict_t self, edict_t inflictor, edict_t attacker,
+ int damage, float[] point) {
+
+ edict_t ent;
+
+ // level the gun
+ self.target_ent.move_angles[0] = 0;
+
+ // remove the driver from the end of them team chain
+ for (ent = self.target_ent.teammaster; ent.teamchain != self; ent = ent.teamchain)
+ ;
+ ent.teamchain = null;
+ self.teammaster = null;
+ self.flags &= ~Defines.FL_TEAMSLAVE;
+
+ self.target_ent.owner = null;
+ self.target_ent.teammaster.owner = null;
+
+ //TODO: null appended as last missing argument, was unclean. rst
+ M_Infantry.infantry_die
+ .die(self, inflictor, attacker, damage, null);
+ }
+ };
+
+ static EntThinkAdapter turret_driver_think = new EntThinkAdapter() {
+ public boolean think(edict_t self) {
+
+ float[] target = { 0, 0, 0 };
+ float[] dir = { 0, 0, 0 };
+ float reaction_time;
+
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+
+ if (self.enemy != null
+ && (!self.enemy.inuse || self.enemy.health <= 0))
+ self.enemy = null;
+
+ if (null == self.enemy) {
+ if (!GameUtil.FindTarget(self))
+ return true;
+ self.monsterinfo.trail_time = GameBase.level.time;
+ self.monsterinfo.aiflags &= ~Defines.AI_LOST_SIGHT;
+ } else {
+ if (GameUtil.visible(self, self.enemy)) {
+ if ((self.monsterinfo.aiflags & Defines.AI_LOST_SIGHT) != 0) {
+ self.monsterinfo.trail_time = GameBase.level.time;
+ self.monsterinfo.aiflags &= ~Defines.AI_LOST_SIGHT;
+ }
+ } else {
+ self.monsterinfo.aiflags |= Defines.AI_LOST_SIGHT;
+ return true;
+ }
+ }
+
+ // let the turret know where we want it to aim
+ Math3D.VectorCopy(self.enemy.s.origin, target);
+ target[2] += self.enemy.viewheight;
+ Math3D.VectorSubtract(target, self.target_ent.s.origin, dir);
+ Math3D.vectoangles(dir, self.target_ent.move_angles);
+
+ // decide if we should shoot
+ if (GameBase.level.time < self.monsterinfo.attack_finished)
+ return true;
+
+ reaction_time = (3 - GameBase.skill.value) * 1.0f;
+ if ((GameBase.level.time - self.monsterinfo.trail_time) < reaction_time)
+ return true;
+
+ self.monsterinfo.attack_finished = GameBase.level.time
+ + reaction_time + 1.0f;
+ //FIXME how do we really want to pass this along?
+ self.target_ent.spawnflags |= 65536;
+ return true;
+ }
+ };
+
+ public static EntThinkAdapter turret_driver_link = new EntThinkAdapter() {
+ public boolean think(edict_t self) {
+
+ float[] vec = { 0, 0, 0 };
+ edict_t ent;
+
+ self.think = turret_driver_think;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+
+ self.target_ent = GameBase.G_PickTarget(self.target);
+ self.target_ent.owner = self;
+ self.target_ent.teammaster.owner = self;
+ Math3D.VectorCopy(self.target_ent.s.angles, self.s.angles);
+
+ vec[0] = self.target_ent.s.origin[0] - self.s.origin[0];
+ vec[1] = self.target_ent.s.origin[1] - self.s.origin[1];
+ vec[2] = 0;
+ self.move_origin[0] = Math3D.VectorLength(vec);
+
+ Math3D.VectorSubtract(self.s.origin, self.target_ent.s.origin, vec);
+ Math3D.vectoangles(vec, vec);
+ GameTurret.AnglesNormalize(vec);
+ self.move_origin[1] = vec[1];
+
+ self.move_origin[2] = self.s.origin[2]
+ - self.target_ent.s.origin[2];
+
+ // add the driver to the end of them team chain
+ for (ent = self.target_ent.teammaster; ent.teamchain != null; ent = ent.teamchain)
+ ;
+ ent.teamchain = self;
+ self.teammaster = self.target_ent.teammaster;
+ self.flags |= Defines.FL_TEAMSLAVE;
+ return true;
+ }
+ };
+} \ No newline at end of file