Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

p_view.c (26675B)


      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;	// always 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 always 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 	// never take falling damage if completely underwater
    526 	if (ent->waterlevel == 3)
    527 		return;
    528 	if (ent->waterlevel == 2)
    529 		delta *= 0.25;
    530 	if (ent->waterlevel == 1)
    531 		delta *= 0.5;
    532 
    533 	if (delta < 1)
    534 		return;
    535 
    536 	if (delta < 15)
    537 	{
    538 		ent->s.event = EV_FOOTSTEP;
    539 		return;
    540 	}
    541 
    542 	ent->client->fall_value = delta*0.5;
    543 	if (ent->client->fall_value > 40)
    544 		ent->client->fall_value = 40;
    545 	ent->client->fall_time = level.time + FALL_TIME;
    546 
    547 	if (delta > 30)
    548 	{
    549 		if (ent->health > 0)
    550 		{
    551 			if (delta >= 55)
    552 				ent->s.event = EV_FALLFAR;
    553 			else
    554 				ent->s.event = EV_FALL;
    555 		}
    556 		ent->pain_debounce_time = level.time;	// no normal pain sound
    557 		damage = (delta-30)/2;
    558 		if (damage < 1)
    559 			damage = 1;
    560 		VectorSet (dir, 0, 0, 1);
    561 
    562 		if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING) )
    563 			T_Damage (ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING);
    564 	}
    565 	else
    566 	{
    567 		ent->s.event = EV_FALLSHORT;
    568 		return;
    569 	}
    570 }
    571 
    572 
    573 
    574 /*
    575 =============
    576 P_WorldEffects
    577 =============
    578 */
    579 void P_WorldEffects (void)
    580 {
    581 	qboolean	breather;
    582 	qboolean	envirosuit;
    583 	int			waterlevel, old_waterlevel;
    584 
    585 	if (current_player->movetype == MOVETYPE_NOCLIP)
    586 	{
    587 		current_player->air_finished = level.time + 12;	// don't need air
    588 		return;
    589 	}
    590 
    591 	waterlevel = current_player->waterlevel;
    592 	old_waterlevel = current_client->old_waterlevel;
    593 	current_client->old_waterlevel = waterlevel;
    594 
    595 	breather = current_client->breather_framenum > level.framenum;
    596 	envirosuit = current_client->enviro_framenum > level.framenum;
    597 
    598 	//
    599 	// if just entered a water volume, play a sound
    600 	//
    601 	if (!old_waterlevel && waterlevel)
    602 	{
    603 		PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
    604 		if (current_player->watertype & CONTENTS_LAVA)
    605 			gi.sound (current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0);
    606 		else if (current_player->watertype & CONTENTS_SLIME)
    607 			gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
    608 		else if (current_player->watertype & CONTENTS_WATER)
    609 			gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
    610 		current_player->flags |= FL_INWATER;
    611 
    612 		// clear damage_debounce, so the pain sound will play immediately
    613 		current_player->damage_debounce_time = level.time - 1;
    614 	}
    615 
    616 	//
    617 	// if just completely exited a water volume, play a sound
    618 	//
    619 	if (old_waterlevel && ! waterlevel)
    620 	{
    621 		PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
    622 		gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
    623 		current_player->flags &= ~FL_INWATER;
    624 	}
    625 
    626 	//
    627 	// check for head just going under water
    628 	//
    629 	if (old_waterlevel != 3 && waterlevel == 3)
    630 	{
    631 		gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0);
    632 	}
    633 
    634 	//
    635 	// check for head just coming out of water
    636 	//
    637 	if (old_waterlevel == 3 && waterlevel != 3)
    638 	{
    639 		if (current_player->air_finished < level.time)
    640 		{	// gasp for air
    641 			gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0);
    642 			PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
    643 		}
    644 		else  if (current_player->air_finished < level.time + 11)
    645 		{	// just break surface
    646 			gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0);
    647 		}
    648 	}
    649 
    650 	//
    651 	// check for drowning
    652 	//
    653 	if (waterlevel == 3)
    654 	{
    655 		// breather or envirosuit give air
    656 		if (breather || envirosuit)
    657 		{
    658 			current_player->air_finished = level.time + 10;
    659 
    660 			if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0)
    661 			{
    662 				if (!current_client->breather_sound)
    663 					gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0);
    664 				else
    665 					gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0);
    666 				current_client->breather_sound ^= 1;
    667 				PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF);
    668 				//FIXME: release a bubble?
    669 			}
    670 		}
    671 
    672 		// if out of air, start drowning
    673 		if (current_player->air_finished < level.time)
    674 		{	// drown!
    675 			if (current_player->client->next_drown_time < level.time 
    676 				&& current_player->health > 0)
    677 			{
    678 				current_player->client->next_drown_time = level.time + 1;
    679 
    680 				// take more damage the longer underwater
    681 				current_player->dmg += 2;
    682 				if (current_player->dmg > 15)
    683 					current_player->dmg = 15;
    684 
    685 				// play a gurp sound instead of a normal pain sound
    686 				if (current_player->health <= current_player->dmg)
    687 					gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0);
    688 				else if (rand()&1)
    689 					gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0);
    690 				else
    691 					gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0);
    692 
    693 				current_player->pain_debounce_time = level.time;
    694 
    695 				T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
    696 			}
    697 		}
    698 	}
    699 	else
    700 	{
    701 		current_player->air_finished = level.time + 12;
    702 		current_player->dmg = 2;
    703 	}
    704 
    705 	//
    706 	// check for sizzle damage
    707 	//
    708 	if (waterlevel && (current_player->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
    709 	{
    710 		if (current_player->watertype & CONTENTS_LAVA)
    711 		{
    712 			if (current_player->health > 0
    713 				&& current_player->pain_debounce_time <= level.time
    714 				&& current_client->invincible_framenum < level.framenum)
    715 			{
    716 				if (rand()&1)
    717 					gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0);
    718 				else
    719 					gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0);
    720 				current_player->pain_debounce_time = level.time + 1;
    721 			}
    722 
    723 			if (envirosuit)	// take 1/3 damage with envirosuit
    724 				T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_LAVA);
    725 			else
    726 				T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3*waterlevel, 0, 0, MOD_LAVA);
    727 		}
    728 
    729 		if (current_player->watertype & CONTENTS_SLIME)
    730 		{
    731 			if (!envirosuit)
    732 			{	// no damage from slime with envirosuit
    733 				T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_SLIME);
    734 			}
    735 		}
    736 	}
    737 }
    738 
    739 
    740 /*
    741 ===============
    742 G_SetClientEffects
    743 ===============
    744 */
    745 void G_SetClientEffects (edict_t *ent)
    746 {
    747 	int		pa_type;
    748 	int		remaining;
    749 
    750 	ent->s.effects = 0;
    751 	ent->s.renderfx = 0;
    752 
    753 	if (ent->health <= 0 || level.intermissiontime)
    754 		return;
    755 
    756 	if (ent->powerarmor_time > level.time)
    757 	{
    758 		pa_type = PowerArmorType (ent);
    759 		if (pa_type == POWER_ARMOR_SCREEN)
    760 		{
    761 			ent->s.effects |= EF_POWERSCREEN;
    762 		}
    763 		else if (pa_type == POWER_ARMOR_SHIELD)
    764 		{
    765 			ent->s.effects |= EF_COLOR_SHELL;
    766 			ent->s.renderfx |= RF_SHELL_GREEN;
    767 		}
    768 	}
    769 
    770 	if (ent->client->quad_framenum > level.framenum)
    771 	{
    772 		remaining = ent->client->quad_framenum - level.framenum;
    773 		if (remaining > 30 || (remaining & 4) )
    774 			ent->s.effects |= EF_QUAD;
    775 	}
    776 
    777 	if (ent->client->invincible_framenum > level.framenum)
    778 	{
    779 		remaining = ent->client->invincible_framenum - level.framenum;
    780 		if (remaining > 30 || (remaining & 4) )
    781 			ent->s.effects |= EF_PENT;
    782 	}
    783 
    784 	// show cheaters!!!
    785 	if (ent->flags & FL_GODMODE)
    786 	{
    787 		ent->s.effects |= EF_COLOR_SHELL;
    788 		ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE);
    789 	}
    790 }
    791 
    792 
    793 /*
    794 ===============
    795 G_SetClientEvent
    796 ===============
    797 */
    798 void G_SetClientEvent (edict_t *ent)
    799 {
    800 	if (ent->s.event)
    801 		return;
    802 
    803 	if ( ent->groundentity && xyspeed > 225)
    804 	{
    805 		if ( (int)(current_client->bobtime+bobmove) != bobcycle )
    806 			ent->s.event = EV_FOOTSTEP;
    807 	}
    808 }
    809 
    810 /*
    811 ===============
    812 G_SetClientSound
    813 ===============
    814 */
    815 void G_SetClientSound (edict_t *ent)
    816 {
    817 	char	*weap;
    818 
    819 	if (ent->client->pers.game_helpchanged != game.helpchanged)
    820 	{
    821 		ent->client->pers.game_helpchanged = game.helpchanged;
    822 		ent->client->pers.helpchanged = 1;
    823 	}
    824 
    825 	// help beep (no more than three times)
    826 	if (ent->client->pers.helpchanged && ent->client->pers.helpchanged <= 3 && !(level.framenum&63) )
    827 	{
    828 		ent->client->pers.helpchanged++;
    829 		gi.sound (ent, CHAN_VOICE, gi.soundindex ("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
    830 	}
    831 
    832 
    833 	if (ent->client->pers.weapon)
    834 		weap = ent->client->pers.weapon->classname;
    835 	else
    836 		weap = "";
    837 
    838 	if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
    839 		ent->s.sound = snd_fry;
    840 	else if (strcmp(weap, "weapon_railgun") == 0)
    841 		ent->s.sound = gi.soundindex("weapons/rg_hum.wav");
    842 	else if (strcmp(weap, "weapon_bfg") == 0)
    843 		ent->s.sound = gi.soundindex("weapons/bfg_hum.wav");
    844 	else if (ent->client->weapon_sound)
    845 		ent->s.sound = ent->client->weapon_sound;
    846 	else
    847 		ent->s.sound = 0;
    848 }
    849 
    850 /*
    851 ===============
    852 G_SetClientFrame
    853 ===============
    854 */
    855 void G_SetClientFrame (edict_t *ent)
    856 {
    857 	gclient_t	*client;
    858 	qboolean	duck, run;
    859 
    860 	if (ent->s.modelindex != 255)
    861 		return;		// not in the player model
    862 
    863 	client = ent->client;
    864 
    865 	if (client->ps.pmove.pm_flags & PMF_DUCKED)
    866 		duck = true;
    867 	else
    868 		duck = false;
    869 	if (xyspeed)
    870 		run = true;
    871 	else
    872 		run = false;
    873 
    874 	// check for stand/duck and stop/go transitions
    875 	if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH)
    876 		goto newanim;
    877 	if (run != client->anim_run && client->anim_priority == ANIM_BASIC)
    878 		goto newanim;
    879 	if (!ent->groundentity && client->anim_priority <= ANIM_WAVE)
    880 		goto newanim;
    881 
    882 	if(client->anim_priority == ANIM_REVERSE)
    883 	{
    884 		if(ent->s.frame > client->anim_end)
    885 		{
    886 			ent->s.frame--;
    887 			return;
    888 		}
    889 	}
    890 	else if (ent->s.frame < client->anim_end)
    891 	{	// continue an animation
    892 		ent->s.frame++;
    893 		return;
    894 	}
    895 
    896 	if (client->anim_priority == ANIM_DEATH)
    897 		return;		// stay there
    898 	if (client->anim_priority == ANIM_JUMP)
    899 	{
    900 		if (!ent->groundentity)
    901 			return;		// stay there
    902 		ent->client->anim_priority = ANIM_WAVE;
    903 		ent->s.frame = FRAME_jump3;
    904 		ent->client->anim_end = FRAME_jump6;
    905 		return;
    906 	}
    907 
    908 newanim:
    909 	// return to either a running or standing frame
    910 	client->anim_priority = ANIM_BASIC;
    911 	client->anim_duck = duck;
    912 	client->anim_run = run;
    913 
    914 	if (!ent->groundentity)
    915 	{
    916 		client->anim_priority = ANIM_JUMP;
    917 		if (ent->s.frame != FRAME_jump2)
    918 			ent->s.frame = FRAME_jump1;
    919 		client->anim_end = FRAME_jump2;
    920 	}
    921 	else if (run)
    922 	{	// running
    923 		if (duck)
    924 		{
    925 			ent->s.frame = FRAME_crwalk1;
    926 			client->anim_end = FRAME_crwalk6;
    927 		}
    928 		else
    929 		{
    930 			ent->s.frame = FRAME_run1;
    931 			client->anim_end = FRAME_run6;
    932 		}
    933 	}
    934 	else
    935 	{	// standing
    936 		if (duck)
    937 		{
    938 			ent->s.frame = FRAME_crstnd01;
    939 			client->anim_end = FRAME_crstnd19;
    940 		}
    941 		else
    942 		{
    943 			ent->s.frame = FRAME_stand01;
    944 			client->anim_end = FRAME_stand40;
    945 		}
    946 	}
    947 }
    948 
    949 
    950 /*
    951 =================
    952 ClientEndServerFrame
    953 
    954 Called for each player at the end of the server frame
    955 and right after spawning
    956 =================
    957 */
    958 void ClientEndServerFrame (edict_t *ent)
    959 {
    960 	float	bobtime;
    961 	int		i;
    962 
    963 	current_player = ent;
    964 	current_client = ent->client;
    965 
    966 	//
    967 	// If the origin or velocity have changed since ClientThink(),
    968 	// update the pmove values.  This will happen when the client
    969 	// is pushed by a bmodel or kicked by an explosion.
    970 	// 
    971 	// If it wasn't updated here, the view position would lag a frame
    972 	// behind the body position when pushed -- "sinking into plats"
    973 	//
    974 	for (i=0 ; i<3 ; i++)
    975 	{
    976 		current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0;
    977 		current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0;
    978 	}
    979 
    980 	//
    981 	// If the end of unit layout is displayed, don't give
    982 	// the player any normal movement attributes
    983 	//
    984 	if (level.intermissiontime)
    985 	{
    986 		// FIXME: add view drifting here?
    987 		current_client->ps.blend[3] = 0;
    988 		current_client->ps.fov = 90;
    989 		G_SetStats (ent);
    990 		return;
    991 	}
    992 
    993 	AngleVectors (ent->client->v_angle, forward, right, up);
    994 
    995 	// burn from lava, etc
    996 	P_WorldEffects ();
    997 
    998 	//
    999 	// set model angles from view angles so other things in
   1000 	// the world can tell which direction you are looking
   1001 	//
   1002 	if (ent->client->v_angle[PITCH] > 180)
   1003 		ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3;
   1004 	else
   1005 		ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3;
   1006 	ent->s.angles[YAW] = ent->client->v_angle[YAW];
   1007 	ent->s.angles[ROLL] = 0;
   1008 	ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4;
   1009 
   1010 	//
   1011 	// calculate speed and cycle to be used for
   1012 	// all cyclic walking effects
   1013 	//
   1014 	xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]);
   1015 
   1016 	if (xyspeed < 5)
   1017 	{
   1018 		bobmove = 0;
   1019 		current_client->bobtime = 0;	// start at beginning of cycle again
   1020 	}
   1021 	else if (ent->groundentity)
   1022 	{	// so bobbing only cycles when on ground
   1023 		if (xyspeed > 210)
   1024 			bobmove = 0.25;
   1025 		else if (xyspeed > 100)
   1026 			bobmove = 0.125;
   1027 		else
   1028 			bobmove = 0.0625;
   1029 	}
   1030 	
   1031 	bobtime = (current_client->bobtime += bobmove);
   1032 
   1033 	if (current_client->ps.pmove.pm_flags & PMF_DUCKED)
   1034 		bobtime *= 4;
   1035 
   1036 	bobcycle = (int)bobtime;
   1037 	bobfracsin = fabs(sin(bobtime*M_PI));
   1038 
   1039 	// detect hitting the floor
   1040 	P_FallingDamage (ent);
   1041 
   1042 	// apply all the damage taken this frame
   1043 	P_DamageFeedback (ent);
   1044 
   1045 	// determine the view offsets
   1046 	SV_CalcViewOffset (ent);
   1047 
   1048 	// determine the gun offsets
   1049 	SV_CalcGunOffset (ent);
   1050 
   1051 	// determine the full screen color blend
   1052 	// must be after viewoffset, so eye contents can be
   1053 	// accurately determined
   1054 	// FIXME: with client prediction, the contents
   1055 	// should be determined by the client
   1056 	SV_CalcBlend (ent);
   1057 
   1058 	// chase cam stuff
   1059 	if (ent->client->resp.spectator)
   1060 		G_SetSpectatorStats(ent);
   1061 	else
   1062 		G_SetStats (ent);
   1063 	G_CheckChaseStats(ent);
   1064 
   1065 	G_SetClientEvent (ent);
   1066 
   1067 	G_SetClientEffects (ent);
   1068 
   1069 	G_SetClientSound (ent);
   1070 
   1071 	G_SetClientFrame (ent);
   1072 
   1073 	VectorCopy (ent->velocity, ent->client->oldvelocity);
   1074 	VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles);
   1075 
   1076 	// clear weapon kicks
   1077 	VectorClear (ent->client->kick_origin);
   1078 	VectorClear (ent->client->kick_angles);
   1079 
   1080 	// if the scoreboard is up, update it
   1081 	if (ent->client->showscores && !(level.framenum & 31) )
   1082 	{
   1083 		DeathmatchScoreboardMessage (ent, ent->enemy);
   1084 		gi.unicast (ent, false);
   1085 	}
   1086 }
   1087