DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Physics_Player.cpp (57784B)


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