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