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