1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22 //
23 // bg_pmove.c -- both games player movement code
24 // takes a playerstate and a usercmd as input and returns a modifed playerstate
25
26 #include "../qcommon/q_shared.h"
27 #include "bg_public.h"
28 #include "bg_local.h"
29
30 pmove_t *pm;
31 pml_t pml;
32
33 // movement parameters
34 float pm_stopspeed = 100.0f;
35 float pm_duckScale = 0.25f;
36 float pm_swimScale = 0.50f;
37 float pm_wadeScale = 0.70f;
38
39 float pm_accelerate = 10.0f;
40 float pm_airaccelerate = 1.0f;
41 float pm_wateraccelerate = 4.0f;
42 float pm_flyaccelerate = 8.0f;
43
44 float pm_friction = 6.0f;
45 float pm_waterfriction = 1.0f;
46 float pm_flightfriction = 3.0f;
47 float pm_spectatorfriction = 5.0f;
48
49 int c_pmove = 0;
50
51
52 /*
53 ===============
54 PM_AddEvent
55
56 ===============
57 */
PM_AddEvent(int newEvent)58 void PM_AddEvent( int newEvent ) {
59 BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
60 }
61
62 /*
63 ===============
64 PM_AddTouchEnt
65 ===============
66 */
PM_AddTouchEnt(int entityNum)67 void PM_AddTouchEnt( int entityNum ) {
68 int i;
69
70 if ( entityNum == ENTITYNUM_WORLD ) {
71 return;
72 }
73 if ( pm->numtouch == MAXTOUCH ) {
74 return;
75 }
76
77 // see if it is already added
78 for ( i = 0 ; i < pm->numtouch ; i++ ) {
79 if ( pm->touchents[ i ] == entityNum ) {
80 return;
81 }
82 }
83
84 // add it
85 pm->touchents[pm->numtouch] = entityNum;
86 pm->numtouch++;
87 }
88
89 /*
90 ===================
91 PM_StartTorsoAnim
92 ===================
93 */
PM_StartTorsoAnim(int anim)94 static void PM_StartTorsoAnim( int anim ) {
95 if ( pm->ps->pm_type >= PM_DEAD ) {
96 return;
97 }
98 pm->ps->torsoAnim = ( ( pm->ps->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
99 | anim;
100 }
PM_StartLegsAnim(int anim)101 static void PM_StartLegsAnim( int anim ) {
102 if ( pm->ps->pm_type >= PM_DEAD ) {
103 return;
104 }
105 if ( pm->ps->legsTimer > 0 ) {
106 return; // a high priority animation is running
107 }
108 pm->ps->legsAnim = ( ( pm->ps->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
109 | anim;
110 }
111
PM_ContinueLegsAnim(int anim)112 static void PM_ContinueLegsAnim( int anim ) {
113 if ( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == anim ) {
114 return;
115 }
116 if ( pm->ps->legsTimer > 0 ) {
117 return; // a high priority animation is running
118 }
119 PM_StartLegsAnim( anim );
120 }
121
PM_ContinueTorsoAnim(int anim)122 static void PM_ContinueTorsoAnim( int anim ) {
123 if ( ( pm->ps->torsoAnim & ~ANIM_TOGGLEBIT ) == anim ) {
124 return;
125 }
126 if ( pm->ps->torsoTimer > 0 ) {
127 return; // a high priority animation is running
128 }
129 PM_StartTorsoAnim( anim );
130 }
131
PM_ForceLegsAnim(int anim)132 static void PM_ForceLegsAnim( int anim ) {
133 pm->ps->legsTimer = 0;
134 PM_StartLegsAnim( anim );
135 }
136
137
138 /*
139 ==================
140 PM_ClipVelocity
141
142 Slide off of the impacting surface
143 ==================
144 */
PM_ClipVelocity(vec3_t in,vec3_t normal,vec3_t out,float overbounce)145 void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) {
146 float backoff;
147 float change;
148 int i;
149
150 backoff = DotProduct (in, normal);
151
152 if ( backoff < 0 ) {
153 backoff *= overbounce;
154 } else {
155 backoff /= overbounce;
156 }
157
158 for ( i=0 ; i<3 ; i++ ) {
159 change = normal[i]*backoff;
160 out[i] = in[i] - change;
161 }
162 }
163
164
165 /*
166 ==================
167 PM_Friction
168
169 Handles both ground friction and water friction
170 ==================
171 */
PM_Friction(void)172 static void PM_Friction( void ) {
173 vec3_t vec;
174 float *vel;
175 float speed, newspeed, control;
176 float drop;
177
178 vel = pm->ps->velocity;
179
180 VectorCopy( vel, vec );
181 if ( pml.walking ) {
182 vec[2] = 0; // ignore slope movement
183 }
184
185 speed = VectorLength(vec);
186 if (speed < 1) {
187 vel[0] = 0;
188 vel[1] = 0; // allow sinking underwater
189 // FIXME: still have z friction underwater?
190 return;
191 }
192
193 drop = 0;
194
195 // apply ground friction
196 if ( pm->waterlevel <= 1 ) {
197 if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) {
198 // if getting knocked back, no friction
199 if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) {
200 control = speed < pm_stopspeed ? pm_stopspeed : speed;
201 drop += control*pm_friction*pml.frametime;
202 }
203 }
204 }
205
206 // apply water friction even if just wading
207 if ( pm->waterlevel ) {
208 drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
209 }
210
211 // apply flying friction
212 if ( pm->ps->powerups[PW_FLIGHT]) {
213 drop += speed*pm_flightfriction*pml.frametime;
214 }
215
216 if ( pm->ps->pm_type == PM_SPECTATOR) {
217 drop += speed*pm_spectatorfriction*pml.frametime;
218 }
219
220 // scale the velocity
221 newspeed = speed - drop;
222 if (newspeed < 0) {
223 newspeed = 0;
224 }
225 newspeed /= speed;
226
227 vel[0] = vel[0] * newspeed;
228 vel[1] = vel[1] * newspeed;
229 vel[2] = vel[2] * newspeed;
230 }
231
232
233 /*
234 ==============
235 PM_Accelerate
236
237 Handles user intended acceleration
238 ==============
239 */
PM_Accelerate(vec3_t wishdir,float wishspeed,float accel)240 static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) {
241 #if 1
242 // q2 style
243 int i;
244 float addspeed, accelspeed, currentspeed;
245
246 currentspeed = DotProduct (pm->ps->velocity, wishdir);
247 addspeed = wishspeed - currentspeed;
248 if (addspeed <= 0) {
249 return;
250 }
251 accelspeed = accel*pml.frametime*wishspeed;
252 if (accelspeed > addspeed) {
253 accelspeed = addspeed;
254 }
255
256 for (i=0 ; i<3 ; i++) {
257 pm->ps->velocity[i] += accelspeed*wishdir[i];
258 }
259 #else
260 // proper way (avoids strafe jump maxspeed bug), but feels bad
261 vec3_t wishVelocity;
262 vec3_t pushDir;
263 float pushLen;
264 float canPush;
265
266 VectorScale( wishdir, wishspeed, wishVelocity );
267 VectorSubtract( wishVelocity, pm->ps->velocity, pushDir );
268 pushLen = VectorNormalize( pushDir );
269
270 canPush = accel*pml.frametime*wishspeed;
271 if (canPush > pushLen) {
272 canPush = pushLen;
273 }
274
275 VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity );
276 #endif
277 }
278
279
280
281 /*
282 ============
283 PM_CmdScale
284
285 Returns the scale factor to apply to cmd movements
286 This allows the clients to use axial -127 to 127 values for all directions
287 without getting a sqrt(2) distortion in speed.
288 ============
289 */
PM_CmdScale(usercmd_t * cmd)290 static float PM_CmdScale( usercmd_t *cmd ) {
291 int max;
292 float total;
293 float scale;
294
295 max = abs( cmd->forwardmove );
296 if ( abs( cmd->rightmove ) > max ) {
297 max = abs( cmd->rightmove );
298 }
299 if ( abs( cmd->upmove ) > max ) {
300 max = abs( cmd->upmove );
301 }
302 if ( !max ) {
303 return 0;
304 }
305
306 total = sqrt( cmd->forwardmove * cmd->forwardmove
307 + cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove );
308 scale = (float)pm->ps->speed * max / ( 127.0 * total );
309
310 return scale;
311 }
312
313
314 /*
315 ================
316 PM_SetMovementDir
317
318 Determine the rotation of the legs reletive
319 to the facing dir
320 ================
321 */
PM_SetMovementDir(void)322 static void PM_SetMovementDir( void ) {
323 if ( pm->cmd.forwardmove || pm->cmd.rightmove ) {
324 if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) {
325 pm->ps->movementDir = 0;
326 } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) {
327 pm->ps->movementDir = 1;
328 } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) {
329 pm->ps->movementDir = 2;
330 } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) {
331 pm->ps->movementDir = 3;
332 } else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) {
333 pm->ps->movementDir = 4;
334 } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) {
335 pm->ps->movementDir = 5;
336 } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) {
337 pm->ps->movementDir = 6;
338 } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) {
339 pm->ps->movementDir = 7;
340 }
341 } else {
342 // if they aren't actively going directly sideways,
343 // change the animation to the diagonal so they
344 // don't stop too crooked
345 if ( pm->ps->movementDir == 2 ) {
346 pm->ps->movementDir = 1;
347 } else if ( pm->ps->movementDir == 6 ) {
348 pm->ps->movementDir = 7;
349 }
350 }
351 }
352
353
354 /*
355 =============
356 PM_CheckJump
357 =============
358 */
PM_CheckJump(void)359 static qboolean PM_CheckJump( void ) {
360 if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
361 return qfalse; // don't allow jump until all buttons are up
362 }
363
364 if ( pm->cmd.upmove < 10 ) {
365 // not holding jump
366 return qfalse;
367 }
368
369 // must wait for jump to be released
370 if ( pm->ps->pm_flags & PMF_JUMP_HELD ) {
371 // clear upmove so cmdscale doesn't lower running speed
372 pm->cmd.upmove = 0;
373 return qfalse;
374 }
375
376 pml.groundPlane = qfalse; // jumping away
377 pml.walking = qfalse;
378 pm->ps->pm_flags |= PMF_JUMP_HELD;
379
380 pm->ps->groundEntityNum = ENTITYNUM_NONE;
381 pm->ps->velocity[2] = JUMP_VELOCITY;
382 PM_AddEvent( EV_JUMP );
383
384 if ( pm->cmd.forwardmove >= 0 ) {
385 PM_ForceLegsAnim( LEGS_JUMP );
386 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
387 } else {
388 PM_ForceLegsAnim( LEGS_JUMPB );
389 pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
390 }
391
392 return qtrue;
393 }
394
395 /*
396 =============
397 PM_CheckWaterJump
398 =============
399 */
PM_CheckWaterJump(void)400 static qboolean PM_CheckWaterJump( void ) {
401 vec3_t spot;
402 int cont;
403 vec3_t flatforward;
404
405 if (pm->ps->pm_time) {
406 return qfalse;
407 }
408
409 // check for water jump
410 if ( pm->waterlevel != 2 ) {
411 return qfalse;
412 }
413
414 flatforward[0] = pml.forward[0];
415 flatforward[1] = pml.forward[1];
416 flatforward[2] = 0;
417 VectorNormalize (flatforward);
418
419 VectorMA (pm->ps->origin, 30, flatforward, spot);
420 spot[2] += 4;
421 cont = pm->pointcontents (spot, pm->ps->clientNum );
422 if ( !(cont & CONTENTS_SOLID) ) {
423 return qfalse;
424 }
425
426 spot[2] += 16;
427 cont = pm->pointcontents (spot, pm->ps->clientNum );
428 if ( cont ) {
429 return qfalse;
430 }
431
432 // jump out of water
433 VectorScale (pml.forward, 200, pm->ps->velocity);
434 pm->ps->velocity[2] = 350;
435
436 pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
437 pm->ps->pm_time = 2000;
438
439 return qtrue;
440 }
441
442 //============================================================================
443
444
445 /*
446 ===================
447 PM_WaterJumpMove
448
449 Flying out of the water
450 ===================
451 */
PM_WaterJumpMove(void)452 static void PM_WaterJumpMove( void ) {
453 // waterjump has no control, but falls
454
455 PM_StepSlideMove( qtrue );
456
457 pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
458 if (pm->ps->velocity[2] < 0) {
459 // cancel as soon as we are falling down again
460 pm->ps->pm_flags &= ~PMF_ALL_TIMES;
461 pm->ps->pm_time = 0;
462 }
463 }
464
465 /*
466 ===================
467 PM_WaterMove
468
469 ===================
470 */
PM_WaterMove(void)471 static void PM_WaterMove( void ) {
472 int i;
473 vec3_t wishvel;
474 float wishspeed;
475 vec3_t wishdir;
476 float scale;
477 float vel;
478
479 if ( PM_CheckWaterJump() ) {
480 PM_WaterJumpMove();
481 return;
482 }
483 #if 0
484 // jump = head for surface
485 if ( pm->cmd.upmove >= 10 ) {
486 if (pm->ps->velocity[2] > -300) {
487 if ( pm->watertype == CONTENTS_WATER ) {
488 pm->ps->velocity[2] = 100;
489 } else if (pm->watertype == CONTENTS_SLIME) {
490 pm->ps->velocity[2] = 80;
491 } else {
492 pm->ps->velocity[2] = 50;
493 }
494 }
495 }
496 #endif
497 PM_Friction ();
498
499 scale = PM_CmdScale( &pm->cmd );
500 //
501 // user intentions
502 //
503 if ( !scale ) {
504 wishvel[0] = 0;
505 wishvel[1] = 0;
506 wishvel[2] = -60; // sink towards bottom
507 } else {
508 for (i=0 ; i<3 ; i++)
509 wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
510
511 wishvel[2] += scale * pm->cmd.upmove;
512 }
513
514 VectorCopy (wishvel, wishdir);
515 wishspeed = VectorNormalize(wishdir);
516
517 if ( wishspeed > pm->ps->speed * pm_swimScale ) {
518 wishspeed = pm->ps->speed * pm_swimScale;
519 }
520
521 PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate);
522
523 // make sure we can go up slopes easily under water
524 if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) {
525 vel = VectorLength(pm->ps->velocity);
526 // slide along the ground plane
527 PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
528 pm->ps->velocity, OVERCLIP );
529
530 VectorNormalize(pm->ps->velocity);
531 VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
532 }
533
534 PM_SlideMove( qfalse );
535 }
536
537 #ifdef MISSIONPACK
538 /*
539 ===================
540 PM_InvulnerabilityMove
541
542 Only with the invulnerability powerup
543 ===================
544 */
PM_InvulnerabilityMove(void)545 static void PM_InvulnerabilityMove( void ) {
546 pm->cmd.forwardmove = 0;
547 pm->cmd.rightmove = 0;
548 pm->cmd.upmove = 0;
549 VectorClear(pm->ps->velocity);
550 }
551 #endif
552
553 /*
554 ===================
555 PM_FlyMove
556
557 Only with the flight powerup
558 ===================
559 */
PM_FlyMove(void)560 static void PM_FlyMove( void ) {
561 int i;
562 vec3_t wishvel;
563 float wishspeed;
564 vec3_t wishdir;
565 float scale;
566
567 // normal slowdown
568 PM_Friction ();
569
570 scale = PM_CmdScale( &pm->cmd );
571 //
572 // user intentions
573 //
574 if ( !scale ) {
575 wishvel[0] = 0;
576 wishvel[1] = 0;
577 wishvel[2] = 0;
578 } else {
579 for (i=0 ; i<3 ; i++) {
580 wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
581 }
582
583 wishvel[2] += scale * pm->cmd.upmove;
584 }
585
586 VectorCopy (wishvel, wishdir);
587 wishspeed = VectorNormalize(wishdir);
588
589 PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);
590
591 PM_StepSlideMove( qfalse );
592 }
593
594
595 /*
596 ===================
597 PM_AirMove
598
599 ===================
600 */
PM_AirMove(void)601 static void PM_AirMove( void ) {
602 int i;
603 vec3_t wishvel;
604 float fmove, smove;
605 vec3_t wishdir;
606 float wishspeed;
607 float scale;
608 usercmd_t cmd;
609
610 PM_Friction();
611
612 fmove = pm->cmd.forwardmove;
613 smove = pm->cmd.rightmove;
614
615 cmd = pm->cmd;
616 scale = PM_CmdScale( &cmd );
617
618 // set the movementDir so clients can rotate the legs for strafing
619 PM_SetMovementDir();
620
621 // project moves down to flat plane
622 pml.forward[2] = 0;
623 pml.right[2] = 0;
624 VectorNormalize (pml.forward);
625 VectorNormalize (pml.right);
626
627 for ( i = 0 ; i < 2 ; i++ ) {
628 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
629 }
630 wishvel[2] = 0;
631
632 VectorCopy (wishvel, wishdir);
633 wishspeed = VectorNormalize(wishdir);
634 wishspeed *= scale;
635
636 // not on ground, so little effect on velocity
637 PM_Accelerate (wishdir, wishspeed, pm_airaccelerate);
638
639 // we may have a ground plane that is very steep, even
640 // though we don't have a groundentity
641 // slide along the steep plane
642 if ( pml.groundPlane ) {
643 PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
644 pm->ps->velocity, OVERCLIP );
645 }
646
647 #if 0
648 //ZOID: If we are on the grapple, try stair-stepping
649 //this allows a player to use the grapple to pull himself
650 //over a ledge
651 if (pm->ps->pm_flags & PMF_GRAPPLE_PULL)
652 PM_StepSlideMove ( qtrue );
653 else
654 PM_SlideMove ( qtrue );
655 #endif
656
657 PM_StepSlideMove ( qtrue );
658 }
659
660 /*
661 ===================
662 PM_GrappleMove
663
664 ===================
665 */
PM_GrappleMove(void)666 static void PM_GrappleMove( void ) {
667 vec3_t vel, v;
668 float vlen;
669
670 VectorScale(pml.forward, -16, v);
671 VectorAdd(pm->ps->grapplePoint, v, v);
672 VectorSubtract(v, pm->ps->origin, vel);
673 vlen = VectorLength(vel);
674 VectorNormalize( vel );
675
676 if (vlen <= 100)
677 VectorScale(vel, 10 * vlen, vel);
678 else
679 VectorScale(vel, 800, vel);
680
681 VectorCopy(vel, pm->ps->velocity);
682
683 pml.groundPlane = qfalse;
684 }
685
686 /*
687 ===================
688 PM_WalkMove
689
690 ===================
691 */
PM_WalkMove(void)692 static void PM_WalkMove( void ) {
693 int i;
694 vec3_t wishvel;
695 float fmove, smove;
696 vec3_t wishdir;
697 float wishspeed;
698 float scale;
699 usercmd_t cmd;
700 float accelerate;
701 float vel;
702
703 if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
704 // begin swimming
705 PM_WaterMove();
706 return;
707 }
708
709
710 if ( PM_CheckJump () ) {
711 // jumped away
712 if ( pm->waterlevel > 1 ) {
713 PM_WaterMove();
714 } else {
715 PM_AirMove();
716 }
717 return;
718 }
719
720 PM_Friction ();
721
722 fmove = pm->cmd.forwardmove;
723 smove = pm->cmd.rightmove;
724
725 cmd = pm->cmd;
726 scale = PM_CmdScale( &cmd );
727
728 // set the movementDir so clients can rotate the legs for strafing
729 PM_SetMovementDir();
730
731 // project moves down to flat plane
732 pml.forward[2] = 0;
733 pml.right[2] = 0;
734
735 // project the forward and right directions onto the ground plane
736 PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP );
737 PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP );
738 //
739 VectorNormalize (pml.forward);
740 VectorNormalize (pml.right);
741
742 for ( i = 0 ; i < 3 ; i++ ) {
743 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
744 }
745 // when going up or down slopes the wish velocity should Not be zero
746 // wishvel[2] = 0;
747
748 VectorCopy (wishvel, wishdir);
749 wishspeed = VectorNormalize(wishdir);
750 wishspeed *= scale;
751
752 // clamp the speed lower if ducking
753 if ( pm->ps->pm_flags & PMF_DUCKED ) {
754 if ( wishspeed > pm->ps->speed * pm_duckScale ) {
755 wishspeed = pm->ps->speed * pm_duckScale;
756 }
757 }
758
759 // clamp the speed lower if wading or walking on the bottom
760 if ( pm->waterlevel ) {
761 float waterScale;
762
763 waterScale = pm->waterlevel / 3.0;
764 waterScale = 1.0 - ( 1.0 - pm_swimScale ) * waterScale;
765 if ( wishspeed > pm->ps->speed * waterScale ) {
766 wishspeed = pm->ps->speed * waterScale;
767 }
768 }
769
770 // when a player gets hit, they temporarily lose
771 // full control, which allows them to be moved a bit
772 if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
773 accelerate = pm_airaccelerate;
774 } else {
775 accelerate = pm_accelerate;
776 }
777
778 PM_Accelerate (wishdir, wishspeed, accelerate);
779
780 //Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
781 //Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity));
782
783 if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
784 pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
785 } else {
786 // don't reset the z velocity for slopes
787 // pm->ps->velocity[2] = 0;
788 }
789
790 vel = VectorLength(pm->ps->velocity);
791
792 // slide along the ground plane
793 PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
794 pm->ps->velocity, OVERCLIP );
795
796 // don't decrease velocity when going up or down a slope
797 VectorNormalize(pm->ps->velocity);
798 VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
799
800 // don't do anything if standing still
801 if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) {
802 return;
803 }
804
805 PM_StepSlideMove( qfalse );
806
807 //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity));
808
809 }
810
811
812 /*
813 ==============
814 PM_DeadMove
815 ==============
816 */
PM_DeadMove(void)817 static void PM_DeadMove( void ) {
818 float forward;
819
820 if ( !pml.walking ) {
821 return;
822 }
823
824 // extra friction
825
826 forward = VectorLength (pm->ps->velocity);
827 forward -= 20;
828 if ( forward <= 0 ) {
829 VectorClear (pm->ps->velocity);
830 } else {
831 VectorNormalize (pm->ps->velocity);
832 VectorScale (pm->ps->velocity, forward, pm->ps->velocity);
833 }
834 }
835
836
837 /*
838 ===============
839 PM_NoclipMove
840 ===============
841 */
PM_NoclipMove(void)842 static void PM_NoclipMove( void ) {
843 float speed, drop, friction, control, newspeed;
844 int i;
845 vec3_t wishvel;
846 float fmove, smove;
847 vec3_t wishdir;
848 float wishspeed;
849 float scale;
850
851 pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
852
853 // friction
854
855 speed = VectorLength (pm->ps->velocity);
856 if (speed < 1)
857 {
858 VectorCopy (vec3_origin, pm->ps->velocity);
859 }
860 else
861 {
862 drop = 0;
863
864 friction = pm_friction*1.5; // extra friction
865 control = speed < pm_stopspeed ? pm_stopspeed : speed;
866 drop += control*friction*pml.frametime;
867
868 // scale the velocity
869 newspeed = speed - drop;
870 if (newspeed < 0)
871 newspeed = 0;
872 newspeed /= speed;
873
874 VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity);
875 }
876
877 // accelerate
878 scale = PM_CmdScale( &pm->cmd );
879
880 fmove = pm->cmd.forwardmove;
881 smove = pm->cmd.rightmove;
882
883 for (i=0 ; i<3 ; i++)
884 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
885 wishvel[2] += pm->cmd.upmove;
886
887 VectorCopy (wishvel, wishdir);
888 wishspeed = VectorNormalize(wishdir);
889 wishspeed *= scale;
890
891 PM_Accelerate( wishdir, wishspeed, pm_accelerate );
892
893 // move
894 VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin);
895 }
896
897 //============================================================================
898
899 /*
900 ================
901 PM_FootstepForSurface
902
903 Returns an event number apropriate for the groundsurface
904 ================
905 */
PM_FootstepForSurface(void)906 static int PM_FootstepForSurface( void ) {
907 if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS ) {
908 return 0;
909 }
910 if ( pml.groundTrace.surfaceFlags & SURF_METALSTEPS ) {
911 return EV_FOOTSTEP_METAL;
912 }
913 return EV_FOOTSTEP;
914 }
915
916
917 /*
918 =================
919 PM_CrashLand
920
921 Check for hard landings that generate sound events
922 =================
923 */
PM_CrashLand(void)924 static void PM_CrashLand( void ) {
925 float delta;
926 float dist;
927 float vel, acc;
928 float t;
929 float a, b, c, den;
930
931 // decide which landing animation to use
932 if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) {
933 PM_ForceLegsAnim( LEGS_LANDB );
934 } else {
935 PM_ForceLegsAnim( LEGS_LAND );
936 }
937
938 pm->ps->legsTimer = TIMER_LAND;
939
940 // calculate the exact velocity on landing
941 dist = pm->ps->origin[2] - pml.previous_origin[2];
942 vel = pml.previous_velocity[2];
943 acc = -pm->ps->gravity;
944
945 a = acc / 2;
946 b = vel;
947 c = -dist;
948
949 den = b * b - 4 * a * c;
950 if ( den < 0 ) {
951 return;
952 }
953 t = (-b - sqrt( den ) ) / ( 2 * a );
954
955 delta = vel + t * acc;
956 delta = delta*delta * 0.0001;
957
958 // ducking while falling doubles damage
959 if ( pm->ps->pm_flags & PMF_DUCKED ) {
960 delta *= 2;
961 }
962
963 // never take falling damage if completely underwater
964 if ( pm->waterlevel == 3 ) {
965 return;
966 }
967
968 // reduce falling damage if there is standing water
969 if ( pm->waterlevel == 2 ) {
970 delta *= 0.25;
971 }
972 if ( pm->waterlevel == 1 ) {
973 delta *= 0.5;
974 }
975
976 if ( delta < 1 ) {
977 return;
978 }
979
980 // create a local entity event to play the sound
981
982 // SURF_NODAMAGE is used for bounce pads where you don't ever
983 // want to take damage or play a crunch sound
984 if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) {
985 if ( delta > 60 ) {
986 PM_AddEvent( EV_FALL_FAR );
987 } else if ( delta > 40 ) {
988 // this is a pain grunt, so don't play it if dead
989 if ( pm->ps->stats[STAT_HEALTH] > 0 ) {
990 PM_AddEvent( EV_FALL_MEDIUM );
991 }
992 } else if ( delta > 7 ) {
993 PM_AddEvent( EV_FALL_SHORT );
994 } else {
995 PM_AddEvent( PM_FootstepForSurface() );
996 }
997 }
998
999 // start footstep cycle over
1000 pm->ps->bobCycle = 0;
1001 }
1002
1003 /*
1004 =============
1005 PM_CheckStuck
1006 =============
1007 */
1008 /*
1009 void PM_CheckStuck(void) {
1010 trace_t trace;
1011
1012 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask);
1013 if (trace.allsolid) {
1014 //int shit = qtrue;
1015 }
1016 }
1017 */
1018
1019 /*
1020 =============
1021 PM_CorrectAllSolid
1022 =============
1023 */
PM_CorrectAllSolid(trace_t * trace)1024 static int PM_CorrectAllSolid( trace_t *trace ) {
1025 int i, j, k;
1026 vec3_t point;
1027
1028 if ( pm->debugLevel ) {
1029 Com_Printf("%i:allsolid\n", c_pmove);
1030 }
1031
1032 // jitter around
1033 for (i = -1; i <= 1; i++) {
1034 for (j = -1; j <= 1; j++) {
1035 for (k = -1; k <= 1; k++) {
1036 VectorCopy(pm->ps->origin, point);
1037 point[0] += (float) i;
1038 point[1] += (float) j;
1039 point[2] += (float) k;
1040 pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
1041 if ( !trace->allsolid ) {
1042 point[0] = pm->ps->origin[0];
1043 point[1] = pm->ps->origin[1];
1044 point[2] = pm->ps->origin[2] - 0.25;
1045
1046 pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
1047 pml.groundTrace = *trace;
1048 return qtrue;
1049 }
1050 }
1051 }
1052 }
1053
1054 pm->ps->groundEntityNum = ENTITYNUM_NONE;
1055 pml.groundPlane = qfalse;
1056 pml.walking = qfalse;
1057
1058 return qfalse;
1059 }
1060
1061
1062 /*
1063 =============
1064 PM_GroundTraceMissed
1065
1066 The ground trace didn't hit a surface, so we are in freefall
1067 =============
1068 */
PM_GroundTraceMissed(void)1069 static void PM_GroundTraceMissed( void ) {
1070 trace_t trace;
1071 vec3_t point;
1072
1073 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) {
1074 // we just transitioned into freefall
1075 if ( pm->debugLevel ) {
1076 Com_Printf("%i:lift\n", c_pmove);
1077 }
1078
1079 // if they aren't in a jumping animation and the ground is a ways away, force into it
1080 // if we didn't do the trace, the player would be backflipping down staircases
1081 VectorCopy( pm->ps->origin, point );
1082 point[2] -= 64;
1083
1084 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
1085 if ( trace.fraction == 1.0 ) {
1086 if ( pm->cmd.forwardmove >= 0 ) {
1087 PM_ForceLegsAnim( LEGS_JUMP );
1088 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1089 } else {
1090 PM_ForceLegsAnim( LEGS_JUMPB );
1091 pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
1092 }
1093 }
1094 }
1095
1096 pm->ps->groundEntityNum = ENTITYNUM_NONE;
1097 pml.groundPlane = qfalse;
1098 pml.walking = qfalse;
1099 }
1100
1101
1102 /*
1103 =============
1104 PM_GroundTrace
1105 =============
1106 */
PM_GroundTrace(void)1107 static void PM_GroundTrace( void ) {
1108 vec3_t point;
1109 trace_t trace;
1110
1111 point[0] = pm->ps->origin[0];
1112 point[1] = pm->ps->origin[1];
1113 point[2] = pm->ps->origin[2] - 0.25;
1114
1115 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
1116 pml.groundTrace = trace;
1117
1118 // do something corrective if the trace starts in a solid...
1119 if ( trace.allsolid ) {
1120 if ( !PM_CorrectAllSolid(&trace) )
1121 return;
1122 }
1123
1124 // if the trace didn't hit anything, we are in free fall
1125 if ( trace.fraction == 1.0 ) {
1126 PM_GroundTraceMissed();
1127 pml.groundPlane = qfalse;
1128 pml.walking = qfalse;
1129 return;
1130 }
1131
1132 // check if getting thrown off the ground
1133 if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) {
1134 if ( pm->debugLevel ) {
1135 Com_Printf("%i:kickoff\n", c_pmove);
1136 }
1137 // go into jump animation
1138 if ( pm->cmd.forwardmove >= 0 ) {
1139 PM_ForceLegsAnim( LEGS_JUMP );
1140 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1141 } else {
1142 PM_ForceLegsAnim( LEGS_JUMPB );
1143 pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
1144 }
1145
1146 pm->ps->groundEntityNum = ENTITYNUM_NONE;
1147 pml.groundPlane = qfalse;
1148 pml.walking = qfalse;
1149 return;
1150 }
1151
1152 // slopes that are too steep will not be considered onground
1153 if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) {
1154 if ( pm->debugLevel ) {
1155 Com_Printf("%i:steep\n", c_pmove);
1156 }
1157 // FIXME: if they can't slide down the slope, let them
1158 // walk (sharp crevices)
1159 pm->ps->groundEntityNum = ENTITYNUM_NONE;
1160 pml.groundPlane = qtrue;
1161 pml.walking = qfalse;
1162 return;
1163 }
1164
1165 pml.groundPlane = qtrue;
1166 pml.walking = qtrue;
1167
1168 // hitting solid ground will end a waterjump
1169 if (pm->ps->pm_flags & PMF_TIME_WATERJUMP)
1170 {
1171 pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
1172 pm->ps->pm_time = 0;
1173 }
1174
1175 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
1176 // just hit the ground
1177 if ( pm->debugLevel ) {
1178 Com_Printf("%i:Land\n", c_pmove);
1179 }
1180
1181 PM_CrashLand();
1182
1183 // don't do landing time if we were just going down a slope
1184 if ( pml.previous_velocity[2] < -200 ) {
1185 // don't allow another jump for a little while
1186 pm->ps->pm_flags |= PMF_TIME_LAND;
1187 pm->ps->pm_time = 250;
1188 }
1189 }
1190
1191 pm->ps->groundEntityNum = trace.entityNum;
1192
1193 // don't reset the z velocity for slopes
1194 // pm->ps->velocity[2] = 0;
1195
1196 PM_AddTouchEnt( trace.entityNum );
1197 }
1198
1199
1200 /*
1201 =============
1202 PM_SetWaterLevel FIXME: avoid this twice? certainly if not moving
1203 =============
1204 */
PM_SetWaterLevel(void)1205 static void PM_SetWaterLevel( void ) {
1206 vec3_t point;
1207 int cont;
1208 int sample1;
1209 int sample2;
1210
1211 //
1212 // get waterlevel, accounting for ducking
1213 //
1214 pm->waterlevel = 0;
1215 pm->watertype = 0;
1216
1217 point[0] = pm->ps->origin[0];
1218 point[1] = pm->ps->origin[1];
1219 point[2] = pm->ps->origin[2] + MINS_Z + 1;
1220 cont = pm->pointcontents( point, pm->ps->clientNum );
1221
1222 if ( cont & MASK_WATER ) {
1223 sample2 = pm->ps->viewheight - MINS_Z;
1224 sample1 = sample2 / 2;
1225
1226 pm->watertype = cont;
1227 pm->waterlevel = 1;
1228 point[2] = pm->ps->origin[2] + MINS_Z + sample1;
1229 cont = pm->pointcontents (point, pm->ps->clientNum );
1230 if ( cont & MASK_WATER ) {
1231 pm->waterlevel = 2;
1232 point[2] = pm->ps->origin[2] + MINS_Z + sample2;
1233 cont = pm->pointcontents (point, pm->ps->clientNum );
1234 if ( cont & MASK_WATER ){
1235 pm->waterlevel = 3;
1236 }
1237 }
1238 }
1239
1240 }
1241
1242 /*
1243 ==============
1244 PM_CheckDuck
1245
1246 Sets mins, maxs, and pm->ps->viewheight
1247 ==============
1248 */
PM_CheckDuck(void)1249 static void PM_CheckDuck (void)
1250 {
1251 trace_t trace;
1252
1253 if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
1254 if ( pm->ps->pm_flags & PMF_INVULEXPAND ) {
1255 // invulnerability sphere has a 42 units radius
1256 VectorSet( pm->mins, -42, -42, -42 );
1257 VectorSet( pm->maxs, 42, 42, 42 );
1258 }
1259 else {
1260 VectorSet( pm->mins, -15, -15, MINS_Z );
1261 VectorSet( pm->maxs, 15, 15, 16 );
1262 }
1263 pm->ps->pm_flags |= PMF_DUCKED;
1264 pm->ps->viewheight = CROUCH_VIEWHEIGHT;
1265 return;
1266 }
1267 pm->ps->pm_flags &= ~PMF_INVULEXPAND;
1268
1269 pm->mins[0] = -15;
1270 pm->mins[1] = -15;
1271
1272 pm->maxs[0] = 15;
1273 pm->maxs[1] = 15;
1274
1275 pm->mins[2] = MINS_Z;
1276
1277 if (pm->ps->pm_type == PM_DEAD)
1278 {
1279 pm->maxs[2] = -8;
1280 pm->ps->viewheight = DEAD_VIEWHEIGHT;
1281 return;
1282 }
1283
1284 if (pm->cmd.upmove < 0)
1285 { // duck
1286 pm->ps->pm_flags |= PMF_DUCKED;
1287 }
1288 else
1289 { // stand up if possible
1290 if (pm->ps->pm_flags & PMF_DUCKED)
1291 {
1292 // try to stand up
1293 pm->maxs[2] = 32;
1294 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
1295 if (!trace.allsolid)
1296 pm->ps->pm_flags &= ~PMF_DUCKED;
1297 }
1298 }
1299
1300 if (pm->ps->pm_flags & PMF_DUCKED)
1301 {
1302 pm->maxs[2] = 16;
1303 pm->ps->viewheight = CROUCH_VIEWHEIGHT;
1304 }
1305 else
1306 {
1307 pm->maxs[2] = 32;
1308 pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
1309 }
1310 }
1311
1312
1313
1314 //===================================================================
1315
1316
1317 /*
1318 ===============
1319 PM_Footsteps
1320 ===============
1321 */
PM_Footsteps(void)1322 static void PM_Footsteps( void ) {
1323 float bobmove;
1324 int old;
1325 qboolean footstep;
1326
1327 //
1328 // calculate speed and cycle to be used for
1329 // all cyclic walking effects
1330 //
1331 pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0]
1332 + pm->ps->velocity[1] * pm->ps->velocity[1] );
1333
1334 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
1335
1336 if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
1337 PM_ContinueLegsAnim( LEGS_IDLECR );
1338 }
1339 // airborne leaves position in cycle intact, but doesn't advance
1340 if ( pm->waterlevel > 1 ) {
1341 PM_ContinueLegsAnim( LEGS_SWIM );
1342 }
1343 return;
1344 }
1345
1346 // if not trying to move
1347 if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) {
1348 if ( pm->xyspeed < 5 ) {
1349 pm->ps->bobCycle = 0; // start at beginning of cycle again
1350 if ( pm->ps->pm_flags & PMF_DUCKED ) {
1351 PM_ContinueLegsAnim( LEGS_IDLECR );
1352 } else {
1353 PM_ContinueLegsAnim( LEGS_IDLE );
1354 }
1355 }
1356 return;
1357 }
1358
1359
1360 footstep = qfalse;
1361
1362 if ( pm->ps->pm_flags & PMF_DUCKED ) {
1363 bobmove = 0.5; // ducked characters bob much faster
1364 if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
1365 PM_ContinueLegsAnim( LEGS_BACKCR );
1366 }
1367 else {
1368 PM_ContinueLegsAnim( LEGS_WALKCR );
1369 }
1370 // ducked characters never play footsteps
1371 /*
1372 } else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
1373 if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
1374 bobmove = 0.4; // faster speeds bob faster
1375 footstep = qtrue;
1376 } else {
1377 bobmove = 0.3;
1378 }
1379 PM_ContinueLegsAnim( LEGS_BACK );
1380 */
1381 } else {
1382 if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
1383 bobmove = 0.4f; // faster speeds bob faster
1384 if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
1385 PM_ContinueLegsAnim( LEGS_BACK );
1386 }
1387 else {
1388 PM_ContinueLegsAnim( LEGS_RUN );
1389 }
1390 footstep = qtrue;
1391 } else {
1392 bobmove = 0.3f; // walking bobs slow
1393 if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
1394 PM_ContinueLegsAnim( LEGS_BACKWALK );
1395 }
1396 else {
1397 PM_ContinueLegsAnim( LEGS_WALK );
1398 }
1399 }
1400 }
1401
1402 // check for footstep / splash sounds
1403 old = pm->ps->bobCycle;
1404 pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
1405
1406 // if we just crossed a cycle boundary, play an apropriate footstep event
1407 if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 ) {
1408 if ( pm->waterlevel == 0 ) {
1409 // on ground will only play sounds if running
1410 if ( footstep && !pm->noFootsteps ) {
1411 PM_AddEvent( PM_FootstepForSurface() );
1412 }
1413 } else if ( pm->waterlevel == 1 ) {
1414 // splashing
1415 PM_AddEvent( EV_FOOTSPLASH );
1416 } else if ( pm->waterlevel == 2 ) {
1417 // wading / swimming at surface
1418 PM_AddEvent( EV_SWIM );
1419 } else if ( pm->waterlevel == 3 ) {
1420 // no sound when completely underwater
1421
1422 }
1423 }
1424 }
1425
1426 /*
1427 ==============
1428 PM_WaterEvents
1429
1430 Generate sound events for entering and leaving water
1431 ==============
1432 */
PM_WaterEvents(void)1433 static void PM_WaterEvents( void ) { // FIXME?
1434 //
1435 // if just entered a water volume, play a sound
1436 //
1437 if (!pml.previous_waterlevel && pm->waterlevel) {
1438 PM_AddEvent( EV_WATER_TOUCH );
1439 }
1440
1441 //
1442 // if just completely exited a water volume, play a sound
1443 //
1444 if (pml.previous_waterlevel && !pm->waterlevel) {
1445 PM_AddEvent( EV_WATER_LEAVE );
1446 }
1447
1448 //
1449 // check for head just going under water
1450 //
1451 if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) {
1452 PM_AddEvent( EV_WATER_UNDER );
1453 }
1454
1455 //
1456 // check for head just coming out of water
1457 //
1458 if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) {
1459 PM_AddEvent( EV_WATER_CLEAR );
1460 }
1461 }
1462
1463
1464 /*
1465 ===============
1466 PM_BeginWeaponChange
1467 ===============
1468 */
PM_BeginWeaponChange(int weapon)1469 static void PM_BeginWeaponChange( int weapon ) {
1470 if ( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS ) {
1471 return;
1472 }
1473
1474 if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
1475 return;
1476 }
1477
1478 if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
1479 return;
1480 }
1481
1482 PM_AddEvent( EV_CHANGE_WEAPON );
1483 pm->ps->weaponstate = WEAPON_DROPPING;
1484 pm->ps->weaponTime += 200;
1485 PM_StartTorsoAnim( TORSO_DROP );
1486 }
1487
1488
1489 /*
1490 ===============
1491 PM_FinishWeaponChange
1492 ===============
1493 */
PM_FinishWeaponChange(void)1494 static void PM_FinishWeaponChange( void ) {
1495 int weapon;
1496
1497 weapon = pm->cmd.weapon;
1498 if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) {
1499 weapon = WP_NONE;
1500 }
1501
1502 if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
1503 weapon = WP_NONE;
1504 }
1505
1506 pm->ps->weapon = weapon;
1507 pm->ps->weaponstate = WEAPON_RAISING;
1508 pm->ps->weaponTime += 250;
1509 PM_StartTorsoAnim( TORSO_RAISE );
1510 }
1511
1512
1513 /*
1514 ==============
1515 PM_TorsoAnimation
1516
1517 ==============
1518 */
PM_TorsoAnimation(void)1519 static void PM_TorsoAnimation( void ) {
1520 if ( pm->ps->weaponstate == WEAPON_READY ) {
1521 if ( pm->ps->weapon == WP_GAUNTLET ) {
1522 PM_ContinueTorsoAnim( TORSO_STAND2 );
1523 } else {
1524 PM_ContinueTorsoAnim( TORSO_STAND );
1525 }
1526 return;
1527 }
1528 }
1529
1530
1531 /*
1532 ==============
1533 PM_Weapon
1534
1535 Generates weapon events and modifes the weapon counter
1536 ==============
1537 */
PM_Weapon(void)1538 static void PM_Weapon( void ) {
1539 int addTime;
1540
1541 // don't allow attack until all buttons are up
1542 if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
1543 return;
1544 }
1545
1546 // ignore if spectator
1547 if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
1548 return;
1549 }
1550
1551 // check for dead player
1552 if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
1553 pm->ps->weapon = WP_NONE;
1554 return;
1555 }
1556
1557 // check for item using
1558 if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) {
1559 if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) {
1560 if ( bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag == HI_MEDKIT
1561 && pm->ps->stats[STAT_HEALTH] >= (pm->ps->stats[STAT_MAX_HEALTH] + 25) ) {
1562 // don't use medkit if at max health
1563 } else {
1564 pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
1565 PM_AddEvent( EV_USE_ITEM0 + bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag );
1566 pm->ps->stats[STAT_HOLDABLE_ITEM] = 0;
1567 }
1568 return;
1569 }
1570 } else {
1571 pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD;
1572 }
1573
1574
1575 // make weapon function
1576 if ( pm->ps->weaponTime > 0 ) {
1577 pm->ps->weaponTime -= pml.msec;
1578 }
1579
1580 // check for weapon change
1581 // can't change if weapon is firing, but can change
1582 // again if lowering or raising
1583 if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
1584 if ( pm->ps->weapon != pm->cmd.weapon ) {
1585 PM_BeginWeaponChange( pm->cmd.weapon );
1586 }
1587 }
1588
1589 if ( pm->ps->weaponTime > 0 ) {
1590 return;
1591 }
1592
1593 // change weapon if time
1594 if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
1595 PM_FinishWeaponChange();
1596 return;
1597 }
1598
1599 if ( pm->ps->weaponstate == WEAPON_RAISING ) {
1600 pm->ps->weaponstate = WEAPON_READY;
1601 if ( pm->ps->weapon == WP_GAUNTLET ) {
1602 PM_StartTorsoAnim( TORSO_STAND2 );
1603 } else {
1604 PM_StartTorsoAnim( TORSO_STAND );
1605 }
1606 return;
1607 }
1608
1609 // check for fire
1610 if ( ! (pm->cmd.buttons & BUTTON_ATTACK) ) {
1611 pm->ps->weaponTime = 0;
1612 pm->ps->weaponstate = WEAPON_READY;
1613 return;
1614 }
1615
1616 // start the animation even if out of ammo
1617 if ( pm->ps->weapon == WP_GAUNTLET ) {
1618 // the guantlet only "fires" when it actually hits something
1619 if ( !pm->gauntletHit ) {
1620 pm->ps->weaponTime = 0;
1621 pm->ps->weaponstate = WEAPON_READY;
1622 return;
1623 }
1624 PM_StartTorsoAnim( TORSO_ATTACK2 );
1625 } else {
1626 PM_StartTorsoAnim( TORSO_ATTACK );
1627 }
1628
1629 pm->ps->weaponstate = WEAPON_FIRING;
1630
1631 // check for out of ammo
1632 if ( ! pm->ps->ammo[ pm->ps->weapon ] ) {
1633 PM_AddEvent( EV_NOAMMO );
1634 pm->ps->weaponTime += 500;
1635 return;
1636 }
1637
1638 // take an ammo away if not infinite
1639 if ( pm->ps->ammo[ pm->ps->weapon ] != -1 ) {
1640 pm->ps->ammo[ pm->ps->weapon ]--;
1641 }
1642
1643 // fire weapon
1644 PM_AddEvent( EV_FIRE_WEAPON );
1645
1646 switch( pm->ps->weapon ) {
1647 default:
1648 case WP_GAUNTLET:
1649 addTime = 400;
1650 break;
1651 case WP_LIGHTNING:
1652 addTime = 50;
1653 break;
1654 case WP_SHOTGUN:
1655 addTime = 1000;
1656 break;
1657 case WP_MACHINEGUN:
1658 addTime = 100;
1659 break;
1660 case WP_GRENADE_LAUNCHER:
1661 addTime = 800;
1662 break;
1663 case WP_ROCKET_LAUNCHER:
1664 addTime = 800;
1665 break;
1666 case WP_PLASMAGUN:
1667 addTime = 100;
1668 break;
1669 case WP_RAILGUN:
1670 addTime = 1500;
1671 break;
1672 case WP_BFG:
1673 addTime = 200;
1674 break;
1675 case WP_GRAPPLING_HOOK:
1676 addTime = 400;
1677 break;
1678 #ifdef MISSIONPACK
1679 case WP_NAILGUN:
1680 addTime = 1000;
1681 break;
1682 case WP_PROX_LAUNCHER:
1683 addTime = 800;
1684 break;
1685 case WP_CHAINGUN:
1686 addTime = 30;
1687 break;
1688 #endif
1689 }
1690
1691 #ifdef MISSIONPACK
1692 if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) {
1693 addTime /= 1.5;
1694 }
1695 else
1696 if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) {
1697 addTime /= 1.3;
1698 }
1699 else
1700 #endif
1701 if ( pm->ps->powerups[PW_HASTE] ) {
1702 addTime /= 1.3;
1703 }
1704
1705 pm->ps->weaponTime += addTime;
1706 }
1707
1708 /*
1709 ================
1710 PM_Animate
1711 ================
1712 */
1713
PM_Animate(void)1714 static void PM_Animate( void ) {
1715 if ( pm->cmd.buttons & BUTTON_GESTURE ) {
1716 if ( pm->ps->torsoTimer == 0 ) {
1717 PM_StartTorsoAnim( TORSO_GESTURE );
1718 pm->ps->torsoTimer = TIMER_GESTURE;
1719 PM_AddEvent( EV_TAUNT );
1720 }
1721 #ifdef MISSIONPACK
1722 } else if ( pm->cmd.buttons & BUTTON_GETFLAG ) {
1723 if ( pm->ps->torsoTimer == 0 ) {
1724 PM_StartTorsoAnim( TORSO_GETFLAG );
1725 pm->ps->torsoTimer = 600; //TIMER_GESTURE;
1726 }
1727 } else if ( pm->cmd.buttons & BUTTON_GUARDBASE ) {
1728 if ( pm->ps->torsoTimer == 0 ) {
1729 PM_StartTorsoAnim( TORSO_GUARDBASE );
1730 pm->ps->torsoTimer = 600; //TIMER_GESTURE;
1731 }
1732 } else if ( pm->cmd.buttons & BUTTON_PATROL ) {
1733 if ( pm->ps->torsoTimer == 0 ) {
1734 PM_StartTorsoAnim( TORSO_PATROL );
1735 pm->ps->torsoTimer = 600; //TIMER_GESTURE;
1736 }
1737 } else if ( pm->cmd.buttons & BUTTON_FOLLOWME ) {
1738 if ( pm->ps->torsoTimer == 0 ) {
1739 PM_StartTorsoAnim( TORSO_FOLLOWME );
1740 pm->ps->torsoTimer = 600; //TIMER_GESTURE;
1741 }
1742 } else if ( pm->cmd.buttons & BUTTON_AFFIRMATIVE ) {
1743 if ( pm->ps->torsoTimer == 0 ) {
1744 PM_StartTorsoAnim( TORSO_AFFIRMATIVE);
1745 pm->ps->torsoTimer = 600; //TIMER_GESTURE;
1746 }
1747 } else if ( pm->cmd.buttons & BUTTON_NEGATIVE ) {
1748 if ( pm->ps->torsoTimer == 0 ) {
1749 PM_StartTorsoAnim( TORSO_NEGATIVE );
1750 pm->ps->torsoTimer = 600; //TIMER_GESTURE;
1751 }
1752 #endif
1753 }
1754 }
1755
1756
1757 /*
1758 ================
1759 PM_DropTimers
1760 ================
1761 */
PM_DropTimers(void)1762 static void PM_DropTimers( void ) {
1763 // drop misc timing counter
1764 if ( pm->ps->pm_time ) {
1765 if ( pml.msec >= pm->ps->pm_time ) {
1766 pm->ps->pm_flags &= ~PMF_ALL_TIMES;
1767 pm->ps->pm_time = 0;
1768 } else {
1769 pm->ps->pm_time -= pml.msec;
1770 }
1771 }
1772
1773 // drop animation counter
1774 if ( pm->ps->legsTimer > 0 ) {
1775 pm->ps->legsTimer -= pml.msec;
1776 if ( pm->ps->legsTimer < 0 ) {
1777 pm->ps->legsTimer = 0;
1778 }
1779 }
1780
1781 if ( pm->ps->torsoTimer > 0 ) {
1782 pm->ps->torsoTimer -= pml.msec;
1783 if ( pm->ps->torsoTimer < 0 ) {
1784 pm->ps->torsoTimer = 0;
1785 }
1786 }
1787 }
1788
1789 /*
1790 ================
1791 PM_UpdateViewAngles
1792
1793 This can be used as another entry point when only the viewangles
1794 are being updated isntead of a full move
1795 ================
1796 */
PM_UpdateViewAngles(playerState_t * ps,const usercmd_t * cmd)1797 void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) {
1798 short temp;
1799 int i;
1800
1801 if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
1802 return; // no view changes at all
1803 }
1804
1805 if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
1806 return; // no view changes at all
1807 }
1808
1809 // circularly clamp the angles with deltas
1810 for (i=0 ; i<3 ; i++) {
1811 temp = cmd->angles[i] + ps->delta_angles[i];
1812 if ( i == PITCH ) {
1813 // don't let the player look up or down more than 90 degrees
1814 if ( temp > 16000 ) {
1815 ps->delta_angles[i] = 16000 - cmd->angles[i];
1816 temp = 16000;
1817 } else if ( temp < -16000 ) {
1818 ps->delta_angles[i] = -16000 - cmd->angles[i];
1819 temp = -16000;
1820 }
1821 }
1822 ps->viewangles[i] = SHORT2ANGLE(temp);
1823 }
1824
1825 }
1826
1827
1828 /*
1829 ================
1830 PmoveSingle
1831
1832 ================
1833 */
1834 void trap_SnapVector( float *v );
1835
PmoveSingle(pmove_t * pmove)1836 void PmoveSingle (pmove_t *pmove) {
1837 pm = pmove;
1838
1839 // this counter lets us debug movement problems with a journal
1840 // by setting a conditional breakpoint fot the previous frame
1841 c_pmove++;
1842
1843 // clear results
1844 pm->numtouch = 0;
1845 pm->watertype = 0;
1846 pm->waterlevel = 0;
1847
1848 if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
1849 pm->tracemask &= ~CONTENTS_BODY; // corpses can fly through bodies
1850 }
1851
1852 // make sure walking button is clear if they are running, to avoid
1853 // proxy no-footsteps cheats
1854 if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) {
1855 pm->cmd.buttons &= ~BUTTON_WALKING;
1856 }
1857
1858 // set the talk balloon flag
1859 if ( pm->cmd.buttons & BUTTON_TALK ) {
1860 pm->ps->eFlags |= EF_TALK;
1861 } else {
1862 pm->ps->eFlags &= ~EF_TALK;
1863 }
1864
1865 // set the firing flag for continuous beam weapons
1866 if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION
1867 && ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] ) {
1868 pm->ps->eFlags |= EF_FIRING;
1869 } else {
1870 pm->ps->eFlags &= ~EF_FIRING;
1871 }
1872
1873 // clear the respawned flag if attack and use are cleared
1874 if ( pm->ps->stats[STAT_HEALTH] > 0 &&
1875 !( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) {
1876 pm->ps->pm_flags &= ~PMF_RESPAWNED;
1877 }
1878
1879 // if talk button is down, dissallow all other input
1880 // this is to prevent any possible intercept proxy from
1881 // adding fake talk balloons
1882 if ( pmove->cmd.buttons & BUTTON_TALK ) {
1883 // keep the talk button set tho for when the cmd.serverTime > 66 msec
1884 // and the same cmd is used multiple times in Pmove
1885 pmove->cmd.buttons = BUTTON_TALK;
1886 pmove->cmd.forwardmove = 0;
1887 pmove->cmd.rightmove = 0;
1888 pmove->cmd.upmove = 0;
1889 }
1890
1891 // clear all pmove local vars
1892 memset (&pml, 0, sizeof(pml));
1893
1894 // determine the time
1895 pml.msec = pmove->cmd.serverTime - pm->ps->commandTime;
1896 if ( pml.msec < 1 ) {
1897 pml.msec = 1;
1898 } else if ( pml.msec > 200 ) {
1899 pml.msec = 200;
1900 }
1901 pm->ps->commandTime = pmove->cmd.serverTime;
1902
1903 // save old org in case we get stuck
1904 VectorCopy (pm->ps->origin, pml.previous_origin);
1905
1906 // save old velocity for crashlanding
1907 VectorCopy (pm->ps->velocity, pml.previous_velocity);
1908
1909 pml.frametime = pml.msec * 0.001;
1910
1911 // update the viewangles
1912 PM_UpdateViewAngles( pm->ps, &pm->cmd );
1913
1914 AngleVectors (pm->ps->viewangles, pml.forward, pml.right, pml.up);
1915
1916 if ( pm->cmd.upmove < 10 ) {
1917 // not holding jump
1918 pm->ps->pm_flags &= ~PMF_JUMP_HELD;
1919 }
1920
1921 // decide if backpedaling animations should be used
1922 if ( pm->cmd.forwardmove < 0 ) {
1923 pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
1924 } else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
1925 pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;
1926 }
1927
1928 if ( pm->ps->pm_type >= PM_DEAD ) {
1929 pm->cmd.forwardmove = 0;
1930 pm->cmd.rightmove = 0;
1931 pm->cmd.upmove = 0;
1932 }
1933
1934 if ( pm->ps->pm_type == PM_SPECTATOR ) {
1935 PM_CheckDuck ();
1936 PM_FlyMove ();
1937 PM_DropTimers ();
1938 return;
1939 }
1940
1941 if ( pm->ps->pm_type == PM_NOCLIP ) {
1942 PM_NoclipMove ();
1943 PM_DropTimers ();
1944 return;
1945 }
1946
1947 if (pm->ps->pm_type == PM_FREEZE) {
1948 return; // no movement at all
1949 }
1950
1951 if ( pm->ps->pm_type == PM_INTERMISSION || pm->ps->pm_type == PM_SPINTERMISSION) {
1952 return; // no movement at all
1953 }
1954
1955 // set watertype, and waterlevel
1956 PM_SetWaterLevel();
1957 pml.previous_waterlevel = pmove->waterlevel;
1958
1959 // set mins, maxs, and viewheight
1960 PM_CheckDuck ();
1961
1962 // set groundentity
1963 PM_GroundTrace();
1964
1965 if ( pm->ps->pm_type == PM_DEAD ) {
1966 PM_DeadMove ();
1967 }
1968
1969 PM_DropTimers();
1970
1971 #ifdef MISSIONPACK
1972 if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
1973 PM_InvulnerabilityMove();
1974 } else
1975 #endif
1976 if ( pm->ps->powerups[PW_FLIGHT] ) {
1977 // flight powerup doesn't allow jump and has different friction
1978 PM_FlyMove();
1979 } else if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) {
1980 PM_GrappleMove();
1981 // We can wiggle a bit
1982 PM_AirMove();
1983 } else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) {
1984 PM_WaterJumpMove();
1985 } else if ( pm->waterlevel > 1 ) {
1986 // swimming
1987 PM_WaterMove();
1988 } else if ( pml.walking ) {
1989 // walking on ground
1990 PM_WalkMove();
1991 } else {
1992 // airborne
1993 PM_AirMove();
1994 }
1995
1996 PM_Animate();
1997
1998 // set groundentity, watertype, and waterlevel
1999 PM_GroundTrace();
2000 PM_SetWaterLevel();
2001
2002 // weapons
2003 PM_Weapon();
2004
2005 // torso animation
2006 PM_TorsoAnimation();
2007
2008 // footstep events / legs animations
2009 PM_Footsteps();
2010
2011 // entering / leaving water splashes
2012 PM_WaterEvents();
2013
2014 // snap some parts of playerstate to save network bandwidth
2015 trap_SnapVector( pm->ps->velocity );
2016 }
2017
2018
2019 /*
2020 ================
2021 Pmove
2022
2023 Can be called by either the server or the client
2024 ================
2025 */
Pmove(pmove_t * pmove)2026 void Pmove (pmove_t *pmove) {
2027 int finalTime;
2028
2029 finalTime = pmove->cmd.serverTime;
2030
2031 if ( finalTime < pmove->ps->commandTime ) {
2032 return; // should not happen
2033 }
2034
2035 if ( finalTime > pmove->ps->commandTime + 1000 ) {
2036 pmove->ps->commandTime = finalTime - 1000;
2037 }
2038
2039 pmove->ps->pmove_framecount = (pmove->ps->pmove_framecount+1) & ((1<<PS_PMOVEFRAMECOUNTBITS)-1);
2040
2041 // chop the move up if it is too long, to prevent framerate
2042 // dependent behavior
2043 while ( pmove->ps->commandTime != finalTime ) {
2044 int msec;
2045
2046 msec = finalTime - pmove->ps->commandTime;
2047
2048 if ( pmove->pmove_fixed ) {
2049 if ( msec > pmove->pmove_msec ) {
2050 msec = pmove->pmove_msec;
2051 }
2052 }
2053 else {
2054 if ( msec > 66 ) {
2055 msec = 66;
2056 }
2057 }
2058 pmove->cmd.serverTime = pmove->ps->commandTime + msec;
2059 PmoveSingle( pmove );
2060
2061 if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) {
2062 pmove->cmd.upmove = 20;
2063 }
2064 }
2065
2066 //PM_CheckStuck();
2067
2068 }
2069
2070