2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
29 #include "../../idlib/precompiled.h"
32 #include "../Game_local.h"
34 CLASS_DECLARATION( idPhysics_Actor, idPhysics_Monster )
37 const float OVERCLIP = 1.001f;
41 idPhysics_Monster::CheckGround
44 void idPhysics_Monster::CheckGround( monsterPState_t &state ) {
48 if ( gravityNormal == vec3_zero ) {
49 state.onGround = false;
50 groundEntityPtr = NULL;
54 down = state.origin + gravityNormal * CONTACT_EPSILON;
55 gameLocal.clip.Translation( groundTrace, state.origin, down, clipModel, clipModel->GetAxis(), clipMask, self );
57 if ( groundTrace.fraction == 1.0f ) {
58 state.onGround = false;
59 groundEntityPtr = NULL;
63 groundEntityPtr = gameLocal.entities[ groundTrace.c.entityNum ];
65 if ( ( groundTrace.c.normal * -gravityNormal ) < minFloorCosine ) {
66 state.onGround = false;
70 state.onGround = true;
72 // let the entity know about the collision
73 self->Collide( groundTrace, state.velocity );
75 // apply impact to a non world floor entity
76 if ( groundTrace.c.entityNum != ENTITYNUM_WORLD && groundEntityPtr.GetEntity() ) {
78 groundEntityPtr.GetEntity()->GetImpactInfo( self, groundTrace.c.id, groundTrace.c.point, &info );
79 if ( info.invMass != 0.0f ) {
80 groundEntityPtr.GetEntity()->ApplyImpulse( self, 0, groundTrace.c.point, state.velocity / ( info.invMass * 10.0f ) );
87 idPhysics_Monster::SlideMove
90 monsterMoveResult_t idPhysics_Monster::SlideMove( idVec3 &start, idVec3 &velocity, const idVec3 &delta ) {
95 blockingEntity = NULL;
97 for( i = 0; i < 3; i++ ) {
98 gameLocal.clip.Translation( tr, start, start + move, clipModel, clipModel->GetAxis(), clipMask, self );
102 if ( tr.fraction == 1.0f ) {
109 if ( tr.c.entityNum != ENTITYNUM_NONE ) {
110 blockingEntity = gameLocal.entities[ tr.c.entityNum ];
113 // clip the movement delta and velocity
114 move.ProjectOntoPlane( tr.c.normal, OVERCLIP );
115 velocity.ProjectOntoPlane( tr.c.normal, OVERCLIP );
122 =====================
123 idPhysics_Monster::StepMove
125 move start into the delta direction
126 the velocity is clipped conform any collisions
127 =====================
129 monsterMoveResult_t idPhysics_Monster::StepMove( idVec3 &start, idVec3 &velocity, const idVec3 &delta ) {
131 idVec3 up, down, noStepPos, noStepVel, stepPos, stepVel;
132 monsterMoveResult_t result1, result2;
136 if ( delta == vec3_origin ) {
140 // try to move without stepping up
142 noStepVel = velocity;
143 result1 = SlideMove( noStepPos, noStepVel, delta );
144 if ( result1 == MM_OK ) {
145 velocity = noStepVel;
146 if ( gravityNormal == vec3_zero ) {
151 // try to step down so that we walk down slopes and stairs at a normal rate
152 down = noStepPos + gravityNormal * maxStepHeight;
153 gameLocal.clip.Translation( tr, noStepPos, down, clipModel, clipModel->GetAxis(), clipMask, self );
154 if ( tr.fraction < 1.0f ) {
163 if ( blockingEntity && blockingEntity->IsType( idActor::Type ) ) {
164 // try to step down in case walking into an actor while going down steps
165 down = noStepPos + gravityNormal * maxStepHeight;
166 gameLocal.clip.Translation( tr, noStepPos, down, clipModel, clipModel->GetAxis(), clipMask, self );
168 velocity = noStepVel;
172 if ( gravityNormal == vec3_zero ) {
177 up = start - gravityNormal * maxStepHeight;
178 gameLocal.clip.Translation( tr, start, up, clipModel, clipModel->GetAxis(), clipMask, self );
179 if ( tr.fraction == 0.0f ) {
181 velocity = noStepVel;
185 // try to move at the stepped up position
188 result2 = SlideMove( stepPos, stepVel, delta );
189 if ( result2 == MM_BLOCKED ) {
191 velocity = noStepVel;
196 down = stepPos + gravityNormal * maxStepHeight;
197 gameLocal.clip.Translation( tr, stepPos, down, clipModel, clipModel->GetAxis(), clipMask, self );
200 // if the move is further without stepping up, or the slope is too steap, don't step up
201 nostepdist = ( noStepPos - start ).LengthSqr();
202 stepdist = ( stepPos - start ).LengthSqr();
203 if ( ( nostepdist >= stepdist ) || ( ( tr.c.normal * -gravityNormal ) < minFloorCosine ) ) {
205 velocity = noStepVel;
217 idPhysics_Monster::Activate
220 void idPhysics_Monster::Activate( void ) {
222 self->BecomeActive( TH_PHYSICS );
227 idPhysics_Monster::Rest
230 void idPhysics_Monster::Rest( void ) {
231 current.atRest = gameLocal.time;
232 current.velocity.Zero();
233 self->BecomeInactive( TH_PHYSICS );
238 idPhysics_Monster::PutToRest
241 void idPhysics_Monster::PutToRest( void ) {
247 idPhysics_Monster::idPhysics_Monster
250 idPhysics_Monster::idPhysics_Monster( void ) {
252 memset( ¤t, 0, sizeof( current ) );
257 maxStepHeight = 18.0f;
258 minFloorCosine = 0.7f;
260 forceDeltaMove = false;
262 useVelocityMove = false;
264 blockingEntity = NULL;
269 idPhysics_Monster_SavePState
272 void idPhysics_Monster_SavePState( idSaveGame *savefile, const monsterPState_t &state ) {
273 savefile->WriteVec3( state.origin );
274 savefile->WriteVec3( state.velocity );
275 savefile->WriteVec3( state.localOrigin );
276 savefile->WriteVec3( state.pushVelocity );
277 savefile->WriteBool( state.onGround );
278 savefile->WriteInt( state.atRest );
283 idPhysics_Monster_RestorePState
286 void idPhysics_Monster_RestorePState( idRestoreGame *savefile, monsterPState_t &state ) {
287 savefile->ReadVec3( state.origin );
288 savefile->ReadVec3( state.velocity );
289 savefile->ReadVec3( state.localOrigin );
290 savefile->ReadVec3( state.pushVelocity );
291 savefile->ReadBool( state.onGround );
292 savefile->ReadInt( state.atRest );
297 idPhysics_Monster::Save
300 void idPhysics_Monster::Save( idSaveGame *savefile ) const {
302 idPhysics_Monster_SavePState( savefile, current );
303 idPhysics_Monster_SavePState( savefile, saved );
305 savefile->WriteFloat( maxStepHeight );
306 savefile->WriteFloat( minFloorCosine );
307 savefile->WriteVec3( delta );
309 savefile->WriteBool( forceDeltaMove );
310 savefile->WriteBool( fly );
311 savefile->WriteBool( useVelocityMove );
312 savefile->WriteBool( noImpact );
314 savefile->WriteInt( (int)moveResult );
315 savefile->WriteObject( blockingEntity );
320 idPhysics_Monster::Restore
323 void idPhysics_Monster::Restore( idRestoreGame *savefile ) {
325 idPhysics_Monster_RestorePState( savefile, current );
326 idPhysics_Monster_RestorePState( savefile, saved );
328 savefile->ReadFloat( maxStepHeight );
329 savefile->ReadFloat( minFloorCosine );
330 savefile->ReadVec3( delta );
332 savefile->ReadBool( forceDeltaMove );
333 savefile->ReadBool( fly );
334 savefile->ReadBool( useVelocityMove );
335 savefile->ReadBool( noImpact );
337 savefile->ReadInt( (int &)moveResult );
338 savefile->ReadObject( reinterpret_cast<idClass *&>( blockingEntity ) );
343 idPhysics_Monster::SetDelta
346 void idPhysics_Monster::SetDelta( const idVec3 &d ) {
348 if ( delta != vec3_origin ) {
355 idPhysics_Monster::SetMaxStepHeight
358 void idPhysics_Monster::SetMaxStepHeight( const float newMaxStepHeight ) {
359 maxStepHeight = newMaxStepHeight;
364 idPhysics_Monster::GetMaxStepHeight
367 float idPhysics_Monster::GetMaxStepHeight( void ) const {
368 return maxStepHeight;
373 idPhysics_Monster::OnGround
376 bool idPhysics_Monster::OnGround( void ) const {
377 return current.onGround;
382 idPhysics_Monster::GetSlideMoveEntity
385 idEntity *idPhysics_Monster::GetSlideMoveEntity( void ) const {
386 return blockingEntity;
391 idPhysics_Monster::GetMoveResult
394 monsterMoveResult_t idPhysics_Monster::GetMoveResult( void ) const {
400 idPhysics_Monster::ForceDeltaMove
403 void idPhysics_Monster::ForceDeltaMove( bool force ) {
404 forceDeltaMove = force;
409 idPhysics_Monster::UseFlyMove
412 void idPhysics_Monster::UseFlyMove( bool force ) {
418 idPhysics_Monster::UseVelocityMove
421 void idPhysics_Monster::UseVelocityMove( bool force ) {
422 useVelocityMove = force;
427 idPhysics_Monster::EnableImpact
430 void idPhysics_Monster::EnableImpact( void ) {
436 idPhysics_Monster::DisableImpact
439 void idPhysics_Monster::DisableImpact( void ) {
445 idPhysics_Monster::Evaluate
448 bool idPhysics_Monster::Evaluate( int timeStepMSec, int endTimeMSec ) {
449 idVec3 masterOrigin, oldOrigin;
453 timeStep = MS2SEC( timeStepMSec );
456 blockingEntity = NULL;
457 oldOrigin = current.origin;
459 // if bound to a master
460 if ( masterEntity ) {
461 self->GetMasterPosition( masterOrigin, masterAxis );
462 current.origin = masterOrigin + current.localOrigin * masterAxis;
463 clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
464 current.velocity = ( current.origin - oldOrigin ) / timeStep;
465 masterDeltaYaw = masterYaw;
466 masterYaw = masterAxis[0].ToYaw();
467 masterDeltaYaw = masterYaw - masterDeltaYaw;
471 // if the monster is at rest
472 if ( current.atRest >= 0 ) {
476 ActivateContactEntities();
478 // move the monster velocity into the frame of a pusher
479 current.velocity -= current.pushVelocity;
483 // check if on the ground
484 idPhysics_Monster::CheckGround( current );
486 // if not on the ground or moving upwards
488 if ( gravityNormal != vec3_zero ) {
489 upspeed = -( current.velocity * gravityNormal );
491 upspeed = current.velocity.z;
493 if ( fly || ( !forceDeltaMove && ( !current.onGround || upspeed > 1.0f ) ) ) {
494 if ( upspeed < 0.0f ) {
495 moveResult = MM_FALLING;
498 current.onGround = false;
501 delta = current.velocity * timeStep;
502 if ( delta != vec3_origin ) {
503 moveResult = idPhysics_Monster::SlideMove( current.origin, current.velocity, delta );
508 current.velocity += gravityVector * timeStep;
511 if ( useVelocityMove ) {
512 delta = current.velocity * timeStep;
514 current.velocity = delta / timeStep;
517 current.velocity -= ( current.velocity * gravityNormal ) * gravityNormal;
519 if ( delta == vec3_origin ) {
522 // try moving into the desired direction
523 moveResult = idPhysics_Monster::StepMove( current.origin, current.velocity, delta );
528 clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
530 // get all the ground contacts
533 // move the monster velocity back into the world frame
534 current.velocity += current.pushVelocity;
535 current.pushVelocity.Zero();
537 if ( IsOutsideWorld() ) {
538 gameLocal.Warning( "clip model outside world bounds for entity '%s' at (%s)", self->name.c_str(), current.origin.ToString(0) );
542 return ( current.origin != oldOrigin );
547 idPhysics_Monster::UpdateTime
550 void idPhysics_Monster::UpdateTime( int endTimeMSec ) {
555 idPhysics_Monster::GetTime
558 int idPhysics_Monster::GetTime( void ) const {
559 return gameLocal.time;
564 idPhysics_Monster::GetImpactInfo
567 void idPhysics_Monster::GetImpactInfo( const int id, const idVec3 &point, impactInfo_t *info ) const {
568 info->invMass = invMass;
569 info->invInertiaTensor.Zero();
570 info->position.Zero();
571 info->velocity = current.velocity;
576 idPhysics_Monster::ApplyImpulse
579 void idPhysics_Monster::ApplyImpulse( const int id, const idVec3 &point, const idVec3 &impulse ) {
583 current.velocity += impulse * invMass;
589 idPhysics_Monster::IsAtRest
592 bool idPhysics_Monster::IsAtRest( void ) const {
593 return current.atRest >= 0;
598 idPhysics_Monster::GetRestStartTime
601 int idPhysics_Monster::GetRestStartTime( void ) const {
602 return current.atRest;
607 idPhysics_Monster::SaveState
610 void idPhysics_Monster::SaveState( void ) {
616 idPhysics_Monster::RestoreState
619 void idPhysics_Monster::RestoreState( void ) {
622 clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
629 idPhysics_Player::SetOrigin
632 void idPhysics_Monster::SetOrigin( const idVec3 &newOrigin, int id ) {
636 current.localOrigin = newOrigin;
637 if ( masterEntity ) {
638 self->GetMasterPosition( masterOrigin, masterAxis );
639 current.origin = masterOrigin + newOrigin * masterAxis;
642 current.origin = newOrigin;
644 clipModel->Link( gameLocal.clip, self, 0, newOrigin, clipModel->GetAxis() );
650 idPhysics_Player::SetAxis
653 void idPhysics_Monster::SetAxis( const idMat3 &newAxis, int id ) {
654 clipModel->Link( gameLocal.clip, self, 0, clipModel->GetOrigin(), newAxis );
660 idPhysics_Monster::Translate
663 void idPhysics_Monster::Translate( const idVec3 &translation, int id ) {
665 current.localOrigin += translation;
666 current.origin += translation;
667 clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() );
673 idPhysics_Monster::Rotate
676 void idPhysics_Monster::Rotate( const idRotation &rotation, int id ) {
680 current.origin *= rotation;
681 if ( masterEntity ) {
682 self->GetMasterPosition( masterOrigin, masterAxis );
683 current.localOrigin = ( current.origin - masterOrigin ) * masterAxis.Transpose();
686 current.localOrigin = current.origin;
688 clipModel->Link( gameLocal.clip, self, 0, current.origin, clipModel->GetAxis() * rotation.ToMat3() );
694 idPhysics_Monster::SetLinearVelocity
697 void idPhysics_Monster::SetLinearVelocity( const idVec3 &newLinearVelocity, int id ) {
698 current.velocity = newLinearVelocity;
704 idPhysics_Monster::GetLinearVelocity
707 const idVec3 &idPhysics_Monster::GetLinearVelocity( int id ) const {
708 return current.velocity;
713 idPhysics_Monster::SetPushed
716 void idPhysics_Monster::SetPushed( int deltaTime ) {
717 // velocity with which the monster is pushed
718 current.pushVelocity += ( current.origin - saved.origin ) / ( deltaTime * idMath::M_MS2SEC );
723 idPhysics_Monster::GetPushedLinearVelocity
726 const idVec3 &idPhysics_Monster::GetPushedLinearVelocity( const int id ) const {
727 return current.pushVelocity;
732 idPhysics_Monster::SetMaster
734 the binding is never orientated
737 void idPhysics_Monster::SetMaster( idEntity *master, const bool orientated ) {
742 if ( !masterEntity ) {
743 // transform from world space to master space
744 self->GetMasterPosition( masterOrigin, masterAxis );
745 current.localOrigin = ( current.origin - masterOrigin ) * masterAxis.Transpose();
746 masterEntity = master;
747 masterYaw = masterAxis[0].ToYaw();
752 if ( masterEntity ) {
759 const float MONSTER_VELOCITY_MAX = 4000;
760 const int MONSTER_VELOCITY_TOTAL_BITS = 16;
761 const int MONSTER_VELOCITY_EXPONENT_BITS = idMath::BitsForInteger( idMath::BitsForFloat( MONSTER_VELOCITY_MAX ) ) + 1;
762 const int MONSTER_VELOCITY_MANTISSA_BITS = MONSTER_VELOCITY_TOTAL_BITS - 1 - MONSTER_VELOCITY_EXPONENT_BITS;
766 idPhysics_Monster::WriteToSnapshot
769 void idPhysics_Monster::WriteToSnapshot( idBitMsgDelta &msg ) const {
770 msg.WriteFloat( current.origin[0] );
771 msg.WriteFloat( current.origin[1] );
772 msg.WriteFloat( current.origin[2] );
773 msg.WriteFloat( current.velocity[0], MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
774 msg.WriteFloat( current.velocity[1], MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
775 msg.WriteFloat( current.velocity[2], MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
776 msg.WriteDeltaFloat( current.origin[0], current.localOrigin[0] );
777 msg.WriteDeltaFloat( current.origin[1], current.localOrigin[1] );
778 msg.WriteDeltaFloat( current.origin[2], current.localOrigin[2] );
779 msg.WriteDeltaFloat( 0.0f, current.pushVelocity[0], MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
780 msg.WriteDeltaFloat( 0.0f, current.pushVelocity[1], MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
781 msg.WriteDeltaFloat( 0.0f, current.pushVelocity[2], MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
782 msg.WriteLong( current.atRest );
783 msg.WriteBits( current.onGround, 1 );
788 idPhysics_Monster::ReadFromSnapshot
791 void idPhysics_Monster::ReadFromSnapshot( const idBitMsgDelta &msg ) {
792 current.origin[0] = msg.ReadFloat();
793 current.origin[1] = msg.ReadFloat();
794 current.origin[2] = msg.ReadFloat();
795 current.velocity[0] = msg.ReadFloat( MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
796 current.velocity[1] = msg.ReadFloat( MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
797 current.velocity[2] = msg.ReadFloat( MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
798 current.localOrigin[0] = msg.ReadDeltaFloat( current.origin[0] );
799 current.localOrigin[1] = msg.ReadDeltaFloat( current.origin[1] );
800 current.localOrigin[2] = msg.ReadDeltaFloat( current.origin[2] );
801 current.pushVelocity[0] = msg.ReadDeltaFloat( 0.0f, MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
802 current.pushVelocity[1] = msg.ReadDeltaFloat( 0.0f, MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
803 current.pushVelocity[2] = msg.ReadDeltaFloat( 0.0f, MONSTER_VELOCITY_EXPONENT_BITS, MONSTER_VELOCITY_MANTISSA_BITS );
804 current.atRest = msg.ReadLong();
805 current.onGround = msg.ReadBits( 1 ) != 0;