Hello World!
This commit is contained in:
553
src/jake2/game/GameCombat.java
Normal file
553
src/jake2/game/GameCombat.java
Normal file
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
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 16.11.2005 by RST.
|
||||
// $Id: GameCombat.java,v 1.3 2006/01/21 21:53:32 salomo Exp $
|
||||
|
||||
package jake2.game;
|
||||
|
||||
import jake2.Defines;
|
||||
import jake2.Globals;
|
||||
import jake2.qcommon.Com;
|
||||
import jake2.util.Math3D;
|
||||
|
||||
public class GameCombat {
|
||||
|
||||
/**
|
||||
* CanDamage
|
||||
*
|
||||
* Returns true if the inflictor can directly damage the target. Used for
|
||||
* explosions and melee attacks.
|
||||
*/
|
||||
static boolean CanDamage(edict_t targ, edict_t inflictor) {
|
||||
float[] dest = { 0, 0, 0 };
|
||||
trace_t trace;
|
||||
|
||||
// bmodels need special checking because their origin is 0,0,0
|
||||
if (targ.movetype == Defines.MOVETYPE_PUSH) {
|
||||
Math3D.VectorAdd(targ.absmin, targ.absmax, dest);
|
||||
Math3D.VectorScale(dest, 0.5f, dest);
|
||||
trace = GameBase.gi.trace(inflictor.s.origin, Globals.vec3_origin,
|
||||
Globals.vec3_origin, dest, inflictor, Defines.MASK_SOLID);
|
||||
if (trace.fraction == 1.0f)
|
||||
return true;
|
||||
if (trace.ent == targ)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
trace = GameBase.gi.trace(inflictor.s.origin, Globals.vec3_origin,
|
||||
Globals.vec3_origin, targ.s.origin, inflictor,
|
||||
Defines.MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
Math3D.VectorCopy(targ.s.origin, dest);
|
||||
dest[0] += 15.0;
|
||||
dest[1] += 15.0;
|
||||
trace = GameBase.gi.trace(inflictor.s.origin, Globals.vec3_origin,
|
||||
Globals.vec3_origin, dest, inflictor, Defines.MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
Math3D.VectorCopy(targ.s.origin, dest);
|
||||
dest[0] += 15.0;
|
||||
dest[1] -= 15.0;
|
||||
trace = GameBase.gi.trace(inflictor.s.origin, Globals.vec3_origin,
|
||||
Globals.vec3_origin, dest, inflictor, Defines.MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
Math3D.VectorCopy(targ.s.origin, dest);
|
||||
dest[0] -= 15.0;
|
||||
dest[1] += 15.0;
|
||||
trace = GameBase.gi.trace(inflictor.s.origin, Globals.vec3_origin,
|
||||
Globals.vec3_origin, dest, inflictor, Defines.MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
Math3D.VectorCopy(targ.s.origin, dest);
|
||||
dest[0] -= 15.0;
|
||||
dest[1] -= 15.0;
|
||||
trace = GameBase.gi.trace(inflictor.s.origin, Globals.vec3_origin,
|
||||
Globals.vec3_origin, dest, inflictor, Defines.MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Killed.
|
||||
*/
|
||||
public static void Killed(edict_t targ, edict_t inflictor,
|
||||
edict_t attacker, int damage, float[] point) {
|
||||
Com.DPrintf("Killing a " + targ.classname + "\n");
|
||||
if (targ.health < -999)
|
||||
targ.health = -999;
|
||||
|
||||
targ.enemy = attacker;
|
||||
|
||||
if ((targ.svflags & Defines.SVF_MONSTER) != 0
|
||||
&& (targ.deadflag != Defines.DEAD_DEAD)) {
|
||||
// targ.svflags |= SVF_DEADMONSTER; // now treat as a different
|
||||
// content type
|
||||
if (0 == (targ.monsterinfo.aiflags & Defines.AI_GOOD_GUY)) {
|
||||
GameBase.level.killed_monsters++;
|
||||
if (GameBase.coop.value != 0 && attacker.client != null)
|
||||
attacker.client.resp.score++;
|
||||
// medics won't heal monsters that they kill themselves
|
||||
if (attacker.classname.equals("monster_medic"))
|
||||
targ.owner = attacker;
|
||||
}
|
||||
}
|
||||
|
||||
if (targ.movetype == Defines.MOVETYPE_PUSH
|
||||
|| targ.movetype == Defines.MOVETYPE_STOP
|
||||
|| targ.movetype == Defines.MOVETYPE_NONE) { // doors, triggers,
|
||||
// etc
|
||||
targ.die.die(targ, inflictor, attacker, damage, point);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((targ.svflags & Defines.SVF_MONSTER) != 0
|
||||
&& (targ.deadflag != Defines.DEAD_DEAD)) {
|
||||
targ.touch = null;
|
||||
Monster.monster_death_use(targ);
|
||||
}
|
||||
|
||||
targ.die.die(targ, inflictor, attacker, damage, point);
|
||||
}
|
||||
|
||||
/**
|
||||
* SpawnDamage.
|
||||
*/
|
||||
static void SpawnDamage(int type, float[] origin, float[] normal, int damage) {
|
||||
if (damage > 255)
|
||||
damage = 255;
|
||||
GameBase.gi.WriteByte(Defines.svc_temp_entity);
|
||||
GameBase.gi.WriteByte(type);
|
||||
// gi.WriteByte (damage);
|
||||
GameBase.gi.WritePosition(origin);
|
||||
GameBase.gi.WriteDir(normal);
|
||||
GameBase.gi.multicast(origin, Defines.MULTICAST_PVS);
|
||||
}
|
||||
|
||||
static int CheckPowerArmor(edict_t ent, float[] point, float[] normal,
|
||||
int damage, int dflags) {
|
||||
gclient_t client;
|
||||
int save;
|
||||
int power_armor_type;
|
||||
int index = 0;
|
||||
int damagePerCell;
|
||||
int pa_te_type;
|
||||
int power = 0;
|
||||
int power_used;
|
||||
|
||||
if (damage == 0)
|
||||
return 0;
|
||||
|
||||
client = ent.client;
|
||||
|
||||
if ((dflags & Defines.DAMAGE_NO_ARMOR) != 0)
|
||||
return 0;
|
||||
|
||||
if (client != null) {
|
||||
power_armor_type = GameItems.PowerArmorType(ent);
|
||||
if (power_armor_type != Defines.POWER_ARMOR_NONE) {
|
||||
index = GameItems.ITEM_INDEX(GameItems.FindItem("Cells"));
|
||||
power = client.pers.inventory[index];
|
||||
}
|
||||
} else if ((ent.svflags & Defines.SVF_MONSTER) != 0) {
|
||||
power_armor_type = ent.monsterinfo.power_armor_type;
|
||||
power = ent.monsterinfo.power_armor_power;
|
||||
} else
|
||||
return 0;
|
||||
|
||||
if (power_armor_type == Defines.POWER_ARMOR_NONE)
|
||||
return 0;
|
||||
if (power == 0)
|
||||
return 0;
|
||||
|
||||
if (power_armor_type == Defines.POWER_ARMOR_SCREEN) {
|
||||
float[] vec = { 0, 0, 0 };
|
||||
float dot;
|
||||
float[] forward = { 0, 0, 0 };
|
||||
|
||||
// only works if damage point is in front
|
||||
Math3D.AngleVectors(ent.s.angles, forward, null, null);
|
||||
Math3D.VectorSubtract(point, ent.s.origin, vec);
|
||||
Math3D.VectorNormalize(vec);
|
||||
dot = Math3D.DotProduct(vec, forward);
|
||||
if (dot <= 0.3)
|
||||
return 0;
|
||||
|
||||
damagePerCell = 1;
|
||||
pa_te_type = Defines.TE_SCREEN_SPARKS;
|
||||
damage = damage / 3;
|
||||
} else {
|
||||
damagePerCell = 2;
|
||||
pa_te_type = Defines.TE_SHIELD_SPARKS;
|
||||
damage = (2 * damage) / 3;
|
||||
}
|
||||
|
||||
save = power * damagePerCell;
|
||||
|
||||
if (save == 0)
|
||||
return 0;
|
||||
if (save > damage)
|
||||
save = damage;
|
||||
|
||||
SpawnDamage(pa_te_type, point, normal, save);
|
||||
ent.powerarmor_time = GameBase.level.time + 0.2f;
|
||||
|
||||
power_used = save / damagePerCell;
|
||||
|
||||
if (client != null)
|
||||
client.pers.inventory[index] -= power_used;
|
||||
else
|
||||
ent.monsterinfo.power_armor_power -= power_used;
|
||||
return save;
|
||||
}
|
||||
|
||||
static int CheckArmor(edict_t ent, float[] point, float[] normal,
|
||||
int damage, int te_sparks, int dflags) {
|
||||
gclient_t client;
|
||||
int save;
|
||||
int index;
|
||||
gitem_t armor;
|
||||
|
||||
if (damage == 0)
|
||||
return 0;
|
||||
|
||||
client = ent.client;
|
||||
|
||||
if (client == null)
|
||||
return 0;
|
||||
|
||||
if ((dflags & Defines.DAMAGE_NO_ARMOR) != 0)
|
||||
return 0;
|
||||
|
||||
index = GameItems.ArmorIndex(ent);
|
||||
|
||||
if (index == 0)
|
||||
return 0;
|
||||
|
||||
armor = GameItems.GetItemByIndex(index);
|
||||
gitem_armor_t garmor = (gitem_armor_t) armor.info;
|
||||
|
||||
if (0 != (dflags & Defines.DAMAGE_ENERGY))
|
||||
save = (int) Math.ceil(garmor.energy_protection * damage);
|
||||
else
|
||||
save = (int) Math.ceil(garmor.normal_protection * damage);
|
||||
|
||||
if (save >= client.pers.inventory[index])
|
||||
save = client.pers.inventory[index];
|
||||
|
||||
if (save == 0)
|
||||
return 0;
|
||||
|
||||
client.pers.inventory[index] -= save;
|
||||
SpawnDamage(te_sparks, point, normal, save);
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
public static void M_ReactToDamage(edict_t targ, edict_t attacker) {
|
||||
if ((null != attacker.client)
|
||||
&& 0 != (attacker.svflags & Defines.SVF_MONSTER))
|
||||
return;
|
||||
|
||||
if (attacker == targ || attacker == targ.enemy)
|
||||
return;
|
||||
|
||||
// if we are a good guy monster and our attacker is a player
|
||||
// or another good guy, do not get mad at them
|
||||
if (0 != (targ.monsterinfo.aiflags & Defines.AI_GOOD_GUY)) {
|
||||
if (attacker.client != null
|
||||
|| (attacker.monsterinfo.aiflags & Defines.AI_GOOD_GUY) != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// we now know that we are not both good guys
|
||||
|
||||
// if attacker is a client, get mad at them because he's good and we're
|
||||
// not
|
||||
if (attacker.client != null) {
|
||||
targ.monsterinfo.aiflags &= ~Defines.AI_SOUND_TARGET;
|
||||
|
||||
// this can only happen in coop (both new and old enemies are
|
||||
// clients)
|
||||
// only switch if can't see the current enemy
|
||||
if (targ.enemy != null && targ.enemy.client != null) {
|
||||
if (GameUtil.visible(targ, targ.enemy)) {
|
||||
targ.oldenemy = attacker;
|
||||
return;
|
||||
}
|
||||
targ.oldenemy = targ.enemy;
|
||||
}
|
||||
targ.enemy = attacker;
|
||||
if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED))
|
||||
GameUtil.FoundTarget(targ);
|
||||
return;
|
||||
}
|
||||
|
||||
// it's the same base (walk/swim/fly) type and a different classname and
|
||||
// it's not a tank
|
||||
// (they spray too much), get mad at them
|
||||
if (((targ.flags & (Defines.FL_FLY | Defines.FL_SWIM)) == (attacker.flags & (Defines.FL_FLY | Defines.FL_SWIM)))
|
||||
&& (!(targ.classname.equals(attacker.classname)))
|
||||
&& (!(attacker.classname.equals("monster_tank")))
|
||||
&& (!(attacker.classname.equals("monster_supertank")))
|
||||
&& (!(attacker.classname.equals("monster_makron")))
|
||||
&& (!(attacker.classname.equals("monster_jorg")))) {
|
||||
if (targ.enemy != null && targ.enemy.client != null)
|
||||
targ.oldenemy = targ.enemy;
|
||||
targ.enemy = attacker;
|
||||
if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED))
|
||||
GameUtil.FoundTarget(targ);
|
||||
}
|
||||
// if they *meant* to shoot us, then shoot back
|
||||
else if (attacker.enemy == targ) {
|
||||
if (targ.enemy != null && targ.enemy.client != null)
|
||||
targ.oldenemy = targ.enemy;
|
||||
targ.enemy = attacker;
|
||||
if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED))
|
||||
GameUtil.FoundTarget(targ);
|
||||
}
|
||||
// otherwise get mad at whoever they are mad at (help our buddy) unless
|
||||
// it is us!
|
||||
else if (attacker.enemy != null && attacker.enemy != targ) {
|
||||
if (targ.enemy != null && targ.enemy.client != null)
|
||||
targ.oldenemy = targ.enemy;
|
||||
targ.enemy = attacker.enemy;
|
||||
if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED))
|
||||
GameUtil.FoundTarget(targ);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean CheckTeamDamage(edict_t targ, edict_t attacker) {
|
||||
//FIXME make the next line real and uncomment this block
|
||||
// if ((ability to damage a teammate == OFF) && (targ's team ==
|
||||
// attacker's team))
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* T_RadiusDamage.
|
||||
*/
|
||||
static void T_RadiusDamage(edict_t inflictor, edict_t attacker,
|
||||
float damage, edict_t ignore, float radius, int mod) {
|
||||
float points;
|
||||
EdictIterator edictit = null;
|
||||
|
||||
float[] v = { 0, 0, 0 };
|
||||
float[] dir = { 0, 0, 0 };
|
||||
|
||||
while ((edictit = GameBase.findradius(edictit, inflictor.s.origin,
|
||||
radius)) != null) {
|
||||
edict_t ent = edictit.o;
|
||||
if (ent == ignore)
|
||||
continue;
|
||||
if (ent.takedamage == 0)
|
||||
continue;
|
||||
|
||||
Math3D.VectorAdd(ent.mins, ent.maxs, v);
|
||||
Math3D.VectorMA(ent.s.origin, 0.5f, v, v);
|
||||
Math3D.VectorSubtract(inflictor.s.origin, v, v);
|
||||
points = damage - 0.5f * Math3D.VectorLength(v);
|
||||
if (ent == attacker)
|
||||
points = points * 0.5f;
|
||||
if (points > 0) {
|
||||
if (CanDamage(ent, inflictor)) {
|
||||
Math3D.VectorSubtract(ent.s.origin, inflictor.s.origin, dir);
|
||||
T_Damage(ent, inflictor, attacker, dir, inflictor.s.origin,
|
||||
Globals.vec3_origin, (int) points, (int) points,
|
||||
Defines.DAMAGE_RADIUS, mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void T_Damage(edict_t targ, edict_t inflictor,
|
||||
edict_t attacker, float[] dir, float[] point, float[] normal,
|
||||
int damage, int knockback, int dflags, int mod) {
|
||||
gclient_t client;
|
||||
int take;
|
||||
int save;
|
||||
int asave;
|
||||
int psave;
|
||||
int te_sparks;
|
||||
|
||||
if (targ.takedamage == 0)
|
||||
return;
|
||||
|
||||
// friendly fire avoidance
|
||||
// if enabled you can't hurt teammates (but you can hurt yourself)
|
||||
// knockback still occurs
|
||||
if ((targ != attacker)
|
||||
&& ((GameBase.deathmatch.value != 0 && 0 != ((int) (GameBase.dmflags.value) & (Defines.DF_MODELTEAMS | Defines.DF_SKINTEAMS))) || GameBase.coop.value != 0)) {
|
||||
if (GameUtil.OnSameTeam(targ, attacker)) {
|
||||
if (((int) (GameBase.dmflags.value) & Defines.DF_NO_FRIENDLY_FIRE) != 0)
|
||||
damage = 0;
|
||||
else
|
||||
mod |= Defines.MOD_FRIENDLY_FIRE;
|
||||
}
|
||||
}
|
||||
GameBase.meansOfDeath = mod;
|
||||
|
||||
// easy mode takes half damage
|
||||
if (GameBase.skill.value == 0 && GameBase.deathmatch.value == 0
|
||||
&& targ.client != null) {
|
||||
damage *= 0.5;
|
||||
if (damage == 0)
|
||||
damage = 1;
|
||||
}
|
||||
|
||||
client = targ.client;
|
||||
|
||||
if ((dflags & Defines.DAMAGE_BULLET) != 0)
|
||||
te_sparks = Defines.TE_BULLET_SPARKS;
|
||||
else
|
||||
te_sparks = Defines.TE_SPARKS;
|
||||
|
||||
Math3D.VectorNormalize(dir);
|
||||
|
||||
// bonus damage for suprising a monster
|
||||
if (0 == (dflags & Defines.DAMAGE_RADIUS)
|
||||
&& (targ.svflags & Defines.SVF_MONSTER) != 0
|
||||
&& (attacker.client != null) && (targ.enemy == null)
|
||||
&& (targ.health > 0))
|
||||
damage *= 2;
|
||||
|
||||
if ((targ.flags & Defines.FL_NO_KNOCKBACK) != 0)
|
||||
knockback = 0;
|
||||
|
||||
// figure momentum add
|
||||
if (0 == (dflags & Defines.DAMAGE_NO_KNOCKBACK)) {
|
||||
if ((knockback != 0) && (targ.movetype != Defines.MOVETYPE_NONE)
|
||||
&& (targ.movetype != Defines.MOVETYPE_BOUNCE)
|
||||
&& (targ.movetype != Defines.MOVETYPE_PUSH)
|
||||
&& (targ.movetype != Defines.MOVETYPE_STOP)) {
|
||||
float[] kvel = { 0, 0, 0 };
|
||||
float mass;
|
||||
|
||||
if (targ.mass < 50)
|
||||
mass = 50;
|
||||
else
|
||||
mass = targ.mass;
|
||||
|
||||
if (targ.client != null && attacker == targ)
|
||||
Math3D.VectorScale(dir, 1600.0f * (float) knockback / mass,
|
||||
kvel);
|
||||
// the rocket jump hack...
|
||||
else
|
||||
Math3D.VectorScale(dir, 500.0f * (float) knockback / mass,
|
||||
kvel);
|
||||
|
||||
Math3D.VectorAdd(targ.velocity, kvel, targ.velocity);
|
||||
}
|
||||
}
|
||||
|
||||
take = damage;
|
||||
save = 0;
|
||||
|
||||
// check for godmode
|
||||
if ((targ.flags & Defines.FL_GODMODE) != 0
|
||||
&& 0 == (dflags & Defines.DAMAGE_NO_PROTECTION)) {
|
||||
take = 0;
|
||||
save = damage;
|
||||
SpawnDamage(te_sparks, point, normal, save);
|
||||
}
|
||||
|
||||
// check for invincibility
|
||||
if ((client != null && client.invincible_framenum > GameBase.level.framenum)
|
||||
&& 0 == (dflags & Defines.DAMAGE_NO_PROTECTION)) {
|
||||
if (targ.pain_debounce_time < GameBase.level.time) {
|
||||
GameBase.gi.sound(targ, Defines.CHAN_ITEM, GameBase.gi
|
||||
.soundindex("items/protect4.wav"), 1,
|
||||
Defines.ATTN_NORM, 0);
|
||||
targ.pain_debounce_time = GameBase.level.time + 2;
|
||||
}
|
||||
take = 0;
|
||||
save = damage;
|
||||
}
|
||||
|
||||
psave = CheckPowerArmor(targ, point, normal, take, dflags);
|
||||
take -= psave;
|
||||
|
||||
asave = CheckArmor(targ, point, normal, take, te_sparks, dflags);
|
||||
take -= asave;
|
||||
|
||||
// treat cheat/powerup savings the same as armor
|
||||
asave += save;
|
||||
|
||||
// team damage avoidance
|
||||
if (0 == (dflags & Defines.DAMAGE_NO_PROTECTION)
|
||||
&& CheckTeamDamage(targ, attacker))
|
||||
return;
|
||||
|
||||
// do the damage
|
||||
if (take != 0) {
|
||||
if (0 != (targ.svflags & Defines.SVF_MONSTER) || (client != null))
|
||||
SpawnDamage(Defines.TE_BLOOD, point, normal, take);
|
||||
else
|
||||
SpawnDamage(te_sparks, point, normal, take);
|
||||
|
||||
targ.health = targ.health - take;
|
||||
|
||||
if (targ.health <= 0) {
|
||||
if ((targ.svflags & Defines.SVF_MONSTER) != 0
|
||||
|| (client != null))
|
||||
targ.flags |= Defines.FL_NO_KNOCKBACK;
|
||||
Killed(targ, inflictor, attacker, take, point);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((targ.svflags & Defines.SVF_MONSTER) != 0) {
|
||||
M_ReactToDamage(targ, attacker);
|
||||
if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED)
|
||||
&& (take != 0)) {
|
||||
targ.pain.pain(targ, attacker, knockback, take);
|
||||
// nightmare mode monsters don't go into pain frames often
|
||||
if (GameBase.skill.value == 3)
|
||||
targ.pain_debounce_time = GameBase.level.time + 5;
|
||||
}
|
||||
} else if (client != null) {
|
||||
if (((targ.flags & Defines.FL_GODMODE) == 0) && (take != 0))
|
||||
targ.pain.pain(targ, attacker, knockback, take);
|
||||
} else if (take != 0) {
|
||||
if (targ.pain != null)
|
||||
targ.pain.pain(targ, attacker, knockback, take);
|
||||
}
|
||||
|
||||
// add to the damage inflicted on a player this frame
|
||||
// the total will be turned into screen blends and view angle kicks
|
||||
// at the end of the frame
|
||||
if (client != null) {
|
||||
client.damage_parmor += psave;
|
||||
client.damage_armor += asave;
|
||||
client.damage_blood += take;
|
||||
client.damage_knockback += knockback;
|
||||
Math3D.VectorCopy(point, client.damage_from);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user