summaryrefslogtreecommitdiffstats
path: root/src/jake2/game/PlayerView.java
diff options
context:
space:
mode:
authorHolger Zickner <[email protected]>2004-07-07 19:59:59 +0000
committerHolger Zickner <[email protected]>2004-07-07 19:59:59 +0000
commit6e23fc1074d1f0c2c2812f4c2e663f5a21a43c20 (patch)
tree46ecc6d0255c874ba4cd26dc3d0733f785019896 /src/jake2/game/PlayerView.java
import of Jake2 version sunrisesunrise
Diffstat (limited to 'src/jake2/game/PlayerView.java')
-rw-r--r--src/jake2/game/PlayerView.java1054
1 files changed, 1054 insertions, 0 deletions
diff --git a/src/jake2/game/PlayerView.java b/src/jake2/game/PlayerView.java
new file mode 100644
index 0000000..289aa0f
--- /dev/null
+++ b/src/jake2/game/PlayerView.java
@@ -0,0 +1,1054 @@
+/*
+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: PlayerView.java,v 1.1 2004-07-07 19:59:24 hzi Exp $
+
+package jake2.game;
+
+import jake2.*;
+import jake2.client.*;
+import jake2.qcommon.*;
+import jake2.render.*;
+import jake2.server.*;
+
+public class PlayerView extends PlayerClient {
+
+ public static edict_t current_player;
+ public static gclient_t current_client;
+
+ public static float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 };
+ public static float xyspeed;
+
+ public static float bobmove;
+ public static int bobcycle; // odd cycles are right foot going forward
+ public static float bobfracsin; // sin(bobfrac*M_PI)
+
+ /*
+ ===============
+ SV_CalcRoll
+
+ ===============
+ */
+ public static float SV_CalcRoll(float[] angles, float[] velocity) {
+ float sign;
+ float side;
+ float value;
+
+ side = DotProduct(velocity, right);
+ sign = side < 0 ? -1 : 1;
+ side = Math.abs(side);
+
+ value = sv_rollangle.value;
+
+ if (side < sv_rollspeed.value)
+ side = side * value / sv_rollspeed.value;
+ else
+ side = value;
+
+ return side * sign;
+ }
+
+ /*
+ ===============
+ P_DamageFeedback
+
+ Handles color blends and view kicks
+ ===============
+ */
+ private static int xxxi = 0;
+
+ public static void P_DamageFeedback(edict_t player) {
+ gclient_t client;
+ float side;
+ float realcount, count, kick;
+ float[] v = { 0, 0, 0 };
+ int r, l;
+ float[] power_color = { 0.0f, 1.0f, 0.0f };
+ float[] acolor = { 1.0f, 1.0f, 1.0f };
+ float[] bcolor = { 1.0f, 0.0f, 0.0f };
+
+ client = player.client;
+
+ // flash the backgrounds behind the status numbers
+ client.ps.stats[STAT_FLASHES] = 0;
+ if (client.damage_blood != 0)
+ client.ps.stats[STAT_FLASHES] |= 1;
+ if (client.damage_armor != 0 && 0 == (player.flags & FL_GODMODE) && (client.invincible_framenum <= level.framenum))
+ client.ps.stats[STAT_FLASHES] |= 2;
+
+ // total points of damage shot at the player this frame
+ count = (client.damage_blood + client.damage_armor + client.damage_parmor);
+
+ if (count == 0)
+ return; // didn't take any damage
+
+ // start a pain animation if still in the player model
+ if ((client.anim_priority < ANIM_PAIN) & (player.s.modelindex == 255)) {
+ client.anim_priority = ANIM_PAIN;
+ if ((client.ps.pmove.pm_flags & PMF_DUCKED) != 0) {
+ player.s.frame = FRAME_crpain1 - 1;
+ client.anim_end = FRAME_crpain4;
+ }
+ else {
+
+ xxxi = (xxxi + 1) % 3;
+ switch (xxxi) {
+ case 0 :
+ player.s.frame = FRAME_pain101 - 1;
+ client.anim_end = FRAME_pain104;
+ break;
+ case 1 :
+ player.s.frame = FRAME_pain201 - 1;
+ client.anim_end = FRAME_pain204;
+ break;
+ case 2 :
+ player.s.frame = FRAME_pain301 - 1;
+ client.anim_end = FRAME_pain304;
+ break;
+ }
+ }
+ }
+
+ realcount = count;
+ if (count < 10)
+ count = 10; // always make a visible effect
+
+ // play an apropriate pain sound
+ if ((level.time > player.pain_debounce_time)
+ && 0 == (player.flags & FL_GODMODE)
+ && (client.invincible_framenum <= level.framenum)) {
+ r = 1 + (rand() & 1);
+ player.pain_debounce_time = level.time + 0.7f;
+ if (player.health < 25)
+ l = 25;
+ else if (player.health < 50)
+ l = 50;
+ else if (player.health < 75)
+ l = 75;
+ else
+ l = 100;
+ gi.sound(player, CHAN_VOICE, gi.soundindex("*pain" + l + "_" + r + ".wav"), 1, ATTN_NORM, 0);
+ }
+
+ // the total alpha of the blend is always proportional to count
+ if (client.damage_alpha < 0)
+ client.damage_alpha = 0;
+ client.damage_alpha += count * 0.01f;
+ if (client.damage_alpha < 0.2f)
+ client.damage_alpha = 0.2f;
+ if (client.damage_alpha > 0.6f)
+ client.damage_alpha = 0.6f; // don't go too saturated
+
+ // the color of the blend will vary based on how much was absorbed
+ // by different armors
+ VectorClear(v);
+ if (client.damage_parmor != 0)
+ VectorMA(v, (float) client.damage_parmor / realcount, power_color, v);
+
+ if (client.damage_armor != 0)
+ VectorMA(v, (float) client.damage_armor / realcount, acolor, v);
+
+ if (client.damage_blood != 0)
+ VectorMA(v, (float) client.damage_blood / realcount, bcolor, v);
+ VectorCopy(v, client.damage_blend);
+
+ //
+ // calculate view angle kicks
+ //
+ kick = Math.abs(client.damage_knockback);
+ if (kick != 0 && player.health > 0) // kick of 0 means no view adjust at all
+ {
+ kick = kick * 100 / player.health;
+
+ if (kick < count * 0.5)
+ kick = count * 0.5f;
+ if (kick > 50)
+ kick = 50;
+
+ VectorSubtract(client.damage_from, player.s.origin, v);
+ VectorNormalize(v);
+
+ side = DotProduct(v, right);
+ client.v_dmg_roll = kick * side * 0.3f;
+
+ side = -DotProduct(v, forward);
+ client.v_dmg_pitch = kick * side * 0.3f;
+
+ client.v_dmg_time = level.time + DAMAGE_TIME;
+ }
+
+ //
+ // clear totals
+ //
+ client.damage_blood = 0;
+ client.damage_armor = 0;
+ client.damage_parmor = 0;
+ client.damage_knockback = 0;
+ }
+
+ /*
+ ===============
+ SV_CalcViewOffset
+
+ Auto pitching on slopes?
+
+ fall from 128: 400 = 160000
+ fall from 256: 580 = 336400
+ fall from 384: 720 = 518400
+ fall from 512: 800 = 640000
+ fall from 640: 960 =
+
+ damage = deltavelocity*deltavelocity * 0.0001
+
+ ===============
+ */
+ public static void SV_CalcViewOffset(edict_t ent) {
+ float angles[] = { 0, 0, 0 };
+ float bob;
+ float ratio;
+ float delta;
+ float[] v = { 0, 0, 0 };
+
+ //===================================
+
+ // base angles
+ angles = ent.client.ps.kick_angles;
+
+ // if dead, fix the angle and don't add any kick
+ if (ent.deadflag != 0) {
+ VectorClear(angles);
+
+ ent.client.ps.viewangles[ROLL] = 40;
+ ent.client.ps.viewangles[PITCH] = -15;
+ ent.client.ps.viewangles[YAW] = ent.client.killer_yaw;
+ }
+ else {
+ // add angles based on weapon kick
+
+ VectorCopy(ent.client.kick_angles, angles);
+
+ // add angles based on damage kick
+
+ ratio = (ent.client.v_dmg_time - level.time) / DAMAGE_TIME;
+ if (ratio < 0) {
+ ratio = 0;
+ ent.client.v_dmg_pitch = 0;
+ ent.client.v_dmg_roll = 0;
+ }
+ angles[PITCH] += ratio * ent.client.v_dmg_pitch;
+ angles[ROLL] += ratio * ent.client.v_dmg_roll;
+
+ // add pitch based on fall kick
+
+ ratio = (ent.client.fall_time - level.time) / FALL_TIME;
+ if (ratio < 0)
+ ratio = 0;
+ angles[PITCH] += ratio * ent.client.fall_value;
+
+ // add angles based on velocity
+
+ delta = DotProduct(ent.velocity, forward);
+ angles[PITCH] += delta * run_pitch.value;
+
+ delta = DotProduct(ent.velocity, right);
+ angles[ROLL] += delta * run_roll.value;
+
+ // add angles based on bob
+
+ delta = bobfracsin * bob_pitch.value * xyspeed;
+ if ((ent.client.ps.pmove.pm_flags & PMF_DUCKED) != 0)
+ delta *= 6; // crouching
+ angles[PITCH] += delta;
+ delta = bobfracsin * bob_roll.value * xyspeed;
+ if ((ent.client.ps.pmove.pm_flags & PMF_DUCKED) != 0)
+ delta *= 6; // crouching
+ if ((bobcycle & 1) != 0)
+ delta = -delta;
+ angles[ROLL] += delta;
+ }
+
+ //===================================
+
+ // base origin
+
+ VectorClear(v);
+
+ // add view height
+
+ v[2] += ent.viewheight;
+
+ // add fall height
+
+ ratio = (ent.client.fall_time - level.time) / FALL_TIME;
+ if (ratio < 0)
+ ratio = 0;
+ v[2] -= ratio * ent.client.fall_value * 0.4;
+
+ // add bob height
+
+ bob = bobfracsin * xyspeed * bob_up.value;
+ if (bob > 6)
+ bob = 6;
+ //gi.DebugGraph (bob *2, 255);
+ v[2] += bob;
+
+ // add kick offset
+
+ VectorAdd(v, ent.client.kick_origin, v);
+
+ // absolutely bound offsets
+ // so the view can never be outside the player box
+
+ if (v[0] < -14)
+ v[0] = -14;
+ else if (v[0] > 14)
+ v[0] = 14;
+ if (v[1] < -14)
+ v[1] = -14;
+ else if (v[1] > 14)
+ v[1] = 14;
+ if (v[2] < -22)
+ v[2] = -22;
+ else if (v[2] > 30)
+ v[2] = 30;
+
+ VectorCopy(v, ent.client.ps.viewoffset);
+ }
+
+ /*
+ ==============
+ SV_CalcGunOffset
+ ==============
+ */
+ public static void SV_CalcGunOffset(edict_t ent) {
+ int i;
+ float delta;
+
+ // gun angles from bobbing
+ ent.client.ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005f;
+ ent.client.ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01f;
+ if ((bobcycle & 1) != 0) {
+ ent.client.ps.gunangles[ROLL] = -ent.client.ps.gunangles[ROLL];
+ ent.client.ps.gunangles[YAW] = -ent.client.ps.gunangles[YAW];
+ }
+
+ ent.client.ps.gunangles[PITCH] = xyspeed * bobfracsin * 0.005f;
+
+ // gun angles from delta movement
+ for (i = 0; i < 3; i++) {
+ delta = ent.client.oldviewangles[i] - ent.client.ps.viewangles[i];
+ if (delta > 180)
+ delta -= 360;
+ if (delta < -180)
+ delta += 360;
+ if (delta > 45)
+ delta = 45;
+ if (delta < -45)
+ delta = -45;
+ if (i == YAW)
+ ent.client.ps.gunangles[ROLL] += 0.1 * delta;
+ ent.client.ps.gunangles[i] += 0.2 * delta;
+ }
+
+ // gun height
+ VectorClear(ent.client.ps.gunoffset);
+ // ent.ps.gunorigin[2] += bob;
+
+ // gun_x / gun_y / gun_z are development tools
+ for (i = 0; i < 3; i++) {
+ ent.client.ps.gunoffset[i] += forward[i] * (gun_y.value);
+ ent.client.ps.gunoffset[i] += right[i] * gun_x.value;
+ ent.client.ps.gunoffset[i] += up[i] * (-gun_z.value);
+ }
+ }
+
+ /*
+ =============
+ SV_AddBlend
+ =============
+ */
+ public static void SV_AddBlend(float r, float g, float b, float a, float v_blend[]) {
+ float a2, a3;
+
+ if (a <= 0)
+ return;
+ a2 = v_blend[3] + (1 - v_blend[3]) * a; // new total alpha
+ a3 = v_blend[3] / a2; // fraction of color from old
+
+ v_blend[0] = v_blend[0] * a3 + r * (1 - a3);
+ v_blend[1] = v_blend[1] * a3 + g * (1 - a3);
+ v_blend[2] = v_blend[2] * a3 + b * (1 - a3);
+ v_blend[3] = a2;
+ }
+
+ /*
+ =============
+ SV_CalcBlend
+ =============
+ */
+ public static void SV_CalcBlend(edict_t ent) {
+ int contents;
+ float[] vieworg = { 0, 0, 0 };
+ int remaining;
+
+ ent.client.ps.blend[0] = ent.client.ps.blend[1] = ent.client.ps.blend[2] = ent.client.ps.blend[3] = 0;
+
+ // add for contents
+ VectorAdd(ent.s.origin, ent.client.ps.viewoffset, vieworg);
+ contents = gi.pointcontents.pointcontents(vieworg);
+ if ((contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) != 0)
+ ent.client.ps.rdflags |= RDF_UNDERWATER;
+ else
+ ent.client.ps.rdflags &= ~RDF_UNDERWATER;
+
+ if ((contents & (CONTENTS_SOLID | CONTENTS_LAVA)) != 0)
+ SV_AddBlend(1.0f, 0.3f, 0.0f, 0.6f, ent.client.ps.blend);
+ else if ((contents & CONTENTS_SLIME) != 0)
+ SV_AddBlend(0.0f, 0.1f, 0.05f, 0.6f, ent.client.ps.blend);
+ else if ((contents & CONTENTS_WATER) != 0)
+ SV_AddBlend(0.5f, 0.3f, 0.2f, 0.4f, ent.client.ps.blend);
+
+ // add for powerups
+ if (ent.client.quad_framenum > level.framenum) {
+ remaining = (int) (ent.client.quad_framenum - level.framenum);
+ if (remaining == 30) // beginning to fade
+ gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage2.wav"), 1, ATTN_NORM, 0);
+ if (remaining > 30 || (remaining & 4) != 0)
+ SV_AddBlend(0, 0, 1, 0.08f, ent.client.ps.blend);
+ }
+ else if (ent.client.invincible_framenum > level.framenum) {
+ remaining = (int) ent.client.invincible_framenum - level.framenum;
+ if (remaining == 30) // beginning to fade
+ gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect2.wav"), 1, ATTN_NORM, 0);
+ if (remaining > 30 || (remaining & 4) != 0)
+ SV_AddBlend(1, 1, 0, 0.08f, ent.client.ps.blend);
+ }
+ else if (ent.client.enviro_framenum > level.framenum) {
+ remaining = (int) ent.client.enviro_framenum - level.framenum;
+ if (remaining == 30) // beginning to fade
+ gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
+ if (remaining > 30 || (remaining & 4) != 0)
+ SV_AddBlend(0, 1, 0, 0.08f, ent.client.ps.blend);
+ }
+ else if (ent.client.breather_framenum > level.framenum) {
+ remaining = (int) ent.client.breather_framenum - level.framenum;
+ if (remaining == 30) // beginning to fade
+ gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
+ if (remaining > 30 || (remaining & 4) != 0)
+ SV_AddBlend(0.4f, 1, 0.4f, 0.04f, ent.client.ps.blend);
+ }
+
+ // add for damage
+ if (ent.client.damage_alpha > 0)
+ SV_AddBlend(
+ ent.client.damage_blend[0],
+ ent.client.damage_blend[1],
+ ent.client.damage_blend[2],
+ ent.client.damage_alpha,
+ ent.client.ps.blend);
+
+ if (ent.client.bonus_alpha > 0)
+ SV_AddBlend(0.85f, 0.7f, 0.3f, ent.client.bonus_alpha, ent.client.ps.blend);
+
+ // drop the damage value
+ ent.client.damage_alpha -= 0.06;
+ if (ent.client.damage_alpha < 0)
+ ent.client.damage_alpha = 0;
+
+ // drop the bonus value
+ ent.client.bonus_alpha -= 0.1;
+ if (ent.client.bonus_alpha < 0)
+ ent.client.bonus_alpha = 0;
+ }
+
+ /*
+ =================
+ P_FallingDamage
+ =================
+ */
+ public static void P_FallingDamage(edict_t ent) {
+ float delta;
+ int damage;
+ float[] dir = { 0, 0, 0 };
+
+ if (ent.s.modelindex != 255)
+ return; // not in the player model
+
+ if (ent.movetype == MOVETYPE_NOCLIP)
+ return;
+
+ if ((ent.client.oldvelocity[2] < 0) && (ent.velocity[2] > ent.client.oldvelocity[2]) && (null == ent.groundentity)) {
+ delta = ent.client.oldvelocity[2];
+ }
+ else {
+ if (ent.groundentity == null)
+ return;
+ delta = ent.velocity[2] - ent.client.oldvelocity[2];
+ }
+ delta = delta * delta * 0.0001f;
+
+ // never take falling damage if completely underwater
+ if (ent.waterlevel == 3)
+ return;
+ if (ent.waterlevel == 2)
+ delta *= 0.25;
+ if (ent.waterlevel == 1)
+ delta *= 0.5;
+
+ if (delta < 1)
+ return;
+
+ if (delta < 15) {
+ ent.s.event = EV_FOOTSTEP;
+ return;
+ }
+
+ ent.client.fall_value = delta * 0.5f;
+ if (ent.client.fall_value > 40)
+ ent.client.fall_value = 40;
+ ent.client.fall_time = level.time + FALL_TIME;
+
+ if (delta > 30) {
+ if (ent.health > 0) {
+ if (delta >= 55)
+ ent.s.event = EV_FALLFAR;
+ else
+ ent.s.event = EV_FALL;
+ }
+ ent.pain_debounce_time = level.time; // no normal pain sound
+ damage = (int) ((delta - 30) / 2);
+ if (damage < 1)
+ damage = 1;
+ VectorSet(dir, 0, 0, 1);
+
+ if (deathmatch.value == 0 || 0 == ((int) dmflags.value & DF_NO_FALLING))
+ T_Damage(ent, g_edicts[0], g_edicts[0], dir, ent.s.origin, vec3_origin, damage, 0, 0, MOD_FALLING);
+ }
+ else {
+ ent.s.event = EV_FALLSHORT;
+ return;
+ }
+ }
+
+ /*
+ =============
+ P_WorldEffects
+ =============
+ */
+ public static void P_WorldEffects() {
+ boolean breather;
+ boolean envirosuit;
+ int waterlevel, old_waterlevel;
+
+ if (current_player.movetype == MOVETYPE_NOCLIP) {
+ current_player.air_finished = level.time + 12; // don't need air
+ return;
+ }
+
+ waterlevel = current_player.waterlevel;
+ old_waterlevel = current_client.old_waterlevel;
+ current_client.old_waterlevel = waterlevel;
+
+ breather = current_client.breather_framenum > level.framenum;
+ envirosuit = current_client.enviro_framenum > level.framenum;
+
+ //
+ // if just entered a water volume, play a sound
+ //
+ if (old_waterlevel == 0 && waterlevel != 0) {
+ PlayerNoise(current_player, current_player.s.origin, PNOISE_SELF);
+ if ((current_player.watertype & CONTENTS_LAVA) != 0)
+ gi.sound(current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0);
+ else if ((current_player.watertype & CONTENTS_SLIME) != 0)
+ gi.sound(current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
+ else if ((current_player.watertype & CONTENTS_WATER) != 0)
+ gi.sound(current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
+ current_player.flags |= FL_INWATER;
+
+ // clear damage_debounce, so the pain sound will play immediately
+ current_player.damage_debounce_time = level.time - 1;
+ }
+
+ //
+ // if just completely exited a water volume, play a sound
+ //
+ if (old_waterlevel != 0 && waterlevel == 0) {
+ PlayerNoise(current_player, current_player.s.origin, PNOISE_SELF);
+ gi.sound(current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
+ current_player.flags &= ~FL_INWATER;
+ }
+
+ //
+ // check for head just going under water
+ //
+ if (old_waterlevel != 3 && waterlevel == 3) {
+ gi.sound(current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0);
+ }
+
+ //
+ // check for head just coming out of water
+ //
+ if (old_waterlevel == 3 && waterlevel != 3) {
+ if (current_player.air_finished < level.time) { // gasp for air
+ gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0);
+ PlayerNoise(current_player, current_player.s.origin, PNOISE_SELF);
+ }
+ else if (current_player.air_finished < level.time + 11) { // just break surface
+ gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0);
+ }
+ }
+
+ //
+ // check for drowning
+ //
+ if (waterlevel == 3) {
+ // breather or envirosuit give air
+ if (breather || envirosuit) {
+ current_player.air_finished = level.time + 10;
+
+ if (((int) (current_client.breather_framenum - level.framenum) % 25) == 0) {
+ if (current_client.breather_sound == 0)
+ gi.sound(current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0);
+ else
+ gi.sound(current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0);
+ current_client.breather_sound ^= 1;
+ PlayerNoise(current_player, current_player.s.origin, PNOISE_SELF);
+ //FIXME: release a bubble?
+ }
+ }
+
+ // if out of air, start drowning
+ if (current_player.air_finished < level.time) { // drown!
+ if (current_player.client.next_drown_time < level.time && current_player.health > 0) {
+ current_player.client.next_drown_time = level.time + 1;
+
+ // take more damage the longer underwater
+ current_player.dmg += 2;
+ if (current_player.dmg > 15)
+ current_player.dmg = 15;
+
+ // play a gurp sound instead of a normal pain sound
+ if (current_player.health <= current_player.dmg)
+ gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0);
+ else if ((rand() & 1) != 0)
+ gi.sound(current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0);
+ else
+ gi.sound(current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0);
+
+ current_player.pain_debounce_time = level.time;
+
+ T_Damage(
+ current_player,
+ g_edicts[0],
+ g_edicts[0],
+ vec3_origin,
+ current_player.s.origin,
+ vec3_origin,
+ current_player.dmg,
+ 0,
+ DAMAGE_NO_ARMOR,
+ MOD_WATER);
+ }
+ }
+ }
+ else {
+ current_player.air_finished = level.time + 12;
+ current_player.dmg = 2;
+ }
+
+ //
+ // check for sizzle damage
+ //
+ if (waterlevel != 0 && 0 != (current_player.watertype & (CONTENTS_LAVA | CONTENTS_SLIME))) {
+ if ((current_player.watertype & CONTENTS_LAVA) != 0) {
+ if (current_player.health > 0
+ && current_player.pain_debounce_time <= level.time
+ && current_client.invincible_framenum < level.framenum) {
+ if ((rand() & 1) != 0)
+ gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0);
+ else
+ gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0);
+ current_player.pain_debounce_time = level.time + 1;
+ }
+
+ if (envirosuit) // take 1/3 damage with envirosuit
+ T_Damage(
+ current_player,
+ g_edicts[0],
+ g_edicts[0],
+ vec3_origin,
+ current_player.s.origin,
+ vec3_origin,
+ 1 * waterlevel,
+ 0,
+ 0,
+ MOD_LAVA);
+ else
+ T_Damage(
+ current_player,
+ g_edicts[0],
+ g_edicts[0],
+ vec3_origin,
+ current_player.s.origin,
+ vec3_origin,
+ 3 * waterlevel,
+ 0,
+ 0,
+ MOD_LAVA);
+ }
+
+ if ((current_player.watertype & CONTENTS_SLIME) != 0) {
+ if (!envirosuit) { // no damage from slime with envirosuit
+ T_Damage(
+ current_player,
+ g_edicts[0],
+ g_edicts[0],
+ vec3_origin,
+ current_player.s.origin,
+ vec3_origin,
+ 1 * waterlevel,
+ 0,
+ 0,
+ MOD_SLIME);
+ }
+ }
+ }
+ }
+
+ /*
+ ===============
+ G_SetClientEffects
+ ===============
+ */
+ public static void G_SetClientEffects(edict_t ent) {
+ int pa_type;
+ int remaining;
+
+ ent.s.effects = 0;
+ ent.s.renderfx = 0;
+
+ if (ent.health <= 0 || level.intermissiontime != 0)
+ return;
+
+ if (ent.powerarmor_time > level.time) {
+ pa_type = PowerArmorType(ent);
+ if (pa_type == POWER_ARMOR_SCREEN) {
+ ent.s.effects |= EF_POWERSCREEN;
+ }
+ else if (pa_type == POWER_ARMOR_SHIELD) {
+ ent.s.effects |= EF_COLOR_SHELL;
+ ent.s.renderfx |= RF_SHELL_GREEN;
+ }
+ }
+
+ if (ent.client.quad_framenum > level.framenum) {
+ remaining = (int) ent.client.quad_framenum - level.framenum;
+ if (remaining > 30 || 0 != (remaining & 4))
+ ent.s.effects |= EF_QUAD;
+ }
+
+ if (ent.client.invincible_framenum > level.framenum) {
+ remaining = (int) ent.client.invincible_framenum - level.framenum;
+ if (remaining > 30 || 0 != (remaining & 4))
+ ent.s.effects |= EF_PENT;
+ }
+
+ // show cheaters!!!
+ if ((ent.flags & FL_GODMODE) != 0) {
+ ent.s.effects |= EF_COLOR_SHELL;
+ ent.s.renderfx |= (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE);
+ }
+ }
+
+ /*
+ ===============
+ G_SetClientEvent
+ ===============
+ */
+ public static void G_SetClientEvent(edict_t ent) {
+ if (ent.s.event != 0)
+ return;
+
+ if (ent.groundentity != null && xyspeed > 225) {
+ if ((int) (current_client.bobtime + bobmove) != bobcycle)
+ ent.s.event = EV_FOOTSTEP;
+ }
+ }
+
+ /*
+ ===============
+ G_SetClientSound
+ ===============
+ */
+ public static void G_SetClientSound(edict_t ent) {
+ String weap;
+
+ if (ent.client.pers.game_helpchanged != game.helpchanged) {
+ ent.client.pers.game_helpchanged = game.helpchanged;
+ ent.client.pers.helpchanged = 1;
+ }
+
+ // help beep (no more than three times)
+ if (ent.client.pers.helpchanged != 0 && ent.client.pers.helpchanged <= 3 && 0 == (level.framenum & 63)) {
+ ent.client.pers.helpchanged++;
+ gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
+ }
+
+ if (ent.client.pers.weapon != null)
+ weap = ent.client.pers.weapon.classname;
+ else
+ weap = "";
+
+ if (ent.waterlevel != 0 && 0 != (ent.watertype & (CONTENTS_LAVA | CONTENTS_SLIME)))
+ ent.s.sound = snd_fry;
+ else if (strcmp(weap, "weapon_railgun") == 0)
+ ent.s.sound = gi.soundindex("weapons/rg_hum.wav");
+ else if (strcmp(weap, "weapon_bfg") == 0)
+ ent.s.sound = gi.soundindex("weapons/bfg_hum.wav");
+ else if (ent.client.weapon_sound != 0)
+ ent.s.sound = ent.client.weapon_sound;
+ else
+ ent.s.sound = 0;
+ }
+
+ /*
+ ===============
+ G_SetClientFrame
+ ===============
+ */
+ public static void G_SetClientFrame(edict_t ent) {
+ gclient_t client;
+ boolean duck, run;
+
+ if (ent.s.modelindex != 255)
+ return; // not in the player model
+
+ client = ent.client;
+
+ if ((client.ps.pmove.pm_flags & PMF_DUCKED)!=0)
+ duck = true;
+ else
+ duck = false;
+ if (xyspeed!=0)
+ run = true;
+ else
+ run = false;
+
+ boolean skip = false;
+ // check for stand/duck and stop/go transitions
+ if (duck != client.anim_duck && client.anim_priority < ANIM_DEATH)
+ skip = true;
+
+ if (run != client.anim_run && client.anim_priority == ANIM_BASIC)
+ skip = true;
+
+ if (null==ent.groundentity && client.anim_priority <= ANIM_WAVE)
+ skip = true;
+
+ if (!skip) {
+ if (client.anim_priority == ANIM_REVERSE) {
+ if (ent.s.frame > client.anim_end) {
+ ent.s.frame--;
+ return;
+ }
+ }
+ else if (ent.s.frame < client.anim_end) { // continue an animation
+ ent.s.frame++;
+ return;
+ }
+
+ if (client.anim_priority == ANIM_DEATH)
+ return; // stay there
+ if (client.anim_priority == ANIM_JUMP) {
+ if (null==ent.groundentity)
+ return; // stay there
+ ent.client.anim_priority = ANIM_WAVE;
+ ent.s.frame = FRAME_jump3;
+ ent.client.anim_end = FRAME_jump6;
+ return;
+ }
+ }
+
+ // return to either a running or standing frame
+ client.anim_priority = ANIM_BASIC;
+ client.anim_duck = duck;
+ client.anim_run = run;
+
+ if (null==ent.groundentity) {
+ client.anim_priority = ANIM_JUMP;
+ if (ent.s.frame != FRAME_jump2)
+ ent.s.frame = FRAME_jump1;
+ client.anim_end = FRAME_jump2;
+ }
+ else if (run) { // running
+ if (duck) {
+ ent.s.frame = FRAME_crwalk1;
+ client.anim_end = FRAME_crwalk6;
+ }
+ else {
+ ent.s.frame = FRAME_run1;
+ client.anim_end = FRAME_run6;
+ }
+ }
+ else { // standing
+ if (duck) {
+ ent.s.frame = FRAME_crstnd01;
+ client.anim_end = FRAME_crstnd19;
+ }
+ else {
+ ent.s.frame = FRAME_stand01;
+ client.anim_end = FRAME_stand40;
+ }
+ }
+ }
+
+ /*
+ =================
+ ClientEndServerFrame
+
+ Called for each player at the end of the server frame
+ and right after spawning
+ =================
+ */
+ public static void ClientEndServerFrame(edict_t ent) {
+ float bobtime;
+ int i;
+
+ current_player = ent;
+ current_client = ent.client;
+
+ //
+ // If the origin or velocity have changed since ClientThink(),
+ // update the pmove values. This will happen when the client
+ // is pushed by a bmodel or kicked by an explosion.
+ //
+ // If it wasn't updated here, the view position would lag a frame
+ // behind the body position when pushed -- "sinking into plats"
+ //
+ for (i = 0; i < 3; i++) {
+ current_client.ps.pmove.origin[i] = (short) (ent.s.origin[i] * 8.0);
+ current_client.ps.pmove.velocity[i] = (short) (ent.velocity[i] * 8.0);
+ }
+
+ //
+ // If the end of unit layout is displayed, don't give
+ // the player any normal movement attributes
+ //
+ if (level.intermissiontime!=0) {
+ // FIXME: add view drifting here?
+ current_client.ps.blend[3] = 0;
+ current_client.ps.fov = 90;
+ G_SetStats(ent);
+ return;
+ }
+
+ AngleVectors(ent.client.v_angle, forward, right, up);
+
+ // burn from lava, etc
+ P_WorldEffects();
+
+ //
+ // set model angles from view angles so other things in
+ // the world can tell which direction you are looking
+ //
+ if (ent.client.v_angle[PITCH] > 180)
+ ent.s.angles[PITCH] = (-360 + ent.client.v_angle[PITCH]) / 3;
+ else
+ ent.s.angles[PITCH] = ent.client.v_angle[PITCH] / 3;
+ ent.s.angles[YAW] = ent.client.v_angle[YAW];
+ ent.s.angles[ROLL] = 0;
+ ent.s.angles[ROLL] = SV_CalcRoll(ent.s.angles, ent.velocity) * 4;
+
+ //
+ // calculate speed and cycle to be used for
+ // all cyclic walking effects
+ //
+ xyspeed = (float) Math.sqrt(ent.velocity[0] * ent.velocity[0] + ent.velocity[1] * ent.velocity[1]);
+
+ if (xyspeed < 5) {
+ bobmove = 0;
+ current_client.bobtime = 0; // start at beginning of cycle again
+ }
+ else if (ent.groundentity!=null) { // so bobbing only cycles when on ground
+ if (xyspeed > 210)
+ bobmove = 0.25f;
+ else if (xyspeed > 100)
+ bobmove = 0.125f;
+ else
+ bobmove = 0.0625f;
+ }
+
+ bobtime = (current_client.bobtime += bobmove);
+
+ if ((current_client.ps.pmove.pm_flags & PMF_DUCKED)!=0)
+ bobtime *= 4;
+
+ bobcycle = (int) bobtime;
+ bobfracsin = (float) Math.abs(Math.sin(bobtime * Math.PI));
+
+ // detect hitting the floor
+ P_FallingDamage(ent);
+
+ // apply all the damage taken this frame
+ P_DamageFeedback(ent);
+
+ // determine the view offsets
+ SV_CalcViewOffset(ent);
+
+ // determine the gun offsets
+ SV_CalcGunOffset(ent);
+
+ // determine the full screen color blend
+ // must be after viewoffset, so eye contents can be
+ // accurately determined
+ // FIXME: with client prediction, the contents
+ // should be determined by the client
+ SV_CalcBlend(ent);
+
+ // chase cam stuff
+ if (ent.client.resp.spectator)
+ G_SetSpectatorStats(ent);
+ else
+ G_SetStats(ent);
+ G_CheckChaseStats(ent);
+
+ G_SetClientEvent(ent);
+
+ G_SetClientEffects(ent);
+
+ G_SetClientSound(ent);
+
+ G_SetClientFrame(ent);
+
+ VectorCopy(ent.velocity, ent.client.oldvelocity);
+ VectorCopy(ent.client.ps.viewangles, ent.client.oldviewangles);
+
+ // clear weapon kicks
+ VectorClear(ent.client.kick_origin);
+ VectorClear(ent.client.kick_angles);
+
+ // if the scoreboard is up, update it
+ if (ent.client.showscores && 0==(level.framenum & 31)) {
+ DeathmatchScoreboardMessage(ent, ent.enemy);
+ gi.unicast(ent, false);
+ }
+ }
+}