1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW SP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 
30 
31 // bg_pmove.c -- both games player movement code
32 // takes a playerstate and a usercmd as input and returns a modifed playerstate
33 
34 #include "../qcommon/q_shared.h"
35 #include "bg_public.h"
36 #include "bg_local.h"
37 
38 // Rafael gameskill
39 int bg_pmove_gameskill_integer;
40 // done
41 
42 // JPW NERVE -- added because I need to check single/multiplayer instances and branch accordingly
43 #ifdef CGAMEDLL
44 extern vmCvar_t cg_gameType;
45 #endif
46 #ifdef GAMEDLL
47 extern vmCvar_t g_gametype;
48 #endif
49 
50 // JPW NERVE -- stuck this here so it can be seen client & server side
Com_GetFlamethrowerRange(void)51 float Com_GetFlamethrowerRange( void ) {
52 #ifdef CGAMEDLL
53 	if ( cg_gameType.integer != GT_SINGLE_PLAYER ) {
54 		return 2500; // multiplayer range is longer for balance
55 	} else {
56 		return 1250; // single player range remains unchanged
57 	}
58 #endif
59 #ifdef GAMEDLL
60 	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
61 		return 2500;
62 	} else {
63 		return 1250;
64 	}
65 #endif
66 }
67 // jpw
68 
69 pmove_t     *pm;
70 pml_t pml;
71 
72 // movement parameters
73 float pm_stopspeed = 100;
74 //float	pm_duckScale = 0.25;
75 
76 //----(SA)	modified
77 float pm_waterSwimScale   = 0.50;
78 float pm_waterSprintSwimScale = 0.70;
79 float pm_waterWadeScale   = 0.70;
80 float pm_slagSwimScale    = 0.30;
81 float pm_slagWadeScale    = 0.70;
82 
83 float pm_accelerate       = 10;
84 float pm_airaccelerate    = 1;
85 float pm_wateraccelerate  = 4;
86 float pm_slagaccelerate   = 2;
87 float pm_flyaccelerate    = 8;
88 
89 float pm_friction         = 6;
90 float pm_waterfriction    = 1;
91 float pm_slagfriction     = 1;
92 float pm_flightfriction   = 3;
93 float pm_ladderfriction   = 14;
94 float pm_spectatorfriction = 5.0f;
95 
96 //----(SA)	end
97 
98 int c_pmove = 0;
99 
100 
101 /*
102 ===============
103 PM_AddEvent
104 
105 ===============
106 */
PM_AddEvent(int newEvent)107 void PM_AddEvent( int newEvent ) {
108 	BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
109 }
110 
111 /*
112 ===============
113 PM_AddTouchEnt
114 ===============
115 */
PM_AddTouchEnt(int entityNum)116 void PM_AddTouchEnt( int entityNum ) {
117 	int i;
118 
119 	if ( entityNum == ENTITYNUM_WORLD ) {
120 		return;
121 	}
122 	if ( pm->numtouch == MAXTOUCH ) {
123 		return;
124 	}
125 
126 	// see if it is already added
127 	for ( i = 0 ; i < pm->numtouch ; i++ ) {
128 		if ( pm->touchents[ i ] == entityNum ) {
129 			return;
130 		}
131 	}
132 
133 	// add it
134 	pm->touchents[pm->numtouch] = entityNum;
135 	pm->numtouch++;
136 }
137 
138 /*
139 ==============
140 PM_StartWeaponAnim
141 ==============
142 */
PM_StartWeaponAnim(int anim)143 static void PM_StartWeaponAnim( int anim ) {
144 	if ( pm->ps->pm_type >= PM_DEAD ) {
145 		return;
146 	}
147 
148 	if ( pm->ps->weapAnimTimer > 0 ) {
149 		return;
150 	}
151 
152 	if ( pm->cmd.weapon == WP_NONE ) {
153 		return;
154 	}
155 
156 	pm->ps->weapAnim = ( ( pm->ps->weapAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
157 }
158 
PM_ContinueWeaponAnim(int anim)159 static void PM_ContinueWeaponAnim( int anim ) {
160 	if ( pm->cmd.weapon == WP_NONE ) {
161 		return;
162 	}
163 
164 	if ( ( pm->ps->weapAnim & ~ANIM_TOGGLEBIT ) == anim ) {
165 		return;
166 	}
167 	if ( pm->ps->weapAnimTimer > 0 ) {
168 		return;     // a high priority animation is running
169 	}
170 	PM_StartWeaponAnim( anim );
171 }
172 
173 /*
174 ==================
175 PM_ClipVelocity
176 
177 Slide off of the impacting surface
178 ==================
179 */
PM_ClipVelocity(vec3_t in,vec3_t normal,vec3_t out,float overbounce)180 void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) {
181 	float backoff;
182 	float change;
183 	int i;
184 
185 	backoff = DotProduct( in, normal );
186 
187 	if ( backoff < 0 ) {
188 		backoff *= overbounce;
189 	} else {
190 		backoff /= overbounce;
191 	}
192 
193 	for ( i = 0 ; i < 3 ; i++ ) {
194 		change = normal[i] * backoff;
195 		out[i] = in[i] - change;
196 	}
197 }
198 
199 /*
200 ========================
201 PM_ExertSound
202 
203 plays random exertion sound when sprint key is press
204 ========================
205 */
PM_ExertSound(void)206 static void PM_ExertSound( void ) {
207 	int rval;
208 	static int oldexerttime = 0;
209 	static int oldexertcnt = 0;
210 
211 	if ( pm->cmd.serverTime > oldexerttime + 500 ) {
212 		oldexerttime = pm->cmd.serverTime;
213 	} else {
214 		return;
215 	}
216 
217 	rval = rand() % 3;
218 
219 	if ( oldexertcnt != rval ) {
220 		oldexertcnt = rval;
221 	} else {
222 		oldexertcnt++;
223 	}
224 
225 	if ( oldexertcnt > 2 ) {
226 		oldexertcnt = 0;
227 	}
228 
229 	if ( oldexertcnt == 1 ) {
230 		PM_AddEvent( EV_EXERT2 );
231 	} else if ( oldexertcnt == 2 ) {
232 		PM_AddEvent( EV_EXERT3 );
233 	} else {
234 		PM_AddEvent( EV_EXERT1 );
235 	}
236 }
237 
238 
239 /*
240 ==================
241 PM_Friction
242 
243 Handles both ground friction and water friction
244 ==================
245 */
PM_Friction(void)246 static void PM_Friction( void ) {
247 	vec3_t vec;
248 	float   *vel;
249 	float speed, newspeed, control;
250 	float drop;
251 
252 	vel = pm->ps->velocity;
253 
254 	VectorCopy( vel, vec );
255 	if ( pml.walking ) {
256 		vec[2] = 0; // ignore slope movement
257 	}
258 
259 	speed = VectorLength( vec );
260 	if ( speed < 1 ) {
261 		vel[0] = 0;
262 		vel[1] = 0;     // allow sinking underwater
263 		// FIXME: still have z friction underwater?
264 		return;
265 	}
266 
267 	drop = 0;
268 
269 	// apply ground friction
270 	if ( pm->waterlevel <= 1 ) {
271 		if ( pml.walking && !( pml.groundTrace.surfaceFlags & SURF_SLICK ) ) {
272 			// if getting knocked back, no friction
273 			if ( !( pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) ) {
274 				control = speed < pm_stopspeed ? pm_stopspeed : speed;
275 				drop += control * pm_friction * pml.frametime;
276 			}
277 		}
278 	}
279 
280 	// apply water friction even if just wading
281 	if ( pm->waterlevel ) {
282 		if ( pm->watertype & CONTENTS_SLIME ) { //----(SA)	slag
283 			drop += speed * pm_slagfriction * pm->waterlevel * pml.frametime;
284 		} else {
285 			drop += speed * pm_waterfriction * pm->waterlevel * pml.frametime;
286 		}
287 	}
288 
289 	// apply flying friction
290 	if ( pm->ps->powerups[PW_FLIGHT] ) {
291 		drop += speed * pm_flightfriction * pml.frametime;
292 	}
293 
294 	if ( pm->ps->pm_type == PM_SPECTATOR ) {
295 		drop += speed * pm_spectatorfriction * pml.frametime;
296 	}
297 
298 	// apply ladder strafe friction
299 	if ( pml.ladder ) {
300 		drop += speed * pm_ladderfriction * pml.frametime;
301 	}
302 
303 	// scale the velocity
304 	newspeed = speed - drop;
305 	if ( newspeed < 0 ) {
306 		newspeed = 0;
307 	}
308 	newspeed /= speed;
309 
310 	vel[0] = vel[0] * newspeed;
311 	vel[1] = vel[1] * newspeed;
312 	vel[2] = vel[2] * newspeed;
313 }
314 
315 
316 /*
317 ==============
318 PM_Accelerate
319 
320 Handles user intended acceleration
321 ==============
322 */
PM_Accelerate(vec3_t wishdir,float wishspeed,float accel)323 static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) {
324 #if 1
325 	// q2 style
326 	int i;
327 	float addspeed, accelspeed, currentspeed;
328 
329 	currentspeed = DotProduct( pm->ps->velocity, wishdir );
330 	addspeed = wishspeed - currentspeed;
331 	if ( addspeed <= 0 ) {
332 		return;
333 	}
334 	accelspeed = accel * pml.frametime * wishspeed;
335 	if ( accelspeed > addspeed ) {
336 		accelspeed = addspeed;
337 	}
338 
339 	// Ridah, variable friction for AI's
340 	if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) {
341 		accelspeed *= ( 1.0 / pm->ps->friction );
342 	}
343 	if ( accelspeed > addspeed ) {
344 		accelspeed = addspeed;
345 	}
346 
347 	for ( i = 0 ; i < 3 ; i++ ) {
348 		pm->ps->velocity[i] += accelspeed * wishdir[i];
349 	}
350 #else
351 	// proper way (avoids strafe jump maxspeed bug), but feels bad
352 	vec3_t wishVelocity;
353 	vec3_t pushDir;
354 	float pushLen;
355 	float canPush;
356 
357 	VectorScale( wishdir, wishspeed, wishVelocity );
358 	VectorSubtract( wishVelocity, pm->ps->velocity, pushDir );
359 	pushLen = VectorNormalize( pushDir );
360 
361 	canPush = accel * pml.frametime * wishspeed;
362 	if ( canPush > pushLen ) {
363 		canPush = pushLen;
364 	}
365 
366 	VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity );
367 #endif
368 }
369 
370 
371 
372 /*
373 ============
374 PM_CmdScale
375 
376 Returns the scale factor to apply to cmd movements
377 This allows the clients to use axial -127 to 127 values for all directions
378 without getting a sqrt(2) distortion in speed.
379 ============
380 */
PM_CmdScale(usercmd_t * cmd)381 static float PM_CmdScale( usercmd_t *cmd ) {
382 	int max;
383 	float total;
384 	float scale;
385 
386 	if ( pm->ps->aiChar && !( pm->ps->eFlags & EF_DUMMY_PMOVE ) ) {
387 		// restrict AI character movements (don't strafe or run backwards as fast as they can run forwards)
388 		if ( cmd->forwardmove < -64.0 ) {
389 			cmd->forwardmove = -64.0;
390 		}
391 		if ( cmd->rightmove > 64.0 ) {
392 			cmd->rightmove = 64.0;
393 		} else if ( cmd->rightmove < -64.0 ) {
394 			cmd->rightmove = -64.0;
395 		}
396 	}
397 
398 	max = abs( cmd->forwardmove );
399 	if ( abs( cmd->rightmove ) > max ) {
400 		max = abs( cmd->rightmove );
401 	}
402 	if ( abs( cmd->upmove ) > max ) {
403 		max = abs( cmd->upmove );
404 	}
405 	if ( !max ) {
406 		return 0;
407 	}
408 
409 	total = sqrt( cmd->forwardmove * cmd->forwardmove
410 				  + cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove );
411 	scale = (float)pm->ps->speed * max / ( 127.0 * total );
412 
413 	if ( pm->cmd.buttons & BUTTON_SPRINT && pm->ps->sprintTime > 50 ) {
414 		scale *= pm->ps->sprintSpeedScale;
415 	} else {
416 		scale *= pm->ps->runSpeedScale;
417 	}
418 
419 	if ( pm->ps->pm_type == PM_NOCLIP ) {
420 		scale *= 3;
421 	}
422 
423 // JPW NERVE -- half move speed if heavy weapon is carried
424 // this is the counterstrike way of doing it -- ie you can switch to a non-heavy weapon and move at
425 // full speed.  not completely realistic (well, sure, you can run faster with the weapon strapped to your
426 // back than in carry position) but more fun to play.  If it doesn't play well this way we'll bog down the
427 // player if the own the weapon at all.
428 //
429 // added #ifdef for game/cgame to project so we can get correct g_gametype variable and only do this in
430 // multiplayer if necessary
431 #ifdef CGAMEDLL
432 	if ( cg_gameType.integer != GT_SINGLE_PLAYER )
433 #endif
434 #ifdef GAMEDLL
435 	if ( g_gametype.integer != GT_SINGLE_PLAYER )
436 #endif
437 	{
438 		if ( ( pm->ps->weapon == WP_VENOM ) || ( pm->ps->weapon == WP_PANZERFAUST ) ) {
439 			scale *= 0.5;
440 		}
441 	}
442 // jpw
443 
444 	return scale;
445 }
446 
447 
448 /*
449 ================
450 PM_SetMovementDir
451 
452 Determine the rotation of the legs relative
453 to the facing dir
454 ================
455 */
PM_SetMovementDir(void)456 static void PM_SetMovementDir( void ) {
457 // Ridah, changed this for more realistic angles (at the cost of more network traffic?)
458 #if 1
459 	float speed;
460 	vec3_t moved;
461 	int moveyaw;
462 
463 	VectorSubtract( pm->ps->origin, pml.previous_origin, moved );
464 
465 	if (    ( pm->cmd.forwardmove || pm->cmd.rightmove )
466 			&&  ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
467 			&&  ( speed = VectorLength( moved ) )
468 			&&  ( speed > pml.frametime * 5 ) ) { // if moving slower than 20 units per second, just face head angles
469 		vec3_t dir;
470 
471 		VectorNormalize2( moved, dir );
472 		vectoangles( dir, dir );
473 
474 		moveyaw = (int)AngleDelta( dir[YAW], pm->ps->viewangles[YAW] );
475 
476 		if ( pm->cmd.forwardmove < 0 ) {
477 			moveyaw = (int)AngleNormalize180( moveyaw + 180 );
478 		}
479 
480 		if ( abs( moveyaw ) > 75 ) {
481 			if ( moveyaw > 0 ) {
482 				moveyaw = 75;
483 			} else
484 			{
485 				moveyaw = -75;
486 			}
487 		}
488 
489 		pm->ps->movementDir = (signed char)moveyaw;
490 	} else
491 	{
492 		pm->ps->movementDir = 0;
493 	}
494 #else
495 	if ( pm->cmd.forwardmove || pm->cmd.rightmove ) {
496 		if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) {
497 			pm->ps->movementDir = 0;
498 		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) {
499 			pm->ps->movementDir = 1;
500 		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) {
501 			pm->ps->movementDir = 2;
502 		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) {
503 			pm->ps->movementDir = 3;
504 		} else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) {
505 			pm->ps->movementDir = 4;
506 		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) {
507 			pm->ps->movementDir = 5;
508 		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) {
509 			pm->ps->movementDir = 6;
510 		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) {
511 			pm->ps->movementDir = 7;
512 		}
513 	} else {
514 		// if they aren't actively going directly sideways,
515 		// change the animation to the diagonal so they
516 		// don't stop too crooked
517 		if ( pm->ps->movementDir == 2 ) {
518 			pm->ps->movementDir = 1;
519 		} else if ( pm->ps->movementDir == 6 ) {
520 			pm->ps->movementDir = 7;
521 		}
522 	}
523 #endif
524 }
525 
526 
527 /*
528 =============
529 PM_CheckJump
530 =============
531 */
PM_CheckJump(void)532 static qboolean PM_CheckJump( void ) {
533 	// JPW NERVE -- jumping in multiplayer uses and requires sprint juice (to prevent turbo skating, sprint + jumps)
534 	// don't allow jump accel
535 //	if (pm->cmd.serverTime - pm->ps->jumpTime < 850)
536 	if ( pm->cmd.serverTime - pm->ps->jumpTime < 500 ) {  // (SA) trying shorter time.  I find this effect annoying ;)
537 		return qfalse;
538 	}
539 
540 	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
541 		return qfalse;      // don't allow jump until all buttons are up
542 	}
543 
544 	if ( pm->cmd.upmove < 10 ) {
545 		// not holding jump
546 		return qfalse;
547 	}
548 
549 	// must wait for jump to be released
550 	if ( pm->ps->pm_flags & PMF_JUMP_HELD ) {
551 		// clear upmove so cmdscale doesn't lower running speed
552 		pm->cmd.upmove = 0;
553 		return qfalse;
554 	}
555 
556 	pml.groundPlane = qfalse;       // jumping away
557 	pml.walking = qfalse;
558 	pm->ps->pm_flags |= PMF_JUMP_HELD;
559 
560 	pm->ps->groundEntityNum = ENTITYNUM_NONE;
561 	pm->ps->velocity[2] = JUMP_VELOCITY;
562 	PM_AddEvent( EV_JUMP );
563 
564 	if ( pm->cmd.forwardmove >= 0 ) {
565 		BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMP, qfalse, qtrue );
566 		pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
567 	} else {
568 		BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMPBK, qfalse, qtrue );
569 		pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
570 	}
571 
572 	return qtrue;
573 }
574 
575 /*
576 =============
577 PM_CheckWaterJump
578 =============
579 */
PM_CheckWaterJump(void)580 static qboolean PM_CheckWaterJump( void ) {
581 	vec3_t spot;
582 	int cont;
583 	vec3_t flatforward;
584 
585 	if ( pm->ps->pm_time ) {
586 		return qfalse;
587 	}
588 
589 	// check for water jump
590 	if ( pm->waterlevel != 2 ) {
591 		return qfalse;
592 	}
593 
594 	flatforward[0] = pml.forward[0];
595 	flatforward[1] = pml.forward[1];
596 	flatforward[2] = 0;
597 	VectorNormalize( flatforward );
598 
599 	VectorMA( pm->ps->origin, 30, flatforward, spot );
600 	spot[2] += 4;
601 	cont = pm->pointcontents( spot, pm->ps->clientNum );
602 	if ( !( cont & CONTENTS_SOLID ) ) {
603 		return qfalse;
604 	}
605 
606 	spot[2] += 16;
607 	cont = pm->pointcontents( spot, pm->ps->clientNum );
608 	if ( cont & (CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY) ) {
609 		return qfalse;
610 	}
611 
612 	// jump out of water
613 	VectorScale( pml.forward, 200, pm->ps->velocity );
614 	pm->ps->velocity[2] = 350;
615 
616 	pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
617 	pm->ps->pm_time = 2000;
618 
619 	return qtrue;
620 }
621 
622 //============================================================================
623 
624 
625 /*
626 ===================
627 PM_WaterJumpMove
628 
629 Flying out of the water
630 ===================
631 */
PM_WaterJumpMove(void)632 static void PM_WaterJumpMove( void ) {
633 	// waterjump has no control, but falls
634 
635 	PM_StepSlideMove( qtrue );
636 
637 	pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
638 	if ( pm->ps->velocity[2] < 0 ) {
639 		// cancel as soon as we are falling down again
640 		pm->ps->pm_flags &= ~PMF_ALL_TIMES;
641 		pm->ps->pm_time = 0;
642 	}
643 }
644 
645 /*
646 ===================
647 PM_WaterMove
648 
649 ===================
650 */
PM_WaterMove(void)651 static void PM_WaterMove( void ) {
652 	int i;
653 	vec3_t wishvel;
654 	float wishspeed;
655 	vec3_t wishdir;
656 	float scale;
657 	float vel;
658 
659 	if ( PM_CheckWaterJump() ) {
660 		PM_WaterJumpMove();
661 		return;
662 	}
663 #if 0
664 	// jump = head for surface
665 	if ( pm->cmd.upmove >= 10 ) {
666 		if ( pm->ps->velocity[2] > -300 ) {
667 			if ( pm->watertype & CONTENTS_WATER ) {
668 				pm->ps->velocity[2] = 100;
669 			} else if ( pm->watertype & CONTENTS_SLIME ) {
670 				pm->ps->velocity[2] = 80;
671 			} else {
672 				pm->ps->velocity[2] = 50;
673 			}
674 		}
675 	}
676 #endif
677 	PM_Friction();
678 
679 	scale = PM_CmdScale( &pm->cmd );
680 	//
681 	// user intentions
682 	//
683 	if ( !scale ) {
684 		wishvel[0] = 0;
685 		wishvel[1] = 0;
686 		wishvel[2] = -60;       // sink towards bottom
687 	} else {
688 		for ( i = 0 ; i < 3 ; i++ )
689 			wishvel[i] = scale * pml.forward[i] * pm->cmd.forwardmove + scale * pml.right[i] * pm->cmd.rightmove;
690 
691 		wishvel[2] += scale * pm->cmd.upmove;
692 	}
693 
694 	VectorCopy( wishvel, wishdir );
695 	wishspeed = VectorNormalize( wishdir );
696 
697 	if ( pm->watertype & CONTENTS_SLIME ) {    //----(SA)	slag
698 		if ( wishspeed > pm->ps->speed * pm_slagSwimScale ) {
699 			wishspeed = pm->ps->speed * pm_slagSwimScale;
700 		}
701 
702 		PM_Accelerate( wishdir, wishspeed, pm_slagaccelerate );
703 	} else {
704 		if ( wishspeed > pm->ps->speed * pm_waterSwimScale ) {
705 			wishspeed = pm->ps->speed * pm_waterSwimScale;
706 		}
707 
708 		PM_Accelerate( wishdir, wishspeed, pm_wateraccelerate );
709 	}
710 
711 
712 	// make sure we can go up slopes easily under water
713 	if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) {
714 		vel = VectorLength( pm->ps->velocity );
715 		// slide along the ground plane
716 		PM_ClipVelocity( pm->ps->velocity, pml.groundTrace.plane.normal,
717 						 pm->ps->velocity, OVERCLIP );
718 
719 		VectorNormalize( pm->ps->velocity );
720 		VectorScale( pm->ps->velocity, vel, pm->ps->velocity );
721 	}
722 
723 	PM_SlideMove( qfalse );
724 }
725 
726 
727 /*
728 ===================
729 PM_InvulnerabilityMove
730 
731 Only with the invulnerability powerup
732 ===================
733 */
734 // TTimo: unused
735 /*
736 static void PM_InvulnerabilityMove( void ) {
737 	pm->cmd.forwardmove = 0;
738 	pm->cmd.rightmove = 0;
739 	pm->cmd.upmove = 0;
740 	VectorClear(pm->ps->velocity);
741 }
742 */
743 
744 /*
745 ===================
746 PM_FlyMove
747 
748 Only with the flight powerup
749 ===================
750 */
PM_FlyMove(void)751 static void PM_FlyMove( void ) {
752 	int i;
753 	vec3_t wishvel;
754 	float wishspeed;
755 	vec3_t wishdir;
756 	float scale;
757 
758 	// normal slowdown
759 	PM_Friction();
760 
761 	if ( pm->ps->aiChar == AICHAR_NONE || pml.ladder ) {
762 		scale = PM_CmdScale( &pm->cmd );
763 	} else {
764 		// AI is allowed to fly freely
765 		scale = 1.0;
766 	}
767 	//
768 	// user intentions
769 	//
770 	if ( !scale ) {
771 		wishvel[0] = 0;
772 		wishvel[1] = 0;
773 		wishvel[2] = 0;
774 	} else {
775 		for ( i = 0 ; i < 3 ; i++ ) {
776 			wishvel[i] = scale * pml.forward[i] * pm->cmd.forwardmove + scale * pml.right[i] * pm->cmd.rightmove;
777 		}
778 
779 		wishvel[2] += scale * pm->cmd.upmove;
780 	}
781 
782 	VectorCopy( wishvel, wishdir );
783 	wishspeed = VectorNormalize( wishdir );
784 
785 	PM_Accelerate( wishdir, wishspeed, pm_flyaccelerate );
786 
787 	PM_StepSlideMove( qfalse );
788 }
789 
790 
791 /*
792 ===================
793 PM_AirMove
794 
795 ===================
796 */
PM_AirMove(void)797 static void PM_AirMove( void ) {
798 	int i;
799 	vec3_t wishvel;
800 	float fmove, smove;
801 	vec3_t wishdir;
802 	float wishspeed;
803 	float scale;
804 	usercmd_t cmd;
805 
806 	PM_Friction();
807 
808 	fmove = pm->cmd.forwardmove;
809 	smove = pm->cmd.rightmove;
810 
811 	cmd = pm->cmd;
812 	scale = PM_CmdScale( &cmd );
813 
814 // Ridah, moved this down, so we use the actual movement direction
815 	// set the movementDir so clients can rotate the legs for strafing
816 //	PM_SetMovementDir();
817 
818 	// project moves down to flat plane
819 	pml.forward[2] = 0;
820 	pml.right[2] = 0;
821 	VectorNormalize( pml.forward );
822 	VectorNormalize( pml.right );
823 
824 	for ( i = 0 ; i < 2 ; i++ ) {
825 		wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove;
826 	}
827 	wishvel[2] = 0;
828 
829 	VectorCopy( wishvel, wishdir );
830 	wishspeed = VectorNormalize( wishdir );
831 	wishspeed *= scale;
832 
833 	// not on ground, so little effect on velocity
834 	PM_Accelerate( wishdir, wishspeed, pm_airaccelerate );
835 
836 	// we may have a ground plane that is very steep, even
837 	// though we don't have a groundentity
838 	// slide along the steep plane
839 	if ( pml.groundPlane ) {
840 		PM_ClipVelocity( pm->ps->velocity, pml.groundTrace.plane.normal,
841 						 pm->ps->velocity, OVERCLIP );
842 	}
843 
844 #if 0
845 	//ZOID:  If we are on the grapple, try stair-stepping
846 	//this allows a player to use the grapple to pull himself
847 	//over a ledge
848 	if ( pm->ps->pm_flags & PMF_GRAPPLE_PULL ) {
849 		PM_StepSlideMove( qtrue );
850 	} else {
851 		PM_SlideMove( qtrue );
852 	}
853 #endif
854 
855 	PM_StepSlideMove( qtrue );
856 
857 // Ridah, moved this down, so we use the actual movement direction
858 	// set the movementDir so clients can rotate the legs for strafing
859 	PM_SetMovementDir();
860 }
861 
862 /*
863 ===================
864 PM_GrappleMove
865 
866 ===================
867 */
868 // TTimo: unused
869 /*
870 static void PM_GrappleMove( void ) {
871 // Ridah, removed this code since we don't have a grapple, and the grapplePoint was consuming valuable msg space
872 #if 0
873 	vec3_t vel, v;
874 	float vlen;
875 
876 	VectorScale(pml.forward, -16, v);
877 	VectorAdd(pm->ps->grapplePoint, v, v);
878 	VectorSubtract(v, pm->ps->origin, vel);
879 	vlen = VectorLength(vel);
880 	VectorNormalize( vel );
881 
882 	if (vlen <= 100)
883 		VectorScale(vel, 10 * vlen, vel);
884 	else
885 		VectorScale(vel, 800, vel);
886 
887 	VectorCopy(vel, pm->ps->velocity);
888 
889 	pml.groundPlane = qfalse;
890 #endif
891 }
892 */
893 
894 /*
895 ===================
896 PM_WalkMove
897 
898 ===================
899 */
PM_WalkMove(void)900 static void PM_WalkMove( void ) {
901 	int i, stamtake;            //----(SA)
902 	vec3_t wishvel, oldvel;
903 	float fmove, smove;
904 	vec3_t wishdir;
905 	float wishspeed;
906 	float scale;
907 	usercmd_t cmd;
908 	float accelerate;
909 	float vel;
910 
911 	if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
912 		// begin swimming
913 		PM_WaterMove();
914 		return;
915 	}
916 
917 
918 	if ( PM_CheckJump() ) {
919 		// jumped away
920 		if ( pm->waterlevel > 1 ) {
921 			PM_WaterMove();
922 		} else {
923 			PM_AirMove();
924 // JPW NERVE
925 #if defined ( CGAMEDLL )
926 			if ( cg_gameType.integer != GT_SINGLE_PLAYER )
927 #elif defined ( GAMEDLL )
928 			if ( g_gametype.integer != GT_SINGLE_PLAYER )
929 #endif
930 			{
931 				pm->ps->sprintTime -= 2500;
932 				if ( pm->ps->sprintTime < 0 ) {
933 					pm->ps->sprintTime = 0;
934 				}
935 			} else {
936 				pm->ps->jumpTime = pm->cmd.serverTime;
937 
938 				stamtake = 2000;    // amount to take for jump
939 
940 				// take time from powerup before taking it from sprintTime
941 				if ( pm->ps->powerups[PW_NOFATIGUE] ) {
942 					if ( pm->ps->powerups[PW_NOFATIGUE] > stamtake ) {
943 						pm->ps->powerups[PW_NOFATIGUE] -= stamtake;
944 						if ( pm->ps->powerups[PW_NOFATIGUE] < 0 ) {
945 							pm->ps->powerups[PW_NOFATIGUE] = 0;
946 						}
947 						stamtake = 0;
948 					} else {
949 						// don't have that much bonus.  clear what you've got and take the remainder from regular stamina
950 						stamtake -= pm->ps->powerups[PW_NOFATIGUE];
951 						pm->ps->powerups[PW_NOFATIGUE] = 0;
952 					}
953 				}
954 				if ( stamtake ) {
955 					pm->ps->sprintTime -= stamtake;
956 					if ( pm->ps->sprintTime < 0 ) {
957 						pm->ps->sprintTime = 0;
958 					}
959 				}
960 			}
961 // jpw
962 		}
963 		return;
964 	}
965 
966 	PM_Friction();
967 
968 	fmove = pm->cmd.forwardmove;
969 	smove = pm->cmd.rightmove;
970 
971 	cmd = pm->cmd;
972 	scale = PM_CmdScale( &cmd );
973 
974 // Ridah, moved this down, so we use the actual movement direction
975 	// set the movementDir so clients can rotate the legs for strafing
976 //	PM_SetMovementDir();
977 
978 	// project moves down to flat plane
979 	pml.forward[2] = 0;
980 	pml.right[2] = 0;
981 
982 	// project the forward and right directions onto the ground plane
983 	PM_ClipVelocity( pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP );
984 	PM_ClipVelocity( pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP );
985 	//
986 	VectorNormalize( pml.forward );
987 	VectorNormalize( pml.right );
988 
989 	for ( i = 0 ; i < 3 ; i++ ) {
990 		wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove;
991 	}
992 	// when going up or down slopes the wish velocity should Not be zero
993 //	wishvel[2] = 0;
994 
995 	VectorCopy( wishvel, wishdir );
996 	wishspeed = VectorNormalize( wishdir );
997 	wishspeed *= scale;
998 
999 	// clamp the speed lower if ducking
1000 	if ( pm->ps->pm_flags & PMF_DUCKED ) {
1001 		/*
1002 		if ( wishspeed > pm->ps->speed * pm_duckScale ) {
1003 			wishspeed = pm->ps->speed * pm_duckScale;
1004 		}
1005 		*/
1006 		if ( wishspeed > pm->ps->speed * pm->ps->crouchSpeedScale ) {
1007 			wishspeed = pm->ps->speed * pm->ps->crouchSpeedScale;
1008 		}
1009 	}
1010 
1011 	// clamp the speed lower if wading or walking on the bottom
1012 	if ( pm->waterlevel ) {
1013 		float waterScale;
1014 
1015 		waterScale = pm->waterlevel / 3.0;
1016 		if ( pm->watertype & CONTENTS_SLIME ) { //----(SA)	slag
1017 			waterScale = 1.0 - ( 1.0 - pm_slagSwimScale ) * waterScale;
1018 		} else {
1019 			waterScale = 1.0 - ( 1.0 - pm_waterSwimScale ) * waterScale;
1020 		}
1021 
1022 		if ( wishspeed > pm->ps->speed * waterScale ) {
1023 			wishspeed = pm->ps->speed * waterScale;
1024 		}
1025 	}
1026 
1027 	// when a player gets hit, they temporarily lose
1028 	// full control, which allows them to be moved a bit
1029 	if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
1030 		accelerate = pm_airaccelerate;
1031 	} else if ( ( pm->ps->stats[STAT_HEALTH] <= 0 ) && pm->ps->aiChar && ( pml.groundTrace.surfaceFlags & SURF_MONSTERSLICK ) )    {
1032 		accelerate = pm_airaccelerate;
1033 	} else {
1034 		accelerate = pm_accelerate;
1035 	}
1036 
1037 	PM_Accelerate( wishdir, wishspeed, accelerate );
1038 
1039 	//Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
1040 	//Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity));
1041 
1042 	if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
1043 		pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
1044 	} else if ( ( pm->ps->stats[STAT_HEALTH] <= 0 ) && pm->ps->aiChar && ( pml.groundTrace.surfaceFlags & SURF_MONSTERSLICK ) )   {
1045 		pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
1046 	} else {
1047 		// don't reset the z velocity for slopes
1048 //		pm->ps->velocity[2] = 0;
1049 	}
1050 
1051 	// show breath when standing on 'snow' surfaces
1052 	if ( pml.groundTrace.surfaceFlags & SURF_SNOW ) {
1053 		pm->ps->eFlags |= EF_BREATH;
1054 	} else {
1055 		pm->ps->eFlags &= ~EF_BREATH;
1056 	}
1057 
1058 	if ( pm->ps->eFlags & EF_CIG ) {
1059 		pm->ps->eFlags |= EF_BREATH;
1060 	}
1061 
1062 
1063 	vel = VectorLength( pm->ps->velocity );
1064 	VectorCopy( pm->ps->velocity, oldvel );
1065 
1066 	// slide along the ground plane
1067 	PM_ClipVelocity( pm->ps->velocity, pml.groundTrace.plane.normal,
1068 					 pm->ps->velocity, OVERCLIP );
1069 
1070 	// RF, only maintain speed if the direction is similar
1071 	if ( DotProduct( pm->ps->velocity, oldvel ) > 0 ) {
1072 		// don't decrease velocity when going up or down a slope
1073 		VectorNormalize( pm->ps->velocity );
1074 		VectorScale( pm->ps->velocity, vel, pm->ps->velocity );
1075 	}
1076 
1077 	// don't do anything if standing still
1078 	if ( !pm->ps->velocity[0] && !pm->ps->velocity[1] ) {
1079 		return;
1080 	}
1081 
1082 	PM_StepSlideMove( qfalse );
1083 
1084 // Ridah, moved this down, so we use the actual movement direction
1085 	// set the movementDir so clients can rotate the legs for strafing
1086 	PM_SetMovementDir();
1087 }
1088 
1089 
1090 /*
1091 ==============
1092 PM_DeadMove
1093 ==============
1094 */
PM_DeadMove(void)1095 static void PM_DeadMove( void ) {
1096 	float forward;
1097 
1098 	if ( !pml.walking ) {
1099 		return;
1100 	}
1101 
1102 	// extra friction
1103 
1104 	forward = VectorLength( pm->ps->velocity );
1105 	forward -= 20;
1106 	if ( forward <= 0 ) {
1107 		VectorClear( pm->ps->velocity );
1108 	} else {
1109 		VectorNormalize( pm->ps->velocity );
1110 		VectorScale( pm->ps->velocity, forward, pm->ps->velocity );
1111 	}
1112 }
1113 
1114 
1115 /*
1116 ===============
1117 PM_NoclipMove
1118 ===============
1119 */
PM_NoclipMove(void)1120 static void PM_NoclipMove( void ) {
1121 	float speed, drop, friction, control, newspeed;
1122 	int i;
1123 	vec3_t wishvel;
1124 	float fmove, smove;
1125 	vec3_t wishdir;
1126 	float wishspeed;
1127 	float scale;
1128 
1129 	pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
1130 
1131 	// friction
1132 
1133 	speed = VectorLength( pm->ps->velocity );
1134 	if ( speed < 1 ) {
1135 		VectorCopy( vec3_origin, pm->ps->velocity );
1136 	} else
1137 	{
1138 		drop = 0;
1139 
1140 		friction = pm_friction * 1.5; // extra friction
1141 		control = speed < pm_stopspeed ? pm_stopspeed : speed;
1142 		drop += control * friction * pml.frametime;
1143 
1144 		// scale the velocity
1145 		newspeed = speed - drop;
1146 		if ( newspeed < 0 ) {
1147 			newspeed = 0;
1148 		}
1149 		newspeed /= speed;
1150 
1151 		VectorScale( pm->ps->velocity, newspeed, pm->ps->velocity );
1152 	}
1153 
1154 	// accelerate
1155 	scale = PM_CmdScale( &pm->cmd );
1156 
1157 	fmove = pm->cmd.forwardmove;
1158 	smove = pm->cmd.rightmove;
1159 
1160 	for ( i = 0 ; i < 3 ; i++ )
1161 		wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove;
1162 	wishvel[2] += pm->cmd.upmove;
1163 
1164 	VectorCopy( wishvel, wishdir );
1165 	wishspeed = VectorNormalize( wishdir );
1166 	wishspeed *= scale;
1167 
1168 	PM_Accelerate( wishdir, wishspeed, pm_accelerate );
1169 
1170 	// move
1171 	VectorMA( pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin );
1172 }
1173 
1174 //============================================================================
1175 
1176 /*
1177 ================
1178 PM_FootstepForSurface
1179 
1180 Returns an event number apropriate for the groundsurface
1181 ================
1182 */
PM_FootstepForSurface(void)1183 static int PM_FootstepForSurface( void ) {
1184 
1185 	if ( pm->ps->aiChar == AICHAR_HEINRICH ) {
1186 		return EV_FOOTSTEP;
1187 	}
1188 
1189 	if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS ) {
1190 		return 0;
1191 	}
1192 	// JOSEPH 9-16-99
1193 	if ( pml.groundTrace.surfaceFlags & SURF_METAL ) {
1194 		return EV_FOOTSTEP_METAL;
1195 	}
1196 
1197 	if ( pml.groundTrace.surfaceFlags & SURF_WOOD ) {
1198 		return EV_FOOTSTEP_WOOD;
1199 	}
1200 
1201 	if ( pml.groundTrace.surfaceFlags & SURF_GRASS ) {
1202 		return EV_FOOTSTEP_GRASS;
1203 	}
1204 
1205 	if ( pml.groundTrace.surfaceFlags & SURF_GRAVEL ) {
1206 		return EV_FOOTSTEP_GRAVEL;
1207 	}
1208 	// END JOSEPH
1209 
1210 	if ( pml.groundTrace.surfaceFlags & SURF_ROOF ) {
1211 		return EV_FOOTSTEP_ROOF;
1212 	}
1213 
1214 	if ( pml.groundTrace.surfaceFlags & SURF_SNOW ) {
1215 		return EV_FOOTSTEP_SNOW;
1216 	}
1217 
1218 //----(SA)	added
1219 	if ( pml.groundTrace.surfaceFlags & SURF_CARPET ) {
1220 		return EV_FOOTSTEP_CARPET;
1221 	}
1222 //----(SA)	end
1223 	return EV_FOOTSTEP;
1224 }
1225 
1226 
1227 
1228 /*
1229 ==============
1230 PM_AddFallEvent
1231 ==============
1232 */
PM_AddFallEvent(int landing,int surfaceparms)1233 void PM_AddFallEvent( int landing, int surfaceparms ) {
1234 //	PM_AddEvent( landing );		// old way
1235 	BG_AddPredictableEventToPlayerstate( landing, surfaceparms, pm->ps );
1236 }
1237 
1238 /*
1239 =================
1240 PM_CrashLand
1241 
1242 Check for hard landings that generate sound events
1243 =================
1244 */
PM_CrashLand(void)1245 static void PM_CrashLand( void ) {
1246 	float delta;
1247 	float dist;
1248 	float vel, acc;
1249 	float t;
1250 	float a, b, c, den;
1251 
1252 	// Ridah, only play this if coming down hard
1253 	if ( !pm->ps->legsTimer ) {
1254 		if ( pml.previous_velocity[2] < -220 ) {
1255 			BG_AnimScriptEvent( pm->ps, ANIM_ET_LAND, qfalse, qtrue );
1256 		}
1257 	}
1258 
1259 	// calculate the exact velocity on landing
1260 	dist = pm->ps->origin[2] - pml.previous_origin[2];
1261 	vel = pml.previous_velocity[2];
1262 	acc = -pm->ps->gravity;
1263 
1264 	a = acc / 2;
1265 	b = vel;
1266 	c = -dist;
1267 
1268 	den =  b * b - 4 * a * c;
1269 	if ( den < 0 ) {
1270 		return;
1271 	}
1272 	t = ( -b - sqrt( den ) ) / ( 2 * a );
1273 
1274 	delta = vel + t * acc;
1275 	delta = delta * delta * 0.0001;
1276 
1277 	// never take falling damage if completely underwater
1278 	if ( pm->waterlevel == 3 ) {
1279 		return;
1280 	}
1281 
1282 	// reduce falling damage if there is standing water
1283 	if ( pm->waterlevel == 2 ) {
1284 		delta *= 0.25;
1285 	}
1286 	if ( pm->waterlevel == 1 ) {
1287 		delta *= 0.5;
1288 	}
1289 
1290 	if ( delta < 1 ) {
1291 		return;
1292 	}
1293 
1294 	// create a local entity event to play the sound
1295 
1296 	// SURF_NODAMAGE is used for bounce pads where you don't ever
1297 	// want to take damage or play a crunch sound
1298 	if ( !( pml.groundTrace.surfaceFlags & SURF_NODAMAGE ) ) {
1299 		if ( pm->debugLevel ) {
1300 			Com_Printf( "delta: %5.2f\n", delta );
1301 		}
1302 
1303 //----(SA)	removed per DM
1304 		// Rafael gameskill
1305 //		if (bg_pmove_gameskill_integer == 1)
1306 //		{
1307 //			if (delta > 7)
1308 //				delta = 8;
1309 //		}
1310 		// done
1311 //----(SA)	end
1312 
1313 		if ( delta > 77 ) {
1314 			PM_AddFallEvent( EV_FALL_NDIE, pml.groundTrace.surfaceFlags );
1315 		} else if ( delta > 67 ) {
1316 			PM_AddFallEvent( EV_FALL_DMG_50, pml.groundTrace.surfaceFlags );
1317 		} else if ( delta > 58 ) {
1318 			// this is a pain grunt, so don't play it if dead
1319 			if ( pm->ps->stats[STAT_HEALTH] > 0 ) {
1320 				PM_AddFallEvent( EV_FALL_DMG_25, pml.groundTrace.surfaceFlags );
1321 			}
1322 		} else if ( delta > 48 ) {
1323 			// this is a pain grunt, so don't play it if dead
1324 			if ( pm->ps->stats[STAT_HEALTH] > 0 ) {
1325 				PM_AddFallEvent( EV_FALL_DMG_15, pml.groundTrace.surfaceFlags );
1326 			}
1327 		} else if ( delta > 38.75 ) {
1328 			// this is a pain grunt, so don't play it if dead
1329 			if ( pm->ps->stats[STAT_HEALTH] > 0 ) {
1330 				PM_AddFallEvent( EV_FALL_DMG_10, pml.groundTrace.surfaceFlags );
1331 			}
1332 		} else if ( delta > 7 ) {
1333 			PM_AddFallEvent( EV_FALL_SHORT, pml.groundTrace.surfaceFlags );
1334 		} else {
1335 			if ( !( pm->ps->pm_flags & PMF_DUCKED ) && !( pm->cmd.buttons & BUTTON_WALKING ) ) {  // quiet if crouching or walking
1336 				PM_AddFallEvent( PM_FootstepForSurface(), pml.groundTrace.surfaceFlags );
1337 			}
1338 		}
1339 	}
1340 
1341 	// start footstep cycle over
1342 	pm->ps->bobCycle = 0;
1343 	pm->ps->footstepCount = 0;
1344 }
1345 
1346 
1347 
1348 /*
1349 =============
1350 PM_CorrectAllSolid
1351 =============
1352 */
PM_CorrectAllSolid(trace_t * trace)1353 static int PM_CorrectAllSolid( trace_t *trace ) {
1354 	int i, j, k;
1355 	vec3_t point;
1356 
1357 	if ( pm->debugLevel ) {
1358 		Com_Printf( "%i:allsolid\n", c_pmove );
1359 	}
1360 
1361 	// jitter around
1362 	for ( i = -1; i <= 1; i++ ) {
1363 		for ( j = -1; j <= 1; j++ ) {
1364 			for ( k = -1; k <= 1; k++ ) {
1365 				VectorCopy( pm->ps->origin, point );
1366 				point[0] += (float) i;
1367 				point[1] += (float) j;
1368 				point[2] += (float) k;
1369 				pm->trace( trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask );
1370 				if ( !trace->allsolid ) {
1371 					point[0] = pm->ps->origin[0];
1372 					point[1] = pm->ps->origin[1];
1373 					point[2] = pm->ps->origin[2] - 0.25;
1374 
1375 					pm->trace( trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask );
1376 					pml.groundTrace = *trace;
1377 					return qtrue;
1378 				}
1379 			}
1380 		}
1381 	}
1382 
1383 	pm->ps->groundEntityNum = ENTITYNUM_NONE;
1384 	pml.groundPlane = qfalse;
1385 	pml.walking = qfalse;
1386 
1387 	return qfalse;
1388 }
1389 
1390 
1391 /*
1392 =============
1393 PM_GroundTraceMissed
1394 
1395 The ground trace didn't hit a surface, so we are in freefall
1396 =============
1397 */
PM_GroundTraceMissed(void)1398 static void PM_GroundTraceMissed( void ) {
1399 	trace_t trace;
1400 	vec3_t point;
1401 
1402 #define AI_STEPTEST_FALLDIST_PER_SEC    ( STEPSIZE + pm->ps->gravity * 0.35 )   // always allow them to fall down the minimum stepsize
1403 
1404 	if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) {
1405 		// we just transitioned into freefall
1406 		if ( pm->debugLevel ) {
1407 			Com_Printf( "%i:lift\n", c_pmove );
1408 		}
1409 
1410 		// if they aren't in a jumping animation and the ground is a ways away, force into it
1411 		// if we didn't do the trace, the player would be backflipping down staircases
1412 		VectorCopy( pm->ps->origin, point );
1413 		point[2] -= 64;
1414 
1415 		pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask );
1416 		//
1417 		// RF, try and keep AI's on the ground if walking down steps
1418 		if ( pm->ps->aiChar ) {
1419 			if ( trace.fraction < 1.0 ) {
1420 				float falldist, xyspeed;
1421 				vec3_t vel;
1422 				falldist = Distance( pm->ps->origin, trace.endpos );
1423 				VectorCopy( pm->ps->velocity, vel );
1424 				vel[2] = 0;
1425 				xyspeed = VectorLength( vel );
1426 				if ( xyspeed > 120 && falldist * pml.frametime < ( xyspeed * pml.frametime + STEPSIZE ) ) {
1427 					// put them on the ground
1428 					VectorCopy( trace.endpos, pm->ps->origin );
1429 					return;
1430 				}
1431 			}
1432 		}
1433 		//
1434 		if ( trace.fraction == 1.0 && !( pm->ps->pm_flags & PMF_LADDER ) ) {
1435 			if ( pm->cmd.forwardmove >= 0 ) {
1436 				BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMP, qfalse, qtrue );
1437 				pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1438 			} else {
1439 				BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMPBK, qfalse, qtrue );
1440 				pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
1441 			}
1442 		}
1443 	}
1444 
1445 	pm->ps->groundEntityNum = ENTITYNUM_NONE;
1446 	pml.groundPlane = qfalse;
1447 	pml.walking = qfalse;
1448 }
1449 
1450 
1451 /*
1452 =============
1453 PM_GroundTrace
1454 =============
1455 */
PM_GroundTrace(void)1456 static void PM_GroundTrace( void ) {
1457 	vec3_t point;
1458 	trace_t trace;
1459 
1460 	point[0] = pm->ps->origin[0];
1461 	point[1] = pm->ps->origin[1];
1462 	point[2] = pm->ps->origin[2] - 0.25;
1463 
1464 	pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask );
1465 	pml.groundTrace = trace;
1466 
1467 	// do something corrective if the trace starts in a solid...
1468 	if ( trace.allsolid ) {
1469 		if ( !PM_CorrectAllSolid( &trace ) ) {
1470 			return;
1471 		}
1472 	}
1473 
1474 	// if the trace didn't hit anything, we are in free fall
1475 	if ( trace.fraction == 1.0 ) {
1476 		PM_GroundTraceMissed();
1477 		pml.groundPlane = qfalse;
1478 		pml.walking = qfalse;
1479 		return;
1480 	}
1481 
1482 	// check if getting thrown off the ground
1483 	if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) {
1484 		if ( pm->debugLevel ) {
1485 			Com_Printf( "%i:kickoff\n", c_pmove );
1486 		}
1487 		if ( !( pm->ps->pm_flags & PMF_LADDER ) ) {
1488 			// go into jump animation
1489 			if ( pm->cmd.forwardmove >= 0 ) {
1490 				BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMP, qfalse, qfalse );
1491 				pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1492 			} else {
1493 				BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMPBK, qfalse, qfalse );
1494 				pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
1495 			}
1496 		}
1497 
1498 		pm->ps->groundEntityNum = ENTITYNUM_NONE;
1499 		pml.groundPlane = qfalse;
1500 		pml.walking = qfalse;
1501 		return;
1502 	}
1503 
1504 	// slopes that are too steep will not be considered onground
1505 	if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) {
1506 		if ( pm->debugLevel ) {
1507 			Com_Printf( "%i:steep\n", c_pmove );
1508 		}
1509 		// FIXME: if they can't slide down the slope, let them
1510 		// walk (sharp crevices)
1511 		pm->ps->groundEntityNum = ENTITYNUM_NONE;
1512 		pml.groundPlane = qtrue;
1513 		pml.walking = qfalse;
1514 		return;
1515 	}
1516 
1517 	pml.groundPlane = qtrue;
1518 	pml.walking = qtrue;
1519 
1520 	// hitting solid ground will end a waterjump
1521 	if ( pm->ps->pm_flags & PMF_TIME_WATERJUMP ) {
1522 		pm->ps->pm_flags &= ~( PMF_TIME_WATERJUMP | PMF_TIME_LAND );
1523 		pm->ps->pm_time = 0;
1524 	}
1525 
1526 	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
1527 		// just hit the ground
1528 		if ( pm->debugLevel ) {
1529 			Com_Printf( "%i:Land\n", c_pmove );
1530 		}
1531 
1532 		PM_CrashLand();
1533 
1534 		// don't do landing time if we were just going down a slope
1535 		if ( pml.previous_velocity[2] < -200 ) {
1536 			// don't allow another jump for a little while
1537 			pm->ps->pm_flags |= PMF_TIME_LAND;
1538 			pm->ps->pm_time = 250;
1539 		}
1540 	}
1541 
1542 	pm->ps->groundEntityNum = trace.entityNum;
1543 
1544 	// don't reset the z velocity for slopes
1545 //	pm->ps->velocity[2] = 0;
1546 
1547 	PM_AddTouchEnt( trace.entityNum );
1548 }
1549 
1550 
1551 /*
1552 =============
1553 PM_SetWaterLevel	FIXME: avoid this twice?  certainly if not moving
1554 =============
1555 */
PM_SetWaterLevel(void)1556 static void PM_SetWaterLevel( void ) {
1557 	vec3_t point;
1558 	int cont;
1559 	int sample1;
1560 	int sample2;
1561 
1562 	//
1563 	// get waterlevel, accounting for ducking
1564 	//
1565 	pm->waterlevel = 0;
1566 	pm->watertype = 0;
1567 
1568 	// Ridah, modified this
1569 	point[0] = pm->ps->origin[0];
1570 	point[1] = pm->ps->origin[1];
1571 	point[2] = pm->ps->origin[2] + pm->ps->mins[2] + 1;
1572 	cont = pm->pointcontents( point, pm->ps->clientNum );
1573 
1574 	if ( cont & MASK_WATER ) {
1575 		sample2 = pm->ps->viewheight - pm->ps->mins[2];
1576 		sample1 = sample2 / 2;
1577 
1578 		pm->watertype = cont;
1579 		pm->waterlevel = 1;
1580 		point[2] = pm->ps->origin[2] + pm->ps->mins[2] + sample1;
1581 		cont = pm->pointcontents( point, pm->ps->clientNum );
1582 		if ( cont & MASK_WATER ) {
1583 			pm->waterlevel = 2;
1584 			point[2] = pm->ps->origin[2] + pm->ps->mins[2] + sample2;
1585 			cont = pm->pointcontents( point, pm->ps->clientNum );
1586 			if ( cont & MASK_WATER ) {
1587 				pm->waterlevel = 3;
1588 			}
1589 		}
1590 	}
1591 	// done.
1592 
1593 	// UNDERWATER
1594 	BG_UpdateConditionValue( pm->ps->clientNum, ANIM_COND_UNDERWATER, ( pm->waterlevel > 1 ), qtrue );
1595 
1596 }
1597 
1598 
1599 
1600 /*
1601 ==============
1602 PM_CheckDuck
1603 
1604 Sets mins, maxs, and pm->ps->viewheight
1605 ==============
1606 */
PM_CheckDuck(void)1607 static void PM_CheckDuck( void ) {
1608 	trace_t trace;
1609 
1610 	// Ridah, modified this for configurable bounding boxes
1611 	pm->mins[0] = pm->ps->mins[0];
1612 	pm->mins[1] = pm->ps->mins[1];
1613 
1614 	pm->maxs[0] = pm->ps->maxs[0];
1615 	pm->maxs[1] = pm->ps->maxs[1];
1616 
1617 	pm->mins[2] = pm->ps->mins[2];
1618 
1619 	if ( pm->ps->pm_type == PM_DEAD ) {
1620 		pm->maxs[2] = pm->ps->maxs[2];          // NOTE: must set death bounding box in game code
1621 		pm->ps->viewheight = pm->ps->deadViewHeight;
1622 		return;
1623 	}
1624 
1625 	// RF, disable crouching while using MG42
1626 	if ( pm->ps->eFlags & EF_MG42_ACTIVE ) {
1627 		pm->maxs[2] = pm->ps->maxs[2];
1628 		pm->ps->viewheight = pm->ps->standViewHeight;
1629 		return;
1630 	}
1631 
1632 	if ( pm->cmd.upmove < 0 ) { // duck
1633 		pm->ps->pm_flags |= PMF_DUCKED;
1634 	} else
1635 	{   // stand up if possible
1636 		if ( pm->ps->pm_flags & PMF_DUCKED ) {
1637 			// try to stand up
1638 			pm->maxs[2] = pm->ps->maxs[2];
1639 			pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
1640 			if ( !trace.allsolid ) {
1641 				pm->ps->pm_flags &= ~PMF_DUCKED;
1642 			}
1643 		}
1644 	}
1645 
1646 	if ( pm->ps->pm_flags & PMF_DUCKED ) {
1647 		pm->maxs[2] = pm->ps->crouchMaxZ;
1648 		pm->ps->viewheight = pm->ps->crouchViewHeight;
1649 	} else
1650 	{
1651 		pm->maxs[2] = pm->ps->maxs[2];
1652 		pm->ps->viewheight = pm->ps->standViewHeight;
1653 	}
1654 	// done.
1655 }
1656 
1657 
1658 
1659 //===================================================================
1660 
1661 
1662 /*
1663 ===============
1664 PM_Footsteps
1665 ===============
1666 */
PM_Footsteps(void)1667 static void PM_Footsteps( void ) {
1668 	float bobmove, animGap;
1669 	int old;
1670 	qboolean footstep;
1671 	qboolean iswalking;
1672 	int animResult = -1;
1673 
1674 	if ( pm->ps->eFlags & EF_DEAD ) {
1675 		return;
1676 	}
1677 
1678 	iswalking = qfalse;
1679 
1680 	//
1681 	// calculate speed and cycle to be used for
1682 	// all cyclic walking effects
1683 	//
1684 	pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0]
1685 						+  pm->ps->velocity[1] * pm->ps->velocity[1] );
1686 
1687 	// mg42, always idle
1688 	if ( pm->ps->persistant[PERS_HWEAPON_USE] ) {
1689 		BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_IDLE, qtrue );
1690 		//
1691 		return;
1692 	}
1693 
1694 	// swimming
1695 	if ( pm->waterlevel > 1 ) {
1696 
1697 		if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
1698 			BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_SWIMBK, qtrue );
1699 		} else {
1700 			BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_SWIM, qtrue );
1701 		}
1702 
1703 		return;
1704 	}
1705 
1706 	// in the air
1707 	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
1708 		if ( pm->ps->pm_flags & PMF_LADDER ) {             // on ladder
1709 			if ( pm->ps->velocity[2] >= 0 ) {
1710 				BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_CLIMBUP, qtrue );
1711 				//BG_PlayAnimName( pm->ps, "BOTH_CLIMB", ANIM_BP_BOTH, qfalse, qtrue, qfalse );
1712 			} else if ( pm->ps->velocity[2] < 0 )     {
1713 				BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_CLIMBDOWN, qtrue );
1714 				//BG_PlayAnimName( pm->ps, "BOTH_CLIMB_DOWN", ANIM_BP_BOTH, qfalse, qtrue, qfalse );
1715 			}
1716 		}
1717 
1718 		return;
1719 	}
1720 
1721 	// if not trying to move
1722 	if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) {
1723 		if (  pm->xyspeed < 5 ) {
1724 			pm->ps->bobCycle = 0;   // start at beginning of cycle again
1725 			pm->ps->footstepCount = 0;
1726 		}
1727 		if ( pm->xyspeed > 120 ) {
1728 			return; // continue what they were doing last frame, until we stop
1729 		}
1730 		if ( pm->ps->pm_flags & PMF_DUCKED ) {
1731 			animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_IDLECR, qtrue );
1732 		}
1733 		if ( animResult < 0 ) {
1734 			BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_IDLE, qtrue );
1735 		}
1736 		//
1737 		return;
1738 	}
1739 
1740 
1741 	footstep = qfalse;
1742 
1743 	if ( pm->ps->pm_flags & PMF_DUCKED ) {
1744 		bobmove = 0.5;  // ducked characters bob much faster
1745 		if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
1746 			animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_WALKCRBK, qtrue );
1747 		} else {
1748 			animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_WALKCR, qtrue );
1749 		}
1750 		// ducked characters never play footsteps
1751 	} else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
1752 		if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
1753 			bobmove = 0.4;  // faster speeds bob faster
1754 			footstep = qtrue;
1755 			// check for strafing
1756 			if ( pm->cmd.rightmove && !pm->cmd.forwardmove ) {
1757 				if ( pm->cmd.rightmove > 0 ) {
1758 					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFERIGHT, qtrue );
1759 				} else {
1760 					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFELEFT, qtrue );
1761 				}
1762 			}
1763 			if ( animResult < 0 ) {   // if we havent found an anim yet, play the run
1764 				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_RUNBK, qtrue );
1765 			}
1766 			if ( animResult < 0 ) {   // if we havent found an anim yet, play the run
1767 				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_WALKBK, qtrue );
1768 			}
1769 		} else {
1770 			bobmove = 0.3;
1771 			// check for strafing
1772 			if ( pm->cmd.rightmove && !pm->cmd.forwardmove ) {
1773 				if ( pm->cmd.rightmove > 0 ) {
1774 					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFERIGHT, qtrue );
1775 				} else {
1776 					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFELEFT, qtrue );
1777 				}
1778 			}
1779 			if ( animResult < 0 ) {   // if we havent found an anim yet, play the run
1780 				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_WALKBK, qtrue );
1781 			}
1782 			if ( animResult < 0 ) {   // if we havent found an anim yet, play the run
1783 				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_RUNBK, qtrue );
1784 			}
1785 		}
1786 
1787 	} else {
1788 
1789 		if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
1790 			bobmove = 0.4;  // faster speeds bob faster
1791 			footstep = qtrue;
1792 			// check for strafing
1793 			if ( pm->cmd.rightmove && !pm->cmd.forwardmove ) {
1794 				if ( pm->cmd.rightmove > 0 ) {
1795 					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFERIGHT, qtrue );
1796 				} else {
1797 					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFELEFT, qtrue );
1798 				}
1799 			}
1800 			if ( animResult < 0 ) {   // if we havent found an anim yet, play the run
1801 				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_RUN, qtrue );
1802 			}
1803 			if ( animResult < 0 ) {   // if we havent found an anim yet, play the run
1804 				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_WALK, qtrue );
1805 			}
1806 		} else {
1807 			bobmove = 0.3;  // walking bobs slow
1808 			if ( pm->ps->aiChar != AICHAR_NONE ) {
1809 				footstep = qtrue;
1810 				iswalking = qtrue;
1811 			} else {
1812 				footstep = qfalse;  // walking is quiet for the player
1813 			}
1814 			if ( pm->cmd.rightmove && !pm->cmd.forwardmove ) {
1815 				if ( pm->cmd.rightmove > 0 ) {
1816 					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFERIGHT, qtrue );
1817 				} else {
1818 					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFELEFT, qtrue );
1819 				}
1820 			}
1821 			if ( animResult < 0 ) {   // if we havent found an anim yet, play the run
1822 				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_WALK, qtrue );
1823 			}
1824 			if ( animResult < 0 ) {   // if we havent found an anim yet, play the run
1825 				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_RUN, qtrue );
1826 			}
1827 		}
1828 	}
1829 
1830 	// if no anim found yet, then just use the idle as default
1831 	if ( animResult < 0 ) {
1832 		BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_IDLE, qtrue );
1833 	}
1834 
1835 	// check for footstep / splash sounds
1836 	animGap = BG_AnimGetFootstepGap( pm->ps, pm->xyspeed );
1837 
1838 	// new method
1839 	if ( animGap > 0 ) {
1840 
1841 		// do the bobCycle for weapon bobbing
1842 		pm->ps->bobCycle = (int)( pm->ps->bobCycle + bobmove * pml.msec ) & 255;
1843 
1844 		// now footsteps
1845 		pm->ps->footstepCount += pm->xyspeed * pml.frametime;
1846 
1847 		if ( pm->ps->footstepCount > animGap ) {
1848 
1849 			pm->ps->footstepCount = pm->ps->footstepCount - animGap;
1850 
1851 			if ( !iswalking && pm->ps->sprintExertTime && pm->waterlevel <= 2 ) {
1852 				PM_ExertSound();
1853 			}
1854 
1855 			if ( pm->waterlevel == 0 ) {
1856 				if ( footstep && !pm->noFootsteps ) {
1857 					if ( pm->ps->aiChar == AICHAR_HEINRICH ) {
1858 						PM_AddEvent( EV_FOOTSTEP );
1859 					} else {
1860 						PM_AddEvent( PM_FootstepForSurface() );
1861 					}
1862 				}
1863 			} else if ( pm->waterlevel == 1 ) {
1864 				// splashing
1865 				PM_AddEvent( EV_FOOTSPLASH );
1866 			} else if ( pm->waterlevel == 2 ) {
1867 				// wading / swimming at surface
1868 				PM_AddEvent( EV_SWIM );
1869 			} else if ( pm->waterlevel == 3 ) {
1870 				// no sound when completely underwater
1871 			}
1872 
1873 		}
1874 
1875 	} else {    // default back to old method
1876 
1877 		old = pm->ps->bobCycle;
1878 
1879 		if ( pm->ps->aiChar == AICHAR_SUPERSOLDIER || pm->ps->aiChar == AICHAR_PROTOSOLDIER ) {
1880 			//iswalking = qfalse;
1881 			bobmove = 0.4 * 0.75f;  // slow down footsteps for big guys
1882 		}
1883 
1884 		if ( pm->ps->aiChar == AICHAR_HEINRICH ) {
1885 			iswalking = qfalse;
1886 			bobmove = 0.4 * 1.3f;
1887 		}
1888 
1889 		if ( pm->ps->aiChar == AICHAR_HELGA ) {
1890 			iswalking = qfalse;
1891 			bobmove = 0.4 * 1.5f;
1892 		}
1893 
1894 		pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
1895 
1896 		// if we just crossed a cycle boundary, play an apropriate footstep event
1897 		if ( iswalking /*|| (pm->ps->aiChar == AICHAR_HEINRICH)*/ ) {
1898 			// sounds much more natural this way
1899 			if ( old > pm->ps->bobCycle ) {
1900 
1901 				if ( pm->waterlevel == 0 ) {
1902 					if ( footstep && !pm->noFootsteps ) {
1903 						if ( pm->ps->aiChar == AICHAR_HEINRICH ) {
1904 							PM_AddEvent( EV_FOOTSTEP );
1905 						} else {
1906 							PM_AddEvent( PM_FootstepForSurface() );
1907 						}
1908 					}
1909 				} else if ( pm->waterlevel == 1 ) {
1910 					// splashing
1911 					PM_AddEvent( EV_FOOTSPLASH );
1912 				} else if ( pm->waterlevel == 2 ) {
1913 					// wading / swimming at surface
1914 					PM_AddEvent( EV_SWIM );
1915 				} else if ( pm->waterlevel == 3 ) {
1916 					// no sound when completely underwater
1917 				}
1918 
1919 			}
1920 		} else if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 )   {
1921 
1922 			if ( pm->ps->sprintExertTime && pm->waterlevel <= 2 ) {
1923 				PM_ExertSound();
1924 			}
1925 
1926 			if ( pm->waterlevel == 0 ) {
1927 				// on ground will only play sounds if running
1928 				if ( footstep && !pm->noFootsteps ) {
1929 					//				if (pm->ps->aiChar == AICHAR_HEINRICH) {	// <-- this stuff now handled in PM_footstepforsurf
1930 					//					PM_AddEvent( EV_FOOTSTEP );
1931 					//				} else {
1932 					PM_AddEvent( PM_FootstepForSurface() );
1933 					//				}
1934 				}
1935 			} else if ( pm->waterlevel == 1 ) {
1936 				// splashing
1937 				PM_AddEvent( EV_FOOTSPLASH );
1938 			} else if ( pm->waterlevel == 2 ) {
1939 				// wading / swimming at surface
1940 				PM_AddEvent( EV_SWIM );
1941 			} else if ( pm->waterlevel == 3 ) {
1942 				// no sound when completely underwater
1943 
1944 			}
1945 		}
1946 
1947 	}
1948 
1949 }
1950 
1951 /*
1952 ==============
1953 PM_WaterEvents
1954 
1955 Generate sound events for entering and leaving water
1956 ==============
1957 */
PM_WaterEvents(void)1958 static void PM_WaterEvents( void ) {        // FIXME?
1959 	//
1960 	// if just entered a water volume, play a sound
1961 	//
1962 	if ( !pml.previous_waterlevel && pm->waterlevel ) {
1963 		PM_AddEvent( EV_WATER_TOUCH );
1964 	}
1965 
1966 	//
1967 	// if just completely exited a water volume, play a sound
1968 	//
1969 	if ( pml.previous_waterlevel && !pm->waterlevel ) {
1970 		PM_AddEvent( EV_WATER_LEAVE );
1971 	}
1972 
1973 	//
1974 	// check for head just going under water
1975 	//
1976 	if ( pml.previous_waterlevel != 3 && pm->waterlevel == 3 ) {
1977 		PM_AddEvent( EV_WATER_UNDER );
1978 	}
1979 
1980 	//
1981 	// check for head just coming out of water
1982 	//
1983 	if ( pml.previous_waterlevel == 3 && pm->waterlevel != 3 ) {
1984 		PM_AddEvent( EV_WATER_CLEAR );
1985 	}
1986 }
1987 
1988 
1989 /*
1990 ==============
1991 PM_BeginWeaponReload
1992 ==============
1993 */
PM_BeginWeaponReload(int weapon)1994 static void PM_BeginWeaponReload( int weapon ) {
1995 	// only allow reload if the weapon isn't already occupied (firing is okay)
1996 	if ( pm->ps->weaponstate != WEAPON_READY && pm->ps->weaponstate != WEAPON_FIRING ) {
1997 		return;
1998 	}
1999 
2000 	if ( weapon < WP_BEGINGERMAN || weapon > WP_DYNAMITE ) {
2001 		return;
2002 	}
2003 
2004 	// no reload when you've got a chair in your hands
2005 	if ( pm->ps->eFlags & EF_MELEE_ACTIVE ) {
2006 		return;
2007 	}
2008 
2009 	switch ( weapon ) {
2010 	case WP_DYNAMITE:
2011 	case WP_GRENADE_LAUNCHER:
2012 	case WP_GRENADE_PINEAPPLE:
2013 		break;
2014 
2015 		// no reloading
2016 	case WP_KNIFE:
2017 	case WP_TESLA:
2018 		return;
2019 
2020 	default:
2021 		// DHM - Nerve :: override current animation (so reloading after firing will work)
2022 		BG_AnimScriptEvent( pm->ps, ANIM_ET_RELOAD, qfalse, qtrue );
2023 		break;
2024 	}
2025 
2026 
2027 	PM_ContinueWeaponAnim( WEAP_RELOAD1 );
2028 
2029 
2030 	// okay to reload while overheating without tacking the reload time onto the end of the
2031 	// current weaponTime (the reload time is partially absorbed into the overheat time)
2032 	if ( pm->ps->weaponstate == WEAPON_READY ) {                  // set wait to the reload duration
2033 		pm->ps->weaponTime += ammoTable[weapon].reloadTime;
2034 	} else if ( pm->ps->weaponTime < ammoTable[weapon].reloadTime ) {
2035 		pm->ps->weaponTime += ( ammoTable[weapon].reloadTime - pm->ps->weaponTime );
2036 	}
2037 
2038 	pm->ps->weaponstate = WEAPON_RELOADING;
2039 	PM_AddEvent( EV_FILL_CLIP );    // play reload sound
2040 }
2041 
2042 
2043 
2044 
2045 /*
2046 ===============
2047 PM_BeginWeaponChange
2048 ===============
2049 */
PM_BeginWeaponChange(int oldweapon,int newweapon,qboolean reload)2050 static void PM_BeginWeaponChange( int oldweapon, int newweapon, qboolean reload ) {  //----(SA)	modified to play 1st person alt-mode transition animations.
2051 	int switchtime;
2052 	qboolean altswitch, showdrop;
2053 
2054 	if ( newweapon < WP_NONE || newweapon >= WP_NUM_WEAPONS ) {
2055 		return;
2056 	}
2057 
2058 	if ( !pm->ps->aiChar && !( pm->ps->eFlags & EF_DEAD ) && ( newweapon == WP_NONE ) ) {   // RF, dont allow changing to null weapon
2059 		return;
2060 	}
2061 
2062 	if ( newweapon != WP_NONE && !( COM_BitCheck( pm->ps->weapons, newweapon ) ) ) {
2063 		return;
2064 	}
2065 
2066 	if ( pm->ps->weaponstate == WEAPON_DROPPING || pm->ps->weaponstate == WEAPON_DROPPING_TORELOAD ) {   //----(SA)	added
2067 		return;
2068 	}
2069 
2070 	if ( pm->ps->grenadeTimeLeft > 0 ) {   // don't allow switch if you're holding a hot potato or dynamite
2071 		return;
2072 	}
2073 
2074 	if ( !pm->ps->aiChar && !oldweapon ) {    // go straight to the new weapon
2075 		pm->ps->weaponDelay = 0;
2076 		pm->ps->weaponTime = 0;
2077 		pm->ps->weaponstate = WEAPON_RAISING;
2078 		pm->ps->weapon = newweapon;
2079 		return;
2080 	}
2081 
2082 	altswitch = (qboolean)( newweapon == weapAlts[oldweapon] );
2083 
2084 	showdrop = qtrue;
2085 
2086 	if ( oldweapon == WP_GRENADE_LAUNCHER ||
2087 		 oldweapon == WP_GRENADE_PINEAPPLE ||
2088 		 oldweapon == WP_DYNAMITE ||
2089 		 oldweapon == WP_PANZERFAUST ) {
2090 		if ( !pm->ps->ammoclip[oldweapon] ) {  // you're empty, don't show grenade '0'
2091 			showdrop = qfalse;
2092 		}
2093 	}
2094 
2095 
2096 	switch ( newweapon ) {
2097 
2098 	case WP_GAUNTLET:
2099 	case WP_MONSTER_ATTACK1:
2100 	case WP_MONSTER_ATTACK2:
2101 	case WP_MONSTER_ATTACK3:
2102 		break;
2103 
2104 	case WP_DYNAMITE:
2105 	case WP_GRENADE_LAUNCHER:
2106 	case WP_GRENADE_PINEAPPLE:
2107 		pm->ps->grenadeTimeLeft = 0;        // initialize the timer on the potato you're switching to
2108 
2109 	default:
2110 		//----(SA)	only play the weapon switch sound for the player
2111 		if ( !( pm->ps->aiChar ) ) {
2112 			PM_AddEvent( EV_CHANGE_WEAPON );
2113 		}
2114 
2115 		if ( altswitch ) {  // it's an alt mode, play different anim
2116 			PM_StartWeaponAnim( WEAP_ALTSWITCHFROM );
2117 		} else {
2118 			if ( showdrop ) {
2119 				PM_StartWeaponAnim( WEAP_DROP );    // PM_ContinueWeaponAnim(WEAP_DROP);
2120 			}
2121 		}
2122 	}
2123 
2124 	BG_AnimScriptEvent( pm->ps, ANIM_ET_DROPWEAPON, qfalse, qfalse );
2125 
2126 	if ( reload ) {
2127 		pm->ps->weaponstate = WEAPON_DROPPING_TORELOAD;
2128 	} else {
2129 		pm->ps->weaponstate = WEAPON_DROPPING;
2130 	}
2131 
2132 	switchtime = 250;   // dropping/raising usually takes 1/4 sec.
2133 	// sometimes different switch times for alt weapons
2134 	switch ( oldweapon ) {
2135 	case WP_LUGER:
2136 		if ( altswitch ) {
2137 			switchtime = 50;
2138 		}
2139 		break;
2140 	case WP_SILENCER:
2141 		if ( altswitch ) {
2142 			switchtime = 1200;
2143 		}
2144 		break;
2145 	case WP_FG42:
2146 	case WP_FG42SCOPE:
2147 		if ( altswitch ) {
2148 			switchtime = 50;        // fast
2149 		}
2150 		break;
2151 
2152 //		case WP_MAUSER:
2153 //		case WP_SNIPERRIFLE:
2154 //		case WP_GARAND:
2155 //		case WP_SNOOPERSCOPE:
2156 //			if(altswitch)
2157 //				switchtime  = 100;
2158 //			break;
2159 	}
2160 
2161 	pm->ps->weaponTime += switchtime;
2162 }
2163 
2164 
2165 /*
2166 ===============
2167 PM_FinishWeaponChange
2168 ===============
2169 */
PM_FinishWeaponChange(void)2170 static void PM_FinishWeaponChange( void ) {
2171 	int oldweapon, newweapon, switchtime;
2172 
2173 	newweapon = pm->cmd.weapon;
2174 	if ( newweapon < WP_NONE || newweapon >= WP_NUM_WEAPONS ) {
2175 		newweapon = WP_NONE;
2176 	}
2177 
2178 	if ( !( COM_BitCheck( pm->ps->weapons, newweapon ) ) ) {
2179 		newweapon = WP_NONE;
2180 	}
2181 
2182 	oldweapon = pm->ps->weapon;
2183 
2184 	pm->ps->weapon = newweapon;
2185 
2186 	if ( pm->ps->weaponstate == WEAPON_DROPPING_TORELOAD ) {
2187 		pm->ps->weaponstate = WEAPON_RAISING_TORELOAD;  //----(SA)	added
2188 	} else {
2189 		pm->ps->weaponstate = WEAPON_RAISING;
2190 	}
2191 
2192 	switch ( newweapon )
2193 	{
2194 		// don't really care about anim since these weapons don't show in view.
2195 		// However, need to set the animspreadscale so they are initally at worst accuracy
2196 	case WP_SNOOPERSCOPE:
2197 	case WP_SNIPERRIFLE:
2198 	case WP_FG42SCOPE:
2199 		pm->ps->aimSpreadScale = 255;               // initially at lowest accuracy
2200 		pm->ps->aimSpreadScaleFloat = 255.0f;       // initially at lowest accuracy
2201 
2202 	default:
2203 		break;
2204 	}
2205 
2206 	// doesn't happen too often (player switched weapons away then back very quickly)
2207 	if ( oldweapon == newweapon ) {
2208 		return;
2209 	}
2210 
2211 	// dropping/raising usually takes 1/4 sec.
2212 	switchtime = 250;
2213 
2214 	// sometimes different switch times for alt weapons
2215 	switch ( newweapon ) {
2216 	case WP_LUGER:
2217 		if ( newweapon == weapAlts[oldweapon] ) {
2218 			switchtime = 50;
2219 		}
2220 		break;
2221 	case WP_SILENCER:
2222 		if ( newweapon == weapAlts[oldweapon] ) {
2223 			switchtime = 1190;
2224 		}
2225 		break;
2226 	case WP_FG42:
2227 	case WP_FG42SCOPE:
2228 		if ( newweapon == weapAlts[oldweapon] ) {
2229 			switchtime = 50;        // fast
2230 		}
2231 		break;
2232 	}
2233 
2234 	pm->ps->weaponTime += switchtime;
2235 
2236 	// make scripting aware of new weapon
2237 	BG_UpdateConditionValue( pm->ps->clientNum, ANIM_COND_WEAPON, newweapon, qtrue );
2238 
2239 	// play an animation
2240 	BG_AnimScriptEvent( pm->ps, ANIM_ET_RAISEWEAPON, qfalse, qfalse );
2241 
2242 	// alt weapon switch was played when switching away, just go into idle
2243 	if ( weapAlts[oldweapon] == newweapon ) {
2244 		PM_StartWeaponAnim( WEAP_ALTSWITCHTO );
2245 	} else {
2246 		PM_StartWeaponAnim( WEAP_RAISE );
2247 	}
2248 
2249 }
2250 
2251 
2252 /*
2253 ==============
2254 PM_ReloadClip
2255 ==============
2256 */
PM_ReloadClip(int weapon)2257 static void PM_ReloadClip( int weapon ) {
2258 	int ammoreserve, ammoclip, ammomove;
2259 
2260 	ammoreserve = pm->ps->ammo[ BG_FindAmmoForWeapon( weapon )];
2261 	ammoclip    = pm->ps->ammoclip[BG_FindClipForWeapon( weapon )];
2262 
2263 	ammomove = ammoTable[weapon].maxclip - ammoclip;
2264 
2265 	if ( ammoreserve < ammomove ) {
2266 		ammomove = ammoreserve;
2267 	}
2268 
2269 	if ( ammomove ) {
2270 		pm->ps->ammo[ BG_FindAmmoForWeapon( weapon )] -= ammomove;
2271 		pm->ps->ammoclip[BG_FindClipForWeapon( weapon )] += ammomove;
2272 	}
2273 
2274 	if ( weapon == WP_AKIMBO ) { // reload colt too
2275 		PM_ReloadClip( WP_COLT );
2276 	}
2277 }
2278 
2279 /*
2280 ==============
2281 PM_FinishWeaponReload
2282 ==============
2283 */
2284 
PM_FinishWeaponReload(void)2285 static void PM_FinishWeaponReload( void ) {
2286 	PM_ReloadClip( pm->ps->weapon );          // move ammo into clip
2287 	pm->ps->weaponstate = WEAPON_READY;     // ready to fire
2288 }
2289 
2290 
2291 /*
2292 ==============
2293 PM_CheckforReload
2294 ==============
2295 */
PM_CheckForReload(int weapon)2296 void PM_CheckForReload( int weapon ) {
2297 	qboolean reloadRequested;
2298 	qboolean doReload = qfalse;
2299 	int clipWeap, ammoWeap;
2300 
2301 	if ( pm->noWeapClips ) { // no need to reload
2302 		return;
2303 	}
2304 
2305 	// user is forcing a reload (manual reload)
2306 	reloadRequested = (qboolean)( pm->cmd.wbuttons & WBUTTON_RELOAD );
2307 
2308 	switch ( pm->ps->weaponstate ) {
2309 	case WEAPON_RAISING:
2310 	case WEAPON_RAISING_TORELOAD:       //----(SA)	added
2311 	case WEAPON_DROPPING:
2312 	case WEAPON_DROPPING_TORELOAD:      //----(SA)	added
2313 	case WEAPON_READYING:
2314 	case WEAPON_RELAXING:
2315 	case WEAPON_RELOADING:
2316 		return;
2317 		break;
2318 	default:
2319 		break;
2320 	}
2321 
2322 	clipWeap = BG_FindClipForWeapon( weapon );
2323 	ammoWeap = BG_FindAmmoForWeapon( weapon );
2324 
2325 	// don't allow reloading for these weapons.  when the player hits 'reload' or 'fire'
2326 	// when the weapon is empty, it will switch away to the primary.  (so player can see results of
2327 	// last shot through scope without it auto-switching away for the reload)
2328 	if ( !pm->ps->aiChar ) {  // RF, added this check since this confuses the AI
2329 		switch ( weapon ) {
2330 		case WP_SNOOPERSCOPE:
2331 		case WP_SNIPERRIFLE:
2332 		case WP_FG42SCOPE:
2333 			if ( reloadRequested ) {
2334 				doReload = qtrue;
2335 				if ( !( pm->ps->ammo[ammoWeap] ) ) { // no ammo left. when you switch out, don't try to reload
2336 					doReload = qfalse;
2337 				}
2338 				PM_BeginWeaponChange( weapon, weapAlts[weapon], doReload );
2339 			}
2340 			return;
2341 		default:
2342 			break;
2343 		}
2344 	}
2345 
2346 
2347 //	if ( pm->ps->weaponTime <= 0) {
2348 
2349 	if ( reloadRequested ) {
2350 		// don't allow a force reload if it won't have any effect (no more ammo reserves or full clip)
2351 		if ( pm->ps->ammo[ammoWeap] ) {
2352 			if ( pm->ps->ammoclip[clipWeap] < ammoTable[weapon].maxclip ) {
2353 				doReload = qtrue;
2354 			}
2355 			if ( weapon == WP_AKIMBO ) {
2356 				// akimbo should also check Colt status
2357 				if ( pm->ps->ammoclip[BG_FindClipForWeapon( WP_COLT )] < ammoTable[BG_FindClipForWeapon( WP_COLT )].maxclip ) {
2358 					doReload = qtrue;
2359 				}
2360 			}
2361 		}
2362 	}
2363 	// clip is empty, but you have reserves.  (auto reload)
2364 	else if ( !( pm->ps->ammoclip[clipWeap] ) ) {   // clip is empty...
2365 		if ( pm->ps->ammo[ammoWeap] ) {         // and you have reserves
2366 			if ( weapon == WP_AKIMBO ) {    // if colt's got ammo, don't force reload yet (you know you've got it 'out' since you've got the akimbo selected
2367 				if ( !( pm->ps->ammoclip[WP_COLT] ) ) {
2368 					doReload = qtrue;
2369 				}
2370 				// likewise.  however, you need to check if you've got the akimbo selected, since you could have the colt alone
2371 			} else if ( weapon == WP_COLT ) {   // weapon checking for reload is colt...
2372 				if ( pm->ps->weapon == WP_AKIMBO ) {    // you've got the akimbo selected...
2373 					if ( !( pm->ps->ammoclip[WP_AKIMBO] ) ) {   // and it's got no ammo either
2374 						doReload = qtrue;       // so reload
2375 					}
2376 				} else {     // single colt selected
2377 					doReload = qtrue;       // so reload
2378 				}
2379 			} else {
2380 				doReload = qtrue;
2381 			}
2382 		}
2383 	}
2384 
2385 //	}
2386 
2387 	if ( doReload ) {
2388 		PM_BeginWeaponReload( weapon );
2389 	}
2390 
2391 
2392 }
2393 
2394 /*
2395 ==============
2396 PM_SwitchIfEmpty
2397 ==============
2398 */
PM_SwitchIfEmpty(void)2399 static void PM_SwitchIfEmpty( void ) {
2400 	// TODO: cvar for emptyswitch
2401 //	if(!cg_emptyswitch.integer)
2402 //		return;
2403 
2404 	// weapon from here down will be a thrown explosive
2405 	switch ( pm->ps->weapon ) {
2406 	case WP_GRENADE_LAUNCHER:
2407 	case WP_GRENADE_PINEAPPLE:
2408 	case WP_DYNAMITE:
2409 	case WP_PANZERFAUST:
2410 		break;
2411 	default:
2412 		return;
2413 	}
2414 
2415 
2416 	if ( pm->ps->ammoclip[ BG_FindClipForWeapon( pm->ps->weapon )] ) { // still got ammo in clip
2417 		return;
2418 	}
2419 
2420 	if ( pm->ps->ammo[ BG_FindAmmoForWeapon( pm->ps->weapon )] ) { // still got ammo in reserve
2421 		return;
2422 	}
2423 
2424 	// If this was the last one, remove the weapon and switch away before the player tries to fire next
2425 
2426 	// NOTE: giving grenade ammo to a player will re-give him the weapon (if you do it through add_ammo())
2427 	switch ( pm->ps->weapon ) {
2428 	case WP_GRENADE_LAUNCHER:
2429 	case WP_GRENADE_PINEAPPLE:
2430 	case WP_DYNAMITE:
2431 		// take the 'weapon' away from the player
2432 		COM_BitClear( pm->ps->weapons, pm->ps->weapon );
2433 		break;
2434 	default:
2435 		break;
2436 	}
2437 
2438 	PM_AddEvent( EV_NOAMMO );
2439 }
2440 
2441 
2442 /*
2443 ==============
2444 PM_WeaponUseAmmo
2445 	accounts for clips being used/not used
2446 ==============
2447 */
PM_WeaponUseAmmo(int wp,int amount)2448 void PM_WeaponUseAmmo( int wp, int amount ) {
2449 	int takeweapon;
2450 
2451 	if ( pm->noWeapClips ) {
2452 		pm->ps->ammo[ BG_FindAmmoForWeapon( wp )] -= amount;
2453 	} else {
2454 		takeweapon = BG_FindClipForWeapon( wp );
2455 		if ( wp == WP_AKIMBO ) {
2456 			if ( !BG_AkimboFireSequence( wp, pm->ps->ammoclip[WP_AKIMBO], pm->ps->ammoclip[WP_COLT] ) ) {
2457 				takeweapon = WP_COLT;
2458 			}
2459 		}
2460 
2461 		pm->ps->ammoclip[takeweapon] -= amount;
2462 	}
2463 }
2464 
2465 
2466 /*
2467 ==============
2468 PM_WeaponAmmoAvailable
2469 	accounts for clips being used/not used
2470 ==============
2471 */
PM_WeaponAmmoAvailable(int wp)2472 int PM_WeaponAmmoAvailable( int wp ) {
2473 	int takeweapon;
2474 
2475 	if ( pm->noWeapClips ) {
2476 		return pm->ps->ammo[ BG_FindAmmoForWeapon( wp )];
2477 	} else {
2478 //		return pm->ps->ammoclip[BG_FindClipForWeapon( wp )];
2479 		takeweapon = BG_FindClipForWeapon( wp );
2480 		if ( wp == WP_AKIMBO ) {
2481 			if ( !BG_AkimboFireSequence( pm->ps->weapon, pm->ps->ammoclip[WP_AKIMBO], pm->ps->ammoclip[WP_COLT] ) ) {
2482 				takeweapon = WP_COLT;
2483 			}
2484 		}
2485 
2486 		return pm->ps->ammoclip[takeweapon];
2487 	}
2488 }
2489 
2490 /*
2491 ==============
2492 PM_WeaponClipEmpty
2493 	accounts for clips being used/not used
2494 ==============
2495 */
PM_WeaponClipEmpty(int wp)2496 int PM_WeaponClipEmpty( int wp ) {
2497 	if ( pm->noWeapClips ) {
2498 		if ( !( pm->ps->ammo[ BG_FindAmmoForWeapon( wp )] ) ) {
2499 			return 1;
2500 		}
2501 	} else {
2502 		if ( !( pm->ps->ammoclip[BG_FindClipForWeapon( wp )] ) ) {
2503 			return 1;
2504 		}
2505 	}
2506 
2507 	return 0;
2508 }
2509 
2510 
2511 /*
2512 ==============
2513 PM_CoolWeapons
2514 ==============
2515 */
PM_CoolWeapons(void)2516 void PM_CoolWeapons( void ) {
2517 	int wp;
2518 
2519 	for ( wp = 0; wp < WP_NUM_WEAPONS; wp++ ) {
2520 
2521 		// if you have the weapon
2522 		if ( COM_BitCheck( pm->ps->weapons, wp ) ) {
2523 			// and it's hot
2524 			if ( pm->ps->weapHeat[wp] ) {
2525 				pm->ps->weapHeat[wp] -= ( (float)ammoTable[wp].coolRate * pml.frametime );
2526 
2527 				if ( pm->ps->weapHeat[wp] < 0 ) {
2528 					pm->ps->weapHeat[wp] = 0;
2529 				}
2530 
2531 			}
2532 		}
2533 	}
2534 
2535 	// a weapon is currently selected, convert current heat value to 0-255 range for client transmission
2536 	if ( pm->ps->weapon ) {
2537 		pm->ps->curWeapHeat = ( ( (float)pm->ps->weapHeat[pm->ps->weapon] / (float)ammoTable[pm->ps->weapon].maxHeat ) ) * 255.0f;
2538 
2539 //		if(pm->ps->weapHeat[pm->ps->weapon])
2540 //			Com_Printf("pm heat: %d, %d\n", pm->ps->weapHeat[pm->ps->weapon], pm->ps->curWeapHeat);
2541 	}
2542 
2543 }
2544 
2545 /*
2546 ==============
2547 PM_AdjustAimSpreadScale
2548 ==============
2549 */
PM_AdjustAimSpreadScale(void)2550 void PM_AdjustAimSpreadScale( void ) {
2551 //	int		increase, decrease, i;
2552 	int i;
2553 	float increase, decrease;       // (SA) was losing lots of precision on slower weapons (scoped)
2554 	float viewchange, cmdTime, wpnScale;
2555 //#define	AIMSPREAD_DECREASE_RATE		300.0f
2556 #define AIMSPREAD_DECREASE_RATE     200.0f      // (SA) when I made the increase/decrease floats (so slower weapon recover could happen for scoped weaps) the average rate increased significantly
2557 #define AIMSPREAD_INCREASE_RATE     800.0f
2558 #define AIMSPREAD_VIEWRATE_MIN      30.0f       // degrees per second
2559 #define AIMSPREAD_VIEWRATE_RANGE    120.0f      // degrees per second
2560 
2561 	// all weapons are very inaccurate in zoomed mode
2562 
2563 	if ( pm->ps->eFlags & EF_ZOOMING ) {
2564 
2565 		pm->ps->aimSpreadScale = 255;
2566 		pm->ps->aimSpreadScaleFloat = 255;
2567 		return;
2568 	}
2569 
2570 	cmdTime = (float)( pm->cmd.serverTime - pm->oldcmd.serverTime ) / 1000.0;
2571 
2572 	wpnScale = 0.0f;
2573 	switch ( pm->ps->weapon ) {
2574 	case WP_LUGER:
2575 	case WP_SILENCER:
2576 		wpnScale = 0.5f;
2577 		break;
2578 	case WP_AKIMBO: //----(SA)	added
2579 		wpnScale = 0.5;
2580 		break;
2581 	case WP_COLT:
2582 		wpnScale = 0.4f;        // doesn't fire as fast, but easier to handle than luger
2583 		break;
2584 	case WP_VENOM:
2585 		wpnScale = 0.9f;        // very heavy
2586 		break;
2587 	case WP_SNIPERRIFLE:    // (SA) looong time to recover
2588 		wpnScale = 10.0f;
2589 		break;
2590 	case WP_SNOOPERSCOPE:   // (SA) looong time to recover
2591 		wpnScale = 8.0f;
2592 		break;
2593 	case WP_MAUSER:
2594 		wpnScale = 0.5f;
2595 		break;
2596 	case WP_GARAND:
2597 		wpnScale = 0.5f;
2598 		break;
2599 	case WP_MP40:
2600 		wpnScale = 0.5f;        // 2 handed, but not as long as mauser, so harder to keep aim
2601 		break;
2602 	case WP_FG42:
2603 		wpnScale = 0.9f;
2604 		break;
2605 	case WP_FG42SCOPE:
2606 		wpnScale = 0.7f;
2607 		break;
2608 	case WP_THOMPSON:
2609 		wpnScale = 0.4f;
2610 		break;
2611 	case WP_STEN:
2612 		wpnScale = 0.6f;
2613 		break;
2614 	case WP_PANZERFAUST:
2615 //		wpnScale = 1.3f;
2616 		wpnScale = 0.6f;
2617 		break;
2618 	}
2619 
2620 	if ( wpnScale ) {
2621 
2622 // JPW NERVE crouched players recover faster (mostly useful for snipers)
2623 		if ( ( pm->ps->eFlags & EF_CROUCHING ) && ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) ) {  //----(SA)	modified so you can't do this in the air.  cool?
2624 			wpnScale *= 0.5;
2625 		}
2626 // jpw
2627 
2628 		decrease = ( cmdTime * AIMSPREAD_DECREASE_RATE ) / wpnScale;
2629 
2630 		viewchange = 0;
2631 		// take player view rotation into account
2632 		for ( i = 0; i < 2; i++ )
2633 			viewchange += fabs( SHORT2ANGLE( pm->cmd.angles[i] ) - SHORT2ANGLE( pm->oldcmd.angles[i] ) );
2634 
2635 		// take player movement into account (even if only for the scoped weapons)
2636 		// TODO: also check for jump/crouch and adjust accordingly
2637 		switch ( pm->ps->weapon ) {
2638 		case WP_SNIPERRIFLE:
2639 		case WP_SNOOPERSCOPE:
2640 		case WP_FG42SCOPE:
2641 			for ( i = 0; i < 2; i++ )
2642 				viewchange += fabs( pm->ps->velocity[i] );
2643 			break;
2644 		case WP_PANZERFAUST:        // don't take movement into account as much
2645 			for ( i = 0; i < 2; i++ )
2646 				viewchange += ( 0.01f * fabs( pm->ps->velocity[i] ) );
2647 			break;
2648 		default:
2649 			break;
2650 		}
2651 
2652 
2653 
2654 		viewchange = (float)viewchange / cmdTime;   // convert into this movement for a second
2655 		viewchange -= AIMSPREAD_VIEWRATE_MIN / wpnScale;
2656 		if ( viewchange <= 0 ) {
2657 			viewchange = 0;
2658 		} else if ( viewchange > ( AIMSPREAD_VIEWRATE_RANGE / wpnScale ) ) {
2659 			viewchange = AIMSPREAD_VIEWRATE_RANGE / wpnScale;
2660 		}
2661 
2662 		// now give us a scale from 0.0 to 1.0 to apply the spread increase
2663 		viewchange = viewchange / (float)( AIMSPREAD_VIEWRATE_RANGE / wpnScale );
2664 
2665 		increase = (int)( cmdTime * viewchange * AIMSPREAD_INCREASE_RATE );
2666 	} else {
2667 		increase = 0;
2668 		decrease = AIMSPREAD_DECREASE_RATE;
2669 	}
2670 
2671 	// update the aimSpreadScale
2672 	pm->ps->aimSpreadScaleFloat += ( increase - decrease );
2673 	if ( pm->ps->aimSpreadScaleFloat < 0 ) {
2674 		pm->ps->aimSpreadScaleFloat = 0;
2675 	}
2676 	if ( pm->ps->aimSpreadScaleFloat > 255 ) {
2677 		pm->ps->aimSpreadScaleFloat = 255;
2678 	}
2679 
2680 	pm->ps->aimSpreadScale = (int)pm->ps->aimSpreadScaleFloat;  // update the int for the client
2681 }
2682 
2683 #define weaponstateFiring ( pm->ps->weaponstate == WEAPON_FIRING || pm->ps->weaponstate == WEAPON_FIRINGALT )
2684 
2685 #define GRENADE_DELAY   250
2686 
2687 /*
2688 ==============
2689 PM_Weapon
2690 
2691 Generates weapon events and modifes the weapon counter
2692 ==============
2693 */
2694 
2695 #define VENOM_LOW_IDLE  WEAP_IDLE1
2696 #define VENOM_HI_IDLE   WEAP_IDLE2
2697 #define VENOM_RAISE     WEAP_ATTACK1
2698 #define VENOM_ATTACK    WEAP_ATTACK2
2699 #define VENOM_LOWER     WEAP_ATTACK_LASTSHOT
2700 
2701 // JPW NERVE
2702 #ifdef CGAMEDLL
2703 extern vmCvar_t cg_soldierChargeTime;
2704 extern vmCvar_t cg_engineerChargeTime;
2705 #endif
2706 #ifdef GAMEDLL
2707 extern vmCvar_t g_soldierChargeTime;
2708 extern vmCvar_t g_engineerChargeTime;
2709 #endif
2710 // jpw
2711 
2712 
2713 #ifdef CGAMEDLL
2714 extern vmCvar_t cg_reloading;
2715 #endif
2716 #ifdef GAMEDLL
2717 extern vmCvar_t g_reloading;
2718 #endif
2719 
2720 
2721 /*
2722 ==============
2723 PM_Weapon
2724 ==============
2725 */
PM_Weapon(void)2726 static void PM_Weapon( void ) {
2727 	int addTime;
2728 	int ammoNeeded;
2729 	qboolean delayedFire;       //----(SA)  true if the delay time has just expired and this is the frame to send the fire event
2730 	int aimSpreadScaleAdd;
2731 	int weapattackanim;
2732 	qboolean akimboFire;
2733 	qboolean gameReloading;
2734 
2735 	// don't allow attack until all buttons are up
2736 	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
2737 		return;
2738 	}
2739 
2740 	// game is reloading (mission fail/success)
2741 #ifdef CGAMEDLL
2742 	if ( cg_reloading.integer )
2743 #endif
2744 #ifdef GAMEDLL
2745 	if ( g_reloading.integer )
2746 #endif
2747 		gameReloading = qtrue;
2748 	else {
2749 		gameReloading = qfalse;
2750 	}
2751 
2752 	// ignore if spectator
2753 	if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
2754 		return;
2755 	}
2756 
2757 	// check for dead player
2758 	if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
2759 		pm->ps->weapon = WP_NONE;
2760 		return;
2761 	}
2762 
2763 	// don't allow any weapon stuff if using the mg42
2764 	if ( pm->ps->persistant[PERS_HWEAPON_USE] ) {
2765 		return;
2766 	}
2767 
2768 	// RF, remoed this, was preventing lava from hurting player
2769 	//pm->watertype = 0;
2770 
2771 	akimboFire = BG_AkimboFireSequence( pm->ps->weapon, pm->ps->ammoclip[WP_AKIMBO], pm->ps->ammoclip[WP_COLT] );
2772 
2773 	if ( 0 ) {
2774 		switch ( pm->ps->weaponstate ) {
2775 		case WEAPON_READY:
2776 			Com_Printf( " -- WEAPON_READY\n" );
2777 			break;
2778 		case WEAPON_RAISING:
2779 			Com_Printf( " -- WEAPON_RAISING\n" );
2780 			break;
2781 		case WEAPON_RAISING_TORELOAD:       //----(SA)	added
2782 			Com_Printf( " -- WEAPON_RAISING_TORELOAD\n" );
2783 			break;
2784 		case WEAPON_DROPPING:
2785 			Com_Printf( " -- WEAPON_DROPPING\n" );
2786 			break;
2787 		case WEAPON_DROPPING_TORELOAD:      //----(SA)	added
2788 			Com_Printf( " -- WEAPON_DROPPING_TORELOAD\n" );
2789 			break;
2790 		case WEAPON_READYING:
2791 			Com_Printf( " -- WEAPON_READYING\n" );
2792 			break;
2793 		case WEAPON_RELAXING:
2794 			Com_Printf( " -- WEAPON_RELAXING\n" );
2795 			break;
2796 		case WEAPON_VENOM_REST:
2797 			Com_Printf( " -- WEAPON_VENOM_REST\n" );
2798 			break;
2799 		case WEAPON_FIRING:
2800 			Com_Printf( " -- WEAPON_FIRING\n" );
2801 			break;
2802 		case WEAPON_FIRINGALT:
2803 			Com_Printf( " -- WEAPON_FIRINGALT\n" );
2804 			break;
2805 		case WEAPON_RELOADING:
2806 			Com_Printf( " -- WEAPON_RELOADING\n" );
2807 			break;
2808 		}
2809 	}
2810 
2811 	// dec venom timer
2812 	if ( pm->ps->venomTime > 0 ) {
2813 		pm->ps->venomTime -= pml.msec;
2814 	}
2815 
2816 
2817 //----(SA)	removed 'quickgrenade'
2818 
2819 	// weapon cool down
2820 	PM_CoolWeapons();
2821 
2822 	// check for item using
2823 	if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) {
2824 		if ( !( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) {
2825 			gitem_t *item;
2826 
2827 			pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
2828 
2829 			if ( pm->cmd.holdable ) {
2830 				item = BG_FindItemForHoldable( pm->cmd.holdable );
2831 
2832 				if ( item && ( pm->ps->holdable[pm->cmd.holdable] >= item->quantity ) ) { // ->quantity being how much 'ammo' is taken per use
2833 					PM_AddEvent( EV_USE_ITEM0 + pm->cmd.holdable );
2834 					// don't take books away when used
2835 					if ( pm->cmd.holdable < HI_BOOK1 || pm->cmd.holdable > HI_BOOK3 ) {
2836 						pm->ps->holdable[ pm->cmd.holdable ] -= item->quantity;
2837 					}
2838 
2839 					if ( pm->ps->holdable[pm->cmd.holdable] <= 0 ) {   // empty
2840 						PM_AddEvent( EV_NOITEM );
2841 					}
2842 				}
2843 			} else {
2844 				PM_AddEvent( EV_USE_ITEM0 );     // send "using nothing"
2845 			}
2846 			return;
2847 		}
2848 	} else {
2849 		pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD;
2850 	}
2851 
2852 
2853 	delayedFire = qfalse;
2854 
2855 	if ( pm->ps->weapon == WP_GRENADE_LAUNCHER || pm->ps->weapon == WP_GRENADE_PINEAPPLE || pm->ps->weapon == WP_DYNAMITE ) {
2856 		// (SA) AI's don't set grenadeTimeLeft on +attack, so I don't check for (pm->ps->aiChar) here
2857 		if ( pm->ps->grenadeTimeLeft > 0 ) {
2858 			if ( pm->ps->weapon == WP_DYNAMITE ) {
2859 				pm->ps->grenadeTimeLeft += pml.msec;
2860 // JPW NERVE -- in multiplayer, dynamite becomes strategic, so start timer @ 30 seconds
2861 #ifdef CGAMEDLL
2862 				if ( cg_gameType.integer != GT_SINGLE_PLAYER ) {
2863 #endif
2864 #ifdef GAMEDLL
2865 				if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
2866 #endif
2867 					if ( pm->ps->grenadeTimeLeft < 5000 ) {
2868 						pm->ps->grenadeTimeLeft = 5000;
2869 					}
2870 				}
2871 // jpw
2872 
2873 				if ( pm->ps->grenadeTimeLeft > 8000 ) {
2874 					PM_AddEvent( EV_FIRE_WEAPON );
2875 					pm->ps->weaponTime = 1600;
2876 					PM_WeaponUseAmmo( pm->ps->weapon, 1 );      //----(SA)	take ammo
2877 					return;
2878 				}
2879 
2880 //				Com_Printf("Dynamite Timer: %d\n", pm->ps->grenadeTimeLeft);
2881 			} else {
2882 				pm->ps->grenadeTimeLeft -= pml.msec;
2883 //				Com_Printf("Grenade Timer: %d\n", pm->ps->grenadeTimeLeft);
2884 
2885 				if ( pm->ps->grenadeTimeLeft <= 0 ) {   // give two frames advance notice so there's time to launch and detonate
2886 //					pm->ps->grenadeTimeLeft = 100;
2887 //					PM_AddEvent( EV_FIRE_WEAPON );
2888 					PM_WeaponUseAmmo( pm->ps->weapon, 1 );      //----(SA)	take ammo
2889 //					pm->ps->weaponTime = 1600;
2890 
2891 					PM_AddEvent( EV_GRENADE_SUICIDE );      //----(SA)	die, dumbass
2892 
2893 					return;
2894 				}
2895 			}
2896 
2897 			if ( !( pm->cmd.buttons & BUTTON_ATTACK ) ) { //----(SA)	modified
2898 				if ( pm->ps->weaponDelay == ammoTable[pm->ps->weapon].fireDelayTime ) {
2899 					// released fire button.  Fire!!!
2900 					BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue );
2901 				}
2902 			} else {
2903 				return;
2904 			}
2905 		}
2906 	}
2907 
2908 	if ( pm->ps->weaponDelay > 0 ) {
2909 		pm->ps->weaponDelay -= pml.msec;
2910 		if ( pm->ps->weaponDelay <= 0 ) {
2911 			pm->ps->weaponDelay = 0;
2912 			delayedFire = qtrue;            // weapon delay has expired.  Fire this frame
2913 
2914 			// double check the player is still holding the fire button down for these weapons
2915 			// so you don't get a delayed "non-fire" (fire hit and released, then shot fires)
2916 			switch ( pm->ps->weapon ) {
2917 			case WP_VENOM:
2918 				if ( pm->ps->weaponstate == WEAPON_FIRING ) {
2919 					delayedFire = qfalse;
2920 				}
2921 				break;
2922 			default:
2923 				break;
2924 			}
2925 		}
2926 	}
2927 
2928 	if ( pm->ps->weaponstate == WEAPON_RELAXING ) {
2929 		pm->ps->weaponstate = WEAPON_READY;
2930 		return;
2931 	}
2932 
2933 	// make weapon function
2934 	if ( pm->ps->weaponTime > 0 ) {
2935 		pm->ps->weaponTime -= pml.msec;
2936 		if ( pm->ps->weaponTime < 0 ) {
2937 			pm->ps->weaponTime = 0;
2938 		}
2939 
2940 //----(SA)	removed for DM and id
2941 /*
2942 		// RF, testing special case Pistol, which fires faster if you tap the fire button
2943 		if ( pm->ps->weapon == WP_LUGER ) {
2944 			if ( pm->ps->releasedFire ) {
2945 				if (pm->ps->weaponTime <= 250 && (pm->cmd.buttons & BUTTON_ATTACK)) {
2946 					pm->ps->weaponTime = 0;
2947 				}
2948 			} else if (!(pm->cmd.buttons & BUTTON_ATTACK)) {
2949 				pm->ps->releasedFire = qtrue;
2950 			}
2951 		} else if ( pm->ps->weapon == WP_COLT ) {
2952 			if ( pm->ps->releasedFire ) {
2953 				if (pm->ps->weaponTime <= 150 && (pm->cmd.buttons & BUTTON_ATTACK)) {
2954 					pm->ps->weaponTime = 0;
2955 				}
2956 			} else if (!(pm->cmd.buttons & BUTTON_ATTACK)) {
2957 				pm->ps->releasedFire = qtrue;
2958 			}
2959 		} else if ( pm->ps->weapon == WP_SILENCER ) {
2960 			if ( pm->ps->releasedFire ) {
2961 				if (pm->ps->weaponTime <= 250 && (pm->cmd.buttons & BUTTON_ATTACK)) {
2962 					pm->ps->weaponTime = 0;
2963 				}
2964 			} else if (!(pm->cmd.buttons & BUTTON_ATTACK)) {
2965 				pm->ps->releasedFire = qtrue;
2966 			}
2967 		}
2968 */
2969 //----(SA)	end
2970 
2971 // JPW NERVE -- added back for multiplayer pistol balancing
2972 #ifdef CGAMEDLL
2973 	if ( cg_gameType.integer != GT_SINGLE_PLAYER ) {
2974 #endif
2975 #ifdef GAMEDLL
2976 	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
2977 #endif
2978 		if ( pm->ps->weapon == WP_LUGER ) {
2979 			if ( pm->ps->releasedFire ) {
2980 				if ( pm->ps->weaponTime <= 150 && ( pm->cmd.buttons & BUTTON_ATTACK ) ) {
2981 					pm->ps->weaponTime = 0;
2982 				}
2983 			} else if ( !( pm->cmd.buttons & BUTTON_ATTACK ) && ( pm->ps->weaponTime >= 50 ) ) {
2984 				pm->ps->releasedFire = qtrue;
2985 			}
2986 		} else if ( pm->ps->weapon == WP_COLT ) {
2987 			if ( pm->ps->releasedFire ) {
2988 				if ( pm->ps->weaponTime <= 150 && ( pm->cmd.buttons & BUTTON_ATTACK ) ) {
2989 					pm->ps->weaponTime = 0;
2990 				}
2991 			} else if ( !( pm->cmd.buttons & BUTTON_ATTACK ) && ( pm->ps->weaponTime >= 100 ) ) {
2992 				pm->ps->releasedFire = qtrue;
2993 			}
2994 		}
2995 	}
2996 // jpw
2997 
2998 }
2999 
3000 	// check for weapon change
3001 	// can't change if weapon is firing, but can change
3002 	// again if lowering or raising
3003 
3004 	// TTimo gcc: suggest parentheses around && within ||
3005 	if ( pm->ps->weaponTime <= 0 || ( !weaponstateFiring && pm->ps->weaponDelay <= 0 ) ) {
3006 		if ( pm->ps->weapon != pm->cmd.weapon ) {
3007 			PM_BeginWeaponChange( pm->ps->weapon, pm->cmd.weapon, qfalse );     //----(SA)	modified
3008 		}
3009 	}
3010 
3011 	// check for clip change
3012 	PM_CheckForReload( pm->ps->weapon );
3013 
3014 	if ( pm->ps->weaponTime > 0 || pm->ps->weaponDelay > 0 ) {
3015 		return;
3016 	}
3017 
3018 	if ( pm->ps->weaponstate == WEAPON_RELOADING ) {
3019 		PM_FinishWeaponReload();
3020 	}
3021 
3022 	// change weapon if time
3023 	if ( pm->ps->weaponstate == WEAPON_DROPPING || pm->ps->weaponstate == WEAPON_DROPPING_TORELOAD ) {
3024 		PM_FinishWeaponChange();
3025 		return;
3026 	}
3027 
3028 	if ( pm->ps->weaponstate == WEAPON_RAISING ) {
3029 		pm->ps->weaponstate = WEAPON_READY;
3030 		PM_StartWeaponAnim( WEAP_IDLE1 );
3031 		return;
3032 	} else if ( pm->ps->weaponstate == WEAPON_RAISING_TORELOAD ) {
3033 		pm->ps->weaponstate = WEAPON_READY;     // need to switch to READY so the reload will work
3034 		PM_BeginWeaponReload( pm->ps->weapon );
3035 		return;
3036 	}
3037 
3038 	if ( pm->ps->weapon == WP_NONE ) {  // this is possible since the player starts with nothing
3039 		return;
3040 	}
3041 
3042 
3043 // JPW NERVE -- in multiplayer, don't allow panzerfaust or dynamite to fire if charge bar isn't full
3044 #ifdef GAMEDLL
3045 	if ( g_gametype.integer == GT_WOLF ) {
3046 		if ( pm->ps->weapon == WP_PANZERFAUST ) {
3047 			if ( pm->cmd.serverTime - pm->ps->classWeaponTime < g_soldierChargeTime.integer ) {
3048 				return;
3049 			}
3050 		}
3051 		if ( pm->ps->weapon == WP_DYNAMITE ) {
3052 			if ( pm->cmd.serverTime - pm->ps->classWeaponTime < g_engineerChargeTime.integer ) {        // had to use multiplier because chargetime is used elsewhere as bomb diffuse time FIXME not really but NOTE changing bomb diffuse time will require changing this time as well (intended function: new charge ready when old one explodes, ie every 30 seconds or so)
3053 
3054 				return;
3055 			}
3056 		}
3057 	}
3058 #endif
3059 #ifdef CGAMEDLL
3060 	if ( cg_gameType.integer == GT_WOLF ) {
3061 		if ( pm->ps->weapon == WP_PANZERFAUST ) {
3062 			if ( pm->cmd.serverTime - pm->ps->classWeaponTime < cg_soldierChargeTime.integer ) {
3063 				return;
3064 			}
3065 		}
3066 		if ( pm->ps->weapon == WP_DYNAMITE ) {
3067 			if ( pm->cmd.serverTime - pm->ps->classWeaponTime < cg_engineerChargeTime.integer ) {
3068 				return;
3069 			}
3070 		}
3071 	}
3072 #endif
3073 // jpw
3074 
3075 	// check for fire
3076 	if ( !( pm->cmd.buttons & ( BUTTON_ATTACK | WBUTTON_ATTACK2 ) ) && !delayedFire ) {     // if not on fire button and there's not a delayed shot this frame...
3077 		pm->ps->weaponTime  = 0;
3078 		pm->ps->weaponDelay = 0;
3079 
3080 		if ( weaponstateFiring ) {  // you were just firing, time to relax
3081 			PM_ContinueWeaponAnim( WEAP_IDLE1 );
3082 		}
3083 
3084 		pm->ps->weaponstate = WEAPON_READY;
3085 		return;
3086 	}
3087 
3088 	if ( gameReloading ) {
3089 		return;
3090 	}
3091 
3092 	// player is zooming - no fire
3093 	if ( pm->ps->eFlags & EF_ZOOMING ) {
3094 		return;
3095 	}
3096 
3097 	// player is leaning - no fire
3098 	if ( pm->ps->leanf != 0 && pm->ps->weapon != WP_GRENADE_LAUNCHER && pm->ps->weapon != WP_GRENADE_PINEAPPLE && pm->ps->weapon != WP_DYNAMITE ) {
3099 		return;
3100 	}
3101 
3102 	// player is underwater - no fire
3103 	if ( pm->waterlevel == 3 ) {
3104 		if ( pm->ps->weapon != WP_KNIFE &&
3105 			 pm->ps->weapon != WP_GRENADE_LAUNCHER &&
3106 			 pm->ps->weapon != WP_GRENADE_PINEAPPLE ) {
3107 			PM_AddEvent( EV_NOFIRE_UNDERWATER );        // event for underwater 'click' for nofire
3108 			pm->ps->weaponTime  = 500;
3109 			return;
3110 		}
3111 	}
3112 
3113 	// start the animation even if out of ammo
3114 	switch ( pm->ps->weapon ) {
3115 	default:
3116 		if ( !weaponstateFiring ) {
3117 			// delay so the weapon can get up into position before firing (and showing the flash)
3118 			pm->ps->weaponDelay = ammoTable[pm->ps->weapon].fireDelayTime;
3119 		} else {
3120 			BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue );
3121 		}
3122 		break;
3123 	// machineguns should continue the anim, rather than start each fire
3124 	case WP_MP40:
3125 	case WP_THOMPSON:
3126 	case WP_STEN:
3127 	case WP_VENOM:
3128 	case WP_FG42:
3129 	case WP_FG42SCOPE:
3130 		if ( !weaponstateFiring ) {
3131 			if ( pm->ps->aiChar && pm->ps->weapon == WP_VENOM ) {
3132 				// AI get fast spin-up
3133 				pm->ps->weaponDelay = 150;
3134 			} else {
3135 				// delay so the weapon can get up into position before firing (and showing the flash)
3136 				pm->ps->weaponDelay = ammoTable[pm->ps->weapon].fireDelayTime;
3137 			}
3138 		} else {
3139 			BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qtrue, qtrue );
3140 		}
3141 		break;
3142 	case WP_PANZERFAUST:
3143 	case WP_SILENCER:
3144 	case WP_LUGER:
3145 	case WP_COLT:
3146 	case WP_AKIMBO:         //----(SA)	added
3147 	case WP_SNIPERRIFLE:
3148 	case WP_SNOOPERSCOPE:
3149 	case WP_MAUSER:
3150 	case WP_GARAND:
3151 		if ( !weaponstateFiring ) {
3152 			// NERVE's panzerfaust spinup
3153 //			if (pm->ps->weapon == WP_PANZERFAUST)
3154 //				PM_AddEvent( EV_SPINUP );
3155 			pm->ps->weaponDelay = ammoTable[pm->ps->weapon].fireDelayTime;
3156 		} else {
3157 			BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue );
3158 		}
3159 		break;
3160 	// melee
3161 	case WP_KNIFE:
3162 		if ( !delayedFire ) {
3163 			BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qfalse, qfalse );
3164 		}
3165 		break;
3166 	case WP_GAUNTLET:
3167 		if ( !delayedFire ) {
3168 			BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qfalse, qfalse );
3169 		}
3170 		break;
3171 	// throw
3172 	case WP_DYNAMITE:
3173 	case WP_GRENADE_LAUNCHER:
3174 	case WP_GRENADE_PINEAPPLE:
3175 		if ( !delayedFire ) {
3176 			if ( pm->ps->aiChar ) {
3177 				// ai characters go into their regular animation setup
3178 				BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qtrue, qtrue );
3179 			} else {
3180 				// the player pulls the fuse and holds the hot potato
3181 				if ( PM_WeaponAmmoAvailable( pm->ps->weapon ) ) {
3182 					if ( pm->ps->weapon == WP_DYNAMITE ) {
3183 						pm->ps->grenadeTimeLeft = 50;
3184 					} else {
3185 						// start at four seconds and count down
3186 						pm->ps->grenadeTimeLeft = 4000;
3187 					}
3188 					PM_StartWeaponAnim( WEAP_ATTACK1 );
3189 				}
3190 			}
3191 
3192 			pm->ps->weaponDelay = ammoTable[pm->ps->weapon].fireDelayTime;
3193 
3194 		}
3195 		break;
3196 	}
3197 
3198 	pm->ps->weaponstate = WEAPON_FIRING;
3199 
3200 	// check for out of ammo
3201 	ammoNeeded = ammoTable[pm->ps->weapon].uses;
3202 
3203 	if ( pm->ps->weapon ) {
3204 		int ammoAvailable;
3205 		qboolean reloadingW, playswitchsound = qtrue;
3206 
3207 		ammoAvailable = PM_WeaponAmmoAvailable( pm->ps->weapon );
3208 
3209 		if ( ammoNeeded > ammoAvailable ) {
3210 
3211 			// you have ammo for this, just not in the clip
3212 			reloadingW = (qboolean)( ammoNeeded <= pm->ps->ammo[ BG_FindAmmoForWeapon( pm->ps->weapon )] );
3213 
3214 			if ( pm->ps->eFlags & EF_MELEE_ACTIVE ) {
3215 				// not going to be allowed to reload if holding a chair
3216 				reloadingW = qfalse;
3217 			}
3218 
3219 			if ( pm->ps->weapon == WP_SNOOPERSCOPE ) {
3220 				reloadingW = qfalse;
3221 			}
3222 
3223 			switch ( pm->ps->weapon ) {
3224 			// Ridah, only play if using a triggered weapon
3225 			case WP_GAUNTLET:
3226 			case WP_MONSTER_ATTACK1:
3227 			case WP_DYNAMITE:
3228 			case WP_GRENADE_LAUNCHER:
3229 			case WP_GRENADE_PINEAPPLE:
3230 				playswitchsound = qfalse;
3231 				break;
3232 			// some weapons not allowed to reload.  must switch back to primary first
3233 			case WP_SNOOPERSCOPE:
3234 			case WP_SNIPERRIFLE:
3235 			case WP_FG42SCOPE:
3236 				reloadingW = qfalse;
3237 				break;
3238 			}
3239 
3240 			if ( playswitchsound ) {
3241 				if ( reloadingW ) {
3242 					PM_AddEvent( EV_EMPTYCLIP );
3243 				} else {
3244 					PM_AddEvent( EV_NOAMMO );
3245 				}
3246 			}
3247 
3248 			if ( reloadingW ) {
3249 				PM_ContinueWeaponAnim( WEAP_RELOAD1 );      //----(SA)
3250 			} else {
3251 				PM_ContinueWeaponAnim( WEAP_IDLE1 );
3252 				pm->ps->weaponTime += 500;
3253 			}
3254 
3255 			return;
3256 		}
3257 	}
3258 
3259 	if ( pm->ps->weaponDelay > 0 ) {
3260 		// if it hits here, the 'fire' has just been hit and the weapon dictated a delay.
3261 		// animations have been started, weaponstate has been set, but no weapon events yet. (except possibly EV_NOAMMO)
3262 		// checks for delayed weapons that have already been fired are return'ed above.
3263 		return;
3264 	}
3265 
3266 
3267 	// take an ammo away if not infinite
3268 	if ( PM_WeaponAmmoAvailable( pm->ps->weapon ) != -1 ) {
3269 		// Rafael - check for being mounted on mg42
3270 		if ( !( pm->ps->persistant[PERS_HWEAPON_USE] ) ) {
3271 			PM_WeaponUseAmmo( pm->ps->weapon, ammoNeeded );
3272 		}
3273 	}
3274 
3275 
3276 	// fire weapon
3277 
3278 	// add weapon heat
3279 	if ( ammoTable[pm->ps->weapon].maxHeat ) {
3280 		pm->ps->weapHeat[pm->ps->weapon] += ammoTable[pm->ps->weapon].nextShotTime;
3281 	}
3282 
3283 	// first person weapon animations
3284 
3285 	// if this was the last round in the clip, play the 'lastshot' animation
3286 	// this animation has the weapon in a "ready to reload" state
3287 	if ( pm->ps->weapon == WP_AKIMBO ) {
3288 		if ( akimboFire ) {
3289 			weapattackanim = WEAP_ATTACK1;      // attack1 is right hand
3290 		} else {
3291 			weapattackanim = WEAP_ATTACK2;      // attack2 is left hand
3292 		}
3293 	} else {
3294 		if ( PM_WeaponClipEmpty( pm->ps->weapon ) ) {
3295 			weapattackanim = WEAP_ATTACK_LASTSHOT;
3296 		} else {
3297 			weapattackanim = WEAP_ATTACK1;
3298 		}
3299 	}
3300 
3301 	switch ( pm->ps->weapon ) {
3302 	case WP_MAUSER:
3303 	case WP_GRENADE_LAUNCHER:
3304 	case WP_GRENADE_PINEAPPLE:
3305 	case WP_DYNAMITE:
3306 		PM_StartWeaponAnim( weapattackanim );
3307 		break;
3308 	case WP_VENOM:
3309 	case WP_MP40:
3310 	case WP_THOMPSON:
3311 	case WP_STEN:
3312 		PM_ContinueWeaponAnim( weapattackanim );
3313 		break;
3314 
3315 	default:
3316 // RF, testing
3317 //		PM_ContinueWeaponAnim(weapattackanim);
3318 		PM_StartWeaponAnim( weapattackanim );
3319 		break;
3320 	}
3321 
3322 
3323 
3324 	if ( pm->ps->weapon == WP_AKIMBO ) {
3325 		if ( pm->ps->weapon == WP_AKIMBO && !akimboFire ) {
3326 			PM_AddEvent( EV_FIRE_WEAPONB );     // really firing colt
3327 		} else {
3328 			PM_AddEvent( EV_FIRE_WEAPON );
3329 		}
3330 	} else {
3331 		if ( PM_WeaponClipEmpty( pm->ps->weapon ) ) {
3332 			PM_AddEvent( EV_FIRE_WEAPON_LASTSHOT );
3333 		} else {
3334 			PM_AddEvent( EV_FIRE_WEAPON );
3335 		}
3336 	}
3337 // RF
3338 	pm->ps->releasedFire = qfalse;
3339 	pm->ps->lastFireTime = pm->cmd.serverTime;
3340 
3341 
3342 	aimSpreadScaleAdd = 0;
3343 
3344 	switch ( pm->ps->weapon ) {
3345 	case WP_KNIFE:
3346 	case WP_DYNAMITE:
3347 	case WP_GRENADE_LAUNCHER:
3348 	case WP_GRENADE_PINEAPPLE:
3349 	case WP_FLAMETHROWER:
3350 		addTime = ammoTable[pm->ps->weapon].nextShotTime;
3351 		break;
3352 	case WP_PANZERFAUST:
3353 		addTime = ammoTable[pm->ps->weapon].nextShotTime;
3354 		aimSpreadScaleAdd = 30;
3355 		break;
3356 	case WP_LUGER:
3357 		addTime = ammoTable[pm->ps->weapon].nextShotTime;
3358 		aimSpreadScaleAdd = 35;
3359 		break;
3360 	case WP_COLT:
3361 		addTime = ammoTable[pm->ps->weapon].nextShotTime;
3362 		aimSpreadScaleAdd = 20;
3363 		break;
3364 //----(SA)	added
3365 	case WP_AKIMBO:
3366 		// if you're firing an akimbo colt, and your other gun is dry,
3367 		// nextshot needs to take 2x time
3368 		addTime = ammoTable[pm->ps->weapon].nextShotTime;
3369 
3370 		// (SA) (added check for last shot in both guns so there's no delay for the last shot)
3371 		if ( !pm->ps->ammoclip[WP_AKIMBO] || !pm->ps->ammoclip[WP_COLT] ) {
3372 			if ( ( !pm->ps->ammoclip[WP_AKIMBO] && !akimboFire ) || ( !pm->ps->ammoclip[WP_COLT] && akimboFire ) ) {
3373 				addTime = 2 * ammoTable[pm->ps->weapon].nextShotTime;
3374 			}
3375 		}
3376 
3377 		aimSpreadScaleAdd = 20;
3378 		break;
3379 //----(SA)	end
3380 	case WP_MAUSER:
3381 	case WP_GARAND:
3382 		addTime = ammoTable[pm->ps->weapon].nextShotTime;
3383 		aimSpreadScaleAdd = 50;
3384 		break;
3385 	case WP_SNIPERRIFLE:
3386 		// (SA) not so much added per shot.  these weapons mostly uses player movement to get out of whack
3387 		addTime = ammoTable[pm->ps->weapon].nextShotTime;
3388 // JPW NERVE crippling the rifle a bit in multiplayer; it's way too strong so make it go completely out every time you fire
3389 #ifdef CGAMEDLL
3390 		if ( cg_gameType.integer != GT_SINGLE_PLAYER ) {
3391 #endif
3392 #ifdef GAMEDLL
3393 		if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
3394 #endif
3395 //			addTime *= 2; // pulled this and reduced rifle damage
3396 			aimSpreadScaleAdd = 100;
3397 		} else {
3398 // jpw
3399 			aimSpreadScaleAdd = 20;
3400 		}
3401 		break;
3402 	case WP_SNOOPERSCOPE:
3403 // JPW NERVE crippling the rifle a bit in multiplayer; it's way too strong so make it go completely out every time you fire
3404 // snooper doesn't do one-shot body kills, so give it a little less bounce
3405 		addTime = ammoTable[pm->ps->weapon].nextShotTime;
3406 #ifdef CGAMEDLL
3407 		if ( cg_gameType.integer != GT_SINGLE_PLAYER ) {
3408 #endif
3409 #ifdef GAMEDLL
3410 		if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
3411 #endif
3412 			aimSpreadScaleAdd = 50;
3413 //			addTime *= 2;
3414 		} else {
3415 // jpw
3416 			aimSpreadScaleAdd = 10;
3417 		}
3418 		break;
3419 	case WP_FG42SCOPE:
3420 		addTime = ammoTable[pm->ps->weapon].nextShotTime;
3421 		aimSpreadScaleAdd = 10;
3422 		break;
3423 	case WP_FG42:
3424 	case WP_MP40:
3425 	case WP_THOMPSON:
3426 		addTime = ammoTable[pm->ps->weapon].nextShotTime;
3427 		aimSpreadScaleAdd = 15 + rand() % 10;       // (SA) new values for DM
3428 		break;
3429 	case WP_STEN:
3430 		addTime = ammoTable[pm->ps->weapon].nextShotTime;
3431 		aimSpreadScaleAdd = 15 + rand() % 10;       // (SA) new values for DM
3432 		break;
3433 	case WP_SILENCER:
3434 		addTime = ammoTable[pm->ps->weapon].nextShotTime;
3435 		aimSpreadScaleAdd = 35;
3436 		break;
3437 	case WP_VENOM:
3438 		addTime = ammoTable[pm->ps->weapon].nextShotTime;
3439 		aimSpreadScaleAdd = 10;
3440 		break;
3441 // JPW NERVE
3442 	case WP_CLASS_SPECIAL:
3443 		// can't reliably get cvars for restart time in here, so set it small and check it in fireweapon routine
3444 		addTime = 50;
3445 		break;
3446 // jpw
3447 	case WP_MONSTER_ATTACK1:
3448 		addTime = 1000;
3449 		break;
3450 	default:
3451 		case WP_GAUNTLET:
3452 		switch ( pm->ps->aiChar ) {
3453 		case AICHAR_LOPER:              // delay 'til next attack
3454 			addTime = 1000;
3455 			break;
3456 		default:
3457 			addTime = 250;
3458 			break;
3459 		}
3460 		break;
3461 	}
3462 
3463 	// check for overheat
3464 
3465 	// the weapon can overheat, and it's hot
3466 	if ( ( pm->ps->aiChar != AICHAR_PROTOSOLDIER ) &&
3467 		 ( pm->ps->aiChar != AICHAR_SUPERSOLDIER ) &&
3468 		 ( ammoTable[pm->ps->weapon].maxHeat && pm->ps->weapHeat[pm->ps->weapon] ) ) {
3469 		// it is overheating
3470 		if ( pm->ps->weapHeat[pm->ps->weapon] >= ammoTable[pm->ps->weapon].maxHeat ) {
3471 			pm->ps->weapHeat[pm->ps->weapon] = ammoTable[pm->ps->weapon].maxHeat;       // cap heat to max
3472 			PM_AddEvent( EV_WEAP_OVERHEAT );
3473 //			PM_StartWeaponAnim(WEAP_IDLE1);	// removed.  client handles anim in overheat event
3474 			addTime = 2000;         // force "heat recovery minimum" to 2 sec right now
3475 		}
3476 	}
3477 
3478 	if ( pm->ps->powerups[PW_HASTE] ) {
3479 		addTime /= 1.3;
3480 	}
3481 
3482 	// add the recoil amount to the aimSpreadScale
3483 //	pm->ps->aimSpreadScale += 3.0*aimSpreadScaleAdd;
3484 //	if (pm->ps->aimSpreadScale > 255)
3485 //		pm->ps->aimSpreadScale = 255;
3486 	pm->ps->aimSpreadScaleFloat += 3.0 * aimSpreadScaleAdd;
3487 	if ( pm->ps->aimSpreadScaleFloat > 255 ) {
3488 		pm->ps->aimSpreadScaleFloat = 255;
3489 	}
3490 	pm->ps->aimSpreadScale = (int)( pm->ps->aimSpreadScaleFloat );
3491 
3492 	pm->ps->weaponTime += addTime;
3493 
3494 	PM_SwitchIfEmpty();
3495 }
3496 
3497 
3498 /*
3499 ================
3500 PM_Animate
3501 ================
3502 */
3503 #define MYTIMER_SALUTE   1133   // 17 frames, 15 fps
3504 #define MYTIMER_DISMOUNT 667    // 10 frames, 15 fps
3505 
3506 static void PM_Animate( void ) {
3507 /*
3508 	if ( pm->cmd.buttons & BUTTON_GESTURE ) {
3509 		if ( pm->ps->torsoTimer == 0) {
3510 			PM_StartTorsoAnim( BOTH_SALUTE );
3511 			PM_StartLegsAnim( BOTH_SALUTE );
3512 
3513 			pm->ps->torsoTimer = MYTIMER_SALUTE;
3514 			pm->ps->legsTimer = MYTIMER_SALUTE;
3515 
3516 			if (!pm->ps->aiChar)	// Ridah, we'll play a custom sound upon calling the Taunt
3517 				PM_AddEvent( EV_TAUNT );	// for playing the sound
3518 		}
3519 	}
3520 */
3521 }
3522 
3523 
3524 /*
3525 ================
3526 PM_DropTimers
3527 ================
3528 */
3529 static void PM_DropTimers( void ) {
3530 	// drop misc timing counter
3531 	if ( pm->ps->pm_time ) {
3532 		if ( pml.msec >= pm->ps->pm_time ) {
3533 			pm->ps->pm_flags &= ~PMF_ALL_TIMES;
3534 			pm->ps->pm_time = 0;
3535 		} else {
3536 			pm->ps->pm_time -= pml.msec;
3537 		}
3538 	}
3539 
3540 	// drop animation counter
3541 	if ( pm->ps->legsTimer > 0 ) {
3542 		pm->ps->legsTimer -= pml.msec;
3543 		if ( pm->ps->legsTimer < 0 ) {
3544 			pm->ps->legsTimer = 0;
3545 		}
3546 	}
3547 
3548 	if ( pm->ps->torsoTimer > 0 ) {
3549 		pm->ps->torsoTimer -= pml.msec;
3550 		if ( pm->ps->torsoTimer < 0 ) {
3551 			pm->ps->torsoTimer = 0;
3552 		}
3553 	}
3554 
3555 	// first person weapon counter
3556 	if ( pm->ps->weapAnimTimer > 0 ) {
3557 		pm->ps->weapAnimTimer -= pml.msec;
3558 		if ( pm->ps->weapAnimTimer < 0 ) {
3559 			pm->ps->weapAnimTimer = 0;
3560 		}
3561 	}
3562 }
3563 
3564 
3565 
3566 #define LEAN_MAX    28.0f
3567 #define LEAN_TIME_TO    280.0f  // time to get to full lean
3568 #define LEAN_TIME_FR    350.0f  // time to get from full lean
3569 
3570 /*
3571 ==============
3572 PM_CalcLean
3573 
3574 ==============
3575 */
3576 void PM_UpdateLean( playerState_t *ps, usercmd_t *cmd, pmove_t *tpm ) {
3577 	vec3_t start, end, tmins, tmaxs, right;
3578 	int leaning = 0;            // -1 left, 1 right
3579 	float leanofs = 0;
3580 	vec3_t viewangles;
3581 	trace_t trace;
3582 
3583 	if ( ps->aiChar ) {
3584 		return;
3585 	}
3586 
3587 	if ( ( cmd->wbuttons & ( WBUTTON_LEANLEFT | WBUTTON_LEANRIGHT ) )  && !cmd->forwardmove && cmd->upmove <= 0 ) {
3588 		// if both are pressed, result is no lean
3589 		if ( cmd->wbuttons & WBUTTON_LEANLEFT ) {
3590 			leaning -= 1;
3591 		}
3592 		if ( cmd->wbuttons & WBUTTON_LEANRIGHT ) {
3593 			leaning += 1;
3594 		}
3595 	}
3596 
3597 	// not allowed when...
3598 	if ( ( ps->eFlags & ( EF_MELEE_ACTIVE |   // ...holding a chair
3599 						  EF_MG42_ACTIVE  | // ...on mg42
3600 						  EF_FIRING ) ) ) { // ...firing
3601 		leaning = 0;
3602 	}
3603 
3604 	leanofs = ps->leanf;
3605 
3606 	if ( !leaning ) {  // go back to center position
3607 		if ( leanofs > 0 ) {        // right
3608 			//FIXME: play lean anim backwards?
3609 			leanofs -= ( ( (float)pml.msec / LEAN_TIME_FR ) * LEAN_MAX );
3610 			if ( leanofs < 0 ) {
3611 				leanofs = 0;
3612 			}
3613 		} else if ( leanofs < 0 )   { // left
3614 			//FIXME: play lean anim backwards?
3615 			leanofs += ( ( (float)pml.msec / LEAN_TIME_FR ) * LEAN_MAX );
3616 			if ( leanofs > 0 ) {
3617 				leanofs = 0;
3618 			}
3619 		}
3620 	}
3621 
3622 	if ( leaning ) {
3623 		if ( leaning > 0 ) {   // right
3624 			if ( leanofs < LEAN_MAX ) {
3625 				leanofs += ( ( (float)pml.msec / LEAN_TIME_TO ) * LEAN_MAX );
3626 			}
3627 
3628 			if ( leanofs > LEAN_MAX ) {
3629 				leanofs = LEAN_MAX;
3630 			}
3631 
3632 		} else {              // left
3633 			if ( leanofs > -LEAN_MAX ) {
3634 				leanofs -= ( ( (float)pml.msec / LEAN_TIME_TO ) * LEAN_MAX );
3635 			}
3636 
3637 			if ( leanofs < -LEAN_MAX ) {
3638 				leanofs = -LEAN_MAX;
3639 			}
3640 
3641 		}
3642 	}
3643 
3644 	ps->leanf = leanofs;
3645 
3646 	if ( leaning ) {
3647 		VectorCopy( ps->origin, start );
3648 		start[2] += ps->viewheight;
3649 		VectorCopy( ps->viewangles, viewangles );
3650 		viewangles[ROLL] = 0;
3651 		AngleVectors( viewangles, NULL, right, NULL );
3652 		VectorNormalize( right );
3653 		right[2] = ( leanofs < 0 ) ? 0.25 : -0.25;
3654 		VectorMA( start, leanofs, right, end );
3655 		VectorSet( tmins, -12, -12, -6 );
3656 		VectorSet( tmaxs, 12, 12, 10 );
3657 
3658 		if ( pm ) {
3659 			pm->trace( &trace, start, tmins, tmaxs, end, ps->clientNum, MASK_PLAYERSOLID );
3660 		} else {
3661 			tpm->trace( &trace, start, tmins, tmaxs, end, ps->clientNum, MASK_PLAYERSOLID );
3662 		}
3663 
3664 		ps->leanf *= trace.fraction;
3665 	}
3666 
3667 
3668 	if ( ps->leanf ) {
3669 		cmd->rightmove = 0;     // also disallowed in cl_input ~391
3670 
3671 	}
3672 }
3673 
3674 
3675 
3676 /*
3677 ================
3678 PM_UpdateViewAngles
3679 
3680 This can be used as another entry point when only the viewangles
3681 are being updated instead of a full move
3682 ================
3683 */
3684 void PM_UpdateViewAngles( playerState_t *ps, usercmd_t *cmd, void( trace ) ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask ) ) {   //----(SA)	modified
3685 	short temp;
3686 	int i;
3687 	pmove_t tpm;
3688 
3689 	if ( ps->pm_type == PM_FREEZE ) {
3690 		return;
3691 	}
3692 
3693 	if ( ps->pm_type == PM_INTERMISSION ) {
3694 		return;     // no view changes at all
3695 	}
3696 
3697 	if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
3698 		return;     // no view changes at all
3699 	}
3700 
3701 	// circularly clamp the angles with deltas
3702 	for ( i = 0 ; i < 3 ; i++ ) {
3703 		temp = cmd->angles[i] + ps->delta_angles[i];
3704 		if ( i == PITCH ) {
3705 			// don't let the player look up or down more than 90 degrees
3706 			if ( temp > 16000 ) {
3707 				ps->delta_angles[i] = 16000 - cmd->angles[i];
3708 				temp = 16000;
3709 			} else if ( temp < -16000 ) {
3710 				ps->delta_angles[i] = -16000 - cmd->angles[i];
3711 				temp = -16000;
3712 			}
3713 		}
3714 		ps->viewangles[i] = SHORT2ANGLE( temp );
3715 	}
3716 
3717 
3718 	tpm.trace = trace;
3719 //	tpm.trace (&trace, start, tmins, tmaxs, end, ps->clientNum, MASK_PLAYERSOLID);
3720 
3721 	PM_UpdateLean( ps, cmd, &tpm );
3722 }
3723 
3724 /*
3725 ================
3726 PM_CheckLadderMove
3727 
3728   Checks to see if we are on a ladder
3729 ================
3730 */
3731 qboolean ladderforward;
3732 vec3_t laddervec;
3733 
3734 void PM_CheckLadderMove( void ) {
3735 	vec3_t spot;
3736 	vec3_t flatforward;
3737 	trace_t trace;
3738 	float tracedist;
3739 	#define TRACE_LADDER_DIST   48.0
3740 	qboolean wasOnLadder;
3741 
3742 	if ( pm->ps->pm_time ) {
3743 		return;
3744 	}
3745 
3746 	//if (pm->ps->pm_flags & PM_DEAD)
3747 	//	return;
3748 
3749 	if ( pml.walking ) {
3750 		tracedist = 1.0;
3751 	} else {
3752 		tracedist = TRACE_LADDER_DIST;
3753 	}
3754 
3755 	wasOnLadder = ( ( pm->ps->pm_flags & PMF_LADDER ) != 0 );
3756 
3757 	pml.ladder = qfalse;
3758 	pm->ps->pm_flags &= ~PMF_LADDER;    // clear ladder bit
3759 	ladderforward = qfalse;
3760 
3761 	/*
3762 	if (pm->ps->eFlags & EF_DEAD) {	// dead bodies should fall down ladders
3763 		return;
3764 	}
3765 
3766 	if (pm->ps->pm_flags & PM_DEAD && pm->ps->stats[STAT_HEALTH] <= 0)
3767 	{
3768 		return;
3769 	}
3770 	*/
3771 	if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
3772 		pm->ps->groundEntityNum = ENTITYNUM_NONE;
3773 		pml.groundPlane = qfalse;
3774 		pml.walking = qfalse;
3775 		return;
3776 	}
3777 
3778 	// check for ladder
3779 	flatforward[0] = pml.forward[0];
3780 	flatforward[1] = pml.forward[1];
3781 	flatforward[2] = 0;
3782 	VectorNormalize( flatforward );
3783 
3784 	VectorMA( pm->ps->origin, tracedist, flatforward, spot );
3785 	pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, spot, pm->ps->clientNum, pm->tracemask );
3786 	if ( ( trace.fraction < 1 ) && ( trace.surfaceFlags & SURF_LADDER ) ) {
3787 		pml.ladder = qtrue;
3788 	}
3789 /*
3790 	if (!pml.ladder && DotProduct(pm->ps->velocity, pml.forward) < 0) {
3791 		// trace along the negative velocity, so we grab onto a ladder if we are trying to reverse onto it from above the ladder
3792 		flatforward[0] = -pm->ps->velocity[0];
3793 		flatforward[1] = -pm->ps->velocity[1];
3794 		flatforward[2] = 0;
3795 		VectorNormalize (flatforward);
3796 
3797 		VectorMA (pm->ps->origin, tracedist, flatforward, spot);
3798 		pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, spot, pm->ps->clientNum, pm->tracemask);
3799 		if ((trace.fraction < 1) && (trace.surfaceFlags & SURF_LADDER))
3800 		{
3801 			pml.ladder = qtrue;
3802 		}
3803 	}
3804 */
3805 	if ( pml.ladder ) {
3806 		VectorCopy( trace.plane.normal, laddervec );
3807 	}
3808 
3809 	if ( pml.ladder && !pml.walking && ( trace.fraction * tracedist > 1.0 ) ) {
3810 		vec3_t mins;
3811 		// if we are only just on the ladder, don't do this yet, or it may throw us back off the ladder
3812 		pml.ladder = qfalse;
3813 		VectorCopy( pm->mins, mins );
3814 		mins[2] = -1;
3815 		VectorMA( pm->ps->origin, -tracedist, laddervec, spot );
3816 		pm->trace( &trace, pm->ps->origin, mins, pm->maxs, spot, pm->ps->clientNum, pm->tracemask );
3817 		if ( ( trace.fraction < 1 ) && ( trace.surfaceFlags & SURF_LADDER ) ) {
3818 			// if AI, then be more stringent on their viewangles
3819 			if ( pm->ps->aiChar && ( DotProduct( trace.plane.normal, pml.forward ) > -0.9 ) ) {
3820 				pml.ladder = qfalse;
3821 			} else {
3822 				ladderforward = qtrue;
3823 				pml.ladder = qtrue;
3824 				pm->ps->pm_flags |= PMF_LADDER; // set ladder bit
3825 			}
3826 		} else {
3827 			pml.ladder = qfalse;
3828 		}
3829 	} else if ( pml.ladder ) {
3830 		pm->ps->pm_flags |= PMF_LADDER; // set ladder bit
3831 	}
3832 
3833 	// create some up/down velocity if touching ladder
3834 	if ( pml.ladder ) {
3835 		if ( pml.walking ) {
3836 			// we are currently on the ground, only go up and prevent X/Y if we are pushing forwards
3837 			if ( pm->cmd.forwardmove <= 0 ) {
3838 				pml.ladder = qfalse;
3839 			}
3840 		}
3841 	}
3842 
3843 	// if we have just dismounted the ladder at the top, play dismount
3844 	if ( !pml.ladder && wasOnLadder && pm->ps->velocity[2] > 0 ) {
3845 		BG_AnimScriptEvent( pm->ps, ANIM_ET_CLIMB_DISMOUNT, qfalse, qfalse );
3846 	}
3847 	// if we have just mounted the ladder
3848 	if ( pml.ladder && !wasOnLadder && pm->ps->velocity[2] < 0 ) {    // only play anim if going down ladder
3849 		BG_AnimScriptEvent( pm->ps, ANIM_ET_CLIMB_MOUNT, qfalse, qfalse );
3850 	}
3851 }
3852 
3853 /*
3854 ============
3855 PM_LadderMove
3856 ============
3857 */
3858 void PM_LadderMove( void ) {
3859 	float wishspeed, scale;
3860 	vec3_t wishdir, wishvel;
3861 	float upscale;
3862 
3863 	if ( ladderforward ) {
3864 		// move towards the ladder
3865 		VectorScale( laddervec, -200.0, wishvel );
3866 		pm->ps->velocity[0] = wishvel[0];
3867 		pm->ps->velocity[1] = wishvel[1];
3868 	}
3869 
3870 	upscale = ( pml.forward[2] + 0.5 ) * 2.5;
3871 	if ( upscale > 1.0 ) {
3872 		upscale = 1.0;
3873 	} else if ( upscale < -1.0 ) {
3874 		upscale = -1.0;
3875 	}
3876 
3877 	// forward/right should be horizontal only
3878 	pml.forward[2] = 0;
3879 	pml.right[2] = 0;
3880 	VectorNormalize( pml.forward );
3881 	VectorNormalize( pml.right );
3882 
3883 	// move depending on the view, if view is straight forward, then go up
3884 	// if view is down more then X degrees, start going down
3885 	// if they are back pedalling, then go in reverse of above
3886 	scale = PM_CmdScale( &pm->cmd );
3887 	VectorClear( wishvel );
3888 
3889 	if ( pm->cmd.forwardmove ) {
3890 		if ( pm->ps->aiChar ) {
3891 			wishvel[2] = 0.5 * upscale * scale * (float)pm->cmd.forwardmove;
3892 		} else { // player speed
3893 			wishvel[2] = 0.9 * upscale * scale * (float)pm->cmd.forwardmove;
3894 		}
3895 	}
3896 //Com_Printf("wishvel[2] = %i, fwdmove = %i\n", (int)wishvel[2], (int)pm->cmd.forwardmove );
3897 
3898 	if ( pm->cmd.rightmove ) {
3899 		// strafe, so we can jump off ladder
3900 		vec3_t ladder_right, ang;
3901 		vectoangles( laddervec, ang );
3902 		AngleVectors( ang, NULL, ladder_right, NULL );
3903 
3904 		// if we are looking away from the ladder, reverse the right vector
3905 		if ( DotProduct( laddervec, pml.forward ) > 0 ) {
3906 			VectorInverse( ladder_right );
3907 		}
3908 
3909 		VectorMA( wishvel, 0.5 * scale * (float)pm->cmd.rightmove, pml.right, wishvel );
3910 	}
3911 
3912 	// do strafe friction
3913 	PM_Friction();
3914 
3915 	wishspeed = VectorNormalize2( wishvel, wishdir );
3916 
3917 	PM_Accelerate( wishdir, wishspeed, pm_accelerate );
3918 	if ( !wishvel[2] ) {
3919 		if ( pm->ps->velocity[2] > 0 ) {
3920 			pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
3921 			if ( pm->ps->velocity[2] < 0 ) {
3922 				pm->ps->velocity[2]  = 0;
3923 			}
3924 		} else
3925 		{
3926 			pm->ps->velocity[2] += pm->ps->gravity * pml.frametime;
3927 			if ( pm->ps->velocity[2] > 0 ) {
3928 				pm->ps->velocity[2]  = 0;
3929 			}
3930 		}
3931 	}
3932 
3933 //Com_Printf("vel[2] = %i\n", (int)pm->ps->velocity[2] );
3934 
3935 	PM_StepSlideMove( qfalse );  // no gravity while going up ladder
3936 
3937 	// always point legs forward
3938 	pm->ps->movementDir = 0;
3939 }
3940 
3941 
3942 /*
3943 ==============
3944 PM_Sprint
3945 ==============
3946 */
3947 //----(SA)	cleaned up for SP (10/22/01)
3948 void PM_Sprint( void ) {
3949 	if (    ( pm->cmd.buttons & BUTTON_SPRINT ) &&
3950 			( pm->cmd.forwardmove || pm->cmd.rightmove ) &&
3951 			!( pm->ps->pm_flags & PMF_DUCKED ) &&
3952 			( !pm->waterlevel )
3953 			) {
3954 
3955 		if ( pm->ps->powerups[PW_NOFATIGUE] ) {    // take time from powerup before taking it from sprintTime
3956 			pm->ps->powerups[PW_NOFATIGUE] -= 50;
3957 
3958 			pm->ps->sprintTime += 10;           // (SA) go ahead and continue to recharge stamina at double rate with stamina powerup even when exerting
3959 			if ( pm->ps->sprintTime > 20000 ) {
3960 				pm->ps->sprintTime = 20000;
3961 			}
3962 
3963 			if ( pm->ps->powerups[PW_NOFATIGUE] < 0 ) {
3964 				pm->ps->powerups[PW_NOFATIGUE] = 0;
3965 			}
3966 		} else {
3967 			// RF, dont drain sprintTime if not moving
3968 			if ( VectorLength( pm->ps->velocity ) > 128 ) { // (SA) check for a bit more movement
3969 				pm->ps->sprintTime -= 50;
3970 			}
3971 		}
3972 
3973 		if ( pm->ps->sprintTime < 0 ) {
3974 			pm->ps->sprintTime = 0;
3975 		}
3976 
3977 		if ( !pm->ps->sprintExertTime ) {
3978 			pm->ps->sprintExertTime = 1;
3979 		}
3980 	} else
3981 	{
3982 		// JPW NERVE adjusted for framerate independence
3983 
3984 		// regular recharge
3985 		pm->ps->sprintTime += 500 * pml.frametime;
3986 
3987 		// additional (2x) recharge if in top 75% of sprint bar, or with stamina powerup
3988 		if ( pm->ps->sprintTime > 5000 || pm->ps->powerups[PW_NOFATIGUE] ) {
3989 			pm->ps->sprintTime += 500 * pml.frametime;
3990 		}
3991 
3992 		// additional recharge if standing still
3993 		if ( !( pm->cmd.forwardmove || pm->cmd.rightmove ) ) {
3994 			pm->ps->sprintTime += 500 * pml.frametime;
3995 		}
3996 
3997 		if ( pm->ps->sprintTime > 20000 ) {
3998 			pm->ps->sprintTime = 20000;
3999 		}
4000 
4001 		pm->ps->sprintExertTime = 0;
4002 	}
4003 }
4004 
4005 /*
4006 ================
4007 PmoveSingle
4008 
4009 ================
4010 */
4011 void trap_SnapVector( float *v );
4012 
4013 void PmoveSingle( pmove_t *pmove ) {
4014 	// Ridah
4015 	qboolean isDummy;
4016 
4017 	isDummy = ( ( pmove->ps->eFlags & EF_DUMMY_PMOVE ) != 0 );
4018 	// done.
4019 
4020 	if ( !isDummy ) {
4021 		// RF, update conditional values for anim system
4022 		BG_AnimUpdatePlayerStateConditions( pmove );
4023 	}
4024 
4025 	pm = pmove;
4026 
4027 	// this counter lets us debug movement problems with a journal
4028 	// by setting a conditional breakpoint fot the previous frame
4029 	c_pmove++;
4030 
4031 	// clear results
4032 	pm->numtouch = 0;
4033 	pm->watertype = 0;
4034 	pm->waterlevel = 0;
4035 
4036 	if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
4037 		pm->tracemask &= ~CONTENTS_BODY;    // corpses can fly through bodies
4038 	}
4039 
4040 	// make sure walking button is clear if they are running, to avoid
4041 	// proxy no-footsteps cheats
4042 	if ( !pm->ps->aiChar ) {
4043 		if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) {
4044 			pm->cmd.buttons &= ~BUTTON_WALKING;
4045 		}
4046 	}
4047 
4048 	// set the talk balloon flag
4049 	if ( !isDummy ) {
4050 		if ( !pm->ps->aiChar && pm->cmd.buttons & BUTTON_TALK ) {
4051 			pm->ps->eFlags |= EF_TALK;
4052 		} else {
4053 			pm->ps->eFlags &= ~EF_TALK;
4054 		}
4055 	}
4056 
4057 	// set the firing flag for continuous beam weapons
4058 
4059 	pm->ps->eFlags &= ~( EF_FIRING | EF_ZOOMING );
4060 
4061 	if ( pm->cmd.wbuttons & WBUTTON_ZOOM ) {
4062 		if ( pm->ps->stats[STAT_KEYS] & ( 1 << INV_BINOCS ) ) {        // (SA) binoculars are an inventory item (inventory==keys)
4063 			if ( pm->ps->weapon != WP_SNIPERRIFLE && pm->ps->weapon != WP_SNOOPERSCOPE && pm->ps->weapon != WP_FG42SCOPE ) {   // don't allow binocs if using scope
4064 				if ( !( pm->ps->eFlags & EF_MG42_ACTIVE ) ) {    // or if mounted on a weapon
4065 					pm->ps->eFlags |= EF_ZOOMING;
4066 				}
4067 			}
4068 
4069 			// don't allow binocs if in the middle of throwing grenade
4070 			if ( ( pm->ps->weapon == WP_GRENADE_LAUNCHER || pm->ps->weapon == WP_GRENADE_PINEAPPLE || pm->ps->weapon == WP_DYNAMITE ) && pm->ps->grenadeTimeLeft > 0 ) {
4071 				pm->ps->eFlags &= ~EF_ZOOMING;
4072 			}
4073 		}
4074 	}
4075 
4076 
4077 	if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION && pm->ps->pm_type != PM_NOCLIP ) {
4078 		// check if zooming
4079 		if ( !( pm->cmd.wbuttons & WBUTTON_ZOOM ) ) {
4080 			if ( pm->cmd.buttons & BUTTON_ATTACK ) {
4081 				// check for ammo
4082 				if ( PM_WeaponAmmoAvailable( pm->ps->weapon ) ) {
4083 					// all clear, fire!
4084 					pm->ps->eFlags |= EF_FIRING;
4085 				}
4086 			}
4087 		}
4088 	}
4089 
4090 
4091 	// clear the respawned flag if attack and use are cleared
4092 	if ( pm->ps->stats[STAT_HEALTH] > 0 &&
4093 		 !( pm->cmd.buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) ) {
4094 		pm->ps->pm_flags &= ~PMF_RESPAWNED;
4095 	}
4096 
4097 	// if talk button is down, dissallow all other input
4098 	// this is to prevent any possible intercept proxy from
4099 	// adding fake talk balloons
4100 	if ( pmove->cmd.buttons & BUTTON_TALK ) {
4101 		// keep the talk button set tho for when the cmd.serverTime > 66 msec
4102 		// and the same cmd is used multiple times in Pmove
4103 		pmove->cmd.buttons = BUTTON_TALK;
4104 		pmove->cmd.wbuttons = 0;
4105 		pmove->cmd.forwardmove = 0;
4106 		pmove->cmd.rightmove = 0;
4107 		pmove->cmd.upmove = 0;
4108 	}
4109 
4110 	// clear all pmove local vars
4111 	memset( &pml, 0, sizeof( pml ) );
4112 
4113 	// determine the time
4114 	pml.msec = pmove->cmd.serverTime - pm->ps->commandTime;
4115 	if ( !isDummy ) {
4116 		if ( pml.msec < 1 ) {
4117 			pml.msec = 1;
4118 		} else if ( pml.msec > 200 ) {
4119 			pml.msec = 200;
4120 		}
4121 	}
4122 	pm->ps->commandTime = pmove->cmd.serverTime;
4123 
4124 	// save old org in case we get stuck
4125 	VectorCopy( pm->ps->origin, pml.previous_origin );
4126 
4127 	// save old velocity for crashlanding
4128 	VectorCopy( pm->ps->velocity, pml.previous_velocity );
4129 
4130 	pml.frametime = pml.msec * 0.001;
4131 
4132 	// update the viewangles
4133 	// Ridah
4134 	if ( !isDummy ) {
4135 		// done.
4136 		if ( !( pm->ps->pm_flags & PMF_LIMBO ) ) { // JPW NERVE
4137 			PM_UpdateViewAngles( pm->ps, &pm->cmd, pm->trace ); //----(SA)	modified
4138 
4139 		}
4140 	}
4141 	AngleVectors( pm->ps->viewangles, pml.forward, pml.right, pml.up );
4142 
4143 	if ( pm->cmd.upmove < 10 ) {
4144 		// not holding jump
4145 		pm->ps->pm_flags &= ~PMF_JUMP_HELD;
4146 	}
4147 
4148 	// decide if backpedaling animations should be used
4149 	if ( pm->cmd.forwardmove < 0 ) {
4150 		pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
4151 	} else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
4152 		pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;
4153 	}
4154 
4155 	if ( pm->ps->pm_type >= PM_DEAD || pm->ps->pm_flags & PMF_LIMBO ) {         // DHM - Nerve
4156 		pm->cmd.forwardmove = 0;
4157 		pm->cmd.rightmove = 0;
4158 		pm->cmd.upmove = 0;
4159 	}
4160 
4161 	if ( ( pm->ps->pm_type == PM_SPECTATOR ) || ( pm->ps->pm_flags & PMF_LIMBO ) ) { // JPW NERVE for limbo mode chase
4162 		PM_CheckDuck();
4163 		PM_FlyMove();
4164 		PM_DropTimers();
4165 		return;
4166 	}
4167 
4168 	if ( pm->ps->pm_type == PM_NOCLIP ) {
4169 		PM_NoclipMove();
4170 		PM_DropTimers();
4171 		return;
4172 	}
4173 
4174 	if ( pm->ps->pm_type == PM_FREEZE ) {
4175 		return;     // no movement at all
4176 	}
4177 
4178 	if ( pm->ps->pm_type == PM_INTERMISSION ) {
4179 		return;     // no movement at all
4180 	}
4181 
4182 	// set watertype, and waterlevel
4183 	PM_SetWaterLevel();
4184 	pml.previous_waterlevel = pmove->waterlevel;
4185 
4186 	// set mins, maxs, and viewheight
4187 	PM_CheckDuck();
4188 
4189 	// set groundentity
4190 	PM_GroundTrace();
4191 
4192 	if ( pm->ps->pm_type == PM_DEAD ) {
4193 		PM_DeadMove();
4194 	}
4195 
4196 	// Ridah, ladders
4197 	PM_CheckLadderMove();
4198 
4199 	if ( !isDummy ) {
4200 		PM_DropTimers();
4201 	}
4202 
4203 	if ( pm->ps->powerups[PW_FLIGHT] ) {
4204 		// flight powerup doesn't allow jump and has different friction
4205 		PM_FlyMove();
4206 // RF, removed grapple flag since it's not used
4207 #if 0
4208 	} else if ( pm->ps->pm_flags & PMF_GRAPPLE_PULL ) {
4209 		PM_GrappleMove();
4210 		// We can wiggle a bit
4211 		PM_AirMove();
4212 #endif
4213 		// Ridah, ladders
4214 	} else if ( pml.ladder ) {
4215 		PM_LadderMove();
4216 		// done.
4217 	} else if ( pm->ps->pm_flags & PMF_TIME_WATERJUMP ) {
4218 		PM_WaterJumpMove();
4219 	} else if ( pm->waterlevel > 1 ) {
4220 		// swimming
4221 		PM_WaterMove();
4222 	} else if ( pml.walking ) {
4223 		// walking on ground
4224 		PM_WalkMove();
4225 	} else {
4226 		// airborne
4227 		PM_AirMove();
4228 	}
4229 
4230 
4231 	PM_Sprint();
4232 
4233 
4234 	// Ridah
4235 	if ( !isDummy ) {
4236 		// done.
4237 		PM_Animate();
4238 	}
4239 
4240 	// set groundentity, watertype, and waterlevel
4241 	PM_GroundTrace();
4242 	PM_SetWaterLevel();
4243 
4244 	// Ridah
4245 	if ( !isDummy ) {
4246 		// done.
4247 
4248 		// weapons
4249 		PM_Weapon();
4250 
4251 		// footstep events / legs animations
4252 		PM_Footsteps();
4253 
4254 		// entering / leaving water splashes
4255 		PM_WaterEvents();
4256 
4257 		// snap some parts of playerstate to save network bandwidth
4258 		trap_SnapVector( pm->ps->velocity );
4259 //		SnapVector( pm->ps->velocity );
4260 
4261 		// Ridah
4262 	}
4263 	// done.
4264 }
4265 
4266 
4267 /*
4268 ================
4269 Pmove
4270 
4271 Can be called by either the server or the client
4272 ================
4273 */
4274 int Pmove( pmove_t *pmove ) {
4275 	int finalTime;
4276 
4277 	// Ridah
4278 	if ( pmove->ps->eFlags & EF_DUMMY_PMOVE ) {
4279 		PmoveSingle( pmove );
4280 		return ( 0 );
4281 	} else if ( pmove->ps->pm_flags & PMF_IGNORE_INPUT ) {
4282 		pmove->cmd.forwardmove = 0;
4283 		pmove->cmd.rightmove = 0;
4284 		pmove->cmd.upmove = 0;
4285 		pmove->cmd.buttons = 0;
4286 		pmove->cmd.wbuttons = 0;
4287 		pmove->cmd.wolfkick = 0;
4288 	}
4289 	// done.
4290 
4291 	finalTime = pmove->cmd.serverTime;
4292 
4293 	if ( finalTime < pmove->ps->commandTime ) {
4294 		return ( 0 ); // should not happen
4295 	}
4296 
4297 	if ( finalTime > pmove->ps->commandTime + 1000 ) {
4298 		pmove->ps->commandTime = finalTime - 1000;
4299 	}
4300 
4301 	// RF, after a loadgame, prevent huge pmove's
4302 	if ( pmove->ps->pm_flags & PMF_TIME_LOAD ) {
4303 		if ( !pmove->ps->aiChar ) {
4304 			if ( finalTime - pmove->ps->commandTime > 50 ) {
4305 				pmove->ps->commandTime = finalTime - 50;
4306 			}
4307 		} else {
4308 			if ( finalTime - pmove->ps->commandTime > 50 ) {
4309 				pmove->ps->commandTime = finalTime - 50;
4310 			}
4311 		}
4312 	}
4313 
4314 	pmove->ps->pmove_framecount = ( pmove->ps->pmove_framecount + 1 ) & ( ( 1 << PS_PMOVEFRAMECOUNTBITS ) - 1 );
4315 
4316 	// RF
4317 	pm = pmove;
4318 	PM_AdjustAimSpreadScale();
4319 
4320 //	startedTorsoAnim = -1;
4321 //	startedLegAnim = -1;
4322 
4323 	// chop the move up if it is too long, to prevent framerate
4324 	// dependent behavior
4325 	while ( pmove->ps->commandTime != finalTime ) {
4326 		int msec;
4327 
4328 		msec = finalTime - pmove->ps->commandTime;
4329 
4330 		if ( pmove->pmove_fixed ) {
4331 			if ( msec > pmove->pmove_msec ) {
4332 				msec = pmove->pmove_msec;
4333 			}
4334 		} else {
4335 			if ( msec > 66 ) {
4336 				msec = 66;
4337 			}
4338 		}
4339 		pmove->cmd.serverTime = pmove->ps->commandTime + msec;
4340 		PmoveSingle( pmove );
4341 
4342 		if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) {
4343 			pmove->cmd.upmove = 20;
4344 		}
4345 	}
4346 
4347 	//PM_CheckStuck();
4348 
4349 	if ( ( pm->ps->stats[STAT_HEALTH] <= 0 || pm->ps->pm_type == PM_DEAD ) && pml.groundTrace.surfaceFlags & SURF_MONSTERSLICK ) {
4350 		return ( pml.groundTrace.surfaceFlags );
4351 	} else {
4352 		return ( 0 );
4353 	}
4354 
4355 }
4356