]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/d3xp/physics/Physics_Player.cpp
hello world
[icculus/iodoom3.git] / neo / d3xp / physics / Physics_Player.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "../Game_local.h"
33
34 CLASS_DECLARATION( idPhysics_Actor, idPhysics_Player )
35 END_CLASS
36
37 // movement parameters
38 const float PM_STOPSPEED                = 100.0f;
39 const float PM_SWIMSCALE                = 0.5f;
40 const float PM_LADDERSPEED              = 100.0f;
41 const float PM_STEPSCALE                = 1.0f;
42
43 const float PM_ACCELERATE               = 10.0f;
44 const float PM_AIRACCELERATE    = 1.0f;
45 const float PM_WATERACCELERATE  = 4.0f;
46 const float PM_FLYACCELERATE    = 8.0f;
47
48 const float PM_FRICTION                 = 6.0f;
49 const float PM_AIRFRICTION              = 0.0f;
50 const float PM_WATERFRICTION    = 1.0f;
51 const float PM_FLYFRICTION              = 3.0f;
52 const float PM_NOCLIPFRICTION   = 12.0f;
53
54 const float MIN_WALK_NORMAL             = 0.7f;         // can't walk on very steep slopes
55 const float OVERCLIP                    = 1.001f;
56
57 // movementFlags
58 const int PMF_DUCKED                    = 1;            // set when ducking
59 const int PMF_JUMPED                    = 2;            // set when the player jumped this frame
60 const int PMF_STEPPED_UP                = 4;            // set when the player stepped up this frame
61 const int PMF_STEPPED_DOWN              = 8;            // set when the player stepped down this frame
62 const int PMF_JUMP_HELD                 = 16;           // set when jump button is held down
63 const int PMF_TIME_LAND                 = 32;           // movementTime is time before rejump
64 const int PMF_TIME_KNOCKBACK    = 64;           // movementTime is an air-accelerate only time
65 const int PMF_TIME_WATERJUMP    = 128;          // movementTime is waterjump
66 const int PMF_ALL_TIMES                 = (PMF_TIME_WATERJUMP|PMF_TIME_LAND|PMF_TIME_KNOCKBACK);
67
68 int c_pmove = 0;
69
70 /*
71 ============
72 idPhysics_Player::CmdScale
73
74 Returns the scale factor to apply to cmd movements
75 This allows the clients to use axial -127 to 127 values for all directions
76 without getting a sqrt(2) distortion in speed.
77 ============
78 */
79 float idPhysics_Player::CmdScale( const usercmd_t &cmd ) const {
80         int             max;
81         float   total;
82         float   scale;
83         int             forwardmove;
84         int             rightmove;
85         int             upmove;
86
87         forwardmove = cmd.forwardmove;
88         rightmove = cmd.rightmove;
89
90         // since the crouch key doubles as downward movement, ignore downward movement when we're on the ground
91         // otherwise crouch speed will be lower than specified
92         if ( walking ) {
93                 upmove = 0;
94         } else {
95                 upmove = cmd.upmove;
96         }
97
98         max = abs( forwardmove );
99         if ( abs( rightmove ) > max ) {
100                 max = abs( rightmove );
101         }
102         if ( abs( upmove ) > max ) {
103                 max = abs( upmove );
104         }
105
106         if ( !max ) {
107                 return 0.0f;
108         }
109
110         total = idMath::Sqrt( (float) forwardmove * forwardmove + rightmove * rightmove + upmove * upmove );
111         scale = (float) playerSpeed * max / ( 127.0f * total );
112
113         return scale;
114 }
115
116 /*
117 ==============
118 idPhysics_Player::Accelerate
119
120 Handles user intended acceleration
121 ==============
122 */
123 void idPhysics_Player::Accelerate( const idVec3 &wishdir, const float wishspeed, const float accel ) {
124 #if 1
125         // q2 style
126         float addspeed, accelspeed, currentspeed;
127
128         currentspeed = current.velocity * wishdir;
129         addspeed = wishspeed - currentspeed;
130         if (addspeed <= 0) {
131                 return;
132         }
133         accelspeed = accel * frametime * wishspeed;
134         if (accelspeed > addspeed) {
135                 accelspeed = addspeed;
136         }
137         
138         current.velocity += accelspeed * wishdir;
139 #else
140         // proper way (avoids strafe jump maxspeed bug), but feels bad
141         idVec3          wishVelocity;
142         idVec3          pushDir;
143         float           pushLen;
144         float           canPush;
145
146         wishVelocity = wishdir * wishspeed;
147         pushDir = wishVelocity - current.velocity;
148         pushLen = pushDir.Normalize();
149
150         canPush = accel * frametime * wishspeed;
151         if (canPush > pushLen) {
152                 canPush = pushLen;
153         }
154
155         current.velocity += canPush * pushDir;
156 #endif
157 }
158
159 /*
160 ==================
161 idPhysics_Player::SlideMove
162
163 Returns true if the velocity was clipped in some way
164 ==================
165 */
166 #define MAX_CLIP_PLANES 5
167
168 bool idPhysics_Player::SlideMove( bool gravity, bool stepUp, bool stepDown, bool push ) {
169         int                     i, j, k, pushFlags;
170         int                     bumpcount, numbumps, numplanes;
171         float           d, time_left, into, totalMass;
172         idVec3          dir, planes[MAX_CLIP_PLANES];
173         idVec3          end, stepEnd, primal_velocity, endVelocity, endClipVelocity, clipVelocity;
174         trace_t         trace, stepTrace, downTrace;
175         bool            nearGround, stepped, pushed;
176
177         numbumps = 4;
178
179         primal_velocity = current.velocity;
180
181         if ( gravity ) {
182                 endVelocity = current.velocity + gravityVector * frametime;
183                 current.velocity = ( current.velocity + endVelocity ) * 0.5f;
184                 primal_velocity = endVelocity;
185                 if ( groundPlane ) {
186                         // slide along the ground plane
187                         current.velocity.ProjectOntoPlane( groundTrace.c.normal, OVERCLIP );
188                 }
189         }
190         else {
191                 endVelocity = current.velocity;
192         }
193
194         time_left = frametime;
195
196         // never turn against the ground plane
197         if ( groundPlane ) {
198                 numplanes = 1;
199                 planes[0] = groundTrace.c.normal;
200         } else {
201                 numplanes = 0;
202         }
203
204         // never turn against original velocity
205         planes[numplanes] = current.velocity;
206         planes[numplanes].Normalize();
207         numplanes++;
208
209         for ( bumpcount = 0; bumpcount < numbumps; bumpcount++ ) {
210
211                 // calculate position we are trying to move to
212                 end = current.origin + time_left * current.velocity;
213
214                 // see if we can make it there
215                 gameLocal.clip.Translation( trace, current.origin, end, clipModel, clipModel->GetAxis(), clipMask, self );
216
217                 time_left -= time_left * trace.fraction;
218                 current.origin = trace.endpos;
219
220                 // if moved the entire distance
221                 if ( trace.fraction >= 1.0f ) {
222                         break;
223                 }
224
225                 stepped = pushed = false;
226
227                 // if we are allowed to step up
228                 if ( stepUp ) {
229
230                         nearGround = groundPlane | ladder;
231
232                         if ( !nearGround ) {
233                                 // trace down to see if the player is near the ground
234                                 // step checking when near the ground allows the player to move up stairs smoothly while jumping
235                                 stepEnd = current.origin + maxStepHeight * gravityNormal;
236                                 gameLocal.clip.Translation( downTrace, current.origin, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self );
237                                 nearGround = ( downTrace.fraction < 1.0f && (downTrace.c.normal * -gravityNormal) > MIN_WALK_NORMAL );
238                         }
239
240                         // may only step up if near the ground or on a ladder
241                         if ( nearGround ) {
242
243                                 // step up
244                                 stepEnd = current.origin - maxStepHeight * gravityNormal;
245                                 gameLocal.clip.Translation( downTrace, current.origin, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self );
246
247                                 // trace along velocity
248                                 stepEnd = downTrace.endpos + time_left * current.velocity;
249                                 gameLocal.clip.Translation( stepTrace, downTrace.endpos, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self );
250
251                                 // step down
252                                 stepEnd = stepTrace.endpos + maxStepHeight * gravityNormal;
253                                 gameLocal.clip.Translation( downTrace, stepTrace.endpos, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self );
254
255                                 if ( downTrace.fraction >= 1.0f || (downTrace.c.normal * -gravityNormal) > MIN_WALK_NORMAL ) {
256
257                                         // if moved the entire distance
258                                         if ( stepTrace.fraction >= 1.0f ) {
259                                                 time_left = 0;
260                                                 current.stepUp -= ( downTrace.endpos - current.origin ) * gravityNormal;
261                                                 current.origin = downTrace.endpos;
262                                                 current.movementFlags |= PMF_STEPPED_UP;
263                                                 current.velocity *= PM_STEPSCALE;
264                                                 break;
265                                         }
266
267                                         // if the move is further when stepping up
268                                         if ( stepTrace.fraction > trace.fraction ) {
269                                                 time_left -= time_left * stepTrace.fraction;
270                                                 current.stepUp -= ( downTrace.endpos - current.origin ) * gravityNormal;
271                                                 current.origin = downTrace.endpos;
272                                                 current.movementFlags |= PMF_STEPPED_UP;
273                                                 current.velocity *= PM_STEPSCALE;
274                                                 trace = stepTrace;
275                                                 stepped = true;
276                                         }
277                                 }
278                         }
279                 }
280
281                 // if we can push other entities and not blocked by the world
282                 if ( push && trace.c.entityNum != ENTITYNUM_WORLD ) {
283
284                         clipModel->SetPosition( current.origin, clipModel->GetAxis() );
285
286                         // clip movement, only push idMoveables, don't push entities the player is standing on
287                         // apply impact to pushed objects
288                         pushFlags = PUSHFL_CLIP|PUSHFL_ONLYMOVEABLE|PUSHFL_NOGROUNDENTITIES|PUSHFL_APPLYIMPULSE;
289
290                         // clip & push
291                         totalMass = gameLocal.push.ClipTranslationalPush( trace, self, pushFlags, end, end - current.origin );
292
293                         if ( totalMass > 0.0f ) {
294                                 // decrease velocity based on the total mass of the objects being pushed ?
295                                 current.velocity *= 1.0f - idMath::ClampFloat( 0.0f, 1000.0f, totalMass - 20.0f ) * ( 1.0f / 950.0f );
296                                 pushed = true;
297                         }
298         
299                         current.origin = trace.endpos;
300                         time_left -= time_left * trace.fraction;
301
302                         // if moved the entire distance
303                         if ( trace.fraction >= 1.0f ) {
304                                 break;
305                         }
306                 }
307
308                 if ( !stepped ) {
309                         // let the entity know about the collision
310                         self->Collide( trace, current.velocity );
311                 }
312
313                 if ( numplanes >= MAX_CLIP_PLANES ) {
314                         // MrElusive: I think we have some relatively high poly LWO models with a lot of slanted tris
315                         // where it may hit the max clip planes
316                         current.velocity = vec3_origin;
317                         return true;
318                 }
319
320                 //
321                 // if this is the same plane we hit before, nudge velocity
322                 // out along it, which fixes some epsilon issues with
323                 // non-axial planes
324                 //
325                 for ( i = 0; i < numplanes; i++ ) {
326                         if ( ( trace.c.normal * planes[i] ) > 0.999f ) {
327                                 current.velocity += trace.c.normal;
328                                 break;
329                         }
330                 }
331                 if ( i < numplanes ) {
332                         continue;
333                 }
334                 planes[numplanes] = trace.c.normal;
335                 numplanes++;
336
337                 //
338                 // modify velocity so it parallels all of the clip planes
339                 //
340
341                 // find a plane that it enters
342                 for ( i = 0; i < numplanes; i++ ) {
343                         into = current.velocity * planes[i];
344                         if ( into >= 0.1f ) {
345                                 continue;               // move doesn't interact with the plane
346                         }
347
348                         // slide along the plane
349                         clipVelocity = current.velocity;
350                         clipVelocity.ProjectOntoPlane( planes[i], OVERCLIP );
351
352                         // slide along the plane
353                         endClipVelocity = endVelocity;
354                         endClipVelocity.ProjectOntoPlane( planes[i], OVERCLIP );
355
356                         // see if there is a second plane that the new move enters
357                         for ( j = 0; j < numplanes; j++ ) {
358                                 if ( j == i ) {
359                                         continue;
360                                 }
361                                 if ( ( clipVelocity * planes[j] ) >= 0.1f ) {
362                                         continue;               // move doesn't interact with the plane
363                                 }
364
365                                 // try clipping the move to the plane
366                                 clipVelocity.ProjectOntoPlane( planes[j], OVERCLIP );
367                                 endClipVelocity.ProjectOntoPlane( planes[j], OVERCLIP );
368
369                                 // see if it goes back into the first clip plane
370                                 if ( ( clipVelocity * planes[i] ) >= 0 ) {
371                                         continue;
372                                 }
373
374                                 // slide the original velocity along the crease
375                                 dir = planes[i].Cross( planes[j] );
376                                 dir.Normalize();
377                                 d = dir * current.velocity;
378                                 clipVelocity = d * dir;
379
380                                 dir = planes[i].Cross( planes[j] );
381                                 dir.Normalize();
382                                 d = dir * endVelocity;
383                                 endClipVelocity = d * dir;
384
385                                 // see if there is a third plane the the new move enters
386                                 for ( k = 0; k < numplanes; k++ ) {
387                                         if ( k == i || k == j ) {
388                                                 continue;
389                                         }
390                                         if ( ( clipVelocity * planes[k] ) >= 0.1f ) {
391                                                 continue;               // move doesn't interact with the plane
392                                         }
393
394                                         // stop dead at a tripple plane interaction
395                                         current.velocity = vec3_origin;
396                                         return true;
397                                 }
398                         }
399
400                         // if we have fixed all interactions, try another move
401                         current.velocity = clipVelocity;
402                         endVelocity = endClipVelocity;
403                         break;
404                 }
405         }
406
407         // step down
408         if ( stepDown && groundPlane ) {
409                 stepEnd = current.origin + gravityNormal * maxStepHeight;
410                 gameLocal.clip.Translation( downTrace, current.origin, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self );
411                 if ( downTrace.fraction > 1e-4f && downTrace.fraction < 1.0f ) {
412                         current.stepUp -= ( downTrace.endpos - current.origin ) * gravityNormal;
413                         current.origin = downTrace.endpos;
414                         current.movementFlags |= PMF_STEPPED_DOWN;
415                         current.velocity *= PM_STEPSCALE;
416                 }
417         }
418
419         if ( gravity ) {
420                 current.velocity = endVelocity;
421         }
422
423         // come to a dead stop when the velocity orthogonal to the gravity flipped
424         clipVelocity = current.velocity - gravityNormal * current.velocity * gravityNormal;
425         endClipVelocity = endVelocity - gravityNormal * endVelocity * gravityNormal;
426         if ( clipVelocity * endClipVelocity < 0.0f ) {
427                 current.velocity = gravityNormal * current.velocity * gravityNormal;
428         }
429
430         return (bool)( bumpcount == 0 );
431 }
432
433 /*
434 ==================
435 idPhysics_Player::Friction
436
437 Handles both ground friction and water friction
438 ==================
439 */
440 void idPhysics_Player::Friction( void ) {
441         idVec3  vel;
442         float   speed, newspeed, control;
443         float   drop;
444         
445         vel = current.velocity;
446         if ( walking ) {
447                 // ignore slope movement, remove all velocity in gravity direction
448                 vel += (vel * gravityNormal) * gravityNormal;
449         }
450
451         speed = vel.Length();
452         if ( speed < 1.0f ) {
453                 // remove all movement orthogonal to gravity, allows for sinking underwater
454                 if ( fabs( current.velocity * gravityNormal ) < 1e-5f ) {
455                         current.velocity.Zero();
456                 } else {
457                         current.velocity = (current.velocity * gravityNormal) * gravityNormal;
458                 }
459                 // FIXME: still have z friction underwater?
460                 return;
461         }
462
463         drop = 0;
464
465         // spectator friction
466         if ( current.movementType == PM_SPECTATOR ) {
467                 drop += speed * PM_FLYFRICTION * frametime;
468         }
469         // apply ground friction
470         else if ( walking && waterLevel <= WATERLEVEL_FEET ) {
471                 // no friction on slick surfaces
472                 if ( !(groundMaterial && groundMaterial->GetSurfaceFlags() & SURF_SLICK) ) {
473                         // if getting knocked back, no friction
474                         if ( !(current.movementFlags & PMF_TIME_KNOCKBACK) ) {
475                                 control = speed < PM_STOPSPEED ? PM_STOPSPEED : speed;
476                                 drop += control * PM_FRICTION * frametime;
477                         }
478                 }
479         }
480         // apply water friction even if just wading
481         else if ( waterLevel ) {
482                 drop += speed * PM_WATERFRICTION * waterLevel * frametime;
483         }
484         // apply air friction
485         else {
486                 drop += speed * PM_AIRFRICTION * frametime;
487         }
488
489         // scale the velocity
490         newspeed = speed - drop;
491         if (newspeed < 0) {
492                 newspeed = 0;
493         }
494         current.velocity *= ( newspeed / speed );
495 }
496
497 /*
498 ===================
499 idPhysics_Player::WaterJumpMove
500
501 Flying out of the water
502 ===================
503 */
504 void idPhysics_Player::WaterJumpMove( void ) {
505
506         // waterjump has no control, but falls
507         idPhysics_Player::SlideMove( true, true, false, false );
508
509         // add gravity
510         current.velocity += gravityNormal * frametime;
511         // if falling down
512         if ( current.velocity * gravityNormal > 0.0f ) {
513                 // cancel as soon as we are falling down again
514                 current.movementFlags &= ~PMF_ALL_TIMES;
515                 current.movementTime = 0;
516         }
517 }
518
519 /*
520 ===================
521 idPhysics_Player::WaterMove
522 ===================
523 */
524 void idPhysics_Player::WaterMove( void ) {
525         idVec3  wishvel;
526         float   wishspeed;
527         idVec3  wishdir;
528         float   scale;
529         float   vel;
530
531         if ( idPhysics_Player::CheckWaterJump() ) {
532                 idPhysics_Player::WaterJumpMove();
533                 return;
534         }
535
536         idPhysics_Player::Friction();
537
538         scale = idPhysics_Player::CmdScale( command );
539
540         // user intentions
541         if ( !scale ) {
542                 wishvel = gravityNormal * 60; // sink towards bottom
543         } else {
544                 wishvel = scale * (viewForward * command.forwardmove + viewRight * command.rightmove);
545                 wishvel -= scale * gravityNormal * command.upmove;
546         }
547
548         wishdir = wishvel;
549         wishspeed = wishdir.Normalize();
550
551         if ( wishspeed > playerSpeed * PM_SWIMSCALE ) {
552                 wishspeed = playerSpeed * PM_SWIMSCALE;
553         }
554
555         idPhysics_Player::Accelerate( wishdir, wishspeed, PM_WATERACCELERATE );
556
557         // make sure we can go up slopes easily under water
558         if ( groundPlane && ( current.velocity * groundTrace.c.normal ) < 0.0f ) {
559                 vel = current.velocity.Length();
560                 // slide along the ground plane
561                 current.velocity.ProjectOntoPlane( groundTrace.c.normal, OVERCLIP );
562
563                 current.velocity.Normalize();
564                 current.velocity *= vel;
565         }
566
567         idPhysics_Player::SlideMove( false, true, false, false );
568 }
569
570 /*
571 ===================
572 idPhysics_Player::FlyMove
573 ===================
574 */
575 void idPhysics_Player::FlyMove( void ) {
576         idVec3  wishvel;
577         float   wishspeed;
578         idVec3  wishdir;
579         float   scale;
580
581         // normal slowdown
582         idPhysics_Player::Friction();
583
584         scale = idPhysics_Player::CmdScale( command );
585
586         if ( !scale ) {
587                 wishvel = vec3_origin;
588         } else {
589                 wishvel = scale * (viewForward * command.forwardmove + viewRight * command.rightmove);
590                 wishvel -= scale * gravityNormal * command.upmove;
591         }
592
593         wishdir = wishvel;
594         wishspeed = wishdir.Normalize();
595
596         idPhysics_Player::Accelerate( wishdir, wishspeed, PM_FLYACCELERATE );
597
598         idPhysics_Player::SlideMove( false, false, false, false );
599 }
600
601 /*
602 ===================
603 idPhysics_Player::AirMove
604 ===================
605 */
606 void idPhysics_Player::AirMove( void ) {
607         idVec3          wishvel;
608         idVec3          wishdir;
609         float           wishspeed;
610         float           scale;
611
612         idPhysics_Player::Friction();
613
614         scale = idPhysics_Player::CmdScale( command );
615
616         // project moves down to flat plane
617         viewForward -= (viewForward * gravityNormal) * gravityNormal;
618         viewRight -= (viewRight * gravityNormal) * gravityNormal;
619         viewForward.Normalize();
620         viewRight.Normalize();
621
622         wishvel = viewForward * command.forwardmove + viewRight * command.rightmove;
623         wishvel -= (wishvel * gravityNormal) * gravityNormal;
624         wishdir = wishvel;
625         wishspeed = wishdir.Normalize();
626         wishspeed *= scale;
627
628         // not on ground, so little effect on velocity
629         idPhysics_Player::Accelerate( wishdir, wishspeed, PM_AIRACCELERATE );
630
631         // we may have a ground plane that is very steep, even
632         // though we don't have a groundentity
633         // slide along the steep plane
634         if ( groundPlane ) {
635                 current.velocity.ProjectOntoPlane( groundTrace.c.normal, OVERCLIP );
636         }
637
638         idPhysics_Player::SlideMove( true, false, false, false );
639 }
640
641 /*
642 ===================
643 idPhysics_Player::WalkMove
644 ===================
645 */
646 void idPhysics_Player::WalkMove( void ) {
647         idVec3          wishvel;
648         idVec3          wishdir;
649         float           wishspeed;
650         float           scale;
651         float           accelerate;
652         idVec3          oldVelocity, vel;
653         float           oldVel, newVel;
654
655         if ( waterLevel > WATERLEVEL_WAIST && ( viewForward * groundTrace.c.normal ) > 0.0f ) {
656                 // begin swimming
657                 idPhysics_Player::WaterMove();
658                 return;
659         }
660
661         if ( idPhysics_Player::CheckJump() ) {
662                 // jumped away
663                 if ( waterLevel > WATERLEVEL_FEET ) {
664                         idPhysics_Player::WaterMove();
665                 }
666                 else {
667                         idPhysics_Player::AirMove();
668                 }
669                 return;
670         }
671
672         idPhysics_Player::Friction();
673
674         scale = idPhysics_Player::CmdScale( command );
675
676         // project moves down to flat plane
677         viewForward -= (viewForward * gravityNormal) * gravityNormal;
678         viewRight -= (viewRight * gravityNormal) * gravityNormal;
679
680         // project the forward and right directions onto the ground plane
681         viewForward.ProjectOntoPlane( groundTrace.c.normal, OVERCLIP );
682         viewRight.ProjectOntoPlane( groundTrace.c.normal, OVERCLIP );
683         //
684         viewForward.Normalize();
685         viewRight.Normalize();
686
687         wishvel = viewForward * command.forwardmove + viewRight * command.rightmove;
688         wishdir = wishvel;
689         wishspeed = wishdir.Normalize();
690         wishspeed *= scale;
691
692         // clamp the speed lower if wading or walking on the bottom
693         if ( waterLevel ) {
694                 float   waterScale;
695
696                 waterScale = waterLevel / 3.0f;
697                 waterScale = 1.0f - ( 1.0f - PM_SWIMSCALE ) * waterScale;
698                 if ( wishspeed > playerSpeed * waterScale ) {
699                         wishspeed = playerSpeed * waterScale;
700                 }
701         }
702
703         // when a player gets hit, they temporarily lose full control, which allows them to be moved a bit
704         if ( ( groundMaterial && groundMaterial->GetSurfaceFlags() & SURF_SLICK ) || current.movementFlags & PMF_TIME_KNOCKBACK ) {
705                 accelerate = PM_AIRACCELERATE;
706         }
707         else {
708                 accelerate = PM_ACCELERATE;
709         }
710
711         idPhysics_Player::Accelerate( wishdir, wishspeed, accelerate );
712
713         if ( ( groundMaterial && groundMaterial->GetSurfaceFlags() & SURF_SLICK ) || current.movementFlags & PMF_TIME_KNOCKBACK ) {
714                 current.velocity += gravityVector * frametime;
715         }
716
717         oldVelocity = current.velocity;
718
719         // slide along the ground plane
720         current.velocity.ProjectOntoPlane( groundTrace.c.normal, OVERCLIP );
721
722         // if not clipped into the opposite direction
723         if ( oldVelocity * current.velocity > 0.0f ) {
724                 newVel = current.velocity.LengthSqr();
725                 if ( newVel > 1.0f ) {
726                         oldVel = oldVelocity.LengthSqr();
727                         if ( oldVel > 1.0f ) {
728                                 // don't decrease velocity when going up or down a slope
729                                 current.velocity *= idMath::Sqrt( oldVel / newVel );
730                         }
731                 }
732         }
733
734         // don't do anything if standing still
735         vel = current.velocity - (current.velocity * gravityNormal) * gravityNormal;
736         if ( !vel.LengthSqr() ) {
737                 return;
738         }
739
740         gameLocal.push.InitSavingPushedEntityPositions();
741
742         idPhysics_Player::SlideMove( false, true, true, true );
743 }
744
745 /*
746 ==============
747 idPhysics_Player::DeadMove
748 ==============
749 */
750 void idPhysics_Player::DeadMove( void ) {
751         float   forward;
752
753         if ( !walking ) {
754                 return;
755         }
756
757         // extra friction
758         forward = current.velocity.Length();
759         forward -= 20;
760         if ( forward <= 0 ) {
761                 current.velocity = vec3_origin;
762         }
763         else {
764                 current.velocity.Normalize();
765                 current.velocity *= forward;
766         }
767 }
768
769 /*
770 ===============
771 idPhysics_Player::NoclipMove
772 ===============
773 */
774 void idPhysics_Player::NoclipMove( void ) {
775         float           speed, drop, friction, newspeed, stopspeed;
776         float           scale, wishspeed;
777         idVec3          wishdir;
778
779         // friction
780         speed = current.velocity.Length();
781         if ( speed < 20.0f ) {
782                 current.velocity = vec3_origin;
783         }
784         else {
785                 stopspeed = playerSpeed * 0.3f;
786                 if ( speed < stopspeed ) {
787                         speed = stopspeed;
788                 }
789                 friction = PM_NOCLIPFRICTION;
790                 drop = speed * friction * frametime;
791
792                 // scale the velocity
793                 newspeed = speed - drop;
794                 if (newspeed < 0) {
795                         newspeed = 0;
796                 }
797
798                 current.velocity *= newspeed / speed;
799         }
800
801         // accelerate
802         scale = idPhysics_Player::CmdScale( command );
803
804         wishdir = scale * (viewForward * command.forwardmove + viewRight * command.rightmove);
805         wishdir -= scale * gravityNormal * command.upmove;
806         wishspeed = wishdir.Normalize();
807         wishspeed *= scale;
808
809         idPhysics_Player::Accelerate( wishdir, wishspeed, PM_ACCELERATE );
810
811         // move
812         current.origin += frametime * current.velocity;
813 }
814
815 /*
816 ===============
817 idPhysics_Player::SpectatorMove
818 ===============
819 */
820 void idPhysics_Player::SpectatorMove( void ) {
821         idVec3  wishvel;
822         float   wishspeed;
823         idVec3  wishdir;
824         float   scale;
825
826         trace_t trace;
827         idVec3  end;
828
829         // fly movement
830
831         idPhysics_Player::Friction();
832
833         scale = idPhysics_Player::CmdScale( command );
834
835         if ( !scale ) {
836                 wishvel = vec3_origin;
837         } else {
838                 wishvel = scale * (viewForward * command.forwardmove + viewRight * command.rightmove);
839         }
840
841         wishdir = wishvel;
842         wishspeed = wishdir.Normalize();
843
844         idPhysics_Player::Accelerate( wishdir, wishspeed, PM_FLYACCELERATE );
845
846         idPhysics_Player::SlideMove( false, false, false, false );
847 }
848
849 /*
850 ============
851 idPhysics_Player::LadderMove
852 ============
853 */
854 void idPhysics_Player::LadderMove( void ) {
855         idVec3  wishdir, wishvel, right;
856         float   wishspeed, scale;
857         float   upscale;
858
859         // stick to the ladder
860         wishvel = -100.0f * ladderNormal;
861         current.velocity = (gravityNormal * current.velocity) * gravityNormal + wishvel;
862
863         upscale = (-gravityNormal * viewForward + 0.5f) * 2.5f;
864         if ( upscale > 1.0f ) {
865                 upscale = 1.0f;
866         }
867         else if ( upscale < -1.0f ) {
868                 upscale = -1.0f;
869         }
870
871         scale = idPhysics_Player::CmdScale( command );
872         wishvel = -0.9f * gravityNormal * upscale * scale * (float)command.forwardmove;
873
874         // strafe
875         if ( command.rightmove ) {
876                 // right vector orthogonal to gravity
877                 right = viewRight - (gravityNormal * viewRight) * gravityNormal;
878                 // project right vector into ladder plane
879                 right = right - (ladderNormal * right) * ladderNormal;
880                 right.Normalize();
881
882                 // if we are looking away from the ladder, reverse the right vector
883                 if ( ladderNormal * viewForward > 0.0f ) {
884                         right = -right;
885                 }
886                 wishvel += 2.0f * right * scale * (float) command.rightmove;
887         }
888
889         // up down movement
890         if ( command.upmove ) {
891                 wishvel += -0.5f * gravityNormal * scale * (float) command.upmove;
892         }
893
894         // do strafe friction
895         idPhysics_Player::Friction();
896
897         // accelerate
898         wishspeed = wishvel.Normalize();
899         idPhysics_Player::Accelerate( wishvel, wishspeed, PM_ACCELERATE );
900
901         // cap the vertical velocity
902         upscale = current.velocity * -gravityNormal;
903         if ( upscale < -PM_LADDERSPEED ) {
904                 current.velocity += gravityNormal * (upscale + PM_LADDERSPEED);
905         }
906         else if ( upscale > PM_LADDERSPEED ) {
907                 current.velocity += gravityNormal * (upscale - PM_LADDERSPEED);
908         }
909
910         if ( (wishvel * gravityNormal) == 0.0f ) {
911                 if ( current.velocity * gravityNormal < 0.0f ) {
912                         current.velocity += gravityVector * frametime;
913                         if ( current.velocity * gravityNormal > 0.0f ) {
914                                 current.velocity -= (gravityNormal * current.velocity) * gravityNormal;
915                         }
916                 }
917                 else {
918                         current.velocity -= gravityVector * frametime;
919                         if ( current.velocity * gravityNormal < 0.0f ) {
920                                 current.velocity -= (gravityNormal * current.velocity) * gravityNormal;
921                         }
922                 }
923         }
924
925         idPhysics_Player::SlideMove( false, ( command.forwardmove > 0 ), false, false );
926 }
927
928 /*
929 =============
930 idPhysics_Player::CorrectAllSolid
931 =============
932 */
933 void idPhysics_Player::CorrectAllSolid( trace_t &trace, int contents ) {
934         if ( debugLevel ) {
935                 gameLocal.Printf( "%i:allsolid\n", c_pmove );
936         }
937
938         // FIXME: jitter around to find a free spot ?
939
940         if ( trace.fraction >= 1.0f ) {
941                 memset( &trace, 0, sizeof( trace ) );
942                 trace.endpos = current.origin;
943                 trace.endAxis = clipModelAxis;
944                 trace.fraction = 0.0f;
945                 trace.c.dist = current.origin.z;
946                 trace.c.normal.Set( 0, 0, 1 );
947                 trace.c.point = current.origin;
948                 trace.c.entityNum = ENTITYNUM_WORLD;
949                 trace.c.id = 0;
950                 trace.c.type = CONTACT_TRMVERTEX;
951                 trace.c.material = NULL;
952                 trace.c.contents = contents;
953         }
954 }
955
956 /*
957 =============
958 idPhysics_Player::CheckGround
959 =============
960 */
961 void idPhysics_Player::CheckGround( void ) {
962         int i, contents;
963         idVec3 point;
964         bool hadGroundContacts;
965
966         hadGroundContacts = HasGroundContacts();
967
968         // set the clip model origin before getting the contacts
969         clipModel->SetPosition( current.origin, clipModel->GetAxis() );
970
971         EvaluateContacts();
972
973         // setup a ground trace from the contacts
974         groundTrace.endpos = current.origin;
975         groundTrace.endAxis = clipModel->GetAxis();
976         if ( contacts.Num() ) {
977                 groundTrace.fraction = 0.0f;
978                 groundTrace.c = contacts[0];
979                 for ( i = 1; i < contacts.Num(); i++ ) {
980                         groundTrace.c.normal += contacts[i].normal;
981                 }
982                 groundTrace.c.normal.Normalize();
983         } else {
984                 groundTrace.fraction = 1.0f;
985         }
986
987         contents = gameLocal.clip.Contents( current.origin, clipModel, clipModel->GetAxis(), -1, self );
988         if ( contents & MASK_SOLID ) {
989                 // do something corrective if stuck in solid
990                 idPhysics_Player::CorrectAllSolid( groundTrace, contents );
991         }
992
993         // if the trace didn't hit anything, we are in free fall
994         if ( groundTrace.fraction == 1.0f ) {
995                 groundPlane = false;
996                 walking = false;
997                 groundEntityPtr = NULL;
998                 return;
999         }
1000
1001         groundMaterial = groundTrace.c.material;
1002         groundEntityPtr = gameLocal.entities[ groundTrace.c.entityNum ];
1003
1004         // check if getting thrown off the ground
1005         if ( (current.velocity * -gravityNormal) > 0.0f && ( current.velocity * groundTrace.c.normal ) > 10.0f ) {
1006                 if ( debugLevel ) {
1007                         gameLocal.Printf( "%i:kickoff\n", c_pmove );
1008                 }
1009
1010                 groundPlane = false;
1011                 walking = false;
1012                 return;
1013         }
1014         
1015         // slopes that are too steep will not be considered onground
1016         if ( ( groundTrace.c.normal * -gravityNormal ) < MIN_WALK_NORMAL ) {
1017                 if ( debugLevel ) {
1018                         gameLocal.Printf( "%i:steep\n", c_pmove );
1019                 }
1020
1021                 // FIXME: if they can't slide down the slope, let them walk (sharp crevices)
1022
1023                 // make sure we don't die from sliding down a steep slope
1024                 if ( current.velocity * gravityNormal > 150.0f ) {
1025                         current.velocity -= ( current.velocity * gravityNormal - 150.0f ) * gravityNormal;
1026                 }
1027
1028                 groundPlane = true;
1029                 walking = false;
1030                 return;
1031         }
1032
1033         groundPlane = true;
1034         walking = true;
1035
1036         // hitting solid ground will end a waterjump
1037         if ( current.movementFlags & PMF_TIME_WATERJUMP ) {
1038                 current.movementFlags &= ~( PMF_TIME_WATERJUMP | PMF_TIME_LAND );
1039                 current.movementTime = 0;
1040         }
1041
1042         // if the player didn't have ground contacts the previous frame
1043         if ( !hadGroundContacts ) {
1044
1045                 // don't do landing time if we were just going down a slope
1046                 if ( (current.velocity * -gravityNormal) < -200.0f ) {
1047                         // don't allow another jump for a little while
1048                         current.movementFlags |= PMF_TIME_LAND;
1049                         current.movementTime = 250;
1050                 }
1051         }
1052
1053         // let the entity know about the collision
1054         self->Collide( groundTrace, current.velocity );
1055
1056         if ( groundEntityPtr.GetEntity() ) {
1057                 impactInfo_t info;
1058                 groundEntityPtr.GetEntity()->GetImpactInfo( self, groundTrace.c.id, groundTrace.c.point, &info );
1059                 if ( info.invMass != 0.0f ) {
1060                         groundEntityPtr.GetEntity()->ApplyImpulse( self, groundTrace.c.id, groundTrace.c.point, current.velocity / ( info.invMass * 10.0f ) );
1061                 }
1062         }
1063 }
1064
1065 /*
1066 ==============
1067 idPhysics_Player::CheckDuck
1068
1069 Sets clip model size
1070 ==============
1071 */
1072 void idPhysics_Player::CheckDuck( void ) {
1073         trace_t trace;
1074         idVec3 end;
1075         idBounds bounds;
1076         float maxZ;
1077
1078         if ( current.movementType == PM_DEAD ) {
1079                 maxZ = pm_deadheight.GetFloat();
1080         } else {
1081                 // stand up when up against a ladder
1082                 if ( command.upmove < 0 && !ladder ) {
1083                         // duck
1084                         current.movementFlags |= PMF_DUCKED;
1085                 } else {
1086                         // stand up if possible
1087                         if ( current.movementFlags & PMF_DUCKED ) {
1088                                 // try to stand up
1089                                 end = current.origin - ( pm_normalheight.GetFloat() - pm_crouchheight.GetFloat() ) * gravityNormal;
1090                                 gameLocal.clip.Translation( trace, current.origin, end, clipModel, clipModel->GetAxis(), clipMask, self );
1091                                 if ( trace.fraction >= 1.0f ) {
1092                                         current.movementFlags &= ~PMF_DUCKED;
1093                                 }
1094                         }
1095                 }
1096
1097                 if ( current.movementFlags & PMF_DUCKED ) {
1098                         playerSpeed = crouchSpeed;
1099                         maxZ = pm_crouchheight.GetFloat();
1100                 } else {
1101                         maxZ = pm_normalheight.GetFloat();
1102                 }
1103         }
1104         // if the clipModel height should change
1105         if ( clipModel->GetBounds()[1][2] != maxZ ) {
1106
1107                 bounds = clipModel->GetBounds();
1108                 bounds[1][2] = maxZ;
1109                 if ( pm_usecylinder.GetBool() ) {
1110                         clipModel->LoadModel( idTraceModel( bounds, 8 ) );
1111                 } else {
1112                         clipModel->LoadModel( idTraceModel( bounds ) );
1113                 }
1114         }
1115 }
1116
1117 /*
1118 ================
1119 idPhysics_Player::CheckLadder
1120 ================
1121 */
1122 void idPhysics_Player::CheckLadder( void ) {
1123         idVec3          forward, start, end;
1124         trace_t         trace;
1125         float           tracedist;
1126         
1127         if ( current.movementTime ) {
1128                 return;
1129         }
1130
1131         // if on the ground moving backwards
1132         if ( walking && command.forwardmove <= 0 ) {
1133                 return;
1134         }
1135
1136         // forward vector orthogonal to gravity
1137         forward = viewForward - (gravityNormal * viewForward) * gravityNormal;
1138         forward.Normalize();
1139
1140         if ( walking ) {
1141                 // don't want to get sucked towards the ladder when still walking
1142                 tracedist = 1.0f;
1143         } else {
1144                 tracedist = 48.0f;
1145         }
1146
1147         end = current.origin + tracedist * forward;
1148         gameLocal.clip.Translation( trace, current.origin, end, clipModel, clipModel->GetAxis(), clipMask, self );
1149
1150         // if near a surface
1151         if ( trace.fraction < 1.0f ) {
1152
1153                 // if a ladder surface
1154                 if ( trace.c.material && ( trace.c.material->GetSurfaceFlags() & SURF_LADDER ) ) {
1155
1156                         // check a step height higher
1157                         end = current.origin - gravityNormal * ( maxStepHeight * 0.75f );
1158                         gameLocal.clip.Translation( trace, current.origin, end, clipModel, clipModel->GetAxis(), clipMask, self );
1159                         start = trace.endpos;
1160                         end = start + tracedist * forward;
1161                         gameLocal.clip.Translation( trace, start, end, clipModel, clipModel->GetAxis(), clipMask, self );
1162
1163                         // if also near a surface a step height higher
1164                         if ( trace.fraction < 1.0f ) {
1165
1166                                 // if it also is a ladder surface
1167                                 if ( trace.c.material && trace.c.material->GetSurfaceFlags() & SURF_LADDER ) {
1168                                         ladder = true;
1169                                         ladderNormal = trace.c.normal;
1170                                 }
1171                         }
1172                 }
1173         }
1174 }
1175
1176 /*
1177 =============
1178 idPhysics_Player::CheckJump
1179 =============
1180 */
1181 bool idPhysics_Player::CheckJump( void ) {
1182         idVec3 addVelocity;
1183
1184         if ( command.upmove < 10 ) {
1185                 // not holding jump
1186                 return false;
1187         }
1188
1189         // must wait for jump to be released
1190         if ( current.movementFlags & PMF_JUMP_HELD ) {
1191                 return false;
1192         }
1193
1194         // don't jump if we can't stand up
1195         if ( current.movementFlags & PMF_DUCKED ) {
1196                 return false;
1197         }
1198
1199         groundPlane = false;            // jumping away
1200         walking = false;
1201         current.movementFlags |= PMF_JUMP_HELD | PMF_JUMPED;
1202
1203         addVelocity = 2.0f * maxJumpHeight * -gravityVector;
1204         addVelocity *= idMath::Sqrt( addVelocity.Normalize() );
1205         current.velocity += addVelocity;
1206
1207         return true;
1208 }
1209
1210 /*
1211 =============
1212 idPhysics_Player::CheckWaterJump
1213 =============
1214 */
1215 bool idPhysics_Player::CheckWaterJump( void ) {
1216         idVec3  spot;
1217         int             cont;
1218         idVec3  flatforward;
1219
1220         if ( current.movementTime ) {
1221                 return false;
1222         }
1223
1224         // check for water jump
1225         if ( waterLevel != WATERLEVEL_WAIST ) {
1226                 return false;
1227         }
1228
1229         flatforward = viewForward - (viewForward * gravityNormal) * gravityNormal;
1230         flatforward.Normalize();
1231
1232         spot = current.origin + 30.0f * flatforward;
1233         spot -= 4.0f * gravityNormal;
1234         cont = gameLocal.clip.Contents( spot, NULL, mat3_identity, -1, self );
1235         if ( !(cont & CONTENTS_SOLID) ) {
1236                 return false;
1237         }
1238
1239         spot -= 16.0f * gravityNormal;
1240         cont = gameLocal.clip.Contents( spot, NULL, mat3_identity, -1, self );
1241         if ( cont ) {
1242                 return false;
1243         }
1244
1245         // jump out of water
1246         current.velocity = 200.0f * viewForward - 350.0f * gravityNormal;
1247         current.movementFlags |= PMF_TIME_WATERJUMP;
1248         current.movementTime = 2000;
1249
1250         return true;
1251 }
1252
1253 /*
1254 =============
1255 idPhysics_Player::SetWaterLevel
1256 =============
1257 */
1258 void idPhysics_Player::SetWaterLevel( void ) {
1259         idVec3          point;
1260         idBounds        bounds;
1261         int                     contents;
1262
1263         //
1264         // get waterlevel, accounting for ducking
1265         //
1266         waterLevel = WATERLEVEL_NONE;
1267         waterType = 0;
1268
1269         bounds = clipModel->GetBounds();
1270
1271         // check at feet level
1272         point = current.origin - ( bounds[0][2] + 1.0f ) * gravityNormal;
1273         contents = gameLocal.clip.Contents( point, NULL, mat3_identity, -1, self );
1274         if ( contents & MASK_WATER ) {
1275
1276                 waterType = contents;
1277                 waterLevel = WATERLEVEL_FEET;
1278
1279                 // check at waist level
1280                 point = current.origin - ( bounds[1][2] - bounds[0][2] ) * 0.5f * gravityNormal;
1281                 contents = gameLocal.clip.Contents( point, NULL, mat3_identity, -1, self );
1282                 if ( contents & MASK_WATER ) {
1283
1284                         waterLevel = WATERLEVEL_WAIST;
1285
1286                         // check at head level
1287                         point = current.origin - ( bounds[1][2] - 1.0f ) * gravityNormal;
1288                         contents = gameLocal.clip.Contents( point, NULL, mat3_identity, -1, self );
1289                         if ( contents & MASK_WATER ) {
1290                                 waterLevel = WATERLEVEL_HEAD;
1291                         }
1292                 }
1293         }
1294 }
1295
1296 /*
1297 ================
1298 idPhysics_Player::DropTimers
1299 ================
1300 */
1301 void idPhysics_Player::DropTimers( void ) {
1302         // drop misc timing counter
1303         if ( current.movementTime ) {
1304                 if ( framemsec >= current.movementTime ) {
1305                         current.movementFlags &= ~PMF_ALL_TIMES;
1306                         current.movementTime = 0;
1307                 }
1308                 else {
1309                         current.movementTime -= framemsec;
1310                 }
1311         }
1312 }
1313
1314 /*
1315 ================
1316 idPhysics_Player::MovePlayer
1317 ================
1318 */
1319 void idPhysics_Player::MovePlayer( int msec ) {
1320
1321         // this counter lets us debug movement problems with a journal
1322         // by setting a conditional breakpoint for the previous frame
1323         c_pmove++;
1324
1325         walking = false;
1326         groundPlane = false;
1327         ladder = false;
1328
1329         // determine the time
1330         framemsec = msec;
1331         frametime = framemsec * 0.001f;
1332
1333         // default speed
1334         playerSpeed = walkSpeed;
1335
1336         // remove jumped and stepped up flag
1337         current.movementFlags &= ~(PMF_JUMPED|PMF_STEPPED_UP|PMF_STEPPED_DOWN);
1338         current.stepUp = 0.0f;
1339
1340         if ( command.upmove < 10 ) {
1341                 // not holding jump
1342                 current.movementFlags &= ~PMF_JUMP_HELD;
1343         }
1344
1345         // if no movement at all
1346         if ( current.movementType == PM_FREEZE ) {
1347                 return;
1348         }
1349
1350         // move the player velocity into the frame of a pusher
1351         current.velocity -= current.pushVelocity;
1352
1353         // view vectors
1354         viewAngles.ToVectors( &viewForward, NULL, NULL );
1355         viewForward *= clipModelAxis;
1356         viewRight = gravityNormal.Cross( viewForward );
1357         viewRight.Normalize();
1358
1359         // fly in spectator mode
1360         if ( current.movementType == PM_SPECTATOR ) {
1361                 SpectatorMove();
1362                 idPhysics_Player::DropTimers();
1363                 return;
1364         }
1365
1366         // special no clip mode
1367         if ( current.movementType == PM_NOCLIP ) {
1368                 idPhysics_Player::NoclipMove();
1369                 idPhysics_Player::DropTimers();
1370                 return;
1371         }
1372
1373         // no control when dead
1374         if ( current.movementType == PM_DEAD ) {
1375                 command.forwardmove = 0;
1376                 command.rightmove = 0;
1377                 command.upmove = 0;
1378         }
1379
1380         // set watertype and waterlevel
1381         idPhysics_Player::SetWaterLevel();
1382
1383         // check for ground
1384         idPhysics_Player::CheckGround();
1385
1386         // check if up against a ladder
1387         idPhysics_Player::CheckLadder();
1388
1389         // set clip model size
1390         idPhysics_Player::CheckDuck();
1391
1392         // handle timers
1393         idPhysics_Player::DropTimers();
1394
1395         // move
1396         if ( current.movementType == PM_DEAD ) {
1397                 // dead
1398                 idPhysics_Player::DeadMove();
1399         }
1400         else if ( ladder ) {
1401                 // going up or down a ladder
1402                 idPhysics_Player::LadderMove();
1403         }
1404         else if ( current.movementFlags & PMF_TIME_WATERJUMP ) {
1405                 // jumping out of water
1406                 idPhysics_Player::WaterJumpMove();
1407         }
1408         else if ( waterLevel > 1 ) {
1409                 // swimming
1410                 idPhysics_Player::WaterMove();
1411         }
1412         else if ( walking ) {
1413                 // walking on ground
1414                 idPhysics_Player::WalkMove();
1415         }
1416         else {
1417                 // airborne
1418                 idPhysics_Player::AirMove();
1419         }
1420
1421         // set watertype, waterlevel and groundentity
1422         idPhysics_Player::SetWaterLevel();
1423         idPhysics_Player::CheckGround();
1424
1425         // move the player velocity back into the world frame
1426         current.velocity += current.pushVelocity;
1427         current.pushVelocity.Zero();
1428 }
1429
1430 /*
1431 ================
1432 idPhysics_Player::GetWaterLevel
1433 ================
1434 */
1435 waterLevel_t idPhysics_Player::GetWaterLevel( void ) const {
1436         return waterLevel;
1437 }
1438
1439 /*
1440 ================
1441 idPhysics_Player::GetWaterType
1442 ================
1443 */
1444 int idPhysics_Player::GetWaterType( void ) const {
1445         return waterType;
1446 }
1447
1448 /*
1449 ================
1450 idPhysics_Player::HasJumped
1451 ================
1452 */
1453 bool idPhysics_Player::HasJumped( void ) const {
1454         return ( ( current.movementFlags & PMF_JUMPED ) != 0 );
1455 }
1456
1457 /*
1458 ================
1459 idPhysics_Player::HasSteppedUp
1460 ================
1461 */
1462 bool idPhysics_Player::HasSteppedUp( void ) const {
1463         return ( ( current.movementFlags & ( PMF_STEPPED_UP | PMF_STEPPED_DOWN ) ) != 0 );
1464 }
1465
1466 /*
1467 ================
1468 idPhysics_Player::GetStepUp
1469 ================
1470 */
1471 float idPhysics_Player::GetStepUp( void ) const {
1472         return current.stepUp;
1473 }
1474
1475 /*
1476 ================
1477 idPhysics_Player::IsCrouching
1478 ================
1479 */
1480 bool idPhysics_Player::IsCrouching( void ) const {
1481         return ( ( current.movementFlags & PMF_DUCKED ) != 0 );
1482 }
1483
1484 /*
1485 ================
1486 idPhysics_Player::OnLadder
1487 ================
1488 */
1489 bool idPhysics_Player::OnLadder( void ) const {
1490         return ladder;
1491 }
1492
1493 /*
1494 ================
1495 idPhysics_Player::idPhysics_Player
1496 ================
1497 */
1498 idPhysics_Player::idPhysics_Player( void ) {
1499         debugLevel = false;
1500         clipModel = NULL;
1501         clipMask = 0;
1502         memset( &current, 0, sizeof( current ) );
1503         saved = current;
1504         walkSpeed = 0;
1505         crouchSpeed = 0;
1506         maxStepHeight = 0;
1507         maxJumpHeight = 0;
1508         memset( &command, 0, sizeof( command ) );
1509         viewAngles.Zero();
1510         framemsec = 0;
1511         frametime = 0;
1512         playerSpeed = 0;
1513         viewForward.Zero();
1514         viewRight.Zero();
1515         walking = false;
1516         groundPlane = false;
1517         memset( &groundTrace, 0, sizeof( groundTrace ) );
1518         groundMaterial = NULL;
1519         ladder = false;
1520         ladderNormal.Zero();
1521         waterLevel = WATERLEVEL_NONE;
1522         waterType = 0;
1523 }
1524
1525 /*
1526 ================
1527 idPhysics_Player_SavePState
1528 ================
1529 */
1530 void idPhysics_Player_SavePState( idSaveGame *savefile, const playerPState_t &state ) {
1531         savefile->WriteVec3( state.origin );
1532         savefile->WriteVec3( state.velocity );
1533         savefile->WriteVec3( state.localOrigin );
1534         savefile->WriteVec3( state.pushVelocity );
1535         savefile->WriteFloat( state.stepUp );
1536         savefile->WriteInt( state.movementType );
1537         savefile->WriteInt( state.movementFlags );
1538         savefile->WriteInt( state.movementTime );
1539 }
1540
1541 /*
1542 ================
1543 idPhysics_Player_RestorePState
1544 ================
1545 */
1546 void idPhysics_Player_RestorePState( idRestoreGame *savefile, playerPState_t &state ) {
1547         savefile->ReadVec3( state.origin );
1548         savefile->ReadVec3( state.velocity );
1549         savefile->ReadVec3( state.localOrigin );
1550         savefile->ReadVec3( state.pushVelocity );
1551         savefile->ReadFloat( state.stepUp );
1552         savefile->ReadInt( state.movementType );
1553         savefile->ReadInt( state.movementFlags );
1554         savefile->ReadInt( state.movementTime );
1555 }
1556
1557 /*
1558 ================
1559 idPhysics_Player::Save
1560 ================
1561 */
1562 void idPhysics_Player::Save( idSaveGame *savefile ) const {
1563
1564         idPhysics_Player_SavePState( savefile, current );
1565         idPhysics_Player_SavePState( savefile, saved );
1566
1567         savefile->WriteFloat( walkSpeed );
1568         savefile->WriteFloat( crouchSpeed );
1569         savefile->WriteFloat( maxStepHeight );
1570         savefile->WriteFloat( maxJumpHeight );
1571         savefile->WriteInt( debugLevel );
1572
1573         savefile->WriteUsercmd( command );
1574         savefile->WriteAngles( viewAngles );
1575
1576         savefile->WriteInt( framemsec );
1577         savefile->WriteFloat( frametime );
1578         savefile->WriteFloat( playerSpeed );
1579         savefile->WriteVec3( viewForward );
1580         savefile->WriteVec3( viewRight );
1581
1582         savefile->WriteBool( walking );
1583         savefile->WriteBool( groundPlane );
1584         savefile->WriteTrace( groundTrace );
1585         savefile->WriteMaterial( groundMaterial );
1586
1587         savefile->WriteBool( ladder );
1588         savefile->WriteVec3( ladderNormal );
1589
1590         savefile->WriteInt( (int)waterLevel );
1591         savefile->WriteInt( waterType );
1592 }
1593
1594 /*
1595 ================
1596 idPhysics_Player::Restore
1597 ================
1598 */
1599 void idPhysics_Player::Restore( idRestoreGame *savefile ) {
1600
1601         idPhysics_Player_RestorePState( savefile, current );
1602         idPhysics_Player_RestorePState( savefile, saved );
1603
1604         savefile->ReadFloat( walkSpeed );
1605         savefile->ReadFloat( crouchSpeed );
1606         savefile->ReadFloat( maxStepHeight );
1607         savefile->ReadFloat( maxJumpHeight );
1608         savefile->ReadInt( debugLevel );
1609
1610         savefile->ReadUsercmd( command );
1611         savefile->ReadAngles( viewAngles );
1612
1613         savefile->ReadInt( framemsec );
1614         savefile->ReadFloat( frametime );
1615         savefile->ReadFloat( playerSpeed );
1616         savefile->ReadVec3( viewForward );
1617         savefile->ReadVec3( viewRight );
1618
1619         savefile->ReadBool( walking );
1620         savefile->ReadBool( groundPlane );
1621         savefile->ReadTrace( groundTrace );
1622         savefile->ReadMaterial( groundMaterial );
1623
1624         savefile->ReadBool( ladder );
1625         savefile->ReadVec3( ladderNormal );
1626
1627         savefile->ReadInt( (int &)waterLevel );
1628         savefile->ReadInt( waterType );
1629 }
1630
1631 /*
1632 ================
1633 idPhysics_Player::SetPlayerInput
1634 ================
1635 */
1636 void idPhysics_Player::SetPlayerInput( const usercmd_t &cmd, const idAngles &newViewAngles ) {
1637         command = cmd;
1638         viewAngles = newViewAngles;             // can't use cmd.angles cause of the delta_angles
1639 }
1640
1641 /*
1642 ================
1643 idPhysics_Player::SetSpeed
1644 ================
1645 */
1646 void idPhysics_Player::SetSpeed( const float newWalkSpeed, const float newCrouchSpeed ) {
1647         walkSpeed = newWalkSpeed;
1648         crouchSpeed = newCrouchSpeed;
1649 }
1650
1651 /*
1652 ================
1653 idPhysics_Player::SetMaxStepHeight
1654 ================
1655 */
1656 void idPhysics_Player::SetMaxStepHeight( const float newMaxStepHeight ) {
1657         maxStepHeight = newMaxStepHeight;
1658 }
1659
1660 /*
1661 ================
1662 idPhysics_Player::GetMaxStepHeight
1663 ================
1664 */
1665 float idPhysics_Player::GetMaxStepHeight( void ) const {
1666         return maxStepHeight;
1667 }
1668
1669 /*
1670 ================
1671 idPhysics_Player::SetMaxJumpHeight
1672 ================
1673 */
1674 void idPhysics_Player::SetMaxJumpHeight( const float newMaxJumpHeight ) {
1675         maxJumpHeight = newMaxJumpHeight;
1676 }
1677
1678 /*
1679 ================
1680 idPhysics_Player::SetMovementType
1681 ================
1682 */
1683 void idPhysics_Player::SetMovementType( const pmtype_t type ) {
1684         current.movementType = type;
1685 }
1686
1687 /*
1688 ================
1689 idPhysics_Player::SetKnockBack
1690 ================
1691 */
1692 void idPhysics_Player::SetKnockBack( const int knockBackTime ) {
1693         if ( current.movementTime ) {
1694                 return;
1695         }
1696         current.movementFlags |= PMF_TIME_KNOCKBACK;
1697         current.movementTime = knockBackTime;
1698 }
1699
1700 /*
1701 ================
1702 idPhysics_Player::SetDebugLevel
1703 ================
1704 */
1705 void idPhysics_Player::SetDebugLevel( bool set ) {
1706         debugLevel = set;
1707 }
1708
1709 /*
1710 ================
1711 idPhysics_Player::Evaluate
1712 ================
1713 */
1714 bool idPhysics_Player::Evaluate( int timeStepMSec, int endTimeMSec ) {
1715         idVec3 masterOrigin, oldOrigin;
1716         idMat3 masterAxis;
1717
1718         waterLevel = WATERLEVEL_NONE;
1719         waterType = 0;
1720         oldOrigin = current.origin;
1721
1722         clipModel->Unlink();
1723
1724         // if bound to a master
1725         if ( masterEntity ) {
1726                 self->GetMasterPosition( masterOrigin, masterAxis );
1727                 current.origin = masterOrigin + current.localOrigin * masterAxis;
1728                 clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
1729                 current.velocity = ( current.origin - oldOrigin ) / ( timeStepMSec * 0.001f );
1730                 masterDeltaYaw = masterYaw;
1731                 masterYaw = masterAxis[0].ToYaw();
1732                 masterDeltaYaw = masterYaw - masterDeltaYaw;
1733                 return true;
1734         }
1735
1736         ActivateContactEntities();
1737
1738         idPhysics_Player::MovePlayer( timeStepMSec );
1739
1740         clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
1741
1742         if ( IsOutsideWorld() ) {
1743                 gameLocal.Warning( "clip model outside world bounds for entity '%s' at (%s)", self->name.c_str(), current.origin.ToString(0) );
1744         }
1745
1746         return true; //( current.origin != oldOrigin );
1747 }
1748
1749 /*
1750 ================
1751 idPhysics_Player::UpdateTime
1752 ================
1753 */
1754 void idPhysics_Player::UpdateTime( int endTimeMSec ) {
1755 }
1756
1757 /*
1758 ================
1759 idPhysics_Player::GetTime
1760 ================
1761 */
1762 int idPhysics_Player::GetTime( void ) const {
1763         return gameLocal.time;
1764 }
1765
1766 /*
1767 ================
1768 idPhysics_Player::GetImpactInfo
1769 ================
1770 */
1771 void idPhysics_Player::GetImpactInfo( const int id, const idVec3 &point, impactInfo_t *info ) const {
1772         info->invMass = invMass;
1773         info->invInertiaTensor.Zero();
1774         info->position.Zero();
1775         info->velocity = current.velocity;
1776 }
1777
1778 /*
1779 ================
1780 idPhysics_Player::ApplyImpulse
1781 ================
1782 */
1783 void idPhysics_Player::ApplyImpulse( const int id, const idVec3 &point, const idVec3 &impulse ) {
1784         if ( current.movementType != PM_NOCLIP ) {
1785                 current.velocity += impulse * invMass;
1786         }
1787 }
1788
1789 /*
1790 ================
1791 idPhysics_Player::IsAtRest
1792 ================
1793 */
1794 bool idPhysics_Player::IsAtRest( void ) const {
1795         return false;
1796 }
1797
1798 /*
1799 ================
1800 idPhysics_Player::GetRestStartTime
1801 ================
1802 */
1803 int idPhysics_Player::GetRestStartTime( void ) const {
1804         return -1;
1805 }
1806
1807 /*
1808 ================
1809 idPhysics_Player::SaveState
1810 ================
1811 */
1812 void idPhysics_Player::SaveState( void ) {
1813         saved = current;
1814 }
1815
1816 /*
1817 ================
1818 idPhysics_Player::RestoreState
1819 ================
1820 */
1821 void idPhysics_Player::RestoreState( void ) {
1822         current = saved;
1823
1824         clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
1825
1826         EvaluateContacts();
1827 }
1828
1829 /*
1830 ================
1831 idPhysics_Player::SetOrigin
1832 ================
1833 */
1834 void idPhysics_Player::SetOrigin( const idVec3 &newOrigin, int id ) {
1835         idVec3 masterOrigin;
1836         idMat3 masterAxis;
1837
1838         current.localOrigin = newOrigin;
1839         if ( masterEntity ) {
1840                 self->GetMasterPosition( masterOrigin, masterAxis );
1841                 current.origin = masterOrigin + newOrigin * masterAxis;
1842         }
1843         else {
1844                 current.origin = newOrigin;
1845         }
1846
1847         clipModel->Link( gameLocal.clip, self, 0, newOrigin, clipModel->GetAxis() );
1848 }
1849
1850 /*
1851 ================
1852 idPhysics_Player::GetOrigin
1853 ================
1854 */
1855 const idVec3 & idPhysics_Player::PlayerGetOrigin( void ) const {
1856         return current.origin;
1857 }
1858
1859 /*
1860 ================
1861 idPhysics_Player::SetAxis
1862 ================
1863 */
1864 void idPhysics_Player::SetAxis( const idMat3 &newAxis, int id ) {
1865         clipModel->Link( gameLocal.clip, self, 0, clipModel->GetOrigin(), newAxis );
1866 }
1867
1868 /*
1869 ================
1870 idPhysics_Player::Translate
1871 ================
1872 */
1873 void idPhysics_Player::Translate( const idVec3 &translation, int id ) {
1874
1875         current.localOrigin += translation;
1876         current.origin += translation;
1877
1878         clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
1879 }
1880
1881 /*
1882 ================
1883 idPhysics_Player::Rotate
1884 ================
1885 */
1886 void idPhysics_Player::Rotate( const idRotation &rotation, int id ) {
1887         idVec3 masterOrigin;
1888         idMat3 masterAxis;
1889
1890         current.origin *= rotation;
1891         if ( masterEntity ) {
1892                 self->GetMasterPosition( masterOrigin, masterAxis );
1893                 current.localOrigin = ( current.origin - masterOrigin ) * masterAxis.Transpose();
1894         }
1895         else {
1896                 current.localOrigin = current.origin;
1897         }
1898
1899         clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() * rotation.ToMat3() );
1900 }
1901
1902 /*
1903 ================
1904 idPhysics_Player::SetLinearVelocity
1905 ================
1906 */
1907 void idPhysics_Player::SetLinearVelocity( const idVec3 &newLinearVelocity, int id ) {
1908         current.velocity = newLinearVelocity;
1909 }
1910
1911 /*
1912 ================
1913 idPhysics_Player::GetLinearVelocity
1914 ================
1915 */
1916 const idVec3 &idPhysics_Player::GetLinearVelocity( int id ) const {
1917         return current.velocity;
1918 }
1919
1920 /*
1921 ================
1922 idPhysics_Player::SetPushed
1923 ================
1924 */
1925 void idPhysics_Player::SetPushed( int deltaTime ) {
1926         idVec3 velocity;
1927         float d;
1928
1929         // velocity with which the player is pushed
1930         velocity = ( current.origin - saved.origin ) / ( deltaTime * idMath::M_MS2SEC );
1931
1932         // remove any downward push velocity
1933         d = velocity * gravityNormal;
1934         if ( d > 0.0f ) {
1935                 velocity -= d * gravityNormal;
1936         }
1937
1938         current.pushVelocity += velocity;
1939 }
1940
1941 /*
1942 ================
1943 idPhysics_Player::GetPushedLinearVelocity
1944 ================
1945 */
1946 const idVec3 &idPhysics_Player::GetPushedLinearVelocity( const int id ) const {
1947         return current.pushVelocity;
1948 }
1949
1950 /*
1951 ================
1952 idPhysics_Player::ClearPushedVelocity
1953 ================
1954 */
1955 void idPhysics_Player::ClearPushedVelocity( void ) {
1956         current.pushVelocity.Zero();
1957 }
1958
1959 /*
1960 ================
1961 idPhysics_Player::SetMaster
1962
1963   the binding is never orientated
1964 ================
1965 */
1966 void idPhysics_Player::SetMaster( idEntity *master, const bool orientated ) {
1967         idVec3 masterOrigin;
1968         idMat3 masterAxis;
1969
1970         if ( master ) {
1971                 if ( !masterEntity ) {
1972                         // transform from world space to master space
1973                         self->GetMasterPosition( masterOrigin, masterAxis );
1974                         current.localOrigin = ( current.origin - masterOrigin ) * masterAxis.Transpose();
1975                         masterEntity = master;
1976                         masterYaw = masterAxis[0].ToYaw();
1977                 }
1978                 ClearContacts();
1979         }
1980         else {
1981                 if ( masterEntity ) {
1982                         masterEntity = NULL;
1983                 }
1984         }
1985 }
1986
1987 const float     PLAYER_VELOCITY_MAX                             = 4000;
1988 const int       PLAYER_VELOCITY_TOTAL_BITS              = 16;
1989 const int       PLAYER_VELOCITY_EXPONENT_BITS   = idMath::BitsForInteger( idMath::BitsForFloat( PLAYER_VELOCITY_MAX ) ) + 1;
1990 const int       PLAYER_VELOCITY_MANTISSA_BITS   = PLAYER_VELOCITY_TOTAL_BITS - 1 - PLAYER_VELOCITY_EXPONENT_BITS;
1991 const int       PLAYER_MOVEMENT_TYPE_BITS               = 3;
1992 const int       PLAYER_MOVEMENT_FLAGS_BITS              = 8;
1993
1994 /*
1995 ================
1996 idPhysics_Player::WriteToSnapshot
1997 ================
1998 */
1999 void idPhysics_Player::WriteToSnapshot( idBitMsgDelta &msg ) const {
2000         msg.WriteFloat( current.origin[0] );
2001         msg.WriteFloat( current.origin[1] );
2002         msg.WriteFloat( current.origin[2] );
2003         msg.WriteFloat( current.velocity[0], PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS );
2004         msg.WriteFloat( current.velocity[1], PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS );
2005         msg.WriteFloat( current.velocity[2], PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS );
2006         msg.WriteDeltaFloat( current.origin[0], current.localOrigin[0] );
2007         msg.WriteDeltaFloat( current.origin[1], current.localOrigin[1] );
2008         msg.WriteDeltaFloat( current.origin[2], current.localOrigin[2] );
2009         msg.WriteDeltaFloat( 0.0f, current.pushVelocity[0], PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS );
2010         msg.WriteDeltaFloat( 0.0f, current.pushVelocity[1], PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS );
2011         msg.WriteDeltaFloat( 0.0f, current.pushVelocity[2], PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS );
2012         msg.WriteDeltaFloat( 0.0f, current.stepUp );
2013         msg.WriteBits( current.movementType, PLAYER_MOVEMENT_TYPE_BITS );
2014         msg.WriteBits( current.movementFlags, PLAYER_MOVEMENT_FLAGS_BITS );
2015         msg.WriteDeltaLong( 0, current.movementTime );
2016 }
2017
2018 /*
2019 ================
2020 idPhysics_Player::ReadFromSnapshot
2021 ================
2022 */
2023 void idPhysics_Player::ReadFromSnapshot( const idBitMsgDelta &msg ) {
2024         current.origin[0] = msg.ReadFloat();
2025         current.origin[1] = msg.ReadFloat();
2026         current.origin[2] = msg.ReadFloat();
2027         current.velocity[0] = msg.ReadFloat( PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS );
2028         current.velocity[1] = msg.ReadFloat( PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS );
2029         current.velocity[2] = msg.ReadFloat( PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS );
2030         current.localOrigin[0] = msg.ReadDeltaFloat( current.origin[0] );
2031         current.localOrigin[1] = msg.ReadDeltaFloat( current.origin[1] );
2032         current.localOrigin[2] = msg.ReadDeltaFloat( current.origin[2] );
2033         current.pushVelocity[0] = msg.ReadDeltaFloat( 0.0f, PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS );
2034         current.pushVelocity[1] = msg.ReadDeltaFloat( 0.0f, PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS );
2035         current.pushVelocity[2] = msg.ReadDeltaFloat( 0.0f, PLAYER_VELOCITY_EXPONENT_BITS, PLAYER_VELOCITY_MANTISSA_BITS );
2036         current.stepUp = msg.ReadDeltaFloat( 0.0f );
2037         current.movementType = msg.ReadBits( PLAYER_MOVEMENT_TYPE_BITS );
2038         current.movementFlags = msg.ReadBits( PLAYER_MOVEMENT_FLAGS_BITS );
2039         current.movementTime = msg.ReadDeltaLong( 0 );
2040
2041         if ( clipModel ) {
2042                 clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
2043         }
2044 }
2045