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