Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

p_view.c (27656B)


      1 /*
      2 Copyright (C) 1997-2001 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 
     21 #include "g_local.h"
     22 #include "m_player.h"
     23 
     24 
     25 
     26 static	edict_t		*current_player;
     27 static	gclient_t	*current_client;
     28 
     29 static	vec3_t	forward, right, up;
     30 float	xyspeed;
     31 
     32 float	bobmove;
     33 int		bobcycle;		// odd cycles are right foot going forward
     34 float	bobfracsin;		// sin(bobfrac*M_PI)
     35 
     36 /*
     37 ===============
     38 SV_CalcRoll
     39 
     40 ===============
     41 */
     42 float SV_CalcRoll (vec3_t angles, vec3_t velocity)
     43 {
     44 	float	sign;
     45 	float	side;
     46 	float	value;
     47 	
     48 	side = DotProduct (velocity, right);
     49 	sign = side < 0 ? -1 : 1;
     50 	side = fabs(side);
     51 	
     52 	value = sv_rollangle->value;
     53 
     54 	if (side < sv_rollspeed->value)
     55 		side = side * value / sv_rollspeed->value;
     56 	else
     57 		side = value;
     58 	
     59 	return side*sign;
     60 	
     61 }
     62 
     63 
     64 /*
     65 ===============
     66 P_DamageFeedback
     67 
     68 Handles color blends and view kicks
     69 ===============
     70 */
     71 void P_DamageFeedback (edict_t *player)
     72 {
     73 	gclient_t	*client;
     74 	float	side;
     75 	float	realcount, count, kick;
     76 	vec3_t	v;
     77 	int		r, l;
     78 	static	vec3_t	power_color = {0.0, 1.0, 0.0};
     79 	static	vec3_t	acolor = {1.0, 1.0, 1.0};
     80 	static	vec3_t	bcolor = {1.0, 0.0, 0.0};
     81 
     82 	client = player->client;
     83 
     84 	// flash the backgrounds behind the status numbers
     85 	client->ps.stats[STAT_FLASHES] = 0;
     86 	if (client->damage_blood)
     87 		client->ps.stats[STAT_FLASHES] |= 1;
     88 	if (client->damage_armor && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum))
     89 		client->ps.stats[STAT_FLASHES] |= 2;
     90 
     91 	// total points of damage shot at the player this frame
     92 	count = (client->damage_blood + client->damage_armor + client->damage_parmor);
     93 	if (count == 0)
     94 		return;		// didn't take any damage
     95 
     96 	// start a pain animation if still in the player model
     97 	if (client->anim_priority < ANIM_PAIN && player->s.modelindex == 255)
     98 	{
     99 		static int		i;
    100 
    101 		client->anim_priority = ANIM_PAIN;
    102 		if (client->ps.pmove.pm_flags & PMF_DUCKED)
    103 		{
    104 			player->s.frame = FRAME_crpain1-1;
    105 			client->anim_end = FRAME_crpain4;
    106 		}
    107 		else
    108 		{
    109 			i = (i+1)%3;
    110 			switch (i)
    111 			{
    112 			case 0:
    113 				player->s.frame = FRAME_pain101-1;
    114 				client->anim_end = FRAME_pain104;
    115 				break;
    116 			case 1:
    117 				player->s.frame = FRAME_pain201-1;
    118 				client->anim_end = FRAME_pain204;
    119 				break;
    120 			case 2:
    121 				player->s.frame = FRAME_pain301-1;
    122 				client->anim_end = FRAME_pain304;
    123 				break;
    124 			}
    125 		}
    126 	}
    127 
    128 	realcount = count;
    129 	if (count < 10)
    130 		count = 10;	// allways make a visible effect
    131 
    132 	// play an apropriate pain sound
    133 	if ((level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum))
    134 	{
    135 		r = 1 + (rand()&1);
    136 		player->pain_debounce_time = level.time + 0.7;
    137 		if (player->health < 25)
    138 			l = 25;
    139 		else if (player->health < 50)
    140 			l = 50;
    141 		else if (player->health < 75)
    142 			l = 75;
    143 		else
    144 			l = 100;
    145 		gi.sound (player, CHAN_VOICE, gi.soundindex(va("*pain%i_%i.wav", l, r)), 1, ATTN_NORM, 0);
    146 	}
    147 
    148 	// the total alpha of the blend is allways proportional to count
    149 	if (client->damage_alpha < 0)
    150 		client->damage_alpha = 0;
    151 	client->damage_alpha += count*0.01;
    152 	if (client->damage_alpha < 0.2)
    153 		client->damage_alpha = 0.2;
    154 	if (client->damage_alpha > 0.6)
    155 		client->damage_alpha = 0.6;		// don't go too saturated
    156 
    157 	// the color of the blend will vary based on how much was absorbed
    158 	// by different armors
    159 	VectorClear (v);
    160 	if (client->damage_parmor)
    161 		VectorMA (v, (float)client->damage_parmor/realcount, power_color, v);
    162 	if (client->damage_armor)
    163 		VectorMA (v, (float)client->damage_armor/realcount,  acolor, v);
    164 	if (client->damage_blood)
    165 		VectorMA (v, (float)client->damage_blood/realcount,  bcolor, v);
    166 	VectorCopy (v, client->damage_blend);
    167 
    168 
    169 	//
    170 	// calculate view angle kicks
    171 	//
    172 	kick = abs(client->damage_knockback);
    173 	if (kick && player->health > 0)	// kick of 0 means no view adjust at all
    174 	{
    175 		kick = kick * 100 / player->health;
    176 
    177 		if (kick < count*0.5)
    178 			kick = count*0.5;
    179 		if (kick > 50)
    180 			kick = 50;
    181 
    182 		VectorSubtract (client->damage_from, player->s.origin, v);
    183 		VectorNormalize (v);
    184 		
    185 		side = DotProduct (v, right);
    186 		client->v_dmg_roll = kick*side*0.3;
    187 		
    188 		side = -DotProduct (v, forward);
    189 		client->v_dmg_pitch = kick*side*0.3;
    190 
    191 		client->v_dmg_time = level.time + DAMAGE_TIME;
    192 	}
    193 
    194 	//
    195 	// clear totals
    196 	//
    197 	client->damage_blood = 0;
    198 	client->damage_armor = 0;
    199 	client->damage_parmor = 0;
    200 	client->damage_knockback = 0;
    201 }
    202 
    203 
    204 
    205 
    206 /*
    207 ===============
    208 SV_CalcViewOffset
    209 
    210 Auto pitching on slopes?
    211 
    212   fall from 128: 400 = 160000
    213   fall from 256: 580 = 336400
    214   fall from 384: 720 = 518400
    215   fall from 512: 800 = 640000
    216   fall from 640: 960 = 
    217 
    218   damage = deltavelocity*deltavelocity  * 0.0001
    219 
    220 ===============
    221 */
    222 void SV_CalcViewOffset (edict_t *ent)
    223 {
    224 	float		*angles;
    225 	float		bob;
    226 	float		ratio;
    227 	float		delta;
    228 	vec3_t		v;
    229 
    230 
    231 //===================================
    232 
    233 	// base angles
    234 	angles = ent->client->ps.kick_angles;
    235 
    236 	// if dead, fix the angle and don't add any kick
    237 	if (ent->deadflag)
    238 	{
    239 		VectorClear (angles);
    240 
    241 		ent->client->ps.viewangles[ROLL] = 40;
    242 		ent->client->ps.viewangles[PITCH] = -15;
    243 		ent->client->ps.viewangles[YAW] = ent->client->killer_yaw;
    244 	}
    245 	else
    246 	{
    247 		// add angles based on weapon kick
    248 
    249 		VectorCopy (ent->client->kick_angles, angles);
    250 
    251 		// add angles based on damage kick
    252 
    253 		ratio = (ent->client->v_dmg_time - level.time) / DAMAGE_TIME;
    254 		if (ratio < 0)
    255 		{
    256 			ratio = 0;
    257 			ent->client->v_dmg_pitch = 0;
    258 			ent->client->v_dmg_roll = 0;
    259 		}
    260 		angles[PITCH] += ratio * ent->client->v_dmg_pitch;
    261 		angles[ROLL] += ratio * ent->client->v_dmg_roll;
    262 
    263 		// add pitch based on fall kick
    264 
    265 		ratio = (ent->client->fall_time - level.time) / FALL_TIME;
    266 		if (ratio < 0)
    267 			ratio = 0;
    268 		angles[PITCH] += ratio * ent->client->fall_value;
    269 
    270 		// add angles based on velocity
    271 
    272 		delta = DotProduct (ent->velocity, forward);
    273 		angles[PITCH] += delta*run_pitch->value;
    274 		
    275 		delta = DotProduct (ent->velocity, right);
    276 		angles[ROLL] += delta*run_roll->value;
    277 
    278 		// add angles based on bob
    279 
    280 		delta = bobfracsin * bob_pitch->value * xyspeed;
    281 		if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
    282 			delta *= 6;		// crouching
    283 		angles[PITCH] += delta;
    284 		delta = bobfracsin * bob_roll->value * xyspeed;
    285 		if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
    286 			delta *= 6;		// crouching
    287 		if (bobcycle & 1)
    288 			delta = -delta;
    289 		angles[ROLL] += delta;
    290 	}
    291 
    292 //===================================
    293 
    294 	// base origin
    295 
    296 	VectorClear (v);
    297 
    298 	// add view height
    299 
    300 	v[2] += ent->viewheight;
    301 
    302 	// add fall height
    303 
    304 	ratio = (ent->client->fall_time - level.time) / FALL_TIME;
    305 	if (ratio < 0)
    306 		ratio = 0;
    307 	v[2] -= ratio * ent->client->fall_value * 0.4;
    308 
    309 	// add bob height
    310 
    311 	bob = bobfracsin * xyspeed * bob_up->value;
    312 	if (bob > 6)
    313 		bob = 6;
    314 	//gi.DebugGraph (bob *2, 255);
    315 	v[2] += bob;
    316 
    317 	// add kick offset
    318 
    319 	VectorAdd (v, ent->client->kick_origin, v);
    320 
    321 	// absolutely bound offsets
    322 	// so the view can never be outside the player box
    323 
    324 	if (v[0] < -14)
    325 		v[0] = -14;
    326 	else if (v[0] > 14)
    327 		v[0] = 14;
    328 	if (v[1] < -14)
    329 		v[1] = -14;
    330 	else if (v[1] > 14)
    331 		v[1] = 14;
    332 	if (v[2] < -22)
    333 		v[2] = -22;
    334 	else if (v[2] > 30)
    335 		v[2] = 30;
    336 
    337 	VectorCopy (v, ent->client->ps.viewoffset);
    338 }
    339 
    340 /*
    341 ==============
    342 SV_CalcGunOffset
    343 ==============
    344 */
    345 void SV_CalcGunOffset (edict_t *ent)
    346 {
    347 	int		i;
    348 	float	delta;
    349 
    350 	// gun angles from bobbing
    351 	ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005;
    352 	ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01;
    353 	if (bobcycle & 1)
    354 	{
    355 		ent->client->ps.gunangles[ROLL] = -ent->client->ps.gunangles[ROLL];
    356 		ent->client->ps.gunangles[YAW] = -ent->client->ps.gunangles[YAW];
    357 	}
    358 
    359 	ent->client->ps.gunangles[PITCH] = xyspeed * bobfracsin * 0.005;
    360 
    361 	// gun angles from delta movement
    362 	for (i=0 ; i<3 ; i++)
    363 	{
    364 		delta = ent->client->oldviewangles[i] - ent->client->ps.viewangles[i];
    365 		if (delta > 180)
    366 			delta -= 360;
    367 		if (delta < -180)
    368 			delta += 360;
    369 		if (delta > 45)
    370 			delta = 45;
    371 		if (delta < -45)
    372 			delta = -45;
    373 		if (i == YAW)
    374 			ent->client->ps.gunangles[ROLL] += 0.1*delta;
    375 		ent->client->ps.gunangles[i] += 0.2 * delta;
    376 	}
    377 
    378 	// gun height
    379 	VectorClear (ent->client->ps.gunoffset);
    380 //	ent->ps->gunorigin[2] += bob;
    381 
    382 	// gun_x / gun_y / gun_z are development tools
    383 	for (i=0 ; i<3 ; i++)
    384 	{
    385 		ent->client->ps.gunoffset[i] += forward[i]*(gun_y->value);
    386 		ent->client->ps.gunoffset[i] += right[i]*gun_x->value;
    387 		ent->client->ps.gunoffset[i] += up[i]* (-gun_z->value);
    388 	}
    389 }
    390 
    391 
    392 /*
    393 =============
    394 SV_AddBlend
    395 =============
    396 */
    397 void SV_AddBlend (float r, float g, float b, float a, float *v_blend)
    398 {
    399 	float	a2, a3;
    400 
    401 	if (a <= 0)
    402 		return;
    403 	a2 = v_blend[3] + (1-v_blend[3])*a;	// new total alpha
    404 	a3 = v_blend[3]/a2;		// fraction of color from old
    405 
    406 	v_blend[0] = v_blend[0]*a3 + r*(1-a3);
    407 	v_blend[1] = v_blend[1]*a3 + g*(1-a3);
    408 	v_blend[2] = v_blend[2]*a3 + b*(1-a3);
    409 	v_blend[3] = a2;
    410 }
    411 
    412 
    413 /*
    414 =============
    415 SV_CalcBlend
    416 =============
    417 */
    418 void SV_CalcBlend (edict_t *ent)
    419 {
    420 	int		contents;
    421 	vec3_t	vieworg;
    422 	int		remaining;
    423 
    424 	ent->client->ps.blend[0] = ent->client->ps.blend[1] = 
    425 		ent->client->ps.blend[2] = ent->client->ps.blend[3] = 0;
    426 
    427 	// add for contents
    428 	VectorAdd (ent->s.origin, ent->client->ps.viewoffset, vieworg);
    429 	contents = gi.pointcontents (vieworg);
    430 	if (contents & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER) )
    431 		ent->client->ps.rdflags |= RDF_UNDERWATER;
    432 	else
    433 		ent->client->ps.rdflags &= ~RDF_UNDERWATER;
    434 
    435 	if (contents & (CONTENTS_SOLID|CONTENTS_LAVA))
    436 		SV_AddBlend (1.0, 0.3, 0.0, 0.6, ent->client->ps.blend);
    437 	else if (contents & CONTENTS_SLIME)
    438 		SV_AddBlend (0.0, 0.1, 0.05, 0.6, ent->client->ps.blend);
    439 	else if (contents & CONTENTS_WATER)
    440 		SV_AddBlend (0.5, 0.3, 0.2, 0.4, ent->client->ps.blend);
    441 
    442 	// add for powerups
    443 	if (ent->client->quad_framenum > level.framenum)
    444 	{
    445 		remaining = ent->client->quad_framenum - level.framenum;
    446 		if (remaining == 30)	// beginning to fade
    447 			gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage2.wav"), 1, ATTN_NORM, 0);
    448 		if (remaining > 30 || (remaining & 4) )
    449 			SV_AddBlend (0, 0, 1, 0.08, ent->client->ps.blend);
    450 	}
    451 	else if (ent->client->invincible_framenum > level.framenum)
    452 	{
    453 		remaining = ent->client->invincible_framenum - level.framenum;
    454 		if (remaining == 30)	// beginning to fade
    455 			gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect2.wav"), 1, ATTN_NORM, 0);
    456 		if (remaining > 30 || (remaining & 4) )
    457 			SV_AddBlend (1, 1, 0, 0.08, ent->client->ps.blend);
    458 	}
    459 	else if (ent->client->enviro_framenum > level.framenum)
    460 	{
    461 		remaining = ent->client->enviro_framenum - level.framenum;
    462 		if (remaining == 30)	// beginning to fade
    463 			gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
    464 		if (remaining > 30 || (remaining & 4) )
    465 			SV_AddBlend (0, 1, 0, 0.08, ent->client->ps.blend);
    466 	}
    467 	else if (ent->client->breather_framenum > level.framenum)
    468 	{
    469 		remaining = ent->client->breather_framenum - level.framenum;
    470 		if (remaining == 30)	// beginning to fade
    471 			gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
    472 		if (remaining > 30 || (remaining & 4) )
    473 			SV_AddBlend (0.4, 1, 0.4, 0.04, ent->client->ps.blend);
    474 	}
    475 
    476 	// add for damage
    477 	if (ent->client->damage_alpha > 0)
    478 		SV_AddBlend (ent->client->damage_blend[0],ent->client->damage_blend[1]
    479 		,ent->client->damage_blend[2], ent->client->damage_alpha, ent->client->ps.blend);
    480 
    481 	if (ent->client->bonus_alpha > 0)
    482 		SV_AddBlend (0.85, 0.7, 0.3, ent->client->bonus_alpha, ent->client->ps.blend);
    483 
    484 	// drop the damage value
    485 	ent->client->damage_alpha -= 0.06;
    486 	if (ent->client->damage_alpha < 0)
    487 		ent->client->damage_alpha = 0;
    488 
    489 	// drop the bonus value
    490 	ent->client->bonus_alpha -= 0.1;
    491 	if (ent->client->bonus_alpha < 0)
    492 		ent->client->bonus_alpha = 0;
    493 }
    494 
    495 
    496 /*
    497 =================
    498 P_FallingDamage
    499 =================
    500 */
    501 void P_FallingDamage (edict_t *ent)
    502 {
    503 	float	delta;
    504 	int		damage;
    505 	vec3_t	dir;
    506 
    507 	if (ent->s.modelindex != 255)
    508 		return;		// not in the player model
    509 
    510  	if (ent->movetype == MOVETYPE_NOCLIP)
    511 		return;
    512 
    513 	if ((ent->client->oldvelocity[2] < 0) && (ent->velocity[2] > ent->client->oldvelocity[2]) && (!ent->groundentity))
    514 	{
    515 		delta = ent->client->oldvelocity[2];
    516 	}
    517 	else
    518 	{
    519 		if (!ent->groundentity)
    520 			return;
    521 		delta = ent->velocity[2] - ent->client->oldvelocity[2];
    522 	}
    523 	delta = delta*delta * 0.0001;
    524 
    525 //ZOID
    526 	// never take damage if just release grapple or on grapple
    527 	if (level.time - ent->client->ctf_grapplereleasetime <= FRAMETIME * 2 ||
    528 		(ent->client->ctf_grapple && 
    529 		ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY))
    530 		return;
    531 //ZOID
    532 
    533 	// never take falling damage if completely underwater
    534 	if (ent->waterlevel == 3)
    535 		return;
    536 	if (ent->waterlevel == 2)
    537 		delta *= 0.25;
    538 	if (ent->waterlevel == 1)
    539 		delta *= 0.5;
    540 
    541 	if (delta < 1)
    542 		return;
    543 
    544 	if (delta < 15)
    545 	{
    546 		ent->s.event = EV_FOOTSTEP;
    547 		return;
    548 	}
    549 
    550 	ent->client->fall_value = delta*0.5;
    551 	if (ent->client->fall_value > 40)
    552 		ent->client->fall_value = 40;
    553 	ent->client->fall_time = level.time + FALL_TIME;
    554 
    555 	if (delta > 30)
    556 	{
    557 		if (ent->health > 0)
    558 		{
    559 			if (delta >= 55)
    560 				ent->s.event = EV_FALLFAR;
    561 			else
    562 				ent->s.event = EV_FALL;
    563 		}
    564 		ent->pain_debounce_time = level.time;	// no normal pain sound
    565 		damage = (delta-30)/2;
    566 		if (damage < 1)
    567 			damage = 1;
    568 		VectorSet (dir, 0, 0, 1);
    569 
    570 		if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING) )
    571 			T_Damage (ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING);
    572 	}
    573 	else
    574 	{
    575 		ent->s.event = EV_FALLSHORT;
    576 		return;
    577 	}
    578 }
    579 
    580 
    581 
    582 /*
    583 =============
    584 P_WorldEffects
    585 =============
    586 */
    587 void P_WorldEffects (void)
    588 {
    589 	qboolean	breather;
    590 	qboolean	envirosuit;
    591 	int			waterlevel, old_waterlevel;
    592 
    593 	if (current_player->movetype == MOVETYPE_NOCLIP)
    594 	{
    595 		current_player->air_finished = level.time + 12;	// don't need air
    596 		return;
    597 	}
    598 
    599 	waterlevel = current_player->waterlevel;
    600 	old_waterlevel = current_client->old_waterlevel;
    601 	current_client->old_waterlevel = waterlevel;
    602 
    603 	breather = current_client->breather_framenum > level.framenum;
    604 	envirosuit = current_client->enviro_framenum > level.framenum;
    605 
    606 	//
    607 	// if just entered a water volume, play a sound
    608 	//
    609 	if (!old_waterlevel && waterlevel)
    610 	{
    611 		PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
    612 		if (current_player->watertype & CONTENTS_LAVA)
    613 			gi.sound (current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0);
    614 		else if (current_player->watertype & CONTENTS_SLIME)
    615 			gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
    616 		else if (current_player->watertype & CONTENTS_WATER)
    617 			gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
    618 		current_player->flags |= FL_INWATER;
    619 
    620 		// clear damage_debounce, so the pain sound will play immediately
    621 		current_player->damage_debounce_time = level.time - 1;
    622 	}
    623 
    624 	//
    625 	// if just completely exited a water volume, play a sound
    626 	//
    627 	if (old_waterlevel && ! waterlevel)
    628 	{
    629 		PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
    630 		gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
    631 		current_player->flags &= ~FL_INWATER;
    632 	}
    633 
    634 	//
    635 	// check for head just going under water
    636 	//
    637 	if (old_waterlevel != 3 && waterlevel == 3)
    638 	{
    639 		gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0);
    640 	}
    641 
    642 	//
    643 	// check for head just coming out of water
    644 	//
    645 	if (old_waterlevel == 3 && waterlevel != 3)
    646 	{
    647 		if (current_player->air_finished < level.time)
    648 		{	// gasp for air
    649 			gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0);
    650 			PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
    651 		}
    652 		else  if (current_player->air_finished < level.time + 11)
    653 		{	// just break surface
    654 			gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0);
    655 		}
    656 	}
    657 
    658 	//
    659 	// check for drowning
    660 	//
    661 	if (waterlevel == 3)
    662 	{
    663 		// breather or envirosuit give air
    664 		if (breather || envirosuit)
    665 		{
    666 			current_player->air_finished = level.time + 10;
    667 
    668 			if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0)
    669 			{
    670 				if (!current_client->breather_sound)
    671 					gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0);
    672 				else
    673 					gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0);
    674 				current_client->breather_sound ^= 1;
    675 				PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
    676 				//FIXME: release a bubble?
    677 			}
    678 		}
    679 
    680 		// if out of air, start drowning
    681 		if (current_player->air_finished < level.time)
    682 		{	// drown!
    683 			if (current_player->client->next_drown_time < level.time 
    684 				&& current_player->health > 0)
    685 			{
    686 				current_player->client->next_drown_time = level.time + 1;
    687 
    688 				// take more damage the longer underwater
    689 				current_player->dmg += 2;
    690 				if (current_player->dmg > 15)
    691 					current_player->dmg = 15;
    692 
    693 				// play a gurp sound instead of a normal pain sound
    694 				if (current_player->health <= current_player->dmg)
    695 					gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0);
    696 				else if (rand()&1)
    697 					gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0);
    698 				else
    699 					gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0);
    700 
    701 				current_player->pain_debounce_time = level.time;
    702 
    703 				T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
    704 			}
    705 		}
    706 	}
    707 	else
    708 	{
    709 		current_player->air_finished = level.time + 12;
    710 		current_player->dmg = 2;
    711 	}
    712 
    713 	//
    714 	// check for sizzle damage
    715 	//
    716 	if (waterlevel && (current_player->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
    717 	{
    718 		if (current_player->watertype & CONTENTS_LAVA)
    719 		{
    720 			if (current_player->health > 0
    721 				&& current_player->pain_debounce_time <= level.time
    722 				&& current_client->invincible_framenum < level.framenum)
    723 			{
    724 				if (rand()&1)
    725 					gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0);
    726 				else
    727 					gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0);
    728 				current_player->pain_debounce_time = level.time + 1;
    729 			}
    730 
    731 			if (envirosuit)	// take 1/3 damage with envirosuit
    732 				T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_LAVA);
    733 			else
    734 				T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3*waterlevel, 0, 0, MOD_LAVA);
    735 		}
    736 
    737 		if (current_player->watertype & CONTENTS_SLIME)
    738 		{
    739 			if (!envirosuit)
    740 			{	// no damage from slime with envirosuit
    741 				T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_SLIME);
    742 			}
    743 		}
    744 	}
    745 }
    746 
    747 
    748 /*
    749 ===============
    750 G_SetClientEffects
    751 ===============
    752 */
    753 void G_SetClientEffects (edict_t *ent)
    754 {
    755 	int		pa_type;
    756 	int		remaining;
    757 
    758 	ent->s.effects = 0;
    759 	ent->s.renderfx = 0;
    760 
    761 	if (ent->health <= 0 || level.intermissiontime)
    762 		return;
    763 
    764 	if (ent->powerarmor_time > level.time)
    765 	{
    766 		pa_type = PowerArmorType (ent);
    767 		if (pa_type == POWER_ARMOR_SCREEN)
    768 		{
    769 			ent->s.effects |= EF_POWERSCREEN;
    770 		}
    771 		else if (pa_type == POWER_ARMOR_SHIELD)
    772 		{
    773 			ent->s.effects |= EF_COLOR_SHELL;
    774 			ent->s.renderfx |= RF_SHELL_GREEN;
    775 		}
    776 	}
    777 
    778 //ZOID
    779 	CTFEffects(ent);
    780 //ZOID
    781 
    782 	if (ent->client->quad_framenum > level.framenum
    783 //ZOID
    784 		&& (level.framenum & 8)
    785 //ZOID
    786 		)
    787 	{
    788 		remaining = ent->client->quad_framenum - level.framenum;
    789 		if (remaining > 30 || (remaining & 4) )
    790 			ent->s.effects |= EF_QUAD;
    791 	}
    792 
    793 	if (ent->client->invincible_framenum > level.framenum
    794 //ZOID
    795 		&& (level.framenum & 8)
    796 //ZOID
    797 		)
    798 	{
    799 		remaining = ent->client->invincible_framenum - level.framenum;
    800 		if (remaining > 30 || (remaining & 4) )
    801 			ent->s.effects |= EF_PENT;
    802 	}
    803 
    804 	// show cheaters!!!
    805 	if (ent->flags & FL_GODMODE)
    806 	{
    807 		ent->s.effects |= EF_COLOR_SHELL;
    808 		ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE);
    809 	}
    810 }
    811 
    812 
    813 /*
    814 ===============
    815 G_SetClientEvent
    816 ===============
    817 */
    818 void G_SetClientEvent (edict_t *ent)
    819 {
    820 	if (ent->s.event)
    821 		return;
    822 
    823 	if ( ent->groundentity && xyspeed > 225)
    824 	{
    825 		if ( (int)(current_client->bobtime+bobmove) != bobcycle )
    826 			ent->s.event = EV_FOOTSTEP;
    827 	}
    828 }
    829 
    830 /*
    831 ===============
    832 G_SetClientSound
    833 ===============
    834 */
    835 void G_SetClientSound (edict_t *ent)
    836 {
    837 	char	*weap;
    838 
    839 	if (ent->client->resp.game_helpchanged != game.helpchanged)
    840 	{
    841 		ent->client->resp.game_helpchanged = game.helpchanged;
    842 		ent->client->resp.helpchanged = 1;
    843 	}
    844 
    845 	// help beep (no more than three times)
    846 	if (ent->client->resp.helpchanged && ent->client->resp.helpchanged <= 3 && !(level.framenum&63) )
    847 	{
    848 		ent->client->resp.helpchanged++;
    849 		gi.sound (ent, CHAN_VOICE, gi.soundindex ("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
    850 	}
    851 
    852 
    853 	if (ent->client->pers.weapon)
    854 		weap = ent->client->pers.weapon->classname;
    855 	else
    856 		weap = "";
    857 
    858 	if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
    859 		ent->s.sound = snd_fry;
    860 	else if (strcmp(weap, "weapon_railgun") == 0)
    861 		ent->s.sound = gi.soundindex("weapons/rg_hum.wav");
    862 	else if (strcmp(weap, "weapon_bfg") == 0)
    863 		ent->s.sound = gi.soundindex("weapons/bfg_hum.wav");
    864 	else if (ent->client->weapon_sound)
    865 		ent->s.sound = ent->client->weapon_sound;
    866 	else
    867 		ent->s.sound = 0;
    868 }
    869 
    870 /*
    871 ===============
    872 G_SetClientFrame
    873 ===============
    874 */
    875 void G_SetClientFrame (edict_t *ent)
    876 {
    877 	gclient_t	*client;
    878 	qboolean	duck, run;
    879 
    880 	if (ent->s.modelindex != 255)
    881 		return;		// not in the player model
    882 
    883 	client = ent->client;
    884 
    885 	if (client->ps.pmove.pm_flags & PMF_DUCKED)
    886 		duck = true;
    887 	else
    888 		duck = false;
    889 	if (xyspeed)
    890 		run = true;
    891 	else
    892 		run = false;
    893 
    894 	// check for stand/duck and stop/go transitions
    895 	if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH)
    896 		goto newanim;
    897 	if (run != client->anim_run && client->anim_priority == ANIM_BASIC)
    898 		goto newanim;
    899 	if (!ent->groundentity && client->anim_priority <= ANIM_WAVE)
    900 		goto newanim;
    901 
    902 	if(client->anim_priority == ANIM_REVERSE)
    903 	{
    904 		if(ent->s.frame > client->anim_end)
    905 		{
    906 			ent->s.frame--;
    907 			return;
    908 		}
    909 	}
    910 	else if (ent->s.frame < client->anim_end)
    911 	{	// continue an animation
    912 		ent->s.frame++;
    913 		return;
    914 	}
    915 
    916 	if (client->anim_priority == ANIM_DEATH)
    917 		return;		// stay there
    918 	if (client->anim_priority == ANIM_JUMP)
    919 	{
    920 		if (!ent->groundentity)
    921 			return;		// stay there
    922 		ent->client->anim_priority = ANIM_WAVE;
    923 		ent->s.frame = FRAME_jump3;
    924 		ent->client->anim_end = FRAME_jump6;
    925 		return;
    926 	}
    927 
    928 newanim:
    929 	// return to either a running or standing frame
    930 	client->anim_priority = ANIM_BASIC;
    931 	client->anim_duck = duck;
    932 	client->anim_run = run;
    933 
    934 	if (!ent->groundentity)
    935 	{
    936 //ZOID: if on grapple, don't go into jump frame, go into standing
    937 //frame
    938 		if (client->ctf_grapple) {
    939 			ent->s.frame = FRAME_stand01;
    940 			client->anim_end = FRAME_stand40;
    941 		} else {
    942 //ZOID
    943 		client->anim_priority = ANIM_JUMP;
    944 		if (ent->s.frame != FRAME_jump2)
    945 			ent->s.frame = FRAME_jump1;
    946 		client->anim_end = FRAME_jump2;
    947 	}
    948 	}
    949 	else if (run)
    950 	{	// running
    951 		if (duck)
    952 		{
    953 			ent->s.frame = FRAME_crwalk1;
    954 			client->anim_end = FRAME_crwalk6;
    955 		}
    956 		else
    957 		{
    958 			ent->s.frame = FRAME_run1;
    959 			client->anim_end = FRAME_run6;
    960 		}
    961 	}
    962 	else
    963 	{	// standing
    964 		if (duck)
    965 		{
    966 			ent->s.frame = FRAME_crstnd01;
    967 			client->anim_end = FRAME_crstnd19;
    968 		}
    969 		else
    970 		{
    971 			ent->s.frame = FRAME_stand01;
    972 			client->anim_end = FRAME_stand40;
    973 		}
    974 	}
    975 }
    976 
    977 
    978 /*
    979 =================
    980 ClientEndServerFrame
    981 
    982 Called for each player at the end of the server frame
    983 and right after spawning
    984 =================
    985 */
    986 void ClientEndServerFrame (edict_t *ent)
    987 {
    988 	float	bobtime;
    989 	int		i;
    990 
    991 	current_player = ent;
    992 	current_client = ent->client;
    993 
    994 	//
    995 	// If the origin or velocity have changed since ClientThink(),
    996 	// update the pmove values.  This will happen when the client
    997 	// is pushed by a bmodel or kicked by an explosion.
    998 	// 
    999 	// If it wasn't updated here, the view position would lag a frame
   1000 	// behind the body position when pushed -- "sinking into plats"
   1001 	//
   1002 	for (i=0 ; i<3 ; i++)
   1003 	{
   1004 		current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0;
   1005 		current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0;
   1006 	}
   1007 
   1008 	//
   1009 	// If the end of unit layout is displayed, don't give
   1010 	// the player any normal movement attributes
   1011 	//
   1012 	if (level.intermissiontime)
   1013 	{
   1014 		// FIXME: add view drifting here?
   1015 		current_client->ps.blend[3] = 0;
   1016 		current_client->ps.fov = 90;
   1017 		G_SetStats (ent);
   1018 		return;
   1019 	}
   1020 
   1021 	AngleVectors (ent->client->v_angle, forward, right, up);
   1022 
   1023 	// burn from lava, etc
   1024 	P_WorldEffects ();
   1025 
   1026 	//
   1027 	// set model angles from view angles so other things in
   1028 	// the world can tell which direction you are looking
   1029 	//
   1030 	if (ent->client->v_angle[PITCH] > 180)
   1031 		ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3;
   1032 	else
   1033 		ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3;
   1034 	ent->s.angles[YAW] = ent->client->v_angle[YAW];
   1035 	ent->s.angles[ROLL] = 0;
   1036 	ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4;
   1037 
   1038 	//
   1039 	// calculate speed and cycle to be used for
   1040 	// all cyclic walking effects
   1041 	//
   1042 	xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]);
   1043 
   1044 	if (xyspeed < 5)
   1045 	{
   1046 		bobmove = 0;
   1047 		current_client->bobtime = 0;	// start at beginning of cycle again
   1048 	}
   1049 	else if (ent->groundentity)
   1050 	{	// so bobbing only cycles when on ground
   1051 		if (xyspeed > 210)
   1052 			bobmove = 0.25;
   1053 		else if (xyspeed > 100)
   1054 			bobmove = 0.125;
   1055 		else
   1056 			bobmove = 0.0625;
   1057 	}
   1058 	
   1059 	bobtime = (current_client->bobtime += bobmove);
   1060 
   1061 	if (current_client->ps.pmove.pm_flags & PMF_DUCKED)
   1062 		bobtime *= 4;
   1063 
   1064 	bobcycle = (int)bobtime;
   1065 	bobfracsin = fabs(sin(bobtime*M_PI));
   1066 
   1067 	// detect hitting the floor
   1068 	P_FallingDamage (ent);
   1069 
   1070 	// apply all the damage taken this frame
   1071 	P_DamageFeedback (ent);
   1072 
   1073 	// determine the view offsets
   1074 	SV_CalcViewOffset (ent);
   1075 
   1076 	// determine the gun offsets
   1077 	SV_CalcGunOffset (ent);
   1078 
   1079 	// determine the full screen color blend
   1080 	// must be after viewoffset, so eye contents can be
   1081 	// accurately determined
   1082 	// FIXME: with client prediction, the contents
   1083 	// should be determined by the client
   1084 	SV_CalcBlend (ent);
   1085 
   1086 //ZOID
   1087 	if (!ent->client->chase_target)
   1088 //ZOID
   1089 		G_SetStats (ent);
   1090 
   1091 //ZOID
   1092 //update chasecam follower stats
   1093 	for (i = 1; i <= maxclients->value; i++) {
   1094 		edict_t *e = g_edicts + i;
   1095 		if (!e->inuse || e->client->chase_target != ent)
   1096 			continue;
   1097 		memcpy(e->client->ps.stats, 
   1098 			ent->client->ps.stats, 
   1099 			sizeof(ent->client->ps.stats));
   1100 		e->client->ps.stats[STAT_LAYOUTS] = 1;
   1101 		break;
   1102 	}
   1103 //ZOID
   1104 
   1105 
   1106 	G_SetClientEvent (ent);
   1107 
   1108 	G_SetClientEffects (ent);
   1109 
   1110 	G_SetClientSound (ent);
   1111 
   1112 	G_SetClientFrame (ent);
   1113 
   1114 	VectorCopy (ent->velocity, ent->client->oldvelocity);
   1115 	VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles);
   1116 
   1117 	// clear weapon kicks
   1118 	VectorClear (ent->client->kick_origin);
   1119 	VectorClear (ent->client->kick_angles);
   1120 
   1121 	// if the scoreboard is up, update it
   1122 	if (ent->client->showscores && !(level.framenum & 31) )
   1123 	{
   1124 //ZOID
   1125 		if (ent->client->menu) {
   1126 			PMenu_Do_Update(ent);
   1127 			ent->client->menudirty = false;
   1128 			ent->client->menutime = level.time;
   1129 		} else
   1130 //ZOID
   1131 			DeathmatchScoreboardMessage (ent, ent->enemy);
   1132 		gi.unicast (ent, false);
   1133 	}
   1134 }
   1135