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;	// 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 */
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 //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 */
P_WorldEffects(void)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 */
G_SetClientEffects(edict_t * ent)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 	{
784 		remaining = ent->client->quad_framenum - level.framenum;
785 		if (remaining > 30 || (remaining & 4) )
786 //			ent->s.effects |= EF_QUAD;
787 			CTFSetPowerUpEffect(ent, EF_QUAD);
788 	}
789 
790 	if (ent->client->invincible_framenum > level.framenum)
791 	{
792 		remaining = ent->client->invincible_framenum - level.framenum;
793 		if (remaining > 30 || (remaining & 4) )
794 //			ent->s.effects |= EF_PENT;
795 			CTFSetPowerUpEffect(ent, EF_PENT);
796 	}
797 
798 	// show cheaters!!!
799 	if (ent->flags & FL_GODMODE)
800 	{
801 		ent->s.effects |= EF_COLOR_SHELL;
802 		ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE);
803 	}
804 }
805 
806 
807 /*
808 ===============
809 G_SetClientEvent
810 ===============
811 */
G_SetClientEvent(edict_t * ent)812 void G_SetClientEvent (edict_t *ent)
813 {
814 	if (ent->s.event)
815 		return;
816 
817 	if ( ent->groundentity && xyspeed > 225)
818 	{
819 		if ( (int)(current_client->bobtime+bobmove) != bobcycle )
820 			ent->s.event = EV_FOOTSTEP;
821 	}
822 }
823 
824 /*
825 ===============
826 G_SetClientSound
827 ===============
828 */
G_SetClientSound(edict_t * ent)829 void G_SetClientSound (edict_t *ent)
830 {
831 	char	*weap;
832 
833 	if (ent->client->resp.game_helpchanged != game.helpchanged)
834 	{
835 		ent->client->resp.game_helpchanged = game.helpchanged;
836 		ent->client->resp.helpchanged = 1;
837 	}
838 
839 	// help beep (no more than three times)
840 	if (ent->client->resp.helpchanged && ent->client->resp.helpchanged <= 3 && !(level.framenum&63) )
841 	{
842 		ent->client->resp.helpchanged++;
843 		gi.sound (ent, CHAN_VOICE, gi.soundindex ("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
844 	}
845 
846 
847 	if (ent->client->pers.weapon)
848 		weap = ent->client->pers.weapon->classname;
849 	else
850 		weap = "";
851 
852 	if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
853 		ent->s.sound = snd_fry;
854 	else if (strcmp(weap, "weapon_railgun") == 0)
855 		ent->s.sound = gi.soundindex("weapons/rg_hum.wav");
856 	else if (strcmp(weap, "weapon_bfg") == 0)
857 		ent->s.sound = gi.soundindex("weapons/bfg_hum.wav");
858 	else if (ent->client->weapon_sound)
859 		ent->s.sound = ent->client->weapon_sound;
860 	else
861 		ent->s.sound = 0;
862 }
863 
864 /*
865 ===============
866 G_SetClientFrame
867 ===============
868 */
G_SetClientFrame(edict_t * ent)869 void G_SetClientFrame (edict_t *ent)
870 {
871 	gclient_t	*client;
872 	qboolean	duck, run;
873 
874 	if (ent->s.modelindex != 255)
875 		return;		// not in the player model
876 
877 	client = ent->client;
878 
879 	if (client->ps.pmove.pm_flags & PMF_DUCKED)
880 		duck = true;
881 	else
882 		duck = false;
883 	if (xyspeed)
884 		run = true;
885 	else
886 		run = false;
887 
888 	// check for stand/duck and stop/go transitions
889 	if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH)
890 		goto newanim;
891 	if (run != client->anim_run && client->anim_priority == ANIM_BASIC)
892 		goto newanim;
893 	if (!ent->groundentity && client->anim_priority <= ANIM_WAVE)
894 		goto newanim;
895 
896 	if(client->anim_priority == ANIM_REVERSE)
897 	{
898 		if(ent->s.frame > client->anim_end)
899 		{
900 			ent->s.frame--;
901 			return;
902 		}
903 	}
904 	else if (ent->s.frame < client->anim_end)
905 	{	// continue an animation
906 		ent->s.frame++;
907 		return;
908 	}
909 
910 	if (client->anim_priority == ANIM_DEATH)
911 		return;		// stay there
912 	if (client->anim_priority == ANIM_JUMP)
913 	{
914 		if (!ent->groundentity)
915 			return;		// stay there
916 		ent->client->anim_priority = ANIM_WAVE;
917 		ent->s.frame = FRAME_jump3;
918 		ent->client->anim_end = FRAME_jump6;
919 		return;
920 	}
921 
922 newanim:
923 	// return to either a running or standing frame
924 	client->anim_priority = ANIM_BASIC;
925 	client->anim_duck = duck;
926 	client->anim_run = run;
927 
928 	if (!ent->groundentity)
929 	{
930 //ZOID: if on grapple, don't go into jump frame, go into standing
931 //frame
932 		if (client->ctf_grapple) {
933 			ent->s.frame = FRAME_stand01;
934 			client->anim_end = FRAME_stand40;
935 		} else {
936 //ZOID
937 		client->anim_priority = ANIM_JUMP;
938 		if (ent->s.frame != FRAME_jump2)
939 			ent->s.frame = FRAME_jump1;
940 		client->anim_end = FRAME_jump2;
941 	}
942 	}
943 	else if (run)
944 	{	// running
945 		if (duck)
946 		{
947 			ent->s.frame = FRAME_crwalk1;
948 			client->anim_end = FRAME_crwalk6;
949 		}
950 		else
951 		{
952 			ent->s.frame = FRAME_run1;
953 			client->anim_end = FRAME_run6;
954 		}
955 	}
956 	else
957 	{	// standing
958 		if (duck)
959 		{
960 			ent->s.frame = FRAME_crstnd01;
961 			client->anim_end = FRAME_crstnd19;
962 		}
963 		else
964 		{
965 			ent->s.frame = FRAME_stand01;
966 			client->anim_end = FRAME_stand40;
967 		}
968 	}
969 }
970 
971 
972 /*
973 =================
974 ClientEndServerFrame
975 
976 Called for each player at the end of the server frame
977 and right after spawning
978 =================
979 */
ClientEndServerFrame(edict_t * ent)980 void ClientEndServerFrame (edict_t *ent)
981 {
982 	float	bobtime;
983 	int		i;
984 
985 	current_player = ent;
986 	current_client = ent->client;
987 
988 	//
989 	// If the origin or velocity have changed since ClientThink(),
990 	// update the pmove values.  This will happen when the client
991 	// is pushed by a bmodel or kicked by an explosion.
992 	//
993 	// If it wasn't updated here, the view position would lag a frame
994 	// behind the body position when pushed -- "sinking into plats"
995 	//
996 	for (i=0 ; i<3 ; i++)
997 	{
998 		current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0;
999 		current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0;
1000 	}
1001 
1002 	//
1003 	// If the end of unit layout is displayed, don't give
1004 	// the player any normal movement attributes
1005 	//
1006 	if (level.intermissiontime)
1007 	{
1008 		// FIXME: add view drifting here?
1009 		current_client->ps.blend[3] = 0;
1010 		current_client->ps.fov = 90;
1011 		G_SetStats (ent);
1012 		return;
1013 	}
1014 
1015 	AngleVectors (ent->client->v_angle, forward, right, up);
1016 
1017 	// burn from lava, etc
1018 	P_WorldEffects ();
1019 
1020 	//
1021 	// set model angles from view angles so other things in
1022 	// the world can tell which direction you are looking
1023 	//
1024 	if (ent->client->v_angle[PITCH] > 180)
1025 		ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3;
1026 	else
1027 		ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3;
1028 	ent->s.angles[YAW] = ent->client->v_angle[YAW];
1029 	ent->s.angles[ROLL] = 0;
1030 	ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4;
1031 
1032 	//
1033 	// calculate speed and cycle to be used for
1034 	// all cyclic walking effects
1035 	//
1036 	xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]);
1037 
1038 	if (xyspeed < 5)
1039 	{
1040 		bobmove = 0;
1041 		current_client->bobtime = 0;	// start at beginning of cycle again
1042 	}
1043 	else if (ent->groundentity)
1044 	{	// so bobbing only cycles when on ground
1045 		if (xyspeed > 210)
1046 			bobmove = 0.25;
1047 		else if (xyspeed > 100)
1048 			bobmove = 0.125;
1049 		else
1050 			bobmove = 0.0625;
1051 	}
1052 
1053 	bobtime = (current_client->bobtime += bobmove);
1054 
1055 	if (current_client->ps.pmove.pm_flags & PMF_DUCKED)
1056 		bobtime *= 4;
1057 
1058 	bobcycle = (int)bobtime;
1059 	bobfracsin = fabs(sin(bobtime*M_PI));
1060 
1061 	// detect hitting the floor
1062 	P_FallingDamage (ent);
1063 
1064 	// apply all the damage taken this frame
1065 	P_DamageFeedback (ent);
1066 
1067 	// determine the view offsets
1068 	SV_CalcViewOffset (ent);
1069 
1070 	// determine the gun offsets
1071 	SV_CalcGunOffset (ent);
1072 
1073 	// determine the full screen color blend
1074 	// must be after viewoffset, so eye contents can be
1075 	// accurately determined
1076 	// FIXME: with client prediction, the contents
1077 	// should be determined by the client
1078 	SV_CalcBlend (ent);
1079 
1080 //ZOID
1081 	if (!ent->client->chase_target)
1082 //ZOID
1083 		G_SetStats (ent);
1084 
1085 //ZOID
1086 //update chasecam follower stats
1087 	for (i = 1; i <= maxclients->value; i++) {
1088 		edict_t *e = g_edicts + i;
1089 		if (!e->inuse || e->client->chase_target != ent)
1090 			continue;
1091 		memcpy(e->client->ps.stats,
1092 			ent->client->ps.stats,
1093 			sizeof(ent->client->ps.stats));
1094 		e->client->ps.stats[STAT_LAYOUTS] = 1;
1095 		break;
1096 	}
1097 //ZOID
1098 
1099 
1100 	G_SetClientEvent (ent);
1101 
1102 	G_SetClientEffects (ent);
1103 
1104 	G_SetClientSound (ent);
1105 
1106 	G_SetClientFrame (ent);
1107 
1108 	VectorCopy (ent->velocity, ent->client->oldvelocity);
1109 	VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles);
1110 
1111 	// clear weapon kicks
1112 	VectorClear (ent->client->kick_origin);
1113 	VectorClear (ent->client->kick_angles);
1114 
1115 	// if the scoreboard is up, update it
1116 	if (ent->client->showscores && !(level.framenum & 31) )
1117 	{
1118 //ZOID
1119 		if (ent->client->menu) {
1120 			PMenu_Do_Update(ent);
1121 			ent->client->menudirty = false;
1122 			ent->client->menutime = level.time;
1123 		} else
1124 //ZOID
1125 			DeathmatchScoreboardMessage (ent, ent->enemy);
1126 		gi.unicast (ent, false);
1127 	}
1128 }
1129 
1130