2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../../idlib/precompiled.h"
32 #include "../Game_local.h"
34 CLASS_DECLARATION( idPhysics_Actor, idPhysics_Player )
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;
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;
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;
54 const float MIN_WALK_NORMAL = 0.7f; // can't walk on very steep slopes
55 const float OVERCLIP = 1.001f;
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);
72 idPhysics_Player::CmdScale
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.
79 float idPhysics_Player::CmdScale( const usercmd_t &cmd ) const {
87 forwardmove = cmd.forwardmove;
88 rightmove = cmd.rightmove;
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
98 max = abs( forwardmove );
99 if ( abs( rightmove ) > max ) {
100 max = abs( rightmove );
102 if ( abs( upmove ) > max ) {
110 total = idMath::Sqrt( (float) forwardmove * forwardmove + rightmove * rightmove + upmove * upmove );
111 scale = (float) playerSpeed * max / ( 127.0f * total );
118 idPhysics_Player::Accelerate
120 Handles user intended acceleration
123 void idPhysics_Player::Accelerate( const idVec3 &wishdir, const float wishspeed, const float accel ) {
126 float addspeed, accelspeed, currentspeed;
128 currentspeed = current.velocity * wishdir;
129 addspeed = wishspeed - currentspeed;
133 accelspeed = accel * frametime * wishspeed;
134 if (accelspeed > addspeed) {
135 accelspeed = addspeed;
138 current.velocity += accelspeed * wishdir;
140 // proper way (avoids strafe jump maxspeed bug), but feels bad
146 wishVelocity = wishdir * wishspeed;
147 pushDir = wishVelocity - current.velocity;
148 pushLen = pushDir.Normalize();
150 canPush = accel * frametime * wishspeed;
151 if (canPush > pushLen) {
155 current.velocity += canPush * pushDir;
161 idPhysics_Player::SlideMove
163 Returns true if the velocity was clipped in some way
166 #define MAX_CLIP_PLANES 5
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;
179 primal_velocity = current.velocity;
182 endVelocity = current.velocity + gravityVector * frametime;
183 current.velocity = ( current.velocity + endVelocity ) * 0.5f;
184 primal_velocity = endVelocity;
186 // slide along the ground plane
187 current.velocity.ProjectOntoPlane( groundTrace.c.normal, OVERCLIP );
191 endVelocity = current.velocity;
194 time_left = frametime;
196 // never turn against the ground plane
199 planes[0] = groundTrace.c.normal;
204 // never turn against original velocity
205 planes[numplanes] = current.velocity;
206 planes[numplanes].Normalize();
209 for ( bumpcount = 0; bumpcount < numbumps; bumpcount++ ) {
211 // calculate position we are trying to move to
212 end = current.origin + time_left * current.velocity;
214 // see if we can make it there
215 gameLocal.clip.Translation( trace, current.origin, end, clipModel, clipModel->GetAxis(), clipMask, self );
217 time_left -= time_left * trace.fraction;
218 current.origin = trace.endpos;
220 // if moved the entire distance
221 if ( trace.fraction >= 1.0f ) {
225 stepped = pushed = false;
227 // if we are allowed to step up
230 nearGround = groundPlane | ladder;
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 );
240 // may only step up if near the ground or on a ladder
244 stepEnd = current.origin - maxStepHeight * gravityNormal;
245 gameLocal.clip.Translation( downTrace, current.origin, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self );
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 );
252 stepEnd = stepTrace.endpos + maxStepHeight * gravityNormal;
253 gameLocal.clip.Translation( downTrace, stepTrace.endpos, stepEnd, clipModel, clipModel->GetAxis(), clipMask, self );
255 if ( downTrace.fraction >= 1.0f || (downTrace.c.normal * -gravityNormal) > MIN_WALK_NORMAL ) {
257 // if moved the entire distance
258 if ( stepTrace.fraction >= 1.0f ) {
260 current.stepUp -= ( downTrace.endpos - current.origin ) * gravityNormal;
261 current.origin = downTrace.endpos;
262 current.movementFlags |= PMF_STEPPED_UP;
263 current.velocity *= PM_STEPSCALE;
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;
281 // if we can push other entities and not blocked by the world
282 if ( push && trace.c.entityNum != ENTITYNUM_WORLD ) {
284 clipModel->SetPosition( current.origin, clipModel->GetAxis() );
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;
291 totalMass = gameLocal.push.ClipTranslationalPush( trace, self, pushFlags, end, end - current.origin );
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 );
299 current.origin = trace.endpos;
300 time_left -= time_left * trace.fraction;
302 // if moved the entire distance
303 if ( trace.fraction >= 1.0f ) {
309 // let the entity know about the collision
310 self->Collide( trace, current.velocity );
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;
321 // if this is the same plane we hit before, nudge velocity
322 // out along it, which fixes some epsilon issues with
325 for ( i = 0; i < numplanes; i++ ) {
326 if ( ( trace.c.normal * planes[i] ) > 0.999f ) {
327 current.velocity += trace.c.normal;
331 if ( i < numplanes ) {
334 planes[numplanes] = trace.c.normal;
338 // modify velocity so it parallels all of the clip planes
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
348 // slide along the plane
349 clipVelocity = current.velocity;
350 clipVelocity.ProjectOntoPlane( planes[i], OVERCLIP );
352 // slide along the plane
353 endClipVelocity = endVelocity;
354 endClipVelocity.ProjectOntoPlane( planes[i], OVERCLIP );
356 // see if there is a second plane that the new move enters
357 for ( j = 0; j < numplanes; j++ ) {
361 if ( ( clipVelocity * planes[j] ) >= 0.1f ) {
362 continue; // move doesn't interact with the plane
365 // try clipping the move to the plane
366 clipVelocity.ProjectOntoPlane( planes[j], OVERCLIP );
367 endClipVelocity.ProjectOntoPlane( planes[j], OVERCLIP );
369 // see if it goes back into the first clip plane
370 if ( ( clipVelocity * planes[i] ) >= 0 ) {
374 // slide the original velocity along the crease
375 dir = planes[i].Cross( planes[j] );
377 d = dir * current.velocity;
378 clipVelocity = d * dir;
380 dir = planes[i].Cross( planes[j] );
382 d = dir * endVelocity;
383 endClipVelocity = d * dir;
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 ) {
390 if ( ( clipVelocity * planes[k] ) >= 0.1f ) {
391 continue; // move doesn't interact with the plane
394 // stop dead at a tripple plane interaction
395 current.velocity = vec3_origin;
400 // if we have fixed all interactions, try another move
401 current.velocity = clipVelocity;
402 endVelocity = endClipVelocity;
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;
420 current.velocity = endVelocity;
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;
430 return (bool)( bumpcount == 0 );
435 idPhysics_Player::Friction
437 Handles both ground friction and water friction
440 void idPhysics_Player::Friction( void ) {
442 float speed, newspeed, control;
445 vel = current.velocity;
447 // ignore slope movement, remove all velocity in gravity direction
448 vel += (vel * gravityNormal) * gravityNormal;
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();
457 current.velocity = (current.velocity * gravityNormal) * gravityNormal;
459 // FIXME: still have z friction underwater?
465 // spectator friction
466 if ( current.movementType == PM_SPECTATOR ) {
467 drop += speed * PM_FLYFRICTION * frametime;
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;
480 // apply water friction even if just wading
481 else if ( waterLevel ) {
482 drop += speed * PM_WATERFRICTION * waterLevel * frametime;
484 // apply air friction
486 drop += speed * PM_AIRFRICTION * frametime;
489 // scale the velocity
490 newspeed = speed - drop;
494 current.velocity *= ( newspeed / speed );
499 idPhysics_Player::WaterJumpMove
501 Flying out of the water
504 void idPhysics_Player::WaterJumpMove( void ) {
506 // waterjump has no control, but falls
507 idPhysics_Player::SlideMove( true, true, false, false );
510 current.velocity += gravityNormal * frametime;
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;
521 idPhysics_Player::WaterMove
524 void idPhysics_Player::WaterMove( void ) {
531 if ( idPhysics_Player::CheckWaterJump() ) {
532 idPhysics_Player::WaterJumpMove();
536 idPhysics_Player::Friction();
538 scale = idPhysics_Player::CmdScale( command );
542 wishvel = gravityNormal * 60; // sink towards bottom
544 wishvel = scale * (viewForward * command.forwardmove + viewRight * command.rightmove);
545 wishvel -= scale * gravityNormal * command.upmove;
549 wishspeed = wishdir.Normalize();
551 if ( wishspeed > playerSpeed * PM_SWIMSCALE ) {
552 wishspeed = playerSpeed * PM_SWIMSCALE;
555 idPhysics_Player::Accelerate( wishdir, wishspeed, PM_WATERACCELERATE );
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 );
563 current.velocity.Normalize();
564 current.velocity *= vel;
567 idPhysics_Player::SlideMove( false, true, false, false );
572 idPhysics_Player::FlyMove
575 void idPhysics_Player::FlyMove( void ) {
582 idPhysics_Player::Friction();
584 scale = idPhysics_Player::CmdScale( command );
587 wishvel = vec3_origin;
589 wishvel = scale * (viewForward * command.forwardmove + viewRight * command.rightmove);
590 wishvel -= scale * gravityNormal * command.upmove;
594 wishspeed = wishdir.Normalize();
596 idPhysics_Player::Accelerate( wishdir, wishspeed, PM_FLYACCELERATE );
598 idPhysics_Player::SlideMove( false, false, false, false );
603 idPhysics_Player::AirMove
606 void idPhysics_Player::AirMove( void ) {
612 idPhysics_Player::Friction();
614 scale = idPhysics_Player::CmdScale( command );
616 // project moves down to flat plane
617 viewForward -= (viewForward * gravityNormal) * gravityNormal;
618 viewRight -= (viewRight * gravityNormal) * gravityNormal;
619 viewForward.Normalize();
620 viewRight.Normalize();
622 wishvel = viewForward * command.forwardmove + viewRight * command.rightmove;
623 wishvel -= (wishvel * gravityNormal) * gravityNormal;
625 wishspeed = wishdir.Normalize();
628 // not on ground, so little effect on velocity
629 idPhysics_Player::Accelerate( wishdir, wishspeed, PM_AIRACCELERATE );
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
635 current.velocity.ProjectOntoPlane( groundTrace.c.normal, OVERCLIP );
638 idPhysics_Player::SlideMove( true, false, false, false );
643 idPhysics_Player::WalkMove
646 void idPhysics_Player::WalkMove( void ) {
652 idVec3 oldVelocity, vel;
653 float oldVel, newVel;
655 if ( waterLevel > WATERLEVEL_WAIST && ( viewForward * groundTrace.c.normal ) > 0.0f ) {
657 idPhysics_Player::WaterMove();
661 if ( idPhysics_Player::CheckJump() ) {
663 if ( waterLevel > WATERLEVEL_FEET ) {
664 idPhysics_Player::WaterMove();
667 idPhysics_Player::AirMove();
672 idPhysics_Player::Friction();
674 scale = idPhysics_Player::CmdScale( command );
676 // project moves down to flat plane
677 viewForward -= (viewForward * gravityNormal) * gravityNormal;
678 viewRight -= (viewRight * gravityNormal) * gravityNormal;
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 );
684 viewForward.Normalize();
685 viewRight.Normalize();
687 wishvel = viewForward * command.forwardmove + viewRight * command.rightmove;
689 wishspeed = wishdir.Normalize();
692 // clamp the speed lower if wading or walking on the bottom
696 waterScale = waterLevel / 3.0f;
697 waterScale = 1.0f - ( 1.0f - PM_SWIMSCALE ) * waterScale;
698 if ( wishspeed > playerSpeed * waterScale ) {
699 wishspeed = playerSpeed * waterScale;
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;
708 accelerate = PM_ACCELERATE;
711 idPhysics_Player::Accelerate( wishdir, wishspeed, accelerate );
713 if ( ( groundMaterial && groundMaterial->GetSurfaceFlags() & SURF_SLICK ) || current.movementFlags & PMF_TIME_KNOCKBACK ) {
714 current.velocity += gravityVector * frametime;
717 oldVelocity = current.velocity;
719 // slide along the ground plane
720 current.velocity.ProjectOntoPlane( groundTrace.c.normal, OVERCLIP );
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 );
734 // don't do anything if standing still
735 vel = current.velocity - (current.velocity * gravityNormal) * gravityNormal;
736 if ( !vel.LengthSqr() ) {
740 gameLocal.push.InitSavingPushedEntityPositions();
742 idPhysics_Player::SlideMove( false, true, true, true );
747 idPhysics_Player::DeadMove
750 void idPhysics_Player::DeadMove( void ) {
758 forward = current.velocity.Length();
760 if ( forward <= 0 ) {
761 current.velocity = vec3_origin;
764 current.velocity.Normalize();
765 current.velocity *= forward;
771 idPhysics_Player::NoclipMove
774 void idPhysics_Player::NoclipMove( void ) {
775 float speed, drop, friction, newspeed, stopspeed;
776 float scale, wishspeed;
780 speed = current.velocity.Length();
781 if ( speed < 20.0f ) {
782 current.velocity = vec3_origin;
785 stopspeed = playerSpeed * 0.3f;
786 if ( speed < stopspeed ) {
789 friction = PM_NOCLIPFRICTION;
790 drop = speed * friction * frametime;
792 // scale the velocity
793 newspeed = speed - drop;
798 current.velocity *= newspeed / speed;
802 scale = idPhysics_Player::CmdScale( command );
804 wishdir = scale * (viewForward * command.forwardmove + viewRight * command.rightmove);
805 wishdir -= scale * gravityNormal * command.upmove;
806 wishspeed = wishdir.Normalize();
809 idPhysics_Player::Accelerate( wishdir, wishspeed, PM_ACCELERATE );
812 current.origin += frametime * current.velocity;
817 idPhysics_Player::SpectatorMove
820 void idPhysics_Player::SpectatorMove( void ) {
831 idPhysics_Player::Friction();
833 scale = idPhysics_Player::CmdScale( command );
836 wishvel = vec3_origin;
838 wishvel = scale * (viewForward * command.forwardmove + viewRight * command.rightmove);
842 wishspeed = wishdir.Normalize();
844 idPhysics_Player::Accelerate( wishdir, wishspeed, PM_FLYACCELERATE );
846 idPhysics_Player::SlideMove( false, false, false, false );
851 idPhysics_Player::LadderMove
854 void idPhysics_Player::LadderMove( void ) {
855 idVec3 wishdir, wishvel, right;
856 float wishspeed, scale;
859 // stick to the ladder
860 wishvel = -100.0f * ladderNormal;
861 current.velocity = (gravityNormal * current.velocity) * gravityNormal + wishvel;
863 upscale = (-gravityNormal * viewForward + 0.5f) * 2.5f;
864 if ( upscale > 1.0f ) {
867 else if ( upscale < -1.0f ) {
871 scale = idPhysics_Player::CmdScale( command );
872 wishvel = -0.9f * gravityNormal * upscale * scale * (float)command.forwardmove;
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;
882 // if we are looking away from the ladder, reverse the right vector
883 if ( ladderNormal * viewForward > 0.0f ) {
886 wishvel += 2.0f * right * scale * (float) command.rightmove;
890 if ( command.upmove ) {
891 wishvel += -0.5f * gravityNormal * scale * (float) command.upmove;
894 // do strafe friction
895 idPhysics_Player::Friction();
898 wishspeed = wishvel.Normalize();
899 idPhysics_Player::Accelerate( wishvel, wishspeed, PM_ACCELERATE );
901 // cap the vertical velocity
902 upscale = current.velocity * -gravityNormal;
903 if ( upscale < -PM_LADDERSPEED ) {
904 current.velocity += gravityNormal * (upscale + PM_LADDERSPEED);
906 else if ( upscale > PM_LADDERSPEED ) {
907 current.velocity += gravityNormal * (upscale - PM_LADDERSPEED);
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;
918 current.velocity -= gravityVector * frametime;
919 if ( current.velocity * gravityNormal < 0.0f ) {
920 current.velocity -= (gravityNormal * current.velocity) * gravityNormal;
925 idPhysics_Player::SlideMove( false, ( command.forwardmove > 0 ), false, false );
930 idPhysics_Player::CorrectAllSolid
933 void idPhysics_Player::CorrectAllSolid( trace_t &trace, int contents ) {
935 gameLocal.Printf( "%i:allsolid\n", c_pmove );
938 // FIXME: jitter around to find a free spot ?
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;
950 trace.c.type = CONTACT_TRMVERTEX;
951 trace.c.material = NULL;
952 trace.c.contents = contents;
958 idPhysics_Player::CheckGround
961 void idPhysics_Player::CheckGround( void ) {
964 bool hadGroundContacts;
966 hadGroundContacts = HasGroundContacts();
968 // set the clip model origin before getting the contacts
969 clipModel->SetPosition( current.origin, clipModel->GetAxis() );
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;
982 groundTrace.c.normal.Normalize();
984 groundTrace.fraction = 1.0f;
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 );
993 // if the trace didn't hit anything, we are in free fall
994 if ( groundTrace.fraction == 1.0f ) {
997 groundEntityPtr = NULL;
1001 groundMaterial = groundTrace.c.material;
1002 groundEntityPtr = gameLocal.entities[ groundTrace.c.entityNum ];
1004 // check if getting thrown off the ground
1005 if ( (current.velocity * -gravityNormal) > 0.0f && ( current.velocity * groundTrace.c.normal ) > 10.0f ) {
1007 gameLocal.Printf( "%i:kickoff\n", c_pmove );
1010 groundPlane = false;
1015 // slopes that are too steep will not be considered onground
1016 if ( ( groundTrace.c.normal * -gravityNormal ) < MIN_WALK_NORMAL ) {
1018 gameLocal.Printf( "%i:steep\n", c_pmove );
1021 // FIXME: if they can't slide down the slope, let them walk (sharp crevices)
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;
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;
1042 // if the player didn't have ground contacts the previous frame
1043 if ( !hadGroundContacts ) {
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;
1053 // let the entity know about the collision
1054 self->Collide( groundTrace, current.velocity );
1056 if ( groundEntityPtr.GetEntity() ) {
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 ) );
1067 idPhysics_Player::CheckDuck
1069 Sets clip model size
1072 void idPhysics_Player::CheckDuck( void ) {
1078 if ( current.movementType == PM_DEAD ) {
1079 maxZ = pm_deadheight.GetFloat();
1081 // stand up when up against a ladder
1082 if ( command.upmove < 0 && !ladder ) {
1084 current.movementFlags |= PMF_DUCKED;
1086 // stand up if possible
1087 if ( current.movementFlags & PMF_DUCKED ) {
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;
1097 if ( current.movementFlags & PMF_DUCKED ) {
1098 playerSpeed = crouchSpeed;
1099 maxZ = pm_crouchheight.GetFloat();
1101 maxZ = pm_normalheight.GetFloat();
1104 // if the clipModel height should change
1105 if ( clipModel->GetBounds()[1][2] != maxZ ) {
1107 bounds = clipModel->GetBounds();
1108 bounds[1][2] = maxZ;
1109 if ( pm_usecylinder.GetBool() ) {
1110 clipModel->LoadModel( idTraceModel( bounds, 8 ) );
1112 clipModel->LoadModel( idTraceModel( bounds ) );
1119 idPhysics_Player::CheckLadder
1122 void idPhysics_Player::CheckLadder( void ) {
1123 idVec3 forward, start, end;
1127 if ( current.movementTime ) {
1131 // if on the ground moving backwards
1132 if ( walking && command.forwardmove <= 0 ) {
1136 // forward vector orthogonal to gravity
1137 forward = viewForward - (gravityNormal * viewForward) * gravityNormal;
1138 forward.Normalize();
1141 // don't want to get sucked towards the ladder when still walking
1147 end = current.origin + tracedist * forward;
1148 gameLocal.clip.Translation( trace, current.origin, end, clipModel, clipModel->GetAxis(), clipMask, self );
1150 // if near a surface
1151 if ( trace.fraction < 1.0f ) {
1153 // if a ladder surface
1154 if ( trace.c.material && ( trace.c.material->GetSurfaceFlags() & SURF_LADDER ) ) {
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 );
1163 // if also near a surface a step height higher
1164 if ( trace.fraction < 1.0f ) {
1166 // if it also is a ladder surface
1167 if ( trace.c.material && trace.c.material->GetSurfaceFlags() & SURF_LADDER ) {
1169 ladderNormal = trace.c.normal;
1178 idPhysics_Player::CheckJump
1181 bool idPhysics_Player::CheckJump( void ) {
1184 if ( command.upmove < 10 ) {
1189 // must wait for jump to be released
1190 if ( current.movementFlags & PMF_JUMP_HELD ) {
1194 // don't jump if we can't stand up
1195 if ( current.movementFlags & PMF_DUCKED ) {
1199 groundPlane = false; // jumping away
1201 current.movementFlags |= PMF_JUMP_HELD | PMF_JUMPED;
1203 addVelocity = 2.0f * maxJumpHeight * -gravityVector;
1204 addVelocity *= idMath::Sqrt( addVelocity.Normalize() );
1205 current.velocity += addVelocity;
1212 idPhysics_Player::CheckWaterJump
1215 bool idPhysics_Player::CheckWaterJump( void ) {
1220 if ( current.movementTime ) {
1224 // check for water jump
1225 if ( waterLevel != WATERLEVEL_WAIST ) {
1229 flatforward = viewForward - (viewForward * gravityNormal) * gravityNormal;
1230 flatforward.Normalize();
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) ) {
1239 spot -= 16.0f * gravityNormal;
1240 cont = gameLocal.clip.Contents( spot, NULL, mat3_identity, -1, self );
1245 // jump out of water
1246 current.velocity = 200.0f * viewForward - 350.0f * gravityNormal;
1247 current.movementFlags |= PMF_TIME_WATERJUMP;
1248 current.movementTime = 2000;
1255 idPhysics_Player::SetWaterLevel
1258 void idPhysics_Player::SetWaterLevel( void ) {
1264 // get waterlevel, accounting for ducking
1266 waterLevel = WATERLEVEL_NONE;
1269 bounds = clipModel->GetBounds();
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 ) {
1276 waterType = contents;
1277 waterLevel = WATERLEVEL_FEET;
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 ) {
1284 waterLevel = WATERLEVEL_WAIST;
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;
1298 idPhysics_Player::DropTimers
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;
1309 current.movementTime -= framemsec;
1316 idPhysics_Player::MovePlayer
1319 void idPhysics_Player::MovePlayer( int msec ) {
1321 // this counter lets us debug movement problems with a journal
1322 // by setting a conditional breakpoint for the previous frame
1326 groundPlane = false;
1329 // determine the time
1331 frametime = framemsec * 0.001f;
1334 playerSpeed = walkSpeed;
1336 // remove jumped and stepped up flag
1337 current.movementFlags &= ~(PMF_JUMPED|PMF_STEPPED_UP|PMF_STEPPED_DOWN);
1338 current.stepUp = 0.0f;
1340 if ( command.upmove < 10 ) {
1342 current.movementFlags &= ~PMF_JUMP_HELD;
1345 // if no movement at all
1346 if ( current.movementType == PM_FREEZE ) {
1350 // move the player velocity into the frame of a pusher
1351 current.velocity -= current.pushVelocity;
1354 viewAngles.ToVectors( &viewForward, NULL, NULL );
1355 viewForward *= clipModelAxis;
1356 viewRight = gravityNormal.Cross( viewForward );
1357 viewRight.Normalize();
1359 // fly in spectator mode
1360 if ( current.movementType == PM_SPECTATOR ) {
1362 idPhysics_Player::DropTimers();
1366 // special no clip mode
1367 if ( current.movementType == PM_NOCLIP ) {
1368 idPhysics_Player::NoclipMove();
1369 idPhysics_Player::DropTimers();
1373 // no control when dead
1374 if ( current.movementType == PM_DEAD ) {
1375 command.forwardmove = 0;
1376 command.rightmove = 0;
1380 // set watertype and waterlevel
1381 idPhysics_Player::SetWaterLevel();
1384 idPhysics_Player::CheckGround();
1386 // check if up against a ladder
1387 idPhysics_Player::CheckLadder();
1389 // set clip model size
1390 idPhysics_Player::CheckDuck();
1393 idPhysics_Player::DropTimers();
1396 if ( current.movementType == PM_DEAD ) {
1398 idPhysics_Player::DeadMove();
1400 else if ( ladder ) {
1401 // going up or down a ladder
1402 idPhysics_Player::LadderMove();
1404 else if ( current.movementFlags & PMF_TIME_WATERJUMP ) {
1405 // jumping out of water
1406 idPhysics_Player::WaterJumpMove();
1408 else if ( waterLevel > 1 ) {
1410 idPhysics_Player::WaterMove();
1412 else if ( walking ) {
1413 // walking on ground
1414 idPhysics_Player::WalkMove();
1418 idPhysics_Player::AirMove();
1421 // set watertype, waterlevel and groundentity
1422 idPhysics_Player::SetWaterLevel();
1423 idPhysics_Player::CheckGround();
1425 // move the player velocity back into the world frame
1426 current.velocity += current.pushVelocity;
1427 current.pushVelocity.Zero();
1432 idPhysics_Player::GetWaterLevel
1435 waterLevel_t idPhysics_Player::GetWaterLevel( void ) const {
1441 idPhysics_Player::GetWaterType
1444 int idPhysics_Player::GetWaterType( void ) const {
1450 idPhysics_Player::HasJumped
1453 bool idPhysics_Player::HasJumped( void ) const {
1454 return ( ( current.movementFlags & PMF_JUMPED ) != 0 );
1459 idPhysics_Player::HasSteppedUp
1462 bool idPhysics_Player::HasSteppedUp( void ) const {
1463 return ( ( current.movementFlags & ( PMF_STEPPED_UP | PMF_STEPPED_DOWN ) ) != 0 );
1468 idPhysics_Player::GetStepUp
1471 float idPhysics_Player::GetStepUp( void ) const {
1472 return current.stepUp;
1477 idPhysics_Player::IsCrouching
1480 bool idPhysics_Player::IsCrouching( void ) const {
1481 return ( ( current.movementFlags & PMF_DUCKED ) != 0 );
1486 idPhysics_Player::OnLadder
1489 bool idPhysics_Player::OnLadder( void ) const {
1495 idPhysics_Player::idPhysics_Player
1498 idPhysics_Player::idPhysics_Player( void ) {
1502 memset( ¤t, 0, sizeof( current ) );
1508 memset( &command, 0, sizeof( command ) );
1516 groundPlane = false;
1517 memset( &groundTrace, 0, sizeof( groundTrace ) );
1518 groundMaterial = NULL;
1520 ladderNormal.Zero();
1521 waterLevel = WATERLEVEL_NONE;
1527 idPhysics_Player_SavePState
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 );
1543 idPhysics_Player_RestorePState
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 );
1559 idPhysics_Player::Save
1562 void idPhysics_Player::Save( idSaveGame *savefile ) const {
1564 idPhysics_Player_SavePState( savefile, current );
1565 idPhysics_Player_SavePState( savefile, saved );
1567 savefile->WriteFloat( walkSpeed );
1568 savefile->WriteFloat( crouchSpeed );
1569 savefile->WriteFloat( maxStepHeight );
1570 savefile->WriteFloat( maxJumpHeight );
1571 savefile->WriteInt( debugLevel );
1573 savefile->WriteUsercmd( command );
1574 savefile->WriteAngles( viewAngles );
1576 savefile->WriteInt( framemsec );
1577 savefile->WriteFloat( frametime );
1578 savefile->WriteFloat( playerSpeed );
1579 savefile->WriteVec3( viewForward );
1580 savefile->WriteVec3( viewRight );
1582 savefile->WriteBool( walking );
1583 savefile->WriteBool( groundPlane );
1584 savefile->WriteTrace( groundTrace );
1585 savefile->WriteMaterial( groundMaterial );
1587 savefile->WriteBool( ladder );
1588 savefile->WriteVec3( ladderNormal );
1590 savefile->WriteInt( (int)waterLevel );
1591 savefile->WriteInt( waterType );
1596 idPhysics_Player::Restore
1599 void idPhysics_Player::Restore( idRestoreGame *savefile ) {
1601 idPhysics_Player_RestorePState( savefile, current );
1602 idPhysics_Player_RestorePState( savefile, saved );
1604 savefile->ReadFloat( walkSpeed );
1605 savefile->ReadFloat( crouchSpeed );
1606 savefile->ReadFloat( maxStepHeight );
1607 savefile->ReadFloat( maxJumpHeight );
1608 savefile->ReadInt( debugLevel );
1610 savefile->ReadUsercmd( command );
1611 savefile->ReadAngles( viewAngles );
1613 savefile->ReadInt( framemsec );
1614 savefile->ReadFloat( frametime );
1615 savefile->ReadFloat( playerSpeed );
1616 savefile->ReadVec3( viewForward );
1617 savefile->ReadVec3( viewRight );
1619 savefile->ReadBool( walking );
1620 savefile->ReadBool( groundPlane );
1621 savefile->ReadTrace( groundTrace );
1622 savefile->ReadMaterial( groundMaterial );
1624 savefile->ReadBool( ladder );
1625 savefile->ReadVec3( ladderNormal );
1627 savefile->ReadInt( (int &)waterLevel );
1628 savefile->ReadInt( waterType );
1633 idPhysics_Player::SetPlayerInput
1636 void idPhysics_Player::SetPlayerInput( const usercmd_t &cmd, const idAngles &newViewAngles ) {
1638 viewAngles = newViewAngles; // can't use cmd.angles cause of the delta_angles
1643 idPhysics_Player::SetSpeed
1646 void idPhysics_Player::SetSpeed( const float newWalkSpeed, const float newCrouchSpeed ) {
1647 walkSpeed = newWalkSpeed;
1648 crouchSpeed = newCrouchSpeed;
1653 idPhysics_Player::SetMaxStepHeight
1656 void idPhysics_Player::SetMaxStepHeight( const float newMaxStepHeight ) {
1657 maxStepHeight = newMaxStepHeight;
1662 idPhysics_Player::GetMaxStepHeight
1665 float idPhysics_Player::GetMaxStepHeight( void ) const {
1666 return maxStepHeight;
1671 idPhysics_Player::SetMaxJumpHeight
1674 void idPhysics_Player::SetMaxJumpHeight( const float newMaxJumpHeight ) {
1675 maxJumpHeight = newMaxJumpHeight;
1680 idPhysics_Player::SetMovementType
1683 void idPhysics_Player::SetMovementType( const pmtype_t type ) {
1684 current.movementType = type;
1689 idPhysics_Player::SetKnockBack
1692 void idPhysics_Player::SetKnockBack( const int knockBackTime ) {
1693 if ( current.movementTime ) {
1696 current.movementFlags |= PMF_TIME_KNOCKBACK;
1697 current.movementTime = knockBackTime;
1702 idPhysics_Player::SetDebugLevel
1705 void idPhysics_Player::SetDebugLevel( bool set ) {
1711 idPhysics_Player::Evaluate
1714 bool idPhysics_Player::Evaluate( int timeStepMSec, int endTimeMSec ) {
1715 idVec3 masterOrigin, oldOrigin;
1718 waterLevel = WATERLEVEL_NONE;
1720 oldOrigin = current.origin;
1722 clipModel->Unlink();
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;
1736 ActivateContactEntities();
1738 idPhysics_Player::MovePlayer( timeStepMSec );
1740 clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
1742 if ( IsOutsideWorld() ) {
1743 gameLocal.Warning( "clip model outside world bounds for entity '%s' at (%s)", self->name.c_str(), current.origin.ToString(0) );
1746 return true; //( current.origin != oldOrigin );
1751 idPhysics_Player::UpdateTime
1754 void idPhysics_Player::UpdateTime( int endTimeMSec ) {
1759 idPhysics_Player::GetTime
1762 int idPhysics_Player::GetTime( void ) const {
1763 return gameLocal.time;
1768 idPhysics_Player::GetImpactInfo
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;
1780 idPhysics_Player::ApplyImpulse
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;
1791 idPhysics_Player::IsAtRest
1794 bool idPhysics_Player::IsAtRest( void ) const {
1800 idPhysics_Player::GetRestStartTime
1803 int idPhysics_Player::GetRestStartTime( void ) const {
1809 idPhysics_Player::SaveState
1812 void idPhysics_Player::SaveState( void ) {
1818 idPhysics_Player::RestoreState
1821 void idPhysics_Player::RestoreState( void ) {
1824 clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
1831 idPhysics_Player::SetOrigin
1834 void idPhysics_Player::SetOrigin( const idVec3 &newOrigin, int id ) {
1835 idVec3 masterOrigin;
1838 current.localOrigin = newOrigin;
1839 if ( masterEntity ) {
1840 self->GetMasterPosition( masterOrigin, masterAxis );
1841 current.origin = masterOrigin + newOrigin * masterAxis;
1844 current.origin = newOrigin;
1847 clipModel->Link( gameLocal.clip, self, 0, newOrigin, clipModel->GetAxis() );
1852 idPhysics_Player::GetOrigin
1855 const idVec3 & idPhysics_Player::PlayerGetOrigin( void ) const {
1856 return current.origin;
1861 idPhysics_Player::SetAxis
1864 void idPhysics_Player::SetAxis( const idMat3 &newAxis, int id ) {
1865 clipModel->Link( gameLocal.clip, self, 0, clipModel->GetOrigin(), newAxis );
1870 idPhysics_Player::Translate
1873 void idPhysics_Player::Translate( const idVec3 &translation, int id ) {
1875 current.localOrigin += translation;
1876 current.origin += translation;
1878 clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
1883 idPhysics_Player::Rotate
1886 void idPhysics_Player::Rotate( const idRotation &rotation, int id ) {
1887 idVec3 masterOrigin;
1890 current.origin *= rotation;
1891 if ( masterEntity ) {
1892 self->GetMasterPosition( masterOrigin, masterAxis );
1893 current.localOrigin = ( current.origin - masterOrigin ) * masterAxis.Transpose();
1896 current.localOrigin = current.origin;
1899 clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() * rotation.ToMat3() );
1904 idPhysics_Player::SetLinearVelocity
1907 void idPhysics_Player::SetLinearVelocity( const idVec3 &newLinearVelocity, int id ) {
1908 current.velocity = newLinearVelocity;
1913 idPhysics_Player::GetLinearVelocity
1916 const idVec3 &idPhysics_Player::GetLinearVelocity( int id ) const {
1917 return current.velocity;
1922 idPhysics_Player::SetPushed
1925 void idPhysics_Player::SetPushed( int deltaTime ) {
1929 // velocity with which the player is pushed
1930 velocity = ( current.origin - saved.origin ) / ( deltaTime * idMath::M_MS2SEC );
1932 // remove any downward push velocity
1933 d = velocity * gravityNormal;
1935 velocity -= d * gravityNormal;
1938 current.pushVelocity += velocity;
1943 idPhysics_Player::GetPushedLinearVelocity
1946 const idVec3 &idPhysics_Player::GetPushedLinearVelocity( const int id ) const {
1947 return current.pushVelocity;
1952 idPhysics_Player::ClearPushedVelocity
1955 void idPhysics_Player::ClearPushedVelocity( void ) {
1956 current.pushVelocity.Zero();
1961 idPhysics_Player::SetMaster
1963 the binding is never orientated
1966 void idPhysics_Player::SetMaster( idEntity *master, const bool orientated ) {
1967 idVec3 masterOrigin;
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();
1981 if ( masterEntity ) {
1982 masterEntity = NULL;
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;
1996 idPhysics_Player::WriteToSnapshot
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 );
2020 idPhysics_Player::ReadFromSnapshot
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 );
2042 clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );