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 */
SV_CalcRoll(vec3_t angles,vec3_t velocity)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 */
P_DamageFeedback(edict_t * player)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 */
SV_CalcViewOffset(edict_t * ent)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 */
SV_CalcGunOffset(edict_t * ent)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 */
SV_AddBlend(float r,float g,float b,float a,float * v_blend)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 */
SV_CalcBlend(edict_t * ent)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 */
P_FallingDamage(edict_t * ent)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 */
P_WorldEffects(void)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 */
G_SetClientEffects(edict_t * ent)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 */
G_SetClientEvent(edict_t * ent)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 */
G_SetClientSound(edict_t * ent)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 */
G_SetClientFrame(edict_t * ent)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 */
ClientEndServerFrame(edict_t * ent)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 
1088