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