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 #include "common_headers.h"
25
26 #include "../rd-common/tr_public.h"
27
28
29 // bg_pmove.c -- both games player movement code
30 // takes a playerstate and a usercmd as input and returns a modifed playerstate
31
32 // define GAME_INCLUDE so that g_public.h does not define the
33 // short, server-visible gclient_t and gentity_t structures,
34 // because we define the full size ones in this file
35
36 #define GAME_INCLUDE
37 #include "../qcommon/q_shared.h"
38 #include "g_shared.h"
39 #include "bg_local.h"
40 #include "g_local.h"
41 #include "g_functions.h"
42 #include "anims.h"
43 #include "../cgame/cg_local.h" // yeah I know this is naughty, but we're shipping soon...
44
45 #include "wp_saber.h"
46 #include "g_vehicles.h"
47 #include <float.h>
48
49 extern qboolean G_DoDismemberment( gentity_t *self, vec3_t point, int mod, int damage, int hitLoc, qboolean force = qfalse );
50 extern qboolean G_EntIsUnlockedDoor( int entityNum );
51 extern qboolean G_EntIsDoor( int entityNum );
52 extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f );
53 extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
54 extern qboolean Q3_TaskIDPending( gentity_t *ent, taskID_t taskType );
55 extern qboolean WP_SaberLose( gentity_t *self, vec3_t throwDir );
56 extern int Jedi_ReCalcParryTime( gentity_t *self, evasionType_t evasionType );
57 extern qboolean PM_HasAnimation( gentity_t *ent, int animation );
58 extern saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveName_t newmove );
59 extern saberMoveName_t PM_AttackMoveForQuad( int quad );
60 extern qboolean PM_SaberInTransition( int move );
61 extern qboolean PM_SaberInTransitionAny( int move );
62 extern qboolean PM_SaberInBounce( int move );
63 extern qboolean PM_SaberInSpecialAttack( int anim );
64 extern qboolean PM_SaberInAttack( int move );
65 extern qboolean PM_InAnimForSaberMove( int anim, int saberMove );
66 extern saberMoveName_t PM_SaberBounceForAttack( int move );
67 extern saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int curmove );
68 extern saberMoveName_t PM_BrokenParryForParry( int move );
69 extern saberMoveName_t PM_KnockawayForParry( int move );
70 extern qboolean PM_SaberInParry( int move );
71 extern qboolean PM_SaberInKnockaway( int move );
72 extern qboolean PM_SaberInBrokenParry( int move );
73 extern qboolean PM_SaberInReflect( int move );
74 extern qboolean PM_SaberInIdle( int move );
75 extern qboolean PM_SaberInStart( int move );
76 extern qboolean PM_SaberInReturn( int move );
77 extern qboolean PM_SaberKataDone( int curmove, int newmove );
78 extern qboolean PM_SaberInSpecial( int move );
79 extern qboolean PM_InDeathAnim ( void );
80 extern qboolean PM_StandingAnim( int anim );
81 extern qboolean PM_KickMove( int move );
82 extern qboolean PM_KickingAnim( int anim );
83 extern qboolean PM_InAirKickingAnim( int anim );
84 extern qboolean PM_SuperBreakLoseAnim( int anim );
85 extern qboolean PM_SuperBreakWinAnim( int anim );
86 extern qboolean PM_InCartwheel( int anim );
87 extern qboolean PM_InButterfly( int anim );
88 extern qboolean PM_CanRollFromSoulCal( playerState_t *ps );
89 extern saberMoveName_t PM_SaberFlipOverAttackMove( void );
90 extern qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy );
91 extern saberMoveName_t PM_SaberJumpForwardAttackMove( void );
92 extern qboolean PM_CheckJumpForwardAttackMove( void );
93 extern saberMoveName_t PM_SaberBackflipAttackMove( void );
94 extern qboolean PM_CheckBackflipAttackMove( void );
95 extern saberMoveName_t PM_SaberDualJumpAttackMove( void );
96 extern qboolean PM_CheckDualJumpAttackMove( void );
97 extern saberMoveName_t PM_SaberLungeAttackMove( qboolean fallbackToNormalLunge );
98 extern qboolean PM_CheckLungeAttackMove( void );
99 extern qboolean PM_InSecondaryStyle( void );
100 extern qboolean PM_KnockDownAnimExtended( int anim );
101 extern void G_StartMatrixEffect( gentity_t *ent, int meFlags = 0, int length = 1000, float timeScale = 0.0f, int spinTime = 0 );
102 extern void WP_ForcePowerStop( gentity_t *self, forcePowers_t forcePower );
103 extern qboolean WP_ForcePowerAvailable( gentity_t *self, forcePowers_t forcePower, int overrideAmt );
104 extern void WP_ForcePowerDrain( gentity_t *self, forcePowers_t forcePower, int overrideAmt );
105 extern float G_ForceWallJumpStrength( void );
106 extern qboolean G_CheckRollSafety( gentity_t *self, int anim, float testDist );
107 extern saberMoveName_t PM_CheckDualSpinProtect( void );
108 extern saberMoveName_t PM_CheckPullAttack( void );
109 extern qboolean JET_Flying( gentity_t *self );
110 extern void JET_FlyStart( gentity_t *self );
111 extern void JET_FlyStop( gentity_t *self );
112 extern qboolean PM_LockedAnim( int anim );
113 extern qboolean G_TryingKataAttack( gentity_t *self, usercmd_t *cmd );
114 extern qboolean G_TryingCartwheel( gentity_t *self, usercmd_t *cmd );
115 extern qboolean G_TryingSpecial( gentity_t *self, usercmd_t *cmd );
116 extern qboolean G_TryingJumpAttack( gentity_t *self, usercmd_t *cmd );
117 extern qboolean G_TryingJumpForwardAttack( gentity_t *self, usercmd_t *cmd );
118 extern void WP_SaberSwingSound( gentity_t *ent, int saberNum, swingType_t swingType );
119 extern qboolean WP_UseFirstValidSaberStyle( gentity_t *ent, int *saberAnimLevel );
120 extern qboolean WP_SaberStyleValidForSaber( gentity_t *ent, int saberAnimLevel );
121
122 qboolean PM_InKnockDown( playerState_t *ps );
123 qboolean PM_InKnockDownOnGround( playerState_t *ps );
124 qboolean PM_InGetUp( playerState_t *ps );
125 qboolean PM_InRoll( playerState_t *ps );
126 qboolean PM_SpinningSaberAnim( int anim );
127 qboolean PM_GettingUpFromKnockDown( float standheight, float crouchheight );
128 qboolean PM_SpinningAnim( int anim );
129 qboolean PM_FlippingAnim( int anim );
130 qboolean PM_PainAnim( int anim );
131 qboolean PM_RollingAnim( int anim );
132 qboolean PM_SwimmingAnim( int anim );
133 qboolean PM_InReboundJump( int anim );
134 qboolean PM_ForceJumpingAnim( int anim );
135 void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd );
136
137 extern int parryDebounce[];
138 extern qboolean cg_usingInFrontOf;
139 extern qboolean player_locked;
140 extern qboolean MatrixMode;
141 qboolean waterForceJump;
142 extern cvar_t *g_timescale;
143 extern cvar_t *g_speederControlScheme;
144 extern cvar_t *d_slowmodeath;
145 extern cvar_t *g_debugMelee;
146 extern cvar_t *g_saberNewControlScheme;
147 extern cvar_t *g_stepSlideFix;
148 extern cvar_t *g_saberAutoBlocking;
149
150 static void PM_SetWaterLevelAtPoint( vec3_t org, int *waterlevel, int *watertype );
151
152 #define FLY_NONE 0
153 #define FLY_NORMAL 1
154 #define FLY_VEHICLE 2
155 #define FLY_HOVER 3
156 int Flying = FLY_NONE;
157
158 pmove_t *pm;
159 pml_t pml;
160
161 // movement parameters
162 const float pm_stopspeed = 100.0f;
163 const float pm_duckScale = 0.50f;
164 const float pm_swimScale = 0.50f;
165 float pm_ladderScale = 0.7f;
166
167 const float pm_vehicleaccelerate = 36.0f;
168 const float pm_accelerate = 12.0f;
169 const float pm_airaccelerate = 4.0f;
170 const float pm_wateraccelerate = 4.0f;
171 const float pm_flyaccelerate = 8.0f;
172
173 const float pm_friction = 6.0f;
174 const float pm_waterfriction = 1.0f;
175 const float pm_flightfriction = 3.0f;
176
177 //const float pm_frictionModifier = 3.0f; //Used for "careful" mode (when pressing use)
178 const float pm_airDecelRate = 1.35f; //Used for air decelleration away from current movement velocity
179
180 int c_pmove = 0;
181
182 extern void PM_SetTorsoAnimTimer( gentity_t *ent, int *torsoAnimTimer, int time );
183 extern void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time );
184 extern void PM_TorsoAnimation( void );
185 extern int PM_TorsoAnimForFrame( gentity_t *ent, int torsoFrame );
186 extern int PM_AnimLength( int index, animNumber_t anim );
187 extern qboolean PM_InDeathAnim ( void );
188 extern qboolean PM_InOnGroundAnim ( playerState_t *ps );
189 extern weaponInfo_t cg_weapons[MAX_WEAPONS];
190 extern int PM_PickAnim( gentity_t *self, int minAnim, int maxAnim );
191
192 extern void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf, trace_t *trace );
193
194 #define PHASER_RECHARGE_TIME 100
195 extern saberMoveName_t transitionMove[Q_NUM_QUADS][Q_NUM_QUADS];
196
197 extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent );
PM_RidingVehicle(void)198 Vehicle_t *PM_RidingVehicle( void )
199 {
200 return (G_IsRidingVehicle( pm->gent ));
201 }
202
203 extern qboolean G_ControlledByPlayer( gentity_t *self );
PM_ControlledByPlayer(void)204 qboolean PM_ControlledByPlayer( void )
205 {
206 return G_ControlledByPlayer( pm->gent );
207 }
208
BG_UnrestrainedPitchRoll(playerState_t * ps,Vehicle_t * pVeh)209 qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh )
210 {
211 /*
212 if ( 0 && ps->clientNum < MAX_CLIENTS //real client
213 && ps->m_iVehicleNum//in a vehicle
214 && pVeh //valid vehicle data pointer
215 && pVeh->m_pVehicleInfo//valid vehicle info
216 && pVeh->m_pVehicleInfo->type == VH_FIGHTER )//fighter
217 //FIXME: specify per vehicle instead of assuming true for all fighters
218 //FIXME: map/server setting?
219 {//can roll and pitch without limitation!
220 return qtrue;
221 }*/
222 return qfalse;
223 }
224
225
226 /*
227 ===============
228 PM_AddEvent
229
230 ===============
231 */
PM_AddEvent(int newEvent)232 void PM_AddEvent( int newEvent )
233 {
234 AddEventToPlayerstate( newEvent, 0, pm->ps );
235 }
236
PM_PredictJumpSafe(vec3_t jumpHorizDir,float jumpHorizSpeed,float jumpVertSpeed,int predictTimeLength)237 qboolean PM_PredictJumpSafe( vec3_t jumpHorizDir, float jumpHorizSpeed, float jumpVertSpeed, int predictTimeLength )
238 {
239 return qtrue;
240 }
241
242
PM_GrabWallForJump(int anim)243 void PM_GrabWallForJump( int anim )
244 {//NOTE!!! assumes an appropriate anim is being passed in!!!
245 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_RESTART|SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
246 PM_AddEvent( EV_JUMP );//make sound for grab
247 pm->ps->pm_flags |= PMF_STUCK_TO_WALL;
248 }
249
PM_CheckGrabWall(trace_t * trace)250 qboolean PM_CheckGrabWall( trace_t *trace )
251 {
252 if ( !pm->gent || !pm->gent->client )
253 {
254 return qfalse;
255 }
256 if ( pm->gent->health <= 0 )
257 {//must be alive
258 return qfalse;
259 }
260 if ( pm->gent->client->ps.groundEntityNum != ENTITYNUM_NONE )
261 {//must be in air
262 return qfalse;
263 }
264 if ( trace->plane.normal[2] != 0 )
265 {//must be a flat wall
266 return qfalse;
267 }
268 if ( !trace->plane.normal[0] && !trace->plane.normal[1] )
269 {//invalid normal
270 return qfalse;
271 }
272 if ( (trace->contents&(CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP)) )
273 {//can't jump off of clip brushes
274 return qfalse;
275 }
276 if ( pm->gent->client->ps.forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_1 )
277 {//must have at least FJ 1
278 return qfalse;
279 }
280 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
281 && pm->gent->client->ps.forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_3 )
282 {//player must have force jump 3
283 return qfalse;
284 }
285 if ( (pm->ps->saber[0].saberFlags&SFL_NO_WALL_GRAB) )
286 {
287 return qfalse;
288 }
289 if ( pm->ps->dualSabers
290 && (pm->ps->saber[1].saberFlags&SFL_NO_WALL_GRAB) )
291 {
292 return qfalse;
293 }
294 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
295 {//player
296 //only if we were in a longjump
297 if ( pm->ps->legsAnim != BOTH_FORCELONGLEAP_START
298 && pm->ps->legsAnim != BOTH_FORCELONGLEAP_ATTACK )
299 {
300 return qfalse;
301 }
302 //hit a flat wall during our long jump, see if we should grab it
303 vec3_t moveDir;
304 VectorCopy( pm->ps->velocity, moveDir );
305 VectorNormalize( moveDir );
306 if ( DotProduct( moveDir, trace->plane.normal ) > -0.65f )
307 {//not enough of a direct impact, just slide off
308 return qfalse;
309 }
310 if ( fabs(trace->plane.normal[2]) > MAX_WALL_GRAB_SLOPE )
311 {
312 return qfalse;
313 }
314 //grab it!
315 //FIXME: stop Matrix effect!
316 VectorClear( pm->ps->velocity );
317 //FIXME: stop slidemove!
318 //NOTE: we know it's forward, so...
319 PM_GrabWallForJump( BOTH_FORCEWALLREBOUND_FORWARD );
320 return qtrue;
321 }
322 else
323 {//NPCs
324 if ( PM_InReboundJump( pm->ps->legsAnim ) )
325 {//already in a rebound!
326 return qfalse;
327 }
328 if ( (pm->ps->eFlags&EF_FORCE_GRIPPED) )
329 {//being gripped!
330 return qfalse;
331 }
332 /*
333 if ( pm->gent->painDebounceTime > level.time )
334 {//can't move!
335 return qfalse;
336 }
337 if ( (pm->ps->pm_flags&PMF_TIME_KNOCKBACK) )
338 {//being thrown back!
339 return qfalse;
340 }
341 */
342 if ( pm->gent->NPC && (pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) )
343 {//faling to our death!
344 return qfalse;
345 }
346 //FIXME: random chance, based on skill/rank?
347 if ( pm->ps->legsAnim != BOTH_FORCELONGLEAP_START
348 && pm->ps->legsAnim != BOTH_FORCELONGLEAP_ATTACK )
349 {//not in a long-jump
350 if ( !pm->gent->enemy )
351 {//no enemy
352 return qfalse;
353 }
354 else
355 {//see if the enemy is in the direction of the wall or above us
356 //if ( pm->gent->enemy->currentOrigin[2] < (pm->ps->origin[2]-128) )
357 {//enemy is way below us
358 vec3_t enemyDir;
359 VectorSubtract( pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir );
360 enemyDir[2] = 0;
361 VectorNormalize( enemyDir );
362 if ( DotProduct( enemyDir, trace->plane.normal ) < 0.65f )
363 {//jumping off this wall would not launch me in the general direction of my enemy
364 return qfalse;
365 }
366 }
367 }
368 }
369 //FIXME: check for ground close beneath us?
370 //FIXME: check for obstructions in the dir we're going to jump
371 // - including "do not enter" brushes!
372 //hit a flat wall during our long jump, see if we should grab it
373 vec3_t moveDir;
374 VectorCopy( pm->ps->velocity, moveDir );
375 VectorNormalize( moveDir );
376 if ( DotProduct( moveDir, trace->plane.normal ) > -0.65f )
377 {//not enough of a direct impact, just slide off
378 return qfalse;
379 }
380
381 //Okay, now see if jumping off this thing would send us into a do not enter brush
382 if ( !PM_PredictJumpSafe( trace->plane.normal, JUMP_OFF_WALL_SPEED, G_ForceWallJumpStrength(), 1500 ) )
383 {//we would hit a do not enter brush, so don't grab the wall
384 return qfalse;
385 }
386
387 //grab it!
388 //Pick the proper anim
389 int anim = BOTH_FORCEWALLREBOUND_FORWARD;
390 vec3_t facingAngles, wallDir, fwdDir, rtDir;
391 VectorSubtract( trace->endpos, pm->gent->lastOrigin, wallDir );
392 wallDir[2] = 0;
393 VectorNormalize( wallDir );
394 VectorSet( facingAngles, 0, pm->ps->viewangles[YAW], 0 );
395 AngleVectors( facingAngles, fwdDir, rtDir, NULL );
396 float fDot = DotProduct( fwdDir, wallDir );
397 if ( fabs( fDot ) >= 0.5f )
398 {//hit a wall in front/behind
399 if ( fDot > 0.0f )
400 {//in front
401 anim = BOTH_FORCEWALLREBOUND_FORWARD;
402 }
403 else
404 {//behind
405 anim = BOTH_FORCEWALLREBOUND_BACK;
406 }
407 }
408 else if ( DotProduct( rtDir, wallDir ) > 0 )
409 {//hit a wall on the right
410 anim = BOTH_FORCEWALLREBOUND_RIGHT;
411 }
412 else
413 {//hit a wall on the left
414 anim = BOTH_FORCEWALLREBOUND_LEFT;
415 }
416 VectorClear( pm->ps->velocity );
417 //FIXME: stop slidemove!
418 PM_GrabWallForJump( anim );
419 return qtrue;
420 }
421 //return qfalse;
422 }
423 /*
424 ===============
425 qboolean PM_ClientImpact( trace_t *trace, qboolean damageSelf )
426
427 ===============
428 */
PM_ClientImpact(trace_t * trace,qboolean damageSelf)429 qboolean PM_ClientImpact( trace_t *trace, qboolean damageSelf )
430 {
431 gentity_t *traceEnt;
432 int otherEntityNum = trace->entityNum;
433
434 if ( !pm->gent )
435 {
436 return qfalse;
437 }
438
439 traceEnt = &g_entities[otherEntityNum];
440
441 if ( otherEntityNum == ENTITYNUM_WORLD
442 || (traceEnt->bmodel && traceEnt->s.pos.trType == TR_STATIONARY ) )
443 {//hit world or a non-moving brush
444 if ( PM_CheckGrabWall( trace ) )
445 {//stopped on the wall
446 return qtrue;
447 }
448 }
449
450 if( (VectorLength( pm->ps->velocity )*(pm->gent->mass/10)) >= 100 && (pm->gent->client->NPC_class == CLASS_VEHICLE || pm->ps->lastOnGround+100<level.time) )//was 300 ||(other->material>=MAT_GLASS&&pm->gent->lastImpact+100<=level.time))
451 {
452 DoImpact( pm->gent, &g_entities[otherEntityNum], damageSelf, trace );
453 }
454
455 if ( otherEntityNum >= ENTITYNUM_WORLD )
456 {
457 return qfalse;
458 }
459
460 if ( !traceEnt || !(traceEnt->contents&pm->tracemask) )
461 {//it's dead or not in my way anymore
462 return qtrue;
463 }
464
465 return qfalse;
466 }
467 /*
468 ===============
469 PM_AddTouchEnt
470 ===============
471 */
PM_AddTouchEnt(int entityNum)472 void PM_AddTouchEnt( int entityNum ) {
473 int i;
474
475 if ( entityNum == ENTITYNUM_WORLD ) {
476 return;
477 }
478 if ( pm->numtouch == MAXTOUCH ) {
479 return;
480 }
481
482 // see if it is already added
483 for ( i = 0 ; i < pm->numtouch ; i++ ) {
484 if ( pm->touchents[ i ] == entityNum ) {
485 return;
486 }
487 }
488
489 // add it
490 pm->touchents[pm->numtouch] = entityNum;
491 pm->numtouch++;
492 }
493
494
495
496
497 /*
498 ==================
499 PM_ClipVelocity
500
501 Slide off of the impacting surface
502
503 This will pull you down onto slopes if heading away from
504 them and push you up them as you go up them.
505 Also stops you when you hit walls.
506
507 ==================
508 */
PM_ClipVelocity(vec3_t in,vec3_t normal,vec3_t out,float overbounce)509 void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) {
510 float backoff;
511 float change;
512 float oldInZ;
513 int i;
514
515 if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
516 {//no sliding!
517 VectorCopy( in, out );
518 return;
519 }
520 oldInZ = in[2];
521
522 backoff = DotProduct (in, normal);
523
524 if ( backoff < 0 ) {
525 backoff *= overbounce;
526 } else {
527 backoff /= overbounce;
528 }
529
530 for ( i=0 ; i<3 ; i++ )
531 {
532 change = normal[i]*backoff;
533 /*
534 if ( i == 2 && Flying == FLY_HOVER && change > 0 )
535 {//don't pull a hovercraft down
536 change = 0;
537 }
538 else
539 */
540 {
541 out[i] = in[i] - change;
542 }
543 }
544 if ( g_stepSlideFix->integer )
545 {
546 if ( pm->ps->clientNum < MAX_CLIENTS//normal player
547 && normal[2] < MIN_WALK_NORMAL )//sliding against a steep slope
548 {
549 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )//on the ground
550 {//if walking on the ground, don't slide up slopes that are too steep to walk on
551 out[2] = oldInZ;
552 }
553 }
554 }
555 }
556
557
558 /*
559 ==================
560 PM_Friction
561
562 Handles both ground friction and water friction
563 ==================
564 */
PM_Friction(void)565 static void PM_Friction( void ) {
566 vec3_t vec;
567 float *vel;
568 float speed, newspeed, control;
569 float drop, friction = pm->ps->friction;
570
571 vel = pm->ps->velocity;
572
573 VectorCopy( vel, vec );
574 if ( pml.walking ) {
575 vec[2] = 0; // ignore slope movement
576 }
577
578 speed = VectorLength(vec);
579 if (speed < 1) {
580 vel[0] = 0;
581 vel[1] = 0; // allow sinking underwater
582 // FIXME: still have z friction underwater?
583 return;
584 }
585
586 drop = 0;
587
588 // apply ground friction, even if on ladder
589 if ( pm->gent
590 && pm->gent->client
591 && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->gent->m_pVehicle
592 && pm->gent->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL )
593 {
594 friction = pm->gent->m_pVehicle->m_pVehicleInfo->friction;
595
596 if ( pm->gent->m_pVehicle && pm->gent->m_pVehicle->m_pVehicleInfo->hoverHeight > 0 )
597 {//in a hovering vehicle, have air control
598 if ( pm->gent->m_pVehicle->m_ulFlags & VEH_FLYING )
599 {
600 friction = 0.10f;
601 }
602 }
603
604 if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) && !(pm->ps->pm_flags & PMF_TIME_NOFRICTION) )
605 {
606 control = speed < pm_stopspeed ? pm_stopspeed : speed;
607 drop += control*friction*pml.frametime;
608 /*
609 if ( Flying == FLY_HOVER )
610 {
611 if ( pm->cmd.rightmove )
612 {//if turning, increase friction
613 control *= 2.0f;
614 }
615 if ( pm->ps->groundEntityNum < ENTITYNUM_NONE )
616 {//on the ground
617 drop += control*friction*pml.frametime;
618 }
619 else if ( pml.groundPlane )
620 {//on a slope
621 drop += control*friction*2.0f*pml.frametime;
622 }
623 else
624 {//in air
625 drop += control*2.0f*friction*pml.frametime;
626 }
627 }
628 */
629 }
630 }
631 else if ( Flying != FLY_NORMAL )
632 {
633 if ( (pm->watertype & CONTENTS_LADDER) || pm->waterlevel <= 1 )
634 {
635 if ( (pm->watertype & CONTENTS_LADDER) || (pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK)) )
636 {
637 // if getting knocked back, no friction
638 if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) && !(pm->ps->pm_flags & PMF_TIME_NOFRICTION) )
639 {
640 if ( pm->ps->legsAnim == BOTH_FORCELONGLEAP_START
641 || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK
642 || pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND )
643 {//super forward jump
644 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
645 {//not in air
646 if ( pm->cmd.forwardmove < 0 )
647 {//trying to hold back some
648 friction *= 0.5f;//0.25f;
649 }
650 else
651 {//free slide
652 friction *= 0.2f;//0.1f;
653 }
654 pm->cmd.forwardmove = pm->cmd.rightmove = 0;
655 if ( pml.groundPlane && pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND )
656 {
657 //slide effect
658 G_PlayEffect( "env/slide_dust", pml.groundTrace.endpos, pml.groundTrace.plane.normal );
659 //FIXME: slide sound
660 }
661 }
662 }
663 /*
664 else if ( pm->cmd.buttons & BUTTON_USE )
665 {//If the use key is pressed. slow the player more quickly
666 if ( pm->gent->client->NPC_class != CLASS_VEHICLE ) // if not in a vehicle...
667 {//in a vehicle, use key makes you turbo-boost
668 friction *= pm_frictionModifier;
669 }
670 }
671 */
672
673 control = speed < pm_stopspeed ? pm_stopspeed : speed;
674 drop += control*friction*pml.frametime;
675 }
676 }
677 }
678 }
679 else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
680 && pm->gent
681 && pm->gent->client
682 && (pm->gent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) && pm->gent->client->moveType == MT_FLYSWIM )
683 {//player as Boba
684 drop += speed*pm_waterfriction*pml.frametime;
685 }
686
687 if ( Flying == FLY_VEHICLE )
688 {
689 if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) && !(pm->ps->pm_flags & PMF_TIME_NOFRICTION) )
690 {
691 control = speed < pm_stopspeed ? pm_stopspeed : speed;
692 drop += control*friction*pml.frametime;
693 }
694 }
695
696 // apply water friction even if just wading
697 if ( !waterForceJump )
698 {
699 if ( pm->waterlevel && !(pm->watertype & CONTENTS_LADDER))
700 {
701 drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
702 }
703 }
704
705 // apply flying friction
706 if ( pm->ps->pm_type == PM_SPECTATOR )
707 {
708 drop += speed*pm_flightfriction*pml.frametime;
709 }
710
711 // scale the velocity
712 newspeed = speed - drop;
713 if (newspeed < 0)
714 newspeed = 0;
715
716 newspeed /= speed;
717
718 vel[0] = vel[0] * newspeed;
719 vel[1] = vel[1] * newspeed;
720 vel[2] = vel[2] * newspeed;
721 }
722
723
724 /*
725 ==============
726 PM_Accelerate
727
728 Handles user intended acceleration
729 ==============
730 */
731
PM_Accelerate(vec3_t wishdir,float wishspeed,float accel)732 static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel )
733 {
734 int i;
735 float addspeed, accelspeed, currentspeed;
736
737 currentspeed = DotProduct (pm->ps->velocity, wishdir);
738
739 addspeed = wishspeed - currentspeed;
740
741 if (addspeed <= 0) {
742 return;
743 }
744 accelspeed = ( accel * pml.frametime ) * wishspeed;
745
746 if (accelspeed > addspeed) {
747 accelspeed = addspeed;
748 }
749 for (i=0 ; i<3 ; i++) {
750 pm->ps->velocity[i] += accelspeed * wishdir[i];
751 }
752 }
753
754 /*
755 ============
756 PM_CmdScale
757
758 Returns the scale factor to apply to cmd movements
759 This allows the clients to use axial -127 to 127 values for all directions
760 without getting a sqrt(2) distortion in speed.
761 ============
762 */
PM_CmdScale(usercmd_t * cmd)763 static float PM_CmdScale( usercmd_t *cmd )
764 {
765 int max;
766 float total;
767 float scale;
768
769 max = abs( cmd->forwardmove );
770
771 if ( abs( cmd->rightmove ) > max ) {
772 max = abs( cmd->rightmove );
773 }
774 if ( abs( cmd->upmove ) > max ) {
775 max = abs( cmd->upmove );
776 }
777 if ( !max ) {
778 return 0;
779 }
780 total = sqrt( (float)(( cmd->forwardmove * cmd->forwardmove )
781 + ( cmd->rightmove * cmd->rightmove )
782 + ( cmd->upmove * cmd->upmove )) );
783
784 scale = (float) pm->ps->speed * max / ( 127.0f * total );
785
786 return scale;
787 }
788
789
790 /*
791 ================
792 PM_SetMovementDir
793
794 Determine the rotation of the legs reletive
795 to the facing dir
796 ================
797 */
PM_SetMovementDir(void)798 static void PM_SetMovementDir( void ) {
799 if ( pm->cmd.forwardmove || pm->cmd.rightmove ) {
800 if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) {
801 pm->ps->movementDir = 0;
802 } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) {
803 pm->ps->movementDir = 1;
804 } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) {
805 pm->ps->movementDir = 2;
806 } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) {
807 pm->ps->movementDir = 3;
808 } else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) {
809 pm->ps->movementDir = 4;
810 } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) {
811 pm->ps->movementDir = 5;
812 } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) {
813 pm->ps->movementDir = 6;
814 } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) {
815 pm->ps->movementDir = 7;
816 }
817 } else {
818 // if they aren't actively going directly sideways,
819 // change the animation to the diagonal so they
820 // don't stop too crooked
821 if ( pm->ps->movementDir == 2 ) {
822 pm->ps->movementDir = 1;
823 } else if ( pm->ps->movementDir == 6 ) {
824 pm->ps->movementDir = 7;
825 }
826 }
827 }
828
829
830 /*
831 =============
832 PM_CheckJump
833 =============
834 */
835 #define METROID_JUMP 1
PM_InReboundJump(int anim)836 qboolean PM_InReboundJump( int anim )
837 {
838 switch ( anim )
839 {
840 case BOTH_FORCEWALLREBOUND_FORWARD:
841 case BOTH_FORCEWALLREBOUND_LEFT:
842 case BOTH_FORCEWALLREBOUND_BACK:
843 case BOTH_FORCEWALLREBOUND_RIGHT:
844 return qtrue;
845 break;
846 }
847 return qfalse;
848 }
849
PM_InReboundHold(int anim)850 qboolean PM_InReboundHold( int anim )
851 {
852 switch ( anim )
853 {
854 case BOTH_FORCEWALLHOLD_FORWARD:
855 case BOTH_FORCEWALLHOLD_LEFT:
856 case BOTH_FORCEWALLHOLD_BACK:
857 case BOTH_FORCEWALLHOLD_RIGHT:
858 return qtrue;
859 break;
860 }
861 return qfalse;
862 }
863
PM_InReboundRelease(int anim)864 qboolean PM_InReboundRelease( int anim )
865 {
866 switch ( anim )
867 {
868 case BOTH_FORCEWALLRELEASE_FORWARD:
869 case BOTH_FORCEWALLRELEASE_LEFT:
870 case BOTH_FORCEWALLRELEASE_BACK:
871 case BOTH_FORCEWALLRELEASE_RIGHT:
872 return qtrue;
873 break;
874 }
875 return qfalse;
876 }
877
PM_InBackFlip(int anim)878 qboolean PM_InBackFlip( int anim )
879 {
880 switch ( anim )
881 {
882 case BOTH_FLIP_BACK1:
883 case BOTH_FLIP_BACK2:
884 case BOTH_FLIP_BACK3:
885 case BOTH_ALORA_FLIP_B:
886 return qtrue;
887 break;
888 }
889 return qfalse;
890 }
891
PM_InSpecialJump(int anim)892 qboolean PM_InSpecialJump( int anim )
893 {
894 switch ( anim )
895 {
896 case BOTH_WALL_RUN_RIGHT:
897 case BOTH_WALL_RUN_RIGHT_STOP:
898 case BOTH_WALL_RUN_RIGHT_FLIP:
899 case BOTH_WALL_RUN_LEFT:
900 case BOTH_WALL_RUN_LEFT_STOP:
901 case BOTH_WALL_RUN_LEFT_FLIP:
902 case BOTH_WALL_FLIP_RIGHT:
903 case BOTH_WALL_FLIP_LEFT:
904 case BOTH_WALL_FLIP_BACK1:
905 case BOTH_BUTTERFLY_LEFT:
906 case BOTH_BUTTERFLY_RIGHT:
907 case BOTH_BUTTERFLY_FL1:
908 case BOTH_BUTTERFLY_FR1:
909 case BOTH_FJSS_TR_BL:
910 case BOTH_FJSS_TL_BR:
911 case BOTH_FORCELEAP2_T__B_:
912 case BOTH_JUMPFLIPSLASHDOWN1://#
913 case BOTH_JUMPFLIPSTABDOWN://#
914 case BOTH_JUMPATTACK6:
915 case BOTH_JUMPATTACK7:
916 case BOTH_ARIAL_LEFT:
917 case BOTH_ARIAL_RIGHT:
918 case BOTH_ARIAL_F1:
919 case BOTH_CARTWHEEL_LEFT:
920 case BOTH_CARTWHEEL_RIGHT:
921
922 case BOTH_FORCELONGLEAP_START:
923 case BOTH_FORCELONGLEAP_ATTACK:
924 case BOTH_FORCEWALLRUNFLIP_START:
925 case BOTH_FORCEWALLRUNFLIP_END:
926 case BOTH_FORCEWALLRUNFLIP_ALT:
927 case BOTH_FLIP_ATTACK7:
928 case BOTH_FLIP_HOLD7:
929 case BOTH_FLIP_LAND:
930 case BOTH_A7_SOULCAL:
931 return qtrue;
932 }
933 if ( PM_InReboundJump( anim ) )
934 {
935 return qtrue;
936 }
937 if ( PM_InReboundHold( anim ) )
938 {
939 return qtrue;
940 }
941 if ( PM_InReboundRelease( anim ) )
942 {
943 return qtrue;
944 }
945 if ( PM_InBackFlip( anim ) )
946 {
947 return qtrue;
948 }
949 return qfalse;
950 }
951
952 extern void CG_PlayerLockedWeaponSpeech( int jumping );
PM_ForceJumpingUp(gentity_t * gent)953 qboolean PM_ForceJumpingUp( gentity_t *gent )
954 {
955 if ( !gent || !gent->client )
956 {
957 return qfalse;
958 }
959
960 if ( gent->NPC )
961 {//this is ONLY for the player
962 if ( player
963 && player->client
964 && player->client->ps.viewEntity == gent->s.number )
965 {//okay to jump if an NPC controlled by the player
966 }
967 else
968 {
969 return qfalse;
970 }
971 }
972
973 if ( !(gent->client->ps.forcePowersActive&(1<<FP_LEVITATION)) && gent->client->ps.forceJumpCharge )
974 {//already jumped and let go
975 return qfalse;
976 }
977
978 if ( PM_InSpecialJump( gent->client->ps.legsAnim ) )
979 {
980 return qfalse;
981 }
982
983 if ( PM_InKnockDown( &gent->client->ps ) )
984 {
985 return qfalse;
986 }
987
988 if ( (gent->s.number<MAX_CLIENTS||G_ControlledByPlayer(gent)) && in_camera )
989 {//player can't use force powers in cinematic
990 return qfalse;
991 }
992 if ( gent->client->ps.groundEntityNum == ENTITYNUM_NONE //in air
993 && ( (gent->client->ps.pm_flags&PMF_JUMPING) && gent->client->ps.velocity[2] > 0 )//jumped & going up or at water surface///*(gent->client->ps.waterHeightLevel==WHL_SHOULDERS&&gent->client->usercmd.upmove>0) ||*/
994 && gent->client->ps.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 //force-jump capable
995 && !(gent->client->ps.pm_flags&PMF_TRIGGER_PUSHED) )//not pushed by a trigger
996 {
997 if( gent->flags & FL_LOCK_PLAYER_WEAPONS ) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one
998 {
999 CG_PlayerLockedWeaponSpeech( qtrue );
1000 return qfalse;
1001 }
1002 return qtrue;
1003 }
1004 return qfalse;
1005 }
1006
PM_JumpForDir(void)1007 static void PM_JumpForDir( void )
1008 {
1009 int anim = BOTH_JUMP1;
1010 if ( pm->cmd.forwardmove > 0 )
1011 {
1012 anim = BOTH_JUMP1;
1013 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1014 }
1015 else if ( pm->cmd.forwardmove < 0 )
1016 {
1017 anim = BOTH_JUMPBACK1;
1018 pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
1019 }
1020 else if ( pm->cmd.rightmove > 0 )
1021 {
1022 anim = BOTH_JUMPRIGHT1;
1023 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1024 }
1025 else if ( pm->cmd.rightmove < 0 )
1026 {
1027 anim = BOTH_JUMPLEFT1;
1028 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1029 }
1030 else
1031 {
1032 anim = BOTH_JUMP1;
1033 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1034 }
1035 if(!PM_InDeathAnim())
1036 {
1037 PM_SetAnim(pm,SETANIM_LEGS,anim,SETANIM_FLAG_OVERRIDE, 100); // Only blend over 100ms
1038 }
1039 }
1040
PM_GentCantJump(gentity_t * gent)1041 qboolean PM_GentCantJump( gentity_t *gent )
1042 {//FIXME: ugh, hacky, set a flag on NPC or something, please...
1043 if ( gent && gent->client &&
1044 ( gent->client->NPC_class == CLASS_ATST ||
1045 gent->client->NPC_class == CLASS_GONK ||
1046 gent->client->NPC_class == CLASS_MARK1 ||
1047 gent->client->NPC_class == CLASS_MARK2 ||
1048 gent->client->NPC_class == CLASS_MOUSE ||
1049 gent->client->NPC_class == CLASS_PROBE ||
1050 gent->client->NPC_class == CLASS_PROTOCOL ||
1051 gent->client->NPC_class == CLASS_R2D2 ||
1052 gent->client->NPC_class == CLASS_R5D2 ||
1053 gent->client->NPC_class == CLASS_SEEKER ||
1054 gent->client->NPC_class == CLASS_REMOTE ||
1055 gent->client->NPC_class == CLASS_SENTRY ) )
1056 {
1057 return qtrue;
1058 }
1059 return qfalse;
1060 }
1061
PM_CheckJump(void)1062 static qboolean PM_CheckJump( void )
1063 {
1064 //Don't allow jump until all buttons are up
1065 if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
1066 return qfalse;
1067 }
1068
1069 if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps ) )
1070 {//in knockdown
1071 return qfalse;
1072 }
1073
1074 if ( PM_GentCantJump( pm->gent ) )
1075 {
1076 return qfalse;
1077 }
1078
1079 if ( PM_KickingAnim( pm->ps->legsAnim ) && !PM_InAirKickingAnim( pm->ps->legsAnim ) )
1080 {//can't jump when in a kicking anim
1081 return qfalse;
1082 }
1083 /*
1084 if ( pm->cmd.buttons & BUTTON_FORCEJUMP )
1085 {
1086 pm->ps->pm_flags |= PMF_JUMP_HELD;
1087 }
1088 */
1089
1090 #if METROID_JUMP
1091 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
1092 && pm->gent && pm->gent->client
1093 && (pm->gent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) )
1094 {//player playing as boba fett
1095 if ( pm->cmd.upmove > 0 )
1096 {//turn on/go up
1097 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE && !(pm->ps->pm_flags&PMF_JUMP_HELD) )
1098 {//double-tap - must activate while in air
1099 if ( !JET_Flying( pm->gent ) )
1100 {
1101 JET_FlyStart( pm->gent );
1102 }
1103 }
1104 }
1105 else if ( pm->cmd.upmove < 0 )
1106 {//turn it off (or should we just go down)?
1107 /*
1108 if ( JET_Flying( pm->gent ) )
1109 {
1110 JET_FlyStop( pm->gent );
1111 }
1112 */
1113 }
1114 }
1115 else if ( pm->waterlevel < 3 )//|| (pm->ps->waterHeightLevel==WHL_SHOULDERS&&pm->cmd.upmove>0) )
1116 {
1117 if ( pm->ps->gravity > 0 )
1118 {//can't do this in zero-G
1119 if ( pm->ps->legsAnim == BOTH_FORCELONGLEAP_START
1120 || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK
1121 || pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND )
1122 {//in the middle of a force long-jump
1123 /*
1124 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
1125 && pm->cmd.upmove > 0
1126 && pm->cmd.forwardmove > 0 )
1127 */
1128 if ( (pm->ps->legsAnim == BOTH_FORCELONGLEAP_START || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK)
1129 && pm->ps->legsAnimTimer > 0 )
1130 {//in the air
1131 //FIXME: need an actual set time so it doesn't matter when the attack happens
1132 //FIXME: make sure we don't jump further than force jump 3 allows
1133 vec3_t jFwdAngs, jFwdVec;
1134 VectorSet( jFwdAngs, 0, pm->ps->viewangles[YAW], 0 );
1135 AngleVectors( jFwdAngs, jFwdVec, NULL, NULL );
1136 float oldZVel = pm->ps->velocity[2];
1137 if ( pm->ps->legsAnimTimer > 150 && oldZVel < 0 )
1138 {
1139 oldZVel = 0;
1140 }
1141 VectorScale( jFwdVec, FORCE_LONG_LEAP_SPEED, pm->ps->velocity );
1142 pm->ps->velocity[2] = oldZVel;
1143 pm->ps->pm_flags |= PMF_JUMP_HELD;
1144 pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
1145 pm->ps->forcePowersActive |= (1<<FP_LEVITATION);
1146 return qtrue;
1147 }
1148 else
1149 {//landing-slide
1150 if ( pm->ps->legsAnim == BOTH_FORCELONGLEAP_START
1151 || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK )
1152 {//still in start anim, but it's run out
1153 pm->ps->forcePowersActive |= (1<<FP_LEVITATION);
1154 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
1155 {//still in air?
1156 //hold it for another 50ms
1157 //PM_SetAnim( pm, SETANIM_BOTH, BOTH_FORCELONGLEAP_START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1158 }
1159 }
1160 else
1161 {//in land-slide anim
1162 //FIXME: force some forward movement? Less if holding back?
1163 }
1164 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE//still in air
1165 && pm->ps->origin[2] < pm->ps->jumpZStart )//dropped below original jump start
1166 {//slow down
1167 pm->ps->velocity[0] *= 0.75f;
1168 pm->ps->velocity[1] *= 0.75f;
1169 if ( (pm->ps->velocity[0]+pm->ps->velocity[1])*0.5f<=10.0f )
1170 {//falling straight down
1171 PM_SetAnim( pm, SETANIM_BOTH, BOTH_FORCEINAIR1, SETANIM_FLAG_OVERRIDE );
1172 }
1173 }
1174 return qfalse;
1175 }
1176 }
1177 else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) //player-only for now
1178 && pm->cmd.upmove > 0 //trying to jump
1179 && pm->ps->forcePowerLevel[FP_LEVITATION] >= FORCE_LEVEL_3 //force jump 3 or better
1180 && pm->ps->forcePower >= FORCE_LONGJUMP_POWER //this costs 20 force to do
1181 && (pm->ps->forcePowersActive&(1<<FP_SPEED)) //force-speed is on
1182 && pm->cmd.forwardmove > 0 //pushing forward
1183 && !pm->cmd.rightmove //not strafing
1184 && pm->ps->groundEntityNum != ENTITYNUM_NONE//not in mid-air
1185 && !(pm->ps->pm_flags&PMF_JUMP_HELD)
1186 //&& (float)(level.time-pm->ps->lastStationary) >= (3000.0f*g_timescale->value)//have to have a 3 second running start - relative to force speed slowdown
1187 && (level.time-pm->ps->forcePowerDebounce[FP_SPEED]) <= 250//have to have just started the force speed within the last half second
1188 && pm->gent )
1189 {//start a force long-jump!
1190 vec3_t jFwdAngs, jFwdVec;
1191 //BOTH_FORCELONGLEAP_ATTACK if holding attack, too?
1192 PM_SetAnim( pm, SETANIM_BOTH, BOTH_FORCELONGLEAP_START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1193 VectorSet( jFwdAngs, 0, pm->ps->viewangles[YAW], 0 );
1194 AngleVectors( jFwdAngs, jFwdVec, NULL, NULL );
1195 VectorScale( jFwdVec, FORCE_LONG_LEAP_SPEED, pm->ps->velocity );
1196 pm->ps->velocity[2] = 320;
1197 pml.groundPlane = qfalse;
1198 pml.walking = qfalse;
1199 pm->ps->groundEntityNum = ENTITYNUM_NONE;
1200 pm->ps->jumpZStart = pm->ps->origin[2];
1201 pm->ps->pm_flags |= PMF_JUMP_HELD;
1202 pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
1203 //start force jump
1204 pm->ps->forcePowersActive |= (1<<FP_LEVITATION);
1205 pm->cmd.upmove = 0;
1206 // keep track of force jump stat
1207 if(pm->ps->clientNum == 0)
1208 {
1209 if( pm->gent && pm->gent->client )
1210 {
1211 pm->gent->client->sess.missionStats.forceUsed[(int)FP_LEVITATION]++;
1212 }
1213 }
1214 G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
1215 WP_ForcePowerStop( pm->gent, FP_SPEED );
1216 WP_ForcePowerDrain( pm->gent, FP_LEVITATION, FORCE_LONGJUMP_POWER );//drain the required force power
1217 G_StartMatrixEffect( pm->gent, 0, pm->ps->legsAnimTimer+500 );
1218 return qtrue;
1219 }
1220 else if ( PM_InCartwheel( pm->ps->legsAnim )
1221 || PM_InButterfly( pm->ps->legsAnim ) )
1222 {//can't keep jumping up in cartwheels, ariels and butterflies
1223 }
1224 //FIXME: still able to pogo-jump...
1225 else if ( PM_ForceJumpingUp( pm->gent ) && (pm->ps->pm_flags&PMF_JUMP_HELD) )//||pm->ps->waterHeightLevel==WHL_SHOULDERS) )
1226 {//force jumping && holding jump
1227 /*
1228 if ( !pm->ps->forceJumpZStart && (pm->ps->waterHeightLevel==WHL_SHOULDERS&&pm->cmd.upmove>0) )
1229 {
1230 pm->ps->forceJumpZStart = pm->ps->origin[2];
1231 }
1232 */
1233 float curHeight = pm->ps->origin[2] - pm->ps->forceJumpZStart;
1234 //check for max force jump level and cap off & cut z vel
1235 if ( ( curHeight<=forceJumpHeight[0] ||//still below minimum jump height
1236 (pm->ps->forcePower&&pm->cmd.upmove>=10) ) &&////still have force power available and still trying to jump up
1237 curHeight < forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]] )//still below maximum jump height
1238 {//can still go up
1239 //FIXME: after a certain amount of time of held jump, play force jump sound and flip if a dir is being held
1240 //FIXME: if hit a wall... should we cut velocity or allow them to slide up it?
1241 //FIXME: constantly drain force power at a rate by which the usage for maximum height would use up the full cost of force jump
1242 if ( curHeight > forceJumpHeight[0] )
1243 {//passed normal jump height *2?
1244 if ( !(pm->ps->forcePowersActive&(1<<FP_LEVITATION)) )//haven't started forcejump yet
1245 {
1246 //start force jump
1247 pm->ps->forcePowersActive |= (1<<FP_LEVITATION);
1248 if ( pm->gent )
1249 {
1250 G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
1251 // keep track of force jump stat
1252 if(pm->ps->clientNum == 0 && pm->gent->client)
1253 {
1254 pm->gent->client->sess.missionStats.forceUsed[(int)FP_LEVITATION]++;
1255 }
1256 }
1257 //play flip
1258 //FIXME: do this only when they stop the jump (below) or when they're just about to hit the peak of the jump
1259 if ( PM_InAirKickingAnim( pm->ps->legsAnim )
1260 && pm->ps->legsAnimTimer )
1261 {//still in kick
1262 }
1263 else if ((pm->cmd.forwardmove || pm->cmd.rightmove) && //pushing in a dir
1264 //pm->ps->legsAnim != BOTH_ARIAL_F1 &&//not already flipping
1265 pm->ps->legsAnim != BOTH_FLIP_F &&
1266 pm->ps->legsAnim != BOTH_FLIP_B &&
1267 pm->ps->legsAnim != BOTH_FLIP_R &&
1268 pm->ps->legsAnim != BOTH_FLIP_L &&
1269 pm->ps->legsAnim != BOTH_ALORA_FLIP_1 &&
1270 pm->ps->legsAnim != BOTH_ALORA_FLIP_2 &&
1271 pm->ps->legsAnim != BOTH_ALORA_FLIP_3
1272 && cg.renderingThirdPerson//third person only
1273 && !cg.zoomMode //not zoomed in
1274 && !(pm->ps->saber[0].saberFlags&SFL_NO_FLIPS)//okay to do flips with this saber
1275 && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_FLIPS) )//okay to do flips with this saber
1276 )
1277 {//FIXME: this could end up playing twice if the jump is very long...
1278 int anim = BOTH_FORCEINAIR1;
1279 int parts = SETANIM_BOTH;
1280
1281 if ( pm->cmd.forwardmove > 0 )
1282 {
1283 /*
1284 if ( pm->ps->forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_2 )
1285 {
1286 anim = BOTH_ARIAL_F1;
1287 }
1288 else
1289 */
1290 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA && Q_irand( 0, 3 ) )
1291 {
1292 anim = Q_irand( BOTH_ALORA_FLIP_1, BOTH_ALORA_FLIP_3 );
1293 }
1294 else
1295 {
1296 anim = BOTH_FLIP_F;
1297 }
1298 }
1299 else if ( pm->cmd.forwardmove < 0 )
1300 {
1301 anim = BOTH_FLIP_B;
1302 }
1303 else if ( pm->cmd.rightmove > 0 )
1304 {
1305 anim = BOTH_FLIP_R;
1306 }
1307 else if ( pm->cmd.rightmove < 0 )
1308 {
1309 anim = BOTH_FLIP_L;
1310 }
1311 if ( pm->ps->weaponTime )
1312 {//FIXME: really only care if we're in a saber attack anim...
1313 parts = SETANIM_LEGS;
1314 }
1315
1316 PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1317 }
1318 else if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
1319 {//FIXME: really want to know how far off ground we are, probably...
1320 vec3_t facingFwd, facingRight, facingAngles = {0, pm->ps->viewangles[YAW], 0};
1321 int anim = -1;
1322 AngleVectors( facingAngles, facingFwd, facingRight, NULL );
1323 float dotR = DotProduct( facingRight, pm->ps->velocity );
1324 float dotF = DotProduct( facingFwd, pm->ps->velocity );
1325 if ( fabs(dotR) > fabs(dotF) * 1.5 )
1326 {
1327 if ( dotR > 150 )
1328 {
1329 anim = BOTH_FORCEJUMPRIGHT1;
1330 }
1331 else if ( dotR < -150 )
1332 {
1333 anim = BOTH_FORCEJUMPLEFT1;
1334 }
1335 }
1336 else
1337 {
1338 if ( dotF > 150 )
1339 {
1340 anim = BOTH_FORCEJUMP1;
1341 }
1342 else if ( dotF < -150 )
1343 {
1344 anim = BOTH_FORCEJUMPBACK1;
1345 }
1346 }
1347 if ( anim != -1 )
1348 {
1349 int parts = SETANIM_BOTH;
1350 if ( pm->ps->weaponTime )
1351 {//FIXME: really only care if we're in a saber attack anim...
1352 parts = SETANIM_LEGS;
1353 }
1354
1355 PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1356 }
1357 }
1358 }
1359 else
1360 {
1361 if ( !pm->ps->legsAnimTimer )
1362 {//not in the middle of a legsAnim
1363 int anim = pm->ps->legsAnim;
1364 int newAnim = -1;
1365 switch ( anim )
1366 {
1367 case BOTH_FORCEJUMP1:
1368 newAnim = BOTH_FORCELAND1;//BOTH_FORCEINAIR1;
1369 break;
1370 case BOTH_FORCEJUMPBACK1:
1371 newAnim = BOTH_FORCELANDBACK1;//BOTH_FORCEINAIRBACK1;
1372 break;
1373 case BOTH_FORCEJUMPLEFT1:
1374 newAnim = BOTH_FORCELANDLEFT1;//BOTH_FORCEINAIRLEFT1;
1375 break;
1376 case BOTH_FORCEJUMPRIGHT1:
1377 newAnim = BOTH_FORCELANDRIGHT1;//BOTH_FORCEINAIRRIGHT1;
1378 break;
1379 }
1380 if ( newAnim != -1 )
1381 {
1382 int parts = SETANIM_BOTH;
1383 if ( pm->ps->weaponTime )
1384 {//FIXME: really only care if we're in a saber attack anim...
1385 parts = SETANIM_LEGS;
1386 }
1387
1388 PM_SetAnim( pm, parts, newAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1389 }
1390 }
1391 }
1392 }
1393
1394 //need to scale this down, start with height velocity (based on max force jump height) and scale down to regular jump vel
1395 pm->ps->velocity[2] = (forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]]-curHeight)/forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]]*forceJumpStrength[pm->ps->forcePowerLevel[FP_LEVITATION]];//JUMP_VELOCITY;
1396 pm->ps->velocity[2] /= 10;
1397 pm->ps->velocity[2] += JUMP_VELOCITY;
1398 pm->ps->pm_flags |= PMF_JUMP_HELD;
1399 }
1400 else if ( curHeight > forceJumpHeight[0] && curHeight < forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]] - forceJumpHeight[0] )
1401 {//still have some headroom, don't totally stop it
1402 if ( pm->ps->velocity[2] > JUMP_VELOCITY )
1403 {
1404 pm->ps->velocity[2] = JUMP_VELOCITY;
1405 }
1406 }
1407 else
1408 {
1409 pm->ps->velocity[2] = 0;
1410 }
1411 pm->cmd.upmove = 0;
1412 return qfalse;
1413 }
1414 }
1415 }
1416
1417 #endif
1418
1419 //Not jumping
1420 if ( pm->cmd.upmove < 10 ) {
1421 return qfalse;
1422 }
1423
1424 // must wait for jump to be released
1425 if ( pm->ps->pm_flags & PMF_JUMP_HELD )
1426 {
1427 // clear upmove so cmdscale doesn't lower running speed
1428 pm->cmd.upmove = 0;
1429 return qfalse;
1430 }
1431
1432 if ( pm->ps->gravity <= 0 )
1433 {//in low grav, you push in the dir you're facing as long as there is something behind you to shove off of
1434 vec3_t forward, back;
1435 trace_t trace;
1436
1437 AngleVectors( pm->ps->viewangles, forward, NULL, NULL );
1438 VectorMA( pm->ps->origin, -8, forward, back );
1439 pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, back, pm->ps->clientNum, pm->tracemask&~(CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP), (EG2_Collision)0, 0 );
1440
1441 pm->cmd.upmove = 0;
1442
1443 if ( trace.fraction < 1.0f )
1444 {
1445 VectorMA( pm->ps->velocity, JUMP_VELOCITY/2, forward, pm->ps->velocity );
1446 //FIXME: kicking off wall anim? At least check what anim we're in?
1447 PM_SetAnim(pm,SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART);
1448 }
1449 else
1450 {//else no surf close enough to push off of
1451 return qfalse;
1452 }
1453
1454 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
1455 {//need to set some things and return
1456 //Jumping
1457 pm->ps->forceJumpZStart = 0;
1458 pml.groundPlane = qfalse;
1459 pml.walking = qfalse;
1460 pm->ps->pm_flags |= (PMF_JUMPING|PMF_JUMP_HELD);
1461 pm->ps->groundEntityNum = ENTITYNUM_NONE;
1462 pm->ps->jumpZStart = pm->ps->origin[2];
1463
1464 if ( pm->gent )
1465 {
1466 if ( !Q3_TaskIDPending( pm->gent, TID_CHAN_VOICE ) )
1467 {
1468 PM_AddEvent( EV_JUMP );
1469 }
1470 }
1471 else
1472 {
1473 PM_AddEvent( EV_JUMP );
1474 }
1475
1476 return qtrue;
1477 }//else no surf close enough to push off of
1478 }
1479 else if ( pm->cmd.upmove > 0 //want to jump
1480 && pm->waterlevel < 2 //not in water above ankles
1481 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 //have force jump ability
1482 && !(pm->ps->pm_flags&PMF_JUMP_HELD)//not holding jump from a previous jump
1483 //&& !PM_InKnockDown( pm->ps )//not in a knockdown
1484 && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period
1485 && pm->gent && WP_ForcePowerAvailable( pm->gent, FP_LEVITATION, 0 ) //have enough force power to jump
1486 && ((pm->ps->clientNum&&!PM_ControlledByPlayer())||((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) )) )// yes this locked weapons check also includes force powers, if we need a separate check later I'll make one
1487 {
1488 if ( pm->gent->NPC && pm->gent->NPC->rank != RANK_CREWMAN && pm->gent->NPC->rank <= RANK_LT_JG )
1489 {//reborn who are not acrobats can't do any of these acrobatics
1490 //FIXME: extern these abilities in the .npc file!
1491 }
1492 else if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
1493 {//on the ground
1494 //check for left-wall and right-wall special jumps
1495 int anim = -1;
1496 float vertPush = 0;
1497 int forcePowerCostOverride = 0;
1498
1499 // Cartwheels/ariels/butterflies
1500 if ( (pm->ps->weapon==WP_SABER&&G_TryingCartwheel(pm->gent,&pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/&&(pm->cmd.buttons&BUTTON_ATTACK))//using saber and holding focus + attack
1501 // ||(pm->ps->weapon!=WP_SABER&&((pm->cmd.buttons&BUTTON_ATTACK)||(pm->cmd.buttons&BUTTON_ALT_ATTACK)) ) )//using any other weapon and hitting either attack button
1502 && (((pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer())&&pm->cmd.upmove > 0&& pm->ps->velocity[2] >= 0 )//jumping NPC, going up already
1503 ||((pm->ps->clientNum<MAX_CLIENTS||PM_ControlledByPlayer())&&G_TryingCartwheel(pm->gent,&pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/))//focus-holding player
1504 && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR*/ )// have enough power
1505 {//holding attack and jumping
1506 if ( pm->cmd.rightmove > 0 )
1507 {
1508 // If they're using the staff we do different anims.
1509 if ( pm->ps->saberAnimLevel == SS_STAFF
1510 && pm->ps->weapon == WP_SABER )
1511 {
1512 if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())
1513 || pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 )
1514 {
1515 anim = BOTH_BUTTERFLY_RIGHT;
1516 forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1517 }
1518 }
1519 else if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())
1520 || pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
1521 {
1522 if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS)
1523 && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) )
1524 {//okay to do cartwheels with this saber
1525 if ( pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer() )
1526 {//player: since we're on the ground, always do a cartwheel
1527 /*
1528 anim = BOTH_CARTWHEEL_RIGHT;
1529 forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1530 */
1531 }
1532 else
1533 {
1534 vertPush = JUMP_VELOCITY;
1535 if ( Q_irand( 0, 1 ) )
1536 {
1537 anim = BOTH_ARIAL_RIGHT;
1538 forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1539 }
1540 else
1541 {
1542 anim = BOTH_CARTWHEEL_RIGHT;
1543 forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1544 }
1545 }
1546 }
1547 }
1548 }
1549 else if ( pm->cmd.rightmove < 0 )
1550 {
1551 // If they're using the staff we do different anims.
1552 if ( pm->ps->saberAnimLevel == SS_STAFF
1553 && pm->ps->weapon == WP_SABER )
1554 {
1555 if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())
1556 || pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 )
1557 {
1558 anim = BOTH_BUTTERFLY_LEFT;
1559 forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1560 }
1561 }
1562 else if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())
1563 || pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
1564 {
1565 if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS)
1566 && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) )
1567 {//okay to do cartwheels with this saber
1568 if ( pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer() )
1569 {//player: since we're on the ground, always do a cartwheel
1570 /*
1571 anim = BOTH_CARTWHEEL_LEFT;
1572 forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1573 */
1574 }
1575 else
1576 {
1577 vertPush = JUMP_VELOCITY;
1578 if ( Q_irand( 0, 1 ) )
1579 {
1580 anim = BOTH_ARIAL_LEFT;
1581 forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1582 }
1583 else
1584 {
1585 anim = BOTH_CARTWHEEL_LEFT;
1586 forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1587 }
1588 }
1589 }
1590 }
1591 }
1592 }
1593 else if ( pm->cmd.rightmove > 0 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
1594 {//strafing right
1595 if ( pm->cmd.forwardmove > 0 )
1596 {//wall-run
1597 if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_RUNS)
1598 && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_RUNS)) )
1599 {//okay to do wall-runs with this saber
1600 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
1601 anim = BOTH_WALL_RUN_RIGHT;
1602 }
1603 }
1604 else if ( pm->cmd.forwardmove == 0 )
1605 {//wall-flip
1606 if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_FLIPS)
1607 && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_FLIPS)) )
1608 {//okay to do wall-flips with this saber
1609 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
1610 anim = BOTH_WALL_FLIP_RIGHT;
1611 }
1612 }
1613 }
1614 else if ( pm->cmd.rightmove < 0 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
1615 {//strafing left
1616 if ( pm->cmd.forwardmove > 0 )
1617 {//wall-run
1618 if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_RUNS)
1619 && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_RUNS)) )
1620 {//okay to do wall-runs with this saber
1621 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
1622 anim = BOTH_WALL_RUN_LEFT;
1623 }
1624 }
1625 else if ( pm->cmd.forwardmove == 0 )
1626 {//wall-flip
1627 if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_FLIPS)
1628 && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_FLIPS)) )
1629 {//okay to do wall-flips with this saber
1630 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
1631 anim = BOTH_WALL_FLIP_LEFT;
1632 }
1633 }
1634 }
1635 else if ( /*pm->ps->clientNum >= MAX_CLIENTS//not the player
1636 && !PM_ControlledByPlayer() //not controlled by player
1637 &&*/ pm->cmd.forwardmove > 0 //pushing forward
1638 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )//have jump 2 or higher
1639 {//step off wall, flip backwards
1640 if ( VectorLengthSquared( pm->ps->velocity ) > 40000 /*200*200*/)
1641 {//have to be moving... FIXME: make sure it's opposite the wall... or at least forward?
1642 if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 )
1643 {//run all the way up wwall
1644 if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_RUNS)
1645 && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_RUNS)) )
1646 {//okay to do wall-runs with this saber
1647 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
1648 anim = BOTH_FORCEWALLRUNFLIP_START;
1649 }
1650 }
1651 else
1652 {//run just a couple steps up
1653 if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_FLIPS)
1654 && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_FLIPS)) )
1655 {//okay to do wall-flips with this saber
1656 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
1657 anim = BOTH_WALL_FLIP_BACK1;
1658 }
1659 }
1660 }
1661 }
1662 else if ( pm->cmd.forwardmove < 0 //pushing back
1663 //&& pm->ps->clientNum//not the player
1664 && !(pm->cmd.buttons&BUTTON_ATTACK) )//not attacking
1665 {//back-jump does backflip... FIXME: always?! What about just doing a normal jump backwards?
1666 if ( pm->ps->velocity[2] >= 0 )
1667 {//must be going up already
1668 if ( !(pm->ps->saber[0].saberFlags&SFL_NO_FLIPS)
1669 && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_FLIPS)) )
1670 {//okay to do backstabs with this saber
1671 vertPush = JUMP_VELOCITY;
1672 if ( pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA && !Q_irand( 0, 2 ) )
1673 {
1674 anim = BOTH_ALORA_FLIP_B;
1675 }
1676 else
1677 {
1678 anim = PM_PickAnim( pm->gent, BOTH_FLIP_BACK1, BOTH_FLIP_BACK3 );
1679 }
1680 }
1681 }
1682 }
1683 else if ( VectorLengthSquared( pm->ps->velocity ) < 256 /*16 squared*/)
1684 {//not moving
1685 if ( pm->ps->weapon == WP_SABER && (pm->cmd.buttons & BUTTON_ATTACK) )
1686 {
1687 saberMoveName_t overrideJumpAttackUpMove = LS_INVALID;
1688 if ( pm->ps->saber[0].jumpAtkUpMove != LS_INVALID )
1689 {
1690 if ( pm->ps->saber[0].jumpAtkUpMove != LS_NONE )
1691 {//actually overriding
1692 overrideJumpAttackUpMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkUpMove;
1693 }
1694 else if ( pm->ps->dualSabers
1695 && pm->ps->saber[1].jumpAtkUpMove > LS_NONE )
1696 {//would be cancelling it, but check the second saber, too
1697 overrideJumpAttackUpMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkUpMove;
1698 }
1699 else
1700 {//nope, just cancel it
1701 overrideJumpAttackUpMove = LS_NONE;
1702 }
1703 }
1704 else if ( pm->ps->dualSabers
1705 && pm->ps->saber[1].jumpAtkUpMove != LS_INVALID )
1706 {//first saber not overridden, check second
1707 overrideJumpAttackUpMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkUpMove;
1708 }
1709 if ( overrideJumpAttackUpMove != LS_INVALID )
1710 {//do this move instead
1711 if ( overrideJumpAttackUpMove != LS_NONE )
1712 {
1713 anim = saberMoveData[overrideJumpAttackUpMove].animToUse;
1714 }
1715 }
1716 else if ( pm->ps->saberAnimLevel == SS_MEDIUM )
1717 {
1718 /*
1719 //Only tavion does these now
1720 if ( pm->ps->clientNum && Q_irand( 0, 1 ) )
1721 {//butterfly... FIXME: does direction matter?
1722 vertPush = JUMP_VELOCITY;
1723 if ( Q_irand( 0, 1 ) )
1724 {
1725 anim = BOTH_BUTTERFLY_LEFT;
1726 }
1727 else
1728 {
1729 anim = BOTH_BUTTERFLY_RIGHT;
1730 }
1731 }
1732 else
1733 */if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() )//NOTE: pretty much useless, so player never does these
1734 {//jump-spin FIXME: does direction matter?
1735 vertPush = forceJumpStrength[FORCE_LEVEL_2]/1.5f;
1736 if ( pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA )
1737 {
1738 anim = BOTH_ALORA_SPIN;
1739 }
1740 else
1741 {
1742 anim = Q_irand( BOTH_FJSS_TR_BL, BOTH_FJSS_TL_BR );
1743 }
1744 }
1745 }
1746 }
1747 }
1748
1749 if ( anim != -1 && PM_HasAnimation( pm->gent, anim ) )
1750 {
1751 vec3_t fwd, right, traceto, mins = {pm->mins[0],pm->mins[1],0}, maxs = {pm->maxs[0],pm->maxs[1],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0};
1752 trace_t trace;
1753 qboolean doTrace = qfalse;
1754 int contents = CONTENTS_SOLID;
1755
1756 AngleVectors( fwdAngles, fwd, right, NULL );
1757
1758 //trace-check for a wall, if necc.
1759 switch ( anim )
1760 {
1761 case BOTH_WALL_FLIP_LEFT:
1762 if ( g_debugMelee->integer )
1763 {
1764 contents |= CONTENTS_BODY;
1765 }
1766 //NOTE: purposely falls through to next case!
1767 case BOTH_WALL_RUN_LEFT:
1768 doTrace = qtrue;
1769 VectorMA( pm->ps->origin, -16, right, traceto );
1770 break;
1771
1772 case BOTH_WALL_FLIP_RIGHT:
1773 if ( g_debugMelee->integer )
1774 {
1775 contents |= CONTENTS_BODY;
1776 }
1777 //NOTE: purposely falls through to next case!
1778 case BOTH_WALL_RUN_RIGHT:
1779 doTrace = qtrue;
1780 VectorMA( pm->ps->origin, 16, right, traceto );
1781 break;
1782
1783 case BOTH_WALL_FLIP_BACK1:
1784 if ( g_debugMelee->integer )
1785 {
1786 contents |= CONTENTS_BODY;
1787 }
1788 doTrace = qtrue;
1789 VectorMA( pm->ps->origin, 32, fwd, traceto );//was 16
1790 break;
1791
1792 case BOTH_FORCEWALLRUNFLIP_START:
1793 if ( g_debugMelee->integer )
1794 {
1795 contents |= CONTENTS_BODY;
1796 }
1797 doTrace = qtrue;
1798 VectorMA( pm->ps->origin, 32, fwd, traceto );//was 16
1799 break;
1800 }
1801
1802 vec3_t idealNormal={0}, wallNormal={0};
1803 if ( doTrace )
1804 {
1805 //FIXME: all these jump ones should check for head clearance
1806 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1807 VectorCopy( trace.plane.normal, wallNormal );
1808 VectorNormalize( wallNormal );
1809 VectorSubtract( pm->ps->origin, traceto, idealNormal );
1810 VectorNormalize( idealNormal );
1811 if ( anim == BOTH_WALL_FLIP_LEFT )
1812 {//sigh.. check for bottomless pit to the right
1813 trace_t trace2;
1814 vec3_t start;
1815 VectorMA( pm->ps->origin, 128, right, traceto );
1816 pm->trace( &trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1817 if ( !trace2.allsolid && !trace2.startsolid )
1818 {
1819 VectorCopy( trace2.endpos, traceto );
1820 VectorCopy( traceto, start );
1821 traceto[2] -= 384;
1822 pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1823 if ( !trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f )
1824 {//bottomless pit!
1825 trace.fraction = 1.0f;//way to stop it from doing the side-flip
1826 }
1827 }
1828 }
1829 else if ( anim == BOTH_WALL_FLIP_RIGHT )
1830 {//sigh.. check for bottomless pit to the left
1831 trace_t trace2;
1832 vec3_t start;
1833 VectorMA( pm->ps->origin, -128, right, traceto );
1834 pm->trace( &trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1835 if ( !trace2.allsolid && !trace2.startsolid )
1836 {
1837 VectorCopy( trace2.endpos, traceto );
1838 VectorCopy( traceto, start );
1839 traceto[2] -= 384;
1840 pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1841 if ( !trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f )
1842 {//bottomless pit!
1843 trace.fraction = 1.0f;//way to stop it from doing the side-flip
1844 }
1845 }
1846 }
1847 else
1848 {
1849 if ( anim == BOTH_WALL_FLIP_BACK1
1850 || anim == BOTH_FORCEWALLRUNFLIP_START )
1851 {//trace up and forward a little to make sure the wall it at least 64 tall
1852 if ( (contents&CONTENTS_BODY)//included entitied
1853 && (trace.contents&CONTENTS_BODY) //hit an entity
1854 && g_entities[trace.entityNum].client )//hit a client
1855 {//no need to trace up, it's all good...
1856 if ( PM_InOnGroundAnim( &g_entities[trace.entityNum].client->ps ) )//on the ground, no jump
1857 {//can't jump off guys on ground
1858 trace.fraction = 1.0f;//way to stop if from doing the jump
1859 }
1860 else if ( anim == BOTH_FORCEWALLRUNFLIP_START )
1861 {//instead of wall-running up, do the backflip
1862 anim = BOTH_WALL_FLIP_BACK1;
1863 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
1864 }
1865 }
1866 else if ( anim == BOTH_WALL_FLIP_BACK1 )
1867 {
1868 trace_t trace2;
1869 vec3_t start;
1870 VectorCopy( pm->ps->origin, start );
1871 start[2] += 64;
1872 VectorMA( start, 32, fwd, traceto );
1873 pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1874 if ( trace2.allsolid
1875 || trace2.startsolid
1876 || trace2.fraction >= 1.0f )
1877 {//no room above or no wall in front at that height
1878 trace.fraction = 1.0f;//way to stop if from doing the jump
1879 }
1880 }
1881 }
1882 if ( trace.fraction < 1.0f )
1883 {//still valid to jump
1884 if ( anim == BOTH_WALL_FLIP_BACK1 )
1885 {//sigh.. check for bottomless pit to the rear
1886 trace_t trace2;
1887 vec3_t start;
1888 VectorMA( pm->ps->origin, -128, fwd, traceto );
1889 pm->trace( &trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1890 if ( !trace2.allsolid && !trace2.startsolid )
1891 {
1892 VectorCopy( trace2.endpos, traceto );
1893 VectorCopy( traceto, start );
1894 traceto[2] -= 384;
1895 pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1896 if ( !trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f )
1897 {//bottomless pit!
1898 trace.fraction = 1.0f;//way to stop it from doing the side-flip
1899 }
1900 }
1901 }
1902 }
1903 }
1904 }
1905 gentity_t *traceEnt = &g_entities[trace.entityNum];
1906
1907 if ( !doTrace || (trace.fraction < 1.0f&&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(wallNormal,idealNormal)>0.7)) )
1908 {//there is a wall there
1909
1910 if ( (anim != BOTH_WALL_RUN_LEFT
1911 && anim != BOTH_WALL_RUN_RIGHT
1912 && anim != BOTH_FORCEWALLRUNFLIP_START)
1913 || (wallNormal[2] >= 0.0f && wallNormal[2] <= MAX_WALL_RUN_Z_NORMAL) )
1914 {//wall-runs can only run on relatively flat walls, sorry.
1915 if ( anim == BOTH_ARIAL_LEFT || anim == BOTH_CARTWHEEL_LEFT )
1916 {
1917 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1918 VectorMA( pm->ps->velocity, -185, right, pm->ps->velocity );
1919 }
1920 else if ( anim == BOTH_ARIAL_RIGHT || anim == BOTH_CARTWHEEL_RIGHT )
1921 {
1922 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1923 VectorMA( pm->ps->velocity, 185, right, pm->ps->velocity );
1924 }
1925 else if ( anim == BOTH_BUTTERFLY_LEFT )
1926 {
1927 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1928 VectorMA( pm->ps->velocity, -190, right, pm->ps->velocity );
1929 }
1930 else if ( anim == BOTH_BUTTERFLY_RIGHT )
1931 {
1932 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1933 VectorMA( pm->ps->velocity, 190, right, pm->ps->velocity );
1934 }
1935 //move me to side
1936 else if ( anim == BOTH_WALL_FLIP_LEFT )
1937 {
1938 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1939 VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity );
1940 }
1941 else if ( anim == BOTH_WALL_FLIP_RIGHT )
1942 {
1943 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1944 VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity );
1945 }
1946 else if ( anim == BOTH_FLIP_BACK1
1947 || anim == BOTH_FLIP_BACK2
1948 || anim == BOTH_FLIP_BACK3
1949 || anim == BOTH_ALORA_FLIP_B
1950 || anim == BOTH_WALL_FLIP_BACK1 )
1951 {
1952 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1953 VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
1954 }
1955 //kick if jumping off an ent
1956 if ( doTrace
1957 && anim != BOTH_WALL_RUN_LEFT
1958 && anim != BOTH_WALL_RUN_RIGHT
1959 && anim != BOTH_FORCEWALLRUNFLIP_START)
1960 {
1961 if ( pm->gent && trace.entityNum < ENTITYNUM_WORLD )
1962 {
1963 if ( traceEnt
1964 && traceEnt->client
1965 && traceEnt->health > 0
1966 && traceEnt->takedamage
1967 && traceEnt->client->NPC_class != CLASS_GALAKMECH
1968 && traceEnt->client->NPC_class != CLASS_DESANN
1969 && !(traceEnt->flags&FL_NO_KNOCKBACK) )
1970 {//push them away and do pain
1971 vec3_t oppDir, fxDir;
1972 float strength = VectorNormalize2( pm->ps->velocity, oppDir );
1973 VectorScale( oppDir, -1, oppDir );
1974 //FIXME: need knockdown anim
1975 G_Damage( traceEnt, pm->gent, pm->gent, oppDir, traceEnt->currentOrigin, 10, DAMAGE_NO_ARMOR|DAMAGE_NO_HIT_LOC|DAMAGE_NO_KNOCKBACK, MOD_MELEE );
1976 VectorCopy( fwd, fxDir );
1977 VectorScale( fxDir, -1, fxDir );
1978 G_PlayEffect( G_EffectIndex( "melee/kick_impact" ), trace.endpos, fxDir );
1979 //G_Sound( traceEnt, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
1980 if ( traceEnt->health > 0 )
1981 {//didn't kill him
1982 if ( (traceEnt->s.number==0&&!Q_irand(0,g_spskill->integer))
1983 || (traceEnt->NPC!=NULL&&Q_irand(RANK_CIVILIAN,traceEnt->NPC->rank)+Q_irand(-2,2)<RANK_ENSIGN) )
1984 {
1985 NPC_SetAnim( traceEnt, SETANIM_BOTH, BOTH_KNOCKDOWN2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1986 G_Throw( traceEnt, oppDir, strength );
1987 }
1988 }
1989 }
1990 }
1991 }
1992 //up
1993 if ( vertPush )
1994 {
1995 pm->ps->velocity[2] = vertPush;
1996 }
1997 //animate me
1998 if ( anim == BOTH_BUTTERFLY_RIGHT )
1999 {
2000 PM_SetSaberMove( LS_BUTTERFLY_RIGHT );
2001 }
2002 else if ( anim == BOTH_BUTTERFLY_LEFT )
2003 {
2004 PM_SetSaberMove( LS_BUTTERFLY_LEFT );
2005 }
2006 else
2007 {//not a proper saberMove, so do set all the details manually
2008 int parts = SETANIM_LEGS;
2009 if ( /*anim == BOTH_BUTTERFLY_LEFT ||
2010 anim == BOTH_BUTTERFLY_RIGHT ||*/
2011 anim == BOTH_FJSS_TR_BL ||
2012 anim == BOTH_FJSS_TL_BR )
2013 {
2014 parts = SETANIM_BOTH;
2015 pm->cmd.buttons&=~BUTTON_ATTACK;
2016 pm->ps->saberMove = LS_NONE;
2017 pm->gent->client->ps.SaberActivateTrail( 300 );
2018 }
2019 else if ( !pm->ps->weaponTime )
2020 {
2021 parts = SETANIM_BOTH;
2022 }
2023 PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
2024 if ( /*anim == BOTH_BUTTERFLY_LEFT
2025 || anim == BOTH_BUTTERFLY_RIGHT
2026 ||*/ anim == BOTH_FJSS_TR_BL
2027 || anim == BOTH_FJSS_TL_BR
2028 || anim == BOTH_FORCEWALLRUNFLIP_START )
2029 {
2030 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
2031 }
2032 else if ( anim == BOTH_WALL_FLIP_LEFT
2033 || anim == BOTH_WALL_FLIP_RIGHT
2034 || anim == BOTH_WALL_FLIP_BACK1 )
2035 {//let us do some more moves after this
2036 pm->ps->saberAttackChainCount = 0;
2037 }
2038 }
2039 pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
2040 pm->ps->pm_flags |= (PMF_JUMPING|PMF_SLOW_MO_FALL);
2041 pm->cmd.upmove = 0;
2042 G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
2043 WP_ForcePowerDrain( pm->gent, FP_LEVITATION, forcePowerCostOverride );
2044 }
2045 }
2046 }
2047 }
2048 else
2049 {//in the air
2050 int legsAnim = pm->ps->legsAnim;
2051
2052 if ( legsAnim == BOTH_WALL_RUN_LEFT || legsAnim == BOTH_WALL_RUN_RIGHT )
2053 {//running on a wall
2054 vec3_t right, traceto, mins = {pm->mins[0],pm->mins[0],0}, maxs = {pm->maxs[0],pm->maxs[0],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0};
2055 trace_t trace;
2056 int anim = -1;
2057
2058 AngleVectors( fwdAngles, NULL, right, NULL );
2059
2060 if ( legsAnim == BOTH_WALL_RUN_LEFT )
2061 {
2062 if ( pm->ps->legsAnimTimer > 400 )
2063 {//not at the end of the anim
2064 float animLen = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, BOTH_WALL_RUN_LEFT );
2065 if ( pm->ps->legsAnimTimer < animLen - 400 )
2066 {//not at start of anim
2067 VectorMA( pm->ps->origin, -16, right, traceto );
2068 anim = BOTH_WALL_RUN_LEFT_FLIP;
2069 }
2070 }
2071 }
2072 else if ( legsAnim == BOTH_WALL_RUN_RIGHT )
2073 {
2074 if ( pm->ps->legsAnimTimer > 400 )
2075 {//not at the end of the anim
2076 float animLen = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, BOTH_WALL_RUN_RIGHT );
2077 if ( pm->ps->legsAnimTimer < animLen - 400 )
2078 {//not at start of anim
2079 VectorMA( pm->ps->origin, 16, right, traceto );
2080 anim = BOTH_WALL_RUN_RIGHT_FLIP;
2081 }
2082 }
2083 }
2084 if ( anim != -1 )
2085 {
2086 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY, (EG2_Collision)0, 0 );
2087 if ( trace.fraction < 1.0f )
2088 {//flip off wall
2089 if ( anim == BOTH_WALL_RUN_LEFT_FLIP )
2090 {
2091 pm->ps->velocity[0] *= 0.5f;
2092 pm->ps->velocity[1] *= 0.5f;
2093 VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity );
2094 }
2095 else if ( anim == BOTH_WALL_RUN_RIGHT_FLIP )
2096 {
2097 pm->ps->velocity[0] *= 0.5f;
2098 pm->ps->velocity[1] *= 0.5f;
2099 VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity );
2100 }
2101 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
2102 pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
2103 pm->cmd.upmove = 0;
2104 }
2105 }
2106 if ( pm->cmd.upmove != 0 )
2107 {//jump failed, so don't try to do normal jump code, just return
2108 return qfalse;
2109 }
2110 }
2111 else if ( pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START )
2112 {//want to jump off wall
2113 vec3_t fwd, traceto, mins = {pm->mins[0],pm->mins[0],0}, maxs = {pm->maxs[0],pm->maxs[0],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0};
2114 trace_t trace;
2115 int anim = -1;
2116
2117 AngleVectors( fwdAngles, fwd, NULL, NULL );
2118
2119 float animLen = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, BOTH_FORCEWALLRUNFLIP_START );
2120 if ( pm->ps->legsAnimTimer < animLen - 250 )//was 400
2121 {//not at start of anim
2122 VectorMA( pm->ps->origin, 16, fwd, traceto );
2123 anim = BOTH_FORCEWALLRUNFLIP_END;
2124 }
2125 if ( anim != -1 )
2126 {
2127 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY, (EG2_Collision)0, 0 );
2128 if ( trace.fraction < 1.0f )
2129 {//flip off wall
2130 pm->ps->velocity[0] *= 0.5f;
2131 pm->ps->velocity[1] *= 0.5f;
2132 VectorMA( pm->ps->velocity, WALL_RUN_UP_BACKFLIP_SPEED, fwd, pm->ps->velocity );
2133 pm->ps->velocity[2] += 200;
2134 int parts = SETANIM_LEGS;
2135 if ( !pm->ps->weaponTime )
2136 {//not attacking, set anim on both
2137 parts = SETANIM_BOTH;
2138 }
2139 PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
2140 //FIXME: do damage to traceEnt, like above?
2141 pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
2142 pm->cmd.upmove = 0;
2143 PM_AddEvent( EV_JUMP );
2144 }
2145 }
2146 if ( pm->cmd.upmove != 0 )
2147 {//jump failed, so don't try to do normal jump code, just return
2148 return qfalse;
2149 }
2150 }
2151 /*
2152 else if ( pm->cmd.forwardmove < 0
2153 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1
2154 && !(pm->ps->pm_flags&PMF_JUMP_HELD) //not holding jump
2155 && (level.time - pm->ps->lastOnGround) <= 250 //just jumped
2156 )//&& !(pm->cmd.buttons&BUTTON_ATTACK) )
2157 {//double-tap back-jump does backflip
2158 vec3_t fwd, fwdAngles = {0, pm->ps->viewangles[YAW], 0};
2159
2160 AngleVectors( fwdAngles, fwd, NULL, NULL );
2161 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2162 VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
2163 //pm->ps->velocity[2] = JUMP_VELOCITY;
2164 int parts = SETANIM_LEGS;
2165 if ( !pm->ps->weaponTime )
2166 {
2167 parts = SETANIM_BOTH;
2168 }
2169 PM_SetAnim( pm, parts, PM_PickAnim( pm->gent, BOTH_FLIP_BACK1, BOTH_FLIP_BACK3 ), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
2170 pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
2171 pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
2172 pm->cmd.upmove = 0;
2173 G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
2174 WP_ForcePowerDrain( pm->gent, FP_LEVITATION, 0 );
2175 }
2176 */
2177 /*
2178 else if ( pm->cmd.forwardmove > 0 //pushing forward
2179 && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period
2180 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //have force jump 2 or higher
2181 && (level.time - pm->ps->lastOnGround) <= 250//just jumped
2182 && (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 )//not in a flip or spin or anything
2183 {//run up wall, flip backwards
2184 //FIXME: have to be moving... make sure it's opposite the wall... or at least forward?
2185 int wallWalkAnim = BOTH_WALL_FLIP_BACK1;
2186 int parts = SETANIM_LEGS;
2187 int contents = CONTENTS_SOLID;
2188 qboolean kick = qtrue;
2189 if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 )
2190 {
2191 wallWalkAnim = BOTH_FORCEWALLRUNFLIP_START;
2192 parts = SETANIM_BOTH;
2193 kick = qfalse;
2194 }
2195 else
2196 {
2197 contents |= CONTENTS_BODY;
2198 if ( !pm->ps->weaponTime )
2199 {
2200 parts = SETANIM_BOTH;
2201 }
2202 }
2203 if ( PM_HasAnimation( pm->gent, wallWalkAnim ) )
2204 {
2205 vec3_t fwd, traceto, mins = {pm->mins[0],pm->mins[1],0}, maxs = {pm->maxs[0],pm->maxs[1],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0};
2206 trace_t trace;
2207 vec3_t idealNormal;
2208
2209 AngleVectors( fwdAngles, fwd, NULL, NULL );
2210 VectorMA( pm->ps->origin, 32, fwd, traceto );
2211
2212 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents );//FIXME: clip brushes too?
2213 VectorSubtract( pm->ps->origin, traceto, idealNormal );
2214 VectorNormalize( idealNormal );
2215 gentity_t *traceEnt = &g_entities[trace.entityNum];
2216
2217 if ( trace.fraction < 1.0f
2218 &&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) )
2219 {//there is a wall there
2220 pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2221 if ( wallWalkAnim == BOTH_FORCEWALLRUNFLIP_START )
2222 {
2223 pm->ps->velocity[2] = forceJumpStrength[FORCE_LEVEL_3]/2.0f;
2224 }
2225 else
2226 {
2227 VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
2228 }
2229 //animate me
2230 PM_SetAnim( pm, parts, wallWalkAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
2231 pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
2232 pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
2233 pm->cmd.upmove = 0;
2234 G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
2235 WP_ForcePowerDrain( pm->gent, FP_LEVITATION, 0 );
2236 //kick if jumping off an ent
2237 if ( kick && pm->gent && trace.entityNum < ENTITYNUM_WORLD )
2238 {
2239 if ( traceEnt
2240 && traceEnt->client
2241 && traceEnt->health > 0
2242 && traceEnt->takedamage
2243 && traceEnt->client->NPC_class != CLASS_GALAKMECH
2244 && traceEnt->client->NPC_class != CLASS_DESANN
2245 && !(traceEnt->flags&FL_NO_KNOCKBACK) )
2246 {//push them away and do pain
2247 vec3_t oppDir;
2248 float strength = VectorNormalize2( pm->ps->velocity, oppDir );
2249 VectorScale( oppDir, -1, oppDir );
2250 //FIXME: need knockdown anim
2251 G_Damage( traceEnt, pm->gent, pm->gent, oppDir, traceEnt->currentOrigin, 10, DAMAGE_NO_ARMOR|DAMAGE_NO_HIT_LOC|DAMAGE_NO_KNOCKBACK, MOD_MELEE );
2252 G_Sound( traceEnt, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
2253 if ( trace.fraction <= 0.5f )
2254 {//close to him
2255 if ( traceEnt->health > 0 )
2256 {//didn't kill him
2257 if ( (traceEnt->s.number==0&&!Q_irand(0,g_spskill->integer))
2258 || (traceEnt->NPC!=NULL&&Q_irand(RANK_CIVILIAN,traceEnt->NPC->rank)+Q_irand(-2,2)<RANK_ENSIGN) )
2259 {
2260 NPC_SetAnim( traceEnt, SETANIM_BOTH, BOTH_KNOCKDOWN2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2261 G_Throw( traceEnt, oppDir, strength );
2262 }
2263 }
2264 }
2265 }
2266 }
2267 }
2268 }
2269 }
2270 */
2271 else if ( (pm->ps->legsAnimTimer<=100
2272 ||!PM_InSpecialJump( legsAnim )//not in a special jump anim
2273 ||PM_InReboundJump( legsAnim )//we're already in a rebound
2274 ||PM_InBackFlip( legsAnim ) )//a backflip (needed so you can jump off a wall behind you)
2275 //&& pm->ps->velocity[2] <= 0
2276 && pm->ps->velocity[2] > -1200 //not falling down very fast
2277 && !(pm->ps->pm_flags&PMF_JUMP_HELD)//have to have released jump since last press
2278 && (pm->cmd.forwardmove||pm->cmd.rightmove)//pushing in a direction
2279 //&& pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period
2280 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2//level 3 jump or better
2281 && pm->ps->forcePower > 10 //have enough force power to do another one
2282 && (level.time-pm->ps->lastOnGround) > 250 //haven't been on the ground in the last 1/4 of a second
2283 && (!(pm->ps->pm_flags&PMF_JUMPING)//not jumping
2284 || ( (level.time-pm->ps->lastOnGround) > 250 //we are jumping, but have been in the air for at least half a second
2285 &&( g_debugMelee->integer//if you know kung fu, no height cap on wall-grab-jumps
2286 || ((pm->ps->origin[2]-pm->ps->forceJumpZStart) < (forceJumpHeightMax[FORCE_LEVEL_3]-(G_ForceWallJumpStrength()/2.0f))) )//can fit at least one more wall jump in (yes, using "magic numbers"... for now)
2287 )
2288 )
2289 //&& (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 ) )//not in a flip or spin or anything
2290 )
2291 {//see if we're pushing at a wall and jump off it if so
2292 if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_GRAB)
2293 && ( !pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_GRAB) ) )
2294 {//okay to do wall-grabs with this saber
2295 //FIXME: make sure we have enough force power
2296 //FIXME: check to see if we can go any higher
2297 //FIXME: limit to a certain number of these in a row?
2298 //FIXME: maybe don't require a ucmd direction, just check all 4?
2299 //FIXME: should stick to the wall for a second, then push off...
2300 vec3_t checkDir, traceto, mins = {pm->mins[0],pm->mins[1],0}, maxs = {pm->maxs[0],pm->maxs[1],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0};
2301 trace_t trace;
2302 vec3_t idealNormal;
2303 int anim = -1;
2304
2305 if ( pm->cmd.rightmove )
2306 {
2307 if ( pm->cmd.rightmove > 0 )
2308 {
2309 anim = BOTH_FORCEWALLREBOUND_RIGHT;
2310 AngleVectors( fwdAngles, NULL, checkDir, NULL );
2311 }
2312 else if ( pm->cmd.rightmove < 0 )
2313 {
2314 anim = BOTH_FORCEWALLREBOUND_LEFT;
2315 AngleVectors( fwdAngles, NULL, checkDir, NULL );
2316 VectorScale( checkDir, -1, checkDir );
2317 }
2318 }
2319 else if ( pm->cmd.forwardmove > 0 )
2320 {
2321 anim = BOTH_FORCEWALLREBOUND_FORWARD;
2322 AngleVectors( fwdAngles, checkDir, NULL, NULL );
2323 }
2324 else if ( pm->cmd.forwardmove < 0 )
2325 {
2326 anim = BOTH_FORCEWALLREBOUND_BACK;
2327 AngleVectors( fwdAngles, checkDir, NULL, NULL );
2328 VectorScale( checkDir, -1, checkDir );
2329 }
2330 if ( anim != -1 )
2331 {//trace in the dir we're pushing in and see if there's a vertical wall there
2332 VectorMA( pm->ps->origin, 16, checkDir, traceto );//was 8
2333 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID, (EG2_Collision)0, 0 );//FIXME: clip brushes too?
2334 VectorSubtract( pm->ps->origin, traceto, idealNormal );
2335 VectorNormalize( idealNormal );
2336 gentity_t *traceEnt = &g_entities[trace.entityNum];
2337 if ( trace.fraction < 1.0f
2338 && fabs(trace.plane.normal[2]) <= MAX_WALL_GRAB_SLOPE
2339 &&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) )
2340 {//there is a wall there
2341 float dot = DotProduct( pm->ps->velocity, trace.plane.normal );
2342 if ( dot < 1.0f )
2343 {//can't be heading *away* from the wall!
2344 //grab it!
2345 PM_GrabWallForJump( anim );
2346 }
2347 }
2348 }
2349 }
2350 }
2351 else
2352 {
2353 //FIXME: if in a butterfly, kick people away?
2354 }
2355 }
2356 }
2357
2358 if ( pm->gent
2359 //&& pm->cmd.upmove > 0
2360 && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period
2361 && pm->ps->weapon == WP_SABER
2362 && (pm->ps->weaponTime > 0||(pm->cmd.buttons&BUTTON_ATTACK))
2363 && ((pm->ps->clientNum&&!PM_ControlledByPlayer())||((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode)) )
2364 {//okay, we just jumped and we're in an attack
2365 if ( !PM_RollingAnim( pm->ps->legsAnim )
2366 && !PM_InKnockDown( pm->ps )
2367 && !PM_InDeathAnim()
2368 && !PM_PainAnim( pm->ps->torsoAnim )
2369 && !PM_FlippingAnim( pm->ps->legsAnim )
2370 && !PM_SpinningAnim( pm->ps->legsAnim )
2371 && !PM_SaberInSpecialAttack( pm->ps->torsoAnim ) )
2372 {//HMM... do NPCs need this logic?
2373 if ( !PM_SaberInTransitionAny( pm->ps->saberMove ) //not going to/from/between an attack anim
2374 && !PM_SaberInAttack( pm->ps->saberMove ) //not in attack anim
2375 && pm->ps->weaponTime <= 0//not busy
2376 && (pm->cmd.buttons&BUTTON_ATTACK) )//want to attack
2377 {//not in an attack or finishing/starting/transitioning one
2378 if ( PM_CheckBackflipAttackMove() )
2379 {
2380 PM_SetSaberMove( PM_SaberBackflipAttackMove() );//backflip attack
2381 }
2382 /*
2383 else if ( PM_CheckSaberDualJumpAttackMove() )
2384 {
2385 PM_SetSaberMove( PM_SaberDualJumpAttackMove() );//jump forward sideways flip attack
2386 }
2387 */
2388 }
2389 /*
2390 else if ( ( PM_SaberInTransitionAny( pm->ps->saberMove ) || PM_SaberInAttack( pm->ps->saberMove ) )
2391 && PM_InAnimForSaberMove( pm->ps->torsoAnim, pm->ps->saberMove ) )
2392 {//not in an anim we shouldn't interrupt
2393 //see if it's not too late to start a special jump-attack
2394 float animLength = PM_AnimLength( g_entities[pm->ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)pm->ps->torsoAnim );
2395 if ( animLength - pm->ps->torsoAnimTimer < 500 )
2396 {//just started the saberMove
2397 //check for special-case jump attacks
2398 if ( PM_CheckFlipOverAttackMove( qtrue ) )
2399 {
2400 PM_SetSaberMove( PM_SaberFlipOverAttackMove() );
2401 }
2402 else if ( PM_CheckJumpAttackMove() )
2403 {
2404 PM_SetSaberMove( PM_SaberJumpAttackMove() );
2405 }
2406 }
2407 }
2408 */
2409 }
2410 }
2411 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
2412 {
2413 return qfalse;
2414 }
2415 if ( pm->cmd.upmove > 0 )
2416 {//no special jumps
2417 /*
2418 gentity_t *groundEnt = &g_entities[pm->ps->groundEntityNum];
2419 if ( groundEnt && groundEnt->NPC )
2420 {//Can't jump off of someone's head
2421 return qfalse;
2422 }
2423 */
2424
2425 pm->ps->velocity[2] = JUMP_VELOCITY;
2426 pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
2427 pm->ps->pm_flags |= PMF_JUMPING;
2428 }
2429
2430 if ( d_JediAI->integer )
2431 {
2432 if ( pm->ps->clientNum && pm->ps->weapon == WP_SABER )
2433 {
2434 Com_Printf( "jumping\n" );
2435 }
2436 }
2437 //Jumping
2438 pml.groundPlane = qfalse;
2439 pml.walking = qfalse;
2440 pm->ps->pm_flags |= PMF_JUMP_HELD;
2441 pm->ps->groundEntityNum = ENTITYNUM_NONE;
2442 pm->ps->jumpZStart = pm->ps->origin[2];
2443
2444 if ( pm->gent )
2445 {
2446 if ( !Q3_TaskIDPending( pm->gent, TID_CHAN_VOICE ) )
2447 {
2448 PM_AddEvent( EV_JUMP );
2449 }
2450 }
2451 else
2452 {
2453 PM_AddEvent( EV_JUMP );
2454 }
2455
2456 //Set the animations
2457 if ( pm->ps->gravity > 0 && !PM_InSpecialJump( pm->ps->legsAnim ) && !PM_InGetUp( pm->ps ) )
2458 {
2459 PM_JumpForDir();
2460 }
2461
2462 return qtrue;
2463 }
2464
2465 /*
2466 =============
2467 PM_CheckWaterJump
2468 =============
2469 */
PM_CheckWaterJump(void)2470 static qboolean PM_CheckWaterJump( void ) {
2471 vec3_t spot;
2472 int cont;
2473 vec3_t flatforward;
2474
2475 if (pm->ps->pm_time) {
2476 return qfalse;
2477 }
2478
2479 if ( pm->cmd.forwardmove <= 0 && pm->cmd.upmove <= 0 )
2480 {//they must not want to get out?
2481 return qfalse;
2482 }
2483 // check for water jump
2484 if ( pm->waterlevel != 2 ) {
2485 return qfalse;
2486 }
2487
2488 if ( pm->watertype & CONTENTS_LADDER ) {
2489 if (pm->ps->velocity[2] <= 0)
2490 return qfalse;
2491 }
2492
2493 flatforward[0] = pml.forward[0];
2494 flatforward[1] = pml.forward[1];
2495 flatforward[2] = 0;
2496 VectorNormalize( flatforward );
2497
2498 VectorMA( pm->ps->origin, 30, flatforward, spot );
2499 spot[2] += 24;
2500 cont = pm->pointcontents (spot, pm->ps->clientNum );
2501 if ( !(cont & CONTENTS_SOLID) ) {
2502 return qfalse;
2503 }
2504
2505 spot[2] += 16;
2506 cont = pm->pointcontents( spot, pm->ps->clientNum );
2507 if ( cont&(CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_BODY) ) {
2508 return qfalse;
2509 }
2510
2511 // jump out of water
2512 VectorScale( pml.forward, 200, pm->ps->velocity );
2513 pm->ps->velocity[2] = 350+((pm->ps->waterheight-pm->ps->origin[2])*2);
2514
2515 pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
2516 pm->ps->pm_time = 2000;
2517
2518 return qtrue;
2519 }
2520
2521 //============================================================================
2522
2523
2524 /*
2525 ===================
2526 PM_WaterJumpMove
2527
2528 Flying out of the water
2529 ===================
2530 */
PM_WaterJumpMove(void)2531 static void PM_WaterJumpMove( void )
2532 {
2533 // waterjump has no control, but falls
2534
2535 PM_StepSlideMove( 1 );
2536
2537 pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
2538 if (pm->ps->velocity[2] < 0)
2539 {
2540 // cancel as soon as we are falling down again
2541 pm->ps->pm_flags &= ~PMF_ALL_TIMES;
2542 pm->ps->pm_time = 0;
2543 }
2544 }
2545
2546 /*
2547 ===================
2548 PM_WaterMove
2549
2550 ===================
2551 */
PM_WaterMove(void)2552 static void PM_WaterMove( void ) {
2553 int i;
2554 vec3_t wishvel;
2555 float wishspeed;
2556 vec3_t wishdir;
2557 float scale;
2558 float vel;
2559
2560 if ( PM_CheckWaterJump() ) {
2561 PM_WaterJumpMove();
2562 return;
2563 }
2564 else if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 && pm->waterlevel < 3 )
2565 {
2566 if ( PM_CheckJump () ) {
2567 // jumped away
2568 return;
2569 }
2570 }
2571 #if 0
2572 // jump = head for surface
2573 if ( pm->cmd.upmove >= 10 ) {
2574 if (pm->ps->velocity[2] > -300) {
2575 if ( pm->watertype == CONTENTS_WATER ) {
2576 pm->ps->velocity[2] = 100;
2577 } else if (pm->watertype == CONTENTS_SLIME) {
2578 pm->ps->velocity[2] = 80;
2579 } else {
2580 pm->ps->velocity[2] = 50;
2581 }
2582 }
2583 }
2584 #endif
2585 PM_Friction ();
2586
2587 scale = PM_CmdScale( &pm->cmd );
2588 //
2589 // user intentions
2590 //
2591 if ( !scale ) {
2592 wishvel[0] = 0;
2593 wishvel[1] = 0;
2594 if ( pm->watertype & CONTENTS_LADDER ) {
2595 wishvel[2] = 0;
2596 } else {
2597 wishvel[2] = -60; // sink towards bottom
2598 }
2599 } else {
2600 for (i=0 ; i<3 ; i++) {
2601 wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
2602 }
2603 wishvel[2] += scale * pm->cmd.upmove;
2604 if ( !(pm->watertype&CONTENTS_LADDER) ) //ladder
2605 {
2606 float depth = (pm->ps->origin[2]+pm->gent->client->standheight)-pm->ps->waterheight;
2607 if ( depth >= 12 )
2608 {//too high!
2609 wishvel[2] -= 120; // sink towards bottom
2610 if ( wishvel[2] > 0 )
2611 {
2612 wishvel[2] = 0;
2613 }
2614 }
2615 else if ( pm->ps->waterHeightLevel >= WHL_UNDER )//!depth && pm->waterlevel == 3 )
2616 {
2617 }
2618 else if ( depth < 12 )
2619 {//still deep
2620 wishvel[2] -= 60; // sink towards bottom
2621 if ( wishvel[2] > 30 )
2622 {
2623 wishvel[2] = 30;
2624 }
2625 }
2626 }
2627 }
2628
2629 VectorCopy (wishvel, wishdir);
2630 wishspeed = VectorNormalize(wishdir);
2631
2632 if ( pm->watertype & CONTENTS_LADDER ) //ladder
2633 {
2634 if ( wishspeed > pm->ps->speed * pm_ladderScale ) {
2635 wishspeed = pm->ps->speed * pm_ladderScale;
2636 }
2637 PM_Accelerate( wishdir, wishspeed, pm_flyaccelerate );
2638 } else {
2639 if ( pm->ps->gravity < 0 )
2640 {//float up
2641 pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
2642 }
2643 if ( wishspeed > pm->ps->speed * pm_swimScale ) {
2644 wishspeed = pm->ps->speed * pm_swimScale;
2645 }
2646 PM_Accelerate( wishdir, wishspeed, pm_wateraccelerate );
2647 }
2648
2649 // make sure we can go up slopes easily under water
2650 if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) {
2651 vel = VectorLength(pm->ps->velocity);
2652 // slide along the ground plane
2653 PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
2654 pm->ps->velocity, OVERCLIP );
2655
2656 VectorNormalize(pm->ps->velocity);
2657 VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
2658 }
2659
2660 PM_SlideMove( qfalse );
2661 }
2662
2663
2664 /*
2665 ===================
2666 PM_FlyVehicleMove
2667
2668 ===================
2669 */
PM_FlyVehicleMove(void)2670 static void PM_FlyVehicleMove( void )
2671 {
2672 int i;
2673 vec3_t wishvel;
2674 float wishspeed;
2675 vec3_t wishdir;
2676 float scale;
2677 float zVel;
2678 float fmove = 0.0f, smove = 0.0f;
2679
2680 // We don't use these here because we pre-calculate the movedir in the vehicle update anyways, and if
2681 // you leave this, you get strange motion during boarding (the player can move the vehicle).
2682 //fmove = pm->cmd.forwardmove;
2683 //smove = pm->cmd.rightmove;
2684
2685 // normal slowdown
2686 if ( pm->ps->gravity && pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum == ENTITYNUM_NONE )
2687 {//falling
2688 zVel = pm->ps->velocity[2];
2689 PM_Friction ();
2690 pm->ps->velocity[2] = zVel;
2691 }
2692 else
2693 {
2694 PM_Friction ();
2695 if ( pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE )
2696 {
2697 pm->ps->velocity[2] = 0; // ignore slope movement
2698 }
2699 }
2700
2701 scale = PM_CmdScale( &pm->cmd );
2702
2703 // Get The WishVel And WishSpeed
2704 //-------------------------------
2705 if ( pm->ps->clientNum && (USENEWNAVSYSTEM || !VectorCompare( pm->ps->moveDir, vec3_origin )) )
2706 {//NPC
2707
2708 // If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds
2709 //--------------------------------------------------------------------------------------------
2710 if ((fmove!=0.0f || smove!=0.0f) && VectorCompare(pm->ps->moveDir, vec3_origin))
2711 {
2712 //gi.Printf("Generating MoveDir\n");
2713 for ( i = 0 ; i < 3 ; i++ )
2714 {
2715 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
2716 }
2717
2718 VectorCopy( wishvel, wishdir );
2719 wishspeed = VectorNormalize(wishdir);
2720 wishspeed *= scale;
2721 }
2722 // Otherwise, Use The Move Dir
2723 //-----------------------------
2724 else
2725 {
2726 wishspeed = pm->ps->speed;
2727 VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
2728 VectorCopy( pm->ps->moveDir, wishdir );
2729 }
2730
2731 }
2732 else
2733 {
2734 for ( i = 0 ; i < 3 ; i++ ) {
2735 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
2736 }
2737 // when going up or down slopes the wish velocity should Not be zero
2738 // wishvel[2] = 0;
2739
2740 VectorCopy (wishvel, wishdir);
2741 wishspeed = VectorNormalize(wishdir);
2742 wishspeed *= scale;
2743 }
2744
2745 // Handle negative speed.
2746 if ( wishspeed < 0 )
2747 {
2748 wishspeed = wishspeed * -1.0f;
2749 VectorScale( wishvel, -1.0f, wishvel );
2750 VectorScale( wishdir, -1.0f, wishdir );
2751 }
2752
2753 VectorCopy( wishvel, wishdir );
2754 wishspeed = VectorNormalize( wishdir );
2755
2756 PM_Accelerate( wishdir, wishspeed, 100 );
2757
2758 PM_StepSlideMove( 1 );
2759 }
2760
2761 /*
2762 ===================
2763 PM_FlyMove
2764
2765 Only with the flight powerup
2766 ===================
2767 */
PM_FlyMove(void)2768 static void PM_FlyMove( void )
2769 {
2770 int i;
2771 vec3_t wishvel;
2772 float wishspeed;
2773 vec3_t wishdir;
2774 float scale;
2775 float accel;
2776 qboolean lowGravMove = qfalse;
2777 qboolean jetPackMove = qfalse;
2778
2779 // normal slowdown
2780 PM_Friction ();
2781
2782 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
2783 && pm->gent
2784 && pm->gent->client
2785 && (pm->gent->client->NPC_class == CLASS_BOBAFETT||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) && pm->gent->client->moveType == MT_FLYSWIM )
2786 {//jetpack accel
2787 accel = pm_flyaccelerate;
2788 jetPackMove = qtrue;
2789 }
2790 else if ( pm->ps->gravity <= 0
2791 && ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || (pm->gent&&pm->gent->client&&pm->gent->client->moveType == MT_RUNJUMP)) )
2792 {
2793 PM_CheckJump();
2794 accel = 1.0f;
2795 pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
2796 pm->ps->jumpZStart = pm->ps->origin[2];//so we don't take a lot of damage when the gravity comes back on
2797 lowGravMove = qtrue;
2798 }
2799 else
2800 {
2801 accel = pm_flyaccelerate;
2802 }
2803
2804 scale = PM_CmdScale( &pm->cmd );
2805 //
2806 // user intentions
2807 //
2808 if ( !scale )
2809 {
2810 wishvel[0] = 0;
2811 wishvel[1] = 0;
2812 wishvel[2] = 0;
2813 }
2814 else
2815 {
2816 for (i=0 ; i<3 ; i++)
2817 {
2818 wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
2819 }
2820 if ( jetPackMove )
2821 {
2822 wishvel[2] += pm->cmd.upmove;
2823 }
2824 else if ( lowGravMove )
2825 {
2826 wishvel[2] += scale * pm->cmd.upmove;
2827 VectorScale( wishvel, 0.5f, wishvel );
2828 }
2829 }
2830
2831 VectorCopy( wishvel, wishdir );
2832 wishspeed = VectorNormalize( wishdir );
2833
2834 PM_Accelerate( wishdir, wishspeed, accel );
2835
2836 PM_StepSlideMove( 1 );
2837 }
2838
PM_GroundSlideOkay(float zNormal)2839 qboolean PM_GroundSlideOkay( float zNormal )
2840 {
2841 if ( zNormal > 0 )
2842 {
2843 if ( pm->ps->velocity[2] > 0 )
2844 {
2845 if ( pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT
2846 || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT
2847 || pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT_STOP
2848 || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT_STOP
2849 || pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START
2850 || pm->ps->legsAnim == BOTH_FORCELONGLEAP_START
2851 || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK
2852 || pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND
2853 || PM_InReboundJump( pm->ps->legsAnim ))
2854 {
2855 return qfalse;
2856 }
2857 }
2858 }
2859 return qtrue;
2860 }
2861 /*
2862 ===================
2863 PM_AirMove
2864
2865 ===================
2866 */
PM_AirMove(void)2867 static void PM_AirMove( void ) {
2868 int i;
2869 vec3_t wishvel;
2870 float fmove, smove;
2871 vec3_t wishdir;
2872 float wishspeed;
2873 usercmd_t cmd;
2874 float gravMod = 1.0f;
2875
2876 #if METROID_JUMP
2877 PM_CheckJump();
2878 #endif
2879
2880 PM_Friction();
2881
2882 fmove = pm->cmd.forwardmove;
2883 smove = pm->cmd.rightmove;
2884
2885 cmd = pm->cmd;
2886 PM_CmdScale( &cmd );
2887
2888 // set the movementDir so clients can rotate the legs for strafing
2889 PM_SetMovementDir();
2890
2891 // project moves down to flat plane
2892 pml.forward[2] = 0;
2893 pml.right[2] = 0;
2894 VectorNormalize (pml.forward);
2895 VectorNormalize (pml.right);
2896
2897 Vehicle_t *pVeh = NULL;
2898
2899 if ( pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
2900 {
2901 pVeh = pm->gent->m_pVehicle;
2902 }
2903
2904 if ( pVeh && pVeh->m_pVehicleInfo->hoverHeight > 0 )
2905 {//in a hovering vehicle, have air control
2906
2907 // Flying Or Breaking, No Control
2908 //--------------------------------
2909 if ( pVeh->m_ulFlags&VEH_FLYING || pVeh->m_ulFlags&VEH_SLIDEBREAKING)
2910 {
2911 wishspeed = 0.0f;
2912 VectorClear( wishvel );
2913 VectorClear( wishdir );
2914 }
2915
2916 // Out Of Control - Maintain pos3 Velocity
2917 //-----------------------------------------
2918 else if ((pVeh->m_ulFlags&VEH_OUTOFCONTROL) || (pVeh->m_ulFlags&VEH_STRAFERAM))
2919 {
2920 VectorCopy(pm->gent->pos3, wishvel);
2921 VectorCopy(wishvel, wishdir);
2922 wishspeed = VectorNormalize(wishdir);
2923 }
2924
2925 // Boarding - Maintain Boarding Velocity
2926 //---------------------------------------
2927 else if (pVeh->m_iBoarding)
2928 {
2929 VectorCopy(pVeh->m_vBoardingVelocity, wishvel);
2930 VectorCopy(wishvel, wishdir);
2931 wishspeed = VectorNormalize(wishdir);
2932 }
2933
2934 // Otherwise, Normal Velocity
2935 //----------------------------
2936 else
2937 {
2938 wishspeed = pm->ps->speed;
2939 VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
2940 VectorCopy( pm->ps->moveDir, wishdir );
2941 }
2942 }
2943 else if ( (pm->ps->pm_flags&PMF_SLOW_MO_FALL) )
2944 {//no air-control
2945 VectorClear( wishvel );
2946 }
2947 else
2948 {
2949 for ( i = 0 ; i < 2 ; i++ )
2950 {
2951 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
2952 }
2953 wishvel[2] = 0;
2954 }
2955
2956 VectorCopy (wishvel, wishdir);
2957 wishspeed = VectorNormalize(wishdir);
2958
2959 if ( ( DotProduct (pm->ps->velocity, wishdir) ) < 0.0f )
2960 {//Encourage deceleration away from the current velocity
2961 wishspeed *= pm_airDecelRate;
2962 }
2963
2964 // not on ground, so little effect on velocity
2965 float accelerate = pm_airaccelerate;
2966 if ( pVeh && pVeh->m_pVehicleInfo->type == VH_SPEEDER )
2967 {//speeders have more control in air
2968 //in mid-air
2969 accelerate = pVeh->m_pVehicleInfo->acceleration;
2970 if ( pml.groundPlane )
2971 {//on a slope of some kind, shouldn't have much control and should slide a lot
2972 accelerate *= 0.5f;
2973 }
2974 if (pVeh->m_ulFlags & VEH_SLIDEBREAKING)
2975 {
2976 VectorScale(pm->ps->velocity, 0.80f, pm->ps->velocity);
2977 }
2978 if (pm->ps->velocity[2]>1000.0f)
2979 {
2980 pm->ps->velocity[2] = 1000.0f;
2981 }
2982 }
2983 PM_Accelerate( wishdir, wishspeed, accelerate );
2984
2985
2986 // we may have a ground plane that is very steep, even
2987 // though we don't have a groundentity
2988 // slide along the steep plane
2989 if ( pml.groundPlane )
2990 {
2991 if ( PM_GroundSlideOkay( pml.groundTrace.plane.normal[2] ) )
2992 {
2993 PM_ClipVelocity( pm->ps->velocity, pml.groundTrace.plane.normal,
2994 pm->ps->velocity, OVERCLIP );
2995 }
2996 }
2997
2998 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
2999 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0
3000 && pm->ps->forceJumpZStart
3001 && pm->ps->velocity[2] > 0 )
3002 {//I am force jumping and I'm not holding the button anymore
3003 float curHeight = pm->ps->origin[2] - pm->ps->forceJumpZStart + (pm->ps->velocity[2]*pml.frametime);
3004 float maxJumpHeight = forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]];
3005 if ( curHeight >= maxJumpHeight )
3006 {//reached top, cut velocity
3007 pm->ps->velocity[2] = 0;
3008 }
3009 }
3010 if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
3011 {
3012 gravMod = 0.0f;
3013 }
3014 PM_StepSlideMove( gravMod );
3015
3016 if (pVeh && pm->ps->pm_flags&PMF_BUMPED)
3017 {
3018
3019 /*
3020 // Turn Vehicle In Direction Of Collision
3021 //----------------------------------------
3022 vec3_t nAngles;
3023 vectoangles(pm->ps->velocity, nAngles);
3024 nAngles[0] = pVeh->m_pParentEntity->client->ps.viewangles[0];
3025 nAngles[2] = pVeh->m_pParentEntity->client->ps.viewangles[2];
3026
3027 // toggle the teleport bit so the client knows to not lerp
3028 player->client->ps.eFlags ^= EF_TELEPORT_BIT;
3029
3030 // set angles
3031 SetClientViewAngle( pVeh->m_pParentEntity, nAngles );
3032 if (pVeh->m_pPilot)
3033 {
3034 SetClientViewAngle( pVeh->m_pPilot, nAngles );
3035 }
3036
3037 VectorCopy(nAngles, pVeh->m_vPrevOrientation);
3038 VectorCopy(nAngles, pVeh->m_vOrientation);
3039 pVeh->m_vAngularVelocity = 0.0f;
3040 */
3041
3042 // Reduce "Bounce Up Wall" Velocity
3043 //----------------------------------
3044 if (pm->ps->velocity[2]>0)
3045 {
3046 pm->ps->velocity[2] *= 0.1f;
3047 }
3048 }
3049 }
3050
3051
3052 /*
3053 ===================
3054 PM_WalkMove
3055
3056 ===================
3057 */
PM_WalkMove(void)3058 static void PM_WalkMove( void ) {
3059 int i;
3060 vec3_t wishvel;
3061 float fmove, smove;
3062 vec3_t wishdir;
3063 float wishspeed;
3064 float scale;
3065 usercmd_t cmd;
3066 float accelerate;
3067 float vel;
3068
3069 if ( pm->ps->gravity < 0 )
3070 {//float away
3071 pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
3072 pm->ps->groundEntityNum = ENTITYNUM_NONE;
3073 pml.groundPlane = qfalse;
3074 pml.walking = qfalse;
3075 if ( pm->waterlevel > 1 ) {
3076 PM_WaterMove();
3077 } else {
3078 PM_AirMove();
3079 }
3080 return;
3081 }
3082
3083 if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
3084 // begin swimming
3085 PM_WaterMove();
3086 return;
3087 }
3088
3089
3090 if ( PM_CheckJump () ) {
3091 // jumped away
3092 if ( pm->waterlevel > 1 ) {
3093 PM_WaterMove();
3094 } else {
3095 PM_AirMove();
3096 }
3097 return;
3098 }
3099
3100 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE &&//on ground
3101 pm->ps->velocity[2] <= 0 &&//not going up
3102 pm->ps->pm_flags&PMF_TIME_KNOCKBACK )//knockback fimter on (stops friction)
3103 {
3104 pm->ps->pm_flags &= ~PMF_TIME_KNOCKBACK;
3105 }
3106
3107 qboolean slide = qfalse;
3108 if ( pm->ps->pm_type == PM_DEAD )
3109 {//corpse
3110 if ( g_entities[pm->ps->groundEntityNum].client )
3111 {//on a client
3112 if ( g_entities[pm->ps->groundEntityNum].health > 0 )
3113 {//a living client
3114 //no friction
3115 slide = qtrue;
3116 }
3117 }
3118 }
3119 if ( !slide )
3120 {
3121 PM_Friction ();
3122 }
3123
3124 if ( g_debugMelee->integer )
3125 {
3126 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//player
3127 && cg.renderingThirdPerson//in third person
3128 && ((pm->cmd.buttons&BUTTON_USE)||pm->ps->leanStopDebounceTime)//holding use or leaning
3129 //&& (pm->ps->forcePowersActive&(1<<FP_SPEED))
3130 && pm->ps->groundEntityNum != ENTITYNUM_NONE//on ground
3131 && !cg_usingInFrontOf )//nothing to use
3132 {//holding use stops you from moving
3133 return;
3134 }
3135 }
3136 fmove = pm->cmd.forwardmove;
3137 smove = pm->cmd.rightmove;
3138
3139 cmd = pm->cmd;
3140 scale = PM_CmdScale( &cmd );
3141
3142 // set the movementDir so clients can rotate the legs for strafing
3143 PM_SetMovementDir();
3144
3145 // project moves down to flat plane
3146 pml.forward[2] = 0;
3147 pml.right[2] = 0;
3148
3149 // project the forward and right directions onto the ground plane
3150 PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP );
3151 PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP );
3152 //
3153 VectorNormalize (pml.forward);
3154 VectorNormalize (pml.right);
3155
3156 /* if ( ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE ) && pm->gent->NPC )
3157 {//speeder control scheme
3158 vec3_t vfwd, vrt;
3159 AngleVectors( ((CVehicleNPC *)pm->gent->NPC)->m_vOrientation, vfwd, vrt, NULL );
3160
3161 float speed = pm->ps->speed;
3162 if ( fmove < 0 )
3163 {//going backwards
3164 if ( speed < 0 )
3165 {//speed is negative, but since our command is reverse, make speed positive
3166 speed = fabs( speed );
3167 }
3168 else if ( speed > 0 )
3169 {//trying to move back but speed is still positive, so keep moving forward (we'll slow down eventually)
3170 speed *= -1;
3171 }
3172 }
3173 VectorScale( vfwd, speed*fmove/127.0f, wishvel );
3174 //VectorMA( wishvel, pm->ps->speed*smove/127.0f, vrt, wishvel );
3175 wishspeed = VectorNormalize2( wishvel, wishdir );
3176 }
3177 else*/
3178
3179 // Get The WishVel And WishSpeed
3180 //-------------------------------
3181 if ( pm->ps->clientNum && (USENEWNAVSYSTEM || !VectorCompare( pm->ps->moveDir, vec3_origin )) )
3182 {//NPC
3183
3184 // If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds
3185 //--------------------------------------------------------------------------------------------
3186 if ((fmove!=0.0f || smove!=0.0f) && VectorCompare(pm->ps->moveDir, vec3_origin))
3187 {
3188 //gi.Printf("Generating MoveDir\n");
3189 for ( i = 0 ; i < 3 ; i++ )
3190 {
3191 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3192 }
3193
3194 VectorCopy( wishvel, wishdir );
3195 wishspeed = VectorNormalize(wishdir);
3196 wishspeed *= scale;
3197 }
3198 // Otherwise, Use The Move Dir
3199 //-----------------------------
3200 else
3201 {
3202 wishspeed = pm->ps->speed;
3203 VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
3204 VectorCopy( pm->ps->moveDir, wishdir );
3205 }
3206
3207 }
3208 else
3209 {
3210 for ( i = 0 ; i < 3 ; i++ ) {
3211 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3212 }
3213 // when going up or down slopes the wish velocity should Not be zero
3214 // wishvel[2] = 0;
3215
3216 VectorCopy (wishvel, wishdir);
3217 wishspeed = VectorNormalize(wishdir);
3218 wishspeed *= scale;
3219 }
3220
3221 // Handle negative speed.
3222 if ( wishspeed < 0 )
3223 {
3224 wishspeed = wishspeed * -1.0f;
3225 VectorScale( wishvel, -1.0f, wishvel );
3226 VectorScale( wishdir, -1.0f, wishdir );
3227 }
3228
3229 // clamp the speed lower if ducking
3230 if ( pm->ps->pm_flags & PMF_DUCKED && !PM_InKnockDown( pm->ps ) )
3231 {
3232 if ( wishspeed > pm->ps->speed * pm_duckScale )
3233 {
3234 wishspeed = pm->ps->speed * pm_duckScale;
3235 }
3236 }
3237
3238 // clamp the speed lower if wading or walking on the bottom
3239 if ( pm->waterlevel ) {
3240 float waterScale;
3241
3242 waterScale = pm->waterlevel / 3.0;
3243 waterScale = 1.0 - ( 1.0 - pm_swimScale ) * waterScale;
3244 if ( wishspeed > pm->ps->speed * waterScale ) {
3245 wishspeed = pm->ps->speed * waterScale;
3246 }
3247 }
3248
3249 // when a player gets hit, they temporarily lose
3250 // full control, which allows them to be moved a bit
3251 if ( Flying == FLY_HOVER )
3252 {
3253 accelerate = pm_vehicleaccelerate;
3254 }
3255 else if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || (pm->ps->pm_flags&PMF_TIME_KNOCKBACK) || (pm->ps->pm_flags&PMF_TIME_NOFRICTION) )
3256 {
3257 accelerate = pm_airaccelerate;
3258 }
3259 else
3260 {
3261 accelerate = pm_accelerate;
3262
3263 // Wind Affects Acceleration
3264 //===================================================
3265 if (wishspeed>0.0f && pm->gent && !pml.walking)
3266 {
3267 if (gi.WE_GetWindGusting(pm->gent->currentOrigin))
3268 {
3269 vec3_t windDir;
3270 if (gi.WE_GetWindVector(windDir, pm->gent->currentOrigin))
3271 {
3272 if (gi.WE_IsOutside(pm->gent->currentOrigin))
3273 {
3274 VectorScale(windDir, -1.0f, windDir);
3275 accelerate *= (1.0f - (DotProduct(wishdir, windDir)*0.55f));
3276 }
3277 }
3278 }
3279 }
3280 //===================================================
3281 }
3282
3283 PM_Accelerate (wishdir, wishspeed, accelerate);
3284
3285 //Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
3286 //Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity));
3287
3288 if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK || (pm->ps->pm_flags&PMF_TIME_NOFRICTION) ) {
3289 if ( pm->ps->gravity >= 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE && !VectorLengthSquared( pm->ps->velocity ) && pml.groundTrace.plane.normal[2] == 1.0 )
3290 {//on ground and not moving and on level ground, no reason to do stupid fucking gravity with the clipvelocity!!!!
3291 }
3292 else
3293 {
3294 if ( !(pm->ps->eFlags&EF_FORCE_GRIPPED) && !(pm->ps->eFlags&EF_FORCE_DRAINED) )
3295 {
3296 pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
3297 }
3298 }
3299 } else {
3300 // don't reset the z velocity for slopes
3301 // pm->ps->velocity[2] = 0;
3302 }
3303
3304 vel = VectorLength(pm->ps->velocity);
3305
3306 // slide along the ground plane
3307 PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
3308 pm->ps->velocity, OVERCLIP );
3309
3310 // don't decrease velocity when going up or down a slope
3311 VectorNormalize(pm->ps->velocity);
3312 VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
3313
3314 // don't do anything if standing still
3315 if ( !pm->ps->velocity[0] && !pm->ps->velocity[1] ) {
3316 return;
3317 }
3318
3319 if ( pm->ps->gravity <= 0 )
3320 {//need to apply gravity since we're going to float up from ground
3321 PM_StepSlideMove( 1 );
3322 }
3323 else
3324 {
3325 PM_StepSlideMove( 0 );
3326 }
3327
3328 //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity));
3329
3330 }
3331
3332
3333 /*
3334 ==============
3335 PM_DeadMove
3336 ==============
3337 */
PM_DeadMove(void)3338 static void PM_DeadMove( void ) {
3339 float forward;
3340
3341 // If this is a vehicle, tell him he's dead, but give him a little while to do his things.
3342 /* if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->gent->NPC && pm->gent->health != -99999 )
3343 {
3344 pm->gent->health = 1;
3345 ((CVehicleNPC *)pm->gent->NPC)->StartDeathDelay( 0 );
3346 }
3347 else
3348 {
3349 pm->gent->health = 0;
3350 }*/
3351
3352 if ( !pml.walking ) {
3353 return;
3354 }
3355
3356 // extra friction
3357
3358 forward = VectorLength (pm->ps->velocity);
3359 forward -= 20;
3360 if ( forward <= 0 ) {
3361 VectorClear (pm->ps->velocity);
3362 } else {
3363 VectorNormalize (pm->ps->velocity);
3364 VectorScale (pm->ps->velocity, forward, pm->ps->velocity);
3365 }
3366 }
3367
3368
3369 /*
3370 ===============
3371 PM_NoclipMove
3372 ===============
3373 */
PM_NoclipMove(void)3374 static void PM_NoclipMove( void ) {
3375 float speed, drop, friction, control, newspeed;
3376 int i;
3377 vec3_t wishvel;
3378 float fmove, smove;
3379 vec3_t wishdir;
3380 float wishspeed;
3381 float scale;
3382
3383 if(pm->gent && pm->gent->client)
3384 {
3385 pm->ps->viewheight = pm->gent->client->standheight + STANDARD_VIEWHEIGHT_OFFSET;
3386 // if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] )
3387 // {
3388 // assert(0);
3389 // }
3390
3391 VectorCopy( pm->gent->mins, pm->mins );
3392 VectorCopy( pm->gent->maxs, pm->maxs );
3393 }
3394 else
3395 {
3396 pm->ps->viewheight = DEFAULT_MAXS_2 + STANDARD_VIEWHEIGHT_OFFSET;//DEFAULT_VIEWHEIGHT;
3397
3398 if ( !DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2 )
3399 {
3400 assert(0);
3401 }
3402
3403 pm->mins[0] = DEFAULT_MINS_0;
3404 pm->mins[1] = DEFAULT_MINS_1;
3405 pm->mins[2] = DEFAULT_MINS_2;
3406
3407 pm->maxs[0] = DEFAULT_MAXS_0;
3408 pm->maxs[1] = DEFAULT_MAXS_1;
3409 pm->maxs[2] = DEFAULT_MAXS_2;
3410 }
3411
3412 // friction
3413
3414 speed = VectorLength (pm->ps->velocity);
3415 if (speed < 1)
3416 {
3417 VectorCopy (vec3_origin, pm->ps->velocity);
3418 }
3419 else
3420 {
3421 drop = 0;
3422
3423 friction = pm_friction*1.5; // extra friction
3424 control = speed < pm_stopspeed ? pm_stopspeed : speed;
3425 drop += control*friction*pml.frametime;
3426
3427 // scale the velocity
3428 newspeed = speed - drop;
3429 if (newspeed < 0)
3430 newspeed = 0;
3431 newspeed /= speed;
3432
3433 VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity);
3434 }
3435
3436 // accelerate
3437 scale = PM_CmdScale( &pm->cmd );
3438 if (pm->cmd.buttons & BUTTON_ATTACK) { //turbo boost
3439 scale *= 10;
3440 }
3441 if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { //turbo boost
3442 scale *= 10;
3443 }
3444
3445 fmove = pm->cmd.forwardmove;
3446 smove = pm->cmd.rightmove;
3447
3448 for (i=0 ; i<3 ; i++)
3449 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3450 wishvel[2] += pm->cmd.upmove;
3451
3452 VectorCopy (wishvel, wishdir);
3453 wishspeed = VectorNormalize(wishdir);
3454 wishspeed *= scale;
3455
3456 PM_Accelerate( wishdir, wishspeed, pm_accelerate );
3457
3458 // move
3459 VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin);
3460 }
3461
3462 //============================================================================
3463
PM_DamageForDelta(int delta)3464 static float PM_DamageForDelta( int delta )
3465 {
3466 float damage = delta;
3467 if ( pm->gent->NPC )
3468 {
3469 if ( pm->ps->weapon == WP_SABER
3470 || (pm->gent->client && pm->gent->client->NPC_class == CLASS_REBORN) )
3471 {//FIXME: for now Jedi take no falling damage, but really they should if pushed off?
3472 damage = 0;
3473 }
3474 }
3475 else if ( pm->ps->clientNum < MAX_CLIENTS )
3476 {
3477 if ( damage < 50 )
3478 {
3479 if ( damage > 24 )
3480 {
3481 damage = damage - 25;
3482 }
3483 }
3484 else
3485 {
3486 damage *= 0.5f;
3487 }
3488 }
3489 return damage * 0.5f;
3490 }
3491
PM_CrashLandDamage(int damage)3492 static void PM_CrashLandDamage( int damage )
3493 {
3494 if ( pm->gent )
3495 {
3496 int dflags = DAMAGE_NO_ARMOR;
3497 if ( pm->gent->NPC && (pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) )
3498 {
3499 damage = 1000;
3500 dflags |= DAMAGE_DIE_ON_IMPACT;
3501 }
3502 else
3503 {
3504 damage = PM_DamageForDelta( damage );
3505
3506 if ( (pm->gent->flags&FL_NO_IMPACT_DMG) )
3507 return;
3508 }
3509
3510 if ( damage )
3511 {
3512 pm->gent->painDebounceTime = level.time + 200; // no normal pain sound
3513 G_Damage( pm->gent, NULL, player, NULL, NULL, damage, dflags, MOD_FALLING );
3514 }
3515 }
3516 }
3517
3518 /*
3519 static float PM_CrashLandDelta( vec3_t org, vec3_t prevOrg, vec3_t prev_vel, float grav, int waterlevel )
3520 {
3521 float delta;
3522 float dist;
3523 float vel, acc;
3524 float t;
3525 float a, b, c, den;
3526
3527 // calculate the exact velocity on landing
3528 dist = org[2] - prevOrg[2];
3529 vel = prev_vel[2];
3530 acc = -grav;
3531
3532 a = acc / 2;
3533 b = vel;
3534 c = -dist;
3535
3536 den = b * b - 4 * a * c;
3537 if ( den < 0 )
3538 {
3539 return 0;
3540 }
3541 t = (-b - sqrt( den ) ) / ( 2 * a );
3542
3543 delta = vel + t * acc;
3544 delta = delta*delta * 0.0001;
3545
3546 // never take falling damage if completely underwater
3547 if ( waterlevel == 3 )
3548 {
3549 return 0;
3550 }
3551 // reduce falling damage if there is standing water
3552 if ( waterlevel == 2 )
3553 {
3554 delta *= 0.25;
3555 }
3556 if ( waterlevel == 1 )
3557 {
3558 delta *= 0.5;
3559 }
3560
3561 return delta;
3562 }
3563 */
3564
PM_CrashLandDelta(vec3_t prev_vel,int waterlevel)3565 static float PM_CrashLandDelta( vec3_t prev_vel, int waterlevel )
3566 {
3567 float delta;
3568
3569 if ( pm->waterlevel == 3 )
3570 {
3571 return 0;
3572 }
3573 delta = fabs(prev_vel[2])/10;//VectorLength( prev_vel )
3574
3575 // reduce falling damage if there is standing water
3576 if ( pm->waterlevel == 2 )
3577 {
3578 delta *= 0.25;
3579 }
3580 if ( pm->waterlevel == 1 )
3581 {
3582 delta *= 0.5;
3583 }
3584
3585 return delta;
3586 }
3587
PM_GetLandingAnim(void)3588 int PM_GetLandingAnim( void )
3589 {
3590 int anim = pm->ps->legsAnim;
3591
3592 //special cases:
3593 if ( anim == BOTH_FLIP_ATTACK7
3594 || anim == BOTH_FLIP_HOLD7 )
3595 {
3596 return BOTH_FLIP_LAND;
3597 }
3598 else if ( anim == BOTH_FLIP_LAND )
3599 {
3600 if ( !g_allowBunnyhopping->integer ) {
3601 //stick landings some
3602 pm->ps->velocity[0] *= 0.5f;
3603 pm->ps->velocity[1] *= 0.5f;
3604 }
3605 return BOTH_LAND1;
3606 }
3607 else if ( PM_InAirKickingAnim( anim ) )
3608 {
3609 switch ( anim )
3610 {
3611 case BOTH_A7_KICK_F_AIR:
3612 return BOTH_FORCELAND1;
3613 break;
3614 case BOTH_A7_KICK_B_AIR:
3615 return BOTH_FORCELANDBACK1;
3616 break;
3617 case BOTH_A7_KICK_R_AIR:
3618 return BOTH_FORCELANDRIGHT1;
3619 break;
3620 case BOTH_A7_KICK_L_AIR:
3621 return BOTH_FORCELANDLEFT1;
3622 break;
3623 }
3624 }
3625
3626 if ( PM_SpinningAnim( anim ) || PM_SaberInSpecialAttack( anim ) )
3627 {
3628 return -1;
3629 }
3630 switch ( anim )
3631 {
3632 case BOTH_FORCEJUMPLEFT1:
3633 case BOTH_FORCEINAIRLEFT1:
3634 anim = BOTH_FORCELANDLEFT1;
3635 if ( !g_allowBunnyhopping->integer ) {
3636 //stick landings some
3637 pm->ps->velocity[0] *= 0.5f;
3638 pm->ps->velocity[1] *= 0.5f;
3639 }
3640 break;
3641 case BOTH_FORCEJUMPRIGHT1:
3642 case BOTH_FORCEINAIRRIGHT1:
3643 anim = BOTH_FORCELANDRIGHT1;
3644 if ( !g_allowBunnyhopping->integer ) {
3645 //stick landings some
3646 pm->ps->velocity[0] *= 0.5f;
3647 pm->ps->velocity[1] *= 0.5f;
3648 }
3649 break;
3650 case BOTH_FORCEJUMP1:
3651 case BOTH_FORCEINAIR1:
3652 if ( !g_allowBunnyhopping->integer ) {
3653 //stick landings some
3654 pm->ps->velocity[0] *= 0.5f;
3655 pm->ps->velocity[1] *= 0.5f;
3656 }
3657 anim = BOTH_FORCELAND1;
3658 break;
3659 case BOTH_FORCEJUMPBACK1:
3660 case BOTH_FORCEINAIRBACK1:
3661 if ( !g_allowBunnyhopping->integer ) {
3662 //stick landings some
3663 pm->ps->velocity[0] *= 0.5f;
3664 pm->ps->velocity[1] *= 0.5f;
3665 }
3666 anim = BOTH_FORCELANDBACK1;
3667 break;
3668 case BOTH_JUMPLEFT1:
3669 case BOTH_INAIRLEFT1:
3670 anim = BOTH_LANDLEFT1;
3671 if ( !g_allowBunnyhopping->integer ) {
3672 //stick landings some
3673 pm->ps->velocity[0] *= 0.5f;
3674 pm->ps->velocity[1] *= 0.5f;
3675 }
3676 break;
3677 case BOTH_JUMPRIGHT1:
3678 case BOTH_INAIRRIGHT1:
3679 anim = BOTH_LANDRIGHT1;
3680 if ( !g_allowBunnyhopping->integer ) {
3681 //stick landings some
3682 pm->ps->velocity[0] *= 0.5f;
3683 pm->ps->velocity[1] *= 0.5f;
3684 }
3685 break;
3686 case BOTH_JUMP1:
3687 case BOTH_INAIR1:
3688 anim = BOTH_LAND1;
3689 if ( !g_allowBunnyhopping->integer ) {
3690 //stick landings some
3691 pm->ps->velocity[0] *= 0.5f;
3692 pm->ps->velocity[1] *= 0.5f;
3693 }
3694 break;
3695 case BOTH_JUMPBACK1:
3696 case BOTH_INAIRBACK1:
3697 anim = BOTH_LANDBACK1;
3698 if ( !g_allowBunnyhopping->integer ) {
3699 //stick landings some
3700 pm->ps->velocity[0] *= 0.5f;
3701 pm->ps->velocity[1] *= 0.5f;
3702 }
3703 break;
3704 case BOTH_BUTTERFLY_LEFT:
3705 case BOTH_BUTTERFLY_RIGHT:
3706 case BOTH_BUTTERFLY_FL1:
3707 case BOTH_BUTTERFLY_FR1:
3708 case BOTH_FJSS_TR_BL:
3709 case BOTH_FJSS_TL_BR:
3710 case BOTH_LUNGE2_B__T_:
3711 case BOTH_FORCELEAP2_T__B_:
3712 case BOTH_ARIAL_LEFT:
3713 case BOTH_ARIAL_RIGHT:
3714 case BOTH_ARIAL_F1:
3715 case BOTH_CARTWHEEL_LEFT:
3716 case BOTH_CARTWHEEL_RIGHT:
3717 case BOTH_JUMPFLIPSLASHDOWN1://#
3718 case BOTH_JUMPFLIPSTABDOWN://#
3719 case BOTH_JUMPATTACK6:
3720 case BOTH_JUMPATTACK7:
3721 case BOTH_A7_KICK_F:
3722 case BOTH_A7_KICK_B:
3723 case BOTH_A7_KICK_R:
3724 case BOTH_A7_KICK_L:
3725 case BOTH_A7_KICK_S:
3726 case BOTH_A7_KICK_BF:
3727 case BOTH_A7_KICK_RL:
3728 case BOTH_A7_KICK_F_AIR:
3729 case BOTH_A7_KICK_B_AIR:
3730 case BOTH_A7_KICK_R_AIR:
3731 case BOTH_A7_KICK_L_AIR:
3732 case BOTH_STABDOWN:
3733 case BOTH_STABDOWN_STAFF:
3734 case BOTH_STABDOWN_DUAL:
3735 case BOTH_A6_SABERPROTECT:
3736 case BOTH_A7_SOULCAL:
3737 case BOTH_A1_SPECIAL:
3738 case BOTH_A2_SPECIAL:
3739 case BOTH_A3_SPECIAL:
3740 case BOTH_PULL_IMPALE_STAB:
3741 case BOTH_PULL_IMPALE_SWING:
3742 anim = -1;
3743 break;
3744 case BOTH_FORCELONGLEAP_START:
3745 case BOTH_FORCELONGLEAP_ATTACK:
3746 return BOTH_FORCELONGLEAP_LAND;
3747 break;
3748 case BOTH_WALL_RUN_LEFT://#
3749 case BOTH_WALL_RUN_RIGHT://#
3750 if ( pm->ps->legsAnimTimer > 500 )
3751 {//only land at end of anim
3752 return -1;
3753 }
3754 //NOTE: falls through on purpose!
3755 default:
3756 if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP )
3757 {
3758 anim = BOTH_LANDBACK1;
3759 }
3760 else
3761 {
3762 anim = BOTH_LAND1;
3763 }
3764 if ( !g_allowBunnyhopping->integer ) {
3765 //stick landings some
3766 pm->ps->velocity[0] *= 0.5f;
3767 pm->ps->velocity[1] *= 0.5f;
3768 }
3769 break;
3770 }
3771 return anim;
3772 }
3773
G_StartRoll(gentity_t * ent,int anim)3774 void G_StartRoll( gentity_t *ent, int anim )
3775 {
3776 NPC_SetAnim(ent,SETANIM_BOTH,anim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS);
3777 ent->client->ps.weaponTime = ent->client->ps.torsoAnimTimer - 200;//just to make sure it's cleared when roll is done
3778 G_AddEvent( ent, EV_ROLL, 0 );
3779 ent->client->ps.saberMove = LS_NONE;
3780 }
3781
PM_TryRoll(void)3782 static qboolean PM_TryRoll( void )
3783 {
3784 float rollDist = 192;//was 64;
3785 if ( PM_SaberInAttack( pm->ps->saberMove ) || PM_SaberInSpecialAttack( pm->ps->torsoAnim )
3786 || PM_SpinningSaberAnim( pm->ps->legsAnim )
3787 || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&PM_SaberInStart( pm->ps->saberMove )) )
3788 {//attacking or spinning (or, if player, starting an attack)
3789 if ( PM_CanRollFromSoulCal( pm->ps ) )
3790 {//hehe
3791 }
3792 else
3793 {
3794 return qfalse;
3795 }
3796 }
3797 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && (!cg.renderingThirdPerson || cg.zoomMode) )
3798 {//player can't do this in 1st person
3799 return qfalse;
3800 }
3801 if ( !pm->gent )
3802 {
3803 return qfalse;
3804 }
3805 if ( (pm->ps->saber[0].saberFlags&SFL_NO_ROLLS) )
3806 {
3807 return qfalse;
3808 }
3809 if ( pm->ps->dualSabers
3810 && (pm->ps->saber[1].saberFlags&SFL_NO_ROLLS) )
3811 {
3812 return qfalse;
3813 }
3814 if ( pm->ps->clientNum && pm->gent->NPC )
3815 {//NPC
3816 if ( pm->gent->NPC->scriptFlags&SCF_NO_ACROBATICS )
3817 {//scripted to never do acrobatics
3818 return qfalse;
3819 }
3820
3821 if ( pm->ps->weapon == WP_SABER )
3822 {//jedi/reborn
3823 if ( pm->gent->NPC->rank != RANK_CREWMAN && pm->gent->NPC->rank < RANK_LT_JG )
3824 {//reborn/jedi who are not acrobats or fencers can't do any of these acrobatics
3825 return qfalse;
3826 }
3827 }
3828 else
3829 {//non-jedi/reborn
3830 if ( pm->ps->weapon != WP_NONE )//not empty-handed...who would that be???
3831 {//only jedi/reborn NPCs should be able to do rolls (with a few exceptions)
3832 if ( !pm->gent
3833 || !pm->gent->client
3834 || (pm->gent->client->NPC_class != CLASS_BOBAFETT //boba can roll with it, baby
3835 && pm->gent->client->NPC_class != CLASS_REBORN //reborn using weapons other than saber can still roll
3836 ))
3837 {//can't roll
3838 return qfalse;
3839 }
3840 }
3841 }
3842 }
3843
3844 vec3_t fwd, right, traceto,
3845 mins = { pm->mins[0], pm->mins[1], pm->mins[2] + STEPSIZE },
3846 maxs = { pm->maxs[0], pm->maxs[1], (float)pm->gent->client->crouchheight },
3847 fwdAngles = { 0, pm->ps->viewangles[YAW], 0 };
3848 trace_t trace;
3849 int anim = -1;
3850 AngleVectors( fwdAngles, fwd, right, NULL );
3851 //FIXME: trace ahead for clearance to roll
3852 if ( pm->cmd.forwardmove )
3853 {
3854 if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
3855 {
3856 anim = BOTH_ROLL_B;
3857 VectorMA( pm->ps->origin, -rollDist, fwd, traceto );
3858 }
3859 else
3860 {
3861 anim = BOTH_ROLL_F;
3862 VectorMA( pm->ps->origin, rollDist, fwd, traceto );
3863 }
3864 }
3865 else if ( pm->cmd.rightmove > 0 )
3866 {
3867 anim = BOTH_ROLL_R;
3868 VectorMA( pm->ps->origin, rollDist, right, traceto );
3869 }
3870 else if ( pm->cmd.rightmove < 0 )
3871 {
3872 anim = BOTH_ROLL_L;
3873 VectorMA( pm->ps->origin, -rollDist, right, traceto );
3874 }
3875 else
3876 {//???
3877 }
3878 if ( anim != -1 )
3879 {
3880 qboolean roll = qfalse;
3881 int clipmask = CONTENTS_SOLID;
3882 if ( pm->ps->clientNum )
3883 {
3884 clipmask |= (CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP);
3885 }
3886 else
3887 {
3888 if ( pm->gent && pm->gent->enemy && pm->gent->enemy->health > 0 )
3889 {//player can always roll in combat
3890 roll = qtrue;
3891 }
3892 else
3893 {
3894 clipmask |= CONTENTS_PLAYERCLIP;
3895 }
3896 }
3897 if ( !roll )
3898 {
3899 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, clipmask, (EG2_Collision)0, 0 );
3900 if ( trace.fraction >= 1.0f )
3901 {//okay, clear, check for a bottomless drop
3902 vec3_t top;
3903 VectorCopy( traceto, top );
3904 traceto[2] -= 256;
3905 pm->trace( &trace, top, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID, (EG2_Collision)0, 0 );
3906 if ( trace.fraction < 1.0f )
3907 {//not a bottomless drop
3908 roll = qtrue;
3909 }
3910 }
3911 else
3912 {//hit an architectural obstruction
3913 if ( pm->ps->clientNum )
3914 {//NPCs don't care about rolling into walls, just off ledges
3915 if ( !(trace.contents&CONTENTS_BOTCLIP) )
3916 {
3917 roll = qtrue;
3918 }
3919 }
3920 else if ( G_EntIsDoor( trace.entityNum ) )
3921 {//okay to roll into a door
3922 if ( G_EntIsUnlockedDoor( trace.entityNum ) )
3923 {//if it's an auto-door
3924 roll = qtrue;
3925 }
3926 }
3927 else
3928 {//check other conditions
3929 gentity_t *traceEnt = &g_entities[trace.entityNum];
3930 if ( traceEnt && (traceEnt->svFlags&SVF_GLASS_BRUSH) )
3931 {//okay to roll through glass
3932 roll = qtrue;
3933 }
3934 }
3935 }
3936 }
3937 if ( roll )
3938 {
3939 G_StartRoll( pm->gent, anim );
3940 return qtrue;
3941 }
3942 }
3943 return qfalse;
3944 }
3945
3946 extern void CG_LandingEffect( vec3_t origin, vec3_t normal, int material );
PM_CrashLandEffect(void)3947 static void PM_CrashLandEffect( void )
3948 {
3949 if ( pm->waterlevel )
3950 {
3951 return;
3952 }
3953 float delta = fabs(pml.previous_velocity[2])/10;//VectorLength( pml.previous_velocity );?
3954 if ( delta >= 30 )
3955 {
3956 vec3_t bottom = {pm->ps->origin[0],pm->ps->origin[1],pm->ps->origin[2]+pm->mins[2]+1};
3957 CG_LandingEffect( bottom, pml.groundTrace.plane.normal, (pml.groundTrace.surfaceFlags&MATERIAL_MASK) );
3958 }
3959 }
3960 /*
3961 =================
3962 PM_CrashLand
3963
3964 Check for hard landings that generate sound events
3965 =================
3966 */
PM_CrashLand(void)3967 static void PM_CrashLand( void )
3968 {
3969 float delta = 0;
3970 qboolean forceLanding = qfalse;
3971
3972 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
3973 {
3974 if ( pm->gent->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL )
3975 {
3976 float dot = DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal );
3977 //Com_Printf("%i:crashland %4.2f\n", c_pmove, pm->ps->velocity[2]);
3978 if ( dot < -100.0f )
3979 {
3980 //NOTE: never hits this anyway
3981 if ( pm->gent->m_pVehicle->m_pVehicleInfo->iImpactFX )
3982 {//make sparks
3983 if ( !Q_irand( 0, 3 ) )
3984 {//FIXME: debounce
3985 G_PlayEffect( pm->gent->m_pVehicle->m_pVehicleInfo->iImpactFX, pm->ps->origin, pml.groundTrace.plane.normal );
3986 }
3987 }
3988 int damage = floor(fabs(dot+100)/10.0f);
3989 if ( damage >= 0 )
3990 {
3991 G_Damage( pm->gent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING );
3992 }
3993 }
3994 }
3995 return;
3996 }
3997
3998 if ( (pm->ps->pm_flags&PMF_TRIGGER_PUSHED) )
3999 {
4000 delta = 21;//?
4001 forceLanding = qtrue;
4002 }
4003 else
4004 {
4005 if ( pm->gent && pm->gent->NPC && pm->gent->NPC->aiFlags & NPCAI_DIE_ON_IMPACT )
4006 {//have to do death on impact if we are falling to our death, FIXME: should we avoid any additional damage this func?
4007 PM_CrashLandDamage( 1000 );
4008 }
4009 else if ( pm->gent
4010 && pm->gent->client
4011 && (pm->gent->client->NPC_class == CLASS_BOBAFETT||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) )
4012 {
4013 if ( JET_Flying( pm->gent ) )
4014 {
4015 if ( pm->gent->client->NPC_class == CLASS_BOBAFETT
4016 || (pm->gent->client->NPC_class == CLASS_ROCKETTROOPER&&pm->gent->NPC&&pm->gent->NPC->rank<RANK_LT) )
4017 {
4018 JET_FlyStop( pm->gent );
4019 }
4020 else
4021 {
4022 pm->ps->velocity[2] += Q_flrand( 100, 200 );
4023 }
4024 PM_AddEvent( EV_FALL_SHORT );
4025 }
4026 if ( pm->ps->forceJumpZStart )
4027 {//we were force-jumping
4028 forceLanding = qtrue;
4029 }
4030 delta = 1;
4031 }
4032 else if ( pm->ps->jumpZStart && (pm->ps->forcePowerLevel[FP_LEVITATION] >= FORCE_LEVEL_1||(pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())) )
4033 {//we were force-jumping
4034 if ( pm->ps->origin[2] >= pm->ps->jumpZStart )
4035 {//we landed at same height or higher than we landed
4036 if ( pm->ps->forceJumpZStart )
4037 {//we were force-jumping
4038 forceLanding = qtrue;
4039 }
4040 delta = 0;
4041 }
4042 else
4043 {//take off some of it, at least
4044 delta = (pm->ps->jumpZStart-pm->ps->origin[2]);
4045 float dropAllow = forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]];
4046 if ( dropAllow < 128 )
4047 {//always allow a drop from 128, at least
4048 dropAllow = 128;
4049 }
4050 if ( delta > forceJumpHeight[FORCE_LEVEL_1] )
4051 {//will have to use force jump ability to absorb some of it
4052 forceLanding = qtrue;//absorbed some - just to force the correct animation to play below
4053 }
4054 delta = (delta - dropAllow)/2;
4055 }
4056 if ( delta < 1 )
4057 {
4058 delta = 1;
4059 }
4060 }
4061
4062 if ( !delta )
4063 {
4064 delta = PM_CrashLandDelta( pml.previous_velocity, pm->waterlevel );
4065 }
4066 }
4067
4068 PM_CrashLandEffect();
4069
4070 if ( (pm->ps->pm_flags&PMF_DUCKED) && (level.time-pm->ps->lastOnGround)>500 )
4071 {//must be crouched and have been inthe air for half a second minimum
4072 if( !PM_InOnGroundAnim( pm->ps ) && !PM_InKnockDown( pm->ps ) )
4073 {//roll!
4074 if ( PM_TryRoll() )
4075 {//absorb some impact
4076 delta *= 0.5f;
4077 }
4078 }
4079 }
4080
4081 // If he just entered the water (from a fall presumably), absorb some of the impact.
4082 if ( pm->waterlevel >= 2 )
4083 {
4084 delta *= 0.4f;
4085 }
4086
4087 if ( delta < 1 )
4088 {
4089 AddSoundEvent( pm->gent, pm->ps->origin, 32, AEL_MINOR, qfalse, qtrue );
4090 return;
4091 }
4092
4093 qboolean deadFallSound = qfalse;
4094 if( !PM_InDeathAnim() )
4095 {
4096 if ( PM_InAirKickingAnim( pm->ps->legsAnim )
4097 && pm->ps->torsoAnim == pm->ps->legsAnim )
4098 {
4099 int anim = PM_GetLandingAnim();
4100 if ( anim != -1 )
4101 {//interrupting a kick clears everything
4102 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms
4103 pm->ps->saberMove = LS_READY;
4104 pm->ps->weaponTime = 0;
4105 if ( !g_allowBunnyhopping->integer ) {
4106 //stick landings some
4107 pm->ps->velocity[0] *= 0.5f;
4108 pm->ps->velocity[1] *= 0.5f;
4109 }
4110 }
4111 }
4112 else if ( pm->gent
4113 && pm->gent->client
4114 && pm->gent->client->NPC_class == CLASS_ROCKETTROOPER )
4115 {//rockettroopers are simpler
4116 int anim = PM_GetLandingAnim();
4117 if ( anim != -1 )
4118 {
4119 if ( pm->gent->NPC
4120 && pm->gent->NPC->rank < RANK_LT )
4121 {//special case: ground-based rocket troopers *always* play land anim on whole body
4122 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms
4123 }
4124 else
4125 {
4126 PM_SetAnim( pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms
4127 }
4128 }
4129 }
4130 else if ( pm->cmd.upmove >= 0 && !PM_InKnockDown( pm->ps ) && !PM_InRoll( pm->ps ))
4131 {//not crouching
4132 if ( delta > 10
4133 || pm->ps->pm_flags & PMF_BACKWARDS_JUMP
4134 || (pm->ps->forcePowersActive&(1<<FP_LEVITATION))
4135 || forceLanding ) //EV_FALL_SHORT or jumping back or force-land
4136 {// decide which landing animation to use
4137 if ( pm->gent
4138 && pm->gent->client
4139 && (pm->gent->client->NPC_class == CLASS_RANCOR || pm->gent->client->NPC_class == CLASS_WAMPA ) )
4140 {
4141 }
4142 else
4143 {
4144 int anim = PM_GetLandingAnim();
4145 if ( anim != -1 )
4146 {
4147 if ( PM_FlippingAnim( pm->ps->torsoAnim )
4148 || PM_SpinningAnim( pm->ps->torsoAnim )
4149 || pm->ps->torsoAnim == BOTH_FLIP_LAND )
4150 {//interrupt these if we're going to play a land
4151 pm->ps->torsoAnimTimer = 0;
4152 }
4153 if ( anim == BOTH_FORCELONGLEAP_LAND )
4154 {
4155 if ( pm->gent )
4156 {
4157 G_SoundOnEnt( pm->gent, CHAN_AUTO, "sound/player/slide.wav" );
4158 }
4159 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms
4160 }
4161 else if ( anim == BOTH_FLIP_LAND
4162 || (pm->ps->torsoAnim == BOTH_FLIP_LAND) )
4163 {
4164 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms
4165 }
4166 else if ( PM_InAirKickingAnim( pm->ps->legsAnim )
4167 && pm->ps->torsoAnim == pm->ps->legsAnim )
4168 {//interrupting a kick clears everything
4169 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms
4170 pm->ps->saberMove = LS_READY;
4171 pm->ps->weaponTime = 0;
4172 }
4173 else if ( PM_ForceJumpingAnim( pm->ps->legsAnim )
4174 && pm->ps->torsoAnim == pm->ps->legsAnim )
4175 {//special case: if land during one of these, set the torso, too, if it's not doing something else
4176 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms
4177 }
4178 else
4179 {
4180 PM_SetAnim( pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms
4181 }
4182 }
4183 }
4184 }
4185 }
4186 }
4187 else
4188 {
4189 pm->ps->gravity = 1.0;
4190 //PM_CrashLandDamage( delta );
4191 if ( pm->gent )
4192 {
4193 if ((!(pml.groundTrace.surfaceFlags & SURF_NODAMAGE)) &&
4194 // (!(pml.groundTrace.contents & CONTENTS_NODROP)) &&
4195 (!(pm->pointcontents(pm->ps->origin,pm->ps->clientNum) & CONTENTS_NODROP)))
4196 {
4197 if ( pm->waterlevel < 2 )
4198 {//don't play fallsplat when impact in the water
4199 deadFallSound = qtrue;
4200 if ( !(pm->ps->eFlags&EF_NODRAW) )
4201 {//no sound if no draw
4202 if ( delta >= 75 )
4203 {
4204 G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/player/fallsplat.wav" );
4205 }
4206 else
4207 {
4208 G_SoundOnEnt( pm->gent, CHAN_BODY, va("sound/player/bodyfall_human%d.wav",Q_irand(1,3)) );
4209 }
4210 }
4211 }
4212 else
4213 {
4214 G_SoundOnEnt( pm->gent, CHAN_BODY, va("sound/player/bodyfall_water%d.wav",Q_irand(1,3)) );
4215 }
4216 if ( gi.VoiceVolume[pm->ps->clientNum]
4217 && pm->gent->NPC && (pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) )
4218 {//I was talking, so cut it off... with a jump sound?
4219 if ( !(pm->ps->eFlags&EF_NODRAW) )
4220 {//no sound if no draw
4221 G_SoundOnEnt( pm->gent, CHAN_VOICE_ATTEN, "*pain100.wav" );
4222 }
4223 }
4224 }
4225 }
4226 if( pm->ps->legsAnim == BOTH_FALLDEATH1 || pm->ps->legsAnim == BOTH_FALLDEATH1INAIR)
4227 {//FIXME: add a little bounce?
4228 //FIXME: cut voice channel?
4229 int old_pm_type = pm->ps->pm_type;
4230 pm->ps->pm_type = PM_NORMAL;
4231 //Hack because for some reason PM_SetAnim just returns if you're dead...???
4232 PM_SetAnim(pm, SETANIM_BOTH, BOTH_FALLDEATH1LAND, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
4233 pm->ps->pm_type = old_pm_type;
4234 AddSoundEvent( pm->gent, pm->ps->origin, 256, AEL_SUSPICIOUS, qfalse, qtrue );
4235 return;
4236 }
4237 }
4238
4239 // create a local entity event to play the sound
4240
4241 if ( pm->gent && pm->gent->client && pm->gent->client->respawnTime >= level.time - 500 )
4242 {//just spawned in, don't make a noise
4243 return;
4244 }
4245
4246 if ( delta >= 75 )
4247 {
4248 if ( !deadFallSound )
4249 {
4250 if ( !(pm->ps->eFlags&EF_NODRAW) )
4251 {//no sound if no draw
4252 PM_AddEvent( EV_FALL_FAR );
4253 }
4254 }
4255 if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) )
4256 {
4257 PM_CrashLandDamage( delta );
4258 }
4259 if ( pm->gent )
4260 {
4261 if ( pm->gent->s.number == 0 )
4262 {
4263 vec3_t bottom;
4264
4265 VectorCopy( pm->ps->origin, bottom );
4266 bottom[2] += pm->mins[2];
4267 // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
4268 {
4269 AddSoundEvent( pm->gent, bottom, 256, AEL_SUSPICIOUS, qfalse, qtrue );
4270 }
4271 }
4272 else if ( pm->ps->stats[STAT_HEALTH] <= 0 && pm->gent && pm->gent->enemy )
4273 {
4274 AddSoundEvent( pm->gent->enemy, pm->ps->origin, 256, AEL_DISCOVERED, qfalse, qtrue );
4275 }
4276 }
4277 }
4278 else if ( delta >= 50 )
4279 {
4280 // this is a pain grunt, so don't play it if dead
4281 if ( pm->ps->stats[STAT_HEALTH] > 0 )
4282 {
4283 if ( !deadFallSound )
4284 {
4285 if ( !(pm->ps->eFlags&EF_NODRAW) )
4286 {//no sound if no draw
4287 PM_AddEvent( EV_FALL_MEDIUM );//damage is dealt in g_active, ClientEvents
4288 }
4289 }
4290 if ( pm->gent )
4291 {
4292 if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) )
4293 {
4294 PM_CrashLandDamage( delta );
4295 }
4296 if ( pm->gent->s.number == 0 )
4297 {
4298 vec3_t bottom;
4299
4300 VectorCopy( pm->ps->origin, bottom );
4301 bottom[2] += pm->mins[2];
4302 // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
4303 {
4304 AddSoundEvent( pm->gent, bottom, 256, AEL_MINOR, qfalse, qtrue );
4305 }
4306 }
4307 }
4308 }
4309 }
4310 else if ( delta >= 30 )
4311 {
4312 if ( !deadFallSound )
4313 {
4314 if ( !(pm->ps->eFlags&EF_NODRAW) )
4315 {//no sound if no draw
4316 PM_AddEvent( EV_FALL_SHORT );
4317 }
4318 }
4319 if ( pm->gent )
4320 {
4321 if ( pm->gent->s.number == 0 )
4322 {
4323 vec3_t bottom;
4324
4325 VectorCopy( pm->ps->origin, bottom );
4326 bottom[2] += pm->mins[2];
4327 // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
4328 {
4329 AddSoundEvent( pm->gent, bottom, 128, AEL_MINOR, qfalse, qtrue );
4330 }
4331 }
4332 else
4333 {
4334 if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) )
4335 {
4336 PM_CrashLandDamage( delta );
4337 }
4338 }
4339 }
4340 }
4341 else
4342 {
4343 if ( !deadFallSound )
4344 {
4345 if ( !(pm->ps->pm_flags&PMF_DUCKED) || !Q_irand( 0, 3 ) )
4346 {//only 25% chance of making landing alert when ducked
4347 AddSoundEvent( pm->gent, pm->ps->origin, 32, AEL_MINOR, qfalse, qtrue );
4348 }
4349 if ( !(pm->ps->eFlags&EF_NODRAW) )
4350 {//no sound if no draw
4351 if ( forceLanding )
4352 {//we were force-jumping
4353 PM_AddEvent( EV_FALL_SHORT );
4354 }
4355 else
4356 {
4357 //moved to cg_player PM_AddEvent( PM_FootstepForSurface() );
4358 }
4359 }
4360 }
4361 }
4362
4363 // start footstep cycle over
4364 pm->ps->bobCycle = 0;
4365 if ( pm->gent && pm->gent->client )
4366 {//stop the force push effect when you land
4367 pm->gent->forcePushTime = 0;
4368 }
4369 }
4370
4371
4372
4373 /*
4374 =============
4375 PM_CorrectAllSolid
4376 =============
4377 */
PM_CorrectAllSolid(void)4378 static void PM_CorrectAllSolid( void ) {
4379 if ( pm->debugLevel ) {
4380 Com_Printf("%i:allsolid\n", c_pmove); //NOTENOTE: If this ever happens, I'd really like to see this print!
4381 }
4382
4383 // FIXME: jitter around
4384
4385 pm->ps->groundEntityNum = ENTITYNUM_NONE;
4386 pml.groundPlane = qfalse;
4387 pml.walking = qfalse;
4388 }
4389
FlyingCreature(gentity_t * ent)4390 qboolean FlyingCreature( gentity_t *ent )
4391 {
4392 if ( ent->client->ps.gravity <= 0 && (ent->svFlags&SVF_CUSTOM_GRAVITY) )
4393 {
4394 return qtrue;
4395 }
4396 return qfalse;
4397 }
4398
PM_RocketeersAvoidDangerousFalls(void)4399 qboolean PM_RocketeersAvoidDangerousFalls( void )
4400 {
4401 if ( pm->gent->NPC
4402 && pm->gent->client
4403 && (pm->gent->client->NPC_class == CLASS_BOBAFETT||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) )
4404 {//fixme: fall through if jetpack broken?
4405 if ( JET_Flying( pm->gent ) )
4406 {
4407 if ( pm->gent->client->NPC_class == CLASS_BOBAFETT )
4408 {
4409 pm->gent->client->jetPackTime = level.time + 2000;
4410 //Wait, what if the effect is already playing, how do we know???
4411 //G_PlayEffect( G_EffectIndex( "boba/jetSP" ), pm->gent->playerModel, pm->gent->genericBolt1, pm->gent->s.number, pm->gent->currentOrigin, pm->gent->client->jetPackTime-level.time );
4412 }
4413 else
4414 {
4415 pm->gent->client->jetPackTime = Q3_INFINITE;
4416 }
4417 }
4418 else
4419 {
4420 TIMER_Set( pm->gent, "jetRecharge", 0 );
4421 JET_FlyStart( pm->gent );
4422 }
4423 return qtrue;
4424 }
4425 return qfalse;
4426 }
4427
PM_FallToDeath(void)4428 static void PM_FallToDeath( void )
4429 {
4430 if ( !pm->gent )
4431 {
4432 return;
4433 }
4434
4435 if ( PM_RocketeersAvoidDangerousFalls() )
4436 {
4437 return;
4438 }
4439
4440 // If this is a vehicle, more precisely an animal...
4441 if ( pm->gent->client->NPC_class == CLASS_VEHICLE && pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL )
4442 {
4443 Vehicle_t *pVeh = pm->gent->m_pVehicle;
4444 pVeh->m_pVehicleInfo->EjectAll( pVeh );
4445 }
4446 else
4447 {
4448 if ( PM_HasAnimation( pm->gent, BOTH_FALLDEATH1 ) )
4449 {
4450 PM_SetAnim(pm,SETANIM_LEGS,BOTH_FALLDEATH1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
4451 }
4452 else
4453 {
4454 PM_SetAnim(pm,SETANIM_LEGS,BOTH_DEATH1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
4455 }
4456 G_SoundOnEnt( pm->gent, CHAN_VOICE, "*falling1.wav" );//CHAN_VOICE_ATTEN
4457 }
4458
4459 if ( pm->gent->NPC )
4460 {
4461 pm->gent->NPC->aiFlags |= NPCAI_DIE_ON_IMPACT;
4462 pm->gent->NPC->nextBStateThink = Q3_INFINITE;
4463 }
4464 pm->ps->friction = 1;
4465 }
4466
PM_ForceJumpAnimForJumpAnim(int anim)4467 int PM_ForceJumpAnimForJumpAnim( int anim )
4468 {
4469 switch( anim )
4470 {
4471 case BOTH_JUMP1: //# Jump - wind-up and leave ground
4472 anim = BOTH_FORCEJUMP1; //# Jump - wind-up and leave ground
4473 break;
4474 case BOTH_INAIR1: //# In air loop (from jump)
4475 anim = BOTH_FORCEINAIR1; //# In air loop (from jump)
4476 break;
4477 case BOTH_LAND1: //# Landing (from in air loop)
4478 anim = BOTH_FORCELAND1; //# Landing (from in air loop)
4479 break;
4480 case BOTH_JUMPBACK1: //# Jump backwards - wind-up and leave ground
4481 anim = BOTH_FORCEJUMPBACK1; //# Jump backwards - wind-up and leave ground
4482 break;
4483 case BOTH_INAIRBACK1: //# In air loop (from jump back)
4484 anim = BOTH_FORCEINAIRBACK1; //# In air loop (from jump back)
4485 break;
4486 case BOTH_LANDBACK1: //# Landing backwards(from in air loop)
4487 anim = BOTH_FORCELANDBACK1; //# Landing backwards(from in air loop)
4488 break;
4489 case BOTH_JUMPLEFT1: //# Jump left - wind-up and leave ground
4490 anim = BOTH_FORCEJUMPLEFT1; //# Jump left - wind-up and leave ground
4491 break;
4492 case BOTH_INAIRLEFT1: //# In air loop (from jump left)
4493 anim = BOTH_FORCEINAIRLEFT1; //# In air loop (from jump left)
4494 break;
4495 case BOTH_LANDLEFT1: //# Landing left(from in air loop)
4496 anim = BOTH_FORCELANDLEFT1; //# Landing left(from in air loop)
4497 break;
4498 case BOTH_JUMPRIGHT1: //# Jump right - wind-up and leave ground
4499 anim = BOTH_FORCEJUMPRIGHT1; //# Jump right - wind-up and leave ground
4500 break;
4501 case BOTH_INAIRRIGHT1: //# In air loop (from jump right)
4502 anim = BOTH_FORCEINAIRRIGHT1; //# In air loop (from jump right)
4503 break;
4504 case BOTH_LANDRIGHT1: //# Landing right(from in air loop)
4505 anim = BOTH_FORCELANDRIGHT1; //# Landing right(from in air loop)
4506 break;
4507 }
4508 return anim;
4509 }
4510
PM_SetVehicleAngles(vec3_t normal)4511 static void PM_SetVehicleAngles( vec3_t normal )
4512 {
4513 if ( !pm->gent->client || pm->gent->client->NPC_class != CLASS_VEHICLE )
4514 return;
4515
4516 Vehicle_t *pVeh = pm->gent->m_pVehicle;
4517
4518 //float curVehicleBankingSpeed;
4519 float vehicleBankingSpeed = pVeh->m_pVehicleInfo->bankingSpeed;//0.25f
4520 vec3_t vAngles;
4521
4522 if ( vehicleBankingSpeed <= 0
4523 || ( pVeh->m_pVehicleInfo->pitchLimit <= 0 && pVeh->m_pVehicleInfo->rollLimit <= 0 ) )
4524 {//don't bother, this vehicle doesn't bank
4525 return;
4526 }
4527 //FIXME: do 3 traces to define a plane and use that... smoothes it out some, too...
4528 //pitch_roll_for_slope( pm->gent, normal, vAngles );
4529 //FIXME: maybe have some pitch control in water and/or air?
4530
4531 //center of gravity affects pitch in air/water (FIXME: what about roll?)
4532 //float 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
4533
4534 VectorClear( vAngles );
4535
4536
4537 if (pm->waterlevel>0 || (normal && (pml.groundTrace.contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA))))
4538 {//in water
4539 // vAngles[PITCH] += (pm->ps->viewangles[PITCH]-vAngles[PITCH])*0.75f + (pitchBias*0.5);
4540 }
4541 else if ( normal )
4542 {//have a valid surface below me
4543 pitch_roll_for_slope( pm->gent, normal, vAngles );
4544 float deltaPitch = (vAngles[PITCH] - pVeh->m_vOrientation[PITCH]);
4545 if (deltaPitch< -10.0f)
4546 {
4547 vAngles[PITCH] = pVeh->m_vOrientation[PITCH] - 10.0f;
4548 }
4549 else if (deltaPitch>10.0f)
4550 {
4551 vAngles[PITCH] = pVeh->m_vOrientation[PITCH] + 10.0f;
4552 }
4553 }
4554 else
4555 {//in air, let pitch match view...?
4556 //FIXME: take center of gravity into account
4557 vAngles[PITCH] = pVeh->m_vOrientation[PITCH] - 1;
4558 if (vAngles[PITCH]<-15)
4559 {
4560 vAngles[PITCH]=-15;
4561 }
4562 //don't bank so fast when in the air
4563 vehicleBankingSpeed *= 0.125f;
4564 }
4565 //NOTE: if angles are flat and we're moving through air (not on ground),
4566 // then pitch/bank?
4567 if (pVeh->m_ulFlags & VEH_SPINNING)
4568 {
4569 vAngles[ROLL] = pVeh->m_vOrientation[ROLL] - 25;
4570 }
4571 else if (pVeh->m_ulFlags & VEH_OUTOFCONTROL)
4572 {
4573 //vAngles[ROLL] = pVeh->m_vOrientation[ROLL] + 5;
4574 }
4575 else if ( pVeh->m_pVehicleInfo->rollLimit > 0 )
4576 {
4577 //roll when banking
4578 vec3_t velocity;
4579 float speed;
4580 VectorCopy( pm->ps->velocity, velocity );
4581 speed = VectorNormalize( velocity );
4582 if ( speed>0.01f )
4583 {
4584 vec3_t rt, tempVAngles;
4585 float side, dotp;
4586
4587
4588 VectorCopy( pVeh->m_vOrientation, tempVAngles );
4589 tempVAngles[ROLL] = 0;
4590 AngleVectors( tempVAngles, NULL, rt, NULL );
4591 dotp = DotProduct( velocity, rt );
4592 //if (fabsf(dotp)>0.5f)
4593 {
4594 speed *= dotp;
4595
4596
4597 // Magic number fun! Speed is used for banking, so modulate the speed by a sine wave
4598 //FIXME: this banks too early
4599 //speed *= sin( (150 + pml.frametime) * 0.003 );
4600 if (level.time < pVeh->m_iTurboTime)
4601 {
4602 speed /= pVeh->m_pVehicleInfo->turboSpeed;
4603 }
4604 else
4605 {
4606 speed /= pVeh->m_pVehicleInfo->speedMax;
4607 }
4608
4609 /// if (pm->cmd.forwardmove==0)
4610 // {
4611 // speed *= 0.5;
4612 // }
4613 // if (pm->cmd.forwardmove<0)
4614 // {
4615 // speed *= 0.1f;
4616 // }
4617 if (pVeh->m_ulFlags & VEH_SLIDEBREAKING)
4618 {
4619 speed *= 3.0f;
4620 }
4621
4622
4623 side = speed * 75.0f;
4624 // if (pVeh->m_ulFlags & VEH_STRAFERAM)
4625 // {
4626 // vAngles[ROLL] += side;
4627 // }
4628 // else
4629 {
4630 vAngles[ROLL] -= side;
4631 }
4632
4633 //gi.Printf("VAngles(%f)", vAngles[2]);
4634 }
4635 if (fabsf(vAngles[ROLL])<0.001f)
4636 {
4637 vAngles[ROLL] = 0.0f;
4638 }
4639 }
4640 }
4641
4642 //cap
4643 if ( vAngles[PITCH] > pVeh->m_pVehicleInfo->pitchLimit )
4644 {
4645 vAngles[PITCH] = pVeh->m_pVehicleInfo->pitchLimit;
4646 }
4647 else if ( vAngles[PITCH] < -pVeh->m_pVehicleInfo->pitchLimit )
4648 {
4649 vAngles[PITCH] = -pVeh->m_pVehicleInfo->pitchLimit;
4650 }
4651
4652 if (!(pVeh->m_ulFlags & VEH_SPINNING))
4653 {
4654 if ( vAngles[ROLL] > pVeh->m_pVehicleInfo->rollLimit )
4655 {
4656 vAngles[ROLL] = pVeh->m_pVehicleInfo->rollLimit;
4657 }
4658 else if ( vAngles[ROLL] < -pVeh->m_pVehicleInfo->rollLimit )
4659 {
4660 vAngles[ROLL] = -pVeh->m_pVehicleInfo->rollLimit;
4661 }
4662 }
4663
4664 //do it
4665 for ( int i = 0; i < 3; i++ )
4666 {
4667 if ( i == YAW )
4668 {//yawing done elsewhere
4669 continue;
4670 }
4671
4672 if ( i == ROLL && pVeh->m_ulFlags & VEH_STRAFERAM)
4673 {//during strafe ram, roll is done elsewhere
4674 continue;
4675 }
4676 //bank faster the higher the difference is
4677 /*
4678 else if ( i == PITCH )
4679 {
4680 curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[PITCH], pVeh->m_vOrientation[PITCH] )))/(g_vehicleInfo[pm->ps->vehicleIndex].pitchLimit/2.0f);
4681 }
4682 else if ( i == ROLL )
4683 {
4684 curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[ROLL], pVeh->m_vOrientation[ROLL] )))/(g_vehicleInfo[pm->ps->vehicleIndex].rollLimit/2.0f);
4685 }
4686
4687 if ( curVehicleBankingSpeed )
4688 */
4689 {
4690 // if ( pVeh->m_vOrientation[i] >= vAngles[i] + vehicleBankingSpeed )
4691 // {
4692 // pVeh->m_vOrientation[i] -= vehicleBankingSpeed;
4693 // }
4694 // else if ( pVeh->m_vOrientation[i] <= vAngles[i] - vehicleBankingSpeed )
4695 // {
4696 // pVeh->m_vOrientation[i] += vehicleBankingSpeed;
4697 // }
4698 // else
4699 {
4700 pVeh->m_vOrientation[i] = vAngles[i];
4701 }
4702 }
4703 }
4704 //gi.Printf("Orientation(%f)", pVeh->m_vOrientation[2]);
4705 }
4706
BG_ExternThisSoICanRecompileInDebug(Vehicle_t * pVeh,playerState_t * riderPS)4707 void BG_ExternThisSoICanRecompileInDebug( Vehicle_t *pVeh, playerState_t *riderPS )
4708 {/*
4709 float pitchSubtract, pitchDelta, yawDelta;
4710 //Com_Printf( S_COLOR_RED"PITCH: %4.2f, YAW: %4.2f, ROLL: %4.2f\n", riderPS->viewangles[0],riderPS->viewangles[1],riderPS->viewangles[2]);
4711 yawDelta = AngleSubtract(riderPS->viewangles[YAW],pVeh->m_vPrevRiderViewAngles[YAW]);
4712 #ifndef QAGAME
4713 if ( !cg_paused.integer )
4714 {
4715 //Com_Printf( "%d - yawDelta %4.2f\n", pm->cmd.serverTime, yawDelta );
4716 }
4717 #endif
4718 yawDelta *= (4.0f*pVeh->m_fTimeModifier);
4719 pVeh->m_vOrientation[ROLL] -= yawDelta;
4720
4721 pitchDelta = AngleSubtract(riderPS->viewangles[PITCH],pVeh->m_vPrevRiderViewAngles[PITCH]);
4722 pitchDelta *= (2.0f*pVeh->m_fTimeModifier);
4723 pitchSubtract = pitchDelta * (fabs(pVeh->m_vOrientation[ROLL])/90.0f);
4724 pVeh->m_vOrientation[PITCH] += pitchDelta-pitchSubtract;
4725 if ( pVeh->m_vOrientation[ROLL] > 0 )
4726 {
4727 pVeh->m_vOrientation[YAW] += pitchSubtract;
4728 }
4729 else
4730 {
4731 pVeh->m_vOrientation[YAW] -= pitchSubtract;
4732 }
4733 pVeh->m_vOrientation[PITCH] = AngleNormalize180( pVeh->m_vOrientation[PITCH] );
4734 pVeh->m_vOrientation[YAW] = AngleNormalize360( pVeh->m_vOrientation[YAW] );
4735 pVeh->m_vOrientation[ROLL] = AngleNormalize180( pVeh->m_vOrientation[ROLL] );
4736
4737 VectorCopy( riderPS->viewangles, pVeh->m_vPrevRiderViewAngles );*/
4738 }
4739
BG_VehicleTurnRateForSpeed(Vehicle_t * pVeh,float speed,float * mPitchOverride,float * mYawOverride)4740 void BG_VehicleTurnRateForSpeed( Vehicle_t *pVeh, float speed, float *mPitchOverride, float *mYawOverride )
4741 {
4742 if ( pVeh && pVeh->m_pVehicleInfo )
4743 {
4744 float speedFrac = 1.0f;
4745 if ( pVeh->m_pVehicleInfo->speedDependantTurning )
4746 {
4747 if ( pVeh->m_LandTrace.fraction >= 1.0f
4748 || pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE )
4749 {
4750 speedFrac = (speed/(pVeh->m_pVehicleInfo->speedMax*0.75f));
4751 if ( speedFrac < 0.25f )
4752 {
4753 speedFrac = 0.25f;
4754 }
4755 else if ( speedFrac > 1.0f )
4756 {
4757 speedFrac = 1.0f;
4758 }
4759 }
4760 }
4761 if ( pVeh->m_pVehicleInfo->mousePitch )
4762 {
4763 *mPitchOverride = pVeh->m_pVehicleInfo->mousePitch*speedFrac;
4764 }
4765 if ( pVeh->m_pVehicleInfo->mouseYaw )
4766 {
4767 *mYawOverride = pVeh->m_pVehicleInfo->mouseYaw*speedFrac;
4768 }
4769 }
4770 }
4771 /*
4772 =============
4773 PM_GroundTraceMissed
4774
4775 The ground trace didn't hit a surface, so we are in freefall
4776 =============
4777 */
PM_GroundTraceMissed(void)4778 static void PM_GroundTraceMissed( void ) {
4779 trace_t trace;
4780 vec3_t point;
4781 qboolean cliff_fall = qfalse;
4782
4783 if ( Flying != FLY_HOVER )
4784 {
4785 if ( !(pm->ps->eFlags&EF_FORCE_DRAINED) )
4786 {
4787 //FIXME: if in a contents_falldeath brush, play the falling death anim and sound?
4788 if ( pm->ps->clientNum != 0 && pm->gent && pm->gent->NPC && pm->gent->client && pm->gent->client->NPC_class != CLASS_DESANN )//desann never falls to his death
4789 {
4790 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
4791 {
4792 if ( pm->ps->stats[STAT_HEALTH] > 0
4793 && !(pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT)
4794 && !(pm->gent->NPC->aiFlags&NPCAI_JUMP) // doing a path jump
4795 && !(pm->gent->NPC->scriptFlags&SCF_NO_FALLTODEATH)
4796 && pm->gent->NPC->behaviorState != BS_JUMP )//not being scripted to jump
4797 {
4798 if ( (level.time - pm->gent->client->respawnTime > 2000)//been in the world for at least 2 seconds
4799 && (!pm->gent->NPC->timeOfDeath || level.time - pm->gent->NPC->timeOfDeath < 1000) && pm->gent->e_ThinkFunc != thinkF_NPC_RemoveBody //Have to do this now because timeOfDeath is used by thinkF_NPC_RemoveBody to debounce removal checks
4800 && !(pm->gent->client->ps.forcePowersActive&(1<<FP_LEVITATION)) )
4801 {
4802 if ( !FlyingCreature( pm->gent )
4803 && g_gravity->value > 0 )
4804 {
4805 if ( !(pm->gent->flags&FL_UNDYING)
4806 && !(pm->gent->flags&FL_GODMODE) )
4807 {
4808 if ( !(pm->ps->eFlags&EF_FORCE_GRIPPED)
4809 && !(pm->ps->eFlags&EF_FORCE_DRAINED)
4810 && !(pm->ps->pm_flags&PMF_TRIGGER_PUSHED) )
4811 {
4812 if ( !pm->ps->forceJumpZStart || pm->ps->forceJumpZStart > pm->ps->origin[2] )// && fabs(pm->ps->velocity[0])<10 && fabs(pm->ps->velocity[1])<10 && pm->ps->velocity[2]<0)//either not force-jumping or force-jumped and now fell below original jump start height
4813 {
4814 /*if ( pm->ps->legsAnim = BOTH_FALLDEATH1
4815 && pm->ps->legsAnim != BOTH_DEATH1
4816 && PM_HasAnimation( pm->gent, BOTH_FALLDEATH1 )*/
4817 //New method: predict impact, 400 ahead
4818 vec3_t vel;
4819 float time;
4820
4821 VectorCopy( pm->ps->velocity, vel );
4822 float speed = VectorLength( vel );
4823 if ( !speed )
4824 {//damn divide by zero
4825 speed = 1;
4826 }
4827 time = 400/speed;
4828 vel[2] -= 0.5 * time * pm->ps->gravity;
4829 speed = VectorLength( vel );
4830 if ( !speed )
4831 {//damn divide by zero
4832 speed = 1;
4833 }
4834 time = 400/speed;
4835 VectorScale( vel, time, vel );
4836 VectorAdd( pm->ps->origin, vel, point );
4837
4838 pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 );
4839
4840 if ( (trace.contents&CONTENTS_LAVA)
4841 && PM_RocketeersAvoidDangerousFalls() )
4842 {//got out of it
4843 }
4844 else if ( !trace.allsolid && !trace.startsolid && (pm->ps->origin[2] - trace.endpos[2]) >= 128 )//>=128 so we don't die on steps!
4845 {
4846 if ( trace.fraction == 1.0 )
4847 {//didn't hit, we're probably going to die
4848 if ( pm->ps->velocity[2] < 0 && pm->ps->origin[2] - point[2] > 256 )
4849 {//going down, into a bottomless pit, apparently
4850 PM_FallToDeath();
4851 cliff_fall = qtrue;
4852 }
4853 }
4854 else if ( trace.entityNum < ENTITYNUM_NONE
4855 && pm->ps->weapon != WP_SABER
4856 && (!pm->gent || !pm->gent->client || (pm->gent->client->NPC_class != CLASS_BOBAFETT&&pm->gent->client->NPC_class!=CLASS_REBORN&&pm->gent->client->NPC_class!=CLASS_ROCKETTROOPER)) )
4857 {//Jedi don't scream and die if they're heading for a hard impact
4858 gentity_t *traceEnt = &g_entities[trace.entityNum];
4859 if ( trace.entityNum == ENTITYNUM_WORLD || (traceEnt && traceEnt->bmodel) )
4860 {//hit architecture, find impact force
4861 float dmg;
4862
4863 VectorCopy( pm->ps->velocity, vel );
4864 time = Distance( trace.endpos, pm->ps->origin )/VectorLength( vel );
4865 vel[2] -= 0.5 * time * pm->ps->gravity;
4866
4867 if ( trace.plane.normal[2] > 0.5 )
4868 {//use falling damage
4869 int waterlevel, junk;
4870 PM_SetWaterLevelAtPoint( trace.endpos, &waterlevel, &junk );
4871 dmg = PM_CrashLandDelta( vel, waterlevel );
4872 if ( dmg >= 30 )
4873 {//there is a minimum fall threshhold
4874 dmg = PM_DamageForDelta( dmg );
4875 }
4876 else
4877 {
4878 dmg = 0;
4879 }
4880 }
4881 else
4882 {//use impact damage
4883 //guestimate
4884 if ( pm->gent->client && pm->gent->client->ps.forceJumpZStart )
4885 {//we were force-jumping
4886 if ( pm->gent->currentOrigin[2] >= pm->gent->client->ps.forceJumpZStart )
4887 {//we landed at same height or higher than we landed
4888 dmg = 0;
4889 }
4890 else
4891 {//FIXME: take off some of it, at least?
4892 dmg = (pm->gent->client->ps.forceJumpZStart-pm->gent->currentOrigin[2])/3;
4893 }
4894 }
4895 dmg = 10 * VectorLength( pm->ps->velocity );
4896 if ( pm->ps->clientNum < MAX_CLIENTS )
4897 {
4898 dmg /= 2;
4899 }
4900 dmg *= 0.01875f;//magic number
4901 }
4902 float maxDmg = pm->ps->stats[STAT_HEALTH]>20?pm->ps->stats[STAT_HEALTH]:20;//a fall that would do less than 20 points of damage should never make us scream to our deaths
4903 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_HOWLER )
4904 {//howlers can survive long falls, despire their health
4905 maxDmg *= 5;
4906 }
4907 if ( dmg >= pm->ps->stats[STAT_HEALTH] )//armor?
4908 {
4909 PM_FallToDeath();
4910 cliff_fall = qtrue;
4911 }
4912 }
4913 }
4914 }
4915
4916 /*
4917 vec3_t start;
4918 //okay, kind of expensive temp hack here, but let's check to see if we should scream
4919 //FIXME: we should either do a better check (predict using actual velocity) or we should wait until they've been over a bottomless pit for a certain amount of time...
4920 VectorCopy( pm->ps->origin, start );
4921 if ( pm->ps->forceJumpZStart < start[2] )
4922 {//Jedi who are force-jumping should only do this from landing point down?
4923 start[2] = pm->ps->forceJumpZStart;
4924 }
4925 VectorCopy( start, point );
4926 point[2] -= 400;//320
4927 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
4928 //FIXME: somehow, people can still get stuck on ledges and not splat when hit...?
4929 if ( !trace.allsolid && !trace.startsolid && trace.fraction == 1.0 )
4930 {
4931 PM_FallToDeath();
4932 cliff_fall = qtrue;
4933 }
4934 */
4935 }
4936 }
4937 }
4938 }
4939 }
4940 }
4941 }
4942 }
4943 if ( !cliff_fall )
4944 {
4945 if ( pm->ps->legsAnim == BOTH_FORCELONGLEAP_START
4946 || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK
4947 || pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START
4948 || pm->ps->legsAnim == BOTH_FLIP_LAND )
4949 {//let it stay on this anim
4950 }
4951 else if ( PM_KnockDownAnimExtended( pm->ps->legsAnim ) )
4952 {//no in-air anims when in knockdown anim
4953 }
4954 else if ( ( pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT
4955 || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT
4956 || pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT_STOP
4957 || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT_STOP
4958 || pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT_FLIP
4959 || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT_FLIP
4960 || pm->ps->legsAnim == BOTH_WALL_FLIP_RIGHT
4961 || pm->ps->legsAnim == BOTH_WALL_FLIP_LEFT
4962 || pm->ps->legsAnim == BOTH_FORCEWALLREBOUND_FORWARD
4963 || pm->ps->legsAnim == BOTH_FORCEWALLREBOUND_LEFT
4964 || pm->ps->legsAnim == BOTH_FORCEWALLREBOUND_BACK
4965 || pm->ps->legsAnim == BOTH_FORCEWALLREBOUND_RIGHT
4966 || pm->ps->legsAnim == BOTH_CEILING_DROP )
4967 && !pm->ps->legsAnimTimer )
4968 {//if flip anim is done, okay to use inair
4969 PM_SetAnim( pm, SETANIM_LEGS, BOTH_FORCEINAIR1, SETANIM_FLAG_OVERRIDE, 350 ); // Only blend over 100ms
4970 }
4971 else if ( pm->ps->legsAnim == BOTH_FLIP_ATTACK7
4972 || pm->ps->legsAnim == BOTH_FLIP_HOLD7 )
4973 {
4974 if ( !pm->ps->legsAnimTimer )
4975 {//done?
4976 PM_SetAnim( pm, SETANIM_BOTH, BOTH_FLIP_HOLD7, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 350 ); // Only blend over 100ms
4977 }
4978 }
4979 else if ( PM_InCartwheel( pm->ps->legsAnim ) )
4980 {
4981 if ( pm->ps->legsAnimTimer > 0 )
4982 {//still playing on bottom
4983 }
4984 else
4985 {
4986 PM_SetAnim( pm, SETANIM_BOTH, BOTH_INAIR1, SETANIM_FLAG_NORMAL, 350 );
4987 }
4988 }
4989 else if ( PM_InAirKickingAnim( pm->ps->legsAnim ) )
4990 {
4991 if ( pm->ps->legsAnimTimer > 0 )
4992 {//still playing on bottom
4993 }
4994 else
4995 {
4996 PM_SetAnim( pm, SETANIM_BOTH, BOTH_INAIR1, SETANIM_FLAG_NORMAL, 350 );
4997 pm->ps->saberMove = LS_READY;
4998 pm->ps->weaponTime = 0;
4999 }
5000 }
5001 else if ( !PM_InRoll( pm->ps )
5002 && !PM_SpinningAnim( pm->ps->legsAnim )
5003 && !PM_FlippingAnim( pm->ps->legsAnim )
5004 && !PM_InSpecialJump( pm->ps->legsAnim ) )
5005 {
5006 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
5007 {
5008 // we just transitioned into freefall
5009 if ( pm->debugLevel )
5010 {
5011 Com_Printf("%i:lift\n", c_pmove);
5012 }
5013
5014 // if they aren't in a jumping animation and the ground is a ways away, force into it
5015 // if we didn't do the trace, the player would be backflipping down staircases
5016 VectorCopy( pm->ps->origin, point );
5017 point[2] -= 64;
5018
5019 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0);
5020 if ( trace.fraction == 1.0 )
5021 {//FIXME: if velocity[2] < 0 and didn't jump, use some falling anim
5022 if ( pm->ps->velocity[2] <= 0 && !(pm->ps->pm_flags&PMF_JUMP_HELD))
5023 {
5024 if(!PM_InDeathAnim())
5025 {
5026 // If we're a vehicle, set our falling flag.
5027 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
5028 {
5029 // We're flying in the air.
5030 if ( pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL )
5031 {
5032 pm->gent->m_pVehicle->m_ulFlags |= VEH_FLYING;
5033 }
5034 }
5035 else
5036 {
5037 vec3_t moveDir, lookAngles, lookDir, lookRight;
5038 int anim = BOTH_INAIR1;
5039
5040 VectorCopy( pm->ps->velocity, moveDir );
5041 moveDir[2] = 0;
5042 VectorNormalize( moveDir );
5043
5044 VectorCopy( pm->ps->viewangles, lookAngles );
5045 lookAngles[PITCH] = lookAngles[ROLL] = 0;
5046 AngleVectors( lookAngles, lookDir, lookRight, NULL );
5047
5048 float dot = DotProduct( moveDir, lookDir );
5049 if ( dot > 0.5 )
5050 {//redundant
5051 anim = BOTH_INAIR1;
5052 }
5053 else if ( dot < -0.5 )
5054 {
5055 anim = BOTH_INAIRBACK1;
5056 }
5057 else
5058 {
5059 dot = DotProduct( moveDir, lookRight );
5060 if ( dot > 0.5 )
5061 {
5062 anim = BOTH_INAIRRIGHT1;
5063 }
5064 else if ( dot < -0.5 )
5065 {
5066 anim = BOTH_INAIRLEFT1;
5067 }
5068 else
5069 {//redundant
5070 anim = BOTH_INAIR1;
5071 }
5072 }
5073 if ( pm->ps->forcePowersActive & ( 1 << FP_LEVITATION ) )
5074 {
5075 anim = PM_ForceJumpAnimForJumpAnim( anim );
5076 }
5077 PM_SetAnim( pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE, 100 ); // Only blend over 100ms
5078 }
5079 }
5080 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
5081 }
5082 else if ( !(pm->ps->forcePowersActive&(1<<FP_LEVITATION)) )
5083 {
5084 if ( pm->cmd.forwardmove >= 0 )
5085 {
5086 if(!PM_InDeathAnim())
5087 {
5088 PM_SetAnim(pm,SETANIM_LEGS,BOTH_JUMP1,SETANIM_FLAG_OVERRIDE, 100); // Only blend over 100ms
5089 }
5090 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
5091 }
5092 else if ( pm->cmd.forwardmove < 0 )
5093 {
5094 if(!PM_InDeathAnim())
5095 {
5096 PM_SetAnim(pm,SETANIM_LEGS,BOTH_JUMPBACK1,SETANIM_FLAG_OVERRIDE, 100); // Only blend over 100ms
5097 }
5098 pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
5099 }
5100 }
5101 }
5102 else
5103 {
5104 // If we're a vehicle, set our falling flag.
5105 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
5106 {
5107 // We're on the ground.
5108 if ( pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL )
5109 {
5110 pm->gent->m_pVehicle->m_ulFlags &= ~VEH_FLYING;
5111 }
5112 }
5113 }
5114 }
5115 }
5116 }
5117 }
5118
5119 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
5120 {
5121 pm->ps->jumpZStart = pm->ps->origin[2];
5122 }
5123 }
5124 pm->ps->groundEntityNum = ENTITYNUM_NONE;
5125 pml.groundPlane = qfalse;
5126 pml.walking = qfalse;
5127 }
5128
5129
5130 /*
5131 =============
5132 PM_GroundTrace
5133 =============
5134 */
PM_GroundTrace(void)5135 static void PM_GroundTrace( void ) {
5136 vec3_t point;
5137 trace_t trace;
5138
5139 if ( (pm->ps->eFlags&EF_LOCKED_TO_WEAPON)
5140 || (pm->ps->eFlags&EF_HELD_BY_RANCOR)
5141 || (pm->ps->eFlags&EF_HELD_BY_WAMPA)
5142 || (pm->ps->eFlags&EF_HELD_BY_SAND_CREATURE)
5143 || PM_RidingVehicle() )
5144 {
5145 pml.groundPlane = qtrue;
5146 pml.walking = qtrue;
5147 pm->ps->groundEntityNum = ENTITYNUM_WORLD;
5148 pm->ps->lastOnGround = level.time;
5149 /*
5150 pml.groundTrace.allsolid = qfalse;
5151 pml.groundTrace.contents = CONTENTS_SOLID;
5152 VectorCopy( pm->ps->origin, pml.groundTrace.endpos );
5153 pml.groundTrace.entityNum = ENTITYNUM_WORLD;
5154 pml.groundTrace.fraction = 0.0f;;
5155 pml.groundTrace.G2CollisionMap = NULL;
5156 pml.groundTrace.plane.dist = 0.0f;
5157 VectorSet( pml.groundTrace.plane.normal, 0, 0, 1 );
5158 pml.groundTrace.plane.pad = 0;
5159 pml.groundTrace.plane.signbits = 0;
5160 pml.groundTrace.plane.type = 0;;
5161 pml.groundTrace.startsolid = qfalse;
5162 pml.groundTrace.surfaceFlags = 0;
5163 */
5164 return;
5165 }
5166 else if ( pm->ps->legsAnimTimer > 300
5167 && (pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT
5168 || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT
5169 || pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START) )
5170 {//wall-running forces you to be in the air
5171 pml.groundPlane = qfalse;
5172 pml.walking = qfalse;
5173 pm->ps->groundEntityNum = ENTITYNUM_NONE;
5174 return;
5175 }
5176
5177 float minNormal = (float)MIN_WALK_NORMAL;
5178 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
5179 {//FIXME: extern this as part of vehicle data
5180 minNormal = pm->gent->m_pVehicle->m_pVehicleInfo->maxSlope;
5181 }
5182
5183 point[0] = pm->ps->origin[0];
5184 point[1] = pm->ps->origin[1];
5185 point[2] = pm->ps->origin[2] - 0.25f;
5186
5187 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0);
5188 pml.groundTrace = trace;
5189
5190 // do something corrective if the trace starts in a solid...
5191 if ( trace.allsolid ) {
5192 PM_CorrectAllSolid();
5193 return;
5194 }
5195
5196 // if the trace didn't hit anything, we are in free fall
5197 if ( trace.fraction == 1.0 || g_gravity->value <= 0 )
5198 {
5199 PM_GroundTraceMissed();
5200 pml.groundPlane = qfalse;
5201 pml.walking = qfalse;
5202 /*
5203 if ( pm->ps->vehicleIndex != VEHICLE_NONE )
5204 {
5205 PM_SetVehicleAngles( NULL );
5206 }
5207 */
5208 return;
5209 }
5210
5211 // Not a vehicle and not riding one.
5212 if ( pm->gent
5213 && pm->gent->client
5214 && pm->gent->client->NPC_class != CLASS_SAND_CREATURE
5215 && (pm->gent->client->NPC_class != CLASS_VEHICLE && !PM_RidingVehicle() ) )
5216 {
5217 // check if getting thrown off the ground
5218 if ( ((pm->ps->velocity[2]>0&&(pm->ps->pm_flags&PMF_TIME_KNOCKBACK))||pm->ps->velocity[2]>100) && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 )
5219 {//either thrown off ground (PMF_TIME_KNOCKBACK) or going off the ground at a large velocity
5220 if ( pm->debugLevel ) {
5221 Com_Printf("%i:kickoff\n", c_pmove);
5222 }
5223 // go into jump animation
5224 if ( PM_FlippingAnim( pm->ps->legsAnim) )
5225 {//we're flipping
5226 }
5227 else if ( PM_InSpecialJump( pm->ps->legsAnim ) )
5228 {//special jumps
5229 }
5230 else if ( PM_InKnockDown( pm->ps ) )
5231 {//in knockdown
5232 }
5233 else if ( PM_InRoll( pm->ps ) )
5234 {//in knockdown
5235 }
5236 else if ( PM_KickingAnim( pm->ps->legsAnim ) )
5237 {//in kick
5238 }
5239 else if ( pm->gent
5240 && pm->gent->client
5241 && (pm->gent->client->NPC_class == CLASS_RANCOR || pm->gent->client->NPC_class == CLASS_WAMPA) )
5242 {
5243 }
5244 else
5245 {
5246 PM_JumpForDir();
5247 }
5248
5249 pm->ps->groundEntityNum = ENTITYNUM_NONE;
5250 pml.groundPlane = qfalse;
5251 pml.walking = qfalse;
5252 return;
5253 }
5254 }
5255
5256 /*
5257 if ( pm->ps->vehicleIndex != VEHICLE_NONE )
5258 {
5259 PM_SetVehicleAngles( trace.plane.normal );
5260 }
5261 */
5262
5263 // slopes that are too steep will not be considered onground
5264 if ( trace.plane.normal[2] < minNormal ) {
5265 if ( pm->debugLevel ) {
5266 Com_Printf("%i:steep\n", c_pmove);
5267 }
5268 // FIXME: if they can't slide down the slope, let them
5269 // walk (sharp crevices)
5270 pm->ps->groundEntityNum = ENTITYNUM_NONE;
5271 pml.groundPlane = qtrue;
5272 pml.walking = qfalse;
5273 return;
5274 }
5275
5276 //FIXME: if the ground surface is a "cover surface (like tall grass), add a "cover" flag to me
5277 pml.groundPlane = qtrue;
5278 pml.walking = qtrue;
5279
5280 // hitting solid ground will end a waterjump
5281 if (pm->ps->pm_flags & PMF_TIME_WATERJUMP)
5282 {
5283 pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
5284 pm->ps->pm_time = 0;
5285 }
5286
5287 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
5288 // just hit the ground
5289 if ( pm->debugLevel ) {
5290 Com_Printf("%i:Land\n", c_pmove);
5291 }
5292
5293 //if ( !PM_ClientImpact( trace.entityNum, qtrue ) )
5294 {
5295 PM_CrashLand();
5296
5297 // don't do landing time if we were just going down a slope
5298 if ( pml.previous_velocity[2] < -200 ) {
5299 // don't allow another jump for a little while
5300 pm->ps->pm_flags |= PMF_TIME_LAND;
5301 pm->ps->pm_time = 250;
5302 }
5303 if (!pm->cmd.forwardmove && !pm->cmd.rightmove) {
5304 if ( Flying != FLY_HOVER )
5305 {
5306 pm->ps->velocity[2] = 0; //wouldn't normally want this because of slopes, but we aren't tyring to move...
5307 }
5308 }
5309 }
5310 }
5311
5312 pm->ps->groundEntityNum = trace.entityNum;
5313 pm->ps->lastOnGround = level.time;
5314 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
5315 {//if a player, clear the jumping "flag" so can't double-jump
5316 pm->ps->forceJumpCharge = 0;
5317 }
5318
5319 // don't reset the z velocity for slopes
5320 // pm->ps->velocity[2] = 0;
5321
5322 PM_AddTouchEnt( trace.entityNum );
5323 }
5324
5325 int LastMatrixJumpTime = 0;
5326 #define DEBUGMATRIXJUMP 0
5327
PM_HoverTrace(void)5328 void PM_HoverTrace( void )
5329 {
5330 if ( !pm->gent || !pm->gent->client || pm->gent->client->NPC_class != CLASS_VEHICLE )
5331 {
5332 return;
5333 }
5334
5335 Vehicle_t *pVeh = pm->gent->m_pVehicle;
5336 float hoverHeight = pVeh->m_pVehicleInfo->hoverHeight;
5337 vec3_t point, vAng, fxAxis[3];
5338 trace_t *trace = &pml.groundTrace;
5339 int traceContents = pm->tracemask;
5340
5341 pml.groundPlane = qfalse;
5342
5343 float relativeWaterLevel = (pm->ps->waterheight - (pm->ps->origin[2]+pm->mins[2]));
5344 if ( pm->waterlevel && relativeWaterLevel >= 0 )
5345 {//in water
5346 if ( pVeh->m_pVehicleInfo->bouyancy <= 0.0f )
5347 {//sink like a rock
5348 }
5349 else
5350 {//rise up
5351 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
5352 if ( relativeWaterLevel > floatHeight )
5353 {//too low, should rise up
5354 pm->ps->velocity[2] += (relativeWaterLevel - floatHeight) * pVeh->m_fTimeModifier;
5355 }
5356 }
5357 if ( pm->ps->waterheight < pm->ps->origin[2]+pm->maxs[2] )
5358 {//part of us is sticking out of water
5359 if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 )
5360 {//moving at a decent speed
5361 if ( Q_irand( pml.frametime, 100 ) >= 50 )
5362 {//splash
5363 vAng[PITCH] = vAng[ROLL] = 0;
5364 vAng[YAW] = pVeh->m_vOrientation[YAW];
5365 AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] );
5366 vec3_t wakeOrg;
5367 VectorCopy( pm->ps->origin, wakeOrg );
5368 wakeOrg[2] = pm->ps->waterheight;
5369 if ( pVeh->m_pVehicleInfo->iWakeFX )
5370 {
5371 G_PlayEffect( pVeh->m_pVehicleInfo->iWakeFX, wakeOrg, fxAxis );
5372 }
5373 }
5374 }
5375 pml.groundPlane = qtrue;
5376 }
5377 }
5378 else
5379 {
5380 float minNormal = (float)MIN_WALK_NORMAL;
5381 minNormal = pVeh->m_pVehicleInfo->maxSlope;
5382
5383 point[0] = pm->ps->origin[0];
5384 point[1] = pm->ps->origin[1];
5385 point[2] = pm->ps->origin[2] - (hoverHeight*3.0f);
5386
5387 //FIXME: check for water, too? If over water, go slower and make wave effect
5388 // If *in* water, go really slow and use bouyancy stat to determine how far below surface to float
5389
5390 //NOTE: if bouyancy is 2.0f or higher, you float over water like it's solid ground.
5391 // if it's 1.0f, you sink halfway into water. If it's 0, you sink...
5392 if ( pVeh->m_pVehicleInfo->bouyancy >= 2.0f )
5393 {//sit on water
5394 traceContents |= (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA);
5395 }
5396 pm->trace( trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, traceContents, (EG2_Collision)0, 0 );
5397 if ( trace->plane.normal[2] >= minNormal )
5398 {//not a steep slope, so push us up
5399 if ( trace->fraction < 0.3f )
5400 {//push up off ground
5401 float hoverForce = pVeh->m_pVehicleInfo->hoverStrength;
5402 pm->ps->velocity[2] += (0.3f-trace->fraction)*hoverForce*pVeh->m_fTimeModifier;
5403
5404 // if (pm->ps->velocity[2]>60.0f)
5405 // {
5406 // pm->ps->velocity[2] = 60.0f;
5407 // }
5408
5409 if ( (trace->contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) )
5410 {//hovering on water, make a spash if moving
5411 if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 )
5412 {//moving at a decent speed
5413 if ( Q_irand( pml.frametime, 100 ) >= 50 )
5414 {//splash
5415 vAng[PITCH] = vAng[ROLL] = 0;
5416 vAng[YAW] = pVeh->m_vOrientation[YAW];
5417 AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] );
5418 if ( pVeh->m_pVehicleInfo->iWakeFX )
5419 {
5420 G_PlayEffect( pVeh->m_pVehicleInfo->iWakeFX, trace->endpos, fxAxis );
5421 }
5422 }
5423 }
5424 }
5425
5426 if (pVeh->m_ulFlags & VEH_SLIDEBREAKING)
5427 {
5428 if ( Q_irand( pml.frametime, 100 ) >= 50 )
5429 {//dust
5430 VectorClear(fxAxis[0]);
5431 fxAxis[0][2] = 1;
5432
5433 VectorCopy(pm->ps->velocity, fxAxis[1]);
5434 fxAxis[1][2] *= 0.01f;
5435 VectorMA(pm->ps->origin, 0.25f, fxAxis[1], point);
5436 G_PlayEffect("ships/swoop_dust", point, fxAxis[0]);
5437 }
5438 }
5439 pml.groundPlane = qtrue;
5440 }
5441 }
5442 }
5443 if ( pml.groundPlane )
5444 {
5445 PM_SetVehicleAngles( pml.groundTrace.plane.normal );
5446 // We're on the ground.
5447 // if (pVeh->m_ulFlags&VEH_FLYING && level.time<pVeh->m_iTurboTime)
5448 // {
5449 // pVeh->m_iTurboTime = 0; // stop turbo
5450 // }
5451 pVeh->m_ulFlags &= ~VEH_FLYING;
5452 pVeh->m_vAngularVelocity = 0.0f;
5453 }
5454 else
5455 {
5456 PM_SetVehicleAngles( NULL );
5457 // We're flying in the air.
5458 pVeh->m_ulFlags |= VEH_FLYING;
5459 //groundTrace
5460
5461 if (pVeh->m_vAngularVelocity==0.0f)
5462 {
5463 pVeh->m_vAngularVelocity = pVeh->m_vOrientation[YAW] - pVeh->m_vPrevOrientation[YAW];
5464 if (pVeh->m_vAngularVelocity<-15.0f)
5465 {
5466 pVeh->m_vAngularVelocity = -15.0f;
5467 }
5468 if (pVeh->m_vAngularVelocity> 15.0f)
5469 {
5470 pVeh->m_vAngularVelocity = 15.0f;
5471 }
5472
5473 // BEGIN MATRIX MODE INIT FOR JUMP
5474 //=================================
5475 if (pm->gent &&
5476 pm->gent->owner &&
5477 (pm->gent->owner->s.number<MAX_CLIENTS||G_ControlledByPlayer(pm->gent->owner)) &&
5478 pVeh->m_pVehicleInfo->type==VH_SPEEDER &&
5479 level.time>(LastMatrixJumpTime+5000) && VectorLength(pm->ps->velocity)>30.0f)
5480 {
5481 LastMatrixJumpTime = level.time;
5482 vec3_t predictedApx;
5483 vec3_t predictedFallVelocity;
5484 vec3_t predictedLandPosition;
5485
5486 VectorScale(pm->ps->velocity, 2.0f, predictedFallVelocity); // take friction into account
5487 predictedFallVelocity[2] = -(pm->ps->gravity * 1.1f); // take gravity into account
5488
5489 VectorMA(pm->ps->origin, 0.25f, pm->ps->velocity, predictedApx);
5490 VectorMA(predictedApx, 0.25f, predictedFallVelocity, predictedLandPosition);
5491
5492
5493
5494
5495 trace_t trace2;
5496 gi.trace( &trace2, predictedApx, pm->mins, pm->maxs, predictedLandPosition, pm->ps->clientNum, traceContents, (EG2_Collision)0, 0);
5497 if (!trace2.startsolid && !trace2.allsolid && trace2.fraction>0.75 && Q_irand(0, 3)==0)
5498 {
5499 LastMatrixJumpTime += 20000;
5500 G_StartMatrixEffect(pm->gent, MEF_HIT_GROUND_STOP);
5501 // CG_DrawEdge(pm->ps->origin, predictedApx, EDGE_WHITE_TWOSECOND);
5502 // CG_DrawEdge(predictedApx, predictedLandPosition, EDGE_WHITE_TWOSECOND);
5503 }
5504 // else
5505 // {
5506 // CG_DrawEdge(pm->ps->origin, predictedApx, EDGE_RED_TWOSECOND);
5507 // CG_DrawEdge(predictedApx, predictedLandPosition, EDGE_RED_TWOSECOND);
5508 // }
5509 }
5510 //=================================
5511 }
5512 pVeh->m_vAngularVelocity *= 0.95f; // Angular Velocity Decays Over Time
5513 }
5514 PM_GroundTraceMissed();
5515 }
5516
5517 /*
5518 =============
5519 PM_SetWaterLevelAtPoint FIXME: avoid this twice? certainly if not moving
5520 =============
5521 */
PM_SetWaterLevelAtPoint(vec3_t org,int * waterlevel,int * watertype)5522 static void PM_SetWaterLevelAtPoint( vec3_t org, int *waterlevel, int *watertype )
5523 {
5524 vec3_t point;
5525 int cont;
5526 int sample1;
5527 int sample2;
5528
5529 //
5530 // get waterlevel, accounting for ducking
5531 //
5532 *waterlevel = 0;
5533 *watertype = 0;
5534
5535 point[0] = org[0];
5536 point[1] = org[1];
5537 point[2] = org[2] + DEFAULT_MINS_2 + 1;
5538 if (gi.totalMapContents() & (MASK_WATER|CONTENTS_LADDER))
5539 {
5540 cont = pm->pointcontents( point, pm->ps->clientNum );
5541
5542 if ( cont & (MASK_WATER|CONTENTS_LADDER) )
5543 {
5544 sample2 = pm->ps->viewheight - DEFAULT_MINS_2;
5545 sample1 = sample2 / 2;
5546
5547 *watertype = cont;
5548 *waterlevel = 1;
5549 point[2] = org[2] + DEFAULT_MINS_2 + sample1;
5550 cont = pm->pointcontents( point, pm->ps->clientNum );
5551 if ( cont & (MASK_WATER|CONTENTS_LADDER) )
5552 {
5553 *waterlevel = 2;
5554 point[2] = org[2] + DEFAULT_MINS_2 + sample2;
5555 cont = pm->pointcontents( point, pm->ps->clientNum );
5556 if ( cont & (MASK_WATER|CONTENTS_LADDER) )
5557 {
5558 *waterlevel = 3;
5559 }
5560 }
5561 }
5562 }
5563 }
5564
PM_SetWaterHeight(void)5565 void PM_SetWaterHeight( void )
5566 {
5567 pm->ps->waterHeightLevel = WHL_NONE;
5568 if ( pm->waterlevel < 1 )
5569 {
5570 pm->ps->waterheight = pm->ps->origin[2] + DEFAULT_MINS_2 - 4;
5571 return;
5572 }
5573 trace_t trace;
5574 vec3_t top, bottom;
5575
5576 VectorCopy( pm->ps->origin, top );
5577 VectorCopy( pm->ps->origin, bottom );
5578 top[2] += pm->gent->client->standheight;
5579 bottom[2] += DEFAULT_MINS_2;
5580
5581 gi.trace( &trace, top, pm->mins, pm->maxs, bottom, pm->ps->clientNum, MASK_WATER, (EG2_Collision)0, 0 );
5582
5583 if ( trace.startsolid )
5584 {//under water
5585 pm->ps->waterheight = top[2] + 4;
5586 }
5587 else if ( trace.fraction < 1.0f )
5588 {//partially in and partially out of water
5589 pm->ps->waterheight = trace.endpos[2]+pm->mins[2];
5590 }
5591 else if ( trace.contents&MASK_WATER )
5592 {//water is above me
5593 pm->ps->waterheight = top[2] + 4;
5594 }
5595 else
5596 {//water is below me
5597 pm->ps->waterheight = bottom[2] - 4;
5598 }
5599 float distFromEyes = (pm->ps->origin[2]+pm->gent->client->standheight)-pm->ps->waterheight;
5600
5601 if ( distFromEyes < 0 )
5602 {
5603 pm->ps->waterHeightLevel = WHL_UNDER;
5604 }
5605 else if ( distFromEyes < 6 )
5606 {
5607 pm->ps->waterHeightLevel = WHL_HEAD;
5608 }
5609 else if ( distFromEyes < 18 )
5610 {
5611 pm->ps->waterHeightLevel = WHL_SHOULDERS;
5612 }
5613 else if ( distFromEyes < pm->gent->client->standheight-8 )
5614 {//at least 8 above origin
5615 pm->ps->waterHeightLevel = WHL_TORSO;
5616 }
5617 else
5618 {
5619 float distFromOrg = pm->ps->origin[2]-pm->ps->waterheight;
5620 if ( distFromOrg < 6 )
5621 {
5622 pm->ps->waterHeightLevel = WHL_WAIST;
5623 }
5624 else if ( distFromOrg < 16 )
5625 {
5626 pm->ps->waterHeightLevel = WHL_KNEES;
5627 }
5628 else if ( distFromOrg > fabs(pm->mins[2]) )
5629 {
5630 pm->ps->waterHeightLevel = WHL_NONE;
5631 }
5632 else
5633 {
5634 pm->ps->waterHeightLevel = WHL_ANKLES;
5635 }
5636 }
5637 }
5638
5639
5640 /*
5641 ==============
5642 PM_SetBounds
5643
5644 Sets mins, maxs
5645 ==============
5646 */
PM_SetBounds(void)5647 static void PM_SetBounds (void)
5648 {
5649 if ( pm->gent && pm->gent->client )
5650 {
5651 if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] )
5652 {
5653 //assert(0);
5654 }
5655
5656 VectorCopy( pm->gent->mins, pm->mins );
5657 VectorCopy( pm->gent->maxs, pm->maxs );
5658 }
5659 else
5660 {
5661 if ( !DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2 )
5662 {
5663 assert(0);
5664 }
5665
5666 pm->mins[0] = DEFAULT_MINS_0;
5667 pm->mins[1] = DEFAULT_MINS_1;
5668
5669 pm->maxs[0] = DEFAULT_MAXS_0;
5670 pm->maxs[1] = DEFAULT_MAXS_1;
5671
5672 pm->mins[2] = DEFAULT_MINS_2;
5673 pm->maxs[2] = DEFAULT_MAXS_2;
5674 }
5675 }
5676
5677 /*
5678 ==============
5679 PM_CheckDuck
5680
5681 Sets mins, maxs, and pm->ps->viewheight
5682 ==============
5683 */
PM_CheckDuck(void)5684 static void PM_CheckDuck (void)
5685 {
5686 trace_t trace;
5687 int standheight;
5688 int crouchheight;
5689 int oldHeight;
5690
5691 if ( pm->gent && pm->gent->client )
5692 {
5693 if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] )
5694 {
5695 //assert(0);
5696 }
5697
5698 if ( pm->ps->clientNum < MAX_CLIENTS
5699 && (pm->gent->client->NPC_class == CLASS_ATST ||pm->gent->client->NPC_class == CLASS_RANCOR)
5700 && !cg.renderingThirdPerson )
5701 {
5702 standheight = crouchheight = 128;
5703 }
5704 else
5705 {
5706 standheight = pm->gent->client->standheight;
5707 crouchheight = pm->gent->client->crouchheight;
5708 }
5709 }
5710 else
5711 {
5712 if ( !DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2 )
5713 {
5714 assert(0);
5715 }
5716
5717 standheight = DEFAULT_MAXS_2;
5718 crouchheight = CROUCH_MAXS_2;
5719 }
5720
5721 if ( PM_RidingVehicle() || (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE) )
5722 {//riding a vehicle or are a vehicle
5723 //no ducking or rolling when on a vehicle
5724 //right? not even on ones that you just ride on top of?
5725 pm->ps->pm_flags &= ~PMF_DUCKED;
5726 pm->maxs[2] = standheight;
5727 //FIXME: have a crouchviewheight and standviewheight on ent?
5728 pm->ps->viewheight = standheight + STANDARD_VIEWHEIGHT_OFFSET;//DEFAULT_VIEWHEIGHT;
5729 //NOTE: we don't clear the pm->cmd.upmove here because
5730 //the vehicle code may need it later... but, for riders,
5731 //it should have already been copied over to the vehicle, right?
5732 return;
5733 }
5734
5735 if ( PM_InGetUp( pm->ps ) )
5736 {//can't do any kind of crouching when getting up
5737 if ( pm->ps->legsAnim == BOTH_GETUP_CROUCH_B1 || pm->ps->legsAnim == BOTH_GETUP_CROUCH_F1 )
5738 {//crouched still
5739 pm->ps->pm_flags |= PMF_DUCKED;
5740 pm->maxs[2] = crouchheight;
5741 }
5742 pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;
5743 return;
5744 }
5745
5746 oldHeight = pm->maxs[2];
5747
5748 if ( PM_InRoll( pm->ps ) )
5749 {
5750 /*
5751 if ( pm->ps->clientNum && pm->gent && pm->gent->client )
5752 {
5753 pm->maxs[2] = pm->gent->client->renderInfo.eyePoint[2]-pm->ps->origin[2] + 4;
5754 if ( crouchheight > pm->maxs[2] )
5755 {
5756 pm->maxs[2] = crouchheight;
5757 }
5758 }
5759 else
5760 */
5761 {
5762 pm->maxs[2] = crouchheight;
5763 }
5764 pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;
5765 pm->ps->pm_flags |= PMF_DUCKED;
5766 return;
5767 }
5768 if ( PM_GettingUpFromKnockDown( standheight, crouchheight ) )
5769 {
5770 pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;
5771 return;
5772 }
5773 if ( PM_InKnockDown( pm->ps ) )
5774 {//forced crouch
5775 if ( pm->gent && pm->gent->client )
5776 {//interrupted any potential delayed weapon fires
5777 pm->gent->client->fireDelay = 0;
5778 }
5779 pm->maxs[2] = crouchheight;
5780 pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;
5781 pm->ps->pm_flags |= PMF_DUCKED;
5782 return;
5783 }
5784 if ( pm->cmd.upmove < 0 )
5785 { // trying to duck
5786 pm->maxs[2] = crouchheight;
5787 pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;//CROUCH_VIEWHEIGHT;
5788 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE && !PM_SwimmingAnim( pm->ps->legsAnim ) )
5789 {//Not ducked already and trying to duck in mid-air
5790 //will raise your feet, unducking whilst in air will drop feet
5791 if ( !(pm->ps->pm_flags&PMF_DUCKED) )
5792 {
5793 pm->ps->eFlags ^= EF_TELEPORT_BIT;
5794 }
5795 if ( pm->gent )
5796 {
5797 pm->ps->origin[2] += oldHeight - pm->maxs[2];//diff will be zero if were already ducking
5798 //Don't worry, we know we fit in a smaller size
5799 }
5800 }
5801 pm->ps->pm_flags |= PMF_DUCKED;
5802 if ( d_JediAI->integer )
5803 {
5804 if ( pm->ps->clientNum && pm->ps->weapon == WP_SABER )
5805 {
5806 Com_Printf( "ducking\n" );
5807 }
5808 }
5809 }
5810 else
5811 { // want to stop ducking, stand up if possible
5812 if ( pm->ps->pm_flags & PMF_DUCKED )
5813 {//Was ducking
5814 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
5815 {//unducking whilst in air will try to drop feet
5816 pm->maxs[2] = standheight;
5817 pm->ps->origin[2] += oldHeight - pm->maxs[2];
5818 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 );
5819 if ( !trace.allsolid )
5820 {
5821 pm->ps->eFlags ^= EF_TELEPORT_BIT;
5822 pm->ps->pm_flags &= ~PMF_DUCKED;
5823 }
5824 else
5825 {//Put us back
5826 pm->ps->origin[2] -= oldHeight - pm->maxs[2];
5827 }
5828 //NOTE: this isn't the best way to check this, you may have room to unduck
5829 //while in air, but your feet are close to landing. Probably won't be a
5830 //noticable shortcoming
5831 }
5832 else
5833 {
5834 // try to stand up
5835 pm->maxs[2] = standheight;
5836 pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 );
5837 if ( !trace.allsolid )
5838 {
5839 pm->ps->pm_flags &= ~PMF_DUCKED;
5840 }
5841 }
5842 }
5843
5844 if ( pm->ps->pm_flags & PMF_DUCKED )
5845 {//Still ducking
5846 pm->maxs[2] = crouchheight;
5847 pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;//CROUCH_VIEWHEIGHT;
5848 }
5849 else
5850 {//standing now
5851 pm->maxs[2] = standheight;
5852 //FIXME: have a crouchviewheight and standviewheight on ent?
5853 pm->ps->viewheight = standheight + STANDARD_VIEWHEIGHT_OFFSET;//DEFAULT_VIEWHEIGHT;
5854 }
5855 }
5856 }
5857
5858
5859
5860 //===================================================================
PM_SaberLockAnim(int anim)5861 qboolean PM_SaberLockAnim( int anim )
5862 {
5863 switch ( anim )
5864 {
5865 case BOTH_BF2LOCK: //#
5866 case BOTH_BF1LOCK: //#
5867 case BOTH_CWCIRCLELOCK: //#
5868 case BOTH_CCWCIRCLELOCK: //#
5869 return qtrue;
5870 }
5871 return qfalse;
5872 }
5873
PM_ForceAnim(int anim)5874 qboolean PM_ForceAnim( int anim )
5875 {
5876 switch ( anim )
5877 {
5878 case BOTH_CHOKE1: //being choked...???
5879 case BOTH_GESTURE1: //taunting...
5880 case BOTH_RESISTPUSH: //# plant yourself to resist force push/pulls.
5881 case BOTH_FORCEPUSH: //# Use off-hand to do force power.
5882 case BOTH_FORCEPULL: //# Use off-hand to do force power.
5883 case BOTH_MINDTRICK1: //# Use off-hand to do mind trick
5884 case BOTH_MINDTRICK2: //# Use off-hand to do distraction
5885 case BOTH_FORCELIGHTNING: //# Use off-hand to do lightning
5886 case BOTH_FORCELIGHTNING_START:
5887 case BOTH_FORCELIGHTNING_HOLD: //# Use off-hand to do lightning
5888 case BOTH_FORCELIGHTNING_RELEASE: //# Use off-hand to do lightning
5889 case BOTH_FORCEHEAL_START: //# Healing meditation pose start
5890 case BOTH_FORCEHEAL_STOP: //# Healing meditation pose end
5891 case BOTH_FORCEHEAL_QUICK: //# Healing meditation gesture
5892 case BOTH_FORCEGRIP1: //# temp force-grip anim (actually re-using push)
5893 case BOTH_FORCEGRIP_HOLD: //# temp force-grip anim (actually re-using push)
5894 case BOTH_FORCEGRIP_RELEASE: //# temp force-grip anim (actually re-using push)
5895 //case BOTH_FORCEGRIP3: //# force-gripping
5896 case BOTH_FORCE_RAGE:
5897 case BOTH_FORCE_2HANDEDLIGHTNING:
5898 case BOTH_FORCE_2HANDEDLIGHTNING_START:
5899 case BOTH_FORCE_2HANDEDLIGHTNING_HOLD:
5900 case BOTH_FORCE_2HANDEDLIGHTNING_RELEASE:
5901 case BOTH_FORCE_DRAIN:
5902 case BOTH_FORCE_DRAIN_START:
5903 case BOTH_FORCE_DRAIN_HOLD:
5904 case BOTH_FORCE_DRAIN_RELEASE:
5905 case BOTH_FORCE_ABSORB:
5906 case BOTH_FORCE_ABSORB_START:
5907 case BOTH_FORCE_ABSORB_END:
5908 case BOTH_FORCE_PROTECT:
5909 case BOTH_FORCE_PROTECT_FAST:
5910 return qtrue;
5911 break;
5912 }
5913 return qfalse;
5914 }
5915
PM_InSaberAnim(int anim)5916 qboolean PM_InSaberAnim( int anim )
5917 {
5918 if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_H1_S1_BR )
5919 {
5920 return qtrue;
5921 }
5922 return qfalse;
5923 }
5924
PM_InForceGetUp(playerState_t * ps)5925 qboolean PM_InForceGetUp( playerState_t *ps )
5926 {
5927 switch ( ps->legsAnim )
5928 {
5929 case BOTH_FORCE_GETUP_F1:
5930 case BOTH_FORCE_GETUP_F2:
5931 case BOTH_FORCE_GETUP_B1:
5932 case BOTH_FORCE_GETUP_B2:
5933 case BOTH_FORCE_GETUP_B3:
5934 case BOTH_FORCE_GETUP_B4:
5935 case BOTH_FORCE_GETUP_B5:
5936 case BOTH_FORCE_GETUP_B6:
5937 case BOTH_GETUP_BROLL_B:
5938 case BOTH_GETUP_BROLL_F:
5939 case BOTH_GETUP_BROLL_L:
5940 case BOTH_GETUP_BROLL_R:
5941 case BOTH_GETUP_FROLL_B:
5942 case BOTH_GETUP_FROLL_F:
5943 case BOTH_GETUP_FROLL_L:
5944 case BOTH_GETUP_FROLL_R:
5945 if ( ps->legsAnimTimer )
5946 {
5947 return qtrue;
5948 }
5949 break;
5950 }
5951 return qfalse;
5952 }
5953
PM_InGetUp(playerState_t * ps)5954 qboolean PM_InGetUp( playerState_t *ps )
5955 {
5956 switch ( ps->legsAnim )
5957 {
5958 case BOTH_GETUP1:
5959 case BOTH_GETUP2:
5960 case BOTH_GETUP3:
5961 case BOTH_GETUP4:
5962 case BOTH_GETUP5:
5963 case BOTH_GETUP_CROUCH_F1:
5964 case BOTH_GETUP_CROUCH_B1:
5965 case BOTH_GETUP_BROLL_B:
5966 case BOTH_GETUP_BROLL_F:
5967 case BOTH_GETUP_BROLL_L:
5968 case BOTH_GETUP_BROLL_R:
5969 case BOTH_GETUP_FROLL_B:
5970 case BOTH_GETUP_FROLL_F:
5971 case BOTH_GETUP_FROLL_L:
5972 case BOTH_GETUP_FROLL_R:
5973 if ( ps->legsAnimTimer )
5974 {
5975 return qtrue;
5976 }
5977 break;
5978 default:
5979 return PM_InForceGetUp( ps );
5980 break;
5981 }
5982 //what the hell, redundant, but...
5983 return qfalse;
5984 }
5985
PM_InGetUpNoRoll(playerState_t * ps)5986 qboolean PM_InGetUpNoRoll( playerState_t *ps )
5987 {
5988 switch ( ps->legsAnim )
5989 {
5990 case BOTH_GETUP1:
5991 case BOTH_GETUP2:
5992 case BOTH_GETUP3:
5993 case BOTH_GETUP4:
5994 case BOTH_GETUP5:
5995 case BOTH_GETUP_CROUCH_F1:
5996 case BOTH_GETUP_CROUCH_B1:
5997 case BOTH_FORCE_GETUP_F1:
5998 case BOTH_FORCE_GETUP_F2:
5999 case BOTH_FORCE_GETUP_B1:
6000 case BOTH_FORCE_GETUP_B2:
6001 case BOTH_FORCE_GETUP_B3:
6002 case BOTH_FORCE_GETUP_B4:
6003 case BOTH_FORCE_GETUP_B5:
6004 case BOTH_FORCE_GETUP_B6:
6005 if ( ps->legsAnimTimer )
6006 {
6007 return qtrue;
6008 }
6009 break;
6010 }
6011 return qfalse;
6012 }
6013
PM_InKnockDown(playerState_t * ps)6014 qboolean PM_InKnockDown( playerState_t *ps )
6015 {
6016 switch ( ps->legsAnim )
6017 {
6018 case BOTH_KNOCKDOWN1:
6019 case BOTH_KNOCKDOWN2:
6020 case BOTH_KNOCKDOWN3:
6021 case BOTH_KNOCKDOWN4:
6022 case BOTH_KNOCKDOWN5:
6023 //special anims:
6024 case BOTH_RELEASED:
6025 return qtrue;
6026 break;
6027 case BOTH_LK_DL_ST_T_SB_1_L:
6028 if ( ps->legsAnimTimer < 550 )
6029 {
6030 return qtrue;
6031 }
6032 break;
6033 case BOTH_PLAYER_PA_3_FLY:
6034 if ( ps->legsAnimTimer < 300 )
6035 {
6036 return qtrue;
6037 }
6038 /*
6039 else if ( ps->clientNum < MAX_CLIENTS
6040 && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME )
6041 {
6042 return qtrue;
6043 }
6044 */
6045 break;
6046 default:
6047 return PM_InGetUp( ps );
6048 break;
6049 }
6050 return qfalse;
6051 }
6052
PM_InKnockDownNoGetup(playerState_t * ps)6053 qboolean PM_InKnockDownNoGetup( playerState_t *ps )
6054 {
6055 switch ( ps->legsAnim )
6056 {
6057 case BOTH_KNOCKDOWN1:
6058 case BOTH_KNOCKDOWN2:
6059 case BOTH_KNOCKDOWN3:
6060 case BOTH_KNOCKDOWN4:
6061 case BOTH_KNOCKDOWN5:
6062 //special anims:
6063 case BOTH_RELEASED:
6064 return qtrue;
6065 break;
6066 case BOTH_LK_DL_ST_T_SB_1_L:
6067 if ( ps->legsAnimTimer < 550 )
6068 {
6069 return qtrue;
6070 }
6071 break;
6072 case BOTH_PLAYER_PA_3_FLY:
6073 if ( ps->legsAnimTimer < 300 )
6074 {
6075 return qtrue;
6076 }
6077 /*
6078 else if ( ps->clientNum < MAX_CLIENTS
6079 && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME )
6080 {
6081 return qtrue;
6082 }
6083 */
6084 break;
6085 }
6086 return qfalse;
6087 }
6088
PM_InKnockDownOnGround(playerState_t * ps)6089 qboolean PM_InKnockDownOnGround( playerState_t *ps )
6090 {
6091 switch ( ps->legsAnim )
6092 {
6093 case BOTH_KNOCKDOWN1:
6094 case BOTH_KNOCKDOWN2:
6095 case BOTH_KNOCKDOWN3:
6096 case BOTH_KNOCKDOWN4:
6097 case BOTH_KNOCKDOWN5:
6098 case BOTH_RELEASED:
6099 //if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->legsAnimTimer > 300 )
6100 {//at end of fall down anim
6101 return qtrue;
6102 }
6103 break;
6104 case BOTH_GETUP1:
6105 case BOTH_GETUP2:
6106 case BOTH_GETUP3:
6107 case BOTH_GETUP4:
6108 case BOTH_GETUP5:
6109 case BOTH_GETUP_CROUCH_F1:
6110 case BOTH_GETUP_CROUCH_B1:
6111 case BOTH_FORCE_GETUP_F1:
6112 case BOTH_FORCE_GETUP_F2:
6113 case BOTH_FORCE_GETUP_B1:
6114 case BOTH_FORCE_GETUP_B2:
6115 case BOTH_FORCE_GETUP_B3:
6116 case BOTH_FORCE_GETUP_B4:
6117 case BOTH_FORCE_GETUP_B5:
6118 case BOTH_FORCE_GETUP_B6:
6119 if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->legsAnimTimer < 500 )
6120 {//at beginning of getup anim
6121 return qtrue;
6122 }
6123 break;
6124 case BOTH_GETUP_BROLL_B:
6125 case BOTH_GETUP_BROLL_F:
6126 case BOTH_GETUP_BROLL_L:
6127 case BOTH_GETUP_BROLL_R:
6128 case BOTH_GETUP_FROLL_B:
6129 case BOTH_GETUP_FROLL_F:
6130 case BOTH_GETUP_FROLL_L:
6131 case BOTH_GETUP_FROLL_R:
6132 if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->legsAnimTimer < 500 )
6133 {//at beginning of getup anim
6134 return qtrue;
6135 }
6136 break;
6137 case BOTH_LK_DL_ST_T_SB_1_L:
6138 if ( ps->legsAnimTimer < 1000 )
6139 {
6140 return qtrue;
6141 }
6142 break;
6143 case BOTH_PLAYER_PA_3_FLY:
6144 if ( ps->legsAnimTimer < 300 )
6145 {
6146 return qtrue;
6147 }
6148 /*
6149 else if ( ps->clientNum < MAX_CLIENTS
6150 && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME )
6151 {
6152 return qtrue;
6153 }
6154 */
6155 break;
6156 }
6157 return qfalse;
6158 }
6159
PM_CrouchGetup(float crouchheight)6160 qboolean PM_CrouchGetup( float crouchheight )
6161 {
6162 pm->maxs[2] = crouchheight;
6163 pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;
6164 int anim = -1;
6165 switch ( pm->ps->legsAnim )
6166 {
6167 case BOTH_KNOCKDOWN1:
6168 case BOTH_KNOCKDOWN2:
6169 case BOTH_KNOCKDOWN4:
6170 case BOTH_RELEASED:
6171 case BOTH_PLAYER_PA_3_FLY:
6172 anim = BOTH_GETUP_CROUCH_B1;
6173 break;
6174 case BOTH_KNOCKDOWN3:
6175 case BOTH_KNOCKDOWN5:
6176 case BOTH_LK_DL_ST_T_SB_1_L:
6177 anim = BOTH_GETUP_CROUCH_F1;
6178 break;
6179 }
6180 if ( anim == -1 )
6181 {//WTF? stay down?
6182 pm->ps->legsAnimTimer = 100;//hold this anim for another 10th of a second
6183 return qfalse;
6184 }
6185 else
6186 {//get up into crouch anim
6187 if ( PM_LockedAnim( pm->ps->torsoAnim ) )
6188 {//need to be able to override this anim
6189 pm->ps->torsoAnimTimer = 0;
6190 }
6191 if ( PM_LockedAnim( pm->ps->legsAnim ) )
6192 {//need to be able to override this anim
6193 pm->ps->legsAnimTimer = 0;
6194 }
6195 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS );
6196 pm->ps->saberMove = pm->ps->saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in
6197 pm->ps->saberBlocked = BLOCKED_NONE;
6198 return qtrue;
6199 }
6200 }
6201
6202 extern qboolean PM_GoingToAttackDown( playerState_t *ps );
PM_CheckRollGetup(void)6203 qboolean PM_CheckRollGetup( void )
6204 {
6205 if ( pm->ps->legsAnim == BOTH_KNOCKDOWN1
6206 || pm->ps->legsAnim == BOTH_KNOCKDOWN2
6207 || pm->ps->legsAnim == BOTH_KNOCKDOWN3
6208 || pm->ps->legsAnim == BOTH_KNOCKDOWN4
6209 || pm->ps->legsAnim == BOTH_KNOCKDOWN5
6210 || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L
6211 || pm->ps->legsAnim == BOTH_PLAYER_PA_3_FLY
6212 || pm->ps->legsAnim == BOTH_RELEASED )
6213 {//lying on back or front
6214 if ( ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && ( pm->cmd.rightmove || (pm->cmd.forwardmove&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) ) )//player pressing left or right
6215 || ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) && pm->gent->NPC//an NPC
6216 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have at least force jump 1
6217 && pm->gent->enemy //I have an enemy
6218 && pm->gent->enemy->client//a client
6219 && pm->gent->enemy->enemy == pm->gent//he's mad at me!
6220 && (PM_GoingToAttackDown( &pm->gent->enemy->client->ps )||!Q_irand(0,2))//he's attacking downward! (or we just feel like doing it this time)
6221 && ((pm->gent->client&&pm->gent->client->NPC_class==CLASS_ALORA)||Q_irand( 0, RANK_CAPTAIN )<pm->gent->NPC->rank) ) )//higher rank I am, more likely I am to roll away!
6222 {//roll away!
6223 int anim;
6224 qboolean forceGetUp = qfalse;
6225 if ( pm->cmd.forwardmove > 0 )
6226 {
6227 if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3
6228 || pm->ps->legsAnim == BOTH_KNOCKDOWN5
6229 || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6230 {
6231 anim = BOTH_GETUP_FROLL_F;
6232 }
6233 else
6234 {
6235 anim = BOTH_GETUP_BROLL_F;
6236 }
6237 forceGetUp = qtrue;
6238 }
6239 else if ( pm->cmd.forwardmove < 0 )
6240 {
6241 if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3
6242 || pm->ps->legsAnim == BOTH_KNOCKDOWN5
6243 || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6244 {
6245 anim = BOTH_GETUP_FROLL_B;
6246 }
6247 else
6248 {
6249 anim = BOTH_GETUP_BROLL_B;
6250 }
6251 forceGetUp = qtrue;
6252 }
6253 else if ( pm->cmd.rightmove > 0 )
6254 {
6255 if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3
6256 || pm->ps->legsAnim == BOTH_KNOCKDOWN5
6257 || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6258 {
6259 anim = BOTH_GETUP_FROLL_R;
6260 }
6261 else
6262 {
6263 anim = BOTH_GETUP_BROLL_R;
6264 }
6265 }
6266 else if ( pm->cmd.rightmove < 0 )
6267 {
6268 if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3
6269 || pm->ps->legsAnim == BOTH_KNOCKDOWN5
6270 || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6271 {
6272 anim = BOTH_GETUP_FROLL_L;
6273 }
6274 else
6275 {
6276 anim = BOTH_GETUP_BROLL_L;
6277 }
6278 }
6279 else
6280 {
6281 if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3
6282 || pm->ps->legsAnim == BOTH_KNOCKDOWN5
6283 || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6284 {//on your front
6285 anim = Q_irand( BOTH_GETUP_FROLL_B, BOTH_GETUP_FROLL_R );
6286 }
6287 else
6288 {
6289 anim = Q_irand( BOTH_GETUP_BROLL_B, BOTH_GETUP_BROLL_R );
6290 }
6291 }
6292 if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) )
6293 {
6294 if ( !G_CheckRollSafety( pm->gent, anim, 64 ) )
6295 {//oops, try other one
6296 if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3
6297 || pm->ps->legsAnim == BOTH_KNOCKDOWN5
6298 || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6299 {
6300 if ( anim == BOTH_GETUP_FROLL_R )
6301 {
6302 anim = BOTH_GETUP_FROLL_L;
6303 }
6304 else if ( anim == BOTH_GETUP_FROLL_F )
6305 {
6306 anim = BOTH_GETUP_FROLL_B;
6307 }
6308 else if ( anim == BOTH_GETUP_FROLL_B )
6309 {
6310 anim = BOTH_GETUP_FROLL_F;
6311 }
6312 else
6313 {
6314 anim = BOTH_GETUP_FROLL_L;
6315 }
6316 if ( !G_CheckRollSafety( pm->gent, anim, 64 ) )
6317 {//neither side is clear, screw it
6318 return qfalse;
6319 }
6320 }
6321 else
6322 {
6323 if ( anim == BOTH_GETUP_BROLL_R )
6324 {
6325 anim = BOTH_GETUP_BROLL_L;
6326 }
6327 else if ( anim == BOTH_GETUP_BROLL_F )
6328 {
6329 anim = BOTH_GETUP_BROLL_B;
6330 }
6331 else if ( anim == BOTH_GETUP_FROLL_B )
6332 {
6333 anim = BOTH_GETUP_BROLL_F;
6334 }
6335 else
6336 {
6337 anim = BOTH_GETUP_BROLL_L;
6338 }
6339 if ( !G_CheckRollSafety( pm->gent, anim, 64 ) )
6340 {//neither side is clear, screw it
6341 return qfalse;
6342 }
6343 }
6344 }
6345 }
6346 pm->cmd.rightmove = pm->cmd.forwardmove = 0;
6347 if ( PM_LockedAnim( pm->ps->torsoAnim ) )
6348 {//need to be able to override this anim
6349 pm->ps->torsoAnimTimer = 0;
6350 }
6351 if ( PM_LockedAnim( pm->ps->legsAnim ) )
6352 {//need to be able to override this anim
6353 pm->ps->legsAnimTimer = 0;
6354 }
6355 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS );
6356 pm->ps->weaponTime = pm->ps->torsoAnimTimer - 300;//don't attack until near end of this anim
6357 pm->ps->saberMove = pm->ps->saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in
6358 pm->ps->saberBlocked = BLOCKED_NONE;
6359 if ( forceGetUp )
6360 {
6361 if ( pm->gent && pm->gent->client && pm->gent->client->playerTeam == TEAM_ENEMY
6362 && pm->gent->NPC && pm->gent->NPC->blockedSpeechDebounceTime < level.time
6363 && !Q_irand( 0, 1 ) )
6364 {
6365 PM_AddEvent( Q_irand( EV_COMBAT1, EV_COMBAT3 ) );
6366 pm->gent->NPC->blockedSpeechDebounceTime = level.time + 1000;
6367 }
6368 G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
6369 //launch off ground?
6370 pm->ps->weaponTime = 300;//just to make sure it's cleared
6371 }
6372 return qtrue;
6373 }
6374 }
6375 return qfalse;
6376 }
6377
6378 extern int G_MinGetUpTime( gentity_t *ent );
PM_GettingUpFromKnockDown(float standheight,float crouchheight)6379 qboolean PM_GettingUpFromKnockDown( float standheight, float crouchheight )
6380 {
6381 int legsAnim = pm->ps->legsAnim;
6382 if ( legsAnim == BOTH_KNOCKDOWN1
6383 ||legsAnim == BOTH_KNOCKDOWN2
6384 ||legsAnim == BOTH_KNOCKDOWN3
6385 ||legsAnim == BOTH_KNOCKDOWN4
6386 ||legsAnim == BOTH_KNOCKDOWN5
6387 ||legsAnim == BOTH_PLAYER_PA_3_FLY
6388 ||legsAnim == BOTH_LK_DL_ST_T_SB_1_L
6389 ||legsAnim == BOTH_RELEASED )
6390 {//in a knockdown
6391 int minTimeLeft = G_MinGetUpTime( pm->gent );
6392 if ( pm->ps->legsAnimTimer <= minTimeLeft )
6393 {//if only a quarter of a second left, allow roll-aways
6394 if ( PM_CheckRollGetup() )
6395 {
6396 pm->cmd.rightmove = pm->cmd.forwardmove = 0;
6397 return qtrue;
6398 }
6399 }
6400 if ( TIMER_Exists( pm->gent, "noGetUpStraight" ) )
6401 {
6402 if ( !TIMER_Done2( pm->gent, "noGetUpStraight", qtrue ) )
6403 {//not allowed to do straight get-ups for another few seconds
6404 if ( pm->ps->legsAnimTimer <= minTimeLeft )
6405 {//hold it for a bit
6406 pm->ps->legsAnimTimer = minTimeLeft+1;
6407 }
6408 }
6409 }
6410 if ( !pm->ps->legsAnimTimer || (pm->ps->legsAnimTimer<=minTimeLeft&&(pm->cmd.upmove>0||(pm->gent&&pm->gent->client&&pm->gent->client->NPC_class==CLASS_ALORA))) )
6411 {//done with the knockdown - FIXME: somehow this is allowing an *instant* getup...???
6412 //FIXME: if trying to crouch (holding button?), just get up into a crouch?
6413 if ( pm->cmd.upmove < 0 )
6414 {
6415 return PM_CrouchGetup( crouchheight );
6416 }
6417 else
6418 {
6419 trace_t trace;
6420 // try to stand up
6421 pm->maxs[2] = standheight;
6422 pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 );
6423 if ( !trace.allsolid )
6424 {//stand up
6425 int anim = BOTH_GETUP1;
6426 qboolean forceGetUp = qfalse;
6427 pm->maxs[2] = standheight;
6428 pm->ps->viewheight = standheight + STANDARD_VIEWHEIGHT_OFFSET;
6429 //NOTE: the force power checks will stop fencers and grunts from getting up using force jump
6430 switch ( pm->ps->legsAnim )
6431 {
6432 case BOTH_KNOCKDOWN1:
6433 if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )FORCE_LEVEL_1) )
6434 {
6435 anim = Q_irand( BOTH_FORCE_GETUP_B1, BOTH_FORCE_GETUP_B6 );//NOTE: BOTH_FORCE_GETUP_B5 takes soe steps forward at end
6436 forceGetUp = qtrue;
6437 }
6438 else
6439 {
6440 anim = BOTH_GETUP1;
6441 }
6442 break;
6443 case BOTH_KNOCKDOWN2:
6444 case BOTH_PLAYER_PA_3_FLY:
6445 if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )FORCE_LEVEL_1) )
6446 {
6447 anim = Q_irand( BOTH_FORCE_GETUP_B1, BOTH_FORCE_GETUP_B6 );//NOTE: BOTH_FORCE_GETUP_B5 takes soe steps forward at end
6448 forceGetUp = qtrue;
6449 }
6450 else
6451 {
6452 anim = BOTH_GETUP2;
6453 }
6454 break;
6455 case BOTH_KNOCKDOWN3:
6456 if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )
6457 {
6458 anim = Q_irand( BOTH_FORCE_GETUP_F1, BOTH_FORCE_GETUP_F2 );
6459 forceGetUp = qtrue;
6460 }
6461 else
6462 {
6463 anim = BOTH_GETUP3;
6464 }
6465 break;
6466 case BOTH_KNOCKDOWN4:
6467 case BOTH_RELEASED:
6468 if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )FORCE_LEVEL_1) )
6469 {
6470 anim = Q_irand( BOTH_FORCE_GETUP_B1, BOTH_FORCE_GETUP_B6 );//NOTE: BOTH_FORCE_GETUP_B5 takes soe steps forward at end
6471 forceGetUp = qtrue;
6472 }
6473 else
6474 {
6475 anim = BOTH_GETUP4;
6476 }
6477 break;
6478 case BOTH_KNOCKDOWN5:
6479 case BOTH_LK_DL_ST_T_SB_1_L:
6480 if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )FORCE_LEVEL_1) )
6481 {
6482 anim = Q_irand( BOTH_FORCE_GETUP_F1, BOTH_FORCE_GETUP_F2 );
6483 forceGetUp = qtrue;
6484 }
6485 else
6486 {
6487 anim = BOTH_GETUP5;
6488 }
6489 break;
6490 }
6491 //Com_Printf( "getupanim = %s\n", animTable[anim].name );
6492 if ( forceGetUp )
6493 {
6494 if ( pm->gent && pm->gent->client && pm->gent->client->playerTeam == TEAM_ENEMY
6495 && pm->gent->NPC && pm->gent->NPC->blockedSpeechDebounceTime < level.time
6496 && !Q_irand( 0, 1 ) )
6497 {
6498 PM_AddEvent( Q_irand( EV_COMBAT1, EV_COMBAT3 ) );
6499 pm->gent->NPC->blockedSpeechDebounceTime = level.time + 1000;
6500 }
6501 G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
6502 //launch off ground?
6503 pm->ps->weaponTime = 300;//just to make sure it's cleared
6504 }
6505 if ( PM_LockedAnim( pm->ps->torsoAnim ) )
6506 {//need to be able to override this anim
6507 pm->ps->torsoAnimTimer = 0;
6508 }
6509 if ( PM_LockedAnim( pm->ps->legsAnim ) )
6510 {//need to be able to override this anim
6511 pm->ps->legsAnimTimer = 0;
6512 }
6513 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS );
6514 pm->ps->saberMove = pm->ps->saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in
6515 pm->ps->saberBlocked = BLOCKED_NONE;
6516 return qtrue;
6517 }
6518 else
6519 {
6520 return PM_CrouchGetup( crouchheight );
6521 }
6522 }
6523 }
6524 else
6525 {
6526 if ( pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6527 {
6528 PM_CmdForRoll( pm->ps, &pm->cmd );
6529 }
6530 else
6531 {
6532 pm->cmd.rightmove = pm->cmd.forwardmove = 0;
6533 }
6534 }
6535 }
6536 return qfalse;
6537 }
6538
PM_CmdForRoll(playerState_t * ps,usercmd_t * pCmd)6539 void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd )
6540 {
6541 switch ( ps->legsAnim )
6542 {
6543 case BOTH_ROLL_F:
6544 pCmd->forwardmove = 127;
6545 pCmd->rightmove = 0;
6546 break;
6547 case BOTH_ROLL_B:
6548 pCmd->forwardmove = -127;
6549 pCmd->rightmove = 0;
6550 break;
6551 case BOTH_ROLL_R:
6552 pCmd->forwardmove = 0;
6553 pCmd->rightmove = 127;
6554 break;
6555 case BOTH_ROLL_L:
6556 pCmd->forwardmove = 0;
6557 pCmd->rightmove = -127;
6558 break;
6559
6560 case BOTH_GETUP_BROLL_R:
6561 pCmd->forwardmove = 0;
6562 pCmd->rightmove = 48;
6563 //NOTE: speed is 400
6564 break;
6565
6566 case BOTH_GETUP_FROLL_R:
6567 if ( ps->legsAnimTimer <= 250 )
6568 {//end of anim
6569 pCmd->forwardmove = pCmd->rightmove = 0;
6570 }
6571 else
6572 {
6573 pCmd->forwardmove = 0;
6574 pCmd->rightmove = 48;
6575 //NOTE: speed is 400
6576 }
6577 break;
6578
6579 case BOTH_GETUP_BROLL_L:
6580 pCmd->forwardmove = 0;
6581 pCmd->rightmove = -48;
6582 //NOTE: speed is 400
6583 break;
6584
6585 case BOTH_GETUP_FROLL_L:
6586 if ( ps->legsAnimTimer <= 250 )
6587 {//end of anim
6588 pCmd->forwardmove = pCmd->rightmove = 0;
6589 }
6590 else
6591 {
6592 pCmd->forwardmove = 0;
6593 pCmd->rightmove = -48;
6594 //NOTE: speed is 400
6595 }
6596 break;
6597
6598 case BOTH_GETUP_BROLL_B:
6599 if ( ps->torsoAnimTimer <= 250 )
6600 {//end of anim
6601 pCmd->forwardmove = pCmd->rightmove = 0;
6602 }
6603 else if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->torsoAnimTimer < 350 )
6604 {//beginning of anim
6605 pCmd->forwardmove = pCmd->rightmove = 0;
6606 }
6607 else
6608 {
6609 //FIXME: ramp down over length of anim
6610 pCmd->forwardmove = -64;
6611 pCmd->rightmove = 0;
6612 //NOTE: speed is 400
6613 }
6614 break;
6615
6616 case BOTH_GETUP_FROLL_B:
6617 if ( ps->torsoAnimTimer <= 100 )
6618 {//end of anim
6619 pCmd->forwardmove = pCmd->rightmove = 0;
6620 }
6621 else if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->torsoAnimTimer < 200 )
6622 {//beginning of anim
6623 pCmd->forwardmove = pCmd->rightmove = 0;
6624 }
6625 else
6626 {
6627 //FIXME: ramp down over length of anim
6628 pCmd->forwardmove = -64;
6629 pCmd->rightmove = 0;
6630 //NOTE: speed is 400
6631 }
6632 break;
6633
6634 case BOTH_GETUP_BROLL_F:
6635 if ( ps->torsoAnimTimer <= 550 )
6636 {//end of anim
6637 pCmd->forwardmove = pCmd->rightmove = 0;
6638 }
6639 else if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->torsoAnimTimer < 150 )
6640 {//beginning of anim
6641 pCmd->forwardmove = pCmd->rightmove = 0;
6642 }
6643 else
6644 {
6645 pCmd->forwardmove = 64;
6646 pCmd->rightmove = 0;
6647 //NOTE: speed is 400
6648 }
6649 break;
6650
6651 case BOTH_GETUP_FROLL_F:
6652 if ( ps->torsoAnimTimer <= 100 )
6653 {//end of anim
6654 pCmd->forwardmove = pCmd->rightmove = 0;
6655 }
6656 else
6657 {
6658 //FIXME: ramp down over length of anim
6659 pCmd->forwardmove = 64;
6660 pCmd->rightmove = 0;
6661 //NOTE: speed is 400
6662 }
6663 break;
6664 case BOTH_LK_DL_ST_T_SB_1_L:
6665 //kicked backwards
6666 if ( ps->legsAnimTimer < 3050//at least 10 frames in
6667 && ps->legsAnimTimer > 550 )//at least 6 frames from end
6668 {//move backwards
6669 pCmd->forwardmove = -64;
6670 pCmd->rightmove = 0;
6671 }
6672 else
6673 {
6674 pCmd->forwardmove = pCmd->rightmove = 0;
6675 }
6676 break;
6677 }
6678
6679 pCmd->upmove = 0;
6680 }
6681
PM_InRollIgnoreTimer(playerState_t * ps)6682 qboolean PM_InRollIgnoreTimer( playerState_t *ps )
6683 {
6684 switch ( ps->legsAnim )
6685 {
6686 case BOTH_ROLL_F:
6687 case BOTH_ROLL_B:
6688 case BOTH_ROLL_R:
6689 case BOTH_ROLL_L:
6690 case BOTH_GETUP_BROLL_B:
6691 case BOTH_GETUP_BROLL_F:
6692 case BOTH_GETUP_BROLL_L:
6693 case BOTH_GETUP_BROLL_R:
6694 case BOTH_GETUP_FROLL_B:
6695 case BOTH_GETUP_FROLL_F:
6696 case BOTH_GETUP_FROLL_L:
6697 case BOTH_GETUP_FROLL_R:
6698 return qtrue;
6699 }
6700 return qfalse;
6701 }
6702
PM_InRoll(playerState_t * ps)6703 qboolean PM_InRoll( playerState_t *ps )
6704 {
6705 if ( ps->legsAnimTimer && PM_InRollIgnoreTimer( ps ) )
6706 {
6707 return qtrue;
6708 }
6709
6710 return qfalse;
6711 }
6712
PM_CrouchAnim(int anim)6713 qboolean PM_CrouchAnim( int anim )
6714 {
6715 switch ( anim )
6716 {
6717 case BOTH_SIT1: //# Normal chair sit.
6718 case BOTH_SIT2: //# Lotus position.
6719 case BOTH_SIT3: //# Sitting in tired position: elbows on knees
6720 case BOTH_CROUCH1: //# Transition from standing to crouch
6721 case BOTH_CROUCH1IDLE: //# Crouching idle
6722 case BOTH_CROUCH1WALK: //# Walking while crouched
6723 case BOTH_CROUCH1WALKBACK: //# Walking while crouched
6724 case BOTH_CROUCH2TOSTAND1: //# going from crouch2 to stand1
6725 case BOTH_CROUCH3: //# Desann crouching down to Kyle (cin 9)
6726 case BOTH_KNEES1: //# Tavion on her knees
6727 case BOTH_CROUCHATTACKBACK1://FIXME: not if in middle of anim?
6728 case BOTH_ROLL_STAB:
6729 //???
6730 case BOTH_STAND_TO_KNEEL:
6731 case BOTH_KNEEL_TO_STAND:
6732 case BOTH_TURNCROUCH1:
6733 case BOTH_CROUCH4:
6734 case BOTH_KNEES2: //# Tavion on her knees looking down
6735 case BOTH_KNEES2TO1: //# Transition of KNEES2 to KNEES1
6736 return qtrue;
6737 break;
6738 }
6739 return qfalse;
6740 }
6741
PM_PainAnim(int anim)6742 qboolean PM_PainAnim( int anim )
6743 {
6744 switch ( anim )
6745 {
6746 case BOTH_PAIN1: //# First take pain anim
6747 case BOTH_PAIN2: //# Second take pain anim
6748 case BOTH_PAIN3: //# Third take pain anim
6749 case BOTH_PAIN4: //# Fourth take pain anim
6750 case BOTH_PAIN5: //# Fifth take pain anim - from behind
6751 case BOTH_PAIN6: //# Sixth take pain anim - from behind
6752 case BOTH_PAIN7: //# Seventh take pain anim - from behind
6753 case BOTH_PAIN8: //# Eigth take pain anim - from behind
6754 case BOTH_PAIN9: //#
6755 case BOTH_PAIN10: //#
6756 case BOTH_PAIN11: //#
6757 case BOTH_PAIN12: //#
6758 case BOTH_PAIN13: //#
6759 case BOTH_PAIN14: //#
6760 case BOTH_PAIN15: //#
6761 case BOTH_PAIN16: //#
6762 case BOTH_PAIN17: //#
6763 case BOTH_PAIN18: //#
6764 return qtrue;
6765 break;
6766 }
6767 return qfalse;
6768 }
6769
6770
PM_DodgeHoldAnim(int anim)6771 qboolean PM_DodgeHoldAnim( int anim )
6772 {
6773 switch ( anim )
6774 {
6775 case BOTH_DODGE_HOLD_FL:
6776 case BOTH_DODGE_HOLD_FR:
6777 case BOTH_DODGE_HOLD_BL:
6778 case BOTH_DODGE_HOLD_BR:
6779 case BOTH_DODGE_HOLD_L:
6780 case BOTH_DODGE_HOLD_R:
6781 return qtrue;
6782 break;
6783 }
6784 return qfalse;
6785 }
6786
PM_DodgeAnim(int anim)6787 qboolean PM_DodgeAnim( int anim )
6788 {
6789 switch ( anim )
6790 {
6791 case BOTH_DODGE_FL: //# lean-dodge forward left
6792 case BOTH_DODGE_FR: //# lean-dodge forward right
6793 case BOTH_DODGE_BL: //# lean-dodge backwards left
6794 case BOTH_DODGE_BR: //# lean-dodge backwards right
6795 case BOTH_DODGE_L: //# lean-dodge left
6796 case BOTH_DODGE_R: //# lean-dodge right
6797 return qtrue;
6798 break;
6799 default:
6800 return PM_DodgeHoldAnim( anim );
6801 break;
6802 }
6803 //return qfalse;
6804 }
6805
PM_ForceJumpingAnim(int anim)6806 qboolean PM_ForceJumpingAnim( int anim )
6807 {
6808 switch ( anim )
6809 {
6810 case BOTH_FORCEJUMP1: //# Jump - wind-up and leave ground
6811 case BOTH_FORCEINAIR1: //# In air loop (from jump)
6812 case BOTH_FORCELAND1: //# Landing (from in air loop)
6813 case BOTH_FORCEJUMPBACK1: //# Jump backwards - wind-up and leave ground
6814 case BOTH_FORCEINAIRBACK1: //# In air loop (from jump back)
6815 case BOTH_FORCELANDBACK1: //# Landing backwards(from in air loop)
6816 case BOTH_FORCEJUMPLEFT1: //# Jump left - wind-up and leave ground
6817 case BOTH_FORCEINAIRLEFT1: //# In air loop (from jump left)
6818 case BOTH_FORCELANDLEFT1: //# Landing left(from in air loop)
6819 case BOTH_FORCEJUMPRIGHT1: //# Jump right - wind-up and leave ground
6820 case BOTH_FORCEINAIRRIGHT1: //# In air loop (from jump right)
6821 case BOTH_FORCELANDRIGHT1: //# Landing right(from in air loop)
6822 return qtrue;
6823 break;
6824 }
6825 return qfalse;
6826 }
6827
PM_JumpingAnim(int anim)6828 qboolean PM_JumpingAnim( int anim )
6829 {
6830 switch ( anim )
6831 {
6832 case BOTH_JUMP1: //# Jump - wind-up and leave ground
6833 case BOTH_INAIR1: //# In air loop (from jump)
6834 case BOTH_LAND1: //# Landing (from in air loop)
6835 case BOTH_LAND2: //# Landing Hard (from a great height)
6836 case BOTH_JUMPBACK1: //# Jump backwards - wind-up and leave ground
6837 case BOTH_INAIRBACK1: //# In air loop (from jump back)
6838 case BOTH_LANDBACK1: //# Landing backwards(from in air loop)
6839 case BOTH_JUMPLEFT1: //# Jump left - wind-up and leave ground
6840 case BOTH_INAIRLEFT1: //# In air loop (from jump left)
6841 case BOTH_LANDLEFT1: //# Landing left(from in air loop)
6842 case BOTH_JUMPRIGHT1: //# Jump right - wind-up and leave ground
6843 case BOTH_INAIRRIGHT1: //# In air loop (from jump right)
6844 case BOTH_LANDRIGHT1: //# Landing right(from in air loop)
6845 return qtrue;
6846 break;
6847 default:
6848 if ( PM_InAirKickingAnim( anim ) )
6849 {
6850 return qtrue;
6851 }
6852 return PM_ForceJumpingAnim( anim );
6853 break;
6854 }
6855 //return qfalse;
6856 }
6857
PM_LandingAnim(int anim)6858 qboolean PM_LandingAnim( int anim )
6859 {
6860 switch ( anim )
6861 {
6862 case BOTH_LAND1: //# Landing (from in air loop)
6863 case BOTH_LAND2: //# Landing Hard (from a great height)
6864 case BOTH_LANDBACK1: //# Landing backwards(from in air loop)
6865 case BOTH_LANDLEFT1: //# Landing left(from in air loop)
6866 case BOTH_LANDRIGHT1: //# Landing right(from in air loop)
6867 case BOTH_FORCELAND1: //# Landing (from in air loop)
6868 case BOTH_FORCELANDBACK1: //# Landing backwards(from in air loop)
6869 case BOTH_FORCELANDLEFT1: //# Landing left(from in air loop)
6870 case BOTH_FORCELANDRIGHT1: //# Landing right(from in air loop)
6871 return qtrue;
6872 break;
6873 }
6874 return qfalse;
6875 }
6876
PM_FlippingAnim(int anim)6877 qboolean PM_FlippingAnim( int anim )
6878 {
6879 switch ( anim )
6880 {
6881 case BOTH_FLIP_F: //# Flip forward
6882 case BOTH_FLIP_B: //# Flip backwards
6883 case BOTH_FLIP_L: //# Flip left
6884 case BOTH_FLIP_R: //# Flip right
6885 case BOTH_ALORA_FLIP_1:
6886 case BOTH_ALORA_FLIP_2:
6887 case BOTH_ALORA_FLIP_3:
6888 case BOTH_WALL_RUN_RIGHT_FLIP:
6889 case BOTH_WALL_RUN_LEFT_FLIP:
6890 case BOTH_WALL_FLIP_RIGHT:
6891 case BOTH_WALL_FLIP_LEFT:
6892 case BOTH_FLIP_BACK1:
6893 case BOTH_FLIP_BACK2:
6894 case BOTH_FLIP_BACK3:
6895 case BOTH_WALL_FLIP_BACK1:
6896 case BOTH_ALORA_FLIP_B:
6897 //Not really flips, but...
6898 case BOTH_WALL_RUN_RIGHT:
6899 case BOTH_WALL_RUN_LEFT:
6900 case BOTH_WALL_RUN_RIGHT_STOP:
6901 case BOTH_WALL_RUN_LEFT_STOP:
6902 case BOTH_BUTTERFLY_LEFT:
6903 case BOTH_BUTTERFLY_RIGHT:
6904 case BOTH_BUTTERFLY_FL1:
6905 case BOTH_BUTTERFLY_FR1:
6906 //
6907 case BOTH_ARIAL_LEFT:
6908 case BOTH_ARIAL_RIGHT:
6909 case BOTH_ARIAL_F1:
6910 case BOTH_CARTWHEEL_LEFT:
6911 case BOTH_CARTWHEEL_RIGHT:
6912 case BOTH_JUMPFLIPSLASHDOWN1:
6913 case BOTH_JUMPFLIPSTABDOWN:
6914 case BOTH_JUMPATTACK6:
6915 case BOTH_JUMPATTACK7:
6916 //JKA
6917 case BOTH_FORCEWALLRUNFLIP_END:
6918 case BOTH_FORCEWALLRUNFLIP_ALT:
6919 case BOTH_FLIP_ATTACK7:
6920 case BOTH_A7_SOULCAL:
6921 return qtrue;
6922 break;
6923 }
6924 return qfalse;
6925 }
6926
PM_WalkingAnim(int anim)6927 qboolean PM_WalkingAnim( int anim )
6928 {
6929 switch ( anim )
6930 {
6931 case BOTH_WALK1: //# Normal walk
6932 case BOTH_WALK2: //# Normal walk with saber
6933 case BOTH_WALK_STAFF: //# Normal walk with staff
6934 case BOTH_WALK_DUAL: //# Normal walk with staff
6935 case BOTH_WALK5: //# Tavion taunting Kyle (cin 22)
6936 case BOTH_WALK6: //# Slow walk for Luke (cin 12)
6937 case BOTH_WALK7: //# Fast walk
6938 case BOTH_WALKBACK1: //# Walk1 backwards
6939 case BOTH_WALKBACK2: //# Walk2 backwards
6940 case BOTH_WALKBACK_STAFF: //# Walk backwards with staff
6941 case BOTH_WALKBACK_DUAL: //# Walk backwards with dual
6942 return qtrue;
6943 break;
6944 }
6945 return qfalse;
6946 }
6947
PM_RunningAnim(int anim)6948 qboolean PM_RunningAnim( int anim )
6949 {
6950 switch ( anim )
6951 {
6952 case BOTH_RUN1:
6953 case BOTH_RUN2:
6954 case BOTH_RUN4:
6955 case BOTH_RUN_STAFF:
6956 case BOTH_RUN_DUAL:
6957 case BOTH_RUNBACK1:
6958 case BOTH_RUNBACK2:
6959 case BOTH_RUNBACK_STAFF:
6960 case BOTH_RUN1START: //# Start into full run1
6961 case BOTH_RUN1STOP: //# Stop from full run1
6962 case BOTH_RUNSTRAFE_LEFT1: //# Sidestep left: should loop
6963 case BOTH_RUNSTRAFE_RIGHT1: //# Sidestep right: should loop
6964 return qtrue;
6965 break;
6966 }
6967 return qfalse;
6968 }
6969
PM_RollingAnim(int anim)6970 qboolean PM_RollingAnim( int anim )
6971 {
6972 switch ( anim )
6973 {
6974 case BOTH_ROLL_F: //# Roll forward
6975 case BOTH_ROLL_B: //# Roll backward
6976 case BOTH_ROLL_L: //# Roll left
6977 case BOTH_ROLL_R: //# Roll right
6978 case BOTH_GETUP_BROLL_B:
6979 case BOTH_GETUP_BROLL_F:
6980 case BOTH_GETUP_BROLL_L:
6981 case BOTH_GETUP_BROLL_R:
6982 case BOTH_GETUP_FROLL_B:
6983 case BOTH_GETUP_FROLL_F:
6984 case BOTH_GETUP_FROLL_L:
6985 case BOTH_GETUP_FROLL_R:
6986 return qtrue;
6987 break;
6988 }
6989 return qfalse;
6990 }
6991
PM_SwimmingAnim(int anim)6992 qboolean PM_SwimmingAnim( int anim )
6993 {
6994 switch ( anim )
6995 {
6996 case BOTH_SWIM_IDLE1: //# Swimming Idle 1
6997 case BOTH_SWIMFORWARD: //# Swim forward loop
6998 case BOTH_SWIMBACKWARD: //# Swim backward loop
6999 return qtrue;
7000 break;
7001 }
7002 return qfalse;
7003 }
7004
PM_LeapingSaberAnim(int anim)7005 qboolean PM_LeapingSaberAnim( int anim )
7006 {
7007 switch ( anim )
7008 {
7009 //level 7
7010 case BOTH_T7_BR_TL:
7011 case BOTH_T7__L_BR:
7012 case BOTH_T7__L__R:
7013 case BOTH_T7_BL_BR:
7014 case BOTH_T7_BL__R:
7015 case BOTH_T7_BL_TR:
7016 return qtrue;
7017 break;
7018 }
7019 return qfalse;
7020 }
7021
PM_SpinningSaberAnim(int anim)7022 qboolean PM_SpinningSaberAnim( int anim )
7023 {
7024 switch ( anim )
7025 {
7026 //level 1 - FIXME: level 1 will have *no* spins
7027 case BOTH_T1_BR_BL:
7028 case BOTH_T1__R__L:
7029 case BOTH_T1__R_BL:
7030 case BOTH_T1_TR_BL:
7031 case BOTH_T1_BR_TL:
7032 case BOTH_T1_BR__L:
7033 case BOTH_T1_TL_BR:
7034 case BOTH_T1__L_BR:
7035 case BOTH_T1__L__R:
7036 case BOTH_T1_BL_BR:
7037 case BOTH_T1_BL__R:
7038 case BOTH_T1_BL_TR:
7039 //level 2
7040 case BOTH_T2_BR__L:
7041 case BOTH_T2_BR_BL:
7042 case BOTH_T2__R_BL:
7043 case BOTH_T2__L_BR:
7044 case BOTH_T2_BL_BR:
7045 case BOTH_T2_BL__R:
7046 //level 3
7047 case BOTH_T3_BR__L:
7048 case BOTH_T3_BR_BL:
7049 case BOTH_T3__R_BL:
7050 case BOTH_T3__L_BR:
7051 case BOTH_T3_BL_BR:
7052 case BOTH_T3_BL__R:
7053 //level 4
7054 case BOTH_T4_BR__L:
7055 case BOTH_T4_BR_BL:
7056 case BOTH_T4__R_BL:
7057 case BOTH_T4__L_BR:
7058 case BOTH_T4_BL_BR:
7059 case BOTH_T4_BL__R:
7060 //level 5
7061 case BOTH_T5_BR_BL:
7062 case BOTH_T5__R__L:
7063 case BOTH_T5__R_BL:
7064 case BOTH_T5_TR_BL:
7065 case BOTH_T5_BR_TL:
7066 case BOTH_T5_BR__L:
7067 case BOTH_T5_TL_BR:
7068 case BOTH_T5__L_BR:
7069 case BOTH_T5__L__R:
7070 case BOTH_T5_BL_BR:
7071 case BOTH_T5_BL__R:
7072 case BOTH_T5_BL_TR:
7073 //level 6
7074 case BOTH_T6_BR_TL:
7075 case BOTH_T6__R_TL:
7076 case BOTH_T6__R__L:
7077 case BOTH_T6__R_BL:
7078 case BOTH_T6_TR_TL:
7079 case BOTH_T6_TR__L:
7080 case BOTH_T6_TR_BL:
7081 case BOTH_T6_T__TL:
7082 case BOTH_T6_T__BL:
7083 case BOTH_T6_TL_BR:
7084 case BOTH_T6__L_BR:
7085 case BOTH_T6__L__R:
7086 case BOTH_T6_TL__R:
7087 case BOTH_T6_TL_TR:
7088 case BOTH_T6__L_TR:
7089 case BOTH_T6__L_T_:
7090 case BOTH_T6_BL_T_:
7091 case BOTH_T6_BR__L:
7092 case BOTH_T6_BR_BL:
7093 case BOTH_T6_BL_BR:
7094 case BOTH_T6_BL__R:
7095 case BOTH_T6_BL_TR:
7096 //level 7
7097 case BOTH_T7_BR_TL:
7098 case BOTH_T7_BR__L:
7099 case BOTH_T7_BR_BL:
7100 case BOTH_T7__R__L:
7101 case BOTH_T7__R_BL:
7102 case BOTH_T7_TR__L:
7103 case BOTH_T7_T___R:
7104 case BOTH_T7_TL_BR:
7105 case BOTH_T7__L_BR:
7106 case BOTH_T7__L__R:
7107 case BOTH_T7_BL_BR:
7108 case BOTH_T7_BL__R:
7109 case BOTH_T7_BL_TR:
7110 case BOTH_T7_TL_TR:
7111 case BOTH_T7_T__BR:
7112 case BOTH_T7__L_TR:
7113 case BOTH_V7_BL_S7:
7114 //special
7115 //case BOTH_A2_STABBACK1:
7116 case BOTH_ATTACK_BACK:
7117 case BOTH_CROUCHATTACKBACK1:
7118 case BOTH_BUTTERFLY_LEFT:
7119 case BOTH_BUTTERFLY_RIGHT:
7120 case BOTH_FJSS_TR_BL:
7121 case BOTH_FJSS_TL_BR:
7122 case BOTH_JUMPFLIPSLASHDOWN1:
7123 case BOTH_JUMPFLIPSTABDOWN:
7124 return qtrue;
7125 break;
7126 }
7127 return qfalse;
7128 }
7129
PM_SpinningAnim(int anim)7130 qboolean PM_SpinningAnim( int anim )
7131 {
7132 /*
7133 switch ( anim )
7134 {
7135 //FIXME: list any other spinning anims
7136 default:
7137 break;
7138 }
7139 */
7140 return PM_SpinningSaberAnim( anim );
7141 }
7142
PM_ResetAnkleAngles(void)7143 void PM_ResetAnkleAngles( void )
7144 {
7145 if ( !pm->gent || !pm->gent->client || pm->gent->client->NPC_class != CLASS_ATST )
7146 {
7147 return;
7148 }
7149 if ( pm->gent->footLBone != -1 )
7150 {
7151 gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footLBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 );
7152 }
7153 if ( pm->gent->footRBone != -1 )
7154 {
7155 gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footRBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 );
7156 }
7157 }
7158
PM_AnglesForSlope(const float yaw,const vec3_t slope,vec3_t angles)7159 void PM_AnglesForSlope( const float yaw, const vec3_t slope, vec3_t angles )
7160 {
7161 vec3_t nvf, ovf, ovr, new_angles;
7162 float pitch, mod, dot;
7163
7164 VectorSet( angles, 0, yaw, 0 );
7165 AngleVectors( angles, ovf, ovr, NULL );
7166
7167 vectoangles( slope, new_angles );
7168 pitch = new_angles[PITCH] + 90;
7169 new_angles[ROLL] = new_angles[PITCH] = 0;
7170
7171 AngleVectors( new_angles, nvf, NULL, NULL );
7172
7173 mod = DotProduct( nvf, ovr );
7174
7175 if ( mod < 0 )
7176 mod = -1;
7177 else
7178 mod = 1;
7179
7180 dot = DotProduct( nvf, ovf );
7181
7182 angles[YAW] = 0;
7183 angles[PITCH] = dot * pitch;
7184 angles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
7185 }
7186
PM_FootSlopeTrace(float * pDiff,float * pInterval)7187 void PM_FootSlopeTrace( float *pDiff, float *pInterval )
7188 {
7189 vec3_t footLOrg, footROrg, footLBot, footRBot;
7190 trace_t trace;
7191 float diff, interval;
7192 if ( pm->gent->client->NPC_class == CLASS_ATST )
7193 {
7194 interval = 10;
7195 }
7196 else
7197 {
7198 interval = 4;//?
7199 }
7200
7201 if ( pm->gent->footLBolt == -1 || pm->gent->footRBolt == -1 )
7202 {
7203 if ( pDiff != NULL )
7204 {
7205 *pDiff = 0;
7206 }
7207 if ( pInterval != NULL )
7208 {
7209 *pInterval = interval;
7210 }
7211 return;
7212 }
7213 #if 1
7214 for ( int i = 0; i < 3; i++ )
7215 {
7216 if ( Q_isnan( pm->gent->client->renderInfo.footLPoint[i] )
7217 || Q_isnan( pm->gent->client->renderInfo.footRPoint[i] ) )
7218 {
7219 if ( pDiff != NULL )
7220 {
7221 *pDiff = 0;
7222 }
7223 if ( pInterval != NULL )
7224 {
7225 *pInterval = interval;
7226 }
7227 return;
7228 }
7229 }
7230 #else
7231
7232 //FIXME: these really should have been gotten on the cgame, but I guess sometimes they're not and we end up with qnan numbers!
7233 mdxaBone_t boltMatrix;
7234 vec3_t G2Angles = {0, pm->gent->client->ps.legsYaw, 0};
7235 //get the feet
7236 gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footLBolt,
7237 &boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time),
7238 NULL, pm->gent->s.modelScale );
7239 gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footLPoint );
7240
7241 gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footRBolt,
7242 &boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time),
7243 NULL, pm->gent->s.modelScale );
7244 gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footRPoint );
7245 #endif
7246 //NOTE: on AT-STs, rotating the foot moves this point, so it will wiggle...
7247 // we have to do this extra work (more G2 transforms) to stop the wiggle... is it worth it?
7248 /*
7249 if ( pm->gent->client->NPC_class == CLASS_ATST )
7250 {
7251 mdxaBone_t boltMatrix;
7252 vec3_t G2Angles = {0, pm->gent->client->ps.legsYaw, 0};
7253 //get the feet
7254 gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footLBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL );
7255 gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footLBolt,
7256 &boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time),
7257 NULL, pm->gent->s.modelScale );
7258 gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footLPoint );
7259
7260 gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footRBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL );
7261 gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footRBolt,
7262 &boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time),
7263 NULL, pm->gent->s.modelScale );
7264 gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footRPoint );
7265 }
7266 */
7267 //get these on the cgame and store it, save ourselves a ghoul2 construct skel call
7268 VectorCopy( pm->gent->client->renderInfo.footLPoint, footLOrg );
7269 VectorCopy( pm->gent->client->renderInfo.footRPoint, footROrg );
7270
7271 //step 2: adjust foot tag z height to bottom of bbox+1
7272 footLOrg[2] = pm->gent->currentOrigin[2] + pm->gent->mins[2] + 1;
7273 footROrg[2] = pm->gent->currentOrigin[2] + pm->gent->mins[2] + 1;
7274 VectorSet( footLBot, footLOrg[0], footLOrg[1], footLOrg[2] - interval*10 );
7275 VectorSet( footRBot, footROrg[0], footROrg[1], footROrg[2] - interval*10 );
7276
7277 //step 3: trace down from each, find difference
7278 vec3_t footMins, footMaxs;
7279 vec3_t footLSlope, footRSlope;
7280 if ( pm->gent->client->NPC_class == CLASS_ATST )
7281 {
7282 VectorSet( footMins, -16, -16, 0 );
7283 VectorSet( footMaxs, 16, 16, 1 );
7284 }
7285 else
7286 {
7287 VectorSet( footMins, -3, -3, 0 );
7288 VectorSet( footMaxs, 3, 3, 1 );
7289 }
7290
7291 pm->trace( &trace, footLOrg, footMins, footMaxs, footLBot, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 );
7292 VectorCopy( trace.endpos, footLBot );
7293 VectorCopy( trace.plane.normal, footLSlope );
7294
7295 pm->trace( &trace, footROrg, footMins, footMaxs, footRBot, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 );
7296 VectorCopy( trace.endpos, footRBot );
7297 VectorCopy( trace.plane.normal, footRSlope );
7298
7299 diff = footLBot[2] - footRBot[2];
7300
7301 //optional step: for atst, tilt the footpads to match the slopes under it...
7302 if ( pm->gent->client->NPC_class == CLASS_ATST )
7303 {
7304 vec3_t footAngles;
7305 if ( !VectorCompare( footLSlope, vec3_origin ) )
7306 {//rotate the ATST's left foot pad to match the slope
7307 PM_AnglesForSlope( pm->gent->client->renderInfo.legsYaw, footLSlope, footAngles );
7308 //Hmm... lerp this?
7309 gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footLBone, footAngles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 );
7310 }
7311 if ( !VectorCompare( footRSlope, vec3_origin ) )
7312 {//rotate the ATST's right foot pad to match the slope
7313 PM_AnglesForSlope( pm->gent->client->renderInfo.legsYaw, footRSlope, footAngles );
7314 //Hmm... lerp this?
7315 gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footRBone, footAngles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 );
7316 }
7317 }
7318
7319 if ( pDiff != NULL )
7320 {
7321 *pDiff = diff;
7322 }
7323 if ( pInterval != NULL )
7324 {
7325 *pInterval = interval;
7326 }
7327 }
7328
PM_InSlopeAnim(int anim)7329 qboolean PM_InSlopeAnim( int anim )
7330 {
7331 switch ( anim )
7332 {
7333 case LEGS_LEFTUP1: //# On a slope with left foot 4 higher than right
7334 case LEGS_LEFTUP2: //# On a slope with left foot 8 higher than right
7335 case LEGS_LEFTUP3: //# On a slope with left foot 12 higher than right
7336 case LEGS_LEFTUP4: //# On a slope with left foot 16 higher than right
7337 case LEGS_LEFTUP5: //# On a slope with left foot 20 higher than right
7338 case LEGS_RIGHTUP1: //# On a slope with RIGHT foot 4 higher than left
7339 case LEGS_RIGHTUP2: //# On a slope with RIGHT foot 8 higher than left
7340 case LEGS_RIGHTUP3: //# On a slope with RIGHT foot 12 higher than left
7341 case LEGS_RIGHTUP4: //# On a slope with RIGHT foot 16 higher than left
7342 case LEGS_RIGHTUP5: //# On a slope with RIGHT foot 20 higher than left
7343 case LEGS_S1_LUP1:
7344 case LEGS_S1_LUP2:
7345 case LEGS_S1_LUP3:
7346 case LEGS_S1_LUP4:
7347 case LEGS_S1_LUP5:
7348 case LEGS_S1_RUP1:
7349 case LEGS_S1_RUP2:
7350 case LEGS_S1_RUP3:
7351 case LEGS_S1_RUP4:
7352 case LEGS_S1_RUP5:
7353 case LEGS_S3_LUP1:
7354 case LEGS_S3_LUP2:
7355 case LEGS_S3_LUP3:
7356 case LEGS_S3_LUP4:
7357 case LEGS_S3_LUP5:
7358 case LEGS_S3_RUP1:
7359 case LEGS_S3_RUP2:
7360 case LEGS_S3_RUP3:
7361 case LEGS_S3_RUP4:
7362 case LEGS_S3_RUP5:
7363 case LEGS_S4_LUP1:
7364 case LEGS_S4_LUP2:
7365 case LEGS_S4_LUP3:
7366 case LEGS_S4_LUP4:
7367 case LEGS_S4_LUP5:
7368 case LEGS_S4_RUP1:
7369 case LEGS_S4_RUP2:
7370 case LEGS_S4_RUP3:
7371 case LEGS_S4_RUP4:
7372 case LEGS_S4_RUP5:
7373 case LEGS_S5_LUP1:
7374 case LEGS_S5_LUP2:
7375 case LEGS_S5_LUP3:
7376 case LEGS_S5_LUP4:
7377 case LEGS_S5_LUP5:
7378 case LEGS_S5_RUP1:
7379 case LEGS_S5_RUP2:
7380 case LEGS_S5_RUP3:
7381 case LEGS_S5_RUP4:
7382 case LEGS_S5_RUP5:
7383 case LEGS_S6_LUP1:
7384 case LEGS_S6_LUP2:
7385 case LEGS_S6_LUP3:
7386 case LEGS_S6_LUP4:
7387 case LEGS_S6_LUP5:
7388 case LEGS_S6_RUP1:
7389 case LEGS_S6_RUP2:
7390 case LEGS_S6_RUP3:
7391 case LEGS_S6_RUP4:
7392 case LEGS_S6_RUP5:
7393 case LEGS_S7_LUP1:
7394 case LEGS_S7_LUP2:
7395 case LEGS_S7_LUP3:
7396 case LEGS_S7_LUP4:
7397 case LEGS_S7_LUP5:
7398 case LEGS_S7_RUP1:
7399 case LEGS_S7_RUP2:
7400 case LEGS_S7_RUP3:
7401 case LEGS_S7_RUP4:
7402 case LEGS_S7_RUP5:
7403 return qtrue;
7404 break;
7405 }
7406 return qfalse;
7407 }
7408
PM_SaberStanceAnim(int anim)7409 qboolean PM_SaberStanceAnim( int anim )
7410 {
7411 switch ( anim )
7412 {
7413 case BOTH_STAND1://not really a saberstance anim, actually... "saber off" stance
7414 case BOTH_STAND2://single-saber, medium style
7415 case BOTH_SABERFAST_STANCE://single-saber, fast style
7416 case BOTH_SABERSLOW_STANCE://single-saber, strong style
7417 case BOTH_SABERSTAFF_STANCE://saber staff style
7418 case BOTH_SABERDUAL_STANCE://dual saber style
7419 return qtrue;
7420 break;
7421 }
7422 return qfalse;
7423 }
7424
PM_SaberDrawPutawayAnim(int anim)7425 qboolean PM_SaberDrawPutawayAnim( int anim )
7426 {
7427 switch ( anim )
7428 {
7429 case BOTH_STAND1TO2:
7430 case BOTH_STAND2TO1:
7431 case BOTH_S1_S7:
7432 case BOTH_S7_S1:
7433 case BOTH_S1_S6:
7434 case BOTH_S6_S1:
7435 return qtrue;
7436 break;
7437 }
7438 return qfalse;
7439 }
7440
7441 #define SLOPE_RECALC_INT 100
7442 extern qboolean G_StandardHumanoid( gentity_t *self );
PM_AdjustStandAnimForSlope(void)7443 qboolean PM_AdjustStandAnimForSlope( void )
7444 {
7445 if ( !pm->gent || !pm->gent->client )
7446 {
7447 return qfalse;
7448 }
7449 if ( pm->gent->client->NPC_class != CLASS_ATST
7450 && (!pm->gent||!G_StandardHumanoid( pm->gent )) )
7451 {//only ATST and player does this
7452 return qfalse;
7453 }
7454 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
7455 && (!cg.renderingThirdPerson || cg.zoomMode) )
7456 {//first person doesn't do this
7457 return qfalse;
7458 }
7459 if ( pm->gent->footLBolt == -1 || pm->gent->footRBolt == -1 )
7460 {//need these bolts!
7461 return qfalse;
7462 }
7463 //step 1: find the 2 foot tags
7464 float diff;
7465 float interval;
7466 PM_FootSlopeTrace( &diff, &interval );
7467
7468 //step 4: based on difference, choose one of the left/right slope-match intervals
7469 int destAnim;
7470 if ( diff >= interval*5 )
7471 {
7472 destAnim = LEGS_LEFTUP5;
7473 }
7474 else if ( diff >= interval*4 )
7475 {
7476 destAnim = LEGS_LEFTUP4;
7477 }
7478 else if ( diff >= interval*3 )
7479 {
7480 destAnim = LEGS_LEFTUP3;
7481 }
7482 else if ( diff >= interval*2 )
7483 {
7484 destAnim = LEGS_LEFTUP2;
7485 }
7486 else if ( diff >= interval )
7487 {
7488 destAnim = LEGS_LEFTUP1;
7489 }
7490 else if ( diff <= interval*-5 )
7491 {
7492 destAnim = LEGS_RIGHTUP5;
7493 }
7494 else if ( diff <= interval*-4 )
7495 {
7496 destAnim = LEGS_RIGHTUP4;
7497 }
7498 else if ( diff <= interval*-3 )
7499 {
7500 destAnim = LEGS_RIGHTUP3;
7501 }
7502 else if ( diff <= interval*-2 )
7503 {
7504 destAnim = LEGS_RIGHTUP2;
7505 }
7506 else if ( diff <= interval*-1 )
7507 {
7508 destAnim = LEGS_RIGHTUP1;
7509 }
7510 else
7511 {
7512 return qfalse;
7513 }
7514
7515 int legsAnim = pm->ps->legsAnim;
7516 if ( pm->gent->client->NPC_class != CLASS_ATST )
7517 {
7518 //adjust for current legs anim
7519 switch ( legsAnim )
7520 {
7521 case BOTH_STAND1:
7522 case LEGS_S1_LUP1:
7523 case LEGS_S1_LUP2:
7524 case LEGS_S1_LUP3:
7525 case LEGS_S1_LUP4:
7526 case LEGS_S1_LUP5:
7527 case LEGS_S1_RUP1:
7528 case LEGS_S1_RUP2:
7529 case LEGS_S1_RUP3:
7530 case LEGS_S1_RUP4:
7531 case LEGS_S1_RUP5:
7532 destAnim = LEGS_S1_LUP1 + (destAnim-LEGS_LEFTUP1);
7533 break;
7534 case BOTH_STAND2:
7535 case BOTH_SABERFAST_STANCE:
7536 case BOTH_SABERSLOW_STANCE:
7537 case BOTH_CROUCH1IDLE:
7538 case BOTH_CROUCH1:
7539 case LEGS_LEFTUP1: //# On a slope with left foot 4 higher than right
7540 case LEGS_LEFTUP2: //# On a slope with left foot 8 higher than right
7541 case LEGS_LEFTUP3: //# On a slope with left foot 12 higher than right
7542 case LEGS_LEFTUP4: //# On a slope with left foot 16 higher than right
7543 case LEGS_LEFTUP5: //# On a slope with left foot 20 higher than right
7544 case LEGS_RIGHTUP1: //# On a slope with RIGHT foot 4 higher than left
7545 case LEGS_RIGHTUP2: //# On a slope with RIGHT foot 8 higher than left
7546 case LEGS_RIGHTUP3: //# On a slope with RIGHT foot 12 higher than left
7547 case LEGS_RIGHTUP4: //# On a slope with RIGHT foot 16 higher than left
7548 case LEGS_RIGHTUP5: //# On a slope with RIGHT foot 20 higher than left
7549 //fine
7550 break;
7551 case BOTH_STAND3:
7552 case LEGS_S3_LUP1:
7553 case LEGS_S3_LUP2:
7554 case LEGS_S3_LUP3:
7555 case LEGS_S3_LUP4:
7556 case LEGS_S3_LUP5:
7557 case LEGS_S3_RUP1:
7558 case LEGS_S3_RUP2:
7559 case LEGS_S3_RUP3:
7560 case LEGS_S3_RUP4:
7561 case LEGS_S3_RUP5:
7562 destAnim = LEGS_S3_LUP1 + (destAnim-LEGS_LEFTUP1);
7563 break;
7564 case BOTH_STAND4:
7565 case LEGS_S4_LUP1:
7566 case LEGS_S4_LUP2:
7567 case LEGS_S4_LUP3:
7568 case LEGS_S4_LUP4:
7569 case LEGS_S4_LUP5:
7570 case LEGS_S4_RUP1:
7571 case LEGS_S4_RUP2:
7572 case LEGS_S4_RUP3:
7573 case LEGS_S4_RUP4:
7574 case LEGS_S4_RUP5:
7575 destAnim = LEGS_S4_LUP1 + (destAnim-LEGS_LEFTUP1);
7576 break;
7577 case BOTH_STAND5:
7578 case LEGS_S5_LUP1:
7579 case LEGS_S5_LUP2:
7580 case LEGS_S5_LUP3:
7581 case LEGS_S5_LUP4:
7582 case LEGS_S5_LUP5:
7583 case LEGS_S5_RUP1:
7584 case LEGS_S5_RUP2:
7585 case LEGS_S5_RUP3:
7586 case LEGS_S5_RUP4:
7587 case LEGS_S5_RUP5:
7588 destAnim = LEGS_S5_LUP1 + (destAnim-LEGS_LEFTUP1);
7589 break;
7590 case BOTH_SABERDUAL_STANCE:
7591 case LEGS_S6_LUP1:
7592 case LEGS_S6_LUP2:
7593 case LEGS_S6_LUP3:
7594 case LEGS_S6_LUP4:
7595 case LEGS_S6_LUP5:
7596 case LEGS_S6_RUP1:
7597 case LEGS_S6_RUP2:
7598 case LEGS_S6_RUP3:
7599 case LEGS_S6_RUP4:
7600 case LEGS_S6_RUP5:
7601 destAnim = LEGS_S6_LUP1 + (destAnim-LEGS_LEFTUP1);
7602 break;
7603 case BOTH_SABERSTAFF_STANCE:
7604 case LEGS_S7_LUP1:
7605 case LEGS_S7_LUP2:
7606 case LEGS_S7_LUP3:
7607 case LEGS_S7_LUP4:
7608 case LEGS_S7_LUP5:
7609 case LEGS_S7_RUP1:
7610 case LEGS_S7_RUP2:
7611 case LEGS_S7_RUP3:
7612 case LEGS_S7_RUP4:
7613 case LEGS_S7_RUP5:
7614 destAnim = LEGS_S7_LUP1 + (destAnim-LEGS_LEFTUP1);
7615 break;
7616 case BOTH_STAND6:
7617 default:
7618 return qfalse;
7619 break;
7620 }
7621 }
7622 //step 5: based on the chosen interval and the current legsAnim, pick the correct anim
7623 //step 6: increment/decrement to the dest anim, not instant
7624 if ( (legsAnim >= LEGS_LEFTUP1 && legsAnim <= LEGS_LEFTUP5)
7625 || (legsAnim >= LEGS_S1_LUP1 && legsAnim <= LEGS_S1_LUP5)
7626 || (legsAnim >= LEGS_S3_LUP1 && legsAnim <= LEGS_S3_LUP5)
7627 || (legsAnim >= LEGS_S4_LUP1 && legsAnim <= LEGS_S4_LUP5)
7628 || (legsAnim >= LEGS_S5_LUP1 && legsAnim <= LEGS_S5_LUP5)
7629 || (legsAnim >= LEGS_S6_LUP1 && legsAnim <= LEGS_S6_LUP5)
7630 || (legsAnim >= LEGS_S7_LUP1 && legsAnim <= LEGS_S7_LUP5) )
7631 {//already in left-side up
7632 if ( destAnim > legsAnim && pm->gent->client->slopeRecalcTime < level.time )
7633 {
7634 legsAnim++;
7635 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7636 }
7637 else if ( destAnim < legsAnim && pm->gent->client->slopeRecalcTime < level.time )
7638 {
7639 legsAnim--;
7640 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7641 }
7642 else
7643 {
7644 destAnim = legsAnim;
7645 }
7646 }
7647 else if ( (legsAnim >= LEGS_RIGHTUP1 && legsAnim <= LEGS_RIGHTUP5)
7648 || (legsAnim >= LEGS_S1_RUP1 && legsAnim <= LEGS_S1_RUP5)
7649 || (legsAnim >= LEGS_S3_RUP1 && legsAnim <= LEGS_S3_RUP5)
7650 || (legsAnim >= LEGS_S4_RUP1 && legsAnim <= LEGS_S4_RUP5)
7651 || (legsAnim >= LEGS_S5_RUP1 && legsAnim <= LEGS_S5_RUP5)
7652 || (legsAnim >= LEGS_S6_RUP1 && legsAnim <= LEGS_S6_RUP5)
7653 || (legsAnim >= LEGS_S7_RUP1 && legsAnim <= LEGS_S7_RUP5) )
7654 {//already in right-side up
7655 if ( destAnim > legsAnim && pm->gent->client->slopeRecalcTime < level.time )
7656 {
7657 legsAnim++;
7658 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7659 }
7660 else if ( destAnim < legsAnim && pm->gent->client->slopeRecalcTime < level.time )
7661 {
7662 legsAnim--;
7663 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7664 }
7665 else
7666 {
7667 destAnim = legsAnim;
7668 }
7669 }
7670 else
7671 {//in a stand of some sort?
7672 if ( pm->gent->client->NPC_class == CLASS_ATST )
7673 {
7674 if ( legsAnim == BOTH_STAND1 || legsAnim == BOTH_STAND2 || legsAnim == BOTH_CROUCH1IDLE )
7675 {
7676 if ( destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5 )
7677 {//going into left side up
7678 destAnim = LEGS_LEFTUP1;
7679 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7680 }
7681 else if ( destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5 )
7682 {//going into right side up
7683 destAnim = LEGS_RIGHTUP1;
7684 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7685 }
7686 else
7687 {//will never get here
7688 return qfalse;
7689 }
7690 }
7691 }
7692 else
7693 {
7694 switch ( legsAnim )
7695 {
7696 case BOTH_STAND1:
7697 if ( destAnim >= LEGS_S1_LUP1 && destAnim <= LEGS_S1_LUP5 )
7698 {//going into left side up
7699 destAnim = LEGS_S1_LUP1;
7700 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7701 }
7702 else if ( destAnim >= LEGS_S1_RUP1 && destAnim <= LEGS_S1_RUP5 )
7703 {//going into right side up
7704 destAnim = LEGS_S1_RUP1;
7705 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7706 }
7707 else
7708 {//will never get here
7709 return qfalse;
7710 }
7711 break;
7712 case BOTH_STAND2:
7713 case BOTH_SABERFAST_STANCE:
7714 case BOTH_SABERSLOW_STANCE:
7715 case BOTH_CROUCH1IDLE:
7716 if ( destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5 )
7717 {//going into left side up
7718 destAnim = LEGS_LEFTUP1;
7719 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7720 }
7721 else if ( destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5 )
7722 {//going into right side up
7723 destAnim = LEGS_RIGHTUP1;
7724 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7725 }
7726 else
7727 {//will never get here
7728 return qfalse;
7729 }
7730 break;
7731 case BOTH_STAND3:
7732 if ( destAnim >= LEGS_S3_LUP1 && destAnim <= LEGS_S3_LUP5 )
7733 {//going into left side up
7734 destAnim = LEGS_S3_LUP1;
7735 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7736 }
7737 else if ( destAnim >= LEGS_S3_RUP1 && destAnim <= LEGS_S3_RUP5 )
7738 {//going into right side up
7739 destAnim = LEGS_S3_RUP1;
7740 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7741 }
7742 else
7743 {//will never get here
7744 return qfalse;
7745 }
7746 break;
7747 case BOTH_STAND4:
7748 if ( destAnim >= LEGS_S4_LUP1 && destAnim <= LEGS_S4_LUP5 )
7749 {//going into left side up
7750 destAnim = LEGS_S4_LUP1;
7751 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7752 }
7753 else if ( destAnim >= LEGS_S4_RUP1 && destAnim <= LEGS_S4_RUP5 )
7754 {//going into right side up
7755 destAnim = LEGS_S4_RUP1;
7756 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7757 }
7758 else
7759 {//will never get here
7760 return qfalse;
7761 }
7762 break;
7763 case BOTH_STAND5:
7764 if ( destAnim >= LEGS_S5_LUP1 && destAnim <= LEGS_S5_LUP5 )
7765 {//going into left side up
7766 destAnim = LEGS_S5_LUP1;
7767 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7768 }
7769 else if ( destAnim >= LEGS_S5_RUP1 && destAnim <= LEGS_S5_RUP5 )
7770 {//going into right side up
7771 destAnim = LEGS_S5_RUP1;
7772 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7773 }
7774 else
7775 {//will never get here
7776 return qfalse;
7777 }
7778 break;
7779 case BOTH_SABERDUAL_STANCE:
7780 if ( destAnim >= LEGS_S6_LUP1 && destAnim <= LEGS_S6_LUP5 )
7781 {//going into left side up
7782 destAnim = LEGS_S6_LUP1;
7783 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7784 }
7785 else if ( destAnim >= LEGS_S6_RUP1 && destAnim <= LEGS_S6_RUP5 )
7786 {//going into right side up
7787 destAnim = LEGS_S6_RUP1;
7788 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7789 }
7790 else
7791 {//will never get here
7792 return qfalse;
7793 }
7794 break;
7795 case BOTH_SABERSTAFF_STANCE:
7796 if ( destAnim >= LEGS_S7_LUP1 && destAnim <= LEGS_S7_LUP5 )
7797 {//going into left side up
7798 destAnim = LEGS_S7_LUP1;
7799 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7800 }
7801 else if ( destAnim >= LEGS_S7_RUP1 && destAnim <= LEGS_S7_RUP5 )
7802 {//going into right side up
7803 destAnim = LEGS_S7_RUP1;
7804 pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7805 }
7806 else
7807 {//will never get here
7808 return qfalse;
7809 }
7810 break;
7811 case BOTH_STAND6:
7812 default:
7813 return qfalse;
7814 break;
7815 }
7816 }
7817 }
7818 //step 7: set the anim
7819 PM_SetAnim( pm, SETANIM_LEGS, destAnim, SETANIM_FLAG_NORMAL );
7820
7821 return qtrue;
7822 }
7823
PM_JetPackAnim(void)7824 void PM_JetPackAnim( void )
7825 {
7826 if ( !PM_ForceJumpingAnim( pm->ps->legsAnim ) )//haven't started forcejump yet
7827 {
7828 vec3_t facingFwd, facingRight, facingAngles = {0, pm->ps->viewangles[YAW], 0};
7829 int anim = BOTH_FORCEJUMP1;
7830 AngleVectors( facingAngles, facingFwd, facingRight, NULL );
7831 float dotR = DotProduct( facingRight, pm->ps->velocity );
7832 float dotF = DotProduct( facingFwd, pm->ps->velocity );
7833 if ( fabs(dotR) > fabs(dotF) * 1.5 )
7834 {
7835 if ( dotR > 150 )
7836 {
7837 anim = BOTH_FORCEJUMPRIGHT1;
7838 }
7839 else if ( dotR < -150 )
7840 {
7841 anim = BOTH_FORCEJUMPLEFT1;
7842 }
7843 }
7844 else
7845 {
7846 if ( dotF > 150 )
7847 {
7848 anim = BOTH_FORCEJUMP1;
7849 }
7850 else if ( dotF < -150 )
7851 {
7852 anim = BOTH_FORCEJUMPBACK1;
7853 }
7854 }
7855 int parts = SETANIM_BOTH;
7856 if ( pm->ps->weaponTime )
7857 {//FIXME: really only care if we're in a saber attack anim...
7858 parts = SETANIM_LEGS;
7859 }
7860
7861 PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
7862 }
7863 /*
7864 else
7865 {
7866 if ( !pm->ps->legsAnimTimer )
7867 {//not in the middle of a legsAnim
7868 int anim = pm->ps->legsAnim;
7869 int newAnim = -1;
7870 switch ( anim )
7871 {
7872 case BOTH_FORCEJUMP1:
7873 newAnim = BOTH_FORCELAND1;//BOTH_FORCEINAIR1;
7874 break;
7875 case BOTH_FORCEJUMPBACK1:
7876 newAnim = BOTH_FORCELANDBACK1;//BOTH_FORCEINAIRBACK1;
7877 break;
7878 case BOTH_FORCEJUMPLEFT1:
7879 newAnim = BOTH_FORCELANDLEFT1;//BOTH_FORCEINAIRLEFT1;
7880 break;
7881 case BOTH_FORCEJUMPRIGHT1:
7882 newAnim = BOTH_FORCELANDRIGHT1;//BOTH_FORCEINAIRRIGHT1;
7883 break;
7884 }
7885 if ( newAnim != -1 )
7886 {
7887 int parts = SETANIM_BOTH;
7888 if ( pm->ps->weaponTime )
7889 {//FIXME: really only care if we're in a saber attack anim...
7890 parts = SETANIM_LEGS;
7891 }
7892
7893 PM_SetAnim( pm, parts, newAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
7894 }
7895 }
7896 }
7897 */
7898 }
PM_SwimFloatAnim(void)7899 void PM_SwimFloatAnim( void )
7900 {
7901 int legsAnim = pm->ps->legsAnim;
7902 //FIXME: no start or stop anims
7903 if ( pm->cmd.forwardmove || pm->cmd.rightmove || pm->cmd.upmove )
7904 {
7905 PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL);
7906 }
7907 else
7908 {//stopping
7909 if ( legsAnim == BOTH_SWIMFORWARD )
7910 {//I was swimming
7911 if ( !pm->ps->legsAnimTimer )
7912 {
7913 PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL);
7914 }
7915 }
7916 else
7917 {//idle
7918 if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 )
7919 {//not crouching
7920 PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL);
7921 }
7922 }
7923 }
7924 }
7925
7926 /*
7927 ===============
7928 PM_Footsteps
7929 ===============
7930 */
PM_Footsteps(void)7931 static void PM_Footsteps( void )
7932 {
7933 float bobmove;
7934 int old, oldAnim;
7935 qboolean footstep = qfalse;
7936 qboolean validNPC = qfalse;
7937 qboolean flipping = qfalse;
7938 int setAnimFlags = SETANIM_FLAG_NORMAL;
7939
7940 if( pm->gent == NULL || pm->gent->client == NULL )
7941 return;
7942
7943 if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) )
7944 {
7945 PM_SetAnim( pm, SETANIM_BOTH, BOTH_HANG_IDLE, SETANIM_FLAG_NORMAL );
7946 if ( pm->ps->legsAnim == BOTH_HANG_IDLE )
7947 {
7948 if ( pm->ps->torsoAnimTimer < 100 )
7949 {
7950 pm->ps->torsoAnimTimer = 100;
7951 }
7952 if ( pm->ps->legsAnimTimer < 100 )
7953 {
7954 pm->ps->legsAnimTimer = 100;
7955 }
7956 }
7957 return;
7958 }
7959
7960 if ( PM_SpinningSaberAnim( pm->ps->legsAnim ) && pm->ps->legsAnimTimer )
7961 {//spinning
7962 return;
7963 }
7964 if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps ))
7965 {//in knockdown
7966 return;
7967 }
7968 if ( (pm->ps->eFlags&EF_FORCE_DRAINED) )
7969 {//being drained
7970 //PM_SetAnim( pm, SETANIM_LEGS, BOTH_HUGGEE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
7971 return;
7972 }
7973 if ( (pm->ps->forcePowersActive&(1<<FP_DRAIN))
7974 && pm->ps->forceDrainEntityNum < ENTITYNUM_WORLD )
7975 {//draining
7976 //PM_SetAnim( pm, SETANIM_LEGS, BOTH_HUGGER1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
7977 return;
7978 }
7979 else if (pm->gent->NPC && pm->gent->NPC->aiFlags & NPCAI_KNEEL)
7980 {//kneeling
7981 return;
7982 }
7983
7984 if( pm->gent->NPC != NULL )
7985 {
7986 validNPC = qtrue;
7987 }
7988
7989 pm->gent->client->renderInfo.legsFpsMod = 1.0f;
7990 //PM_ResetAnkleAngles();
7991
7992 //
7993 // calculate speed and cycle to be used for
7994 // all cyclic walking effects
7995 //
7996 pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0]
7997 + pm->ps->velocity[1] * pm->ps->velocity[1] );
7998
7999 if ( pm->ps->legsAnim == BOTH_FLIP_F ||
8000 pm->ps->legsAnim == BOTH_FLIP_B ||
8001 pm->ps->legsAnim == BOTH_FLIP_L ||
8002 pm->ps->legsAnim == BOTH_FLIP_R ||
8003 pm->ps->legsAnim == BOTH_ALORA_FLIP_1 ||
8004 pm->ps->legsAnim == BOTH_ALORA_FLIP_2 ||
8005 pm->ps->legsAnim == BOTH_ALORA_FLIP_3 )
8006 {
8007 flipping = qtrue;
8008 }
8009
8010 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
8011 || ( pm->watertype & CONTENTS_LADDER )
8012 || pm->ps->waterHeightLevel >= WHL_TORSO )
8013 {//in air or submerged in water or in ladder
8014 // airborne leaves position in cycle intact, but doesn't advance
8015 if ( pm->waterlevel > 0 )
8016 {
8017 if ( pm->watertype & CONTENTS_LADDER )
8018 {//FIXME: check for watertype, save waterlevel for whether to play
8019 //the get off ladder transition anim!
8020 if ( pm->ps->velocity[2] )
8021 {//going up or down it
8022 int anim;
8023 if ( pm->ps->velocity[2] > 0 )
8024 {
8025 anim = BOTH_LADDER_UP1;
8026 }
8027 else
8028 {
8029 anim = BOTH_LADDER_DWN1;
8030 }
8031 PM_SetAnim( pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
8032 if ( pm->waterlevel >= 2 ) //arms on ladder
8033 {
8034 PM_SetAnim( pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
8035 }
8036 if (fabs(pm->ps->velocity[2]) >5) {
8037 bobmove = 0.005 * fabs(pm->ps->velocity[2]); // climbing bobs slow
8038 if (bobmove > 0.3)
8039 bobmove = 0.3F;
8040 goto DoFootSteps;
8041 }
8042 }
8043 else
8044 {
8045 PM_SetAnim( pm, SETANIM_LEGS, BOTH_LADDER_IDLE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
8046 pm->ps->legsAnimTimer += 300;
8047 if ( pm->waterlevel >= 2 ) //arms on ladder
8048 {
8049 PM_SetAnim( pm, SETANIM_TORSO, BOTH_LADDER_IDLE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
8050 pm->ps->torsoAnimTimer += 300;
8051 }
8052 }
8053 return;
8054 }
8055 else if ( pm->ps->waterHeightLevel >= WHL_TORSO
8056 && ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
8057 ||pm->ps->weapon==WP_SABER||pm->ps->weapon==WP_NONE||pm->ps->weapon==WP_MELEE) )//pm->waterlevel > 1 ) //in deep water
8058 {
8059 if ( !PM_ForceJumpingUp( pm->gent ) )
8060 {
8061 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE && (pm->ps->pm_flags&PMF_DUCKED) )
8062 {
8063 if ( !flipping )
8064 {//you can crouch under water if feet are on ground
8065 if ( pm->cmd.forwardmove || pm->cmd.rightmove )
8066 {
8067 if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
8068 {
8069 PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALKBACK,setAnimFlags);
8070 }
8071 else
8072 {
8073 PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALK,setAnimFlags);
8074 }
8075 }
8076 else
8077 {
8078 PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1,SETANIM_FLAG_NORMAL);
8079 }
8080 return;
8081 }
8082 }
8083 PM_SwimFloatAnim();
8084 if ( pm->ps->legsAnim != BOTH_SWIM_IDLE1 )
8085 {//moving
8086 old = pm->ps->bobCycle;
8087 bobmove = 0.15f; // swim is a slow cycle
8088 pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
8089
8090 // if we just crossed a cycle boundary, play an apropriate footstep event
8091 if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 )
8092 {
8093 PM_AddEvent( EV_SWIM );
8094 }
8095 }
8096 }
8097 return;
8098 }
8099 else
8100 {//hmm, in water, but not high enough to swim
8101 //fall through to walk/run/stand
8102 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
8103 {//unless in the air
8104 //NOTE: this is a dupe of the code just below... for when you are not in the water at all
8105 if ( pm->ps->pm_flags & PMF_DUCKED )
8106 {
8107 if ( !flipping )
8108 {
8109 PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1,SETANIM_FLAG_NORMAL);
8110 }
8111 }
8112 else if ( pm->ps->gravity <= 0 )//FIXME: or just less than normal?
8113 {
8114 if ( pm->gent
8115 && pm->gent->client
8116 && (pm->gent->client->NPC_class == CLASS_BOBAFETT ||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER)
8117 && pm->gent->client->moveType == MT_FLYSWIM )
8118 {//flying around with jetpack
8119 //do something else?
8120 PM_JetPackAnim();
8121 }
8122 else
8123 {
8124 PM_SwimFloatAnim();
8125 }
8126 }
8127 return;
8128 }
8129 }
8130 }
8131 else
8132 {
8133 if ( pm->ps->pm_flags & PMF_DUCKED )
8134 {
8135 if ( !flipping )
8136 {
8137 PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1,SETANIM_FLAG_NORMAL);
8138 }
8139 }
8140 else if ( pm->ps->gravity <= 0 )//FIXME: or just less than normal?
8141 {
8142 if ( pm->gent
8143 && pm->gent->client
8144 && (pm->gent->client->NPC_class == CLASS_BOBAFETT||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER)
8145 && pm->gent->client->moveType == MT_FLYSWIM )
8146 {//flying around with jetpack
8147 //do something else?
8148 PM_JetPackAnim();
8149 }
8150 else
8151 {
8152 PM_SwimFloatAnim();
8153 }
8154 }
8155 return;
8156 }
8157 }
8158
8159 if ( PM_SwimmingAnim( pm->ps->legsAnim ) && pm->waterlevel < 2 )
8160 {//legs are in swim anim, and not swimming, be sure to override it
8161 setAnimFlags |= SETANIM_FLAG_OVERRIDE;
8162 }
8163
8164 // if not trying to move
8165 if ( !pm->cmd.forwardmove && !pm->cmd.rightmove )
8166 {
8167 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST )
8168 {
8169 if ( !PM_AdjustStandAnimForSlope() )
8170 {
8171 PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
8172 }
8173 }
8174 else if ( pm->ps->pm_flags & PMF_DUCKED )
8175 {
8176 if( !PM_InOnGroundAnim( pm->ps ) )
8177 {
8178 if ( !PM_AdjustStandAnimForSlope() )
8179 {
8180 PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1,SETANIM_FLAG_NORMAL);
8181 }
8182 }
8183 }
8184 else
8185 {
8186 if ( pm->ps->legsAnimTimer && PM_LandingAnim( pm->ps->legsAnim ) )
8187 {//still in a landing anim, let it play
8188 return;
8189 }
8190 if ( pm->ps->legsAnimTimer
8191 && (pm->ps->legsAnim == BOTH_THERMAL_READY
8192 ||pm->ps->legsAnim == BOTH_THERMAL_THROW
8193 ||pm->ps->legsAnim == BOTH_ATTACK10) )
8194 {//still in a thermal anim, let it play
8195 return;
8196 }
8197 qboolean saberInAir = qtrue;
8198 if ( pm->ps->saberInFlight )
8199 {//guiding saber
8200 if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) )
8201 {//we're stuck in a broken parry
8202 saberInAir = qfalse;
8203 }
8204 if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0
8205 {//
8206 if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY )
8207 {//fell to the ground and we're not trying to pull it back
8208 saberInAir = qfalse;
8209 }
8210 }
8211 }
8212 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH )
8213 {//NOTE: stand1 is with the helmet retracted, stand1to2 is the helmet going into place
8214 PM_SetAnim( pm, SETANIM_BOTH, BOTH_STAND2, SETANIM_FLAG_NORMAL );
8215 }
8216 else if ( pm->ps->weapon == WP_SABER
8217 && pm->ps->saberInFlight
8218 && saberInAir
8219 && (!pm->ps->dualSabers || !pm->ps->saber[1].Active()))
8220 {
8221 if ( !PM_AdjustStandAnimForSlope() )
8222 {
8223 if ( pm->ps->legsAnim != BOTH_LOSE_SABER
8224 || !pm->ps->legsAnimTimer )
8225 {
8226 PM_SetAnim(pm,SETANIM_LEGS,BOTH_SABERPULL,SETANIM_FLAG_NORMAL);
8227 }
8228 }
8229 }
8230 else if ( (pm->ps->weapon == WP_SABER
8231 &&pm->ps->SaberLength()>0
8232 &&!pm->ps->saberInFlight
8233 &&!PM_SaberDrawPutawayAnim( pm->ps->legsAnim )) )
8234 {
8235 if ( !PM_AdjustStandAnimForSlope() )
8236 {
8237 int legsAnim;
8238 if ( (pm->ps->torsoAnim == BOTH_SPINATTACK6
8239 || pm->ps->torsoAnim == BOTH_SPINATTACK7
8240 || PM_SaberInAttack( pm->ps->saberMove )
8241 || PM_SaberInTransitionAny( pm->ps->saberMove ))
8242 && pm->ps->legsAnim != BOTH_FORCELONGLEAP_LAND
8243 && (pm->ps->groundEntityNum == ENTITYNUM_NONE//in air
8244 || (!PM_JumpingAnim( pm->ps->torsoAnim )&&!PM_InAirKickingAnim( pm->ps->torsoAnim ))) )//OR: on ground and torso not in a jump anim
8245 {
8246 legsAnim = pm->ps->torsoAnim;
8247 }
8248 else
8249 {
8250 switch ( pm->ps->saberAnimLevel )
8251 {
8252 case SS_FAST:
8253 case SS_TAVION:
8254 legsAnim = BOTH_SABERFAST_STANCE;
8255 break;
8256 case SS_STRONG:
8257 legsAnim = BOTH_SABERSLOW_STANCE;
8258 break;
8259 case SS_DUAL:
8260 legsAnim = BOTH_SABERDUAL_STANCE;
8261 break;
8262 case SS_STAFF:
8263 legsAnim = BOTH_SABERSTAFF_STANCE;
8264 break;
8265 case SS_NONE:
8266 case SS_MEDIUM:
8267 case SS_DESANN:
8268 default:
8269 legsAnim = BOTH_STAND2;
8270 break;
8271 }
8272 }
8273 PM_SetAnim(pm,SETANIM_LEGS,legsAnim,SETANIM_FLAG_NORMAL);
8274 }
8275 }
8276 else if( (validNPC && pm->ps->weapon > WP_SABER && pm->ps->weapon < WP_DET_PACK ))//Being careful or carrying a 2-handed weapon
8277 {//Squadmates use BOTH_STAND3
8278 oldAnim = pm->ps->legsAnim;
8279 if(oldAnim != BOTH_GUARD_LOOKAROUND1 && oldAnim != BOTH_GUARD_IDLE1
8280 && oldAnim != BOTH_STAND2TO4
8281 && oldAnim != BOTH_STAND4TO2 && oldAnim != BOTH_STAND4 )
8282 {//Don't auto-override the guard idles
8283 if ( !PM_AdjustStandAnimForSlope() )
8284 {
8285 PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND3,SETANIM_FLAG_NORMAL);
8286 //if(oldAnim != BOTH_STAND2 && pm->ps->legsAnim == BOTH_STAND2)
8287 //{
8288 // pm->ps->legsAnimTimer = 500;
8289 //}
8290 }
8291 }
8292 }
8293 else
8294 {
8295 if ( !PM_AdjustStandAnimForSlope() )
8296 {
8297 // FIXME: Do we need this here... The imps stand is 4, not 1...
8298 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_IMPERIAL )
8299 {
8300 PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND4,SETANIM_FLAG_NORMAL);
8301 }
8302 else if ( pm->ps->weapon == WP_TUSKEN_STAFF )
8303 {
8304 PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
8305 }
8306 else
8307 {
8308 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR )
8309 {
8310 if ( pm->gent->count )
8311 {
8312 PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND4,SETANIM_FLAG_NORMAL);
8313 }
8314 else if ( pm->gent->enemy || pm->gent->wait )
8315 {//have an enemy or have had one since we spawned
8316 PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL);
8317 }
8318 else
8319 {
8320 PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
8321 }
8322 }
8323 else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_WAMPA )
8324 {
8325 if ( pm->gent->count )
8326 {//holding a victim
8327 PM_SetAnim(pm,SETANIM_LEGS,BOTH_HOLD_IDLE/*BOTH_STAND2*/,SETANIM_FLAG_NORMAL);
8328 }
8329 else
8330 {//not holding a victim
8331 PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
8332 }
8333 }
8334 else
8335 {
8336 PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
8337 }
8338 }
8339 }
8340 }
8341 }
8342 return;
8343 }
8344
8345 //maybe call this every frame, even when moving?
8346 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST )
8347 {
8348 PM_FootSlopeTrace( NULL, NULL );
8349 }
8350
8351 //trying to move laterally
8352 if ( (pm->ps->eFlags&EF_IN_ATST)
8353 || (pm->gent&&pm->gent->client&&pm->gent->client->NPC_class==CLASS_RANCOR)//does this catch NPCs, too?
8354 || (pm->gent&&pm->gent->client&&pm->gent->client->NPC_class==CLASS_WAMPA) )//does this catch NPCs, too?
8355 {//atst, Rancor & Wampa, only override turn anims on legs (no torso)
8356 if ( pm->ps->legsAnim == BOTH_TURN_LEFT1 ||
8357 pm->ps->legsAnim == BOTH_TURN_RIGHT1 )
8358 {//moving overrides turning
8359 setAnimFlags |= SETANIM_FLAG_OVERRIDE;
8360 }
8361 }
8362 else
8363 {//all other NPCs...
8364 if ( (PM_InSaberAnim( pm->ps->legsAnim ) && !PM_SpinningSaberAnim( pm->ps->legsAnim ))
8365 || PM_SaberStanceAnim( pm->ps->legsAnim )
8366 || PM_SaberDrawPutawayAnim( pm->ps->legsAnim )
8367 || pm->ps->legsAnim == BOTH_SPINATTACK6//not a full-body spin, just spinning the saber
8368 || pm->ps->legsAnim == BOTH_SPINATTACK7//not a full-body spin, just spinning the saber
8369 || pm->ps->legsAnim == BOTH_BUTTON_HOLD
8370 || pm->ps->legsAnim == BOTH_BUTTON_RELEASE
8371 || pm->ps->legsAnim == BOTH_THERMAL_READY
8372 || pm->ps->legsAnim == BOTH_THERMAL_THROW
8373 || pm->ps->legsAnim == BOTH_ATTACK10
8374 || PM_LandingAnim( pm->ps->legsAnim )
8375 || PM_PainAnim( pm->ps->legsAnim )
8376 || PM_ForceAnim( pm->ps->legsAnim ))
8377 {//legs are in a saber anim, and not spinning, be sure to override it
8378 setAnimFlags |= SETANIM_FLAG_OVERRIDE;
8379 }
8380 }
8381
8382 if ( pm->ps->pm_flags & PMF_DUCKED )
8383 {
8384 bobmove = 0.5; // ducked characters bob much faster
8385 if( !PM_InOnGroundAnim( pm->ps ) //not on the ground
8386 && ( !PM_InRollIgnoreTimer( pm->ps )||(!pm->ps->legsAnimTimer&&pm->cmd.upmove<0) ) )//not in a roll (or you just finished one and you're still holding crouch)
8387 {
8388 qboolean rolled = qfalse;
8389 if ( PM_RunningAnim( pm->ps->legsAnim )
8390 || pm->ps->legsAnim == BOTH_FORCEHEAL_START
8391 || PM_CanRollFromSoulCal( pm->ps ))
8392 {//roll!
8393 rolled = PM_TryRoll();
8394 }
8395 if ( !rolled )
8396 {
8397 if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
8398 {
8399 PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALKBACK,setAnimFlags);
8400 }
8401 else
8402 {
8403 PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALK,setAnimFlags);
8404 }
8405 if ( !Q_irand( 0, 19 ) )
8406 {//5% chance of making an alert
8407 AddSoundEvent( pm->gent, pm->ps->origin, 16, AEL_MINOR, qtrue, qtrue );
8408 }
8409 }
8410 else
8411 {//rolling is a little noisy
8412 AddSoundEvent( pm->gent, pm->ps->origin, 128, AEL_MINOR, qtrue, qtrue );
8413 }
8414 }
8415 // ducked characters never play footsteps
8416 }
8417 else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
8418 {//Moving backwards
8419 if ( !( pm->cmd.buttons & BUTTON_WALKING ) )
8420 {//running backwards
8421 bobmove = 0.4F; // faster speeds bob faster
8422 if ( pm->ps->weapon == WP_SABER && pm->ps->SaberActive() )
8423 {
8424 /*
8425 if ( pm->ps->saberAnimLevel == SS_STAFF )
8426 {
8427 PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUNBACK_STAFF,setAnimFlags);
8428 }
8429 else
8430 */
8431 {
8432 PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUNBACK2,setAnimFlags);
8433 }
8434 }
8435 else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR )
8436 {//no run anim
8437 PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK1,setAnimFlags);
8438 }
8439 else
8440 {
8441 PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUNBACK1,setAnimFlags);
8442 }
8443 footstep = qtrue;
8444 }
8445 else
8446 {//walking backwards
8447 bobmove = 0.3F; // faster speeds bob faster
8448 if ( pm->ps->weapon == WP_SABER && pm->ps->SaberActive() )
8449 {
8450 if ( pm->ps->saberAnimLevel == SS_DUAL )
8451 {
8452 PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK_DUAL,setAnimFlags);
8453 }
8454 else if ( pm->ps->saberAnimLevel == SS_STAFF )
8455 {
8456 PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK_STAFF,setAnimFlags);
8457 }
8458 else
8459 {
8460 PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK2,setAnimFlags);
8461 }
8462 }
8463 else
8464 {
8465 PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK1,setAnimFlags);
8466 }
8467 if ( !Q_irand( 0, 9 ) )
8468 {//10% chance of a small alert, mainly for the sand_creature
8469 AddSoundEvent( pm->gent, pm->ps->origin, 16, AEL_MINOR, qtrue, qtrue );
8470 }
8471 }
8472 }
8473 else
8474 {
8475 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH )
8476 {
8477 bobmove = 0.3F; // walking bobs slow
8478 if ( pm->ps->weapon == WP_NONE )
8479 {//helmet retracted
8480 PM_SetAnim( pm, SETANIM_BOTH, BOTH_WALK1, SETANIM_FLAG_NORMAL );
8481 }
8482 else
8483 {//helmet in place
8484 PM_SetAnim( pm, SETANIM_BOTH, BOTH_WALK2, SETANIM_FLAG_NORMAL );
8485 }
8486 AddSoundEvent( pm->gent, pm->ps->origin, 128, AEL_SUSPICIOUS, qtrue, qtrue );
8487 }
8488 else
8489 {
8490 if ( !( pm->cmd.buttons & BUTTON_WALKING ) )
8491 {//running
8492 bobmove = 0.4F; // faster speeds bob faster
8493 if ( pm->ps->weapon == WP_SABER && pm->ps->SaberActive() )
8494 {
8495 if ( pm->ps->saberAnimLevel == SS_DUAL )
8496 {
8497 PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN_DUAL,setAnimFlags);
8498 }
8499 else if ( pm->ps->saberAnimLevel == SS_STAFF )
8500 {
8501 PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN_STAFF,setAnimFlags);
8502 }
8503 else
8504 {
8505 PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN2,setAnimFlags);
8506 }
8507 }
8508 else
8509 {
8510 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_JAWA )
8511 {
8512 //if ( pm->gent->enemy && (pm->ps->weapon == WP_NONE || pm->ps->weapon == WP_MELEE) )
8513 {
8514 PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN4,setAnimFlags);
8515 }
8516 /*
8517 else
8518 {
8519 PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN1,setAnimFlags);
8520 }
8521 */
8522 }
8523 else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST )
8524 {
8525 if ( pm->ps->legsAnim != BOTH_RUN1 )
8526 {
8527 if ( pm->ps->legsAnim != BOTH_RUN1START )
8528 {//Hmm, he should really start slow and have to accelerate... also need to do this for stopping
8529 PM_SetAnim( pm,SETANIM_LEGS, BOTH_RUN1START, setAnimFlags|SETANIM_FLAG_HOLD );
8530 }
8531 else if ( !pm->ps->legsAnimTimer )
8532 {
8533 PM_SetAnim( pm, SETANIM_LEGS, BOTH_RUN1, setAnimFlags );
8534 }
8535 }
8536 else
8537 {
8538 PM_SetAnim( pm, SETANIM_LEGS, BOTH_RUN1, setAnimFlags );
8539 }
8540 }
8541 else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_WAMPA )
8542 {
8543 if ( pm->gent->NPC && pm->gent->NPC->stats.runSpeed == 300 )
8544 {//full on run, on all fours
8545 PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN1,SETANIM_FLAG_NORMAL);
8546 }
8547 else
8548 {//regular, upright run
8549 PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN2,SETANIM_FLAG_NORMAL);
8550 }
8551 }
8552 else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR )
8553 {//no run anim
8554 PM_SetAnim( pm, SETANIM_LEGS, BOTH_WALK1, setAnimFlags );
8555 }
8556 else
8557 {
8558 PM_SetAnim( pm, SETANIM_LEGS, BOTH_RUN1, setAnimFlags );
8559 }
8560 }
8561 footstep = qtrue;
8562 }
8563 else
8564 {//walking forward
8565 bobmove = 0.3F; // walking bobs slow
8566 if ( pm->ps->weapon == WP_SABER && pm->ps->SaberActive() )
8567 {
8568 if ( pm->ps->saberAnimLevel == SS_DUAL )
8569 {
8570 PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK_DUAL,setAnimFlags);
8571 }
8572 else if ( pm->ps->saberAnimLevel == SS_STAFF )
8573 {
8574 PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK_STAFF,setAnimFlags);
8575 }
8576 else
8577 {
8578 PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK2,setAnimFlags);
8579 }
8580 }
8581 else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_WAMPA )
8582 {
8583 if ( pm->gent->health <= 50 )
8584 {//hurt walk
8585 PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK2,SETANIM_FLAG_NORMAL);
8586 }
8587 else
8588 {//normal walk
8589 PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK1,SETANIM_FLAG_NORMAL);
8590 }
8591 }
8592 else
8593 {
8594 PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK1,setAnimFlags);
8595 }
8596 //Enemy NPCs always make footsteps for the benefit of the player
8597 if ( pm->gent
8598 && pm->gent->NPC
8599 && pm->gent->client
8600 && pm->gent->client->playerTeam != TEAM_PLAYER )
8601 {
8602 footstep = qtrue;
8603 }
8604 else if ( !Q_irand( 0, 9 ) )
8605 {//10% chance of a small alert, mainly for the sand_creature
8606 AddSoundEvent( pm->gent, pm->ps->origin, 16, AEL_MINOR, qtrue, qtrue );
8607 }
8608 }
8609 }
8610 }
8611
8612 if(pm->gent != NULL)
8613 {
8614 if( pm->gent->client->renderInfo.legsFpsMod > 2 )
8615 {
8616 pm->gent->client->renderInfo.legsFpsMod = 2;
8617 }
8618 else if(pm->gent->client->renderInfo.legsFpsMod < 0.5)
8619 {
8620 pm->gent->client->renderInfo.legsFpsMod = 0.5;
8621 }
8622 }
8623
8624 DoFootSteps:
8625
8626 // check for footstep / splash sounds
8627 old = pm->ps->bobCycle;
8628 pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
8629
8630 // if we just crossed a cycle boundary, play an apropriate footstep event
8631 if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 )
8632 {
8633 if ( pm->watertype & CONTENTS_LADDER )
8634 {
8635 if ( !pm->noFootsteps )
8636 {
8637 if (pm->ps->groundEntityNum == ENTITYNUM_NONE) {// on ladder
8638 PM_AddEvent( EV_FOOTSTEP_METAL );
8639 } else {
8640 //PM_AddEvent( PM_FootstepForSurface() ); //still on ground
8641 }
8642 }
8643 if ( pm->gent && pm->gent->s.number == 0 )
8644 {
8645 // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
8646 {
8647 AddSoundEvent( pm->gent, pm->ps->origin, 128, AEL_MINOR, qtrue );
8648 }
8649 }
8650 }
8651 else if ( pm->waterlevel == 0 )
8652 {
8653 // on ground will only play sounds if running
8654 if ( footstep )
8655 {
8656 if ( !pm->noFootsteps )
8657 {
8658 //PM_AddEvent( PM_FootstepForSurface() );
8659 }
8660 if ( pm->gent && pm->gent->s.number == 0 )
8661 {
8662 vec3_t bottom;
8663
8664 VectorCopy( pm->ps->origin, bottom );
8665 bottom[2] += pm->mins[2];
8666 // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
8667 {
8668 AddSoundEvent( pm->gent, bottom, 256, AEL_MINOR, qtrue, qtrue );
8669 }
8670 }
8671 }
8672 }
8673 else if ( pm->waterlevel == 1 )
8674 {
8675 // splashing
8676 if ( pm->ps->waterHeightLevel >= WHL_KNEES )
8677 {
8678 PM_AddEvent( EV_FOOTWADE );
8679 }
8680 else
8681 {
8682 PM_AddEvent( EV_FOOTSPLASH );
8683 }
8684 if ( pm->gent && pm->gent->s.number == 0 )
8685 {
8686 vec3_t bottom;
8687
8688 VectorCopy( pm->ps->origin, bottom );
8689 bottom[2] += pm->mins[2];
8690 // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
8691 {
8692 AddSoundEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS, qfalse, qtrue );//was bottom
8693 AddSightEvent( pm->gent, pm->ps->origin, 512, AEL_MINOR );
8694 }
8695 }
8696 }
8697 else if ( pm->waterlevel == 2 )
8698 {
8699 // wading / swimming at surface
8700 /*
8701 if ( pm->ps->waterHeightLevel >= WHL_TORSO )
8702 {
8703 PM_AddEvent( EV_SWIM );
8704 }
8705 else
8706 */
8707 {
8708 PM_AddEvent( EV_FOOTWADE );
8709 }
8710 if ( pm->gent && pm->gent->s.number == 0 )
8711 {
8712 // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
8713 {
8714 AddSoundEvent( pm->gent, pm->ps->origin, 256, AEL_MINOR, qfalse, qtrue );
8715 AddSightEvent( pm->gent, pm->ps->origin, 512, AEL_SUSPICIOUS );
8716 }
8717 }
8718 }
8719 else
8720 {// or no sound when completely underwater...?
8721 PM_AddEvent( EV_SWIM );
8722 }
8723 }
8724 }
8725
8726 /*
8727 ==============
8728 PM_WaterEvents
8729
8730 Generate sound events for entering and leaving water
8731 ==============
8732 */
PM_WaterEvents(void)8733 static void PM_WaterEvents( void ) { // FIXME?
8734
8735 qboolean impact_splash = qfalse;
8736
8737 if ( pm->watertype & CONTENTS_LADDER ) //fake water for ladder
8738 {
8739 return;
8740 }
8741 //
8742 // if just entered a water volume, play a sound
8743 //
8744 if (!pml.previous_waterlevel && pm->waterlevel)
8745 {
8746 if ( (pm->watertype&CONTENTS_LAVA) )
8747 {
8748 PM_AddEvent( EV_LAVA_TOUCH );
8749 }
8750 else
8751 {
8752 PM_AddEvent( EV_WATER_TOUCH );
8753 }
8754 if ( pm->gent )
8755 {
8756 if ( VectorLengthSquared( pm->ps->velocity ) > 40000 )
8757 {
8758 impact_splash = qtrue;
8759 }
8760
8761 if ( pm->ps->clientNum < MAX_CLIENTS )
8762 {
8763 AddSoundEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS );
8764 AddSightEvent( pm->gent, pm->ps->origin, 512, AEL_SUSPICIOUS );
8765 }
8766 }
8767 }
8768
8769 //
8770 // if just completely exited a water volume, play a sound
8771 //
8772 if (pml.previous_waterlevel && !pm->waterlevel)
8773 {
8774 if ( (pm->watertype&CONTENTS_LAVA) )
8775 {
8776 PM_AddEvent( EV_LAVA_LEAVE );
8777 }
8778 else
8779 {
8780 PM_AddEvent( EV_WATER_LEAVE );
8781 }
8782 if ( pm->gent && VectorLengthSquared( pm->ps->velocity ) > 40000 )
8783 {
8784 impact_splash = qtrue;
8785 }
8786 if ( pm->gent && pm->ps->clientNum < MAX_CLIENTS )
8787 {
8788 AddSoundEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS );
8789 AddSightEvent( pm->gent, pm->ps->origin, 512, AEL_SUSPICIOUS );
8790 }
8791 }
8792
8793 if ( impact_splash )
8794 {
8795 //play the splash effect
8796 trace_t tr;
8797 vec3_t axis[3], angs, start, end;
8798
8799 VectorSet( angs, 0, pm->gent->currentAngles[YAW], 0 );
8800 AngleVectors( angs, axis[2], axis[1], axis[0] );
8801
8802 VectorCopy( pm->ps->origin, start );
8803 VectorCopy( pm->ps->origin, end );
8804
8805 // FIXME: set start and end better
8806 start[2] += 10;
8807 end[2] -= 40;
8808
8809 gi.trace( &tr, start, vec3_origin, vec3_origin, end, pm->gent->s.number, MASK_WATER, (EG2_Collision)0, 0 );
8810
8811 if ( tr.fraction < 1.0f )
8812 {
8813 if ( (tr.contents&CONTENTS_LAVA) )
8814 {
8815 G_PlayEffect( "env/lava_splash", tr.endpos, axis );
8816 }
8817 else if ( (tr.contents&CONTENTS_SLIME) )
8818 {
8819 G_PlayEffect( "env/acid_splash", tr.endpos, axis );
8820 }
8821 else //must be water
8822 {
8823 G_PlayEffect( "env/water_impact", tr.endpos, axis );
8824 }
8825 }
8826 }
8827
8828 //
8829 // check for head just going under water
8830 //
8831 if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) {
8832 if ( (pm->watertype&CONTENTS_LAVA) )
8833 {
8834 PM_AddEvent( EV_LAVA_UNDER );
8835 }
8836 else
8837 {
8838 PM_AddEvent( EV_WATER_UNDER );
8839 }
8840
8841 if ( pm->gent && pm->ps->clientNum < MAX_CLIENTS )
8842 {
8843 AddSoundEvent( pm->gent, pm->ps->origin, 256, AEL_MINOR );
8844 AddSightEvent( pm->gent, pm->ps->origin, 384, AEL_MINOR );
8845 }
8846 }
8847
8848 //
8849 // check for head just coming out of water
8850 //
8851 if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) {
8852 if ( !pm->gent || !pm->gent->client || pm->gent->client->airOutTime < level.time + 2000 )
8853 {//only do this if we were drowning or about to start drowning
8854 PM_AddEvent( EV_WATER_CLEAR );
8855 }
8856 else
8857 {
8858 if ( (pm->watertype&CONTENTS_LAVA) )
8859 {
8860 PM_AddEvent( EV_LAVA_LEAVE );
8861 }
8862 else
8863 {
8864 PM_AddEvent( EV_WATER_LEAVE );
8865 }
8866 }
8867 if ( pm->gent && pm->ps->clientNum < MAX_CLIENTS )
8868 {
8869 AddSoundEvent( pm->gent, pm->ps->origin, 256, AEL_MINOR );
8870 AddSightEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS );
8871 }
8872 }
8873 }
8874
8875
8876 /*
8877 ===============
8878 PM_BeginWeaponChange
8879 ===============
8880 */
PM_BeginWeaponChange(int weapon)8881 static void PM_BeginWeaponChange( int weapon ) {
8882
8883 if ( pm->gent && pm->gent->client && pm->gent->client->pers.enterTime >= level.time - 500 )
8884 {//just entered map
8885 if ( weapon == WP_NONE && pm->ps->weapon != weapon )
8886 {//don't switch to weapon none if just entered map
8887 return;
8888 }
8889 }
8890
8891 if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) {
8892 return;
8893 }
8894
8895 if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
8896 return;
8897 }
8898
8899 if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
8900 return;
8901 }
8902
8903 if ( cg.time > 0 )
8904 {//this way we don't get that annoying change weapon sound every time a map starts
8905 PM_AddEvent( EV_CHANGE_WEAPON );
8906 }
8907
8908 pm->ps->weaponstate = WEAPON_DROPPING;
8909 pm->ps->weaponTime += 200;
8910 if ( !(pm->ps->eFlags&EF_HELD_BY_WAMPA) && !G_IsRidingVehicle(pm->gent))
8911 {
8912 PM_SetAnim(pm,SETANIM_TORSO,TORSO_DROPWEAP1,SETANIM_FLAG_HOLD);
8913 }
8914
8915 // turn of any kind of zooming when weapon switching....except the LA Goggles
8916 // eezstreet edit: also ignore if we change to WP_NONE..sorta hacky fix for binoculars using WP_SABER
8917 if ( pm->ps->clientNum == 0 && cg.weaponSelect != WP_NONE )
8918 {
8919 if ( cg.zoomMode > 0 && cg.zoomMode < 3 )
8920 {
8921 cg.zoomMode = 0;
8922 cg.zoomTime = cg.time;
8923 }
8924 }
8925
8926 if ( pm->gent
8927 && pm->gent->client
8928 && (pm->gent->client->NPC_class == CLASS_ATST||pm->gent->client->NPC_class == CLASS_RANCOR) )
8929 {
8930 if ( pm->ps->clientNum < MAX_CLIENTS )
8931 {
8932 gi.cvar_set( "cg_thirdperson", "1" );
8933 }
8934 }
8935 else if ( weapon == WP_SABER )
8936 {//going to switch to lightsaber
8937 }
8938 else
8939 {
8940 if ( pm->ps->weapon == WP_SABER )
8941 {//going to switch away from saber
8942 if ( pm->gent )
8943 {
8944 G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/saber/saberoffquick.wav" );
8945 }
8946 if (!G_IsRidingVehicle(pm->gent))
8947 {
8948 PM_SetSaberMove(LS_PUTAWAY);
8949 }
8950 }
8951 //put this back in because saberActive isn't being set somewhere else anymore
8952 pm->ps->SaberDeactivate();
8953 pm->ps->SetSaberLength( 0.0f );
8954 }
8955 }
8956
8957
8958 /*
8959 ===============
8960 PM_FinishWeaponChange
8961 ===============
8962 */
PM_FinishWeaponChange(void)8963 static void PM_FinishWeaponChange( void ) {
8964 int weapon;
8965 qboolean trueSwitch = qtrue;
8966
8967 if ( pm->gent && pm->gent->client && pm->gent->client->pers.enterTime >= level.time - 500 )
8968 {//just entered map
8969 if ( pm->cmd.weapon == WP_NONE && pm->ps->weapon != pm->cmd.weapon )
8970 {//don't switch to weapon none if just entered map
8971 return;
8972 }
8973 }
8974 weapon = pm->cmd.weapon;
8975 if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) {
8976 weapon = WP_NONE;
8977 }
8978
8979 if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
8980 weapon = WP_NONE;
8981 }
8982
8983 if ( pm->ps->weapon == weapon )
8984 {
8985 trueSwitch = qfalse;
8986 }
8987 //int oldWeap = pm->ps->weapon;
8988 pm->ps->weapon = weapon;
8989 pm->ps->weaponstate = WEAPON_RAISING;
8990 pm->ps->weaponTime += 250;
8991
8992 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST )
8993 {//do nothing
8994 }
8995 else if ( weapon == WP_SABER )
8996 {//turn on the lightsaber
8997 //FIXME: somehow sometimes I still end up with 2 weapons in hand... usually if I
8998 // cycle weapons fast enough that I end up in 1st person lightsaber, then
8999 // somehow throw the saber and switch to another weapon (all in 1st person),
9000 // making my saber drop to the ground... when I switch back to the saber, it
9001 // does not remove the current weapon model and then, when I pull the saber
9002 // back to my hand, I have 2 weaponModels active...?
9003 if ( pm->gent )
9004 {// remove gun if we had it.
9005 G_RemoveWeaponModels( pm->gent );
9006 }
9007
9008 if ( !pm->ps->saberInFlight || pm->ps->dualSabers )
9009 {//if it's not in flight or lying around, turn it on!
9010 //FIXME: AddSound/Sight Event
9011 //FIXME: don't do this if just loaded a game
9012 if ( trueSwitch )
9013 {//actually did switch weapons, turn it on
9014 if ( PM_RidingVehicle() )
9015 {//only turn on the first saber's first blade...?
9016 pm->ps->SaberBladeActivate( 0, 0 );
9017 }
9018 else
9019 {
9020 pm->ps->SaberActivate();
9021 }
9022 pm->ps->SetSaberLength( 0.0f );
9023 }
9024
9025 if ( pm->gent )
9026 {
9027 WP_SaberAddG2SaberModels( pm->gent );
9028 }
9029 }
9030 else
9031 {//FIXME: pull it back to us?
9032 }
9033
9034 if ( pm->gent )
9035 {
9036 WP_SaberInitBladeData( pm->gent );
9037 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
9038 {
9039 gi.cvar_set( "cg_thirdperson", "1" );
9040 }
9041 }
9042 if ( trueSwitch )
9043 {//actually did switch weapons, play anim
9044 if (!G_IsRidingVehicle(pm->gent))
9045 {
9046 PM_SetSaberMove(LS_DRAW);
9047 }
9048 }
9049 }
9050 else
9051 {//switched away from saber
9052 if ( pm->gent )
9053 {
9054 // remove the sabre if we had it.
9055 G_RemoveWeaponModels( pm->gent );
9056 if (weaponData[weapon].weaponMdl[0]) { //might be NONE, so check if it has a model
9057 G_CreateG2AttachedWeaponModel( pm->gent, weaponData[weapon].weaponMdl, pm->gent->handRBolt, 0 );
9058 }
9059 }
9060
9061 if ( !(pm->ps->eFlags&EF_HELD_BY_WAMPA) )
9062 {
9063 if ( pm->ps->weapon != WP_THERMAL
9064 && pm->ps->weapon != WP_TRIP_MINE
9065 && pm->ps->weapon != WP_DET_PACK
9066 && !G_IsRidingVehicle(pm->gent))
9067 {
9068 PM_SetAnim(pm,SETANIM_TORSO,TORSO_RAISEWEAP1,SETANIM_FLAG_HOLD);
9069 }
9070 }
9071
9072 if ( pm->ps->clientNum < MAX_CLIENTS
9073 && cg_gunAutoFirst.integer
9074 && !PM_RidingVehicle()
9075 // && oldWeap == WP_SABER
9076 && weapon != WP_NONE )
9077 {
9078 gi.cvar_set( "cg_thirdperson", "0" );
9079 }
9080 pm->ps->saberMove = LS_NONE;
9081 pm->ps->saberBlocking = BLK_NO;
9082 pm->ps->saberBlocked = BLOCKED_NONE;
9083 }
9084 }
9085
PM_ReadyPoseForSaberAnimLevel(void)9086 int PM_ReadyPoseForSaberAnimLevel( void )
9087 {
9088 int anim = BOTH_STAND2;
9089 if (PM_RidingVehicle())
9090 {
9091 return -1;
9092 }
9093 switch ( pm->ps->saberAnimLevel )
9094 {
9095 case SS_DUAL:
9096 anim = BOTH_SABERDUAL_STANCE;
9097 break;
9098 case SS_STAFF:
9099 anim = BOTH_SABERSTAFF_STANCE;
9100 break;
9101 case SS_FAST:
9102 case SS_TAVION:
9103 anim = BOTH_SABERFAST_STANCE;
9104 break;
9105 case SS_STRONG:
9106 anim = BOTH_SABERSLOW_STANCE;
9107 break;
9108 case SS_NONE:
9109 case SS_MEDIUM:
9110 case SS_DESANN:
9111 default:
9112 anim = BOTH_STAND2;
9113 break;
9114 }
9115 return anim;
9116 }
9117
PM_CanDoDualDoubleAttacks(void)9118 qboolean PM_CanDoDualDoubleAttacks( void )
9119 {
9120 if ( (pm->ps->saber[0].saberFlags&SFL_NO_MIRROR_ATTACKS) )
9121 {
9122 return qfalse;
9123 }
9124 if ( pm->ps->dualSabers
9125 && (pm->ps->saber[1].saberFlags&SFL_NO_MIRROR_ATTACKS) )
9126 {
9127 return qfalse;
9128 }
9129 //NOTE: assumes you're using SS_DUAL style and have both sabers on...
9130 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
9131 {//player
9132 return qtrue;
9133 }
9134 if ( pm->gent && pm->gent->NPC && pm->gent->NPC->rank >= Q_irand( RANK_LT_COMM, RANK_CAPTAIN+2 ) )
9135 {//high-rank NPC
9136 return qtrue;
9137 }
9138 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA )
9139 {//Alora
9140 return qtrue;
9141 }
9142 return qfalse;
9143 }
9144
PM_SetJumped(float height,qboolean force)9145 void PM_SetJumped( float height, qboolean force )
9146 {
9147 pm->ps->velocity[2] = height;
9148 pml.groundPlane = qfalse;
9149 pml.walking = qfalse;
9150 pm->ps->groundEntityNum = ENTITYNUM_NONE;
9151 pm->ps->pm_flags |= PMF_JUMP_HELD;
9152 pm->ps->pm_flags |= PMF_JUMPING;
9153 pm->cmd.upmove = 0;
9154
9155 if ( force )
9156 {
9157 pm->ps->jumpZStart = pm->ps->origin[2];
9158 pm->ps->pm_flags |= PMF_SLOW_MO_FALL;
9159 //start force jump
9160 pm->ps->forcePowersActive |= (1<<FP_LEVITATION);
9161 G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
9162 }
9163 else
9164 {
9165 PM_AddEvent( EV_JUMP );
9166 }
9167 }
9168
9169
PM_SetSaberMove(saberMoveName_t newMove)9170 void PM_SetSaberMove(saberMoveName_t newMove)
9171 {
9172 unsigned int setflags;
9173 int anim;
9174 int parts = SETANIM_TORSO;
9175 qboolean manualBlocking = qfalse;
9176
9177 if ( newMove < LS_NONE || newMove >= LS_MOVE_MAX )
9178 {
9179 assert(0);
9180 return;
9181 }
9182
9183 setflags = saberMoveData[newMove].animSetFlags;
9184 anim = saberMoveData[newMove].animToUse;
9185
9186 if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) )
9187 {//no anim
9188 return;
9189 }
9190
9191 if ( cg_debugSaber.integer&0x01 && (newMove != LS_READY) )
9192 {
9193 Com_Printf("SetSaberMove: From '%s' to '%s'\n",
9194 saberMoveData[pm->ps->saberMove].name,
9195 saberMoveData[newMove].name);
9196 }
9197
9198 if ( newMove == LS_READY || newMove == LS_A_FLIP_STAB || newMove == LS_A_FLIP_SLASH )
9199 {//finished with a kata (or in a special move) reset attack counter
9200 pm->ps->saberAttackChainCount = 0;
9201 }
9202 else if ( PM_SaberInAttack( newMove ) )
9203 {//continuing with a kata, increment attack counter
9204 //FIXME: maybe some contextual/style-specific logic in here
9205 pm->ps->saberAttackChainCount++;
9206 }
9207
9208 if ( newMove == LS_READY )
9209 {
9210 if ( pm->ps->saberBlockingTime > cg.time )
9211 {
9212 manualBlocking = qtrue;
9213 if ( !pm->ps->SaberActive() )
9214 {//turn on all blades and sabers if none are currently on
9215 pm->ps->SaberActivate();
9216 }
9217 if ( pm->ps->saber[0].type == SABER_CLAW )
9218 {
9219 anim = BOTH_INAIR1;//FIXME: is there a better anim for this?
9220 }
9221 else if ( pm->ps->dualSabers && pm->ps->saber[1].Active() )
9222 {
9223 anim = BOTH_INAIR1;
9224 }
9225 else
9226 {
9227 anim = BOTH_P1_S1_T_;
9228 }
9229 }
9230 else if ( pm->ps->saber[0].readyAnim != -1 )
9231 {
9232 anim = pm->ps->saber[0].readyAnim;
9233 }
9234 else if ( pm->ps->dualSabers
9235 && pm->ps->saber[1].readyAnim != -1 )
9236 {
9237 anim = pm->ps->saber[1].readyAnim;
9238 }
9239 else if ( pm->ps->saber[0].type == SABER_ARC )
9240 {//FIXME: need it's own style?
9241 anim = BOTH_SABERFAST_STANCE;
9242 }
9243 else if ( (pm->ps->dualSabers && pm->ps->saber[1].Active()) )
9244 {
9245 anim = BOTH_SABERDUAL_STANCE;
9246 }
9247 else if ( (pm->ps->SaberStaff() && (!pm->ps->saber[0].singleBladeStyle||pm->ps->saber[0].blade[1].active))//saber staff with more than first blade active
9248 || pm->ps->saber[0].type == SABER_ARC )
9249 {
9250 anim = BOTH_SABERSTAFF_STANCE;
9251 }
9252 else if ( pm->ps->saber[0].type == SABER_LANCE || pm->ps->saber[0].type == SABER_TRIDENT )
9253 {//FIXME: need some 2-handed forward-pointing anim
9254 anim = BOTH_STAND1;
9255 }
9256 else
9257 {
9258 anim = PM_ReadyPoseForSaberAnimLevel();
9259 }
9260 }
9261 else if ( newMove == LS_DRAW )
9262 {
9263 if ( PM_RunningAnim( pm->ps->torsoAnim ) )
9264 {
9265 pm->ps->saberMove = newMove;
9266 return;
9267 }
9268 if ( pm->ps->saber[0].drawAnim != -1 )
9269 {
9270 anim = pm->ps->saber[0].drawAnim;
9271 }
9272 else if ( pm->ps->dualSabers
9273 && pm->ps->saber[1].drawAnim != -1 )
9274 {
9275 anim = pm->ps->saber[1].drawAnim;
9276 }
9277 else if ( pm->ps->saber[0].stylesLearned==(1<<SS_STAFF) )
9278 {
9279 anim = BOTH_S1_S7;
9280 }
9281 else if ( pm->ps->dualSabers
9282 && !(pm->ps->saber[0].stylesForbidden&(1<<SS_DUAL))
9283 && !(pm->ps->saber[1].stylesForbidden&(1<<SS_DUAL)) )
9284 {
9285 anim = BOTH_S1_S6;
9286 }
9287 if ( pm->ps->torsoAnim == BOTH_STAND1IDLE1 )
9288 {
9289 setflags |= SETANIM_FLAG_OVERRIDE;
9290 }
9291 }
9292 else if ( newMove == LS_PUTAWAY )
9293 {
9294 if ( pm->ps->saber[0].putawayAnim != -1 )
9295 {
9296 anim = pm->ps->saber[0].putawayAnim;
9297 }
9298 else if ( pm->ps->dualSabers
9299 && pm->ps->saber[1].putawayAnim != -1 )
9300 {
9301 anim = pm->ps->saber[1].putawayAnim;
9302 }
9303 else if ( pm->ps->saber[0].stylesLearned==(1<<SS_STAFF)
9304 && pm->ps->saber[0].blade[1].active )
9305 {
9306 anim = BOTH_S7_S1;
9307 }
9308 else if ( pm->ps->dualSabers
9309 && !(pm->ps->saber[0].stylesForbidden&(1<<SS_DUAL))
9310 && !(pm->ps->saber[1].stylesForbidden&(1<<SS_DUAL))
9311 && pm->ps->saber[1].Active() )
9312 {
9313 anim = BOTH_S6_S1;
9314 }
9315 if ( PM_SaberStanceAnim( pm->ps->legsAnim ) && pm->ps->legsAnim != BOTH_STAND1 )
9316 {
9317 parts = SETANIM_BOTH;
9318 }
9319 else
9320 {
9321 if ( PM_RunningAnim( pm->ps->torsoAnim ) )
9322 {
9323 pm->ps->saberMove = newMove;
9324 return;
9325 }
9326 parts = SETANIM_TORSO;
9327 }
9328 //FIXME: also dual
9329 }
9330 else if ( pm->ps->saberAnimLevel == SS_STAFF && newMove >= LS_S_TL2BR && newMove < LS_REFLECT_LL )
9331 {//staff has an entirely new set of anims, besides special attacks
9332 //FIXME: include ready and draw/putaway?
9333 //FIXME: get hand-made bounces and deflections?
9334 if ( newMove >= LS_V1_BR && newMove <= LS_REFLECT_LL )
9335 {//there aren't 1-7, just 1, 6 and 7, so just set it
9336 anim = BOTH_P7_S7_T_ + (anim-BOTH_P1_S1_T_);//shift it up to the proper set
9337 }
9338 else
9339 {//add the appropriate animLevel
9340 anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE;
9341 }
9342 }
9343 else if ( (pm->ps->saberAnimLevel == SS_DUAL
9344 || (pm->ps->dualSabers&& pm->ps->saber[1].Active()))
9345 && newMove >= LS_S_TL2BR
9346 && newMove < LS_REFLECT_LL )
9347 {//staff has an entirely new set of anims, besides special attacks
9348 //FIXME: include ready and draw/putaway?
9349 //FIXME: get hand-made bounces and deflections?
9350 //FIXME: only do the dual FB & LR attacks when on ground?
9351 if ( newMove >= LS_V1_BR && newMove <= LS_REFLECT_LL )
9352 {//there aren't 1-7, just 1, 6 and 7, so just set it
9353 anim = BOTH_P6_S6_T_ + (anim-BOTH_P1_S1_T_);//shift it up to the proper set
9354 }
9355 else if ( ( newMove == LS_A_R2L || newMove == LS_S_R2L
9356 || newMove == LS_A_L2R || newMove == LS_S_L2R )
9357 && PM_CanDoDualDoubleAttacks()
9358 && G_CheckEnemyPresence( pm->gent, DIR_RIGHT, 150.0f )
9359 && G_CheckEnemyPresence( pm->gent, DIR_LEFT, 150.0f ) )
9360 {//enemy both on left and right
9361 newMove = LS_DUAL_LR;
9362 anim = saberMoveData[newMove].animToUse;
9363 //probably already moved, but...
9364 pm->cmd.rightmove = 0;
9365 }
9366 else if ( (newMove == LS_A_T2B || newMove == LS_S_T2B
9367 || newMove == LS_A_BACK || newMove == LS_A_BACK_CR )
9368 && PM_CanDoDualDoubleAttacks()
9369 && G_CheckEnemyPresence( pm->gent, DIR_FRONT, 150.0f )
9370 && G_CheckEnemyPresence( pm->gent, DIR_BACK, 150.0f ) )
9371 {//enemy both in front and back
9372 newMove = LS_DUAL_FB;
9373 anim = saberMoveData[newMove].animToUse;
9374 //probably already moved, but...
9375 pm->cmd.forwardmove = 0;
9376 }
9377 else
9378 {//add the appropriate animLevel
9379 anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE;
9380 }
9381 }
9382 /*
9383 else if ( newMove == LS_DRAW && pm->ps->saberAnimLevel == SS_STAFF )//pm->ps->SaberStaff() )
9384 {//hold saber out front as we turn it on
9385 //FIXME: need a real "draw" anim for this (and put-away)
9386 anim = BOTH_SABERSTAFF_STANCE;
9387 }
9388 */
9389 else if ( pm->ps->saberAnimLevel > FORCE_LEVEL_1 &&
9390 !PM_SaberInIdle( newMove ) && !PM_SaberInParry( newMove ) && !PM_SaberInKnockaway( newMove ) && !PM_SaberInBrokenParry( newMove ) && !PM_SaberInReflect( newMove ) && !PM_SaberInSpecial( newMove ))
9391 {//readies, parries and reflections have only 1 level
9392 if ( pm->ps->saber[0].type == SABER_LANCE || pm->ps->saber[0].type == SABER_TRIDENT )
9393 {//FIXME: hack for now - these use the fast anims, but slowed down. Should have own style
9394 }
9395 else
9396 {//increment the anim to the next level of saber anims
9397 anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE;
9398 }
9399 }
9400 else if ( newMove == LS_KICK_F_AIR
9401 || newMove == LS_KICK_B_AIR
9402 || newMove == LS_KICK_R_AIR
9403 || newMove == LS_KICK_L_AIR )
9404 {
9405 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
9406 {
9407 PM_SetJumped( 200, qtrue );
9408 }
9409 }
9410
9411 // If the move does the same animation as the last one, we need to force a restart...
9412 // if ( saberMoveData[pm->ps->saberMove].animToUse == anim && newMove > LS_PUTAWAY)
9413 if ( ( pm->ps->torsoAnim == anim || pm->ps->legsAnim == anim )
9414 && newMove > LS_PUTAWAY )
9415 {
9416 setflags |= SETANIM_FLAG_RESTART;
9417 }
9418
9419 if ( (anim == BOTH_STAND1 && (pm->ps->saber[0].type == SABER_ARC || (pm->ps->dualSabers && pm->ps->saber[1].Active())) )
9420 || anim == BOTH_STAND2
9421 //FIXME: temp hack to stop it from using run2 with staff
9422 || (0 && anim == BOTH_SABERSTAFF_STANCE)
9423 || anim == BOTH_SABERDUAL_STANCE
9424 || anim == BOTH_SABERFAST_STANCE
9425 || anim == BOTH_SABERSLOW_STANCE )
9426 {//match torso anim to walk/run anim if newMove is just LS_READY
9427 //FIXME: play both_stand2_random1 when you've been idle for a while
9428 switch ( pm->ps->legsAnim )
9429 {
9430 case BOTH_WALK1:
9431 case BOTH_WALK2:
9432 case BOTH_WALK_STAFF:
9433 case BOTH_WALK_DUAL:
9434 case BOTH_WALKBACK1:
9435 case BOTH_WALKBACK2:
9436 case BOTH_WALKBACK_STAFF:
9437 case BOTH_WALKBACK_DUAL:
9438 case BOTH_RUN1:
9439 case BOTH_RUN2:
9440 case BOTH_RUN_STAFF:
9441 case BOTH_RUN_DUAL:
9442 case BOTH_RUNBACK1:
9443 case BOTH_RUNBACK2:
9444 case BOTH_RUNBACK_STAFF:
9445 anim = pm->ps->legsAnim;
9446 break;
9447 }
9448 }
9449
9450 if ( !PM_RidingVehicle() )
9451 {
9452 if ( !manualBlocking )
9453 {
9454 if ( newMove == LS_A_LUNGE
9455 || newMove == LS_A_JUMP_T__B_
9456 || newMove == LS_A_BACKSTAB
9457 || newMove == LS_A_BACK
9458 || newMove == LS_A_BACK_CR
9459 || newMove == LS_ROLL_STAB
9460 || newMove == LS_A_FLIP_STAB
9461 || newMove == LS_A_FLIP_SLASH
9462 || newMove == LS_JUMPATTACK_DUAL
9463 || newMove == LS_JUMPATTACK_ARIAL_LEFT
9464 || newMove == LS_JUMPATTACK_ARIAL_RIGHT
9465 || newMove == LS_JUMPATTACK_CART_LEFT
9466 || newMove == LS_JUMPATTACK_CART_RIGHT
9467 || newMove == LS_JUMPATTACK_STAFF_LEFT
9468 || newMove == LS_JUMPATTACK_STAFF_RIGHT
9469 || newMove == LS_BUTTERFLY_LEFT
9470 || newMove == LS_BUTTERFLY_RIGHT
9471 || newMove == LS_A_BACKFLIP_ATK
9472 || newMove == LS_STABDOWN
9473 || newMove == LS_STABDOWN_STAFF
9474 || newMove == LS_STABDOWN_DUAL
9475 || newMove == LS_DUAL_SPIN_PROTECT
9476 || newMove == LS_STAFF_SOULCAL
9477 || newMove == LS_A1_SPECIAL
9478 || newMove == LS_A2_SPECIAL
9479 || newMove == LS_A3_SPECIAL
9480 || newMove == LS_UPSIDE_DOWN_ATTACK
9481 || newMove == LS_PULL_ATTACK_STAB
9482 || newMove == LS_PULL_ATTACK_SWING
9483 || PM_KickMove( newMove ) )
9484 {
9485 parts = SETANIM_BOTH;
9486 }
9487 else if ( PM_SpinningSaberAnim( anim ) )
9488 {//spins must be played on entire body
9489 parts = SETANIM_BOTH;
9490 }
9491 else if ( (!pm->cmd.forwardmove&&!pm->cmd.rightmove&&!pm->cmd.upmove))
9492 {//not trying to run, duck or jump
9493 if ( !PM_FlippingAnim( pm->ps->legsAnim ) &&
9494 !PM_InRoll( pm->ps ) &&
9495 !PM_InKnockDown( pm->ps ) &&
9496 !PM_JumpingAnim( pm->ps->legsAnim ) &&
9497 !PM_PainAnim( pm->ps->legsAnim ) &&
9498 !PM_InSpecialJump( pm->ps->legsAnim ) &&
9499 !PM_InSlopeAnim( pm->ps->legsAnim ) &&
9500 //!PM_CrouchAnim( pm->ps->legsAnim ) &&
9501 //pm->cmd.upmove >= 0 &&
9502 !(pm->ps->pm_flags & PMF_DUCKED) &&
9503 newMove != LS_PUTAWAY )
9504 {
9505 parts = SETANIM_BOTH;
9506 }
9507 else if ( !(pm->ps->pm_flags & PMF_DUCKED)
9508 && ( newMove == LS_SPINATTACK_DUAL || newMove == LS_SPINATTACK ) )
9509 {
9510 parts = SETANIM_BOTH;
9511 }
9512 }
9513 }
9514 }
9515 else
9516 {
9517 if (!pm->ps->saberBlocked)
9518 {
9519 parts = SETANIM_BOTH;
9520 setflags &= ~SETANIM_FLAG_RESTART;
9521 }
9522 }
9523 if (anim!=-1)
9524 {
9525 PM_SetAnim( pm, parts, anim, setflags, saberMoveData[newMove].blendTime );
9526 }
9527
9528 if ( pm->ps->torsoAnim == anim )
9529 {//successfully changed anims
9530 //special check for *starting* a saber swing
9531 if ( pm->gent && pm->ps->SaberLength() > 1 )
9532 {
9533 if ( PM_SaberInAttack( newMove ) || PM_SaberInSpecialAttack( anim ) )
9534 {//playing an attack
9535 if ( pm->ps->saberMove != newMove )
9536 {//wasn't playing that attack before
9537 if ( PM_SaberInSpecialAttack( anim ) )
9538 {
9539 WP_SaberSwingSound( pm->gent, 0, SWING_FAST );
9540 if ( !PM_InCartwheel( pm->ps->torsoAnim ) )
9541 {//can still attack during a cartwheel/arial
9542 pm->ps->weaponTime = pm->ps->torsoAnimTimer;//so we know our weapon is busy
9543 }
9544 }
9545 else
9546 {
9547 switch ( pm->ps->saberAnimLevel )
9548 {
9549 case SS_DESANN:
9550 case SS_STRONG:
9551 WP_SaberSwingSound( pm->gent, 0, SWING_STRONG );
9552 break;
9553 case SS_MEDIUM:
9554 case SS_DUAL:
9555 case SS_STAFF:
9556 WP_SaberSwingSound( pm->gent, 0, SWING_MEDIUM );
9557 break;
9558 case SS_TAVION:
9559 case SS_FAST:
9560 WP_SaberSwingSound( pm->gent, 0, SWING_FAST );
9561 break;
9562 }
9563 }
9564 }
9565 else if ( (setflags&SETANIM_FLAG_RESTART) && PM_SaberInSpecialAttack( anim ) )
9566 {//sigh, if restarted a special, then set the weaponTime *again*
9567 if ( !PM_InCartwheel( pm->ps->torsoAnim ) )
9568 {//can still attack during a cartwheel/arial
9569 pm->ps->weaponTime = pm->ps->torsoAnimTimer;//so we know our weapon is busy
9570 }
9571 }
9572 }
9573 else if ( PM_SaberInStart( newMove ) )
9574 {
9575 //if ( g_saberRealisticCombat->integer < 1 )
9576 {//don't damage on the first few frames of a start anim because it may pop from one position to some drastically different one, killing the enemy without hitting them.
9577 int damageDelay = 150;
9578 if ( pm->ps->torsoAnimTimer < damageDelay )
9579 {
9580 damageDelay = pm->ps->torsoAnimTimer;
9581 }
9582 //ent->client->ps.saberDamageDebounceTime = level.time + damageDelay;
9583 }
9584 if ( pm->ps->saberAnimLevel == SS_STRONG )
9585 {
9586 WP_SaberSwingSound( pm->gent, 0, SWING_FAST );
9587 }
9588 }
9589 }
9590
9591 /*
9592 //wtf... getting stuck with weaponTime set even though we're not in an attack...?
9593 if ( PM_SaberInAttack( pm->ps->saberMove )
9594 && !PM_SaberInAttack( newMove ) )
9595 {
9596 pm->ps->weaponTime = 0;
9597 }
9598 */
9599
9600
9601 //Some special attacks can be started when sabers are off, make sure we turn them on, first!
9602 switch ( newMove )
9603 {//make sure the saber is on!
9604 case LS_A_LUNGE:
9605 case LS_ROLL_STAB:
9606 if ( PM_InSecondaryStyle() )
9607 {//staff as medium or dual as fast
9608 if ( pm->ps->dualSabers )
9609 {//only force on the first saber
9610 pm->ps->saber[0].Activate();
9611 }
9612 else if ( pm->ps->saber[0].numBlades > 1 )
9613 {//only force on the first saber's first blade
9614 pm->ps->SaberBladeActivate(0,0);
9615 }
9616 }
9617 else
9618 {//turn on all blades on all sabers
9619 pm->ps->SaberActivate();
9620 }
9621 break;
9622 case LS_SPINATTACK_ALORA:
9623 case LS_SPINATTACK_DUAL:
9624 case LS_SPINATTACK:
9625 case LS_A1_SPECIAL:
9626 case LS_A2_SPECIAL:
9627 case LS_A3_SPECIAL:
9628 case LS_DUAL_SPIN_PROTECT:
9629 case LS_STAFF_SOULCAL:
9630 //FIXME: probably more...
9631 pm->ps->SaberActivate();
9632 break;
9633 default:
9634 break;
9635 }
9636
9637 pm->ps->saberMove = newMove;
9638 pm->ps->saberBlocking = saberMoveData[newMove].blocking;
9639
9640 if ( pm->ps->clientNum == 0 || PM_ControlledByPlayer() )
9641 {
9642 if ( pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT_PROJ && pm->ps->saberBlocked <= BLOCKED_TOP_PROJ
9643 && newMove >= LS_REFLECT_UP && newMove <= LS_REFLECT_LL )
9644 {//don't clear it when blocking projectiles
9645 }
9646 else
9647 {
9648 pm->ps->saberBlocked = BLOCKED_NONE;
9649 }
9650 }
9651 else if ( pm->ps->saberBlocked <= BLOCKED_ATK_BOUNCE || !pm->ps->SaberActive() || (newMove < LS_PARRY_UR || newMove > LS_REFLECT_LL) )
9652 {//NPCs only clear blocked if not blocking?
9653 pm->ps->saberBlocked = BLOCKED_NONE;
9654 }
9655
9656 if ( pm->gent && pm->gent->client )
9657 {
9658 if ( saberMoveData[newMove].trailLength > 0 )
9659 {
9660 pm->gent->client->ps.SaberActivateTrail( saberMoveData[newMove].trailLength ); // saber trail lasts for 75ms...feel free to change this if you want it longer or shorter
9661 }
9662 else
9663 {
9664 pm->gent->client->ps.SaberDeactivateTrail( 0 );
9665 }
9666 }
9667 }
9668 }
9669
9670
9671 /*
9672 ==============
9673 PM_Use
9674
9675 Generates a use event
9676 ==============
9677 */
9678 #define USE_DELAY 250
9679
PM_Use(void)9680 void PM_Use( void )
9681 {
9682 if ( pm->ps->useTime > 0 )
9683 {
9684 pm->ps->useTime -= pml.msec;
9685 if ( pm->ps->useTime < 0 )
9686 {
9687 pm->ps->useTime = 0;
9688 }
9689 }
9690
9691 if ( pm->ps->useTime > 0 ) {
9692 return;
9693 }
9694
9695 if ( ! (pm->cmd.buttons & BUTTON_USE ) )
9696 {
9697 pm->useEvent = 0;
9698 pm->ps->useTime = 0;
9699 return;
9700 }
9701
9702 pm->useEvent = EV_USE;
9703 pm->ps->useTime = USE_DELAY;
9704 }
9705
9706 extern saberMoveName_t PM_AttackForEnemyPos( qboolean allowFB, qboolean allowStabDown );
PM_NPCSaberAttackFromQuad(int quad)9707 saberMoveName_t PM_NPCSaberAttackFromQuad( int quad )
9708 {
9709 //FIXME: this should be an AI decision
9710 // It should be based on the enemy's current LS_ move, saberAnimLevel,
9711 // the jedi's difficulty level, rank and FP_OFFENSE skill...
9712 saberMoveName_t autoMove = LS_NONE;
9713 if ( pm->gent && ((pm->gent->NPC && pm->gent->NPC->rank != RANK_ENSIGN && pm->gent->NPC->rank != RANK_CIVILIAN ) || (pm->gent->client && (pm->gent->client->NPC_class == CLASS_TAVION||pm->gent->client->NPC_class == CLASS_ALORA))) )
9714 {
9715 autoMove = PM_AttackForEnemyPos( qtrue, qtrue );
9716 }
9717 if ( autoMove != LS_NONE && PM_SaberInSpecial( autoMove ) )
9718 {//if have opportunity to do a special attack, do one
9719 return autoMove;
9720 }
9721 else
9722 {//pick another one
9723 saberMoveName_t newmove = LS_NONE;
9724 switch( quad )
9725 {
9726 case Q_T://blocked top
9727 if ( Q_irand( 0, 1 ) )
9728 {
9729 newmove = LS_A_T2B;
9730 }
9731 else
9732 {
9733 newmove = LS_A_TR2BL;
9734 }
9735 break;
9736 case Q_TR:
9737 if ( !Q_irand( 0, 2 ) )
9738 {
9739 newmove = LS_A_R2L;
9740 }
9741 else if ( !Q_irand( 0, 1 ) )
9742 {
9743 newmove = LS_A_TR2BL;
9744 }
9745 else
9746 {
9747 newmove = LS_T1_TR_BR;
9748 }
9749 break;
9750 case Q_TL:
9751 if ( !Q_irand( 0, 2 ) )
9752 {
9753 newmove = LS_A_L2R;
9754 }
9755 else if ( !Q_irand( 0, 1 ) )
9756 {
9757 newmove = LS_A_TL2BR;
9758 }
9759 else
9760 {
9761 newmove = LS_T1_TL_BL;
9762 }
9763 break;
9764 case Q_BR:
9765 if ( !Q_irand( 0, 2 ) )
9766 {
9767 newmove = LS_A_BR2TL;
9768 }
9769 else if ( !Q_irand( 0, 1 ) )
9770 {
9771 newmove = LS_T1_BR_TR;
9772 }
9773 else
9774 {
9775 newmove = LS_A_R2L;
9776 }
9777 break;
9778 case Q_BL:
9779 if ( !Q_irand( 0, 2 ) )
9780 {
9781 newmove = LS_A_BL2TR;
9782 }
9783 else if ( !Q_irand( 0, 1 ) )
9784 {
9785 newmove = LS_T1_BL_TL;
9786 }
9787 else
9788 {
9789 newmove = LS_A_L2R;
9790 }
9791 break;
9792 case Q_L:
9793 if ( !Q_irand( 0, 2 ) )
9794 {
9795 newmove = LS_A_L2R;
9796 }
9797 else if ( !Q_irand( 0, 1 ) )
9798 {
9799 newmove = LS_T1__L_T_;
9800 }
9801 else
9802 {
9803 newmove = LS_A_R2L;
9804 }
9805 break;
9806 case Q_R:
9807 if ( !Q_irand( 0, 2 ) )
9808 {
9809 newmove = LS_A_R2L;
9810 }
9811 else if ( !Q_irand( 0, 1 ) )
9812 {
9813 newmove = LS_T1__R_T_;
9814 }
9815 else
9816 {
9817 newmove = LS_A_L2R;
9818 }
9819 break;
9820 case Q_B:
9821 if ( pm->gent
9822 && pm->gent->NPC
9823 && pm->gent->NPC->rank >= RANK_LT_JG )
9824 {//fencers and above can do bottom-up attack
9825 if ( Q_irand( 0, pm->gent->NPC->rank ) >= RANK_LT_JG )
9826 {//but not overly likely
9827 newmove = PM_SaberLungeAttackMove( qtrue );
9828 }
9829 }
9830 break;
9831 default:
9832 break;
9833 }
9834 return newmove;
9835 }
9836 }
9837
PM_SaberMoveQuadrantForMovement(usercmd_t * ucmd)9838 int PM_SaberMoveQuadrantForMovement( usercmd_t *ucmd )
9839 {
9840 if ( ucmd->rightmove > 0 )
9841 {//moving right
9842 if ( ucmd->forwardmove > 0 )
9843 {//forward right = TL2BR slash
9844 return Q_TL;
9845 }
9846 else if ( ucmd->forwardmove < 0 )
9847 {//backward right = BL2TR uppercut
9848 return Q_BL;
9849 }
9850 else
9851 {//just right is a left slice
9852 return Q_L;
9853 }
9854 }
9855 else if ( ucmd->rightmove < 0 )
9856 {//moving left
9857 if ( ucmd->forwardmove > 0 )
9858 {//forward left = TR2BL slash
9859 return Q_TR;
9860 }
9861 else if ( ucmd->forwardmove < 0 )
9862 {//backward left = BR2TL uppercut
9863 return Q_BR;
9864 }
9865 else
9866 {//just left is a right slice
9867 return Q_R;
9868 }
9869 }
9870 else
9871 {//not moving left or right
9872 if ( ucmd->forwardmove > 0 )
9873 {//forward= T2B slash
9874 return Q_T;
9875 }
9876 else if ( ucmd->forwardmove < 0 )
9877 {//backward= T2B slash //or B2T uppercut?
9878 return Q_T;
9879 }
9880 else //if ( curmove == LS_READY )//???
9881 {//Not moving at all
9882 return Q_R;
9883 }
9884 }
9885 //return Q_R;//????
9886 }
9887
PM_SetAnimFrame(gentity_t * gent,int frame,qboolean torso,qboolean legs)9888 void PM_SetAnimFrame( gentity_t *gent, int frame, qboolean torso, qboolean legs )
9889 {
9890 if ( !gi.G2API_HaveWeGhoul2Models( gent->ghoul2 ) )
9891 {
9892 return;
9893 }
9894 int actualTime = (cg.time?cg.time:level.time);
9895 if ( torso && gent->lowerLumbarBone != -1 )//gent->upperLumbarBone
9896 {
9897 gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->lowerLumbarBone, //gent->upperLumbarBone
9898 frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 );
9899 if ( gent->motionBone != -1 )
9900 {
9901 gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->motionBone, //gent->upperLumbarBone
9902 frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 );
9903 }
9904 }
9905 if ( legs && gent->rootBone != -1 )
9906 {
9907 gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->rootBone,
9908 frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 );
9909 }
9910 }
9911
PM_SaberLockWinAnim(saberLockResult_t result,int breakType)9912 int PM_SaberLockWinAnim( saberLockResult_t result, int breakType )
9913 {
9914 int winAnim = -1;
9915 switch ( pm->ps->torsoAnim )
9916 {
9917 /*
9918 default:
9919 #ifndef FINAL_BUILD
9920 Com_Printf( S_COLOR_RED"ERROR-PM_SaberLockBreak: %s not in saberlock anim, anim = (%d)%s\n", pm->gent->NPC_type, pm->ps->torsoAnim, animTable[pm->ps->torsoAnim].name );
9921 #endif
9922 */
9923 case BOTH_BF2LOCK:
9924 if ( breakType == SABERLOCK_SUPERBREAK )
9925 {
9926 winAnim = BOTH_LK_S_S_T_SB_1_W;
9927 }
9928 else if ( result == LOCK_DRAW )
9929 {
9930 winAnim = BOTH_BF1BREAK;
9931 }
9932 else
9933 {
9934 pm->ps->saberMove = LS_A_T2B;
9935 winAnim = BOTH_A3_T__B_;
9936 }
9937 break;
9938 case BOTH_BF1LOCK:
9939 if ( breakType == SABERLOCK_SUPERBREAK )
9940 {
9941 winAnim = BOTH_LK_S_S_T_SB_1_W;
9942 }
9943 else if ( result == LOCK_DRAW )
9944 {
9945 winAnim = BOTH_KNOCKDOWN4;
9946 }
9947 else
9948 {
9949 pm->ps->saberMove = LS_K1_T_;
9950 winAnim = BOTH_K1_S1_T_;
9951 }
9952 break;
9953 case BOTH_CWCIRCLELOCK:
9954 if ( breakType == SABERLOCK_SUPERBREAK )
9955 {
9956 winAnim = BOTH_LK_S_S_S_SB_1_W;
9957 }
9958 else if ( result == LOCK_DRAW )
9959 {
9960 pm->ps->saberMove = pm->ps->saberBounceMove = LS_V1_BL;
9961 pm->ps->saberBlocked = BLOCKED_PARRY_BROKEN;
9962 winAnim = BOTH_V1_BL_S1;
9963 }
9964 else
9965 {
9966 winAnim = BOTH_CWCIRCLEBREAK;
9967 }
9968 break;
9969 case BOTH_CCWCIRCLELOCK:
9970 if ( breakType == SABERLOCK_SUPERBREAK )
9971 {
9972 winAnim = BOTH_LK_S_S_S_SB_1_W;
9973 }
9974 else if ( result == LOCK_DRAW )
9975 {
9976 pm->ps->saberMove = pm->ps->saberBounceMove = LS_V1_BR;
9977 pm->ps->saberBlocked = BLOCKED_PARRY_BROKEN;
9978 winAnim = BOTH_V1_BR_S1;
9979 }
9980 else
9981 {
9982 winAnim = BOTH_CCWCIRCLEBREAK;
9983 }
9984 break;
9985 default:
9986 //must be using new system:
9987 break;
9988 }
9989 if ( winAnim != -1 )
9990 {
9991 PM_SetAnim( pm, SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
9992 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
9993 pm->ps->saberBlocked = BLOCKED_NONE;
9994 pm->ps->weaponstate = WEAPON_FIRING;
9995 if ( breakType == SABERLOCK_SUPERBREAK
9996 && winAnim != BOTH_LK_ST_DL_T_SB_1_W )
9997 {//going to attack with saber, do a saber trail
9998 pm->ps->SaberActivateTrail( 200 );
9999 }
10000 }
10001 return winAnim;
10002 }
10003
PM_SaberLockLoseAnim(gentity_t * genemy,saberLockResult_t result,int breakType)10004 int PM_SaberLockLoseAnim( gentity_t *genemy, saberLockResult_t result, int breakType )
10005 {
10006 int loseAnim = -1;
10007 switch ( genemy->client->ps.torsoAnim )
10008 {
10009 /*
10010 default:
10011 #ifndef FINAL_BUILD
10012 Com_Printf( S_COLOR_RED"ERROR-PM_SaberLockBreak: %s not in saberlock anim, anim = (%d)%s\n", genemy->NPC_type, genemy->client->ps.torsoAnim, animTable[genemy->client->ps.torsoAnim].name );
10013 #endif
10014 */
10015 case BOTH_BF2LOCK:
10016 if ( breakType == SABERLOCK_SUPERBREAK )
10017 {
10018 loseAnim = BOTH_LK_S_S_T_SB_1_L;
10019 }
10020 else if ( result == LOCK_DRAW )
10021 {
10022 loseAnim = BOTH_BF1BREAK;
10023 }
10024 else
10025 {
10026 if ( result == LOCK_STALEMATE )
10027 {//no-one won
10028 genemy->client->ps.saberMove = LS_K1_T_;
10029 loseAnim = BOTH_K1_S1_T_;
10030 }
10031 else
10032 {//FIXME: this anim needs to transition back to ready when done
10033 loseAnim = BOTH_BF1BREAK;
10034 }
10035 }
10036 break;
10037 case BOTH_BF1LOCK:
10038 if ( breakType == SABERLOCK_SUPERBREAK )
10039 {
10040 loseAnim = BOTH_LK_S_S_T_SB_1_L;
10041 }
10042 else if ( result == LOCK_DRAW )
10043 {
10044 loseAnim = BOTH_KNOCKDOWN4;
10045 }
10046 else
10047 {
10048 if ( result == LOCK_STALEMATE )
10049 {//no-one won
10050 genemy->client->ps.saberMove = LS_A_T2B;
10051 loseAnim = BOTH_A3_T__B_;
10052 }
10053 else
10054 {
10055 loseAnim = BOTH_KNOCKDOWN4;
10056 }
10057 }
10058 break;
10059 case BOTH_CWCIRCLELOCK:
10060 if ( breakType == SABERLOCK_SUPERBREAK )
10061 {
10062 loseAnim = BOTH_LK_S_S_S_SB_1_L;
10063 }
10064 else if ( result == LOCK_DRAW )
10065 {
10066 genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_V1_BL;
10067 genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
10068 loseAnim = BOTH_V1_BL_S1;
10069 }
10070 else
10071 {
10072 if ( result == LOCK_STALEMATE )
10073 {//no-one won
10074 loseAnim = BOTH_CCWCIRCLEBREAK;
10075 }
10076 else
10077 {
10078 genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_V1_BL;
10079 genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
10080 loseAnim = BOTH_V1_BL_S1;
10081 /*
10082 genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_H1_BR;
10083 genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
10084 loseAnim = BOTH_H1_S1_BL;
10085 */
10086 }
10087 }
10088 break;
10089 case BOTH_CCWCIRCLELOCK:
10090 if ( breakType == SABERLOCK_SUPERBREAK )
10091 {
10092 loseAnim = BOTH_LK_S_S_S_SB_1_L;
10093 }
10094 else if ( result == LOCK_DRAW )
10095 {
10096 genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_V1_BR;
10097 genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
10098 loseAnim = BOTH_V1_BR_S1;
10099 }
10100 else
10101 {
10102 if ( result == LOCK_STALEMATE )
10103 {//no-one won
10104 loseAnim = BOTH_CWCIRCLEBREAK;
10105 }
10106 else
10107 {
10108 genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_V1_BR;
10109 genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
10110 loseAnim = BOTH_V1_BR_S1;
10111 /*
10112 genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_H1_BL;
10113 genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
10114 loseAnim = BOTH_H1_S1_BR;
10115 */
10116 }
10117 }
10118 break;
10119 }
10120 if ( loseAnim != -1 )
10121 {
10122 NPC_SetAnim( genemy, SETANIM_BOTH, loseAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
10123 genemy->client->ps.weaponTime = genemy->client->ps.torsoAnimTimer;// + 250;
10124 genemy->client->ps.saberBlocked = BLOCKED_NONE;
10125 genemy->client->ps.weaponstate = WEAPON_READY;
10126 }
10127 return loseAnim;
10128 }
10129
PM_SaberLockResultAnim(gentity_t * duelist,int lockOrBreakOrSuperBreak,int winOrLose)10130 int PM_SaberLockResultAnim( gentity_t *duelist, int lockOrBreakOrSuperBreak, int winOrLose )
10131 {
10132 int baseAnim = duelist->client->ps.torsoAnim;
10133 switch ( baseAnim )
10134 {
10135 case BOTH_LK_S_S_S_L_2: //lock if I'm using single vs. a single and other intitiated
10136 baseAnim = BOTH_LK_S_S_S_L_1;
10137 break;
10138 case BOTH_LK_S_S_T_L_2: //lock if I'm using single vs. a single and other initiated
10139 baseAnim = BOTH_LK_S_S_T_L_1;
10140 break;
10141 case BOTH_LK_DL_DL_S_L_2: //lock if I'm using dual vs. dual and other initiated
10142 baseAnim = BOTH_LK_DL_DL_S_L_1;
10143 break;
10144 case BOTH_LK_DL_DL_T_L_2: //lock if I'm using dual vs. dual and other initiated
10145 baseAnim = BOTH_LK_DL_DL_T_L_1;
10146 break;
10147 case BOTH_LK_ST_ST_S_L_2: //lock if I'm using staff vs. a staff and other initiated
10148 baseAnim = BOTH_LK_ST_ST_S_L_1;
10149 break;
10150 case BOTH_LK_ST_ST_T_L_2: //lock if I'm using staff vs. a staff and other initiated
10151 baseAnim = BOTH_LK_ST_ST_T_L_1;
10152 break;
10153 }
10154 //what kind of break?
10155 if ( lockOrBreakOrSuperBreak == SABERLOCK_BREAK )
10156 {
10157 baseAnim -= 2;
10158 }
10159 else if ( lockOrBreakOrSuperBreak == SABERLOCK_SUPERBREAK )
10160 {
10161 baseAnim += 1;
10162 }
10163 else
10164 {//WTF? Not a valid result
10165 return -1;
10166 }
10167 //win or lose?
10168 if ( winOrLose == SABERLOCK_WIN )
10169 {
10170 baseAnim += 1;
10171 }
10172 //play the anim and hold it
10173 NPC_SetAnim( duelist, SETANIM_BOTH, baseAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
10174
10175 if ( lockOrBreakOrSuperBreak == SABERLOCK_SUPERBREAK
10176 && winOrLose == SABERLOCK_LOSE )
10177 {//if you lose a superbreak, you're defenseless
10178 //make saberent not block
10179 gentity_t *saberent = &g_entities[duelist->client->ps.saberEntityNum];
10180 if ( saberent )
10181 {
10182 VectorClear(saberent->mins);
10183 VectorClear(saberent->maxs);
10184 G_SetOrigin(saberent, duelist->currentOrigin);
10185 }
10186 //set sabermove to none
10187 duelist->client->ps.saberMove = LS_NONE;
10188 //Hold the anim a little longer than it is
10189 duelist->client->ps.torsoAnimTimer += 250;;
10190 }
10191
10192 //no attacking during this anim
10193 duelist->client->ps.weaponTime = duelist->client->ps.torsoAnimTimer;
10194 duelist->client->ps.saberBlocked = BLOCKED_NONE;
10195 if ( lockOrBreakOrSuperBreak == SABERLOCK_SUPERBREAK
10196 && winOrLose == SABERLOCK_WIN
10197 && baseAnim != BOTH_LK_ST_DL_T_SB_1_W )
10198 {//going to attack with saber, do a saber trail
10199 duelist->client->ps.SaberActivateTrail( 200 );
10200 }
10201 return baseAnim;
10202 }
10203
PM_SaberLockBreak(gentity_t * gent,gentity_t * genemy,saberLockResult_t result,int victoryStrength)10204 void PM_SaberLockBreak( gentity_t *gent, gentity_t *genemy, saberLockResult_t result, int victoryStrength )
10205 {
10206 int winAnim = -1, loseAnim = -1;
10207 int breakType = SABERLOCK_BREAK;
10208 qboolean singleVsSingle = qtrue;
10209
10210 if ( result == LOCK_VICTORY
10211 && Q_irand(0,7) < victoryStrength )
10212 {
10213 if ( genemy
10214 && genemy->NPC
10215 && ((genemy->NPC->aiFlags&NPCAI_BOSS_CHARACTER)
10216 ||(genemy->NPC->aiFlags&NPCAI_SUBBOSS_CHARACTER)
10217 ||(genemy->client&&genemy->client->NPC_class == CLASS_SHADOWTROOPER))
10218 && Q_irand(0, 4)
10219 )
10220 {//less of a chance of getting a superbreak against a boss
10221 breakType = SABERLOCK_BREAK;
10222 }
10223 else
10224 {
10225 breakType = SABERLOCK_SUPERBREAK;
10226 }
10227 }
10228 else
10229 {
10230 breakType = SABERLOCK_BREAK;
10231 }
10232 winAnim = PM_SaberLockWinAnim( result, breakType );
10233 if ( winAnim != -1 )
10234 {//a single vs. single break
10235 if ( genemy && genemy->client )
10236 {
10237 loseAnim = PM_SaberLockLoseAnim( genemy, result, breakType );
10238 }
10239 }
10240 else
10241 {//must be a saberlock that's not between single and single...
10242 singleVsSingle = qfalse;
10243 winAnim = PM_SaberLockResultAnim( gent, breakType, SABERLOCK_WIN );
10244 pm->ps->weaponstate = WEAPON_FIRING;
10245 if ( genemy && genemy->client )
10246 {
10247 loseAnim = PM_SaberLockResultAnim( genemy, breakType, SABERLOCK_LOSE );
10248 genemy->client->ps.weaponstate = WEAPON_READY;
10249 }
10250 }
10251
10252 if ( d_saberCombat->integer )
10253 {
10254 Com_Printf( "%s won saber lock, anim = %s!\n", gent->NPC_type, animTable[winAnim].name );
10255 Com_Printf( "%s lost saber lock, anim = %s!\n", genemy->NPC_type, animTable[loseAnim].name );
10256 }
10257
10258 pm->ps->saberLockTime = genemy->client->ps.saberLockTime = 0;
10259 pm->ps->saberLockEnemy = genemy->client->ps.saberLockEnemy = ENTITYNUM_NONE;
10260 pm->ps->saberMoveNext = LS_NONE;
10261 if ( genemy && genemy->client )
10262 {
10263 genemy->client->ps.saberMoveNext = LS_NONE;
10264 }
10265
10266 PM_AddEvent( EV_JUMP );
10267 if ( result == LOCK_STALEMATE )
10268 {//no-one won
10269 G_AddEvent( genemy, EV_JUMP, 0 );
10270 }
10271 else
10272 {
10273 if ( pm->ps->clientNum )
10274 {//an NPC
10275 pm->ps->saberEventFlags |= SEF_LOCK_WON;//tell the winner to press the advantage
10276 }
10277 //painDebounceTime will stop them from doing anything
10278 genemy->painDebounceTime = level.time + genemy->client->ps.torsoAnimTimer + 500;
10279 if ( Q_irand( 0, 1 ) )
10280 {
10281 G_AddEvent( genemy, EV_PAIN, Q_irand( 0, 75 ) );
10282 }
10283 else
10284 {
10285 if ( genemy->NPC )
10286 {
10287 genemy->NPC->blockedSpeechDebounceTime = 0;
10288 }
10289 G_AddVoiceEvent( genemy, Q_irand( EV_PUSHED1, EV_PUSHED3 ), 500 );
10290 }
10291 if ( result == LOCK_VICTORY )
10292 {//one person won
10293 if ( Q_irand( FORCE_LEVEL_1, FORCE_LEVEL_2 ) < pm->ps->forcePowerLevel[FP_SABER_OFFENSE] )
10294 {
10295 vec3_t throwDir = {0,0,350};
10296 int winMove = pm->ps->saberMove;
10297 if ( !singleVsSingle )
10298 {//all others have their own super breaks
10299 //so it doesn't try to set some other anim below
10300 winAnim = -1;
10301 }
10302 else if ( winAnim == BOTH_LK_S_S_S_SB_1_W
10303 || winAnim == BOTH_LK_S_S_T_SB_1_W )
10304 {//doing a superbreak on single-vs-single, don't do the old superbreaks this time
10305 //so it doesn't try to set some other anim below
10306 winAnim = -1;
10307 }
10308 else
10309 {//JK2-style
10310 switch ( winAnim )
10311 {
10312 case BOTH_A3_T__B_:
10313 winAnim = BOTH_D1_TL___;
10314 winMove = LS_D1_TL;
10315 //FIXME: mod throwDir?
10316 break;
10317 case BOTH_K1_S1_T_:
10318 //FIXME: mod throwDir?
10319 break;
10320 case BOTH_CWCIRCLEBREAK:
10321 //FIXME: mod throwDir?
10322 break;
10323 case BOTH_CCWCIRCLEBREAK:
10324 winAnim = BOTH_A1_BR_TL;
10325 winMove = LS_A_BR2TL;
10326 //FIXME: mod throwDir?
10327 break;
10328 }
10329 if ( winAnim != BOTH_CCWCIRCLEBREAK )
10330 {
10331 if ( (!genemy->s.number&&genemy->health<=25)//player low on health
10332 ||(genemy->s.number&&genemy->client->NPC_class!=CLASS_KYLE&&genemy->client->NPC_class!=CLASS_LUKE&&genemy->client->NPC_class!=CLASS_TAVION&&genemy->client->NPC_class!=CLASS_ALORA&&genemy->client->NPC_class!=CLASS_DESANN)//any NPC that's not a boss character
10333 ||(genemy->s.number&&genemy->health<=50) )//boss character with less than 50 health left
10334 {//possibly knock saber out of hand OR cut hand off!
10335 if ( Q_irand( 0, 25 ) < victoryStrength
10336 && ((!genemy->s.number&&genemy->health<=10)||genemy->s.number) )
10337 {
10338 NPC_SetAnim( genemy, SETANIM_BOTH, BOTH_RIGHTHANDCHOPPEDOFF, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );//force this
10339 genemy->client->dismembered = false;
10340 G_DoDismemberment( genemy, genemy->client->renderInfo.handRPoint, MOD_SABER, 1000, HL_HAND_RT, qtrue );
10341 G_Damage( genemy, gent, gent, throwDir, genemy->client->renderInfo.handRPoint, genemy->health+10, DAMAGE_NO_PROTECTION|DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_SABER, HL_NONE );
10342
10343 PM_SetAnim( pm, SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
10344 pm->ps->weaponTime = pm->ps->torsoAnimTimer + 500;
10345 pm->ps->saberMove = winMove;
10346 pm->ps->saberBlocked = BLOCKED_NONE;
10347 pm->ps->weaponstate = WEAPON_FIRING;
10348 return;
10349 }
10350 }
10351 }
10352 }
10353 //else see if we can knock the saber out of their hand
10354 //FIXME: for now, always disarm the right-hand saber
10355 if ( !(genemy->client->ps.saber[0].saberFlags&SFL_NOT_DISARMABLE) )
10356 {
10357 //add disarmBonus into this check
10358 victoryStrength += pm->ps->SaberDisarmBonus( 0 )*2;
10359 if ( (genemy->client->ps.saber[0].saberFlags&SFL_TWO_HANDED)
10360 || (genemy->client->ps.dualSabers && genemy->client->ps.saber[1].Active()) )
10361 {//defender gets a bonus for using a 2-handed saber or 2 sabers
10362 victoryStrength -= 2;
10363 }
10364 if ( pm->ps->forcePowersActive&(1<<FP_RAGE) )
10365 {
10366 victoryStrength += gent->client->ps.forcePowerLevel[FP_RAGE];
10367 }
10368 else if ( pm->ps->forceRageRecoveryTime > pm->cmd.serverTime )
10369 {
10370 victoryStrength--;
10371 }
10372 if ( genemy->client->ps.forceRageRecoveryTime > pm->cmd.serverTime )
10373 {
10374 victoryStrength++;
10375 }
10376 if ( Q_irand( 0, 10 ) < victoryStrength )
10377 {
10378 if ( !(genemy->client->ps.saber[0].saberFlags&SFL_TWO_HANDED)
10379 || !Q_irand( 0, 1 ) )
10380 {//if it's a two-handed saber, it has a 50% chance of resisting a disarming
10381 WP_SaberLose( genemy, throwDir );
10382 if ( winAnim != -1 )
10383 {
10384 PM_SetAnim( pm, SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
10385 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
10386 pm->ps->saberMove = winMove;
10387 pm->ps->saberBlocked = BLOCKED_NONE;
10388 pm->ps->weaponstate = WEAPON_FIRING;
10389 }
10390 }
10391 }
10392 }
10393 }
10394 }
10395 }
10396 }
10397
G_SaberLockStrength(gentity_t * gent)10398 int G_SaberLockStrength( gentity_t *gent )
10399 {
10400 int strength = gent->client->ps.saber[0].lockBonus;
10401 if ( (gent->client->ps.saber[0].saberFlags&SFL_TWO_HANDED) )
10402 {
10403 strength += 1;
10404 }
10405 if ( gent->client->ps.dualSabers && gent->client->ps.saber[1].Active() )
10406 {
10407 strength += 1 + gent->client->ps.saber[1].lockBonus;
10408 }
10409 if ( gent->client->ps.forcePowersActive&(1<<FP_RAGE) )
10410 {
10411 strength += gent->client->ps.forcePowerLevel[FP_RAGE];
10412 }
10413 else if ( gent->client->ps.forceRageRecoveryTime > pm->cmd.serverTime )
10414 {
10415 strength--;
10416 }
10417 if ( gent->s.number >= MAX_CLIENTS )
10418 {
10419 if ( gent->client->NPC_class == CLASS_DESANN || gent->client->NPC_class == CLASS_LUKE )
10420 {
10421 strength += 5+Q_irand(0,g_spskill->integer);
10422 }
10423 else
10424 {
10425 strength += gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]+Q_irand(0,g_spskill->integer);
10426 if ( gent->NPC )
10427 {
10428 if ( (gent->NPC->aiFlags&NPCAI_BOSS_CHARACTER)
10429 || (gent->NPC->aiFlags&NPCAI_ROSH)
10430 || gent->client->NPC_class == CLASS_SHADOWTROOPER )
10431 {
10432 strength += Q_irand(0,2);
10433 }
10434 else if ( (gent->NPC->aiFlags&NPCAI_SUBBOSS_CHARACTER) )
10435 {
10436 strength += Q_irand(-1,1);
10437 }
10438 }
10439 }
10440 }
10441 else
10442 {//player
10443 strength += gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]+Q_irand(0,g_spskill->integer)+Q_irand(0,1);
10444 }
10445 return strength;
10446 }
10447
PM_InSaberLockOld(int anim)10448 qboolean PM_InSaberLockOld( int anim )
10449 {
10450 switch ( anim )
10451 {
10452 case BOTH_BF2LOCK:
10453 case BOTH_BF1LOCK:
10454 case BOTH_CWCIRCLELOCK:
10455 case BOTH_CCWCIRCLELOCK:
10456 return qtrue;
10457 }
10458 return qfalse;
10459 }
10460
PM_InSaberLock(int anim)10461 qboolean PM_InSaberLock( int anim )
10462 {
10463 switch ( anim )
10464 {
10465 case BOTH_LK_S_DL_S_L_1: //lock if I'm using single vs. a dual
10466 case BOTH_LK_S_DL_T_L_1: //lock if I'm using single vs. a dual
10467 case BOTH_LK_S_ST_S_L_1: //lock if I'm using single vs. a staff
10468 case BOTH_LK_S_ST_T_L_1: //lock if I'm using single vs. a staff
10469 case BOTH_LK_S_S_S_L_1: //lock if I'm using single vs. a single and I initiated
10470 case BOTH_LK_S_S_T_L_1: //lock if I'm using single vs. a single and I initiated
10471 case BOTH_LK_DL_DL_S_L_1: //lock if I'm using dual vs. dual and I initiated
10472 case BOTH_LK_DL_DL_T_L_1: //lock if I'm using dual vs. dual and I initiated
10473 case BOTH_LK_DL_ST_S_L_1: //lock if I'm using dual vs. a staff
10474 case BOTH_LK_DL_ST_T_L_1: //lock if I'm using dual vs. a staff
10475 case BOTH_LK_DL_S_S_L_1: //lock if I'm using dual vs. a single
10476 case BOTH_LK_DL_S_T_L_1: //lock if I'm using dual vs. a single
10477 case BOTH_LK_ST_DL_S_L_1: //lock if I'm using staff vs. dual
10478 case BOTH_LK_ST_DL_T_L_1: //lock if I'm using staff vs. dual
10479 case BOTH_LK_ST_ST_S_L_1: //lock if I'm using staff vs. a staff and I initiated
10480 case BOTH_LK_ST_ST_T_L_1: //lock if I'm using staff vs. a staff and I initiated
10481 case BOTH_LK_ST_S_S_L_1: //lock if I'm using staff vs. a single
10482 case BOTH_LK_ST_S_T_L_1: //lock if I'm using staff vs. a single
10483 case BOTH_LK_S_S_S_L_2:
10484 case BOTH_LK_S_S_T_L_2:
10485 case BOTH_LK_DL_DL_S_L_2:
10486 case BOTH_LK_DL_DL_T_L_2:
10487 case BOTH_LK_ST_ST_S_L_2:
10488 case BOTH_LK_ST_ST_T_L_2:
10489 return qtrue;
10490 break;
10491 default:
10492 return PM_InSaberLockOld( anim );
10493 break;
10494 }
10495 //return qfalse;
10496 }
10497
10498 extern qboolean ValidAnimFileIndex ( int index );
10499 extern qboolean G_CheckIncrementLockAnim( int anim, int winOrLose );
PM_SaberLocked(void)10500 qboolean PM_SaberLocked( void )
10501 {
10502 //FIXME: maybe kick out of saberlock?
10503 if ( pm->ps->saberLockEnemy == ENTITYNUM_NONE )
10504 {
10505 if ( PM_InSaberLock( pm->ps->torsoAnim ) )
10506 {//wtf? Maybe enemy died?
10507 PM_SaberLockWinAnim( LOCK_STALEMATE, SABERLOCK_BREAK );
10508 }
10509 return qfalse;
10510 }
10511 gentity_t *gent = pm->gent;
10512 if ( !gent )
10513 {
10514 return qfalse;
10515 }
10516 gentity_t *genemy = &g_entities[pm->ps->saberLockEnemy];
10517 if ( !genemy )
10518 {
10519 return qfalse;
10520 }
10521 if ( PM_InSaberLock( pm->ps->torsoAnim ) && PM_InSaberLock( genemy->client->ps.torsoAnim ) )
10522 {
10523 if ( pm->ps->saberLockTime <= level.time + 500
10524 && pm->ps->saberLockEnemy != ENTITYNUM_NONE )
10525 {//lock just ended
10526 int strength = G_SaberLockStrength( gent );
10527 int eStrength = G_SaberLockStrength( genemy );
10528 if ( strength > 1 && eStrength > 1 && !Q_irand( 0, abs(strength-eStrength)+1 ) )
10529 {//both knock each other down!
10530 PM_SaberLockBreak( gent, genemy, LOCK_DRAW, 0 );
10531 }
10532 else
10533 {//both "win"
10534 PM_SaberLockBreak( gent, genemy, LOCK_STALEMATE, 0 );
10535 }
10536 return qtrue;
10537 }
10538 else if ( pm->ps->saberLockTime < level.time )
10539 {//done... tie breaker above should have handled this, but...?
10540 if ( PM_InSaberLock( pm->ps->torsoAnim ) && pm->ps->torsoAnimTimer > 0 )
10541 {
10542 pm->ps->torsoAnimTimer = 0;
10543 }
10544 if ( PM_InSaberLock( pm->ps->legsAnim ) && pm->ps->legsAnimTimer > 0 )
10545 {
10546 pm->ps->legsAnimTimer = 0;
10547 }
10548 return qfalse;
10549 }
10550 else if ( pm->cmd.buttons & BUTTON_ATTACK )
10551 {//holding attack
10552 if ( !(pm->ps->pm_flags&PMF_ATTACK_HELD) )
10553 {//tapping
10554 int remaining = 0;
10555 if( ValidAnimFileIndex( gent->client->clientInfo.animFileIndex ) )
10556 {
10557 animation_t *anim;
10558 float currentFrame, junk2;
10559 int curFrame, junk;
10560 int strength = 1;
10561 anim = &level.knownAnimFileSets[gent->client->clientInfo.animFileIndex].animations[pm->ps->torsoAnim];
10562
10563 #ifdef _DEBUG
10564 qboolean ret =
10565 #endif
10566 gi.G2API_GetBoneAnimIndex( &gent->ghoul2[gent->playerModel], gent->lowerLumbarBone,
10567 (cg.time?cg.time:level.time), ¤tFrame, &junk, &junk, &junk, &junk2, NULL );
10568 #ifdef _DEBUG
10569 assert( ret ); // this would be pretty bad, the below code seems to assume the call succeeds. -gil
10570 #endif
10571 strength = G_SaberLockStrength( gent );
10572 if ( PM_InSaberLockOld( pm->ps->torsoAnim ) )
10573 {//old locks
10574 if ( pm->ps->torsoAnim == BOTH_CCWCIRCLELOCK ||
10575 pm->ps->torsoAnim == BOTH_BF2LOCK )
10576 {
10577 curFrame = floor( currentFrame )-strength;
10578 //drop my frame one
10579 if ( curFrame <= anim->firstFrame )
10580 {//I won! Break out
10581 PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, strength );
10582 return qtrue;
10583 }
10584 else
10585 {
10586 PM_SetAnimFrame( gent, curFrame, qtrue, qtrue );
10587 remaining = curFrame-anim->firstFrame;
10588 if ( d_saberCombat->integer )
10589 {
10590 Com_Printf( "%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining );
10591 }
10592 }
10593 }
10594 else
10595 {
10596 curFrame = ceil( currentFrame )+strength;
10597 //advance my frame one
10598 if ( curFrame >= anim->firstFrame+anim->numFrames )
10599 {//I won! Break out
10600 PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, strength );
10601 return qtrue;
10602 }
10603 else
10604 {
10605 PM_SetAnimFrame( gent, curFrame, qtrue, qtrue );
10606 remaining = anim->firstFrame+anim->numFrames-curFrame;
10607 if ( d_saberCombat->integer )
10608 {
10609 Com_Printf( "%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining );
10610 }
10611 }
10612 }
10613 }
10614 else
10615 {//new locks
10616 if ( G_CheckIncrementLockAnim( pm->ps->torsoAnim, SABERLOCK_WIN ) )
10617 {
10618 curFrame = ceil( currentFrame )+strength;
10619 //advance my frame one
10620 if ( curFrame >= anim->firstFrame+anim->numFrames )
10621 {//I won! Break out
10622 PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, strength );
10623 return qtrue;
10624 }
10625 else
10626 {
10627 PM_SetAnimFrame( gent, curFrame, qtrue, qtrue );
10628 remaining = anim->firstFrame+anim->numFrames-curFrame;
10629 if ( d_saberCombat->integer )
10630 {
10631 Com_Printf( "%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining );
10632 }
10633 }
10634 }
10635 else
10636 {
10637 curFrame = floor( currentFrame )-strength;
10638 //drop my frame one
10639 if ( curFrame <= anim->firstFrame )
10640 {//I won! Break out
10641 PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, strength );
10642 return qtrue;
10643 }
10644 else
10645 {
10646 PM_SetAnimFrame( gent, curFrame, qtrue, qtrue );
10647 remaining = curFrame-anim->firstFrame;
10648 if ( d_saberCombat->integer )
10649 {
10650 Com_Printf( "%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining );
10651 }
10652 }
10653 }
10654 }
10655 if ( !Q_irand( 0, 2 ) )
10656 {
10657 if ( pm->ps->clientNum < MAX_CLIENTS )
10658 {
10659 if ( !Q_irand( 0, 3 ) )
10660 {
10661 PM_AddEvent( EV_JUMP );
10662 }
10663 else
10664 {
10665 PM_AddEvent( Q_irand( EV_PUSHED1, EV_PUSHED3 ) );
10666 }
10667 }
10668 else
10669 {
10670 if ( gent->NPC && gent->NPC->blockedSpeechDebounceTime < level.time )
10671 {
10672 switch ( Q_irand( 0, 3 ) )
10673 {
10674 case 0:
10675 PM_AddEvent( EV_JUMP );
10676 break;
10677 case 1:
10678 PM_AddEvent( Q_irand( EV_ANGER1, EV_ANGER3 ) );
10679 gent->NPC->blockedSpeechDebounceTime = level.time + 3000;
10680 break;
10681 case 2:
10682 PM_AddEvent( Q_irand( EV_TAUNT1, EV_TAUNT3 ) );
10683 gent->NPC->blockedSpeechDebounceTime = level.time + 3000;
10684 break;
10685 case 3:
10686 PM_AddEvent( Q_irand( EV_GLOAT1, EV_GLOAT3 ) );
10687 gent->NPC->blockedSpeechDebounceTime = level.time + 3000;
10688 break;
10689 }
10690 }
10691 }
10692 }
10693 }
10694 else
10695 {
10696 return qfalse;
10697 }
10698
10699 if( ValidAnimFileIndex( genemy->client->clientInfo.animFileIndex ) )
10700 {
10701 animation_t *anim;
10702 anim = &level.knownAnimFileSets[genemy->client->clientInfo.animFileIndex].animations[genemy->client->ps.torsoAnim];
10703 /*
10704 float currentFrame, junk2;
10705 int junk;
10706
10707 gi.G2API_GetBoneAnimIndex( &genemy->ghoul2[genemy->playerModel], genemy->lowerLumbarBone, (cg.time?cg.time:level.time), ¤tFrame, &junk, &junk, &junk, &junk2, NULL );
10708 */
10709
10710 if ( !Q_irand( 0, 2 ) )
10711 {
10712 switch ( Q_irand( 0, 3 ) )
10713 {
10714 case 0:
10715 G_AddEvent( genemy, EV_PAIN, floor((float)genemy->health/genemy->max_health*100.0f) );
10716 break;
10717 case 1:
10718 G_AddVoiceEvent( genemy, Q_irand( EV_PUSHED1, EV_PUSHED3 ), 500 );
10719 break;
10720 case 2:
10721 G_AddVoiceEvent( genemy, Q_irand( EV_CHOKE1, EV_CHOKE3 ), 500 );
10722 break;
10723 case 3:
10724 G_AddVoiceEvent( genemy, EV_PUSHFAIL, 2000 );
10725 break;
10726 }
10727 }
10728
10729 if ( PM_InSaberLockOld( genemy->client->ps.torsoAnim ) )
10730 {
10731 if ( genemy->client->ps.torsoAnim == BOTH_CCWCIRCLELOCK ||
10732 genemy->client->ps.torsoAnim == BOTH_BF2LOCK )
10733 {
10734 PM_SetAnimFrame( genemy, anim->firstFrame+anim->numFrames-remaining, qtrue, qtrue );
10735 }
10736 else
10737 {
10738 PM_SetAnimFrame( genemy, anim->firstFrame+remaining, qtrue, qtrue );
10739 }
10740 }
10741 else
10742 {//new locks
10743 //???
10744 if ( G_CheckIncrementLockAnim( genemy->client->ps.torsoAnim, SABERLOCK_LOSE ) )
10745 {
10746 PM_SetAnimFrame( genemy, anim->firstFrame+anim->numFrames-remaining, qtrue, qtrue );
10747 }
10748 else
10749 {
10750 PM_SetAnimFrame( genemy, anim->firstFrame+remaining, qtrue, qtrue );
10751 }
10752 }
10753 }
10754 }
10755 }
10756 else
10757 {//FIXME: other ways out of a saberlock?
10758 //force-push? (requires more force power?)
10759 //kick? (requires anim ... hit jump key?)
10760 //roll?
10761 //backflip?
10762 }
10763 }
10764 else
10765 {//something broke us out of it
10766 if ( gent->painDebounceTime > level.time && genemy->painDebounceTime > level.time )
10767 {
10768 PM_SaberLockBreak( gent, genemy, LOCK_DRAW, 0 );
10769 }
10770 else if ( gent->painDebounceTime > level.time )
10771 {
10772 PM_SaberLockBreak( genemy, gent, LOCK_VICTORY, 0 );
10773 }
10774 else if ( genemy->painDebounceTime > level.time )
10775 {
10776 PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, 0 );
10777 }
10778 else
10779 {
10780 PM_SaberLockBreak( gent, genemy, LOCK_STALEMATE, 0 );
10781 }
10782 }
10783 return qtrue;
10784 }
10785
G_EnemyInKickRange(gentity_t * self,gentity_t * enemy)10786 qboolean G_EnemyInKickRange( gentity_t *self, gentity_t *enemy )
10787 {
10788 if ( !self || !enemy )
10789 {
10790 return qfalse;
10791 }
10792 if ( fabs(self->currentOrigin[2]-enemy->currentOrigin[2]) < 32 )
10793 {//generally at same height
10794 if ( DistanceHorizontal( self->currentOrigin, enemy->currentOrigin ) <= (STAFF_KICK_RANGE+8.0f+(self->maxs[0]*1.5f)+(enemy->maxs[0]*1.5f)) )
10795 {//within kicking range!
10796 return qtrue;
10797 }
10798 }
10799 return qfalse;
10800 }
10801
G_CanKickEntity(gentity_t * self,gentity_t * target)10802 qboolean G_CanKickEntity( gentity_t *self, gentity_t *target )
10803 {
10804 if ( target && target->client
10805 && !PM_InKnockDown( &target->client->ps )
10806 && G_EnemyInKickRange( self, target ) )
10807 {
10808 return qtrue;
10809 }
10810 return qfalse;
10811 }
10812
PM_GroundDistance(void)10813 float PM_GroundDistance(void)
10814 {
10815 trace_t tr;
10816 vec3_t down;
10817
10818 VectorCopy(pm->ps->origin, down);
10819
10820 down[2] -= 4096;
10821
10822 pm->trace(&tr, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0);
10823
10824 VectorSubtract(pm->ps->origin, tr.endpos, down);
10825
10826 return VectorLength(down);
10827 }
10828
G_GroundDistance(gentity_t * self)10829 float G_GroundDistance(gentity_t *self)
10830 {
10831 if ( !self )
10832 {//wtf?!!
10833 return Q3_INFINITE;
10834 }
10835 trace_t tr;
10836 vec3_t down;
10837
10838 VectorCopy(self->currentOrigin, down);
10839
10840 down[2] -= 4096;
10841
10842 gi.trace(&tr, self->currentOrigin, self->mins, self->maxs, down, self->s.number, self->clipmask, (EG2_Collision)0, 0);
10843
10844 VectorSubtract(self->currentOrigin, tr.endpos, down);
10845
10846 return VectorLength(down);
10847 }
10848
G_PickAutoKick(gentity_t * self,gentity_t * enemy,qboolean storeMove)10849 saberMoveName_t G_PickAutoKick( gentity_t *self, gentity_t *enemy, qboolean storeMove )
10850 {
10851 saberMoveName_t kickMove = LS_NONE;
10852 if ( !self || !self->client )
10853 {
10854 return LS_NONE;
10855 }
10856 if ( !enemy )
10857 {
10858 return LS_NONE;
10859 }
10860 vec3_t v_fwd, v_rt, enemyDir, fwdAngs = {0,self->client->ps.viewangles[YAW],0};
10861 VectorSubtract( enemy->currentOrigin, self->currentOrigin, enemyDir );
10862 VectorNormalize( enemyDir );//not necessary, I guess, but doesn't happen often
10863 AngleVectors( fwdAngs, v_fwd, v_rt, NULL );
10864 float fDot = DotProduct( enemyDir, v_fwd );
10865 float rDot = DotProduct( enemyDir, v_rt );
10866 if ( fabs( rDot ) > 0.5f && fabs( fDot ) < 0.5f )
10867 {//generally to one side
10868 if ( rDot > 0 )
10869 {//kick right
10870 kickMove = LS_KICK_R;
10871 }
10872 else
10873 {//kick left
10874 kickMove = LS_KICK_L;
10875 }
10876 }
10877 else if ( fabs( fDot ) > 0.5f && fabs( rDot ) < 0.5f )
10878 {//generally in front or behind us
10879 if ( fDot > 0 )
10880 {//kick fwd
10881 kickMove = LS_KICK_F;
10882 }
10883 else
10884 {//kick back
10885 kickMove = LS_KICK_B;
10886 }
10887 }
10888 else
10889 {//diagonal to us, kick would miss
10890 }
10891 if ( kickMove != LS_NONE )
10892 {//have a valid one to do
10893 if ( self->client->ps.groundEntityNum == ENTITYNUM_NONE )
10894 {//if in air, convert kick to an in-air kick
10895 float gDist = G_GroundDistance( self );
10896 //let's only allow air kicks if a certain distance from the ground
10897 //it's silly to be able to do them right as you land.
10898 //also looks wrong to transition from a non-complete flip anim...
10899 if ((!PM_FlippingAnim( self->client->ps.legsAnim ) || self->client->ps.legsAnimTimer <= 0) &&
10900 gDist > 64.0f && //strict minimum
10901 gDist > (-self->client->ps.velocity[2])-64.0f //make sure we are high to ground relative to downward velocity as well
10902 )
10903 {
10904 switch ( kickMove )
10905 {
10906 case LS_KICK_F:
10907 kickMove = LS_KICK_F_AIR;
10908 break;
10909 case LS_KICK_B:
10910 kickMove = LS_KICK_B_AIR;
10911 break;
10912 case LS_KICK_R:
10913 kickMove = LS_KICK_R_AIR;
10914 break;
10915 case LS_KICK_L:
10916 kickMove = LS_KICK_L_AIR;
10917 break;
10918 default: //oh well, can't do any other kick move while in-air
10919 kickMove = LS_NONE;
10920 break;
10921 }
10922 }
10923 else
10924 {//leave it as a normal kick unless we're too high up
10925 if ( gDist > 128.0f || self->client->ps.velocity[2] >= 0 )
10926 { //off ground, but too close to ground
10927 kickMove = LS_NONE;
10928 }
10929 }
10930 }
10931 if ( storeMove )
10932 {
10933 self->client->ps.saberMoveNext = kickMove;
10934 }
10935 }
10936 return kickMove;
10937 }
10938
PM_PickAutoKick(gentity_t * enemy)10939 saberMoveName_t PM_PickAutoKick( gentity_t *enemy )
10940 {
10941 return G_PickAutoKick( pm->gent, enemy, qfalse );
10942 }
10943
G_PickAutoMultiKick(gentity_t * self,qboolean allowSingles,qboolean storeMove)10944 saberMoveName_t G_PickAutoMultiKick( gentity_t *self, qboolean allowSingles, qboolean storeMove )
10945 {
10946 gentity_t *ent;
10947 gentity_t *entityList[MAX_GENTITIES];
10948 vec3_t mins, maxs;
10949 int i, e;
10950 int radius = ((self->maxs[0]*1.5f)+(self->maxs[0]*1.5f)+STAFF_KICK_RANGE+24.0f);//a little wide on purpose
10951 vec3_t center;
10952 saberMoveName_t kickMove, bestKick = LS_NONE;
10953 float distToEnt, bestDistToEnt = Q3_INFINITE;
10954 gentity_t *bestEnt = NULL;
10955 int enemiesFront = 0;
10956 int enemiesBack = 0;
10957 int enemiesRight = 0;
10958 int enemiesLeft = 0;
10959 int enemiesSpin = 0;
10960
10961 if ( !self || !self->client )
10962 {
10963 return LS_NONE;
10964 }
10965
10966 VectorCopy( self->currentOrigin, center );
10967
10968 for ( i = 0 ; i < 3 ; i++ )
10969 {
10970 mins[i] = center[i] - radius;
10971 maxs[i] = center[i] + radius;
10972 }
10973
10974 int numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
10975
10976 for ( e = 0 ; e < numListedEntities ; e++ )
10977 {
10978 ent = entityList[ e ];
10979
10980 if (ent == self)
10981 continue;
10982 if (ent->owner == self)
10983 continue;
10984 if ( !(ent->inuse) )
10985 continue;
10986 //not a client?
10987 if ( !ent->client )
10988 continue;
10989 //ally?
10990 if ( ent->client->playerTeam == self->client->playerTeam )
10991 continue;
10992 //dead?
10993 if ( ent->health <= 0 )
10994 continue;
10995 //too far?
10996 distToEnt = DistanceSquared( ent->currentOrigin, center );
10997 if ( distToEnt > (radius*radius) )
10998 continue;
10999 kickMove = G_PickAutoKick( self, ent, qfalse );
11000 if ( kickMove == LS_KICK_F_AIR
11001 && kickMove == LS_KICK_B_AIR
11002 && kickMove == LS_KICK_R_AIR
11003 && kickMove == LS_KICK_L_AIR )
11004 {//in air? Can't do multikicks
11005 }
11006 else
11007 {
11008 switch ( kickMove )
11009 {
11010 case LS_KICK_F:
11011 enemiesFront++;
11012 break;
11013 case LS_KICK_B:
11014 enemiesBack++;
11015 break;
11016 case LS_KICK_R:
11017 enemiesRight++;
11018 break;
11019 case LS_KICK_L:
11020 enemiesLeft++;
11021 break;
11022 default:
11023 enemiesSpin++;
11024 break;
11025 }
11026 }
11027 if ( allowSingles )
11028 {
11029 if ( kickMove != LS_NONE
11030 && distToEnt < bestDistToEnt )
11031 {
11032 bestKick = kickMove;
11033 bestEnt = ent;
11034 }
11035 }
11036 }
11037 kickMove = LS_NONE;
11038 if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE )
11039 {//can't do the multikicks in air
11040 if ( enemiesFront && enemiesBack
11041 && (enemiesFront+enemiesBack)-(enemiesRight+enemiesLeft)>1 )
11042 {//more enemies in front/back than left/right
11043 kickMove = LS_KICK_BF;
11044 }
11045 else if ( enemiesRight && enemiesLeft
11046 && (enemiesRight+enemiesLeft)-(enemiesFront+enemiesBack)>1 )
11047 {//more enemies on left & right than front/back
11048 kickMove = LS_KICK_RL;
11049 }
11050 else if ( (enemiesFront || enemiesBack) && (enemiesRight || enemiesLeft) )
11051 {//at least 2 enemies around us, not aligned
11052 kickMove = LS_KICK_S;
11053 }
11054 else if ( enemiesSpin > 1 )
11055 {//at least 2 enemies around us, not aligned
11056 kickMove = LS_KICK_S;
11057 }
11058 }
11059 if ( kickMove == LS_NONE
11060 && bestKick != LS_NONE )
11061 {//no good multi-kick move, but we do have a nice single-kick we found
11062 kickMove = bestKick;
11063 //get mad at him so he knows he's being targetted
11064 if ( (self->s.number < MAX_CLIENTS||G_ControlledByPlayer(self))
11065 && bestEnt != NULL )
11066 {//player
11067 G_SetEnemy( self, bestEnt );
11068 }
11069 }
11070 if ( kickMove != LS_NONE )
11071 {
11072 if ( storeMove )
11073 {
11074 self->client->ps.saberMoveNext = kickMove;
11075 }
11076 }
11077 return kickMove;
11078 }
11079
PM_PickAutoMultiKick(qboolean allowSingles)11080 qboolean PM_PickAutoMultiKick( qboolean allowSingles )
11081 {
11082 saberMoveName_t kickMove = G_PickAutoMultiKick( pm->gent, allowSingles, qfalse );
11083 if ( kickMove != LS_NONE )
11084 {
11085 PM_SetSaberMove( kickMove );
11086 return qtrue;
11087 }
11088 return qfalse;
11089 }
11090
PM_SaberThrowable(void)11091 qboolean PM_SaberThrowable( void )
11092 {
11093 //ugh, hard-coding this is bad...
11094 if ( pm->ps->saberAnimLevel == SS_STAFF )
11095 {
11096 return qfalse;
11097 }
11098
11099 if ( !(pm->ps->saber[0].saberFlags&SFL_NOT_THROWABLE) )
11100 {//yes, this saber is always throwable
11101 return qtrue;
11102 }
11103
11104 //saber is not normally throwable
11105 if ( (pm->ps->saber[0].saberFlags&SFL_SINGLE_BLADE_THROWABLE) )
11106 {//it is throwable if only one blade is on
11107 if ( pm->ps->saber[0].numBlades > 1 )
11108 {//it has more than one blade
11109 int numBladesActive = 0;
11110 for ( int i = 0; i < pm->ps->saber[0].numBlades; i++ )
11111 {
11112 if ( pm->ps->saber[0].blade[i].active )
11113 {
11114 numBladesActive++;
11115 }
11116 }
11117 if ( numBladesActive == 1 )
11118 {//only 1 blade is on
11119 return qtrue;
11120 }
11121 }
11122 }
11123 //nope, can't throw it
11124 return qfalse;
11125 }
11126
PM_CheckAltKickAttack(void)11127 qboolean PM_CheckAltKickAttack( void )
11128 {
11129 if ( (pm->cmd.buttons&BUTTON_ALT_ATTACK)
11130 && (!(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD) ||PM_SaberInReturn(pm->ps->saberMove))
11131 && (!PM_FlippingAnim(pm->ps->legsAnim)||pm->ps->legsAnimTimer<=250)
11132 && (!PM_SaberThrowable())
11133 && pm->ps->SaberActive()
11134 && !(pm->ps->saber[0].saberFlags&SFL_NO_KICKS)//okay to do kicks with this saber
11135 && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_KICKS) )//okay to do kicks with this saber
11136 )
11137 {
11138 return qtrue;
11139 }
11140 return qfalse;
11141 }
11142
PM_CheckUpsideDownAttack(void)11143 qboolean PM_CheckUpsideDownAttack( void )
11144 {
11145 if ( pm->ps->saberMove != LS_READY )
11146 {
11147 return qfalse;
11148 }
11149 if ( !(pm->cmd.buttons&BUTTON_ATTACK) )
11150 {
11151 return qfalse;
11152 }
11153 if ( pm->ps->saberAnimLevel < SS_FAST
11154 || pm->ps->saberAnimLevel > SS_STRONG )
11155 {
11156 return qfalse;
11157 }
11158 if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) )
11159 {//FIXME: check ranks?
11160 return qfalse;
11161 }
11162 //FIXME: enemy below
11163 //FIXME: more than 64 off ground
11164 if ( !g_debugMelee->integer )
11165 {//hmm, can't get this to work quite the way we wanted... secret move, then!
11166 return qfalse;
11167 }
11168
11169 switch( pm->ps->legsAnim )
11170 {
11171 case BOTH_WALL_RUN_RIGHT_FLIP:
11172 case BOTH_WALL_RUN_LEFT_FLIP:
11173 case BOTH_WALL_FLIP_RIGHT:
11174 case BOTH_WALL_FLIP_LEFT:
11175 case BOTH_FLIP_BACK1:
11176 case BOTH_FLIP_BACK2:
11177 case BOTH_FLIP_BACK3:
11178 case BOTH_WALL_FLIP_BACK1:
11179 case BOTH_ALORA_FLIP_B:
11180 //JKA
11181 case BOTH_FORCEWALLRUNFLIP_END:
11182 {
11183 float animLength = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, (animNumber_t)pm->ps->legsAnim );
11184 float elapsedTime = (float)(animLength-pm->ps->legsAnimTimer);
11185 float midPoint = animLength/2.0f;
11186 if ( elapsedTime < midPoint-100.0f
11187 || elapsedTime > midPoint+100.0f )
11188 {//only a 200ms window (in middle of anim) of opportunity to do this move in these anims
11189 return qfalse;
11190 }
11191 }
11192 //NOTE: falls through on purpose
11193 case BOTH_FLIP_HOLD7:
11194 pm->ps->pm_flags |= PMF_SLOW_MO_FALL;
11195 PM_SetSaberMove( LS_UPSIDE_DOWN_ATTACK );
11196 return qtrue;
11197 break;
11198 }
11199 return qfalse;
11200 }
11201
PM_SaberMoveOkayForKata(void)11202 qboolean PM_SaberMoveOkayForKata( void )
11203 {
11204 if ( g_saberNewControlScheme->integer )
11205 {
11206 if ( pm->ps->saberMove == LS_READY //not doing anything
11207 || PM_SaberInReflect( pm->ps->saberMove ) )//interrupt a projectile blocking move
11208 {
11209 return qtrue;
11210 }
11211 else
11212 {
11213 return qfalse;
11214 }
11215 }
11216 else
11217 {//old control scheme, allow it to interrupt a start or ready
11218 if ( pm->ps->saberMove == LS_READY
11219 || PM_SaberInReflect( pm->ps->saberMove )//interrupt a projectile blocking move
11220 || PM_SaberInStart( pm->ps->saberMove ) )
11221 {
11222 return qtrue;
11223 }
11224 else
11225 {
11226 return qfalse;
11227 }
11228 }
11229 }
11230
PM_CanDoKata(void)11231 qboolean PM_CanDoKata( void )
11232 {
11233 if ( PM_InSecondaryStyle() )
11234 {
11235 return qfalse;
11236 }
11237 if ( !pm->ps->saberInFlight//not throwing saber
11238 && PM_SaberMoveOkayForKata()
11239 /*
11240 && pm->ps->saberAnimLevel >= SS_FAST//fast, med or strong style
11241 && pm->ps->saberAnimLevel <= SS_STRONG//FIXME: Tavion, too?
11242 */
11243 && pm->ps->groundEntityNum != ENTITYNUM_NONE//not in the air
11244 && (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack
11245 && pm->cmd.forwardmove >=0 //not moving back (used to be !pm->cmd.forwardmove)
11246 && !pm->cmd.rightmove//not moving r/l
11247 && pm->cmd.upmove <= 0//not jumping...?
11248 && G_TryingKataAttack(pm->gent,&pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*///holding focus
11249 && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER*/ )//SINGLE_SPECIAL_POWER )// have enough power
11250 {//FIXME: check rage, etc...
11251 return qtrue;
11252 }
11253 return qfalse;
11254 }
11255
PM_SaberDroidWeapon(void)11256 void PM_SaberDroidWeapon( void )
11257 {
11258 // make weapon function
11259 if ( pm->ps->weaponTime > 0 ) {
11260 pm->ps->weaponTime -= pml.msec;
11261 if ( pm->ps->weaponTime <= 0 )
11262 {
11263 pm->ps->weaponTime = 0;
11264 }
11265 }
11266
11267 // Now we react to a block action by the player's lightsaber.
11268 if ( pm->ps->saberBlocked )
11269 {
11270 switch ( pm->ps->saberBlocked )
11271 {
11272 case BLOCKED_PARRY_BROKEN:
11273 PM_SetAnim( pm, SETANIM_BOTH, Q_irand(BOTH_PAIN1,BOTH_PAIN3), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
11274 pm->ps->weaponTime = pm->ps->legsAnimTimer;
11275 break;
11276 case BLOCKED_ATK_BOUNCE:
11277 PM_SetAnim( pm, SETANIM_BOTH, Q_irand(BOTH_PAIN1,BOTH_PAIN3), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
11278 pm->ps->weaponTime = pm->ps->legsAnimTimer;
11279 break;
11280 case BLOCKED_UPPER_RIGHT:
11281 case BLOCKED_UPPER_RIGHT_PROJ:
11282 case BLOCKED_LOWER_RIGHT:
11283 case BLOCKED_LOWER_RIGHT_PROJ:
11284 PM_SetAnim( pm, SETANIM_BOTH, BOTH_P1_S1_TR, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
11285 pm->ps->legsAnimTimer += Q_irand( 200, 1000 );
11286 pm->ps->weaponTime = pm->ps->legsAnimTimer;
11287 break;
11288 case BLOCKED_UPPER_LEFT:
11289 case BLOCKED_UPPER_LEFT_PROJ:
11290 case BLOCKED_LOWER_LEFT:
11291 case BLOCKED_LOWER_LEFT_PROJ:
11292 PM_SetAnim( pm, SETANIM_BOTH, BOTH_P1_S1_TL, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
11293 pm->ps->legsAnimTimer += Q_irand( 200, 1000 );
11294 pm->ps->weaponTime = pm->ps->legsAnimTimer;
11295 break;
11296 case BLOCKED_TOP:
11297 case BLOCKED_TOP_PROJ:
11298 PM_SetAnim( pm, SETANIM_BOTH, BOTH_P1_S1_T_, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
11299 pm->ps->legsAnimTimer += Q_irand( 200, 1000 );
11300 pm->ps->weaponTime = pm->ps->legsAnimTimer;
11301 break;
11302 default:
11303 pm->ps->saberBlocked = BLOCKED_NONE;
11304 break;
11305 }
11306
11307 pm->ps->saberBlocked = BLOCKED_NONE;
11308 pm->ps->saberBounceMove = LS_NONE;
11309 pm->ps->weaponstate = WEAPON_READY;
11310
11311 // Done with block, so stop these active weapon branches.
11312 return;
11313 }
11314 }
11315
PM_TryGrab(void)11316 void PM_TryGrab( void )
11317 {
11318 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE
11319 //&& !pm->ps->saberInFlight
11320 && pm->ps->weaponTime <= 0 )//< 200 )
11321 {
11322 PM_SetAnim( pm, SETANIM_BOTH, BOTH_KYLE_GRAB, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
11323 pm->ps->torsoAnimTimer += 200;
11324 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11325 pm->ps->saberMove = pm->ps->saberMoveNext = LS_READY;
11326 VectorClear( pm->ps->velocity );
11327 VectorClear( pm->ps->moveDir );
11328 pm->cmd.rightmove = pm->cmd.forwardmove = pm->cmd.upmove = 0;
11329 if ( pm->gent )
11330 {
11331 pm->gent->painDebounceTime = level.time + pm->ps->torsoAnimTimer;
11332 }
11333 pm->ps->SaberDeactivate();
11334 }
11335 }
11336
PM_TryAirKick(saberMoveName_t kickMove)11337 void PM_TryAirKick( saberMoveName_t kickMove )
11338 {
11339 if ( pm->ps->groundEntityNum < ENTITYNUM_NONE )
11340 {//just do it
11341 PM_SetSaberMove( kickMove );
11342 }
11343 else
11344 {
11345 float gDist = PM_GroundDistance();
11346 //let's only allow air kicks if a certain distance from the ground
11347 //it's silly to be able to do them right as you land.
11348 //also looks wrong to transition from a non-complete flip anim...
11349 if ((!PM_FlippingAnim( pm->ps->legsAnim ) || pm->ps->legsAnimTimer <= 0) &&
11350 gDist > 64.0f && //strict minimum
11351 gDist > (-pm->ps->velocity[2])-64.0f //make sure we are high to ground relative to downward velocity as well
11352 )
11353 {
11354 PM_SetSaberMove( kickMove );
11355 }
11356 else
11357 {//leave it as a normal kick unless we're too high up
11358 if ( gDist > 128.0f || pm->ps->velocity[2] >= 0 )
11359 { //off ground, but too close to ground
11360 }
11361 else
11362 {//high close enough to ground to do a normal kick, convert it
11363 switch ( kickMove )
11364 {
11365 case LS_KICK_F_AIR:
11366 PM_SetSaberMove( LS_KICK_F );
11367 break;
11368 case LS_KICK_B_AIR:
11369 PM_SetSaberMove( LS_KICK_B );
11370 break;
11371 case LS_KICK_R_AIR:
11372 PM_SetSaberMove( LS_KICK_R );
11373 break;
11374 case LS_KICK_L_AIR:
11375 PM_SetSaberMove( LS_KICK_L );
11376 break;
11377 default:
11378 break;
11379 }
11380 }
11381 }
11382 }
11383 }
11384
PM_CheckKick(void)11385 void PM_CheckKick( void )
11386 {
11387 if ( !PM_KickMove( pm->ps->saberMove )//not already in a kick
11388 && !(pm->ps->pm_flags&PMF_DUCKED)//not ducked
11389 && (pm->cmd.upmove >= 0 ) )//not trying to duck
11390 {//player kicks
11391 //FIXME: only if FP_SABER_OFFENSE >= 3
11392 if ( pm->cmd.rightmove )
11393 {//kick to side
11394 if ( pm->cmd.rightmove > 0 )
11395 {//kick right
11396 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
11397 || pm->cmd.upmove > 0 )
11398 {
11399 PM_TryAirKick( LS_KICK_R_AIR );
11400 }
11401 else
11402 {
11403 PM_SetSaberMove( LS_KICK_R );
11404 }
11405 }
11406 else
11407 {//kick left
11408 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
11409 || pm->cmd.upmove > 0 )
11410 {
11411 PM_TryAirKick( LS_KICK_L_AIR );
11412 }
11413 else
11414 {
11415 PM_SetSaberMove( LS_KICK_L );
11416 }
11417 }
11418 pm->cmd.rightmove = 0;
11419 }
11420 else if ( pm->cmd.forwardmove )
11421 {//kick front/back
11422 if ( pm->cmd.forwardmove > 0 )
11423 {//kick fwd
11424 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
11425 || pm->cmd.upmove > 0 )
11426 {
11427 PM_TryAirKick( LS_KICK_F_AIR );
11428 }
11429 /*
11430 else if ( pm->ps->weapon == WP_SABER
11431 && pm->ps->saberAnimLevel == SS_STAFF
11432 && pm->gent
11433 && G_CheckEnemyPresence( pm->gent, DIR_FRONT, 64, 0.8f ) )
11434 {//FIXME: don't jump while doing this move and don't do this move if in air
11435 PM_SetSaberMove( LS_HILT_BASH );
11436 }
11437 */
11438 else
11439 {
11440 PM_SetSaberMove( LS_KICK_F );
11441 }
11442 }
11443 else if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
11444 || pm->cmd.upmove > 0 )
11445 {
11446 PM_TryAirKick( LS_KICK_B_AIR );
11447 }
11448 else
11449 {//kick back
11450 PM_SetSaberMove( LS_KICK_B );
11451 }
11452 pm->cmd.forwardmove = 0;
11453 }
11454 else if ( pm->gent
11455 && pm->gent->enemy
11456 && G_CanKickEntity( pm->gent, pm->gent->enemy ) )
11457 {//auto-pick?
11458 if ( /*(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD)
11459 && (pm->cmd.buttons&BUTTON_ATTACK)
11460 &&*/ PM_PickAutoMultiKick( qfalse ) )
11461 {//kicked!
11462 if ( pm->ps->saberMove == LS_KICK_RL )
11463 {//just pull back
11464 if ( d_slowmodeath->integer > 3 )
11465 {
11466 G_StartMatrixEffect( pm->gent, MEF_NO_SPIN, pm->ps->legsAnimTimer+500 );
11467 }
11468 }
11469 else
11470 {//normal spin
11471 if ( d_slowmodeath->integer > 3 )
11472 {
11473 G_StartMatrixEffect( pm->gent, 0, pm->ps->legsAnimTimer+500 );
11474 }
11475 }
11476 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
11477 &&( pm->ps->saberMove == LS_KICK_S
11478 ||pm->ps->saberMove == LS_KICK_BF
11479 ||pm->ps->saberMove == LS_KICK_RL ) )
11480 {//in the air and doing a jump-kick, which is a ground anim, so....
11481 //cut z velocity...?
11482 pm->ps->velocity[2] = 0;
11483 }
11484 pm->cmd.upmove = 0;
11485 }
11486 else
11487 {
11488 saberMoveName_t kickMove = PM_PickAutoKick( pm->gent->enemy );
11489 if ( kickMove != LS_NONE )
11490 {//Matrix?
11491 PM_SetSaberMove( kickMove );
11492 int meFlags = 0;
11493 switch ( kickMove )
11494 {
11495 case LS_KICK_B://just pull back
11496 case LS_KICK_B_AIR://just pull back
11497 meFlags = MEF_NO_SPIN;
11498 break;
11499 case LS_KICK_L://spin to the left
11500 case LS_KICK_L_AIR://spin to the left
11501 meFlags = MEF_REVERSE_SPIN;
11502 break;
11503 default:
11504 break;
11505 }
11506 if ( d_slowmodeath->integer > 3 )
11507 {
11508 G_StartMatrixEffect( pm->gent, meFlags, pm->ps->legsAnimTimer+500 );
11509 }
11510 }
11511 }
11512 }
11513 else
11514 {
11515 if ( PM_PickAutoMultiKick( qtrue ) )
11516 {
11517 int meFlags = 0;
11518 switch ( pm->ps->saberMove )
11519 {
11520 case LS_KICK_RL://just pull back
11521 case LS_KICK_B://just pull back
11522 case LS_KICK_B_AIR://just pull back
11523 meFlags = MEF_NO_SPIN;
11524 break;
11525 case LS_KICK_L://spin to the left
11526 case LS_KICK_L_AIR://spin to the left
11527 meFlags = MEF_REVERSE_SPIN;
11528 break;
11529 default:
11530 break;
11531 }
11532 if ( d_slowmodeath->integer > 3 )
11533 {
11534 G_StartMatrixEffect( pm->gent, meFlags, pm->ps->legsAnimTimer+500 );
11535 }
11536 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
11537 &&( pm->ps->saberMove == LS_KICK_S
11538 ||pm->ps->saberMove == LS_KICK_BF
11539 ||pm->ps->saberMove == LS_KICK_RL ) )
11540 {//in the air and doing a jump-kick, which is a ground anim, so....
11541 //cut z velocity...?
11542 pm->ps->velocity[2] = 0;
11543 }
11544 pm->cmd.upmove = 0;
11545 }
11546 }
11547 }
11548 }
11549
PM_CheckClearSaberBlock(void)11550 void PM_CheckClearSaberBlock( void )
11551 {
11552 if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() )
11553 {//player
11554 if ( pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT_PROJ && pm->ps->saberBlocked <= BLOCKED_TOP_PROJ )
11555 {//blocking a projectile
11556 if ( pm->ps->forcePowerDebounce[FP_SABER_DEFENSE] < level.time )
11557 {//block is done or breaking out of it with an attack
11558 pm->ps->weaponTime = 0;
11559 pm->ps->saberBlocked = BLOCKED_NONE;
11560 }
11561 else if ( (pm->cmd.buttons&BUTTON_ATTACK) )
11562 {//block is done or breaking out of it with an attack
11563 pm->ps->weaponTime = 0;
11564 pm->ps->saberBlocked = BLOCKED_NONE;
11565 }
11566 }
11567 else if ( pm->ps->saberBlocked == BLOCKED_UPPER_LEFT
11568 && pm->ps->powerups[PW_SHOCKED] > level.time )
11569 {//probably blocking lightning
11570 if ( (pm->cmd.buttons&BUTTON_ATTACK) )
11571 {//trying to attack
11572 //allow the attack
11573 pm->ps->weaponTime = 0;
11574 pm->ps->saberBlocked = BLOCKED_NONE;
11575 }
11576 }
11577 }
11578 }
11579
PM_SaberBlocking(void)11580 qboolean PM_SaberBlocking( void )
11581 {
11582 // Now we react to a block action by the player's lightsaber.
11583 if ( pm->ps->saberBlocked )
11584 {
11585 if ( pm->ps->saberMove > LS_PUTAWAY && pm->ps->saberMove <= LS_A_BL2TR && pm->ps->saberBlocked != BLOCKED_PARRY_BROKEN &&
11586 (pm->ps->saberBlocked < BLOCKED_UPPER_RIGHT_PROJ || pm->ps->saberBlocked > BLOCKED_TOP_PROJ))//&& Q_irand( 0, 2 )
11587 {//we parried another lightsaber while attacking, so treat it as a bounce
11588 pm->ps->saberBlocked = BLOCKED_ATK_BOUNCE;
11589 }
11590 else if ( pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer() )//player
11591 {
11592 if ( pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT_PROJ
11593 && pm->ps->saberBlocked <= BLOCKED_TOP_PROJ )
11594 {//blocking a projectile
11595 if ( (pm->cmd.buttons&BUTTON_ATTACK) )
11596 {//trying to attack
11597 if ( pm->ps->saberMove == LS_READY
11598 || PM_SaberInReflect( pm->ps->saberMove ) )
11599 {//not already busy or already in projectile deflection
11600 //trying to attack during projectile blocking, so do the attack instead
11601 pm->ps->saberBlocked = BLOCKED_NONE;
11602 pm->ps->saberBounceMove = LS_NONE;
11603 pm->ps->weaponstate = WEAPON_READY;
11604 if ( PM_SaberInReflect( pm->ps->saberMove ) && pm->ps->weaponTime > 0 )
11605 {//interrupt the current deflection move
11606 pm->ps->weaponTime = 0;
11607 }
11608 return qfalse;
11609 }
11610 }
11611 }
11612 }
11613 /*
11614 else if ( (pm->cmd.buttons&BUTTON_ATTACK)
11615 && pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT
11616 && pm->ps->saberBlocked <= BLOCKED_TOP
11617 && !PM_SaberInKnockaway( pm->ps->saberBounceMove ) )
11618 {//if hitting attack during a parry (not already a knockaway)
11619 if ( pm->ps->forcePowerLevel[FP_SABER_DEFENSE] > FORCE_LEVEL_2 )
11620 {//have high saber defense, turn the parry into a knockaway?
11621 //FIXME: this could actually be bad for us...? Leaves us open
11622 pm->ps->saberBounceMove = PM_KnockawayForParry( pm->ps->saberBlocked );
11623 }
11624 }
11625 */
11626
11627 if ( pm->ps->saberBlocked != BLOCKED_ATK_BOUNCE )
11628 {//can't attack for twice whatever your skill level's parry debounce time is
11629 if ( pm->ps->clientNum == 0 || PM_ControlledByPlayer() )
11630 {//player
11631 if ( pm->ps->forcePowerLevel[FP_SABER_DEFENSE] <= FORCE_LEVEL_1 )
11632 {
11633 pm->ps->weaponTime = parryDebounce[pm->ps->forcePowerLevel[FP_SABER_DEFENSE]];
11634 }
11635 }
11636 else
11637 {//NPC
11638 //pm->ps->weaponTime = parryDebounce[pm->ps->forcePowerLevel[FP_SABER_DEFENSE]] * 2;
11639 if ( pm->gent )
11640 {
11641 pm->ps->weaponTime = Jedi_ReCalcParryTime( pm->gent, EVASION_PARRY );
11642 }
11643 else
11644 {//WTF???
11645 pm->ps->weaponTime = parryDebounce[pm->ps->forcePowerLevel[FP_SABER_DEFENSE]] * 2;
11646 }
11647 }
11648 }
11649 switch ( pm->ps->saberBlocked )
11650 {
11651 case BLOCKED_PARRY_BROKEN:
11652 //whatever parry we were is in now broken, play the appropriate knocked-away anim
11653 {
11654 saberMoveName_t nextMove;
11655 if ( PM_SaberInBrokenParry( pm->ps->saberBounceMove ) )
11656 {//already have one...?
11657 nextMove = (saberMoveName_t)pm->ps->saberBounceMove;
11658 }
11659 else
11660 {
11661 nextMove = PM_BrokenParryForParry( (saberMoveName_t)pm->ps->saberMove );
11662 }
11663 if ( nextMove != LS_NONE )
11664 {
11665 PM_SetSaberMove( nextMove );
11666 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11667 }
11668 else
11669 {//Maybe in a knockaway?
11670 }
11671 //pm->ps->saberBounceMove = LS_NONE;
11672 }
11673 break;
11674 case BLOCKED_ATK_BOUNCE:
11675 // If there is absolutely no blocked move in the chart, don't even mess with the animation.
11676 // OR if we are already in a block or parry.
11677 if ( pm->ps->saberMove >= LS_T1_BR__R/*LS_BOUNCE_TOP*/ )//|| saberMoveData[pm->ps->saberMove].bounceMove == LS_NONE )
11678 {//an actual bounce? Other bounces before this are actually transitions?
11679 pm->ps->saberBlocked = BLOCKED_NONE;
11680 }
11681 else if ( PM_SaberInBounce( pm->ps->saberMove ) || !PM_SaberInAttack( pm->ps->saberMove ) )
11682 {//already in the bounce, go into an attack or transition to ready.. should never get here since can't be blocked in a bounce!
11683 int nextMove;
11684
11685 if ( pm->cmd.buttons & BUTTON_ATTACK )
11686 {//transition to a new attack
11687 if ( pm->ps->clientNum && !PM_ControlledByPlayer() )
11688 {//NPC
11689 nextMove = saberMoveData[pm->ps->saberMove].chain_attack;
11690 }
11691 else
11692 {//player
11693 int newQuad = PM_SaberMoveQuadrantForMovement( &pm->cmd );
11694 while ( newQuad == saberMoveData[pm->ps->saberMove].startQuad )
11695 {//player is still in same attack quad, don't repeat that attack because it looks bad,
11696 //FIXME: try to pick one that might look cool?
11697 newQuad = Q_irand( Q_BR, Q_BL );
11698 //FIXME: sanity check, just in case?
11699 }//else player is switching up anyway, take the new attack dir
11700 nextMove = transitionMove[saberMoveData[pm->ps->saberMove].startQuad][newQuad];
11701 }
11702 }
11703 else
11704 {//return to ready
11705 if ( pm->ps->clientNum && !PM_ControlledByPlayer() )
11706 {//NPC
11707 nextMove = saberMoveData[pm->ps->saberMove].chain_idle;
11708 }
11709 else
11710 {//player
11711 if ( saberMoveData[pm->ps->saberMove].startQuad == Q_T )
11712 {
11713 nextMove = LS_R_BL2TR;
11714 }
11715 else if ( saberMoveData[pm->ps->saberMove].startQuad < Q_T )
11716 {
11717 nextMove = LS_R_TL2BR+(saberMoveName_t)(saberMoveData[pm->ps->saberMove].startQuad-Q_BR);
11718 }
11719 else// if ( saberMoveData[pm->ps->saberMove].startQuad > Q_T )
11720 {
11721 nextMove = LS_R_BR2TL+(saberMoveName_t)(saberMoveData[pm->ps->saberMove].startQuad-Q_TL);
11722 }
11723 }
11724 }
11725 PM_SetSaberMove( (saberMoveName_t)nextMove );
11726 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11727 }
11728 else
11729 {//start the bounce move
11730 saberMoveName_t bounceMove;
11731 if ( pm->ps->saberBounceMove != LS_NONE )
11732 {
11733 bounceMove = (saberMoveName_t)pm->ps->saberBounceMove;
11734 }
11735 else
11736 {
11737 bounceMove = PM_SaberBounceForAttack( (saberMoveName_t)pm->ps->saberMove );
11738 }
11739 PM_SetSaberMove( bounceMove );
11740 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11741 }
11742 //clear the saberBounceMove
11743 //pm->ps->saberBounceMove = LS_NONE;
11744
11745 if (cg_debugSaber.integer>=2)
11746 {
11747 Com_Printf("Saber Block: Bounce\n");
11748 }
11749 break;
11750 case BLOCKED_UPPER_RIGHT:
11751 if ( pm->ps->saberBounceMove != LS_NONE )
11752 {
11753 PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove );
11754 //pm->ps->saberBounceMove = LS_NONE;
11755 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11756 }
11757 else
11758 {
11759 PM_SetSaberMove( LS_PARRY_UR );
11760 }
11761
11762 if ( cg_debugSaber.integer >= 2 )
11763 {
11764 Com_Printf( "Saber Block: Parry UR\n" );
11765 }
11766 break;
11767 case BLOCKED_UPPER_RIGHT_PROJ:
11768 PM_SetSaberMove( LS_REFLECT_UR );
11769
11770 if ( cg_debugSaber.integer >= 2 )
11771 {
11772 Com_Printf("Saber Block: Deflect UR\n");
11773 }
11774 break;
11775 case BLOCKED_UPPER_LEFT:
11776 if ( pm->ps->saberBounceMove != LS_NONE )
11777 {
11778 PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove );
11779 //pm->ps->saberBounceMove = LS_NONE;
11780 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11781 }
11782 else
11783 {
11784 PM_SetSaberMove( LS_PARRY_UL );
11785 }
11786
11787 if ( cg_debugSaber.integer >= 2 )
11788 {
11789 Com_Printf( "Saber Block: Parry UL\n" );
11790 }
11791 break;
11792 case BLOCKED_UPPER_LEFT_PROJ:
11793 PM_SetSaberMove( LS_REFLECT_UL );
11794
11795 if ( cg_debugSaber.integer >= 2 )
11796 {
11797 Com_Printf("Saber Block: Deflect UL\n");
11798 }
11799 break;
11800 case BLOCKED_LOWER_RIGHT:
11801 if ( pm->ps->saberBounceMove != LS_NONE )
11802 {
11803 PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove );
11804 //pm->ps->saberBounceMove = LS_NONE;
11805 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11806 }
11807 else
11808 {
11809 PM_SetSaberMove( LS_PARRY_LR );
11810 }
11811
11812 if ( cg_debugSaber.integer >= 2 )
11813 {
11814 Com_Printf("Saber Block: Parry LR\n");
11815 }
11816 break;
11817 case BLOCKED_LOWER_RIGHT_PROJ:
11818 PM_SetSaberMove( LS_REFLECT_LR );
11819
11820 if ( cg_debugSaber.integer >= 2 )
11821 {
11822 Com_Printf("Saber Block: Deflect LR\n");
11823 }
11824 break;
11825 case BLOCKED_LOWER_LEFT:
11826 if ( pm->ps->saberBounceMove != LS_NONE )
11827 {
11828 PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove );
11829 //pm->ps->saberBounceMove = LS_NONE;
11830 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11831 }
11832 else
11833 {
11834 PM_SetSaberMove( LS_PARRY_LL );
11835 }
11836
11837 if ( cg_debugSaber.integer >= 2 )
11838 {
11839 Com_Printf("Saber Block: Parry LL\n");
11840 }
11841 break;
11842 case BLOCKED_LOWER_LEFT_PROJ:
11843 PM_SetSaberMove( LS_REFLECT_LL);
11844
11845 if ( cg_debugSaber.integer >= 2 )
11846 {
11847 Com_Printf("Saber Block: Deflect LL\n");
11848 }
11849 break;
11850 case BLOCKED_TOP:
11851 if ( pm->ps->saberBounceMove != LS_NONE )
11852 {
11853 PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove );
11854 //pm->ps->saberBounceMove = LS_NONE;
11855 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11856 }
11857 else
11858 {
11859 PM_SetSaberMove( LS_PARRY_UP );
11860 }
11861
11862 if ( cg_debugSaber.integer >= 2 )
11863 {
11864 Com_Printf("Saber Block: Parry Top\n");
11865 }
11866 break;
11867 case BLOCKED_TOP_PROJ:
11868 PM_SetSaberMove( LS_REFLECT_UP );
11869
11870 if ( cg_debugSaber.integer >= 2 )
11871 {
11872 Com_Printf("Saber Block: Deflect Top\n");
11873 }
11874 break;
11875 default:
11876 pm->ps->saberBlocked = BLOCKED_NONE;
11877 break;
11878 }
11879
11880 // Charging is like a lead-up before attacking again. This is an appropriate use, or we can create a new weaponstate for blocking
11881 pm->ps->saberBounceMove = LS_NONE;
11882 pm->ps->weaponstate = WEAPON_READY;
11883
11884 // Done with block, so stop these active weapon branches.
11885 return qtrue;
11886 }
11887 return qfalse;
11888 }
11889
PM_NPCCheckAttackRoll(void)11890 qboolean PM_NPCCheckAttackRoll( void )
11891 {
11892 if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer()//NPC
11893 && pm->gent
11894 && pm->gent->NPC
11895 //&& Q_irand(-3,pm->gent->NPC->rank)>RANK_CREWMAN)
11896 && ( pm->gent->NPC->rank > RANK_CREWMAN && !Q_irand(0,3-g_spskill->integer) )
11897 && pm->gent->enemy
11898 && fabs(pm->gent->enemy->currentOrigin[2]-pm->ps->origin[2])<32
11899 && DistanceHorizontalSquared(pm->gent->enemy->currentOrigin, pm->ps->origin ) < (128.0f*128.0f)
11900 && InFOV( pm->gent->enemy->currentOrigin, pm->ps->origin, pm->ps->viewangles, 30, 90 ) )
11901 {//stab!
11902 return qtrue;
11903 }
11904 return qfalse;
11905 }
11906 /*
11907 =================
11908 PM_WeaponLightsaber
11909
11910 Consults a chart to choose what to do with the lightsaber.
11911 While this is a little different than the Quake 3 code, there is no clean way of using the Q3 code for this kind of thing.
11912 =================
11913 */
11914 // Ultimate goal is to set the sabermove to the proper next location
11915 // Note that if the resultant animation is NONE, then the animation is essentially "idle", and is set in WP_TorsoAnim
PM_WeaponLightsaber(void)11916 void PM_WeaponLightsaber(void)
11917 {
11918 int addTime;
11919 qboolean delayed_fire = qfalse, animLevelOverridden = qfalse;
11920 int anim=-1;
11921 int curmove, newmove=LS_NONE;
11922
11923 if ( pm->gent
11924 && pm->gent->client
11925 && pm->gent->client->NPC_class == CLASS_SABER_DROID )
11926 {//Saber droid does it's own attack logic
11927 PM_SaberDroidWeapon();
11928 return;
11929 }
11930
11931 // don't allow attack until all buttons are up
11932 if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
11933 return;
11934 }
11935
11936 // check for dead player
11937 if ( pm->ps->stats[STAT_HEALTH] <= 0 )
11938 {
11939 if ( pm->gent )
11940 {
11941 pm->gent->s.loopSound = 0;
11942 }
11943 return;
11944 }
11945
11946 // make weapon function
11947 if ( pm->ps->weaponTime > 0 ) {
11948 pm->ps->weaponTime -= pml.msec;
11949 if ( pm->ps->weaponTime <= 0 )
11950 {
11951 pm->ps->weaponTime = 0;
11952 }
11953 }
11954
11955 if ( pm->ps->stats[STAT_WEAPONS]&(1<<WP_SCEPTER)
11956 && !pm->ps->dualSabers
11957 && pm->gent
11958 && pm->gent->weaponModel[1] )
11959 {//holding scepter in left hand, use dual style
11960 pm->ps->saberAnimLevel = SS_DUAL;
11961 animLevelOverridden = qtrue;
11962 }
11963 else if ( pm->ps->saber[0].singleBladeStyle != SS_NONE//SaberStaff()
11964 && !pm->ps->dualSabers
11965 && pm->ps->saber[0].blade[0].active
11966 && !pm->ps->saber[0].blade[1].active )
11967 {//using a staff, but only with first blade turned on, so use is as a normal saber...?
11968 //override so that single-blade staff must be used with specified style
11969 pm->ps->saberAnimLevel = pm->ps->saber[0].singleBladeStyle;//SS_STRONG;
11970 animLevelOverridden = qtrue;
11971 }
11972 else if ( pm->gent
11973 && cg.saberAnimLevelPending != pm->ps->saberAnimLevel
11974 && WP_SaberStyleValidForSaber( pm->gent, cg.saberAnimLevelPending ) )
11975 {//go ahead and use the cg.saberAnimLevelPending below
11976 animLevelOverridden = qfalse;
11977 }
11978 else if ( pm->gent
11979 && ( WP_SaberStyleValidForSaber( pm->gent, pm->ps->saberAnimLevel )
11980 || WP_UseFirstValidSaberStyle( pm->gent, &pm->ps->saberAnimLevel ) ) )
11981 {//style we are using is not valid, switched us to a valid one
11982 animLevelOverridden = qtrue;
11983 }
11984 /*
11985 else if ( pm->ps->saber[0].Active()
11986 && pm->ps->saber[0].stylesAllowed )
11987 {//one of the sabers I'm using forces me to use one of a set of styles
11988 if ( !(pm->ps->saber[0].stylesAllowed&(1<<pm->ps->saberAnimLevel)) )
11989 {//I'm not currently using a valid one
11990 for ( int styleNum = SS_NONE+1; styleNum < SS_NUM_SABER_STYLES; styleNum++ )
11991 {//loop through and use the first valid one
11992 if ( (pm->ps->saber[0].stylesAllowed&(1<<styleNum)) )
11993 {//found one we can use
11994 pm->ps->saberAnimLevel = styleNum;
11995 animLevelOverridden = qtrue;
11996 }
11997 }
11998 }
11999 }
12000 */
12001 else if ( pm->ps->dualSabers )
12002 {
12003 /*
12004 if ( pm->ps->saber[1].Active()
12005 && pm->ps->saber[1].stylesAllowed )
12006 {//one of the sabers I'm using forces me to use one of a set of styles
12007 if ( !(pm->ps->saber[1].stylesAllowed&(1<<pm->ps->saberAnimLevel)) )
12008 {//I'm not currently using a valid one
12009 for ( int styleNum = SS_NONE+1; styleNum < SS_NUM_SABER_STYLES; styleNum++ )
12010 {//loop through and use the first valid one
12011 if ( (pm->ps->saber[1].stylesAllowed&(1<<styleNum)) )
12012 {//found one we can use
12013 pm->ps->saberAnimLevel = styleNum;
12014 animLevelOverridden = qtrue;
12015 }
12016 }
12017 }
12018 }
12019 else*/ if ( pm->ps->saber[1].Active() )
12020 {//if second saber is on, must use dual style
12021 pm->ps->saberAnimLevel = SS_DUAL;
12022 animLevelOverridden = qtrue;
12023 }
12024 else if ( pm->ps->saber[0].Active() )
12025 {//with only one saber on, use fast style
12026 pm->ps->saberAnimLevel = SS_FAST;
12027 animLevelOverridden = qtrue;
12028 }
12029 }
12030 if ( !animLevelOverridden )
12031 {
12032 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
12033 && cg.saberAnimLevelPending > SS_NONE
12034 && cg.saberAnimLevelPending != pm->ps->saberAnimLevel )
12035 {
12036 if ( !PM_SaberInStart( pm->ps->saberMove )
12037 && !PM_SaberInTransition( pm->ps->saberMove )
12038 && !PM_SaberInAttack( pm->ps->saberMove ) )
12039 {//don't allow changes when in the middle of an attack set...(or delay the change until it's done)
12040 pm->ps->saberAnimLevel = cg.saberAnimLevelPending;
12041 }
12042 }
12043 }
12044 else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
12045 {//if overrid the player's saberAnimLevel, let the cgame know
12046 cg.saberAnimLevelPending = pm->ps->saberAnimLevel;
12047 }
12048 /*
12049 if ( PM_InForceGetUp( pm->ps ) )
12050 {//if mostly up, can start attack
12051 if ( pm->ps->torsoAnimTimer > 800 )
12052 {//not up enough yet
12053 // make weapon function
12054 if ( pm->ps->weaponTime > 0 ) {
12055 pm->ps->weaponTime -= pml.msec;
12056 if ( pm->ps->weaponTime <= 0 )
12057 {
12058 pm->ps->weaponTime = 0;
12059 }
12060 }
12061 return;
12062 }
12063 else
12064 {
12065 if ( pm->cmd.buttons & BUTTON_ATTACK )
12066 {//let an attack interrupt the torso part of this force getup
12067 pm->ps->weaponTime = 0;
12068 }
12069 }
12070 }
12071 else
12072 */
12073 if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps ))
12074 {//in knockdown
12075 if ( pm->ps->legsAnim == BOTH_ROLL_F
12076 && pm->ps->legsAnimTimer <= 250 )
12077 {
12078 if ( (pm->cmd.buttons&BUTTON_ATTACK)
12079 || PM_NPCCheckAttackRoll() )
12080 {
12081 if ( G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) )
12082 {
12083 if ( !(pm->ps->saber[0].saberFlags&SFL_NO_ROLL_STAB)
12084 && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_ROLL_STAB)) )
12085 {//okay to do roll-stab
12086 PM_SetSaberMove( LS_ROLL_STAB );
12087 if ( pm->gent )
12088 {
12089 G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER_FB );
12090 }
12091 }
12092 }
12093 }
12094 }
12095 return;
12096 }
12097
12098 if ( PM_SaberLocked() )
12099 {
12100 pm->ps->saberMove = LS_NONE;
12101 return;
12102 }
12103
12104 if ( pm->ps->torsoAnim == BOTH_FORCELONGLEAP_LAND
12105 || (pm->ps->torsoAnim == BOTH_FORCELONGLEAP_START && !(pm->cmd.buttons&BUTTON_ATTACK)) )
12106 {//if you're in the long-jump and you're not attacking (or are landing), you're not doing anything
12107 if ( pm->ps->torsoAnimTimer )
12108 {
12109 return;
12110 }
12111 }
12112
12113 if ( pm->ps->legsAnim == BOTH_FLIP_HOLD7
12114 && !(pm->cmd.buttons&BUTTON_ATTACK) )
12115 {//if you're in the upside-down attack hold, don't do anything unless you're attacking
12116 return;
12117 }
12118
12119 if ( PM_KickingAnim( pm->ps->legsAnim ) )
12120 {
12121 if ( pm->ps->legsAnimTimer )
12122 {//you're kicking, no interruptions
12123 return;
12124 }
12125 //done? be immeditately ready to do an attack
12126 pm->ps->saberMove = LS_READY;
12127 pm->ps->weaponTime = 0;
12128 }
12129
12130 if ( pm->ps->saberMoveNext != LS_NONE
12131 && (pm->ps->saberMove == LS_READY||pm->ps->saberMove == LS_NONE))//ready for another one
12132 {//something is forcing us to set a specific next saberMove
12133 //FIXME: if this is a NPC kick, re-verify it before executing it!
12134 PM_SetSaberMove( (saberMoveName_t)pm->ps->saberMoveNext );
12135 pm->ps->saberMoveNext = LS_NONE;//clear it now that we played it
12136 return;
12137 }
12138
12139 if ( pm->ps->saberEventFlags&SEF_INWATER )//saber in water
12140 {
12141 pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS);
12142 }
12143
12144 qboolean saberInAir = qtrue;
12145 if ( !PM_SaberInBrokenParry( pm->ps->saberMove ) && pm->ps->saberBlocked != BLOCKED_PARRY_BROKEN && !PM_DodgeAnim( pm->ps->torsoAnim ) &&
12146 pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_CHARGING)
12147 {//we're not stuck in a broken parry
12148 if ( pm->ps->saberInFlight )
12149 {//guiding saber
12150 if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0
12151 {//
12152 if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY )
12153 {//fell to the ground and we're not trying to pull it back
12154 saberInAir = qfalse;
12155 }
12156 }
12157 if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING )
12158 {
12159 if ( pm->ps->weapon != pm->cmd.weapon )
12160 {
12161 PM_BeginWeaponChange( pm->cmd.weapon );
12162 }
12163 }
12164 else if ( pm->ps->weapon == WP_SABER
12165 && (!pm->ps->dualSabers || !pm->ps->saber[1].Active()) )
12166 {//guiding saber
12167 if ( saberInAir )
12168 {
12169 if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 )
12170 {//don't interrupt a force power anim
12171 if ( pm->ps->torsoAnim != BOTH_LOSE_SABER
12172 || !pm->ps->torsoAnimTimer )
12173 {
12174 PM_SetAnim( pm, SETANIM_TORSO, BOTH_SABERPULL, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
12175 }
12176 }
12177 }
12178 return;
12179 }
12180 }
12181 }
12182
12183 if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
12184 {//FIXME: this is going to fire off one frame before you expect, actually
12185 pm->gent->client->fireDelay -= pml.msec;
12186 if ( pm->gent->client->fireDelay <= 0 )
12187 {//just finished delay timer
12188 pm->gent->client->fireDelay = 0;
12189 delayed_fire = qtrue;
12190 }
12191 }
12192
12193 PM_CheckClearSaberBlock();
12194
12195 if ( PM_LockedAnim( pm->ps->torsoAnim )
12196 && pm->ps->torsoAnimTimer )
12197 {//can't interrupt these anims ever
12198 return;
12199 }
12200 if ( PM_SuperBreakLoseAnim( pm->ps->torsoAnim )
12201 && pm->ps->torsoAnimTimer )
12202 {//don't interrupt these anims
12203 return;
12204 }
12205 if ( PM_SuperBreakWinAnim( pm->ps->torsoAnim )
12206 && pm->ps->torsoAnimTimer )
12207 {//don't interrupt these anims
12208 return;
12209 }
12210
12211 if ( PM_SaberBlocking() )
12212 {//busy blocking, don't do attacks
12213 return;
12214 }
12215
12216 // check for weapon change
12217 // can't change if weapon is firing, but can change again if lowering or raising
12218 if ( (pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING) && pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_CHARGING) {
12219 if ( pm->ps->weapon != pm->cmd.weapon ) {
12220 PM_BeginWeaponChange( pm->cmd.weapon );
12221 }
12222 }
12223
12224 if ( pm->ps->weaponTime > 0 )
12225 {
12226 //FIXME: allow some window of opportunity to change your attack
12227 // if it just started and your directional input is different
12228 // than it was before... but only 100 milliseconds at most?
12229 //OR: Make it so that attacks don't start until 100ms after you
12230 // press the attack button...???
12231 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) //player
12232 && PM_SaberInReturn( pm->ps->saberMove )//in a saber return move - FIXME: what about transitions?
12233 //&& pm->ps->torsoAnimTimer<=250//towards the end of a saber return anim
12234 && pm->ps->saberBlocked == BLOCKED_NONE//not interacting with any other saber
12235 && !(pm->cmd.buttons&BUTTON_ATTACK)//not trying to swing the saber
12236 && (pm->cmd.forwardmove||pm->cmd.rightmove) )//trying to kick in a specific direction
12237 {
12238 if ( PM_CheckAltKickAttack() )//trying to do a kick
12239 {//allow them to do the kick now!
12240 pm->ps->weaponTime = 0;
12241 PM_CheckKick();
12242 return;
12243 }
12244 }
12245 else
12246 {
12247 if ( !pm->cmd.rightmove
12248 &&!pm->cmd.forwardmove
12249 &&(pm->cmd.buttons&BUTTON_ATTACK) )
12250 {
12251 /*
12252 if ( PM_CheckDualSpinProtect() )
12253 {//check to see if we're going to do the special dual push protect move
12254 PM_SetSaberMove( LS_DUAL_SPIN_PROTECT );
12255 pm->ps->weaponstate = WEAPON_FIRING;
12256 return;
12257 }
12258 else
12259 */
12260 if ( !g_saberNewControlScheme->integer )
12261 {
12262 saberMoveName_t pullAtk = PM_CheckPullAttack();
12263 if ( pullAtk != LS_NONE )
12264 {
12265 PM_SetSaberMove( pullAtk );
12266 pm->ps->weaponstate = WEAPON_FIRING;
12267 return;
12268 }
12269 }
12270 }
12271 else
12272 {
12273 return;
12274 }
12275 }
12276 }
12277
12278 // *********************************************************
12279 // WEAPON_DROPPING
12280 // *********************************************************
12281
12282 // change weapon if time
12283 if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
12284 PM_FinishWeaponChange();
12285 return;
12286 }
12287
12288 // *********************************************************
12289 // WEAPON_RAISING
12290 // *********************************************************
12291
12292 if ( pm->ps->weaponstate == WEAPON_RAISING )
12293 {//Just selected the weapon
12294 pm->ps->weaponstate = WEAPON_IDLE;
12295 if(pm->gent && (pm->gent->s.number<MAX_CLIENTS||G_ControlledByPlayer(pm->gent)))
12296 {
12297 switch ( pm->ps->legsAnim )
12298 {
12299 case BOTH_WALK1:
12300 case BOTH_WALK2:
12301 case BOTH_WALK_STAFF:
12302 case BOTH_WALK_DUAL:
12303 case BOTH_WALKBACK1:
12304 case BOTH_WALKBACK2:
12305 case BOTH_WALKBACK_STAFF:
12306 case BOTH_WALKBACK_DUAL:
12307 case BOTH_RUN1:
12308 case BOTH_RUN2:
12309 case BOTH_RUN_STAFF:
12310 case BOTH_RUN_DUAL:
12311 case BOTH_RUNBACK1:
12312 case BOTH_RUNBACK2:
12313 case BOTH_RUNBACK_STAFF:
12314 PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL);
12315 break;
12316 default:
12317 int anim = PM_ReadyPoseForSaberAnimLevel();
12318 if (anim!=-1)
12319 {
12320 PM_SetAnim(pm,SETANIM_TORSO,anim,SETANIM_FLAG_NORMAL);
12321 }
12322 break;
12323 }
12324 }
12325 else
12326 {
12327 qboolean saberInAir = qtrue;
12328 if ( pm->ps->saberInFlight )
12329 {//guiding saber
12330 if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) )
12331 {//we're stuck in a broken parry
12332 saberInAir = qfalse;
12333 }
12334 if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0
12335 {//
12336 if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY )
12337 {//fell to the ground and we're not trying to pull it back
12338 saberInAir = qfalse;
12339 }
12340 }
12341 }
12342 if ( pm->ps->weapon == WP_SABER
12343 && pm->ps->saberInFlight
12344 && saberInAir
12345 && (!pm->ps->dualSabers || !pm->ps->saber[1].Active()))
12346 {//guiding saber
12347 if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 )
12348 {//don't interrupt a force power anim
12349 if ( pm->ps->torsoAnim != BOTH_LOSE_SABER
12350 || !pm->ps->torsoAnimTimer )
12351 {
12352 PM_SetAnim( pm, SETANIM_TORSO, BOTH_SABERPULL, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
12353 }
12354 }
12355 }
12356 else
12357 {
12358 // PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK1, SETANIM_FLAG_NORMAL);
12359 // Select the proper idle Lightsaber attack move from the chart.
12360 PM_SetSaberMove(LS_READY);
12361 }
12362 }
12363 return;
12364 }
12365
12366 // *********************************************************
12367 // Check for WEAPON ATTACK
12368 // *********************************************************
12369
12370 if ( PM_CanDoKata() )
12371 {
12372 saberMoveName_t overrideMove = LS_INVALID;
12373 //see if we have an overridden (or cancelled) kata move
12374 if ( pm->ps->saber[0].kataMove != LS_INVALID )
12375 {
12376 if ( pm->ps->saber[0].kataMove != LS_NONE )
12377 {
12378 overrideMove = (saberMoveName_t)pm->ps->saber[0].kataMove;
12379 }
12380 }
12381 if ( overrideMove == LS_INVALID )
12382 {//not overridden by first saber, check second
12383 if ( pm->ps->dualSabers )
12384 {
12385 if ( pm->ps->saber[1].kataMove != LS_INVALID )
12386 {
12387 if ( pm->ps->saber[1].kataMove != LS_NONE )
12388 {
12389 overrideMove = (saberMoveName_t)pm->ps->saber[1].kataMove;
12390 }
12391 }
12392 }
12393 }
12394 //no overrides, cancelled?
12395 if ( overrideMove == LS_INVALID )
12396 {
12397 if ( pm->ps->saber[0].kataMove == LS_NONE )
12398 {
12399 overrideMove = LS_NONE;
12400 }
12401 else if ( pm->ps->dualSabers )
12402 {
12403 if ( pm->ps->saber[1].kataMove == LS_NONE )
12404 {
12405 overrideMove = LS_NONE;
12406 }
12407 }
12408 }
12409 if ( overrideMove == LS_INVALID )
12410 {//not overridden
12411 //FIXME: make sure to turn on saber(s)!
12412 switch ( pm->ps->saberAnimLevel )
12413 {
12414 case SS_FAST:
12415 case SS_TAVION:
12416 PM_SetSaberMove( LS_A1_SPECIAL );
12417 break;
12418 case SS_MEDIUM:
12419 PM_SetSaberMove( LS_A2_SPECIAL );
12420 break;
12421 case SS_STRONG:
12422 case SS_DESANN:
12423 PM_SetSaberMove( LS_A3_SPECIAL );
12424 break;
12425 case SS_DUAL:
12426 PM_SetSaberMove( LS_DUAL_SPIN_PROTECT );//PM_CheckDualSpinProtect();
12427 break;
12428 case SS_STAFF:
12429 PM_SetSaberMove( LS_STAFF_SOULCAL );
12430 break;
12431 }
12432 pm->ps->weaponstate = WEAPON_FIRING;
12433 if ( pm->gent )
12434 {
12435 G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER, qtrue );//FP_SPEED, SINGLE_SPECIAL_POWER );
12436 //G_StartMatrixEffect( pm->gent, MEF_REVERSE_SPIN, pm->ps->torsoAnimTimer );
12437 }
12438 }
12439 else if ( overrideMove != LS_NONE )
12440 {
12441 PM_SetSaberMove( overrideMove );
12442 pm->ps->weaponstate = WEAPON_FIRING;
12443 if ( pm->gent )
12444 {
12445 G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER, qtrue );//FP_SPEED, SINGLE_SPECIAL_POWER );
12446 //G_StartMatrixEffect( pm->gent, MEF_REVERSE_SPIN, pm->ps->torsoAnimTimer );
12447 }
12448 }
12449 if ( overrideMove != LS_NONE )
12450 {//not cancelled
12451 return;
12452 }
12453 }
12454
12455 if ( PM_CheckAltKickAttack() )
12456 {//trying to do a kick
12457 //FIXME: in-air kicks?
12458 if ( pm->ps->saberAnimLevel == SS_STAFF
12459 && (pm->ps->clientNum >= MAX_CLIENTS||PM_ControlledByPlayer()) )
12460 {//NPCs spin the staff
12461 //NOTE: only NPCs can do it the easy way... they kick directly, not through ucmds...
12462 PM_SetSaberMove( LS_SPINATTACK );
12463 return;
12464 }
12465 else
12466 {
12467 PM_CheckKick();
12468 }
12469 return;
12470 }
12471 //this is never a valid regular saber attack button
12472 //pm->cmd.buttons &= ~BUTTON_FORCE_FOCUS;
12473
12474 if ( PM_CheckUpsideDownAttack() )
12475 {
12476 return;
12477 }
12478
12479 if(!delayed_fire)
12480 {
12481 // Start with the current move, and cross index it with the current control states.
12482 if ( pm->ps->saberMove > LS_NONE && pm->ps->saberMove < LS_MOVE_MAX )
12483 {
12484 curmove = (saberMoveName_t)pm->ps->saberMove;
12485 }
12486 else
12487 {
12488 curmove = LS_READY;
12489 }
12490 if ( curmove == LS_A_JUMP_T__B_ || pm->ps->torsoAnim == BOTH_FORCELEAP2_T__B_ )
12491 {//must transition back to ready from this anim
12492 newmove = LS_R_T2B;
12493 }
12494 // check for fire
12495 else if ( !(pm->cmd.buttons & BUTTON_ATTACK) )//(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS)) )
12496 {//not attacking
12497 pm->ps->weaponTime = 0;
12498
12499 if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
12500 {//Still firing
12501 pm->ps->weaponstate = WEAPON_FIRING;
12502 }
12503 else if ( pm->ps->weaponstate != WEAPON_READY )
12504 {
12505 pm->ps->weaponstate = WEAPON_IDLE;
12506 }
12507 //Check for finishing an anim if necc.
12508 if ( curmove >= LS_S_TL2BR && curmove <= LS_S_T2B )
12509 {//started a swing, must continue from here
12510 newmove = LS_A_TL2BR + (curmove-LS_S_TL2BR);
12511 }
12512 else if ( curmove >= LS_A_TL2BR && curmove <= LS_A_T2B )
12513 {//finished an attack, must continue from here
12514 newmove = LS_R_TL2BR + (curmove-LS_A_TL2BR);
12515 }
12516 else if ( PM_SaberInTransition( curmove ) )
12517 {//in a transition, must play sequential attack
12518 newmove = saberMoveData[curmove].chain_attack;
12519 }
12520 else if ( PM_SaberInBounce( curmove ) )
12521 {//in a bounce
12522 if ( pm->ps->clientNum && !PM_ControlledByPlayer() )
12523 {//NPCs must play sequential attack
12524 //going into another attack...
12525 //allow endless chaining in level 1 attacks, several in level 2 and only one or a few in level 3
12526 if ( PM_SaberKataDone( LS_NONE, LS_NONE ) )
12527 {//done with this kata, must return to ready before attack again
12528 newmove = saberMoveData[curmove].chain_idle;
12529 }
12530 else
12531 {//okay to chain to another attack
12532 newmove = saberMoveData[curmove].chain_attack;//we assume they're attacking, even if they're not
12533 pm->ps->saberAttackChainCount++;
12534 }
12535 }
12536 else
12537 {//player gets his by directional control
12538 newmove = saberMoveData[curmove].chain_idle;//oops, not attacking, so don't chain
12539 }
12540 }
12541 else
12542 {//FIXME: what about returning from a parry?
12543 //PM_SetSaberMove( LS_READY );
12544 if ( pm->ps->saberBlockingTime > cg.time )
12545 {
12546 PM_SetSaberMove( LS_READY );
12547 }
12548 return;
12549 }
12550 }
12551
12552 // ***************************************************
12553 // Pressing attack, so we must look up the proper attack move.
12554 qboolean saberInAir = qtrue;
12555 if ( pm->ps->saberInFlight )
12556 {//guiding saber
12557 if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) )
12558 {//we're stuck in a broken parry
12559 saberInAir = qfalse;
12560 }
12561 if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0
12562 {//
12563 if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY )
12564 {//fell to the ground and we're not trying to pull it back
12565 saberInAir = qfalse;
12566 }
12567 }
12568 }
12569
12570 if ( pm->ps->weapon == WP_SABER
12571 && pm->ps->saberInFlight
12572 && saberInAir
12573 && (!pm->ps->dualSabers || !pm->ps->saber[1].Active()))
12574 {//guiding saber
12575 if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 )
12576 {//don't interrupt a force power anim
12577 if ( pm->ps->torsoAnim != BOTH_LOSE_SABER
12578 || !pm->ps->torsoAnimTimer )
12579 {
12580 PM_SetAnim( pm, SETANIM_TORSO,BOTH_SABERPULL,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
12581 }
12582 }
12583 }
12584 else if ( pm->ps->weaponTime > 0 )
12585 { // Last attack is not yet complete.
12586 pm->ps->weaponstate = WEAPON_FIRING;
12587 return;
12588 }
12589 else
12590 {
12591 int both = qfalse;
12592 if ( pm->ps->torsoAnim == BOTH_FORCELONGLEAP_ATTACK
12593 || pm->ps->torsoAnim == BOTH_FORCELONGLEAP_LAND )
12594 {//can't attack in these anims
12595 return;
12596 }
12597 else if ( pm->ps->torsoAnim == BOTH_FORCELONGLEAP_START )
12598 {//only 1 attack you can do from this anim
12599 if ( pm->ps->torsoAnimTimer >= 200 )
12600 {//hit it early enough to do the attack
12601 PM_SetSaberMove( LS_LEAP_ATTACK );
12602 }
12603 return;
12604 }
12605 if ( curmove >= LS_PARRY_UP && curmove <= LS_REFLECT_LL )
12606 {//from a parry or reflection, can go directly into an attack
12607 if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() )
12608 {//NPCs
12609 newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad );
12610 }
12611 else
12612 {
12613 newmove = PM_SaberAttackForMovement( pm->cmd.forwardmove, pm->cmd.rightmove, curmove );
12614 }
12615 }
12616
12617 if ( newmove != LS_NONE )
12618 {//have a valid, final LS_ move picked, so skip findingt he transition move and just get the anim
12619 if (PM_HasAnimation( pm->gent, saberMoveData[newmove].animToUse))
12620 {
12621 anim = saberMoveData[newmove].animToUse;
12622 }
12623 }
12624
12625 //FIXME: diagonal dirs use the figure-eight attacks from ready pose?
12626 if ( anim == -1 )
12627 {
12628 //FIXME: take FP_SABER_OFFENSE into account here somehow?
12629 if ( PM_SaberInTransition( curmove ) )
12630 {//in a transition, must play sequential attack
12631 newmove = saberMoveData[curmove].chain_attack;
12632 }
12633 else if ( curmove >= LS_S_TL2BR && curmove <= LS_S_T2B )
12634 {//started a swing, must continue from here
12635 newmove = LS_A_TL2BR + (curmove-LS_S_TL2BR);
12636 }
12637 else if ( PM_SaberInBrokenParry( curmove ) )
12638 {//broken parries must always return to ready
12639 newmove = LS_READY;
12640 }
12641 else//if ( pm->cmd.buttons&BUTTON_ATTACK && !(pm->ps->pm_flags&PMF_ATTACK_HELD) )//only do this if just pressed attack button?
12642 {//get attack move from movement command
12643 /*
12644 if ( PM_SaberKataDone() )
12645 {//we came from a bounce and cannot chain to another attack because our kata is done
12646 newmove = saberMoveData[curmove].chain_idle;
12647 }
12648 else */
12649 if ( pm->ps->clientNum >= MAX_CLIENTS
12650 && !PM_ControlledByPlayer()
12651 && (Q_irand( 0, pm->ps->forcePowerLevel[FP_SABER_OFFENSE]-1 )
12652 || (pm->gent&&pm->gent->enemy&&pm->gent->enemy->client&&PM_InKnockDownOnGround(&pm->gent->enemy->client->ps))//enemy knocked down, use some logic
12653 || ( pm->ps->saberAnimLevel == SS_FAST && pm->gent && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, 1 ) ) ) )//minor change to make fast-attack users use the special attacks more
12654 {//NPCs use more randomized attacks the more skilled they are
12655 newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad );
12656 }
12657 else
12658 {
12659 newmove = PM_SaberAttackForMovement( pm->cmd.forwardmove, pm->cmd.rightmove, curmove );
12660 if ( (PM_SaberInBounce( curmove )||PM_SaberInBrokenParry( curmove ))
12661 && saberMoveData[newmove].startQuad == saberMoveData[curmove].endQuad )
12662 {//this attack would be a repeat of the last (which was blocked), so don't actually use it, use the default chain attack for this bounce
12663 newmove = saberMoveData[curmove].chain_attack;
12664 }
12665 }
12666 if ( PM_SaberKataDone( curmove, newmove ) )
12667 {//cannot chain this time
12668 newmove = saberMoveData[curmove].chain_idle;
12669 }
12670 }
12671 /*
12672 if ( newmove == LS_NONE )
12673 {//FIXME: should we allow this? Are there some anims that you should never be able to chain into an attack?
12674 //only curmove that might get in here is LS_NONE, LS_DRAW, LS_PUTAWAY and the LS_R_ returns... all of which are in Q_R
12675 newmove = PM_AttackMoveForQuad( saberMoveData[curmove].endQuad );
12676 }
12677 */
12678 if ( newmove != LS_NONE )
12679 {
12680 if ( !PM_InCartwheel( pm->ps->legsAnim ) )
12681 {//don't do transitions when cartwheeling - could make you spin!
12682 //Now get the proper transition move
12683 newmove = PM_SaberAnimTransitionMove( (saberMoveName_t)curmove, (saberMoveName_t)newmove );
12684 if ( PM_HasAnimation( pm->gent, saberMoveData[newmove].animToUse ) )
12685 {
12686 anim = saberMoveData[newmove].animToUse;
12687 }
12688 }
12689 }
12690 }
12691
12692 if (anim == -1)
12693 {//not side-stepping, pick neutral anim
12694 if ( !G_TryingSpecial(pm->gent,&pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ )
12695 {//but only if not trying one of the special attacks!
12696 if ( PM_InCartwheel( pm->ps->legsAnim )
12697 && pm->ps->legsAnimTimer > 100 )
12698 {//if in the middle of a cartwheel, the chain attack is just a normal attack
12699 //NOTE: this should match the switch in PM_InCartwheel!
12700 switch( pm->ps->legsAnim )
12701 {
12702 case BOTH_ARIAL_LEFT://swing from l to r
12703 case BOTH_CARTWHEEL_LEFT:
12704 newmove = LS_A_L2R;
12705 break;
12706 case BOTH_ARIAL_RIGHT://swing from r to l
12707 case BOTH_CARTWHEEL_RIGHT:
12708 newmove = LS_A_R2L;
12709 break;
12710 case BOTH_ARIAL_F1://random l/r attack
12711 if ( Q_irand( 0, 1 ) )
12712 {
12713 newmove = LS_A_L2R;
12714 }
12715 else
12716 {
12717 newmove = LS_A_R2L;
12718 }
12719 break;
12720 }
12721 }
12722 else
12723 {
12724 // Add randomness for prototype?
12725 newmove = saberMoveData[curmove].chain_attack;
12726 }
12727 if ( newmove != LS_NONE )
12728 {
12729 if (PM_HasAnimation( pm->gent, saberMoveData[newmove].animToUse))
12730 {
12731 anim= saberMoveData[newmove].animToUse;
12732 }
12733 }
12734 }
12735
12736 if ( !pm->cmd.forwardmove && !pm->cmd.rightmove && pm->cmd.upmove >= 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE )
12737 {//not moving at all, so set the anim on entire body
12738 both = qtrue;
12739 }
12740
12741 }
12742
12743 if ( anim == -1)
12744 {
12745 switch ( pm->ps->legsAnim )
12746 {
12747 case BOTH_WALK1:
12748 case BOTH_WALK2:
12749 case BOTH_WALK_STAFF:
12750 case BOTH_WALK_DUAL:
12751 case BOTH_WALKBACK1:
12752 case BOTH_WALKBACK2:
12753 case BOTH_WALKBACK_STAFF:
12754 case BOTH_WALKBACK_DUAL:
12755 case BOTH_RUN1:
12756 case BOTH_RUN2:
12757 case BOTH_RUN_STAFF:
12758 case BOTH_RUN_DUAL:
12759 case BOTH_RUNBACK1:
12760 case BOTH_RUNBACK2:
12761 case BOTH_RUNBACK_STAFF:
12762 anim = pm->ps->legsAnim;
12763 break;
12764 default:
12765 anim = PM_ReadyPoseForSaberAnimLevel();
12766 break;
12767 }
12768 newmove = LS_READY;
12769 }
12770
12771 if ( !pm->ps->SaberActive() )
12772 {//turn on the saber if it's not on
12773 pm->ps->SaberActivate();
12774 }
12775
12776 PM_SetSaberMove( (saberMoveName_t)newmove );
12777
12778 if ( both && pm->ps->legsAnim != pm->ps->torsoAnim )
12779 {
12780 PM_SetAnim( pm,SETANIM_LEGS,pm->ps->torsoAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
12781 }
12782 if ( pm->gent && pm->gent->client )
12783 {
12784 // pm->gent->client->saberTrail.inAction = qtrue;
12785 // pm->gent->client->saberTrail.duration = 75; // saber trail lasts for 75ms...feel free to change this if you want it longer or shorter
12786 }
12787 if ( !PM_InCartwheel( pm->ps->torsoAnim ) )
12788 {//can still attack during a cartwheel/arial
12789 //don't fire again until anim is done
12790 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
12791 }
12792 /*
12793 //FIXME: this may be making it so sometimes you can't swing again right away...
12794 if ( newmove == LS_READY )
12795 {
12796 pm->ps->weaponTime = 500;
12797 }
12798 */
12799 }
12800 }
12801
12802 // *********************************************************
12803 // WEAPON_FIRING
12804 // *********************************************************
12805
12806 pm->ps->weaponstate = WEAPON_FIRING;
12807
12808 if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
12809 {//FIXME: this is going to fire off one frame before you expect, actually
12810 // Clear these out since we're not actually firing yet
12811 pm->ps->eFlags &= ~EF_FIRING;
12812 pm->ps->eFlags &= ~EF_ALT_FIRING;
12813 return;
12814 }
12815
12816 addTime = pm->ps->weaponTime;
12817 /*if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) {
12818 PM_AddEvent( EV_ALT_FIRE );
12819 if ( !addTime )
12820 {
12821 addTime = weaponData[pm->ps->weapon].altFireTime;
12822 if ( g_timescale != NULL )
12823 {
12824 if ( g_timescale->value < 1.0f )
12825 {
12826 if ( !MatrixMode )
12827 {//Special test for Matrix Mode (tm)
12828 if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
12829 {//player always fires at normal speed
12830 addTime *= g_timescale->value;
12831 }
12832 else if ( g_entities[pm->ps->clientNum].client && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
12833 {
12834 addTime *= g_timescale->value;
12835 }
12836 }
12837 }
12838 }
12839 }
12840 }
12841 else */{
12842 PM_AddEvent( EV_FIRE_WEAPON );
12843 if ( !addTime )
12844 {
12845 addTime = weaponData[pm->ps->weapon].fireTime;
12846 if ( g_timescale != NULL )
12847 {
12848 if ( g_timescale->value < 1.0f )
12849 {
12850 if ( !MatrixMode )
12851 {//Special test for Matrix Mode (tm)
12852 if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
12853 {//player always fires at normal speed
12854 addTime *= g_timescale->value;
12855 }
12856 else if ( g_entities[pm->ps->clientNum].client
12857 && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
12858 {
12859 addTime *= g_timescale->value;
12860 }
12861 }
12862 }
12863 }
12864 }
12865 }
12866
12867 if (pm->ps->forcePowersActive & (1 << FP_RAGE))
12868 {
12869 addTime *= 0.75;
12870 }
12871 else if (pm->ps->forceRageRecoveryTime > pm->cmd.serverTime)
12872 {
12873 addTime *= 1.5;
12874 }
12875
12876 //If the phaser has been fired, delay the next recharge time
12877 if ( !PM_ControlledByPlayer() )
12878 {
12879 if( pm->gent && pm->gent->NPC != NULL )
12880 {//NPCs have their own refire logic
12881 //FIXME: this really should be universal...
12882 return;
12883 }
12884 }
12885
12886 if ( !PM_InCartwheel( pm->ps->torsoAnim ) )
12887 {//can still attack during a cartwheel/arial
12888 pm->ps->weaponTime = addTime;
12889 }
12890 }
12891
12892 //---------------------------------------
PM_DoChargedWeapons(void)12893 static bool PM_DoChargedWeapons( void )
12894 //---------------------------------------
12895 {
12896 qboolean charging = qfalse,
12897 altFire = qfalse;
12898
12899 //FIXME: make jedi aware they're being aimed at with a charged-up weapon (strafe and be evasive?)
12900 // If you want your weapon to be a charging weapon, just set this bit up
12901 switch( pm->ps->weapon )
12902 {
12903 //------------------
12904 case WP_BRYAR_PISTOL:
12905 case WP_BLASTER_PISTOL:
12906
12907 // alt-fire charges the weapon
12908 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
12909 {
12910 charging = qtrue;
12911 altFire = qtrue;
12912 }
12913 break;
12914
12915 //------------------
12916 case WP_DISRUPTOR:
12917
12918 // alt-fire charges the weapon...but due to zooming being controlled by the alt-button, the main button actually charges...but only when zoomed.
12919 // lovely, eh?
12920 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
12921 {
12922 if ( cg.zoomMode == 2 )
12923 {
12924 if ( pm->cmd.buttons & BUTTON_ATTACK )
12925 {
12926 charging = qtrue;
12927 altFire = qtrue; // believe it or not, it really is an alt-fire in this case!
12928 }
12929 }
12930 }
12931 else if ( pm->gent && pm->gent->NPC )
12932 {
12933 if ( (pm->gent->NPC->scriptFlags&SCF_ALT_FIRE) )
12934 {
12935 if ( pm->gent->fly_sound_debounce_time > level.time )
12936 {
12937 charging = qtrue;
12938 altFire = qtrue;
12939 }
12940 }
12941 }
12942 break;
12943
12944 //------------------
12945 case WP_BOWCASTER:
12946
12947 // main-fire charges the weapon
12948 if ( pm->cmd.buttons & BUTTON_ATTACK )
12949 {
12950 charging = qtrue;
12951 }
12952 break;
12953
12954 //------------------
12955 case WP_DEMP2:
12956
12957 // alt-fire charges the weapon
12958 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
12959 {
12960 charging = qtrue;
12961 altFire = qtrue;
12962 }
12963 break;
12964
12965 //------------------
12966 case WP_ROCKET_LAUNCHER:
12967
12968 // Not really a charge weapon, but we still want to delay fire until the button comes up so that we can
12969 // implement our alt-fire locking stuff
12970 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
12971 {
12972 charging = qtrue;
12973 altFire = qtrue;
12974 }
12975 break;
12976
12977 //------------------
12978 case WP_THERMAL:
12979 // FIXME: Really should have a wind-up anim for player
12980 // as he holds down the fire button to throw, then play
12981 // the actual throw when he lets go...
12982 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
12983 {
12984 altFire = qtrue; // override default of not being an alt-fire
12985 charging = qtrue;
12986 }
12987 else if ( pm->cmd.buttons & BUTTON_ATTACK )
12988 {
12989 charging = qtrue;
12990 }
12991 break;
12992
12993 } // end switch
12994
12995 // set up the appropriate weapon state based on the button that's down.
12996 // Note that we ALWAYS return if charging is set ( meaning the buttons are still down )
12997 if ( charging )
12998 {
12999
13000
13001 if ( altFire )
13002 {
13003 if ( pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_DROPPING )
13004 {
13005 if ( pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] <= 0)
13006 {
13007 PM_AddEvent( EV_NOAMMO );
13008 pm->ps->weaponTime += 500;
13009 return true;
13010 }
13011
13012 // charge isn't started, so do it now
13013 pm->ps->weaponstate = WEAPON_CHARGING_ALT;
13014 pm->ps->weaponChargeTime = level.time;
13015
13016 if ( cg_weapons[pm->ps->weapon].altChargeSound )
13017 {
13018 G_SoundOnEnt( pm->gent, CHAN_WEAPON, weaponData[pm->ps->weapon].altChargeSnd );
13019 }
13020 }
13021 }
13022 else
13023 {
13024
13025 if ( pm->ps->weaponstate != WEAPON_CHARGING && pm->ps->weaponstate != WEAPON_DROPPING )
13026 {
13027 if ( pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] <= 0)
13028 {
13029 PM_AddEvent( EV_NOAMMO );
13030 pm->ps->weaponTime += 500;
13031 return true;
13032 }
13033
13034 // charge isn't started, so do it now
13035 pm->ps->weaponstate = WEAPON_CHARGING;
13036 pm->ps->weaponChargeTime = level.time;
13037
13038 if ( cg_weapons[pm->ps->weapon].chargeSound && pm->gent && !pm->gent->NPC ) // HACK: !NPC mostly for bowcaster and weequay
13039 {
13040 G_SoundOnEnt( pm->gent, CHAN_WEAPON, weaponData[pm->ps->weapon].chargeSnd );
13041 }
13042 }
13043 }
13044
13045 return true; // short-circuit rest of weapon code
13046 }
13047
13048 // Only charging weapons should be able to set these states...so....
13049 // let's see which fire mode we need to set up now that the buttons are up
13050 if ( pm->ps->weaponstate == WEAPON_CHARGING )
13051 {
13052 // weapon has a charge, so let us do an attack
13053 // dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now
13054 pm->cmd.buttons |= BUTTON_ATTACK;
13055 pm->ps->eFlags |= EF_FIRING;
13056 }
13057 else if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT )
13058 {
13059 // weapon has a charge, so let us do an alt-attack
13060 // dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now
13061 pm->cmd.buttons |= BUTTON_ALT_ATTACK;
13062 pm->ps->eFlags |= (EF_FIRING|EF_ALT_FIRING);
13063 }
13064
13065 return false; // continue with the rest of the weapon code
13066 }
13067
13068
13069 #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
13070 #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
13071 #define DEMP2_CHARGE_UNIT 500.0f // ditto
13072 #define DISRUPTOR_CHARGE_UNIT 150.0f // ditto
13073
13074 // Specific weapons can opt to modify the ammo usage based on charges, otherwise if no special case code
13075 // is handled below, regular ammo usage will happen
13076 //---------------------------------------
PM_DoChargingAmmoUsage(int * amount)13077 static int PM_DoChargingAmmoUsage( int *amount )
13078 //---------------------------------------
13079 {
13080 int count = 0;
13081
13082 if ( pm->ps->weapon == WP_BOWCASTER && !( pm->cmd.buttons & BUTTON_ALT_ATTACK ))
13083 {
13084 // this code is duplicated ( I know, I know ) in G_weapon.cpp for the bowcaster alt-fire
13085 count = ( level.time - pm->ps->weaponChargeTime ) / BOWCASTER_CHARGE_UNIT;
13086
13087 if ( count < 1 )
13088 {
13089 count = 1;
13090 }
13091 else if ( count > 5 )
13092 {
13093 count = 5;
13094 }
13095
13096 if ( !(count & 1 ))
13097 {
13098 // if we aren't odd, knock us down a level
13099 count--;
13100 }
13101
13102 // Only bother with these checks if we don't have infinite ammo
13103 if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
13104 {
13105 int dif = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - *amount * count;
13106
13107 // If we have enough ammo to do the full charged shot, we are ok
13108 if ( dif < 0 )
13109 {
13110 // we are not ok, so hack our chargetime and ammo usage, note that DIF is going to be negative
13111 count += floor(dif / (float)*amount);
13112
13113 if ( count < 1 )
13114 {
13115 count = 1;
13116 }
13117
13118 // now get a real chargeTime so the duplicated code in g_weapon doesn't get freaked
13119 pm->ps->weaponChargeTime = level.time - ( count * BOWCASTER_CHARGE_UNIT );
13120 }
13121 }
13122
13123 // now that count is cool, get the real ammo usage
13124 *amount *= count;
13125 }
13126 else if( ( pm->ps->weapon == WP_BRYAR_PISTOL && pm->cmd.buttons & BUTTON_ALT_ATTACK )
13127 || ( pm->ps->weapon == WP_BLASTER_PISTOL && pm->cmd.buttons & BUTTON_ALT_ATTACK ) )
13128 {
13129 // this code is duplicated ( I know, I know ) in G_weapon.cpp for the bryar alt-fire
13130 count = ( level.time - pm->ps->weaponChargeTime ) / BRYAR_CHARGE_UNIT;
13131
13132 if ( count < 1 )
13133 {
13134 count = 1;
13135 }
13136 else if ( count > 5 )
13137 {
13138 count = 5;
13139 }
13140
13141 // Only bother with these checks if we don't have infinite ammo
13142 if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
13143 {
13144 int dif = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - *amount * count;
13145
13146 // If we have enough ammo to do the full charged shot, we are ok
13147 if ( dif < 0 )
13148 {
13149 // we are not ok, so hack our chargetime and ammo usage, note that DIF is going to be negative
13150 count += floor(dif / (float)*amount);
13151
13152 if ( count < 1 )
13153 {
13154 count = 1;
13155 }
13156
13157 // now get a real chargeTime so the duplicated code in g_weapon doesn't get freaked
13158 pm->ps->weaponChargeTime = level.time - ( count * BRYAR_CHARGE_UNIT );
13159 }
13160 }
13161
13162 // now that count is cool, get the real ammo usage
13163 *amount *= count;
13164 }
13165 else if ( pm->ps->weapon == WP_DEMP2 && pm->cmd.buttons & BUTTON_ALT_ATTACK )
13166 {
13167 // this code is duplicated ( I know, I know ) in G_weapon.cpp for the demp2 alt-fire
13168 count = ( level.time - pm->ps->weaponChargeTime ) / DEMP2_CHARGE_UNIT;
13169
13170 if ( count < 1 )
13171 {
13172 count = 1;
13173 }
13174 else if ( count > 3 )
13175 {
13176 count = 3;
13177 }
13178
13179 // Only bother with these checks if we don't have infinite ammo
13180 if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
13181 {
13182 int dif = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - *amount * count;
13183
13184 // If we have enough ammo to do the full charged shot, we are ok
13185 if ( dif < 0 )
13186 {
13187 // we are not ok, so hack our chargetime and ammo usage, note that DIF is going to be negative
13188 count += floor(dif / (float)*amount);
13189
13190 if ( count < 1 )
13191 {
13192 count = 1;
13193 }
13194
13195 // now get a real chargeTime so the duplicated code in g_weapon doesn't get freaked
13196 pm->ps->weaponChargeTime = level.time - ( count * DEMP2_CHARGE_UNIT );
13197 }
13198 }
13199
13200 // now that count is cool, get the real ammo usage
13201 *amount *= count;
13202
13203 // this is an after-thought. should probably re-write the function to do this naturally.
13204 if ( *amount > pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] )
13205 {
13206 *amount = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex];
13207 }
13208 }
13209 else if ( pm->ps->weapon == WP_DISRUPTOR && pm->cmd.buttons & BUTTON_ALT_ATTACK ) // BUTTON_ATTACK will have been mapped to BUTTON_ALT_ATTACK if we are zoomed
13210 {
13211 // this code is duplicated ( I know, I know ) in G_weapon.cpp for the disruptor alt-fire
13212 count = ( level.time - pm->ps->weaponChargeTime ) / DISRUPTOR_CHARGE_UNIT;
13213
13214 if ( count < 1 )
13215 {
13216 count = 1;
13217 }
13218 else if ( count > 10 )
13219 {
13220 count = 10;
13221 }
13222
13223 // Only bother with these checks if we don't have infinite ammo
13224 if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
13225 {
13226 int dif = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - *amount * count;
13227
13228 // If we have enough ammo to do the full charged shot, we are ok
13229 if ( dif < 0 )
13230 {
13231 // we are not ok, so hack our chargetime and ammo usage, note that DIF is going to be negative
13232 count += floor(dif / (float)*amount);
13233
13234 if ( count < 1 )
13235 {
13236 count = 1;
13237 }
13238
13239 // now get a real chargeTime so the duplicated code in g_weapon doesn't get freaked
13240 pm->ps->weaponChargeTime = level.time - ( count * DISRUPTOR_CHARGE_UNIT );
13241 }
13242 }
13243
13244 // now that count is cool, get the real ammo usage
13245 *amount *= count;
13246
13247 // this is an after-thought. should probably re-write the function to do this naturally.
13248 if ( *amount > pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] )
13249 {
13250 *amount = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex];
13251 }
13252 }
13253
13254 return count;
13255 }
13256
PM_DroidMelee(int npc_class)13257 qboolean PM_DroidMelee( int npc_class )
13258 {
13259 if ( npc_class == CLASS_PROBE
13260 || npc_class == CLASS_SEEKER
13261 || npc_class == CLASS_INTERROGATOR
13262 || npc_class == CLASS_SENTRY
13263 || npc_class == CLASS_REMOTE )
13264 {
13265 return qtrue;
13266 }
13267 return qfalse;
13268 }
13269
PM_WeaponWampa(void)13270 void PM_WeaponWampa( void )
13271 {
13272 // make weapon function
13273 if ( pm->ps->weaponTime > 0 ) {
13274 pm->ps->weaponTime -= pml.msec;
13275 if ( pm->ps->weaponTime <= 0 )
13276 {
13277 pm->ps->weaponTime = 0;
13278 }
13279 }
13280
13281 // check for weapon change
13282 // can't change if weapon is firing, but can change again if lowering or raising
13283 if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
13284 if ( pm->ps->weapon != pm->cmd.weapon ) {
13285 PM_BeginWeaponChange( pm->cmd.weapon );
13286 }
13287 }
13288
13289 if ( pm->ps->weaponTime > 0 )
13290 {
13291 return;
13292 }
13293
13294 // change weapon if time
13295 if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
13296 PM_FinishWeaponChange();
13297 return;
13298 }
13299
13300 if ( pm->ps->weapon == WP_SABER
13301 && (pm->cmd.buttons&BUTTON_ATTACK)
13302 && pm->ps->torsoAnim == BOTH_HANG_IDLE )
13303 {
13304 pm->ps->SaberActivate();
13305 pm->ps->SaberActivateTrail( 150 );
13306 PM_SetAnim( pm, SETANIM_BOTH, BOTH_HANG_ATTACK, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
13307 pm->ps->weaponstate = WEAPON_FIRING;
13308 pm->ps->saberBlocked = BLOCKED_NONE;
13309 pm->ps->saberMove = LS_READY;
13310 pm->ps->saberMoveNext = LS_NONE;
13311 }
13312 else if ( pm->ps->torsoAnim == BOTH_HANG_IDLE )
13313 {
13314 pm->ps->SaberDeactivateTrail( 0 );
13315 pm->ps->weaponstate = WEAPON_READY;
13316 pm->ps->saberMove = LS_READY;
13317 pm->ps->saberMoveNext = LS_NONE;
13318 }
13319 }
13320 /*
13321 ==============
13322 PM_Weapon
13323
13324 Generates weapon events and modifes the weapon counter
13325 ==============
13326 */
PM_Weapon(void)13327 static void PM_Weapon( void )
13328 {
13329 int addTime, amount, trueCount = 1;
13330 qboolean delayed_fire = qfalse;
13331
13332 if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) )
13333 {
13334 PM_WeaponWampa();
13335 return;
13336 }
13337 if ( pm->ps->eFlags&EF_FORCE_DRAINED )
13338 {//being drained
13339 return;
13340 }
13341 if ( (pm->ps->forcePowersActive&(1<<FP_DRAIN))
13342 && pm->ps->forceDrainEntityNum < ENTITYNUM_WORLD )
13343 {//draining
13344 return;
13345 }
13346 if (pm->ps->weapon == WP_SABER && (cg.zoomMode==3||!cg.zoomMode||pm->ps->clientNum) ) // WP_LIGHTSABER
13347 { // Separate logic for lightsaber, but not for player when zoomed
13348 PM_WeaponLightsaber();
13349 if ( pm->gent && pm->gent->client && pm->ps->saber[0].Active() && pm->ps->saberInFlight )
13350 {//FIXME: put saberTrail in playerState
13351 if ( pm->gent->client->ps.saberEntityState == SES_RETURNING )
13352 {//turn off the saber trail
13353 pm->gent->client->ps.SaberDeactivateTrail( 75 );
13354 }
13355 else
13356 {//turn on the saber trail
13357 pm->gent->client->ps.SaberActivateTrail( 150 );
13358 }
13359 }
13360 return;
13361 }
13362
13363 if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps ))
13364 {//in knockdown
13365 if ( pm->ps->weaponTime > 0 ) {
13366 pm->ps->weaponTime -= pml.msec;
13367 if ( pm->ps->weaponTime <= 0 )
13368 {
13369 pm->ps->weaponTime = 0;
13370 }
13371 }
13372 return;
13373 }
13374
13375 if( pm->gent && pm->gent->client )
13376 {
13377 if ( pm->gent->client->fireDelay > 0 )
13378 {//FIXME: this is going to fire off one frame before you expect, actually
13379 pm->gent->client->fireDelay -= pml.msec;
13380 if(pm->gent->client->fireDelay <= 0)
13381 {//just finished delay timer
13382 if ( pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER )
13383 {
13384 G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/rocket/lock.wav" );
13385 pm->cmd.buttons |= BUTTON_ALT_ATTACK;
13386 }
13387 pm->gent->client->fireDelay = 0;
13388 delayed_fire = qtrue;
13389 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
13390 && pm->ps->weapon == WP_THERMAL
13391 && pm->gent->alt_fire )
13392 {
13393 pm->cmd.buttons |= BUTTON_ALT_ATTACK;
13394 }
13395 }
13396 else
13397 {
13398 if ( pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER && Q_irand( 0, 1 ) )
13399 {
13400 G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/rocket/tick.wav" );
13401 }
13402 }
13403 }
13404 }
13405
13406 // don't allow attack until all buttons are up
13407 if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
13408 return;
13409 }
13410
13411 // check for dead player
13412 if ( pm->ps->stats[STAT_HEALTH] <= 0 )
13413 {
13414 if ( pm->gent && pm->gent->client )
13415 {
13416 pm->ps->weapon = WP_NONE;
13417 }
13418
13419 if ( pm->gent )
13420 {
13421 pm->gent->s.loopSound = 0;
13422 }
13423 return;
13424 }
13425
13426 // make weapon function
13427 if ( pm->ps->weaponTime > 0 ) {
13428 pm->ps->weaponTime -= pml.msec;
13429 if ( pm->ps->weaponTime <= 0 )
13430 {
13431 pm->ps->weaponTime = 0;
13432 }
13433 }
13434
13435 // check for weapon change
13436 // can't change if weapon is firing, but can change again if lowering or raising
13437 if ( (pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING) && pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_CHARGING) {
13438 if ( pm->ps->weapon != pm->cmd.weapon && (!pm->ps->viewEntity || pm->ps->viewEntity >= ENTITYNUM_WORLD) && !PM_DoChargedWeapons()) {
13439 PM_BeginWeaponChange( pm->cmd.weapon );
13440 }
13441 }
13442
13443 if ( pm->ps->weaponTime > 0 )
13444 {
13445 return;
13446 }
13447
13448 // change weapon if time
13449 if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
13450 PM_FinishWeaponChange();
13451 return;
13452 }
13453
13454 if ( pm->ps->weapon == WP_NONE )
13455 {
13456 return;
13457 }
13458
13459 if ( pm->ps->weaponstate == WEAPON_RAISING )
13460 {
13461 //Just selected the weapon
13462 pm->ps->weaponstate = WEAPON_IDLE;
13463
13464 if(pm->gent && (pm->gent->s.number<MAX_CLIENTS||G_ControlledByPlayer(pm->gent)))
13465 {
13466 PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL);
13467 }
13468 else
13469 {
13470 switch(pm->ps->weapon)
13471 {
13472 case WP_BRYAR_PISTOL:
13473 case WP_BLASTER_PISTOL:
13474 if ( pm->gent
13475 && pm->gent->weaponModel[1] > 0 )
13476 {//dual pistols
13477 //FIXME: should be a better way of detecting a dual-pistols user so it's not hardcoded to the saboteurcommando...
13478 PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL);
13479 }
13480 else
13481 {//single pistol
13482 PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL);
13483 }
13484 break;
13485 default:
13486 PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL);
13487 break;
13488 }
13489 }
13490 return;
13491 }
13492
13493 if ( pm->gent )
13494 {//ready to throw thermal again, add it
13495 if ( pm->ps->weapon == WP_THERMAL
13496 && pm->gent->weaponModel[0] == -1 )
13497 {//add the thermal model back in our hand
13498 // remove anything if we have anything already
13499 G_RemoveWeaponModels( pm->gent );
13500 if (weaponData[pm->ps->weapon].weaponMdl[0]) { //might be NONE, so check if it has a model
13501 G_CreateG2AttachedWeaponModel( pm->gent, weaponData[pm->ps->weapon].weaponMdl, pm->gent->handRBolt, 0 );
13502 //make it sound like we took another one out from... uh.. somewhere...
13503 if ( cg.time > 0 )
13504 {//this way we don't get that annoying change weapon sound every time a map starts
13505 PM_AddEvent( EV_CHANGE_WEAPON );
13506 }
13507 }
13508 }
13509 }
13510
13511 if ( !delayed_fire )
13512 {//didn't just finish a fire delay
13513 if ( PM_DoChargedWeapons())
13514 {
13515 // In some cases the charged weapon code may want us to short circuit the rest of the firing code
13516 return;
13517 }
13518 else
13519 {
13520 if ( !pm->gent->client->fireDelay//not already waiting to fire
13521 && (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//player
13522 && pm->ps->weapon == WP_THERMAL//holding thermal
13523 && pm->gent//gent
13524 && pm->gent->client//client
13525 && (pm->cmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )//holding fire
13526 {//delay the actual firing of the missile until the anim has played some
13527 if ( PM_StandingAnim( pm->ps->legsAnim )
13528 || pm->ps->legsAnim == BOTH_THERMAL_READY )
13529 {
13530 PM_SetAnim( pm, SETANIM_LEGS, BOTH_THERMAL_THROW, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
13531 }
13532 PM_SetAnim(pm,SETANIM_TORSO,BOTH_THERMAL_THROW,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13533 pm->gent->client->fireDelay = 300;
13534 pm->ps->weaponstate = WEAPON_FIRING;
13535 pm->gent->alt_fire = (qboolean)(pm->cmd.buttons&BUTTON_ALT_ATTACK);
13536 return;
13537 }
13538 }
13539 }
13540
13541 if(!delayed_fire)
13542 {
13543 if ( pm->ps->weapon == WP_MELEE && (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
13544 {//melee
13545 if ( (pm->cmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)) != (BUTTON_ATTACK|BUTTON_ALT_ATTACK) )
13546 {//not holding both buttons
13547 if ( (pm->cmd.buttons&BUTTON_ATTACK)&&(pm->ps->pm_flags&PMF_ATTACK_HELD) )
13548 {//held button
13549 //clear it
13550 pm->cmd.buttons &= ~BUTTON_ATTACK;
13551 }
13552 if ( (pm->cmd.buttons&BUTTON_ALT_ATTACK)&&(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD) )
13553 {//held button
13554 //clear it
13555 pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
13556 }
13557 }
13558 }
13559 // check for fire
13560 if ( !(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )
13561 {
13562 pm->ps->weaponTime = 0;
13563
13564 if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
13565 {//Still firing
13566 pm->ps->weaponstate = WEAPON_FIRING;
13567 }
13568 else if ( pm->ps->weaponstate != WEAPON_READY )
13569 {
13570 if ( !pm->gent || !pm->gent->NPC || pm->gent->attackDebounceTime < level.time )
13571 {
13572 pm->ps->weaponstate = WEAPON_IDLE;
13573 }
13574 }
13575
13576 if ( pm->ps->weapon == WP_MELEE
13577 && (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
13578 && PM_KickMove( pm->ps->saberMove ) )
13579 {//melee, not attacking, clear move
13580 pm->ps->saberMove = LS_NONE;
13581 }
13582 return;
13583 }
13584 if (pm->gent->s.m_iVehicleNum!=0)
13585 {
13586 // No Anims if on Veh
13587 }
13588
13589 // start the animation even if out of ammo
13590 else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ROCKETTROOPER )
13591 {
13592 if ( pm->gent->client->moveType == MT_FLYSWIM )
13593 {
13594 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13595 }
13596 else
13597 {
13598 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13599 }
13600 }
13601 #ifndef BASE_SAVE_COMPAT
13602 else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_HAZARD_TROOPER )
13603 {
13604 // Kneel attack
13605 //--------------
13606 if( pm->cmd.upmove == -127 )
13607 {
13608 PM_SetAnim(pm,SETANIM_TORSO, BOTH_KNEELATTACK, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13609 }
13610 else
13611 {
13612 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13613 }
13614
13615 // Standing attack
13616 //-----------------
13617 }
13618 #endif
13619 else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ASSASSIN_DROID )
13620 {
13621 // Crouched Attack
13622 if (PM_CrouchAnim(pm->gent->client->ps.legsAnim))
13623 {
13624 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLDLESS);
13625 }
13626
13627 // Standing Attack
13628 //-----------------
13629 else
13630 {
13631 // if (PM_StandingAnim(pm->gent->client->ps.legsAnim))
13632 // {
13633 // PM_SetAnim(pm,SETANIM_BOTH,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLDLESS);
13634 // }
13635 // else
13636 {
13637 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLDLESS);
13638 }
13639 }
13640 }
13641 else
13642 {
13643 switch(pm->ps->weapon)
13644 {
13645 /*
13646 case WP_SABER://1 - handed
13647 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13648 break;
13649 */
13650 case WP_BRYAR_PISTOL://1-handed
13651 case WP_BLASTER_PISTOL://1-handed
13652 if ( pm->gent && pm->gent->weaponModel[1] > 0 )
13653 {//dual pistols
13654 PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13655 }
13656 else
13657 {//single pistol
13658 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13659 }
13660 break;
13661
13662 case WP_MELEE:
13663
13664 // since there's no RACE_BOTS, I listed all the droids that have might have melee attacks - dmv
13665 if ( pm->gent && pm->gent->client )
13666 {
13667 if ( PM_DroidMelee( pm->gent->client->NPC_class ) )
13668 {
13669 if ( rand() & 1 )
13670 PM_SetAnim(pm,SETANIM_BOTH,BOTH_MELEE1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
13671 else
13672 PM_SetAnim(pm,SETANIM_BOTH,BOTH_MELEE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
13673 }
13674 else
13675 {
13676 int anim = -1;
13677 if ( (pm->ps->clientNum < MAX_CLIENTS ||PM_ControlledByPlayer())
13678 && g_debugMelee->integer )
13679 {
13680 if ( (pm->cmd.buttons&BUTTON_ALT_ATTACK) )
13681 {
13682 if ( (pm->cmd.buttons&BUTTON_ATTACK) )
13683 {
13684 PM_TryGrab();
13685 }
13686 else if ( !(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD) )
13687 {
13688 PM_CheckKick();
13689 }
13690 }
13691 else if ( !(pm->ps->pm_flags&PMF_ATTACK_HELD) )
13692 {
13693 anim = PM_PickAnim( pm->gent, BOTH_MELEE1, BOTH_MELEE2 );
13694 }
13695 }
13696 else
13697 {
13698 anim = PM_PickAnim( pm->gent, BOTH_MELEE1, BOTH_MELEE2 );
13699 }
13700 if ( anim != -1 )
13701 {
13702 if ( VectorCompare( pm->ps->velocity, vec3_origin ) && pm->cmd.upmove >= 0 )
13703 {
13704 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
13705 }
13706 else
13707 {
13708 PM_SetAnim( pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
13709 }
13710 }
13711 }
13712 }
13713 break;
13714
13715 case WP_TUSKEN_RIFLE:
13716 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
13717 {//shoot
13718 //in alt-fire, sniper mode
13719 PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
13720 }
13721 else
13722 {//melee
13723 int anim = PM_PickAnim( pm->gent, BOTH_TUSKENATTACK1, BOTH_TUSKENATTACK3 ); // Rifle
13724 if ( VectorCompare( pm->ps->velocity, vec3_origin ) && pm->cmd.upmove >= 0 )
13725 {
13726 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
13727 }
13728 else
13729 {
13730 PM_SetAnim( pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
13731 }
13732 }
13733 break;
13734
13735 case WP_TUSKEN_STAFF:
13736
13737 if ( pm->gent && pm->gent->client )
13738 {
13739 int anim;
13740 int flags = (SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART);
13741 if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
13742 {//player
13743 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
13744 {
13745 if ( pm->cmd.buttons & BUTTON_ATTACK )
13746 {
13747 anim = BOTH_TUSKENATTACK3;
13748 }
13749 else
13750 {
13751 anim = BOTH_TUSKENATTACK2;
13752 }
13753 }
13754 else
13755 {
13756 anim = BOTH_TUSKENATTACK1;
13757 }
13758 }
13759 else
13760 {// npc
13761 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
13762 {
13763 anim = BOTH_TUSKENLUNGE1;
13764 if (pm->ps->torsoAnimTimer>0)
13765 {
13766 flags &= ~SETANIM_FLAG_RESTART;
13767 }
13768 }
13769 else
13770 {
13771 anim = PM_PickAnim( pm->gent, BOTH_TUSKENATTACK1, BOTH_TUSKENATTACK3 );
13772 }
13773 }
13774 if ( VectorCompare( pm->ps->velocity, vec3_origin ) && pm->cmd.upmove >= 0 )
13775 {
13776 PM_SetAnim( pm, SETANIM_BOTH, anim, flags, 0);
13777 }
13778 else
13779 {
13780 PM_SetAnim( pm, SETANIM_TORSO, anim, flags, 0);
13781 }
13782 }
13783 break;
13784
13785 case WP_NOGHRI_STICK:
13786
13787 if ( pm->gent && pm->gent->client )
13788 {
13789 int anim;
13790 if ( pm->cmd.buttons & BUTTON_ATTACK )
13791 {
13792 anim = BOTH_ATTACK3;
13793 }
13794 else
13795 {
13796 anim = PM_PickAnim( pm->gent, BOTH_TUSKENATTACK1, BOTH_TUSKENATTACK3 );
13797 }
13798 if ( anim != BOTH_ATTACK3 && VectorCompare( pm->ps->velocity, vec3_origin ) && pm->cmd.upmove >= 0 )
13799 {
13800 PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
13801 }
13802 else
13803 {
13804 PM_SetAnim( pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
13805 }
13806 }
13807 break;
13808
13809 case WP_BLASTER:
13810 PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART);
13811 break;
13812
13813 case WP_DISRUPTOR:
13814 if ( ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())&& pm->gent && pm->gent->NPC && (pm->gent->NPC->scriptFlags&SCF_ALT_FIRE)) ||
13815 ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.zoomMode == 2 ) )
13816 {//NPC or player in alt-fire, sniper mode
13817 PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
13818 }
13819 else
13820 {//in primary fire mode
13821 PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART);
13822 }
13823 break;
13824
13825 case WP_BOT_LASER:
13826 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13827 break;
13828
13829 case WP_THERMAL:
13830 if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) )
13831 {
13832 if ( PM_StandingAnim( pm->ps->legsAnim ) )
13833 {
13834 PM_SetAnim( pm, SETANIM_LEGS, BOTH_ATTACK10, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
13835 }
13836 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK10,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13837 }
13838 else
13839 {
13840 if ( cg.renderingThirdPerson )
13841 {
13842 if ( PM_StandingAnim( pm->ps->legsAnim )
13843 || pm->ps->legsAnim == BOTH_THERMAL_READY )
13844 {
13845 PM_SetAnim( pm, SETANIM_LEGS, BOTH_THERMAL_THROW, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
13846 }
13847 PM_SetAnim(pm,SETANIM_TORSO,BOTH_THERMAL_THROW,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//|SETANIM_FLAG_RESTART
13848 }
13849 else
13850 {
13851 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13852 }
13853 }
13854 break;
13855
13856 case WP_EMPLACED_GUN:
13857 // Guess we don't play an attack animation? Maybe we should have a custom one??
13858 break;
13859
13860 case WP_NONE:
13861 // no anim
13862 break;
13863
13864 case WP_REPEATER:
13865 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH )
13866 {//
13867 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
13868 {
13869 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13870 }
13871 else
13872 {
13873 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13874 }
13875 }
13876 else
13877 {
13878 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13879 }
13880 break;
13881
13882 case WP_TRIP_MINE:
13883 case WP_DET_PACK:
13884 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK11,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13885 break;
13886
13887 default://2-handed heavy weapon
13888 PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13889 break;
13890 }
13891 }
13892 }
13893
13894 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
13895 {
13896 amount = weaponData[pm->ps->weapon].altEnergyPerShot;
13897 }
13898 else
13899 {
13900 amount = weaponData[pm->ps->weapon].energyPerShot;
13901 }
13902
13903 if ( (pm->ps->weaponstate == WEAPON_CHARGING) || (pm->ps->weaponstate == WEAPON_CHARGING_ALT) )
13904 {
13905 // charging weapons may want to do their own ammo logic.
13906 trueCount = PM_DoChargingAmmoUsage( &amount );
13907 }
13908
13909 pm->ps->weaponstate = WEAPON_FIRING;
13910
13911 // take an ammo away if not infinite
13912 if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
13913 {
13914 // enough energy to fire this weapon?
13915 if ((pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - amount) >= 0)
13916 {
13917 pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= amount;
13918 }
13919 else // Not enough energy
13920 {
13921 if ( !( pm->ps->eFlags & EF_LOCKED_TO_WEAPON ))
13922 {
13923 // Switch weapons
13924 PM_AddEvent( EV_NOAMMO );
13925 pm->ps->weaponTime += 500;
13926 }
13927 return;
13928 }
13929 }
13930
13931 if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
13932 {//FIXME: this is going to fire off one frame before you expect, actually
13933 // Clear these out since we're not actually firing yet
13934 pm->ps->eFlags &= ~EF_FIRING;
13935 pm->ps->eFlags &= ~EF_ALT_FIRING;
13936 return;
13937 }
13938
13939 if ( pm->ps->weapon == WP_EMPLACED_GUN )
13940 {
13941 if ( pm->gent
13942 && pm->gent->owner
13943 && pm->gent->owner->e_UseFunc == useF_eweb_use )
13944 {//eweb always shoots alt-fire, for proper effects and sounds
13945 PM_AddEvent( EV_ALT_FIRE );
13946 addTime = weaponData[pm->ps->weapon].altFireTime;
13947 }
13948 else
13949 {//emplaced gun always shoots normal fire
13950 PM_AddEvent( EV_FIRE_WEAPON );
13951 addTime = weaponData[pm->ps->weapon].fireTime;
13952 }
13953 }
13954 else if ( (pm->ps->weapon == WP_MELEE && (pm->ps->clientNum>=MAX_CLIENTS||!g_debugMelee->integer) )
13955 || pm->ps->weapon == WP_TUSKEN_STAFF
13956 || (pm->ps->weapon == WP_TUSKEN_RIFLE&&!(pm->cmd.buttons&BUTTON_ALT_ATTACK)) )
13957 {
13958 PM_AddEvent( EV_FIRE_WEAPON );
13959 addTime = pm->ps->torsoAnimTimer;
13960 }
13961 else if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
13962 {
13963 PM_AddEvent( EV_ALT_FIRE );
13964 addTime = weaponData[pm->ps->weapon].altFireTime;
13965 if ( pm->ps->weapon == WP_THERMAL )
13966 {//threw our thermal
13967 if ( pm->gent )
13968 {// remove the thermal model if we had it.
13969 G_RemoveWeaponModels( pm->gent );
13970 if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) )
13971 {//NPCs need to know when to put the thermal back in their hand
13972 pm->ps->weaponTime = pm->ps->torsoAnimTimer-500;
13973 }
13974 }
13975 }
13976 }
13977 else
13978 {
13979 if ( pm->ps->clientNum //NPC
13980 && !PM_ControlledByPlayer() //not under player control
13981 && pm->ps->weapon == WP_THERMAL //using thermals
13982 && pm->ps->torsoAnim != BOTH_ATTACK10 )//not in the throw anim
13983 {//oops, got knocked out of the anim, don't throw the thermal
13984 return;
13985 }
13986 PM_AddEvent( EV_FIRE_WEAPON );
13987 addTime = weaponData[pm->ps->weapon].fireTime;
13988
13989 switch( pm->ps->weapon)
13990 {
13991 case WP_REPEATER:
13992 // repeater is supposed to do smoke after sustained bursts
13993 pm->ps->weaponShotCount++;
13994 break;
13995 case WP_BOWCASTER:
13996 addTime *= (( trueCount < 3 ) ? 0.35f : 1.0f );// if you only did a small charge shot with the bowcaster, use less time between shots
13997 break;
13998 case WP_THERMAL:
13999 if ( pm->gent )
14000 {// remove the thermal model if we had it.
14001 G_RemoveWeaponModels( pm->gent );
14002 if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) )
14003 {//NPCs need to know when to put the thermal back in their hand
14004 pm->ps->weaponTime = pm->ps->torsoAnimTimer-500;
14005 }
14006 }
14007 break;
14008 }
14009 }
14010
14011
14012 if(!PM_ControlledByPlayer())
14013 {
14014 if(pm->gent && pm->gent->NPC != NULL )
14015 {//NPCs have their own refire logic
14016 return;
14017 }
14018 }
14019
14020 if ( g_timescale != NULL )
14021 {
14022 if ( g_timescale->value < 1.0f )
14023 {
14024 if ( !MatrixMode )
14025 {//Special test for Matrix Mode (tm)
14026 if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
14027 {//player always fires at normal speed
14028 addTime *= g_timescale->value;
14029 }
14030 else if ( g_entities[pm->ps->clientNum].client
14031 && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
14032 {
14033 addTime *= g_timescale->value;
14034 }
14035 }
14036 }
14037 }
14038
14039 pm->ps->weaponTime += addTime;
14040 pm->ps->lastShotTime = level.time;//so we know when the last time we fired our gun is
14041
14042 // HACK!!!!!
14043 if ( pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] <= 0 )
14044 {
14045 if ( pm->ps->weapon == WP_THERMAL || pm->ps->weapon == WP_TRIP_MINE )
14046 {
14047 // because these weapons have the ammo attached to the hand, we should switch weapons when the last one is thrown, otherwise it will look silly
14048 // NOTE: could also switch to an empty had version, but was told we aren't getting any new models at this point
14049 CG_OutOfAmmoChange();
14050 PM_SetAnim(pm,SETANIM_TORSO,TORSO_DROPWEAP1 + 2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); // hack weapon down!
14051 pm->ps->weaponTime = 50;
14052 }
14053 }
14054 }
14055
14056 /*
14057 ==============
14058 PM_VehicleWeapon
14059
14060 Generates weapon events and modifes the weapon counter
14061 ==============
14062 */
PM_VehicleWeapon(void)14063 static void PM_VehicleWeapon( void )
14064 {
14065 int addTime = 0;
14066 qboolean delayed_fire = qfalse;
14067
14068 if ( pm->ps->weapon == WP_NONE )
14069 {
14070 return;
14071 }
14072
14073 if(pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0)
14074 {//FIXME: this is going to fire off one frame before you expect, actually
14075 pm->gent->client->fireDelay -= pml.msec;
14076 if(pm->gent->client->fireDelay <= 0)
14077 {//just finished delay timer
14078 if ( pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER )
14079 {
14080 G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/rocket/lock.wav" );
14081 pm->cmd.buttons |= BUTTON_ALT_ATTACK;
14082 }
14083 pm->gent->client->fireDelay = 0;
14084 delayed_fire = qtrue;
14085 }
14086 else
14087 {
14088 if ( pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER && Q_irand( 0, 1 ) )
14089 {
14090 G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/rocket/tick.wav" );
14091 }
14092 }
14093 }
14094
14095 // don't allow attack until all buttons are up
14096 if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
14097 return;
14098 }
14099
14100 // check for dead player
14101 if ( pm->ps->stats[STAT_HEALTH] <= 0 )
14102 {
14103 if ( pm->gent )
14104 {
14105 pm->gent->s.loopSound = 0;
14106 }
14107 return;
14108 }
14109
14110 // make weapon function
14111 if ( pm->ps->weaponTime > 0 ) {
14112 pm->ps->weaponTime -= pml.msec;
14113 if ( pm->ps->weaponTime <= 0 )
14114 {
14115 pm->ps->weaponTime = 0;
14116 }
14117 }
14118
14119 if ( pm->ps->weaponTime > 0 )
14120 {
14121 return;
14122 }
14123
14124 // change weapon if time
14125 if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
14126 PM_FinishWeaponChange();
14127 return;
14128 }
14129
14130 if ( PM_DoChargedWeapons())
14131 {
14132 // In some cases the charged weapon code may want us to short circuit the rest of the firing code
14133 return;
14134 }
14135
14136 if(!delayed_fire)
14137 {
14138 // check for fire
14139 if ( !(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )
14140 {
14141 pm->ps->weaponTime = 0;
14142
14143 if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
14144 {//Still firing
14145 pm->ps->weaponstate = WEAPON_FIRING;
14146 }
14147 else if ( pm->ps->weaponstate != WEAPON_READY )
14148 {
14149 if ( !pm->gent || !pm->gent->NPC || pm->gent->attackDebounceTime < level.time )
14150 {
14151 pm->ps->weaponstate = WEAPON_IDLE;
14152 }
14153 }
14154
14155 return;
14156 }
14157 }
14158
14159 pm->ps->weaponstate = WEAPON_FIRING;
14160
14161 if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
14162 {//FIXME: this is going to fire off one frame before you expect, actually
14163 // Clear these out since we're not actually firing yet
14164 pm->ps->eFlags &= ~EF_FIRING;
14165 pm->ps->eFlags &= ~EF_ALT_FIRING;
14166 return;
14167 }
14168
14169 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
14170 {
14171 PM_AddEvent( EV_ALT_FIRE );
14172 //addTime = weaponData[pm->ps->weapon].altFireTime;
14173 }
14174 else
14175 {
14176 PM_AddEvent( EV_FIRE_WEAPON );
14177 // TODO: Use the real weapon fire time from the vehicle cfg file.
14178 //addTime = weaponData[pm->ps->weapon].fireTime;
14179 }
14180
14181 /* if(pm->gent && pm->gent->NPC != NULL )
14182 {//NPCs have their own refire logic
14183 return;
14184 }*/
14185
14186 if ( g_timescale != NULL )
14187 {
14188 if ( g_timescale->value < 1.0f )
14189 {
14190 if ( !MatrixMode )
14191 {//Special test for Matrix Mode (tm)
14192 if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
14193 {//player always fires at normal speed
14194 addTime *= g_timescale->value;
14195 }
14196 else if ( g_entities[pm->ps->clientNum].client
14197 && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
14198 {
14199 addTime *= g_timescale->value;
14200 }
14201 }
14202 }
14203 }
14204
14205 pm->ps->weaponTime += addTime;
14206 pm->ps->lastShotTime = level.time;//so we know when the last time we fired our gun is
14207 }
14208
14209
14210 extern void ForceHeal( gentity_t *self );
14211 extern void ForceTelepathy( gentity_t *self );
14212 extern void ForceRage( gentity_t *self );
14213 extern void ForceProtect( gentity_t *self );
14214 extern void ForceAbsorb( gentity_t *self );
14215 extern void ForceSeeing( gentity_t *self );
PM_CheckForceUseButton(gentity_t * ent,usercmd_t * ucmd)14216 void PM_CheckForceUseButton( gentity_t *ent, usercmd_t *ucmd )
14217 {
14218 if ( !ent )
14219 {
14220 return;
14221 }
14222 if ( ucmd->buttons & BUTTON_USE_FORCE )
14223 {
14224 if (!(ent->client->ps.pm_flags & PMF_USEFORCE_HELD))
14225 {
14226 //impulse one shot
14227 switch ( showPowers[cg.forcepowerSelect] )
14228 {
14229 case FP_HEAL:
14230 ForceHeal( ent );
14231 break;
14232 case FP_SPEED:
14233 ForceSpeed( ent );
14234 break;
14235 case FP_PUSH:
14236 ForceThrow( ent, qfalse );
14237 break;
14238 case FP_PULL:
14239 ForceThrow( ent, qtrue );
14240 break;
14241 case FP_TELEPATHY:
14242 ForceTelepathy( ent );
14243 break;
14244 // Added 01/20/03 by AReis.
14245 // New Jedi Academy powers.
14246 case FP_RAGE: //duration - speed, invincibility and extra damage for short period, drains your health and leaves you weak and slow afterwards.
14247 ForceRage( ent );
14248 break;
14249 case FP_PROTECT: //duration - protect against physical/energy (level 1 stops blaster/energy bolts, level 2 stops projectiles, level 3 protects against explosions)
14250 ForceProtect( ent );
14251 break;
14252 case FP_ABSORB: //duration - protect against dark force powers (grip, lightning, drain - maybe push/pull, too?)
14253 ForceAbsorb( ent );
14254 break;
14255 case FP_SEE: //duration - detect/see hidden enemies
14256 ForceSeeing( ent );
14257 break;
14258 }
14259 }
14260 //these stay are okay to call every frame button is down
14261 switch ( showPowers[cg.forcepowerSelect] )
14262 {
14263 case FP_LEVITATION:
14264 ucmd->upmove = 127;
14265 break;
14266 case FP_GRIP:
14267 ucmd->buttons |= BUTTON_FORCEGRIP;
14268 break;
14269 case FP_LIGHTNING:
14270 ucmd->buttons |= BUTTON_FORCE_LIGHTNING;
14271 break;
14272 case FP_DRAIN:
14273 // FIXME! Failing at WP_ForcePowerUsable(). -AReis
14274 ucmd->buttons |= BUTTON_FORCE_DRAIN;
14275 break;
14276 // default:
14277 // Com_Printf( "Use Force: Unhandled force: %d\n", showPowers[cg.forcepowerSelect]);
14278 // break;
14279 }
14280 ent->client->ps.pm_flags |= PMF_USEFORCE_HELD;
14281 }
14282 else//not pressing USE_FORCE
14283 {
14284 ent->client->ps.pm_flags &= ~PMF_USEFORCE_HELD;
14285 }
14286 }
14287
14288 /*
14289 ================
14290 PM_ForcePower
14291 ================
14292 sends event to client for client side fx, not used
14293 */
14294
14295 /*
14296 static void PM_ForcePower(void)
14297 {
14298 // check for item using
14299 if ( pm->cmd.buttons & BUTTON_USE_FORCE )
14300 {
14301 if ( ! ( pm->ps->pm_flags & PMF_USE_FORCE ) )
14302 {
14303 pm->ps->pm_flags |= PMF_USE_FORCE;
14304 PM_AddEvent( EV_USE_FORCE);
14305 return;
14306 }
14307 }
14308 else
14309 {
14310 pm->ps->pm_flags &= ~PMF_USE_FORCE;
14311 }
14312 }
14313 */
14314
14315 /*
14316 ================
14317 PM_DropTimers
14318 ================
14319 */
PM_DropTimers(void)14320 static void PM_DropTimers( void )
14321 {
14322 // drop misc timing counter
14323 if ( pm->ps->pm_time )
14324 {
14325 if ( pml.msec >= pm->ps->pm_time )
14326 {
14327 pm->ps->pm_flags &= ~PMF_ALL_TIMES;
14328 pm->ps->pm_time = 0;
14329 }
14330 else
14331 {
14332 pm->ps->pm_time -= pml.msec;
14333 }
14334 }
14335
14336 // drop legs animation counter
14337 if ( pm->ps->legsAnimTimer > 0 )
14338 {
14339 int newTime = pm->ps->legsAnimTimer - pml.msec;
14340
14341 if ( newTime < 0 )
14342 {
14343 newTime = 0;
14344 }
14345
14346 PM_SetLegsAnimTimer( pm->gent, &pm->ps->legsAnimTimer, newTime );
14347 }
14348
14349 // drop torso animation counter
14350 if ( pm->ps->torsoAnimTimer > 0 )
14351 {
14352 int newTime = pm->ps->torsoAnimTimer - pml.msec;
14353
14354 if ( newTime < 0 )
14355 {
14356 newTime = 0;
14357 }
14358
14359 PM_SetTorsoAnimTimer( pm->gent, &pm->ps->torsoAnimTimer, newTime );
14360 }
14361 }
14362
PM_SetSpecialMoveValues(void)14363 void PM_SetSpecialMoveValues (void )
14364 {
14365 Flying = 0;
14366 if ( pm->gent )
14367 {
14368 if ( pm->gent->client && pm->gent->client->moveType == MT_FLYSWIM )
14369 {
14370 Flying = FLY_NORMAL;
14371 }
14372 else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
14373 {
14374 if ( pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
14375 {
14376 Flying = FLY_VEHICLE;
14377 }
14378 else if ( pm->gent->m_pVehicle->m_pVehicleInfo->hoverHeight > 0 )
14379 {//FIXME: or just check for hoverHeight?
14380 Flying = FLY_HOVER;
14381 }
14382 }
14383 }
14384
14385 if ( g_timescale != NULL )
14386 {
14387 if ( g_timescale->value < 1.0f )
14388 {
14389 if ( !MatrixMode )
14390 {
14391 if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
14392 {
14393 pml.frametime *= (1.0f/g_timescale->value);
14394 }
14395 else if ( g_entities[pm->ps->clientNum].client
14396 && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
14397 {
14398 pml.frametime *= (1.0f/g_timescale->value);
14399 }
14400 }
14401 }
14402 }
14403 }
14404
14405 extern float cg_zoomFov; //from cg_view.cpp
14406
14407 //-------------------------------------------
PM_AdjustAttackStates(pmove_t * pm)14408 void PM_AdjustAttackStates( pmove_t *pm )
14409 //-------------------------------------------
14410 {
14411 int amount;
14412
14413 if ( !g_saberAutoBlocking->integer
14414 && !g_saberNewControlScheme->integer
14415 && (pm->cmd.buttons&BUTTON_FORCE_FOCUS) )
14416 {
14417 pm->ps->saberBlockingTime = pm->cmd.serverTime + 100;
14418 pm->cmd.buttons &= ~BUTTON_ATTACK;
14419 pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
14420 }
14421 // get ammo usage
14422 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
14423 {
14424 amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - weaponData[pm->ps->weapon].altEnergyPerShot;
14425 }
14426 else
14427 {
14428 amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - weaponData[pm->ps->weapon].energyPerShot;
14429 }
14430
14431 if ( pm->ps->weapon == WP_SABER && (!cg.zoomMode||pm->ps->clientNum) )
14432 {//don't let the alt-attack be interpreted as an actual attack command
14433 if ( pm->ps->saberInFlight )
14434 {
14435 pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
14436 //FIXME: what about alt-attack modifier button?
14437 if ( (!pm->ps->dualSabers || !pm->ps->saber[1].Active()) )
14438 {//saber not in hand, can't swing it
14439 pm->cmd.buttons &= ~BUTTON_ATTACK;
14440 }
14441 }
14442 //saber staff alt-attack does a special attack anim, non-throwable sabers do kicks
14443 if ( pm->ps->saberAnimLevel != SS_STAFF
14444 && !(pm->ps->saber[0].saberFlags&SFL_NOT_THROWABLE) )
14445 {//using a throwable saber, so remove the saber throw button
14446 if ( !g_saberNewControlScheme->integer
14447 && PM_CanDoKata() )
14448 {//old control scheme - alt-attack + attack does kata
14449 }
14450 else
14451 {//new control scheme - alt-attack doesn't have anything to do with katas, safe to clear it here
14452 pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
14453 }
14454 }
14455 }
14456
14457 // disruptor alt-fire should toggle the zoom mode, but only bother doing this for the player?
14458 if ( pm->ps->weapon == WP_DISRUPTOR && pm->gent && (pm->gent->s.number<MAX_CLIENTS||G_ControlledByPlayer(pm->gent)) && pm->ps->weaponstate != WEAPON_DROPPING )
14459 {
14460 // we are not alt-firing yet, but the alt-attack button was just pressed and
14461 // we either are ducking ( in which case we don't care if they are moving )...or they are not ducking...and also not moving right/forward.
14462 if ( !(pm->ps->eFlags & EF_ALT_FIRING) && (pm->cmd.buttons & BUTTON_ALT_ATTACK)
14463 && ( pm->cmd.upmove < 0 || ( !pm->cmd.forwardmove && !pm->cmd.rightmove )))
14464 {
14465 // We just pressed the alt-fire key
14466 if ( cg.zoomMode == 0 || cg.zoomMode == 3 )
14467 {
14468 G_SoundOnEnt( pm->gent, CHAN_AUTO, "sound/weapons/disruptor/zoomstart.wav" );
14469 // not already zooming, so do it now
14470 cg.zoomMode = 2;
14471 cg.zoomLocked = qfalse;
14472 cg_zoomFov = 80.0f;//(cg.overrides.active&CG_OVERRIDE_FOV) ? cg.overrides.fov : cg_fov.value;
14473 }
14474 else if ( cg.zoomMode == 2 )
14475 {
14476 G_SoundOnEnt( pm->gent, CHAN_AUTO, "sound/weapons/disruptor/zoomend.wav" );
14477 // already zooming, so must be wanting to turn it off
14478 cg.zoomMode = 0;
14479 cg.zoomTime = cg.time;
14480 cg.zoomLocked = qfalse;
14481 }
14482 }
14483 else if ( !(pm->cmd.buttons & BUTTON_ALT_ATTACK ))
14484 {
14485 // Not pressing zoom any more
14486 if ( cg.zoomMode == 2 )
14487 {
14488 // were zooming in, so now lock the zoom
14489 cg.zoomLocked = qtrue;
14490 }
14491 }
14492
14493 if ( pm->cmd.buttons & BUTTON_ATTACK )
14494 {
14495 // If we are zoomed, we should switch the ammo usage to the alt-fire, otherwise, we'll
14496 // just use whatever ammo was selected from above
14497 if ( cg.zoomMode == 2 )
14498 {
14499 amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] -
14500 weaponData[pm->ps->weapon].altEnergyPerShot;
14501 }
14502 }
14503 else
14504 {
14505 // alt-fire button pressing doesn't use any ammo
14506 amount = 0;
14507 }
14508
14509 }
14510
14511 // Check for binocular specific mode
14512 if ( cg.zoomMode == 1 && pm->gent && (pm->gent->s.number<MAX_CLIENTS||G_ControlledByPlayer(pm->gent)) ) //
14513 {
14514 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK && pm->ps->batteryCharge )
14515 {
14516 // zooming out
14517 cg.zoomLocked = qfalse;
14518 cg.zoomDir = 1;
14519 }
14520 else if ( pm->cmd.buttons & BUTTON_ATTACK && pm->ps->batteryCharge )
14521 {
14522 // zooming in
14523 cg.zoomLocked = qfalse;
14524 cg.zoomDir = -1;
14525 }
14526 else
14527 {
14528 // if no buttons are down, we should be in a locked state
14529 cg.zoomLocked = qtrue;
14530 }
14531
14532 // kill buttons and associated firing flags so we can't fire
14533 pm->ps->eFlags &= ~EF_FIRING;
14534 pm->ps->eFlags &= ~EF_ALT_FIRING;
14535 pm->cmd.buttons &= ~(BUTTON_ALT_ATTACK|BUTTON_ATTACK);
14536 }
14537
14538 // set the firing flag for continuous beam weapons, phaser will fire even if out of ammo
14539 if ( (( pm->cmd.buttons & BUTTON_ATTACK || pm->cmd.buttons & BUTTON_ALT_ATTACK ) && ( amount >= 0 || pm->ps->weapon == WP_SABER )) )
14540 {
14541 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
14542 {
14543 pm->ps->eFlags |= EF_ALT_FIRING;
14544 if ( pm->ps->clientNum < MAX_CLIENTS && pm->gent && (pm->ps->eFlags&EF_IN_ATST) )
14545 {//switch ATST barrels
14546 pm->gent->alt_fire = qtrue;
14547 }
14548 }
14549 else
14550 {
14551 pm->ps->eFlags &= ~EF_ALT_FIRING;
14552 if ( pm->ps->clientNum < MAX_CLIENTS && pm->gent && (pm->ps->eFlags&EF_IN_ATST) )
14553 {//switch ATST barrels
14554 pm->gent->alt_fire = qfalse;
14555 }
14556 }
14557
14558 // This flag should always get set, even when alt-firing
14559 pm->ps->eFlags |= EF_FIRING;
14560 }
14561 else
14562 {
14563 // int iFlags = pm->ps->eFlags;
14564
14565 // Clear 'em out
14566 pm->ps->eFlags &= ~EF_FIRING;
14567 pm->ps->eFlags &= ~EF_ALT_FIRING;
14568
14569 // if I don't check the flags before stopping FX then it switches them off too often, which tones down
14570 // the stronger FFFX so you can hardly feel them. However, if you only do iton these flags then the
14571 // repeat-fire weapons like tetrion and dreadnought don't switch off quick enough. So...
14572 //
14573 /* // Might need this for beam type weapons
14574 if ( pm->ps->weapon == WP_DREADNOUGHT || (iFlags & (EF_FIRING|EF_ALT_FIRING) )
14575 {
14576 cgi_FF_StopAllFX();
14577 }
14578 */
14579 }
14580
14581 // disruptor should convert a main fire to an alt-fire if the gun is currently zoomed
14582 if ( pm->ps->weapon == WP_DISRUPTOR && pm->gent && (pm->gent->s.number<MAX_CLIENTS||G_ControlledByPlayer(pm->gent)) )
14583 {
14584 if ( pm->cmd.buttons & BUTTON_ATTACK && cg.zoomMode == 2 )
14585 {
14586 // converting the main fire to an alt-fire
14587 pm->cmd.buttons |= BUTTON_ALT_ATTACK;
14588 pm->ps->eFlags |= EF_ALT_FIRING;
14589 }
14590 else
14591 {
14592 // don't let an alt-fire through
14593 pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
14594 }
14595 }
14596 }
14597
PM_WeaponOkOnVehicle(int weapon)14598 qboolean PM_WeaponOkOnVehicle( int weapon )
14599 {
14600 //FIXME: check g_vehicleInfo for our vehicle?
14601 switch ( weapon )
14602 {
14603 case WP_NONE:
14604 case WP_SABER:
14605 case WP_BLASTER:
14606 case WP_THERMAL:
14607 return qtrue;
14608 break;
14609 }
14610 return qfalse;
14611 }
14612
PM_CheckInVehicleSaberAttackAnim(void)14613 void PM_CheckInVehicleSaberAttackAnim( void )
14614 {//A bit of a hack, but makes the vehicle saber attacks act like any other saber attack...
14615 // make weapon function
14616 if ( pm->ps->weaponTime > 0 ) {
14617 pm->ps->weaponTime -= pml.msec;
14618 if ( pm->ps->weaponTime <= 0 )
14619 {
14620 pm->ps->weaponTime = 0;
14621 }
14622 }
14623 PM_CheckClearSaberBlock();
14624
14625 /* if ( PM_SaberBlocking() )
14626 {//busy blocking, don't do attacks
14627 return;
14628 }
14629 */
14630 saberMoveName_t saberMove = LS_INVALID;
14631 switch ( pm->ps->torsoAnim )
14632 {
14633 case BOTH_VS_ATR_S:
14634 saberMove = LS_SWOOP_ATTACK_RIGHT;
14635 break;
14636 case BOTH_VS_ATL_S:
14637 saberMove = LS_SWOOP_ATTACK_LEFT;
14638 break;
14639 case BOTH_VT_ATR_S:
14640 saberMove = LS_TAUNTAUN_ATTACK_RIGHT;
14641 break;
14642 case BOTH_VT_ATL_S:
14643 saberMove = LS_TAUNTAUN_ATTACK_LEFT;
14644 break;
14645 }
14646 if ( saberMove != LS_INVALID )
14647 {
14648 if ( pm->ps->saberMove == saberMove )
14649 {//already playing it
14650 if ( !pm->ps->torsoAnimTimer )
14651 {//anim was done, set it back to ready
14652 PM_SetSaberMove( LS_READY );
14653 pm->ps->saberMove = LS_READY;
14654 pm->ps->weaponstate = WEAPON_IDLE;
14655 if (pm->cmd.buttons&BUTTON_ATTACK)
14656 {
14657 if ( !pm->ps->weaponTime )
14658 {
14659 PM_SetSaberMove( saberMove );
14660 pm->ps->weaponstate = WEAPON_FIRING;
14661 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
14662 }
14663 }
14664 }
14665 }
14666 else if ( pm->ps->torsoAnimTimer
14667 && !pm->ps->weaponTime )
14668 {
14669 PM_SetSaberMove( LS_READY );
14670 pm->ps->saberMove = LS_READY;
14671 pm->ps->weaponstate = WEAPON_IDLE;
14672 PM_SetSaberMove( saberMove );
14673 pm->ps->weaponstate = WEAPON_FIRING;
14674 pm->ps->weaponTime = pm->ps->torsoAnimTimer;
14675 }
14676 }
14677 pm->ps->saberBlocking = saberMoveData[pm->ps->saberMove].blocking;
14678 }
14679
14680 //force the vehicle to turn and travel to its forced destination point
PM_VehForcedTurning(gentity_t * veh)14681 void PM_VehForcedTurning( gentity_t *veh )
14682 {
14683 gentity_t *dst = &g_entities[pm->ps->vehTurnaroundIndex];
14684 float pitchD, yawD;
14685 vec3_t dir;
14686
14687 if (!veh || !veh->m_pVehicle)
14688 {
14689 return;
14690 }
14691
14692 if (!dst)
14693 { //can't find dest ent?
14694 return;
14695 }
14696
14697 pm->cmd.upmove = veh->m_pVehicle->m_ucmd.upmove = 127;
14698 pm->cmd.forwardmove = veh->m_pVehicle->m_ucmd.forwardmove = 0;
14699 pm->cmd.rightmove = veh->m_pVehicle->m_ucmd.rightmove = 0;
14700
14701 VectorSubtract(dst->s.origin, veh->currentOrigin, dir);
14702 vectoangles(dir, dir);
14703
14704 yawD = AngleSubtract(pm->ps->viewangles[YAW], dir[YAW]);
14705 pitchD = AngleSubtract(pm->ps->viewangles[PITCH], dir[PITCH]);
14706
14707 yawD *= 0.2f*pml.frametime;
14708 pitchD *= 0.6f*pml.frametime;
14709
14710 pm->ps->viewangles[YAW] = AngleSubtract(pm->ps->viewangles[YAW], yawD);
14711 pm->ps->viewangles[PITCH] = AngleSubtract(pm->ps->viewangles[PITCH], pitchD);
14712
14713 //PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
14714 SetClientViewAngle(pm->gent, pm->ps->viewangles);
14715 }
14716 /*
14717 ================
14718 Pmove
14719
14720 Can be called by either the server or the client
14721 ================
14722 */
Pmove(pmove_t * pmove)14723 void Pmove( pmove_t *pmove )
14724 {
14725 Vehicle_t *pVeh = NULL;
14726
14727 pm = pmove;
14728
14729 // this counter lets us debug movement problems with a journal by setting a conditional breakpoint fot the previous frame
14730 c_pmove++;
14731
14732 // clear results
14733 pm->numtouch = 0;
14734 pm->watertype = 0;
14735 pm->waterlevel = 0;
14736
14737 // Clear the blocked flag
14738 //pm->ps->pm_flags &= ~PMF_BLOCKED;
14739 pm->ps->pm_flags &= ~PMF_BUMPED;
14740
14741 // In certain situations, we may want to control which attack buttons are pressed and what kind of functionality
14742 // is attached to them
14743 PM_AdjustAttackStates( pm );
14744
14745 // clear the respawned flag if attack and use are cleared
14746 if ( pm->ps->stats[STAT_HEALTH] > 0 &&
14747 !( pm->cmd.buttons & BUTTON_ATTACK ) )
14748 {
14749 pm->ps->pm_flags &= ~PMF_RESPAWNED;
14750 }
14751
14752 // clear all pmove local vars
14753 memset (&pml, 0, sizeof(pml));
14754
14755 // determine the time
14756 pml.msec = pmove->cmd.serverTime - pm->ps->commandTime;
14757 if ( pml.msec < 1 ) {
14758 pml.msec = 1;
14759 } else if ( pml.msec > 200 ) {
14760 pml.msec = 200;
14761 }
14762
14763 pm->ps->commandTime = pmove->cmd.serverTime;
14764
14765 // save old org in case we get stuck
14766 VectorCopy (pm->ps->origin, pml.previous_origin);
14767
14768 // save old velocity for crashlanding
14769 VectorCopy (pm->ps->velocity, pml.previous_velocity);
14770
14771 pml.frametime = pml.msec * 0.001;
14772
14773 if ( pm->ps->clientNum >= MAX_CLIENTS &&
14774 pm->gent &&
14775 pm->gent->client &&
14776 pm->gent->client->NPC_class == CLASS_VEHICLE )
14777 { //we are a vehicle
14778 pVeh = pm->gent->m_pVehicle;
14779 assert( pVeh );
14780 if ( pVeh )
14781 {
14782 pVeh->m_fTimeModifier = (pml.frametime*60.0f);//at 16.67ms (60fps), should be 1.0f
14783 }
14784 }
14785 else if ( pm->gent && PM_RidingVehicle() )
14786 {
14787 if ( pm->ps->vehTurnaroundIndex
14788 && pm->ps->vehTurnaroundTime > pm->cmd.serverTime )
14789 { //riding this vehicle, turn my view too
14790 PM_VehForcedTurning( &g_entities[pm->gent->s.m_iVehicleNum] );
14791 }
14792 }
14793
14794 PM_SetSpecialMoveValues();
14795
14796 // update the viewangles
14797 PM_UpdateViewAngles( pm->ps, &pm->cmd, pm->gent);
14798
14799 AngleVectors ( pm->ps->viewangles, pml.forward, pml.right, pml.up );
14800
14801 if ( pm->cmd.upmove < 10 ) {
14802 // not holding jump
14803 pm->ps->pm_flags &= ~PMF_JUMP_HELD;
14804 }
14805
14806 // decide if backpedaling animations should be used
14807 if ( pm->cmd.forwardmove < 0 ) {
14808 pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
14809 } else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
14810 pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;
14811 }
14812
14813 if ( pm->ps->pm_type >= PM_DEAD ) {
14814 pm->cmd.forwardmove = 0;
14815 pm->cmd.rightmove = 0;
14816 pm->cmd.upmove = 0;
14817 if ( pm->ps->viewheight > -12 )
14818 {//slowly sink view to ground
14819 pm->ps->viewheight -= 1;
14820 }
14821 }
14822
14823 if ( pm->ps->pm_type == PM_SPECTATOR ) {
14824 PM_CheckDuck ();
14825 PM_FlyMove ();
14826 PM_DropTimers ();
14827 return;
14828 }
14829
14830 if ( pm->ps->pm_type == PM_NOCLIP ) {
14831 PM_NoclipMove ();
14832 PM_DropTimers ();
14833 return;
14834 }
14835
14836 if (pm->ps->pm_type == PM_FREEZE) {
14837 return; // no movement at all
14838 }
14839
14840 if ( pm->ps->pm_type == PM_INTERMISSION ) {
14841 return; // no movement at all
14842 }
14843
14844 if ( pm->ps->pm_flags & PMF_SLOW_MO_FALL )
14845 {//half grav
14846 pm->ps->gravity *= 0.5;
14847 }
14848
14849 // set watertype, and waterlevel
14850 PM_SetWaterLevelAtPoint( pm->ps->origin, &pm->waterlevel, &pm->watertype );
14851
14852 PM_SetWaterHeight();
14853
14854 if ( !(pm->watertype & CONTENTS_LADDER) )
14855 {//Don't want to remember this for ladders, is only for waterlevel change events (sounds)
14856 pml.previous_waterlevel = pmove->waterlevel;
14857 }
14858
14859
14860 waterForceJump = qfalse;
14861 if ( pmove->waterlevel && pm->ps->clientNum )
14862 {
14863 if ( pm->ps->forceJumpZStart//force jumping
14864 ||(pm->gent&&pm->gent->NPC && level.time<pm->gent->NPC->jumpTime)) //TIMER_Done(pm->gent, "forceJumpChasing" )) )//force-jumping
14865 {
14866 waterForceJump = qtrue;
14867 }
14868 }
14869
14870 // set mins, maxs, and viewheight
14871 PM_SetBounds();
14872
14873 if ( !Flying && !(pm->watertype & CONTENTS_LADDER) && pm->ps->pm_type != PM_DEAD )
14874 {//NOTE: noclippers shouldn't jump or duck either, no?
14875 PM_CheckDuck();
14876 }
14877
14878 // set groundentity
14879 PM_GroundTrace();
14880 if ( Flying == FLY_HOVER )
14881 {//never stick to the ground
14882 PM_HoverTrace();
14883 }
14884
14885 if ( pm->ps->pm_type == PM_DEAD ) {
14886 PM_DeadMove ();
14887 }
14888
14889 PM_DropTimers();
14890
14891 /*
14892 if ( PM_RidingVehicle() )
14893 {
14894 PM_NoclipMove();
14895 }
14896 else */if ( pm->ps && ( (pm->ps->eFlags&EF_LOCKED_TO_WEAPON)
14897 || (pm->ps->eFlags&EF_HELD_BY_RANCOR)
14898 || (pm->ps->eFlags&EF_HELD_BY_WAMPA)
14899 || (pm->ps->eFlags&EF_HELD_BY_SAND_CREATURE) ) )
14900 {//in an emplaced gun
14901 PM_NoclipMove();
14902 }
14903 else if ( Flying == FLY_NORMAL )//|| pm->ps->gravity <= 0 )
14904 {
14905 // flight powerup doesn't allow jump and has different friction
14906 PM_FlyMove();
14907 }
14908 else if ( Flying == FLY_VEHICLE )
14909 {
14910 PM_FlyVehicleMove();
14911 }
14912 else if ( pm->ps->pm_flags & PMF_TIME_WATERJUMP )
14913 {
14914 PM_WaterJumpMove();
14915 }
14916 else if ( pm->waterlevel > 1 //in water
14917 &&((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || !waterForceJump) )//player or NPC not force jumping
14918 {//force-jumping NPCs should
14919 // swimming or in ladder
14920 PM_WaterMove();
14921 }
14922 else if (pm->gent && pm->gent->NPC && pm->gent->NPC->jumpTime!=0)
14923 {
14924 ucmd.forwardmove = 0;
14925 ucmd.rightmove = 0;
14926 ucmd.upmove = 0;
14927 pm->ps->speed = 0;
14928 VectorClear(pm->ps->moveDir);
14929
14930 PM_AirMove();
14931 }
14932 else if ( pml.walking )
14933 {// walking on ground
14934 vec3_t oldOrg;
14935
14936 VectorCopy( pm->ps->origin, oldOrg );
14937
14938 PM_WalkMove();
14939
14940
14941 float threshHold = 0.001f, movedDist = DistanceSquared( oldOrg, pm->ps->origin );
14942 if ( PM_StandingAnim( pm->ps->legsAnim ) || pm->ps->legsAnim == BOTH_CROUCH1 )
14943 {
14944 threshHold = 0.005f;
14945 }
14946
14947 if ( movedDist < threshHold )
14948 {//didn't move, play no legs anim
14949 // pm->cmd.forwardmove = pm->cmd.rightmove = 0;
14950 }
14951 }
14952 else
14953 {
14954 if ( pm->ps->gravity <= 0 )
14955 {
14956 PM_FlyMove();
14957 }
14958 else
14959 {
14960 // airborne
14961 PM_AirMove();
14962 }
14963 }
14964
14965 //PM_Animate();
14966
14967 // If we didn't move at all, then why bother doing this again -MW.
14968 if(!(VectorCompare(pm->ps->origin,pml.previous_origin)))
14969 {
14970 PM_GroundTrace();
14971 if ( Flying == FLY_HOVER )
14972 {//never stick to the ground
14973 PM_HoverTrace();
14974 }
14975 }
14976
14977 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
14978 {//on ground
14979 pm->ps->forceJumpZStart = 0;
14980 pm->ps->jumpZStart = 0;
14981 pm->ps->pm_flags &= ~PMF_JUMPING;
14982 pm->ps->pm_flags &= ~PMF_TRIGGER_PUSHED;
14983 pm->ps->pm_flags &= ~PMF_SLOW_MO_FALL;
14984 }
14985
14986 // If we didn't move at all, then why bother doing this again -MW.
14987 // Note: ok, so long as we don't have water levels that change.
14988 if(!(VectorCompare(pm->ps->origin,pml.previous_origin)))
14989 {
14990 PM_SetWaterLevelAtPoint( pm->ps->origin, &pm->waterlevel, &pm->watertype );
14991 PM_SetWaterHeight();
14992 }
14993
14994 // PM_ForcePower(); sends event to client for client side fx, not used
14995
14996 // If we're a vehicle, do our special weapon function.
14997 if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
14998 {
14999 pVeh = pm->gent->m_pVehicle;
15000
15001 // Using vehicle weapon...
15002 //if ( pm->cmd.weapon == WP_NONE )
15003 {
15004 //PM_Weapon();
15005 //PM_AddEvent( EV_FIRE_WEAPON );
15006 PM_VehicleWeapon();
15007 }
15008 }
15009 // If we are riding a vehicle...
15010 else if ( PM_RidingVehicle() )
15011 {
15012 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
15013 {// alt attack always does other stuff when riding a vehicle (turbo)
15014 }
15015 else if ( (pm->ps->eFlags&EF_NODRAW) )
15016 {//inside a vehicle? don't do any weapon stuff
15017 }
15018 else if ( pm->ps->weapon == WP_BLASTER//using blaster
15019 || pm->ps->weapon == WP_THERMAL//using thermal
15020 || pm->ps->weaponstate == WEAPON_DROPPING//changing weapon - dropping
15021 || pm->ps->weaponstate == WEAPON_RAISING//changing weapon - raising
15022 || (pm->cmd.weapon != pm->ps->weapon && PM_WeaponOkOnVehicle( pm->cmd.weapon )) )//FIXME: make this a vehicle call to see if this new weapon is valid for this vehicle
15023 {//either weilding a weapon we can fire with normal weapon logic, or trying to change to a valid weapon
15024 // call normal weapons code... should we override the normal fire anims with vehicle fire anims in here or in a subsequent call to VehicleWeapons or something?
15025 //Maybe break PM_Weapon into PM_Weapon and PM_WeaponAnimate (then call our own PM_VehicleWeaponAnimate)?
15026 PM_Weapon();
15027 }
15028 //BUT: now call Vehicle's weapon code, to handle lightsaber and (maybe) overriding weapon ready/firing anims?
15029 }
15030 // otherwise do the normal weapon function.
15031 else
15032 {
15033 // weapons
15034 PM_Weapon();
15035 }
15036 if ( pm->cmd.buttons & BUTTON_ATTACK )
15037 {
15038 pm->ps->pm_flags |= PMF_ATTACK_HELD;
15039 }
15040 else
15041 {
15042 pm->ps->pm_flags &= ~PMF_ATTACK_HELD;
15043 }
15044 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
15045 {
15046 pm->ps->pm_flags |= PMF_ALT_ATTACK_HELD;
15047 }
15048 else
15049 {
15050 pm->ps->pm_flags &= ~PMF_ALT_ATTACK_HELD;
15051 }
15052 if ( pm->cmd.buttons & BUTTON_FORCE_FOCUS )
15053 {
15054 pm->ps->pm_flags |= PMF_FORCE_FOCUS_HELD;
15055 }
15056 else
15057 {
15058 pm->ps->pm_flags &= ~PMF_FORCE_FOCUS_HELD;
15059 }
15060
15061 if ( pm->gent )//&& pm->gent->s.number == 0 )//player only?
15062 {
15063 // Use
15064 PM_Use();
15065 }
15066
15067 // Calculate the resulting speed of the last pmove
15068 //-------------------------------------------------
15069 if ( pm->gent )
15070 {
15071 pm->gent->resultspeed = ((Distance(pm->ps->origin, pm->gent->currentOrigin) / pml.msec) * 1000);
15072 if (pm->gent->resultspeed>5.0f)
15073 {
15074 pm->gent->lastMoveTime = level.time;
15075 }
15076
15077 // If Have Not Been Moving For A While, Stop
15078 //-------------------------------------------
15079 if (pml.walking && (level.time - pm->gent->lastMoveTime)>1000)
15080 {
15081 pm->cmd.forwardmove = pm->cmd.rightmove = 0;
15082 }
15083 }
15084
15085
15086 // ANIMATION
15087 //================================
15088
15089 // TEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMP
15090 if ( pm->gent && pm->ps && pm->ps->eFlags & EF_LOCKED_TO_WEAPON )
15091 {
15092 if ( pm->gent->owner && pm->gent->owner->e_UseFunc == useF_emplaced_gun_use )//ugly way to tell, but...
15093 {//full body
15094 PM_SetAnim(pm,SETANIM_BOTH,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL
15095 }
15096 else
15097 {//stand (or could be overridden by strafe anims)
15098 PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
15099 }
15100 }
15101 else if ( pm->gent && pm->ps && (pm->ps->eFlags&EF_HELD_BY_RANCOR) )
15102 {
15103 PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIM_IDLE1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL
15104 }
15105 // If we are a vehicle, animate...
15106 else if ( pVeh )
15107 {
15108 pVeh->m_pVehicleInfo->Animate( pVeh );
15109 }
15110 // If we're riding a vehicle, don't do anything!.
15111 else if ( ( pVeh = PM_RidingVehicle() ) != 0 )
15112 {
15113 PM_CheckInVehicleSaberAttackAnim();
15114 }
15115 else // TEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMP
15116 {
15117 // footstep events / legs animations
15118 PM_Footsteps();
15119 }
15120 // torso animation
15121 if ( !pVeh )
15122 {//not riding a vehicle
15123 PM_TorsoAnimation();
15124 }
15125
15126 // entering / leaving water splashes
15127 PM_WaterEvents();
15128
15129 // snap some parts of playerstate to save network bandwidth
15130 // SnapVector( pm->ps->velocity );
15131
15132 if ( !pm->cmd.rightmove && !pm->cmd.forwardmove && pm->cmd.upmove <= 0 )
15133 {
15134 if ( VectorCompare( pm->ps->velocity, vec3_origin ) )
15135 {
15136 pm->ps->lastStationary = level.time;
15137 }
15138 }
15139
15140 if ( pm->ps->pm_flags & PMF_SLOW_MO_FALL )
15141 {//half grav
15142 pm->ps->gravity *= 2;
15143 }
15144 }
15145