1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7
8 This file is part of the OpenJK source code.
9
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13
14 This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23
24 // bg_pmove.c -- both games player movement code
25 // takes a playerstate and a usercmd as input and returns a modifed playerstate
26
27 #include "qcommon/q_shared.h"
28 #include "bg_public.h"
29 #include "bg_local.h"
30 #include "ghoul2/G2.h"
31
32 #ifdef _GAME
33 #include "g_local.h"
34 #elif _CGAME
35 #include "cgame/cg_local.h"
36 #elif UI_BUILD
37 #include "ui/ui_local.h"
38 #endif
39
40 #define MAX_WEAPON_CHARGE_TIME 5000
41
42 #ifdef _GAME
43 extern void G_CheapWeaponFire(int entNum, int ev);
44 extern qboolean TryGrapple(gentity_t *ent); //g_cmds.c
45 #endif // _GAME
46
47 extern qboolean BG_FullBodyTauntAnim( int anim );
48 extern float PM_WalkableGroundDistance(void);
49 extern qboolean PM_GroundSlideOkay( float zNormal );
50 extern saberInfo_t *BG_MySaber( int clientNum, int saberNum );
51
52 pmove_t *pm;
53 pml_t pml;
54
55 bgEntity_t *pm_entSelf = NULL;
56 bgEntity_t *pm_entVeh = NULL;
57
58 qboolean gPMDoSlowFall = qfalse;
59
60 qboolean pm_cancelOutZoom = qfalse;
61
62 // movement parameters
63 float pm_stopspeed = 100.0f;
64 float pm_duckScale = 0.50f;
65 float pm_swimScale = 0.50f;
66 float pm_wadeScale = 0.70f;
67
68 float pm_vehicleaccelerate = 36.0f;
69 float pm_accelerate = 10.0f;
70 float pm_airaccelerate = 1.0f;
71 float pm_wateraccelerate = 4.0f;
72 float pm_flyaccelerate = 8.0f;
73
74 float pm_friction = 6.0f;
75 float pm_waterfriction = 1.0f;
76 float pm_flightfriction = 3.0f;
77 float pm_spectatorfriction = 5.0f;
78
79 int c_pmove = 0;
80
81 float forceSpeedLevels[4] =
82 {
83 1, //rank 0?
84 1.25,
85 1.5,
86 1.75
87 };
88
89 int forcePowerNeeded[NUM_FORCE_POWER_LEVELS][NUM_FORCE_POWERS] =
90 {
91 { //nothing should be usable at rank 0..
92 999,//FP_HEAL,//instant
93 999,//FP_LEVITATION,//hold/duration
94 999,//FP_SPEED,//duration
95 999,//FP_PUSH,//hold/duration
96 999,//FP_PULL,//hold/duration
97 999,//FP_TELEPATHY,//instant
98 999,//FP_GRIP,//hold/duration
99 999,//FP_LIGHTNING,//hold/duration
100 999,//FP_RAGE,//duration
101 999,//FP_PROTECT,//duration
102 999,//FP_ABSORB,//duration
103 999,//FP_TEAM_HEAL,//instant
104 999,//FP_TEAM_FORCE,//instant
105 999,//FP_DRAIN,//hold/duration
106 999,//FP_SEE,//duration
107 999,//FP_SABER_OFFENSE,
108 999,//FP_SABER_DEFENSE,
109 999//FP_SABERTHROW,
110 //NUM_FORCE_POWERS
111 },
112 {
113 65,//FP_HEAL,//instant //was 25, but that was way too little
114 10,//FP_LEVITATION,//hold/duration
115 50,//FP_SPEED,//duration
116 20,//FP_PUSH,//hold/duration
117 20,//FP_PULL,//hold/duration
118 20,//FP_TELEPATHY,//instant
119 30,//FP_GRIP,//hold/duration
120 1,//FP_LIGHTNING,//hold/duration
121 50,//FP_RAGE,//duration
122 50,//FP_PROTECT,//duration
123 50,//FP_ABSORB,//duration
124 50,//FP_TEAM_HEAL,//instant
125 50,//FP_TEAM_FORCE,//instant
126 20,//FP_DRAIN,//hold/duration
127 20,//FP_SEE,//duration
128 0,//FP_SABER_OFFENSE,
129 2,//FP_SABER_DEFENSE,
130 20//FP_SABERTHROW,
131 //NUM_FORCE_POWERS
132 },
133 {
134 60,//FP_HEAL,//instant
135 10,//FP_LEVITATION,//hold/duration
136 50,//FP_SPEED,//duration
137 20,//FP_PUSH,//hold/duration
138 20,//FP_PULL,//hold/duration
139 20,//FP_TELEPATHY,//instant
140 30,//FP_GRIP,//hold/duration
141 1,//FP_LIGHTNING,//hold/duration
142 50,//FP_RAGE,//duration
143 25,//FP_PROTECT,//duration
144 25,//FP_ABSORB,//duration
145 33,//FP_TEAM_HEAL,//instant
146 33,//FP_TEAM_FORCE,//instant
147 20,//FP_DRAIN,//hold/duration
148 20,//FP_SEE,//duration
149 0,//FP_SABER_OFFENSE,
150 1,//FP_SABER_DEFENSE,
151 20//FP_SABERTHROW,
152 //NUM_FORCE_POWERS
153 },
154 {
155 50,//FP_HEAL,//instant //You get 5 points of health.. for 50 force points!
156 10,//FP_LEVITATION,//hold/duration
157 50,//FP_SPEED,//duration
158 20,//FP_PUSH,//hold/duration
159 20,//FP_PULL,//hold/duration
160 20,//FP_TELEPATHY,//instant
161 60,//FP_GRIP,//hold/duration
162 1,//FP_LIGHTNING,//hold/duration
163 50,//FP_RAGE,//duration
164 10,//FP_PROTECT,//duration
165 10,//FP_ABSORB,//duration
166 25,//FP_TEAM_HEAL,//instant
167 25,//FP_TEAM_FORCE,//instant
168 20,//FP_DRAIN,//hold/duration
169 20,//FP_SEE,//duration
170 0,//FP_SABER_OFFENSE,
171 0,//FP_SABER_DEFENSE,
172 20//FP_SABERTHROW,
173 //NUM_FORCE_POWERS
174 }
175 };
176
177 float forceJumpHeight[NUM_FORCE_POWER_LEVELS] =
178 {
179 32,//normal jump (+stepheight+crouchdiff = 66)
180 96,//(+stepheight+crouchdiff = 130)
181 192,//(+stepheight+crouchdiff = 226)
182 384//(+stepheight+crouchdiff = 418)
183 };
184
185 float forceJumpStrength[NUM_FORCE_POWER_LEVELS] =
186 {
187 JUMP_VELOCITY,//normal jump
188 420,
189 590,
190 840
191 };
192
193 //rww - Get a pointer to the bgEntity by the index
PM_BGEntForNum(int num)194 bgEntity_t *PM_BGEntForNum( int num )
195 {
196 bgEntity_t *ent;
197
198 if (!pm)
199 {
200 assert(!"You cannot call PM_BGEntForNum outside of pm functions!");
201 return NULL;
202 }
203
204 if (!pm->baseEnt)
205 {
206 assert(!"Base entity address not set");
207 return NULL;
208 }
209
210 if (!pm->entSize)
211 {
212 assert(!"sizeof(ent) is 0, impossible (not set?)");
213 return NULL;
214 }
215
216 assert(num >= 0 && num < MAX_GENTITIES);
217
218 ent = (bgEntity_t *)((byte *)pm->baseEnt + pm->entSize*(num));
219
220 return ent;
221 }
222
BG_SabersOff(playerState_t * ps)223 qboolean BG_SabersOff( playerState_t *ps )
224 {
225 if ( !ps->saberHolstered )
226 {
227 return qfalse;
228 }
229 if ( ps->fd.saberAnimLevelBase == SS_DUAL
230 || ps->fd.saberAnimLevelBase == SS_STAFF )
231 {
232 if ( ps->saberHolstered < 2 )
233 {
234 return qfalse;
235 }
236 }
237 return qtrue;
238 }
239
BG_KnockDownable(playerState_t * ps)240 qboolean BG_KnockDownable(playerState_t *ps)
241 {
242 if (!ps)
243 { //just for safety
244 return qfalse;
245 }
246
247 if (ps->m_iVehicleNum)
248 { //riding a vehicle, don't knock me down
249 return qfalse;
250 }
251
252 if (ps->emplacedIndex)
253 { //using emplaced gun or eweb, can't be knocked down
254 return qfalse;
255 }
256
257 //ok, I guess?
258 return qtrue;
259 }
260
261 //hacky assumption check, assume any client non-humanoid is a rocket trooper
PM_IsRocketTrooper(void)262 static QINLINE qboolean PM_IsRocketTrooper(void)
263 {
264 /*
265 if (pm->ps->clientNum < MAX_CLIENTS &&
266 pm->gametype == GT_SIEGE &&
267 pm->nonHumanoid)
268 {
269 return qtrue;
270 }
271 */
272
273 return qfalse;
274 }
275
PM_GetSaberStance(void)276 int PM_GetSaberStance(void)
277 {
278 int anim = BOTH_STAND2;
279 saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 );
280 saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 );
281
282 if (!pm->ps->saberEntityNum)
283 { //lost it
284 return BOTH_STAND1;
285 }
286
287 if ( BG_SabersOff( pm->ps ) )
288 {
289 return BOTH_STAND1;
290 }
291
292 if ( saber1
293 && saber1->readyAnim != -1 )
294 {
295 return saber1->readyAnim;
296 }
297
298 if ( saber2
299 && saber2->readyAnim != -1 )
300 {
301 return saber2->readyAnim;
302 }
303
304 if ( saber1
305 && saber2
306 && !pm->ps->saberHolstered )
307 {//dual sabers, both on
308 return BOTH_SABERDUAL_STANCE;
309 }
310
311 switch ( pm->ps->fd.saberAnimLevel )
312 {
313 case SS_DUAL:
314 anim = BOTH_SABERDUAL_STANCE;
315 break;
316 case SS_STAFF:
317 anim = BOTH_SABERSTAFF_STANCE;
318 break;
319 case SS_FAST:
320 case SS_TAVION:
321 anim = BOTH_SABERFAST_STANCE;
322 break;
323 case SS_STRONG:
324 anim = BOTH_SABERSLOW_STANCE;
325 break;
326 case SS_NONE:
327 case SS_MEDIUM:
328 case SS_DESANN:
329 default:
330 anim = BOTH_STAND2;
331 break;
332 }
333 return anim;
334 }
335
PM_DoSlowFall(void)336 qboolean PM_DoSlowFall(void)
337 {
338 if ( ( (pm->ps->legsAnim) == BOTH_WALL_RUN_RIGHT || (pm->ps->legsAnim) == BOTH_WALL_RUN_LEFT ) && pm->ps->legsTimer > 500 )
339 {
340 return qtrue;
341 }
342
343 return qfalse;
344 }
345
346 //begin vehicle functions crudely ported from sp -rww
347 /*
348 ====================================================================
349 void pitch_roll_for_slope (edict_t *forwhom, vec3_t *slope, vec3_t storeAngles )
350
351 MG
352
353 This will adjust the pitch and roll of a monster to match
354 a given slope - if a non-'0 0 0' slope is passed, it will
355 use that value, otherwise it will use the ground underneath
356 the monster. If it doesn't find a surface, it does nothinh\g
357 and returns.
358 ====================================================================
359 */
360
PM_pitch_roll_for_slope(bgEntity_t * forwhom,vec3_t pass_slope,vec3_t storeAngles)361 void PM_pitch_roll_for_slope( bgEntity_t *forwhom, vec3_t pass_slope, vec3_t storeAngles )
362 {
363 vec3_t slope;
364 vec3_t nvf, ovf, ovr, startspot, endspot, new_angles = { 0, 0, 0 };
365 float pitch, mod, dot;
366
367 //if we don't have a slope, get one
368 if( !pass_slope || VectorCompare( vec3_origin, pass_slope ) )
369 {
370 trace_t trace;
371
372 VectorCopy( pm->ps->origin, startspot );
373 startspot[2] += pm->mins[2] + 4;
374 VectorCopy( startspot, endspot );
375 endspot[2] -= 300;
376 pm->trace( &trace, pm->ps->origin, vec3_origin, vec3_origin, endspot, forwhom->s.number, MASK_SOLID );
377 // if(trace_fraction>0.05&&forwhom.movetype==MOVETYPE_STEP)
378 // forwhom.flags(-)FL_ONGROUND;
379
380 if ( trace.fraction >= 1.0 )
381 return;
382
383 if ( VectorCompare( vec3_origin, trace.plane.normal ) )
384 return;
385
386 VectorCopy( trace.plane.normal, slope );
387 }
388 else
389 {
390 VectorCopy( pass_slope, slope );
391 }
392
393 if ( forwhom->s.NPC_class == CLASS_VEHICLE )
394 {//special code for vehicles
395 Vehicle_t *pVeh = forwhom->m_pVehicle;
396 vec3_t tempAngles;
397
398 tempAngles[PITCH] = tempAngles[ROLL] = 0;
399 tempAngles[YAW] = pVeh->m_vOrientation[YAW];
400 AngleVectors( tempAngles, ovf, ovr, NULL );
401 }
402 else
403 {
404 AngleVectors( pm->ps->viewangles, ovf, ovr, NULL );
405 }
406
407 vectoangles( slope, new_angles );
408 pitch = new_angles[PITCH] + 90;
409 new_angles[ROLL] = new_angles[PITCH] = 0;
410
411 AngleVectors( new_angles, nvf, NULL, NULL );
412
413 mod = DotProduct( nvf, ovr );
414
415 if ( mod<0 )
416 mod = -1;
417 else
418 mod = 1;
419
420 dot = DotProduct( nvf, ovf );
421
422 if ( storeAngles )
423 {
424 storeAngles[PITCH] = dot * pitch;
425 storeAngles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
426 }
427 else //if ( forwhom->client )
428 {
429 float oldmins2;
430
431 pm->ps->viewangles[PITCH] = dot * pitch;
432 pm->ps->viewangles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
433 oldmins2 = pm->mins[2];
434 pm->mins[2] = -24 + 12 * fabs(pm->ps->viewangles[PITCH])/180.0f;
435 //FIXME: if it gets bigger, move up
436 if ( oldmins2 > pm->mins[2] )
437 {//our mins is now lower, need to move up
438 //FIXME: trace?
439 pm->ps->origin[2] += (oldmins2 - pm->mins[2]);
440 //forwhom->currentOrigin[2] = forwhom->client->ps.origin[2];
441 //trap->linkentity( forwhom );
442 }
443 }
444 /*
445 else
446 {
447 forwhom->currentAngles[PITCH] = dot * pitch;
448 forwhom->currentAngles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
449 }
450 */
451 }
452
453 #define FLY_NONE 0
454 #define FLY_NORMAL 1
455 #define FLY_VEHICLE 2
456 #define FLY_HOVER 3
457 static int pm_flying = FLY_NONE;
458
PM_SetSpecialMoveValues(void)459 void PM_SetSpecialMoveValues (void)
460 {
461 bgEntity_t *pEnt;
462
463 if (pm->ps->clientNum < MAX_CLIENTS)
464 { //we know that real players aren't vehs
465 pm_flying = FLY_NONE;
466 return;
467 }
468
469 //default until we decide otherwise
470 pm_flying = FLY_NONE;
471
472 pEnt = pm_entSelf;
473
474 if ( pEnt )
475 {
476 if ( (pm->ps->eFlags2&EF2_FLYING) )// pm->gent->client->moveType == MT_FLYSWIM )
477 {
478 pm_flying = FLY_NORMAL;
479 }
480 else if ( pEnt->s.NPC_class == CLASS_VEHICLE )
481 {
482 if ( pEnt->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
483 {
484 pm_flying = FLY_VEHICLE;
485 }
486 else if ( pEnt->m_pVehicle->m_pVehicleInfo->hoverHeight > 0 )
487 {
488 pm_flying = FLY_HOVER;
489 }
490 }
491 }
492 }
493
PM_SetVehicleAngles(vec3_t normal)494 static void PM_SetVehicleAngles( vec3_t normal )
495 {
496 bgEntity_t *pEnt = pm_entSelf;
497 Vehicle_t *pVeh;
498 vec3_t vAngles;
499 float vehicleBankingSpeed;
500 float pitchBias;
501 int i;
502
503 if ( !pEnt || pEnt->s.NPC_class != CLASS_VEHICLE )
504 {
505 return;
506 }
507
508 pVeh = pEnt->m_pVehicle;
509
510 //float curVehicleBankingSpeed;
511 vehicleBankingSpeed = (pVeh->m_pVehicleInfo->bankingSpeed*32.0f)*pml.frametime;//0.25f
512
513 if ( vehicleBankingSpeed <= 0
514 || ( pVeh->m_pVehicleInfo->pitchLimit == 0 && pVeh->m_pVehicleInfo->rollLimit == 0 ) )
515 {//don't bother, this vehicle doesn't bank
516 return;
517 }
518 //FIXME: do 3 traces to define a plane and use that... smoothes it out some, too...
519 //pitch_roll_for_slope( pm->gent, normal, vAngles );
520 //FIXME: maybe have some pitch control in water and/or air?
521
522 if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
523 {
524 pitchBias = 0.0f;
525 }
526 else
527 {
528 //FIXME: gravity does not matter in SPACE!!!
529 //center of gravity affects pitch in air/water (FIXME: what about roll?)
530 pitchBias = 90.0f*pVeh->m_pVehicleInfo->centerOfGravity[0];//if centerOfGravity is all the way back (-1.0f), vehicle pitches up 90 degrees when in air
531 }
532
533 VectorClear( vAngles );
534 if ( pm->waterlevel > 0 )
535 {//in water
536 //view pitch has some influence when in water
537 //FIXME: take center of gravity into account?
538 vAngles[PITCH] += (pm->ps->viewangles[PITCH]-vAngles[PITCH])*0.75f + (pitchBias*0.5);
539 }
540 else if ( normal )
541 {//have a valid surface below me
542 PM_pitch_roll_for_slope( pEnt, normal, vAngles );
543 if ( (pml.groundTrace.contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) )
544 {//on water
545 //view pitch has some influence when on a fluid surface
546 //FIXME: take center of gravity into account
547 vAngles[PITCH] += (pm->ps->viewangles[PITCH]-vAngles[PITCH])*0.5f + (pitchBias*0.5f);
548 }
549 }
550 else
551 {//in air, let pitch match view...?
552 //FIXME: take center of gravity into account
553 vAngles[PITCH] = pm->ps->viewangles[PITCH]*0.5f + pitchBias;
554 //don't bank so fast when in the air
555 vehicleBankingSpeed *= (0.125f*pml.frametime);
556 }
557 //NOTE: if angles are flat and we're moving through air (not on ground),
558 // then pitch/bank?
559 if ( pVeh->m_pVehicleInfo->rollLimit > 0 )
560 {
561 //roll when banking
562 vec3_t velocity;
563 float speed;
564 VectorCopy( pm->ps->velocity, velocity );
565 velocity[2] = 0.0f;
566 speed = VectorNormalize( velocity );
567 if ( speed > 32.0f || speed < -32.0f )
568 {
569 vec3_t rt, tempVAngles;
570 float side;
571 float dp;
572
573 // Magic number fun! Speed is used for banking, so modulate the speed by a sine wave
574 //FIXME: this banks too early
575 speed *= sin( (150 + pml.frametime) * 0.003 );
576
577 // Clamp to prevent harsh rolling
578 if ( speed > 60 )
579 speed = 60;
580
581 VectorCopy( pVeh->m_vOrientation, tempVAngles );
582 tempVAngles[ROLL] = 0;
583 AngleVectors( tempVAngles, NULL, rt, NULL );
584 dp = DotProduct( velocity, rt );
585 side = speed * dp;
586 vAngles[ROLL] -= side;
587 }
588 }
589
590 //cap
591 if ( pVeh->m_pVehicleInfo->pitchLimit != -1 )
592 {
593 if ( vAngles[PITCH] > pVeh->m_pVehicleInfo->pitchLimit )
594 {
595 vAngles[PITCH] = pVeh->m_pVehicleInfo->pitchLimit;
596 }
597 else if ( vAngles[PITCH] < -pVeh->m_pVehicleInfo->pitchLimit )
598 {
599 vAngles[PITCH] = -pVeh->m_pVehicleInfo->pitchLimit;
600 }
601 }
602
603 if ( vAngles[ROLL] > pVeh->m_pVehicleInfo->rollLimit )
604 {
605 vAngles[ROLL] = pVeh->m_pVehicleInfo->rollLimit;
606 }
607 else if ( vAngles[ROLL] < -pVeh->m_pVehicleInfo->rollLimit )
608 {
609 vAngles[ROLL] = -pVeh->m_pVehicleInfo->rollLimit;
610 }
611
612 //do it
613 for ( i = 0; i < 3; i++ )
614 {
615 if ( i == YAW )
616 {//yawing done elsewhere
617 continue;
618 }
619 //bank faster the higher the difference is
620 /*
621 else if ( i == PITCH )
622 {
623 curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[PITCH], pVeh->m_vOrientation[PITCH] )))/(g_vehicleInfo[pm->ps->vehicleIndex].pitchLimit/2.0f);
624 }
625 else if ( i == ROLL )
626 {
627 curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[ROLL], pVeh->m_vOrientation[ROLL] )))/(g_vehicleInfo[pm->ps->vehicleIndex].rollLimit/2.0f);
628 }
629
630 if ( curVehicleBankingSpeed )
631 */
632 {
633 if ( pVeh->m_vOrientation[i] >= vAngles[i] + vehicleBankingSpeed )
634 {
635 pVeh->m_vOrientation[i] -= vehicleBankingSpeed;
636 }
637 else if ( pVeh->m_vOrientation[i] <= vAngles[i] - vehicleBankingSpeed )
638 {
639 pVeh->m_vOrientation[i] += vehicleBankingSpeed;
640 }
641 else
642 {
643 pVeh->m_vOrientation[i] = vAngles[i];
644 }
645 }
646 }
647 }
648
BG_VehicleTurnRateForSpeed(Vehicle_t * pVeh,float speed,float * mPitchOverride,float * mYawOverride)649 void BG_VehicleTurnRateForSpeed( Vehicle_t *pVeh, float speed, float *mPitchOverride, float *mYawOverride )
650 {
651 if ( pVeh && pVeh->m_pVehicleInfo )
652 {
653 float speedFrac = 1.0f;
654 if ( pVeh->m_pVehicleInfo->speedDependantTurning )
655 {
656 if ( pVeh->m_LandTrace.fraction >= 1.0f
657 || pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE )
658 {
659 speedFrac = (speed/(pVeh->m_pVehicleInfo->speedMax*0.75f));
660 if ( speedFrac < 0.25f )
661 {
662 speedFrac = 0.25f;
663 }
664 else if ( speedFrac > 1.0f )
665 {
666 speedFrac = 1.0f;
667 }
668 }
669 }
670 if ( pVeh->m_pVehicleInfo->mousePitch )
671 {
672 *mPitchOverride = pVeh->m_pVehicleInfo->mousePitch*speedFrac;
673 }
674 if ( pVeh->m_pVehicleInfo->mouseYaw )
675 {
676 *mYawOverride = pVeh->m_pVehicleInfo->mouseYaw*speedFrac;
677 }
678 }
679 }
680
681
682 // Following couple things don't belong in the DLL namespace!
683 #ifdef _GAME
684 #if !defined(MACOS_X) && !defined(__GCC__) && !defined(__GNUC__)
685 typedef struct gentity_s gentity_t;
686 #endif
687 gentity_t *G_PlayEffectID( const int fxID, vec3_t org, vec3_t ang );
688 #endif
689
690
691 static void PM_GroundTraceMissed( void );
PM_HoverTrace(void)692 void PM_HoverTrace( void )
693 {
694 Vehicle_t *pVeh;
695 float hoverHeight;
696 vec3_t point, vAng, fxAxis[3];
697 trace_t *trace;
698 float relativeWaterLevel;
699
700 bgEntity_t *pEnt = pm_entSelf;
701 if ( !pEnt || pEnt->s.NPC_class != CLASS_VEHICLE )
702 {
703 return;
704 }
705
706 pVeh = pEnt->m_pVehicle;
707 hoverHeight = pVeh->m_pVehicleInfo->hoverHeight;
708 trace = &pml.groundTrace;
709
710 pml.groundPlane = qfalse;
711
712 //relativeWaterLevel = (pm->ps->waterheight - (pm->ps->origin[2]+pm->mins[2]));
713 relativeWaterLevel = pm->waterlevel; //I.. guess this works
714 if ( pm->waterlevel && relativeWaterLevel >= 0 )
715 {//in water
716 if ( pVeh->m_pVehicleInfo->bouyancy <= 0.0f )
717 {//sink like a rock
718 }
719 else
720 {//rise up
721 float floatHeight = (pVeh->m_pVehicleInfo->bouyancy * ((pm->maxs[2]-pm->mins[2])*0.5f)) - (hoverHeight*0.5f);//1.0f should make you float half-in, half-out of water
722 if ( relativeWaterLevel > floatHeight )
723 {//too low, should rise up
724 pm->ps->velocity[2] += (relativeWaterLevel - floatHeight) * pVeh->m_fTimeModifier;
725 }
726 }
727 //if ( pm->ps->waterheight < pm->ps->origin[2]+pm->maxs[2] )
728 if (pm->waterlevel <= 1)
729 {//part of us is sticking out of water
730 if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 )
731 {//moving at a decent speed
732 if ( Q_irand( pml.frametime, 100 ) >= 50 )
733 {//splash
734 vec3_t wakeOrg;
735
736 vAng[PITCH] = vAng[ROLL] = 0;
737 vAng[YAW] = pVeh->m_vOrientation[YAW];
738 AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] );
739 VectorCopy( pm->ps->origin, wakeOrg );
740 //wakeOrg[2] = pm->ps->waterheight;
741 if (pm->waterlevel >= 2)
742 {
743 wakeOrg[2] = pm->ps->origin[2]+16;
744 }
745 else
746 {
747 wakeOrg[2] = pm->ps->origin[2];
748 }
749 #ifdef _GAME //yeah, this is kind of crappy and makes no use of prediction whatsoever
750 if ( pVeh->m_pVehicleInfo->iWakeFX )
751 {
752 //G_PlayEffectID( pVeh->m_pVehicleInfo->iWakeFX, wakeOrg, fxAxis[0] );
753 //tempent use bad!
754 G_AddEvent((gentity_t *)pEnt, EV_PLAY_EFFECT_ID, pVeh->m_pVehicleInfo->iWakeFX);
755 }
756 #endif
757 }
758 }
759 }
760 }
761 else
762 {
763 int traceContents;
764 float minNormal = pVeh->m_pVehicleInfo->maxSlope;
765
766 point[0] = pm->ps->origin[0];
767 point[1] = pm->ps->origin[1];
768 point[2] = pm->ps->origin[2] - hoverHeight;
769
770 //FIXME: check for water, too? If over water, go slower and make wave effect
771 // If *in* water, go really slow and use bouyancy stat to determine how far below surface to float
772
773 //NOTE: if bouyancy is 2.0f or higher, you float over water like it's solid ground.
774 // if it's 1.0f, you sink halfway into water. If it's 0, you sink...
775 traceContents = pm->tracemask;
776 if ( pVeh->m_pVehicleInfo->bouyancy >= 2.0f )
777 {//sit on water
778 traceContents |= (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA);
779 }
780 pm->trace( trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, traceContents );
781 if (trace->plane.normal[0] > 0.5f || trace->plane.normal[0] < -0.5f ||
782 trace->plane.normal[1] > 0.5f || trace->plane.normal[1] < -0.5f)
783 { //steep slanted hill, don't go up it.
784 float d = fabs(trace->plane.normal[0]);
785 float e = fabs(trace->plane.normal[1]);
786 if (e > d)
787 {
788 d = e;
789 }
790 pm->ps->velocity[2] = -300.0f*d;
791 }
792 else if ( trace->plane.normal[2] >= minNormal )
793 {//not a steep slope, so push us up
794 if ( trace->fraction < 1.0f )
795 {//push up off ground
796 float hoverForce = pVeh->m_pVehicleInfo->hoverStrength;
797 if ( trace->fraction > 0.5f )
798 {
799 pm->ps->velocity[2] += (1.0f-trace->fraction)*hoverForce*pVeh->m_fTimeModifier;
800 }
801 else
802 {
803 pm->ps->velocity[2] += (0.5f-(trace->fraction*trace->fraction))*hoverForce*2.0f*pVeh->m_fTimeModifier;
804 }
805 if ( (trace->contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) )
806 {//hovering on water, make a spash if moving
807 if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 )
808 {//moving at a decent speed
809 if ( Q_irand( pml.frametime, 100 ) >= 50 )
810 {//splash
811 vAng[PITCH] = vAng[ROLL] = 0;
812 vAng[YAW] = pVeh->m_vOrientation[YAW];
813 AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] );
814 #ifdef _GAME
815 if ( pVeh->m_pVehicleInfo->iWakeFX )
816 {
817 G_PlayEffectID( pVeh->m_pVehicleInfo->iWakeFX, trace->endpos, fxAxis[0] );
818 }
819 #endif
820 }
821 }
822 }
823 pml.groundPlane = qtrue;
824 }
825 }
826 }
827 if ( pml.groundPlane )
828 {
829 PM_SetVehicleAngles( pml.groundTrace.plane.normal );
830 // We're on the ground.
831 pVeh->m_ulFlags &= ~VEH_FLYING;
832
833 pVeh->m_vAngularVelocity = 0.0f;
834 }
835 else
836 {
837 PM_SetVehicleAngles( NULL );
838 // We're flying in the air.
839 pVeh->m_ulFlags |= VEH_FLYING;
840 //groundTrace
841
842 if (pVeh->m_vAngularVelocity==0.0f)
843 {
844 pVeh->m_vAngularVelocity = pVeh->m_vOrientation[YAW] - pVeh->m_vPrevOrientation[YAW];
845 if (pVeh->m_vAngularVelocity<-15.0f)
846 {
847 pVeh->m_vAngularVelocity = -15.0f;
848 }
849 if (pVeh->m_vAngularVelocity> 15.0f)
850 {
851 pVeh->m_vAngularVelocity = 15.0f;
852 }
853 }
854 //pVeh->m_vAngularVelocity *= 0.95f; // Angular Velocity Decays Over Time
855 if (pVeh->m_vAngularVelocity > 0.0f)
856 {
857 pVeh->m_vAngularVelocity -= pml.frametime;
858 if (pVeh->m_vAngularVelocity < 0.0f)
859 {
860 pVeh->m_vAngularVelocity = 0.0f;
861 }
862 }
863 else if (pVeh->m_vAngularVelocity < 0.0f)
864 {
865 pVeh->m_vAngularVelocity += pml.frametime;
866 if (pVeh->m_vAngularVelocity > 0.0f)
867 {
868 pVeh->m_vAngularVelocity = 0.0f;
869 }
870 }
871 }
872 PM_GroundTraceMissed();
873 }
874 //end vehicle functions crudely ported from sp -rww
875
876 /*
877 ===============
878 PM_AddEvent
879
880 ===============
881 */
PM_AddEvent(int newEvent)882 void PM_AddEvent( int newEvent ) {
883 BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
884 }
885
PM_AddEventWithParm(int newEvent,int parm)886 void PM_AddEventWithParm( int newEvent, int parm )
887 {
888 BG_AddPredictableEventToPlayerstate( newEvent, parm, pm->ps );
889 }
890
891 /*
892 ===============
893 PM_AddTouchEnt
894 ===============
895 */
PM_AddTouchEnt(int entityNum)896 void PM_AddTouchEnt( int entityNum ) {
897 int i;
898
899 if ( entityNum == ENTITYNUM_WORLD ) {
900 return;
901 }
902 if ( pm->numtouch >= MAXTOUCH ) {
903 return;
904 }
905
906 // see if it is already added
907 for ( i = 0 ; i < pm->numtouch ; i++ ) {
908 if ( pm->touchents[ i ] == entityNum ) {
909 return;
910 }
911 }
912
913 // add it
914 pm->touchents[pm->numtouch++] = entityNum;
915 }
916
917
918 /*
919 ==================
920 PM_ClipVelocity
921
922 Slide off of the impacting surface
923 ==================
924 */
PM_ClipVelocity(vec3_t in,vec3_t normal,vec3_t out,float overbounce)925 void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) {
926 float backoff;
927 float change;
928 float oldInZ;
929 int i;
930
931 if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
932 {//no sliding!
933 VectorCopy( in, out );
934 return;
935 }
936 oldInZ = in[2];
937
938 backoff = DotProduct (in, normal);
939
940 if ( backoff < 0 ) {
941 backoff *= overbounce;
942 } else {
943 backoff /= overbounce;
944 }
945
946 for ( i=0 ; i<3 ; i++ ) {
947 change = normal[i]*backoff;
948 out[i] = in[i] - change;
949 }
950 if ( pm->stepSlideFix )
951 {
952 if ( pm->ps->clientNum < MAX_CLIENTS//normal player
953 && pm->ps->groundEntityNum != ENTITYNUM_NONE//on the ground
954 && normal[2] < MIN_WALK_NORMAL )//sliding against a steep slope
955 {//if walking on the ground, don't slide up slopes that are too steep to walk on
956 out[2] = oldInZ;
957 }
958 }
959 }
960
961
962 /*
963 ==================
964 PM_Friction
965
966 Handles both ground friction and water friction
967 ==================
968 */
PM_Friction(void)969 static void PM_Friction( void ) {
970 vec3_t vec;
971 float *vel;
972 float speed, newspeed, control;
973 float drop;
974 bgEntity_t *pEnt = NULL;
975
976 vel = pm->ps->velocity;
977
978 VectorCopy( vel, vec );
979 if ( pml.walking ) {
980 vec[2] = 0; // ignore slope movement
981 }
982
983 speed = VectorLength(vec);
984 if (speed < 1) {
985 vel[0] = 0;
986 vel[1] = 0; // allow sinking underwater
987 if (pm->ps->pm_type == PM_SPECTATOR)
988 {
989 vel[2] = 0;
990 }
991 // FIXME: still have z friction underwater?
992 return;
993 }
994
995 drop = 0;
996
997 if (pm->ps->clientNum >= MAX_CLIENTS)
998 {
999 pEnt = pm_entSelf;
1000 }
1001
1002 // apply ground friction, even if on ladder
1003 if (pm_flying != FLY_VEHICLE &&
1004 pEnt &&
1005 pEnt->s.NPC_class == CLASS_VEHICLE &&
1006 pEnt->m_pVehicle &&
1007 pEnt->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL &&
1008 pEnt->m_pVehicle->m_pVehicleInfo->type != VH_WALKER &&
1009 pEnt->m_pVehicle->m_pVehicleInfo->friction )
1010 {
1011 float friction = pEnt->m_pVehicle->m_pVehicleInfo->friction;
1012 if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) /*&& !(pm->ps->pm_flags & PMF_TIME_NOFRICTION)*/ )
1013 {
1014 control = speed < pm_stopspeed ? pm_stopspeed : speed;
1015 drop += control*friction*pml.frametime;
1016 /*
1017 if ( Flying == FLY_HOVER )
1018 {
1019 if ( pm->cmd.rightmove )
1020 {//if turning, increase friction
1021 control *= 2.0f;
1022 }
1023 if ( pm->ps->groundEntityNum < ENTITYNUM_NONE )
1024 {//on the ground
1025 drop += control*friction*pml.frametime;
1026 }
1027 else if ( pml.groundPlane )
1028 {//on a slope
1029 drop += control*friction*2.0f*pml.frametime;
1030 }
1031 else
1032 {//in air
1033 drop += control*2.0f*friction*pml.frametime;
1034 }
1035 }
1036 */
1037 }
1038 }
1039 else if ( pm_flying != FLY_NORMAL && pm_flying != FLY_VEHICLE )
1040 {
1041 // apply ground friction
1042 if ( pm->waterlevel <= 1 ) {
1043 if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) {
1044 // if getting knocked back, no friction
1045 if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) {
1046 control = speed < pm_stopspeed ? pm_stopspeed : speed;
1047 drop += control*pm_friction*pml.frametime;
1048 }
1049 }
1050 }
1051 }
1052
1053 if ( pm_flying == FLY_VEHICLE )
1054 {
1055 if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) )
1056 {
1057 control = speed;// < pm_stopspeed ? pm_stopspeed : speed;
1058 drop += control*pm_friction*pml.frametime;
1059 }
1060 }
1061
1062 // apply water friction even if just wading
1063 if ( pm->waterlevel ) {
1064 drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
1065 }
1066 // If on a client then there is no friction
1067 else if ( pm->ps->groundEntityNum < MAX_CLIENTS )
1068 {
1069 drop = 0;
1070 }
1071
1072 if ( pm->ps->pm_type == PM_SPECTATOR || pm->ps->pm_type == PM_FLOAT )
1073 {
1074 if (pm->ps->pm_type == PM_FLOAT)
1075 { //almost no friction while floating
1076 drop += speed*0.1*pml.frametime;
1077 }
1078 else
1079 {
1080 drop += speed*pm_spectatorfriction*pml.frametime;
1081 }
1082 }
1083
1084 // scale the velocity
1085 newspeed = speed - drop;
1086 if (newspeed < 0) {
1087 newspeed = 0;
1088 }
1089 newspeed /= speed;
1090
1091 VectorScale( vel, newspeed, vel );
1092 }
1093
1094
1095 /*
1096 ==============
1097 PM_Accelerate
1098
1099 Handles user intended acceleration
1100 ==============
1101 */
PM_Accelerate(vec3_t wishdir,float wishspeed,float accel)1102 static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel )
1103 {
1104 if (pm->gametype != GT_SIEGE
1105 || pm->ps->m_iVehicleNum
1106 || pm->ps->clientNum >= MAX_CLIENTS
1107 || pm->ps->pm_type != PM_NORMAL)
1108 { //standard method, allows "bunnyhopping" and whatnot
1109 int i;
1110 float addspeed, accelspeed, currentspeed;
1111
1112 currentspeed = DotProduct (pm->ps->velocity, wishdir);
1113 addspeed = wishspeed - currentspeed;
1114 if (addspeed <= 0 && pm->ps->clientNum < MAX_CLIENTS) {
1115 return;
1116 }
1117
1118 if (addspeed < 0)
1119 {
1120 accelspeed = (-accel)*pml.frametime*wishspeed;
1121 if (accelspeed < addspeed) {
1122 accelspeed = addspeed;
1123 }
1124 }
1125 else
1126 {
1127 accelspeed = accel*pml.frametime*wishspeed;
1128 if (accelspeed > addspeed) {
1129 accelspeed = addspeed;
1130 }
1131 }
1132
1133 for (i=0 ; i<3 ; i++) {
1134 pm->ps->velocity[i] += accelspeed*wishdir[i];
1135 }
1136 }
1137 else
1138 { //use the proper way for siege
1139 vec3_t wishVelocity;
1140 vec3_t pushDir;
1141 float pushLen;
1142 float canPush;
1143
1144 VectorScale( wishdir, wishspeed, wishVelocity );
1145 VectorSubtract( wishVelocity, pm->ps->velocity, pushDir );
1146 pushLen = VectorNormalize( pushDir );
1147
1148 canPush = accel*pml.frametime*wishspeed;
1149 if (canPush > pushLen) {
1150 canPush = pushLen;
1151 }
1152
1153 VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity );
1154 }
1155 }
1156
1157
1158
1159 /*
1160 ============
1161 PM_CmdScale
1162
1163 Returns the scale factor to apply to cmd movements
1164 This allows the clients to use axial -127 to 127 values for all directions
1165 without getting a sqrt(2) distortion in speed.
1166 ============
1167 */
PM_CmdScale(usercmd_t * cmd)1168 static float PM_CmdScale( usercmd_t *cmd ) {
1169 int max;
1170 float total;
1171 float scale;
1172 int umove = 0; //cmd->upmove;
1173 //don't factor upmove into scaling speed
1174
1175 max = abs( cmd->forwardmove );
1176 if ( abs( cmd->rightmove ) > max ) {
1177 max = abs( cmd->rightmove );
1178 }
1179 if ( abs( umove ) > max ) {
1180 max = abs( umove );
1181 }
1182 if ( !max ) {
1183 return 0;
1184 }
1185
1186 total = sqrt( (float)(cmd->forwardmove * cmd->forwardmove
1187 + cmd->rightmove * cmd->rightmove + umove * umove) );
1188 scale = (float)pm->ps->speed * max / ( 127.0 * total );
1189
1190 return scale;
1191 }
1192
1193
1194 /*
1195 ================
1196 PM_SetMovementDir
1197
1198 Determine the rotation of the legs reletive
1199 to the facing dir
1200 ================
1201 */
PM_SetMovementDir(void)1202 static void PM_SetMovementDir( void ) {
1203 if ( pm->cmd.forwardmove || pm->cmd.rightmove ) {
1204 if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) {
1205 pm->ps->movementDir = 0;
1206 } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) {
1207 pm->ps->movementDir = 1;
1208 } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) {
1209 pm->ps->movementDir = 2;
1210 } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) {
1211 pm->ps->movementDir = 3;
1212 } else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) {
1213 pm->ps->movementDir = 4;
1214 } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) {
1215 pm->ps->movementDir = 5;
1216 } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) {
1217 pm->ps->movementDir = 6;
1218 } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) {
1219 pm->ps->movementDir = 7;
1220 }
1221 } else {
1222 // if they aren't actively going directly sideways,
1223 // change the animation to the diagonal so they
1224 // don't stop too crooked
1225 if ( pm->ps->movementDir == 2 ) {
1226 pm->ps->movementDir = 1;
1227 } else if ( pm->ps->movementDir == 6 ) {
1228 pm->ps->movementDir = 7;
1229 }
1230 }
1231 }
1232
1233 #define METROID_JUMP 1
1234
PM_ForceJumpingUp(void)1235 qboolean PM_ForceJumpingUp(void)
1236 {
1237 if ( !(pm->ps->fd.forcePowersActive&(1<<FP_LEVITATION)) && pm->ps->fd.forceJumpCharge )
1238 {//already jumped and let go
1239 return qfalse;
1240 }
1241
1242 if ( BG_InSpecialJump( pm->ps->legsAnim ) )
1243 {
1244 return qfalse;
1245 }
1246
1247 if (BG_SaberInSpecial(pm->ps->saberMove))
1248 {
1249 return qfalse;
1250 }
1251
1252 if (BG_SaberInSpecialAttack(pm->ps->legsAnim))
1253 {
1254 return qfalse;
1255 }
1256
1257 if (BG_HasYsalamiri(pm->gametype, pm->ps))
1258 {
1259 return qfalse;
1260 }
1261
1262 if (!BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION))
1263 {
1264 return qfalse;
1265 }
1266
1267 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE && //in air
1268 (pm->ps->pm_flags & PMF_JUMP_HELD) && //jumped
1269 pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 && //force-jump capable
1270 pm->ps->velocity[2] > 0 )//going up
1271 {
1272 return qtrue;
1273 }
1274 return qfalse;
1275 }
1276
PM_JumpForDir(void)1277 static void PM_JumpForDir( void )
1278 {
1279 int anim = BOTH_JUMP1;
1280 if ( pm->cmd.forwardmove > 0 )
1281 {
1282 anim = BOTH_JUMP1;
1283 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1284 }
1285 else if ( pm->cmd.forwardmove < 0 )
1286 {
1287 anim = BOTH_JUMPBACK1;
1288 pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
1289 }
1290 else if ( pm->cmd.rightmove > 0 )
1291 {
1292 anim = BOTH_JUMPRIGHT1;
1293 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1294 }
1295 else if ( pm->cmd.rightmove < 0 )
1296 {
1297 anim = BOTH_JUMPLEFT1;
1298 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1299 }
1300 else
1301 {
1302 anim = BOTH_JUMP1;
1303 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1304 }
1305 if(!BG_InDeathAnim(pm->ps->legsAnim))
1306 {
1307 PM_SetAnim(SETANIM_LEGS,anim,SETANIM_FLAG_OVERRIDE);
1308 }
1309 }
1310
PM_SetPMViewAngle(playerState_t * ps,vec3_t angle,usercmd_t * ucmd)1311 void PM_SetPMViewAngle(playerState_t *ps, vec3_t angle, usercmd_t *ucmd)
1312 {
1313 int i;
1314
1315 for (i=0 ; i<3 ; i++)
1316 { // set the delta angle
1317 int cmdAngle;
1318
1319 cmdAngle = ANGLE2SHORT(angle[i]);
1320 ps->delta_angles[i] = cmdAngle - ucmd->angles[i];
1321 }
1322 VectorCopy (angle, ps->viewangles);
1323 }
1324
PM_AdjustAngleForWallRun(playerState_t * ps,usercmd_t * ucmd,qboolean doMove)1325 qboolean PM_AdjustAngleForWallRun( playerState_t *ps, usercmd_t *ucmd, qboolean doMove )
1326 {
1327 if (( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT || (ps->legsAnim) == BOTH_WALL_RUN_LEFT ) && ps->legsTimer > 500 )
1328 {//wall-running and not at end of anim
1329 //stick to wall, if there is one
1330 vec3_t fwd, rt, traceTo, mins, maxs, fwdAngles;
1331 trace_t trace;
1332 float dist, yawAdjust;
1333
1334 VectorSet(mins, -15, -15, 0);
1335 VectorSet(maxs, 15, 15, 24);
1336 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
1337
1338 AngleVectors( fwdAngles, fwd, rt, NULL );
1339 if ( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT )
1340 {
1341 dist = 128;
1342 yawAdjust = -90;
1343 }
1344 else
1345 {
1346 dist = -128;
1347 yawAdjust = 90;
1348 }
1349 VectorMA( ps->origin, dist, rt, traceTo );
1350
1351 pm->trace( &trace, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
1352
1353 if ( trace.fraction < 1.0f
1354 && (trace.plane.normal[2] >= 0.0f && trace.plane.normal[2] <= 0.4f) )//&& ent->client->ps.groundEntityNum == ENTITYNUM_NONE )
1355 {
1356 trace_t trace2;
1357 vec3_t traceTo2;
1358 vec3_t wallRunFwd, wallRunAngles;
1359
1360 VectorClear( wallRunAngles );
1361 wallRunAngles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust;
1362 AngleVectors( wallRunAngles, wallRunFwd, NULL, NULL );
1363
1364 VectorMA( pm->ps->origin, 32, wallRunFwd, traceTo2 );
1365 pm->trace( &trace2, pm->ps->origin, mins, maxs, traceTo2, pm->ps->clientNum, MASK_PLAYERSOLID );
1366 if ( trace2.fraction < 1.0f && DotProduct( trace2.plane.normal, wallRunFwd ) <= -0.999f )
1367 {//wall we can't run on in front of us
1368 trace.fraction = 1.0f;//just a way to get it to kick us off the wall below
1369 }
1370 }
1371
1372 if ( trace.fraction < 1.0f
1373 && (trace.plane.normal[2] >= 0.0f&&trace.plane.normal[2] <= 0.4f/*MAX_WALL_RUN_Z_NORMAL*/) )
1374 {//still a wall there
1375 if ( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT )
1376 {
1377 ucmd->rightmove = 127;
1378 }
1379 else
1380 {
1381 ucmd->rightmove = -127;
1382 }
1383 if ( ucmd->upmove < 0 )
1384 {
1385 ucmd->upmove = 0;
1386 }
1387 //make me face perpendicular to the wall
1388 ps->viewangles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust;
1389
1390 PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
1391
1392 ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
1393 if ( doMove )
1394 {
1395 //push me forward
1396 float zVel = ps->velocity[2];
1397 if ( ps->legsTimer > 500 )
1398 {//not at end of anim yet
1399 float speed = 175;
1400 if ( ucmd->forwardmove < 0 )
1401 {//slower
1402 speed = 100;
1403 }
1404 else if ( ucmd->forwardmove > 0 )
1405 {
1406 speed = 250;//running speed
1407 }
1408 VectorScale( fwd, speed, ps->velocity );
1409 }
1410 ps->velocity[2] = zVel;//preserve z velocity
1411 //pull me toward the wall, too
1412 VectorMA( ps->velocity, dist, rt, ps->velocity );
1413 }
1414 ucmd->forwardmove = 0;
1415 return qtrue;
1416 }
1417 else if ( doMove )
1418 {//stop it
1419 if ( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT )
1420 {
1421 PM_SetAnim(SETANIM_BOTH, BOTH_WALL_RUN_RIGHT_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
1422 }
1423 else if ( (ps->legsAnim) == BOTH_WALL_RUN_LEFT )
1424 {
1425 PM_SetAnim(SETANIM_BOTH, BOTH_WALL_RUN_LEFT_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
1426 }
1427 }
1428 }
1429
1430 return qfalse;
1431 }
1432
PM_AdjustAnglesForWallRunUpFlipAlt(usercmd_t * ucmd)1433 qboolean PM_AdjustAnglesForWallRunUpFlipAlt( usercmd_t *ucmd )
1434 {
1435 // ucmd->angles[PITCH] = ANGLE2SHORT( pm->ps->viewangles[PITCH] ) - pm->ps->delta_angles[PITCH];
1436 // ucmd->angles[YAW] = ANGLE2SHORT( pm->ps->viewangles[YAW] ) - pm->ps->delta_angles[YAW];
1437 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, ucmd);
1438 return qtrue;
1439 }
1440
PM_AdjustAngleForWallRunUp(playerState_t * ps,usercmd_t * ucmd,qboolean doMove)1441 qboolean PM_AdjustAngleForWallRunUp( playerState_t *ps, usercmd_t *ucmd, qboolean doMove )
1442 {
1443 if ( ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START )
1444 {//wall-running up
1445 //stick to wall, if there is one
1446 vec3_t fwd, traceTo, mins, maxs, fwdAngles;
1447 trace_t trace;
1448 float dist = 128;
1449
1450 VectorSet(mins, -15,-15,0);
1451 VectorSet(maxs, 15,15,24);
1452 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
1453
1454 AngleVectors( fwdAngles, fwd, NULL, NULL );
1455 VectorMA( ps->origin, dist, fwd, traceTo );
1456 pm->trace( &trace, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
1457 if ( trace.fraction > 0.5f )
1458 {//hmm, some room, see if there's a floor right here
1459 trace_t trace2;
1460 vec3_t top, bottom;
1461
1462 VectorCopy( trace.endpos, top );
1463 top[2] += (pm->mins[2]*-1) + 4.0f;
1464 VectorCopy( top, bottom );
1465 bottom[2] -= 64.0f;
1466 pm->trace( &trace2, top, pm->mins, pm->maxs, bottom, ps->clientNum, MASK_PLAYERSOLID );
1467 if ( !trace2.allsolid
1468 && !trace2.startsolid
1469 && trace2.fraction < 1.0f
1470 && trace2.plane.normal[2] > 0.7f )//slope we can stand on
1471 {//cool, do the alt-flip and land on whetever it is we just scaled up
1472 VectorScale( fwd, 100, pm->ps->velocity );
1473 pm->ps->velocity[2] += 400;
1474 PM_SetAnim(SETANIM_BOTH, BOTH_FORCEWALLRUNFLIP_ALT, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
1475 pm->ps->pm_flags |= PMF_JUMP_HELD;
1476 //ent->client->ps.pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
1477 //ent->client->ps.forcePowersActive |= (1<<FP_LEVITATION);
1478 //G_AddEvent( ent, EV_JUMP, 0 );
1479 PM_AddEvent(EV_JUMP);
1480 ucmd->upmove = 0;
1481 return qfalse;
1482 }
1483 }
1484
1485 if ( //ucmd->upmove <= 0 &&
1486 ps->legsTimer > 0 &&
1487 ucmd->forwardmove > 0 &&
1488 trace.fraction < 1.0f &&
1489 (trace.plane.normal[2] >= 0.0f&&trace.plane.normal[2]<=0.4f/*MAX_WALL_RUN_Z_NORMAL*/) )
1490 {//still a vertical wall there
1491 //make sure there's not a ceiling above us!
1492 trace_t trace2;
1493 VectorCopy( ps->origin, traceTo );
1494 traceTo[2] += 64;
1495 pm->trace( &trace2, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
1496 if ( trace2.fraction < 1.0f )
1497 {//will hit a ceiling, so force jump-off right now
1498 //NOTE: hits any entity or clip brush in the way, too, not just architecture!
1499 }
1500 else
1501 {//all clear, keep going
1502 //FIXME: don't pull around 90 turns
1503 //FIXME: simulate stepping up steps here, somehow?
1504 ucmd->forwardmove = 127;
1505 if ( ucmd->upmove < 0 )
1506 {
1507 ucmd->upmove = 0;
1508 }
1509 //make me face the wall
1510 ps->viewangles[YAW] = vectoyaw( trace.plane.normal )+180;
1511 PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
1512 /*
1513 if ( ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD )
1514 {//don't clamp angles when looking through a viewEntity
1515 SetClientViewAngle( ent, ent->client->ps.viewangles );
1516 }
1517 */
1518 ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
1519 //if ( ent->s.number || !player_locked )
1520 if (1) //aslkfhsakf
1521 {
1522 if ( doMove )
1523 {
1524 //pull me toward the wall
1525 VectorScale( trace.plane.normal, -dist*trace.fraction, ps->velocity );
1526 //push me up
1527 if ( ps->legsTimer > 200 )
1528 {//not at end of anim yet
1529 float speed = 300;
1530 /*
1531 if ( ucmd->forwardmove < 0 )
1532 {//slower
1533 speed = 100;
1534 }
1535 else if ( ucmd->forwardmove > 0 )
1536 {
1537 speed = 250;//running speed
1538 }
1539 */
1540 ps->velocity[2] = speed;//preserve z velocity
1541 }
1542 }
1543 }
1544 ucmd->forwardmove = 0;
1545 return qtrue;
1546 }
1547 }
1548 //failed!
1549 if ( doMove )
1550 {//stop it
1551 VectorScale( fwd, -300.0f, ps->velocity );
1552 ps->velocity[2] += 200;
1553 //NPC_SetAnim( ent, SETANIM_BOTH, BOTH_FORCEWALLRUNFLIP_END, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1554 //why?!?#?#@!%$R@$KR#F:Hdl;asfm
1555 PM_SetAnim(SETANIM_BOTH, BOTH_FORCEWALLRUNFLIP_END, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
1556 ps->pm_flags |= PMF_JUMP_HELD;
1557 //ent->client->ps.pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
1558
1559 //FIXME do I need this in mp?
1560 //ent->client->ps.forcePowersActive |= (1<<FP_LEVITATION);
1561 PM_AddEvent(EV_JUMP);
1562 ucmd->upmove = 0;
1563 //return qtrue;
1564 }
1565 }
1566 return qfalse;
1567 }
1568
1569 #define JUMP_OFF_WALL_SPEED 200.0f
1570 //nice...
BG_ForceWallJumpStrength(void)1571 static float BG_ForceWallJumpStrength( void )
1572 {
1573 return (forceJumpStrength[FORCE_LEVEL_3]/2.5f);
1574 }
1575
PM_AdjustAngleForWallJump(playerState_t * ps,usercmd_t * ucmd,qboolean doMove)1576 qboolean PM_AdjustAngleForWallJump( playerState_t *ps, usercmd_t *ucmd, qboolean doMove )
1577 {
1578 if ( ( ( BG_InReboundJump( ps->legsAnim ) || BG_InReboundHold( ps->legsAnim ) )
1579 && ( BG_InReboundJump( ps->torsoAnim ) || BG_InReboundHold( ps->torsoAnim ) ) )
1580 || (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
1581 {//hugging wall, getting ready to jump off
1582 //stick to wall, if there is one
1583 vec3_t checkDir, traceTo, mins, maxs, fwdAngles;
1584 trace_t trace;
1585 float dist = 128.0f, yawAdjust;
1586
1587 VectorSet(mins, pm->mins[0],pm->mins[1],0);
1588 VectorSet(maxs, pm->maxs[0],pm->maxs[1],24);
1589 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
1590
1591 switch ( ps->legsAnim )
1592 {
1593 case BOTH_FORCEWALLREBOUND_RIGHT:
1594 case BOTH_FORCEWALLHOLD_RIGHT:
1595 AngleVectors( fwdAngles, NULL, checkDir, NULL );
1596 yawAdjust = -90;
1597 break;
1598 case BOTH_FORCEWALLREBOUND_LEFT:
1599 case BOTH_FORCEWALLHOLD_LEFT:
1600 AngleVectors( fwdAngles, NULL, checkDir, NULL );
1601 VectorScale( checkDir, -1, checkDir );
1602 yawAdjust = 90;
1603 break;
1604 case BOTH_FORCEWALLREBOUND_FORWARD:
1605 case BOTH_FORCEWALLHOLD_FORWARD:
1606 AngleVectors( fwdAngles, checkDir, NULL, NULL );
1607 yawAdjust = 180;
1608 break;
1609 case BOTH_FORCEWALLREBOUND_BACK:
1610 case BOTH_FORCEWALLHOLD_BACK:
1611 AngleVectors( fwdAngles, checkDir, NULL, NULL );
1612 VectorScale( checkDir, -1, checkDir );
1613 yawAdjust = 0;
1614 break;
1615 default:
1616 //WTF???
1617 pm->ps->pm_flags &= ~PMF_STUCK_TO_WALL;
1618 return qfalse;
1619 break;
1620 }
1621 if ( pm->debugMelee )
1622 {//uber-skillz
1623 if ( ucmd->upmove > 0 )
1624 {//hold on until you let go manually
1625 if ( BG_InReboundHold( ps->legsAnim ) )
1626 {//keep holding
1627 if ( ps->legsTimer < 150 )
1628 {
1629 ps->legsTimer = 150;
1630 }
1631 }
1632 else
1633 {//if got to hold part of anim, play hold anim
1634 if ( ps->legsTimer <= 300 )
1635 {
1636 ps->saberHolstered = 2;
1637 PM_SetAnim( SETANIM_BOTH, BOTH_FORCEWALLRELEASE_FORWARD+(ps->legsAnim-BOTH_FORCEWALLHOLD_FORWARD), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1638 ps->legsTimer = ps->torsoTimer = 150;
1639 }
1640 }
1641 }
1642 }
1643 VectorMA( ps->origin, dist, checkDir, traceTo );
1644 pm->trace( &trace, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
1645 if ( //ucmd->upmove <= 0 &&
1646 ps->legsTimer > 100 &&
1647 trace.fraction < 1.0f &&
1648 fabs(trace.plane.normal[2]) <= 0.2f/*MAX_WALL_GRAB_SLOPE*/ )
1649 {//still a vertical wall there
1650 //FIXME: don't pull around 90 turns
1651 /*
1652 if ( ent->s.number || !player_locked )
1653 {
1654 ucmd->forwardmove = 127;
1655 }
1656 */
1657 if ( ucmd->upmove < 0 )
1658 {
1659 ucmd->upmove = 0;
1660 }
1661 //align me to the wall
1662 ps->viewangles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust;
1663 PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
1664 /*
1665 if ( ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD )
1666 {//don't clamp angles when looking through a viewEntity
1667 SetClientViewAngle( ent, ent->client->ps.viewangles );
1668 }
1669 */
1670 ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
1671 //if ( ent->s.number || !player_locked )
1672 if (1)
1673 {
1674 if ( doMove )
1675 {
1676 //pull me toward the wall
1677 VectorScale( trace.plane.normal, -128.0f, ps->velocity );
1678 }
1679 }
1680 ucmd->upmove = 0;
1681 ps->pm_flags |= PMF_STUCK_TO_WALL;
1682 return qtrue;
1683 }
1684 else if ( doMove
1685 && (ps->pm_flags&PMF_STUCK_TO_WALL))
1686 {//jump off
1687 //push off of it!
1688 ps->pm_flags &= ~PMF_STUCK_TO_WALL;
1689 ps->velocity[0] = ps->velocity[1] = 0;
1690 VectorScale( checkDir, -JUMP_OFF_WALL_SPEED, ps->velocity );
1691 ps->velocity[2] = BG_ForceWallJumpStrength();
1692 ps->pm_flags |= PMF_JUMP_HELD;//PMF_JUMPING|PMF_JUMP_HELD;
1693 //G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
1694 ps->fd.forceJumpSound = 1; //this is a stupid thing, i should fix it.
1695 //ent->client->ps.forcePowersActive |= (1<<FP_LEVITATION);
1696 if (ps->origin[2] < ps->fd.forceJumpZStart)
1697 {
1698 ps->fd.forceJumpZStart = ps->origin[2];
1699 }
1700 //FIXME do I need this?
1701
1702 BG_ForcePowerDrain( ps, FP_LEVITATION, 10 );
1703 //no control for half a second
1704 ps->pm_flags |= PMF_TIME_KNOCKBACK;
1705 ps->pm_time = 500;
1706 ucmd->forwardmove = 0;
1707 ucmd->rightmove = 0;
1708 ucmd->upmove = 127;
1709
1710 if ( BG_InReboundHold( ps->legsAnim ) )
1711 {//if was in hold pose, release now
1712 PM_SetAnim( SETANIM_BOTH, BOTH_FORCEWALLRELEASE_FORWARD+(ps->legsAnim-BOTH_FORCEWALLHOLD_FORWARD), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1713 }
1714 else
1715 {
1716 //PM_JumpForDir();
1717 PM_SetAnim(SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART);
1718 }
1719
1720 //return qtrue;
1721 }
1722 }
1723 ps->pm_flags &= ~PMF_STUCK_TO_WALL;
1724 return qfalse;
1725 }
1726
1727 //Set the height for when a force jump was started. If it's 0, nuge it up (slight hack to prevent holding jump over slopes)
PM_SetForceJumpZStart(float value)1728 void PM_SetForceJumpZStart(float value)
1729 {
1730 pm->ps->fd.forceJumpZStart = value;
1731 if (!pm->ps->fd.forceJumpZStart)
1732 {
1733 pm->ps->fd.forceJumpZStart -= 0.1f;
1734 }
1735 }
1736
1737 float forceJumpHeightMax[NUM_FORCE_POWER_LEVELS] =
1738 {
1739 66,//normal jump (32+stepheight(18)+crouchdiff(24) = 74)
1740 130,//(96+stepheight(18)+crouchdiff(24) = 138)
1741 226,//(192+stepheight(18)+crouchdiff(24) = 234)
1742 418//(384+stepheight(18)+crouchdiff(24) = 426)
1743 };
1744
PM_GrabWallForJump(int anim)1745 void PM_GrabWallForJump( int anim )
1746 {//NOTE!!! assumes an appropriate anim is being passed in!!!
1747 PM_SetAnim( SETANIM_BOTH, anim, SETANIM_FLAG_RESTART|SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1748 PM_AddEvent( EV_JUMP );//make sound for grab
1749 pm->ps->pm_flags |= PMF_STUCK_TO_WALL;
1750 }
1751
1752 /*
1753 =============
1754 PM_CheckJump
1755 =============
1756 */
PM_CheckJump(void)1757 static qboolean PM_CheckJump( void )
1758 {
1759 qboolean allowFlips = qtrue;
1760
1761 if (pm->ps->clientNum >= MAX_CLIENTS)
1762 {
1763 bgEntity_t *pEnt = pm_entSelf;
1764
1765 if (pEnt->s.eType == ET_NPC &&
1766 pEnt->s.NPC_class == CLASS_VEHICLE)
1767 { //no!
1768 return qfalse;
1769 }
1770 }
1771
1772 if (pm->ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
1773 pm->ps->forceHandExtend == HANDEXTEND_PRETHROWN ||
1774 pm->ps->forceHandExtend == HANDEXTEND_POSTTHROWN)
1775 {
1776 return qfalse;
1777 }
1778
1779 if (pm->ps->pm_type == PM_JETPACK)
1780 { //there's no actual jumping while we jetpack
1781 return qfalse;
1782 }
1783
1784 //Don't allow jump until all buttons are up
1785 if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
1786 return qfalse;
1787 }
1788
1789 if ( PM_InKnockDown( pm->ps ) || BG_InRoll( pm->ps, pm->ps->legsAnim ) )
1790 {//in knockdown
1791 return qfalse;
1792 }
1793
1794 if ( pm->ps->weapon == WP_SABER )
1795 {
1796 saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 );
1797 saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 );
1798 if ( saber1
1799 && (saber1->saberFlags&SFL_NO_FLIPS) )
1800 {
1801 allowFlips = qfalse;
1802 }
1803 if ( saber2
1804 && (saber2->saberFlags&SFL_NO_FLIPS) )
1805 {
1806 allowFlips = qfalse;
1807 }
1808 }
1809
1810 if (pm->ps->groundEntityNum != ENTITYNUM_NONE || pm->ps->origin[2] < pm->ps->fd.forceJumpZStart)
1811 {
1812 pm->ps->fd.forcePowersActive &= ~(1<<FP_LEVITATION);
1813 }
1814
1815 if (pm->ps->fd.forcePowersActive & (1 << FP_LEVITATION))
1816 { //Force jump is already active.. continue draining power appropriately until we land.
1817 if (pm->ps->fd.forcePowerDebounce[FP_LEVITATION] < pm->cmd.serverTime)
1818 {
1819 if ( pm->gametype == GT_DUEL
1820 || pm->gametype == GT_POWERDUEL )
1821 {//jump takes less power
1822 BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 1 );
1823 }
1824 else
1825 {
1826 BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 5 );
1827 }
1828 if (pm->ps->fd.forcePowerLevel[FP_LEVITATION] >= FORCE_LEVEL_2)
1829 {
1830 pm->ps->fd.forcePowerDebounce[FP_LEVITATION] = pm->cmd.serverTime + 300;
1831 }
1832 else
1833 {
1834 pm->ps->fd.forcePowerDebounce[FP_LEVITATION] = pm->cmd.serverTime + 200;
1835 }
1836 }
1837 }
1838
1839 if (pm->ps->forceJumpFlip)
1840 { //Forced jump anim
1841 int anim = BOTH_FORCEINAIR1;
1842 int parts = SETANIM_BOTH;
1843 if ( allowFlips )
1844 {
1845 if ( pm->cmd.forwardmove > 0 )
1846 {
1847 anim = BOTH_FLIP_F;
1848 }
1849 else if ( pm->cmd.forwardmove < 0 )
1850 {
1851 anim = BOTH_FLIP_B;
1852 }
1853 else if ( pm->cmd.rightmove > 0 )
1854 {
1855 anim = BOTH_FLIP_R;
1856 }
1857 else if ( pm->cmd.rightmove < 0 )
1858 {
1859 anim = BOTH_FLIP_L;
1860 }
1861 }
1862 else
1863 {
1864 if ( pm->cmd.forwardmove > 0 )
1865 {
1866 anim = BOTH_FORCEINAIR1;
1867 }
1868 else if ( pm->cmd.forwardmove < 0 )
1869 {
1870 anim = BOTH_FORCEINAIRBACK1;
1871 }
1872 else if ( pm->cmd.rightmove > 0 )
1873 {
1874 anim = BOTH_FORCEINAIRRIGHT1;
1875 }
1876 else if ( pm->cmd.rightmove < 0 )
1877 {
1878 anim = BOTH_FORCEINAIRLEFT1;
1879 }
1880 }
1881 if ( pm->ps->weaponTime )
1882 {//FIXME: really only care if we're in a saber attack anim...
1883 parts = SETANIM_LEGS;
1884 }
1885
1886 PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1887 pm->ps->forceJumpFlip = qfalse;
1888 return qtrue;
1889 }
1890 #if METROID_JUMP
1891 if ( pm->waterlevel < 2 )
1892 {
1893 if ( pm->ps->gravity > 0 )
1894 {//can't do this in zero-G
1895 if ( PM_ForceJumpingUp() )
1896 {//holding jump in air
1897 float curHeight = pm->ps->origin[2] - pm->ps->fd.forceJumpZStart;
1898 //check for max force jump level and cap off & cut z vel
1899 if ( ( curHeight<=forceJumpHeight[0] ||//still below minimum jump height
1900 (pm->ps->fd.forcePower&&pm->cmd.upmove>=10) ) &&////still have force power available and still trying to jump up
1901 curHeight < forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]] &&
1902 pm->ps->fd.forceJumpZStart)//still below maximum jump height
1903 {//can still go up
1904 if ( curHeight > forceJumpHeight[0] )
1905 {//passed normal jump height *2?
1906 if ( !(pm->ps->fd.forcePowersActive&(1<<FP_LEVITATION)) )//haven't started forcejump yet
1907 {
1908 //start force jump
1909 pm->ps->fd.forcePowersActive |= (1<<FP_LEVITATION);
1910 pm->ps->fd.forceJumpSound = 1;
1911 //play flip
1912 if ((pm->cmd.forwardmove || pm->cmd.rightmove) && //pushing in a dir
1913 (pm->ps->legsAnim) != BOTH_FLIP_F &&//not already flipping
1914 (pm->ps->legsAnim) != BOTH_FLIP_B &&
1915 (pm->ps->legsAnim) != BOTH_FLIP_R &&
1916 (pm->ps->legsAnim) != BOTH_FLIP_L
1917 && allowFlips )
1918 {
1919 int anim = BOTH_FORCEINAIR1;
1920 int parts = SETANIM_BOTH;
1921
1922 if ( pm->cmd.forwardmove > 0 )
1923 {
1924 anim = BOTH_FLIP_F;
1925 }
1926 else if ( pm->cmd.forwardmove < 0 )
1927 {
1928 anim = BOTH_FLIP_B;
1929 }
1930 else if ( pm->cmd.rightmove > 0 )
1931 {
1932 anim = BOTH_FLIP_R;
1933 }
1934 else if ( pm->cmd.rightmove < 0 )
1935 {
1936 anim = BOTH_FLIP_L;
1937 }
1938 if ( pm->ps->weaponTime )
1939 {
1940 parts = SETANIM_LEGS;
1941 }
1942
1943 PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1944 }
1945 else if ( pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
1946 {
1947 vec3_t facingFwd, facingRight, facingAngles;
1948 int anim = -1;
1949 float dotR, dotF;
1950
1951 VectorSet(facingAngles, 0, pm->ps->viewangles[YAW], 0);
1952
1953 AngleVectors( facingAngles, facingFwd, facingRight, NULL );
1954 dotR = DotProduct( facingRight, pm->ps->velocity );
1955 dotF = DotProduct( facingFwd, pm->ps->velocity );
1956
1957 if ( fabs(dotR) > fabs(dotF) * 1.5 )
1958 {
1959 if ( dotR > 150 )
1960 {
1961 anim = BOTH_FORCEJUMPRIGHT1;
1962 }
1963 else if ( dotR < -150 )
1964 {
1965 anim = BOTH_FORCEJUMPLEFT1;
1966 }
1967 }
1968 else
1969 {
1970 if ( dotF > 150 )
1971 {
1972 anim = BOTH_FORCEJUMP1;
1973 }
1974 else if ( dotF < -150 )
1975 {
1976 anim = BOTH_FORCEJUMPBACK1;
1977 }
1978 }
1979 if ( anim != -1 )
1980 {
1981 int parts = SETANIM_BOTH;
1982 if ( pm->ps->weaponTime )
1983 {//FIXME: really only care if we're in a saber attack anim...
1984 parts = SETANIM_LEGS;
1985 }
1986
1987 PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1988 }
1989 }
1990 }
1991 else
1992 { //jump is already active (the anim has started)
1993 if ( pm->ps->legsTimer < 1 )
1994 {//not in the middle of a legsAnim
1995 int anim = (pm->ps->legsAnim);
1996 int newAnim = -1;
1997 switch ( anim )
1998 {
1999 case BOTH_FORCEJUMP1:
2000 newAnim = BOTH_FORCELAND1;//BOTH_FORCEINAIR1;
2001 break;
2002 case BOTH_FORCEJUMPBACK1:
2003 newAnim = BOTH_FORCELANDBACK1;//BOTH_FORCEINAIRBACK1;
2004 break;
2005 case BOTH_FORCEJUMPLEFT1:
2006 newAnim = BOTH_FORCELANDLEFT1;//BOTH_FORCEINAIRLEFT1;
2007 break;
2008 case BOTH_FORCEJUMPRIGHT1:
2009 newAnim = BOTH_FORCELANDRIGHT1;//BOTH_FORCEINAIRRIGHT1;
2010 break;
2011 }
2012 if ( newAnim != -1 )
2013 {
2014 int parts = SETANIM_BOTH;
2015 if ( pm->ps->weaponTime )
2016 {
2017 parts = SETANIM_LEGS;
2018 }
2019
2020 PM_SetAnim( parts, newAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2021 }
2022 }
2023 }
2024 }
2025
2026 //need to scale this down, start with height velocity (based on max force jump height) and scale down to regular jump vel
2027 pm->ps->velocity[2] = (forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]]-curHeight)/forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]]*forceJumpStrength[pm->ps->fd.forcePowerLevel[FP_LEVITATION]];//JUMP_VELOCITY;
2028 pm->ps->velocity[2] /= 10;
2029 pm->ps->velocity[2] += JUMP_VELOCITY;
2030 pm->ps->pm_flags |= PMF_JUMP_HELD;
2031 }
2032 else if ( curHeight > forceJumpHeight[0] && curHeight < forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]] - forceJumpHeight[0] )
2033 {//still have some headroom, don't totally stop it
2034 if ( pm->ps->velocity[2] > JUMP_VELOCITY )
2035 {
2036 pm->ps->velocity[2] = JUMP_VELOCITY;
2037 }
2038 }
2039 else
2040 {
2041 //pm->ps->velocity[2] = 0;
2042 //rww - changed for the sake of balance in multiplayer
2043
2044 if ( pm->ps->velocity[2] > JUMP_VELOCITY )
2045 {
2046 pm->ps->velocity[2] = JUMP_VELOCITY;
2047 }
2048 }
2049 pm->cmd.upmove = 0;
2050 return qfalse;
2051 }
2052 }
2053 }
2054
2055 #endif
2056
2057 //Not jumping
2058 if ( pm->cmd.upmove < 10 && pm->ps->groundEntityNum != ENTITYNUM_NONE) {
2059 return qfalse;
2060 }
2061
2062 // must wait for jump to be released
2063 if ( pm->ps->pm_flags & PMF_JUMP_HELD )
2064 {
2065 // clear upmove so cmdscale doesn't lower running speed
2066 pm->cmd.upmove = 0;
2067 return qfalse;
2068 }
2069
2070 if ( pm->ps->gravity <= 0 )
2071 {//in low grav, you push in the dir you're facing as long as there is something behind you to shove off of
2072 vec3_t forward, back;
2073 trace_t trace;
2074
2075 AngleVectors( pm->ps->viewangles, forward, NULL, NULL );
2076 VectorMA( pm->ps->origin, -8, forward, back );
2077 pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, back, pm->ps->clientNum, pm->tracemask );
2078
2079 if ( trace.fraction <= 1.0f )
2080 {
2081 VectorMA( pm->ps->velocity, JUMP_VELOCITY*2, forward, pm->ps->velocity );
2082 PM_SetAnim(SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART);
2083 }//else no surf close enough to push off of
2084 pm->cmd.upmove = 0;
2085 }
2086 else if ( pm->cmd.upmove > 0 && pm->waterlevel < 2 &&
2087 pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 &&
2088 !(pm->ps->pm_flags&PMF_JUMP_HELD) &&
2089 (pm->ps->weapon == WP_SABER || pm->ps->weapon == WP_MELEE) &&
2090 !PM_IsRocketTrooper() &&
2091 !BG_HasYsalamiri(pm->gametype, pm->ps) &&
2092 BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION) )
2093 {
2094 qboolean allowWallRuns = qtrue;
2095 qboolean allowWallFlips = qtrue;
2096 // qboolean allowFlips = qtrue;
2097 qboolean allowWallGrabs = qtrue;
2098 if ( pm->ps->weapon == WP_SABER )
2099 {
2100 saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 );
2101 saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 );
2102 if ( saber1
2103 && (saber1->saberFlags&SFL_NO_WALL_RUNS) )
2104 {
2105 allowWallRuns = qfalse;
2106 }
2107 if ( saber2
2108 && (saber2->saberFlags&SFL_NO_WALL_RUNS) )
2109 {
2110 allowWallRuns = qfalse;
2111 }
2112 if ( saber1
2113 && (saber1->saberFlags&SFL_NO_WALL_FLIPS) )
2114 {
2115 allowWallFlips = qfalse;
2116 }
2117 if ( saber2
2118 && (saber2->saberFlags&SFL_NO_WALL_FLIPS) )
2119 {
2120 allowWallFlips = qfalse;
2121 }
2122 if ( saber1
2123 && (saber1->saberFlags&SFL_NO_FLIPS) )
2124 {
2125 allowFlips = qfalse;
2126 }
2127 if ( saber2
2128 && (saber2->saberFlags&SFL_NO_FLIPS) )
2129 {
2130 allowFlips = qfalse;
2131 }
2132 if ( saber1
2133 && (saber1->saberFlags&SFL_NO_WALL_GRAB) )
2134 {
2135 allowWallGrabs = qfalse;
2136 }
2137 if ( saber2
2138 && (saber2->saberFlags&SFL_NO_WALL_GRAB) )
2139 {
2140 allowWallGrabs = qfalse;
2141 }
2142 }
2143
2144 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
2145 {//on the ground
2146 //check for left-wall and right-wall special jumps
2147 int anim = -1;
2148 float vertPush = 0;
2149 if ( pm->cmd.rightmove > 0 && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
2150 {//strafing right
2151 if ( pm->cmd.forwardmove > 0 )
2152 {//wall-run
2153 if ( allowWallRuns )
2154 {
2155 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
2156 anim = BOTH_WALL_RUN_RIGHT;
2157 }
2158 }
2159 else if ( pm->cmd.forwardmove == 0 )
2160 {//wall-flip
2161 if ( allowWallFlips )
2162 {
2163 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
2164 anim = BOTH_WALL_FLIP_RIGHT;
2165 }
2166 }
2167 }
2168 else if ( pm->cmd.rightmove < 0 && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
2169 {//strafing left
2170 if ( pm->cmd.forwardmove > 0 )
2171 {//wall-run
2172 if ( allowWallRuns )
2173 {
2174 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
2175 anim = BOTH_WALL_RUN_LEFT;
2176 }
2177 }
2178 else if ( pm->cmd.forwardmove == 0 )
2179 {//wall-flip
2180 if ( allowWallFlips )
2181 {
2182 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
2183 anim = BOTH_WALL_FLIP_LEFT;
2184 }
2185 }
2186 }
2187 else if ( pm->cmd.forwardmove < 0 && !(pm->cmd.buttons&BUTTON_ATTACK) )
2188 {//backflip
2189 if ( allowFlips )
2190 {
2191 vertPush = JUMP_VELOCITY;
2192 anim = BOTH_FLIP_BACK1;//BG_PickAnim( BOTH_FLIP_BACK1, BOTH_FLIP_BACK3 );
2193 }
2194 }
2195
2196 vertPush += 128; //give them an extra shove
2197
2198 if ( anim != -1 )
2199 {
2200 vec3_t fwd, right, traceto, mins, maxs, fwdAngles;
2201 vec3_t idealNormal={0}, wallNormal={0};
2202 trace_t trace;
2203 qboolean doTrace = qfalse;
2204 int contents = MASK_SOLID;//MASK_PLAYERSOLID;
2205
2206 VectorSet(mins, pm->mins[0],pm->mins[1],0);
2207 VectorSet(maxs, pm->maxs[0],pm->maxs[1],24);
2208 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
2209
2210 memset(&trace, 0, sizeof(trace)); //to shut the compiler up
2211
2212 AngleVectors( fwdAngles, fwd, right, NULL );
2213
2214 //trace-check for a wall, if necc.
2215 switch ( anim )
2216 {
2217 case BOTH_WALL_FLIP_LEFT:
2218 //NOTE: purposely falls through to next case!
2219 case BOTH_WALL_RUN_LEFT:
2220 doTrace = qtrue;
2221 VectorMA( pm->ps->origin, -16, right, traceto );
2222 break;
2223
2224 case BOTH_WALL_FLIP_RIGHT:
2225 //NOTE: purposely falls through to next case!
2226 case BOTH_WALL_RUN_RIGHT:
2227 doTrace = qtrue;
2228 VectorMA( pm->ps->origin, 16, right, traceto );
2229 break;
2230
2231 case BOTH_WALL_FLIP_BACK1:
2232 doTrace = qtrue;
2233 VectorMA( pm->ps->origin, 16, fwd, traceto );
2234 break;
2235 }
2236
2237 if ( doTrace )
2238 {
2239 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents );
2240 VectorCopy( trace.plane.normal, wallNormal );
2241 VectorNormalize( wallNormal );
2242 VectorSubtract( pm->ps->origin, traceto, idealNormal );
2243 VectorNormalize( idealNormal );
2244 }
2245
2246 if ( !doTrace || (trace.fraction < 1.0f && (trace.entityNum < MAX_CLIENTS || DotProduct(wallNormal,idealNormal) > 0.7)) )
2247 {//there is a wall there.. or hit a client
2248 if ( (anim != BOTH_WALL_RUN_LEFT
2249 && anim != BOTH_WALL_RUN_RIGHT
2250 && anim != BOTH_FORCEWALLRUNFLIP_START)
2251 || (wallNormal[2] >= 0.0f&&wallNormal[2]<=0.4f/*MAX_WALL_RUN_Z_NORMAL*/) )
2252 {//wall-runs can only run on perfectly flat walls, sorry.
2253 int parts;
2254 //move me to side
2255 if ( anim == BOTH_WALL_FLIP_LEFT )
2256 {
2257 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2258 VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity );
2259 }
2260 else if ( anim == BOTH_WALL_FLIP_RIGHT )
2261 {
2262 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2263 VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity );
2264 }
2265 else if ( anim == BOTH_FLIP_BACK1
2266 || anim == BOTH_FLIP_BACK2
2267 || anim == BOTH_FLIP_BACK3
2268 || anim == BOTH_WALL_FLIP_BACK1 )
2269 {
2270 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2271 VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
2272 }
2273
2274 /*
2275 if ( doTrace && anim != BOTH_WALL_RUN_LEFT && anim != BOTH_WALL_RUN_RIGHT )
2276 {
2277 if (trace.entityNum < MAX_CLIENTS)
2278 {
2279 pm->ps->forceKickFlip = trace.entityNum+1; //let the server know that this person gets kicked by this client
2280 }
2281 }
2282 */
2283
2284 //up
2285 if ( vertPush )
2286 {
2287 pm->ps->velocity[2] = vertPush;
2288 pm->ps->fd.forcePowersActive |= (1 << FP_LEVITATION);
2289 }
2290 //animate me
2291 parts = SETANIM_LEGS;
2292 if ( anim == BOTH_BUTTERFLY_LEFT )
2293 {
2294 parts = SETANIM_BOTH;
2295 pm->cmd.buttons&=~BUTTON_ATTACK;
2296 pm->ps->saberMove = LS_NONE;
2297 }
2298 else if ( !pm->ps->weaponTime )
2299 {
2300 parts = SETANIM_BOTH;
2301 }
2302 PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2303 if ( anim == BOTH_BUTTERFLY_LEFT )
2304 {
2305 pm->ps->weaponTime = pm->ps->torsoTimer;
2306 }
2307 PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
2308 pm->ps->pm_flags |= PMF_JUMP_HELD;
2309 pm->cmd.upmove = 0;
2310 pm->ps->fd.forceJumpSound = 1;
2311 }
2312 }
2313 }
2314 }
2315 else
2316 {//in the air
2317 int legsAnim = pm->ps->legsAnim;
2318
2319 if ( legsAnim == BOTH_WALL_RUN_LEFT || legsAnim == BOTH_WALL_RUN_RIGHT )
2320 {//running on a wall
2321 vec3_t right, traceto, mins, maxs, fwdAngles;
2322 trace_t trace;
2323 int anim = -1;
2324
2325 VectorSet(mins, pm->mins[0], pm->mins[0], 0);
2326 VectorSet(maxs, pm->maxs[0], pm->maxs[0], 24);
2327 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
2328
2329 AngleVectors( fwdAngles, NULL, right, NULL );
2330
2331 if ( legsAnim == BOTH_WALL_RUN_LEFT )
2332 {
2333 if ( pm->ps->legsTimer > 400 )
2334 {//not at the end of the anim
2335 float animLen = PM_AnimLength( 0, (animNumber_t)BOTH_WALL_RUN_LEFT );
2336 if ( pm->ps->legsTimer < animLen - 400 )
2337 {//not at start of anim
2338 VectorMA( pm->ps->origin, -16, right, traceto );
2339 anim = BOTH_WALL_RUN_LEFT_FLIP;
2340 }
2341 }
2342 }
2343 else if ( legsAnim == BOTH_WALL_RUN_RIGHT )
2344 {
2345 if ( pm->ps->legsTimer > 400 )
2346 {//not at the end of the anim
2347 float animLen = PM_AnimLength( 0, (animNumber_t)BOTH_WALL_RUN_RIGHT );
2348 if ( pm->ps->legsTimer < animLen - 400 )
2349 {//not at start of anim
2350 VectorMA( pm->ps->origin, 16, right, traceto );
2351 anim = BOTH_WALL_RUN_RIGHT_FLIP;
2352 }
2353 }
2354 }
2355 if ( anim != -1 )
2356 {
2357 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY );
2358 if ( trace.fraction < 1.0f )
2359 {//flip off wall
2360 int parts = 0;
2361
2362 if ( anim == BOTH_WALL_RUN_LEFT_FLIP )
2363 {
2364 pm->ps->velocity[0] *= 0.5f;
2365 pm->ps->velocity[1] *= 0.5f;
2366 VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity );
2367 }
2368 else if ( anim == BOTH_WALL_RUN_RIGHT_FLIP )
2369 {
2370 pm->ps->velocity[0] *= 0.5f;
2371 pm->ps->velocity[1] *= 0.5f;
2372 VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity );
2373 }
2374 parts = SETANIM_LEGS;
2375 if ( !pm->ps->weaponTime )
2376 {
2377 parts = SETANIM_BOTH;
2378 }
2379 PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2380 pm->cmd.upmove = 0;
2381 }
2382 }
2383 if ( pm->cmd.upmove != 0 )
2384 {//jump failed, so don't try to do normal jump code, just return
2385 return qfalse;
2386 }
2387 }
2388 //NEW JKA
2389 else if ( pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START )
2390 {
2391 vec3_t fwd, traceto, mins, maxs, fwdAngles;
2392 trace_t trace;
2393 int anim = -1;
2394 float animLen;
2395
2396 VectorSet(mins, pm->mins[0], pm->mins[0], 0.0f);
2397 VectorSet(maxs, pm->maxs[0], pm->maxs[0], 24.0f);
2398 //hmm, did you mean [1] and [1]?
2399 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0.0f);
2400 AngleVectors( fwdAngles, fwd, NULL, NULL );
2401
2402 assert(pm_entSelf); //null pm_entSelf would be a Bad Thing<tm>
2403 animLen = BG_AnimLength( pm_entSelf->localAnimIndex, BOTH_FORCEWALLRUNFLIP_START );
2404 if ( pm->ps->legsTimer < animLen - 400 )
2405 {//not at start of anim
2406 VectorMA( pm->ps->origin, 16, fwd, traceto );
2407 anim = BOTH_FORCEWALLRUNFLIP_END;
2408 }
2409 if ( anim != -1 )
2410 {
2411 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY );
2412 if ( trace.fraction < 1.0f )
2413 {//flip off wall
2414 int parts = SETANIM_LEGS;
2415
2416 pm->ps->velocity[0] *= 0.5f;
2417 pm->ps->velocity[1] *= 0.5f;
2418 VectorMA( pm->ps->velocity, -300, fwd, pm->ps->velocity );
2419 pm->ps->velocity[2] += 200;
2420 if ( !pm->ps->weaponTime )
2421 {//not attacking, set anim on both
2422 parts = SETANIM_BOTH;
2423 }
2424 PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2425 //FIXME: do damage to traceEnt, like above?
2426 //pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
2427 //ha ha, so silly with your silly jumpy fally flags.
2428 pm->cmd.upmove = 0;
2429 PM_AddEvent( EV_JUMP );
2430 }
2431 }
2432 if ( pm->cmd.upmove != 0 )
2433 {//jump failed, so don't try to do normal jump code, just return
2434 return qfalse;
2435 }
2436 }
2437 /*
2438 else if ( pm->cmd.forwardmove > 0 //pushing forward
2439 && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1
2440 && pm->ps->velocity[2] > 200
2441 && PM_GroundDistance() <= 80 //unfortunately we do not have a happy ground timer like SP (this would use up more bandwidth if we wanted prediction workign right), so we'll just use the actual ground distance.
2442 && !BG_InSpecialJump(pm->ps->legsAnim))
2443 {//run up wall, flip backwards
2444 vec3_t fwd, traceto, mins, maxs, fwdAngles;
2445 trace_t trace;
2446 vec3_t idealNormal;
2447
2448 VectorSet(mins, pm->mins[0],pm->mins[1],pm->mins[2]);
2449 VectorSet(maxs, pm->maxs[0],pm->maxs[1],pm->maxs[2]);
2450 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
2451
2452 AngleVectors( fwdAngles, fwd, NULL, NULL );
2453 VectorMA( pm->ps->origin, 32, fwd, traceto );
2454
2455 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, MASK_PLAYERSOLID );//FIXME: clip brushes too?
2456 VectorSubtract( pm->ps->origin, traceto, idealNormal );
2457 VectorNormalize( idealNormal );
2458
2459 if ( trace.fraction < 1.0f )
2460 {//there is a wall there
2461 int parts = SETANIM_LEGS;
2462
2463 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2464 VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
2465 pm->ps->velocity[2] += 128;
2466
2467 if ( !pm->ps->weaponTime )
2468 {
2469 parts = SETANIM_BOTH;
2470 }
2471 PM_SetAnim( parts, BOTH_WALL_FLIP_BACK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2472
2473 pm->ps->legsTimer -= 600; //I force this anim to play to the end to prevent landing on your head and suddenly flipping over.
2474 //It is a bit too long at the end though, so I'll just shorten it.
2475
2476 PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
2477 pm->cmd.upmove = 0;
2478 pm->ps->fd.forceJumpSound = 1;
2479 BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 5 );
2480
2481 if (trace.entityNum < MAX_CLIENTS)
2482 {
2483 pm->ps->forceKickFlip = trace.entityNum+1; //let the server know that this person gets kicked by this client
2484 }
2485 }
2486 }
2487 */
2488 else if ( pm->cmd.forwardmove > 0 //pushing forward
2489 && pm->ps->fd.forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period
2490 && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1
2491 && PM_WalkableGroundDistance() <= 80 //unfortunately we do not have a happy ground timer like SP (this would use up more bandwidth if we wanted prediction workign right), so we'll just use the actual ground distance.
2492 && (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 ) )//not in a flip or spin or anything
2493 {//run up wall, flip backwards
2494 if ( allowWallRuns )
2495 {
2496 //FIXME: have to be moving... make sure it's opposite the wall... or at least forward?
2497 int wallWalkAnim = BOTH_WALL_FLIP_BACK1;
2498 int parts = SETANIM_LEGS;
2499 int contents = MASK_SOLID;//MASK_PLAYERSOLID;//CONTENTS_SOLID;
2500 //qboolean kick = qtrue;
2501 if ( pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 )
2502 {
2503 wallWalkAnim = BOTH_FORCEWALLRUNFLIP_START;
2504 parts = SETANIM_BOTH;
2505 //kick = qfalse;
2506 }
2507 else
2508 {
2509 if ( !pm->ps->weaponTime )
2510 {
2511 parts = SETANIM_BOTH;
2512 }
2513 }
2514 //if ( PM_HasAnimation( pm->gent, wallWalkAnim ) )
2515 if (1) //sure, we have it! Because I SAID SO.
2516 {
2517 vec3_t fwd, traceto, mins, maxs, fwdAngles;
2518 trace_t trace;
2519 vec3_t idealNormal;
2520 bgEntity_t *traceEnt;
2521
2522 VectorSet(mins, pm->mins[0], pm->mins[1], 0.0f);
2523 VectorSet(maxs, pm->maxs[0], pm->maxs[1], 24.0f);
2524 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0.0f);
2525
2526 AngleVectors( fwdAngles, fwd, NULL, NULL );
2527 VectorMA( pm->ps->origin, 32, fwd, traceto );
2528
2529 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents );//FIXME: clip brushes too?
2530 VectorSubtract( pm->ps->origin, traceto, idealNormal );
2531 VectorNormalize( idealNormal );
2532 traceEnt = PM_BGEntForNum(trace.entityNum);
2533
2534 if ( trace.fraction < 1.0f
2535 &&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) )
2536 {//there is a wall there
2537 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2538 if ( wallWalkAnim == BOTH_FORCEWALLRUNFLIP_START )
2539 {
2540 pm->ps->velocity[2] = forceJumpStrength[FORCE_LEVEL_3]/2.0f;
2541 }
2542 else
2543 {
2544 VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
2545 pm->ps->velocity[2] += 150.0f;
2546 }
2547 //animate me
2548 PM_SetAnim( parts, wallWalkAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2549 // pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
2550 //again with the flags!
2551 //G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
2552 //yucky!
2553 PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
2554 pm->cmd.upmove = 0;
2555 pm->ps->fd.forceJumpSound = 1;
2556 BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 5 );
2557
2558 //kick if jumping off an ent
2559 /*
2560 if ( kick && traceEnt && (traceEnt->s.eType == ET_PLAYER || traceEnt->s.eType == ET_NPC) )
2561 { //kick that thang!
2562 pm->ps->forceKickFlip = traceEnt->s.number+1;
2563 }
2564 */
2565 pm->cmd.rightmove = pm->cmd.forwardmove= 0;
2566 }
2567 }
2568 }
2569 }
2570 else if ( (!BG_InSpecialJump( legsAnim )//not in a special jump anim
2571 ||BG_InReboundJump( legsAnim )//we're already in a rebound
2572 ||BG_InBackFlip( legsAnim ) )//a backflip (needed so you can jump off a wall behind you)
2573 //&& pm->ps->velocity[2] <= 0
2574 && pm->ps->velocity[2] > -1200 //not falling down very fast
2575 && !(pm->ps->pm_flags&PMF_JUMP_HELD)//have to have released jump since last press
2576 && (pm->cmd.forwardmove||pm->cmd.rightmove)//pushing in a direction
2577 //&& pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period
2578 && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2//level 3 jump or better
2579 //&& WP_ForcePowerAvailable( pm->gent, FP_LEVITATION, 10 )//have enough force power to do another one
2580 && BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION)
2581 && (pm->ps->origin[2]-pm->ps->fd.forceJumpZStart) < (forceJumpHeightMax[FORCE_LEVEL_3]-(BG_ForceWallJumpStrength()/2.0f)) //can fit at least one more wall jump in (yes, using "magic numbers"... for now)
2582 //&& (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 ) )//not in a flip or spin or anything
2583 )
2584 {//see if we're pushing at a wall and jump off it if so
2585 if ( allowWallGrabs )
2586 {
2587 //FIXME: make sure we have enough force power
2588 //FIXME: check to see if we can go any higher
2589 //FIXME: limit to a certain number of these in a row?
2590 //FIXME: maybe don't require a ucmd direction, just check all 4?
2591 //FIXME: should stick to the wall for a second, then push off...
2592 vec3_t checkDir, traceto, mins, maxs, fwdAngles;
2593 trace_t trace;
2594 vec3_t idealNormal;
2595 int anim = -1;
2596
2597 VectorSet(mins, pm->mins[0], pm->mins[1], 0.0f);
2598 VectorSet(maxs, pm->maxs[0], pm->maxs[1], 24.0f);
2599 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0.0f);
2600
2601 if ( pm->cmd.rightmove )
2602 {
2603 if ( pm->cmd.rightmove > 0 )
2604 {
2605 anim = BOTH_FORCEWALLREBOUND_RIGHT;
2606 AngleVectors( fwdAngles, NULL, checkDir, NULL );
2607 }
2608 else if ( pm->cmd.rightmove < 0 )
2609 {
2610 anim = BOTH_FORCEWALLREBOUND_LEFT;
2611 AngleVectors( fwdAngles, NULL, checkDir, NULL );
2612 VectorScale( checkDir, -1, checkDir );
2613 }
2614 }
2615 else if ( pm->cmd.forwardmove > 0 )
2616 {
2617 anim = BOTH_FORCEWALLREBOUND_FORWARD;
2618 AngleVectors( fwdAngles, checkDir, NULL, NULL );
2619 }
2620 else if ( pm->cmd.forwardmove < 0 )
2621 {
2622 anim = BOTH_FORCEWALLREBOUND_BACK;
2623 AngleVectors( fwdAngles, checkDir, NULL, NULL );
2624 VectorScale( checkDir, -1, checkDir );
2625 }
2626 if ( anim != -1 )
2627 {//trace in the dir we're pushing in and see if there's a vertical wall there
2628 bgEntity_t *traceEnt;
2629
2630 VectorMA( pm->ps->origin, 8, checkDir, traceto );
2631 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID );//FIXME: clip brushes too?
2632 VectorSubtract( pm->ps->origin, traceto, idealNormal );
2633 VectorNormalize( idealNormal );
2634 traceEnt = PM_BGEntForNum(trace.entityNum);
2635 if ( trace.fraction < 1.0f
2636 &&fabs(trace.plane.normal[2]) <= 0.2f/*MAX_WALL_GRAB_SLOPE*/
2637 &&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) )
2638 {//there is a wall there
2639 float dot = DotProduct( pm->ps->velocity, trace.plane.normal );
2640 if ( dot < 1.0f )
2641 {//can't be heading *away* from the wall!
2642 //grab it!
2643 PM_GrabWallForJump( anim );
2644 }
2645 }
2646 }
2647 }
2648 }
2649 else
2650 {
2651 //FIXME: if in a butterfly, kick people away?
2652 }
2653 //END NEW JKA
2654 }
2655 }
2656
2657 /*
2658 if ( pm->cmd.upmove > 0
2659 && (pm->ps->weapon == WP_SABER || pm->ps->weapon == WP_MELEE)
2660 && !PM_IsRocketTrooper()
2661 && (pm->ps->weaponTime > 0||pm->cmd.buttons&BUTTON_ATTACK) )
2662 {//okay, we just jumped and we're in an attack
2663 if ( !BG_InRoll( pm->ps, pm->ps->legsAnim )
2664 && !PM_InKnockDown( pm->ps )
2665 && !BG_InDeathAnim(pm->ps->legsAnim)
2666 && !BG_FlippingAnim( pm->ps->legsAnim )
2667 && !PM_SpinningAnim( pm->ps->legsAnim )
2668 && !BG_SaberInSpecialAttack( pm->ps->torsoAnim )
2669 && ( BG_SaberInAttack( pm->ps->saberMove ) ) )
2670 {//not in an anim we shouldn't interrupt
2671 //see if it's not too late to start a special jump-attack
2672 float animLength = PM_AnimLength( 0, (animNumber_t)pm->ps->torsoAnim );
2673 if ( animLength - pm->ps->torsoTimer < 500 )
2674 {//just started the saberMove
2675 //check for special-case jump attacks
2676 if ( pm->ps->fd.saberAnimLevel == FORCE_LEVEL_2 )
2677 {//using medium attacks
2678 if (PM_GroundDistance() < 32 &&
2679 !BG_InSpecialJump(pm->ps->legsAnim))
2680 { //FLIP AND DOWNWARD ATTACK
2681 //trace_t tr;
2682
2683 //if (PM_SomeoneInFront(&tr))
2684 {
2685 PM_SetSaberMove(PM_SaberFlipOverAttackMove());
2686 pml.groundPlane = qfalse;
2687 pml.walking = qfalse;
2688 pm->ps->pm_flags |= PMF_JUMP_HELD;
2689 pm->ps->groundEntityNum = ENTITYNUM_NONE;
2690 VectorClear(pml.groundTrace.plane.normal);
2691
2692 pm->ps->weaponTime = pm->ps->torsoTimer;
2693 }
2694 }
2695 }
2696 else if ( pm->ps->fd.saberAnimLevel == FORCE_LEVEL_3 )
2697 {//using strong attacks
2698 if ( pm->cmd.forwardmove > 0 && //going forward
2699 (pm->cmd.buttons & BUTTON_ATTACK) && //must be holding attack still
2700 PM_GroundDistance() < 32 &&
2701 !BG_InSpecialJump(pm->ps->legsAnim))
2702 {//strong attack: jump-hack
2703 PM_SetSaberMove( PM_SaberJumpAttackMove() );
2704 pml.groundPlane = qfalse;
2705 pml.walking = qfalse;
2706 pm->ps->pm_flags |= PMF_JUMP_HELD;
2707 pm->ps->groundEntityNum = ENTITYNUM_NONE;
2708 VectorClear(pml.groundTrace.plane.normal);
2709
2710 pm->ps->weaponTime = pm->ps->torsoTimer;
2711 }
2712 }
2713 }
2714 }
2715 }
2716 */
2717 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
2718 {
2719 return qfalse;
2720 }
2721 if ( pm->cmd.upmove > 0 )
2722 {//no special jumps
2723 pm->ps->velocity[2] = JUMP_VELOCITY;
2724 PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
2725 pm->ps->pm_flags |= PMF_JUMP_HELD;
2726 }
2727
2728 //Jumping
2729 pml.groundPlane = qfalse;
2730 pml.walking = qfalse;
2731 pm->ps->pm_flags |= PMF_JUMP_HELD;
2732 pm->ps->groundEntityNum = ENTITYNUM_NONE;
2733 PM_SetForceJumpZStart(pm->ps->origin[2]);
2734
2735 PM_AddEvent( EV_JUMP );
2736
2737 //Set the animations
2738 if ( pm->ps->gravity > 0 && !BG_InSpecialJump( pm->ps->legsAnim ) )
2739 {
2740 PM_JumpForDir();
2741 }
2742
2743 return qtrue;
2744 }
2745 /*
2746 =============
2747 PM_CheckWaterJump
2748 =============
2749 */
PM_CheckWaterJump(void)2750 static qboolean PM_CheckWaterJump( void ) {
2751 vec3_t spot;
2752 int cont;
2753 vec3_t flatforward;
2754
2755 if (pm->ps->pm_time) {
2756 return qfalse;
2757 }
2758
2759 // check for water jump
2760 if ( pm->waterlevel != 2 ) {
2761 return qfalse;
2762 }
2763
2764 flatforward[0] = pml.forward[0];
2765 flatforward[1] = pml.forward[1];
2766 flatforward[2] = 0;
2767 VectorNormalize (flatforward);
2768
2769 VectorMA (pm->ps->origin, 30, flatforward, spot);
2770 spot[2] += 4;
2771 cont = pm->pointcontents (spot, pm->ps->clientNum );
2772 if ( !(cont & CONTENTS_SOLID) ) {
2773 return qfalse;
2774 }
2775
2776 spot[2] += 16;
2777 cont = pm->pointcontents (spot, pm->ps->clientNum );
2778 if ( cont & (CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY) ) {
2779 return qfalse;
2780 }
2781
2782 // jump out of water
2783 VectorScale (pml.forward, 200, pm->ps->velocity);
2784 pm->ps->velocity[2] = 350;
2785
2786 pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
2787 pm->ps->pm_time = 2000;
2788
2789 return qtrue;
2790 }
2791
2792 //============================================================================
2793
2794
2795 /*
2796 ===================
2797 PM_WaterJumpMove
2798
2799 Flying out of the water
2800 ===================
2801 */
PM_WaterJumpMove(void)2802 static void PM_WaterJumpMove( void ) {
2803 // waterjump has no control, but falls
2804
2805 PM_StepSlideMove( qtrue );
2806
2807 pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
2808 if (pm->ps->velocity[2] < 0) {
2809 // cancel as soon as we are falling down again
2810 pm->ps->pm_flags &= ~PMF_ALL_TIMES;
2811 pm->ps->pm_time = 0;
2812 }
2813 }
2814
2815 /*
2816 ===================
2817 PM_WaterMove
2818
2819 ===================
2820 */
PM_WaterMove(void)2821 static void PM_WaterMove( void ) {
2822 int i;
2823 vec3_t wishvel;
2824 float wishspeed;
2825 vec3_t wishdir;
2826 float scale;
2827 float vel;
2828
2829 if ( PM_CheckWaterJump() ) {
2830 PM_WaterJumpMove();
2831 return;
2832 }
2833 #if 0
2834 // jump = head for surface
2835 if ( pm->cmd.upmove >= 10 ) {
2836 if (pm->ps->velocity[2] > -300) {
2837 if ( pm->watertype == CONTENTS_WATER ) {
2838 pm->ps->velocity[2] = 100;
2839 } else if (pm->watertype == CONTENTS_SLIME) {
2840 pm->ps->velocity[2] = 80;
2841 } else {
2842 pm->ps->velocity[2] = 50;
2843 }
2844 }
2845 }
2846 #endif
2847 PM_Friction ();
2848
2849 scale = PM_CmdScale( &pm->cmd );
2850 //
2851 // user intentions
2852 //
2853 if ( !scale ) {
2854 wishvel[0] = 0;
2855 wishvel[1] = 0;
2856 wishvel[2] = -60; // sink towards bottom
2857 } else {
2858 for (i=0 ; i<3 ; i++)
2859 wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
2860
2861 wishvel[2] += scale * pm->cmd.upmove;
2862 }
2863
2864 VectorCopy (wishvel, wishdir);
2865 wishspeed = VectorNormalize(wishdir);
2866
2867 if ( wishspeed > pm->ps->speed * pm_swimScale ) {
2868 wishspeed = pm->ps->speed * pm_swimScale;
2869 }
2870
2871 PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate);
2872
2873 // make sure we can go up slopes easily under water
2874 if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) {
2875 vel = VectorLength(pm->ps->velocity);
2876 // slide along the ground plane
2877 PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
2878 pm->ps->velocity, OVERCLIP );
2879
2880 VectorNormalize(pm->ps->velocity);
2881 VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
2882 }
2883
2884 PM_SlideMove( qfalse );
2885 }
2886
2887 /*
2888 ===================
2889 PM_FlyVehicleMove
2890
2891 ===================
2892 */
PM_FlyVehicleMove(void)2893 static void PM_FlyVehicleMove( void )
2894 {
2895 int i;
2896 vec3_t wishvel;
2897 float wishspeed;
2898 vec3_t wishdir;
2899 float scale;
2900 float zVel;
2901 float fmove = 0.0f, smove = 0.0f;
2902
2903 // We don't use these here because we pre-calculate the movedir in the vehicle update anyways, and if
2904 // you leave this, you get strange motion during boarding (the player can move the vehicle).
2905 //fmove = pm->cmd.forwardmove;
2906 //smove = pm->cmd.rightmove;
2907
2908 // normal slowdown
2909 if ( pm->ps->gravity && pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum == ENTITYNUM_NONE )
2910 {//falling
2911 zVel = pm->ps->velocity[2];
2912 PM_Friction ();
2913 pm->ps->velocity[2] = zVel;
2914 }
2915 else
2916 {
2917 PM_Friction ();
2918 if ( pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE )
2919 {
2920 pm->ps->velocity[2] = 0; // ignore slope movement
2921 }
2922 }
2923
2924 scale = PM_CmdScale( &pm->cmd );
2925
2926 // Get The WishVel And WishSpeed
2927 //-------------------------------
2928 if ( pm->ps->clientNum >= MAX_CLIENTS )
2929 {//NPC
2930
2931 // If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds
2932 //--------------------------------------------------------------------------------------------
2933 if ((fmove!=0.0f || smove!=0.0f) && VectorCompare(pm->ps->moveDir, vec3_origin))
2934 {
2935 //trap->Printf("Generating MoveDir\n");
2936 for ( i = 0 ; i < 3 ; i++ )
2937 {
2938 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
2939 }
2940
2941 VectorCopy( wishvel, wishdir );
2942 wishspeed = VectorNormalize(wishdir);
2943 wishspeed *= scale;
2944 }
2945 // Otherwise, Use The Move Dir
2946 //-----------------------------
2947 else
2948 {
2949 wishspeed = pm->ps->speed;
2950 VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
2951 VectorCopy( pm->ps->moveDir, wishdir );
2952 }
2953 }
2954 else
2955 {
2956 for ( i = 0 ; i < 3 ; i++ ) {
2957 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
2958 }
2959 // when going up or down slopes the wish velocity should Not be zero
2960 // wishvel[2] = 0;
2961
2962 VectorCopy (wishvel, wishdir);
2963 wishspeed = VectorNormalize(wishdir);
2964 wishspeed *= scale;
2965 }
2966
2967 // Handle negative speed.
2968 if ( wishspeed < 0 )
2969 {
2970 wishspeed = wishspeed * -1.0f;
2971 VectorScale( wishvel, -1.0f, wishvel );
2972 VectorScale( wishdir, -1.0f, wishdir );
2973 }
2974
2975 VectorCopy( wishvel, wishdir );
2976 wishspeed = VectorNormalize( wishdir );
2977
2978 PM_Accelerate( wishdir, wishspeed, 100 );
2979
2980 PM_StepSlideMove( 1 );
2981 }
2982
2983 /*
2984 ===================
2985 PM_FlyMove
2986
2987 Only with the flight powerup
2988 ===================
2989 */
PM_FlyMove(void)2990 static void PM_FlyMove( void ) {
2991 int i;
2992 vec3_t wishvel;
2993 float wishspeed;
2994 vec3_t wishdir;
2995 float scale;
2996
2997 // normal slowdown
2998 PM_Friction ();
2999
3000 scale = PM_CmdScale( &pm->cmd );
3001
3002 if ( pm->ps->pm_type == PM_SPECTATOR && pm->cmd.buttons & BUTTON_ALT_ATTACK) {
3003 //turbo boost
3004 scale *= 10;
3005 }
3006
3007 //
3008 // user intentions
3009 //
3010 if ( !scale ) {
3011 wishvel[0] = 0;
3012 wishvel[1] = 0;
3013 wishvel[2] = pm->ps->speed * (pm->cmd.upmove/127.0f);
3014 } else {
3015 for (i=0 ; i<3 ; i++) {
3016 wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
3017 }
3018
3019 wishvel[2] += scale * pm->cmd.upmove;
3020 }
3021
3022 VectorCopy (wishvel, wishdir);
3023 wishspeed = VectorNormalize(wishdir);
3024
3025 PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);
3026
3027 PM_StepSlideMove( qfalse );
3028 }
3029
3030
3031 /*
3032 ===================
3033 PM_AirMove
3034
3035 ===================
3036 */
PM_AirMove(void)3037 static void PM_AirMove( void ) {
3038 int i;
3039 vec3_t wishvel;
3040 float fmove, smove;
3041 vec3_t wishdir;
3042 float wishspeed;
3043 float scale;
3044 float accelerate;
3045 usercmd_t cmd;
3046 Vehicle_t *pVeh = NULL;
3047
3048 if (pm->ps->clientNum >= MAX_CLIENTS)
3049 {
3050 bgEntity_t *pEnt = pm_entSelf;
3051
3052 if ( pEnt && pEnt->s.NPC_class == CLASS_VEHICLE )
3053 {
3054 pVeh = pEnt->m_pVehicle;
3055 }
3056 }
3057
3058 if (pm->ps->pm_type != PM_SPECTATOR)
3059 {
3060 #if METROID_JUMP
3061 PM_CheckJump();
3062 #else
3063 if (pm->ps->fd.forceJumpZStart &&
3064 pm->ps->forceJumpFlip)
3065 {
3066 PM_CheckJump();
3067 }
3068 #endif
3069 }
3070 PM_Friction();
3071
3072 fmove = pm->cmd.forwardmove;
3073 smove = pm->cmd.rightmove;
3074
3075 cmd = pm->cmd;
3076 scale = PM_CmdScale( &cmd );
3077
3078 // set the movementDir so clients can rotate the legs for strafing
3079 PM_SetMovementDir();
3080
3081 // project moves down to flat plane
3082 pml.forward[2] = 0;
3083 pml.right[2] = 0;
3084 VectorNormalize (pml.forward);
3085 VectorNormalize (pml.right);
3086
3087 if ( pVeh && pVeh->m_pVehicleInfo->hoverHeight > 0 )
3088 {//in a hovering vehicle, have air control
3089 if ( 1 )
3090 {
3091 wishspeed = pm->ps->speed;
3092 VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
3093 VectorCopy( pm->ps->moveDir, wishdir );
3094 scale = 1.0f;
3095 }
3096 #if 0
3097 else
3098 {
3099 float controlMod = 1.0f;
3100 if ( pml.groundPlane )
3101 {//on a slope of some kind, shouldn't have much control and should slide a lot
3102 controlMod = pml.groundTrace.plane.normal[2];
3103 }
3104
3105 vec3_t vfwd, vrt;
3106 vec3_t vAngles;
3107
3108 VectorCopy( pVeh->m_vOrientation, vAngles );
3109 vAngles[ROLL] = 0;//since we're a hovercraft, we really don't want to stafe up into the air if we're banking
3110 AngleVectors( vAngles, vfwd, vrt, NULL );
3111
3112 float speed = pm->ps->speed;
3113 float strafeSpeed = 0;
3114
3115 if ( fmove < 0 )
3116 {//going backwards
3117 if ( speed < 0 )
3118 {//speed is negative, but since our command is reverse, make speed positive
3119 speed = fabs( speed );
3120 /*
3121 if ( pml.groundPlane )
3122 {//on a slope, still have some control
3123 speed = fabs( speed );
3124 }
3125 else
3126 {//can't reverse in air
3127 speed = 0;
3128 }
3129 */
3130 }
3131 else if ( speed > 0 )
3132 {//trying to move back but speed is still positive, so keep moving forward (we'll slow down eventually)
3133 speed = 0;
3134 }
3135 }
3136
3137 if ( pm->ps->clientNum < MAX_CLIENTS )
3138 {//do normal adding to wishvel
3139 VectorScale( vfwd, speed*controlMod*(fmove/127.0f), wishvel );
3140 //just add strafing
3141 if ( pVeh->m_pVehicleInfo->strafePerc )
3142 {//we can strafe
3143 if ( smove )
3144 {//trying to strafe
3145 float minSpeed = pVeh->m_pVehicleInfo->speedMax * 0.5f * pVeh->m_pVehicleInfo->strafePerc;
3146 strafeSpeed = fabs(DotProduct( pm->ps->velocity, vfwd ))*pVeh->m_pVehicleInfo->strafePerc;
3147 if ( strafeSpeed < minSpeed )
3148 {
3149 strafeSpeed = minSpeed;
3150 }
3151 strafeSpeed *= controlMod*((float)(smove))/127.0f;
3152 if ( strafeSpeed < 0 )
3153 {//pm_accelerate does not understand negative numbers
3154 strafeSpeed *= -1;
3155 VectorScale( vrt, -1, vrt );
3156 }
3157 //now just add it to actual velocity
3158 PM_Accelerate( vrt, strafeSpeed, pVeh->m_pVehicleInfo->traction );
3159 }
3160 }
3161 }
3162 else
3163 {
3164 if ( pVeh->m_pVehicleInfo->strafePerc )
3165 {//we can strafe
3166 if ( pm->ps->clientNum )
3167 {//alternate control scheme: can strafe
3168 if ( smove )
3169 {
3170 /*
3171 if ( fmove > 0 )
3172 {//actively accelerating
3173 strafeSpeed = pm->ps->speed;
3174 }
3175 else
3176 {//not stepping on accelerator, only strafe based on magnitude of current forward velocity
3177 strafeSpeed = fabs(DotProduct( pm->ps->velocity, vfwd ));
3178 }
3179 */
3180 strafeSpeed = ((float)(smove))/127.0f;
3181 }
3182 }
3183 }
3184 //strafing takes away from forward speed
3185 VectorScale( vfwd, (fmove/127.0f)*(1.0f-pVeh->m_pVehicleInfo->strafePerc), wishvel );
3186 if ( strafeSpeed )
3187 {
3188 VectorMA( wishvel, strafeSpeed*pVeh->m_pVehicleInfo->strafePerc, vrt, wishvel );
3189 }
3190 VectorNormalize( wishvel );
3191 VectorScale( wishvel, speed*controlMod, wishvel );
3192 }
3193 }
3194 #endif
3195 }
3196 else if ( gPMDoSlowFall )
3197 {//no air-control
3198 VectorClear( wishvel );
3199 }
3200 else if (pm->ps->pm_type == PM_JETPACK)
3201 { //reduced air control while not jetting
3202 for ( i = 0 ; i < 2 ; i++ )
3203 {
3204 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3205 }
3206 wishvel[2] = 0;
3207
3208 if (pm->cmd.upmove <= 0)
3209 {
3210 VectorScale(wishvel, 0.8f, wishvel);
3211 }
3212 else
3213 { //if we are jetting then we have more control than usual
3214 VectorScale(wishvel, 2.0f, wishvel);
3215 }
3216 }
3217 else
3218 {
3219 for ( i = 0 ; i < 2 ; i++ )
3220 {
3221 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3222 }
3223 wishvel[2] = 0;
3224 }
3225
3226 VectorCopy (wishvel, wishdir);
3227 wishspeed = VectorNormalize(wishdir);
3228 wishspeed *= scale;
3229
3230 accelerate = pm_airaccelerate;
3231 if ( pVeh && pVeh->m_pVehicleInfo->type == VH_SPEEDER )
3232 {//speeders have more control in air
3233 //in mid-air
3234 accelerate = pVeh->m_pVehicleInfo->traction;
3235 if ( pml.groundPlane )
3236 {//on a slope of some kind, shouldn't have much control and should slide a lot
3237 accelerate *= 0.5f;
3238 }
3239 }
3240 // not on ground, so little effect on velocity
3241 PM_Accelerate (wishdir, wishspeed, accelerate);
3242
3243 // we may have a ground plane that is very steep, even
3244 // though we don't have a groundentity
3245 // slide along the steep plane
3246 if ( pml.groundPlane )
3247 {
3248 if ( !(pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
3249 {//don't slide when stuck to a wall
3250 if ( PM_GroundSlideOkay( pml.groundTrace.plane.normal[2] ) )
3251 {
3252 PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
3253 pm->ps->velocity, OVERCLIP );
3254 }
3255 }
3256 }
3257
3258 if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
3259 {//no grav when stuck to wall
3260 PM_StepSlideMove( qfalse );
3261 }
3262 else
3263 {
3264 PM_StepSlideMove( qtrue );
3265 }
3266 }
3267
3268 /*
3269 ===================
3270 PM_WalkMove
3271
3272 ===================
3273 */
PM_WalkMove(void)3274 static void PM_WalkMove( void ) {
3275 int i;
3276 vec3_t wishvel;
3277 float fmove, smove;
3278 vec3_t wishdir;
3279 float wishspeed = 0.0f;
3280 float scale;
3281 usercmd_t cmd;
3282 float accelerate;
3283 float vel;
3284 qboolean npcMovement = qfalse;
3285
3286 if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
3287 // begin swimming
3288 PM_WaterMove();
3289 return;
3290 }
3291
3292
3293 if (pm->ps->pm_type != PM_SPECTATOR)
3294 {
3295 if ( PM_CheckJump () ) {
3296 // jumped away
3297 if ( pm->waterlevel > 1 ) {
3298 PM_WaterMove();
3299 } else {
3300 PM_AirMove();
3301 }
3302 return;
3303 }
3304 }
3305
3306 PM_Friction ();
3307
3308 fmove = pm->cmd.forwardmove;
3309 smove = pm->cmd.rightmove;
3310
3311 cmd = pm->cmd;
3312 scale = PM_CmdScale( &cmd );
3313
3314 // set the movementDir so clients can rotate the legs for strafing
3315 PM_SetMovementDir();
3316
3317 // project moves down to flat plane
3318 pml.forward[2] = 0;
3319 pml.right[2] = 0;
3320
3321 // project the forward and right directions onto the ground plane
3322 PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP );
3323 PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP );
3324 //
3325 VectorNormalize (pml.forward);
3326 VectorNormalize (pml.right);
3327
3328 // Get The WishVel And WishSpeed
3329 //-------------------------------
3330 if ( pm->ps->clientNum >= MAX_CLIENTS && !VectorCompare( pm->ps->moveDir, vec3_origin ) )
3331 {//NPC
3332 bgEntity_t *pEnt = pm_entSelf;
3333
3334 if (pEnt && pEnt->s.NPC_class == CLASS_VEHICLE)
3335 {
3336 // If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds
3337 //--------------------------------------------------------------------------------------------
3338 if ((fmove!=0.0f || smove!=0.0f) && VectorCompare(pm->ps->moveDir, vec3_origin))
3339 {
3340 //trap->Printf("Generating MoveDir\n");
3341 for ( i = 0 ; i < 3 ; i++ )
3342 {
3343 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3344 }
3345
3346 VectorCopy( wishvel, wishdir );
3347 wishspeed = VectorNormalize(wishdir);
3348 wishspeed *= scale;
3349 }
3350 // Otherwise, Use The Move Dir
3351 //-----------------------------
3352 else
3353 {
3354 //wishspeed = pm->ps->speed;
3355 VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
3356 VectorCopy (wishvel, wishdir);
3357 wishspeed = VectorNormalize(wishdir);
3358 }
3359
3360 npcMovement = qtrue;
3361 }
3362 }
3363
3364 if (!npcMovement)
3365 {
3366 for ( i = 0 ; i < 3 ; i++ ) {
3367 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3368 }
3369 // when going up or down slopes the wish velocity should Not be zero
3370
3371 VectorCopy (wishvel, wishdir);
3372 wishspeed = VectorNormalize(wishdir);
3373 wishspeed *= scale;
3374 }
3375
3376 // clamp the speed lower if ducking
3377 if ( pm->ps->pm_flags & PMF_DUCKED ) {
3378 if ( wishspeed > pm->ps->speed * pm_duckScale ) {
3379 wishspeed = pm->ps->speed * pm_duckScale;
3380 }
3381 }
3382 else if ( (pm->ps->pm_flags & PMF_ROLLING) && !BG_InRoll(pm->ps, pm->ps->legsAnim) &&
3383 !PM_InRollComplete(pm->ps, pm->ps->legsAnim))
3384 {
3385 if ( wishspeed > pm->ps->speed * pm_duckScale ) {
3386 wishspeed = pm->ps->speed * pm_duckScale;
3387 }
3388 }
3389
3390 // clamp the speed lower if wading or walking on the bottom
3391 if ( pm->waterlevel ) {
3392 float waterScale;
3393
3394 waterScale = pm->waterlevel / 3.0;
3395 waterScale = 1.0 - ( 1.0 - pm_swimScale ) * waterScale;
3396 if ( wishspeed > pm->ps->speed * waterScale ) {
3397 wishspeed = pm->ps->speed * waterScale;
3398 }
3399 }
3400
3401 // when a player gets hit, they temporarily lose
3402 // full control, which allows them to be moved a bit
3403 if ( pm_flying == FLY_HOVER )
3404 {
3405 accelerate = pm_vehicleaccelerate;
3406 }
3407 else if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK )
3408 {
3409 accelerate = pm_airaccelerate;
3410 }
3411 else
3412 {
3413 accelerate = pm_accelerate;
3414 }
3415
3416 PM_Accelerate (wishdir, wishspeed, accelerate);
3417 /*
3418 if (pm->ps->clientNum >= MAX_CLIENTS)
3419 {
3420 #ifdef _GAME
3421 Com_Printf("^1S: %f, %f\n", wishspeed, pm->ps->speed);
3422 #else
3423 Com_Printf("^2C: %f, %f\n", wishspeed, pm->ps->speed);
3424 #endif
3425 }
3426 */
3427
3428 //Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
3429 //Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity));
3430
3431 if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK )
3432 {
3433 pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
3434 }
3435
3436 vel = VectorLength(pm->ps->velocity);
3437
3438 // slide along the ground plane
3439 PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
3440 pm->ps->velocity, OVERCLIP );
3441
3442 // don't decrease velocity when going up or down a slope
3443 VectorNormalize(pm->ps->velocity);
3444 VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
3445
3446 // don't do anything if standing still
3447 if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) {
3448 return;
3449 }
3450
3451 PM_StepSlideMove( qfalse );
3452
3453 //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity));
3454 }
3455
3456
3457 /*
3458 ==============
3459 PM_DeadMove
3460 ==============
3461 */
PM_DeadMove(void)3462 static void PM_DeadMove( void ) {
3463 float forward;
3464
3465 if ( !pml.walking ) {
3466 return;
3467 }
3468
3469 // extra friction
3470
3471 forward = VectorLength (pm->ps->velocity);
3472 forward -= 20;
3473 if ( forward <= 0 ) {
3474 VectorClear (pm->ps->velocity);
3475 } else {
3476 VectorNormalize (pm->ps->velocity);
3477 VectorScale (pm->ps->velocity, forward, pm->ps->velocity);
3478 }
3479 }
3480
3481
3482 /*
3483 ===============
3484 PM_NoclipMove
3485 ===============
3486 */
PM_NoclipMove(void)3487 static void PM_NoclipMove( void ) {
3488 float speed, drop, friction, control, newspeed;
3489 int i;
3490 vec3_t wishvel;
3491 float fmove, smove;
3492 vec3_t wishdir;
3493 float wishspeed;
3494 float scale;
3495
3496 pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
3497
3498 // friction
3499
3500 speed = VectorLength (pm->ps->velocity);
3501 if (speed < 1)
3502 {
3503 VectorCopy (vec3_origin, pm->ps->velocity);
3504 }
3505 else
3506 {
3507 drop = 0;
3508
3509 friction = pm_friction*1.5; // extra friction
3510 control = speed < pm_stopspeed ? pm_stopspeed : speed;
3511 drop += control*friction*pml.frametime;
3512
3513 // scale the velocity
3514 newspeed = speed - drop;
3515 if (newspeed < 0)
3516 newspeed = 0;
3517 newspeed /= speed;
3518
3519 VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity);
3520 }
3521
3522 // accelerate
3523 scale = PM_CmdScale( &pm->cmd );
3524 if (pm->cmd.buttons & BUTTON_ATTACK) { //turbo boost
3525 scale *= 10;
3526 }
3527 if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { //turbo boost
3528 scale *= 10;
3529 }
3530
3531 fmove = pm->cmd.forwardmove;
3532 smove = pm->cmd.rightmove;
3533
3534 for (i=0 ; i<3 ; i++)
3535 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3536 wishvel[2] += pm->cmd.upmove;
3537
3538 VectorCopy (wishvel, wishdir);
3539 wishspeed = VectorNormalize(wishdir);
3540 wishspeed *= scale;
3541
3542 PM_Accelerate( wishdir, wishspeed, pm_accelerate );
3543
3544 // move
3545 VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin);
3546 }
3547
3548 //============================================================================
3549
3550 /*
3551 ================
3552 PM_FootstepForSurface
3553
3554 Returns an event number appropriate for the groundsurface
3555 ================
3556 */
PM_FootstepForSurface(void)3557 static int PM_FootstepForSurface( void )
3558 {
3559 if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS )
3560 {
3561 return 0;
3562 }
3563 return ( pml.groundTrace.surfaceFlags & MATERIAL_MASK );
3564 }
3565
3566 extern qboolean PM_CanRollFromSoulCal( playerState_t *ps );
PM_TryRoll(void)3567 static int PM_TryRoll( void )
3568 {
3569 trace_t trace;
3570 int anim = -1;
3571 vec3_t fwd, right, traceto, mins, maxs, fwdAngles;
3572
3573 if ( BG_SaberInAttack( pm->ps->saberMove ) || BG_SaberInSpecialAttack( pm->ps->torsoAnim )
3574 || BG_SpinningSaberAnim( pm->ps->legsAnim )
3575 || PM_SaberInStart( pm->ps->saberMove ) )
3576 {//attacking or spinning (or, if player, starting an attack)
3577 if ( PM_CanRollFromSoulCal( pm->ps ) )
3578 {//hehe
3579 }
3580 else
3581 {
3582 return 0;
3583 }
3584 }
3585
3586 if ((pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE) ||
3587 PM_IsRocketTrooper() ||
3588 BG_HasYsalamiri(pm->gametype, pm->ps) ||
3589 !BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION))
3590 { //Not using saber, or can't use jump
3591 return 0;
3592 }
3593
3594 if ( pm->ps->weapon == WP_SABER )
3595 {
3596 saberInfo_t *saber = BG_MySaber( pm->ps->clientNum, 0 );
3597 if ( saber
3598 && (saber->saberFlags&SFL_NO_ROLLS) )
3599 {
3600 return 0;
3601 }
3602 saber = BG_MySaber( pm->ps->clientNum, 1 );
3603 if ( saber
3604 && (saber->saberFlags&SFL_NO_ROLLS) )
3605 {
3606 return 0;
3607 }
3608 }
3609
3610 VectorSet(mins, pm->mins[0],pm->mins[1],pm->mins[2]+STEPSIZE);
3611 VectorSet(maxs, pm->maxs[0],pm->maxs[1],pm->ps->crouchheight);
3612
3613 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
3614
3615 AngleVectors( fwdAngles, fwd, right, NULL );
3616
3617 if ( pm->cmd.forwardmove )
3618 { //check forward/backward rolls
3619 if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
3620 {
3621 anim = BOTH_ROLL_B;
3622 VectorMA( pm->ps->origin, -64, fwd, traceto );
3623 }
3624 else
3625 {
3626 anim = BOTH_ROLL_F;
3627 VectorMA( pm->ps->origin, 64, fwd, traceto );
3628 }
3629 }
3630 else if ( pm->cmd.rightmove > 0 )
3631 { //right
3632 anim = BOTH_ROLL_R;
3633 VectorMA( pm->ps->origin, 64, right, traceto );
3634 }
3635 else if ( pm->cmd.rightmove < 0 )
3636 { //left
3637 anim = BOTH_ROLL_L;
3638 VectorMA( pm->ps->origin, -64, right, traceto );
3639 }
3640
3641 if ( anim != -1 )
3642 { //We want to roll. Perform a trace to see if we can, and if so, send us into one.
3643 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID );
3644 if ( trace.fraction >= 1.0f )
3645 {
3646 pm->ps->saberMove = LS_NONE;
3647 return anim;
3648 }
3649 }
3650 return 0;
3651 }
3652
3653 #ifdef _GAME
PM_CrashLandEffect(void)3654 static void PM_CrashLandEffect( void )
3655 {
3656 float delta;
3657 if ( pm->waterlevel )
3658 {
3659 return;
3660 }
3661 delta = fabs(pml.previous_velocity[2])/10;//VectorLength( pml.previous_velocity );?
3662 if ( delta >= 30 )
3663 {
3664 vec3_t bottom;
3665 int effectID = -1;
3666 int material = (pml.groundTrace.surfaceFlags&MATERIAL_MASK);
3667 VectorSet( bottom, pm->ps->origin[0],pm->ps->origin[1],pm->ps->origin[2]+pm->mins[2]+1 );
3668 switch ( material )
3669 {
3670 case MATERIAL_MUD:
3671 effectID = EFFECT_LANDING_MUD;
3672 break;
3673 case MATERIAL_SAND:
3674 effectID = EFFECT_LANDING_SAND;
3675 break;
3676 case MATERIAL_DIRT:
3677 effectID = EFFECT_LANDING_DIRT;
3678 break;
3679 case MATERIAL_SNOW:
3680 effectID = EFFECT_LANDING_SNOW;
3681 break;
3682 case MATERIAL_GRAVEL:
3683 effectID = EFFECT_LANDING_GRAVEL;
3684 break;
3685 }
3686
3687 if ( effectID != -1 )
3688 {
3689 G_PlayEffect( effectID, bottom, pml.groundTrace.plane.normal );
3690 }
3691 }
3692 }
3693 #endif
3694 /*
3695 =================
3696 PM_CrashLand
3697
3698 Check for hard landings that generate sound events
3699 =================
3700 */
PM_CrashLand(void)3701 static void PM_CrashLand( void ) {
3702 float delta;
3703 float dist;
3704 float vel, acc;
3705 float t;
3706 float a, b, c, den;
3707 qboolean didRoll = qfalse;
3708
3709 // calculate the exact velocity on landing
3710 dist = pm->ps->origin[2] - pml.previous_origin[2];
3711 vel = pml.previous_velocity[2];
3712 acc = -pm->ps->gravity;
3713
3714 a = acc / 2;
3715 b = vel;
3716 c = -dist;
3717
3718 den = b * b - 4 * a * c;
3719 if ( den < 0 ) {
3720 pm->ps->inAirAnim = qfalse;
3721 return;
3722 }
3723 t = (-b - sqrt( den ) ) / ( 2 * a );
3724
3725 delta = vel + t * acc;
3726 delta = delta*delta * 0.0001;
3727
3728 #ifdef _GAME
3729 PM_CrashLandEffect();
3730 #endif
3731 // ducking while falling doubles damage
3732 if ( pm->ps->pm_flags & PMF_DUCKED ) {
3733 delta *= 2;
3734 }
3735
3736 if (pm->ps->legsAnim == BOTH_A7_KICK_F_AIR ||
3737 pm->ps->legsAnim == BOTH_A7_KICK_B_AIR ||
3738 pm->ps->legsAnim == BOTH_A7_KICK_R_AIR ||
3739 pm->ps->legsAnim == BOTH_A7_KICK_L_AIR)
3740 {
3741 int landAnim = -1;
3742 switch ( pm->ps->legsAnim )
3743 {
3744 case BOTH_A7_KICK_F_AIR:
3745 landAnim = BOTH_FORCELAND1;
3746 break;
3747 case BOTH_A7_KICK_B_AIR:
3748 landAnim = BOTH_FORCELANDBACK1;
3749 break;
3750 case BOTH_A7_KICK_R_AIR:
3751 landAnim = BOTH_FORCELANDRIGHT1;
3752 break;
3753 case BOTH_A7_KICK_L_AIR:
3754 landAnim = BOTH_FORCELANDLEFT1;
3755 break;
3756 }
3757 if ( landAnim != -1 )
3758 {
3759 if ( pm->ps->torsoAnim == pm->ps->legsAnim )
3760 {
3761 PM_SetAnim(SETANIM_BOTH, landAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
3762 }
3763 else
3764 {
3765 PM_SetAnim(SETANIM_LEGS, landAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
3766 }
3767 }
3768 }
3769 else if (pm->ps->legsAnim == BOTH_FORCEJUMPLEFT1 ||
3770 pm->ps->legsAnim == BOTH_FORCEJUMPRIGHT1 ||
3771 pm->ps->legsAnim == BOTH_FORCEJUMPBACK1 ||
3772 pm->ps->legsAnim == BOTH_FORCEJUMP1)
3773 {
3774 int fjAnim;
3775 switch (pm->ps->legsAnim)
3776 {
3777 case BOTH_FORCEJUMPLEFT1:
3778 fjAnim = BOTH_LANDLEFT1;
3779 break;
3780 case BOTH_FORCEJUMPRIGHT1:
3781 fjAnim = BOTH_LANDRIGHT1;
3782 break;
3783 case BOTH_FORCEJUMPBACK1:
3784 fjAnim = BOTH_LANDBACK1;
3785 break;
3786 default:
3787 fjAnim = BOTH_LAND1;
3788 break;
3789 }
3790 PM_SetAnim(SETANIM_BOTH, fjAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
3791 }
3792 // decide which landing animation to use
3793 else if (!BG_InRoll(pm->ps, pm->ps->legsAnim) && pm->ps->inAirAnim && !pm->ps->m_iVehicleNum)
3794 { //only play a land animation if we transitioned into an in-air animation while off the ground
3795 if (!BG_SaberInSpecial(pm->ps->saberMove))
3796 {
3797 if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) {
3798 PM_ForceLegsAnim( BOTH_LANDBACK1 );
3799 } else {
3800 PM_ForceLegsAnim( BOTH_LAND1 );
3801 }
3802 }
3803 }
3804
3805 if (pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE && !PM_IsRocketTrooper())
3806 { //saber handles its own anims
3807 //This will push us back into our weaponready stance from the land anim.
3808 if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
3809 {
3810 PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
3811 }
3812 else
3813 {
3814 if (pm->ps->weapon == WP_EMPLACED_GUN)
3815 {
3816 PM_StartTorsoAnim( BOTH_GUNSIT1 );
3817 }
3818 else
3819 {
3820 PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
3821 }
3822 }
3823 }
3824
3825 if (!BG_InSpecialJump(pm->ps->legsAnim) ||
3826 pm->ps->legsTimer < 1 ||
3827 (pm->ps->legsAnim) == BOTH_WALL_RUN_LEFT ||
3828 (pm->ps->legsAnim) == BOTH_WALL_RUN_RIGHT)
3829 { //Only set the timer if we're in an anim that can be interrupted (this would not be, say, a flip)
3830 if (!BG_InRoll(pm->ps, pm->ps->legsAnim) && pm->ps->inAirAnim)
3831 {
3832 if (!BG_SaberInSpecial(pm->ps->saberMove) || pm->ps->weapon != WP_SABER)
3833 {
3834 if (pm->ps->legsAnim != BOTH_FORCELAND1 && pm->ps->legsAnim != BOTH_FORCELANDBACK1 &&
3835 pm->ps->legsAnim != BOTH_FORCELANDRIGHT1 && pm->ps->legsAnim != BOTH_FORCELANDLEFT1)
3836 { //don't override if we have started a force land
3837 pm->ps->legsTimer = TIMER_LAND;
3838 }
3839 }
3840 }
3841 }
3842
3843 pm->ps->inAirAnim = qfalse;
3844
3845 if (pm->ps->m_iVehicleNum)
3846 { //don't do fall stuff while on a vehicle
3847 return;
3848 }
3849
3850 // never take falling damage if completely underwater
3851 if ( pm->waterlevel == 3 ) {
3852 return;
3853 }
3854
3855 // reduce falling damage if there is standing water
3856 if ( pm->waterlevel == 2 ) {
3857 delta *= 0.25;
3858 }
3859 if ( pm->waterlevel == 1 ) {
3860 delta *= 0.5;
3861 }
3862
3863 if ( delta < 1 ) {
3864 return;
3865 }
3866
3867 if ( pm->ps->pm_flags & PMF_DUCKED )
3868 {
3869 if( delta >= 2 && !PM_InOnGroundAnim( pm->ps->legsAnim ) && !PM_InKnockDown( pm->ps ) && !BG_InRoll(pm->ps, pm->ps->legsAnim) &&
3870 pm->ps->forceHandExtend == HANDEXTEND_NONE )
3871 {//roll!
3872 int anim = PM_TryRoll();
3873
3874 if (PM_InRollComplete(pm->ps, pm->ps->legsAnim))
3875 {
3876 anim = 0;
3877 pm->ps->legsTimer = 0;
3878 pm->ps->legsAnim = 0;
3879 PM_SetAnim(SETANIM_BOTH,BOTH_LAND1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
3880 pm->ps->legsTimer = TIMER_LAND;
3881 }
3882
3883 if ( anim )
3884 {//absorb some impact
3885 pm->ps->legsTimer = 0;
3886 delta /= 3; // /= 2 just cancels out the above delta *= 2 when landing while crouched, the roll itself should absorb a little damage
3887 pm->ps->legsAnim = 0;
3888 if (pm->ps->torsoAnim == BOTH_A7_SOULCAL)
3889 { //get out of it on torso
3890 pm->ps->torsoTimer = 0;
3891 }
3892 PM_SetAnim(SETANIM_BOTH,anim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
3893 didRoll = qtrue;
3894 }
3895 }
3896 }
3897
3898 // SURF_NODAMAGE is used for bounce pads where you don't ever
3899 // want to take damage or play a crunch sound
3900 if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) {
3901 if (delta > 7)
3902 {
3903 int delta_send = (int)delta;
3904
3905 if (delta_send > 600)
3906 { //will never need to know any value above this
3907 delta_send = 600;
3908 }
3909
3910 if (pm->ps->fd.forceJumpZStart)
3911 {
3912 if ((int)pm->ps->origin[2] >= (int)pm->ps->fd.forceJumpZStart)
3913 { //was force jumping, landed on higher or same level as when force jump was started
3914 if (delta_send > 8)
3915 {
3916 delta_send = 8;
3917 }
3918 }
3919 else
3920 {
3921 if (delta_send > 8)
3922 {
3923 int dif = ((int)pm->ps->fd.forceJumpZStart - (int)pm->ps->origin[2]);
3924 int dmgLess = (forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]] - dif);
3925
3926 if (dmgLess < 0)
3927 {
3928 dmgLess = 0;
3929 }
3930
3931 delta_send -= (dmgLess*0.3);
3932
3933 if (delta_send < 8)
3934 {
3935 delta_send = 8;
3936 }
3937
3938 //Com_Printf("Damage sub: %i\n", (int)((dmgLess*0.1)));
3939 }
3940 }
3941 }
3942
3943 if (didRoll)
3944 { //Add the appropriate event..
3945 PM_AddEventWithParm( EV_ROLL, delta_send );
3946 }
3947 else
3948 {
3949 PM_AddEventWithParm( EV_FALL, delta_send );
3950 }
3951 }
3952 else
3953 {
3954 if (didRoll)
3955 {
3956 PM_AddEventWithParm( EV_ROLL, 0 );
3957 }
3958 else
3959 {
3960 PM_AddEventWithParm( EV_FOOTSTEP, PM_FootstepForSurface() );
3961 }
3962 }
3963 }
3964
3965 // make sure velocity resets so we don't bounce back up again in case we miss the clear elsewhere
3966 pm->ps->velocity[2] = 0;
3967
3968 // start footstep cycle over
3969 pm->ps->bobCycle = 0;
3970 }
3971
3972 /*
3973 =============
3974 PM_CorrectAllSolid
3975 =============
3976 */
PM_CorrectAllSolid(trace_t * trace)3977 static int PM_CorrectAllSolid( trace_t *trace ) {
3978 int i, j, k;
3979 vec3_t point;
3980
3981 if ( pm->debugLevel ) {
3982 Com_Printf("%i:allsolid\n", c_pmove);
3983 }
3984
3985 // jitter around
3986 for (i = -1; i <= 1; i++) {
3987 for (j = -1; j <= 1; j++) {
3988 for (k = -1; k <= 1; k++) {
3989 VectorCopy(pm->ps->origin, point);
3990 point[0] += (float) i;
3991 point[1] += (float) j;
3992 point[2] += (float) k;
3993 pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
3994 if ( !trace->allsolid ) {
3995 point[0] = pm->ps->origin[0];
3996 point[1] = pm->ps->origin[1];
3997 point[2] = pm->ps->origin[2] - 0.25;
3998
3999 pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
4000 pml.groundTrace = *trace;
4001 return qtrue;
4002 }
4003 }
4004 }
4005 }
4006
4007 pm->ps->groundEntityNum = ENTITYNUM_NONE;
4008 pml.groundPlane = qfalse;
4009 pml.walking = qfalse;
4010
4011 return qfalse;
4012 }
4013
4014 /*
4015 =============
4016 PM_GroundTraceMissed
4017
4018 The ground trace didn't hit a surface, so we are in freefall
4019 =============
4020 */
PM_GroundTraceMissed(void)4021 static void PM_GroundTraceMissed( void ) {
4022 trace_t trace;
4023 vec3_t point;
4024
4025 //rww - don't want to do this when handextend_choke, because you can be standing on the ground
4026 //while still holding your throat.
4027 if ( pm->ps->pm_type == PM_FLOAT )
4028 {
4029 //we're assuming this is because you're being choked
4030 int parts = SETANIM_LEGS;
4031
4032 //rww - also don't use SETANIM_FLAG_HOLD, it will cause the legs to float around a bit before going into
4033 //a proper anim even when on the ground.
4034 PM_SetAnim(parts, BOTH_CHOKE3, SETANIM_FLAG_OVERRIDE);
4035 }
4036 else if ( pm->ps->pm_type == PM_JETPACK )
4037 {//jetpacking
4038 //rww - also don't use SETANIM_FLAG_HOLD, it will cause the legs to float around a bit before going into
4039 //a proper anim even when on the ground.
4040 //PM_SetAnim(SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE);
4041 }
4042 //If the anim is choke3, act like we just went into the air because we aren't in a float
4043 else if ( pm->ps->groundEntityNum != ENTITYNUM_NONE || (pm->ps->legsAnim) == BOTH_CHOKE3 )
4044 {
4045 // we just transitioned into freefall
4046 if ( pm->debugLevel ) {
4047 Com_Printf("%i:lift\n", c_pmove);
4048 }
4049
4050 // if they aren't in a jumping animation and the ground is a ways away, force into it
4051 // if we didn't do the trace, the player would be backflipping down staircases
4052 VectorCopy( pm->ps->origin, point );
4053 point[2] -= 64;
4054
4055 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
4056 if ( trace.fraction == 1.0 || pm->ps->pm_type == PM_FLOAT ) {
4057 if ( pm->ps->velocity[2] <= 0 && !(pm->ps->pm_flags&PMF_JUMP_HELD))
4058 {
4059 //PM_SetAnim(SETANIM_LEGS,BOTH_INAIR1,SETANIM_FLAG_OVERRIDE);
4060 PM_SetAnim(SETANIM_LEGS,BOTH_INAIR1,0);
4061 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
4062 }
4063 else if ( pm->cmd.forwardmove >= 0 )
4064 {
4065 PM_SetAnim(SETANIM_LEGS,BOTH_JUMP1,SETANIM_FLAG_OVERRIDE);
4066 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
4067 }
4068 else
4069 {
4070 PM_SetAnim(SETANIM_LEGS,BOTH_JUMPBACK1,SETANIM_FLAG_OVERRIDE);
4071 pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
4072 }
4073
4074 pm->ps->inAirAnim = qtrue;
4075 }
4076 }
4077 else if (!pm->ps->inAirAnim)
4078 {
4079 // if they aren't in a jumping animation and the ground is a ways away, force into it
4080 // if we didn't do the trace, the player would be backflipping down staircases
4081 VectorCopy( pm->ps->origin, point );
4082 point[2] -= 64;
4083
4084 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
4085 if ( trace.fraction == 1.0 || pm->ps->pm_type == PM_FLOAT )
4086 {
4087 pm->ps->inAirAnim = qtrue;
4088 }
4089 }
4090
4091 if (PM_InRollComplete(pm->ps, pm->ps->legsAnim))
4092 { //Client won't catch an animation restart because it only checks frame against incoming frame, so if you roll when you land after rolling
4093 //off of something it won't replay the roll anim unless we switch it off in the air. This fixes that.
4094 PM_SetAnim(SETANIM_BOTH,BOTH_INAIR1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
4095 pm->ps->inAirAnim = qtrue;
4096 }
4097
4098 pm->ps->groundEntityNum = ENTITYNUM_NONE;
4099 pml.groundPlane = qfalse;
4100 pml.walking = qfalse;
4101 }
4102
4103
4104 /*
4105 =============
4106 PM_GroundTrace
4107 =============
4108 */
PM_GroundTrace(void)4109 static void PM_GroundTrace( void ) {
4110 vec3_t point;
4111 trace_t trace;
4112 float minNormal = (float)MIN_WALK_NORMAL;
4113
4114 if ( pm->ps->clientNum >= MAX_CLIENTS)
4115 {
4116 bgEntity_t *pEnt = pm_entSelf;
4117
4118 if (pEnt && pEnt->s.NPC_class == CLASS_VEHICLE)
4119 {
4120 minNormal = pEnt->m_pVehicle->m_pVehicleInfo->maxSlope;
4121 }
4122 }
4123
4124 point[0] = pm->ps->origin[0];
4125 point[1] = pm->ps->origin[1];
4126 point[2] = pm->ps->origin[2] - 0.25;
4127
4128 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
4129 pml.groundTrace = trace;
4130
4131 // do something corrective if the trace starts in a solid...
4132 if ( trace.allsolid ) {
4133 if ( !PM_CorrectAllSolid(&trace) )
4134 return;
4135 }
4136
4137 if (pm->ps->pm_type == PM_FLOAT || pm->ps->pm_type == PM_JETPACK)
4138 {
4139 PM_GroundTraceMissed();
4140 pml.groundPlane = qfalse;
4141 pml.walking = qfalse;
4142 return;
4143 }
4144
4145 // if the trace didn't hit anything, we are in free fall
4146 if ( trace.fraction == 1.0 ) {
4147 PM_GroundTraceMissed();
4148 pml.groundPlane = qfalse;
4149 pml.walking = qfalse;
4150 return;
4151 }
4152
4153 // check if getting thrown off the ground
4154 if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) {
4155 if ( pm->debugLevel ) {
4156 Com_Printf("%i:kickoff\n", c_pmove);
4157 }
4158 // go into jump animation
4159 if ( pm->cmd.forwardmove >= 0 ) {
4160 PM_ForceLegsAnim( BOTH_JUMP1 );
4161 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
4162 } else {
4163 PM_ForceLegsAnim( BOTH_JUMPBACK1 );
4164 pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
4165 }
4166
4167 pm->ps->groundEntityNum = ENTITYNUM_NONE;
4168 pml.groundPlane = qfalse;
4169 pml.walking = qfalse;
4170 return;
4171 }
4172
4173 // slopes that are too steep will not be considered onground
4174 if ( trace.plane.normal[2] < minNormal ) {
4175 if ( pm->debugLevel ) {
4176 Com_Printf("%i:steep\n", c_pmove);
4177 }
4178 pm->ps->groundEntityNum = ENTITYNUM_NONE;
4179 pml.groundPlane = qtrue;
4180 pml.walking = qfalse;
4181 return;
4182 }
4183
4184 pml.groundPlane = qtrue;
4185 pml.walking = qtrue;
4186
4187 // hitting solid ground will end a waterjump
4188 if (pm->ps->pm_flags & PMF_TIME_WATERJUMP)
4189 {
4190 pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
4191 pm->ps->pm_time = 0;
4192 }
4193
4194 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
4195 // just hit the ground
4196 if ( pm->debugLevel ) {
4197 Com_Printf("%i:Land\n", c_pmove);
4198 }
4199
4200 PM_CrashLand();
4201
4202 #ifdef _GAME
4203 if (pm->ps->clientNum < MAX_CLIENTS &&
4204 !pm->ps->m_iVehicleNum &&
4205 trace.entityNum < ENTITYNUM_WORLD &&
4206 trace.entityNum >= MAX_CLIENTS &&
4207 !pm->ps->zoomMode &&
4208 pm_entSelf)
4209 { //check if we landed on a vehicle
4210 gentity_t *trEnt = &g_entities[trace.entityNum];
4211 if (trEnt->inuse && trEnt->client && trEnt->s.eType == ET_NPC && trEnt->s.NPC_class == CLASS_VEHICLE &&
4212 !trEnt->client->ps.m_iVehicleNum &&
4213 trEnt->m_pVehicle &&
4214 trEnt->m_pVehicle->m_pVehicleInfo->type != VH_WALKER &&
4215 trEnt->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER)
4216 { //it's a vehicle alright, let's board it.. if it's not an atst or ship
4217 if (!BG_SaberInSpecial(pm->ps->saberMove) &&
4218 pm->ps->forceHandExtend == HANDEXTEND_NONE &&
4219 pm->ps->weaponTime <= 0)
4220 {
4221 gentity_t *servEnt = (gentity_t *)pm_entSelf;
4222 if (level.gametype < GT_TEAM ||
4223 !trEnt->alliedTeam ||
4224 (trEnt->alliedTeam == servEnt->client->sess.sessionTeam))
4225 { //not belonging to a team, or client is on same team
4226 trEnt->m_pVehicle->m_pVehicleInfo->Board(trEnt->m_pVehicle, pm_entSelf);
4227 }
4228 }
4229 }
4230 }
4231 #endif
4232
4233 // don't do landing time if we were just going down a slope
4234 if ( pml.previous_velocity[2] < -200 ) {
4235 // don't allow another jump for a little while
4236 pm->ps->pm_flags |= PMF_TIME_LAND;
4237 pm->ps->pm_time = 250;
4238 }
4239 }
4240
4241 pm->ps->groundEntityNum = trace.entityNum;
4242 pm->ps->lastOnGround = pm->cmd.serverTime;
4243
4244 PM_AddTouchEnt( trace.entityNum );
4245 }
4246
4247
4248 /*
4249 =============
4250 PM_SetWaterLevel
4251 =============
4252 */
PM_SetWaterLevel(void)4253 static void PM_SetWaterLevel( void ) {
4254 vec3_t point;
4255 int cont;
4256 int sample1;
4257 int sample2;
4258
4259 //
4260 // get waterlevel, accounting for ducking
4261 //
4262 pm->waterlevel = 0;
4263 pm->watertype = 0;
4264
4265 point[0] = pm->ps->origin[0];
4266 point[1] = pm->ps->origin[1];
4267 point[2] = pm->ps->origin[2] + MINS_Z + 1;
4268 cont = pm->pointcontents( point, pm->ps->clientNum );
4269
4270 if ( cont & MASK_WATER ) {
4271 sample2 = pm->ps->viewheight - MINS_Z;
4272 sample1 = sample2 / 2;
4273
4274 pm->watertype = cont;
4275 pm->waterlevel = 1;
4276 point[2] = pm->ps->origin[2] + MINS_Z + sample1;
4277 cont = pm->pointcontents (point, pm->ps->clientNum );
4278 if ( cont & MASK_WATER ) {
4279 pm->waterlevel = 2;
4280 point[2] = pm->ps->origin[2] + MINS_Z + sample2;
4281 cont = pm->pointcontents (point, pm->ps->clientNum );
4282 if ( cont & MASK_WATER ){
4283 pm->waterlevel = 3;
4284 }
4285 }
4286 }
4287
4288 }
4289
PM_CheckDualForwardJumpDuck(void)4290 qboolean PM_CheckDualForwardJumpDuck( void )
4291 {
4292 qboolean resized = qfalse;
4293 if ( pm->ps->legsAnim == BOTH_JUMPATTACK6 )
4294 {
4295 //dynamically reduce bounding box to let character sail over heads of enemies
4296 if ( ( pm->ps->legsTimer >= 1450
4297 && PM_AnimLength( 0, BOTH_JUMPATTACK6 ) - pm->ps->legsTimer >= 400 )
4298 ||(pm->ps->legsTimer >= 400
4299 && PM_AnimLength( 0, BOTH_JUMPATTACK6 ) - pm->ps->legsTimer >= 1100 ) )
4300 {//in a part of the anim that we're pretty much sideways in, raise up the mins
4301 pm->mins[2] = 0;
4302 pm->ps->pm_flags |= PMF_FIX_MINS;
4303 resized = qtrue;
4304 }
4305 }
4306 return resized;
4307 }
4308
PM_CheckFixMins(void)4309 void PM_CheckFixMins( void )
4310 {
4311 if ( (pm->ps->pm_flags&PMF_FIX_MINS) )// pm->mins[2] > DEFAULT_MINS_2 )
4312 {//drop the mins back down
4313 //do a trace to make sure it's okay
4314 trace_t trace;
4315 vec3_t end, curMins, curMaxs;
4316
4317 VectorSet( end, pm->ps->origin[0], pm->ps->origin[1], pm->ps->origin[2]+MINS_Z );
4318 VectorSet( curMins, pm->mins[0], pm->mins[1], 0 );
4319 VectorSet( curMaxs, pm->maxs[0], pm->maxs[1], pm->ps->standheight );
4320
4321 pm->trace( &trace, pm->ps->origin, curMins, curMaxs, end, pm->ps->clientNum, pm->tracemask );
4322 if ( !trace.allsolid && !trace.startsolid )
4323 {//should never start in solid
4324 if ( trace.fraction >= 1.0f )
4325 {//all clear
4326 //drop the bottom of my bbox back down
4327 pm->mins[2] = MINS_Z;
4328 pm->ps->pm_flags &= ~PMF_FIX_MINS;
4329 }
4330 else
4331 {//move me up so the bottom of my bbox will be where the trace ended, at least
4332 //need to trace up, too
4333 float updist = ((1.0f-trace.fraction) * -MINS_Z);
4334 end[2] = pm->ps->origin[2]+updist;
4335 pm->trace( &trace, pm->ps->origin, curMins, curMaxs, end, pm->ps->clientNum, pm->tracemask );
4336 if ( !trace.allsolid && !trace.startsolid )
4337 {//should never start in solid
4338 if ( trace.fraction >= 1.0f )
4339 {//all clear
4340 //move me up
4341 pm->ps->origin[2] += updist;
4342 //drop the bottom of my bbox back down
4343 pm->mins[2] = MINS_Z;
4344 pm->ps->pm_flags &= ~PMF_FIX_MINS;
4345 }
4346 else
4347 {//crap, no room to expand, so just crouch us
4348 if ( pm->ps->legsAnim != BOTH_JUMPATTACK6
4349 || pm->ps->legsTimer <= 200 )
4350 {//at the end of the anim, and we can't leave ourselves like this
4351 //so drop the maxs, put the mins back and move us up
4352 pm->maxs[2] += MINS_Z;
4353 pm->ps->origin[2] -= MINS_Z;
4354 pm->mins[2] = MINS_Z;
4355 //this way we'll be in a crouch when we're done
4356 if ( pm->ps->legsAnim == BOTH_JUMPATTACK6 )
4357 {
4358 pm->ps->legsTimer = pm->ps->torsoTimer = 0;
4359 }
4360 pm->ps->pm_flags |= PMF_DUCKED;
4361 //FIXME: do we need to set a crouch anim here?
4362 pm->ps->pm_flags &= ~PMF_FIX_MINS;
4363 }
4364 }
4365 }//crap, stuck
4366 }
4367 }//crap, stuck!
4368 }
4369 }
4370
PM_CanStand(void)4371 static qboolean PM_CanStand ( void )
4372 {
4373 qboolean canStand = qtrue;
4374 float x, y;
4375 trace_t trace;
4376
4377 const vec3_t lineMins = { -5.0f, -5.0f, -2.5f };
4378 const vec3_t lineMaxs = { 5.0f, 5.0f, 0.0f };
4379
4380 for ( x = pm->mins[0] + 5.0f; canStand && x <= (pm->maxs[0] - 5.0f); x += 10.0f )
4381 {
4382 for ( y = pm->mins[1] + 5.0f; y <= (pm->maxs[1] - 5.0f); y += 10.0f )
4383 {
4384 vec3_t start, end;//
4385 VectorSet( start, x, y, pm->maxs[2] );
4386 VectorSet( end, x, y, pm->ps->standheight );
4387
4388 VectorAdd (start, pm->ps->origin, start);
4389 VectorAdd (end, pm->ps->origin, end);
4390
4391 pm->trace (&trace, start, lineMins, lineMaxs, end, pm->ps->clientNum, pm->tracemask);
4392 if ( trace.allsolid || trace.fraction < 1.0f )
4393 {
4394 canStand = qfalse;
4395 break;
4396 }
4397 }
4398 }
4399
4400 return canStand;
4401 }
4402
4403 /*
4404 ==============
4405 PM_CheckDuck
4406
4407 Sets mins, maxs, and pm->ps->viewheight
4408 ==============
4409 */
PM_CheckDuck(void)4410 static void PM_CheckDuck (void)
4411 {
4412 // trace_t trace;
4413
4414 if ( pm->ps->m_iVehicleNum > 0 && pm->ps->m_iVehicleNum < ENTITYNUM_NONE )
4415 {//riding a vehicle or are a vehicle
4416 //no ducking or rolling when on a vehicle
4417 //right? not even on ones that you just ride on top of?
4418 pm->ps->pm_flags &= ~PMF_DUCKED;
4419 pm->ps->pm_flags &= ~PMF_ROLLING;
4420 //NOTE: we don't clear the pm->cmd.upmove here because
4421 //the vehicle code may need it later... but, for riders,
4422 //it should have already been copied over to the vehicle, right?
4423
4424 if (pm->ps->clientNum >= MAX_CLIENTS)
4425 {
4426 return;
4427 }
4428 if (pm_entVeh && pm_entVeh->m_pVehicle &&
4429 (pm_entVeh->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER ||
4430 pm_entVeh->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL))
4431 {
4432 trace_t solidTr;
4433
4434 pm->mins[0] = -16;
4435 pm->mins[1] = -16;
4436 pm->mins[2] = MINS_Z;
4437
4438 pm->maxs[0] = 16;
4439 pm->maxs[1] = 16;
4440 pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
4441 pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
4442
4443 pm->trace (&solidTr, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->m_iVehicleNum, pm->tracemask);
4444 if (solidTr.startsolid || solidTr.allsolid || solidTr.fraction != 1.0f)
4445 { //whoops, can't fit here. Down to 0!
4446 VectorClear(pm->mins);
4447 VectorClear(pm->maxs);
4448 #ifdef _GAME
4449 {
4450 gentity_t *me = &g_entities[pm->ps->clientNum];
4451 if (me->inuse && me->client)
4452 { //yeah, this is a really terrible hack.
4453 me->client->solidHack = level.time + 200;
4454 }
4455 }
4456 #endif
4457 }
4458 }
4459 }
4460 else
4461 {
4462 if (pm->ps->clientNum < MAX_CLIENTS)
4463 {
4464 pm->mins[0] = -15;
4465 pm->mins[1] = -15;
4466
4467 pm->maxs[0] = 15;
4468 pm->maxs[1] = 15;
4469 }
4470
4471 if ( PM_CheckDualForwardJumpDuck() )
4472 {//special anim resizing us
4473 }
4474 else
4475 {
4476 PM_CheckFixMins();
4477
4478 if ( !pm->mins[2] )
4479 {
4480 pm->mins[2] = MINS_Z;
4481 }
4482 }
4483
4484 if (pm->ps->pm_type == PM_DEAD && pm->ps->clientNum < MAX_CLIENTS)
4485 {
4486 pm->maxs[2] = -8;
4487 pm->ps->viewheight = DEAD_VIEWHEIGHT;
4488 return;
4489 }
4490
4491 if (BG_InRoll(pm->ps, pm->ps->legsAnim) && !BG_KickingAnim(pm->ps->legsAnim))
4492 {
4493 pm->maxs[2] = pm->ps->crouchheight; //CROUCH_MAXS_2;
4494 pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
4495 pm->ps->pm_flags &= ~PMF_DUCKED;
4496 pm->ps->pm_flags |= PMF_ROLLING;
4497 return;
4498 }
4499 else if (pm->ps->pm_flags & PMF_ROLLING)
4500 {
4501 if ( PM_CanStand() ) {
4502 pm->maxs[2] = pm->ps->standheight;
4503 pm->ps->pm_flags &= ~PMF_ROLLING;
4504 }
4505 }
4506 else if (pm->cmd.upmove < 0 ||
4507 pm->ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
4508 pm->ps->forceHandExtend == HANDEXTEND_PRETHROWN ||
4509 pm->ps->forceHandExtend == HANDEXTEND_POSTTHROWN)
4510 { // duck
4511 pm->ps->pm_flags |= PMF_DUCKED;
4512 }
4513 else
4514 { // stand up if possible
4515 if (pm->ps->pm_flags & PMF_DUCKED)
4516 {
4517 if ( PM_CanStand() ) {
4518 pm->maxs[2] = pm->ps->standheight;
4519 pm->ps->pm_flags &= ~PMF_DUCKED;
4520 }
4521 }
4522 }
4523 }
4524
4525 if (pm->ps->pm_flags & PMF_DUCKED)
4526 {
4527 pm->maxs[2] = pm->ps->crouchheight;//CROUCH_MAXS_2;
4528 pm->ps->viewheight = CROUCH_VIEWHEIGHT;
4529 }
4530 else if (pm->ps->pm_flags & PMF_ROLLING)
4531 {
4532 pm->maxs[2] = pm->ps->crouchheight;//CROUCH_MAXS_2;
4533 pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
4534 }
4535 else
4536 {
4537 pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
4538 pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
4539 }
4540 }
4541
4542
4543
4544 //===================================================================
4545
4546
4547
4548 /*
4549 ==============
4550 PM_Use
4551
4552 Generates a use event
4553 ==============
4554 */
4555 #define USE_DELAY 2000
4556
PM_Use(void)4557 void PM_Use( void )
4558 {
4559 if ( pm->ps->useTime > 0 )
4560 pm->ps->useTime -= 100;//pm->cmd.msec;
4561
4562 if ( pm->ps->useTime > 0 ) {
4563 return;
4564 }
4565
4566 if ( ! (pm->cmd.buttons & BUTTON_USE ) )
4567 {
4568 pm->useEvent = 0;
4569 pm->ps->useTime = 0;
4570 return;
4571 }
4572
4573 pm->useEvent = EV_USE;
4574 pm->ps->useTime = USE_DELAY;
4575 }
4576
PM_WalkingAnim(int anim)4577 qboolean PM_WalkingAnim( int anim )
4578 {
4579 switch ( anim )
4580 {
4581 case BOTH_WALK1: //# Normal walk
4582 case BOTH_WALK2: //# Normal walk with saber
4583 case BOTH_WALK_STAFF: //# Normal walk with staff
4584 case BOTH_WALK_DUAL: //# Normal walk with staff
4585 case BOTH_WALK5: //# Tavion taunting Kyle (cin 22)
4586 case BOTH_WALK6: //# Slow walk for Luke (cin 12)
4587 case BOTH_WALK7: //# Fast walk
4588 case BOTH_WALKBACK1: //# Walk1 backwards
4589 case BOTH_WALKBACK2: //# Walk2 backwards
4590 case BOTH_WALKBACK_STAFF: //# Walk backwards with staff
4591 case BOTH_WALKBACK_DUAL: //# Walk backwards with dual
4592 return qtrue;
4593 break;
4594 }
4595 return qfalse;
4596 }
4597
PM_RunningAnim(int anim)4598 qboolean PM_RunningAnim( int anim )
4599 {
4600 switch ( (anim) )
4601 {
4602 case BOTH_RUN1:
4603 case BOTH_RUN2:
4604 case BOTH_RUN_STAFF:
4605 case BOTH_RUN_DUAL:
4606 case BOTH_RUNBACK1:
4607 case BOTH_RUNBACK2:
4608 case BOTH_RUNBACK_STAFF:
4609 case BOTH_RUNBACK_DUAL:
4610 case BOTH_RUN1START: //# Start into full run1
4611 case BOTH_RUN1STOP: //# Stop from full run1
4612 case BOTH_RUNSTRAFE_LEFT1: //# Sidestep left: should loop
4613 case BOTH_RUNSTRAFE_RIGHT1: //# Sidestep right: should loop
4614 return qtrue;
4615 break;
4616 }
4617 return qfalse;
4618 }
4619
PM_SwimmingAnim(int anim)4620 qboolean PM_SwimmingAnim( int anim )
4621 {
4622 switch ( anim )
4623 {
4624 case BOTH_SWIM_IDLE1: //# Swimming Idle 1
4625 case BOTH_SWIMFORWARD: //# Swim forward loop
4626 case BOTH_SWIMBACKWARD: //# Swim backward loop
4627 return qtrue;
4628 break;
4629 }
4630 return qfalse;
4631 }
4632
PM_RollingAnim(int anim)4633 qboolean PM_RollingAnim( int anim )
4634 {
4635 switch ( anim )
4636 {
4637 case BOTH_ROLL_F: //# Roll forward
4638 case BOTH_ROLL_B: //# Roll backward
4639 case BOTH_ROLL_L: //# Roll left
4640 case BOTH_ROLL_R: //# Roll right
4641 return qtrue;
4642 break;
4643 }
4644 return qfalse;
4645 }
4646
PM_AnglesForSlope(const float yaw,const vec3_t slope,vec3_t angles)4647 void PM_AnglesForSlope( const float yaw, const vec3_t slope, vec3_t angles )
4648 {
4649 vec3_t nvf, ovf, ovr, new_angles;
4650 float pitch, mod, dot;
4651
4652 VectorSet( angles, 0, yaw, 0 );
4653 AngleVectors( angles, ovf, ovr, NULL );
4654
4655 vectoangles( slope, new_angles );
4656 pitch = new_angles[PITCH] + 90;
4657 new_angles[ROLL] = new_angles[PITCH] = 0;
4658
4659 AngleVectors( new_angles, nvf, NULL, NULL );
4660
4661 mod = DotProduct( nvf, ovr );
4662
4663 if ( mod < 0 )
4664 mod = -1;
4665 else
4666 mod = 1;
4667
4668 dot = DotProduct( nvf, ovf );
4669
4670 angles[YAW] = 0;
4671 angles[PITCH] = dot * pitch;
4672 angles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
4673 }
4674
PM_FootSlopeTrace(float * pDiff,float * pInterval)4675 void PM_FootSlopeTrace( float *pDiff, float *pInterval )
4676 {
4677 vec3_t footLOrg, footROrg, footLBot, footRBot;
4678 vec3_t footLPoint, footRPoint;
4679 vec3_t footMins, footMaxs;
4680 vec3_t footLSlope, footRSlope;
4681
4682 trace_t trace;
4683 float diff, interval;
4684
4685 mdxaBone_t boltMatrix;
4686 vec3_t G2Angles;
4687
4688 VectorSet(G2Angles, 0, pm->ps->viewangles[YAW], 0);
4689
4690 interval = 4;//?
4691
4692 trap->G2API_GetBoltMatrix( pm->ghoul2, 0, pm->g2Bolts_LFoot, &boltMatrix, G2Angles, pm->ps->origin, pm->cmd.serverTime, NULL, pm->modelScale );
4693 footLPoint[0] = boltMatrix.matrix[0][3];
4694 footLPoint[1] = boltMatrix.matrix[1][3];
4695 footLPoint[2] = boltMatrix.matrix[2][3];
4696
4697 trap->G2API_GetBoltMatrix( pm->ghoul2, 0, pm->g2Bolts_RFoot, &boltMatrix, G2Angles, pm->ps->origin, pm->cmd.serverTime, NULL, pm->modelScale );
4698 footRPoint[0] = boltMatrix.matrix[0][3];
4699 footRPoint[1] = boltMatrix.matrix[1][3];
4700 footRPoint[2] = boltMatrix.matrix[2][3];
4701
4702 //get these on the cgame and store it, save ourselves a ghoul2 construct skel call
4703 VectorCopy( footLPoint, footLOrg );
4704 VectorCopy( footRPoint, footROrg );
4705
4706 //step 2: adjust foot tag z height to bottom of bbox+1
4707 footLOrg[2] = pm->ps->origin[2] + pm->mins[2] + 1;
4708 footROrg[2] = pm->ps->origin[2] + pm->mins[2] + 1;
4709 VectorSet( footLBot, footLOrg[0], footLOrg[1], footLOrg[2] - interval*10 );
4710 VectorSet( footRBot, footROrg[0], footROrg[1], footROrg[2] - interval*10 );
4711
4712 //step 3: trace down from each, find difference
4713 VectorSet( footMins, -3, -3, 0 );
4714 VectorSet( footMaxs, 3, 3, 1 );
4715
4716 pm->trace( &trace, footLOrg, footMins, footMaxs, footLBot, pm->ps->clientNum, pm->tracemask );
4717 VectorCopy( trace.endpos, footLBot );
4718 VectorCopy( trace.plane.normal, footLSlope );
4719
4720 pm->trace( &trace, footROrg, footMins, footMaxs, footRBot, pm->ps->clientNum, pm->tracemask );
4721 VectorCopy( trace.endpos, footRBot );
4722 VectorCopy( trace.plane.normal, footRSlope );
4723
4724 diff = footLBot[2] - footRBot[2];
4725
4726 if ( pDiff != NULL )
4727 {
4728 *pDiff = diff;
4729 }
4730 if ( pInterval != NULL )
4731 {
4732 *pInterval = interval;
4733 }
4734 }
4735
BG_InSlopeAnim(int anim)4736 qboolean BG_InSlopeAnim( int anim )
4737 {
4738 switch ( anim )
4739 {
4740 case LEGS_LEFTUP1: //# On a slope with left foot 4 higher than right
4741 case LEGS_LEFTUP2: //# On a slope with left foot 8 higher than right
4742 case LEGS_LEFTUP3: //# On a slope with left foot 12 higher than right
4743 case LEGS_LEFTUP4: //# On a slope with left foot 16 higher than right
4744 case LEGS_LEFTUP5: //# On a slope with left foot 20 higher than right
4745 case LEGS_RIGHTUP1: //# On a slope with RIGHT foot 4 higher than left
4746 case LEGS_RIGHTUP2: //# On a slope with RIGHT foot 8 higher than left
4747 case LEGS_RIGHTUP3: //# On a slope with RIGHT foot 12 higher than left
4748 case LEGS_RIGHTUP4: //# On a slope with RIGHT foot 16 higher than left
4749 case LEGS_RIGHTUP5: //# On a slope with RIGHT foot 20 higher than left
4750 case LEGS_S1_LUP1:
4751 case LEGS_S1_LUP2:
4752 case LEGS_S1_LUP3:
4753 case LEGS_S1_LUP4:
4754 case LEGS_S1_LUP5:
4755 case LEGS_S1_RUP1:
4756 case LEGS_S1_RUP2:
4757 case LEGS_S1_RUP3:
4758 case LEGS_S1_RUP4:
4759 case LEGS_S1_RUP5:
4760 case LEGS_S3_LUP1:
4761 case LEGS_S3_LUP2:
4762 case LEGS_S3_LUP3:
4763 case LEGS_S3_LUP4:
4764 case LEGS_S3_LUP5:
4765 case LEGS_S3_RUP1:
4766 case LEGS_S3_RUP2:
4767 case LEGS_S3_RUP3:
4768 case LEGS_S3_RUP4:
4769 case LEGS_S3_RUP5:
4770 case LEGS_S4_LUP1:
4771 case LEGS_S4_LUP2:
4772 case LEGS_S4_LUP3:
4773 case LEGS_S4_LUP4:
4774 case LEGS_S4_LUP5:
4775 case LEGS_S4_RUP1:
4776 case LEGS_S4_RUP2:
4777 case LEGS_S4_RUP3:
4778 case LEGS_S4_RUP4:
4779 case LEGS_S4_RUP5:
4780 case LEGS_S5_LUP1:
4781 case LEGS_S5_LUP2:
4782 case LEGS_S5_LUP3:
4783 case LEGS_S5_LUP4:
4784 case LEGS_S5_LUP5:
4785 case LEGS_S5_RUP1:
4786 case LEGS_S5_RUP2:
4787 case LEGS_S5_RUP3:
4788 case LEGS_S5_RUP4:
4789 case LEGS_S5_RUP5:
4790 return qtrue;
4791 break;
4792 }
4793 return qfalse;
4794 }
4795
4796 #define SLOPE_RECALC_INT 100
4797
PM_AdjustStandAnimForSlope(void)4798 qboolean PM_AdjustStandAnimForSlope( void )
4799 {
4800 float diff;
4801 float interval;
4802 int destAnim;
4803 int legsAnim;
4804 #define SLOPERECALCVAR pm->ps->slopeRecalcTime //this is purely convenience
4805
4806 if (!pm->ghoul2)
4807 { //probably just changed models and not quite in sync yet
4808 return qfalse;
4809 }
4810
4811 if ( pm->g2Bolts_LFoot == -1 || pm->g2Bolts_RFoot == -1 )
4812 {//need these bolts!
4813 return qfalse;
4814 }
4815
4816 //step 1: find the 2 foot tags
4817 PM_FootSlopeTrace( &diff, &interval );
4818
4819 //step 4: based on difference, choose one of the left/right slope-match intervals
4820 if ( diff >= interval*5 )
4821 {
4822 destAnim = LEGS_LEFTUP5;
4823 }
4824 else if ( diff >= interval*4 )
4825 {
4826 destAnim = LEGS_LEFTUP4;
4827 }
4828 else if ( diff >= interval*3 )
4829 {
4830 destAnim = LEGS_LEFTUP3;
4831 }
4832 else if ( diff >= interval*2 )
4833 {
4834 destAnim = LEGS_LEFTUP2;
4835 }
4836 else if ( diff >= interval )
4837 {
4838 destAnim = LEGS_LEFTUP1;
4839 }
4840 else if ( diff <= interval*-5 )
4841 {
4842 destAnim = LEGS_RIGHTUP5;
4843 }
4844 else if ( diff <= interval*-4 )
4845 {
4846 destAnim = LEGS_RIGHTUP4;
4847 }
4848 else if ( diff <= interval*-3 )
4849 {
4850 destAnim = LEGS_RIGHTUP3;
4851 }
4852 else if ( diff <= interval*-2 )
4853 {
4854 destAnim = LEGS_RIGHTUP2;
4855 }
4856 else if ( diff <= interval*-1 )
4857 {
4858 destAnim = LEGS_RIGHTUP1;
4859 }
4860 else
4861 {
4862 return qfalse;
4863 }
4864
4865 legsAnim = pm->ps->legsAnim;
4866 //adjust for current legs anim
4867 switch ( legsAnim )
4868 {
4869 case BOTH_STAND1:
4870
4871 case LEGS_S1_LUP1:
4872 case LEGS_S1_LUP2:
4873 case LEGS_S1_LUP3:
4874 case LEGS_S1_LUP4:
4875 case LEGS_S1_LUP5:
4876 case LEGS_S1_RUP1:
4877 case LEGS_S1_RUP2:
4878 case LEGS_S1_RUP3:
4879 case LEGS_S1_RUP4:
4880 case LEGS_S1_RUP5:
4881 destAnim = LEGS_S1_LUP1 + (destAnim-LEGS_LEFTUP1);
4882 break;
4883 case BOTH_STAND2:
4884 case BOTH_SABERFAST_STANCE:
4885 case BOTH_SABERSLOW_STANCE:
4886 case BOTH_CROUCH1IDLE:
4887 case BOTH_CROUCH1:
4888 case LEGS_LEFTUP1: //# On a slope with left foot 4 higher than right
4889 case LEGS_LEFTUP2: //# On a slope with left foot 8 higher than right
4890 case LEGS_LEFTUP3: //# On a slope with left foot 12 higher than right
4891 case LEGS_LEFTUP4: //# On a slope with left foot 16 higher than right
4892 case LEGS_LEFTUP5: //# On a slope with left foot 20 higher than right
4893 case LEGS_RIGHTUP1: //# On a slope with RIGHT foot 4 higher than left
4894 case LEGS_RIGHTUP2: //# On a slope with RIGHT foot 8 higher than left
4895 case LEGS_RIGHTUP3: //# On a slope with RIGHT foot 12 higher than left
4896 case LEGS_RIGHTUP4: //# On a slope with RIGHT foot 16 higher than left
4897 case LEGS_RIGHTUP5: //# On a slope with RIGHT foot 20 higher than left
4898 //fine
4899 break;
4900 case BOTH_STAND3:
4901 case LEGS_S3_LUP1:
4902 case LEGS_S3_LUP2:
4903 case LEGS_S3_LUP3:
4904 case LEGS_S3_LUP4:
4905 case LEGS_S3_LUP5:
4906 case LEGS_S3_RUP1:
4907 case LEGS_S3_RUP2:
4908 case LEGS_S3_RUP3:
4909 case LEGS_S3_RUP4:
4910 case LEGS_S3_RUP5:
4911 destAnim = LEGS_S3_LUP1 + (destAnim-LEGS_LEFTUP1);
4912 break;
4913 case BOTH_STAND4:
4914 case LEGS_S4_LUP1:
4915 case LEGS_S4_LUP2:
4916 case LEGS_S4_LUP3:
4917 case LEGS_S4_LUP4:
4918 case LEGS_S4_LUP5:
4919 case LEGS_S4_RUP1:
4920 case LEGS_S4_RUP2:
4921 case LEGS_S4_RUP3:
4922 case LEGS_S4_RUP4:
4923 case LEGS_S4_RUP5:
4924 destAnim = LEGS_S4_LUP1 + (destAnim-LEGS_LEFTUP1);
4925 break;
4926 case BOTH_STAND5:
4927 case LEGS_S5_LUP1:
4928 case LEGS_S5_LUP2:
4929 case LEGS_S5_LUP3:
4930 case LEGS_S5_LUP4:
4931 case LEGS_S5_LUP5:
4932 case LEGS_S5_RUP1:
4933 case LEGS_S5_RUP2:
4934 case LEGS_S5_RUP3:
4935 case LEGS_S5_RUP4:
4936 case LEGS_S5_RUP5:
4937 destAnim = LEGS_S5_LUP1 + (destAnim-LEGS_LEFTUP1);
4938 break;
4939 case BOTH_STAND6:
4940 default:
4941 return qfalse;
4942 break;
4943 }
4944
4945 //step 5: based on the chosen interval and the current legsAnim, pick the correct anim
4946 //step 6: increment/decrement to the dest anim, not instant
4947 if ( (legsAnim >= LEGS_LEFTUP1 && legsAnim <= LEGS_LEFTUP5)
4948 || (legsAnim >= LEGS_S1_LUP1 && legsAnim <= LEGS_S1_LUP5)
4949 || (legsAnim >= LEGS_S3_LUP1 && legsAnim <= LEGS_S3_LUP5)
4950 || (legsAnim >= LEGS_S4_LUP1 && legsAnim <= LEGS_S4_LUP5)
4951 || (legsAnim >= LEGS_S5_LUP1 && legsAnim <= LEGS_S5_LUP5) )
4952 {//already in left-side up
4953 if ( destAnim > legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
4954 {
4955 legsAnim++;
4956 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
4957 }
4958 else if ( destAnim < legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
4959 {
4960 legsAnim--;
4961 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
4962 }
4963 else //if (SLOPERECALCVAR < pm->cmd.serverTime)
4964 {
4965 legsAnim = destAnim;
4966 }
4967
4968 destAnim = legsAnim;
4969 }
4970 else if ( (legsAnim >= LEGS_RIGHTUP1 && legsAnim <= LEGS_RIGHTUP5)
4971 || (legsAnim >= LEGS_S1_RUP1 && legsAnim <= LEGS_S1_RUP5)
4972 || (legsAnim >= LEGS_S3_RUP1 && legsAnim <= LEGS_S3_RUP5)
4973 || (legsAnim >= LEGS_S4_RUP1 && legsAnim <= LEGS_S4_RUP5)
4974 || (legsAnim >= LEGS_S5_RUP1 && legsAnim <= LEGS_S5_RUP5) )
4975 {//already in right-side up
4976 if ( destAnim > legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
4977 {
4978 legsAnim++;
4979 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
4980 }
4981 else if ( destAnim < legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
4982 {
4983 legsAnim--;
4984 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
4985 }
4986 else //if (SLOPERECALCVAR < pm->cmd.serverTime)
4987 {
4988 legsAnim = destAnim;
4989 }
4990
4991 destAnim = legsAnim;
4992 }
4993 else
4994 {//in a stand of some sort?
4995 switch ( legsAnim )
4996 {
4997 case BOTH_STAND1:
4998 case TORSO_WEAPONREADY1:
4999 case TORSO_WEAPONREADY2:
5000 case TORSO_WEAPONREADY3:
5001 case TORSO_WEAPONREADY10:
5002
5003 if ( destAnim >= LEGS_S1_LUP1 && destAnim <= LEGS_S1_LUP5 )
5004 {//going into left side up
5005 destAnim = LEGS_S1_LUP1;
5006 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5007 }
5008 else if ( destAnim >= LEGS_S1_RUP1 && destAnim <= LEGS_S1_RUP5 )
5009 {//going into right side up
5010 destAnim = LEGS_S1_RUP1;
5011 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5012 }
5013 else
5014 {//will never get here
5015 return qfalse;
5016 }
5017 break;
5018 case BOTH_STAND2:
5019 case BOTH_SABERFAST_STANCE:
5020 case BOTH_SABERSLOW_STANCE:
5021 case BOTH_CROUCH1IDLE:
5022 if ( destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5 )
5023 {//going into left side up
5024 destAnim = LEGS_LEFTUP1;
5025 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5026 }
5027 else if ( destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5 )
5028 {//going into right side up
5029 destAnim = LEGS_RIGHTUP1;
5030 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5031 }
5032 else
5033 {//will never get here
5034 return qfalse;
5035 }
5036 break;
5037 case BOTH_STAND3:
5038 if ( destAnim >= LEGS_S3_LUP1 && destAnim <= LEGS_S3_LUP5 )
5039 {//going into left side up
5040 destAnim = LEGS_S3_LUP1;
5041 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5042 }
5043 else if ( destAnim >= LEGS_S3_RUP1 && destAnim <= LEGS_S3_RUP5 )
5044 {//going into right side up
5045 destAnim = LEGS_S3_RUP1;
5046 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5047 }
5048 else
5049 {//will never get here
5050 return qfalse;
5051 }
5052 break;
5053 case BOTH_STAND4:
5054 if ( destAnim >= LEGS_S4_LUP1 && destAnim <= LEGS_S4_LUP5 )
5055 {//going into left side up
5056 destAnim = LEGS_S4_LUP1;
5057 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5058 }
5059 else if ( destAnim >= LEGS_S4_RUP1 && destAnim <= LEGS_S4_RUP5 )
5060 {//going into right side up
5061 destAnim = LEGS_S4_RUP1;
5062 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5063 }
5064 else
5065 {//will never get here
5066 return qfalse;
5067 }
5068 break;
5069 case BOTH_STAND5:
5070 if ( destAnim >= LEGS_S5_LUP1 && destAnim <= LEGS_S5_LUP5 )
5071 {//going into left side up
5072 destAnim = LEGS_S5_LUP1;
5073 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5074 }
5075 else if ( destAnim >= LEGS_S5_RUP1 && destAnim <= LEGS_S5_RUP5 )
5076 {//going into right side up
5077 destAnim = LEGS_S5_RUP1;
5078 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5079 }
5080 else
5081 {//will never get here
5082 return qfalse;
5083 }
5084 break;
5085 case BOTH_STAND6:
5086 default:
5087 return qfalse;
5088 break;
5089 }
5090 }
5091 //step 7: set the anim
5092 //PM_SetAnim( SETANIM_LEGS, destAnim, SETANIM_FLAG_NORMAL );
5093 PM_ContinueLegsAnim(destAnim);
5094
5095 return qtrue;
5096 }
5097
5098 extern int WeaponReadyLegsAnim[WP_NUM_WEAPONS];
5099
5100 //rww - slowly back out of slope leg anims, to prevent skipping between slope anims and general jittering
PM_LegsSlopeBackTransition(int desiredAnim)5101 int PM_LegsSlopeBackTransition(int desiredAnim)
5102 {
5103 int anim = pm->ps->legsAnim;
5104 int resultingAnim = desiredAnim;
5105
5106 switch ( anim )
5107 {
5108 case LEGS_LEFTUP2: //# On a slope with left foot 8 higher than right
5109 case LEGS_LEFTUP3: //# On a slope with left foot 12 higher than right
5110 case LEGS_LEFTUP4: //# On a slope with left foot 16 higher than right
5111 case LEGS_LEFTUP5: //# On a slope with left foot 20 higher than right
5112 case LEGS_RIGHTUP2: //# On a slope with RIGHT foot 8 higher than left
5113 case LEGS_RIGHTUP3: //# On a slope with RIGHT foot 12 higher than left
5114 case LEGS_RIGHTUP4: //# On a slope with RIGHT foot 16 higher than left
5115 case LEGS_RIGHTUP5: //# On a slope with RIGHT foot 20 higher than left
5116 case LEGS_S1_LUP2:
5117 case LEGS_S1_LUP3:
5118 case LEGS_S1_LUP4:
5119 case LEGS_S1_LUP5:
5120 case LEGS_S1_RUP2:
5121 case LEGS_S1_RUP3:
5122 case LEGS_S1_RUP4:
5123 case LEGS_S1_RUP5:
5124 case LEGS_S3_LUP2:
5125 case LEGS_S3_LUP3:
5126 case LEGS_S3_LUP4:
5127 case LEGS_S3_LUP5:
5128 case LEGS_S3_RUP2:
5129 case LEGS_S3_RUP3:
5130 case LEGS_S3_RUP4:
5131 case LEGS_S3_RUP5:
5132 case LEGS_S4_LUP2:
5133 case LEGS_S4_LUP3:
5134 case LEGS_S4_LUP4:
5135 case LEGS_S4_LUP5:
5136 case LEGS_S4_RUP2:
5137 case LEGS_S4_RUP3:
5138 case LEGS_S4_RUP4:
5139 case LEGS_S4_RUP5:
5140 case LEGS_S5_LUP2:
5141 case LEGS_S5_LUP3:
5142 case LEGS_S5_LUP4:
5143 case LEGS_S5_LUP5:
5144 case LEGS_S5_RUP2:
5145 case LEGS_S5_RUP3:
5146 case LEGS_S5_RUP4:
5147 case LEGS_S5_RUP5:
5148 if (pm->ps->slopeRecalcTime < pm->cmd.serverTime)
5149 {
5150 resultingAnim = anim-1;
5151 pm->ps->slopeRecalcTime = pm->cmd.serverTime + 8;//SLOPE_RECALC_INT;
5152 }
5153 else
5154 {
5155 resultingAnim = anim;
5156 }
5157 VectorClear(pm->ps->velocity);
5158 break;
5159 }
5160
5161 return resultingAnim;
5162 }
5163
5164 /*
5165 ===============
5166 PM_Footsteps
5167 ===============
5168 */
PM_Footsteps(void)5169 static void PM_Footsteps( void ) {
5170 float bobmove;
5171 int old;
5172 int setAnimFlags = 0;
5173
5174 if ( (PM_InSaberAnim( (pm->ps->legsAnim) ) && !BG_SpinningSaberAnim( (pm->ps->legsAnim) ))
5175 || (pm->ps->legsAnim) == BOTH_STAND1
5176 || (pm->ps->legsAnim) == BOTH_STAND1TO2
5177 || (pm->ps->legsAnim) == BOTH_STAND2TO1
5178 || (pm->ps->legsAnim) == BOTH_STAND2
5179 || (pm->ps->legsAnim) == BOTH_SABERFAST_STANCE
5180 || (pm->ps->legsAnim) == BOTH_SABERSLOW_STANCE
5181 || (pm->ps->legsAnim) == BOTH_BUTTON_HOLD
5182 || (pm->ps->legsAnim) == BOTH_BUTTON_RELEASE
5183 || PM_LandingAnim( (pm->ps->legsAnim) )
5184 || PM_PainAnim( (pm->ps->legsAnim) ))
5185 {//legs are in a saber anim, and not spinning, be sure to override it
5186 setAnimFlags |= SETANIM_FLAG_OVERRIDE;
5187 }
5188
5189 //
5190 // calculate speed and cycle to be used for
5191 // all cyclic walking effects
5192 //
5193 pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0]
5194 + pm->ps->velocity[1] * pm->ps->velocity[1] );
5195
5196 if (pm->ps->saberMove == LS_SPINATTACK)
5197 {
5198 PM_ContinueLegsAnim( pm->ps->torsoAnim );
5199 }
5200 else if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
5201
5202 // airborne leaves position in cycle intact, but doesn't advance
5203 if ( pm->waterlevel > 1 )
5204 {
5205 if (pm->xyspeed > 60)
5206 {
5207 PM_ContinueLegsAnim( BOTH_SWIMFORWARD );
5208 }
5209 else
5210 {
5211 PM_ContinueLegsAnim( BOTH_SWIM_IDLE1 );
5212 }
5213 }
5214 return;
5215 }
5216 // if not trying to move
5217 else if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) {
5218 if ( pm->xyspeed < 5 ) {
5219 pm->ps->bobCycle = 0; // start at beginning of cycle again
5220 if ( pm->ps->clientNum >= MAX_CLIENTS &&
5221 pm_entSelf &&
5222 pm_entSelf->s.NPC_class == CLASS_RANCOR )
5223 {
5224 if ( (pm->ps->eFlags2&EF2_USE_ALT_ANIM) )
5225 {//holding someone
5226 PM_ContinueLegsAnim( BOTH_STAND4 );
5227 //PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND4,SETANIM_FLAG_NORMAL);
5228 }
5229 else if ( (pm->ps->eFlags2&EF2_ALERTED) )
5230 {//have an enemy or have had one since we spawned
5231 PM_ContinueLegsAnim( BOTH_STAND2 );
5232 //PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL);
5233 }
5234 else
5235 {//just stand there
5236 PM_ContinueLegsAnim( BOTH_STAND1 );
5237 //PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
5238 }
5239 }
5240 else if ( pm->ps->clientNum >= MAX_CLIENTS &&
5241 pm_entSelf &&
5242 pm_entSelf->s.NPC_class == CLASS_WAMPA )
5243 {
5244 if ( (pm->ps->eFlags2&EF2_USE_ALT_ANIM) )
5245 {//holding a victim
5246 PM_ContinueLegsAnim( BOTH_STAND2 );
5247 //PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL);
5248 }
5249 else
5250 {//not holding a victim
5251 PM_ContinueLegsAnim( BOTH_STAND1 );
5252 //PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
5253 }
5254 }
5255 else if ( (pm->ps->pm_flags & PMF_DUCKED) || (pm->ps->pm_flags & PMF_ROLLING) ) {
5256 if ((pm->ps->legsAnim) != BOTH_CROUCH1IDLE)
5257 {
5258 PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1IDLE, setAnimFlags);
5259 }
5260 else
5261 {
5262 PM_ContinueLegsAnim( BOTH_CROUCH1IDLE );
5263 }
5264 } else {
5265 if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
5266 {
5267 ///???? continue legs anim on a torso anim...??!!!
5268 //yeah.. the anim has a valid pose for the legs, it uses it (you can't move while using disruptor)
5269 PM_ContinueLegsAnim( TORSO_WEAPONREADY4 );
5270 }
5271 else
5272 {
5273 if (pm->ps->weapon == WP_SABER && BG_SabersOff( pm->ps ) )
5274 {
5275 if (!PM_AdjustStandAnimForSlope())
5276 {
5277 //PM_ContinueLegsAnim( BOTH_STAND1 );
5278 PM_ContinueLegsAnim(PM_LegsSlopeBackTransition(BOTH_STAND1));
5279 }
5280 }
5281 else
5282 {
5283 if (pm->ps->weapon != WP_SABER || !PM_AdjustStandAnimForSlope())
5284 {
5285 if (pm->ps->weapon == WP_SABER)
5286 {
5287 PM_ContinueLegsAnim(PM_LegsSlopeBackTransition(PM_GetSaberStance()));
5288 }
5289 else
5290 {
5291 PM_ContinueLegsAnim(PM_LegsSlopeBackTransition(WeaponReadyLegsAnim[pm->ps->weapon]));
5292 }
5293 }
5294 }
5295 }
5296 }
5297 }
5298 return;
5299 }
5300
5301 if (pm->ps->saberMove == LS_SPINATTACK)
5302 {
5303 bobmove = 0.2f;
5304 PM_ContinueLegsAnim( pm->ps->torsoAnim );
5305 }
5306 else if ( pm->ps->pm_flags & PMF_DUCKED )
5307 {
5308 int rolled = 0;
5309
5310 bobmove = 0.5; // ducked characters bob much faster
5311
5312 if ( ( (PM_RunningAnim( pm->ps->legsAnim )&&VectorLengthSquared(pm->ps->velocity)>=40000/*200*200*/) || PM_CanRollFromSoulCal( pm->ps ) ) &&
5313 !BG_InRoll(pm->ps, pm->ps->legsAnim) )
5314 {//roll!
5315 rolled = PM_TryRoll();
5316 }
5317 if ( !rolled )
5318 { //if the roll failed or didn't attempt, do standard crouching anim stuff.
5319 if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
5320 if ((pm->ps->legsAnim) != BOTH_CROUCH1WALKBACK)
5321 {
5322 PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALKBACK, setAnimFlags);
5323 }
5324 else
5325 {
5326 PM_ContinueLegsAnim( BOTH_CROUCH1WALKBACK );
5327 }
5328 }
5329 else {
5330 if ((pm->ps->legsAnim) != BOTH_CROUCH1WALK)
5331 {
5332 PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALK, setAnimFlags);
5333 }
5334 else
5335 {
5336 PM_ContinueLegsAnim( BOTH_CROUCH1WALK );
5337 }
5338 }
5339 }
5340 else
5341 { //otherwise send us into the roll
5342 pm->ps->legsTimer = 0;
5343 pm->ps->legsAnim = 0;
5344 PM_SetAnim(SETANIM_BOTH,rolled,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
5345 PM_AddEventWithParm( EV_ROLL, 0 );
5346 pm->maxs[2] = pm->ps->crouchheight;//CROUCH_MAXS_2;
5347 pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
5348 pm->ps->pm_flags &= ~PMF_DUCKED;
5349 pm->ps->pm_flags |= PMF_ROLLING;
5350 }
5351 }
5352 else if ((pm->ps->pm_flags & PMF_ROLLING) && !BG_InRoll(pm->ps, pm->ps->legsAnim) &&
5353 !PM_InRollComplete(pm->ps, pm->ps->legsAnim))
5354 {
5355 bobmove = 0.5; // ducked characters bob much faster
5356
5357 if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
5358 {
5359 if ((pm->ps->legsAnim) != BOTH_CROUCH1WALKBACK)
5360 {
5361 PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALKBACK, setAnimFlags);
5362 }
5363 else
5364 {
5365 PM_ContinueLegsAnim( BOTH_CROUCH1WALKBACK );
5366 }
5367 }
5368 else
5369 {
5370 if ((pm->ps->legsAnim) != BOTH_CROUCH1WALK)
5371 {
5372 PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALK, setAnimFlags);
5373 }
5374 else
5375 {
5376 PM_ContinueLegsAnim( BOTH_CROUCH1WALK );
5377 }
5378 }
5379 }
5380 else
5381 {
5382 int desiredAnim = -1;
5383
5384 if ((pm->ps->legsAnim == BOTH_FORCELAND1 ||
5385 pm->ps->legsAnim == BOTH_FORCELANDBACK1 ||
5386 pm->ps->legsAnim == BOTH_FORCELANDRIGHT1 ||
5387 pm->ps->legsAnim == BOTH_FORCELANDLEFT1) &&
5388 pm->ps->legsTimer > 0)
5389 { //let it finish first
5390 bobmove = 0.2f;
5391 }
5392 else if ( !( pm->cmd.buttons & BUTTON_WALKING ) )
5393 {//running
5394 bobmove = 0.4f; // faster speeds bob faster
5395 if ( pm->ps->clientNum >= MAX_CLIENTS &&
5396 pm_entSelf &&
5397 pm_entSelf->s.NPC_class == CLASS_WAMPA )
5398 {
5399 if ( (pm->ps->eFlags2&EF2_USE_ALT_ANIM) )
5400 {//full on run, on all fours
5401 desiredAnim = BOTH_RUN1;
5402 }
5403 else
5404 {//regular, upright run
5405 desiredAnim = BOTH_RUN2;
5406 }
5407 }
5408 else if ( pm->ps->clientNum >= MAX_CLIENTS &&
5409 pm_entSelf &&
5410 pm_entSelf->s.NPC_class == CLASS_RANCOR )
5411 {//no run anims
5412 if ( (pm->ps->pm_flags&PMF_BACKWARDS_RUN) )
5413 {
5414 desiredAnim = BOTH_WALKBACK1;
5415 }
5416 else
5417 {
5418 desiredAnim = BOTH_WALK1;
5419 }
5420 }
5421 #ifdef _GAME
5422 else if ( pm->ps->clientNum >= MAX_CLIENTS &&
5423 pm_entSelf &&
5424 pm_entSelf->s.NPC_class == CLASS_JAWA)
5425 {
5426 // Jawa has a special run animation :D
5427 desiredAnim = BOTH_RUN4;
5428 bobmove = 0.2f;
5429 }
5430 #endif
5431 else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
5432 {
5433 #ifndef BASE_COMPAT
5434 if( pm->ps->weapon != WP_SABER )
5435 {
5436 desiredAnim = BOTH_RUNBACK1;
5437 }
5438 else
5439 {
5440 #endif
5441 switch (pm->ps->fd.saberAnimLevel)
5442 {
5443 case SS_STAFF:
5444 if ( pm->ps->saberHolstered > 1 )
5445 {//saber off
5446 desiredAnim = BOTH_RUNBACK1;
5447 }
5448 else
5449 {
5450 //desiredAnim = BOTH_RUNBACK_STAFF;
5451 //hmm.. stuff runback anim is pretty messed up for some reason.
5452 desiredAnim = BOTH_RUNBACK2;
5453 }
5454 break;
5455 case SS_DUAL:
5456 if ( pm->ps->saberHolstered > 1 )
5457 {//sabers off
5458 desiredAnim = BOTH_RUNBACK1;
5459 }
5460 else
5461 {
5462 //desiredAnim = BOTH_RUNBACK_DUAL;
5463 //and so is the dual
5464 desiredAnim = BOTH_RUNBACK2;
5465 }
5466 break;
5467 default:
5468 if ( pm->ps->saberHolstered )
5469 {//saber off
5470 desiredAnim = BOTH_RUNBACK1;
5471 }
5472 else
5473 {
5474 desiredAnim = BOTH_RUNBACK2;
5475 }
5476 break;
5477 }
5478 #ifndef BASE_COMPAT
5479 }
5480 #endif
5481 }
5482 else
5483 {
5484 #ifndef BASE_COMPAT // FIXME: this doesn't break base compatibility at all, remove #ifndef
5485 if ( pm->ps->weapon != WP_SABER )
5486 {
5487 desiredAnim = BOTH_RUN1;
5488 }
5489 else
5490 {
5491 #endif
5492 switch (pm->ps->fd.saberAnimLevel)
5493 {
5494 case SS_STAFF:
5495 if ( pm->ps->saberHolstered > 1 )
5496 {//blades off
5497 desiredAnim = BOTH_RUN1;
5498 }
5499 else if ( pm->ps->saberHolstered == 1 )
5500 {//1 blade on
5501 desiredAnim = BOTH_RUN2;
5502 }
5503 else
5504 {
5505 if (pm->ps->fd.forcePowersActive & (1<<FP_SPEED))
5506 {
5507 desiredAnim = BOTH_RUN1;
5508 }
5509 else
5510 {
5511 desiredAnim = BOTH_RUN_STAFF;
5512 }
5513 }
5514 break;
5515 case SS_DUAL:
5516 if ( pm->ps->saberHolstered > 1 )
5517 {//blades off
5518 desiredAnim = BOTH_RUN1;
5519 }
5520 else if ( pm->ps->saberHolstered == 1 )
5521 {//1 saber on
5522 desiredAnim = BOTH_RUN2;
5523 }
5524 else
5525 {
5526 desiredAnim = BOTH_RUN_DUAL;
5527 }
5528 break;
5529 default:
5530 if ( pm->ps->saberHolstered )
5531 {//saber off
5532 desiredAnim = BOTH_RUN1;
5533 }
5534 else
5535 {
5536 desiredAnim = BOTH_RUN2;
5537 }
5538 break;
5539 }
5540 #ifndef BASE_COMPAT
5541 }
5542 #endif
5543 }
5544 }
5545 else
5546 {
5547 bobmove = 0.2f; // walking bobs slow
5548 if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
5549 {
5550 #ifndef BASE_COMPAT // fixme, doesn't break base compat if enabled (I tested this to be sure)
5551 if( pm->ps->weapon != WP_SABER )
5552 {
5553 desiredAnim = BOTH_WALKBACK1;
5554 }
5555 else
5556 {
5557 #endif
5558 switch (pm->ps->fd.saberAnimLevel)
5559 {
5560 case SS_STAFF:
5561 if ( pm->ps->saberHolstered > 1 )
5562 {
5563 desiredAnim = BOTH_WALKBACK1;
5564 }
5565 else if ( pm->ps->saberHolstered )
5566 {
5567 desiredAnim = BOTH_WALKBACK2;
5568 }
5569 else
5570 {
5571 desiredAnim = BOTH_WALKBACK_STAFF;
5572 }
5573 break;
5574 case SS_DUAL:
5575 if ( pm->ps->saberHolstered > 1 )
5576 {
5577 desiredAnim = BOTH_WALKBACK1;
5578 }
5579 else if ( pm->ps->saberHolstered )
5580 {
5581 desiredAnim = BOTH_WALKBACK2;
5582 }
5583 else
5584 {
5585 desiredAnim = BOTH_WALKBACK_DUAL;
5586 }
5587 break;
5588 default:
5589 if ( pm->ps->saberHolstered )
5590 {
5591 desiredAnim = BOTH_WALKBACK1;
5592 }
5593 else
5594 {
5595 desiredAnim = BOTH_WALKBACK2;
5596 }
5597 break;
5598 }
5599 #ifndef BASE_COMPAT
5600 }
5601 #endif
5602 }
5603 else
5604 {
5605 if ( pm->ps->weapon == WP_MELEE )
5606 {
5607 desiredAnim = BOTH_WALK1;
5608 }
5609 else if ( BG_SabersOff( pm->ps ) )
5610 {
5611 desiredAnim = BOTH_WALK1;
5612 }
5613 #ifndef BASE_COMPAT
5614 else if ( pm->ps->weapon != WP_SABER )
5615 {
5616 desiredAnim = BOTH_WALK1;
5617 }
5618 #endif
5619 else
5620 {
5621 switch (pm->ps->fd.saberAnimLevel)
5622 {
5623 case SS_STAFF:
5624 if ( pm->ps->saberHolstered > 1 )
5625 {
5626 desiredAnim = BOTH_WALK1;
5627 }
5628 else if ( pm->ps->saberHolstered )
5629 {
5630 desiredAnim = BOTH_WALK2;
5631 }
5632 else
5633 {
5634 desiredAnim = BOTH_WALK_STAFF;
5635 }
5636 break;
5637 case SS_DUAL:
5638 if ( pm->ps->saberHolstered > 1 )
5639 {
5640 desiredAnim = BOTH_WALK1;
5641 }
5642 else if ( pm->ps->saberHolstered )
5643 {
5644 desiredAnim = BOTH_WALK2;
5645 }
5646 else
5647 {
5648 desiredAnim = BOTH_WALK_DUAL;
5649 }
5650 break;
5651 default:
5652 if ( pm->ps->saberHolstered )
5653 {
5654 desiredAnim = BOTH_WALK1;
5655 }
5656 else
5657 {
5658 desiredAnim = BOTH_WALK2;
5659 }
5660 break;
5661 }
5662 }
5663 }
5664 }
5665
5666 if (desiredAnim != -1)
5667 {
5668 int ires = PM_LegsSlopeBackTransition(desiredAnim);
5669
5670 if ((pm->ps->legsAnim) != desiredAnim && ires == desiredAnim)
5671 {
5672 PM_SetAnim(SETANIM_LEGS, desiredAnim, setAnimFlags);
5673 }
5674 else
5675 {
5676 PM_ContinueLegsAnim(ires);
5677 }
5678 }
5679 }
5680
5681 // check for footstep / splash sounds
5682 old = pm->ps->bobCycle;
5683 pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
5684
5685 // if we just crossed a cycle boundary, play an appropriate footstep event
5686 if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 )
5687 {
5688 pm->ps->footstepTime = pm->cmd.serverTime + 300;
5689 if ( pm->waterlevel == 1 ) {
5690 // splashing
5691 PM_AddEvent( EV_FOOTSPLASH );
5692 } else if ( pm->waterlevel == 2 ) {
5693 // wading / swimming at surface
5694 PM_AddEvent( EV_SWIM );
5695 } else if ( pm->waterlevel == 3 ) {
5696 // no sound when completely underwater
5697 }
5698 }
5699 }
5700
5701 /*
5702 ==============
5703 PM_WaterEvents
5704
5705 Generate sound events for entering and leaving water
5706 ==============
5707 */
PM_WaterEvents(void)5708 static void PM_WaterEvents( void ) { // FIXME?
5709 #ifdef _GAME
5710 qboolean impact_splash = qfalse;
5711 #endif
5712 //
5713 // if just entered a water volume, play a sound
5714 //
5715 if (!pml.previous_waterlevel && pm->waterlevel) {
5716 #ifdef _GAME
5717 if ( VectorLengthSquared( pm->ps->velocity ) > 40000 )
5718 {
5719 impact_splash = qtrue;
5720 }
5721 #endif
5722 PM_AddEvent( EV_WATER_TOUCH );
5723 }
5724
5725 //
5726 // if just completely exited a water volume, play a sound
5727 //
5728 if (pml.previous_waterlevel && !pm->waterlevel) {
5729 #ifdef _GAME
5730 if ( VectorLengthSquared( pm->ps->velocity ) > 40000 )
5731 {
5732 impact_splash = qtrue;
5733 }
5734 #endif
5735 PM_AddEvent( EV_WATER_LEAVE );
5736 }
5737
5738 #ifdef _GAME
5739 if ( impact_splash )
5740 {
5741 //play the splash effect
5742 trace_t tr;
5743 vec3_t start, end;
5744
5745
5746 VectorCopy( pm->ps->origin, start );
5747 VectorCopy( pm->ps->origin, end );
5748
5749 // FIXME: set start and end better
5750 start[2] += 10;
5751 end[2] -= 40;
5752
5753 pm->trace( &tr, start, vec3_origin, vec3_origin, end, pm->ps->clientNum, MASK_WATER );
5754
5755 if ( tr.fraction < 1.0f )
5756 {
5757 if ( (tr.contents&CONTENTS_LAVA) )
5758 {
5759 G_PlayEffect( EFFECT_LAVA_SPLASH, tr.endpos, tr.plane.normal );
5760 }
5761 else if ( (tr.contents&CONTENTS_SLIME) )
5762 {
5763 G_PlayEffect( EFFECT_ACID_SPLASH, tr.endpos, tr.plane.normal );
5764 }
5765 else //must be water
5766 {
5767 G_PlayEffect( EFFECT_WATER_SPLASH, tr.endpos, tr.plane.normal );
5768 }
5769 }
5770 }
5771 #endif
5772
5773 //
5774 // check for head just going under water
5775 //
5776 if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) {
5777 PM_AddEvent( EV_WATER_UNDER );
5778 }
5779
5780 //
5781 // check for head just coming out of water
5782 //
5783 if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) {
5784 PM_AddEvent( EV_WATER_CLEAR );
5785 }
5786 }
5787
BG_ClearRocketLock(playerState_t * ps)5788 void BG_ClearRocketLock( playerState_t *ps )
5789 {
5790 if ( ps )
5791 {
5792 ps->rocketLockIndex = ENTITYNUM_NONE;
5793 ps->rocketLastValidTime = 0;
5794 ps->rocketLockTime = -1;
5795 ps->rocketTargetTime = 0;
5796 }
5797 }
5798
5799 /*
5800 ===============
5801 PM_BeginWeaponChange
5802 ===============
5803 */
PM_BeginWeaponChange(int weapon)5804 void PM_BeginWeaponChange( int weapon ) {
5805 if ( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS ) {
5806 return;
5807 }
5808
5809 if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
5810 return;
5811 }
5812
5813 if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
5814 return;
5815 }
5816
5817 // turn of any kind of zooming when weapon switching.
5818 if (pm->ps->zoomMode)
5819 {
5820 pm->ps->zoomMode = 0;
5821 pm->ps->zoomTime = pm->ps->commandTime;
5822 }
5823
5824 PM_AddEventWithParm( EV_CHANGE_WEAPON, weapon );
5825 pm->ps->weaponstate = WEAPON_DROPPING;
5826 pm->ps->weaponTime += 200;
5827 //PM_StartTorsoAnim( TORSO_DROPWEAP1 );
5828 PM_SetAnim(SETANIM_TORSO, TORSO_DROPWEAP1, SETANIM_FLAG_OVERRIDE);
5829
5830 BG_ClearRocketLock( pm->ps );
5831 }
5832
5833
5834 /*
5835 ===============
5836 PM_FinishWeaponChange
5837 ===============
5838 */
PM_FinishWeaponChange(void)5839 void PM_FinishWeaponChange( void ) {
5840 int weapon;
5841
5842 weapon = pm->cmd.weapon;
5843 if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) {
5844 weapon = WP_NONE;
5845 }
5846
5847 if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
5848 weapon = WP_NONE;
5849 }
5850
5851 if (weapon == WP_SABER)
5852 {
5853 PM_SetSaberMove(LS_DRAW);
5854 }
5855 else
5856 {
5857 //PM_StartTorsoAnim( TORSO_RAISEWEAP1);
5858 PM_SetAnim(SETANIM_TORSO, TORSO_RAISEWEAP1, SETANIM_FLAG_OVERRIDE);
5859 }
5860 pm->ps->weapon = weapon;
5861 pm->ps->weaponstate = WEAPON_RAISING;
5862 pm->ps->weaponTime += 250;
5863 }
5864
5865 #ifdef _GAME
5866 extern void WP_GetVehicleCamPos( gentity_t *ent, gentity_t *pilot, vec3_t camPos );
5867 #else
5868 extern void CG_GetVehicleCamPos( vec3_t camPos );
5869 #endif
5870 #define MAX_XHAIR_DIST_ACCURACY 20000.0f
BG_VehTraceFromCamPos(trace_t * camTrace,bgEntity_t * bgEnt,const vec3_t entOrg,const vec3_t shotStart,const vec3_t end,vec3_t newEnd,vec3_t shotDir,float bestDist)5871 int BG_VehTraceFromCamPos( trace_t *camTrace, bgEntity_t *bgEnt, const vec3_t entOrg, const vec3_t shotStart, const vec3_t end, vec3_t newEnd, vec3_t shotDir, float bestDist )
5872 {
5873 //NOTE: this MUST stay up to date with the method used in CG_ScanForCrosshairEntity (where it checks the doExtraVehTraceFromViewPos bool)
5874 vec3_t viewDir2End, extraEnd, camPos;
5875 float minAutoAimDist;
5876
5877 #ifdef _GAME
5878 WP_GetVehicleCamPos( (gentity_t *)bgEnt, (gentity_t *)bgEnt->m_pVehicle->m_pPilot, camPos );
5879 #else
5880 CG_GetVehicleCamPos( camPos );
5881 #endif
5882
5883 minAutoAimDist = Distance( entOrg, camPos ) + (bgEnt->m_pVehicle->m_pVehicleInfo->length/2.0f) + 200.0f;
5884
5885 VectorCopy( end, newEnd );
5886 VectorSubtract( end, camPos, viewDir2End );
5887 VectorNormalize( viewDir2End );
5888 VectorMA( camPos, MAX_XHAIR_DIST_ACCURACY, viewDir2End, extraEnd );
5889
5890 pm->trace( camTrace, camPos, vec3_origin, vec3_origin, extraEnd, bgEnt->s.number, CONTENTS_SOLID|CONTENTS_BODY );
5891
5892 if ( !camTrace->allsolid
5893 && !camTrace->startsolid
5894 && camTrace->fraction < 1.0f
5895 && (camTrace->fraction*MAX_XHAIR_DIST_ACCURACY) > minAutoAimDist
5896 && ((camTrace->fraction*MAX_XHAIR_DIST_ACCURACY)-Distance( entOrg, camPos )) < bestDist )
5897 {//this trace hit *something* that's closer than the thing the main trace hit, so use this result instead
5898 VectorCopy( camTrace->endpos, newEnd );
5899 VectorSubtract( newEnd, shotStart, shotDir );
5900 VectorNormalize( shotDir );
5901 return (camTrace->entityNum+1);
5902 }
5903 return 0;
5904 }
5905
PM_RocketLock(float lockDist,qboolean vehicleLock)5906 void PM_RocketLock( float lockDist, qboolean vehicleLock )
5907 {
5908 // Not really a charge weapon, but we still want to delay fire until the button comes up so that we can
5909 // implement our alt-fire locking stuff
5910 vec3_t ang;
5911 trace_t tr;
5912
5913 vec3_t muzzleOffPoint, muzzlePoint, forward, right, up;
5914
5915 if ( vehicleLock )
5916 {
5917 AngleVectors( pm->ps->viewangles, forward, right, up );
5918 VectorCopy( pm->ps->origin, muzzlePoint );
5919 VectorMA( muzzlePoint, lockDist, forward, ang );
5920 }
5921 else
5922 {
5923 AngleVectors( pm->ps->viewangles, forward, right, up );
5924
5925 AngleVectors(pm->ps->viewangles, ang, NULL, NULL);
5926
5927 VectorCopy( pm->ps->origin, muzzlePoint );
5928 VectorCopy(WP_MuzzlePoint[WP_ROCKET_LAUNCHER], muzzleOffPoint);
5929
5930 VectorMA(muzzlePoint, muzzleOffPoint[0], forward, muzzlePoint);
5931 VectorMA(muzzlePoint, muzzleOffPoint[1], right, muzzlePoint);
5932 muzzlePoint[2] += pm->ps->viewheight + muzzleOffPoint[2];
5933 ang[0] = muzzlePoint[0] + ang[0]*lockDist;
5934 ang[1] = muzzlePoint[1] + ang[1]*lockDist;
5935 ang[2] = muzzlePoint[2] + ang[2]*lockDist;
5936 }
5937
5938
5939 pm->trace(&tr, muzzlePoint, NULL, NULL, ang, pm->ps->clientNum, MASK_PLAYERSOLID);
5940
5941 if ( vehicleLock )
5942 {//vehicles also do a trace from the camera point if the main one misses
5943 if ( tr.fraction >= 1.0f )
5944 {
5945 trace_t camTrace;
5946 vec3_t newEnd, shotDir;
5947 if ( BG_VehTraceFromCamPos( &camTrace, PM_BGEntForNum(pm->ps->clientNum), pm->ps->origin, muzzlePoint, tr.endpos, newEnd, shotDir, (tr.fraction*lockDist) ) )
5948 {
5949 memcpy( &tr, &camTrace, sizeof(tr) );
5950 }
5951 }
5952 }
5953
5954 if (tr.fraction != 1 && tr.entityNum < ENTITYNUM_NONE && tr.entityNum != pm->ps->clientNum)
5955 {
5956 bgEntity_t *bgEnt = PM_BGEntForNum(tr.entityNum);
5957 if ( bgEnt && (bgEnt->s.powerups&PW_CLOAKED) )
5958 {
5959 pm->ps->rocketLockIndex = ENTITYNUM_NONE;
5960 pm->ps->rocketLockTime = 0;
5961 }
5962 else if (bgEnt && (bgEnt->s.eType == ET_PLAYER || bgEnt->s.eType == ET_NPC))
5963 {
5964 if (pm->ps->rocketLockIndex == ENTITYNUM_NONE)
5965 {
5966 pm->ps->rocketLockIndex = tr.entityNum;
5967 pm->ps->rocketLockTime = pm->cmd.serverTime;
5968 }
5969 else if (pm->ps->rocketLockIndex != tr.entityNum && pm->ps->rocketTargetTime < pm->cmd.serverTime)
5970 {
5971 pm->ps->rocketLockIndex = tr.entityNum;
5972 pm->ps->rocketLockTime = pm->cmd.serverTime;
5973 }
5974 else if (pm->ps->rocketLockIndex == tr.entityNum)
5975 {
5976 if (pm->ps->rocketLockTime == -1)
5977 {
5978 pm->ps->rocketLockTime = pm->ps->rocketLastValidTime;
5979 }
5980 }
5981
5982 if (pm->ps->rocketLockIndex == tr.entityNum)
5983 {
5984 pm->ps->rocketTargetTime = pm->cmd.serverTime + 500;
5985 }
5986 }
5987 else if (!vehicleLock)
5988 {
5989 if (pm->ps->rocketTargetTime < pm->cmd.serverTime)
5990 {
5991 pm->ps->rocketLockIndex = ENTITYNUM_NONE;
5992 pm->ps->rocketLockTime = 0;
5993 }
5994 }
5995 }
5996 else if (pm->ps->rocketTargetTime < pm->cmd.serverTime)
5997 {
5998 pm->ps->rocketLockIndex = ENTITYNUM_NONE;
5999 pm->ps->rocketLockTime = 0;
6000 }
6001 else
6002 {
6003 if (pm->ps->rocketLockTime != -1)
6004 {
6005 pm->ps->rocketLastValidTime = pm->ps->rocketLockTime;
6006 }
6007 pm->ps->rocketLockTime = -1;
6008 }
6009 }
6010
6011 //---------------------------------------
PM_DoChargedWeapons(qboolean vehicleRocketLock,bgEntity_t * veh)6012 static qboolean PM_DoChargedWeapons( qboolean vehicleRocketLock, bgEntity_t *veh )
6013 //---------------------------------------
6014 {
6015 qboolean charging = qfalse,
6016 altFire = qfalse;
6017
6018 if ( vehicleRocketLock )
6019 {
6020 if ( (pm->cmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )
6021 {//actually charging
6022 if ( veh
6023 && veh->m_pVehicle )
6024 {//just make sure we have this veh info
6025 if ( ( (pm->cmd.buttons&BUTTON_ATTACK)
6026 &&g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].fHoming
6027 &&pm->ps->ammo[0]>=g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].iAmmoPerShot )
6028 ||
6029 ( (pm->cmd.buttons&BUTTON_ALT_ATTACK)
6030 &&g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].fHoming
6031 &&pm->ps->ammo[1]>=g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].iAmmoPerShot ) )
6032 {//pressing the appropriate fire button for the lock-on/charging weapon
6033 PM_RocketLock(16384, qtrue);
6034 charging = qtrue;
6035 }
6036 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6037 {
6038 altFire = qtrue;
6039 }
6040 }
6041 }
6042 //else, let go and should fire now
6043 }
6044 else
6045 {
6046 // If you want your weapon to be a charging weapon, just set this bit up
6047 switch( pm->ps->weapon )
6048 {
6049 //------------------
6050 case WP_BRYAR_PISTOL:
6051
6052 // alt-fire charges the weapon
6053 //if ( pm->gametype == GT_SIEGE )
6054 if (1)
6055 {
6056 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6057 {
6058 charging = qtrue;
6059 altFire = qtrue;
6060 }
6061 }
6062 break;
6063
6064 case WP_CONCUSSION:
6065 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6066 {
6067 altFire = qtrue;
6068 }
6069 break;
6070
6071 case WP_BRYAR_OLD:
6072
6073 // alt-fire charges the weapon
6074 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6075 {
6076 charging = qtrue;
6077 altFire = qtrue;
6078 }
6079 break;
6080
6081 //------------------
6082 case WP_BOWCASTER:
6083
6084 // primary fire charges the weapon
6085 if ( pm->cmd.buttons & BUTTON_ATTACK )
6086 {
6087 charging = qtrue;
6088 }
6089 break;
6090
6091 //------------------
6092 case WP_ROCKET_LAUNCHER:
6093 if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK)
6094 && pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] >= weaponData[pm->ps->weapon].altEnergyPerShot )
6095 {
6096 PM_RocketLock(2048,qfalse);
6097 charging = qtrue;
6098 altFire = qtrue;
6099 }
6100 break;
6101
6102 //------------------
6103 case WP_THERMAL:
6104
6105 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6106 {
6107 altFire = qtrue; // override default of not being an alt-fire
6108 charging = qtrue;
6109 }
6110 else if ( pm->cmd.buttons & BUTTON_ATTACK )
6111 {
6112 charging = qtrue;
6113 }
6114 break;
6115
6116 case WP_DEMP2:
6117 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6118 {
6119 altFire = qtrue; // override default of not being an alt-fire
6120 charging = qtrue;
6121 }
6122 break;
6123
6124 case WP_DISRUPTOR:
6125 if ((pm->cmd.buttons & BUTTON_ATTACK) &&
6126 pm->ps->zoomMode == 1 &&
6127 pm->ps->zoomLocked)
6128 {
6129 if (!pm->cmd.forwardmove &&
6130 !pm->cmd.rightmove &&
6131 pm->cmd.upmove <= 0)
6132 {
6133 charging = qtrue;
6134 altFire = qtrue;
6135 }
6136 else
6137 {
6138 charging = qfalse;
6139 altFire = qfalse;
6140 }
6141 }
6142
6143 if (pm->ps->zoomMode != 1 &&
6144 pm->ps->weaponstate == WEAPON_CHARGING_ALT)
6145 {
6146 pm->ps->weaponstate = WEAPON_READY;
6147 charging = qfalse;
6148 altFire = qfalse;
6149 }
6150
6151 } // end switch
6152 }
6153
6154 // set up the appropriate weapon state based on the button that's down.
6155 // Note that we ALWAYS return if charging is set ( meaning the buttons are still down )
6156 if ( charging )
6157 {
6158 if ( altFire )
6159 {
6160 if ( pm->ps->weaponstate != WEAPON_CHARGING_ALT )
6161 {
6162 // charge isn't started, so do it now
6163 pm->ps->weaponstate = WEAPON_CHARGING_ALT;
6164 pm->ps->weaponChargeTime = pm->cmd.serverTime;
6165 pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].altChargeSubTime;
6166
6167 #ifdef _DEBUG
6168 // Com_Printf("Starting charge\n");
6169 #endif
6170 assert(pm->ps->weapon > WP_NONE);
6171 BG_AddPredictableEventToPlayerstate(EV_WEAPON_CHARGE_ALT, pm->ps->weapon, pm->ps);
6172 }
6173
6174 if ( vehicleRocketLock )
6175 {//check vehicle ammo
6176 if ( veh && pm->ps->ammo[1] < g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].iAmmoPerShot )
6177 {
6178 pm->ps->weaponstate = WEAPON_CHARGING_ALT;
6179 goto rest;
6180 }
6181 }
6182 else if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < (weaponData[pm->ps->weapon].altChargeSub+weaponData[pm->ps->weapon].altEnergyPerShot))
6183 {
6184 pm->ps->weaponstate = WEAPON_CHARGING_ALT;
6185
6186 goto rest;
6187 }
6188 else if ((pm->cmd.serverTime - pm->ps->weaponChargeTime) < weaponData[pm->ps->weapon].altMaxCharge)
6189 {
6190 if (pm->ps->weaponChargeSubtractTime < pm->cmd.serverTime)
6191 {
6192 pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= weaponData[pm->ps->weapon].altChargeSub;
6193 pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].altChargeSubTime;
6194 }
6195 }
6196 }
6197 else
6198 {
6199 if ( pm->ps->weaponstate != WEAPON_CHARGING )
6200 {
6201 // charge isn't started, so do it now
6202 pm->ps->weaponstate = WEAPON_CHARGING;
6203 pm->ps->weaponChargeTime = pm->cmd.serverTime;
6204 pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].chargeSubTime;
6205
6206 #ifdef _DEBUG
6207 // Com_Printf("Starting charge\n");
6208 #endif
6209 BG_AddPredictableEventToPlayerstate(EV_WEAPON_CHARGE, pm->ps->weapon, pm->ps);
6210 }
6211
6212 if ( vehicleRocketLock )
6213 {
6214 if ( veh && pm->ps->ammo[0] < g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].iAmmoPerShot )
6215 {//check vehicle ammo
6216 pm->ps->weaponstate = WEAPON_CHARGING;
6217 goto rest;
6218 }
6219 }
6220 else if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < (weaponData[pm->ps->weapon].chargeSub+weaponData[pm->ps->weapon].energyPerShot))
6221 {
6222 pm->ps->weaponstate = WEAPON_CHARGING;
6223
6224 goto rest;
6225 }
6226 else if ((pm->cmd.serverTime - pm->ps->weaponChargeTime) < weaponData[pm->ps->weapon].maxCharge)
6227 {
6228 if (pm->ps->weaponChargeSubtractTime < pm->cmd.serverTime)
6229 {
6230 pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= weaponData[pm->ps->weapon].chargeSub;
6231 pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].chargeSubTime;
6232 }
6233 }
6234 }
6235
6236 return qtrue; // short-circuit rest of weapon code
6237 }
6238 rest:
6239 // Only charging weapons should be able to set these states...so....
6240 // let's see which fire mode we need to set up now that the buttons are up
6241 if ( pm->ps->weaponstate == WEAPON_CHARGING )
6242 {
6243 // weapon has a charge, so let us do an attack
6244 #ifdef _DEBUG
6245 // Com_Printf("Firing. Charge time=%d\n", pm->cmd.serverTime - pm->ps->weaponChargeTime);
6246 #endif
6247
6248 // dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now
6249 pm->cmd.buttons |= BUTTON_ATTACK;
6250 pm->ps->eFlags |= EF_FIRING;
6251 }
6252 else if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT )
6253 {
6254 // weapon has a charge, so let us do an alt-attack
6255 #ifdef _DEBUG
6256 // Com_Printf("Firing. Charge time=%d\n", pm->cmd.serverTime - pm->ps->weaponChargeTime);
6257 #endif
6258
6259 // dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now
6260 pm->cmd.buttons |= BUTTON_ALT_ATTACK;
6261 pm->ps->eFlags |= (EF_FIRING|EF_ALT_FIRING);
6262 }
6263
6264 return qfalse; // continue with the rest of the weapon code
6265 }
6266
6267
6268 #define BOWCASTER_CHARGE_UNIT 200.0f // bowcaster charging gives us one more unit every 200ms--if you change this, you'll have to do the same in g_weapon
6269 #define BRYAR_CHARGE_UNIT 200.0f // bryar charging gives us one more unit every 200ms--if you change this, you'll have to do the same in g_weapon
6270
PM_ItemUsable(playerState_t * ps,int forcedUse)6271 int PM_ItemUsable(playerState_t *ps, int forcedUse)
6272 {
6273 vec3_t fwd, fwdorg, dest, pos;
6274 vec3_t yawonly;
6275 vec3_t mins, maxs;
6276 vec3_t trtest;
6277 trace_t tr;
6278
6279 if (ps->m_iVehicleNum)
6280 {
6281 return 0;
6282 }
6283
6284 if (ps->pm_flags & PMF_USE_ITEM_HELD)
6285 { //force to let go first
6286 return 0;
6287 }
6288
6289 if (ps->duelInProgress)
6290 { //not allowed to use holdables while in a private duel.
6291 return 0;
6292 }
6293
6294 if (!forcedUse)
6295 {
6296 forcedUse = bg_itemlist[ps->stats[STAT_HOLDABLE_ITEM]].giTag;
6297 }
6298
6299 if (!BG_IsItemSelectable(ps, forcedUse))
6300 {
6301 return 0;
6302 }
6303
6304 switch (forcedUse)
6305 {
6306 case HI_MEDPAC:
6307 case HI_MEDPAC_BIG:
6308 if (ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH])
6309 {
6310 return 0;
6311 }
6312 if (ps->stats[STAT_HEALTH] <= 0 ||
6313 (ps->eFlags & EF_DEAD))
6314 {
6315 return 0;
6316 }
6317
6318 return 1;
6319 case HI_SEEKER:
6320 if (ps->eFlags & EF_SEEKERDRONE)
6321 {
6322 PM_AddEventWithParm(EV_ITEMUSEFAIL, SEEKER_ALREADYDEPLOYED);
6323 return 0;
6324 }
6325
6326 return 1;
6327 case HI_SENTRY_GUN:
6328 if (ps->fd.sentryDeployed)
6329 {
6330 PM_AddEventWithParm(EV_ITEMUSEFAIL, SENTRY_ALREADYPLACED);
6331 return 0;
6332 }
6333
6334 yawonly[ROLL] = 0;
6335 yawonly[PITCH] = 0;
6336 yawonly[YAW] = ps->viewangles[YAW];
6337
6338 VectorSet( mins, -8, -8, 0 );
6339 VectorSet( maxs, 8, 8, 24 );
6340
6341 AngleVectors(yawonly, fwd, NULL, NULL);
6342
6343 fwdorg[0] = ps->origin[0] + fwd[0]*64;
6344 fwdorg[1] = ps->origin[1] + fwd[1]*64;
6345 fwdorg[2] = ps->origin[2] + fwd[2]*64;
6346
6347 trtest[0] = fwdorg[0] + fwd[0]*16;
6348 trtest[1] = fwdorg[1] + fwd[1]*16;
6349 trtest[2] = fwdorg[2] + fwd[2]*16;
6350
6351 pm->trace(&tr, ps->origin, mins, maxs, trtest, ps->clientNum, MASK_PLAYERSOLID);
6352
6353 if ((tr.fraction != 1 && tr.entityNum != ps->clientNum) || tr.startsolid || tr.allsolid)
6354 {
6355 PM_AddEventWithParm(EV_ITEMUSEFAIL, SENTRY_NOROOM);
6356 return 0;
6357 }
6358
6359 return 1;
6360 case HI_SHIELD:
6361 mins[0] = -8;
6362 mins[1] = -8;
6363 mins[2] = 0;
6364
6365 maxs[0] = 8;
6366 maxs[1] = 8;
6367 maxs[2] = 8;
6368
6369 AngleVectors (ps->viewangles, fwd, NULL, NULL);
6370 fwd[2] = 0;
6371 VectorMA(ps->origin, 64, fwd, dest);
6372 pm->trace(&tr, ps->origin, mins, maxs, dest, ps->clientNum, MASK_SHOT );
6373 if (tr.fraction > 0.9 && !tr.startsolid && !tr.allsolid)
6374 {
6375 VectorCopy(tr.endpos, pos);
6376 VectorSet( dest, pos[0], pos[1], pos[2] - 4096 );
6377 pm->trace( &tr, pos, mins, maxs, dest, ps->clientNum, MASK_SOLID );
6378 if ( !tr.startsolid && !tr.allsolid )
6379 {
6380 return 1;
6381 }
6382 }
6383 PM_AddEventWithParm(EV_ITEMUSEFAIL, SHIELD_NOROOM);
6384 return 0;
6385 case HI_JETPACK: //check for stuff here?
6386 return 1;
6387 case HI_HEALTHDISP:
6388 return 1;
6389 case HI_AMMODISP:
6390 return 1;
6391 case HI_EWEB:
6392 return 1;
6393 case HI_CLOAK: //check for stuff here?
6394 return 1;
6395 default:
6396 return 1;
6397 }
6398 }
6399
6400 //cheesy vehicle weapon hackery
PM_CanSetWeaponAnims(void)6401 qboolean PM_CanSetWeaponAnims(void)
6402 {
6403 if (pm->ps->m_iVehicleNum)
6404 {
6405 return qfalse;
6406 }
6407
6408 return qtrue;
6409 }
6410
6411 //perform player anim overrides while on vehicle.
6412 extern int PM_irand_timesync(int val1, int val2);
PM_VehicleWeaponAnimate(void)6413 void PM_VehicleWeaponAnimate(void)
6414 {
6415 bgEntity_t *veh = pm_entVeh;
6416 Vehicle_t *pVeh;
6417 int iFlags = 0, Anim = -1;
6418
6419 if (!veh ||
6420 !veh->m_pVehicle ||
6421 !veh->m_pVehicle->m_pPilot ||
6422 !veh->m_pVehicle->m_pPilot->playerState ||
6423 pm->ps->clientNum != veh->m_pVehicle->m_pPilot->playerState->clientNum)
6424 { //make sure the vehicle exists, and its pilot is this player
6425 return;
6426 }
6427
6428 pVeh = veh->m_pVehicle;
6429
6430 if (pVeh->m_pVehicleInfo->type == VH_WALKER ||
6431 pVeh->m_pVehicleInfo->type == VH_FIGHTER)
6432 { //slightly hacky I guess, but whatever.
6433 return;
6434 }
6435 backAgain:
6436 // If they're firing, play the right fire animation.
6437 if ( pm->cmd.buttons & ( BUTTON_ATTACK | BUTTON_ALT_ATTACK ) )
6438 {
6439 iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
6440
6441 switch ( pm->ps->weapon )
6442 {
6443 case WP_SABER:
6444 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6445 { //don't do anything.. I guess.
6446 pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
6447 goto backAgain;
6448 }
6449 // If we're already in an attack animation, leave (let it continue).
6450 if (pm->ps->torsoTimer <= 0)
6451 { //we'll be starting a new attack
6452 PM_AddEvent(EV_SABER_ATTACK);
6453 }
6454
6455 //just set it to something so we have a proper trail. This is a stupid
6456 //hack (much like the rest of this function)
6457 pm->ps->saberMove = LS_R_TL2BR;
6458
6459 if ( pm->ps->torsoTimer > 0 && (pm->ps->torsoAnim == BOTH_VS_ATR_S ||
6460 pm->ps->torsoAnim == BOTH_VS_ATL_S) )
6461 {
6462 /*
6463 //FIXME: no need to even call the PM_SetAnim at all in this case
6464 Anim = (animNumber_t)pm->ps->torsoAnim;
6465 iFlags = SETANIM_FLAG_NORMAL;
6466 break;
6467 */
6468 return;
6469 }
6470
6471 // Start the attack.
6472 if ( pm->cmd.rightmove > 0 ) //right side attack
6473 {
6474 Anim = BOTH_VS_ATR_S;
6475 }
6476 else if ( pm->cmd.rightmove < 0 ) //left-side attack
6477 {
6478 Anim = BOTH_VS_ATL_S;
6479 }
6480 else //random
6481 {
6482 //FIXME: alternate back and forth or auto-aim?
6483 //if ( !Q_irand( 0, 1 ) )
6484 if (!PM_irand_timesync(0, 1))
6485 {
6486 Anim = BOTH_VS_ATR_S;
6487 }
6488 else
6489 {
6490 Anim = BOTH_VS_ATL_S;
6491 }
6492 }
6493
6494 if (pm->ps->torsoTimer <= 0)
6495 { //restart the anim if we are already in it (and finished)
6496 iFlags |= SETANIM_FLAG_RESTART;
6497 }
6498 break;
6499
6500 case WP_BLASTER:
6501 // Override the shoot anim.
6502 if ( pm->ps->torsoAnim == BOTH_ATTACK3 )
6503 {
6504 if ( pm->cmd.rightmove > 0 ) //right side attack
6505 {
6506 Anim = BOTH_VS_ATR_G;
6507 }
6508 else if ( pm->cmd.rightmove < 0 ) //left side
6509 {
6510 Anim = BOTH_VS_ATL_G;
6511 }
6512 else //frontal
6513 {
6514 Anim = BOTH_VS_ATF_G;
6515 }
6516 }
6517 break;
6518
6519 default:
6520 Anim = BOTH_VS_IDLE;
6521 break;
6522 }
6523 }
6524 else if (veh->playerState && veh->playerState->speed < 0 &&
6525 pVeh->m_pVehicleInfo->type == VH_ANIMAL)
6526 { //tauntaun is going backwards
6527 Anim = BOTH_VT_WALK_REV;
6528 }
6529 else if (veh->playerState && veh->playerState->speed < 0 &&
6530 pVeh->m_pVehicleInfo->type == VH_SPEEDER)
6531 { //speeder is going backwards
6532 Anim = BOTH_VS_REV;
6533 }
6534 // They're not firing so play the Idle for the weapon.
6535 else
6536 {
6537 iFlags = SETANIM_FLAG_NORMAL;
6538
6539 switch ( pm->ps->weapon )
6540 {
6541 case WP_SABER:
6542 if ( BG_SabersOff( pm->ps ) )
6543 { //saber holstered, normal idle
6544 Anim = BOTH_VS_IDLE;
6545 }
6546 // In the Air.
6547 //else if ( pVeh->m_ulFlags & VEH_FLYING )
6548 else if (0)
6549 {
6550 Anim = BOTH_VS_AIR_G;
6551 iFlags = SETANIM_FLAG_OVERRIDE;
6552 }
6553 // Crashing.
6554 //else if ( pVeh->m_ulFlags & VEH_CRASHING )
6555 else if (0)
6556 {
6557 pVeh->m_ulFlags &= ~VEH_CRASHING; // Remove the flag, we are doing the animation.
6558 Anim = BOTH_VS_LAND_SR;
6559 iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
6560 }
6561 else
6562 {
6563 Anim = BOTH_VS_IDLE_SR;
6564 }
6565 break;
6566
6567 case WP_BLASTER:
6568 // In the Air.
6569 //if ( pVeh->m_ulFlags & VEH_FLYING )
6570 if (0)
6571 {
6572 Anim = BOTH_VS_AIR_G;
6573 iFlags = SETANIM_FLAG_OVERRIDE;
6574 }
6575 // Crashing.
6576 //else if ( pVeh->m_ulFlags & VEH_CRASHING )
6577 else if (0)
6578 {
6579 pVeh->m_ulFlags &= ~VEH_CRASHING; // Remove the flag, we are doing the animation.
6580 Anim = BOTH_VS_LAND_G;
6581 iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
6582 }
6583 else
6584 {
6585 Anim = BOTH_VS_IDLE_G;
6586 }
6587 break;
6588
6589 default:
6590 Anim = BOTH_VS_IDLE;
6591 break;
6592 }
6593 }
6594
6595 if (Anim != -1)
6596 { //override it
6597 if (pVeh->m_pVehicleInfo->type == VH_ANIMAL)
6598 { //agh.. remap anims for the tauntaun
6599 switch (Anim)
6600 {
6601 case BOTH_VS_IDLE:
6602 if (veh->playerState && veh->playerState->speed > 0)
6603 {
6604 if (veh->playerState->speed > pVeh->m_pVehicleInfo->speedMax)
6605 { //turbo
6606 Anim = BOTH_VT_TURBO;
6607 }
6608 else
6609 {
6610 Anim = BOTH_VT_RUN_FWD;
6611 }
6612 }
6613 else
6614 {
6615 Anim = BOTH_VT_IDLE;
6616 }
6617 break;
6618 case BOTH_VS_ATR_S:
6619 Anim = BOTH_VT_ATR_S;
6620 break;
6621 case BOTH_VS_ATL_S:
6622 Anim = BOTH_VT_ATL_S;
6623 break;
6624 case BOTH_VS_ATR_G:
6625 Anim = BOTH_VT_ATR_G;
6626 break;
6627 case BOTH_VS_ATL_G:
6628 Anim = BOTH_VT_ATL_G;
6629 break;
6630 case BOTH_VS_ATF_G:
6631 Anim = BOTH_VT_ATF_G;
6632 break;
6633 case BOTH_VS_IDLE_SL:
6634 Anim = BOTH_VT_IDLE_S;
6635 break;
6636 case BOTH_VS_IDLE_SR:
6637 Anim = BOTH_VT_IDLE_S;
6638 break;
6639 case BOTH_VS_IDLE_G:
6640 Anim = BOTH_VT_IDLE_G;
6641 break;
6642
6643 //should not happen for tauntaun:
6644 case BOTH_VS_AIR_G:
6645 case BOTH_VS_LAND_SL:
6646 case BOTH_VS_LAND_SR:
6647 case BOTH_VS_LAND_G:
6648 return;
6649 default:
6650 break;
6651 }
6652 }
6653
6654 PM_SetAnim(SETANIM_BOTH, Anim, iFlags);
6655 }
6656 }
6657
6658 /*
6659 ==============
6660 PM_Weapon
6661
6662 Generates weapon events and modifes the weapon counter
6663 ==============
6664 */
6665 extern int PM_KickMoveForConditions(void);
PM_Weapon(void)6666 static void PM_Weapon( void )
6667 {
6668 int addTime;
6669 int amount;
6670 int killAfterItem = 0;
6671 bgEntity_t *veh = NULL;
6672 qboolean vehicleRocketLock = qfalse;
6673
6674 #ifdef _GAME
6675 if (pm->ps->clientNum >= MAX_CLIENTS &&
6676 pm->ps->weapon == WP_NONE &&
6677 pm->cmd.weapon == WP_NONE &&
6678 pm_entSelf)
6679 { //npc with no weapon
6680 gentity_t *gent = (gentity_t *)pm_entSelf;
6681 if (gent->inuse && gent->client &&
6682 !gent->localAnimIndex)
6683 { //humanoid
6684 pm->ps->torsoAnim = pm->ps->legsAnim;
6685 pm->ps->torsoTimer = pm->ps->legsTimer;
6686 return;
6687 }
6688 }
6689 #endif
6690
6691 if (!pm->ps->emplacedIndex &&
6692 pm->ps->weapon == WP_EMPLACED_GUN)
6693 { //oh no!
6694 int i = 0;
6695 int weap = -1;
6696
6697 while (i < WP_NUM_WEAPONS)
6698 {
6699 if ((pm->ps->stats[STAT_WEAPONS] & (1 << i)) && i != WP_NONE)
6700 { //this one's good
6701 weap = i;
6702 break;
6703 }
6704 i++;
6705 }
6706
6707 if (weap != -1)
6708 {
6709 pm->cmd.weapon = weap;
6710 pm->ps->weapon = weap;
6711 return;
6712 }
6713 }
6714
6715 if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
6716 &&pm->ps->m_iVehicleNum)
6717 { //riding a vehicle
6718 if ( (veh = pm_entVeh) &&
6719 (veh->m_pVehicle && (veh->m_pVehicle->m_pVehicleInfo->type == VH_WALKER || veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER) ) )
6720 {//riding a walker/fighter
6721 //keep saber off, do no weapon stuff at all!
6722 pm->ps->saberHolstered = 2;
6723 #ifdef _GAME
6724 pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);
6725 #else
6726 if ( g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].fHoming
6727 || g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].fHoming )
6728 {//our vehicle uses a rocket launcher, so do the normal checks
6729 vehicleRocketLock = qtrue;
6730 pm->cmd.buttons &= ~BUTTON_ATTACK;
6731 }
6732 else
6733 {
6734 pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);
6735 }
6736 #endif
6737 }
6738 }
6739
6740 if (pm->ps->weapon != WP_DISRUPTOR //not using disruptor
6741 && pm->ps->weapon != WP_ROCKET_LAUNCHER//not using rocket launcher
6742 && pm->ps->weapon != WP_THERMAL//not using thermals
6743 && !pm->ps->m_iVehicleNum )//not a vehicle or in a vehicle
6744 { //check for exceeding max charge time if not using disruptor or rocket launcher or thermals
6745 if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT )
6746 {
6747 int timeDif = (pm->cmd.serverTime - pm->ps->weaponChargeTime);
6748
6749 if (timeDif > MAX_WEAPON_CHARGE_TIME)
6750 {
6751 pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
6752 }
6753 }
6754
6755 if ( pm->ps->weaponstate == WEAPON_CHARGING )
6756 {
6757 int timeDif = (pm->cmd.serverTime - pm->ps->weaponChargeTime);
6758
6759 if (timeDif > MAX_WEAPON_CHARGE_TIME)
6760 {
6761 pm->cmd.buttons &= ~BUTTON_ATTACK;
6762 }
6763 }
6764 }
6765
6766 if (pm->ps->forceHandExtend == HANDEXTEND_WEAPONREADY &&
6767 PM_CanSetWeaponAnims())
6768 { //reset into weapon stance
6769 if (pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE && !PM_IsRocketTrooper())
6770 { //saber handles its own anims
6771 if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
6772 {
6773 //PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
6774 PM_StartTorsoAnim( TORSO_RAISEWEAP1);
6775 }
6776 else
6777 {
6778 if (pm->ps->weapon == WP_EMPLACED_GUN)
6779 {
6780 PM_StartTorsoAnim( BOTH_GUNSIT1 );
6781 }
6782 else
6783 {
6784 //PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
6785 PM_StartTorsoAnim( TORSO_RAISEWEAP1);
6786 }
6787 }
6788 }
6789
6790 //we now go into a weapon raise anim after every force hand extend.
6791 //this is so that my holster-view-weapon-when-hand-extend stuff works.
6792 pm->ps->weaponstate = WEAPON_RAISING;
6793 pm->ps->weaponTime += 250;
6794
6795 pm->ps->forceHandExtend = HANDEXTEND_NONE;
6796 }
6797 else if (pm->ps->forceHandExtend != HANDEXTEND_NONE)
6798 { //nothing else should be allowed to happen during this time, including weapon fire
6799 int desiredAnim = 0;
6800 qboolean seperateOnTorso = qfalse;
6801 qboolean playFullBody = qfalse;
6802 int desiredOnTorso = 0;
6803
6804 switch(pm->ps->forceHandExtend)
6805 {
6806 case HANDEXTEND_FORCEPUSH:
6807 desiredAnim = BOTH_FORCEPUSH;
6808 break;
6809 case HANDEXTEND_FORCEPULL:
6810 desiredAnim = BOTH_FORCEPULL;
6811 break;
6812 case HANDEXTEND_FORCE_HOLD:
6813 if ( (pm->ps->fd.forcePowersActive&(1<<FP_GRIP)) )
6814 {//gripping
6815 desiredAnim = BOTH_FORCEGRIP_HOLD;
6816 }
6817 else if ( (pm->ps->fd.forcePowersActive&(1<<FP_LIGHTNING)) )
6818 {//lightning
6819 if ( pm->ps->weapon == WP_MELEE
6820 && pm->ps->activeForcePass > FORCE_LEVEL_2 )
6821 {//2-handed lightning
6822 desiredAnim = BOTH_FORCE_2HANDEDLIGHTNING_HOLD;
6823 }
6824 else
6825 {
6826 desiredAnim = BOTH_FORCELIGHTNING_HOLD;
6827 }
6828 }
6829 else if ( (pm->ps->fd.forcePowersActive&(1<<FP_DRAIN)) )
6830 {//draining
6831 desiredAnim = BOTH_FORCEGRIP_HOLD;
6832 }
6833 else
6834 {//???
6835 desiredAnim = BOTH_FORCEGRIP_HOLD;
6836 }
6837 break;
6838 case HANDEXTEND_SABERPULL:
6839 desiredAnim = BOTH_SABERPULL;
6840 break;
6841 case HANDEXTEND_CHOKE:
6842 desiredAnim = BOTH_CHOKE3; //left-handed choke
6843 break;
6844 case HANDEXTEND_DODGE:
6845 desiredAnim = pm->ps->forceDodgeAnim;
6846 break;
6847 case HANDEXTEND_KNOCKDOWN:
6848 if (pm->ps->forceDodgeAnim)
6849 {
6850 if (pm->ps->forceDodgeAnim > 4)
6851 { //this means that we want to play a sepereate anim on the torso
6852 int originalDAnim = pm->ps->forceDodgeAnim-8; //-8 is the original legs anim
6853 if (originalDAnim == 2)
6854 {
6855 desiredAnim = BOTH_FORCE_GETUP_B1;
6856 }
6857 else if (originalDAnim == 3)
6858 {
6859 desiredAnim = BOTH_FORCE_GETUP_B3;
6860 }
6861 else
6862 {
6863 desiredAnim = BOTH_GETUP1;
6864 }
6865
6866 //now specify the torso anim
6867 seperateOnTorso = qtrue;
6868 desiredOnTorso = BOTH_FORCEPUSH;
6869 }
6870 else if (pm->ps->forceDodgeAnim == 2)
6871 {
6872 desiredAnim = BOTH_FORCE_GETUP_B1;
6873 }
6874 else if (pm->ps->forceDodgeAnim == 3)
6875 {
6876 desiredAnim = BOTH_FORCE_GETUP_B3;
6877 }
6878 else
6879 {
6880 desiredAnim = BOTH_GETUP1;
6881 }
6882 }
6883 else
6884 {
6885 desiredAnim = BOTH_KNOCKDOWN1;
6886 }
6887 break;
6888 case HANDEXTEND_DUELCHALLENGE:
6889 desiredAnim = BOTH_ENGAGETAUNT;
6890 break;
6891 case HANDEXTEND_TAUNT:
6892 desiredAnim = pm->ps->forceDodgeAnim;
6893 if ( desiredAnim != BOTH_ENGAGETAUNT
6894 && VectorCompare( pm->ps->velocity, vec3_origin )
6895 && pm->ps->groundEntityNum != ENTITYNUM_NONE )
6896 {
6897 playFullBody = qtrue;
6898 }
6899 break;
6900 case HANDEXTEND_PRETHROW:
6901 desiredAnim = BOTH_A3_TL_BR;
6902 playFullBody = qtrue;
6903 break;
6904 case HANDEXTEND_POSTTHROW:
6905 desiredAnim = BOTH_D3_TL___;
6906 playFullBody = qtrue;
6907 break;
6908 case HANDEXTEND_PRETHROWN:
6909 desiredAnim = BOTH_KNEES1;
6910 playFullBody = qtrue;
6911 break;
6912 case HANDEXTEND_POSTTHROWN:
6913 if (pm->ps->forceDodgeAnim)
6914 {
6915 desiredAnim = BOTH_FORCE_GETUP_F2;
6916 }
6917 else
6918 {
6919 desiredAnim = BOTH_KNOCKDOWN5;
6920 }
6921 playFullBody = qtrue;
6922 break;
6923 case HANDEXTEND_DRAGGING:
6924 desiredAnim = BOTH_B1_BL___;
6925 break;
6926 case HANDEXTEND_JEDITAUNT:
6927 desiredAnim = BOTH_GESTURE1;
6928 //playFullBody = qtrue;
6929 break;
6930 //Hmm... maybe use these, too?
6931 //BOTH_FORCEHEAL_QUICK //quick heal (SP level 2 & 3)
6932 //BOTH_MINDTRICK1 // wave (maybe for mind trick 2 & 3 - whole area, and for force seeing)
6933 //BOTH_MINDTRICK2 // tap (maybe for mind trick 1 - one person)
6934 //BOTH_FORCEGRIP_START //start grip
6935 //BOTH_FORCEGRIP_HOLD //hold grip
6936 //BOTH_FORCEGRIP_RELEASE //release grip
6937 //BOTH_FORCELIGHTNING //quick lightning burst (level 1)
6938 //BOTH_FORCELIGHTNING_START //start lightning
6939 //BOTH_FORCELIGHTNING_HOLD //hold lightning
6940 //BOTH_FORCELIGHTNING_RELEASE //release lightning
6941 default:
6942 desiredAnim = BOTH_FORCEPUSH;
6943 break;
6944 }
6945
6946 if (!seperateOnTorso)
6947 { //of seperateOnTorso, handle it after setting the legs
6948 PM_SetAnim(SETANIM_TORSO, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
6949 pm->ps->torsoTimer = 1;
6950 }
6951
6952 if (playFullBody)
6953 { //sorry if all these exceptions are getting confusing. This one just means play on both legs and torso.
6954 PM_SetAnim(SETANIM_BOTH, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
6955 pm->ps->legsTimer = pm->ps->torsoTimer = 1;
6956 }
6957 else if (pm->ps->forceHandExtend == HANDEXTEND_DODGE || pm->ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
6958 (pm->ps->forceHandExtend == HANDEXTEND_CHOKE && pm->ps->groundEntityNum == ENTITYNUM_NONE) )
6959 { //special case, play dodge anim on whole body, choke anim too if off ground
6960 if (seperateOnTorso)
6961 {
6962 PM_SetAnim(SETANIM_LEGS, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
6963 pm->ps->legsTimer = 1;
6964
6965 PM_SetAnim(SETANIM_TORSO, desiredOnTorso, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
6966 pm->ps->torsoTimer = 1;
6967 }
6968 else
6969 {
6970 PM_SetAnim(SETANIM_LEGS, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
6971 pm->ps->legsTimer = 1;
6972 }
6973 }
6974
6975 return;
6976 }
6977
6978 if (BG_InSpecialJump(pm->ps->legsAnim) ||
6979 BG_InRoll(pm->ps, pm->ps->legsAnim) ||
6980 PM_InRollComplete(pm->ps, pm->ps->legsAnim))
6981 {
6982 /*
6983 if (pm->cmd.weapon != WP_MELEE &&
6984 pm->ps->weapon != WP_MELEE &&
6985 (pm->ps->stats[STAT_WEAPONS] & (1<<WP_SABER)))
6986 { //it's alright also if we are melee
6987 pm->cmd.weapon = WP_SABER;
6988 pm->ps->weapon = WP_SABER;
6989 }
6990 */
6991 if (pm->ps->weaponTime < pm->ps->legsTimer)
6992 {
6993 pm->ps->weaponTime = pm->ps->legsTimer;
6994 }
6995 }
6996
6997 if (pm->ps->duelInProgress)
6998 {
6999 pm->cmd.weapon = WP_SABER;
7000 pm->ps->weapon = WP_SABER;
7001
7002 if (pm->ps->duelTime >= pm->cmd.serverTime)
7003 {
7004 pm->cmd.upmove = 0;
7005 pm->cmd.forwardmove = 0;
7006 pm->cmd.rightmove = 0;
7007 }
7008 }
7009
7010 if (pm->ps->weapon == WP_SABER && pm->ps->saberMove != LS_READY && pm->ps->saberMove != LS_NONE)
7011 {
7012 pm->cmd.weapon = WP_SABER; //don't allow switching out mid-attack
7013 }
7014
7015 if (pm->ps->weapon == WP_SABER)
7016 {
7017 //rww - we still need the item stuff, so we won't return immediately
7018 PM_WeaponLightsaber();
7019 killAfterItem = 1;
7020 }
7021 else if (pm->ps->weapon != WP_EMPLACED_GUN)
7022 {
7023 pm->ps->saberHolstered = 0;
7024 }
7025
7026 if (PM_CanSetWeaponAnims())
7027 {
7028 if (pm->ps->weapon == WP_THERMAL ||
7029 pm->ps->weapon == WP_TRIP_MINE ||
7030 pm->ps->weapon == WP_DET_PACK)
7031 {
7032 if (pm->ps->weapon == WP_THERMAL)
7033 {
7034 if ((pm->ps->torsoAnim) == WeaponAttackAnim[pm->ps->weapon] &&
7035 (pm->ps->weaponTime-200) <= 0)
7036 {
7037 PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
7038 }
7039 }
7040 else
7041 {
7042 if ((pm->ps->torsoAnim) == WeaponAttackAnim[pm->ps->weapon] &&
7043 (pm->ps->weaponTime-700) <= 0)
7044 {
7045 PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
7046 }
7047 }
7048 }
7049 }
7050
7051 // don't allow attack until all buttons are up
7052 if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
7053 return;
7054 }
7055
7056 // ignore if spectator
7057 if ( pm->ps->clientNum < MAX_CLIENTS && pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
7058 return;
7059 }
7060
7061 // check for dead player
7062 if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
7063 pm->ps->weapon = WP_NONE;
7064 return;
7065 }
7066
7067 // check for item using
7068 if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) {
7069 // fix: rocket lock bug, one of many...
7070 BG_ClearRocketLock( pm->ps );
7071
7072 if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) {
7073
7074 if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
7075 && pm->ps->m_iVehicleNum)
7076 {//riding a vehicle, can't use holdable items, this button operates as the weapon link/unlink toggle
7077 return;
7078 }
7079
7080 if (!pm->ps->stats[STAT_HOLDABLE_ITEM])
7081 {
7082 return;
7083 }
7084
7085 if (!PM_ItemUsable(pm->ps, 0))
7086 {
7087 pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
7088 return;
7089 }
7090 else
7091 {
7092 if (pm->ps->stats[STAT_HOLDABLE_ITEMS] & (1 << bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag))
7093 {
7094 if (bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_BINOCULARS &&
7095 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_JETPACK &&
7096 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_HEALTHDISP &&
7097 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_AMMODISP &&
7098 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_CLOAK &&
7099 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_EWEB)
7100 { //never use up the binoculars or jetpack or dispensers or cloak or ...
7101 pm->ps->stats[STAT_HOLDABLE_ITEMS] -= (1 << bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag);
7102 }
7103 }
7104 else
7105 {
7106 return; //this should not happen...
7107 }
7108
7109 pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
7110 PM_AddEvent( EV_USE_ITEM0 + bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag );
7111
7112 if (bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_BINOCULARS &&
7113 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_JETPACK &&
7114 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_HEALTHDISP &&
7115 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_AMMODISP &&
7116 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_CLOAK &&
7117 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_EWEB)
7118 {
7119 pm->ps->stats[STAT_HOLDABLE_ITEM] = 0;
7120 BG_CycleInven(pm->ps, 1);
7121 }
7122 }
7123 return;
7124 }
7125 } else {
7126 pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD;
7127 }
7128
7129 /*
7130 if (pm->ps->weapon == WP_SABER || pm->ps->weapon == WP_MELEE)
7131 { //we can't toggle zoom while using saber (for obvious reasons) so make sure it's always off
7132 pm->ps->zoomMode = 0;
7133 pm->ps->zoomFov = 0;
7134 pm->ps->zoomLocked = qfalse;
7135 pm->ps->zoomLockTime = 0;
7136 }
7137 */
7138
7139 if (killAfterItem)
7140 {
7141 return;
7142 }
7143
7144 // make weapon function
7145 if ( pm->ps->weaponTime > 0 ) {
7146 pm->ps->weaponTime -= pml.msec;
7147 }
7148
7149 if (pm->ps->isJediMaster && pm->ps->emplacedIndex)
7150 {
7151 pm->ps->emplacedIndex = 0;
7152 pm->ps->saberHolstered = 0;
7153 }
7154
7155 if (pm->ps->duelInProgress && pm->ps->emplacedIndex)
7156 {
7157 pm->ps->emplacedIndex = 0;
7158 pm->ps->saberHolstered = 0;
7159 }
7160
7161 if (pm->ps->weapon == WP_EMPLACED_GUN && pm->ps->emplacedIndex)
7162 {
7163 pm->cmd.weapon = WP_EMPLACED_GUN; //No switch for you!
7164 PM_StartTorsoAnim( BOTH_GUNSIT1 );
7165 }
7166
7167 if (pm->ps->isJediMaster || pm->ps->duelInProgress || pm->ps->trueJedi)
7168 {
7169 pm->cmd.weapon = WP_SABER;
7170 pm->ps->weapon = WP_SABER;
7171
7172 if (pm->ps->isJediMaster || pm->ps->trueJedi)
7173 {
7174 pm->ps->stats[STAT_WEAPONS] = (1 << WP_SABER);
7175 }
7176 }
7177
7178 amount = weaponData[pm->ps->weapon].energyPerShot;
7179
7180 // take an ammo away if not infinite
7181 if ( pm->ps->weapon != WP_NONE &&
7182 pm->ps->weapon == pm->cmd.weapon &&
7183 (pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING) )
7184 {
7185 if ( pm->ps->clientNum < MAX_CLIENTS && pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
7186 {
7187 // enough energy to fire this weapon?
7188 if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < weaponData[pm->ps->weapon].energyPerShot &&
7189 pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < weaponData[pm->ps->weapon].altEnergyPerShot)
7190 { //the weapon is out of ammo essentially because it cannot fire primary or secondary, so do the switch
7191 //regardless of if the player is attacking or not
7192 PM_AddEventWithParm( EV_NOAMMO, WP_NUM_WEAPONS+pm->ps->weapon );
7193
7194 if (pm->ps->weaponTime < 500)
7195 {
7196 pm->ps->weaponTime += 500;
7197 }
7198 return;
7199 }
7200
7201 if (pm->ps->weapon == WP_DET_PACK && !pm->ps->hasDetPackPlanted && pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < 1)
7202 {
7203 PM_AddEventWithParm( EV_NOAMMO, WP_NUM_WEAPONS+pm->ps->weapon );
7204
7205 if (pm->ps->weaponTime < 500)
7206 {
7207 pm->ps->weaponTime += 500;
7208 }
7209 return;
7210 }
7211 }
7212 }
7213
7214 // check for weapon change
7215 // can't change if weapon is firing, but can change
7216 // again if lowering or raising
7217 if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
7218 if ( pm->ps->weapon != pm->cmd.weapon ) {
7219 PM_BeginWeaponChange( pm->cmd.weapon );
7220 }
7221 }
7222
7223 if ( pm->ps->weaponTime > 0 ) {
7224 return;
7225 }
7226
7227 if (pm->ps->weapon == WP_DISRUPTOR &&
7228 pm->ps->zoomMode == 1)
7229 {
7230 if (pm_cancelOutZoom)
7231 {
7232 pm->ps->zoomMode = 0;
7233 pm->ps->zoomFov = 0;
7234 pm->ps->zoomLocked = qfalse;
7235 pm->ps->zoomLockTime = 0;
7236 PM_AddEvent( EV_DISRUPTOR_ZOOMSOUND );
7237 return;
7238 }
7239
7240 if (pm->cmd.forwardmove ||
7241 pm->cmd.rightmove ||
7242 pm->cmd.upmove > 0)
7243 {
7244 return;
7245 }
7246 }
7247
7248 // change weapon if time
7249 if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
7250 PM_FinishWeaponChange();
7251 return;
7252 }
7253
7254 if ( pm->ps->weaponstate == WEAPON_RAISING ) {
7255 pm->ps->weaponstate = WEAPON_READY;
7256 if (PM_CanSetWeaponAnims())
7257 {
7258 if ( pm->ps->weapon == WP_SABER )
7259 {
7260 PM_StartTorsoAnim( PM_GetSaberStance() );
7261 }
7262 else if (pm->ps->weapon == WP_MELEE || PM_IsRocketTrooper())
7263 {
7264 PM_StartTorsoAnim( pm->ps->legsAnim );
7265 }
7266 else
7267 {
7268 if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
7269 {
7270 PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
7271 }
7272 else
7273 {
7274 if (pm->ps->weapon == WP_EMPLACED_GUN)
7275 {
7276 PM_StartTorsoAnim( BOTH_GUNSIT1 );
7277 }
7278 else
7279 {
7280 PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
7281 }
7282 }
7283 }
7284 }
7285 return;
7286 }
7287
7288 if (PM_CanSetWeaponAnims() &&
7289 !PM_IsRocketTrooper() &&
7290 pm->ps->weaponstate == WEAPON_READY && pm->ps->weaponTime <= 0 &&
7291 (pm->ps->weapon >= WP_BRYAR_PISTOL || pm->ps->weapon == WP_STUN_BATON) &&
7292 pm->ps->torsoTimer <= 0 &&
7293 (pm->ps->torsoAnim) != WeaponReadyAnim[pm->ps->weapon] &&
7294 pm->ps->torsoAnim != TORSO_WEAPONIDLE3 &&
7295 pm->ps->weapon != WP_EMPLACED_GUN)
7296 {
7297 PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
7298 }
7299 else if (PM_CanSetWeaponAnims() &&
7300 pm->ps->weapon == WP_MELEE)
7301 {
7302 if (pm->ps->weaponTime <= 0 &&
7303 pm->ps->forceHandExtend == HANDEXTEND_NONE)
7304 {
7305 int desTAnim = pm->ps->legsAnim;
7306
7307 if (desTAnim == BOTH_STAND1 ||
7308 desTAnim == BOTH_STAND2)
7309 { //remap the standard standing anims for melee stance
7310 desTAnim = BOTH_STAND6;
7311 }
7312
7313 if (!(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)))
7314 { //don't do this while holding attack
7315 if (pm->ps->torsoAnim != desTAnim)
7316 {
7317 PM_StartTorsoAnim( desTAnim );
7318 }
7319 }
7320 }
7321 }
7322 else if (PM_CanSetWeaponAnims() && PM_IsRocketTrooper())
7323 {
7324 int desTAnim = pm->ps->legsAnim;
7325
7326 if (!(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)))
7327 { //don't do this while holding attack
7328 if (pm->ps->torsoAnim != desTAnim)
7329 {
7330 PM_StartTorsoAnim( desTAnim );
7331 }
7332 }
7333 }
7334
7335 if (((pm->ps->torsoAnim) == TORSO_WEAPONREADY4 ||
7336 (pm->ps->torsoAnim) == BOTH_ATTACK4) &&
7337 (pm->ps->weapon != WP_DISRUPTOR || pm->ps->zoomMode != 1))
7338 {
7339 if (pm->ps->weapon == WP_EMPLACED_GUN)
7340 {
7341 PM_StartTorsoAnim( BOTH_GUNSIT1 );
7342 }
7343 else if (PM_CanSetWeaponAnims())
7344 {
7345 PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
7346 }
7347 }
7348 else if (((pm->ps->torsoAnim) != TORSO_WEAPONREADY4 &&
7349 (pm->ps->torsoAnim) != BOTH_ATTACK4) &&
7350 PM_CanSetWeaponAnims() &&
7351 (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1))
7352 {
7353 PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
7354 }
7355
7356 if (pm->ps->clientNum >= MAX_CLIENTS &&
7357 pm_entSelf &&
7358 pm_entSelf->s.NPC_class == CLASS_VEHICLE)
7359 {//we are a vehicle
7360 veh = pm_entSelf;
7361 }
7362 if ( veh
7363 && veh->m_pVehicle )
7364 {
7365 if ( g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].fHoming
7366 || g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].fHoming )
7367 {//don't clear the rocket locking ever?
7368 vehicleRocketLock = qtrue;
7369 }
7370 }
7371
7372 if ( !vehicleRocketLock )
7373 {
7374 if (pm->ps->weapon != WP_ROCKET_LAUNCHER)
7375 {
7376 if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
7377 &&pm->ps->m_iVehicleNum)
7378 {//riding a vehicle, the vehicle will tell me my rocketlock stuff...
7379 }
7380 else
7381 {
7382 pm->ps->rocketLockIndex = ENTITYNUM_NONE;
7383 pm->ps->rocketLockTime = 0;
7384 pm->ps->rocketTargetTime = 0;
7385 }
7386 }
7387 }
7388
7389 if ( PM_DoChargedWeapons(vehicleRocketLock, veh))
7390 {
7391 // In some cases the charged weapon code may want us to short circuit the rest of the firing code
7392 return;
7393 }
7394
7395 // check for fire
7396 if ( ! (pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)))
7397 {
7398 pm->ps->weaponTime = 0;
7399 pm->ps->weaponstate = WEAPON_READY;
7400 return;
7401 }
7402
7403 if (pm->ps->weapon == WP_EMPLACED_GUN)
7404 {
7405 addTime = weaponData[pm->ps->weapon].fireTime;
7406 pm->ps->weaponTime += addTime;
7407 if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) )
7408 {
7409 PM_AddEvent( EV_ALT_FIRE );
7410 }
7411 else
7412 {
7413 PM_AddEvent( EV_FIRE_WEAPON );
7414 }
7415 return;
7416 }
7417 else if (pm->ps->m_iVehicleNum
7418 && pm_entSelf->s.NPC_class==CLASS_VEHICLE)
7419 { //a vehicle NPC that has a pilot
7420 pm->ps->weaponstate = WEAPON_FIRING;
7421 pm->ps->weaponTime += 100;
7422 #ifdef _GAME //hack, only do it game-side. vehicle weapons don't really need predicting I suppose.
7423 if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) )
7424 {
7425 G_CheapWeaponFire(pm->ps->clientNum, EV_ALT_FIRE);
7426 }
7427 else
7428 {
7429 G_CheapWeaponFire(pm->ps->clientNum, EV_FIRE_WEAPON);
7430 }
7431 #endif
7432 /*
7433 addTime = weaponData[WP_EMPLACED_GUN].fireTime;
7434 pm->ps->weaponTime += addTime;
7435 if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) )
7436 {
7437 PM_AddEvent( EV_ALT_FIRE );
7438 }
7439 else
7440 {
7441 PM_AddEvent( EV_FIRE_WEAPON );
7442 }
7443 */
7444 return;
7445 }
7446
7447 if (pm->ps->weapon == WP_DISRUPTOR &&
7448 (pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
7449 !pm->ps->zoomLocked)
7450 {
7451 return;
7452 }
7453
7454 if (pm->ps->weapon == WP_DISRUPTOR &&
7455 (pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
7456 pm->ps->zoomMode == 2)
7457 { //can't use disruptor secondary while zoomed binoculars
7458 return;
7459 }
7460
7461 if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
7462 {
7463 PM_StartTorsoAnim( BOTH_ATTACK4 );
7464 }
7465 else if (pm->ps->weapon == WP_MELEE)
7466 { //special anims for standard melee attacks
7467 //Alternate between punches and use the anim length as weapon time.
7468 if (!pm->ps->m_iVehicleNum)
7469 { //if riding a vehicle don't do this stuff at all
7470 if (pm->debugMelee &&
7471 (pm->cmd.buttons & BUTTON_ATTACK) &&
7472 (pm->cmd.buttons & BUTTON_ALT_ATTACK))
7473 { //ok, grapple time
7474 #if 0 //eh, I want to try turning the saber off, but can't do that reliably for prediction..
7475 qboolean icandoit = qtrue;
7476 if (pm->ps->weaponTime > 0)
7477 { //weapon busy
7478 icandoit = qfalse;
7479 }
7480 if (pm->ps->forceHandExtend != HANDEXTEND_NONE)
7481 { //force power or knockdown or something
7482 icandoit = qfalse;
7483 }
7484 if (pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE)
7485 {
7486 icandoit = qfalse;
7487 }
7488
7489 if (icandoit)
7490 {
7491 //G_SetAnim(ent, &ent->client->pers.cmd, SETANIM_BOTH, BOTH_KYLE_GRAB, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
7492 PM_SetAnim(SETANIM_BOTH, BOTH_KYLE_GRAB, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
7493 if (pm->ps->torsoAnim == BOTH_KYLE_GRAB)
7494 { //providing the anim set succeeded..
7495 pm->ps->torsoTimer += 500; //make the hand stick out a little longer than it normally would
7496 if (pm->ps->legsAnim == pm->ps->torsoAnim)
7497 {
7498 pm->ps->legsTimer = pm->ps->torsoTimer;
7499 }
7500 pm->ps->weaponTime = pm->ps->torsoTimer;
7501 return;
7502 }
7503 }
7504 #else
7505 #ifdef _GAME
7506 if (pm_entSelf)
7507 {
7508 if (TryGrapple((gentity_t *)pm_entSelf))
7509 {
7510 return;
7511 }
7512 }
7513 #else
7514 return;
7515 #endif
7516 #endif
7517 }
7518 else if (pm->debugMelee &&
7519 (pm->cmd.buttons & BUTTON_ALT_ATTACK))
7520 { //kicks
7521 if (!BG_KickingAnim(pm->ps->torsoAnim) &&
7522 !BG_KickingAnim(pm->ps->legsAnim))
7523 {
7524 int kickMove = PM_KickMoveForConditions();
7525 if (kickMove == LS_HILT_BASH)
7526 { //yeah.. no hilt to bash with!
7527 kickMove = LS_KICK_F;
7528 }
7529
7530 if (kickMove != -1)
7531 {
7532 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
7533 {//if in air, convert kick to an in-air kick
7534 float gDist = PM_GroundDistance();
7535 //let's only allow air kicks if a certain distance from the ground
7536 //it's silly to be able to do them right as you land.
7537 //also looks wrong to transition from a non-complete flip anim...
7538 if ((!BG_FlippingAnim( pm->ps->legsAnim ) || pm->ps->legsTimer <= 0) &&
7539 gDist > 64.0f && //strict minimum
7540 gDist > (-pm->ps->velocity[2])-64.0f //make sure we are high to ground relative to downward velocity as well
7541 )
7542 {
7543 switch ( kickMove )
7544 {
7545 case LS_KICK_F:
7546 kickMove = LS_KICK_F_AIR;
7547 break;
7548 case LS_KICK_B:
7549 kickMove = LS_KICK_B_AIR;
7550 break;
7551 case LS_KICK_R:
7552 kickMove = LS_KICK_R_AIR;
7553 break;
7554 case LS_KICK_L:
7555 kickMove = LS_KICK_L_AIR;
7556 break;
7557 default: //oh well, can't do any other kick move while in-air
7558 kickMove = -1;
7559 break;
7560 }
7561 }
7562 else
7563 { //off ground, but too close to ground
7564 kickMove = -1;
7565 }
7566 }
7567 }
7568
7569 if (kickMove != -1)
7570 {
7571 int kickAnim = saberMoveData[kickMove].animToUse;
7572
7573 if (kickAnim != -1)
7574 {
7575 PM_SetAnim(SETANIM_BOTH, kickAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
7576 if (pm->ps->legsAnim == kickAnim)
7577 {
7578 pm->ps->weaponTime = pm->ps->legsTimer;
7579 return;
7580 }
7581 }
7582 }
7583 }
7584
7585 //if got here then no move to do so put torso into leg idle or whatever
7586 if (pm->ps->torsoAnim != pm->ps->legsAnim)
7587 {
7588 PM_SetAnim(SETANIM_BOTH, pm->ps->legsAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
7589 }
7590 pm->ps->weaponTime = 0;
7591 return;
7592 }
7593 else
7594 { //just punch
7595 int desTAnim = BOTH_MELEE1;
7596 if (pm->ps->torsoAnim == BOTH_MELEE1)
7597 {
7598 desTAnim = BOTH_MELEE2;
7599 }
7600 PM_StartTorsoAnim( desTAnim );
7601
7602 if (pm->ps->torsoAnim == desTAnim)
7603 {
7604 pm->ps->weaponTime = pm->ps->torsoTimer;
7605 }
7606 }
7607 }
7608 }
7609 else
7610 {
7611 PM_StartTorsoAnim( WeaponAttackAnim[pm->ps->weapon] );
7612 }
7613
7614 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
7615 {
7616 amount = weaponData[pm->ps->weapon].altEnergyPerShot;
7617 }
7618 else
7619 {
7620 amount = weaponData[pm->ps->weapon].energyPerShot;
7621 }
7622
7623 pm->ps->weaponstate = WEAPON_FIRING;
7624
7625 // take an ammo away if not infinite
7626 if ( pm->ps->clientNum < MAX_CLIENTS && pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
7627 {
7628 // enough energy to fire this weapon?
7629 if ((pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - amount) >= 0)
7630 {
7631 pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= amount;
7632 }
7633 else // Not enough energy
7634 {
7635 // Switch weapons
7636 if (pm->ps->weapon != WP_DET_PACK || !pm->ps->hasDetPackPlanted)
7637 {
7638 PM_AddEventWithParm( EV_NOAMMO, WP_NUM_WEAPONS+pm->ps->weapon );
7639 if (pm->ps->weaponTime < 500)
7640 {
7641 pm->ps->weaponTime += 500;
7642 }
7643 }
7644 return;
7645 }
7646 }
7647
7648 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) {
7649 //if ( pm->ps->weapon == WP_BRYAR_PISTOL && pm->gametype != GT_SIEGE )
7650 if (0)
7651 { //kind of a hack for now
7652 PM_AddEvent( EV_FIRE_WEAPON );
7653 addTime = weaponData[pm->ps->weapon].fireTime;
7654 }
7655 else if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode != 1)
7656 {
7657 PM_AddEvent( EV_FIRE_WEAPON );
7658 addTime = weaponData[pm->ps->weapon].fireTime;
7659 }
7660 else
7661 {
7662 if (pm->ps->weapon != WP_MELEE ||
7663 !pm->ps->m_iVehicleNum)
7664 { //do not fire melee events at all when on vehicle
7665 PM_AddEvent( EV_ALT_FIRE );
7666 }
7667 addTime = weaponData[pm->ps->weapon].altFireTime;
7668 }
7669 }
7670 else {
7671 if (pm->ps->weapon != WP_MELEE ||
7672 !pm->ps->m_iVehicleNum)
7673 { //do not fire melee events at all when on vehicle
7674 PM_AddEvent( EV_FIRE_WEAPON );
7675 }
7676 addTime = weaponData[pm->ps->weapon].fireTime;
7677 if ( pm->gametype == GT_SIEGE && pm->ps->weapon == WP_DET_PACK )
7678 { // were far too spammy before? So says Rick.
7679 addTime *= 2;
7680 }
7681 }
7682
7683 /*
7684 if ( pm->ps->powerups[PW_HASTE] ) {
7685 addTime /= 1.3;
7686 }
7687 */
7688
7689 if (pm->ps->fd.forcePowersActive & (1 << FP_RAGE))
7690 {
7691 addTime *= 0.75;
7692 }
7693 else if (pm->ps->fd.forceRageRecoveryTime > pm->cmd.serverTime)
7694 {
7695 addTime *= 1.5;
7696 }
7697
7698 pm->ps->weaponTime += addTime;
7699 }
7700
7701 /*
7702 ================
7703 PM_Animate
7704 ================
7705 */
7706
PM_Animate(void)7707 static void PM_Animate( void ) {
7708 if ( pm->cmd.buttons & BUTTON_GESTURE ) {
7709 if (pm->ps->m_iVehicleNum)
7710 { //eh, fine, clear it
7711 if (pm->ps->forceHandExtendTime < pm->cmd.serverTime)
7712 {
7713 pm->ps->forceHandExtend = HANDEXTEND_NONE;
7714 }
7715 }
7716
7717 if ( pm->ps->torsoTimer < 1 && pm->ps->forceHandExtend == HANDEXTEND_NONE &&
7718 pm->ps->legsTimer < 1 && pm->ps->weaponTime < 1 && pm->ps->saberLockTime < pm->cmd.serverTime) {
7719
7720 pm->ps->forceHandExtend = HANDEXTEND_TAUNT;
7721
7722 //FIXME: random taunt anims?
7723 pm->ps->forceDodgeAnim = BOTH_ENGAGETAUNT;
7724
7725 pm->ps->forceHandExtendTime = pm->cmd.serverTime + 1000;
7726
7727 //pm->ps->weaponTime = 100;
7728
7729 PM_AddEvent( EV_TAUNT );
7730 }
7731 #if 0
7732 // Here's an interesting bit. The bots in TA used buttons to do additional gestures.
7733 // I ripped them out because I didn't want too many buttons given the fact that I was already adding some for JK2.
7734 // We can always add some back in if we want though.
7735 } else if ( pm->cmd.buttons & BUTTON_GETFLAG ) {
7736 if ( pm->ps->torsoTimer == 0 ) {
7737 PM_StartTorsoAnim( TORSO_GETFLAG );
7738 pm->ps->torsoTimer = 600; //TIMER_GESTURE;
7739 }
7740 } else if ( pm->cmd.buttons & BUTTON_GUARDBASE ) {
7741 if ( pm->ps->torsoTimer == 0 ) {
7742 PM_StartTorsoAnim( TORSO_GUARDBASE );
7743 pm->ps->torsoTimer = 600; //TIMER_GESTURE;
7744 }
7745 } else if ( pm->cmd.buttons & BUTTON_PATROL ) {
7746 if ( pm->ps->torsoTimer == 0 ) {
7747 PM_StartTorsoAnim( TORSO_PATROL );
7748 pm->ps->torsoTimer = 600; //TIMER_GESTURE;
7749 }
7750 } else if ( pm->cmd.buttons & BUTTON_FOLLOWME ) {
7751 if ( pm->ps->torsoTimer == 0 ) {
7752 PM_StartTorsoAnim( TORSO_FOLLOWME );
7753 pm->ps->torsoTimer = 600; //TIMER_GESTURE;
7754 }
7755 } else if ( pm->cmd.buttons & BUTTON_AFFIRMATIVE ) {
7756 if ( pm->ps->torsoTimer == 0 ) {
7757 PM_StartTorsoAnim( TORSO_AFFIRMATIVE);
7758 pm->ps->torsoTimer = 600; //TIMER_GESTURE;
7759 }
7760 } else if ( pm->cmd.buttons & BUTTON_NEGATIVE ) {
7761 if ( pm->ps->torsoTimer == 0 ) {
7762 PM_StartTorsoAnim( TORSO_NEGATIVE );
7763 pm->ps->torsoTimer = 600; //TIMER_GESTURE;
7764 }
7765 #endif //
7766 }
7767 }
7768
7769
7770 /*
7771 ================
7772 PM_DropTimers
7773 ================
7774 */
PM_DropTimers(void)7775 static void PM_DropTimers( void ) {
7776 // drop misc timing counter
7777 if ( pm->ps->pm_time ) {
7778 if ( pml.msec >= pm->ps->pm_time ) {
7779 pm->ps->pm_flags &= ~PMF_ALL_TIMES;
7780 pm->ps->pm_time = 0;
7781 } else {
7782 pm->ps->pm_time -= pml.msec;
7783 }
7784 }
7785
7786 // drop animation counter
7787 if ( pm->ps->legsTimer > 0 ) {
7788 pm->ps->legsTimer -= pml.msec;
7789 if ( pm->ps->legsTimer < 0 ) {
7790 pm->ps->legsTimer = 0;
7791 }
7792 }
7793
7794 if ( pm->ps->torsoTimer > 0 ) {
7795 pm->ps->torsoTimer -= pml.msec;
7796 if ( pm->ps->torsoTimer < 0 ) {
7797 pm->ps->torsoTimer = 0;
7798 }
7799 }
7800 }
7801
7802 // Following function is stateless (at the moment). And hoisting it out
7803 // of the namespace here is easier than fixing all the places it's used,
7804 // which includes files that are also compiled in SP. We do need to make
7805 // sure we only get one copy in the linker, though.
7806
7807 extern vmCvar_t bg_fighterAltControl;
BG_UnrestrainedPitchRoll(playerState_t * ps,Vehicle_t * pVeh)7808 qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh )
7809 {
7810 if ( bg_fighterAltControl.integer
7811 && ps->clientNum < MAX_CLIENTS //real client
7812 && ps->m_iVehicleNum//in a vehicle
7813 && pVeh //valid vehicle data pointer
7814 && pVeh->m_pVehicleInfo//valid vehicle info
7815 && pVeh->m_pVehicleInfo->type == VH_FIGHTER )//fighter
7816 //FIXME: specify per vehicle instead of assuming true for all fighters
7817 //FIXME: map/server setting?
7818 {//can roll and pitch without limitation!
7819 return qtrue;
7820 }
7821 return qfalse;
7822 }
7823
7824
7825 /*
7826 ================
7827 PM_UpdateViewAngles
7828
7829 This can be used as another entry point when only the viewangles
7830 are being updated isntead of a full move
7831 ================
7832 */
PM_UpdateViewAngles(playerState_t * ps,const usercmd_t * cmd)7833 void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) {
7834 short temp;
7835 int i;
7836
7837 if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
7838 return; // no view changes at all
7839 }
7840
7841 if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
7842 return; // no view changes at all
7843 }
7844
7845 // circularly clamp the angles with deltas
7846 for (i=0 ; i<3 ; i++) {
7847 temp = cmd->angles[i] + ps->delta_angles[i];
7848 #ifdef VEH_CONTROL_SCHEME_4
7849 if ( pm_entVeh
7850 && pm_entVeh->m_pVehicle
7851 && pm_entVeh->m_pVehicle->m_pVehicleInfo
7852 && pm_entVeh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER
7853 && (cmd->serverTime-pm_entVeh->playerState->hyperSpaceTime) >= HYPERSPACE_TIME )
7854 {//in a vehicle and not hyperspacing
7855 if ( i == PITCH )
7856 {
7857 int pitchClamp = ANGLE2SHORT(AngleNormalize180(pm_entVeh->m_pVehicle->m_vPrevRiderViewAngles[PITCH]+10.0f));
7858 // don't let the player look up or down more than 22.5 degrees
7859 if ( temp > pitchClamp )
7860 {
7861 ps->delta_angles[i] = pitchClamp - cmd->angles[i];
7862 temp = pitchClamp;
7863 }
7864 else if ( temp < -pitchClamp )
7865 {
7866 ps->delta_angles[i] = -pitchClamp - cmd->angles[i];
7867 temp = -pitchClamp;
7868 }
7869 }
7870 if ( i == YAW )
7871 {
7872 int yawClamp = ANGLE2SHORT(AngleNormalize180(pm_entVeh->m_pVehicle->m_vPrevRiderViewAngles[YAW]+10.0f));
7873 // don't let the player look left or right more than 22.5 degrees
7874 if ( temp > yawClamp )
7875 {
7876 ps->delta_angles[i] = yawClamp - cmd->angles[i];
7877 temp = yawClamp;
7878 }
7879 else if ( temp < -yawClamp )
7880 {
7881 ps->delta_angles[i] = -yawClamp - cmd->angles[i];
7882 temp = -yawClamp;
7883 }
7884 }
7885 }
7886 #else //VEH_CONTROL_SCHEME_4
7887 if ( pm_entVeh && BG_UnrestrainedPitchRoll( ps, pm_entVeh->m_pVehicle ) )
7888 {//in a fighter
7889 /*
7890 if ( i == ROLL )
7891 {//get roll from vehicle
7892 ps->viewangles[ROLL] = pm_entVeh->playerState->viewangles[ROLL];//->m_pVehicle->m_vOrientation[ROLL];
7893 continue;
7894
7895 }
7896 */
7897 }
7898 #endif // VEH_CONTROL_SCHEME_4
7899 else
7900 {
7901 if ( i == PITCH ) {
7902 // don't let the player look up or down more than 90 degrees
7903 if ( temp > 16000 ) {
7904 ps->delta_angles[i] = 16000 - cmd->angles[i];
7905 temp = 16000;
7906 } else if ( temp < -16000 ) {
7907 ps->delta_angles[i] = -16000 - cmd->angles[i];
7908 temp = -16000;
7909 }
7910 }
7911 }
7912 ps->viewangles[i] = SHORT2ANGLE(temp);
7913 }
7914 }
7915
7916 /*
7917 void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) {
7918 short temp;
7919 int i;
7920 float rootPitch = 0, pitchMin=-90, pitchMax=90, yawMin=0, yawMax=0, lockedYawValue = 0; //just to shut up warnings
7921 qboolean lockedYaw = qfalse, clamped = qfalse;
7922 bgEntity_t *vehEnt = NULL;
7923
7924 if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
7925 return; // no view changes at all
7926 }
7927
7928 if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
7929 return; // no view changes at all
7930 }
7931
7932 // If we're a vehicle, or we're riding a vehicle...?
7933 if ( ps->m_iVehicleNum )
7934 {
7935 if ( ps->clientNum < MAX_CLIENTS )
7936 { //player riding vehicle
7937 vehEnt = PM_BGEntForNum(ps->m_iVehicleNum);
7938 }
7939 else
7940 { //vehicle with player pilot
7941 vehEnt = PM_BGEntForNum(ps->clientNum);
7942 }
7943 if ( vehEnt )
7944 {//there is a vehicle
7945 Vehicle_t *pVeh = vehEnt->m_pVehicle;
7946 if ( pVeh && pVeh->m_pVehicleInfo )
7947 {
7948 // There is a vehicle...
7949 if ( pVeh->m_pVehicleInfo->type != VH_ANIMAL )
7950 {//animals just turn normally, no clamping
7951 if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
7952 {
7953 rootPitch = pVeh->m_vOrientation[PITCH];//gent->owner->client->ps.vehicleAngles[PITCH];//??? what if goes over 90 when add the min/max?
7954 if ( pVeh->m_pVehicleInfo->pitchLimit == -1 )
7955 {
7956 pitchMax = 180;
7957 }
7958 else
7959 {
7960 pitchMax = pVeh->m_pVehicleInfo->pitchLimit;
7961 }
7962 pitchMin = -pitchMax;
7963 }
7964 else
7965 {
7966 lockedYawValue = 0;//gent->owner->client->ps.vehicleAngles[YAW];
7967 lockedYaw = qtrue;
7968 yawMax = pVeh->m_pVehicleInfo->lookYaw;
7969 yawMin = -yawMax;
7970 rootPitch = 0;//gent->owner->client->ps.vehicleAngles[PITCH];//??? what if goes over 90 when add the min/max?
7971 pitchMax = pVeh->m_pVehicleInfo->lookPitch;
7972 pitchMin = -pitchMax;
7973 }
7974 }
7975 }
7976 }
7977 }
7978 if ( 1 )
7979 {
7980 const short pitchClampMin = ANGLE2SHORT(rootPitch+pitchMin);
7981 const short pitchClampMax = ANGLE2SHORT(rootPitch+pitchMax);
7982 const short yawClampMin = ANGLE2SHORT(lockedYawValue+yawMin);
7983 const short yawClampMax = ANGLE2SHORT(lockedYawValue+yawMax);
7984 for (i=0 ; i<3 ; i++)
7985 {
7986 temp = cmd->angles[i] + ps->delta_angles[i];
7987 if ( i == PITCH )
7988 {
7989 //FIXME get this limit from the NPCs stats?
7990 // don't let the player look up or down more than 90 degrees
7991 if ( temp > pitchClampMax )
7992 {
7993 ps->delta_angles[i] = (pitchClampMax - cmd->angles[i]) & 0xffff; //& clamp to short
7994 temp = pitchClampMax;
7995 clamped = qtrue;
7996 }
7997 else if ( temp < pitchClampMin )
7998 {
7999 ps->delta_angles[i] = (pitchClampMin - cmd->angles[i]) & 0xffff; //& clamp to short
8000 temp = pitchClampMin;
8001 clamped = qtrue;
8002 }
8003 }
8004 if ( i == YAW && lockedYaw )
8005 {
8006 //FIXME get this limit from the NPCs stats?
8007 // don't let the player look up or down more than 90 degrees
8008 if ( temp > yawClampMax )
8009 {
8010 ps->delta_angles[i] = (yawClampMax - cmd->angles[i]) & 0xffff; //& clamp to short
8011 temp = yawClampMax;
8012 clamped = qtrue;
8013 }
8014 else if ( temp < yawClampMin )
8015 {
8016 ps->delta_angles[i] = (yawClampMin - cmd->angles[i]) & 0xffff; //& clamp to short
8017 temp = yawClampMin;
8018 clamped = qtrue;
8019 }
8020 ps->viewangles[i] = SHORT2ANGLE(temp);
8021 }
8022 else
8023 {
8024 ps->viewangles[i] = SHORT2ANGLE(temp);
8025 }
8026 }
8027 }
8028 else
8029 {
8030 // circularly clamp the angles with deltas
8031 for (i=0 ; i<3 ; i++) {
8032 temp = cmd->angles[i] + ps->delta_angles[i];
8033 if ( i == PITCH ) {
8034 // don't let the player look up or down more than 90 degrees
8035 if ( temp > 16000 ) {
8036 ps->delta_angles[i] = 16000 - cmd->angles[i];
8037 temp = 16000;
8038 } else if ( temp < -16000 ) {
8039 ps->delta_angles[i] = -16000 - cmd->angles[i];
8040 temp = -16000;
8041 }
8042 }
8043 ps->viewangles[i] = SHORT2ANGLE(temp);
8044 }
8045 }
8046
8047 }
8048 */
8049
8050 //-------------------------------------------
PM_AdjustAttackStates(pmove_t * pmove)8051 void PM_AdjustAttackStates( pmove_t *pmove )
8052 //-------------------------------------------
8053 {
8054 int amount;
8055
8056 if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
8057 &&pmove->ps->m_iVehicleNum)
8058 { //riding a vehicle
8059 bgEntity_t *veh = pm_entVeh;
8060 if ( veh &&
8061 (veh->m_pVehicle && (veh->m_pVehicle->m_pVehicleInfo->type == VH_WALKER || veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)) )
8062 {//riding a walker/fighter
8063 //not firing, ever
8064 pmove->ps->eFlags &= ~(EF_FIRING|EF_ALT_FIRING);
8065 return;
8066 }
8067 }
8068 // get ammo usage
8069 if ( pmove->cmd.buttons & BUTTON_ALT_ATTACK )
8070 {
8071 amount = pmove->ps->ammo[weaponData[ pmove->ps->weapon ].ammoIndex] - weaponData[pmove->ps->weapon].altEnergyPerShot;
8072 }
8073 else
8074 {
8075 amount = pmove->ps->ammo[weaponData[ pmove->ps->weapon ].ammoIndex] - weaponData[pmove->ps->weapon].energyPerShot;
8076 }
8077
8078 // disruptor alt-fire should toggle the zoom mode, but only bother doing this for the player?
8079 if ( pmove->ps->weapon == WP_DISRUPTOR && pmove->ps->weaponstate == WEAPON_READY )
8080 {
8081 if ( !(pmove->ps->eFlags & EF_ALT_FIRING) && (pmove->cmd.buttons & BUTTON_ALT_ATTACK) /*&&
8082 pmove->cmd.upmove <= 0 && !pmove->cmd.forwardmove && !pmove->cmd.rightmove*/)
8083 {
8084 // We just pressed the alt-fire key
8085 if ( !pmove->ps->zoomMode && pmove->ps->pm_type != PM_DEAD )
8086 {
8087 // not already zooming, so do it now
8088 pmove->ps->zoomMode = 1;
8089 pmove->ps->zoomLocked = qfalse;
8090 pmove->ps->zoomFov = 80.0f;//cg_fov.value;
8091 pmove->ps->zoomLockTime = pmove->cmd.serverTime + 50;
8092 PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
8093 }
8094 else if (pmove->ps->zoomMode == 1 && pmove->ps->zoomLockTime < pmove->cmd.serverTime)
8095 { //check for == 1 so we can't turn binoculars off with disruptor alt fire
8096 // already zooming, so must be wanting to turn it off
8097 pmove->ps->zoomMode = 0;
8098 pmove->ps->zoomTime = pmove->ps->commandTime;
8099 pmove->ps->zoomLocked = qfalse;
8100 PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
8101 pmove->ps->weaponTime = 1000;
8102 }
8103 }
8104 else if ( !(pmove->cmd.buttons & BUTTON_ALT_ATTACK ) && pmove->ps->zoomLockTime < pmove->cmd.serverTime)
8105 {
8106 // Not pressing zoom any more
8107 if ( pmove->ps->zoomMode )
8108 {
8109 if (pmove->ps->zoomMode == 1 && !pmove->ps->zoomLocked)
8110 { //approximate what level the client should be zoomed at based on how long zoom was held
8111 pmove->ps->zoomFov = ((pmove->cmd.serverTime+50) - pmove->ps->zoomLockTime) * 0.035f;
8112 if (pmove->ps->zoomFov > 50)
8113 {
8114 pmove->ps->zoomFov = 50;
8115 }
8116 if (pmove->ps->zoomFov < 1)
8117 {
8118 pmove->ps->zoomFov = 1;
8119 }
8120 }
8121 // were zooming in, so now lock the zoom
8122 pmove->ps->zoomLocked = qtrue;
8123 }
8124 }
8125 //This seemed like a good idea, but apparently it confuses people. So disabled for now.
8126 /*
8127 else if (!(pmove->ps->eFlags & EF_ALT_FIRING) && (pmove->cmd.buttons & BUTTON_ALT_ATTACK) &&
8128 (pmove->cmd.upmove > 0 || pmove->cmd.forwardmove || pmove->cmd.rightmove))
8129 { //if you try to zoom while moving, just convert it into a primary attack
8130 pmove->cmd.buttons &= ~BUTTON_ALT_ATTACK;
8131 pmove->cmd.buttons |= BUTTON_ATTACK;
8132 }
8133 */
8134
8135 /*
8136 if (pmove->cmd.upmove > 0 || pmove->cmd.forwardmove || pmove->cmd.rightmove)
8137 {
8138 if (pmove->ps->zoomMode == 1 && pmove->ps->zoomLockTime < pmove->cmd.serverTime)
8139 { //check for == 1 so we can't turn binoculars off with disruptor alt fire
8140 pmove->ps->zoomMode = 0;
8141 pmove->ps->zoomTime = pmove->ps->commandTime;
8142 pmove->ps->zoomLocked = qfalse;
8143 PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
8144 }
8145 }
8146 */
8147
8148 if ( pmove->cmd.buttons & BUTTON_ATTACK )
8149 {
8150 // If we are zoomed, we should switch the ammo usage to the alt-fire, otherwise, we'll
8151 // just use whatever ammo was selected from above
8152 if ( pmove->ps->zoomMode )
8153 {
8154 amount = pmove->ps->ammo[weaponData[ pmove->ps->weapon ].ammoIndex] -
8155 weaponData[pmove->ps->weapon].altEnergyPerShot;
8156 }
8157 }
8158 else
8159 {
8160 // alt-fire button pressing doesn't use any ammo
8161 amount = 0;
8162 }
8163 }
8164 /*
8165 else if (pmove->ps->weapon == WP_DISRUPTOR) //still perform certain checks, even if the weapon is not ready
8166 {
8167 if (pmove->cmd.upmove > 0 || pmove->cmd.forwardmove || pmove->cmd.rightmove)
8168 {
8169 if (pmove->ps->zoomMode == 1 && pmove->ps->zoomLockTime < pmove->cmd.serverTime)
8170 { //check for == 1 so we can't turn binoculars off with disruptor alt fire
8171 pmove->ps->zoomMode = 0;
8172 pmove->ps->zoomTime = pmove->ps->commandTime;
8173 pmove->ps->zoomLocked = qfalse;
8174 PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
8175 }
8176 }
8177 }
8178 */
8179
8180 // set the firing flag for continuous beam weapons, saber will fire even if out of ammo
8181 if ( !(pmove->ps->pm_flags & PMF_RESPAWNED) &&
8182 pmove->ps->pm_type != PM_INTERMISSION &&
8183 pmove->ps->pm_type != PM_NOCLIP &&
8184 ( pmove->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) &&
8185 ( amount >= 0 || pmove->ps->weapon == WP_SABER ))
8186 {
8187 if ( pmove->cmd.buttons & BUTTON_ALT_ATTACK )
8188 {
8189 pmove->ps->eFlags |= EF_ALT_FIRING;
8190 }
8191 else
8192 {
8193 pmove->ps->eFlags &= ~EF_ALT_FIRING;
8194 }
8195
8196 // This flag should always get set, even when alt-firing
8197 pmove->ps->eFlags |= EF_FIRING;
8198 }
8199 else
8200 {
8201 // Clear 'em out
8202 pmove->ps->eFlags &= ~(EF_FIRING|EF_ALT_FIRING);
8203 }
8204
8205 // disruptor should convert a main fire to an alt-fire if the gun is currently zoomed
8206 if ( pmove->ps->weapon == WP_DISRUPTOR)
8207 {
8208 if ( pmove->cmd.buttons & BUTTON_ATTACK && pmove->ps->zoomMode == 1 && pmove->ps->zoomLocked)
8209 {
8210 // converting the main fire to an alt-fire
8211 pmove->cmd.buttons |= BUTTON_ALT_ATTACK;
8212 pmove->ps->eFlags |= EF_ALT_FIRING;
8213 }
8214 else if ( pmove->cmd.buttons & BUTTON_ALT_ATTACK && pmove->ps->zoomMode == 1 && pmove->ps->zoomLocked)
8215 {
8216 pmove->cmd.buttons &= ~BUTTON_ALT_ATTACK;
8217 pmove->ps->eFlags &= ~EF_ALT_FIRING;
8218 }
8219 }
8220 }
8221
BG_CmdForRoll(playerState_t * ps,int anim,usercmd_t * pCmd)8222 void BG_CmdForRoll( playerState_t *ps, int anim, usercmd_t *pCmd )
8223 {
8224 switch ( (anim) )
8225 {
8226 case BOTH_ROLL_F:
8227 pCmd->forwardmove = 127;
8228 pCmd->rightmove = 0;
8229 break;
8230 case BOTH_ROLL_B:
8231 pCmd->forwardmove = -127;
8232 pCmd->rightmove = 0;
8233 break;
8234 case BOTH_ROLL_R:
8235 pCmd->forwardmove = 0;
8236 pCmd->rightmove = 127;
8237 break;
8238 case BOTH_ROLL_L:
8239 pCmd->forwardmove = 0;
8240 pCmd->rightmove = -127;
8241 break;
8242 case BOTH_GETUP_BROLL_R:
8243 pCmd->forwardmove = 0;
8244 pCmd->rightmove = 48;
8245 //NOTE: speed is 400
8246 break;
8247
8248 case BOTH_GETUP_FROLL_R:
8249 if ( ps->legsTimer <= 250 )
8250 {//end of anim
8251 pCmd->forwardmove = pCmd->rightmove = 0;
8252 }
8253 else
8254 {
8255 pCmd->forwardmove = 0;
8256 pCmd->rightmove = 48;
8257 //NOTE: speed is 400
8258 }
8259 break;
8260
8261 case BOTH_GETUP_BROLL_L:
8262 pCmd->forwardmove = 0;
8263 pCmd->rightmove = -48;
8264 //NOTE: speed is 400
8265 break;
8266
8267 case BOTH_GETUP_FROLL_L:
8268 if ( ps->legsTimer <= 250 )
8269 {//end of anim
8270 pCmd->forwardmove = pCmd->rightmove = 0;
8271 }
8272 else
8273 {
8274 pCmd->forwardmove = 0;
8275 pCmd->rightmove = -48;
8276 //NOTE: speed is 400
8277 }
8278 break;
8279
8280 case BOTH_GETUP_BROLL_B:
8281 if ( ps->torsoTimer <= 250 )
8282 {//end of anim
8283 pCmd->forwardmove = pCmd->rightmove = 0;
8284 }
8285 else if ( PM_AnimLength( 0, (animNumber_t)ps->legsAnim ) - ps->torsoTimer < 350 )
8286 {//beginning of anim
8287 pCmd->forwardmove = pCmd->rightmove = 0;
8288 }
8289 else
8290 {
8291 //FIXME: ramp down over length of anim
8292 pCmd->forwardmove = -64;
8293 pCmd->rightmove = 0;
8294 //NOTE: speed is 400
8295 }
8296 break;
8297
8298 case BOTH_GETUP_FROLL_B:
8299 if ( ps->torsoTimer <= 100 )
8300 {//end of anim
8301 pCmd->forwardmove = pCmd->rightmove = 0;
8302 }
8303 else if ( PM_AnimLength( 0, (animNumber_t)ps->legsAnim ) - ps->torsoTimer < 200 )
8304 {//beginning of anim
8305 pCmd->forwardmove = pCmd->rightmove = 0;
8306 }
8307 else
8308 {
8309 //FIXME: ramp down over length of anim
8310 pCmd->forwardmove = -64;
8311 pCmd->rightmove = 0;
8312 //NOTE: speed is 400
8313 }
8314 break;
8315
8316 case BOTH_GETUP_BROLL_F:
8317 if ( ps->torsoTimer <= 550 )
8318 {//end of anim
8319 pCmd->forwardmove = pCmd->rightmove = 0;
8320 }
8321 else if ( PM_AnimLength( 0, (animNumber_t)ps->legsAnim ) - ps->torsoTimer < 150 )
8322 {//beginning of anim
8323 pCmd->forwardmove = pCmd->rightmove = 0;
8324 }
8325 else
8326 {
8327 pCmd->forwardmove = 64;
8328 pCmd->rightmove = 0;
8329 //NOTE: speed is 400
8330 }
8331 break;
8332
8333 case BOTH_GETUP_FROLL_F:
8334 if ( ps->torsoTimer <= 100 )
8335 {//end of anim
8336 pCmd->forwardmove = pCmd->rightmove = 0;
8337 }
8338 else
8339 {
8340 //FIXME: ramp down over length of anim
8341 pCmd->forwardmove = 64;
8342 pCmd->rightmove = 0;
8343 //NOTE: speed is 400
8344 }
8345 break;
8346 }
8347 pCmd->upmove = 0;
8348 }
8349
8350 qboolean PM_SaberInTransition( int move );
8351
BG_AdjustClientSpeed(playerState_t * ps,usercmd_t * cmd,int svTime)8352 void BG_AdjustClientSpeed(playerState_t *ps, usercmd_t *cmd, int svTime)
8353 {
8354 saberInfo_t *saber;
8355
8356 if (ps->clientNum >= MAX_CLIENTS)
8357 {
8358 bgEntity_t *bgEnt = pm_entSelf;
8359
8360 if (bgEnt && bgEnt->s.NPC_class == CLASS_VEHICLE)
8361 { //vehicles manage their own speed
8362 return;
8363 }
8364 }
8365
8366 //For prediction, always reset speed back to the last known server base speed
8367 //If we didn't do this, under lag we'd eventually dwindle speed down to 0 even though
8368 //that would not be the correct predicted value.
8369 ps->speed = ps->basespeed;
8370
8371 if (ps->forceHandExtend == HANDEXTEND_DODGE)
8372 {
8373 ps->speed = 0;
8374 }
8375
8376 if (ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
8377 ps->forceHandExtend == HANDEXTEND_PRETHROWN ||
8378 ps->forceHandExtend == HANDEXTEND_POSTTHROWN)
8379 {
8380 ps->speed = 0;
8381 }
8382
8383
8384 if ( cmd->forwardmove < 0 && !(cmd->buttons&BUTTON_WALKING) && pm->ps->groundEntityNum != ENTITYNUM_NONE )
8385 {//running backwards is slower than running forwards (like SP)
8386 ps->speed *= 0.75f;
8387 }
8388
8389 if (ps->fd.forcePowersActive & (1 << FP_GRIP))
8390 {
8391 ps->speed *= 0.4f;
8392 }
8393
8394 if (ps->fd.forcePowersActive & (1 << FP_SPEED))
8395 {
8396 ps->speed *= 1.7f;
8397 }
8398 else if (ps->fd.forcePowersActive & (1 << FP_RAGE))
8399 {
8400 ps->speed *= 1.3f;
8401 }
8402 else if (ps->fd.forceRageRecoveryTime > svTime)
8403 {
8404 ps->speed *= 0.75f;
8405 }
8406
8407 if (pm->ps->weapon == WP_DISRUPTOR &&
8408 pm->ps->zoomMode == 1 && pm->ps->zoomLockTime < pm->cmd.serverTime)
8409 {
8410 ps->speed *= 0.5f;
8411 }
8412
8413 if ( ps->fd.forceGripCripple && pm->ps->persistant[PERS_TEAM] != TEAM_SPECTATOR ) {
8414 if ( ps->fd.forcePowersActive & (1 << FP_RAGE) )
8415 ps->speed *= 0.9f;
8416 else if ( ps->fd.forcePowersActive & (1 << FP_SPEED) )
8417 ps->speed *= 0.8f;
8418 else
8419 ps->speed *= 0.2f;
8420 }
8421
8422 if ( BG_SaberInAttack( ps->saberMove ) && cmd->forwardmove < 0 )
8423 {//if running backwards while attacking, don't run as fast.
8424 switch( ps->fd.saberAnimLevel )
8425 {
8426 case FORCE_LEVEL_1:
8427 ps->speed *= 0.75f;
8428 break;
8429 case FORCE_LEVEL_2:
8430 case SS_DUAL:
8431 case SS_STAFF:
8432 ps->speed *= 0.60f;
8433 break;
8434 case FORCE_LEVEL_3:
8435 ps->speed *= 0.45f;
8436 break;
8437 default:
8438 break;
8439 }
8440 }
8441 else if ( BG_SpinningSaberAnim( ps->legsAnim ) )
8442 {
8443 if (ps->fd.saberAnimLevel == FORCE_LEVEL_3)
8444 {
8445 ps->speed *= 0.3f;
8446 }
8447 else
8448 {
8449 ps->speed *= 0.5f;
8450 }
8451 }
8452 else if ( ps->weapon == WP_SABER && BG_SaberInAttack( ps->saberMove ) )
8453 {//if attacking with saber while running, drop your speed
8454 switch( ps->fd.saberAnimLevel )
8455 {
8456 case FORCE_LEVEL_2:
8457 case SS_DUAL:
8458 case SS_STAFF:
8459 ps->speed *= 0.85f;
8460 break;
8461 case FORCE_LEVEL_3:
8462 ps->speed *= 0.55f;
8463 break;
8464 default:
8465 break;
8466 }
8467 }
8468 else if (ps->weapon == WP_SABER && ps->fd.saberAnimLevel == FORCE_LEVEL_3 &&
8469 PM_SaberInTransition(ps->saberMove))
8470 { //Now, we want to even slow down in transitions for level 3 (since it has chains and stuff now)
8471 if (cmd->forwardmove < 0)
8472 {
8473 ps->speed *= 0.4f;
8474 }
8475 else
8476 {
8477 ps->speed *= 0.6f;
8478 }
8479 }
8480
8481 if ( BG_InRoll( ps, ps->legsAnim ) && ps->speed > 50 )
8482 { //can't roll unless you're able to move normally
8483 if ((ps->legsAnim) == BOTH_ROLL_B)
8484 { //backwards roll is pretty fast, should also be slower
8485 if (ps->legsTimer > 800)
8486 {
8487 ps->speed = ps->legsTimer/2.5;
8488 }
8489 else
8490 {
8491 ps->speed = ps->legsTimer/6.0;//450;
8492 }
8493 }
8494 else
8495 {
8496 if (ps->legsTimer > 800)
8497 {
8498 ps->speed = ps->legsTimer/1.5;//450;
8499 }
8500 else
8501 {
8502 ps->speed = ps->legsTimer/5.0;//450;
8503 }
8504 }
8505 if (ps->speed > 600)
8506 {
8507 ps->speed = 600;
8508 }
8509 //Automatically slow down as the roll ends.
8510 }
8511
8512 saber = BG_MySaber( ps->clientNum, 0 );
8513 if ( saber
8514 && saber->moveSpeedScale != 1.0f )
8515 {
8516 ps->speed *= saber->moveSpeedScale;
8517 }
8518 saber = BG_MySaber( ps->clientNum, 1 );
8519 if ( saber
8520 && saber->moveSpeedScale != 1.0f )
8521 {
8522 ps->speed *= saber->moveSpeedScale;
8523 }
8524 }
8525
BG_InRollAnim(entityState_t * cent)8526 qboolean BG_InRollAnim( entityState_t *cent )
8527 {
8528 switch ( (cent->legsAnim) )
8529 {
8530 case BOTH_ROLL_F:
8531 case BOTH_ROLL_B:
8532 case BOTH_ROLL_R:
8533 case BOTH_ROLL_L:
8534 return qtrue;
8535 }
8536 return qfalse;
8537 }
8538
BG_InKnockDown(int anim)8539 qboolean BG_InKnockDown( int anim )
8540 {
8541 switch ( (anim) )
8542 {
8543 case BOTH_KNOCKDOWN1:
8544 case BOTH_KNOCKDOWN2:
8545 case BOTH_KNOCKDOWN3:
8546 case BOTH_KNOCKDOWN4:
8547 case BOTH_KNOCKDOWN5:
8548 return qtrue;
8549 break;
8550 case BOTH_GETUP1:
8551 case BOTH_GETUP2:
8552 case BOTH_GETUP3:
8553 case BOTH_GETUP4:
8554 case BOTH_GETUP5:
8555 case BOTH_FORCE_GETUP_F1:
8556 case BOTH_FORCE_GETUP_F2:
8557 case BOTH_FORCE_GETUP_B1:
8558 case BOTH_FORCE_GETUP_B2:
8559 case BOTH_FORCE_GETUP_B3:
8560 case BOTH_FORCE_GETUP_B4:
8561 case BOTH_FORCE_GETUP_B5:
8562 case BOTH_GETUP_BROLL_B:
8563 case BOTH_GETUP_BROLL_F:
8564 case BOTH_GETUP_BROLL_L:
8565 case BOTH_GETUP_BROLL_R:
8566 case BOTH_GETUP_FROLL_B:
8567 case BOTH_GETUP_FROLL_F:
8568 case BOTH_GETUP_FROLL_L:
8569 case BOTH_GETUP_FROLL_R:
8570 return qtrue;
8571 break;
8572 }
8573 return qfalse;
8574 }
8575
BG_InRollES(entityState_t * ps,int anim)8576 qboolean BG_InRollES( entityState_t *ps, int anim )
8577 {
8578 switch ( (anim) )
8579 {
8580 case BOTH_ROLL_F:
8581 case BOTH_ROLL_B:
8582 case BOTH_ROLL_R:
8583 case BOTH_ROLL_L:
8584 return qtrue;
8585 break;
8586 }
8587 return qfalse;
8588 }
8589
BG_IK_MoveArm(void * ghoul2,int lHandBolt,int time,entityState_t * ent,int basePose,vec3_t desiredPos,qboolean * ikInProgress,vec3_t origin,vec3_t angles,vec3_t scale,int blendTime,qboolean forceHalt)8590 void BG_IK_MoveArm(void *ghoul2, int lHandBolt, int time, entityState_t *ent, int basePose, vec3_t desiredPos, qboolean *ikInProgress,
8591 vec3_t origin, vec3_t angles, vec3_t scale, int blendTime, qboolean forceHalt)
8592 {
8593 mdxaBone_t lHandMatrix;
8594 vec3_t lHand;
8595 vec3_t torg;
8596 float distToDest;
8597
8598 if (!ghoul2)
8599 {
8600 return;
8601 }
8602
8603 assert(bgHumanoidAnimations[basePose].firstFrame > 0);
8604
8605 if (!*ikInProgress && !forceHalt)
8606 {
8607 int baseposeAnim = basePose;
8608 sharedSetBoneIKStateParams_t ikP;
8609
8610 //restrict the shoulder joint
8611 //VectorSet(ikP.pcjMins,-50.0f,-80.0f,-15.0f);
8612 //VectorSet(ikP.pcjMaxs,15.0f,40.0f,15.0f);
8613
8614 //for now, leaving it unrestricted, but restricting elbow joint.
8615 //This lets us break the arm however we want in order to fling people
8616 //in throws, and doesn't look bad.
8617 VectorSet(ikP.pcjMins,0,0,0);
8618 VectorSet(ikP.pcjMaxs,0,0,0);
8619
8620 //give the info on our entity.
8621 ikP.blendTime = blendTime;
8622 VectorCopy(origin, ikP.origin);
8623 VectorCopy(angles, ikP.angles);
8624 ikP.angles[PITCH] = 0;
8625 ikP.pcjOverrides = 0;
8626 ikP.radius = 10.0f;
8627 VectorCopy(scale, ikP.scale);
8628
8629 //base pose frames for the limb
8630 ikP.startFrame = bgHumanoidAnimations[baseposeAnim].firstFrame + bgHumanoidAnimations[baseposeAnim].numFrames;
8631 ikP.endFrame = bgHumanoidAnimations[baseposeAnim].firstFrame + bgHumanoidAnimations[baseposeAnim].numFrames;
8632
8633 ikP.forceAnimOnBone = qfalse; //let it use existing anim if it's the same as this one.
8634
8635 //we want to call with a null bone name first. This will init all of the
8636 //ik system stuff on the g2 instance, because we need ragdoll effectors
8637 //in order for our pcj's to know how to angle properly.
8638 if (!trap->G2API_SetBoneIKState(ghoul2, time, NULL, IKS_DYNAMIC, &ikP))
8639 {
8640 assert(!"Failed to init IK system for g2 instance!");
8641 }
8642
8643 //Now, create our IK bone state.
8644 if (trap->G2API_SetBoneIKState(ghoul2, time, "lhumerus", IKS_DYNAMIC, &ikP))
8645 {
8646 //restrict the elbow joint
8647 VectorSet(ikP.pcjMins,-90.0f,-20.0f,-20.0f);
8648 VectorSet(ikP.pcjMaxs,30.0f,20.0f,-20.0f);
8649
8650 if (trap->G2API_SetBoneIKState(ghoul2, time, "lradius", IKS_DYNAMIC, &ikP))
8651 { //everything went alright.
8652 *ikInProgress = qtrue;
8653 }
8654 }
8655 }
8656
8657 if (*ikInProgress && !forceHalt)
8658 { //actively update our ik state.
8659 sharedIKMoveParams_t ikM;
8660 sharedRagDollUpdateParams_t tuParms;
8661 vec3_t tAngles;
8662
8663 //set the argument struct up
8664 VectorCopy(desiredPos, ikM.desiredOrigin); //we want the bone to move here.. if possible
8665
8666 VectorCopy(angles, tAngles);
8667 tAngles[PITCH] = tAngles[ROLL] = 0;
8668
8669 trap->G2API_GetBoltMatrix(ghoul2, 0, lHandBolt, &lHandMatrix, tAngles, origin, time, 0, scale);
8670
8671 //Get the point position from the matrix.
8672 lHand[0] = lHandMatrix.matrix[0][3];
8673 lHand[1] = lHandMatrix.matrix[1][3];
8674 lHand[2] = lHandMatrix.matrix[2][3];
8675
8676 VectorSubtract(lHand, desiredPos, torg);
8677 distToDest = VectorLength(torg);
8678
8679 //closer we are, more we want to keep updated.
8680 //if we're far away we don't want to be too fast or we'll start twitching all over.
8681 if (distToDest < 2)
8682 { //however if we're this close we want very precise movement
8683 ikM.movementSpeed = 0.4f;
8684 }
8685 else if (distToDest < 16)
8686 {
8687 ikM.movementSpeed = 0.9f;//8.0f;
8688 }
8689 else if (distToDest < 32)
8690 {
8691 ikM.movementSpeed = 0.8f;//4.0f;
8692 }
8693 else if (distToDest < 64)
8694 {
8695 ikM.movementSpeed = 0.7f;//2.0f;
8696 }
8697 else
8698 {
8699 ikM.movementSpeed = 0.6f;
8700 }
8701 VectorCopy(origin, ikM.origin); //our position in the world.
8702
8703 ikM.boneName[0] = 0;
8704 if (trap->G2API_IKMove(ghoul2, time, &ikM))
8705 {
8706 //now do the standard model animate stuff with ragdoll update params.
8707 VectorCopy(angles, tuParms.angles);
8708 tuParms.angles[PITCH] = 0;
8709
8710 VectorCopy(origin, tuParms.position);
8711 VectorCopy(scale, tuParms.scale);
8712
8713 tuParms.me = ent->number;
8714 VectorClear(tuParms.velocity);
8715
8716 trap->G2API_AnimateG2Models(ghoul2, time, &tuParms);
8717 }
8718 else
8719 {
8720 *ikInProgress = qfalse;
8721 }
8722 }
8723 else if (*ikInProgress)
8724 { //kill it
8725 float cFrame, animSpeed;
8726 int sFrame, eFrame, flags;
8727
8728 trap->G2API_SetBoneIKState(ghoul2, time, "lhumerus", IKS_NONE, NULL);
8729 trap->G2API_SetBoneIKState(ghoul2, time, "lradius", IKS_NONE, NULL);
8730
8731 //then reset the angles/anims on these PCJs
8732 trap->G2API_SetBoneAngles(ghoul2, 0, "lhumerus", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, time);
8733 trap->G2API_SetBoneAngles(ghoul2, 0, "lradius", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, time);
8734
8735 //Get the anim/frames that the pelvis is on exactly, and match the left arm back up with them again.
8736 trap->G2API_GetBoneAnim(ghoul2, "pelvis", (const int)time, &cFrame, &sFrame, &eFrame, &flags, &animSpeed, 0, 0);
8737 trap->G2API_SetBoneAnim(ghoul2, 0, "lhumerus", sFrame, eFrame, flags, animSpeed, time, sFrame, 300);
8738 trap->G2API_SetBoneAnim(ghoul2, 0, "lradius", sFrame, eFrame, flags, animSpeed, time, sFrame, 300);
8739
8740 //And finally, get rid of all the ik state effector data by calling with null bone name (similar to how we init it).
8741 trap->G2API_SetBoneIKState(ghoul2, time, NULL, IKS_NONE, NULL);
8742
8743 *ikInProgress = qfalse;
8744 }
8745 }
8746
8747 //Adjust the head/neck desired angles
BG_UpdateLookAngles(int lookingDebounceTime,vec3_t lastHeadAngles,int time,vec3_t lookAngles,float lookSpeed,float minPitch,float maxPitch,float minYaw,float maxYaw,float minRoll,float maxRoll)8748 void BG_UpdateLookAngles( int lookingDebounceTime, vec3_t lastHeadAngles, int time, vec3_t lookAngles, float lookSpeed, float minPitch, float maxPitch, float minYaw, float maxYaw, float minRoll, float maxRoll )
8749 {
8750 static const float fFrameInter = 0.1f;
8751 static vec3_t oldLookAngles;
8752 static vec3_t lookAnglesDiff;
8753 static int ang;
8754
8755 if ( lookingDebounceTime > time )
8756 {
8757 //clamp so don't get "Exorcist" effect
8758 if ( lookAngles[PITCH] > maxPitch )
8759 {
8760 lookAngles[PITCH] = maxPitch;
8761 }
8762 else if ( lookAngles[PITCH] < minPitch )
8763 {
8764 lookAngles[PITCH] = minPitch;
8765 }
8766 if ( lookAngles[YAW] > maxYaw )
8767 {
8768 lookAngles[YAW] = maxYaw;
8769 }
8770 else if ( lookAngles[YAW] < minYaw )
8771 {
8772 lookAngles[YAW] = minYaw;
8773 }
8774 if ( lookAngles[ROLL] > maxRoll )
8775 {
8776 lookAngles[ROLL] = maxRoll;
8777 }
8778 else if ( lookAngles[ROLL] < minRoll )
8779 {
8780 lookAngles[ROLL] = minRoll;
8781 }
8782
8783 //slowly lerp to this new value
8784 //Remember last headAngles
8785 VectorCopy( lastHeadAngles, oldLookAngles );
8786 VectorSubtract( lookAngles, oldLookAngles, lookAnglesDiff );
8787
8788 for ( ang = 0; ang < 3; ang++ )
8789 {
8790 lookAnglesDiff[ang] = AngleNormalize180( lookAnglesDiff[ang] );
8791 }
8792
8793 if( VectorLengthSquared( lookAnglesDiff ) )
8794 {
8795 lookAngles[PITCH] = AngleNormalize180( oldLookAngles[PITCH]+(lookAnglesDiff[PITCH]*fFrameInter*lookSpeed) );
8796 lookAngles[YAW] = AngleNormalize180( oldLookAngles[YAW]+(lookAnglesDiff[YAW]*fFrameInter*lookSpeed) );
8797 lookAngles[ROLL] = AngleNormalize180( oldLookAngles[ROLL]+(lookAnglesDiff[ROLL]*fFrameInter*lookSpeed) );
8798 }
8799 }
8800 //Remember current lookAngles next time
8801 VectorCopy( lookAngles, lastHeadAngles );
8802 }
8803
8804 //for setting visual look (headturn) angles
BG_G2ClientNeckAngles(void * ghoul2,int time,const vec3_t lookAngles,vec3_t headAngles,vec3_t neckAngles,vec3_t thoracicAngles,vec3_t headClampMinAngles,vec3_t headClampMaxAngles)8805 static void BG_G2ClientNeckAngles( void *ghoul2, int time, const vec3_t lookAngles, vec3_t headAngles, vec3_t neckAngles, vec3_t thoracicAngles, vec3_t headClampMinAngles, vec3_t headClampMaxAngles )
8806 {
8807 vec3_t lA;
8808 VectorCopy( lookAngles, lA );
8809 //clamp the headangles (which should now be relative to the cervical (neck) angles
8810 if ( lA[PITCH] < headClampMinAngles[PITCH] )
8811 {
8812 lA[PITCH] = headClampMinAngles[PITCH];
8813 }
8814 else if ( lA[PITCH] > headClampMaxAngles[PITCH] )
8815 {
8816 lA[PITCH] = headClampMaxAngles[PITCH];
8817 }
8818
8819 if ( lA[YAW] < headClampMinAngles[YAW] )
8820 {
8821 lA[YAW] = headClampMinAngles[YAW];
8822 }
8823 else if ( lA[YAW] > headClampMaxAngles[YAW] )
8824 {
8825 lA[YAW] = headClampMaxAngles[YAW];
8826 }
8827
8828 if ( lA[ROLL] < headClampMinAngles[ROLL] )
8829 {
8830 lA[ROLL] = headClampMinAngles[ROLL];
8831 }
8832 else if ( lA[ROLL] > headClampMaxAngles[ROLL] )
8833 {
8834 lA[ROLL] = headClampMaxAngles[ROLL];
8835 }
8836
8837 //split it up between the neck and cranium
8838 if ( thoracicAngles[PITCH] )
8839 {//already been set above, blend them
8840 thoracicAngles[PITCH] = (thoracicAngles[PITCH] + (lA[PITCH] * 0.4)) * 0.5f;
8841 }
8842 else
8843 {
8844 thoracicAngles[PITCH] = lA[PITCH] * 0.4;
8845 }
8846 if ( thoracicAngles[YAW] )
8847 {//already been set above, blend them
8848 thoracicAngles[YAW] = (thoracicAngles[YAW] + (lA[YAW] * 0.1)) * 0.5f;
8849 }
8850 else
8851 {
8852 thoracicAngles[YAW] = lA[YAW] * 0.1;
8853 }
8854 if ( thoracicAngles[ROLL] )
8855 {//already been set above, blend them
8856 thoracicAngles[ROLL] = (thoracicAngles[ROLL] + (lA[ROLL] * 0.1)) * 0.5f;
8857 }
8858 else
8859 {
8860 thoracicAngles[ROLL] = lA[ROLL] * 0.1;
8861 }
8862
8863 neckAngles[PITCH] = lA[PITCH] * 0.2f;
8864 neckAngles[YAW] = lA[YAW] * 0.3f;
8865 neckAngles[ROLL] = lA[ROLL] * 0.3f;
8866
8867 headAngles[PITCH] = lA[PITCH] * 0.4;
8868 headAngles[YAW] = lA[YAW] * 0.6;
8869 headAngles[ROLL] = lA[ROLL] * 0.6;
8870
8871 /* //non-applicable SP code
8872 if ( G_RidingVehicle( cent->gent ) )// && type == VH_SPEEDER ?
8873 {//aim torso forward too
8874 headAngles[YAW] = neckAngles[YAW] = thoracicAngles[YAW] = 0;
8875 }
8876 */
8877
8878 trap->G2API_SetBoneAngles(ghoul2, 0, "cranium", headAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
8879 trap->G2API_SetBoneAngles(ghoul2, 0, "cervical", neckAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
8880 trap->G2API_SetBoneAngles(ghoul2, 0, "thoracic", thoracicAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
8881 }
8882
8883 //rww - Finally decided to convert all this stuff to BG form.
BG_G2ClientSpineAngles(void * ghoul2,int motionBolt,vec3_t cent_lerpOrigin,vec3_t cent_lerpAngles,entityState_t * cent,int time,vec3_t viewAngles,int ciLegs,int ciTorso,const vec3_t angles,vec3_t thoracicAngles,vec3_t ulAngles,vec3_t llAngles,vec3_t modelScale,float * tPitchAngle,float * tYawAngle,int * corrTime)8884 static void BG_G2ClientSpineAngles( void *ghoul2, int motionBolt, vec3_t cent_lerpOrigin, vec3_t cent_lerpAngles, entityState_t *cent,
8885 int time, vec3_t viewAngles, int ciLegs, int ciTorso, const vec3_t angles, vec3_t thoracicAngles,
8886 vec3_t ulAngles, vec3_t llAngles, vec3_t modelScale, float *tPitchAngle, float *tYawAngle, int *corrTime )
8887 {
8888 qboolean doCorr = qfalse;
8889
8890 //*tPitchAngle = viewAngles[PITCH];
8891 viewAngles[YAW] = AngleDelta( cent_lerpAngles[YAW], angles[YAW] );
8892 //*tYawAngle = viewAngles[YAW];
8893
8894 #if 1
8895 if ( !BG_FlippingAnim( cent->legsAnim ) &&
8896 !BG_SpinningSaberAnim( cent->legsAnim ) &&
8897 !BG_SpinningSaberAnim( cent->torsoAnim ) &&
8898 !BG_InSpecialJump( cent->legsAnim ) &&
8899 !BG_InSpecialJump( cent->torsoAnim ) &&
8900 !BG_InDeathAnim(cent->legsAnim) &&
8901 !BG_InDeathAnim(cent->torsoAnim) &&
8902 !BG_InRollES(cent, cent->legsAnim) &&
8903 !BG_InRollAnim(cent) &&
8904 !BG_SaberInSpecial(cent->saberMove) &&
8905 !BG_SaberInSpecialAttack(cent->torsoAnim) &&
8906 !BG_SaberInSpecialAttack(cent->legsAnim) &&
8907
8908 !BG_InKnockDown(cent->torsoAnim) &&
8909 !BG_InKnockDown(cent->legsAnim) &&
8910 !BG_InKnockDown(ciTorso) &&
8911 !BG_InKnockDown(ciLegs) &&
8912
8913 !BG_FlippingAnim( ciLegs ) &&
8914 !BG_SpinningSaberAnim( ciLegs ) &&
8915 !BG_SpinningSaberAnim( ciTorso ) &&
8916 !BG_InSpecialJump( ciLegs ) &&
8917 !BG_InSpecialJump( ciTorso ) &&
8918 !BG_InDeathAnim(ciLegs) &&
8919 !BG_InDeathAnim(ciTorso) &&
8920 !BG_SaberInSpecialAttack(ciTorso) &&
8921 !BG_SaberInSpecialAttack(ciLegs) &&
8922
8923 !(cent->eFlags & EF_DEAD) &&
8924 (cent->legsAnim) != (cent->torsoAnim) &&
8925 (ciLegs) != (ciTorso) &&
8926 !cent->m_iVehicleNum)
8927 {
8928 doCorr = qtrue;
8929 }
8930 #else
8931 if ( ((!BG_FlippingAnim( cent->legsAnim )
8932 && !BG_SpinningSaberAnim( cent->legsAnim )
8933 && !BG_SpinningSaberAnim( cent->torsoAnim )
8934 && (cent->legsAnim) != (cent->torsoAnim)) //NOTE: presumes your legs & torso are on the same frame, though they *should* be because PM_SetAnimFinal tries to keep them in synch
8935 ||
8936 (!BG_FlippingAnim( ciLegs )
8937 && !BG_SpinningSaberAnim( ciLegs )
8938 && !BG_SpinningSaberAnim( ciTorso )
8939 && (ciLegs) != (ciTorso)))
8940 ||
8941 ciLegs != cent->legsAnim
8942 ||
8943 ciTorso != cent->torsoAnim)
8944 {
8945 doCorr = qtrue;
8946 *corrTime = time + 1000; //continue correcting for a second after to smooth things out. SP doesn't need this for whatever reason but I can't find a way around it.
8947 }
8948 else if (*corrTime >= time)
8949 {
8950 if (!BG_FlippingAnim( cent->legsAnim )
8951 && !BG_SpinningSaberAnim( cent->legsAnim )
8952 && !BG_SpinningSaberAnim( cent->torsoAnim )
8953 && !BG_FlippingAnim( ciLegs )
8954 && !BG_SpinningSaberAnim( ciLegs )
8955 && !BG_SpinningSaberAnim( ciTorso ))
8956 {
8957 doCorr = qtrue;
8958 }
8959 }
8960 #endif
8961
8962 if (doCorr)
8963 {//FIXME: no need to do this if legs and torso on are same frame
8964 //adjust for motion offset
8965 mdxaBone_t boltMatrix;
8966 vec3_t motionFwd, motionAngles;
8967 vec3_t motionRt, tempAng;
8968 int ang;
8969
8970 trap->G2API_GetBoltMatrix_NoRecNoRot( ghoul2, 0, motionBolt, &boltMatrix, vec3_origin, cent_lerpOrigin, time, 0, modelScale);
8971 //BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, motionFwd );
8972 motionFwd[0] = -boltMatrix.matrix[0][1];
8973 motionFwd[1] = -boltMatrix.matrix[1][1];
8974 motionFwd[2] = -boltMatrix.matrix[2][1];
8975
8976 vectoangles( motionFwd, motionAngles );
8977
8978 //BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_X, motionRt );
8979 motionRt[0] = -boltMatrix.matrix[0][0];
8980 motionRt[1] = -boltMatrix.matrix[1][0];
8981 motionRt[2] = -boltMatrix.matrix[2][0];
8982
8983 vectoangles( motionRt, tempAng );
8984 motionAngles[ROLL] = -tempAng[PITCH];
8985
8986 for ( ang = 0; ang < 3; ang++ )
8987 {
8988 viewAngles[ang] = AngleNormalize180( viewAngles[ang] - AngleNormalize180( motionAngles[ang] ) );
8989 }
8990 }
8991
8992 //distribute the angles differently up the spine
8993 //NOTE: each of these distributions must add up to 1.0f
8994 thoracicAngles[PITCH] = viewAngles[PITCH]*0.20f;
8995 llAngles[PITCH] = viewAngles[PITCH]*0.40f;
8996 ulAngles[PITCH] = viewAngles[PITCH]*0.40f;
8997
8998 thoracicAngles[YAW] = viewAngles[YAW]*0.20f;
8999 ulAngles[YAW] = viewAngles[YAW]*0.35f;
9000 llAngles[YAW] = viewAngles[YAW]*0.45f;
9001
9002 thoracicAngles[ROLL] = viewAngles[ROLL]*0.20f;
9003 ulAngles[ROLL] = viewAngles[ROLL]*0.35f;
9004 llAngles[ROLL] = viewAngles[ROLL]*0.45f;
9005 }
9006
9007 /*
9008 ==================
9009 CG_SwingAngles
9010 ==================
9011 */
BG_SwingAngles(float destination,float swingTolerance,float clampTolerance,float speed,float * angle,qboolean * swinging,int frametime)9012 static float BG_SwingAngles( float destination, float swingTolerance, float clampTolerance,
9013 float speed, float *angle, qboolean *swinging, int frametime ) {
9014 float swing;
9015 float move;
9016 float scale;
9017
9018 if ( !*swinging ) {
9019 // see if a swing should be started
9020 swing = AngleSubtract( *angle, destination );
9021 if ( swing > swingTolerance || swing < -swingTolerance ) {
9022 *swinging = qtrue;
9023 }
9024 }
9025
9026 if ( !*swinging ) {
9027 return 0;
9028 }
9029
9030 // modify the speed depending on the delta
9031 // so it doesn't seem so linear
9032 swing = AngleSubtract( destination, *angle );
9033 scale = fabs( swing );
9034 if ( scale < swingTolerance * 0.5 ) {
9035 scale = 0.5;
9036 } else if ( scale < swingTolerance ) {
9037 scale = 1.0;
9038 } else {
9039 scale = 2.0;
9040 }
9041
9042 // swing towards the destination angle
9043 if ( swing >= 0 ) {
9044 move = frametime * scale * speed;
9045 if ( move >= swing ) {
9046 move = swing;
9047 *swinging = qfalse;
9048 }
9049 *angle = AngleMod( *angle + move );
9050 } else if ( swing < 0 ) {
9051 move = frametime * scale * -speed;
9052 if ( move <= swing ) {
9053 move = swing;
9054 *swinging = qfalse;
9055 }
9056 *angle = AngleMod( *angle + move );
9057 }
9058
9059 // clamp to no more than tolerance
9060 swing = AngleSubtract( destination, *angle );
9061 if ( swing > clampTolerance ) {
9062 *angle = AngleMod( destination - (clampTolerance - 1) );
9063 } else if ( swing < -clampTolerance ) {
9064 *angle = AngleMod( destination + (clampTolerance - 1) );
9065 }
9066
9067 return swing;
9068 }
9069
9070 //#define BONE_BASED_LEG_ANGLES
9071
9072 //I apologize for this function
BG_InRoll2(entityState_t * es)9073 qboolean BG_InRoll2( entityState_t *es )
9074 {
9075 switch ( (es->legsAnim) )
9076 {
9077 case BOTH_GETUP_BROLL_B:
9078 case BOTH_GETUP_BROLL_F:
9079 case BOTH_GETUP_BROLL_L:
9080 case BOTH_GETUP_BROLL_R:
9081 case BOTH_GETUP_FROLL_B:
9082 case BOTH_GETUP_FROLL_F:
9083 case BOTH_GETUP_FROLL_L:
9084 case BOTH_GETUP_FROLL_R:
9085 case BOTH_ROLL_F:
9086 case BOTH_ROLL_B:
9087 case BOTH_ROLL_R:
9088 case BOTH_ROLL_L:
9089 return qtrue;
9090 break;
9091 }
9092 return qfalse;
9093 }
9094
9095
9096 extern qboolean BG_SaberLockBreakAnim( int anim ); //bg_panimate.c
BG_G2PlayerAngles(void * ghoul2,int motionBolt,entityState_t * cent,int time,vec3_t cent_lerpOrigin,vec3_t cent_lerpAngles,matrix3_t legs,vec3_t legsAngles,qboolean * tYawing,qboolean * tPitching,qboolean * lYawing,float * tYawAngle,float * tPitchAngle,float * lYawAngle,int frametime,vec3_t turAngles,vec3_t modelScale,int ciLegs,int ciTorso,int * corrTime,vec3_t lookAngles,vec3_t lastHeadAngles,int lookTime,entityState_t * emplaced,int * crazySmoothFactor)9097 void BG_G2PlayerAngles(void *ghoul2, int motionBolt, entityState_t *cent, int time, vec3_t cent_lerpOrigin,
9098 vec3_t cent_lerpAngles, matrix3_t legs, vec3_t legsAngles, qboolean *tYawing,
9099 qboolean *tPitching, qboolean *lYawing, float *tYawAngle, float *tPitchAngle,
9100 float *lYawAngle, int frametime, vec3_t turAngles, vec3_t modelScale, int ciLegs,
9101 int ciTorso, int *corrTime, vec3_t lookAngles, vec3_t lastHeadAngles, int lookTime,
9102 entityState_t *emplaced, int *crazySmoothFactor)
9103 {
9104 int adddir = 0;
9105 static int dir;
9106 static int i;
9107 // static int movementOffsets[8] = { 0, 22, 45, -22, 0, 22, -45, -22 };
9108 float degrees_negative = 0;
9109 float degrees_positive = 0;
9110 static float dif;
9111 static float dest;
9112 static float speed; //, speed_dif, speed_desired;
9113 static const float lookSpeed = 1.5f;
9114 #ifdef BONE_BASED_LEG_ANGLES
9115 static float legBoneYaw;
9116 #endif
9117 static vec3_t eyeAngles;
9118 static vec3_t neckAngles;
9119 static vec3_t velocity;
9120 static vec3_t torsoAngles, headAngles;
9121 static vec3_t velPos, velAng;
9122 static vec3_t ulAngles, llAngles, viewAngles, angles, thoracicAngles = {0,0,0};
9123 static vec3_t headClampMinAngles = {-25,-55,-10}, headClampMaxAngles = {50,50,10};
9124
9125 if ( cent->m_iVehicleNum || cent->forceFrame || BG_SaberLockBreakAnim(cent->legsAnim) || BG_SaberLockBreakAnim(cent->torsoAnim) )
9126 { //a vehicle or riding a vehicle - in either case we don't need to be in here
9127 vec3_t forcedAngles;
9128
9129 VectorClear(forcedAngles);
9130 forcedAngles[YAW] = cent_lerpAngles[YAW];
9131 forcedAngles[ROLL] = cent_lerpAngles[ROLL];
9132 AnglesToAxis( forcedAngles, legs );
9133 VectorCopy(forcedAngles, legsAngles);
9134 VectorCopy(legsAngles, turAngles);
9135
9136 if (cent->number < MAX_CLIENTS)
9137 {
9138 trap->G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9139 trap->G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9140 trap->G2API_SetBoneAngles(ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9141 trap->G2API_SetBoneAngles(ghoul2, 0, "thoracic", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9142 trap->G2API_SetBoneAngles(ghoul2, 0, "cervical", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9143 }
9144 return;
9145 }
9146
9147 if ((time+2000) < *corrTime)
9148 {
9149 *corrTime = 0;
9150 }
9151
9152 VectorCopy( cent_lerpAngles, headAngles );
9153 headAngles[YAW] = AngleMod( headAngles[YAW] );
9154 VectorClear( legsAngles );
9155 VectorClear( torsoAngles );
9156 // --------- yaw -------------
9157
9158 // allow yaw to drift a bit
9159 if ((( cent->legsAnim ) != BOTH_STAND1) ||
9160 ( cent->torsoAnim ) != WeaponReadyAnim[cent->weapon] )
9161 {
9162 // if not standing still, always point all in the same direction
9163 //cent->pe.torso.yawing = qtrue; // always center
9164 *tYawing = qtrue;
9165 //cent->pe.torso.pitching = qtrue; // always center
9166 *tPitching = qtrue;
9167 //cent->pe.legs.yawing = qtrue; // always center
9168 *lYawing = qtrue;
9169 }
9170
9171 // adjust legs for movement dir
9172 if ( cent->eFlags & EF_DEAD ) {
9173 // don't let dead bodies twitch
9174 dir = 0;
9175 } else {
9176 dir = cent->angles2[YAW];
9177 if ( dir < 0 || dir > 7 ) {
9178 Com_Error( ERR_DROP, "Bad player movement angle (%i)", dir );
9179 }
9180 }
9181
9182 torsoAngles[YAW] = headAngles[YAW];
9183
9184 //for now, turn torso instantly and let the legs swing to follow
9185 *tYawAngle = torsoAngles[YAW];
9186
9187 // --------- pitch -------------
9188
9189 VectorCopy( cent->pos.trDelta, velocity );
9190
9191 if (BG_InRoll2(cent))
9192 { //don't affect angles based on vel then
9193 VectorClear(velocity);
9194 }
9195 else if (cent->weapon == WP_SABER &&
9196 BG_SaberInSpecial(cent->saberMove))
9197 {
9198 VectorClear(velocity);
9199 }
9200
9201 speed = VectorNormalize( velocity );
9202
9203 if (!speed)
9204 {
9205 torsoAngles[YAW] = headAngles[YAW];
9206 }
9207
9208 // only show a fraction of the pitch angle in the torso
9209 if ( headAngles[PITCH] > 180 ) {
9210 dest = (-360 + headAngles[PITCH]) * 0.75;
9211 } else {
9212 dest = headAngles[PITCH] * 0.75;
9213 }
9214
9215 if (cent->m_iVehicleNum)
9216 { //swing instantly on vehicles
9217 *tPitchAngle = dest;
9218 }
9219 else
9220 {
9221 BG_SwingAngles( dest, 15, 30, 0.1f, tPitchAngle, tPitching, frametime );
9222 }
9223 torsoAngles[PITCH] = *tPitchAngle;
9224
9225 // --------- roll -------------
9226
9227 if ( speed ) {
9228 matrix3_t axis;
9229 float side;
9230
9231 speed *= 0.05f;
9232
9233 AnglesToAxis( legsAngles, axis );
9234 side = speed * DotProduct( velocity, axis[1] );
9235 legsAngles[ROLL] -= side;
9236
9237 side = speed * DotProduct( velocity, axis[0] );
9238 legsAngles[PITCH] += side;
9239 }
9240
9241 //legsAngles[YAW] = headAngles[YAW] + (movementOffsets[ dir ]*speed_dif);
9242
9243 //rww - crazy velocity-based leg angle calculation
9244 legsAngles[YAW] = headAngles[YAW];
9245 velPos[0] = cent_lerpOrigin[0] + velocity[0];
9246 velPos[1] = cent_lerpOrigin[1] + velocity[1];
9247 velPos[2] = cent_lerpOrigin[2];// + velocity[2];
9248
9249 if ( cent->groundEntityNum == ENTITYNUM_NONE ||
9250 cent->forceFrame ||
9251 (cent->weapon == WP_EMPLACED_GUN && emplaced) )
9252 { //off the ground, no direction-based leg angles (same if in saberlock)
9253 VectorCopy(cent_lerpOrigin, velPos);
9254 }
9255
9256 VectorSubtract(cent_lerpOrigin, velPos, velAng);
9257
9258 if (!VectorCompare(velAng, vec3_origin))
9259 {
9260 vectoangles(velAng, velAng);
9261
9262 if (velAng[YAW] <= legsAngles[YAW])
9263 {
9264 degrees_negative = (legsAngles[YAW] - velAng[YAW]);
9265 degrees_positive = (360 - legsAngles[YAW]) + velAng[YAW];
9266 }
9267 else
9268 {
9269 degrees_negative = legsAngles[YAW] + (360 - velAng[YAW]);
9270 degrees_positive = (velAng[YAW] - legsAngles[YAW]);
9271 }
9272
9273 if ( degrees_negative < degrees_positive )
9274 {
9275 dif = degrees_negative;
9276 adddir = 0;
9277 }
9278 else
9279 {
9280 dif = degrees_positive;
9281 adddir = 1;
9282 }
9283
9284 if (dif > 90)
9285 {
9286 dif = (180 - dif);
9287 }
9288
9289 if (dif > 60)
9290 {
9291 dif = 60;
9292 }
9293
9294 //Slight hack for when playing is running backward
9295 if (dir == 3 || dir == 5)
9296 {
9297 dif = -dif;
9298 }
9299
9300 if (adddir)
9301 {
9302 legsAngles[YAW] -= dif;
9303 }
9304 else
9305 {
9306 legsAngles[YAW] += dif;
9307 }
9308 }
9309
9310 if (cent->m_iVehicleNum)
9311 { //swing instantly on vehicles
9312 *lYawAngle = legsAngles[YAW];
9313 }
9314 else
9315 {
9316 BG_SwingAngles( legsAngles[YAW], /*40*/0, 90, 0.65f, lYawAngle, lYawing, frametime );
9317 }
9318 legsAngles[YAW] = *lYawAngle;
9319
9320 /*
9321 // pain twitch
9322 CG_AddPainTwitch( cent, torsoAngles );
9323 */
9324
9325 legsAngles[ROLL] = 0;
9326 torsoAngles[ROLL] = 0;
9327
9328 // VectorCopy(legsAngles, turAngles);
9329
9330 // pull the angles back out of the hierarchial chain
9331 AnglesSubtract( headAngles, torsoAngles, headAngles );
9332 AnglesSubtract( torsoAngles, legsAngles, torsoAngles );
9333
9334 legsAngles[PITCH] = 0;
9335
9336 if (cent->heldByClient)
9337 { //keep the base angles clear when doing the IK stuff, it doesn't compensate for it.
9338 //rwwFIXMEFIXME: Store leg angles off and add them to all the fed in angles for G2 functions?
9339 VectorClear(legsAngles);
9340 legsAngles[YAW] = cent_lerpAngles[YAW];
9341 }
9342
9343 #ifdef BONE_BASED_LEG_ANGLES
9344 legBoneYaw = legsAngles[YAW];
9345 VectorClear(legsAngles);
9346 legsAngles[YAW] = cent_lerpAngles[YAW];
9347 #endif
9348
9349 VectorCopy(legsAngles, turAngles);
9350
9351 AnglesToAxis( legsAngles, legs );
9352
9353 VectorCopy( cent_lerpAngles, viewAngles );
9354 viewAngles[YAW] = viewAngles[ROLL] = 0;
9355 viewAngles[PITCH] *= 0.5;
9356
9357 VectorSet( angles, 0, legsAngles[1], 0 );
9358
9359 angles[0] = legsAngles[0];
9360 if ( angles[0] > 30 )
9361 {
9362 angles[0] = 30;
9363 }
9364 else if ( angles[0] < -30 )
9365 {
9366 angles[0] = -30;
9367 }
9368
9369 if (cent->weapon == WP_EMPLACED_GUN &&
9370 emplaced)
9371 { //if using an emplaced gun, then we want to make sure we're angled to "hold" it right
9372 vec3_t facingAngles;
9373
9374 VectorSubtract(emplaced->pos.trBase, cent_lerpOrigin, facingAngles);
9375 vectoangles(facingAngles, facingAngles);
9376
9377 if (emplaced->weapon == WP_NONE)
9378 { //e-web
9379 VectorCopy(facingAngles, legsAngles);
9380 AnglesToAxis( legsAngles, legs );
9381 }
9382 else
9383 { //misc emplaced
9384 float emplacedDif = AngleSubtract(cent_lerpAngles[YAW], facingAngles[YAW]);
9385
9386 /*
9387 if (emplaced->weapon == WP_NONE)
9388 { //offset is a little bit different for the e-web
9389 emplacedDif -= 16.0f;
9390 }
9391 */
9392
9393 VectorSet(facingAngles, -16.0f, -emplacedDif, 0.0f);
9394
9395 if (cent->legsAnim == BOTH_STRAFE_LEFT1 || cent->legsAnim == BOTH_STRAFE_RIGHT1)
9396 { //try to adjust so it doesn't look wrong
9397 if (crazySmoothFactor)
9398 { //want to smooth a lot during this because it chops around and looks like ass
9399 *crazySmoothFactor = time + 1000;
9400 }
9401
9402 BG_G2ClientSpineAngles(ghoul2, motionBolt, cent_lerpOrigin, cent_lerpAngles, cent, time, viewAngles, ciLegs, ciTorso, angles, thoracicAngles, ulAngles, llAngles, modelScale, tPitchAngle, tYawAngle, corrTime);
9403 trap->G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", llAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9404 trap->G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", ulAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9405 trap->G2API_SetBoneAngles(ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9406
9407 VectorAdd(facingAngles, thoracicAngles, facingAngles);
9408
9409 if (cent->legsAnim == BOTH_STRAFE_LEFT1)
9410 { //this one needs some further correction
9411 facingAngles[YAW] -= 32.0f;
9412 }
9413 }
9414 else
9415 {
9416 // trap->G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9417 // trap->G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9418 trap->G2API_SetBoneAngles(ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9419 }
9420
9421 VectorScale(facingAngles, 0.6f, facingAngles); trap->G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9422 VectorScale(facingAngles, 0.8f, facingAngles); trap->G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", facingAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9423 VectorScale(facingAngles, 0.8f, facingAngles); trap->G2API_SetBoneAngles(ghoul2, 0, "thoracic", facingAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9424
9425 //Now we want the head angled toward where we are facing
9426 VectorSet(facingAngles, 0.0f, dif, 0.0f);
9427 VectorScale(facingAngles, 0.6f, facingAngles);
9428 trap->G2API_SetBoneAngles(ghoul2, 0, "cervical", facingAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9429
9430 return; //don't have to bother with the rest then
9431 }
9432 }
9433
9434 BG_G2ClientSpineAngles(ghoul2, motionBolt, cent_lerpOrigin, cent_lerpAngles, cent, time,
9435 viewAngles, ciLegs, ciTorso, angles, thoracicAngles, ulAngles, llAngles, modelScale,
9436 tPitchAngle, tYawAngle, corrTime);
9437
9438 VectorCopy(cent_lerpAngles, eyeAngles);
9439
9440 for ( i = 0; i < 3; i++ )
9441 {
9442 lookAngles[i] = AngleNormalize180( lookAngles[i] );
9443 eyeAngles[i] = AngleNormalize180( eyeAngles[i] );
9444 }
9445 AnglesSubtract( lookAngles, eyeAngles, lookAngles );
9446
9447 BG_UpdateLookAngles(lookTime, lastHeadAngles, time, lookAngles, lookSpeed, -50.0f, 50.0f, -70.0f, 70.0f, -30.0f, 30.0f);
9448
9449 BG_G2ClientNeckAngles(ghoul2, time, lookAngles, headAngles, neckAngles, thoracicAngles, headClampMinAngles, headClampMaxAngles);
9450
9451 #ifdef BONE_BASED_LEG_ANGLES
9452 {
9453 vec3_t bLAngles;
9454 VectorClear(bLAngles);
9455 bLAngles[ROLL] = AngleNormalize180((legBoneYaw - cent_lerpAngles[YAW]));
9456 strap_G2API_SetBoneAngles(ghoul2, 0, "model_root", bLAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9457
9458 if (!llAngles[YAW])
9459 {
9460 llAngles[YAW] -= bLAngles[ROLL];
9461 }
9462 }
9463 #endif
9464
9465 trap->G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", llAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9466 trap->G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", ulAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9467 trap->G2API_SetBoneAngles(ghoul2, 0, "thoracic", thoracicAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9468 // trap->G2API_SetBoneAngles(ghoul2, 0, "cervical", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9469 }
9470
BG_G2ATSTAngles(void * ghoul2,int time,vec3_t cent_lerpAngles)9471 void BG_G2ATSTAngles(void *ghoul2, int time, vec3_t cent_lerpAngles )
9472 {// up right fwd
9473 trap->G2API_SetBoneAngles( ghoul2, 0, "thoracic", cent_lerpAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time );
9474 }
9475
PM_AdjustAnglesForDualJumpAttack(playerState_t * ps,usercmd_t * ucmd)9476 static qboolean PM_AdjustAnglesForDualJumpAttack( playerState_t *ps, usercmd_t *ucmd )
9477 {
9478 //ucmd->angles[PITCH] = ANGLE2SHORT( ps->viewangles[PITCH] ) - ps->delta_angles[PITCH];
9479 //ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
9480 return qtrue;
9481 }
9482
PM_CmdForSaberMoves(usercmd_t * ucmd)9483 static QINLINE void PM_CmdForSaberMoves(usercmd_t *ucmd)
9484 {
9485 //DUAL FORWARD+JUMP+ATTACK
9486 if ( (pm->ps->legsAnim == BOTH_JUMPATTACK6 && pm->ps->saberMove == LS_JUMPATTACK_DUAL) ||
9487 (pm->ps->legsAnim == BOTH_BUTTERFLY_FL1 && pm->ps->saberMove == LS_JUMPATTACK_STAFF_LEFT) ||
9488 (pm->ps->legsAnim == BOTH_BUTTERFLY_FR1 && pm->ps->saberMove == LS_JUMPATTACK_STAFF_RIGHT) ||
9489 (pm->ps->legsAnim == BOTH_BUTTERFLY_RIGHT && pm->ps->saberMove == LS_BUTTERFLY_RIGHT) ||
9490 (pm->ps->legsAnim == BOTH_BUTTERFLY_LEFT && pm->ps->saberMove == LS_BUTTERFLY_LEFT) )
9491 {
9492 int aLen = PM_AnimLength(0, BOTH_JUMPATTACK6);
9493
9494 ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
9495
9496 if ( pm->ps->legsAnim == BOTH_JUMPATTACK6 )
9497 { //dual stance attack
9498 if ( pm->ps->legsTimer >= 100 //not at end
9499 && (aLen - pm->ps->legsTimer) >= 250 ) //not in beginning
9500 { //middle of anim
9501 //push forward
9502 ucmd->forwardmove = 127;
9503 }
9504
9505 if ( (pm->ps->legsTimer >= 900 //not at end
9506 && aLen - pm->ps->legsTimer >= 950 ) //not in beginning
9507 || ( pm->ps->legsTimer >= 1600
9508 && aLen - pm->ps->legsTimer >= 400 ) ) //not in beginning
9509 { //one of the two jumps
9510 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
9511 { //still on ground?
9512 if ( pm->ps->groundEntityNum >= MAX_CLIENTS )
9513 {
9514 //jump!
9515 pm->ps->velocity[2] = 250;//400;
9516 pm->ps->fd.forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
9517 //pm->ps->pm_flags |= PMF_JUMPING;
9518 //FIXME: NPCs yell?
9519 PM_AddEvent(EV_JUMP);
9520 //G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
9521 }
9522 }
9523 else
9524 { //FIXME: if this is the second jump, maybe we should just stop the anim?
9525 }
9526 }
9527 }
9528 else
9529 { //saberstaff attacks
9530 float lenMin = 1700.0f;
9531 float lenMax = 1800.0f;
9532
9533 aLen = PM_AnimLength(0, (animNumber_t)pm->ps->legsAnim);
9534
9535 if (pm->ps->legsAnim == BOTH_BUTTERFLY_LEFT)
9536 {
9537 lenMin = 1200.0f;
9538 lenMax = 1400.0f;
9539 }
9540
9541 //FIXME: don't slide off people/obstacles?
9542 if ( pm->ps->legsAnim == BOTH_BUTTERFLY_RIGHT
9543 || pm->ps->legsAnim == BOTH_BUTTERFLY_LEFT )
9544 {
9545 if ( pm->ps->legsTimer > 450 )
9546 {
9547 switch ( pm->ps->legsAnim )
9548 {
9549 case BOTH_BUTTERFLY_LEFT:
9550 ucmd->rightmove = -127;
9551 break;
9552 case BOTH_BUTTERFLY_RIGHT:
9553 ucmd->rightmove = 127;
9554 break;
9555 default:
9556 break;
9557 }
9558 }
9559 }
9560 else
9561 {
9562 if ( pm->ps->legsTimer >= 100 //not at end
9563 && aLen - pm->ps->legsTimer >= 250 )//not in beginning
9564 {//middle of anim
9565 //push forward
9566 ucmd->forwardmove = 127;
9567 }
9568 }
9569
9570 if ( pm->ps->legsTimer >= lenMin && pm->ps->legsTimer < lenMax )
9571 {//one of the two jumps
9572 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
9573 {//still on ground?
9574 //jump!
9575 if (pm->ps->legsAnim == BOTH_BUTTERFLY_LEFT)
9576 {
9577 pm->ps->velocity[2] = 350;
9578 }
9579 else
9580 {
9581 pm->ps->velocity[2] = 250;
9582 }
9583 pm->ps->fd.forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
9584 //pm->ps->pm_flags |= PMF_JUMPING;//|PMF_SLOW_MO_FALL;
9585 //FIXME: NPCs yell?
9586 PM_AddEvent(EV_JUMP);
9587 //G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
9588 }
9589 else
9590 {//FIXME: if this is the second jump, maybe we should just stop the anim?
9591 }
9592 }
9593 }
9594
9595 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
9596 {//can only turn when your feet hit the ground
9597 if (PM_AdjustAnglesForDualJumpAttack(pm->ps, ucmd))
9598 {
9599 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, ucmd);
9600 }
9601 }
9602 //rwwFIXMEFIXME: Bother with bbox resizing like sp?
9603 }
9604 //STAFF BACK+JUMP+ATTACK
9605 else if (pm->ps->saberMove == LS_A_BACKFLIP_ATK &&
9606 pm->ps->legsAnim == BOTH_JUMPATTACK7)
9607 {
9608 int aLen = PM_AnimLength(0, BOTH_JUMPATTACK7);
9609
9610 if ( pm->ps->legsTimer > 800 //not at end
9611 && aLen - pm->ps->legsTimer >= 400 )//not in beginning
9612 {//middle of anim
9613 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
9614 {//still on ground?
9615 vec3_t yawAngles, backDir;
9616
9617 //push backwards some?
9618 VectorSet( yawAngles, 0, pm->ps->viewangles[YAW]+180, 0 );
9619 AngleVectors( yawAngles, backDir, 0, 0 );
9620 VectorScale( backDir, 100, pm->ps->velocity );
9621
9622 //jump!
9623 pm->ps->velocity[2] = 300;
9624 pm->ps->fd.forceJumpZStart = pm->ps->origin[2]; //so we don't take damage if we land at same height
9625 //pm->ps->pm_flags |= PMF_JUMPING;//|PMF_SLOW_MO_FALL;
9626
9627 //FIXME: NPCs yell?
9628 PM_AddEvent(EV_JUMP);
9629 //G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
9630 ucmd->upmove = 0; //clear any actual jump command
9631 }
9632 }
9633 ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
9634 }
9635 //STAFF/DUAL SPIN ATTACK
9636 else if (pm->ps->saberMove == LS_SPINATTACK ||
9637 pm->ps->saberMove == LS_SPINATTACK_DUAL)
9638 {
9639 ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
9640 //lock their viewangles during these attacks.
9641 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, ucmd);
9642 }
9643 }
9644
9645 //constrain him based on the angles of his vehicle and the caps
PM_VehicleViewAngles(playerState_t * ps,bgEntity_t * veh,usercmd_t * ucmd)9646 void PM_VehicleViewAngles(playerState_t *ps, bgEntity_t *veh, usercmd_t *ucmd)
9647 {
9648 Vehicle_t *pVeh = veh->m_pVehicle;
9649 qboolean setAngles = qfalse;
9650 vec3_t clampMin;
9651 vec3_t clampMax;
9652 int i;
9653
9654 if ( veh->m_pVehicle->m_pPilot
9655 && veh->m_pVehicle->m_pPilot->s.number == ps->clientNum )
9656 {//set the pilot's viewangles to the vehicle's viewangles
9657 #ifdef VEH_CONTROL_SCHEME_4
9658 if ( 1 )
9659 #else //VEH_CONTROL_SCHEME_4
9660 if ( !BG_UnrestrainedPitchRoll( ps, veh->m_pVehicle ) )
9661 #endif //VEH_CONTROL_SCHEME_4
9662 {//only if not if doing special free-roll/pitch control
9663 setAngles = qtrue;
9664 clampMin[PITCH] = -pVeh->m_pVehicleInfo->lookPitch;
9665 clampMax[PITCH] = pVeh->m_pVehicleInfo->lookPitch;
9666 clampMin[YAW] = clampMax[YAW] = 0;
9667 clampMin[ROLL] = clampMax[ROLL] = -1;
9668 }
9669 }
9670 else
9671 {
9672 //NOTE: passengers can look around freely, UNLESS they're controlling a turret!
9673 for ( i = 0; i < MAX_VEHICLE_TURRETS; i++ )
9674 {
9675 if ( veh->m_pVehicle->m_pVehicleInfo->turret[i].passengerNum == ps->generic1 )
9676 {//this turret is my station
9677 setAngles = qtrue;
9678 clampMin[PITCH] = veh->m_pVehicle->m_pVehicleInfo->turret[i].pitchClampUp;
9679 clampMax[PITCH] = veh->m_pVehicle->m_pVehicleInfo->turret[i].pitchClampDown;
9680 clampMin[YAW] = veh->m_pVehicle->m_pVehicleInfo->turret[i].yawClampRight;
9681 clampMax[YAW] = veh->m_pVehicle->m_pVehicleInfo->turret[i].yawClampLeft;
9682 clampMin[ROLL] = clampMax[ROLL] = 0;
9683 break;
9684 }
9685 }
9686 }
9687 if ( setAngles )
9688 {
9689 for ( i = 0; i < 3; i++ )
9690 {//clamp viewangles
9691 if ( clampMin[i] == -1 || clampMax[i] == -1 )
9692 {//no clamp
9693 }
9694 else if ( !clampMin[i] && !clampMax[i] )
9695 {//no allowance
9696 //ps->viewangles[i] = veh->playerState->viewangles[i];
9697 }
9698 else
9699 {//allowance
9700 if (ps->viewangles[i] > clampMax[i])
9701 {
9702 ps->viewangles[i] = clampMax[i];
9703 }
9704 else if (ps->viewangles[i] < clampMin[i])
9705 {
9706 ps->viewangles[i] = clampMin[i];
9707 }
9708 }
9709 }
9710
9711 PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
9712 }
9713 }
9714
9715 /*
9716 //constrain him based on the angles of his vehicle and the caps
9717 void PM_VehicleViewAngles(playerState_t *ps, bgEntity_t *veh, usercmd_t *ucmd)
9718 {
9719 Vehicle_t *pVeh = veh->m_pVehicle;
9720
9721 //now set the viewangles to the vehicle's directly
9722 ps->viewangles[YAW] = veh->playerState->viewangles[YAW];
9723
9724 //constrain the viewangles pitch based on the vehicle properties
9725 if ( !pVeh->m_pVehicleInfo->lookPitch )
9726 {//not allowed to look up & down! ....???
9727 ps->viewangles[PITCH] = veh->playerState->viewangles[PITCH];
9728 }
9729 else
9730 {//clamp
9731 if (ps->viewangles[PITCH] > pVeh->m_pVehicleInfo->lookPitch)
9732 {
9733 ps->viewangles[PITCH] = pVeh->m_pVehicleInfo->lookPitch;
9734 }
9735 else if (ps->viewangles[PITCH] < -pVeh->m_pVehicleInfo->lookPitch)
9736 {
9737 ps->viewangles[PITCH] = -pVeh->m_pVehicleInfo->lookPitch;
9738 }
9739 }
9740
9741 PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
9742 }
9743 */
9744 //see if a weapon is ok to use on a vehicle
PM_WeaponOkOnVehicle(int weapon)9745 qboolean PM_WeaponOkOnVehicle( int weapon )
9746 {
9747 //FIXME: check g_vehicleInfo for our vehicle?
9748 switch ( weapon )
9749 {
9750 //case WP_NONE:
9751 case WP_MELEE:
9752 case WP_SABER:
9753 case WP_BLASTER:
9754 //case WP_THERMAL:
9755 return qtrue;
9756 break;
9757 }
9758 return qfalse;
9759 }
9760
9761 //do we have a weapon that's ok for using on the vehicle?
PM_GetOkWeaponForVehicle(void)9762 int PM_GetOkWeaponForVehicle(void)
9763 {
9764 int i = 0;
9765
9766 while (i < WP_NUM_WEAPONS)
9767 {
9768 if ((pm->ps->stats[STAT_WEAPONS] & (1 << i)) &&
9769 PM_WeaponOkOnVehicle(i))
9770 { //this one's good
9771 return i;
9772 }
9773
9774 i++;
9775 }
9776
9777 //oh dear!
9778 //assert(!"No valid veh weaps");
9779 return -1;
9780 }
9781
9782 //force the vehicle to turn and travel to its forced destination point
PM_VehForcedTurning(bgEntity_t * veh)9783 void PM_VehForcedTurning(bgEntity_t *veh)
9784 {
9785 bgEntity_t *dst = PM_BGEntForNum(veh->playerState->vehTurnaroundIndex);
9786 float pitchD, yawD;
9787 vec3_t dir;
9788
9789 if (!veh || !veh->m_pVehicle)
9790 {
9791 return;
9792 }
9793
9794 if (!dst)
9795 { //can't find dest ent?
9796 return;
9797 }
9798
9799 pm->cmd.upmove = veh->m_pVehicle->m_ucmd.upmove = 127;
9800 pm->cmd.forwardmove = veh->m_pVehicle->m_ucmd.forwardmove = 0;
9801 pm->cmd.rightmove = veh->m_pVehicle->m_ucmd.rightmove = 0;
9802
9803 VectorSubtract(dst->s.origin, veh->playerState->origin, dir);
9804 vectoangles(dir, dir);
9805
9806 yawD = AngleSubtract(pm->ps->viewangles[YAW], dir[YAW]);
9807 pitchD = AngleSubtract(pm->ps->viewangles[PITCH], dir[PITCH]);
9808
9809 yawD *= 0.6f*pml.frametime;
9810 pitchD *= 0.6f*pml.frametime;
9811
9812 #ifdef VEH_CONTROL_SCHEME_4
9813 veh->playerState->viewangles[YAW] = AngleSubtract(veh->playerState->viewangles[YAW], yawD);
9814 veh->playerState->viewangles[PITCH] = AngleSubtract(veh->playerState->viewangles[PITCH], pitchD);
9815 pm->ps->viewangles[YAW] = veh->playerState->viewangles[YAW];
9816 pm->ps->viewangles[PITCH] = 0;
9817
9818 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
9819 PM_SetPMViewAngle(veh->playerState, veh->playerState->viewangles, &pm->cmd);
9820 VectorClear( veh->m_pVehicle->m_vPrevRiderViewAngles );
9821 veh->m_pVehicle->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(pm->ps->viewangles[YAW]);
9822
9823 #else //VEH_CONTROL_SCHEME_4
9824
9825 pm->ps->viewangles[YAW] = AngleSubtract(pm->ps->viewangles[YAW], yawD);
9826 pm->ps->viewangles[PITCH] = AngleSubtract(pm->ps->viewangles[PITCH], pitchD);
9827
9828 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
9829 #endif //VEH_CONTROL_SCHEME_4
9830 }
9831
9832 #ifdef VEH_CONTROL_SCHEME_4
PM_VehFaceHyperspacePoint(bgEntity_t * veh)9833 void PM_VehFaceHyperspacePoint(bgEntity_t *veh)
9834 {
9835
9836 if (!veh || !veh->m_pVehicle)
9837 {
9838 return;
9839 }
9840 else
9841 {
9842 float timeFrac = ((float)(pm->cmd.serverTime-veh->playerState->hyperSpaceTime))/HYPERSPACE_TIME;
9843 float turnRate, aDelta;
9844 int i, matchedAxes = 0;
9845
9846 pm->cmd.upmove = veh->m_pVehicle->m_ucmd.upmove = 127;
9847 pm->cmd.forwardmove = veh->m_pVehicle->m_ucmd.forwardmove = 0;
9848 pm->cmd.rightmove = veh->m_pVehicle->m_ucmd.rightmove = 0;
9849
9850 turnRate = (90.0f*pml.frametime);
9851 for ( i = 0; i < 3; i++ )
9852 {
9853 aDelta = AngleSubtract(veh->playerState->hyperSpaceAngles[i], veh->m_pVehicle->m_vOrientation[i]);
9854 if ( fabs( aDelta ) < turnRate )
9855 {//all is good
9856 veh->playerState->viewangles[i] = veh->playerState->hyperSpaceAngles[i];
9857 matchedAxes++;
9858 }
9859 else
9860 {
9861 aDelta = AngleSubtract(veh->playerState->hyperSpaceAngles[i], veh->playerState->viewangles[i]);
9862 if ( fabs( aDelta ) < turnRate )
9863 {
9864 veh->playerState->viewangles[i] = veh->playerState->hyperSpaceAngles[i];
9865 }
9866 else if ( aDelta > 0 )
9867 {
9868 if ( i == YAW )
9869 {
9870 veh->playerState->viewangles[i] = AngleNormalize360( veh->playerState->viewangles[i]+turnRate );
9871 }
9872 else
9873 {
9874 veh->playerState->viewangles[i] = AngleNormalize180( veh->playerState->viewangles[i]+turnRate );
9875 }
9876 }
9877 else
9878 {
9879 if ( i == YAW )
9880 {
9881 veh->playerState->viewangles[i] = AngleNormalize360( veh->playerState->viewangles[i]-turnRate );
9882 }
9883 else
9884 {
9885 veh->playerState->viewangles[i] = AngleNormalize180( veh->playerState->viewangles[i]-turnRate );
9886 }
9887 }
9888 }
9889 }
9890
9891 pm->ps->viewangles[YAW] = veh->playerState->viewangles[YAW];
9892 pm->ps->viewangles[PITCH] = 0.0f;
9893
9894 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
9895 PM_SetPMViewAngle(veh->playerState, veh->playerState->viewangles, &pm->cmd);
9896 VectorClear( veh->m_pVehicle->m_vPrevRiderViewAngles );
9897 veh->m_pVehicle->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(pm->ps->viewangles[YAW]);
9898
9899 if ( timeFrac < HYPERSPACE_TELEPORT_FRAC )
9900 {//haven't gone through yet
9901 if ( matchedAxes < 3 )
9902 {//not facing the right dir yet
9903 //keep hyperspace time up to date
9904 veh->playerState->hyperSpaceTime += pml.msec;
9905 }
9906 else if ( !(veh->playerState->eFlags2&EF2_HYPERSPACE))
9907 {//flag us as ready to hyperspace!
9908 veh->playerState->eFlags2 |= EF2_HYPERSPACE;
9909 }
9910 }
9911 }
9912 }
9913
9914 #else //VEH_CONTROL_SCHEME_4
9915
PM_VehFaceHyperspacePoint(bgEntity_t * veh)9916 void PM_VehFaceHyperspacePoint(bgEntity_t *veh)
9917 {
9918
9919 if (!veh || !veh->m_pVehicle)
9920 {
9921 return;
9922 }
9923 else
9924 {
9925 float timeFrac = ((float)(pm->cmd.serverTime-veh->playerState->hyperSpaceTime))/HYPERSPACE_TIME;
9926 float turnRate, aDelta;
9927 int i, matchedAxes = 0;
9928
9929 pm->cmd.upmove = veh->m_pVehicle->m_ucmd.upmove = 127;
9930 pm->cmd.forwardmove = veh->m_pVehicle->m_ucmd.forwardmove = 0;
9931 pm->cmd.rightmove = veh->m_pVehicle->m_ucmd.rightmove = 0;
9932
9933 turnRate = (90.0f*pml.frametime);
9934 for ( i = 0; i < 3; i++ )
9935 {
9936 aDelta = AngleSubtract(veh->playerState->hyperSpaceAngles[i], veh->m_pVehicle->m_vOrientation[i]);
9937 if ( fabs( aDelta ) < turnRate )
9938 {//all is good
9939 pm->ps->viewangles[i] = veh->playerState->hyperSpaceAngles[i];
9940 matchedAxes++;
9941 }
9942 else
9943 {
9944 aDelta = AngleSubtract(veh->playerState->hyperSpaceAngles[i], pm->ps->viewangles[i]);
9945 if ( fabs( aDelta ) < turnRate )
9946 {
9947 pm->ps->viewangles[i] = veh->playerState->hyperSpaceAngles[i];
9948 }
9949 else if ( aDelta > 0 )
9950 {
9951 if ( i == YAW )
9952 {
9953 pm->ps->viewangles[i] = AngleNormalize360( pm->ps->viewangles[i]+turnRate );
9954 }
9955 else
9956 {
9957 pm->ps->viewangles[i] = AngleNormalize180( pm->ps->viewangles[i]+turnRate );
9958 }
9959 }
9960 else
9961 {
9962 if ( i == YAW )
9963 {
9964 pm->ps->viewangles[i] = AngleNormalize360( pm->ps->viewangles[i]-turnRate );
9965 }
9966 else
9967 {
9968 pm->ps->viewangles[i] = AngleNormalize180( pm->ps->viewangles[i]-turnRate );
9969 }
9970 }
9971 }
9972 }
9973
9974 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
9975
9976 if ( timeFrac < HYPERSPACE_TELEPORT_FRAC )
9977 {//haven't gone through yet
9978 if ( matchedAxes < 3 )
9979 {//not facing the right dir yet
9980 //keep hyperspace time up to date
9981 veh->playerState->hyperSpaceTime += pml.msec;
9982 }
9983 else if ( !(veh->playerState->eFlags2&EF2_HYPERSPACE))
9984 {//flag us as ready to hyperspace!
9985 veh->playerState->eFlags2 |= EF2_HYPERSPACE;
9986 }
9987 }
9988 }
9989 }
9990
9991 #endif //VEH_CONTROL_SCHEME_4
9992
BG_VehicleAdjustBBoxForOrientation(Vehicle_t * veh,vec3_t origin,vec3_t mins,vec3_t maxs,int clientNum,int tracemask,void (* localTrace)(trace_t * results,const vec3_t start,const vec3_t mins,const vec3_t maxs,const vec3_t end,int passEntityNum,int contentMask))9993 void BG_VehicleAdjustBBoxForOrientation( Vehicle_t *veh, vec3_t origin, vec3_t mins, vec3_t maxs,
9994 int clientNum, int tracemask,
9995 void (*localTrace)(trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask))
9996 {
9997 if ( !veh
9998 || !veh->m_pVehicleInfo->length
9999 || !veh->m_pVehicleInfo->width
10000 || !veh->m_pVehicleInfo->height )
10001 //|| veh->m_LandTrace.fraction < 1.0f )
10002 {
10003 return;
10004 }
10005 else if ( veh->m_pVehicleInfo->type != VH_FIGHTER
10006 //&& veh->m_pVehicleInfo->type != VH_SPEEDER
10007 && veh->m_pVehicleInfo->type != VH_FLIER )
10008 {//only those types of vehicles have dynamic bboxes, the rest just use a static bbox
10009 VectorSet( maxs, veh->m_pVehicleInfo->width/2.0f, veh->m_pVehicleInfo->width/2.0f, veh->m_pVehicleInfo->height+DEFAULT_MINS_2 );
10010 VectorSet( mins, veh->m_pVehicleInfo->width/-2.0f, veh->m_pVehicleInfo->width/-2.0f, DEFAULT_MINS_2 );
10011 return;
10012 }
10013 else
10014 {
10015 matrix3_t axis;
10016 vec3_t point[8], newMins, newMaxs;
10017 int curAxis = 0, i;
10018 trace_t trace;
10019
10020 AnglesToAxis( veh->m_vOrientation, axis );
10021 VectorMA( origin, veh->m_pVehicleInfo->length/2.0f, axis[0], point[0] );
10022 VectorMA( origin, -veh->m_pVehicleInfo->length/2.0f, axis[0], point[1] );
10023 //extrapolate each side up and down
10024 VectorMA( point[0], veh->m_pVehicleInfo->height/2.0f, axis[2], point[0] );
10025 VectorMA( point[0], -veh->m_pVehicleInfo->height, axis[2], point[2] );
10026 VectorMA( point[1], veh->m_pVehicleInfo->height/2.0f, axis[2], point[1] );
10027 VectorMA( point[1], -veh->m_pVehicleInfo->height, axis[2], point[3] );
10028
10029 VectorMA( origin, veh->m_pVehicleInfo->width/2.0f, axis[1], point[4] );
10030 VectorMA( origin, -veh->m_pVehicleInfo->width/2.0f, axis[1], point[5] );
10031 //extrapolate each side up and down
10032 VectorMA( point[4], veh->m_pVehicleInfo->height/2.0f, axis[2], point[4] );
10033 VectorMA( point[4], -veh->m_pVehicleInfo->height, axis[2], point[6] );
10034 VectorMA( point[5], veh->m_pVehicleInfo->height/2.0f, axis[2], point[5] );
10035 VectorMA( point[5], -veh->m_pVehicleInfo->height, axis[2], point[7] );
10036 /*
10037 VectorMA( origin, veh->m_pVehicleInfo->height/2.0f, axis[2], point[4] );
10038 VectorMA( origin, -veh->m_pVehicleInfo->height/2.0f, axis[2], point[5] );
10039 */
10040 //Now inflate a bbox around these points
10041 VectorCopy( origin, newMins );
10042 VectorCopy( origin, newMaxs );
10043 for ( curAxis = 0; curAxis < 3; curAxis++ )
10044 {
10045 for ( i = 0; i < 8; i++ )
10046 {
10047 if ( point[i][curAxis] > newMaxs[curAxis] )
10048 {
10049 newMaxs[curAxis] = point[i][curAxis];
10050 }
10051 else if ( point[i][curAxis] < newMins[curAxis] )
10052 {
10053 newMins[curAxis] = point[i][curAxis];
10054 }
10055 }
10056 }
10057 VectorSubtract( newMins, origin, newMins );
10058 VectorSubtract( newMaxs, origin, newMaxs );
10059 //now see if that's a valid way to be
10060 if (localTrace)
10061 {
10062 localTrace( &trace, origin, newMins, newMaxs, origin, clientNum, tracemask );
10063 }
10064 else
10065 { //don't care about solid stuff then
10066 trace.startsolid = trace.allsolid = 0;
10067 }
10068 if ( !trace.startsolid && !trace.allsolid )
10069 {//let's use it!
10070 VectorCopy( newMins, mins );
10071 VectorCopy( newMaxs, maxs );
10072 }
10073 //else: just use the last one, I guess...?
10074 //FIXME: make it as close as possible? Or actually prevent the change in m_vOrientation? Or push away from anything we hit?
10075 }
10076 }
10077 /*
10078 ================
10079 PmoveSingle
10080
10081 ================
10082 */
10083 extern int BG_EmplacedView(vec3_t baseAngles, vec3_t angles, float *newYaw, float constraint);
10084 extern qboolean BG_FighterUpdate(Vehicle_t *pVeh, const usercmd_t *pUcmd, vec3_t trMins, vec3_t trMaxs, float gravity,
10085 void (*traceFunc)( trace_t *results, const vec3_t start, const vec3_t lmins, const vec3_t lmaxs, const vec3_t end, int passEntityNum, int contentMask )); //FighterNPC.c
10086
10087 #define JETPACK_HOVER_HEIGHT 64
10088
10089 //#define _TESTING_VEH_PREDICTION
10090
PM_MoveForKata(usercmd_t * ucmd)10091 void PM_MoveForKata(usercmd_t *ucmd)
10092 {
10093 if ( pm->ps->legsAnim == BOTH_A7_SOULCAL
10094 && pm->ps->saberMove == LS_STAFF_SOULCAL )
10095 {//forward spinning staff attack
10096 ucmd->upmove = 0;
10097
10098 if ( PM_CanRollFromSoulCal( pm->ps ) )
10099 {
10100 ucmd->upmove = -127;
10101 ucmd->rightmove = 0;
10102 if (ucmd->forwardmove < 0)
10103 {
10104 ucmd->forwardmove = 0;
10105 }
10106 }
10107 else
10108 {
10109 ucmd->rightmove = 0;
10110 //FIXME: don't slide off people/obstacles?
10111 if ( pm->ps->legsTimer >= 2750 )
10112 {//not at end
10113 //push forward
10114 ucmd->forwardmove = 64;
10115 }
10116 else
10117 {
10118 ucmd->forwardmove = 0;
10119 }
10120 }
10121 if ( pm->ps->legsTimer >= 2650
10122 && pm->ps->legsTimer < 2850 )
10123 {//the jump
10124 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
10125 {//still on ground?
10126 //jump!
10127 pm->ps->velocity[2] = 250;
10128 pm->ps->fd.forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
10129 // pm->ps->pm_flags |= PMF_JUMPING;//|PMF_SLOW_MO_FALL;
10130 //FIXME: NPCs yell?
10131 PM_AddEvent(EV_JUMP);
10132 }
10133 }
10134 }
10135 else if (pm->ps->legsAnim == BOTH_A2_SPECIAL)
10136 { //medium kata
10137 pm->cmd.rightmove = 0;
10138 pm->cmd.upmove = 0;
10139 if (pm->ps->legsTimer < 2700 && pm->ps->legsTimer > 2300)
10140 {
10141 pm->cmd.forwardmove = 127;
10142 }
10143 else if (pm->ps->legsTimer < 900 && pm->ps->legsTimer > 500)
10144 {
10145 pm->cmd.forwardmove = 127;
10146 }
10147 else
10148 {
10149 pm->cmd.forwardmove = 0;
10150 }
10151 }
10152 else if (pm->ps->legsAnim == BOTH_A3_SPECIAL)
10153 { //strong kata
10154 pm->cmd.rightmove = 0;
10155 pm->cmd.upmove = 0;
10156 if (pm->ps->legsTimer < 1700 && pm->ps->legsTimer > 1000)
10157 {
10158 pm->cmd.forwardmove = 127;
10159 }
10160 else
10161 {
10162 pm->cmd.forwardmove = 0;
10163 }
10164 }
10165 else
10166 {
10167 pm->cmd.forwardmove = 0;
10168 pm->cmd.rightmove = 0;
10169 pm->cmd.upmove = 0;
10170 }
10171 }
10172
PmoveSingle(pmove_t * pmove)10173 void PmoveSingle (pmove_t *pmove) {
10174 qboolean stiffenedUp = qfalse;
10175 float gDist = 0;
10176 qboolean noAnimate = qfalse;
10177 int savedGravity = 0;
10178
10179 pm = pmove;
10180
10181 if (pm->cmd.buttons & BUTTON_ATTACK && pm->cmd.buttons & BUTTON_USE_HOLDABLE)
10182 {
10183 pm->cmd.buttons &= ~BUTTON_ATTACK;
10184 pm->cmd.buttons &= ~BUTTON_USE_HOLDABLE;
10185 }
10186 if (pm->cmd.buttons & BUTTON_ALT_ATTACK && pm->cmd.buttons & BUTTON_USE_HOLDABLE)
10187 {
10188 pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
10189 pm->cmd.buttons &= ~BUTTON_USE_HOLDABLE;
10190 }
10191
10192 if (pm->ps->emplacedIndex)
10193 {
10194 if (pm->cmd.buttons & BUTTON_ALT_ATTACK)
10195 { //hackerrific.
10196 pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
10197 pm->cmd.buttons |= BUTTON_ATTACK;
10198 }
10199 }
10200
10201 //set up these "global" bg ents
10202 pm_entSelf = PM_BGEntForNum(pm->ps->clientNum);
10203 if (pm->ps->m_iVehicleNum)
10204 {
10205 if (pm->ps->clientNum < MAX_CLIENTS)
10206 { //player riding vehicle
10207 pm_entVeh = PM_BGEntForNum(pm->ps->m_iVehicleNum);
10208 }
10209 else
10210 { //vehicle with player pilot
10211 pm_entVeh = PM_BGEntForNum(pm->ps->m_iVehicleNum-1);
10212 }
10213 }
10214 else
10215 { //no vehicle ent
10216 pm_entVeh = NULL;
10217 }
10218
10219 gPMDoSlowFall = PM_DoSlowFall();
10220
10221 // this counter lets us debug movement problems with a journal
10222 // by setting a conditional breakpoint fot the previous frame
10223 c_pmove++;
10224
10225 // clear results
10226 pm->numtouch = 0;
10227 pm->watertype = 0;
10228 pm->waterlevel = 0;
10229
10230 if (PM_IsRocketTrooper())
10231 { //kind of nasty, don't let them crouch or anything if nonhumanoid (probably a rockettrooper)
10232 if (pm->cmd.upmove < 0)
10233 {
10234 pm->cmd.upmove = 0;
10235 }
10236 }
10237
10238 if (pm->ps->pm_type == PM_FLOAT)
10239 { //You get no control over where you go in grip movement
10240 stiffenedUp = qtrue;
10241 }
10242 else if (pm->ps->eFlags & EF_DISINTEGRATION)
10243 {
10244 stiffenedUp = qtrue;
10245 }
10246 else if ( BG_SaberLockBreakAnim( pm->ps->legsAnim )
10247 || BG_SaberLockBreakAnim( pm->ps->torsoAnim )
10248 || pm->ps->saberLockTime >= pm->cmd.serverTime )
10249 {//can't move or turn
10250 stiffenedUp = qtrue;
10251 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10252 }
10253 else if ( pm->ps->saberMove == LS_A_BACK || pm->ps->saberMove == LS_A_BACK_CR ||
10254 pm->ps->saberMove == LS_A_BACKSTAB || pm->ps->saberMove == LS_A_FLIP_STAB ||
10255 pm->ps->saberMove == LS_A_FLIP_SLASH || pm->ps->saberMove == LS_A_JUMP_T__B_ ||
10256 pm->ps->saberMove == LS_DUAL_LR || pm->ps->saberMove == LS_DUAL_FB)
10257 {
10258 if (pm->ps->legsAnim == BOTH_JUMPFLIPSTABDOWN ||
10259 pm->ps->legsAnim == BOTH_JUMPFLIPSLASHDOWN1)
10260 { //flipover medium stance attack
10261 if (pm->ps->legsTimer < 1600 && pm->ps->legsTimer > 900)
10262 {
10263 pm->ps->viewangles[YAW] += pml.frametime*240.0f;
10264 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10265 }
10266 }
10267 stiffenedUp = qtrue;
10268 }
10269 else if ((pm->ps->legsAnim) == (BOTH_A2_STABBACK1) ||
10270 (pm->ps->legsAnim) == (BOTH_ATTACK_BACK) ||
10271 (pm->ps->legsAnim) == (BOTH_CROUCHATTACKBACK1) ||
10272 (pm->ps->legsAnim) == (BOTH_FORCELEAP2_T__B_) ||
10273 (pm->ps->legsAnim) == (BOTH_JUMPFLIPSTABDOWN) ||
10274 (pm->ps->legsAnim) == (BOTH_JUMPFLIPSLASHDOWN1))
10275 {
10276 stiffenedUp = qtrue;
10277 }
10278 else if (pm->ps->legsAnim == BOTH_ROLL_STAB)
10279 {
10280 stiffenedUp = qtrue;
10281 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10282 }
10283 else if (pm->ps->heldByClient)
10284 {
10285 stiffenedUp = qtrue;
10286 }
10287 else if (BG_KickMove(pm->ps->saberMove) || BG_KickingAnim(pm->ps->legsAnim))
10288 {
10289 stiffenedUp = qtrue;
10290 }
10291 else if (BG_InGrappleMove(pm->ps->torsoAnim))
10292 {
10293 stiffenedUp = qtrue;
10294 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10295 }
10296 else if ( pm->ps->saberMove == LS_STABDOWN_DUAL ||
10297 pm->ps->saberMove == LS_STABDOWN_STAFF ||
10298 pm->ps->saberMove == LS_STABDOWN)
10299 {//FIXME: need to only move forward until we bump into our target...?
10300 if (pm->ps->legsTimer < 800)
10301 { //freeze movement near end of anim
10302 stiffenedUp = qtrue;
10303 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10304 }
10305 else
10306 { //force forward til then
10307 pm->cmd.rightmove = 0;
10308 pm->cmd.upmove = 0;
10309 pm->cmd.forwardmove = 64;
10310 }
10311 }
10312 else if (pm->ps->saberMove == LS_PULL_ATTACK_STAB ||
10313 pm->ps->saberMove == LS_PULL_ATTACK_SWING)
10314 {
10315 stiffenedUp = qtrue;
10316 }
10317 else if (BG_SaberInKata(pm->ps->saberMove) ||
10318 BG_InKataAnim(pm->ps->torsoAnim) ||
10319 BG_InKataAnim(pm->ps->legsAnim))
10320 {
10321 PM_MoveForKata(&pm->cmd);
10322 }
10323 else if ( BG_FullBodyTauntAnim( pm->ps->legsAnim )
10324 && BG_FullBodyTauntAnim( pm->ps->torsoAnim ) )
10325 {
10326 if ( (pm->cmd.buttons&BUTTON_ATTACK)
10327 || (pm->cmd.buttons&BUTTON_ALT_ATTACK)
10328 || (pm->cmd.buttons&BUTTON_FORCEPOWER)
10329 || (pm->cmd.buttons&BUTTON_FORCEGRIP)
10330 || (pm->cmd.buttons&BUTTON_FORCE_LIGHTNING)
10331 || (pm->cmd.buttons&BUTTON_FORCE_DRAIN)
10332 || pm->cmd.upmove )
10333 {//stop the anim
10334 if ( pm->ps->legsAnim == BOTH_MEDITATE
10335 && pm->ps->torsoAnim == BOTH_MEDITATE )
10336 {
10337 PM_SetAnim( SETANIM_BOTH, BOTH_MEDITATE_END, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
10338 }
10339 else
10340 {
10341 pm->ps->legsTimer = pm->ps->torsoTimer = 0;
10342 }
10343 if ( pm->ps->forceHandExtend == HANDEXTEND_TAUNT )
10344 {
10345 pm->ps->forceHandExtend = 0;
10346 }
10347 }
10348 else
10349 {
10350 if ( pm->ps->legsAnim == BOTH_MEDITATE )
10351 {
10352 if ( pm->ps->legsTimer < 100 )
10353 {
10354 pm->ps->legsTimer = 100;
10355 }
10356 }
10357 if ( pm->ps->torsoAnim == BOTH_MEDITATE )
10358 {
10359 if ( pm->ps->torsoTimer < 100 )
10360 {
10361 pm->ps->legsTimer = 100;
10362 }
10363 pm->ps->forceHandExtend = HANDEXTEND_TAUNT;
10364 pm->ps->forceHandExtendTime = pm->cmd.serverTime + 100;
10365 }
10366 if ( pm->ps->legsTimer > 0 || pm->ps->torsoTimer > 0 )
10367 {
10368 stiffenedUp = qtrue;
10369 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10370 pm->cmd.rightmove = 0;
10371 pm->cmd.upmove = 0;
10372 pm->cmd.forwardmove = 0;
10373 pm->cmd.buttons = 0;
10374 }
10375 }
10376 }
10377 else if ( pm->ps->legsAnim == BOTH_MEDITATE_END
10378 && pm->ps->legsTimer > 0 )
10379 {
10380 stiffenedUp = qtrue;
10381 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10382 pm->cmd.rightmove = 0;
10383 pm->cmd.upmove = 0;
10384 pm->cmd.forwardmove = 0;
10385 pm->cmd.buttons = 0;
10386 }
10387 else if (pm->ps->legsAnim == BOTH_FORCELAND1 ||
10388 pm->ps->legsAnim == BOTH_FORCELANDBACK1 ||
10389 pm->ps->legsAnim == BOTH_FORCELANDRIGHT1 ||
10390 pm->ps->legsAnim == BOTH_FORCELANDLEFT1)
10391 { //can't move while in a force land
10392 stiffenedUp = qtrue;
10393 }
10394
10395 if ( pm->ps->saberMove == LS_A_LUNGE )
10396 {//can't move during lunge
10397 pm->cmd.rightmove = pm->cmd.upmove = 0;
10398 if ( pm->ps->legsTimer > 500 )
10399 {
10400 pm->cmd.forwardmove = 127;
10401 }
10402 else
10403 {
10404 pm->cmd.forwardmove = 0;
10405 }
10406 }
10407
10408 if ( pm->ps->saberMove == LS_A_JUMP_T__B_ )
10409 {//can't move during leap
10410 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
10411 {//hit the ground
10412 pm->cmd.forwardmove = 0;
10413 }
10414 pm->cmd.rightmove = pm->cmd.upmove = 0;
10415 }
10416
10417 #if 0
10418 if ((pm->ps->legsAnim) == BOTH_KISSER1LOOP ||
10419 (pm->ps->legsAnim) == BOTH_KISSEE1LOOP)
10420 {
10421 stiffenedUp = qtrue;
10422 }
10423 #endif
10424
10425 if (pm->ps->emplacedIndex)
10426 {
10427 if (pm->cmd.forwardmove < 0 || PM_GroundDistance() > 32.0f)
10428 {
10429 pm->ps->emplacedIndex = 0;
10430 pm->ps->saberHolstered = 0;
10431 }
10432 else
10433 {
10434 stiffenedUp = qtrue;
10435 }
10436 }
10437
10438 /*
10439 if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->weaponstate == WEAPON_CHARGING_ALT)
10440 { //not allowed to move while charging the disruptor
10441 pm->cmd.forwardmove = 0;
10442 pm->cmd.rightmove = 0;
10443 if (pm->cmd.upmove > 0)
10444 {
10445 pm->cmd.upmove = 0;
10446 }
10447 }
10448 */
10449
10450 if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->weaponstate == WEAPON_CHARGING_ALT)
10451 { //not allowed to move while charging the disruptor
10452 if (pm->cmd.forwardmove ||
10453 pm->cmd.rightmove ||
10454 pm->cmd.upmove > 0)
10455 { //get out
10456 pm->ps->weaponstate = WEAPON_READY;
10457 pm->ps->weaponTime = 1000;
10458 PM_AddEventWithParm(EV_WEAPON_CHARGE, WP_DISRUPTOR); //cut the weapon charge sound
10459 pm->cmd.upmove = 0;
10460 }
10461 }
10462 else if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
10463 { //can't jump
10464 if (pm->cmd.upmove > 0)
10465 {
10466 pm->cmd.upmove = 0;
10467 }
10468 }
10469
10470 if (stiffenedUp)
10471 {
10472 pm->cmd.forwardmove = 0;
10473 pm->cmd.rightmove = 0;
10474 pm->cmd.upmove = 0;
10475 }
10476
10477 if (pm->ps->fd.forceGripCripple)
10478 { //don't let attack or alt attack if being gripped I guess
10479 pm->cmd.buttons &= ~BUTTON_ATTACK;
10480 pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
10481 }
10482
10483 if ( BG_InRoll( pm->ps, pm->ps->legsAnim ) )
10484 { //can't roll unless you're able to move normally
10485 BG_CmdForRoll( pm->ps, pm->ps->legsAnim, &pm->cmd );
10486 }
10487
10488 PM_CmdForSaberMoves(&pm->cmd);
10489
10490 BG_AdjustClientSpeed(pm->ps, &pm->cmd, pm->cmd.serverTime);
10491
10492 if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
10493 pm->tracemask &= ~CONTENTS_BODY; // corpses can fly through bodies
10494 }
10495
10496 // make sure walking button is clear if they are running, to avoid
10497 // proxy no-footsteps cheats
10498 if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) {
10499 pm->cmd.buttons &= ~BUTTON_WALKING;
10500 }
10501
10502 // set the talk balloon flag
10503 if ( pm->cmd.buttons & BUTTON_TALK ) {
10504 pm->ps->eFlags |= EF_TALK;
10505 } else {
10506 pm->ps->eFlags &= ~EF_TALK;
10507 }
10508
10509 pm_cancelOutZoom = qfalse;
10510 if (pm->ps->weapon == WP_DISRUPTOR &&
10511 pm->ps->zoomMode == 1)
10512 {
10513 if ((pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
10514 !(pm->cmd.buttons & BUTTON_ATTACK) &&
10515 pm->ps->zoomLocked)
10516 {
10517 pm_cancelOutZoom = qtrue;
10518 }
10519 }
10520 // In certain situations, we may want to control which attack buttons are pressed and what kind of functionality
10521 // is attached to them
10522 PM_AdjustAttackStates( pm );
10523
10524 // clear the respawned flag if attack and use are cleared
10525 if ( pm->ps->stats[STAT_HEALTH] > 0 &&
10526 !( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) {
10527 pm->ps->pm_flags &= ~PMF_RESPAWNED;
10528 }
10529
10530 // if talk button is down, dissallow all other input
10531 // this is to prevent any possible intercept proxy from
10532 // adding fake talk balloons
10533 if ( pmove->cmd.buttons & BUTTON_TALK ) {
10534 // keep the talk button set tho for when the cmd.serverTime > 66 msec
10535 // and the same cmd is used multiple times in Pmove
10536 pmove->cmd.buttons = BUTTON_TALK;
10537 pmove->cmd.forwardmove = 0;
10538 pmove->cmd.rightmove = 0;
10539 pmove->cmd.upmove = 0;
10540 }
10541
10542 // clear all pmove local vars
10543 memset (&pml, 0, sizeof(pml));
10544
10545 // determine the time
10546 pml.msec = pmove->cmd.serverTime - pm->ps->commandTime;
10547 if ( pml.msec < 1 ) {
10548 pml.msec = 1;
10549 } else if ( pml.msec > 200 ) {
10550 pml.msec = 200;
10551 }
10552
10553 /*
10554 if (pm->ps->clientNum >= MAX_CLIENTS)
10555 {
10556 #ifdef _GAME
10557 Com_Printf( S_C0LOR_RED" SERVER N%i msec %d\n", pm->ps->clientNum, pml.msec );
10558 #else
10559 Com_Printf( S_COLOR_GREEN" CLIENT N%i msec %d\n", pm->ps->clientNum, pml.msec );
10560 #endif
10561 }
10562 */
10563
10564 pm->ps->commandTime = pmove->cmd.serverTime;
10565
10566 // save old org in case we get stuck
10567 VectorCopy (pm->ps->origin, pml.previous_origin);
10568
10569 // save old velocity for crashlanding
10570 VectorCopy (pm->ps->velocity, pml.previous_velocity);
10571
10572 pml.frametime = pml.msec * 0.001;
10573
10574 if (pm->ps->clientNum >= MAX_CLIENTS &&
10575 pm_entSelf &&
10576 pm_entSelf->s.NPC_class == CLASS_VEHICLE)
10577 { //we are a vehicle
10578 bgEntity_t *veh = pm_entSelf;
10579 assert( veh && veh->m_pVehicle);
10580 if ( veh && veh->m_pVehicle )
10581 {
10582 veh->m_pVehicle->m_fTimeModifier = pml.frametime*60.0f;
10583 }
10584 }
10585 else if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
10586 &&pm->ps->m_iVehicleNum)
10587 {
10588 bgEntity_t *veh = pm_entVeh;
10589
10590 if (veh && veh->playerState &&
10591 (pm->cmd.serverTime-veh->playerState->hyperSpaceTime) < HYPERSPACE_TIME)
10592 { //going into hyperspace, turn to face the right angles
10593 PM_VehFaceHyperspacePoint(veh);
10594 }
10595 else if (veh && veh->playerState &&
10596 veh->playerState->vehTurnaroundIndex &&
10597 veh->playerState->vehTurnaroundTime > pm->cmd.serverTime)
10598 { //riding this vehicle, turn my view too
10599 PM_VehForcedTurning(veh);
10600 }
10601 }
10602
10603 if ( pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_ALT &&
10604 pm->ps->legsTimer > 0 )
10605 {
10606 vec3_t vFwd, fwdAng;
10607 VectorSet(fwdAng, 0.0f, pm->ps->viewangles[YAW], 0.0f);
10608
10609 AngleVectors( fwdAng, vFwd, NULL, NULL );
10610 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
10611 {
10612 float savZ = pm->ps->velocity[2];
10613 VectorScale( vFwd, 100, pm->ps->velocity );
10614 pm->ps->velocity[2] = savZ;
10615 }
10616 pm->cmd.forwardmove = pm->cmd.rightmove = pm->cmd.upmove = 0;
10617 PM_AdjustAnglesForWallRunUpFlipAlt( &pm->cmd );
10618 }
10619
10620 // PM_AdjustAngleForWallRun(pm->ps, &pm->cmd, qtrue);
10621 // PM_AdjustAnglesForStabDown( pm->ps, &pm->cmd );
10622 PM_AdjustAngleForWallJump( pm->ps, &pm->cmd, qtrue );
10623 PM_AdjustAngleForWallRunUp( pm->ps, &pm->cmd, qtrue );
10624 PM_AdjustAngleForWallRun( pm->ps, &pm->cmd, qtrue );
10625
10626 if (pm->ps->saberMove == LS_A_JUMP_T__B_ || pm->ps->saberMove == LS_A_LUNGE ||
10627 pm->ps->saberMove == LS_A_BACK_CR || pm->ps->saberMove == LS_A_BACK ||
10628 pm->ps->saberMove == LS_A_BACKSTAB)
10629 {
10630 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10631 }
10632
10633 #if 0
10634 if ((pm->ps->legsAnim) == BOTH_KISSER1LOOP ||
10635 (pm->ps->legsAnim) == BOTH_KISSEE1LOOP)
10636 {
10637 pm->ps->viewangles[PITCH] = 0;
10638 PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10639 }
10640 #endif
10641
10642 PM_SetSpecialMoveValues();
10643
10644 // update the viewangles
10645 PM_UpdateViewAngles( pm->ps, &pm->cmd );
10646
10647 AngleVectors (pm->ps->viewangles, pml.forward, pml.right, pml.up);
10648
10649 if ( pm->cmd.upmove < 10 && !(pm->ps->pm_flags & PMF_STUCK_TO_WALL)) {
10650 // not holding jump
10651 pm->ps->pm_flags &= ~PMF_JUMP_HELD;
10652 }
10653
10654 // decide if backpedaling animations should be used
10655 if ( pm->cmd.forwardmove < 0 ) {
10656 pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
10657 } else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
10658 pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;
10659 }
10660
10661 if ( pm->ps->pm_type >= PM_DEAD ) {
10662 pm->cmd.forwardmove = 0;
10663 pm->cmd.rightmove = 0;
10664 pm->cmd.upmove = 0;
10665 }
10666
10667 /*
10668 if (pm->ps->fd.saberAnimLevel == SS_STAFF &&
10669 (pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
10670 pm->cmd.upmove > 0)
10671 { //this is how you do kick-for-condition
10672 pm->cmd.upmove = 0;
10673 pm->ps->pm_flags |= PMF_JUMP_HELD;
10674 }
10675 */
10676
10677 if (pm->ps->saberLockTime >= pm->cmd.serverTime)
10678 {
10679 pm->cmd.upmove = 0;
10680 pm->cmd.forwardmove = 0;//50;
10681 pm->cmd.rightmove = 0;//*= 0.1;
10682 }
10683
10684 if ( pm->ps->pm_type == PM_SPECTATOR ) {
10685 PM_CheckDuck ();
10686 if (!pm->noSpecMove)
10687 {
10688 PM_FlyMove ();
10689 }
10690 PM_DropTimers ();
10691 return;
10692 }
10693
10694 if ( pm->ps->pm_type == PM_NOCLIP ) {
10695 if (pm->ps->clientNum < MAX_CLIENTS)
10696 {
10697 PM_NoclipMove ();
10698 PM_DropTimers ();
10699 return;
10700 }
10701 }
10702
10703 if (pm->ps->pm_type == PM_FREEZE) {
10704 return; // no movement at all
10705 }
10706
10707 if ( pm->ps->pm_type == PM_INTERMISSION || pm->ps->pm_type == PM_SPINTERMISSION) {
10708 return; // no movement at all
10709 }
10710
10711 // set watertype, and waterlevel
10712 PM_SetWaterLevel();
10713 pml.previous_waterlevel = pmove->waterlevel;
10714
10715 // set mins, maxs, and viewheight
10716 PM_CheckDuck ();
10717
10718 if (pm->ps->pm_type == PM_JETPACK)
10719 {
10720 gDist = PM_GroundDistance();
10721 savedGravity = pm->ps->gravity;
10722
10723 if (gDist < JETPACK_HOVER_HEIGHT+64)
10724 {
10725 pm->ps->gravity *= 0.1f;
10726 }
10727 else
10728 {
10729 pm->ps->gravity *= 0.25f;
10730 }
10731 }
10732 else if (gPMDoSlowFall)
10733 {
10734 savedGravity = pm->ps->gravity;
10735 pm->ps->gravity *= 0.5;
10736 }
10737
10738 //if we're in jetpack mode then see if we should be jetting around
10739 if (pm->ps->pm_type == PM_JETPACK)
10740 {
10741 if (pm->cmd.rightmove > 0)
10742 {
10743 PM_ContinueLegsAnim(BOTH_INAIRRIGHT1);
10744 }
10745 else if (pm->cmd.rightmove < 0)
10746 {
10747 PM_ContinueLegsAnim(BOTH_INAIRLEFT1);
10748 }
10749 else if (pm->cmd.forwardmove > 0)
10750 {
10751 PM_ContinueLegsAnim(BOTH_INAIR1);
10752 }
10753 else if (pm->cmd.forwardmove < 0)
10754 {
10755 PM_ContinueLegsAnim(BOTH_INAIRBACK1);
10756 }
10757 else
10758 {
10759 PM_ContinueLegsAnim(BOTH_INAIR1);
10760 }
10761
10762 if (pm->ps->weapon == WP_SABER &&
10763 BG_SpinningSaberAnim( pm->ps->legsAnim ))
10764 { //make him stir around since he shouldn't have any real control when spinning
10765 pm->ps->velocity[0] += Q_irand(-100, 100);
10766 pm->ps->velocity[1] += Q_irand(-100, 100);
10767 }
10768
10769 if (pm->cmd.upmove > 0 && pm->ps->velocity[2] < 256)
10770 { //cap upward velocity off at 256. Seems reasonable.
10771 float addIn = 12.0f;
10772
10773 /*
10774 //Add based on our distance to the ground if we're already travelling upward
10775 if (pm->ps->velocity[2] > 0)
10776 {
10777 while (gDist > 64)
10778 { //subtract 1 for every 64 units off the ground we get
10779 addIn--;
10780
10781 gDist -= 64;
10782
10783 if (addIn <= 0)
10784 { //break out if we're not even going to add anything
10785 break;
10786 }
10787 }
10788 }
10789 */
10790 if (pm->ps->velocity[2] > 0)
10791 {
10792 addIn = 12.0f - (gDist / 64.0f);
10793 }
10794
10795 if (addIn > 0.0f)
10796 {
10797 pm->ps->velocity[2] += addIn;
10798 }
10799
10800 pm->ps->eFlags |= EF_JETPACK_FLAMING; //going up
10801 }
10802 else
10803 {
10804 pm->ps->eFlags &= ~EF_JETPACK_FLAMING; //idling
10805
10806 if (pm->ps->velocity[2] < 256)
10807 {
10808 if (pm->ps->velocity[2] < -100)
10809 {
10810 pm->ps->velocity[2] = -100;
10811 }
10812 if (gDist < JETPACK_HOVER_HEIGHT)
10813 { //make sure we're always hovering off the ground somewhat while jetpack is active
10814 pm->ps->velocity[2] += 2;
10815 }
10816 }
10817 }
10818 }
10819
10820 if (pm->ps->clientNum >= MAX_CLIENTS &&
10821 pm_entSelf && pm_entSelf->m_pVehicle)
10822 { //Now update our mins/maxs to match our m_vOrientation based on our length, width & height
10823 BG_VehicleAdjustBBoxForOrientation( pm_entSelf->m_pVehicle, pm->ps->origin, pm->mins, pm->maxs, pm->ps->clientNum, pm->tracemask, pm->trace );
10824 }
10825
10826 // set groundentity
10827 PM_GroundTrace();
10828 if ( pm_flying == FLY_HOVER )
10829 {//never stick to the ground
10830 PM_HoverTrace();
10831 }
10832
10833 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
10834 {//on ground
10835 pm->ps->fd.forceJumpZStart = 0;
10836 }
10837
10838 if ( pm->ps->pm_type == PM_DEAD ) {
10839 if (pm->ps->clientNum >= MAX_CLIENTS &&
10840 pm_entSelf &&
10841 pm_entSelf->s.NPC_class == CLASS_VEHICLE &&
10842 pm_entSelf->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL)
10843 {//vehicles don't use deadmove
10844 }
10845 else
10846 {
10847 PM_DeadMove ();
10848 }
10849 }
10850
10851 PM_DropTimers();
10852
10853 #ifdef _TESTING_VEH_PREDICTION
10854 #ifndef _GAME
10855 {
10856 vec3_t blah;
10857 VectorMA(pm->ps->origin, 128.0f, pm->ps->moveDir, blah);
10858 CG_TestLine(pm->ps->origin, blah, 1, 0x0000ff, 1);
10859
10860 VectorMA(pm->ps->origin, 1.0f, pm->ps->velocity, blah);
10861 CG_TestLine(pm->ps->origin, blah, 1, 0xff0000, 1);
10862 }
10863 #endif
10864 #endif
10865
10866 if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
10867 &&pm->ps->m_iVehicleNum)
10868 { //a player riding a vehicle
10869 bgEntity_t *veh = pm_entVeh;
10870
10871 if ( veh && veh->m_pVehicle &&
10872 (veh->m_pVehicle->m_pVehicleInfo->type == VH_WALKER || veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER) )
10873 {//*sigh*, until we get forced weapon-switching working?
10874 pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);
10875 pm->ps->eFlags &= ~(EF_FIRING|EF_ALT_FIRING);
10876 //pm->cmd.weapon = pm->ps->weapon;
10877 }
10878 }
10879
10880 if (!pm->ps->m_iVehicleNum &&
10881 pm_entSelf->s.NPC_class!=CLASS_VEHICLE&&
10882 pm_entSelf->s.NPC_class!=CLASS_RANCOR&&
10883 pm->ps->groundEntityNum < ENTITYNUM_WORLD &&
10884 pm->ps->groundEntityNum >= MAX_CLIENTS)
10885 { //I am a player client, not riding on a vehicle, and potentially standing on an NPC
10886 bgEntity_t *pEnt = PM_BGEntForNum(pm->ps->groundEntityNum);
10887
10888 if (pEnt && pEnt->s.eType == ET_NPC &&
10889 pEnt->s.NPC_class != CLASS_VEHICLE) //don't bounce on vehicles
10890 { //this is actually an NPC, let's try to bounce of its head to make sure we can't just stand around on top of it.
10891 if (pm->ps->velocity[2] < 270)
10892 { //try forcing velocity up and also force him to jump
10893 pm->ps->velocity[2] = 270; //seems reasonable
10894 pm->cmd.upmove = 127;
10895 }
10896 }
10897 #ifdef _GAME
10898 else if ( !pm->ps->zoomMode &&
10899 pm_entSelf //I exist
10900 && pEnt->m_pVehicle )//ent has a vehicle
10901 {
10902 gentity_t *gEnt = (gentity_t*)pEnt;
10903 if ( gEnt->client
10904 && !gEnt->client->ps.m_iVehicleNum //vehicle is empty
10905 && (gEnt->spawnflags&2) )//SUSPENDED
10906 {//it's a vehicle, see if we should get in it
10907 //if land on an empty, suspended vehicle, get in it
10908 pEnt->m_pVehicle->m_pVehicleInfo->Board( pEnt->m_pVehicle, (bgEntity_t *)pm_entSelf );
10909 }
10910 }
10911 #endif
10912 }
10913
10914 if (pm->ps->clientNum >= MAX_CLIENTS &&
10915 pm_entSelf &&
10916 pm_entSelf->s.NPC_class == CLASS_VEHICLE)
10917 { //we are a vehicle
10918 bgEntity_t *veh = pm_entSelf;
10919
10920 assert(veh && veh->playerState && veh->m_pVehicle && veh->s.number >= MAX_CLIENTS);
10921
10922 if (veh->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER)
10923 { //kind of hacky, don't want to do this for flying vehicles
10924 veh->m_pVehicle->m_vOrientation[PITCH] = pm->ps->viewangles[PITCH];
10925 }
10926
10927 if (!pm->ps->m_iVehicleNum)
10928 { //no one is driving, just update and get out
10929 #ifdef _GAME
10930 veh->m_pVehicle->m_pVehicleInfo->Update(veh->m_pVehicle, &pm->cmd);
10931 veh->m_pVehicle->m_pVehicleInfo->Animate(veh->m_pVehicle);
10932 #endif
10933 }
10934 else
10935 {
10936 bgEntity_t *self = pm_entVeh;
10937 #ifdef _GAME
10938 int i = 0;
10939 #endif
10940
10941 assert(self && self->playerState && self->s.number < MAX_CLIENTS);
10942
10943 if (pm->ps->pm_type == PM_DEAD &&
10944 (veh->m_pVehicle->m_ulFlags & VEH_CRASHING))
10945 {
10946 veh->m_pVehicle->m_ulFlags &= ~VEH_CRASHING;
10947 }
10948
10949 if (self->playerState->m_iVehicleNum)
10950 { //only do it if they still have a vehicle (didn't get ejected this update or something)
10951 PM_VehicleViewAngles(self->playerState, veh, &veh->m_pVehicle->m_ucmd);
10952 }
10953
10954 #ifdef _GAME
10955 veh->m_pVehicle->m_pVehicleInfo->Update(veh->m_pVehicle, &veh->m_pVehicle->m_ucmd);
10956 veh->m_pVehicle->m_pVehicleInfo->Animate(veh->m_pVehicle);
10957
10958 veh->m_pVehicle->m_pVehicleInfo->UpdateRider(veh->m_pVehicle, self, &veh->m_pVehicle->m_ucmd);
10959 //update the passengers
10960 while (i < veh->m_pVehicle->m_iNumPassengers)
10961 {
10962 if (veh->m_pVehicle->m_ppPassengers[i])
10963 {
10964 gentity_t *thePassenger = (gentity_t *)veh->m_pVehicle->m_ppPassengers[i]; //yes, this is, in fact, ass.
10965 if (thePassenger->inuse && thePassenger->client)
10966 {
10967 veh->m_pVehicle->m_pVehicleInfo->UpdateRider(veh->m_pVehicle, veh->m_pVehicle->m_ppPassengers[i], &thePassenger->client->pers.cmd);
10968 }
10969 }
10970 i++;
10971 }
10972 #else
10973 if (!veh->playerState->vehBoarding )//|| veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
10974 {
10975 if (veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
10976 { //client must explicitly call this for prediction
10977 BG_FighterUpdate(veh->m_pVehicle, &veh->m_pVehicle->m_ucmd, pm->mins, pm->maxs, self->playerState->gravity, pm->trace);
10978 }
10979
10980 if (veh->m_pVehicle->m_iBoarding == 0)
10981 {
10982 vec3_t vRollAng;
10983
10984 //make sure we are set as its pilot cgame side
10985 veh->m_pVehicle->m_pPilot = self;
10986
10987 // Keep track of the old orientation.
10988 VectorCopy( veh->m_pVehicle->m_vOrientation, veh->m_pVehicle->m_vPrevOrientation );
10989
10990 veh->m_pVehicle->m_pVehicleInfo->ProcessOrientCommands(veh->m_pVehicle);
10991 PM_SetPMViewAngle(veh->playerState, veh->m_pVehicle->m_vOrientation, &veh->m_pVehicle->m_ucmd);
10992 veh->m_pVehicle->m_pVehicleInfo->ProcessMoveCommands(veh->m_pVehicle);
10993
10994 vRollAng[YAW] = self->playerState->viewangles[YAW];
10995 vRollAng[PITCH] = self->playerState->viewangles[PITCH];
10996 vRollAng[ROLL] = veh->m_pVehicle->m_vOrientation[ROLL];
10997 PM_SetPMViewAngle(self->playerState, vRollAng, &pm->cmd);
10998
10999 // Setup the move direction.
11000 if ( veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
11001 {
11002 AngleVectors( veh->m_pVehicle->m_vOrientation, veh->playerState->moveDir, NULL, NULL );
11003 }
11004 else
11005 {
11006 vec3_t vVehAngles;
11007
11008 VectorSet(vVehAngles, 0, veh->m_pVehicle->m_vOrientation[YAW], 0);
11009 AngleVectors( vVehAngles, veh->playerState->moveDir, NULL, NULL );
11010 }
11011 }
11012 }
11013 /*
11014 else
11015 {
11016 veh->playerState->speed = 0.0f;
11017 PM_SetPMViewAngle(self->playerState, veh->playerState->viewangles, &veh->m_pVehicle->m_ucmd);
11018 }
11019 */
11020 else if (veh->playerState)
11021 {
11022 veh->playerState->speed = 0.0f;
11023 if (veh->m_pVehicle)
11024 {
11025 PM_SetPMViewAngle(self->playerState, veh->m_pVehicle->m_vOrientation, &pm->cmd);
11026 PM_SetPMViewAngle(veh->playerState, veh->m_pVehicle->m_vOrientation, &pm->cmd);
11027 }
11028 }
11029 #endif
11030 }
11031 noAnimate = qtrue;
11032 }
11033
11034 if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
11035 &&pm->ps->m_iVehicleNum)
11036 {//don't even run physics on a player if he's on a vehicle - he goes where the vehicle goes
11037 }
11038 else
11039 { //don't even run physics on a player if he's on a vehicle - he goes where the vehicle goes
11040 if (pm->ps->pm_type == PM_FLOAT
11041 ||pm_flying == FLY_NORMAL)
11042 {
11043 PM_FlyMove ();
11044 }
11045 else if ( pm_flying == FLY_VEHICLE )
11046 {
11047 PM_FlyVehicleMove();
11048 }
11049 else
11050 {
11051 if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) {
11052 PM_WaterJumpMove();
11053 } else if ( pm->waterlevel > 1 ) {
11054 // swimming
11055 PM_WaterMove();
11056 } else if ( pml.walking ) {
11057 // walking on ground
11058 PM_WalkMove();
11059 } else {
11060 // airborne
11061 PM_AirMove();
11062 }
11063 }
11064 }
11065
11066 if (!noAnimate)
11067 {
11068 PM_Animate();
11069 }
11070
11071 // set groundentity, watertype, and waterlevel
11072 PM_GroundTrace();
11073 if ( pm_flying == FLY_HOVER )
11074 {//never stick to the ground
11075 PM_HoverTrace();
11076 }
11077 PM_SetWaterLevel();
11078 if (pm->cmd.forcesel != (byte)-1 && (pm->ps->fd.forcePowersKnown & (1 << pm->cmd.forcesel)))
11079 {
11080 pm->ps->fd.forcePowerSelected = pm->cmd.forcesel;
11081 }
11082 if (pm->cmd.invensel != (byte)-1 && (pm->ps->stats[STAT_HOLDABLE_ITEMS] & (1 << pm->cmd.invensel)))
11083 {
11084 pm->ps->stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(pm->cmd.invensel, IT_HOLDABLE);
11085 }
11086
11087 if (pm->ps->m_iVehicleNum
11088 /*&&pm_entSelf->s.NPC_class!=CLASS_VEHICLE*/
11089 && pm->ps->clientNum < MAX_CLIENTS)
11090 {//a client riding a vehicle
11091 if ( (pm->ps->eFlags&EF_NODRAW) )
11092 {//inside the vehicle, do nothing
11093 }
11094 else if (!PM_WeaponOkOnVehicle(pm->cmd.weapon) || !PM_WeaponOkOnVehicle(pm->ps->weapon))
11095 { //this weapon is not legal for the vehicle, force to our current one
11096 if (!PM_WeaponOkOnVehicle(pm->ps->weapon))
11097 { //uh-oh!
11098 int weap = PM_GetOkWeaponForVehicle();
11099
11100 if (weap != -1)
11101 {
11102 pm->cmd.weapon = weap;
11103 pm->ps->weapon = weap;
11104 }
11105 }
11106 else
11107 {
11108 pm->cmd.weapon = pm->ps->weapon;
11109 }
11110 }
11111 }
11112
11113 if (!pm->ps->m_iVehicleNum //not a vehicle and not riding one
11114 || pm_entSelf->s.NPC_class==CLASS_VEHICLE //you are a vehicle NPC
11115 || (!(pm->ps->eFlags&EF_NODRAW)&&PM_WeaponOkOnVehicle(pm->cmd.weapon)) )//you're not inside the vehicle and the weapon you're holding can be used when riding this vehicle
11116 { //only run weapons if a valid weapon is selected
11117 // weapons
11118 PM_Weapon();
11119 }
11120
11121 PM_Use();
11122
11123 if (!pm->ps->m_iVehicleNum &&
11124 (pm->ps->clientNum < MAX_CLIENTS ||
11125 !pm_entSelf ||
11126 pm_entSelf->s.NPC_class != CLASS_VEHICLE))
11127 { //don't do this if we're on a vehicle, or we are one
11128 // footstep events / legs animations
11129 PM_Footsteps();
11130 }
11131
11132 // entering / leaving water splashes
11133 PM_WaterEvents();
11134
11135 // snap velocity to integer coordinates to save network bandwidth
11136 if ( !pm->pmove_float )
11137 trap->SnapVector( pm->ps->velocity );
11138
11139 if (pm->ps->pm_type == PM_JETPACK || gPMDoSlowFall )
11140 {
11141 pm->ps->gravity = savedGravity;
11142 }
11143
11144 if (//pm->ps->m_iVehicleNum &&
11145 pm->ps->clientNum >= MAX_CLIENTS &&
11146 pm_entSelf &&
11147 pm_entSelf->s.NPC_class == CLASS_VEHICLE)
11148 { //a vehicle with passengers
11149 bgEntity_t *veh;
11150
11151 veh = pm_entSelf;
11152
11153 assert(veh->m_pVehicle);
11154
11155 //this could be kind of "inefficient" because it's called after every passenger pmove too.
11156 //Maybe instead of AttachRiders we should have each rider call attach for himself?
11157 if (veh->m_pVehicle && veh->ghoul2)
11158 {
11159 veh->m_pVehicle->m_pVehicleInfo->AttachRiders( veh->m_pVehicle );
11160 }
11161 }
11162
11163 if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
11164 && pm->ps->m_iVehicleNum)
11165 { //riding a vehicle, see if we should do some anim overrides
11166 PM_VehicleWeaponAnimate();
11167 }
11168 }
11169
11170
11171 /*
11172 ================
11173 Pmove
11174
11175 Can be called by either the server or the client
11176 ================
11177 */
Pmove(pmove_t * pmove)11178 void Pmove (pmove_t *pmove) {
11179 int finalTime;
11180
11181 finalTime = pmove->cmd.serverTime;
11182
11183 if ( finalTime < pmove->ps->commandTime ) {
11184 return; // should not happen
11185 }
11186
11187 if ( finalTime > pmove->ps->commandTime + 1000 ) {
11188 pmove->ps->commandTime = finalTime - 1000;
11189 }
11190
11191 if (pmove->ps->fallingToDeath)
11192 {
11193 pmove->cmd.forwardmove = 0;
11194 pmove->cmd.rightmove = 0;
11195 pmove->cmd.upmove = 0;
11196 pmove->cmd.buttons = 0;
11197 }
11198
11199 pmove->ps->pmove_framecount = (pmove->ps->pmove_framecount+1) & ((1<<PS_PMOVEFRAMECOUNTBITS)-1);
11200
11201 // chop the move up if it is too long, to prevent framerate
11202 // dependent behavior
11203 while ( pmove->ps->commandTime != finalTime ) {
11204 int msec;
11205
11206 msec = finalTime - pmove->ps->commandTime;
11207
11208 if ( pmove->pmove_fixed ) {
11209 if ( msec > pmove->pmove_msec ) {
11210 msec = pmove->pmove_msec;
11211 }
11212 }
11213 else {
11214 if ( msec > 66 ) {
11215 msec = 66;
11216 }
11217 }
11218 pmove->cmd.serverTime = pmove->ps->commandTime + msec;
11219
11220 PmoveSingle( pmove );
11221
11222 if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) {
11223 pmove->cmd.upmove = 20;
11224 }
11225 }
11226 }
11227
11228