DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Push.cpp (40396B)


      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 
     35 /*
     36 ============
     37 idPush::InitSavingPushedEntityPositions
     38 ============
     39 */
     40 void idPush::InitSavingPushedEntityPositions() {
     41 	numPushed = 0;
     42 }
     43 
     44 /*
     45 ============
     46 idPush::SaveEntityPosition
     47 ============
     48 */
     49 void idPush::SaveEntityPosition( idEntity *ent ) {
     50 	int i;
     51 
     52 	// if already saved the physics state for this entity
     53 	for ( i = 0; i < numPushed; i++ ) {
     54 		if ( pushed[i].ent == ent ) {
     55 			return;
     56 		}
     57 	}
     58 
     59 	// don't overflow
     60 	if ( numPushed >= MAX_GENTITIES ) {
     61 		gameLocal.Error( "more than MAX_GENTITIES pushed entities" );
     62 		return;
     63 	}
     64 
     65 	pushed[numPushed].ent = ent;
     66 
     67 	// if the entity is an actor
     68 	if ( ent->IsType( idActor::Type ) ) {
     69 		// save the delta view angles
     70 		pushed[numPushed].deltaViewAngles = static_cast<idActor *>(ent)->GetDeltaViewAngles();
     71 	}
     72 
     73 	// save the physics state
     74 	ent->GetPhysics()->SaveState();
     75 
     76 	numPushed++;
     77 }
     78 
     79 /*
     80 ============
     81 idPush::RestorePushedEntityPositions
     82 ============
     83 */
     84 void idPush::RestorePushedEntityPositions() {
     85 	int i;
     86 
     87 	for ( i = 0; i < numPushed; i++ ) {
     88 
     89 		// if the entity is an actor
     90 		if ( pushed[i].ent->IsType( idActor::Type ) ) {
     91 			// set back the delta view angles
     92 			static_cast<idActor *>(pushed[i].ent)->SetDeltaViewAngles( pushed[i].deltaViewAngles );
     93 		}
     94 
     95 		// restore the physics state
     96 		pushed[i].ent->GetPhysics()->RestoreState();
     97 	}
     98 }
     99 
    100 /*
    101 ============
    102 idPush::RotateEntityToAxial
    103 ============
    104 */
    105 bool idPush::RotateEntityToAxial( idEntity *ent, idVec3 rotationPoint ) {
    106 	int i;
    107 	trace_t trace;
    108 	idRotation rotation;
    109 	idMat3 axis;
    110 	idPhysics *physics;
    111 
    112 	physics = ent->GetPhysics();
    113 	axis = physics->GetAxis();
    114 	if ( !axis.IsRotated() ) {
    115 		return true;
    116 	}
    117 	// try to rotate the bbox back to axial with at most four rotations
    118 	for ( i = 0; i < 4; i++ ) {
    119 		axis = physics->GetAxis();
    120 		rotation = axis.ToRotation();
    121 		rotation.Scale( -1 );
    122 		rotation.SetOrigin( rotationPoint );
    123 		// tiny float numbers in the clip axis, this can get the entity stuck
    124 		if ( rotation.GetAngle() == 0.0f ) {
    125 			physics->SetAxis( mat3_identity );
    126 			return true;
    127 		}
    128 		//
    129 		ent->GetPhysics()->ClipRotation( trace, rotation, NULL );
    130 		// if the full rotation is possible
    131 		if ( trace.fraction >= 1.0f ) {
    132 			// set bbox in final axial position
    133 			physics->SetOrigin( trace.endpos );
    134 			physics->SetAxis( mat3_identity );
    135 			return true;
    136 		}
    137 		// if partial rotation was possible
    138 		else if ( trace.fraction > 0.0f ) {
    139 			// partial rotation
    140 			physics->SetOrigin( trace.endpos );
    141 			physics->SetAxis( trace.endAxis );
    142 		}
    143 		// next rotate around collision point
    144 		rotationPoint = trace.c.point;
    145 	}
    146 	return false;
    147 }
    148 
    149 #ifdef NEW_PUSH
    150 
    151 /*
    152 ============
    153 idPush::CanPushEntity
    154 ============
    155 */
    156 bool idPush::CanPushEntity( idEntity *ent, idEntity *pusher, idEntity *initialPusher, const int flags ) {
    157 
    158 	// if the physics object is not pushable
    159 	if ( !ent->GetPhysics()->IsPushable() ) {
    160 		return false;
    161 	}
    162 
    163 	// if the entity doesn't clip with this pusher
    164 	if ( !( ent->GetPhysics()->GetClipMask() & pusher->GetPhysics()->GetContents() ) ) {
    165 		return false;
    166 	}
    167 
    168 	// don't push players in noclip mode
    169 	if ( ent->client && ent->client->noclip ) {
    170 		return false;
    171 	}
    172 
    173 	// if we should only push idMoveable entities
    174 	if ( ( flags & PUSHFL_ONLYMOVEABLE ) && !ent->IsType( idMoveable::Type ) ) {
    175 		return false;
    176 	}
    177 
    178 	// if we shouldn't push entities the original pusher rests upon
    179 	if ( flags & PUSHFL_NOGROUNDENTITIES ) {
    180 		if ( initialPusher->GetPhysics()->IsGroundEntity( ent->entityNumber ) ) {
    181 			return false;
    182 		}
    183 	}
    184 
    185 	return true;
    186 }
    187 
    188 /*
    189 ============
    190 idPush::AddEntityToPushedGroup
    191 ============
    192 */
    193 void idPush::AddEntityToPushedGroup( idEntity *ent, float fraction, bool groundContact ) {
    194 	int i, j;
    195 
    196 	for ( i = 0; i < pushedGroupSize; i++ ) {
    197 		if ( ent == pushedGroup[i].ent ) {
    198 			if ( fraction > pushedGroup[i].fraction ) {
    199 				pushedGroup[i].fraction = fraction;
    200 				pushedGroup[i].groundContact &= groundContact;
    201 				pushedGroup[i].test = true;
    202 			}
    203 			return;
    204 		}
    205 		else if ( fraction > pushedGroup[i].fraction ) {
    206 			for ( j = pushedGroupSize; j > i; j-- ) {
    207 				pushedGroup[j] = pushedGroup[j-1];
    208 			}
    209 			break;
    210 		}
    211 	}
    212 
    213 	// put the entity in the group
    214 	pushedGroupSize++;
    215 	pushedGroup[i].ent = ent;
    216 	pushedGroup[i].fraction = fraction;
    217 	pushedGroup[i].groundContact = groundContact;
    218 	pushedGroup[i].test = true;
    219 
    220 	// remove any further occurances of the same entity in the group
    221 	for ( i++; i < pushedGroupSize; i++ ) {
    222 		if ( ent == pushedGroup[i].ent ) {
    223 			for ( j = i+1; j < pushedGroupSize; j++ ) {
    224 				pushedGroup[j-1] = pushedGroup[j];
    225 			}
    226 			pushedGroupSize--;
    227 			break;
    228 		}
    229 	}
    230 }
    231 
    232 /*
    233 ============
    234 idPush::IsFullyPushed
    235 ============
    236 */
    237 bool idPush::IsFullyPushed( idEntity *ent ) {
    238 	int i;
    239 
    240 	for ( i = 0; i < pushedGroupSize; i++ ) {
    241 		if ( pushedGroup[i].fraction < 1.0f ) {
    242 			return false;
    243 		}
    244 		if ( ent == pushedGroup[i].ent ) {
    245 			return true;
    246 		}
    247 	}
    248 	return false;
    249 }
    250 
    251 /*
    252 ============
    253 idPush::ClipTranslationAgainstPusher
    254 ============
    255 */
    256 bool idPush::ClipTranslationAgainstPusher( trace_t &results, idEntity *ent, idEntity *pusher, const idVec3 &translation ) {
    257 	int i, n;
    258 	trace_t t;
    259 
    260 	results.fraction = 1.0f;
    261 
    262 	n = pusher->GetPhysics()->GetNumClipModels();
    263 	for ( i = 0; i < n; i++ ) {
    264 		ent->GetPhysics()->ClipTranslation( t, translation, pusher->GetPhysics()->GetClipModel( i ) );
    265 		if ( t.fraction < results.fraction ) {
    266 			results = t;
    267 		}
    268 	}
    269 	return ( results.fraction < 1.0f );
    270 }
    271 
    272 /*
    273 ============
    274 idPush::GetPushableEntitiesForTranslation
    275 ============
    276 */
    277 int idPush::GetPushableEntitiesForTranslation( idEntity *pusher, idEntity *initialPusher, const int flags,
    278 											const idVec3 &translation, idEntity *entityList[], int maxEntities ) {
    279 	int i, n, l;
    280 	idBounds bounds, pushBounds;
    281 	idPhysics *physics;
    282 
    283 	// get bounds for the whole movement
    284 	physics = pusher->GetPhysics();
    285 	bounds = physics->GetBounds();
    286 	pushBounds.FromBoundsTranslation( bounds, physics->GetOrigin(), physics->GetAxis(), translation );
    287 	pushBounds.ExpandSelf( 2.0f );
    288 
    289 	// get all entities within the push bounds
    290 	n = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES );
    291 
    292 	for ( l = i = 0; i < n; i++ ) {
    293 		if ( entityList[i] == pusher || entityList[i] == initialPusher ) {
    294 			continue;
    295 		}
    296 		if ( CanPushEntity( entityList[i], pusher, initialPusher, flags ) ) {
    297 			entityList[l++] = entityList[i];
    298 		}
    299 	}
    300 
    301 	return l;
    302 }
    303 
    304 /*
    305 ============
    306 idPush::ClipTranslationalPush
    307 
    308   Try to push other entities by translating the given entity.
    309 ============
    310 */
    311 float idPush::ClipTranslationalPush( trace_t &results, idEntity *pusher, const int flags,
    312 										const idVec3 &newOrigin, const idVec3 &translation ) {
    313 	int i, j, numListedEntities;
    314 	idEntity *curPusher, *ent, *entityList[ MAX_GENTITIES ];
    315 	float fraction;
    316 	bool groundContact, blocked = false;
    317 	float totalMass;
    318 	trace_t trace;
    319 	idVec3 realTranslation, partialTranslation;
    320 
    321 	totalMass = 0.0f;
    322 
    323 	results.fraction = 1.0f;
    324 	results.endpos = newOrigin;
    325 	results.endAxis = pusher->GetPhysics()->GetAxis();
    326 	memset( results.c, 0, sizeof( results.c ) );
    327 
    328 	if ( translation == vec3_origin ) {
    329 		return totalMass;
    330 	}
    331 
    332 	// clip against all non-pushable physics objects
    333 	if ( flags & PUSHFL_CLIP ) {
    334 
    335 		numListedEntities = GetPushableEntitiesForTranslation( pusher, pusher, flags, translation, entityList, MAX_GENTITIES );
    336 		// disable pushable entities for collision detection
    337 		for ( i = 0; i < numListedEntities; i++ ) {
    338 			entityList[i]->GetPhysics()->DisableClip();
    339 		}
    340 		// clip translation
    341 		pusher->GetPhysics()->ClipTranslation( results, translation, NULL );
    342 		// enable pushable entities
    343 		for ( i = 0; i < numListedEntities; i++ ) {
    344 			entityList[i]->GetPhysics()->EnableClip();
    345 		}
    346 		if ( results.fraction == 0.0f ) {
    347 			return totalMass;
    348 		}
    349 		realTranslation = results.fraction * translation;
    350 	}
    351 	else {
    352 		realTranslation = translation;
    353 	}
    354 
    355 	// put the pusher in the group of pushed physics objects
    356 	pushedGroup[0].ent = pusher;
    357 	pushedGroup[0].fraction = 1.0f;
    358 	pushedGroup[0].groundContact = true;
    359 	pushedGroup[0].test = true;
    360 	pushedGroupSize = 1;
    361 
    362 	// get all physics objects that need to be pushed
    363 	for ( i = 0; i < pushedGroupSize; ) {
    364 		if ( !pushedGroup[i].test ) {
    365 			i++;
    366 			continue;
    367 		}
    368 		pushedGroup[i].test = false;
    369 		curPusher = pushedGroup[i].ent;
    370 		fraction = pushedGroup[i].fraction;
    371 		groundContact = pushedGroup[i].groundContact;
    372 		i = 0;
    373 
    374 		numListedEntities = GetPushableEntitiesForTranslation( curPusher, pusher, flags, realTranslation, entityList, MAX_GENTITIES );
    375 
    376 		for ( j = 0; j < numListedEntities; j++ ) {
    377 			ent = entityList[ j ];
    378 
    379 			if ( IsFullyPushed( ent ) ) {
    380 				continue;
    381 			}
    382 
    383 			if ( !CanPushEntity( ent, curPusher, pusher, flags ) ) {
    384 				continue;
    385 			}
    386 
    387 			if ( ent->GetPhysics()->IsGroundEntity( curPusher->entityNumber ) ) {
    388 				AddEntityToPushedGroup( ent, 1.0f * fraction, false );
    389 			}
    390 			else if ( ClipTranslationAgainstPusher( trace, ent, curPusher, -fraction * realTranslation ) ) {
    391 				AddEntityToPushedGroup( ent, ( 1.0f - trace.fraction ) * fraction, groundContact );
    392 			}
    393 		}
    394 	}
    395 
    396 	// save physics states and disable physics objects for collision detection
    397 	for ( i = 0; i < pushedGroupSize; i++ ) {
    398 		SaveEntityPosition( pushedGroup[i].ent );
    399 		pushedGroup[i].ent->GetPhysics()->DisableClip();
    400 	}
    401 
    402 	// clip all pushed physics objects
    403 	for ( i = 1; i < pushedGroupSize; i++ ) {
    404 		partialTranslation = realTranslation * pushedGroup[i].fraction;
    405 
    406 		pushedGroup[i].ent->GetPhysics()->ClipTranslation( trace, partialTranslation, NULL );
    407 
    408 		if ( trace.fraction < 1.0f ) {
    409 			blocked = true;
    410 			break;
    411 		}
    412 	}
    413 
    414 	// enable all physics objects for collision detection
    415 	for ( i = 1; i < pushedGroupSize; i++ ) {
    416 		pushedGroup[i].ent->GetPhysics()->EnableClip();
    417 	}
    418 
    419 	// push all or nothing
    420 	if ( blocked ) {
    421 		if ( flags & PUSHFL_CLIP ) {
    422 			pusher->GetPhysics()->ClipTranslation( results, realTranslation, NULL );
    423 		}
    424 		else {
    425 			results.fraction = 0.0f;
    426 			results.endpos = pusher->GetPhysics()->GetOrigin();
    427 			results.endAxis = pusher->GetPhysics()->GetAxis();
    428 		}
    429 	}
    430 	else {
    431 		// translate all pushed physics objects
    432 		for ( i = 1; i < pushedGroupSize; i++ ) {
    433 			partialTranslation = realTranslation * pushedGroup[i].fraction;
    434 			pushedGroup[i].ent->GetPhysics()->Translate( partialTranslation );
    435 			totalMass += pushedGroup[i].ent->GetPhysics()->GetMass();
    436 		}
    437 		// translate the clip models of the pusher
    438 		for ( i = 0; i < pusher->GetPhysics()->GetNumClipModels(); i++ ) {
    439 			pusher->GetPhysics()->GetClipModel(i)->Translate( results.fraction * realTranslation );
    440 			pusher->GetPhysics()->GetClipModel(i)->Link( gameLocal.clip );
    441 		}
    442 	}
    443 
    444 	return totalMass;
    445 }
    446 
    447 /*
    448 ============
    449 idPush::ClipRotationAgainstPusher
    450 ============
    451 */
    452 bool idPush::ClipRotationAgainstPusher( trace_t &results, idEntity *ent, idEntity *pusher, const idRotation &rotation ) {
    453 	int i, n;
    454 	trace_t t;
    455 
    456 	results.fraction = 1.0f;
    457 
    458 	n = pusher->GetPhysics()->GetNumClipModels();
    459 	for ( i = 0; i < n; i++ ) {
    460 		ent->GetPhysics()->ClipRotation( t, rotation, pusher->GetPhysics()->GetClipModel( i ) );
    461 		if ( t.fraction < results.fraction ) {
    462 			results = t;
    463 		}
    464 	}
    465 	return ( results.fraction < 1.0f );
    466 }
    467 
    468 /*
    469 ============
    470 idPush::GetPushableEntitiesForRotation
    471 ============
    472 */
    473 int idPush::GetPushableEntitiesForRotation( idEntity *pusher, idEntity *initialPusher, const int flags,
    474 											const idRotation &rotation, idEntity *entityList[], int maxEntities ) {
    475 	int i, n, l;
    476 	idBounds bounds, pushBounds;
    477 	idPhysics *physics;
    478 
    479 	// get bounds for the whole movement
    480 	physics = pusher->GetPhysics();
    481 	bounds = physics->GetBounds();
    482 	pushBounds.FromBoundsRotation( bounds, physics->GetOrigin(), physics->GetAxis(), rotation );
    483 	pushBounds.ExpandSelf( 2.0f );
    484 
    485 	// get all entities within the push bounds
    486 	n = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES );
    487 
    488 	for ( l = i = 0; i < n; i++ ) {
    489 		if ( entityList[i] == pusher || entityList[i] == initialPusher ) {
    490 			continue;
    491 		}
    492 		if ( CanPushEntity( entityList[i], pusher, initialPusher, flags ) ) {
    493 			entityList[l++] = entityList[i];
    494 		}
    495 	}
    496 
    497 	return l;
    498 }
    499 
    500 /*
    501 ============
    502 idPush::ClipRotationalPush
    503 
    504   Try to push other entities by rotating the given entity.
    505 ============
    506 */
    507 float idPush::ClipRotationalPush( trace_t &results, idEntity *pusher, const int flags,
    508 									const idMat3 &newAxis, const idRotation &rotation ) {
    509 	int i, j, numListedEntities;
    510 	idEntity *curPusher, *ent, *entityList[ MAX_GENTITIES ];
    511 	float fraction;
    512 	bool groundContact, blocked = false;
    513 	float totalMass;
    514 	trace_t trace;
    515 	idRotation realRotation, partialRotation;
    516 	idMat3 oldAxis;
    517 
    518 	totalMass = 0.0f;
    519 
    520 	results.fraction = 1.0f;
    521 	results.endpos = pusher->GetPhysics()->GetOrigin();
    522 	results.endAxis = newAxis;
    523 	memset( results.c, 0, sizeof( results.c ) );
    524 
    525 	if ( !rotation.GetAngle() ) {
    526 		return totalMass;
    527 	}
    528 
    529 	// clip against all non-pushable physics objects
    530 	if ( flags & PUSHFL_CLIP ) {
    531 
    532 		numListedEntities = GetPushableEntitiesForRotation( pusher, pusher, flags, rotation, entityList, MAX_GENTITIES );
    533 		// disable pushable entities for collision detection
    534 		for ( i = 0; i < numListedEntities; i++ ) {
    535 			entityList[i]->GetPhysics()->DisableClip();
    536 		}
    537 		// clip rotation
    538 		pusher->GetPhysics()->ClipRotation( results, rotation, NULL );
    539 		// enable pushable entities
    540 		for ( i = 0; i < numListedEntities; i++ ) {
    541 			entityList[i]->GetPhysics()->EnableClip();
    542 		}
    543 		if ( results.fraction == 0.0f ) {
    544 			return totalMass;
    545 		}
    546 		realRotation = results.fraction * rotation;
    547 	}
    548 	else {
    549 		realRotation = rotation;
    550 	}
    551 
    552 	// put the pusher in the group of pushed physics objects
    553 	pushedGroup[0].ent = pusher;
    554 	pushedGroup[0].fraction = 1.0f;
    555 	pushedGroup[0].groundContact = true;
    556 	pushedGroup[0].test = true;
    557 	pushedGroupSize = 1;
    558 
    559 	// get all physics objects that need to be pushed
    560 	for ( i = 0; i < pushedGroupSize; ) {
    561 		if ( !pushedGroup[i].test ) {
    562 			i++;
    563 			continue;
    564 		}
    565 		pushedGroup[i].test = false;
    566 		curPusher = pushedGroup[i].ent;
    567 		fraction = pushedGroup[i].fraction;
    568 		groundContact = pushedGroup[i].groundContact;
    569 		i = 0;
    570 
    571 		numListedEntities = GetPushableEntitiesForRotation( curPusher, pusher, flags, realRotation, entityList, MAX_GENTITIES );
    572 
    573 		for ( j = 0; j < numListedEntities; j++ ) {
    574 			ent = entityList[ j ];
    575 
    576 			if ( IsFullyPushed( ent ) ) {
    577 				continue;
    578 			}
    579 
    580 			if ( ent->GetPhysics()->IsGroundEntity( curPusher->entityNumber ) ) {
    581 				AddEntityToPushedGroup( ent, 1.0f * fraction, false );
    582 			}
    583 			else if ( ClipRotationAgainstPusher( trace, ent, curPusher, -fraction * realRotation ) ) {
    584 				AddEntityToPushedGroup( ent, ( 1.0f - trace.fraction ) * fraction, groundContact );
    585 			}
    586 		}
    587 	}
    588 
    589 	// save physics states and disable physics objects for collision detection
    590 	for ( i = 1; i < pushedGroupSize; i++ ) {
    591 		SaveEntityPosition( pushedGroup[i].ent );
    592 		pushedGroup[i].ent->GetPhysics()->DisableClip();
    593 	}
    594 
    595 	// clip all pushed physics objects
    596 	for ( i = 1; i < pushedGroupSize; i++ ) {
    597 		partialRotation = realRotation * pushedGroup[i].fraction;
    598 
    599 		pushedGroup[i].ent->GetPhysics()->ClipRotation( trace, partialRotation, NULL );
    600 
    601 		if ( trace.fraction < 1.0f ) {
    602 			blocked = true;
    603 			break;
    604 		}
    605 	}
    606 
    607 	// enable all physics objects for collision detection
    608 	for ( i = 1; i < pushedGroupSize; i++ ) {
    609 		pushedGroup[i].ent->GetPhysics()->EnableClip();
    610 	}
    611 
    612 	// push all or nothing
    613 	if ( blocked ) {
    614 		if ( flags & PUSHFL_CLIP ) {
    615 			pusher->GetPhysics()->ClipRotation( results, realRotation, NULL );
    616 		}
    617 		else {
    618 			results.fraction = 0.0f;
    619 			results.endpos = pusher->GetPhysics()->GetOrigin();
    620 			results.endAxis = pusher->GetPhysics()->GetAxis();
    621 		}
    622 	}
    623 	else {
    624 		// rotate all pushed physics objects
    625 		for ( i = 1; i < pushedGroupSize; i++ ) {
    626 			partialRotation = realRotation * pushedGroup[i].fraction;
    627 			pushedGroup[i].ent->GetPhysics()->Rotate( partialRotation );
    628 			totalMass += pushedGroup[i].ent->GetPhysics()->GetMass();
    629 		}
    630 		// rotate the clip models of the pusher
    631 		for ( i = 0; i < pusher->GetPhysics()->GetNumClipModels(); i++ ) {
    632 			pusher->GetPhysics()->GetClipModel(i)->Rotate( realRotation );
    633 			pusher->GetPhysics()->GetClipModel(i)->Link( gameLocal.clip );
    634 			pusher->GetPhysics()->GetClipModel(i)->Enable();
    635 		}
    636 		// rotate any actors back to axial
    637 		for ( i = 1; i < pushedGroupSize; i++ ) {
    638 			// if the entity is using actor physics
    639 			if ( pushedGroup[i].ent->GetPhysics()->IsType( idPhysics_Actor::Type ) ) {
    640 
    641 				// rotate the collision model back to axial
    642 				if ( !RotateEntityToAxial( pushedGroup[i].ent, pushedGroup[i].ent->GetPhysics()->GetOrigin() ) ) {
    643 					// don't allow rotation if the bbox is no longer axial
    644 					results.fraction = 0.0f;
    645 					results.endpos = pusher->GetPhysics()->GetOrigin();
    646 					results.endAxis = pusher->GetPhysics()->GetAxis();
    647 				}
    648 			}
    649 		}
    650 	}
    651 
    652 	return totalMass;
    653 }
    654 
    655 #else /* !NEW_PUSH */
    656 
    657 enum {
    658 	PUSH_NO,			// not pushed
    659 	PUSH_OK,			// pushed ok
    660 	PUSH_BLOCKED		// blocked
    661 };
    662 
    663 /*
    664 ============
    665 idPush::ClipEntityRotation
    666 ============
    667 */
    668 void idPush::ClipEntityRotation( trace_t &trace, const idEntity *ent, const idClipModel *clipModel, idClipModel *skip, const idRotation &rotation ) {
    669 
    670 	if ( skip ) {
    671 		skip->Disable();
    672 	}
    673 
    674 	ent->GetPhysics()->ClipRotation( trace, rotation, clipModel );
    675 
    676 	if ( skip ) {
    677 		skip->Enable();
    678 	}
    679 }
    680 
    681 /*
    682 ============
    683 idPush::ClipEntityTranslation
    684 ============
    685 */
    686 void idPush::ClipEntityTranslation( trace_t &trace, const idEntity *ent, const idClipModel *clipModel, idClipModel *skip, const idVec3 &translation ) {
    687 
    688 	if ( skip ) {
    689 		skip->Disable();
    690 	}
    691 
    692 	ent->GetPhysics()->ClipTranslation( trace, translation, clipModel );
    693 
    694 	if ( skip ) {
    695 		skip->Enable();
    696 	}
    697 }
    698 
    699 /*
    700 ============
    701 idPush::TryRotatePushEntity
    702 ============
    703 */
    704 #ifdef _DEBUG
    705 //	#define ROTATIONAL_PUSH_DEBUG
    706 #endif
    707 
    708 int idPush::TryRotatePushEntity( trace_t &results, idEntity *check, idClipModel *clipModel, const int flags,
    709 											 const idMat3 &newAxis, const idRotation &rotation ) {
    710 	trace_t trace;
    711 	idVec3 rotationPoint;
    712 	idRotation newRotation;
    713 	float checkAngle;
    714 	idPhysics *physics;
    715 
    716 	physics = check->GetPhysics();
    717 
    718 #ifdef ROTATIONAL_PUSH_DEBUG
    719 	bool startsolid = false;
    720 	if ( physics->ClipContents( clipModel ) ) {
    721 		startsolid = true;
    722 	}
    723 #endif
    724 
    725 	results.fraction = 1.0f;
    726 	results.endpos = clipModel->GetOrigin();
    727 	results.endAxis = newAxis;
    728 	memset( &results.c, 0, sizeof( results.c ) );
    729 
    730 	// always pushed when standing on the pusher
    731 	if ( physics->IsGroundClipModel( clipModel->GetEntity()->entityNumber, clipModel->GetId() ) ) {
    732 		// rotate the entity colliding with all other entities except the pusher itself
    733 		ClipEntityRotation( trace, check, NULL, clipModel, rotation );
    734 		// if there is a collision
    735 		if ( trace.fraction < 1.0f ) {
    736 			// angle along which the entity is pushed
    737 			checkAngle = rotation.GetAngle() * trace.fraction;
    738 			// test if the entity can stay at it's partly pushed position by rotating
    739 			// the entity in reverse only colliding with pusher
    740 			newRotation.Set( rotation.GetOrigin(), rotation.GetVec(), -(rotation.GetAngle() - checkAngle) );
    741 			ClipEntityRotation( results, check, clipModel, NULL, newRotation );
    742 			// if there is a collision
    743 			if ( results.fraction < 1.0f ) {
    744 
    745 				// FIXME: try to push the blocking entity as well or try to slide along collision plane(s)?
    746 
    747 				results.c.normal = -results.c.normal;
    748 				results.c.dist = -results.c.dist;
    749 
    750 				// the entity will be crushed between the pusher and some other entity
    751 				return PUSH_BLOCKED;
    752 			}
    753 		}
    754 		else {
    755 			// angle along which the entity is pushed
    756 			checkAngle = rotation.GetAngle();
    757 		}
    758 		// point to rotate entity bbox around back to axial
    759 		rotationPoint = physics->GetOrigin();
    760 	}
    761 	else {
    762 		// rotate entity in reverse only colliding with pusher
    763 		newRotation = rotation;
    764 		newRotation.Scale( -1 );
    765 		//
    766 		ClipEntityRotation( results, check, clipModel, NULL, newRotation );
    767 		// if no collision with the pusher then the entity is not pushed by the pusher
    768 		if ( results.fraction >= 1.0f ) {
    769 #ifdef ROTATIONAL_PUSH_DEBUG
    770 			// set pusher into final position
    771 			clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), newAxis );
    772 			if ( physics->ClipContents( clipModel ) ) {
    773 				if ( !startsolid ) {
    774 					int bah = 1;
    775 				}
    776 			}
    777 #endif
    778 			return PUSH_NO;
    779 		}
    780 		// get point to rotate bbox around back to axial
    781 		rotationPoint = results.c.point;
    782 		// angle along which the entity will be pushed
    783 		checkAngle = rotation.GetAngle() * (1.0f - results.fraction);
    784 		// rotate the entity colliding with all other entities except the pusher itself
    785 		newRotation.Set( rotation.GetOrigin(), rotation.GetVec(), checkAngle );
    786 		ClipEntityRotation( trace, check, NULL, clipModel, newRotation );
    787 		// if there is a collision
    788 		if ( trace.fraction < 1.0f ) {
    789 
    790 			// FIXME: try to push the blocking entity as well or try to slide along collision plane(s)?
    791 
    792 			results.c.normal = -results.c.normal;
    793 			results.c.dist = -results.c.dist;
    794 
    795 			// the entity will be crushed between the pusher and some other entity
    796 			return PUSH_BLOCKED;
    797 		}
    798 	}
    799 
    800 	SaveEntityPosition( check );
    801 
    802 	newRotation.Set( rotation.GetOrigin(), rotation.GetVec(), checkAngle );
    803 	// NOTE:	this code prevents msvc 6.0 & 7.0 from screwing up the above code in
    804 	//			release builds moving less floats than it should
    805 	static float shit = checkAngle;
    806 
    807 	newRotation.RotatePoint( rotationPoint );
    808 
    809 	// rotate the entity
    810 	physics->Rotate( newRotation );
    811 
    812 	// set pusher into final position
    813 	clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), newAxis );
    814 
    815 #ifdef ROTATIONAL_PUSH_DEBUG
    816 	if ( physics->ClipContents( clipModel ) ) {
    817 		if ( !startsolid ) {
    818 			int bah = 1;
    819 		}
    820 	}
    821 #endif
    822 
    823 	// if the entity uses actor physics
    824 	if ( physics->IsType( idPhysics_Actor::Type ) ) {
    825 
    826 		// rotate the collision model back to axial
    827 		if ( !RotateEntityToAxial( check, rotationPoint ) ) {
    828 			// don't allow rotation if the bbox is no longer axial
    829 			return PUSH_BLOCKED;
    830 		}
    831 	}
    832 
    833 #ifdef ROTATIONAL_PUSH_DEBUG
    834 	if ( physics->ClipContents( clipModel ) ) {
    835 		if ( !startsolid ) {
    836 			int bah = 1;
    837 		}
    838 	}
    839 #endif
    840 
    841 	// if the entity is an actor using actor physics
    842 	if ( check->IsType( idActor::Type ) && physics->IsType( idPhysics_Actor::Type ) ) {
    843 
    844 		// if the entity is standing ontop of the pusher
    845 		if ( physics->IsGroundClipModel( clipModel->GetEntity()->entityNumber, clipModel->GetId() ) ) {
    846 			// rotate actor view
    847 			idActor *actor = static_cast<idActor *>(check);
    848 			idAngles delta = actor->GetDeltaViewAngles();
    849 			delta.yaw += newRotation.ToMat3()[0].ToYaw();
    850 			actor->SetDeltaViewAngles( delta );
    851 		}
    852 	}
    853 
    854 	return PUSH_OK;
    855 }
    856 
    857 /*
    858 ============
    859 idPush::TryTranslatePushEntity
    860 ============
    861 */
    862 #ifdef _DEBUG
    863 //	#define TRANSLATIONAL_PUSH_DEBUG
    864 #endif
    865 
    866 int idPush::TryTranslatePushEntity( trace_t &results, idEntity *check, idClipModel *clipModel, const int flags,
    867 										const idVec3 &newOrigin, const idVec3 &move ) {
    868 	trace_t		trace;
    869 	idVec3		checkMove;
    870 	idVec3		oldOrigin;
    871 	idPhysics	*physics;
    872 
    873 	physics = check->GetPhysics();
    874 
    875 #ifdef TRANSLATIONAL_PUSH_DEBUG
    876 	bool startsolid = false;
    877 	if ( physics->ClipContents( clipModel ) ) {
    878 		startsolid = true;
    879 	}
    880 #endif
    881 
    882 	results.fraction = 1.0f;
    883 	results.endpos = newOrigin;
    884 	results.endAxis = clipModel->GetAxis();
    885 	memset( &results.c, 0, sizeof( results.c ) );
    886 
    887 	// always pushed when standing on the pusher
    888 	if ( physics->IsGroundClipModel( clipModel->GetEntity()->entityNumber, clipModel->GetId() ) ) {
    889 		// move the entity colliding with all other entities except the pusher itself
    890 		ClipEntityTranslation( trace, check, NULL, clipModel, move );
    891 		// if there is a collision
    892 		if ( trace.fraction < 1.0f ) {
    893 			// vector along which the entity is pushed
    894 			checkMove = move * trace.fraction;
    895 			// test if the entity can stay at it's partly pushed position by moving the entity in reverse only colliding with pusher
    896 			ClipEntityTranslation( results, check, clipModel, NULL, -(move - checkMove) );
    897 			// if there is a collision
    898 			if ( results.fraction < 1.0f ) {
    899 
    900 				// FIXME: try to push the blocking entity as well or try to slide along collision plane(s)?
    901 
    902 				results.c.normal = -results.c.normal;
    903 				results.c.dist = -results.c.dist;
    904 
    905 				// the entity will be crushed between the pusher and some other entity
    906 				return PUSH_BLOCKED;
    907 			}
    908 		}
    909 		else {
    910 			// vector along which the entity is pushed
    911 			checkMove = move;
    912 		}
    913 	}
    914 	else {
    915 		// move entity in reverse only colliding with pusher
    916 		ClipEntityTranslation( results, check, clipModel, NULL, -move );
    917 		// if no collision with the pusher then the entity is not pushed by the pusher
    918 		if ( results.fraction >= 1.0f ) {
    919 			return PUSH_NO;
    920 		}
    921 		// vector along which the entity is pushed
    922 		checkMove = move * (1.0f - results.fraction);
    923 		// move the entity colliding with all other entities except the pusher itself
    924 		ClipEntityTranslation( trace, check, NULL, clipModel, checkMove );
    925 		// if there is a collisions
    926 		if ( trace.fraction < 1.0f ) {
    927 
    928 			results.c.normal = -results.c.normal;
    929 			results.c.dist = -results.c.dist;
    930 
    931 			// FIXME: try to push the blocking entity as well ?
    932 			// FIXME: handle sliding along more than one collision plane ?
    933 			// FIXME: this code has issues, player pushing box into corner in "maps/mre/aaron/test.map"
    934 
    935 /*
    936 			oldOrigin = physics->GetOrigin();
    937 
    938 			// movement still remaining
    939 			checkMove *= (1.0f - trace.fraction);
    940 
    941 			// project the movement along the collision plane
    942 			if ( !checkMove.ProjectAlongPlane( trace.c.normal, 0.1f, 1.001f ) ) {
    943 				return PUSH_BLOCKED;
    944 			}
    945 			checkMove *= 1.001f;
    946 
    947 			// move entity from collision point along the collision plane
    948 			physics->SetOrigin( trace.endpos );
    949 			ClipEntityTranslation( trace, check, NULL, NULL, checkMove );
    950 
    951 			if ( trace.fraction < 1.0f ) {
    952 				physics->SetOrigin( oldOrigin );
    953 				return PUSH_BLOCKED;
    954 			}
    955 
    956 			checkMove = trace.endpos - oldOrigin;
    957 
    958 			// move entity in reverse only colliding with pusher
    959 			physics->SetOrigin( trace.endpos );
    960 			ClipEntityTranslation( trace, check, clipModel, NULL, -move );
    961 
    962 			physics->SetOrigin( oldOrigin );
    963 */
    964 			if ( trace.fraction < 1.0f ) {
    965 				return PUSH_BLOCKED;
    966 			}
    967 		}
    968 	}
    969 
    970 	SaveEntityPosition( check );
    971 
    972 	// translate the entity
    973 	physics->Translate( checkMove );
    974 
    975 #ifdef TRANSLATIONAL_PUSH_DEBUG
    976 	// set the pusher in the translated position
    977 	clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), newOrigin, clipModel->GetAxis() );
    978 	if ( physics->ClipContents( clipModel ) ) {
    979 		if ( !startsolid ) {
    980 			int bah = 1;
    981 		}
    982 	}
    983 #endif
    984 
    985 	return PUSH_OK;
    986 }
    987 
    988 /*
    989 ============
    990 idPush::DiscardEntities
    991 ============
    992 */
    993 int idPush::DiscardEntities( idEntity *entityList[], int numEntities, int flags, idEntity *pusher ) {
    994 	int i, num;
    995 	idEntity *check;
    996 
    997 	// remove all entities we cannot or should not push from the list
    998 	for ( num = i = 0; i < numEntities; i++ ) {
    999 		check = entityList[ i ];
   1000 
   1001 		// if the physics object is not pushable
   1002 		if ( !check->GetPhysics()->IsPushable() ) {
   1003 			continue;
   1004 		}
   1005 
   1006 		// if the entity doesn't clip with this pusher
   1007 		if ( !( check->GetPhysics()->GetClipMask() & pusher->GetPhysics()->GetContents() ) ) {
   1008 			continue;
   1009 		}
   1010 
   1011 		// don't push players in noclip mode
   1012 		if ( check->IsType( idPlayer::Type ) && static_cast<idPlayer *>(check)->noclip ) {
   1013 			continue;
   1014 		}
   1015 
   1016 		// if we should only push idMoveable entities
   1017 		if ( ( flags & PUSHFL_ONLYMOVEABLE ) && !check->IsType( idMoveable::Type ) ) {
   1018 			continue;
   1019 		}
   1020 
   1021 		// if we shouldn't push entities the clip model rests upon
   1022 		if ( flags & PUSHFL_NOGROUNDENTITIES ) {
   1023 			if ( pusher->GetPhysics()->IsGroundEntity( check->entityNumber ) ) {
   1024 				continue;
   1025 			}
   1026 		}
   1027 
   1028 		// keep entity in list
   1029 		entityList[ num++ ] = entityList[i];
   1030 	}
   1031 
   1032 	return num;
   1033 }
   1034 
   1035 /*
   1036 ============
   1037 idPush::ClipTranslationalPush
   1038 
   1039   Try to push other entities by moving the given entity.
   1040 ============
   1041 */
   1042 float idPush::ClipTranslationalPush( trace_t &results, idEntity *pusher, const int flags,
   1043 										const idVec3 &newOrigin, const idVec3 &translation ) {
   1044 	int			i, listedEntities, res;
   1045 	idEntity	*check, *entityList[ MAX_GENTITIES ];
   1046 	idBounds	bounds, pushBounds;
   1047 	idVec3		clipMove, clipOrigin, oldOrigin, dir, impulse;
   1048 	trace_t		pushResults;
   1049 	bool		wasEnabled;
   1050 	float		totalMass;
   1051 	idClipModel *clipModel;
   1052 
   1053 	clipModel = pusher->GetPhysics()->GetClipModel();
   1054 
   1055 	totalMass = 0.0f;
   1056 
   1057 	results.fraction = 1.0f;
   1058 	results.endpos = newOrigin;
   1059 	results.endAxis = clipModel->GetAxis();
   1060 	memset( &results.c, 0, sizeof( results.c ) );
   1061 
   1062 	if ( translation == vec3_origin ) {
   1063 		return totalMass;
   1064 	}
   1065 
   1066 	dir = translation;
   1067 	dir.Normalize();
   1068 	dir.z += 1.0f;
   1069 	dir *= 10.0f;
   1070 
   1071 	// get bounds for the whole movement
   1072 	bounds = clipModel->GetBounds();
   1073 	if ( bounds[0].x >= bounds[1].x ) {
   1074 		return totalMass;
   1075 	}
   1076 	pushBounds.FromBoundsTranslation( bounds, clipModel->GetOrigin(), clipModel->GetAxis(), translation );
   1077 
   1078 	wasEnabled = clipModel->IsEnabled();
   1079 
   1080 	// make sure we don't get the pushing clip model in the list
   1081 	clipModel->Disable();
   1082 
   1083 	listedEntities = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES );
   1084 
   1085 	// discard entities we cannot or should not push
   1086 	listedEntities = DiscardEntities( entityList, listedEntities, flags, pusher );
   1087 
   1088 	if ( flags & PUSHFL_CLIP ) {
   1089 
   1090 		// can only clip movement of a trace model
   1091 		assert( clipModel->IsTraceModel() );
   1092 
   1093 		// disable to be pushed entities for collision detection
   1094 		for ( i = 0; i < listedEntities; i++ ) {
   1095 			entityList[i]->GetPhysics()->DisableClip();
   1096 		}
   1097 
   1098 		gameLocal.clip.Translation( results, clipModel->GetOrigin(), clipModel->GetOrigin() + translation, clipModel, clipModel->GetAxis(), pusher->GetPhysics()->GetClipMask(), NULL );
   1099 
   1100 		// enable to be pushed entities for collision detection
   1101 		for ( i = 0; i < listedEntities; i++ ) {
   1102 			entityList[i]->GetPhysics()->EnableClip();
   1103 		}
   1104 
   1105 		if ( results.fraction == 0.0f ) {
   1106 			if ( wasEnabled ) {
   1107 				clipModel->Enable();
   1108 			}
   1109 			return totalMass;
   1110 		}
   1111 
   1112 		clipMove = results.endpos - clipModel->GetOrigin();
   1113 		clipOrigin = results.endpos;
   1114 
   1115 	}
   1116 	else {
   1117 
   1118 		clipMove = translation;
   1119 		clipOrigin = newOrigin;
   1120 	}
   1121 
   1122 	// we have to enable the clip model because we use it during pushing
   1123 	clipModel->Enable();
   1124 
   1125 	// save pusher old position
   1126 	oldOrigin = clipModel->GetOrigin();
   1127 
   1128 	// try to push the entities
   1129 	for ( i = 0; i < listedEntities; i++ ) {
   1130 
   1131 		check = entityList[ i ];
   1132 
   1133 		idPhysics *physics = check->GetPhysics();
   1134 
   1135 		// disable the entity for collision detection
   1136 		physics->DisableClip();
   1137 
   1138 		res = TryTranslatePushEntity( pushResults, check, clipModel, flags, clipOrigin, clipMove );
   1139 
   1140 		// enable the entity for collision detection
   1141 		physics->EnableClip();
   1142 
   1143 		// if the entity is pushed
   1144 		if ( res == PUSH_OK ) {
   1145 			// set the pusher in the translated position
   1146 			clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), newOrigin, clipModel->GetAxis() );
   1147 			// the entity might be pushed off the ground
   1148 			physics->EvaluateContacts();
   1149 			// put pusher back in old position
   1150 			clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), oldOrigin, clipModel->GetAxis() );
   1151 
   1152 			// wake up this object
   1153 			if ( flags & PUSHFL_APPLYIMPULSE ) {
   1154 				impulse = physics->GetMass() * dir;
   1155 			} else {
   1156 				impulse.Zero();
   1157 			}
   1158 			check->ApplyImpulse( clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), impulse );
   1159 
   1160 			// add mass of pushed entity
   1161 			totalMass += physics->GetMass();
   1162 		}
   1163 
   1164 		// if the entity is not blocking
   1165 		if ( res != PUSH_BLOCKED ) {
   1166 			continue;
   1167 		}
   1168 
   1169 		// if the blocking entity is a projectile
   1170 		if ( check->IsType( idProjectile::Type ) ) {
   1171 			check->ProcessEvent( &EV_Explode );
   1172 			continue;
   1173 		}
   1174 
   1175 		// if blocking entities should be crushed
   1176 		if ( flags & PUSHFL_CRUSH ) {
   1177 			check->Damage( clipModel->GetEntity(), clipModel->GetEntity(), vec3_origin, "damage_crush", 1.0f, CLIPMODEL_ID_TO_JOINT_HANDLE( pushResults.c.id ) );
   1178 			continue;
   1179 		}
   1180 
   1181 		// if the entity is an active articulated figure and gibs
   1182 		if ( check->IsType( idAFEntity_Base::Type ) && check->spawnArgs.GetBool( "gib" ) ) {
   1183 			if ( static_cast<idAFEntity_Base *>(check)->IsActiveAF() ) {
   1184 				check->ProcessEvent( &EV_Gib, "damage_Gib" );
   1185 			}
   1186 		}
   1187 
   1188 		// if the entity is a moveable item and gibs
   1189 		if ( check->IsType( idMoveableItem::Type ) && check->spawnArgs.GetBool( "gib" ) ) {
   1190 			check->ProcessEvent( &EV_Gib, "damage_Gib" );
   1191 		}
   1192 
   1193 		// blocked
   1194 		results = pushResults;
   1195 		results.fraction = 0.0f;
   1196 		results.endAxis = clipModel->GetAxis();
   1197 		results.endpos = clipModel->GetOrigin();
   1198 		results.c.entityNum = check->entityNumber;
   1199 		results.c.id = 0;
   1200 
   1201 		if ( !wasEnabled ) {
   1202 			clipModel->Disable();
   1203 		}
   1204 
   1205 		return totalMass;
   1206 	}
   1207 
   1208 	if ( !wasEnabled ) {
   1209 		clipModel->Disable();
   1210 	}
   1211 
   1212 	return totalMass;
   1213 }
   1214 
   1215 /*
   1216 ============
   1217 idPush::ClipRotationalPush
   1218 
   1219   Try to push other entities by moving the given entity.
   1220 ============
   1221 */
   1222 float idPush::ClipRotationalPush( trace_t &results, idEntity *pusher, const int flags,
   1223 									const idMat3 &newAxis, const idRotation &rotation ) {
   1224 	int			i, listedEntities, res;
   1225 	idEntity	*check, *entityList[ MAX_GENTITIES ];
   1226 	idBounds	bounds, pushBounds;
   1227 	idRotation	clipRotation;
   1228 	idMat3		clipAxis, oldAxis;
   1229 	trace_t		pushResults;
   1230 	bool		wasEnabled;
   1231 	float		totalMass;
   1232 	idClipModel *clipModel;
   1233 
   1234 	clipModel = pusher->GetPhysics()->GetClipModel();
   1235 
   1236 	totalMass = 0.0f;
   1237 
   1238 	results.fraction = 1.0f;
   1239 	results.endpos = clipModel->GetOrigin();
   1240 	results.endAxis = newAxis;
   1241 	memset( &results.c, 0, sizeof( results.c ) );
   1242 
   1243 	if ( !rotation.GetAngle() ) {
   1244 		return totalMass;
   1245 	}
   1246 
   1247 	// get bounds for the whole movement
   1248 	bounds = clipModel->GetBounds();
   1249 	if ( bounds[0].x >= bounds[1].x ) {
   1250 		return totalMass;
   1251 	}
   1252 	pushBounds.FromBoundsRotation( bounds, clipModel->GetOrigin(), clipModel->GetAxis(), rotation );
   1253 
   1254 	wasEnabled = clipModel->IsEnabled();
   1255 
   1256 	// make sure we don't get the pushing clip model in the list
   1257 	clipModel->Disable();
   1258 
   1259 	listedEntities = gameLocal.clip.EntitiesTouchingBounds( pushBounds, -1, entityList, MAX_GENTITIES );
   1260 
   1261 	// discard entities we cannot or should not push
   1262 	listedEntities = DiscardEntities( entityList, listedEntities, flags, pusher );
   1263 
   1264 	if ( flags & PUSHFL_CLIP ) {
   1265 
   1266 		// can only clip movement of a trace model
   1267 		assert( clipModel->IsTraceModel() );
   1268 
   1269 		// disable to be pushed entities for collision detection
   1270 		for ( i = 0; i < listedEntities; i++ ) {
   1271 			entityList[i]->GetPhysics()->DisableClip();
   1272 		}
   1273 
   1274 		gameLocal.clip.Rotation( results, clipModel->GetOrigin(), rotation, clipModel, clipModel->GetAxis(), pusher->GetPhysics()->GetClipMask(), NULL );
   1275 
   1276 		// enable to be pushed entities for collision detection
   1277 		for ( i = 0; i < listedEntities; i++ ) {
   1278 			entityList[i]->GetPhysics()->EnableClip();
   1279 		}
   1280 
   1281 		if ( results.fraction == 0.0f ) {
   1282 			if ( wasEnabled ) {
   1283 				clipModel->Enable();
   1284 			}
   1285 			return totalMass;
   1286 		}
   1287 
   1288 		clipRotation = rotation * results.fraction;
   1289 		clipAxis = results.endAxis;
   1290 	}
   1291 	else {
   1292 
   1293 		clipRotation = rotation;
   1294 		clipAxis = newAxis;
   1295 	}
   1296 
   1297 	// we have to enable the clip model because we use it during pushing
   1298 	clipModel->Enable();
   1299 
   1300 	// save pusher old position
   1301 	oldAxis = clipModel->GetAxis();
   1302 
   1303 	// try to push all the entities
   1304 	for ( i = 0; i < listedEntities; i++ ) {
   1305 
   1306 		check = entityList[ i ];
   1307 
   1308 		idPhysics *physics = check->GetPhysics();
   1309 
   1310 		// disable the entity for collision detection
   1311 		physics->DisableClip();
   1312 
   1313 		res = TryRotatePushEntity( pushResults, check, clipModel, flags, clipAxis, clipRotation );
   1314 
   1315 		// enable the entity for collision detection
   1316 		physics->EnableClip();
   1317 
   1318 		// if the entity is pushed
   1319 		if ( res == PUSH_OK ) {
   1320 			// set the pusher in the rotated position
   1321 			clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), newAxis );
   1322 			// the entity might be pushed off the ground
   1323 			physics->EvaluateContacts();
   1324 			// put pusher back in old position
   1325 			clipModel->Link( gameLocal.clip, clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), oldAxis );
   1326 
   1327 			// wake up this object
   1328 			check->ApplyImpulse( clipModel->GetEntity(), clipModel->GetId(), clipModel->GetOrigin(), vec3_origin );
   1329 
   1330 			// add mass of pushed entity
   1331 			totalMass += physics->GetMass();
   1332 		}
   1333 
   1334 		// if the entity is not blocking
   1335 		if ( res != PUSH_BLOCKED ) {
   1336 			continue;
   1337 		}
   1338 
   1339 		// if the blocking entity is a projectile
   1340 		if ( check->IsType( idProjectile::Type ) ) {
   1341 			check->ProcessEvent( &EV_Explode );
   1342 			continue;
   1343 		}
   1344 
   1345 		// if blocking entities should be crushed
   1346 		if ( flags & PUSHFL_CRUSH ) {
   1347 			check->Damage( clipModel->GetEntity(), clipModel->GetEntity(), vec3_origin, "damage_crush", 1.0f, CLIPMODEL_ID_TO_JOINT_HANDLE( pushResults.c.id ) );
   1348 			continue;
   1349 		}
   1350 
   1351 		// if the entity is an active articulated figure and gibs
   1352 		if ( check->IsType( idAFEntity_Base::Type ) && check->spawnArgs.GetBool( "gib" ) ) {
   1353 			if ( static_cast<idAFEntity_Base *>(check)->IsActiveAF() ) {
   1354 				check->ProcessEvent( &EV_Gib, "damage_Gib" );
   1355 			}
   1356 		}
   1357 
   1358 		// blocked
   1359 		results = pushResults;
   1360 		results.fraction = 0.0f;
   1361 		results.endAxis = clipModel->GetAxis();
   1362 		results.endpos = clipModel->GetOrigin();
   1363 		results.c.entityNum = check->entityNumber;
   1364 		results.c.id = 0;
   1365 
   1366 		if ( !wasEnabled ) {
   1367 			clipModel->Disable();
   1368 		}
   1369 
   1370 		return totalMass;
   1371 	}
   1372 
   1373 	if ( !wasEnabled ) {
   1374 		clipModel->Disable();
   1375 	}
   1376 
   1377 	return totalMass;
   1378 }
   1379 
   1380 #endif /* !NEW_PUSH */
   1381 
   1382 
   1383 /*
   1384 ============
   1385 idPush::ClipPush
   1386 
   1387   Try to push other entities by moving the given entity.
   1388 ============
   1389 */
   1390 float idPush::ClipPush( trace_t &results, idEntity *pusher, const int flags,
   1391 								const idVec3 &oldOrigin, const idMat3 &oldAxis,
   1392 									idVec3 &newOrigin, idMat3 &newAxis ) {
   1393 	idVec3 translation;
   1394 	idRotation rotation;
   1395 	float mass;
   1396 
   1397     mass = 0.0f;
   1398 
   1399 	results.fraction = 1.0f;
   1400 	results.endpos = newOrigin;
   1401 	results.endAxis = newAxis;
   1402 	memset( &results.c, 0, sizeof( results.c ) );
   1403 
   1404 	// translational push
   1405 	translation = newOrigin - oldOrigin;
   1406 
   1407 	// if the pusher translates
   1408 	if ( translation != vec3_origin ) {
   1409 
   1410 		mass += ClipTranslationalPush( results, pusher, flags, newOrigin, translation );
   1411 		if ( results.fraction < 1.0f ) {
   1412 			newOrigin = oldOrigin;
   1413 			newAxis = oldAxis;
   1414 			return mass;
   1415 		}
   1416 	} else {
   1417 		newOrigin = oldOrigin;
   1418 	}
   1419 
   1420 	// rotational push
   1421 	rotation = ( oldAxis.Transpose() * newAxis ).ToRotation();
   1422 	rotation.SetOrigin( newOrigin );
   1423 	rotation.Normalize180();
   1424 	rotation.ReCalculateMatrix();		// recalculate the rotation matrix to avoid accumulating rounding errors
   1425 
   1426 	// if the pusher rotates
   1427 	if ( rotation.GetAngle() != 0.0f ) {
   1428 
   1429 		// recalculate new axis to avoid floating point rounding problems
   1430 		newAxis = oldAxis * rotation.ToMat3();
   1431 		newAxis.OrthoNormalizeSelf();
   1432 		newAxis.FixDenormals();
   1433 		newAxis.FixDegeneracies();
   1434 
   1435 		pusher->GetPhysics()->GetClipModel()->SetPosition( newOrigin, oldAxis );
   1436 
   1437 		mass += ClipRotationalPush( results, pusher, flags, newAxis, rotation );
   1438 		if ( results.fraction < 1.0f ) {
   1439 			newOrigin = oldOrigin;
   1440 			newAxis = oldAxis;
   1441 			return mass;
   1442 		}
   1443 	} else {
   1444 		newAxis = oldAxis;
   1445 	}
   1446 
   1447 	return mass;
   1448 }