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( ¤t, 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