diff options
Diffstat (limited to 'src/jake2/game/GameWeapon.java')
-rw-r--r-- | src/jake2/game/GameWeapon.java | 839 |
1 files changed, 648 insertions, 191 deletions
diff --git a/src/jake2/game/GameWeapon.java b/src/jake2/game/GameWeapon.java index e8d48ff..f0c1446 100644 --- a/src/jake2/game/GameWeapon.java +++ b/src/jake2/game/GameWeapon.java @@ -1,165 +1,76 @@ /* - * 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. -// Created on 12.11.2003 by RST. -// $Id: GameWeapon.java,v 1.4 2005-02-13 19:22:09 salomo Exp $ -package jake2.game; - -import jake2.Defines; -import jake2.Globals; -import jake2.util.*; - -public class GameWeapon { +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. - /* - * =============== PlayerNoise - * - * Each player can have two noise objects associated with it: a personal - * noise (jumping, pain, weapon firing), and a weapon target noise (bullet - * wall impacts) - * - * Monsters that don't directly see the player can move to a noise in hopes - * of seeing the player from there. =============== - */ - static void PlayerNoise(edict_t who, float[] where, int type) { - edict_t noise; +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. - if (type == Defines.PNOISE_WEAPON) { - if (who.client.silencer_shots > 0) { - who.client.silencer_shots--; - return; - } - } +See the GNU General Public License for more details. - if (GameBase.deathmatch.value != 0) - return; +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. - if ((who.flags & Defines.FL_NOTARGET) != 0) - return; +*/ - if (who.mynoise == null) { - noise = GameUtil.G_Spawn(); - noise.classname = "player_noise"; - Math3D.VectorSet(noise.mins, -8, -8, -8); - Math3D.VectorSet(noise.maxs, 8, 8, 8); - noise.owner = who; - noise.svflags = Defines.SVF_NOCLIENT; - who.mynoise = noise; +// Created on on 12.11.2003 by RST. +// $Id: GameWeapon.java,v 1.5 2005-11-16 22:24:52 salomo Exp $ - noise = GameUtil.G_Spawn(); - noise.classname = "player_noise"; - Math3D.VectorSet(noise.mins, -8, -8, -8); - Math3D.VectorSet(noise.maxs, 8, 8, 8); - noise.owner = who; - noise.svflags = Defines.SVF_NOCLIENT; - who.mynoise2 = noise; - } +package jake2.game; - if (type == Defines.PNOISE_SELF || type == Defines.PNOISE_WEAPON) { - noise = who.mynoise; - GameBase.level.sound_entity = noise; - GameBase.level.sound_entity_framenum = GameBase.level.framenum; - } else // type == PNOISE_IMPACT - { - noise = who.mynoise2; - GameBase.level.sound2_entity = noise; - GameBase.level.sound2_entity_framenum = GameBase.level.framenum; - } - Math3D.VectorCopy(where, noise.s.origin); - Math3D.VectorSubtract(where, noise.maxs, noise.absmin); - Math3D.VectorAdd(where, noise.maxs, noise.absmax); - noise.teleport_time = GameBase.level.time; - GameBase.gi.linkentity(noise); - } +import jake2.*; +import jake2.client.*; +import jake2.game.*; +import jake2.qcommon.*; +import jake2.render.*; +import jake2.server.*; +import jake2.util.Lib; +import jake2.util.Math3D; - /* - * ================= check_dodge - * - * This is a support routine used when a client is firing a non-instant - * attack weapon. It checks to see if a monster's dodge function should be - * called. ================= - */ - static void check_dodge(edict_t self, float[] start, float[] dir, int speed) { - float[] end = { 0, 0, 0 }; - float[] v = { 0, 0, 0 }; - trace_t tr; - float eta; - // easy mode only ducks one quarter the time - if (GameBase.skill.value == 0) { - if (Lib.random() > 0.25) - return; - } - Math3D.VectorMA(start, 8192, dir, end); - tr = GameBase.gi.trace(start, null, null, end, self, Defines.MASK_SHOT); - if ((tr.ent != null) && (tr.ent.svflags & Defines.SVF_MONSTER) != 0 - && (tr.ent.health > 0) && (null != tr.ent.monsterinfo.dodge) - && GameUtil.infront(tr.ent, self)) { - Math3D.VectorSubtract(tr.endpos, start, v); - eta = (Math3D.VectorLength(v) - tr.ent.maxs[0]) / speed; - tr.ent.monsterinfo.dodge.dodge(tr.ent, self, eta); - } - } +public class GameWeapon { - /* - * ================= fire_blaster - * - * Fires a single blaster bolt. Used by the blaster and hyper blaster. - * ================= - */ static EntTouchAdapter blaster_touch = new EntTouchAdapter() { - + public void touch(edict_t self, edict_t other, cplane_t plane, csurface_t surf) { int mod; - + if (other == self.owner) return; - + if (surf != null && (surf.flags & Defines.SURF_SKY) != 0) { GameUtil.G_FreeEdict(self); return; } - + if (self.owner.client != null) - GameWeapon.PlayerNoise(self.owner, self.s.origin, + PlayerWeapon.PlayerNoise(self.owner, self.s.origin, Defines.PNOISE_IMPACT); - + if (other.takedamage != 0) { if ((self.spawnflags & 1) != 0) mod = Defines.MOD_HYPERBLASTER; else mod = Defines.MOD_BLASTER; - + // bugfix null plane rst float[] normal; if (plane == null) normal = new float[3]; else normal = plane.normal; - - GameUtil.T_Damage(other, self, self.owner, self.velocity, + + GameCombat.T_Damage(other, self, self.owner, self.velocity, self.s.origin, normal, self.dmg, 1, Defines.DAMAGE_ENERGY, mod); - + } else { GameBase.gi.WriteByte(Defines.svc_temp_entity); GameBase.gi.WriteByte(Defines.TE_BLASTER); @@ -170,27 +81,27 @@ public class GameWeapon { GameBase.gi.WriteDir(plane.normal); GameBase.gi.multicast(self.s.origin, Defines.MULTICAST_PVS); } - + GameUtil.G_FreeEdict(self); } }; - + static EntThinkAdapter Grenade_Explode = new EntThinkAdapter() { public boolean think(edict_t ent) { float[] origin = { 0, 0, 0 }; int mod; - + if (ent.owner.client != null) - GameWeapon.PlayerNoise(ent.owner, ent.s.origin, + PlayerWeapon.PlayerNoise(ent.owner, ent.s.origin, Defines.PNOISE_IMPACT); - + //FIXME: if we are onground then raise our Z just a bit since we // are a point? if (ent.enemy != null) { float points = 0; float[] v = { 0, 0, 0 }; float[] dir = { 0, 0, 0 }; - + Math3D.VectorAdd(ent.enemy.mins, ent.enemy.maxs, v); Math3D.VectorMA(ent.enemy.s.origin, 0.5f, v, v); Math3D.VectorSubtract(ent.s.origin, v, v); @@ -200,20 +111,20 @@ public class GameWeapon { mod = Defines.MOD_HANDGRENADE; else mod = Defines.MOD_GRENADE; - GameUtil.T_Damage(ent.enemy, ent, ent.owner, dir, ent.s.origin, + GameCombat.T_Damage(ent.enemy, ent, ent.owner, dir, ent.s.origin, Globals.vec3_origin, (int) points, (int) points, Defines.DAMAGE_RADIUS, mod); } - + if ((ent.spawnflags & 2) != 0) mod = Defines.MOD_HELD_GRENADE; else if ((ent.spawnflags & 1) != 0) mod = Defines.MOD_HG_SPLASH; else mod = Defines.MOD_G_SPLASH; - GameUtil.T_RadiusDamage(ent, ent.owner, ent.dmg, ent.enemy, + GameCombat.T_RadiusDamage(ent, ent.owner, ent.dmg, ent.enemy, ent.dmg_radius, mod); - + Math3D.VectorMA(ent.s.origin, -0.02f, ent.velocity, origin); GameBase.gi.WriteByte(Defines.svc_temp_entity); if (ent.waterlevel != 0) { @@ -229,23 +140,22 @@ public class GameWeapon { } GameBase.gi.WritePosition(origin); GameBase.gi.multicast(ent.s.origin, Defines.MULTICAST_PHS); - + GameUtil.G_FreeEdict(ent); return true; } }; - static EntTouchAdapter Grenade_Touch = new EntTouchAdapter() { public void touch(edict_t ent, edict_t other, cplane_t plane, csurface_t surf) { if (other == ent.owner) return; - + if (surf != null && 0 != (surf.flags & Defines.SURF_SKY)) { GameUtil.G_FreeEdict(ent); return; } - + if (other.takedamage == 0) { if ((ent.spawnflags & 1) != 0) { if (Lib.random() > 0.5f) @@ -263,38 +173,40 @@ public class GameWeapon { } return; } - + ent.enemy = other; Grenade_Explode.think(ent); } }; - + /* - * ================= fire_rocket ================= + * ================= + * fire_rocket + * ================= */ static EntTouchAdapter rocket_touch = new EntTouchAdapter() { public void touch(edict_t ent, edict_t other, cplane_t plane, csurface_t surf) { float[] origin = { 0, 0, 0 }; int n; - + if (other == ent.owner) return; - + if (surf != null && (surf.flags & Defines.SURF_SKY) != 0) { GameUtil.G_FreeEdict(ent); return; } - + if (ent.owner.client != null) - GameWeapon.PlayerNoise(ent.owner, ent.s.origin, + PlayerWeapon.PlayerNoise(ent.owner, ent.s.origin, Defines.PNOISE_IMPACT); - + // calculate position for the explosion entity Math3D.VectorMA(ent.s.origin, -0.02f, ent.velocity, origin); - + if (other.takedamage != 0) { - GameUtil.T_Damage(other, ent, ent.owner, ent.velocity, + GameCombat.T_Damage(other, ent, ent.owner, ent.velocity, ent.s.origin, plane.normal, ent.dmg, 0, 0, Defines.MOD_ROCKET); } else { @@ -306,16 +218,16 @@ public class GameWeapon { | Defines.SURF_TRANS66 | Defines.SURF_FLOWING))) { n = Lib.rand() % 5; while (n-- > 0) - GameAI.ThrowDebris(ent, + GameMisc.ThrowDebris(ent, "models/objects/debris2/tris.md2", 2, ent.s.origin); } } } - - GameUtil.T_RadiusDamage(ent, ent.owner, ent.radius_dmg, other, + + GameCombat.T_RadiusDamage(ent, ent.owner, ent.radius_dmg, other, ent.dmg_radius, Defines.MOD_R_SPLASH); - + GameBase.gi.WriteByte(Defines.svc_temp_entity); if (ent.waterlevel != 0) GameBase.gi.WriteByte(Defines.TE_ROCKET_EXPLOSION_WATER); @@ -323,13 +235,14 @@ public class GameWeapon { GameBase.gi.WriteByte(Defines.TE_ROCKET_EXPLOSION); GameBase.gi.WritePosition(origin); GameBase.gi.multicast(ent.s.origin, Defines.MULTICAST_PHS); - + GameUtil.G_FreeEdict(ent); } }; - /* - * ================= fire_bfg ================= + * ================= + * fire_bfg + * ================= */ static EntThinkAdapter bfg_explode = new EntThinkAdapter() { public boolean think(edict_t self) { @@ -337,9 +250,9 @@ public class GameWeapon { float points; float[] v = { 0, 0, 0 }; float dist; - + EdictIterator edit = null; - + if (self.s.frame == 0) { // the BFG effect ent = null; @@ -350,11 +263,11 @@ public class GameWeapon { continue; if (ent == self.owner) continue; - if (!GameUtil.CanDamage(ent, self)) + if (!GameCombat.CanDamage(ent, self)) continue; - if (!GameUtil.CanDamage(ent, self.owner)) + if (!GameCombat.CanDamage(ent, self.owner)) continue; - + Math3D.VectorAdd(ent.mins, ent.maxs, v); Math3D.VectorMA(ent.s.origin, 0.5f, v, v); Math3D.VectorSubtract(self.s.origin, v, v); @@ -363,49 +276,49 @@ public class GameWeapon { / self.dmg_radius))); if (ent == self.owner) points = points * 0.5f; - + GameBase.gi.WriteByte(Defines.svc_temp_entity); GameBase.gi.WriteByte(Defines.TE_BFG_EXPLOSION); GameBase.gi.WritePosition(ent.s.origin); GameBase.gi.multicast(ent.s.origin, Defines.MULTICAST_PHS); - GameUtil.T_Damage(ent, self, self.owner, self.velocity, + GameCombat.T_Damage(ent, self, self.owner, self.velocity, ent.s.origin, Globals.vec3_origin, (int) points, 0, Defines.DAMAGE_ENERGY, Defines.MOD_BFG_EFFECT); } } - + self.nextthink = GameBase.level.time + Defines.FRAMETIME; self.s.frame++; if (self.s.frame == 5) self.think = GameUtil.G_FreeEdictA; return true; - + } }; - + static EntTouchAdapter bfg_touch = new EntTouchAdapter() { public void touch(edict_t self, edict_t other, cplane_t plane, csurface_t surf) { if (other == self.owner) return; - + if (surf != null && (surf.flags & Defines.SURF_SKY) != 0) { GameUtil.G_FreeEdict(self); return; } - + if (self.owner.client != null) - GameWeapon.PlayerNoise(self.owner, self.s.origin, + PlayerWeapon.PlayerNoise(self.owner, self.s.origin, Defines.PNOISE_IMPACT); - + // core explosion - prevents firing it into the wall/floor if (other.takedamage != 0) - GameUtil.T_Damage(other, self, self.owner, self.velocity, + GameCombat.T_Damage(other, self, self.owner, self.velocity, self.s.origin, plane.normal, 200, 0, 0, Defines.MOD_BFG_BLAST); - GameUtil.T_RadiusDamage(self, self.owner, 200, other, 100, + GameCombat.T_RadiusDamage(self, self.owner, 200, other, 100, Defines.MOD_BFG_BLAST); - + GameBase.gi.sound(self, Defines.CHAN_VOICE, GameBase.gi .soundindex("weapons/bfg__x1b.wav"), 1, Defines.ATTN_NORM, 0); @@ -421,14 +334,14 @@ public class GameWeapon { self.think = bfg_explode; self.nextthink = GameBase.level.time + Defines.FRAMETIME; self.enemy = other; - + GameBase.gi.WriteByte(Defines.svc_temp_entity); GameBase.gi.WriteByte(Defines.TE_BFG_BIGEXPLOSION); GameBase.gi.WritePosition(self.s.origin); GameBase.gi.multicast(self.s.origin, Defines.MULTICAST_PVS); } }; - + static EntThinkAdapter bfg_think = new EntThinkAdapter() { public boolean think(edict_t self) { edict_t ent; @@ -439,35 +352,35 @@ public class GameWeapon { float[] end = { 0, 0, 0 }; int dmg; trace_t tr; - + if (GameBase.deathmatch.value != 0) dmg = 5; else dmg = 10; - + EdictIterator edit = null; while ((edit = GameBase.findradius(edit, self.s.origin, 256)) != null) { ent = edit.o; - + if (ent == self) continue; - + if (ent == self.owner) continue; - + if (ent.takedamage == 0) continue; - + if (0 == (ent.svflags & Defines.SVF_MONSTER) && (null == ent.client) && (Lib.strcmp(ent.classname, "misc_explobox") != 0)) continue; - + Math3D.VectorMA(ent.absmin, 0.5f, ent.size, point); - + Math3D.VectorSubtract(point, self.s.origin, dir); Math3D.VectorNormalize(dir); - + ignore = self; Math3D.VectorCopy(self.s.origin, start); Math3D.VectorMA(start, 2048, dir, end); @@ -475,18 +388,18 @@ public class GameWeapon { tr = GameBase.gi.trace(start, null, null, end, ignore, Defines.CONTENTS_SOLID | Defines.CONTENTS_MONSTER | Defines.CONTENTS_DEADMONSTER); - + if (null == tr.ent) break; - + // hurt it if we can if ((tr.ent.takedamage != 0) && 0 == (tr.ent.flags & Defines.FL_IMMUNE_LASER) && (tr.ent != self.owner)) - GameUtil.T_Damage(tr.ent, self, self.owner, dir, + GameCombat.T_Damage(tr.ent, self, self.owner, dir, tr.endpos, Globals.vec3_origin, dmg, 1, Defines.DAMAGE_ENERGY, Defines.MOD_BFG_LASER); - + // if we hit something that's not a monster or player we're // done if (0 == (tr.ent.svflags & Defines.SVF_MONSTER) @@ -500,20 +413,564 @@ public class GameWeapon { GameBase.gi.multicast(tr.endpos, Defines.MULTICAST_PVS); break; } - + ignore = tr.ent; Math3D.VectorCopy(tr.endpos, start); } - + GameBase.gi.WriteByte(Defines.svc_temp_entity); GameBase.gi.WriteByte(Defines.TE_BFG_LASER); GameBase.gi.WritePosition(self.s.origin); GameBase.gi.WritePosition(tr.endpos); GameBase.gi.multicast(self.s.origin, Defines.MULTICAST_PHS); } - + self.nextthink = GameBase.level.time + Defines.FRAMETIME; return true; } }; -}
\ No newline at end of file + + /* + * ================= + * check_dodge + * + * This is a support routine used when a client is firing a non-instant + * attack weapon. It checks to see if a monster's dodge function should be + * called. + * ================= + */ + static void check_dodge(edict_t self, float[] start, float[] dir, int speed) { + float[] end = { 0, 0, 0 }; + float[] v = { 0, 0, 0 }; + trace_t tr; + float eta; + + // easy mode only ducks one quarter the time + if (GameBase.skill.value == 0) { + if (Lib.random() > 0.25) + return; + } + Math3D.VectorMA(start, 8192, dir, end); + tr = GameBase.gi.trace(start, null, null, end, self, Defines.MASK_SHOT); + if ((tr.ent != null) && (tr.ent.svflags & Defines.SVF_MONSTER) != 0 + && (tr.ent.health > 0) && (null != tr.ent.monsterinfo.dodge) + && GameUtil.infront(tr.ent, self)) { + Math3D.VectorSubtract(tr.endpos, start, v); + eta = (Math3D.VectorLength(v) - tr.ent.maxs[0]) / speed; + tr.ent.monsterinfo.dodge.dodge(tr.ent, self, eta); + } + } + + /* + * ================= + * fire_hit + * + * Used for all impact (hit/punch/slash) attacks + * ================= + */ + public static boolean fire_hit(edict_t self, float[] aim, int damage, + int kick) { + trace_t tr; + float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 }; + float[] v = { 0, 0, 0 }; + float[] point = { 0, 0, 0 }; + float range; + float[] dir = { 0, 0, 0 }; + + //see if enemy is in range + Math3D.VectorSubtract(self.enemy.s.origin, self.s.origin, dir); + range = Math3D.VectorLength(dir); + if (range > aim[0]) + return false; + + if (aim[1] > self.mins[0] && aim[1] < self.maxs[0]) { + // the hit is straight on so back the range up to the edge of their + // bbox + range -= self.enemy.maxs[0]; + } else { + // this is a side hit so adjust the "right" value out to the edge of + // their bbox + if (aim[1] < 0) + aim[1] = self.enemy.mins[0]; + else + aim[1] = self.enemy.maxs[0]; + } + + Math3D.VectorMA(self.s.origin, range, dir, point); + + tr = GameBase.gi.trace(self.s.origin, null, null, point, self, + Defines.MASK_SHOT); + if (tr.fraction < 1) { + if (0 == tr.ent.takedamage) + return false; + // if it will hit any client/monster then hit the one we wanted to + // hit + if ((tr.ent.svflags & Defines.SVF_MONSTER) != 0 + || (tr.ent.client != null)) + tr.ent = self.enemy; + } + + Math3D.AngleVectors(self.s.angles, forward, right, up); + Math3D.VectorMA(self.s.origin, range, forward, point); + Math3D.VectorMA(point, aim[1], right, point); + Math3D.VectorMA(point, aim[2], up, point); + Math3D.VectorSubtract(point, self.enemy.s.origin, dir); + + // do the damage + GameCombat.T_Damage(tr.ent, self, self, dir, point, Globals.vec3_origin, + damage, kick / 2, Defines.DAMAGE_NO_KNOCKBACK, Defines.MOD_HIT); + + if (0 == (tr.ent.svflags & Defines.SVF_MONSTER) + && (null == tr.ent.client)) + return false; + + // do our special form of knockback here + Math3D.VectorMA(self.enemy.absmin, 0.5f, self.enemy.size, v); + Math3D.VectorSubtract(v, point, v); + Math3D.VectorNormalize(v); + Math3D.VectorMA(self.enemy.velocity, kick, v, self.enemy.velocity); + if (self.enemy.velocity[2] > 0) + self.enemy.groundentity = null; + return true; + } + + /* + * ================= + * fire_lead + * + * This is an internal support routine used for bullet/pellet based weapons. + * ================= + */ + public static void fire_lead(edict_t self, float[] start, float[] aimdir, + int damage, int kick, int te_impact, int hspread, int vspread, + int mod) { + trace_t tr; + float[] dir = { 0, 0, 0 }; + float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 }; + float[] end = { 0, 0, 0 }; + float r; + float u; + float[] water_start = { 0, 0, 0 }; + boolean water = false; + int content_mask = Defines.MASK_SHOT | Defines.MASK_WATER; + + tr = GameBase.gi.trace(self.s.origin, null, null, start, self, + Defines.MASK_SHOT); + if (!(tr.fraction < 1.0)) { + Math3D.vectoangles(aimdir, dir); + Math3D.AngleVectors(dir, forward, right, up); + + r = Lib.crandom() * hspread; + u = Lib.crandom() * vspread; + Math3D.VectorMA(start, 8192, forward, end); + Math3D.VectorMA(end, r, right, end); + Math3D.VectorMA(end, u, up, end); + + if ((GameBase.gi.pointcontents.pointcontents(start) & Defines.MASK_WATER) != 0) { + water = true; + Math3D.VectorCopy(start, water_start); + content_mask &= ~Defines.MASK_WATER; + } + + tr = GameBase.gi.trace(start, null, null, end, self, content_mask); + + // see if we hit water + if ((tr.contents & Defines.MASK_WATER) != 0) { + int color; + + water = true; + Math3D.VectorCopy(tr.endpos, water_start); + + if (!Math3D.VectorEquals(start, tr.endpos)) { + if ((tr.contents & Defines.CONTENTS_WATER) != 0) { + if (Lib.strcmp(tr.surface.name, "*brwater") == 0) + color = Defines.SPLASH_BROWN_WATER; + else + color = Defines.SPLASH_BLUE_WATER; + } else if ((tr.contents & Defines.CONTENTS_SLIME) != 0) + color = Defines.SPLASH_SLIME; + else if ((tr.contents & Defines.CONTENTS_LAVA) != 0) + color = Defines.SPLASH_LAVA; + else + color = Defines.SPLASH_UNKNOWN; + + if (color != Defines.SPLASH_UNKNOWN) { + GameBase.gi.WriteByte(Defines.svc_temp_entity); + GameBase.gi.WriteByte(Defines.TE_SPLASH); + GameBase.gi.WriteByte(8); + GameBase.gi.WritePosition(tr.endpos); + GameBase.gi.WriteDir(tr.plane.normal); + GameBase.gi.WriteByte(color); + GameBase.gi.multicast(tr.endpos, Defines.MULTICAST_PVS); + } + + // change bullet's course when it enters water + Math3D.VectorSubtract(end, start, dir); + Math3D.vectoangles(dir, dir); + Math3D.AngleVectors(dir, forward, right, up); + r = Lib.crandom() * hspread * 2; + u = Lib.crandom() * vspread * 2; + Math3D.VectorMA(water_start, 8192, forward, end); + Math3D.VectorMA(end, r, right, end); + Math3D.VectorMA(end, u, up, end); + } + + // re-trace ignoring water this time + tr = GameBase.gi.trace(water_start, null, null, end, self, + Defines.MASK_SHOT); + } + } + + // send gun puff / flash + if (!((tr.surface != null) && 0 != (tr.surface.flags & Defines.SURF_SKY))) { + if (tr.fraction < 1.0) { + if (tr.ent.takedamage != 0) { + GameCombat.T_Damage(tr.ent, self, self, aimdir, tr.endpos, + tr.plane.normal, damage, kick, + Defines.DAMAGE_BULLET, mod); + } else { + if (!"sky".equals(tr.surface.name)) { + GameBase.gi.WriteByte(Defines.svc_temp_entity); + GameBase.gi.WriteByte(te_impact); + GameBase.gi.WritePosition(tr.endpos); + GameBase.gi.WriteDir(tr.plane.normal); + GameBase.gi.multicast(tr.endpos, Defines.MULTICAST_PVS); + + if (self.client != null) + PlayerWeapon.PlayerNoise(self, tr.endpos, + Defines.PNOISE_IMPACT); + } + } + } + } + + // if went through water, determine where the end and make a bubble + // trail + if (water) { + float[] pos = { 0, 0, 0 }; + + Math3D.VectorSubtract(tr.endpos, water_start, dir); + Math3D.VectorNormalize(dir); + Math3D.VectorMA(tr.endpos, -2, dir, pos); + if ((GameBase.gi.pointcontents.pointcontents(pos) & Defines.MASK_WATER) != 0) + Math3D.VectorCopy(pos, tr.endpos); + else + tr = GameBase.gi.trace(pos, null, null, water_start, tr.ent, + Defines.MASK_WATER); + + Math3D.VectorAdd(water_start, tr.endpos, pos); + Math3D.VectorScale(pos, 0.5f, pos); + + GameBase.gi.WriteByte(Defines.svc_temp_entity); + GameBase.gi.WriteByte(Defines.TE_BUBBLETRAIL); + GameBase.gi.WritePosition(water_start); + GameBase.gi.WritePosition(tr.endpos); + GameBase.gi.multicast(pos, Defines.MULTICAST_PVS); + } + } + + /* + * ================= fire_bullet + * + * Fires a single round. Used for machinegun and chaingun. Would be fine for + * pistols, rifles, etc.... ================= + */ + public static void fire_bullet(edict_t self, float[] start, float[] aimdir, + int damage, int kick, int hspread, int vspread, int mod) { + fire_lead(self, start, aimdir, damage, kick, Defines.TE_GUNSHOT, + hspread, vspread, mod); + } + + /* + * ================= + * fire_shotgun + * + * Shoots shotgun pellets. Used by shotgun and super shotgun. + * ================= + */ + public static void fire_shotgun(edict_t self, float[] start, + float[] aimdir, int damage, int kick, int hspread, int vspread, + int count, int mod) { + int i; + + for (i = 0; i < count; i++) + fire_lead(self, start, aimdir, damage, kick, Defines.TE_SHOTGUN, + hspread, vspread, mod); + } + + /* + * ================= + * fire_blaster + * + * Fires a single blaster bolt. Used by the blaster and hyper blaster. + * ================= + */ + + public static void fire_blaster(edict_t self, float[] start, float[] dir, + int damage, int speed, int effect, boolean hyper) { + edict_t bolt; + trace_t tr; + + Math3D.VectorNormalize(dir); + + bolt = GameUtil.G_Spawn(); + bolt.svflags = Defines.SVF_DEADMONSTER; + // yes, I know it looks weird that projectiles are deadmonsters + // what this means is that when prediction is used against the object + // (blaster/hyperblaster shots), the player won't be solid clipped + // against + // the object. Right now trying to run into a firing hyperblaster + // is very jerky since you are predicted 'against' the shots. + Math3D.VectorCopy(start, bolt.s.origin); + Math3D.VectorCopy(start, bolt.s.old_origin); + Math3D.vectoangles(dir, bolt.s.angles); + Math3D.VectorScale(dir, speed, bolt.velocity); + bolt.movetype = Defines.MOVETYPE_FLYMISSILE; + bolt.clipmask = Defines.MASK_SHOT; + bolt.solid = Defines.SOLID_BBOX; + bolt.s.effects |= effect; + Math3D.VectorClear(bolt.mins); + Math3D.VectorClear(bolt.maxs); + bolt.s.modelindex = GameBase.gi + .modelindex("models/objects/laser/tris.md2"); + bolt.s.sound = GameBase.gi.soundindex("misc/lasfly.wav"); + bolt.owner = self; + bolt.touch = blaster_touch; + bolt.nextthink = GameBase.level.time + 2; + bolt.think = GameUtil.G_FreeEdictA; + bolt.dmg = damage; + bolt.classname = "bolt"; + if (hyper) + bolt.spawnflags = 1; + GameBase.gi.linkentity(bolt); + + if (self.client != null) + check_dodge(self, bolt.s.origin, dir, speed); + + tr = GameBase.gi.trace(self.s.origin, null, null, bolt.s.origin, bolt, + Defines.MASK_SHOT); + if (tr.fraction < 1.0) { + Math3D.VectorMA(bolt.s.origin, -10, dir, bolt.s.origin); + bolt.touch.touch(bolt, tr.ent, GameBase.dummyplane, null); + } + } + + public static void fire_grenade(edict_t self, float[] start, + float[] aimdir, int damage, int speed, float timer, + float damage_radius) { + edict_t grenade; + float[] dir = { 0, 0, 0 }; + float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 }; + + Math3D.vectoangles(aimdir, dir); + Math3D.AngleVectors(dir, forward, right, up); + + grenade = GameUtil.G_Spawn(); + Math3D.VectorCopy(start, grenade.s.origin); + Math3D.VectorScale(aimdir, speed, grenade.velocity); + Math3D.VectorMA(grenade.velocity, 200f + Lib.crandom() * 10.0f, up, + grenade.velocity); + Math3D.VectorMA(grenade.velocity, Lib.crandom() * 10.0f, right, + grenade.velocity); + Math3D.VectorSet(grenade.avelocity, 300, 300, 300); + grenade.movetype = Defines.MOVETYPE_BOUNCE; + grenade.clipmask = Defines.MASK_SHOT; + grenade.solid = Defines.SOLID_BBOX; + grenade.s.effects |= Defines.EF_GRENADE; + Math3D.VectorClear(grenade.mins); + Math3D.VectorClear(grenade.maxs); + grenade.s.modelindex = GameBase.gi + .modelindex("models/objects/grenade/tris.md2"); + grenade.owner = self; + grenade.touch = Grenade_Touch; + grenade.nextthink = GameBase.level.time + timer; + grenade.think = Grenade_Explode; + grenade.dmg = damage; + grenade.dmg_radius = damage_radius; + grenade.classname = "grenade"; + + GameBase.gi.linkentity(grenade); + } + + public static void fire_grenade2(edict_t self, float[] start, + float[] aimdir, int damage, int speed, float timer, + float damage_radius, boolean held) { + edict_t grenade; + float[] dir = { 0, 0, 0 }; + float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 }; + + Math3D.vectoangles(aimdir, dir); + Math3D.AngleVectors(dir, forward, right, up); + + grenade = GameUtil.G_Spawn(); + Math3D.VectorCopy(start, grenade.s.origin); + Math3D.VectorScale(aimdir, speed, grenade.velocity); + Math3D.VectorMA(grenade.velocity, 200f + Lib.crandom() * 10.0f, up, + grenade.velocity); + Math3D.VectorMA(grenade.velocity, Lib.crandom() * 10.0f, right, + grenade.velocity); + Math3D.VectorSet(grenade.avelocity, 300f, 300f, 300f); + grenade.movetype = Defines.MOVETYPE_BOUNCE; + grenade.clipmask = Defines.MASK_SHOT; + grenade.solid = Defines.SOLID_BBOX; + grenade.s.effects |= Defines.EF_GRENADE; + Math3D.VectorClear(grenade.mins); + Math3D.VectorClear(grenade.maxs); + grenade.s.modelindex = GameBase.gi + .modelindex("models/objects/grenade2/tris.md2"); + grenade.owner = self; + grenade.touch = Grenade_Touch; + grenade.nextthink = GameBase.level.time + timer; + grenade.think = Grenade_Explode; + grenade.dmg = damage; + grenade.dmg_radius = damage_radius; + grenade.classname = "hgrenade"; + if (held) + grenade.spawnflags = 3; + else + grenade.spawnflags = 1; + grenade.s.sound = GameBase.gi.soundindex("weapons/hgrenc1b.wav"); + + if (timer <= 0.0) + Grenade_Explode.think(grenade); + else { + GameBase.gi.sound(self, Defines.CHAN_WEAPON, GameBase.gi + .soundindex("weapons/hgrent1a.wav"), 1, Defines.ATTN_NORM, + 0); + GameBase.gi.linkentity(grenade); + } + } + + public static void fire_rocket(edict_t self, float[] start, float[] dir, + int damage, int speed, float damage_radius, int radius_damage) { + edict_t rocket; + + rocket = GameUtil.G_Spawn(); + Math3D.VectorCopy(start, rocket.s.origin); + Math3D.VectorCopy(dir, rocket.movedir); + Math3D.vectoangles(dir, rocket.s.angles); + Math3D.VectorScale(dir, speed, rocket.velocity); + rocket.movetype = Defines.MOVETYPE_FLYMISSILE; + rocket.clipmask = Defines.MASK_SHOT; + rocket.solid = Defines.SOLID_BBOX; + rocket.s.effects |= Defines.EF_ROCKET; + Math3D.VectorClear(rocket.mins); + Math3D.VectorClear(rocket.maxs); + rocket.s.modelindex = GameBase.gi + .modelindex("models/objects/rocket/tris.md2"); + rocket.owner = self; + rocket.touch = rocket_touch; + rocket.nextthink = GameBase.level.time + 8000 / speed; + rocket.think = GameUtil.G_FreeEdictA; + rocket.dmg = damage; + rocket.radius_dmg = radius_damage; + rocket.dmg_radius = damage_radius; + rocket.s.sound = GameBase.gi.soundindex("weapons/rockfly.wav"); + rocket.classname = "rocket"; + + if (self.client != null) + check_dodge(self, rocket.s.origin, dir, speed); + + GameBase.gi.linkentity(rocket); + } + + /* + * ================= + * fire_rail + * ================= + */ + public static void fire_rail(edict_t self, float[] start, float[] aimdir, + int damage, int kick) { + float[] from = { 0, 0, 0 }; + float[] end = { 0, 0, 0 }; + trace_t tr = null; + edict_t ignore; + int mask; + boolean water; + + Math3D.VectorMA(start, 8192f, aimdir, end); + Math3D.VectorCopy(start, from); + ignore = self; + water = false; + mask = Defines.MASK_SHOT | Defines.CONTENTS_SLIME + | Defines.CONTENTS_LAVA; + while (ignore != null) { + tr = GameBase.gi.trace(from, null, null, end, ignore, mask); + + if ((tr.contents & (Defines.CONTENTS_SLIME | Defines.CONTENTS_LAVA)) != 0) { + mask &= ~(Defines.CONTENTS_SLIME | Defines.CONTENTS_LAVA); + water = true; + } else { + //ZOID--added so rail goes through SOLID_BBOX entities (gibs, + // etc) + if ((tr.ent.svflags & Defines.SVF_MONSTER) != 0 + || (tr.ent.client != null) + || (tr.ent.solid == Defines.SOLID_BBOX)) + ignore = tr.ent; + else + ignore = null; + + if ((tr.ent != self) && (tr.ent.takedamage != 0)) + GameCombat.T_Damage(tr.ent, self, self, aimdir, tr.endpos, + tr.plane.normal, damage, kick, 0, + Defines.MOD_RAILGUN); + } + + Math3D.VectorCopy(tr.endpos, from); + } + + // send gun puff / flash + GameBase.gi.WriteByte(Defines.svc_temp_entity); + GameBase.gi.WriteByte(Defines.TE_RAILTRAIL); + GameBase.gi.WritePosition(start); + GameBase.gi.WritePosition(tr.endpos); + GameBase.gi.multicast(self.s.origin, Defines.MULTICAST_PHS); + // gi.multicast (start, MULTICAST_PHS); + if (water) { + GameBase.gi.WriteByte(Defines.svc_temp_entity); + GameBase.gi.WriteByte(Defines.TE_RAILTRAIL); + GameBase.gi.WritePosition(start); + GameBase.gi.WritePosition(tr.endpos); + GameBase.gi.multicast(tr.endpos, Defines.MULTICAST_PHS); + } + + if (self.client != null) + PlayerWeapon.PlayerNoise(self, tr.endpos, Defines.PNOISE_IMPACT); + } + + public static void fire_bfg(edict_t self, float[] start, float[] dir, + int damage, int speed, float damage_radius) { + edict_t bfg; + + bfg = GameUtil.G_Spawn(); + Math3D.VectorCopy(start, bfg.s.origin); + Math3D.VectorCopy(dir, bfg.movedir); + Math3D.vectoangles(dir, bfg.s.angles); + Math3D.VectorScale(dir, speed, bfg.velocity); + bfg.movetype = Defines.MOVETYPE_FLYMISSILE; + bfg.clipmask = Defines.MASK_SHOT; + bfg.solid = Defines.SOLID_BBOX; + bfg.s.effects |= Defines.EF_BFG | Defines.EF_ANIM_ALLFAST; + Math3D.VectorClear(bfg.mins); + Math3D.VectorClear(bfg.maxs); + bfg.s.modelindex = GameBase.gi.modelindex("sprites/s_bfg1.sp2"); + bfg.owner = self; + bfg.touch = bfg_touch; + bfg.nextthink = GameBase.level.time + 8000 / speed; + bfg.think = GameUtil.G_FreeEdictA; + bfg.radius_dmg = damage; + bfg.dmg_radius = damage_radius; + bfg.classname = "bfg blast"; + bfg.s.sound = GameBase.gi.soundindex("weapons/bfg__l1a.wav"); + + bfg.think = bfg_think; + bfg.nextthink = GameBase.level.time + Defines.FRAMETIME; + bfg.teammaster = bfg; + bfg.teamchain = null; + + if (self.client != null) + check_dodge(self, bfg.s.origin, dir, speed); + + GameBase.gi.linkentity(bfg); + } +} |